~starkingdoms/starkingdoms

103444e57478b2f702c26a6dfb9a62b34a60e5fe — core 5 months ago b1007ea
chore(wasm)!: fix compilations on webassembly

BREAKING CHANGE: server cli now has two bind options, they must be different
M .cargo/config.toml => .cargo/config.toml +3 -0
@@ 4,3 4,6 @@ xtask = "run --release --package xtask --"
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=lld"]

[target.wasm32-unknown-unknown]
rustflags = ['--cfg', 'getrandom_backend="wasm_js"']
\ No newline at end of file

M Cargo.lock => Cargo.lock +102 -4
@@ 4,9 4,9 @@ version = 4

[[package]]
name = "ab_glyph"
version = "0.2.29"
version = "0.2.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0"
checksum = "1e0f4f6fbdc5ee39f2ede9f5f3ec79477271a6d6a2baff22310d51736bda6cea"
dependencies = [
 "ab_glyph_rasterizer",
 "owned_ttf_parser",


@@ 14,9 14,9 @@ dependencies = [

[[package]]
name = "ab_glyph_rasterizer"
version = "0.1.8"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046"
checksum = "b2187590a23ab1e3df8681afdf0987c48504d80291f002fcdb651f0ef5e25169"

[[package]]
name = "accesskit"


@@ 5113,6 5113,17 @@ dependencies = [
]

[[package]]
name = "io-uring"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013"
dependencies = [
 "bitflags 2.9.1",
 "cfg-if",
 "libc",
]

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


@@ 7255,15 7266,31 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee5d017932669cb57862c8c24ad5294d4fff877ace85487bc771b36ab958a054"
dependencies = [
 "anyhow",
 "async-channel",
 "bevy_ecs 0.16.1",
 "bytes",
 "crossbeam",
 "futures",
 "futures-channel",
 "futures-util",
 "hmac-sha256",
 "http",
 "js-sys",
 "log",
 "octets",
 "renet2",
 "renetcode2",
 "serde",
 "serde_json",
 "tokio",
 "tokio-tungstenite",
 "tungstenite 0.26.2",
 "url",
 "urlencoding",
 "wasm-bindgen",
 "wasm-bindgen-futures",
 "web-sys",
]

[[package]]


@@ 7665,6 7692,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"

[[package]]
name = "signal-hook-registry"
version = "1.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
dependencies = [
 "libc",
]

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


@@ 7795,6 7831,16 @@ dependencies = [
]

[[package]]
name = "socket2"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
dependencies = [
 "libc",
 "windows-sys 0.52.0",
]

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


@@ 7858,10 7904,13 @@ dependencies = [
 "bevy_replicon",
 "bevy_replicon_renet2",
 "clap",
 "getrandom 0.3.3",
 "log",
 "rand 0.9.1",
 "serde",
 "tokio",
 "tracing-subscriber",
 "url",
]

[[package]]


@@ 8347,6 8396,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"

[[package]]
name = "tokio"
version = "1.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1140bb80481756a8cbe10541f37433b459c5aa1e727b4c020fbfebdc25bf3ec4"
dependencies = [
 "backtrace",
 "bytes",
 "io-uring",
 "libc",
 "mio",
 "parking_lot",
 "pin-project-lite",
 "signal-hook-registry",
 "slab",
 "socket2",
 "tokio-macros",
 "windows-sys 0.52.0",
]

[[package]]
name = "tokio-macros"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.104",
]

[[package]]
name = "tokio-tungstenite"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084"
dependencies = [
 "futures-util",
 "log",
 "tokio",
 "tungstenite 0.26.2",
]

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


@@ 8783,6 8875,12 @@ dependencies = [
]

[[package]]
name = "urlencoding"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"

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

M crates/unified/Cargo.toml => crates/unified/Cargo.toml +12 -2
@@ 5,14 5,15 @@ edition = "2024"
version = "0.1.0"

[dependencies]
bevy = { version = "0.16", features = ["serialize", "file_watcher", "tonemapping_luts"] }
bevy = { version = "0.16", features = ["serialize", "tonemapping_luts"] }
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"] }
bevy_replicon_renet2 = { version = "0.10" }

clap = { version = "4", features = ["derive", "cargo"] }
url = "2"

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


@@ 20,3 21,12 @@ log = { version = "*", features = ["max_level_debug", "release_max_level_warn"] 
serde = { version = "1", features = ["derive"] }

rand = "0.9"
getrandom = { version = "0.3", features = [] }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { version = "1", features = ["rt-multi-thread"] }

[features]
default = []
native = ["bevy/file_watcher", "bevy_replicon_renet2/native_transport", "bevy_replicon_renet2/ws_server_transport"]
wasm = ["getrandom/wasm_js", "bevy_replicon_renet2/ws_client_transport"]
\ No newline at end of file

M crates/unified/src/client/mod.rs => crates/unified/src/client/mod.rs +50 -19
@@ 3,7 3,7 @@ mod incoming_parts;
mod key_input;
mod starfield;

use std::net::{SocketAddr, UdpSocket};
use std::net::{IpAddr, SocketAddr, UdpSocket};
use std::time::SystemTime;
use bevy::core_pipeline::bloom::Bloom;
use bevy::core_pipeline::fxaa::Fxaa;


@@ 13,7 13,11 @@ use bevy::window::PrimaryWindow;
use bevy_rapier2d::prelude::RigidBody;
use bevy_replicon::prelude::{ConnectedClient, RepliconChannels};
use bevy_replicon::shared::server_entity_map::ServerEntityMap;
use bevy_replicon_renet2::netcode::{ClientAuthentication, NetcodeClientTransport, NativeSocket};
use bevy_replicon_renet2::netcode::{ClientAuthentication, NetcodeClientTransport};
#[cfg(not(target_arch = "wasm32"))]
use bevy_replicon_renet2::netcode::NativeSocket;
#[cfg(target_arch = "wasm32")]
use bevy_replicon_renet2::netcode::{WebSocketClientConfig, WebSocketClient, ClientSocket};
use bevy_replicon_renet2::renet2::{ConnectionConfig, RenetClient};
use bevy_replicon_renet2::RenetChannelsExt;
use crate::client::incoming_parts::incoming_parts_plugin;


@@ 23,6 27,9 @@ use crate::client::starfield::starfield_plugin;
use crate::ecs::{Ball, CursorWorldCoordinates, Ground, MainCamera, Player, SendBallHere};

pub struct ClientPlugin {
    #[cfg(target_arch = "wasm32")]
    pub server: url::Url,
    #[cfg(not(target_arch = "wasm32"))]
    pub server: SocketAddr
}
impl Plugin for ClientPlugin {


@@ 31,26 38,50 @@ impl Plugin for ClientPlugin {
        app
            .insert_resource(CursorWorldCoordinates(None))
            .add_systems(Startup, move |mut commands: Commands, channels: Res<RepliconChannels>| {
                let client = RenetClient::new(
                    ConnectionConfig::from_channels(channels.server_configs(), channels.client_configs()),
                    false
                );
                let current_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
                let client_id = current_time.as_millis() as u64;
                let socket = UdpSocket::bind(SocketAddr::new("::1".parse().unwrap(), 0)).unwrap();
                let authentication = ClientAuthentication::Unsecure {
                    client_id,
                    protocol_id: 0,
                    socket_id: 0,
                    server_addr: server,
                    user_data: None
                };
                let transport = NetcodeClientTransport::new(current_time, authentication, NativeSocket::new(socket).unwrap()).unwrap();

                commands.insert_resource(client);
                commands.insert_resource(transport);

                info!(?client_id, "connected!");
                #[cfg(target_arch = "wasm32")] {
                    let socket_config = WebSocketClientConfig {
                        server_url: server.clone(),
                    };
                    let socket = WebSocketClient::new(socket_config).unwrap();
                    let client = RenetClient::new(
                        ConnectionConfig::from_channels(channels.server_configs(), channels.client_configs()),
                        socket.is_reliable()
                    );
                    let authentication = ClientAuthentication::Unsecure {
                        socket_id: 1,
                        server_addr: socket.server_address(),
                        client_id: current_time.as_millis() as u64,
                        user_data: None,
                        protocol_id: 0
                    };
                    let mut transport = NetcodeClientTransport::new(current_time, authentication, socket).unwrap();
                    commands.insert_resource(client);
                    commands.insert_resource(transport);
                }
                #[cfg(not(target_arch = "wasm32"))] {
                    let bind = match server.ip() {
                        IpAddr::V4(_) => "127.0.0.1:0",
                        IpAddr::V6(_) => "[::1]:0",
                    };
                    let client_socket = NativeSocket::new(UdpSocket::bind(bind).unwrap()).unwrap();
                    let authentication = ClientAuthentication::Unsecure {
                        socket_id: 0,
                        server_addr: server,
                        client_id: current_time.as_millis() as u64,
                        user_data: None,
                        protocol_id: 0
                    };
                    let client = RenetClient::new(
                        ConnectionConfig::from_channels(channels.server_configs(), channels.client_configs()),
                        false
                    );
                    let mut transport = NetcodeClientTransport::new(current_time, authentication, client_socket).unwrap();
                    commands.insert_resource(client);
                    commands.insert_resource(transport);
                }
            })
            .add_systems(Startup, setup_graphics)
            .add_systems(Update, update_cursor_position)

M crates/unified/src/client_plugins.rs => crates/unified/src/client_plugins.rs +3 -0
@@ 8,6 8,9 @@ use bevy_replicon_renet2::RepliconRenetClientPlugin;
use crate::client::ClientPlugin;

pub struct ClientPluginGroup {
    #[cfg(target_arch = "wasm32")]
    pub server: url::Url,
    #[cfg(not(target_arch = "wasm32"))]
    pub server: SocketAddr
}
impl PluginGroup for ClientPluginGroup {

M crates/unified/src/main.rs => crates/unified/src/main.rs +11 -4
@@ 22,12 22,18 @@ use crate::shared_plugins::SharedPluginGroup;
#[command(version, about)]
enum Cli {
    Client {
        #[cfg(target_arch = "wasm32")]
        #[arg(short, long)]
        server: url::Url,
        #[cfg(not(target_arch = "wasm32"))]
        #[arg(short, long)]
        server: SocketAddr
    },
    Server {
        #[arg(short = 'b', long)]
        bind: SocketAddr,
        #[arg(short = 'w', long)]
        bind_ws: SocketAddr,
        #[arg(short = 'u', long)]
        bind_native: SocketAddr,
        #[arg(short = 'r', long)]
        tick_rate: f64,
        #[arg(short = 'C', long)]


@@ 60,14 66,15 @@ fn main() -> AppExit {
                server
            });
        },
        Cli::Server { bind, tick_rate, max_clients } => {
        Cli::Server { bind_ws, bind_native, tick_rate, max_clients } => {
            if cfg!(target_family = "wasm") {
                eprintln!("the server cannot run on webassembly");
                exit(1);
            }

            app.add_plugins(ServerPluginGroup {
                bind,
                bind_ws,
                bind_native,
                tick_rate,
                max_clients
            });

M crates/unified/src/server/mod.rs => crates/unified/src/server/mod.rs +35 -15
@@ 8,7 8,11 @@ use std::time::{SystemTime, UNIX_EPOCH};
use bevy::prelude::*;
use bevy_rapier2d::prelude::{Collider, Restitution, RigidBody, Velocity};
use bevy_replicon::prelude::{FromClient, Replicated, RepliconChannels};
use bevy_replicon_renet2::netcode::{NativeSocket, NetcodeServerTransport, ServerAuthentication, ServerSetupConfig};
use bevy_replicon_renet2::netcode::{NetcodeServerTransport, ServerAuthentication, ServerSetupConfig, BoxedSocket};

#[cfg(not(target_arch = "wasm32"))]
use bevy_replicon_renet2::netcode::{NativeSocket, WebSocketAcceptor, WebSocketServerConfig, WebSocketServer};

use bevy_replicon_renet2::renet2::{ConnectionConfig, RenetServer};
use bevy_replicon_renet2::RenetChannelsExt;
use crate::ecs::{Ball, Ground, SendBallHere};


@@ 18,37 22,53 @@ use crate::server::player::player_management_plugin;
use crate::server::world_config::world_config_plugin;

pub struct ServerPlugin {
    pub bind: SocketAddr,
    pub bind_ws: SocketAddr,
    pub bind_native: SocketAddr,
    pub max_clients: usize,
}
impl Plugin for ServerPlugin {
    fn build(&self, app: &mut App) {
        let bind = self.bind.clone();
        let bind_ws = self.bind_ws.clone();
        let bind_native = self.bind_native.clone();
        let max_clients = self.max_clients.clone();

        app
            .add_systems(FixedPreUpdate, bevy_replicon::server::increment_tick) // !!important!! do not remove or move
            .add_systems(Startup, move |mut commands: Commands, channels: Res<RepliconChannels>| {

                let server = RenetServer::new(ConnectionConfig::from_channels(
                    channels.server_configs(),
                    channels.client_configs()
                ));

                let server_config = ServerSetupConfig {
                    current_time: SystemTime::now().duration_since(UNIX_EPOCH).unwrap(),
                    max_clients: max_clients,
                    protocol_id: 0,
                    authentication: ServerAuthentication::Unsecure,
                    socket_addresses: vec![vec![bind]]
                };
                let socket = UdpSocket::bind(bind).unwrap();
                #[cfg(not(target_arch = "wasm32"))] {
                    let server_config = ServerSetupConfig {
                        current_time: SystemTime::now().duration_since(UNIX_EPOCH).unwrap(),
                        max_clients: max_clients,
                        protocol_id: 0,
                        authentication: ServerAuthentication::Unsecure,
                        socket_addresses: vec![vec![bind_native], vec![bind_ws]]
                    };

                    let rt = tokio::runtime::Runtime::new().unwrap();

                    let ws_config = WebSocketServerConfig {
                        acceptor: WebSocketAcceptor::Plain { has_tls_proxy: true },
                        listen: bind_ws,
                        max_clients
                    };
                    let ws_server = WebSocketServer::new(ws_config, rt.handle().clone()).unwrap();

                    let native_socket = NativeSocket::new(UdpSocket::bind(bind_native).unwrap()).unwrap();

                    let transport = NetcodeServerTransport::new_with_sockets(server_config, vec![BoxedSocket::new(native_socket), BoxedSocket::new(ws_server)]).unwrap();

                let transport = NetcodeServerTransport::new(server_config, NativeSocket::new(socket).unwrap()).unwrap();
                    commands.insert_resource(server);
                    commands.insert_resource(transport);

                commands.insert_resource(server);
                commands.insert_resource(transport);
                    info!("websocket/native server listening");

                info!("websocket server listening");
                }
            })
            .add_plugins(planets_plugin)
            .add_plugins(world_config_plugin)

M crates/unified/src/server_plugins.rs => crates/unified/src/server_plugins.rs +4 -2
@@ 11,7 11,8 @@ use crate::config::planet::{Planet, PlanetConfigCollection};
use crate::config::world::{GlobalWorldConfig, WorldConfig};

pub struct ServerPluginGroup {
    pub bind: SocketAddr,
    pub bind_ws: SocketAddr,
    pub bind_native: SocketAddr,
    pub tick_rate: f64,
    pub max_clients: usize
}


@@ 35,7 36,8 @@ impl PluginGroup for ServerPluginGroup {
            .add(TomlAssetPlugin::<GlobalWorldConfig>::new(&["wc.toml"]))
            .add(TomlAssetPlugin::<PlanetConfigCollection>::new(&["pc.toml"]))
            .add(crate::server::ServerPlugin {
                bind: self.bind,
                bind_ws: self.bind_ws,
                bind_native: self.bind_native,
                max_clients: self.max_clients,
            })
    }