You've already forked AstralRinth
forked from didirus/AstralRinth
Start monorepo migration
This commit is contained in:
102
packages/daedalus/src/lib.rs
Normal file
102
packages/daedalus/src/lib.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
//! # Daedalus
|
||||
//!
|
||||
//! Daedalus is a library which provides models and methods to fetch metadata about games
|
||||
|
||||
#![warn(missing_docs, unused_import_braces, missing_debug_implementations)]
|
||||
|
||||
/// Models and methods for fetching metadata for Minecraft
|
||||
pub mod minecraft;
|
||||
/// Models and methods for fetching metadata for Minecraft mod loaders
|
||||
pub mod modded;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
/// An error type representing possible errors when fetching metadata
|
||||
pub enum Error {
|
||||
/// Error while parsing input
|
||||
#[error("{0}")]
|
||||
ParseError(String),
|
||||
}
|
||||
|
||||
/// Converts a maven artifact to a path
|
||||
pub fn get_path_from_artifact(artifact: &str) -> Result<String, Error> {
|
||||
let name_items = artifact.split(':').collect::<Vec<&str>>();
|
||||
|
||||
let package = name_items.first().ok_or_else(|| {
|
||||
Error::ParseError(format!(
|
||||
"Unable to find package for library {}",
|
||||
&artifact
|
||||
))
|
||||
})?;
|
||||
let name = name_items.get(1).ok_or_else(|| {
|
||||
Error::ParseError(format!(
|
||||
"Unable to find name for library {}",
|
||||
&artifact
|
||||
))
|
||||
})?;
|
||||
|
||||
if name_items.len() == 3 {
|
||||
let version_ext = name_items
|
||||
.get(2)
|
||||
.ok_or_else(|| {
|
||||
Error::ParseError(format!(
|
||||
"Unable to find version for library {}",
|
||||
&artifact
|
||||
))
|
||||
})?
|
||||
.split('@')
|
||||
.collect::<Vec<&str>>();
|
||||
let version = version_ext.first().ok_or_else(|| {
|
||||
Error::ParseError(format!(
|
||||
"Unable to find version for library {}",
|
||||
&artifact
|
||||
))
|
||||
})?;
|
||||
let ext = version_ext.get(1);
|
||||
|
||||
Ok(format!(
|
||||
"{}/{}/{}/{}-{}.{}",
|
||||
package.replace('.', "/"),
|
||||
name,
|
||||
version,
|
||||
name,
|
||||
version,
|
||||
ext.unwrap_or(&"jar")
|
||||
))
|
||||
} else {
|
||||
let version = name_items.get(2).ok_or_else(|| {
|
||||
Error::ParseError(format!(
|
||||
"Unable to find version for library {}",
|
||||
&artifact
|
||||
))
|
||||
})?;
|
||||
|
||||
let data_ext = name_items
|
||||
.get(3)
|
||||
.ok_or_else(|| {
|
||||
Error::ParseError(format!(
|
||||
"Unable to find data for library {}",
|
||||
&artifact
|
||||
))
|
||||
})?
|
||||
.split('@')
|
||||
.collect::<Vec<&str>>();
|
||||
let data = data_ext.first().ok_or_else(|| {
|
||||
Error::ParseError(format!(
|
||||
"Unable to find data for library {}",
|
||||
&artifact
|
||||
))
|
||||
})?;
|
||||
let ext = data_ext.get(1);
|
||||
|
||||
Ok(format!(
|
||||
"{}/{}/{}/{}-{}-{}.{}",
|
||||
package.replace('.', "/"),
|
||||
name,
|
||||
version,
|
||||
name,
|
||||
version,
|
||||
data,
|
||||
ext.unwrap_or(&"jar")
|
||||
))
|
||||
}
|
||||
}
|
||||
461
packages/daedalus/src/minecraft.rs
Normal file
461
packages/daedalus/src/minecraft.rs
Normal file
@@ -0,0 +1,461 @@
|
||||
use crate::modded::{Processor, SidedDataEntry};
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// The latest version of the format the model structs deserialize to
|
||||
pub const CURRENT_FORMAT_VERSION: usize = 0;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
/// The version type
|
||||
pub enum VersionType {
|
||||
/// A major version, which is stable for all players to use
|
||||
Release,
|
||||
/// An experimental version, which is unstable and used for feature previews and beta testing
|
||||
Snapshot,
|
||||
/// The oldest versions before the game was released
|
||||
OldAlpha,
|
||||
/// Early versions of the game
|
||||
OldBeta,
|
||||
}
|
||||
|
||||
impl VersionType {
|
||||
/// Converts the version type to a string
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
VersionType::Release => "release",
|
||||
VersionType::Snapshot => "snapshot",
|
||||
VersionType::OldAlpha => "old_alpha",
|
||||
VersionType::OldBeta => "old_beta",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
/// A game version of Minecraft
|
||||
pub struct Version {
|
||||
/// A unique identifier of the version
|
||||
pub id: String,
|
||||
#[serde(rename = "type")]
|
||||
/// The release type of the version
|
||||
pub type_: VersionType,
|
||||
/// A link to additional information about the version
|
||||
pub url: String,
|
||||
/// The latest time a file in this version was updated
|
||||
pub time: DateTime<Utc>,
|
||||
/// The time this version was released
|
||||
pub release_time: DateTime<Utc>,
|
||||
/// The SHA1 hash of the additional information about the version
|
||||
pub sha1: String,
|
||||
/// Whether the version supports the latest player safety features
|
||||
pub compliance_level: u32,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// (Modrinth Provided) The SHA1 hash of the original unmodified Minecraft versions JSON
|
||||
pub original_sha1: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
/// The latest snapshot and release of the game
|
||||
pub struct LatestVersion {
|
||||
/// The version id of the latest release
|
||||
pub release: String,
|
||||
/// The version id of the latest snapshot
|
||||
pub snapshot: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
/// Data of all game versions of Minecraft
|
||||
pub struct VersionManifest {
|
||||
/// A struct containing the latest snapshot and release of the game
|
||||
pub latest: LatestVersion,
|
||||
/// A list of game versions of Minecraft
|
||||
pub versions: Vec<Version>,
|
||||
}
|
||||
|
||||
/// The URL to the version manifest
|
||||
pub const VERSION_MANIFEST_URL: &str =
|
||||
"https://piston-meta.mojang.com/mc/game/version_manifest_v2.json";
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
/// Information about the assets of the game
|
||||
pub struct AssetIndex {
|
||||
/// The game version ID the assets are for
|
||||
pub id: String,
|
||||
/// The SHA1 hash of the assets index
|
||||
pub sha1: String,
|
||||
/// The size of the assets index
|
||||
pub size: u32,
|
||||
/// The size of the game version's assets
|
||||
pub total_size: u32,
|
||||
/// A URL to a file which contains information about the version's assets
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Hash)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
/// The type of download
|
||||
pub enum DownloadType {
|
||||
/// The download is for the game client
|
||||
Client,
|
||||
/// The download is mappings for the game
|
||||
ClientMappings,
|
||||
/// The download is for the game server
|
||||
Server,
|
||||
/// The download is mappings for the game server
|
||||
ServerMappings,
|
||||
/// The download is for the windows server
|
||||
WindowsServer,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
/// Download information of a file
|
||||
pub struct Download {
|
||||
/// The SHA1 hash of the file
|
||||
pub sha1: String,
|
||||
/// The size of the file
|
||||
pub size: u32,
|
||||
/// The URL where the file can be downloaded
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
/// Download information of a library
|
||||
pub struct LibraryDownload {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// The path that the library should be saved to
|
||||
pub path: Option<String>,
|
||||
/// The SHA1 hash of the library
|
||||
pub sha1: String,
|
||||
/// The size of the library
|
||||
pub size: u32,
|
||||
/// The URL where the library can be downloaded
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
/// A list of files that should be downloaded for libraries
|
||||
pub struct LibraryDownloads {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// The primary library artifact
|
||||
pub artifact: Option<LibraryDownload>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Conditional files that may be needed to be downloaded alongside the library
|
||||
/// The HashMap key specifies a classifier as additional information for downloading files
|
||||
pub classifiers: Option<HashMap<String, LibraryDownload>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
/// The action a rule can follow
|
||||
pub enum RuleAction {
|
||||
/// The rule's status allows something to be done
|
||||
Allow,
|
||||
/// The rule's status disallows something to be done
|
||||
Disallow,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Hash, Clone)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
/// An enum representing the different types of operating systems
|
||||
pub enum Os {
|
||||
/// MacOS (x86)
|
||||
Osx,
|
||||
/// M1-Based Macs
|
||||
OsxArm64,
|
||||
/// Windows (x86)
|
||||
Windows,
|
||||
/// Windows ARM
|
||||
WindowsArm64,
|
||||
/// Linux (x86) and its derivatives
|
||||
Linux,
|
||||
/// Linux ARM 64
|
||||
LinuxArm64,
|
||||
/// Linux ARM 32
|
||||
LinuxArm32,
|
||||
/// The OS is unknown
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
/// A rule which depends on what OS the user is on
|
||||
pub struct OsRule {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// The name of the OS
|
||||
pub name: Option<Os>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// The version of the OS. This is normally a RegEx
|
||||
pub version: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// The architecture of the OS
|
||||
pub arch: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
/// A rule which depends on the toggled features of the launcher
|
||||
pub struct FeatureRule {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Whether the user is in demo mode
|
||||
pub is_demo_user: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Whether the user is using a custom resolution
|
||||
pub has_custom_resolution: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Whether the launcher has quick plays support
|
||||
pub has_quick_plays_support: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Whether the instance is being launched to a single-player world
|
||||
pub is_quick_play_singleplayer: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Whether the instance is being launched to a multi-player world
|
||||
pub is_quick_play_multiplayer: Option<bool>,
|
||||
/// Whether the instance is being launched to a realms world
|
||||
pub is_quick_play_realms: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
/// A rule deciding whether a file is downloaded, an argument is used, etc.
|
||||
pub struct Rule {
|
||||
/// The action the rule takes
|
||||
pub action: RuleAction,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// The OS rule
|
||||
pub os: Option<OsRule>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// The feature rule
|
||||
pub features: Option<FeatureRule>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
/// Information delegating the extraction of the library
|
||||
pub struct LibraryExtract {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Files/Folders to be excluded from the extraction of the library
|
||||
pub exclude: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
/// Information about the java version the game needs
|
||||
pub struct JavaVersion {
|
||||
/// The component needed for the Java installation
|
||||
pub component: String,
|
||||
/// The major Java version number
|
||||
pub major_version: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
/// A library which the game relies on to run
|
||||
pub struct Library {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// The files the library has
|
||||
pub downloads: Option<LibraryDownloads>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Rules of the extraction of the file
|
||||
pub extract: Option<LibraryExtract>,
|
||||
/// The maven name of the library. The format is `groupId:artifactId:version`
|
||||
pub name: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// The URL to the repository where the library can be downloaded
|
||||
pub url: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Native files that the library relies on
|
||||
pub natives: Option<HashMap<Os, String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Rules deciding whether the library should be downloaded or not
|
||||
pub rules: Option<Vec<Rule>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// SHA1 Checksums for validating the library's integrity. Only present for forge libraries
|
||||
pub checksums: Option<Vec<String>>,
|
||||
#[serde(default = "default_include_in_classpath")]
|
||||
/// Whether the library should be included in the classpath at the game's launch
|
||||
pub include_in_classpath: bool,
|
||||
#[serde(default = "default_downloadable")]
|
||||
/// Whether the library should be downloaded
|
||||
pub downloadable: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
/// A partial library which should be merged with a full library
|
||||
pub struct PartialLibrary {
|
||||
/// The files the library has
|
||||
pub downloads: Option<LibraryDownloads>,
|
||||
/// Rules of the extraction of the file
|
||||
pub extract: Option<LibraryExtract>,
|
||||
/// The maven name of the library. The format is `groupId:artifactId:version`
|
||||
pub name: Option<String>,
|
||||
/// The URL to the repository where the library can be downloaded
|
||||
pub url: Option<String>,
|
||||
/// Native files that the library relies on
|
||||
pub natives: Option<HashMap<Os, String>>,
|
||||
/// Rules deciding whether the library should be downloaded or not
|
||||
pub rules: Option<Vec<Rule>>,
|
||||
/// SHA1 Checksums for validating the library's integrity. Only present for forge libraries
|
||||
pub checksums: Option<Vec<String>>,
|
||||
/// Whether the library should be included in the classpath at the game's launch
|
||||
pub include_in_classpath: Option<bool>,
|
||||
}
|
||||
|
||||
/// Merges a partial library to make a complete library
|
||||
pub fn merge_partial_library(
|
||||
partial: PartialLibrary,
|
||||
mut merge: Library,
|
||||
) -> Library {
|
||||
if let Some(downloads) = partial.downloads {
|
||||
if let Some(merge_downloads) = &mut merge.downloads {
|
||||
if let Some(artifact) = downloads.artifact {
|
||||
merge_downloads.artifact = Some(artifact);
|
||||
}
|
||||
if let Some(classifiers) = downloads.classifiers {
|
||||
if let Some(merge_classifiers) =
|
||||
&mut merge_downloads.classifiers
|
||||
{
|
||||
for classifier in classifiers {
|
||||
merge_classifiers.insert(classifier.0, classifier.1);
|
||||
}
|
||||
} else {
|
||||
merge_downloads.classifiers = Some(classifiers);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
merge.downloads = Some(downloads)
|
||||
}
|
||||
}
|
||||
if let Some(extract) = partial.extract {
|
||||
merge.extract = Some(extract)
|
||||
}
|
||||
if let Some(name) = partial.name {
|
||||
merge.name = name
|
||||
}
|
||||
if let Some(url) = partial.url {
|
||||
merge.url = Some(url)
|
||||
}
|
||||
if let Some(natives) = partial.natives {
|
||||
if let Some(merge_natives) = &mut merge.natives {
|
||||
for native in natives {
|
||||
merge_natives.insert(native.0, native.1);
|
||||
}
|
||||
} else {
|
||||
merge.natives = Some(natives);
|
||||
}
|
||||
}
|
||||
if let Some(rules) = partial.rules {
|
||||
if let Some(merge_rules) = &mut merge.rules {
|
||||
for rule in rules {
|
||||
merge_rules.push(rule);
|
||||
}
|
||||
} else {
|
||||
merge.rules = Some(rules)
|
||||
}
|
||||
}
|
||||
if let Some(checksums) = partial.checksums {
|
||||
merge.checksums = Some(checksums)
|
||||
}
|
||||
if let Some(include_in_classpath) = partial.include_in_classpath {
|
||||
merge.include_in_classpath = include_in_classpath
|
||||
}
|
||||
|
||||
merge
|
||||
}
|
||||
|
||||
fn default_include_in_classpath() -> bool {
|
||||
true
|
||||
}
|
||||
fn default_downloadable() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(untagged)]
|
||||
/// A container for an argument or multiple arguments
|
||||
pub enum ArgumentValue {
|
||||
/// The container has one argument
|
||||
Single(String),
|
||||
/// The container has multiple arguments
|
||||
Many(Vec<String>),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(untagged)]
|
||||
/// A command line argument passed to a program
|
||||
pub enum Argument {
|
||||
/// An argument which is applied no matter what
|
||||
Normal(String),
|
||||
/// An argument which is only applied if certain conditions are met
|
||||
Ruled {
|
||||
/// The rules deciding whether the argument(s) is used or not
|
||||
rules: Vec<Rule>,
|
||||
/// The container of the argument(s) that should be applied accordingly
|
||||
value: ArgumentValue,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Hash, Clone, Copy)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
/// The type of argument
|
||||
pub enum ArgumentType {
|
||||
/// The argument is passed to the game
|
||||
Game,
|
||||
/// The argument is passed to the JVM
|
||||
Jvm,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
/// Information about a version
|
||||
pub struct VersionInfo {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Arguments passed to the game or JVM
|
||||
pub arguments: Option<HashMap<ArgumentType, Vec<Argument>>>,
|
||||
/// Assets for the game
|
||||
pub asset_index: AssetIndex,
|
||||
/// The version ID of the assets
|
||||
pub assets: String,
|
||||
/// Game downloads of the version
|
||||
pub downloads: HashMap<DownloadType, Download>,
|
||||
/// The version ID of the version
|
||||
pub id: String,
|
||||
|
||||
/// The Java version this version supports
|
||||
pub java_version: Option<JavaVersion>,
|
||||
/// Libraries that the version depends on
|
||||
pub libraries: Vec<Library>,
|
||||
/// The classpath to the main class to launch the game
|
||||
pub main_class: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// (Legacy) Arguments passed to the game
|
||||
pub minecraft_arguments: Option<String>,
|
||||
/// The minimum version of the Minecraft Launcher that can run this version of the game
|
||||
pub minimum_launcher_version: u32,
|
||||
/// The time that the version was released
|
||||
pub release_time: DateTime<Utc>,
|
||||
/// The latest time a file in this version was updated
|
||||
pub time: DateTime<Utc>,
|
||||
#[serde(rename = "type")]
|
||||
/// The type of version
|
||||
pub type_: VersionType,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// (Forge-only)
|
||||
pub data: Option<HashMap<String, SidedDataEntry>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// (Forge-only) The list of processors to run after downloading the files
|
||||
pub processors: Option<Vec<Processor>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
/// An asset of the game
|
||||
pub struct Asset {
|
||||
/// The SHA1 hash of the asset file
|
||||
pub hash: String,
|
||||
/// The size of the asset file
|
||||
pub size: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
/// An index containing all assets the game needs
|
||||
pub struct AssetsIndex {
|
||||
/// A hashmap containing the filename (key) and asset (value)
|
||||
pub objects: HashMap<String, Asset>,
|
||||
}
|
||||
208
packages/daedalus/src/modded.rs
Normal file
208
packages/daedalus/src/modded.rs
Normal file
@@ -0,0 +1,208 @@
|
||||
use crate::minecraft::{
|
||||
Argument, ArgumentType, Library, VersionInfo, VersionType,
|
||||
};
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// The latest version of the format the fabric model structs deserialize to
|
||||
pub const CURRENT_FABRIC_FORMAT_VERSION: usize = 0;
|
||||
/// The latest version of the format the fabric model structs deserialize to
|
||||
pub const CURRENT_FORGE_FORMAT_VERSION: usize = 0;
|
||||
/// The latest version of the format the quilt model structs deserialize to
|
||||
pub const CURRENT_QUILT_FORMAT_VERSION: usize = 0;
|
||||
/// The latest version of the format the neoforge model structs deserialize to
|
||||
pub const CURRENT_NEOFORGE_FORMAT_VERSION: usize = 0;
|
||||
|
||||
/// The dummy replace string library names, inheritsFrom, and version names should be replaced with
|
||||
pub const DUMMY_REPLACE_STRING: &str = "${modrinth.gameVersion}";
|
||||
|
||||
/// A data variable entry that depends on the side of the installation
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct SidedDataEntry {
|
||||
/// The value on the client
|
||||
pub client: String,
|
||||
/// The value on the server
|
||||
pub server: String,
|
||||
}
|
||||
|
||||
fn deserialize_date<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
|
||||
serde_json::from_str::<DateTime<Utc>>(&format!("\"{s}\""))
|
||||
.or_else(|_| Utc.datetime_from_str(&s, "%Y-%m-%dT%H:%M:%S%.9f"))
|
||||
.map_err(serde::de::Error::custom)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
/// A partial version returned by fabric meta
|
||||
pub struct PartialVersionInfo {
|
||||
/// The version ID of the version
|
||||
pub id: String,
|
||||
/// The version ID this partial version inherits from
|
||||
pub inherits_from: String,
|
||||
/// The time that the version was released
|
||||
#[serde(deserialize_with = "deserialize_date")]
|
||||
pub release_time: DateTime<Utc>,
|
||||
/// The latest time a file in this version was updated
|
||||
#[serde(deserialize_with = "deserialize_date")]
|
||||
pub time: DateTime<Utc>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// The classpath to the main class to launch the game
|
||||
pub main_class: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// (Legacy) Arguments passed to the game
|
||||
pub minecraft_arguments: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Arguments passed to the game or JVM
|
||||
pub arguments: Option<HashMap<ArgumentType, Vec<Argument>>>,
|
||||
/// Libraries that the version depends on
|
||||
pub libraries: Vec<Library>,
|
||||
#[serde(rename = "type")]
|
||||
/// The type of version
|
||||
pub type_: VersionType,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// (Forge-only)
|
||||
pub data: Option<HashMap<String, SidedDataEntry>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// (Forge-only) The list of processors to run after downloading the files
|
||||
pub processors: Option<Vec<Processor>>,
|
||||
}
|
||||
|
||||
/// A processor to be ran after downloading the files
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Processor {
|
||||
/// Maven coordinates for the JAR library of this processor.
|
||||
pub jar: String,
|
||||
/// Maven coordinates for all the libraries that must be included in classpath when running this processor.
|
||||
pub classpath: Vec<String>,
|
||||
/// Arguments for this processor.
|
||||
pub args: Vec<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Represents a map of outputs. Keys and values can be data values
|
||||
pub outputs: Option<HashMap<String, String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Which sides this processor shall be ran on.
|
||||
/// Valid values: client, server, extract
|
||||
pub sides: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
/// Merges a partial version into a complete one
|
||||
pub fn merge_partial_version(
|
||||
partial: PartialVersionInfo,
|
||||
merge: VersionInfo,
|
||||
) -> VersionInfo {
|
||||
let merge_id = merge.id.clone();
|
||||
|
||||
let mut libraries = vec![];
|
||||
|
||||
// We skip duplicate libraries that exist already in the partial version
|
||||
for mut lib in merge.libraries {
|
||||
let lib_artifact = lib.name.rsplit_once(':').map(|x| x.0);
|
||||
|
||||
if let Some(lib_artifact) = lib_artifact {
|
||||
if !partial.libraries.iter().any(|x| {
|
||||
let target_artifact = x.name.rsplit_once(':').map(|x| x.0);
|
||||
|
||||
target_artifact == Some(lib_artifact) && x.include_in_classpath
|
||||
}) {
|
||||
libraries.push(lib);
|
||||
} else {
|
||||
lib.include_in_classpath = false;
|
||||
}
|
||||
} else {
|
||||
libraries.push(lib);
|
||||
}
|
||||
}
|
||||
|
||||
VersionInfo {
|
||||
arguments: if let Some(partial_args) = partial.arguments {
|
||||
if let Some(merge_args) = merge.arguments {
|
||||
let mut new_map = HashMap::new();
|
||||
|
||||
fn add_keys(
|
||||
new_map: &mut HashMap<ArgumentType, Vec<Argument>>,
|
||||
args: HashMap<ArgumentType, Vec<Argument>>,
|
||||
) {
|
||||
for (type_, arguments) in args {
|
||||
for arg in arguments {
|
||||
if let Some(vec) = new_map.get_mut(&type_) {
|
||||
vec.push(arg);
|
||||
} else {
|
||||
new_map.insert(type_, vec![arg]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_keys(&mut new_map, merge_args);
|
||||
add_keys(&mut new_map, partial_args);
|
||||
|
||||
Some(new_map)
|
||||
} else {
|
||||
Some(partial_args)
|
||||
}
|
||||
} else {
|
||||
merge.arguments
|
||||
},
|
||||
asset_index: merge.asset_index,
|
||||
assets: merge.assets,
|
||||
downloads: merge.downloads,
|
||||
id: partial.id.replace(DUMMY_REPLACE_STRING, &merge_id),
|
||||
java_version: merge.java_version,
|
||||
libraries: libraries.into_iter()
|
||||
.chain(partial.libraries)
|
||||
.map(|mut x| {
|
||||
x.name = x.name.replace(DUMMY_REPLACE_STRING, &merge_id);
|
||||
|
||||
x
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
main_class: if let Some(main_class) = partial.main_class {
|
||||
main_class
|
||||
} else {
|
||||
merge.main_class
|
||||
},
|
||||
minecraft_arguments: partial.minecraft_arguments,
|
||||
minimum_launcher_version: merge.minimum_launcher_version,
|
||||
release_time: partial.release_time,
|
||||
time: partial.time,
|
||||
type_: partial.type_,
|
||||
data: partial.data,
|
||||
processors: partial.processors,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
/// A manifest containing information about a mod loader's versions
|
||||
pub struct Manifest {
|
||||
/// The game versions the mod loader supports
|
||||
pub game_versions: Vec<Version>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
/// A game version of Minecraft
|
||||
pub struct Version {
|
||||
/// The minecraft version ID
|
||||
pub id: String,
|
||||
/// Whether the release is stable or not
|
||||
pub stable: bool,
|
||||
/// A map that contains loader versions for the game version
|
||||
pub loaders: Vec<LoaderVersion>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
/// A version of a Minecraft mod loader
|
||||
pub struct LoaderVersion {
|
||||
/// The version ID of the loader
|
||||
pub id: String,
|
||||
/// The URL of the version's manifest
|
||||
pub url: String,
|
||||
/// Whether the loader is stable or not
|
||||
pub stable: bool,
|
||||
}
|
||||
Reference in New Issue
Block a user