use std::path::PathBuf;
use anyhow::Context;
use nitro_shared::minecraft::{VersionManifest, VersionType};
use nitro_shared::output::{MessageContents, NitroOutput};
use nitro_shared::{UpdateDepth, translate};
use reqwest::Client;
use crate::io::files::create_leading_dirs;
use crate::io::files::paths::Paths;
use crate::io::update::UpdateManager;
use crate::io::{json_from_file, json_to_file};
use crate::net::download::ProgressiveDownload;
use crate::util::versions::MinecraftVersion;
pub async fn get(
requested_version: Option<&MinecraftVersion>,
paths: &Paths,
manager: &UpdateManager,
client: &Client,
o: &mut impl NitroOutput,
) -> anyhow::Result<VersionManifest> {
let manifest = get_contents(requested_version, paths, manager, client, false, o).await;
let manifest = match manifest {
Ok(manifest) => manifest,
Err(err) => {
o.display(MessageContents::Error(
"Failed to obtain version manifest".into(),
));
o.display(MessageContents::Error(format!("{}", err)));
o.display(MessageContents::StartProcess("Redownloading".into()));
get_contents(requested_version, paths, manager, client, true, o)
.await
.context("Failed to download manifest contents")?
}
};
Ok(manifest)
}
pub async fn get_with_output(
requested_version: Option<&MinecraftVersion>,
paths: &Paths,
manager: &UpdateManager,
client: &Client,
o: &mut impl NitroOutput,
) -> anyhow::Result<VersionManifest> {
let show_output = !(manager.get_depth() < UpdateDepth::Full && get_path(paths).exists());
if show_output {
o.start_process();
o.display(MessageContents::StartProcess(
"Obtaining version manifest".into(),
));
}
let manifest = get(requested_version, paths, manager, client, o)
.await
.context("Failed to get version manifest")?;
if show_output {
o.display(MessageContents::Success("Version manifest obtained".into()));
o.end_process();
}
Ok(manifest)
}
async fn get_contents(
requested_version: Option<&MinecraftVersion>,
paths: &Paths,
manager: &UpdateManager,
client: &Client,
force: bool,
o: &mut impl NitroOutput,
) -> anyhow::Result<VersionManifest> {
let path = get_path(paths);
create_leading_dirs(&path)?;
if let Some(requested_version) = requested_version
&& !force
&& manager.update_depth < UpdateDepth::Full
&& path.exists()
{
let contents: VersionManifest =
json_from_file(&path).context("Failed to read manifest contents from file")?;
let version = requested_version.get_version(&contents);
if let Some(version) = version
&& contents
.versions
.iter()
.any(|x| x.id.as_str() == version.as_ref())
{
return Ok(contents);
}
}
let mut download = ProgressiveDownload::bytes(
"https://piston-meta.mojang.com/mc/game/version_manifest_v2.json",
client,
)
.await?;
while !download.is_finished() {
download.poll_download().await?;
o.display(MessageContents::Associated(
Box::new(download.get_progress()),
Box::new(MessageContents::Simple(translate!(
o,
StartDownloadingVersionManifest
))),
));
}
let manifest = download.finish_json()?;
json_to_file(path, &manifest).context("Failed to write manifest to a file")?;
Ok(manifest)
}
fn get_path(paths: &Paths) -> PathBuf {
paths.internal.join("versions/manifest.json")
}
pub fn make_version_list(version_manifest: &VersionManifest) -> Vec<String> {
let mut out = Vec::new();
for entry in &version_manifest.versions {
out.push(entry.id.clone());
}
out.reverse();
out
}
#[derive(Default)]
pub struct VersionManifestAndList {
pub manifest: VersionManifest,
pub list: Vec<String>,
}
impl VersionManifestAndList {
pub fn new(manifest: VersionManifest) -> Self {
let list = make_version_list(&manifest);
Self { manifest, list }
}
pub fn set(&mut self, manifest: VersionManifest) -> anyhow::Result<()> {
self.list = make_version_list(&manifest);
self.manifest = manifest;
Ok(())
}
pub fn get_releases(&self) -> Vec<String> {
self.manifest
.versions
.iter()
.filter_map(|x| {
if x.ty == VersionType::Release {
Some(x.id.clone())
} else {
None
}
})
.collect()
}
}