diff --git a/src/bin/kawari-admin.rs b/src/bin/kawari-admin.rs index 3301565..d20e8b7 100644 --- a/src/bin/kawari-admin.rs +++ b/src/bin/kawari-admin.rs @@ -24,7 +24,7 @@ async fn root() -> Html { let environment = setup_default_environment(); let template = environment.get_template("admin.html").unwrap(); - Html(template.render(context! { worlds_open => config.worlds_open, login_open => config.login_open }).unwrap()) + Html(template.render(context! { worlds_open => config.worlds_open, login_open => config.login_open, boot_patch_location => config.boot_patches_location }).unwrap()) } #[derive(Deserialize, Debug)] @@ -32,6 +32,7 @@ async fn root() -> Html { struct Input { worlds_open: Option, login_open: Option, + boot_patch_location: Option, } async fn apply(Form(input): Form) -> Redirect { @@ -51,6 +52,10 @@ async fn apply(Form(input): Form) -> Redirect { config.login_open = false; } + if let Some(boot_patch_location) = input.boot_patch_location { + config.boot_patches_location = boot_patch_location; + } + serde_json::to_writer( &std::fs::File::create("config.json").unwrap(), &config, diff --git a/src/bin/kawari-patch.rs b/src/bin/kawari-patch.rs index ffc34ce..5399079 100644 --- a/src/bin/kawari-patch.rs +++ b/src/bin/kawari-patch.rs @@ -1,3 +1,5 @@ +use std::cmp::Ordering; +use std::fs::read_dir; use std::net::SocketAddr; use axum::{Form, Json, Router, routing::get}; @@ -9,6 +11,40 @@ use kawari::config::{Config, get_config}; use axum::extract::Path; use axum::response::IntoResponse; use axum::http::{HeaderMap, StatusCode}; +use minijinja::filters::list; + +fn list_patch_files(dir_path: &str) -> Vec { + let mut entries: Vec<_> = read_dir(dir_path).unwrap().flatten().collect(); + entries.sort_by_key(|dir| dir.path()); + let mut game_patches: Vec<_> = entries + .into_iter() + .flat_map(|entry| { + let Ok(meta) = entry.metadata() else { + return vec![]; + }; + if meta.is_dir() { + return vec![]; + } + if meta.is_file() && entry.file_name().to_str().unwrap().contains(".patch") { + return vec![entry.path()]; + } + vec![] + }) + .collect(); + game_patches.sort_by(|a, b| { + // Ignore H/D in front of filenames + let mut a_path = a.as_path().file_name().unwrap().to_str().unwrap().to_string(); + if a_path.starts_with("H") { + return Ordering::Less; + } + let mut b_path = b.as_path().file_name().unwrap().to_str().unwrap().to_string(); + /*if b_path.starts_with("H") { + return Ordering::Greater; + }*/ + a_path.partial_cmp(&b_path).unwrap() + }); // ensure we're actually installing them in the correct order + game_patches.iter().map(|x| x.file_stem().unwrap().to_str().unwrap().to_string() ).collect() +} async fn verify_session(Path((platform, game_version, sid)): Path<(String, String, String)>) -> impl IntoResponse { let config = get_config(); @@ -22,12 +58,28 @@ async fn verify_session(Path((platform, game_version, sid)): Path<(String, Strin (headers).into_response() } + async fn verify_boot(Path((platform, boot_version)): Path<(String, String)>) -> impl IntoResponse { + tracing::info!("Verifying boot components..."); + let config = get_config(); if !config.supports_platform(&platform) { return StatusCode::INTERNAL_SERVER_ERROR.into_response(); } + // Turns 2019.03.12.0000.0001/?time=2024-06-29-18-30 into just 2019.03.12.0000.0001 + let actual_boot_version = boot_version.split("?time").collect::>()[0]; + + // check if we need any patching + let patches = list_patch_files(&config.boot_patches_location); + for patch in patches { + let patch_str: &str = &patch; + if actual_boot_version.partial_cmp(patch_str).unwrap() == Ordering::Less { + // not up to date! + // TODO: serve patchlist + } + } + let mut headers = HeaderMap::new(); (headers).into_response() } @@ -38,7 +90,7 @@ async fn main() { let app = Router::new() .route("/http/:platform/ffxivneo_release_game/:game_version/:sid", post(verify_session)) - .route("/http/:platform/ffxivneo_release_boot/:boot_version", get(verify_boot)); + .route("/http/:platform/ffxivneo_release_boot/*boot_version", get(verify_boot)); // NOTE: for future programmers, this is a wildcard because axum hates the /version/?time=blah format. let addr = SocketAddr::from(([127, 0, 0, 1], 6900)); tracing::info!("Patch server started on {}", addr); diff --git a/src/config.rs b/src/config.rs index 131d5da..e081cd0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,6 +10,9 @@ pub struct Config { #[serde(default = "default_supported_platforms")] pub supported_platforms: Vec, + + #[serde(default)] + pub boot_patches_location: String } impl Default for Config { @@ -17,6 +20,7 @@ impl Default for Config { Self { worlds_open: false, login_open: false, + boot_patches_location: String::new(), supported_platforms: default_supported_platforms() } } diff --git a/templates/admin.html b/templates/admin.html index da509cc..3e3f1b6 100644 --- a/templates/admin.html +++ b/templates/admin.html @@ -1,7 +1,18 @@

Gate open:{{ worlds_open }}

Gate open:{{ login_open }}

+

Boot patch location:{{ boot_patch_location }}

- - + + +
\ No newline at end of file