Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions helixlauncher-meta/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub enum OsName {
Windows,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)]
pub struct ComponentDependency {
pub id: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
Expand Down Expand Up @@ -131,20 +131,28 @@ pub enum MinecraftArgument {
},
}

#[derive(Serialize, Deserialize, Debug, Default)]
pub struct Dependencies {
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub requires: Vec<ComponentDependency>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub conflicts: Vec<ComponentDependency>,
pub optional: Vec<ComponentDependency>,
}

#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct Component {
pub format_version: u32,
pub id: String,
pub version: String,
pub dependencies: Dependencies,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub requires: Vec<ComponentDependency>,
pub provides: Vec<ComponentDependency>,
#[serde(skip_serializing_if = "BTreeSet::is_empty", default)]
pub traits: BTreeSet<Trait>,
pub assets: Option<Assets>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub conflicts: Vec<ComponentDependency>,
pub downloads: Vec<Download>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub jarmods: Vec<GradleSpecifier>,
Expand Down
18 changes: 18 additions & 0 deletions helixlauncher-meta/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,24 @@ impl Display for GradleSpecifier {
}
}

impl GradleSpecifier {
pub fn to_url(&self, base_repo: &str) -> String {
format!(
"{}{}/{}/{}/{}-{}{}.{}",
base_repo,
self.group.replace(".", "/"),
self.artifact,
self.version,
self.artifact,
self.version,
self.classifier
.as_ref()
.map_or("".to_string(), |it| "-".to_string() + &it),
self.extension
)
}
}

