From d47b7563dfd5b0a163e2a912a10b5ea432bb8c89 Mon Sep 17 00:00:00 2001 From: bigfoot547 Date: Wed, 18 Dec 2024 19:36:04 -0600 Subject: implement version list --- src/launcher.rs | 126 +++++++++++++++++++++++++++++++++++++++++++--- src/launcher/constants.rs | 1 + 2 files changed, 120 insertions(+), 7 deletions(-) create mode 100644 src/launcher/constants.rs (limited to 'src') diff --git a/src/launcher.rs b/src/launcher.rs index 695406a..27fdf9c 100644 --- a/src/launcher.rs +++ b/src/launcher.rs @@ -1,29 +1,122 @@ -use std::collections::BTreeMap; - +use std::{collections::{BTreeMap, HashMap}, error::Error, fmt::Write, io::ErrorKind}; +use std::path::PathBuf; use chrono::{DateTime, Utc}; -use serde::Deserialize; +use serde::{Serialize, Deserialize}; + +use log::{debug, warn}; use super::version::{*, manifest::*}; -#[derive(Deserialize, Debug)] +mod constants; +use constants::*; + +#[derive(Serialize, Deserialize, Debug)] struct RemoteVersionIndexEntry { last_update: DateTime } -#[derive(Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug)] struct RemoteVersionIndex { versions: BTreeMap } +impl Default for RemoteVersionIndex { + fn default() -> Self { + RemoteVersionIndex { + versions: BTreeMap::new() + } + } +} + struct RemoteVersionList { - manifest: VersionManifest, + versions: HashMap, index: RemoteVersionIndex } +impl RemoteVersionList { + async fn new(home: &str) -> Result> { + let text = reqwest::get(URL_VERSION_MANIFEST).await?.error_for_status()?.text().await?; + let manifest: VersionManifest = serde_json::from_str(text.as_str())?; + + let fname = format!("{home}/.remote.json"); + let index: RemoteVersionIndex = match tokio::fs::read_to_string(fname).await { + Ok(s) => serde_json::from_str(s.as_str())?, + Err(e) => { + if e.kind() == ErrorKind::NotFound { + RemoteVersionIndex::default() + } else { + return Err(Box::new(e)); + } + } + }; + + let mut versions = HashMap::new(); + for v in manifest.versions { + versions.insert(v.id.clone(), v); + } + + Ok(RemoteVersionList { + versions, + index + }) + } +} + struct LocalVersionList { versions: BTreeMap } +impl LocalVersionList { + async fn load_version(path: &PathBuf) -> Result> { + serde_json::from_str(tokio::fs::read_to_string(path).await?)?; + } + + async fn load_versions(home: &str, skip: impl Fn(&str) -> bool) -> Result> { + let mut rd = tokio::fs::read_dir(home).await?; + let versions = BTreeMap::new(); + + while let Some(ent) = rd.next_entry().await? { + // when the code is fugly + let path = match ent.file_name().to_str() { + Some(s) => { + if skip(s) { + debug!("Skipping local version {s} because (I assume) it is remotely tracked."); + continue + } + + /* FIXME: once https://github.com/rust-lang/rust/issues/127292 is closed, + * use add_extension to avoid extra heap allocations (they hurt my feelings) */ + let mut path = ent.path(); + + // can't use set_extension since s might contain a . (like 1.8.9) + path.push(format!("{s}.json")); + + + }, + + /* We just ignore directories with names that contain invalid unicode. Unfortunately, the laucher + * will not be supporting such custom versions. Name your version something sensible please. */ + None => { + warn!("Ignoring a local version because its id contains invalid unicode."); + continue + } + }; + + match Self::load_version(path).await { + Ok(v) => { + versions.insert(v.id.clone(), v); + }, + Err(e) => { + // FIXME: just display the filename without to_string_lossy when https://github.com/rust-lang/rust/issues/120048 is closed + warn!("Ignoring local version {}: {e}", ent.file_name().to_string_lossy()); + } + } + } + + Ok(LocalVersionList { versions }) + } +} + struct VersionList { offline: bool, remote: Option, @@ -31,5 +124,24 @@ struct VersionList { } impl VersionList { - + pub async fn online(home: &str) -> Result> { + let remote = RemoteVersionList::new(home).await?; + let local = LocalVersionList::load_versions(home, |s| remote.versions.contains_key(s)).await?; + + Ok(VersionList { + offline: false, + remote: Some(remote), + local + }) + } + + pub async fn offline(home: &str) -> Result> { + let local = LocalVersionList::load_versions(home, |_| false).await?; + + Ok(VersionList { + offline: true, + remote: None, + local + }) + } } diff --git a/src/launcher/constants.rs b/src/launcher/constants.rs new file mode 100644 index 0000000..8a9bd1a --- /dev/null +++ b/src/launcher/constants.rs @@ -0,0 +1 @@ +pub const URL_VERSION_MANIFEST: &str = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json"; -- cgit v1.2.3-70-g09d2