M .gitignore => .gitignore +4 -0
@@ 8,3 8,7 @@ crates/api/.env
crates/backplane/config.toml
crates/kabel/tmp.kab
+
+crates/unified/web
+crates/unified/web/
+crates/unified/web/*<
\ No newline at end of file
M Cargo.lock => Cargo.lock +4 -0
@@ 7904,13 7904,17 @@ dependencies = [
"bevy_replicon",
"bevy_replicon_renet2",
"clap",
+ "console_error_panic_hook",
"getrandom 0.3.3",
"log",
"rand 0.9.1",
"serde",
"tokio",
"tracing-subscriber",
+ "tracing-wasm",
"url",
+ "wasm-bindgen",
+ "web-time 1.1.0",
]
[[package]]
M crates/unified/Cargo.toml => crates/unified/Cargo.toml +10 -0
@@ 4,6 4,9 @@ description = "A game about floating through space"
edition = "2024"
version = "0.1.0"
+[lib]
+crate-type = ["cdylib", "rlib"]
+
[dependencies]
bevy = { version = "0.16", features = ["serialize", "tonemapping_luts"] }
bevy_rapier2d = { version = "0.30", features = ["serde-serialize", "simd-stable"] }
@@ 23,9 26,16 @@ serde = { version = "1", features = ["derive"] }
rand = "0.9"
getrandom = { version = "0.3", features = [] }
+web-time = "1"
+
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { version = "1", features = ["rt-multi-thread"] }
+[target.'cfg(target_arch = "wasm32")'.dependencies]
+wasm-bindgen = { version = "0.2" }
+tracing-wasm = "0.2"
+console_error_panic_hook = "0.1"
+
[features]
default = ["native"]
native = ["bevy/file_watcher", "bevy_replicon_renet2/native_transport", "bevy_replicon_renet2/ws_server_transport"]
A crates/unified/index.html => crates/unified/index.html +27 -0
@@ 0,0 1,27 @@
+<html>
+ <head>
+ <meta content="text/html; charset=utf8" http-equiv="content-type" />
+ <title>starkingdoms</title>
+ </head>
+ <body>
+ <div id="menu">
+ <label for="url">input server url</label>
+ <input id="url" type="text" />
+ <button id="play">play!</button>
+ </div>
+
+ <script type="module">
+ import init, { play } from './web/starkingdoms.js';
+ async function run() {
+ await init();
+
+ let url = document.getElementById('url').value;
+ document.getElementById('menu').remove();
+
+ play(url);
+ }
+
+ document.getElementById('play').addEventListener('click', run);
+ </script>
+ </body>
+</html><
\ No newline at end of file
M crates/unified/src/client/mod.rs => crates/unified/src/client/mod.rs +6 -4
@@ 20,8 20,9 @@ use bevy_replicon_renet2::netcode::{ClientAuthentication, NetcodeClientTransport
#[cfg(target_arch = "wasm32")]
use bevy_replicon_renet2::netcode::{ClientSocket, WebSocketClient, WebSocketClientConfig};
use bevy_replicon_renet2::renet2::{ConnectionConfig, RenetClient};
+#[cfg(not(target_arch = "wasm32"))]
use std::net::{IpAddr, SocketAddr, UdpSocket};
-use std::time::SystemTime;
+use web_time::SystemTime;
pub struct ClientPlugin {
#[cfg(target_arch = "wasm32")]
@@ 30,8 31,9 @@ pub struct ClientPlugin {
pub server: SocketAddr,
}
impl Plugin for ClientPlugin {
+ #[allow(clippy::clone_on_copy)]
fn build(&self, app: &mut App) {
- let server = self.server;
+ let server = self.server.clone();
app.insert_resource(CursorWorldCoordinates(None))
.add_systems(
Startup,
@@ 39,7 41,6 @@ impl Plugin for ClientPlugin {
let current_time = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap();
- let client_id = current_time.as_millis() as u64;
#[cfg(target_arch = "wasm32")]
{
@@ 61,7 62,7 @@ impl Plugin for ClientPlugin {
user_data: None,
protocol_id: 0,
};
- let mut transport =
+ let transport =
NetcodeClientTransport::new(current_time, authentication, socket)
.unwrap();
commands.insert_resource(client);
@@ 122,6 123,7 @@ fn find_me(
for (entity, player) in q_clients.iter() {
let this_id_clientside = entity_map.to_client().get(&player.client).unwrap();
if *this_id_clientside == entity {
+ info!("hi! i've been found!");
commands.entity(entity).insert(Me);
}
}
M crates/unified/src/client_plugins.rs => crates/unified/src/client_plugins.rs +1 -0
@@ 4,6 4,7 @@ use bevy::app::{PluginGroup, PluginGroupBuilder};
use bevy::log::LogPlugin;
use bevy_replicon::RepliconPlugins;
use bevy_replicon_renet2::RepliconRenetClientPlugin;
+#[cfg(not(target_arch = "wasm32"))]
use std::net::SocketAddr;
pub struct ClientPluginGroup {
A crates/unified/src/lib.rs => crates/unified/src/lib.rs +15 -0
@@ 0,0 1,15 @@
+//! Primary entrypoint for the lib... mostly useful for wasm
+#[cfg(target_arch = "wasm32")]
+pub mod wasm_entrypoint;
+#[cfg(target_arch = "wasm32")]
+pub use wasm_entrypoint::*;
+
+pub mod client;
+pub mod client_plugins;
+pub mod config;
+pub mod ecs;
+#[cfg(not(target_arch = "wasm32"))]
+pub mod server;
+#[cfg(not(target_arch = "wasm32"))]
+pub mod server_plugins;
+pub mod shared_plugins;<
\ No newline at end of file
M crates/unified/src/main.rs => crates/unified/src/main.rs +6 -16
@@ 1,14 1,8 @@
-pub mod client;
-pub mod client_plugins;
-pub mod config;
-pub mod ecs;
-pub mod server;
-pub mod server_plugins;
-pub mod shared_plugins;
-use crate::client_plugins::ClientPluginGroup;
-use crate::server_plugins::ServerPluginGroup;
-use crate::shared_plugins::SharedPluginGroup;
+use starkingdoms::client_plugins::ClientPluginGroup;
+#[cfg(not(target_arch = "wasm32"))]
+use starkingdoms::server_plugins::ServerPluginGroup;
+use starkingdoms::shared_plugins::SharedPluginGroup;
use bevy::log::{Level, tracing_subscriber};
use bevy::prelude::*;
use clap::Parser;
@@ 28,6 22,7 @@ enum Cli {
#[arg(short, long)]
server: SocketAddr,
},
+ #[cfg(not(target_arch = "wasm32"))]
Server {
#[arg(short = 'w', long)]
bind_ws: SocketAddr,
@@ 49,12 44,6 @@ fn main() -> AppExit {
EnvFilter::builder()
.with_default_directive(Level::INFO.into())
.from_env_lossy()
- .add_directive("bevy_app::plugin_group=error".parse().unwrap())
- .add_directive("bevy_app::app=error".parse().unwrap())
- .add_directive("bevy_replicon=warn".parse().unwrap())
- .add_directive("bevy_replicon_renet2=warn".parse().unwrap())
- .add_directive("naga=warn".parse().unwrap())
- .add_directive("wgpu_hal::vulkan=error".parse().unwrap()),
)
.finish()
.init();
@@ 63,6 52,7 @@ fn main() -> AppExit {
Cli::Client { server } => {
app.add_plugins(ClientPluginGroup { server });
}
+ #[cfg(not(target_arch = "wasm32"))]
Cli::Server {
bind_ws,
bind_native,
M crates/unified/src/server/mod.rs => crates/unified/src/server/mod.rs +23 -14
@@ 5,11 5,9 @@ mod world_config;
use bevy::prelude::*;
use bevy_replicon::prelude::RepliconChannels;
-use bevy_replicon_renet2::netcode::{
- BoxedSocket, NetcodeServerTransport, ServerAuthentication, ServerSetupConfig,
-};
+use bevy_replicon_renet2::netcode::{BoxedSocket, NetcodeServerTransport, ServerAuthentication, ServerSetupConfig, ServerSocket};
use std::net::{SocketAddr, UdpSocket};
-use std::time::{SystemTime, UNIX_EPOCH};
+use web_time::{SystemTime, UNIX_EPOCH};
#[cfg(not(target_arch = "wasm32"))]
use bevy_replicon_renet2::netcode::{
@@ 22,6 20,12 @@ use crate::server::player::player_management_plugin;
use crate::server::world_config::world_config_plugin;
use bevy_replicon_renet2::RenetChannelsExt;
use bevy_replicon_renet2::renet2::{ConnectionConfig, RenetServer};
+use tokio::runtime::Runtime;
+
+#[derive(Resource)]
+pub struct RtResource {
+ rt: Runtime
+}
pub struct ServerPlugin {
pub bind_ws: SocketAddr,
@@ 43,15 47,6 @@ impl Plugin for ServerPlugin {
channels.client_configs(),
));
- #[cfg(not(target_arch = "wasm32"))]
- {
- let server_config = ServerSetupConfig {
- current_time: SystemTime::now().duration_since(UNIX_EPOCH).unwrap(),
- max_clients,
- protocol_id: 0,
- authentication: ServerAuthentication::Unsecure,
- socket_addresses: vec![vec![bind_native], vec![bind_ws]],
- };
let rt = tokio::runtime::Runtime::new().unwrap();
@@ 68,6 63,17 @@ impl Plugin for ServerPlugin {
let native_socket =
NativeSocket::new(UdpSocket::bind(bind_native).unwrap()).unwrap();
+ let server_config = ServerSetupConfig {
+ current_time: SystemTime::now().duration_since(UNIX_EPOCH).unwrap(),
+ max_clients,
+ protocol_id: 0,
+ authentication: ServerAuthentication::Unsecure,
+ socket_addresses: vec![
+ vec![native_socket.addr().unwrap()],
+ vec![ws_server.addr().unwrap()],
+ ],
+ };
+
let transport = NetcodeServerTransport::new_with_sockets(
server_config,
vec![BoxedSocket::new(native_socket), BoxedSocket::new(ws_server)],
@@ 76,9 82,12 @@ impl Plugin for ServerPlugin {
commands.insert_resource(server);
commands.insert_resource(transport);
+ commands.insert_resource(RtResource {
+ rt
+ });
info!("websocket/native server listening");
- }
+
},
)
.add_plugins(planets_plugin)
A crates/unified/src/wasm_entrypoint.rs => crates/unified/src/wasm_entrypoint.rs +24 -0
@@ 0,0 1,24 @@
+use bevy::app::App;
+use url::Url;
+use wasm_bindgen::prelude::*;
+use crate::client_plugins::ClientPluginGroup;
+use crate::shared_plugins::SharedPluginGroup;
+
+#[wasm_bindgen]
+pub fn play(server: &str) -> Result<(), JsValue> {
+ console_error_panic_hook::set_once();
+ tracing_wasm::set_as_global_default();
+
+ let u = Url::parse(server).map_err(|e| JsValue::from(e.to_string()))?;
+
+ let mut app = App::new();
+ app.add_plugins(ClientPluginGroup {
+ server: u.clone(),
+ });
+ app.add_plugins(SharedPluginGroup);
+ app.run();
+
+ bevy::prelude::info!("goodbye!");
+
+ Ok(())
+}<
\ No newline at end of file
M crates/xtask/src/main.rs => crates/xtask/src/main.rs +20 -1
@@ 4,7 4,7 @@ use std::env::{args, var};
use std::fs;
use std::io::{Cursor, stdout, Write};
use std::path::{Path, PathBuf};
-use std::process::exit;
+use std::process::{exit, Command};
use std::sync::mpsc;
use std::sync::mpsc::TryRecvError;
use std::thread::sleep;
@@ 241,6 241,25 @@ fn main() {
println!(" {} {}.png {} {}", "-->".dimmed(), filename.to_string_lossy(), "✓ success".green(), format!("{}ms", start.elapsed().as_millis()).dimmed());
}
},
+ "unified:web" => {
+ let start = Instant::now();
+ let target = workspace_dir().join("target/");
+ let unified = workspace_dir().join("crates/unified/");
+ exec("cargo", "build --package starkingdoms --lib --target wasm32-unknown-unknown -F wasm --no-default-features");
+ let wasm_file = target.join("wasm32-unknown-unknown/debug/starkingdoms.wasm");
+ exec("wasm-bindgen", format!("--target web --typescript --out-dir {} {}", unified.join("web/").display(), wasm_file.display()).as_str());
+ println!("{} {} {}", "-->".dimmed(), "✓ done".green(), format!("{}ms", start.elapsed().as_millis()).dimmed());
+ },
_ => panic!("unsupported command"),
}
}
+
+fn exec(program: &str, args: &str) {
+ println!("[{}] {} {}", "+".blue(), program, args);
+ Command::new(program)
+ .args(args.split(" ").collect::<Vec<&str>>())
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+}