cfg_if::cfg_if! {
if #[cfg(windows)] {
pub const CURRENT_OS: component::OsName = component::OsName::Windows;
Expand Down
14 changes: 9 additions & 5 deletions src/forge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,17 @@ fn process_version(file: &fs::DirEntry, out_base: &Path) -> Result<helix::compon
format_version: 1,
id: "net.minecraftforge.forge".into(),
version: forge_version.into(),
requires: vec![helix::component::ComponentDependency {
id: "net.minecraft".into(),
version: Some(minecraft_version),
}],
dependencies: helix::component::Dependencies {
requires: vec![helix::component::ComponentDependency {
id: "net.minecraft".into(),
version: Some(minecraft_version),
}],
conflicts: vec![],
optional: vec![],
},
traits: BTreeSet::new(),
assets: None,
conflicts: vec![],
provides: vec![],
downloads,
jarmods: vec![],
game_jar: None,
Expand Down
115 changes: 115 additions & 0 deletions src/intermediary.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use std::{collections::BTreeSet, fs, path::Path, str::FromStr};

use anyhow::Result;
use chrono::DateTime;
use helixlauncher_meta::{
component::{
Component, ComponentDependency, ConditionalClasspathEntry, Dependencies, Download,
},
index::Index,
util::GradleSpecifier,
};
use reqwest::Client;
use serde::Deserialize;

use crate::{get_hash, get_size};

pub async fn process(client: &Client) -> Result<()> {
let out_base = Path::new("out/net.fabricmc.intermediary");
fs::create_dir_all(out_base)?;

let mut index: Index = vec![];

for version in get_versions(client).await? {
let library = crate::Library {
name: GradleSpecifier::from_str(&format!("net.fabricmc:intermediary:{version}"))
.unwrap(),
url: "https://maven.fabricmc.net/".into(),
};
let downloads = vec![Download {
name: library.name.clone(),
url: library.name.to_url(&library.url),
hash: get_hash(client, &library).await?,
size: get_size(client, &library).await?.try_into().unwrap(),
}];

let release_time = DateTime::parse_from_rfc2822(
// TODO: This does one more request than necessary, should get_size or get_hash be merged into this?
client
.head(library.name.to_url(&library.url))
.header("User-Agent", "helixlauncher-meta")
.send()
.await?
.headers()
.get("last-modified")
.expect("Cannot handle servers returning no last-modified")
.to_str()?,
)
.expect(&format!(
"Error parsing last-modified header of {}",
library.name.to_url(&library.url)
))
.into();

let classpath = vec![ConditionalClasspathEntry::All(library.name)];

let component = Component {
format_version: 1,
assets: None,
dependencies: Dependencies {
requires: vec![ComponentDependency {
id: "net.minecraft".into(),
version: Some(version.clone()),
}],
conflicts: vec![],
optional: vec![],
},
provides: vec![],
id: "net.fabricmc.intermediary".into(),
jarmods: vec![],
natives: vec![],
release_time,
version,
traits: BTreeSet::new(),
game_jar: None,
main_class: None,
game_arguments: vec![],
classpath,
downloads,
};

fs::write(
out_base.join(format!("{}.json", component.version)),
serde_json::to_string_pretty(&component)?,
)?;

index.push(component.into());
}

index.sort_by(|x, y| y.release_time.cmp(&x.release_time));

fs::write(
out_base.join("index.json"),
serde_json::to_string_pretty(&index)?,
)?;

Ok(())
}

async fn get_versions(client: &Client) -> Result<Vec<String>> {
let response: Vec<IntermediaryVersionData> = client
.get("https://meta.fabricmc.net/v2/versions/intermediary")
.header("User-Agent", "helixlauncher-meta")
.send()
.await?
.json()
.await?;
Ok(response.into_iter().map(|v| v.version).collect())
}

#[derive(Deserialize)]
struct IntermediaryVersionData {
maven: GradleSpecifier,
version: String,
stable: bool,
}
42 changes: 42 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@
#![deny(rust_2018_idioms)]

use anyhow::Result;
use helixlauncher_meta::{component::Hash, util::GradleSpecifier};
use reqwest::Client;
use serde::Deserialize;

mod forge;
mod intermediary;
mod mojang;
mod quilt;

#[tokio::main]
async fn main() -> Result<()> {
Expand All @@ -18,5 +23,42 @@ async fn main() -> Result<()> {

mojang::process()?;

// forge::process()?;

quilt::process(&client).await?;

intermediary::process(&client).await?;

Ok(())
}

pub(crate) async fn get_hash(client: &Client, coord: &Library) -> Result<Hash> {
Ok(Hash::SHA256(
client
.get(coord.name.to_url(&coord.url) + ".sha256")
.header("User-Agent", "helixlauncher-meta")
.send()
.await?
.text()
.await?,
))
}

pub(crate) async fn get_size(client: &Client, coord: &Library) -> Result<u64> {
Ok(client
.head(coord.name.to_url(&coord.url))
.header("User-Agent", "helixlauncher-meta")
.send()
.await?
.headers()
.get("content-length")
.expect("Cannot handle servers returning no content length")
.to_str()?
.parse()?)
}

#[derive(Deserialize, Debug)]
struct Library {
name: GradleSpecifier,
url: String,
}
6 changes: 3 additions & 3 deletions src/mojang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use anyhow::{bail, ensure, Context, Result};
use chrono::{DateTime, Utc};
use data_encoding::HEXLOWER;
use futures::{StreamExt, TryStreamExt};
use helix::component::{ConditionFeature, MinecraftArgument};
use helix::component::{ConditionFeature, MinecraftArgument, Dependencies};
use indexmap::{IndexMap, IndexSet};
use lazy_static::lazy_static;
use maven_version::Maven3ArtifactVersion;
Expand Down Expand Up @@ -657,8 +657,8 @@ pub fn process_version(
traits,
assets: version.asset_index.map(|a| a.into()),
version: version.id.to_owned(),
requires: vec![], // TODO: lwjgl 2 (deal with that later)
conflicts: vec![],
dependencies: Dependencies::default(), // TODO: lwjgl 2 (deal with that later)
provides: vec![],
downloads: downloads.into_values().collect(),
classpath: classpath.into_iter().collect(),
natives: natives.into_iter().collect(),
Expand Down
Loading