diff --git a/.gitignore b/.gitignore index ea8c4bf..33db69f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /target + +steam_appid.txt diff --git a/Cargo.lock b/Cargo.lock index 77f4048..09de94c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -160,6 +160,7 @@ version = "0.1.0" dependencies = [ "env_logger", "hex", + "jsonwebtoken", "libfj", "log", "rocket", @@ -188,6 +189,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" @@ -1033,6 +1040,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonwebtoken" +version = "9.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" +dependencies = [ + "base64 0.21.7", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1050,7 +1072,7 @@ name = "libfj" version = "0.7.5" dependencies = [ "async-trait", - "base64", + "base64 0.22.1", "cgmath 0.18.0", "chrono", "genmesh", @@ -1195,12 +1217,31 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits 0.2.19", +] + [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits 0.2.19", +] + [[package]] name = "num-traits" version = "0.1.43" @@ -1323,6 +1364,16 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64 0.22.1", + "serde", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1507,6 +1558,16 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rc_static_data" +version = "0.1.0" +dependencies = [ + "env_logger", + "libfj", + "log", + "rocket", +] + [[package]] name = "rdrand" version = "0.4.0" @@ -1595,7 +1656,7 @@ version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-core", "futures-util", @@ -1903,6 +1964,18 @@ dependencies = [ "libc", ] +[[package]] +name = "simple_asn1" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +dependencies = [ + "num-bigint", + "num-traits 0.2.19", + "thiserror 2.0.11", + "time", +] + [[package]] name = "slab" version = "0.4.9" @@ -2388,7 +2461,7 @@ version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" dependencies = [ - "base64", + "base64 0.22.1", "flate2", "log", "once_cell", diff --git a/Cargo.toml b/Cargo.toml index 3b3a2a1..4133d12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [workspace] members = [ - "auth", + "auth", "rc_static_data", ] [workspace.dependencies] diff --git a/README.md b/README.md index 60bc60a..2829c86 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,20 @@ A collection of open source servers for FreeJam games ### CardLife -To use get CardLife to use these servers, replace the ServerConfig.json file in the game files with [this ServerConfig.json](assets/cardlife/ServerConfig.json). +To get CardLife to use these servers, replace the ServerConfig.json file in the game files with [this ServerConfig.json](assets/cardlife/ServerConfig.json). ### Robocraft -??? (This might require modifying `/etc/hosts`... remember to mention the Windows equivalent) +To get Robocraft to use these servers, please add the following to your OS's `hosts` file: + +``` +127.0.0.1 robocraftstaticdata.s3.amazonaws.com +127.0.0.1 services-1.servers.robocraftgame.com +``` + +The `hosts` file can be found at `/etc/hosts` on Linux and `C:\Windows\system32\drivers\etc\hosts` on Windows. Usually this requires elevated permissions (root/admin) to edit. + +// TODO Don't expect people to run these servers on their own computer ## Privacy diff --git a/assets/robocraft/auth_notes.md b/assets/robocraft/auth_notes.md new file mode 100644 index 0000000..99dda6d --- /dev/null +++ b/assets/robocraft/auth_notes.md @@ -0,0 +1,51 @@ +# Login + +Seems to use `Login.RoboAuthService`. + +These are mostly assumptions and untested. + +## Standard login + +Username/password login calls `AuthWithIdEnumerator(string identifier, string password, Dictionary dataToReturn)`, which instantiates the class `#=zTk$8WYeWP0gxjV8edvqGncg=` (nested class in `RoboAuthService`). It sets the following variables: + +``` +#=zTk$8WYeWP0gxjV8edvqGncg=.#=zOuYEgQM= = identifier; +#=zTk$8WYeWP0gxjV8edvqGncg=.#=zBLK$yEM= = password; +#=zTk$8WYeWP0gxjV8edvqGncg=.#=zmTDf_G1vkOnQ = dataToReturn; +``` + +`identifier` is presumably user display name. No idea what `dataToReturn` contains. + +## Steam Login + +The first time, steam login calls `RegisterAndAuthenticateSteam(string validDisplayName, string ticket, Action> onAuthSuccess, Action onError)`, which calls `#=zeCm7CtCIMhaXGczndJSgBvo=(...)`, which instantiates `#=zudtivLXmdm5aL7OqfX$JZA2ozi4nV_Rakg==` (nested class in `RoboAuthService`) and schedules it in the TaskRunner (it's an enumerator). + +``` +// in RegisterAndAuthenticateSteam +RoboAuthService.#=zeCm7CtCIMhaXGczndJSgBvo=(validDisplayName, ticket, onAuthSuccess, onError); + +// which calls +[DebuggerHidden] +private static IEnumerator #=zeCm7CtCIMhaXGczndJSgBvo=(string #=zgoyh9IIuSHQ$, string #=zI$MKIcRAkgM$, Action> #=zmoN68pYsK05$, Action #=zN4ekGCs=) +{ + #=zudtivLXmdm5aL7OqfX$JZA2ozi4nV_Rakg== #=zudtivLXmdm5aL7OqfX$JZA2ozi4nV_Rakg== = new #=zudtivLXmdm5aL7OqfX$JZA2ozi4nV_Rakg==(); + #=zudtivLXmdm5aL7OqfX$JZA2ozi4nV_Rakg==.#=zI$MKIcRAkgM$ = #=zI$MKIcRAkgM$; + #=zudtivLXmdm5aL7OqfX$JZA2ozi4nV_Rakg==.#=zgoyh9IIuSHQ$ = #=zgoyh9IIuSHQ$; + #=zudtivLXmdm5aL7OqfX$JZA2ozi4nV_Rakg==.#=zmoN68pYsK05$ = #=zmoN68pYsK05$; + #=zudtivLXmdm5aL7OqfX$JZA2ozi4nV_Rakg==.#=zN4ekGCs= = #=zN4ekGCs=; + return #=zudtivLXmdm5aL7OqfX$JZA2ozi4nV_Rakg==; +} +``` + +If it's not the first time, steam login calls `AuthenticateSteamUser(Action> onSuccess, Action onFailure, Action onError)`, which calls `AuthenticateSteamUserInternal(...)` and then schedules the task (IEnumerator is returned). + +``` +[DebuggerHidden] +public static IEnumerator AuthenticateSteamUserInternal(Action> onSuccess, Action onFailure, Action onError) +{ + #=zxQRony8oFWvBWu4O0RraT0fsXcjv #=zxQRony8oFWvBWu4O0RraT0fsXcjv = new #=zxQRony8oFWvBWu4O0RraT0fsXcjv(); + #=zxQRony8oFWvBWu4O0RraT0fsXcjv.#=zmoN68pYsK05$ = onSuccess; + #=zxQRony8oFWvBWu4O0RraT0fsXcjv.#=zN4ekGCs= = onError; + return #=zxQRony8oFWvBWu4O0RraT0fsXcjv; +} +``` diff --git a/auth/Cargo.toml b/auth/Cargo.toml index be24ea7..f534414 100644 --- a/auth/Cargo.toml +++ b/auth/Cargo.toml @@ -5,7 +5,9 @@ edition = "2021" [features] steam = ["steamworks"] -default = [] +robocraft = [] +cardlife = [] +default = ["robocraft", "cardlife"] [dependencies] rocket.workspace = true @@ -15,3 +17,4 @@ env_logger.workspace = true uuid = { version = "1.12", features = [ "v4", "fast-rng" ] } steamworks = { version = "0.11", optional = true } hex = "0.4" +jsonwebtoken = "9" diff --git a/auth/src/cardlife/mod.rs b/auth/src/cardlife/mod.rs index 4c21cfe..f3616af 100644 --- a/auth/src/cardlife/mod.rs +++ b/auth/src/cardlife/mod.rs @@ -6,7 +6,7 @@ mod temporary_get_user_id; mod token; pub fn stage() -> rocket::fairing::AdHoc { - rocket::fairing::AdHoc::on_ignite("JSON", |rocket| async { + rocket::fairing::AdHoc::on_ignite("cardlife", |rocket| async { rocket.attach(email::stage()) .attach(steam::stage()) .attach(temporary_get_user_id::stage()) diff --git a/auth/src/cardlife/steam.rs b/auth/src/cardlife/steam.rs index cbec862..da928a8 100644 --- a/auth/src/cardlife/steam.rs +++ b/auth/src/cardlife/steam.rs @@ -1,86 +1,34 @@ use rocket::{post, routes, serde::json::Json, http::Status}; -#[cfg(feature = "steam")] -static STEAM_SERVER: std::sync::OnceLock = OnceLock::new(); - -#[cfg(feature = "steam")] -fn init_steam() -> steamworks::Server { - let (server, single_client) = steamworks::Server::init(core::net::Ipv4Addr::new(127, 0, 0, 1), 9000, 9001, steamworks::ServerMode::NoAuthentication, "") - .expect("Steam is unavailable"); - std::thread::spawn(move || { - loop { - single_client.run_callbacks(); - std::thread::sleep(std::time::Duration::from_millis(10)); - } - }); - server.set_product("920690"); - server.log_on_anonymous(); - server -} - -fn get_u64_with_offset(arr: &[u8], start: usize) -> u64 { - let arr_actual: [u8; 8] = [ - arr[start], - arr[start+1], - arr[start+2], - arr[start+3], - arr[start+4], - arr[start+5], - arr[start+6], - arr[start+7], - ]; - u64::from_le_bytes(arr_actual) -} - #[post("/api/auth/steamauthenticate", data = "")] pub fn steam_auth(body: Json) -> Result, Status> { log::debug!("steam ticket: {}", body.steam_ticket); - match hex::decode(&body.steam_ticket) { - Ok(ticket) => { - if ticket.len() < 72 { - return Err(Status { code: 400 }) - } - let steam_id = get_u64_with_offset(&ticket, 12 /* also at 64 ??? */); // should be 76600000000000000 > number > 76500000000000000 - log::debug!("Found steamId {}", steam_id); - #[cfg(feature = "steam")] - { - let steam = STEAM_SERVER.get_or_init(init_steam); - if let Err(e) = steam.begin_authentication_session(steamworks::SteamId::from_raw(steam_id), &ticket) { - log::error!("steam server auth session error: {}", e); - return Err(Status { code: 400 }) - } - } - Ok(Json(libfj::cardlife::AuthenticationInfo { - public_id: uuid::Uuid::from_u64_pair(steam_id, steam_id).to_string(), - email_address: "nobody@openjamgames.com".to_string(), - display_name: steam_id.to_string(), - purchases: vec![1, 2, 3], - flags: vec![], - /*flags: //vec![ - "Dev".to_string(), - "GiveInv".to_string(), - "NoDrop".to_string(), - "DekStruct".to_string(), - "BucketA".to_string(), - "BucketB".to_string(), - "userlogged".to_string(), - ],*/ - confirmed: true, - token: uuid::Uuid::from_u64_pair(steam_id, steam_id).to_string(), - steam_id: Some(steam_id.to_string()), - id: (steam_id & (i32::MAX as u64)) as i32, - })) - }, - Err(e) => { - log::error!("request with bad steam ticket: {}", e); - Err(Status { code: 400 }) - } - } + let steam_id = crate::common::steam_utils::authenticate_steam_ticket(&body.steam_ticket) + .map_err(|_| Status { code: 400 })?; + log::debug!("Found steamId {}", steam_id); + Ok(Json(libfj::cardlife::AuthenticationInfo { + public_id: uuid::Uuid::from_u64_pair(steam_id, steam_id).to_string(), + email_address: "nobody@openjamgames.com".to_string(), + display_name: steam_id.to_string(), + purchases: vec![1, 2, 3], + flags: vec![], + /*flags: //vec![ + "Dev".to_string(), + "GiveInv".to_string(), + "NoDrop".to_string(), + "DelStruct".to_string(), + "BucketA".to_string(), + "BucketB".to_string(), + "userlogged".to_string(), + ],*/ + confirmed: true, + token: uuid::Uuid::from_u64_pair(steam_id, steam_id).to_string(), + steam_id: Some(steam_id.to_string()), + id: (steam_id & (i32::MAX as u64)) as i32, + })) } pub fn stage() -> rocket::fairing::AdHoc { - #[cfg(feature = "steam")] - STEAM_SERVER.get_or_init(init_steam); rocket::fairing::AdHoc::on_ignite("CardLife Steam", |rocket| async { rocket.mount("/", routes![steam_auth]) }) diff --git a/auth/src/common/mod.rs b/auth/src/common/mod.rs new file mode 100644 index 0000000..bda8f79 --- /dev/null +++ b/auth/src/common/mod.rs @@ -0,0 +1 @@ +pub(crate) mod steam_utils; diff --git a/auth/src/common/steam_utils.rs b/auth/src/common/steam_utils.rs new file mode 100644 index 0000000..f1ff031 --- /dev/null +++ b/auth/src/common/steam_utils.rs @@ -0,0 +1,90 @@ +fn get_u64_with_offset(arr: &[u8], start: usize) -> u64 { + let arr_actual: [u8; 8] = [ + arr[start], + arr[start+1], + arr[start+2], + arr[start+3], + arr[start+4], + arr[start+5], + arr[start+6], + arr[start+7], + ]; + u64::from_le_bytes(arr_actual) +} + +#[allow(dead_code)] +fn get_steam_id_from_ticket_hex(hex_ticket: &str) -> Result { + let decoded_ticket = hex::decode(hex_ticket)?; + if decoded_ticket.len() < 72 { + Err(hex::FromHexError::InvalidStringLength) + } else { + Ok(get_steam_id_from_ticket(&decoded_ticket)) + } +} + +#[allow(dead_code)] +fn get_steam_id_from_ticket(ticket: &[u8]) -> u64 { + get_u64_with_offset(&ticket, 12 /* also at 64 ??? */) // should be 76600000000000000 > number > 76500000000000000 +} + +#[cfg(all(feature = "steam", feature = "cardlife"))] +const STEAM_ID: &str = "920690"; // Cardlife steam app id + +#[cfg(all(feature = "steam", feature = "robocraft"))] +const STEAM_ID: &str = "301520"; // Robocraft steam app id + +#[cfg(feature = "steam")] +static STEAM_SERVER: std::sync::OnceLock = std::sync::OnceLock::new(); + +#[cfg(feature = "steam")] +fn init_steam() -> steamworks::Server { + if let Err(e) = std::fs::write::<&str, &[u8]>("./steam_appid.txt", STEAM_ID.as_ref()) { + log::error!("Failed to write appId to steam_appid.txt: {}", e); + } + let (server, single_client) = steamworks::Server::init(core::net::Ipv4Addr::new(127, 0, 0, 1), 9000, 9001, steamworks::ServerMode::NoAuthentication, "") + .expect("Steam is unavailable"); + std::thread::spawn(move || { + loop { + single_client.run_callbacks(); + std::thread::sleep(std::time::Duration::from_millis(10)); + } + }); + server.set_product(STEAM_ID); + server.log_on_anonymous(); + server +} + +#[cfg(feature = "steam")] +fn get_steam() -> &'static steamworks::Server { + STEAM_SERVER.get_or_init(init_steam) +} + +#[cfg(not(feature = "steam"))] +pub fn authenticate_steam_ticket(hex_ticket: &str) -> Result { + get_steam_id_from_ticket_hex(hex_ticket) + .map_err(|e| { + log::error!("Failed to parse steamId: {}", e); + () + }) +} + +#[cfg(feature = "steam")] +pub fn authenticate_steam_ticket(hex_ticket: &str) -> Result { + let decoded_ticket = hex::decode(hex_ticket) + .map_err(|e| { + log::error!("Failed to decode hexadecimal steam ticket: {}", e); + () + })?; + if decoded_ticket.len() < 72 { + log::error!("Failed to parse steamId: ticket too short"); + return Err(()) + } + let steam_id = get_steam_id_from_ticket(&decoded_ticket); + let steam = get_steam(); + steam.begin_authentication_session(steamworks::SteamId::from_raw(steam_id), &decoded_ticket) + .map_err(|e| { + log::error!("steam server auth session error: {}", e); + () + })?; + Ok(steam_id) +} diff --git a/auth/src/main.rs b/auth/src/main.rs index efd5dd6..61519fe 100644 --- a/auth/src/main.rs +++ b/auth/src/main.rs @@ -1,4 +1,9 @@ +mod common; + +#[cfg(feature = "cardlife")] mod cardlife; +#[cfg(feature = "robocraft")] +mod robocraft; #[rocket::get("/")] fn index() -> &'static str { @@ -8,7 +13,17 @@ fn index() -> &'static str { #[rocket::launch] fn rocket() -> _ { env_logger::init(); - rocket::build().mount("/", rocket::routes![index]) - .attach(cardlife::stage()) + #[allow(unused_mut)] + let mut builder = rocket::build().mount("/", rocket::routes![index]); + + #[cfg(feature = "cardlife")] + {builder = builder.attach(cardlife::stage());} + #[cfg(feature = "robocraft")] + {builder = builder.attach(robocraft::stage());} + + builder } +#[cfg(all(feature = "steam", feature = "robocraft", feature = "cardlife"))] +compile_error!("Feature \"steam\" cannot work with features \"cardlife\" and \"robocraft\" at the same time"); + diff --git a/auth/src/robocraft/debug.rs b/auth/src/robocraft/debug.rs new file mode 100644 index 0000000..f553093 --- /dev/null +++ b/auth/src/robocraft/debug.rs @@ -0,0 +1,13 @@ +use rocket::{post, routes}; + +#[post("/", data = "")] +pub fn debug_endpoint(body: String) -> String { + log::info!("got body: `{}`", body); + body.to_string() +} + +pub fn stage() -> rocket::fairing::AdHoc { + rocket::fairing::AdHoc::on_ignite("Robocraft debug", |rocket| async { + rocket.mount("/", routes![debug_endpoint]) + }) +} diff --git a/auth/src/robocraft/email.rs b/auth/src/robocraft/email.rs new file mode 100644 index 0000000..8da7b0a --- /dev/null +++ b/auth/src/robocraft/email.rs @@ -0,0 +1,39 @@ +use rocket::{post, routes, serde::json::Json, http::Status}; + +fn generate_token(user_auth: &libfj::robocraft::EmailUserAuthenticationPayload) -> String { + let header = jsonwebtoken::Header { + typ: Some("JWT".to_string()), + alg: jsonwebtoken::Algorithm::HS256, + ..Default::default() + }; + let payload = libfj::robocraft::TokenPayload { + public_id: user_auth.display_name.to_owned(), + display_name: user_auth.display_name.to_owned(), + robocraft_name: user_auth.display_name.to_owned(), + email_address: user_auth.email_address.to_owned(), + email_verified: true, + flags: Vec::new(), + }; + let secret = jsonwebtoken::EncodingKey::from_secret(user_auth.password.as_bytes()); // FIXME use an actually secret secret + jsonwebtoken::encode(&header, &payload, &secret) + .unwrap_or_else(|e| { + log::error!("Failed to encode JWT: {}", e); + libfj::robocraft::DEFAULT_TOKEN.to_owned() + }) +} + +#[post("/authenticate/robocraft/game", data = "")] +pub fn email_password_auth(body: Json) -> Result, Status> { + log::info!("Authenticating {} user {}", body.target, body.display_name); + Ok(Json(libfj::robocraft::AuthenticationResponseInfo { + token: generate_token(&body), + refresh_token: "qwertyuiop".to_string(), // TODO + refresh_token_expiry: "0".to_string(), // TODO (seems like this isn't actually considered by the client) + })) +} + +pub fn stage() -> rocket::fairing::AdHoc { + rocket::fairing::AdHoc::on_ignite("Robocraft Email/Password", |rocket| async { + rocket.mount("/", routes![email_password_auth]) + }) +} diff --git a/auth/src/robocraft/mod.rs b/auth/src/robocraft/mod.rs new file mode 100644 index 0000000..b53fff9 --- /dev/null +++ b/auth/src/robocraft/mod.rs @@ -0,0 +1,11 @@ +mod debug; +mod email; +mod steam; + +pub fn stage() -> rocket::fairing::AdHoc { + rocket::fairing::AdHoc::on_ignite("robocraft", |rocket| async { + rocket.attach(email::stage()) + .attach(steam::stage()) + .attach(debug::stage()) + }) +} diff --git a/auth/src/robocraft/steam.rs b/auth/src/robocraft/steam.rs new file mode 100644 index 0000000..cdc9891 --- /dev/null +++ b/auth/src/robocraft/steam.rs @@ -0,0 +1,41 @@ +use rocket::{post, routes, serde::json::Json, http::Status}; + +fn generate_token(user_auth: &libfj::robocraft::SteamAuthenticationPayload, steam_id: u64) -> String { + let header = jsonwebtoken::Header { + typ: Some("JWT".to_string()), + alg: jsonwebtoken::Algorithm::HS256, + ..Default::default() + }; + let payload = libfj::robocraft::TokenPayload { + public_id: steam_id.to_string(), + display_name: steam_id.to_string(), + robocraft_name: steam_id.to_string(), + email_address: format!("{}.rc.steam@ngni.us", steam_id), + email_verified: true, + flags: Vec::new(), + }; + let secret = jsonwebtoken::EncodingKey::from_secret(user_auth.steam_ticket.as_ref()); // FIXME use an actually secret secret + jsonwebtoken::encode(&header, &payload, &secret) + .unwrap_or_else(|e| { + log::error!("Failed to encode JWT: {}", e); + libfj::robocraft::DEFAULT_TOKEN.to_owned() + }) +} + +#[post("/authenticate/steam/game", data = "")] +pub fn steam_auth(body: Json) -> Result, Status> { + let steam_id = crate::common::steam_utils::authenticate_steam_ticket(&body.steam_ticket) + .map_err(|_| Status { code: 401 })?; + log::info!("Authenticating {} steam user {}", body.target, steam_id); + Ok(Json(libfj::robocraft::AuthenticationResponseInfo { + token: generate_token(&body, steam_id), + refresh_token: "qwertyuiop".to_string(), // TODO + refresh_token_expiry: "0".to_string(), // TODO (seems like this isn't actually considered by the client) + })) +} + +pub fn stage() -> rocket::fairing::AdHoc { + rocket::fairing::AdHoc::on_ignite("Robocraft Steam", |rocket| async { + rocket.mount("/", routes![steam_auth]) + }) +} diff --git a/auth/steam_appid.txt b/auth/steam_appid.txt deleted file mode 100644 index c0438f3..0000000 --- a/auth/steam_appid.txt +++ /dev/null @@ -1 +0,0 @@ -920690 diff --git a/rc_static_data/Cargo.toml b/rc_static_data/Cargo.toml new file mode 100644 index 0000000..1dc6473 --- /dev/null +++ b/rc_static_data/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "rc_static_data" +version = "0.1.0" +edition = "2021" + +[dependencies] +rocket.workspace = true +libfj.workspace = true +log.workspace = true +env_logger.workspace = true diff --git a/rc_static_data/Rocket.toml b/rc_static_data/Rocket.toml new file mode 100644 index 0000000..5127b13 --- /dev/null +++ b/rc_static_data/Rocket.toml @@ -0,0 +1,12 @@ +## defaults for _all_ profiles +[default] +address = "127.0.0.1" +port = 8010 + +## set only when compiled in debug mode, i.e, `cargo build` +[debug] +port = 80 + +## set only when compiled in release mode, i.e, `cargo build --release` +[release] +address = "0.0.0.0" diff --git a/rc_static_data/build_arm64.sh b/rc_static_data/build_arm64.sh new file mode 100755 index 0000000..7010ff6 --- /dev/null +++ b/rc_static_data/build_arm64.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +cargo build --release --target aarch64-unknown-linux-musl diff --git a/rc_static_data/run_debug.sh b/rc_static_data/run_debug.sh new file mode 100755 index 0000000..d307e6c --- /dev/null +++ b/rc_static_data/run_debug.sh @@ -0,0 +1,3 @@ +#!/bin/bash +cargo build +RUST_LOG=debug sudo -HE ../target/debug/rc_static_data diff --git a/rc_static_data/src/main.rs b/rc_static_data/src/main.rs new file mode 100644 index 0000000..461238e --- /dev/null +++ b/rc_static_data/src/main.rs @@ -0,0 +1,14 @@ +mod robocraft; + +#[rocket::get("/")] +fn index() -> &'static str { + "Hello, world!" +} + +#[rocket::launch] +fn rocket() -> _ { + env_logger::init(); + rocket::build().mount("/", rocket::routes![index]) + .attach(robocraft::stage()) +} + diff --git a/rc_static_data/src/robocraft/live_data.rs b/rc_static_data/src/robocraft/live_data.rs new file mode 100644 index 0000000..1f81d55 --- /dev/null +++ b/rc_static_data/src/robocraft/live_data.rs @@ -0,0 +1,58 @@ +use rocket::{get, routes, serde::json::Json}; + +#[get("/live/data.json")] +pub fn static_live_data() -> Json { + Json(libfj::robocraft::StaticDataRaw { + MaintenanceMode: "false".into(), + MaintenanceRegex: "".into(), + EacEnabled: "true".into(), + MinimumVersion: "2855".into(), + PhotonSocialServer: "rc-backend.servers.robocraftgame.com:4534".into(), + PhotonServicesServer: "rc-backend.servers.robocraftgame.com:4532".into(), + PhotonChatServer: "rc-backend.servers.robocraftgame.com:4530".into(), + PhotonSinglePlayerServer: "rc-backend.servers.robocraftgame.com:4536".into(), + GameplayServerServiceAddress: "rc-backend.servers.robocraftgame.com:4538".into(), + PhotonLobbyServer: "rc-backend.servers.robocraftgame.com:4540".into(), + ErrorLogAddress: "logs.freejamgames.com:4561".into(), + ServerErrorLogAddress: "logs.freejamgames.com:4562".into(), + authUrl: "http://127.0.0.1:8001/".into(), // originally "https://auth-backend.freejamgames.com/" + paymentUrl: "https://pay.robocraftgame.com/".into(), + enterBattleLogGenerationTimeout: "60".into(), + GameServerConnectionTestTimeout: 10, + AvatarCdnUrl: "https://rc-cdn-images.robocraftgame.com/customavatar/Live/".into(), + ClanAvatarCdnUrl: "https://rc-cdn-images.robocraftgame.com/clanavatar/Live/".into(), + FeatureThrottlerOnPercent: "100".into(), + EmailCaptureEnabled: "true".into(), + UnreliableMessages: "true".into(), + MessageQueueEnabled: "true".into(), + BrawlDataUrl: "https://rc-cdn-images.robocraftgame.com/brawldata/Live/".into(), + CampaignDataUrl: "https://rc-cdn-images.robocraftgame.com/campaigndata/Live/".into(), + LeaderboardsUrl: "https://leaderboards.robocraftgame.com".into(), + NetworkChannelTypes: "3113".into(), + MaxSentMessageQueueSize: 64, + IsAcksLong: 1, + NetworkDropThreshold: 80, + PacketSize: 1200, + MaxPacketSize: 5888, + MaxCombinedReliableMessageCount: 20, + MaxCombinedReliableMessageSize: 200, + MaxDelay: 1, + OverflowThreshold: 10, + MinUpdateTimeout: 1, + DevMessageRefresh: 60, + MaintenanceRefresh: 30, + SaveRequestOnPhoton: "false".into(), + UseS3System: "true".into(), + authMigrationUrl: "http://88.150.159.132:3000/auth-migration".into(), + xsollaEnabled: "true".into(), + MaintenanceMessage: "Robocraft is currently undergoing server maintenance. ".into(), + DevMessage: "NGnius says hello".into(), + DevMessageDisplayTime: "10".into(), + }) +} + +pub fn stage() -> rocket::fairing::AdHoc { + rocket::fairing::AdHoc::on_ignite("Robocraft live data.json", |rocket| async { + rocket.mount("/", routes![static_live_data]) + }) +} diff --git a/rc_static_data/src/robocraft/mod.rs b/rc_static_data/src/robocraft/mod.rs new file mode 100644 index 0000000..979ba57 --- /dev/null +++ b/rc_static_data/src/robocraft/mod.rs @@ -0,0 +1,7 @@ +mod live_data; + +pub fn stage() -> rocket::fairing::AdHoc { + rocket::fairing::AdHoc::on_ignite("JSON", |rocket| async { + rocket.attach(live_data::stage()) + }) +}