1
0
mirror of https://git.ngram.ca/OpenJam/rc-servers synced 2026-08-23 23:08:52 +00:00

Add basic RC steam and user/pass authentication

This commit is contained in:
NGnius (Graham)
2025-01-27 19:52:57 -05:00
parent 82d0c3d0f3
commit 49357b2759
23 changed files with 488 additions and 86 deletions

2
.gitignore vendored
View File

@@ -1 +1,3 @@
/target
steam_appid.txt

79
Cargo.lock generated
View File

@@ -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",

View File

@@ -5,7 +5,7 @@ edition = "2021"
[workspace]
members = [
"auth",
"auth", "rc_static_data",
]
[workspace.dependencies]

View File

@@ -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

View File

@@ -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<string, object> 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<Dictionary<string, object>> onAuthSuccess, Action<Exception> 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<Dictionary<string, object>> #=zmoN68pYsK05$, Action<Exception> #=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<Dictionary<string, object>> onSuccess, Action onFailure, Action<Exception> onError)`, which calls `AuthenticateSteamUserInternal(...)` and then schedules the task (IEnumerator is returned).
```
[DebuggerHidden]
public static IEnumerator AuthenticateSteamUserInternal(Action<Dictionary<string, object>> onSuccess, Action onFailure, Action<Exception> onError)
{
#=zxQRony8oFWvBWu4O0RraT0fsXcjv #=zxQRony8oFWvBWu4O0RraT0fsXcjv = new #=zxQRony8oFWvBWu4O0RraT0fsXcjv();
#=zxQRony8oFWvBWu4O0RraT0fsXcjv.#=zmoN68pYsK05$ = onSuccess;
#=zxQRony8oFWvBWu4O0RraT0fsXcjv.#=zN4ekGCs= = onError;
return #=zxQRony8oFWvBWu4O0RraT0fsXcjv;
}
```

View File

@@ -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"

View File

@@ -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())

View File

@@ -1,86 +1,34 @@
use rocket::{post, routes, serde::json::Json, http::Status};
#[cfg(feature = "steam")]
static STEAM_SERVER: std::sync::OnceLock<steamworks::Server> = 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 = "<body>")]
pub fn steam_auth(body: Json<libfj::cardlife::SteamAuthenticationPayload>) -> Result<Json<libfj::cardlife::AuthenticationInfo>, 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])
})

1
auth/src/common/mod.rs Normal file
View File

@@ -0,0 +1 @@
pub(crate) mod steam_utils;

View File

@@ -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<u64, hex::FromHexError> {
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<steamworks::Server> = 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<u64, ()> {
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<u64, ()> {
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)
}

View File

@@ -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");

View File

@@ -0,0 +1,13 @@
use rocket::{post, routes};
#[post("/", data = "<body>")]
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])
})
}

View File

@@ -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 = "<body>")]
pub fn email_password_auth(body: Json<libfj::robocraft::EmailUserAuthenticationPayload>) -> Result<Json<libfj::robocraft::AuthenticationResponseInfo>, 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])
})
}

11
auth/src/robocraft/mod.rs Normal file
View File

@@ -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())
})
}

View File

@@ -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 = "<body>")]
pub fn steam_auth(body: Json<libfj::robocraft::SteamAuthenticationPayload>) -> Result<Json<libfj::robocraft::AuthenticationResponseInfo>, 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])
})
}

View File

@@ -1 +0,0 @@
920690

10
rc_static_data/Cargo.toml Normal file
View File

@@ -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

View File

@@ -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"

3
rc_static_data/build_arm64.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
cargo build --release --target aarch64-unknown-linux-musl

3
rc_static_data/run_debug.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
cargo build
RUST_LOG=debug sudo -HE ../target/debug/rc_static_data

View File

@@ -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())
}

View File

@@ -0,0 +1,58 @@
use rocket::{get, routes, serde::json::Json};
#[get("/live/data.json")]
pub fn static_live_data() -> Json<libfj::robocraft::StaticDataRaw> {
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])
})
}

View File

@@ -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())
})
}