~starkingdoms/starkingdoms

6ef68227aec22ed05ef344675e4e747cedc8d4ac — core 26 days ago 39baf66
chore: please the clippy
M crates/unified/src/client/parts.rs => crates/unified/src/client/parts.rs +10 -9
@@ 3,9 3,9 @@ use std::f32::consts::PI;
use crate::attachment::{Joint, JointOf, Joints, PartInShip, Peer, SnapOf, SnapOfJoint};
use crate::client::Me;
use crate::client::colors::GREEN;
use crate::client::key_input::AttachmentDebugRes;
use crate::ecs::{CursorWorldCoordinates, DragRequestEvent, Part};
use bevy::color::palettes::css::{ORANGE, PURPLE, RED, YELLOW};
use crate::client::ship::attachment::AttachmentDebugRes;
use crate::prelude::*;

pub fn parts_plugin(app: &mut App) {


@@ 111,7 111,6 @@ struct Ghost {
    pub vel: Vec3,
}

const TRANSLATION_SMOOTH: f32 = 0.3;
const ROTATION_SMOOTH: f32 = 0.1;

fn on_part_click(


@@ 180,6 179,14 @@ fn on_part_release(
    drag.0 = None;
}

const F: f32 = 2.0; // frequency (Hz)
// 0.0 is undamped, 0.0-1.0 is underdamped, 1.0 is critical, and >1.0 is over
const ZETA: f32 = 0.7; // damping
const R: f32 = -0.6; // initial response speed
const K1: f32 = ZETA/(PI*F);
const K2: f32 = 1.0/((2.0*PI*F)*(2.0*PI*F));
const K3: f32 = (R*ZETA)/(2.0*PI*F);

/// !IMPORTANT!
/// This function forms the bulk of the attachment system.
/// PLEASE DO NOT MODIFY OR MOVE


@@ 345,13 352,7 @@ fn update_drag_ghosts(
    ghost.rotation = ghost.rotation.slerp(ghost_info.rot, ROTATION_SMOOTH);
    let target_vel = (best_target.translation - ghost_info.last_target_pos)
        /time.delta_secs();
    const F: f32 = 2.0; // frequency (Hz)
    // 0.0 is undamped, 0.0-1.0 is underdamped, 1.0 is critical, and >1.0 is over
    const ZETA: f32 = 0.7; // damping
    const R: f32 = -0.6; // initial response speed
    const K1: f32 = ZETA/(PI*F);
    const K2: f32 = 1.0/((2.0*PI*F)*(2.0*PI*F));
    const K3: f32 = (R*ZETA)/(2.0*PI*F);

    let a = (best_target.translation - ghost.translation
        + K3*target_vel - K1*ghost_info.vel)/K2;


M crates/unified/src/client_plugins.rs => crates/unified/src/client_plugins.rs +0 -7
@@ 1,14 1,11 @@
use crate::client::ClientPlugin;
use aeronet_replicon::client::AeronetRepliconClientPlugin;
use aeronet_websocket::client::WebSocketClientPlugin;
use bevy::DefaultPlugins;
use bevy::app::{PluginGroup, PluginGroupBuilder};
use bevy::dev_tools::picking_debug::DebugPickingPlugin;
use bevy::ecs::schedule::ScheduleLabel;
use bevy::log::LogPlugin;
use crate::prelude::*;
use bevy::ui::UiPlugin;
use bevy_replicon::RepliconPlugins;

pub struct ClientPluginGroup {
    pub server: Option<String>,


@@ 24,10 21,6 @@ impl PluginGroup for ClientPluginGroup {
                server: self.server,
            })
            .add(UiPlugin)
            //.add(PhysicsDebugPlugin) -- debug rendering
            //.add(FpsOverlayPlugin::default())
            //.add(EguiPlugin::default())
            //.add(WorldInspectorPlugin::new())
    }
}


M crates/unified/src/main.rs => crates/unified/src/main.rs +2 -70
@@ 47,7 47,7 @@ use clap::Parser;
use crate::client_plugins::ClientPluginGroup;
#[cfg(not(target_arch = "wasm32"))]
use crate::server_plugins::ServerPluginGroup;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::net::SocketAddr;
use std::process::exit;
use std::str::FromStr;
use std::time::Duration;


@@ 191,73 191,5 @@ fn main() -> AppExit {
        }).unwrap();
    }

    match cli {
        Cli::Client { .. } => { run(cli) },
        #[cfg(feature = "native")]
        Cli::ParticleEditor { .. } => { run(cli) },
        #[cfg(feature = "native")]
        Cli::Server { with_client, bind, hotpatching_enabled, .. } => {
            run(cli)
            /*
            if !with_client {
                run(cli)
            } else {
                warn!("-----------------------------------------");
                warn!("RUNNING IN EXPERIMENTAL LISTENSERVER MODE");
                warn!("-----------------------------------------");
                warn!("This mode is HIGHLY EXPERIMENTAL, relies on janky threading, and may or may not work at all.");
                warn!("Use at your own risk. If weird things happen, try running separately.");
                warn!("-----------------------------------------");
                warn!("RUNNING IN EXPERIMENTAL LISTENSERVER MODE");
                warn!("-----------------------------------------");

                let scli_clone = cli.clone();
                let server_thread = std::thread::Builder::new()
                    .name("server".to_string())
                    .spawn(move || {
                        info!("starting server thread...");
                        run(scli_clone)
                    })
                    .unwrap();

                let clt_exit;

                    info!("starting client thread...");

                    let is_multicast = bind.ip().is_unspecified();

                    let target_ip = if is_multicast {
                        if bind.ip().is_ipv4() { IpAddr::V4(Ipv4Addr::LOCALHOST) } else { IpAddr::V6(Ipv6Addr::LOCALHOST) }
                    } else {
                        bind.ip()
                    };
                    let target_port = bind.port();
                    let target_ip_str = match target_ip {
                        IpAddr::V4(a) => a.to_string(),
                        IpAddr::V6(a) => format!("[{a}]"),
                    };
                    let target_url = format!("ws://{target_ip_str}:{target_port}");
                    info!("starting the client with autocalculated target server url {target_url}");

                    let cli2 = Cli::Client { server: target_url, hotpatching_enabled };

                    clt_exit = run(cli2);

                    if let AppExit::Error(c) = clt_exit {
                        error!("client exited with error {c}");
                    }
                    info!("-------- CLIENT HAS EXITED --------");
                    info!("  ^C  to stop the server");

                let srv_exit = server_thread.join().unwrap();

                match srv_exit {
                    AppExit::Error(c) => AppExit::Error(c),
                    _ => match clt_exit {
                        AppExit::Error(c) => AppExit::Error(c),
                        _ => AppExit::Success
                    }
                }*/
        }
    }
    run(cli)
}

