Compare commits

..

No commits in common. 'f5382ffaf0886ed60e2513141cef58c3a849b4c3' and 'b0e2763521e0c0d968ed7a9bc96e759a73eaebbe' have entirely different histories.

  1. 2592
      Cargo.lock
  2. 5
      Cargo.toml
  3. 4
      client/Cargo.toml
  4. 122
      client/src/main.rs
  5. 15
      common/src/lib.rs
  6. 4
      server/Cargo.toml
  7. 90
      server/src/main.rs

2592
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -8,7 +8,4 @@ members = [
[workspace.dependencies] [workspace.dependencies]
# TODO: Disable default features and enable what's needed in specific crates. # TODO: Disable default features and enable what's needed in specific crates.
bevy = "0.15.*" bevy = "0.14.*"
aeronet = "0.11.*"
aeronet_webtransport = "0.11.*"

@ -5,8 +5,4 @@ edition = "2021"
[dependencies] [dependencies]
common = { package = "gaemstone-common", path = "../common" } common = { package = "gaemstone-common", path = "../common" }
bevy = { workspace = true } bevy = { workspace = true }
aeronet = { workspace = true }
aeronet_webtransport = { workspace = true, features = ["client"] }

@ -1,23 +1,8 @@
use { use bevy::{prelude::*, window::WindowResolution};
aeronet::{
io::{
connection::{DisconnectReason, Disconnected},
Session, SessionEndpoint,
},
transport::TransportConfig,
},
aeronet_webtransport::{
cert,
cert::CertificateHash,
client::{ClientConfig, WebTransportClient, WebTransportClientPlugin},
},
bevy::{prelude::*, window::WindowResolution},
};
fn main() -> AppExit { fn main() -> AppExit {
App::new() App::new()
.add_plugins(( .add_plugins(DefaultPlugins.set(WindowPlugin {
DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window { primary_window: Some(Window {
title: "gæmstone Client".into(), title: "gæmstone Client".into(),
// Steam Deck: DPI appears pretty high, causing everything to be scaled up. // Steam Deck: DPI appears pretty high, causing everything to be scaled up.
@ -30,104 +15,15 @@ fn main() -> AppExit {
..default() ..default()
}), }),
..default() ..default()
}), }))
WebTransportClientPlugin, .add_systems(Startup, setup)
))
.add_systems(Startup, (setup, connect_to_server))
.add_observer(on_connecting)
.add_observer(on_connected)
.add_observer(on_disconnected)
.run() .run()
} }
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2d::default()); commands.spawn(Camera2dBundle::default());
commands.spawn(Sprite::from_image(asset_server.load("heck.png"))); commands.spawn(SpriteBundle {
} texture: asset_server.load("heck.png"),
..default()
fn connect_to_server(mut commands: Commands) { });
// When using a self-signed cert, its hash needs to be provided to the
// client for it to accept it. Works on native and WASM. On WASM we have
// the option to just accept the HTTPS server's certificate. On native we
// could theoretically disable cert checking entirely, but that kind of
// defeats the point.
// TODO: Do not hardcode the server's certificate hash. Enter in a UI of some sort?
// For now, just manually replace the `todo!` with the hash that gets
// logged in the server output. It looks something like this:
// ************************
// SPKI FINGERPRINT
// 1YLqE3c3ZsRBos35nUrMETfZtCUVIxyIjcskEq0LFYE=
// CERTIFICATE HASH
// z3cWU+Pc209kffV440ksqcWxMcCTi9QO6qI7VjVOQfU=
// ************************
let target = format!("https://[::1]:{}", common::WEB_TRANSPORT_PORT);
let cert_hash = todo!();
let cert_hash = cert::hash_from_b64(cert_hash).unwrap();
let config = web_transport_config(Some(cert_hash));
commands
.spawn(Name::new(target.clone()))
.queue(WebTransportClient::connect(config, target));
}
#[cfg(target_family = "wasm")]
fn web_transport_config(cert_hash: Option<CertificateHash>) -> ClientConfig {
use aeronet_webtransport::xwt_web_sys::{CertificateHash, HashAlgorithm};
let server_certificate_hashes = cert_hash
.map(|hash| CertificateHash {
algorithm: HashAlgorithm::Sha256,
value: Vec::from(hash),
})
.into_iter()
.collect::<Vec<_>>();
ClientConfig {
server_certificate_hashes,
..Default::default()
}
}
#[cfg(not(target_family = "wasm"))]
fn web_transport_config(cert_hash: Option<CertificateHash>) -> ClientConfig {
use {aeronet_webtransport::wtransport::tls::Sha256Digest, core::time::Duration};
let server_certificate_hashes = cert_hash
.map(Sha256Digest::new)
.into_iter()
.collect::<Vec<_>>();
ClientConfig::builder()
.with_bind_default()
.with_server_certificate_hashes(server_certificate_hashes)
.keep_alive_interval(Some(Duration::from_secs(1)))
.max_idle_timeout(Some(Duration::from_secs(5)))
.unwrap()
.build()
}
fn on_connecting(trigger: Trigger<OnAdd, SessionEndpoint>, names: Query<&Name>) {
let session = trigger.entity();
let name = names.get(session).unwrap();
info!("Connecting to '{name}' ...");
}
fn on_connected(trigger: Trigger<OnAdd, Session>, mut commands: Commands) {
let session = trigger.entity();
info!("Connected!");
commands.entity(session).insert((TransportConfig {
max_memory_usage: 64 * 1024,
send_bytes_per_sec: 4 * 1024,
},));
}
fn on_disconnected(trigger: Trigger<Disconnected>) {
match &trigger.event().reason {
DisconnectReason::User(reason) => {
info!("Disconnected (user): {reason}")
}
DisconnectReason::Peer(reason) => {
info!("Disconnected (server): {reason}")
}
DisconnectReason::Error(err) => {
info!("Disconnected (error): {err:#}")
}
};
} }

