96 lines
3 KiB
Rust
96 lines
3 KiB
Rust
|
|
use tokio::sync::RwLock;
|
|
|
|
use oneclient_common::paths;
|
|
use oneclient_net::{EtagPolicy, fetch_cached};
|
|
use crate::state::LauncherServices;
|
|
use crate::versions::manifest::{RemoteMigration, VersionMetadata, VersionsManifest};
|
|
use crate::LauncherResult;
|
|
|
|
pub struct VersionsManager {
|
|
manifest: RwLock<VersionsManifest>,
|
|
}
|
|
|
|
impl VersionsManager {
|
|
#[must_use]
|
|
pub fn new() -> Self {
|
|
Self {
|
|
manifest: RwLock::new(VersionsManifest::default()),
|
|
}
|
|
}
|
|
|
|
/// Seeds the manifest from the last synced copy on disk.
|
|
///
|
|
/// Without this the manifest is empty until the network answers, and
|
|
/// everything read off it — a version's art, name, description — waits on a
|
|
/// request that, for a returning user, ends in a 304 and the very same file.
|
|
/// The UI would rather render slightly stale metadata now and pick up the
|
|
/// sync when it lands.
|
|
#[must_use]
|
|
pub async fn from_cache() -> Self {
|
|
let manifest = Self::cached_manifest()
|
|
.await
|
|
.unwrap_or_else(VersionsManifest::default);
|
|
|
|
Self {
|
|
manifest: RwLock::new(manifest),
|
|
}
|
|
}
|
|
|
|
async fn cached_manifest() -> Option<VersionsManifest> {
|
|
let path = paths::caches_dir().ok()?.join("versions.json");
|
|
let bytes = polyio::read(&path).await.ok()?;
|
|
match serde_json::from_slice(&bytes) {
|
|
Ok(manifest) => Some(manifest),
|
|
Err(err) => {
|
|
tracing::warn!("cached versions manifest is unreadable: {err}");
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tracing::instrument(level = "debug", skip(self, services))]
|
|
pub async fn sync(&self, services: &LauncherServices) -> LauncherResult<bool> {
|
|
let Some((manifest, changed)) = Self::fetch_manifest(services).await? else {
|
|
tracing::debug!("skipping versions sync; no remote or cached manifest available");
|
|
return Ok(false);
|
|
};
|
|
*self.manifest.write().await = manifest;
|
|
Ok(changed)
|
|
}
|
|
|
|
pub async fn metadata(&self, meta_url_base: &str) -> Vec<VersionMetadata> {
|
|
self.manifest.read().await.metadata(meta_url_base)
|
|
}
|
|
|
|
pub async fn migrations(&self) -> Vec<RemoteMigration> {
|
|
self.manifest.read().await.migrations.clone()
|
|
}
|
|
|
|
#[tracing::instrument(level = "debug", skip(services))]
|
|
async fn fetch_manifest(
|
|
services: &LauncherServices,
|
|
) -> LauncherResult<Option<(VersionsManifest, bool)>> {
|
|
let manifest_path = paths::caches_dir()?.join("versions.json");
|
|
let url = format!(
|
|
"{}/oneclient/versions/metadata.json",
|
|
services.requester.config().meta_url_base
|
|
);
|
|
|
|
let Some(fetched) =
|
|
fetch_cached(&services.requester, &url, &manifest_path, EtagPolicy::CommitNow).await?
|
|
else {
|
|
return Ok(None);
|
|
};
|
|
|
|
Ok(Some((fetched.json()?, fetched.changed)))
|
|
}
|
|
}
|
|
|
|
impl Default for VersionsManager {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
|