fix: Caching version list

This commit is contained in:
LynithDev 2024-03-23 17:51:26 +01:00
parent ffab61ebf0
commit a75ce01c72
5 changed files with 113 additions and 14 deletions

View file

@ -6,4 +6,7 @@ pub const VERSION: &str = "0.1.0";
pub const USER_AGENT: &str = "OneLauncher/1.0.0 (polyfrost.org)";
pub const CLIENT_ID: &str = "9419b7ee-1448-4d1b-b52a-550d8f36ab56";
// Minecraft
pub const MINECRAFT_VERSIONS_MANIFEST: &str = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json";
// Java Downloading

View file

@ -9,7 +9,7 @@ use uuid::Uuid;
use crate::PolyResult;
use super::{minecraft::MinecraftManifest, vanilla};
use super::{minecraft::{MinecraftManifest, ReleaseType}, vanilla::{self, VanillaImpl}};
#[derive(Debug, Serialize, Deserialize)]
pub struct Manifest {
@ -27,16 +27,33 @@ pub struct Instance {
pub client: ClientType,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Version {
id: String,
url: String,
#[serde(default)]
release_type: ReleaseType,
#[serde(default)]
release_time: chrono::DateTime<chrono::Utc>
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum ClientType {
Vanilla
Vanilla(VanillaImpl)
}
impl ClientType {
pub async fn get_versions(&self, file: Option<&PathBuf>) -> PolyResult<Vec<Version>> {
match self {
ClientType::Vanilla(_) => vanilla::get_versions(file).await
}
}
}
#[async_trait]
pub trait ClientTrait: Send + Sync {
fn from_instance(instance: Instance) -> Self where Self: Sized;
async fn launch(&self) -> PolyResult<()>;
async fn setup(&self) -> PolyResult<()>;
@ -47,6 +64,7 @@ pub trait ClientTrait: Send + Sync {
}
pub struct ClientManager {
handle: AppHandle,
instances: HashMap<Uuid, Instance>,
instances_dir: PathBuf,
@ -63,6 +81,7 @@ impl ClientManager {
let manifests = load_and_serialize::<Manifest>(&manifests_dir)?;
Ok(Self {
handle: handle.clone(),
instances,
instances_dir,
manifests,
@ -101,8 +120,10 @@ impl ClientManager {
group: Option<Uuid>,
client: ClientType
) -> PolyResult<Uuid> {
// TODO: Implement cahcing
let url = String::from("https://piston-meta.mojang.com/v1/packages/d546f1707a3f2b7d034eece5ea2e311eda875787/1.8.9.json");
let version_cache = self.handle.path().app_config_dir()?.join("versions_cache.json");
let versions = client.get_versions(Some(&version_cache)).await?;
let url = &versions.iter().find(|v| v.id == version).ok_or(anyhow!("Version not found"))?.url;
let uuid = Uuid::new_v4();
// Save the manifest

View file

@ -1,16 +1,89 @@
use std::collections::HashMap;
use std::{fs, path::PathBuf};
use anyhow::anyhow;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use crate::{constants::MINECRAFT_VERSIONS_MANIFEST, utils::http, PolyResult};
use super::{client::{ClientTrait, Version}, minecraft::MinecraftManifest};
use crate::{utils::http, PolyResult};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VanillaImpl;
use super::minecraft::MinecraftManifest;
#[async_trait]
impl ClientTrait for VanillaImpl {
async fn launch(&self) -> PolyResult<()> {
Ok(())
}
pub async fn retrieve_versions() -> PolyResult<HashMap<String, String>> {
let map = HashMap::new();
async fn setup(&self) -> PolyResult<()> {
Ok(())
}
Ok(map)
async fn install_game(&self) -> PolyResult<()> {
Ok(())
}
async fn install_libraries(&self) -> PolyResult<String> {
Ok("".to_string())
}
async fn install_natives(&self) -> PolyResult<()> {
Ok(())
}
async fn install_assets(&self) -> PolyResult<()> {
Ok(())
}
}
pub async fn retrieve_version_manifest(url: String) -> PolyResult<MinecraftManifest> {
#[derive(Clone, Serialize, Deserialize)]
struct VersionList {
versions: Vec<Version>
}
#[derive(Serialize, Deserialize)]
struct CachedVersions {
last_updated: i64,
versions: Vec<Version>,
}
pub async fn get_versions(file: Option<&PathBuf>) -> PolyResult<Vec<Version>> {
if let Some(file) = file {
if file.exists() && file.is_file() {
if let Some(versions) = get_cached_versions(file).await? {
return Ok(versions);
}
}
}
let response = http::create_client()?.get(MINECRAFT_VERSIONS_MANIFEST).send().await?;
let response = response.json::<VersionList>().await?;
if let Some(file) = file {
fs::write(file, serde_json::to_string(&CachedVersions {
last_updated: chrono::Utc::now().timestamp(),
versions: response.versions.clone()
})?)?;
}
Ok(response.versions)
}
async fn get_cached_versions(file: &PathBuf) -> PolyResult<Option<Vec<Version>>> {
let cached = serde_json::from_str::<CachedVersions>(&fs::read_to_string(file)?)?;
let head_request = http::create_client()?.head(MINECRAFT_VERSIONS_MANIFEST).send().await?;
let last_updated = head_request.headers().get("Last-Modified").ok_or(anyhow!("Last-Modified header not found"))?;
let last_updated = last_updated.to_str().map_err(|_| anyhow!("Failed to convert Last-Modified header to string"))?;
let last_updated = chrono::DateTime::parse_from_rfc2822(last_updated)?;
if cached.last_updated < last_updated.timestamp() {
Ok(None)
} else {
Ok(Some(cached.versions))
}
}
pub async fn retrieve_version_manifest(url: &str) -> PolyResult<MinecraftManifest> {
let manifest = http::create_client()?.get(url).send().await?.json::<MinecraftManifest>().await?;
Ok(manifest)
}

View file

@ -52,6 +52,9 @@ pub enum PolyError {
/// Wrapper around [`anyhow::Error`] to handle generic errors.
#[error(transparent)]
AnyhowError(#[from] anyhow::Error),
/// Wrapper around [`chrono::ParseError`] to handle date parsing errors.
#[error("failed to parse a date: {0}")]
ChronoError(#[from] chrono::ParseError),
}
/// Alias for a [`Result`] with the error type [`PolyError`].

View file

@ -1,5 +1,4 @@
import { invoke } from '@tauri-apps/api/core';
import * as uuid from 'uuid';
import Button from '../components/base/Button';
function BrowserPage() {