M crates/unified/src/server/earth_parts.rs => crates/unified/src/server/earth_parts.rs +5 -5
@@ 1,5 1,5 @@
use crate::config::planet::Planet;
use crate::server::part::SpawnPartRequest;
use crate::server::part::{SpawnPartBundle, SpawnPartRequest};
use crate::server::world_config::WorldConfigResource;
use bevy::app::App;
use crate::prelude::*;


@@ 46,8 46,8 @@ fn spawn_parts_on_earth(
    new_transform.translation += spawn_planet_pos.translation;

    commands
        .spawn(SpawnPartRequest(
            asset_server.load("config/parts/housing.part.toml"),
        ))
        .insert(new_transform);
        .spawn(SpawnPartBundle {
            req: SpawnPartRequest(asset_server.load("config/parts/housing.part.toml")),
            transform: new_transform
        });
}

M crates/unified/src/server/player.rs => crates/unified/src/server/player.rs +31 -176
@@ 1,16 1,12 @@
pub mod join;

use crate::attachment::{Joint, JointOf, Joints, PartInShip, Peer, SnapOf, SnapOfJoint};
use crate::config::planet::Planet;
use crate::ecs::{DragRequestEvent, Part, Player, PlayerStorage, PlayerThrust, ThrustEvent};
use crate::server::part::SpawnPartRequest;
use crate::ecs::{DragRequestEvent, Part, Player, PlayerStorage};
use crate::server::system_sets::PlayerInputSet;
use crate::server::world_config::WorldConfigResource;
use crate::server::{ConnectedGameEntity, ConnectedNetworkEntity};
use crate::server::ConnectedNetworkEntity;
use crate::prelude::*;
use bevy_replicon::prelude::{ClientId, FromClient};
use std::f32::consts::PI;
use crate::config::world::GlobalWorldConfig;

