use crate::modded::{Processor, SidedDataEntry}; use crate::{download_file, Error}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; #[cfg(feature = "bincode")] use bincode::{Decode, Encode}; /// The latest version of the format the model structs deserialize to pub const CURRENT_FORMAT_VERSION: usize = 0; #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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", } } } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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 #[cfg_attr(feature = "bincode", bincode(with_serde))] pub time: DateTime, /// The time this version was released #[cfg_attr(feature = "bincode", bincode(with_serde))] pub release_time: DateTime, /// 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 link to the assets index for this version /// This is only available when using the Modrinth mirror pub assets_index_url: Option, #[serde(skip_serializing_if = "Option::is_none")] /// (Modrinth Provided) The SHA1 hash of the assets index for this version /// This is only available when using the Modrinth mirror pub assets_index_sha1: Option, } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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, } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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, } /// The URL to the version manifest pub const VERSION_MANIFEST_URL: &str = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json"; /// Fetches a version manifest from the specified URL. If no URL is specified, the default is used. pub async fn fetch_version_manifest( url: Option<&str>, ) -> Result { Ok(serde_json::from_slice( &download_file(url.unwrap_or(VERSION_MANIFEST_URL), None).await?, )?) } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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, } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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, } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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, } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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, /// 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, } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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, #[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>, } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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, } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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, } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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, #[serde(skip_serializing_if = "Option::is_none")] /// The version of the OS. This is normally a RegEx pub version: Option, #[serde(skip_serializing_if = "Option::is_none")] /// The architecture of the OS pub arch: Option, } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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, #[serde(skip_serializing_if = "Option::is_none")] /// Whether the user is using the demo resolution pub has_demo_resolution: Option, } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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, #[serde(skip_serializing_if = "Option::is_none")] /// The feature rule pub features: Option, } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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>, } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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, } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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, #[serde(skip_serializing_if = "Option::is_none")] /// Rules of the extraction of the file pub extract: Option, /// 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, #[serde(skip_serializing_if = "Option::is_none")] /// Native files that the library relies on pub natives: Option>, #[serde(skip_serializing_if = "Option::is_none")] /// Rules deciding whether the library should be downloaded or not pub rules: Option>, #[serde(skip_serializing_if = "Option::is_none")] /// SHA1 Checksums for validating the library's integrity. Only present for forge libraries pub checksums: Option>, #[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, } #[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, /// Rules of the extraction of the file pub extract: Option, /// The maven name of the library. The format is `groupId:artifactId:version` pub name: Option, /// The URL to the repository where the library can be downloaded pub url: Option, /// Native files that the library relies on pub natives: Option>, /// Rules deciding whether the library should be downloaded or not pub rules: Option>, /// SHA1 Checksums for validating the library's integrity. Only present for forge libraries pub checksums: Option>, /// Whether the library should be included in the classpath at the game's launch pub include_in_classpath: Option, } /// 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 } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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), } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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, /// The container of the argument(s) that should be applied accordingly value: ArgumentValue, }, } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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, } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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>>, /// 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, /// The version ID of the version pub id: String, /// The Java version this version supports pub java_version: Option, /// Libraries that the version depends on pub libraries: Vec, /// 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, /// 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 #[cfg_attr(feature = "bincode", bincode(with_serde))] pub release_time: DateTime, /// The latest time a file in this version was updated #[cfg_attr(feature = "bincode", bincode(with_serde))] pub time: DateTime, #[serde(rename = "type")] /// The type of version pub type_: VersionType, #[serde(skip_serializing_if = "Option::is_none")] /// (Forge-only) pub data: Option>, #[serde(skip_serializing_if = "Option::is_none")] /// (Forge-only) The list of processors to run after downloading the files pub processors: Option>, } /// Fetches detailed information about a version from the manifest pub async fn fetch_version_info( version: &Version, ) -> Result { Ok(serde_json::from_slice( &download_file(&version.url, Some(&version.sha1)).await?, )?) } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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, } #[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[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, } /// Fetches the assets index from the version info pub async fn fetch_assets_index( version: &VersionInfo, ) -> Result { Ok(serde_json::from_slice( &download_file( &version.asset_index.url, Some(&version.asset_index.sha1), ) .await?, )?) }