1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-07-10 16:07:45 +00:00

Unify version and file checking behind one config parameter

And make it affect the patch server as well, if you really want to
use an unsupported version.
This commit is contained in:
Joshua Goins 2025-07-04 11:56:12 -04:00
parent 70c423e48f
commit 7ccd132129
3 changed files with 108 additions and 84 deletions

View file

@ -224,7 +224,7 @@ async fn main() {
let config = get_config(); let config = get_config();
// The lobby server does its own version check as well, but it can be turned off if desired. // The lobby server does its own version check as well, but it can be turned off if desired.
if config.lobby.do_version_checks if config.enforce_validity_checks
&& !do_game_version_check(version_info) && !do_game_version_check(version_info)
{ {
// "A version update is required." // "A version update is required."

View file

@ -88,48 +88,56 @@ async fn verify_session(
return StatusCode::INTERNAL_SERVER_ERROR.into_response(); return StatusCode::INTERNAL_SERVER_ERROR.into_response();
} }
tracing::info!("Verifying game components for {platform} {channel} {game_version} {body}..."); if config.enforce_validity_checks {
tracing::info!(
"Verifying game components for {platform} {channel} {game_version} {body}..."
);
let body_parts: Vec<&str> = body.split('\n').collect(); let body_parts: Vec<&str> = body.split('\n').collect();
let _hashes = body_parts[0]; let _hashes = body_parts[0];
let expansion_versions = &body_parts[1..body_parts.len() - 1]; // last part is empty let expansion_versions = &body_parts[1..body_parts.len() - 1]; // last part is empty
let game_version = Version(&game_version); let game_version = Version(&game_version);
let supported_expac_versions = get_supported_expac_versions(); let supported_expac_versions = get_supported_expac_versions();
for expansion_version in expansion_versions { for expansion_version in expansion_versions {
let expac_version_parts: Vec<&str> = expansion_version.split('\t').collect(); let expac_version_parts: Vec<&str> = expansion_version.split('\t').collect();
let expansion_name = expac_version_parts[0]; // e.g. ex1 let expansion_name = expac_version_parts[0]; // e.g. ex1
let expansion_version = expac_version_parts[1]; let expansion_version = expac_version_parts[1];
if Version(expansion_version) > supported_expac_versions[expansion_name] { if Version(expansion_version) > supported_expac_versions[expansion_name] {
tracing::warn!(
"{expansion_name} {expansion_version} is above supported version {}!",
supported_expac_versions[expansion_name]
);
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
}
// Their version is too new
if game_version > SUPPORTED_GAME_VERSION {
tracing::warn!( tracing::warn!(
"{expansion_name} {expansion_version} is above supported version {}!", "{game_version} is above supported game version {SUPPORTED_GAME_VERSION}!"
supported_expac_versions[expansion_name]
); );
return StatusCode::INTERNAL_SERVER_ERROR.into_response(); return StatusCode::INTERNAL_SERVER_ERROR.into_response();
} }
}
// Their version is too new if game_version < SUPPORTED_GAME_VERSION {
if game_version > SUPPORTED_GAME_VERSION { tracing::warn!(
tracing::warn!("{game_version} is above supported game version {SUPPORTED_GAME_VERSION}!"); "{game_version} is below supported game version {SUPPORTED_GAME_VERSION}!"
return StatusCode::INTERNAL_SERVER_ERROR.into_response(); );
} return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
if game_version < SUPPORTED_GAME_VERSION { // If we are up to date, yay!
tracing::warn!("{game_version} is below supported game version {SUPPORTED_GAME_VERSION}!"); if game_version == SUPPORTED_GAME_VERSION {
return StatusCode::INTERNAL_SERVER_ERROR.into_response(); let mut headers = HeaderMap::new();
} headers.insert("X-Patch-Unique-Id", sid.parse().unwrap());
// If we are up to date, yay! return (headers).into_response();
if game_version == SUPPORTED_GAME_VERSION { }
let mut headers = HeaderMap::new();
headers.insert("X-Patch-Unique-Id", sid.parse().unwrap());
return (headers).into_response();
} }
let mut headers = HeaderMap::new(); let mut headers = HeaderMap::new();
@ -147,66 +155,75 @@ async fn verify_boot(
return StatusCode::INTERNAL_SERVER_ERROR.into_response(); return StatusCode::INTERNAL_SERVER_ERROR.into_response();
} }
tracing::info!("Verifying boot components for {platform} {channel} {boot_version}...");
let config = get_config(); let config = get_config();
if !config.supports_platform(&platform) { if !config.supports_platform(&platform) {
tracing::warn!("Invalid platform! {platform}");
return StatusCode::INTERNAL_SERVER_ERROR.into_response(); return StatusCode::INTERNAL_SERVER_ERROR.into_response();
} }
let actual_boot_version = boot_version.split("?time").collect::<Vec<&str>>()[0]; if config.enforce_validity_checks {
let boot_version = Version(actual_boot_version); tracing::info!("Verifying boot components for {platform} {channel} {boot_version}...");
// If we are up to date, yay! let config = get_config();
if boot_version == SUPPORTED_BOOT_VERSION { if !config.supports_platform(&platform) {
let headers = HeaderMap::new(); tracing::warn!("Invalid platform! {platform}");
return (headers).into_response(); return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
// Their version is too new
if boot_version > SUPPORTED_BOOT_VERSION {
tracing::warn!("{boot_version} is above supported boot version {SUPPORTED_BOOT_VERSION}!");
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
// check if we need any patching
let mut send_patches = Vec::new();
let patches = list_patch_files(&format!("{}/boot", &config.patch.patches_location));
for patch in patches {
let patch_str: &str = &patch;
if actual_boot_version.partial_cmp(patch_str).unwrap() == Ordering::Less {
let file = std::fs::File::open(&*format!(
"{}/boot/{}.patch",
&config.patch.patches_location, patch_str
))
.unwrap();
let metadata = file.metadata().unwrap();
send_patches.push(PatchEntry {
url: format!("http://{}/boot/{}.patch", config.patch.patch_dl_url, patch)
.to_string(),
version: patch_str.to_string(),
hash_block_size: 50000000,
length: metadata.len() as i64,
size_on_disk: metadata.len() as i64, // NOTE: wrong but it should be fine to lie
hashes: vec![],
unknown_a: 0,
unknown_b: 0,
});
} }
}
if !send_patches.is_empty() { let actual_boot_version = boot_version.split("?time").collect::<Vec<&str>>()[0];
let patch_list = PatchList { let boot_version = Version(actual_boot_version);
id: "477D80B1_38BC_41d4_8B48_5273ADB89CAC".to_string(),
requested_version: boot_version.to_string().clone(), // If we are up to date, yay!
content_location: format!("ffxivpatch/boot/metainfo/{}.http", boot_version.0), // FIXME: i think this is actually supposed to be the target version if boot_version == SUPPORTED_BOOT_VERSION {
patch_length: 0, let headers = HeaderMap::new();
patches: send_patches, return (headers).into_response();
}; }
let patch_list_str = patch_list.to_string(PatchListType::Boot);
return patch_list_str.into_response(); // Their version is too new
if boot_version > SUPPORTED_BOOT_VERSION {
tracing::warn!(
"{boot_version} is above supported boot version {SUPPORTED_BOOT_VERSION}!"
);
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
// check if we need any patching
let mut send_patches = Vec::new();
let patches = list_patch_files(&format!("{}/boot", &config.patch.patches_location));
for patch in patches {
let patch_str: &str = &patch;
if actual_boot_version.partial_cmp(patch_str).unwrap() == Ordering::Less {
let file = std::fs::File::open(&*format!(
"{}/boot/{}.patch",
&config.patch.patches_location, patch_str
))
.unwrap();
let metadata = file.metadata().unwrap();
send_patches.push(PatchEntry {
url: format!("http://{}/boot/{}.patch", config.patch.patch_dl_url, patch)
.to_string(),
version: patch_str.to_string(),
hash_block_size: 50000000,
length: metadata.len() as i64,
size_on_disk: metadata.len() as i64, // NOTE: wrong but it should be fine to lie
hashes: vec![],
unknown_a: 0,
unknown_b: 0,
});
}
}
if !send_patches.is_empty() {
let patch_list = PatchList {
id: "477D80B1_38BC_41d4_8B48_5273ADB89CAC".to_string(),
requested_version: boot_version.to_string().clone(),
content_location: format!("ffxivpatch/boot/metainfo/{}.http", boot_version.0), // FIXME: i think this is actually supposed to be the target version
patch_length: 0,
patches: send_patches,
};
let patch_list_str = patch_list.to_string(PatchListType::Boot);
return patch_list_str.into_response();
}
} }
let headers = HeaderMap::new(); let headers = HeaderMap::new();

View file

@ -66,7 +66,6 @@ impl FrontierConfig {
pub struct LobbyConfig { pub struct LobbyConfig {
pub port: u16, pub port: u16,
pub listen_address: String, pub listen_address: String,
pub do_version_checks: bool,
} }
impl Default for LobbyConfig { impl Default for LobbyConfig {
@ -74,7 +73,6 @@ impl Default for LobbyConfig {
Self { Self {
port: 7000, port: 7000,
listen_address: "0.0.0.0".to_string(), listen_address: "0.0.0.0".to_string(),
do_version_checks: true,
} }
} }
} }
@ -401,6 +399,10 @@ pub struct Config {
/// Enable various packet debug functions. This will clutter your working directory! /// Enable various packet debug functions. This will clutter your working directory!
#[serde(default)] #[serde(default)]
pub packet_debugging: bool, pub packet_debugging: bool,
/// Enable various validity checks for version and file hashes that emulate retail.
#[serde(default = "Config::default_enforce_validity_checks")]
pub enforce_validity_checks: bool,
} }
impl Default for Config { impl Default for Config {
@ -418,6 +420,7 @@ impl Default for Config {
launcher: LauncherConfig::default(), launcher: LauncherConfig::default(),
save_data_bank: SaveDataBankConfig::default(), save_data_bank: SaveDataBankConfig::default(),
packet_debugging: false, packet_debugging: false,
enforce_validity_checks: Self::default_enforce_validity_checks(),
} }
} }
} }
@ -426,6 +429,10 @@ impl Config {
pub fn supports_platform(&self, platform: &String) -> bool { pub fn supports_platform(&self, platform: &String) -> bool {
self.supported_platforms.contains(platform) self.supported_platforms.contains(platform)
} }
fn default_enforce_validity_checks() -> bool {
true
}
} }
fn default_supported_platforms() -> Vec<String> { fn default_supported_platforms() -> Vec<String> {