diff --git a/app/src/cli/mod.rs b/app/src/cli/mod.rs index aaf2c2f9ea..06d1c325be 100644 --- a/app/src/cli/mod.rs +++ b/app/src/cli/mod.rs @@ -144,6 +144,13 @@ pub struct RunCli { #[clap(short, long)] pub user_id: Option, + /// Allows connecting to servers with a mismatched version. Only available in non-production builds. + /// + /// DO NOT USE THIS UNLESS YOU KNOW WHAT YOU ARE DOING. + #[cfg(not(feature = "production"))] + #[arg(long)] + pub dev_allow_version_mismatch: bool, + /// Specify a trusted certificate authority #[arg(long)] pub ca: Option, diff --git a/app/src/client/mod.rs b/app/src/client/mod.rs index be74f80d5d..78b6021ff3 100644 --- a/app/src/client/mod.rs +++ b/app/src/client/mod.rs @@ -91,9 +91,16 @@ pub fn run( *app.world.resource_mut(window_title()) = "Ambient".to_string(); + #[cfg(feature = "production")] + let fail_on_version_mismatch = true; + + #[cfg(not(feature = "production"))] + let fail_on_version_mismatch = !run.dev_allow_version_mismatch; + MainApp { server_addr, user_id, + fail_on_version_mismatch, show_debug: is_debug, golden_image_cmd: run.golden_image, golden_image_output_dir, @@ -137,6 +144,7 @@ fn MainApp( server_addr: ResolvedAddr, golden_image_output_dir: Option, user_id: String, + fail_on_version_mismatch: bool, show_debug: bool, golden_image_cmd: Option, cert: Option>, @@ -150,6 +158,7 @@ fn MainApp( WindowSized::el([ClientView { server_addr, user_id, + fail_on_version_mismatch, // NOTE: client.game_state is **locked** and accesible through game_state. // // This is to prevent another thread from updating using the client after connection but diff --git a/crates/network/src/native/client.rs b/crates/network/src/native/client.rs index c3ded6ddb2..c36e3970fe 100644 --- a/crates/network/src/native/client.rs +++ b/crates/network/src/native/client.rs @@ -73,6 +73,7 @@ pub struct ClientView { pub server_addr: ResolvedAddr, pub cert: Option>, pub user_id: String, + pub fail_on_version_mismatch: bool, pub systems_and_resources: Cb (SystemGroup, Entity) + Sync + Send>, pub on_loaded: LoadedFunc, pub create_rpc_registry: Cb RpcRegistry + Sync + Send>, @@ -84,6 +85,7 @@ impl ElementComponent for ClientView { let Self { server_addr, user_id, + fail_on_version_mismatch, systems_and_resources, create_rpc_registry, on_loaded, @@ -162,6 +164,7 @@ impl ElementComponent for ClientView { conn.clone(), &assets, user_id, + fail_on_version_mismatch, move |args| { let OnConnectionState { assets, @@ -277,6 +280,7 @@ async fn handle_connection( conn: quinn::Connection, assets: &AssetCache, user_id: String, + fail_on_version_mismatch: bool, mut on_loaded: impl FnMut(OnConnectionState) -> anyhow::Result<(SharedClientGameState, CleanupFunc)> + Send + Sync, @@ -300,7 +304,7 @@ async fn handle_connection( while client.is_pending() { if let Some(frame) = push_recv.next().await { - client.process_push(assets, frame?)?; + client.process_push(assets, fail_on_version_mismatch, frame?)?; } } @@ -343,7 +347,7 @@ async fn handle_connection( while let ClientProtoState::Connected(connected) = &mut client { tokio::select! { Some(frame) = push_recv.next() => { - client.process_push(assets, frame?)?; + client.process_push(assets, fail_on_version_mismatch, frame?)?; } _ = stats_timer.tick() => { let stats = conn.stats(); diff --git a/crates/network/src/proto/client.rs b/crates/network/src/proto/client.rs index 3629cde7ad..70edf7c661 100644 --- a/crates/network/src/proto/client.rs +++ b/crates/network/src/proto/client.rs @@ -51,17 +51,28 @@ impl ClientProtoState { /// Processes an incoming control frame from the server. #[tracing::instrument(level = "debug")] - pub fn process_push(&mut self, assets: &AssetCache, frame: ServerPush) -> anyhow::Result<()> { + pub fn process_push( + &mut self, + assets: &AssetCache, + fail_on_version_mismatch: bool, + frame: ServerPush, + ) -> anyhow::Result<()> { match (frame, &self) { (ServerPush::ServerInfo(server_info), Self::Pending(_user_id)) => { let current_version = ambient_version().to_string(); if server_info.version != current_version { - tracing::error!( - "Client version does not match server version. Server version: {:?}, Client version {:?}", + let msg = format!( + "Client version does not match server version.\n\nServer version: {}\nClient version: {}", server_info.version, current_version ); + + if fail_on_version_mismatch { + anyhow::bail!(msg); + } else { + tracing::error!("{}", msg); + } } tracing::debug!(content_base_url=?server_info.content_base_url, "Inserting content base url"); diff --git a/crates/network/src/web/client.rs b/crates/network/src/web/client.rs index e9c39fc96b..24c871ee35 100644 --- a/crates/network/src/web/client.rs +++ b/crates/network/src/web/client.rs @@ -289,7 +289,7 @@ async fn handle_connection( while client.is_pending() { tracing::info!("Waiting for server to accept connection and send server info"); if let Some(frame) = push_recv.next().await { - client.process_push(&assets, frame?)?; + client.process_push(&assets, true, frame?)?; } } @@ -321,7 +321,7 @@ async fn handle_connection( while let ClientProtoState::Connected(connected) = &mut client { tokio::select! { Some(frame) = push_recv.next() => { - client.process_push(&assets, frame?)?; + client.process_push(&assets, true, frame?)?; } Some(message) = proxy_rx.next() => {