~starkingdoms/starkingdoms

e2c74ff0a4af609afb2869c59835838dd76f639c — core 9 days ago 0a56ebc master
Revert "aaa ??? ?? ? ?? ???????????????????????????????????????????? i would like to explosion"

This reverts commit 0a56ebc381389eb63ab5cb3c3522d9ea2475e9d8.
M Cargo.lock => Cargo.lock +40 -38
@@ 834,7 834,7 @@ dependencies = [
 "js-sys",
 "notify-debouncer-full",
 "parking_lot",
 "ron",
 "ron 0.10.1",
 "serde",
 "stackfuture",
 "thiserror 2.0.17",


@@ 994,7 994,6 @@ dependencies = [
 "const-fnv1a-hash",
 "log",
 "serde",
 "sysinfo",
]

[[package]]


@@ 1191,6 1190,7 @@ dependencies = [
 "guillotiere",
 "half",
 "image",
 "ktx2",
 "rectangle-pack",
 "ruzstd",
 "serde",


@@ 1852,7 1852,6 @@ dependencies = [
 "bevy_image",
 "bevy_input",
 "bevy_math",
 "bevy_picking",
 "bevy_platform",
 "bevy_reflect",
 "bevy_sprite",


@@ 1866,7 1865,6 @@ dependencies = [
 "taffy",
 "thiserror 2.0.17",
 "tracing",
 "uuid",
]

[[package]]


@@ 4566,6 4564,15 @@ dependencies = [
]

[[package]]
name = "ktx2"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff7f53bdf698e7aa7ec916411bbdc8078135da11b66db5182675b2227f6c0d07"
dependencies = [
 "bitflags 2.10.0",
]

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


@@ 5112,15 5119,6 @@ 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"
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
dependencies = [
 "winapi",
]

[[package]]
name = "nu-ansi-term"
version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 5396,16 5394,6 @@ dependencies = [
]

[[package]]
name = "objc2-io-kit"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15"
dependencies = [
 "libc",
 "objc2-core-foundation",
]

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


@@ 6331,6 6319,20 @@ dependencies = [
]

[[package]]
name = "ron"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd490c5b18261893f14449cbd28cb9c0b637aebf161cd77900bfdedaff21ec32"
dependencies = [
 "bitflags 2.10.0",
 "once_cell",
 "serde",
 "serde_derive",
 "typeid",
 "unicode-ident",
]

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


@@ 6891,11 6893,13 @@ dependencies = [
 "ordered-float 5.1.0",
 "pico-args",
 "rand 0.9.2",
 "ron 0.12.0",
 "serde",
 "tracing-subscriber",
 "tracing-wasm",
 "url",
 "wasm-bindgen",
 "wgpu 26.0.1",
]

[[package]]


@@ 7045,20 7049,6 @@ dependencies = [
]

[[package]]
name = "sysinfo"
version = "0.37.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16607d5caffd1c07ce073528f9ed972d88db15dd44023fa57142963be3feb11f"
dependencies = [
 "libc",
 "memchr",
 "ntapi",
 "objc2-core-foundation",
 "objc2-io-kit",
 "windows 0.61.3",
]

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


@@ 8075,12 8065,14 @@ dependencies = [
 "js-sys",
 "log",
 "naga 26.0.0",
 "parking_lot",
 "portable-atomic",
 "profiling",
 "raw-window-handle",
 "smallvec",
 "static_assertions",
 "wasm-bindgen",
 "wasm-bindgen-futures",
 "web-sys",
 "wgpu-core 26.0.1",
 "wgpu-hal 26.0.6",


@@ 8112,7 8104,7 @@ dependencies = [
 "smallvec",
 "thiserror 2.0.17",
 "wgpu-core-deps-apple 25.0.0",
 "wgpu-core-deps-emscripten",
 "wgpu-core-deps-emscripten 25.0.0",
 "wgpu-core-deps-windows-linux-android 25.0.0",
 "wgpu-hal 25.0.2",
 "wgpu-types 25.0.0",


@@ 8143,6 8135,7 @@ dependencies = [
 "smallvec",
 "thiserror 2.0.17",
 "wgpu-core-deps-apple 26.0.0",
 "wgpu-core-deps-emscripten 26.0.0",
 "wgpu-core-deps-wasm",
 "wgpu-core-deps-windows-linux-android 26.0.0",
 "wgpu-hal 26.0.6",


@@ 8177,6 8170,15 @@ dependencies = [
]

[[package]]
name = "wgpu-core-deps-emscripten"
version = "26.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7670e390f416006f746b4600fdd9136455e3627f5bd763abf9a65daa216dd2d"
dependencies = [
 "wgpu-hal 26.0.6",
]

[[package]]
name = "wgpu-core-deps-wasm"
version = "26.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

M crates/unified/Cargo.toml => crates/unified/Cargo.toml +44 -91
@@ 7,12 7,33 @@ version = "0.1.0"

[dependencies]
bevy = { version = "0.17", default-features = false, features = [
    "serialize",
    "tonemapping_luts",
    "bevy_window",
    "bevy_asset",
    "bevy_winit",
    "bevy_render",
    "bevy_core_pipeline",
    "bevy_sprite",
    "bevy_text",
    "bevy_ui",
    "bevy_color",
    "bevy_input_focus",
    "bevy_log",
    "bevy_mesh",
    "bevy_state",
    "multi_threaded"
]}
    "multi_threaded",
    "bevy_dev_tools",
    "bevy_sprite_picking_backend",
    "bevy_mesh_picking_backend",
    "default_font",
    "png",
    "bevy_gizmos",
    "bevy_post_process",
    "bevy_anti_alias",
    "bevy_sprite_render",
    "bevy_ui_render",
    "zstd_rust",
    "debug"
] }

avian2d = { version = "0.4", default-features = false, features = [
    "2d",


@@ 39,12 60,15 @@ serde = { version = "1", features = ["derive"] }
rand = "0.9"
getrandom = { version = "0.3", features = [] }

aeronet = { version = "0.17", optional = true }
aeronet_replicon = { version = "0.17", optional = true }
aeronet_websocket = { version = "0.17", optional = true }
aeronet_transport = { version = "0.17", optional = true }
aeronet = "0.17"
aeronet_replicon = { version = "0.17", features = ["client"] }
aeronet_websocket = { version = "0.17", features = ["client"] }
aeronet_transport = "0.17"

ordered-float = { version = "5", features = ["serde"] }
ron = "0.12"

wgpu = "*"

pico-args = "0.5"



@@ 57,98 81,27 @@ good_lp = { version = "1.14", default-features = false, features = ["clarabel"],
built = { version = "0.8", features = ["git2", "chrono"] }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
ctrlc = { version = "3.5" }
ctrlc = { version = "3.5", optional = true }

[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = { version = "0.2" }
tracing-wasm = "0.2"
console_error_panic_hook = "0.1"

# This is a chaotic mess of conditional compilation, but it significantly slims build times.
# So, sorry not sorry /shrug
[features]
default = []

# enabled when compiling to the native platform
platform_native = [

]
# enabled when compiling to the web platform
platform_wasm = [
    "getrandom/wasm_js",
    "bevy/webgl2"
]

# include code required to actually make connections over the network
# compiled out during development for compile time
network = [
    "dep:aeronet",
    "dep:aeronet_replicon",
    "dep:aeronet_transport",
    "dep:aeronet_websocket"
]

# include server code
target_server = [
]
# include networking server code
target_net_server = [
    "network",
    "aeronet_websocket/server",
    "aeronet_replicon/server"
]

# include client code
target_client = [
    "dep:leafwing-input-manager",
    "dep:good_lp",
    "bevy/bevy_anti_alias",
    "bevy/bevy_camera",
    "bevy/bevy_color",
    "bevy/bevy_core_pipeline",
    "bevy/bevy_gizmos",
    "bevy/bevy_mesh_picking_backend",
    "bevy/bevy_picking",
    "bevy/bevy_post_process",
    "bevy/bevy_render",
    "bevy/bevy_sprite",
    "bevy/bevy_sprite_picking_backend",
    "bevy/bevy_picking",
    "bevy/bevy_sprite_render",
    "bevy/bevy_text",
    "bevy/bevy_ui",
    "bevy/bevy_ui_picking_backend",
    "bevy/bevy_ui_render",
    "bevy/bevy_window",
    "bevy/bevy_winit",
    "bevy/default_font",
    "bevy/png",
    "bevy/std",
    "bevy/wayland",
    "bevy/x11",
    "bevy/bevy_dev_tools"
]
# include networking client code
target_net_client = [
    "network",
    "aeronet_websocket/client",
    "aeronet_replicon/client",
]

# particle editor
target_particle_editor = [
    "dep:bevy_egui"
]

# enabled in development
dev = [
    "bevy/debug",
native = [
    "bevy/file_watcher",
    "bevy/hotpatching",
    "bevy/dynamic_linking",
    "bevy/sysinfo_plugin"
    "bevy/x11",
    "bevy/wayland",
    "dep:ctrlc"
]
wasm = ["getrandom/wasm_js", "bevy/webgl2"]

# enable hotpatching
hotpatching = [
    "bevy/hotpatching"
particle_editor = ["dep:bevy_egui"]
server = ["aeronet_websocket/server", "aeronet_replicon/server"]
client = [
    "dep:leafwing-input-manager",
    "dep:good_lp"
]
\ No newline at end of file

M crates/unified/src/build_meta.rs => crates/unified/src/build_meta.rs +5 -5
@@ 12,19 12,19 @@ pub fn version_and_features_line() -> String {
    let commit = format!("{}{}", built_info::GIT_COMMIT_HASH_SHORT.unwrap_or("<unknown>"), if let Some(d) = built_info::GIT_DIRTY && d { "-dirty" } else { "" }); // TODO
    let mut line = format!("starkingdoms v{} {commit}", env!("CARGO_PKG_VERSION"));

    if cfg!(feature = "platform_native") {
    if cfg!(feature = "native") {
        line += " +native";
    }
    if cfg!(feature = "platform_wasm") {
    if cfg!(feature = "wasm") {
        line += " +wasm";
    }
    if cfg!(feature = "target_server") {
    if cfg!(feature = "server") {
        line += " +server";
    }
    if cfg!(feature = "target_client") {
    if cfg!(feature = "client") {
        line += " +client";
    }
    if cfg!(feature = "target_particle_editor") {
    if cfg!(feature = "particle_editor") {
        line += " +particle_editor";
    }


M crates/unified/src/cli.rs => crates/unified/src/cli.rs +19 -26
@@ 1,32 1,27 @@
use crate::build_meta::built_info::{BUILT_TIME_UTC, HOST, RUSTC_VERSION, TARGET};
use crate::build_meta::version_and_features_line;

#[cfg(not(any(feature = "target_client", feature = "target_server", feature = "target_particle_editor")))]
#[cfg(not(any(feature = "client", feature = "server", feature = "particle_editor")))]
compile_error!("You need to enable one or more of client, server, particle_editor features");
#[cfg(not(any(feature = "platform_native", feature = "platform_wasm")))]
compile_error!("You need to enable one of platform_native, platform_wasm features");
#[cfg(all(feature = "platform_native", feature = "platform_wasm"))]
compile_error!("You cannot enable both platform_native and platform_wasm features");

#[cfg(all(feature = "target_server", not(feature = "target_net_server"), not(feature = "target_client")))]
compile_error!("You have disabled server networking but are not including client code. This binary will do nothing");
#[cfg(not(any(feature = "native", feature = "wasm")))]
compile_error!("You need to enable one of native, wasm features");
#[cfg(all(feature = "native", feature = "wasm"))]
compile_error!("You cannot enable both native and wasm features");

pub enum StkArgs {
    #[cfg(feature = "target_client")]
    #[cfg(feature = "client")]
    Client {
        #[cfg(feature = "target_net_client")]
        server: String,
    },
    #[cfg(feature = "target_server")]
    #[cfg(feature = "server")]
    Server {
        #[cfg(feature = "target_net_server")]
        bind_to: std::net::SocketAddr,
        max_clients: std::num::NonZeroUsize,
        tick_rate: f32,
        #[cfg(feature = "target_client")]
        #[cfg(feature = "client")]
        with_client: bool
    },
    #[cfg(feature = "target_particle_editor")]
    #[cfg(feature = "particle_editor")]
    ParticleEditor
}



@@ 53,25 48,23 @@ pub fn parse_args() -> StkArgs {
    

    match subcommand.as_str() {
        #[cfg(feature = "target_client")]
        #[cfg(feature = "client")]
        "client" => {
            StkArgs::Client {
                #[cfg(feature = "target_net_client")]
                server: pargs.value_from_str(["-s", "--server"]).unwrap(),
            }
        },
        #[cfg(feature = "target_server")]
        #[cfg(feature = "server")]
        "server" => {
            StkArgs::Server {
                #[cfg(feature = "target_net_server")]
                bind_to: pargs.value_from_str(["-b", "--bind-to"]).unwrap(),
                max_clients: pargs.value_from_str(["-c", "--max-clients"]).unwrap(),
                tick_rate: pargs.value_from_str(["-r", "--tick-rate"]).unwrap(),
                #[cfg(feature = "target_client")]
                #[cfg(feature = "client")]
                with_client: pargs.contains("--with-client"),
            }
        },
        #[cfg(feature = "target_particle_editor")]
        #[cfg(feature = "particle_editor")]
        "particle_editor" => {
            StkArgs::ParticleEditor
        },


@@ 97,31 90,31 @@ FLAGS:
SUBCOMMANDS:
    ");

    if cfg!(feature = "target_client") {
    if cfg!(feature = "client") {
        println!("    client            Run the client (see CLIENT for options)");
    }
    if cfg!(feature = "target_server") {
    if cfg!(feature = "server") {
        println!("    server            Run the server (see SERVER for options)");
    }
    if cfg!(feature = "target_particle_editor") {
    if cfg!(feature = "particle_editor") {
        println!("particle_editor   Run the particle editor");
    }

    println!("\n");

    if cfg!(feature = "target_client") {
    if cfg!(feature = "client") {
        println!("\
CLIENT:
    -s, --server <URL>      WebSocket URL to connect to
        ");
    }
    if cfg!(feature = "target_server") {
    if cfg!(feature = "server") {
        print!("\
SERVER:
    -b, --bind-to <IP:PORT> Socket address to bind to
    -c, --max-clients <N>   Maximum number of clients to accept
    -r, --tick-rate   <N>   Tick rate\n");
        if cfg!(feature = "target_client") {
        if cfg!(feature = "client") {
            println!("    --with-client           Start a client connected to this server");
        } else {
            println!();

M crates/unified/src/client/key_input.rs => crates/unified/src/client/key_input.rs +5 -0
@@ 1,3 1,4 @@
use bevy::dev_tools::picking_debug::DebugPickingMode;
use crate::prelude::*;
use bevy::{
    app::{App, Update},


@@ 27,9 28,13 @@ impl Deref for PhysicsDebugRes {

fn debug_render_keybind(
    keys: Res<ButtonInput<KeyCode>>,
    mut picking_debug_mode: ResMut<DebugPickingMode>,
    mut attachment_debug: ResMut<AttachmentDebugRes>,
    mut thruster_debug: ResMut<ThrusterDebugRes>,
) {
    if keys.just_pressed(KeyCode::F4) {
        *picking_debug_mode = DebugPickingMode::Noisy;
    }
    if keys.just_pressed(KeyCode::F5) {
        attachment_debug.0 = !attachment_debug.0;
    }

M crates/unified/src/client/mod.rs => crates/unified/src/client/mod.rs +7 -14
@@ 8,8 8,8 @@ use crate::client::zoom::zoom_plugin;
use crate::client::starguide::init::starguide_init_plugin;
use crate::client::starguide::input::starguide_input_plugin;
use crate::ecs::{Hi, StarguideGizmos};
#[cfg(feature = "target_net_client")]
use aeronet_websocket::client::WebSocketClient;
use bevy::dev_tools::picking_debug::DebugPickingMode;
use crate::prelude::*;
use planet::incoming_planets::incoming_planets_plugin;
use crate::client::ship::attachment::client_attachment_plugin;


@@ 17,7 17,6 @@ use crate::ecs::{Me, GameplayState};

pub mod colors;
pub mod key_input;
#[cfg(feature = "target_net_client")]
pub mod net;
pub mod particles;
pub mod parts;


@@ 36,8 35,8 @@ pub struct ClientPlugin {
impl Plugin for ClientPlugin {
    fn build(&self, app: &mut App) {
        let server = self.server.clone();
        #[cfg(feature = "target_net_client")]
        app

            .add_systems(Startup, move |mut commands: Commands| {
                let Some(server) = server.as_ref() else { return };
                let config = net::websocket_config();


@@ 45,19 44,14 @@ impl Plugin for ClientPlugin {
                commands
                    .spawn(Name::new("default-session"))
                    .queue(WebSocketClient::connect(config, server.clone()));
            });

        app
            })
            .init_gizmo_group::<StarguideGizmos>()
            .add_plugins(rendering::render_plugin)
            .add_plugins(input::input_plugin)
            .add_plugins(ship::thrusters::client_thrusters_plugin)
            .add_systems(Update, find_me);

        #[cfg(feature = "target_net_client")]
            app.add_systems(Update, net::set_config);

        app.add_plugins((incoming_planets_plugin, indicators_plugin))
            .add_systems(Update, find_me)
            .add_systems(Update, net::set_config)
            .add_plugins((incoming_planets_plugin, indicators_plugin))
            .add_plugins(parts_plugin)
            .add_plugins(key_input_plugin)
            .add_plugins(starfield_plugin)


@@ 68,10 62,9 @@ impl Plugin for ClientPlugin {
            .add_plugins(starguide_input_plugin)
            .add_plugins(starguide_orbit_plugin)
            .insert_state(GameplayState::Main)
            .insert_resource(bevy::dev_tools::picking_debug::DebugPickingMode::Normal);
            .insert_resource(DebugPickingMode::Disabled);

        // These are only needed if we're actually doing network things
        #[cfg(feature = "target_net_client")]
        if self.server.is_some() {
            app.add_observer(net::on_connecting)
                .add_observer(net::on_connected)

M crates/unified/src/client_plugins.rs => crates/unified/src/client_plugins.rs +5 -11
@@ 1,9 1,8 @@
use crate::client::ClientPlugin;
#[cfg(feature = "target_net_client")]
use aeronet_replicon::client::AeronetRepliconClientPlugin;
#[cfg(feature = "target_net_client")]
use aeronet_websocket::client::WebSocketClientPlugin;
use bevy::app::{PluginGroup, PluginGroupBuilder};
use bevy::dev_tools::picking_debug::DebugPickingPlugin;
use bevy::ecs::schedule::ScheduleLabel;
use crate::prelude::*;
use bevy::ui::UiPlugin;


@@ 14,16 13,11 @@ pub struct ClientPluginGroup {
}
impl PluginGroup for ClientPluginGroup {
    fn build(self) -> PluginGroupBuilder {
        let mut b = PluginGroupBuilder::start::<Self>();

        #[cfg(feature = "target_net_client")] {
            b = b.add(WebSocketClientPlugin);
            b = b.add(AeronetRepliconClientPlugin);
        }

        b
            .add(bevy::dev_tools::picking_debug::DebugPickingPlugin)
        PluginGroupBuilder::start::<Self>()
            .add(WebSocketClientPlugin)
            .add(AeronetRepliconClientPlugin)
            .add(MeshPickingPlugin)
            .add(DebugPickingPlugin)
            .add(ClientPlugin {
                server: self.server,
            })

M crates/unified/src/main.rs => crates/unified/src/main.rs +16 -22
@@ 20,18 20,18 @@
)]

pub mod attachment;
#[cfg(feature = "target_client")]
#[cfg(feature = "client")]
pub mod client;
#[cfg(feature = "target_client")]
#[cfg(feature = "client")]
pub mod client_plugins;
pub mod config;
pub mod ecs;
#[cfg(feature = "target_particle_editor")]
#[cfg(feature = "particle_editor")]
pub mod particle_editor;
pub mod particles;
#[cfg(feature = "target_server")]
#[cfg(feature = "server")]
pub mod server;
#[cfg(feature = "target_server")]
#[cfg(feature = "server")]
pub mod server_plugins;
pub mod shared_plugins;
pub mod world_config;


@@ 51,9 51,9 @@ pub use wasm_entrypoint::*;

use crate::cli::StkArgs;
use crate::prelude::*;
#[cfg(feature = "target_client")]
#[cfg(feature = "client")]
use crate::client_plugins::ClientPluginGroup;
#[cfg(feature = "target_server")]
#[cfg(feature = "server")]
use crate::server_plugins::ServerPluginGroup;




@@ 62,32 62,26 @@ fn run(cli: StkArgs) -> AppExit {
    let mut app = App::new();

    match cli {
        #[cfg(feature = "target_client")]
        StkArgs::Client {
            #[cfg(feature = "target_net_client")]
            server
        } => {
        #[cfg(feature = "client")]
        StkArgs::Client { server } => {
            app.add_plugins(
                DefaultPlugins.build()
                    .disable::<bevy::log::LogPlugin>()
                    .disable::<bevy::ui::UiPlugin>()
            );

            #[cfg(feature = "target_net_client")]
            app.add_plugins(ClientPluginGroup { server: Some(server) });
            app.add_plugins(ClientPluginGroup { server: None });
            app.add_plugins(shared_plugins::SharedPluginGroup);
        }
        #[cfg(feature = "target_server")]
        #[cfg(feature = "server")]
        StkArgs::Server {
            bind_to,
            tick_rate,
            max_clients,
            #[cfg(feature = "target_client")]
            #[cfg(feature = "client")]
            with_client
        } => {
            let mut with_client_all = false;
            #[cfg(feature = "target_client")]
            #[cfg(feature = "client")]
            if with_client {
                with_client_all = true;
                app.add_plugins(


@@ 119,7 113,7 @@ fn run(cli: StkArgs) -> AppExit {
                max_clients: max_clients.into(),
            }.build();

            #[cfg(feature = "target_client")]
            #[cfg(feature = "client")]
            if with_client {
                pg = pg.add_group(ClientPluginGroup {
                    server: None


@@ 128,7 122,7 @@ fn run(cli: StkArgs) -> AppExit {

            app.add_plugins(pg);
        }
        #[cfg(feature = "target_particle_editor")]
        #[cfg(feature = "particle_editor")]
        StkArgs::ParticleEditor {} => {
            app.add_plugins(crate::particle_editor::particle_editor_plugin);
        }


@@ 137,12 131,12 @@ fn run(cli: StkArgs) -> AppExit {
    app.run()
}

#[cfg(feature = "platform_wasm")]
#[cfg(feature = "wasm")]
fn main() {
    // noop on webassembly
}

#[cfg(feature = "platform_native")]
#[cfg(feature = "native")]
fn main() -> AppExit {
    use tracing_subscriber::util::SubscriberInitExt;
    use bevy::log::{tracing_subscriber};

M crates/unified/src/server/player/join.rs => crates/unified/src/server/player/join.rs +2 -0
@@ 71,6 71,7 @@ pub fn handle_new_players(
        });
    }
    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.0).insert(PendingPlayer);
        }


@@ 89,6 90,7 @@ pub fn handle_pending_players(
    asset_server: Res<AssetServer>,
) {
    let Some(wc) = &world_config.config else {
        warn!("there are pending players, but world config is not loaded! waiting until it is...");
        return;
    };


M crates/xtask/src/main.rs => crates/xtask/src/main.rs +9 -8
@@ 1,7 1,7 @@
use std::io::{stdout, Write};
use colored::Colorize;
use qsv_tabwriter::TabWriter;
use crate::unified::{RunClientNative, RunListenServerNative, RunServerNative, RunSingleplayerNative};
use crate::unified::{RunClientNative, RunListenServerNative, RunServerNative};
use crate::unified_web::RunClientWeb;

mod unified;


@@ 34,13 34,14 @@ pub trait Task {
}

fn tasks() -> Vec<Box<dyn Task>> {
    vec![
        Box::new(RunClientNative),
        Box::new(RunServerNative),
        Box::new(RunClientWeb),
        Box::new(RunListenServerNative),
        Box::new(RunSingleplayerNative)
    ]
    let mut v: Vec<Box<dyn Task>> = vec![];

    v.push(Box::new(RunClientNative));
    v.push(Box::new(RunServerNative));
    v.push(Box::new(RunClientWeb));
    v.push(Box::new(RunListenServerNative));

    v
}

fn print_tasks() {

M crates/xtask/src/unified.rs => crates/xtask/src/unified.rs +8 -29
@@ 19,28 19,7 @@ impl Task for RunServerNative {
            args.join(" ")
        };
        set_current_dir(workspace_dir().join("crates/unified/")).unwrap();
        cargo(format!("run -F platform_native -F target_net_server -F target_server -F dev --package starkingdoms -- server {args}"));
    }
}

#[derive(Default)]
pub struct RunClientNative;
impl Task for RunClientNative {
    fn name(&self) -> &'static str {
        "unified:client"
    }
    fn help(&self) -> &'static str {
        "Run the client (native)"
    }

    fn run(&self, args: Vec<String>) {
        let args = if args.is_empty() {
            "-s ws://[::1]:5151".to_string()
        } else {
            args.join(" ")
        };
        set_current_dir(workspace_dir().join("crates/unified/")).unwrap();
        cargo(format!("run -F platform_native -F target_client -F target_net_client -F dev --package starkingdoms -- client {args}"));
        cargo(format!("run -F native -F server --package starkingdoms -- server {args}"));
    }
}



@@ 61,27 40,27 @@ impl Task for RunListenServerNative {
            args.join(" ")
        };
        set_current_dir(workspace_dir().join("crates/unified/")).unwrap();
        cargo(format!("run -F platform_native -F target_net_server -F target_server -F target_client -F dev --package starkingdoms -- server {args}"));
        cargo(format!("run -F native -F server -F client --package starkingdoms -- server {args}"));
    }
}

#[derive(Default)]
pub struct RunSingleplayerNative;
impl Task for RunSingleplayerNative {
pub struct RunClientNative;
impl Task for RunClientNative {
    fn name(&self) -> &'static str {
        "unified:singleplayer"
        "unified:client"
    }
    fn help(&self) -> &'static str {
        "Run a singleplayer game with networking disabled (native)"
        "Run the client (native)"
    }

    fn run(&self, args: Vec<String>) {
        let args = if args.is_empty() {
            "-r 60 -c 10 --with-client".to_string()
            "-s [::]:5151".to_string()
        } else {
            args.join(" ")
        };
        set_current_dir(workspace_dir().join("crates/unified/")).unwrap();
        cargo(format!("run -F target_server -F target_client -F dev --package starkingdoms -- server {args}"));
        cargo(format!("run -F native -F client --package starkingdoms -- client {args}"));
    }
}
\ No newline at end of file