@ -1 +1,14 @@
pub const WEB_TRANSPORT_PORT: u16 = 25565; pub fn add(left: u64, right: u64) -> u64 {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}

@ -5,8 +5,4 @@ edition = "2021"
[dependencies] [dependencies]
common = { package = "gaemstone-common", path = "../common" } common = { package = "gaemstone-common", path = "../common" }
bevy = { workspace = true } bevy = { workspace = true }
aeronet = { workspace = true }
aeronet_webtransport = { workspace = true, features = ["server"] }

@ -1,96 +1,12 @@
use { use bevy::{log::LogPlugin, prelude::*};
aeronet::io::{
connection::{DisconnectReason, Disconnected, LocalAddr},
server::Server,
Session,
},
aeronet_webtransport::{
cert,
server::{SessionRequest, SessionResponse, WebTransportServer, WebTransportServerPlugin},
wtransport::{Identity, ServerConfig},
},
bevy::{log::LogPlugin, prelude::*},
std::time::Duration,
};
fn main() -> AppExit { fn main() -> AppExit {
App::new() App::new()
.add_plugins(( .add_plugins((LogPlugin::default(), MinimalPlugins))
LogPlugin::default(), .add_systems(Startup, setup)
MinimalPlugins,
WebTransportServerPlugin,
))
.add_systems(Startup, (setup, open_web_transport_server))
.add_observer(on_server_opened)
.add_observer(on_client_session_request)
.add_observer(on_client_connected)
.add_observer(on_client_disconnected)
.run() .run()
} }
fn setup() { fn setup() {
info!("Hello from gæmstone server!"); info!("Hello from gæmstone server!");
} }
fn open_web_transport_server(mut commands: Commands) {
let identity = Identity::self_signed(["localhost", "127.0.0.1", "::1"]).unwrap();
let cert = &identity.certificate_chain().as_slice()[0];
let spki_fingerprint = cert::spki_fingerprint_b64(cert).unwrap();
let cert_hash = cert::hash_to_b64(cert.hash());
info!("************************");
info!("SPKI FINGERPRINT");
info!(" {spki_fingerprint}");
info!("CERTIFICATE HASH");
info!(" {cert_hash}");
info!("************************");
let config = ServerConfig::builder()
.with_bind_default(common::WEB_TRANSPORT_PORT)
.with_identity(&identity)
.keep_alive_interval(Some(Duration::from_secs(1)))
.max_idle_timeout(Some(Duration::from_secs(5)))
.unwrap()
.build();
commands
.spawn_empty()
.queue(WebTransportServer::open(config));
info!("Starting WebTransport server ...");
}
fn on_server_opened(trigger: Trigger<OnAdd, Server>, servers: Query<&LocalAddr>) {
let server = trigger.entity();
let local_addr = servers.get(server).unwrap();
info!("Server listening on {}", **local_addr);
}
fn on_client_session_request(mut trigger: Trigger<SessionRequest>) {
let client = trigger.entity();
let request = trigger.event_mut();
request.respond(SessionResponse::Accepted);
info!("{client} connecting with headers:");
for (header_key, header_value) in &request.headers {
info!(" {header_key}: {header_value}");
}
}
fn on_client_connected(trigger: Trigger<OnAdd, Session>) {
let client = trigger.entity();
info!("{client} connected");
}
fn on_client_disconnected(trigger: Trigger<Disconnected>) {
let client = trigger.entity();
match &trigger.event().reason {
DisconnectReason::User(reason) => {
info!("{client} disconnected (server): {reason}");
}
DisconnectReason::Peer(reason) => {
info!("{client} disconnected (client): {reason}");
}
DisconnectReason::Error(err) => {
warn!("{client} disconnected (error): {err:#}");
}
}
}

Loading…
Cancel
Save