use crate::modded::{Processor, SidedDataEntry};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub const CURRENT_FORMAT_VERSION: usize = 0;
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub enum VersionType {
Release,
Snapshot,
OldAlpha,
OldBeta,
}
impl VersionType {
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")]
pub struct Version {
pub id: String,
#[serde(rename = "type")]
pub type_: VersionType,
pub url: String,
pub time: DateTime<Utc>,
pub release_time: DateTime<Utc>,
pub sha1: String,
pub compliance_level: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub original_sha1: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct LatestVersion {
pub release: String,
pub snapshot: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct VersionManifest {
pub latest: LatestVersion,
pub versions: Vec<Version>,
}
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")]
pub struct AssetIndex {
pub id: String,
pub sha1: String,
pub size: u32,
pub total_size: u32,
pub url: String,
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Hash)]
#[serde(rename_all = "snake_case")]
pub enum DownloadType {
Client,
ClientMappings,
Server,
ServerMappings,
WindowsServer,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Download {
pub sha1: String,
pub size: u32,
pub url: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct LibraryDownload {
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
pub sha1: String,
pub size: u32,
pub url: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct LibraryDownloads {
#[serde(skip_serializing_if = "Option::is_none")]
pub artifact: Option<LibraryDownload>,
#[serde(skip_serializing_if = "Option::is_none")]
pub classifiers: Option<HashMap<String, LibraryDownload>>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub enum RuleAction {
Allow,
Disallow,
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Hash, Clone)]
#[serde(rename_all = "kebab-case")]
pub enum Os {
Osx,
OsxArm64,
Windows,
WindowsArm64,
Linux,
LinuxArm64,
LinuxArm32,
Unknown,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct OsRule {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<Os>,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub arch: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct FeatureRule {
#[serde(skip_serializing_if = "Option::is_none")]
pub is_demo_user: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub has_custom_resolution: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub has_quick_plays_support: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_quick_play_singleplayer: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_quick_play_multiplayer: Option<bool>,
pub is_quick_play_realms: Option<bool>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Rule {
pub action: RuleAction,
#[serde(skip_serializing_if = "Option::is_none")]
pub os: Option<OsRule>,
#[serde(skip_serializing_if = "Option::is_none")]
pub features: Option<FeatureRule>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct LibraryExtract {
#[serde(skip_serializing_if = "Option::is_none")]
pub exclude: Option<Vec<String>>,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct JavaVersion {
pub component: String,
pub major_version: u32,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Library {
#[serde(skip_serializing_if = "Option::is_none")]
pub downloads: Option<LibraryDownloads>,
#[serde(skip_serializing_if = "Option::is_none")]
pub extract: Option<LibraryExtract>,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub natives: Option<HashMap<Os, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rules: Option<Vec<Rule>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub checksums: Option<Vec<String>>,
#[serde(default = "default_include_in_classpath")]
pub include_in_classpath: bool,
#[serde(default = "default_downloadable")]
pub downloadable: bool,
}
#[derive(Deserialize, Debug, Clone)]
pub struct PartialLibrary {
pub downloads: Option<LibraryDownloads>,
pub extract: Option<LibraryExtract>,
pub name: Option<String>,
pub url: Option<String>,
pub natives: Option<HashMap<Os, String>>,
pub rules: Option<Vec<Rule>>,
pub checksums: Option<Vec<String>>,
pub include_in_classpath: Option<bool>,
}
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)]
pub enum ArgumentValue {
Single(String),
Many(Vec<String>),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
pub enum Argument {
Normal(String),
Ruled {
rules: Vec<Rule>,
value: ArgumentValue,
},
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Hash, Clone, Copy)]
#[serde(rename_all = "snake_case")]
pub enum ArgumentType {
Game,
Jvm,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct VersionInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub arguments: Option<HashMap<ArgumentType, Vec<Argument>>>,
pub asset_index: AssetIndex,
pub assets: String,
pub downloads: HashMap<DownloadType, Download>,
pub id: String,
pub java_version: Option<JavaVersion>,
pub libraries: Vec<Library>,
pub main_class: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub minecraft_arguments: Option<String>,
pub minimum_launcher_version: u32,
pub release_time: DateTime<Utc>,
pub time: DateTime<Utc>,
#[serde(rename = "type")]
pub type_: VersionType,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<HashMap<String, SidedDataEntry>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub processors: Option<Vec<Processor>>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Asset {
pub hash: String,
pub size: u32,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct AssetsIndex {
pub objects: HashMap<String, Asset>,
}