pub fn player_management_plugin(app: &mut App) {
    app.add_systems(


@@ 18,7 14,6 @@ pub fn player_management_plugin(app: &mut App) {
        (
            join::handle_pending_players,
            join::handle_new_players,
            player_thrust,
            magic_fuel_regen,
            dragging,
        )


@@ 40,8 35,8 @@ fn complete_partial_disconnects(
    mut q_maybe_peer: Query<Option<&Peer>>,
    mut q_joint_of_part: Query<&JointOf>,
    mut q_is_hearty: Query<Option<&Player>>,
    mut dc_q_joints: Query<(&Joint, &JointOf, &Transform, Option<&Peer>, Entity)>,
    mut dc_q_only_joints: Query<&Joints>,
    dc_q_joints: Query<(&Joint, &JointOf, &Transform, Option<&Peer>, Entity)>,
    dc_q_only_joints: Query<&Joints>,

    mut commands: Commands,
) {


@@ 71,12 66,9 @@ fn complete_partial_disconnects(
        let mut disconnect_queue = vec![];
        commands.entity(partially_disconnected_part).remove::<PartInShip>(); // they're no longer in the ship, remove them from the meta
        disconnect_part(
            (partially_disconnected_part, match q_joints.get(partially_disconnected_part) {
                Ok(j) => j,
                Err(e) => {
                    warn!(?partially_disconnected_part, "part does not have a Joints? this should be impossible...");
                    continue;
                }
            (partially_disconnected_part, if let Ok(j) = q_joints.get(partially_disconnected_part) { j } else {
                warn!(?partially_disconnected_part, "part does not have a Joints? this should be impossible...");
                continue;
            }),
            &dc_q_joints,
            &dc_q_only_joints,


@@ 98,7 90,7 @@ fn can_reach_hearty(
    mut q_joint_of_part: Query<&JointOf>,
    mut q_is_hearty: Query<Option<&Player>>,

    mut visited_joints: &mut Vec<Entity>,
    visited_joints: &mut Vec<Entity>,

    is_top_of_recursion: bool
) -> bool {


@@ 142,30 134,24 @@ fn can_reach_hearty(
                debug!("partial detach DFS: visited {} joints => found hearty @ {:?}", visited_joints.len(), other_part);
                debug!("-> via {:?}", part);
                return true;
            } else {
                // not hearty. but can the other part reach hearty?
                let can_other_part_reach = can_reach_hearty(
                    other_part,
                    q_joints.reborrow(),
                    q_maybe_peer.reborrow(),
                    q_joint_of_part.reborrow(),
                    q_is_hearty.reborrow(),
                    visited_joints,
                    false
                );
                if can_other_part_reach {
                    // great, they are connected
                    // log that we're in the path, then bubble up
                    debug!("-> via {:?}", part);
                    return true;
                } else {
                    // lame. continue to next part, and remove PartInShip as they're not connected anymore
                    continue 'to_next_joint;
                }
            }
        } else {
            // we do not have a peer. move on
            continue 'to_next_joint;
            // not hearty. but can the other part reach hearty?
            let can_other_part_reach = can_reach_hearty(
                other_part,
                q_joints.reborrow(),
                q_maybe_peer.reborrow(),
                q_joint_of_part.reborrow(),
                q_is_hearty.reborrow(),
                visited_joints,
                false
            );
            if can_other_part_reach {
                // great, they are connected
                // log that we're in the path, then bubble up
                debug!("-> via {:?}", part);
                return true;
            }

        }
    }



@@ 206,13 192,12 @@ fn disconnect_part(
        physics_joint.despawn();

        // delete the other side's peer that exists for some reason
        let Ok((_, other_joint_of, _, other_peer, _)) = q_joints.get(peer.peer_joint_entity_id) else {
        let Ok((_, other_joint_of, _, _, _)) = q_joints.get(peer.peer_joint_entity_id) else {
            continue;
        };
        let Some(other_peer) = other_peer else { continue; };
        commands.entity(peer.peer_joint_entity_id).remove::<Peer>();

        let Ok(other_joints) = q_only_joints.get(other_joint_of.0) else {
        let Ok(_) = q_only_joints.get(other_joint_of.0) else {
            continue
        };
        //commands.entity(other_joint_of.0).remove::<PartInShip>();


@@ 241,13 226,11 @@ fn dragging(
            &mut AngularVelocity,
            &Part,
        ),
        (Without<Joint>),
        Without<Joint>,
    >,
    snaps: Query<(&SnapOf, &SnapOfJoint)>,
    joints: Query<(&Joint, &JointOf, &Transform, Option<&Peer>, Entity)>,
    peer: Query<(Entity, &Peer, &JointOf)>,
    q_joints: Query<&Joints>,
    q_joint: Query<&FixedJoint>,
    clients: Query<&ConnectedNetworkEntity>,
    q_ls_me: Query<Entity, With<crate::client::Me>>,
    mut commands: Commands,


@@ 269,7 252,9 @@ fn dragging(

        debug!(?event, "got drag request event");

        #[allow(unused_assignments, reason = "false positive")]
        let mut teleport_to_translation = Vec2::new(0.0, 0.0);
        #[allow(unused_assignments, reason = "false positive")]
        let mut teleport_to_rotation = Quat::from_rotation_z(0.0);
        let mut new_linvel = None;
        let mut new_angvel = None;


@@ 349,7 334,6 @@ fn dragging(

            // great, we have a valid peering request

            let did_disconnect = q_joint.get(source_part.2).is_ok();
            let mut processed = vec![source_joint.4];
            disconnect_part(
                (source_part.2, source_part.4),


@@ 391,9 375,6 @@ fn dragging(

            let target_position = target_part.0.mul_transform(*target_joint.2);

            let rotation = event.set_rotation.to_scaled_axis().z;
            let rotation = Rot2::radians(rotation);

            teleport_to_translation = target_position.translation.xy();
            teleport_to_rotation = target_position.rotation
                * source_joint.0.transform.rotation.inverse()


@@ 420,10 401,8 @@ fn dragging(
        }

        let mut part = parts.get_mut(event.drag_target).unwrap();
        debug!("{:?} {:?}", part.0.translation, teleport_to_translation);
        part.0.translation.x = teleport_to_translation.x;
        part.0.translation.y = teleport_to_translation.y;
        debug!("{:?} {:?}", part.0.translation, teleport_to_translation);
        part.0.rotation = teleport_to_rotation; // client calculates this; no reason to recalculate
        if let Some(new_vel) = new_linvel {
            *part.3 = new_vel;


@@ 435,132 414,8 @@ fn dragging(
    }
}



fn magic_fuel_regen(players: Query<&mut PlayerStorage, With<Player>>, time: Res<Time>) {
    for mut storage in players {
        storage.fuel = (storage.fuel + 5.0 * time.delta_secs()).min(storage.fuel_capacity);
    }
}

fn player_thrust(
    mut players: Query<(
        &Transform,
        &Part,
        &mut ConstantForce,
        &mut PlayerThrust,
        &mut PlayerStorage,
    )>,
    clients: Query<&ConnectedNetworkEntity>,
    mut thrust_event: MessageReader<FromClient<ThrustEvent>>,
    q_ls_me: Query<Entity, With<crate::client::Me>>,
    world_config: Res<WorldConfigResource>,
) {
    for FromClient {
        client_id,
        message: event,
    } in thrust_event.read()
    {
        let player_hearty_entity = match client_id {
            ClientId::Client(client_entity) => {
                let ConnectedNetworkEntity {
                    game_entity: player_hearty_entity,
                } = clients.get(*client_entity).unwrap();
                player_hearty_entity
            },
            ClientId::Server => &q_ls_me.iter().next().unwrap()
        };

        let Ok((_, _, _, mut thrust, _)) = players.get_mut(*player_hearty_entity) else {
            continue;
        };

        match *event {
            ThrustEvent::Up(on) => thrust.up = on,
            ThrustEvent::Down(on) => thrust.down = on,
            ThrustEvent::Left(on) => thrust.left = on,
            ThrustEvent::Right(on) => thrust.right = on,
        }
    }
    for (transform, part, force, thrust, storage) in &mut players {
        let Some(world_config) = &world_config.config else {
            return;
        };

        let forward = (transform.rotation * Vec3::Y).xy();
        let external_force = ConstantForce::default();

        // indices are quadrants:
        // 1 | 0
        // --+--
        // 2 | 3
        //
        let mut thrusters = [0.0; 4];

        if thrust.up {
            thrusters[1] = 1.0;
            thrusters[2] = 1.0;
        }
        if thrust.down {
            thrusters[0] = 1.0;
            thrusters[3] = 1.0;
        }
        if thrust.left {
            thrusters[0] = 1.0;
            thrusters[2] = 1.0;
        }
        if thrust.right {
            thrusters[1] = 1.0;
            thrusters[3] = 1.0;
        }

        // prevent fuel wasting when turning while moving forward/reverse
        if thrusters[2] == thrusters[3] && thrusters[3] != 0.0 {
            (thrusters[2], thrusters[3]) = (0.0, 0.0);
        }
        if thrusters[0] == thrusters[1] && thrusters[1] != 0.0 {
            (thrusters[0], thrusters[1]) = (0.0, 0.0);
        }

        let half_size = Vec2::new(
            world_config.part.default_width / 2.0,
            world_config.part.default_height / 2.0,
        )
        .length();

        // TODO: TF does this mess even do?
        /*
        external_force += ConstantForce::at_point(
            -forward * thrusters[0] * world_config.hearty.thrust,
            transform.translation.xy()
                + half_size
                    * Vec2::new((1.0 * PI / 4.0).cos(), (1.0 * PI / 4.0).sin()).rotate(forward),
            transform.translation.xy(),
        );
        external_force += ConstantForce::at_point(
            forward * thrusters[1] * world_config.hearty.thrust,
            transform.translation.xy()
                + half_size
                    * Vec2::new((3.0 * PI / 4.0).cos(), (3.0 * PI / 4.0).sin()).rotate(forward),
            transform.translation.xy(),
        );
        external_force += ConstantForce::at_point(
            forward * thrusters[2] * world_config.hearty.thrust,
            transform.translation.xy()
                + half_size
                    * Vec2::new((5.0 * PI / 4.0).cos(), (5.0 * PI / 4.0).sin()).rotate(forward),
            transform.translation.xy(),
        );
        external_force += ConstantForce::at_point(
            -forward * thrusters[3] * world_config.hearty.thrust,
            transform.translation.xy()
                + half_size
                    * Vec2::new((7.0 * PI / 4.0).cos(), (7.0 * PI / 4.0).sin()).rotate(forward),
            transform.translation.xy(),
        );
        *force += external_force;

        storage.fuel -=
            thrusters.iter().sum::<f32>() * part.strong_config.thruster.as_ref().unwrap().flow_rate;*/
    }
}
}
\ No newline at end of file

M crates/unified/src/server/player/join.rs => crates/unified/src/server/player/join.rs +0 -4
@@ 92,9 92,6 @@ pub fn handle_pending_players(

pub fn ls_magically_invent_player(
    mut commands: Commands,
    world_config: Res<WorldConfigResource>,
    planets: Query<(&Transform, &Planet)>,
    asset_server: Res<AssetServer>,
) {
    // Magically invent a player for listenserver
    let fake_network_entity = commands.spawn(Replicated).id();


@@ 106,5 103,4 @@ pub fn ls_magically_invent_player(
        crate::client::Me
    )).id();
    debug!(?fake_network_entity, ?local_player, "listenserver: magically invented a player");
    //join_player(local_player, commands.reborrow(), wc, planets, &asset_server);
}
\ No newline at end of file

M crates/unified/src/server_plugins.rs => crates/unified/src/server_plugins.rs +1 -11
@@ 1,19 1,9 @@
use crate::config::part::PartConfig;
use crate::config::planet::PlanetConfigCollection;
use crate::config::world::GlobalWorldConfig;
use aeronet_replicon::server::AeronetRepliconServerPlugin;
use aeronet_websocket::server::WebSocketServerPlugin;
use avian2d::prelude::PhysicsInterpolationPlugin;
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::app::{PluginGroup, PluginGroupBuilder};
use std::net::SocketAddr;
use std::time::Duration;
use avian2d::PhysicsPlugins;
use bevy::state::app::StatesPlugin;

pub struct ServerPluginGroup {
    pub bind: SocketAddr,

M crates/unified/src/shared_plugins.rs => crates/unified/src/shared_plugins.rs +0 -3
@@ 1,12 1,9 @@
use avian2d::PhysicsPlugins;
use crate::attachment::{Joint, JointOf, PartInShip, Peer, Ship, SnapOf, SnapOfJoint};
use crate::config::planet::{Planet, PlanetConfigCollection};
use crate::ecs::{DragRequestEvent, Part, Particles, Player, PlayerStorage, ThrustEvent};
use bevy::app::{App, PluginGroup, PluginGroupBuilder};
use bevy::ui::UiPlugin;
use bevy_common_assets::toml::TomlAssetPlugin;
use crate::prelude::*;
//use bevy_rapier2d::prelude::*;
use bevy_replicon::prelude::{AppRuleExt, Channel, ClientMessageAppExt};
use crate::config::part::PartConfig;
use crate::config::world::GlobalWorldConfig;