1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-04-19 22:36:49 +00:00

Add configurable boot patch location, fix verifying boot components

And more, this is mostly laying the groundwork for serving patch files.
This commit is contained in:
Joshua Goins 2024-06-29 14:44:13 -04:00
parent 91e80a36f8
commit c1ad0c023c
4 changed files with 76 additions and 4 deletions

View file

@ -24,7 +24,7 @@ async fn root() -> Html<String> {
let environment = setup_default_environment(); let environment = setup_default_environment();
let template = environment.get_template("admin.html").unwrap(); 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)] #[derive(Deserialize, Debug)]
@ -32,6 +32,7 @@ async fn root() -> Html<String> {
struct Input { struct Input {
worlds_open: Option<String>, worlds_open: Option<String>,
login_open: Option<String>, login_open: Option<String>,
boot_patch_location: Option<String>,
} }
async fn apply(Form(input): Form<Input>) -> Redirect { async fn apply(Form(input): Form<Input>) -> Redirect {
@ -51,6 +52,10 @@ async fn apply(Form(input): Form<Input>) -> Redirect {
config.login_open = false; 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( serde_json::to_writer(
&std::fs::File::create("config.json").unwrap(), &std::fs::File::create("config.json").unwrap(),
&config, &config,

View file

@ -1,3 +1,5 @@
use std::cmp::Ordering;
use std::fs::read_dir;
use std::net::SocketAddr; use std::net::SocketAddr;
use axum::{Form, Json, Router, routing::get}; use axum::{Form, Json, Router, routing::get};
@ -9,6 +11,40 @@ use kawari::config::{Config, get_config};
use axum::extract::Path; use axum::extract::Path;
use axum::response::IntoResponse; use axum::response::IntoResponse;
use axum::http::{HeaderMap, StatusCode}; use axum::http::{HeaderMap, StatusCode};
use minijinja::filters::list;
fn list_patch_files(dir_path: &str) -> Vec<String> {
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 { async fn verify_session(Path((platform, game_version, sid)): Path<(String, String, String)>) -> impl IntoResponse {
let config = get_config(); let config = get_config();
@ -22,12 +58,28 @@ async fn verify_session(Path((platform, game_version, sid)): Path<(String, Strin
(headers).into_response() (headers).into_response()
} }
async fn verify_boot(Path((platform, boot_version)): Path<(String, String)>) -> impl IntoResponse { async fn verify_boot(Path((platform, boot_version)): Path<(String, String)>) -> impl IntoResponse {
tracing::info!("Verifying boot components...");
let config = get_config(); let config = get_config();
if !config.supports_platform(&platform) { if !config.supports_platform(&platform) {
return StatusCode::INTERNAL_SERVER_ERROR.into_response(); 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::<Vec<&str>>()[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(); let mut headers = HeaderMap::new();
(headers).into_response() (headers).into_response()
} }
@ -38,7 +90,7 @@ async fn main() {
let app = Router::new() let app = Router::new()
.route("/http/:platform/ffxivneo_release_game/:game_version/:sid", post(verify_session)) .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)); let addr = SocketAddr::from(([127, 0, 0, 1], 6900));
tracing::info!("Patch server started on {}", addr); tracing::info!("Patch server started on {}", addr);

View file

@ -10,6 +10,9 @@ pub struct Config {
#[serde(default = "default_supported_platforms")] #[serde(default = "default_supported_platforms")]
pub supported_platforms: Vec<String>, pub supported_platforms: Vec<String>,
#[serde(default)]
pub boot_patches_location: String
} }
impl Default for Config { impl Default for Config {
@ -17,6 +20,7 @@ impl Default for Config {
Self { Self {
worlds_open: false, worlds_open: false,
login_open: false, login_open: false,
boot_patches_location: String::new(),
supported_platforms: default_supported_platforms() supported_platforms: default_supported_platforms()
} }
} }

View file

@ -1,7 +1,18 @@
<p>Gate open:{{ worlds_open }}</p> <p>Gate open:{{ worlds_open }}</p>
<p>Gate open:{{ login_open }}</p> <p>Gate open:{{ login_open }}</p>
<p>Boot patch location:{{ boot_patch_location }}</p>
<form action='apply' method='post'> <form action='apply' method='post'>
<input type='checkbox' id='worlds_open' name='worlds_open' checked/> <label>
<input type='checkbox' id='login_open' name='login_open' checked/> Worlds Open:
<input type='checkbox' id='worlds_open' name='worlds_open' checked/>
</label>
<label>
Login Open:
<input type='checkbox' id='login_open' name='login_open' checked/>
</label>
<label>
Boot Patch Location:
<input type='text' id='boot_patch_location' name='boot_patch_location'/>
</label>
<button type='submit'>Apply</button> <button type='submit'>Apply</button>
</form> </form>