dispatch-server: unhardcode cdn_conf_ext and load res versions from configuration file

This commit is contained in:
xeon 2025-03-13 18:16:48 +03:00
parent de4bf109ed
commit f89b49f903
6 changed files with 80 additions and 22 deletions

1
Cargo.lock generated
View file

@ -3652,6 +3652,7 @@ dependencies = [
"serde_json", "serde_json",
"thiserror 2.0.11", "thiserror 2.0.11",
"tokio", "tokio",
"toml",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"trigger-cryptography", "trigger-cryptography",

View file

@ -9,6 +9,7 @@ axum.workspace = true
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
toml.workspace = true
base64.workspace = true base64.workspace = true
tracing.workspace = true tracing.workspace = true

View file

@ -1,7 +1,7 @@
use std::net::SocketAddr; use std::{collections::HashMap, net::SocketAddr};
use serde::Deserialize; use serde::Deserialize;
use trigger_sv::config::TomlConfig; use trigger_sv::config::{ConfigurationLoadError, TomlConfig};
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct NetworkSetting { pub struct NetworkSetting {
@ -37,6 +37,29 @@ pub struct DispatchConfig {
pub bound_server: BoundRegionSetting, pub bound_server: BoundRegionSetting,
} }
#[derive(Deserialize)]
pub struct ResVersionConfig {
pub design_data_url: String,
pub design_data_revision: String,
pub design_data_files: String,
pub game_res_url: String,
pub game_res_branch: String,
pub game_audio_revision: String,
pub game_res_revision: String,
pub game_res_files: String,
pub silence_url: String,
pub silence_revision: String,
pub silence_files: String,
}
impl ResVersionConfig {
pub fn load_version_map_from_file(
path: &str,
) -> Result<HashMap<String, Self>, ConfigurationLoadError> {
Ok(toml::from_str(&std::fs::read_to_string(path)?)?)
}
}
impl TomlConfig for DispatchConfig { impl TomlConfig for DispatchConfig {
const DEFAULT_TOML: &str = include_str!("../dispatch.default.toml"); const DEFAULT_TOML: &str = include_str!("../dispatch.default.toml");
} }

View file

@ -1,10 +1,11 @@
use std::{ use std::{
collections::HashMap,
process::ExitCode, process::ExitCode,
sync::{LazyLock, OnceLock}, sync::{LazyLock, OnceLock},
}; };
use axum::{routing::get, Router}; use axum::{routing::get, Router};
use config::DispatchConfig; use config::{DispatchConfig, ResVersionConfig};
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tracing::error; use tracing::error;
use trigger_sv::{ use trigger_sv::{
@ -19,11 +20,12 @@ mod query_dispatch;
mod query_gateway; mod query_gateway;
const CONFIG_FILE: &str = "dispatch.toml"; const CONFIG_FILE: &str = "dispatch.toml";
const RES_CONFIG_FILE: &str = "res_versions.toml";
#[derive(Clone)]
struct AppState { struct AppState {
pub config: &'static DispatchConfig, pub config: &'static DispatchConfig,
pub environment: &'static ServerEnvironmentConfiguration, pub environment: &'static ServerEnvironmentConfiguration,
pub res_versions: HashMap<String, ResVersionConfig>,
} }
#[tokio::main] #[tokio::main]
@ -41,9 +43,16 @@ async fn main() -> ExitCode {
print_banner(); print_banner();
logging::init_tracing(tracing::Level::DEBUG); logging::init_tracing(tracing::Level::DEBUG);
let res_versions = ResVersionConfig::load_version_map_from_file(RES_CONFIG_FILE)
.unwrap_or_else(|err| {
error!("failed to load {RES_CONFIG_FILE}: {err}");
die();
});
let state = APP_STATE.get_or_init(|| AppState { let state = APP_STATE.get_or_init(|| AppState {
config: &CONFIG, config: &CONFIG,
environment: &ENVIRONMENT, environment: &ENVIRONMENT,
res_versions,
}); });
let app = Router::new() let app = Router::new()
@ -52,11 +61,10 @@ async fn main() -> ExitCode {
.route(query_gateway::ROUTE_ENDPOINT, get(query_gateway::process)) .route(query_gateway::ROUTE_ENDPOINT, get(query_gateway::process))
.with_state(state); .with_state(state);
let Ok(listener) = TcpListener::bind(CONFIG.network.http_addr).await.inspect_err(|err| { let listener = TcpListener::bind(CONFIG.network.http_addr).await.unwrap_or_else(|err| {
error!("TcpListener::bind failed. Is another instance of the server already running? Error: {err}"); error!("TcpListener::bind failed. Is another instance of the server already running? Error: {err}");
}) else {
die(); die();
}; });
axum::serve(listener, app).await.unwrap_or_else(|err| { axum::serve(listener, app).await.unwrap_or_else(|err| {
error!("axum::serve failed: {err}"); error!("axum::serve failed: {err}");

View file

@ -30,13 +30,15 @@ pub enum QueryGatewayError {
InvalidRsaVer(u32), InvalidRsaVer(u32),
#[error("invalid dispatch seed, expected: {0}, got: {1}")] #[error("invalid dispatch seed, expected: {0}, got: {1}")]
InvalidDispatchSeed(&'static str, String), InvalidDispatchSeed(&'static str, String),
#[error("ResVersionConfig for {0} is not defined")]
MissingResVersionConfig(String),
} }
impl QueryGatewayError { impl QueryGatewayError {
pub fn retcode(&self) -> i32 { pub fn retcode(&self) -> i32 {
match self { match self {
Self::InvalidRsaVer(_) => 74, Self::InvalidRsaVer(_) => 74,
Self::InvalidDispatchSeed(_, _) => 75, Self::InvalidDispatchSeed(_, _) | Self::MissingResVersionConfig(_) => 75,
} }
} }
@ -71,6 +73,7 @@ impl IntoResponse for Response {
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct QueryGatewayParam { pub struct QueryGatewayParam {
pub version: String,
pub rsa_ver: u32, pub rsa_ver: u32,
pub seed: String, pub seed: String,
} }
@ -118,6 +121,13 @@ fn internal_process(
QueryGatewayError::InvalidDispatchSeed(&state.config.bound_server.seed, param.seed), QueryGatewayError::InvalidDispatchSeed(&state.config.bound_server.seed, param.seed),
))?; ))?;
let res_version = state.res_versions.get(&param.version).ok_or_else(|| {
(
rsa,
QueryGatewayError::MissingResVersionConfig(param.version),
)
})?;
let server = &state.config.bound_server; let server = &state.config.bound_server;
Ok(Response { Ok(Response {
@ -127,7 +137,10 @@ fn internal_process(
msg: String::with_capacity(0), msg: String::with_capacity(0),
region_name: Borrowed(&server.name), region_name: Borrowed(&server.name),
title: Borrowed(&server.title), title: Borrowed(&server.title),
client_secret_key: Owned(base64::engine::general_purpose::STANDARD.encode(&state.environment.security.static_key.seed_buf)), client_secret_key: Owned(
base64::engine::general_purpose::STANDARD
.encode(&state.environment.security.static_key.seed_buf),
),
cdn_check_url: String::with_capacity(0), cdn_check_url: String::with_capacity(0),
gateway: Some(ServerGateway { gateway: Some(ServerGateway {
ip: Borrowed(&server.addr), ip: Borrowed(&server.addr),
@ -137,23 +150,22 @@ fn internal_process(
force_update_url: String::new(), force_update_url: String::new(),
stop_jump_url: String::new(), stop_jump_url: String::new(),
cdn_conf_ext: Some(CdnConfExt { cdn_conf_ext: Some(CdnConfExt {
// TODO: unhardcode this
design_data: CdnDesignData { design_data: CdnDesignData {
base_url: Borrowed("https://autopatchos.zenlesszonezero.com/design_data/1.6_live/output_6898738_6ffe812558/client/"), base_url: Borrowed(&res_version.design_data_url),
data_revision: Borrowed("6898738"), data_revision: Borrowed(&res_version.design_data_revision),
md5_files: Borrowed(r#"[{"fileName": "data_version", "fileSize": 2065, "fileMD5": "16970197844668905690"}]"#), md5_files: Borrowed(&res_version.design_data_files),
}, },
game_res: CdnGameRes { game_res: CdnGameRes {
audio_revision: Borrowed("6898738"), audio_revision: Borrowed(&res_version.game_audio_revision),
base_url: Borrowed("https://autopatchos.zenlesszonezero.com/game_res/1.6_live/output_6898738_6ffe812558/client/"), base_url: Borrowed(&res_version.game_res_url),
branch: Borrowed("beta_live"), branch: Borrowed(&res_version.game_res_branch),
md5_files: Borrowed(r#"[{"fileName": "res_version", "fileSize": 1167660, "fileMD5": "8072678507435758384"}, {"fileName": "audio_version", "fileSize": 15447, "fileMD5": "5401804085122358755"}, {"fileName": "base_revision", "fileSize": 4, "fileMD5": "4524394692449115962"}]"#), md5_files: Borrowed(&res_version.game_res_files),
res_revision: Borrowed("6898738"), res_revision: Borrowed(&res_version.game_res_revision),
}, },
silence_data: CdnSilenceData { silence_data: CdnSilenceData {
base_url: Borrowed("https://autopatchos.zenlesszonezero.com/design_data/1.6_live/output_6898738_6ffe812558/client_silence/"), base_url: Borrowed(&res_version.silence_url),
md5_files: Borrowed(r#"[{"fileName": "silence_version", "fileSize": 130, "fileMD5": "2077712550601860122"}]"#), md5_files: Borrowed(&res_version.silence_files),
silence_revision: Borrowed("6898738"), silence_revision: Borrowed(&res_version.silence_revision),
}, },
pre_download: None, pre_download: None,
}), }),
@ -174,7 +186,7 @@ fn internal_process(
url_check_nap: String::new(), url_check_nap: String::new(),
url_check_sdk: String::new(), url_check_sdk: String::new(),
}), }),
} },
}) })
} }

13
res_versions.toml Normal file
View file

@ -0,0 +1,13 @@
["OSPRODWin1.6.0"]
design_data_url = "https://autopatchos.zenlesszonezero.com/design_data/1.6_live/output_7108077_40d564da66/client/"
design_data_revision = "7108077"
design_data_files = '[{"fileName":"data_version","fileSize":4304,"fileMD5":"941245265336071363"}]'
game_res_url = "https://autopatchos.zenlesszonezero.com/game_res/1.6_live/output_7108077_40d564da66/client/"
game_res_branch = "1.6_live"
game_audio_revision = "7036823"
game_res_revision = "7108077"
game_res_files = '[{"fileName":"res_version","fileSize":2487933,"fileMD5":"16738022146290696861"},{"fileName":"audio_version","fileSize":32246,"fileMD5":"718221674779072830"},{"fileName":"base_revision","fileSize":18,"fileMD5":"3467029748880207404"}]'
silence_url = "https://autopatchos.zenlesszonezero.com/design_data/1.6_live/output_7108077_40d564da66/client_silence/"
silence_revision = "7036823"
silence_files = '[{"fileName":"silence_version","fileSize":467,"fileMD5":"2392489994088458280"}]'