Compare commits
2 Commits
b0e2763521
...
f5382ffaf0
Author | SHA1 | Date |
---|---|---|
copygirl | f5382ffaf0 | 3 weeks ago |
copygirl | 3a59fd8263 | 3 weeks ago |
7 changed files with 2507 additions and 519 deletions
File diff suppressed because it is too large
Load Diff
@ -1,29 +1,133 @@ |
||||
use bevy::{prelude::*, window::WindowResolution}; |
||||
use { |
||||
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 { |
||||
App::new() |
||||
.add_plugins(DefaultPlugins.set(WindowPlugin { |
||||
primary_window: Some(Window { |
||||
title: "gæmstone Client".into(), |
||||
// Steam Deck: DPI appears pretty high, causing everything to be scaled up.
|
||||
// Setting scale factor override prevents this from happening.
|
||||
resolution: WindowResolution::new(1280., 720.).with_scale_factor_override(1.0), |
||||
// WASM: Fit canvas to parent element, so `Window` resizes automatically.
|
||||
fit_canvas_to_parent: true, |
||||
// WASM: Don't override default event handling like browser hotkeys while focused.
|
||||
prevent_default_event_handling: false, |
||||
.add_plugins(( |
||||
DefaultPlugins.set(WindowPlugin { |
||||
primary_window: Some(Window { |
||||
title: "gæmstone Client".into(), |
||||
// Steam Deck: DPI appears pretty high, causing everything to be scaled up.
|
||||
// Setting scale factor override prevents this from happening.
|
||||
resolution: WindowResolution::new(1280., 720.).with_scale_factor_override(1.0), |
||||
// WASM: Fit canvas to parent element, so `Window` resizes automatically.
|
||||
fit_canvas_to_parent: true, |
||||
// WASM: Don't override default event handling like browser hotkeys while focused.
|
||||
prevent_default_event_handling: false, |
||||
..default() |
||||
}), |
||||
..default() |
||||
}), |
||||
..default() |
||||
})) |
||||
.add_systems(Startup, setup) |
||||
WebTransportClientPlugin, |
||||
)) |
||||
.add_systems(Startup, (setup, connect_to_server)) |
||||
.add_observer(on_connecting) |
||||
.add_observer(on_connected) |
||||
.add_observer(on_disconnected) |
||||
.run() |
||||
} |
||||
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { |
||||
commands.spawn(Camera2dBundle::default()); |
||||
commands.spawn(SpriteBundle { |
||||
texture: asset_server.load("heck.png"), |
||||
..default() |
||||
}); |
||||
commands.spawn(Camera2d::default()); |
||||
commands.spawn(Sprite::from_image(asset_server.load("heck.png"))); |
||||
} |
||||
|
||||
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,14 +1 @@ |
||||
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); |
||||
} |
||||
} |
||||
pub const WEB_TRANSPORT_PORT: u16 = 25565; |
||||
|
@ -1,12 +1,96 @@ |
||||
use bevy::{log::LogPlugin, prelude::*}; |
||||
use { |
||||
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 { |
||||
App::new() |
||||
.add_plugins((LogPlugin::default(), MinimalPlugins)) |
||||
.add_systems(Startup, setup) |
||||
.add_plugins(( |
||||
LogPlugin::default(), |
||||
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() |
||||
} |
||||
|
||||
fn setup() { |
||||
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…
Reference in new issue