From fc076c2e54f3975f6787d171d95f7a1ffbe0be9a Mon Sep 17 00:00:00 2001 From: Daniel Hutzley Date: Sun, 5 Dec 2021 12:20:59 -0800 Subject: [PATCH] Add modpack creation and compilation --- Cargo.lock | 142 ++++++++++++++++++++++++++++++++++++ theseus/Cargo.toml | 1 + theseus/src/launcher/mod.rs | 4 +- theseus/src/modpack/mod.rs | 70 +++++++++++++++++- theseus/src/modpack/pack.rs | 12 +-- 5 files changed, 219 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87e05e039..d6aaabb93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,12 +75,39 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + [[package]] name = "bumpalo" version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + [[package]] name = "byteorder" version = "1.4.3" @@ -181,6 +208,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array", +] + [[package]] name = "encoding_rs" version = "0.8.29" @@ -190,6 +226,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + [[package]] name = "flate2" version = "1.0.22" @@ -328,6 +370,15 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + [[package]] name = "getrandom" version = "0.2.3" @@ -504,6 +555,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -534,6 +596,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "matches" version = "0.1.9" @@ -646,6 +714,12 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + [[package]] name = "openssl" version = "0.10.38" @@ -716,6 +790,49 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +dependencies = [ + "maplit", + "pest", + "sha-1", +] + [[package]] name = "pin-project-lite" version = "0.2.7" @@ -956,6 +1073,18 @@ dependencies = [ "serde", ] +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer", + "digest", + "fake-simd", + "opaque-debug", +] + [[package]] name = "sha1" version = "0.6.0" @@ -1039,6 +1168,7 @@ dependencies = [ "daedalus", "fs_extra", "futures", + "json5", "lazy_static", "path-clean", "regex", @@ -1194,6 +1324,18 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[package]] +name = "typenum" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" + +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + [[package]] name = "unicode-bidi" version = "0.3.7" diff --git a/theseus/Cargo.toml b/theseus/Cargo.toml index 283d57262..28eb816e9 100644 --- a/theseus/Cargo.toml +++ b/theseus/Cargo.toml @@ -15,6 +15,7 @@ daedalus = "0.1.6" reqwest = { version = "0.11", features = ["json"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +json5 = "0.4.1" chrono = { version = "0.4", features = ["serde"] } uuid = { version = "0.8", features = ["serde", "v4"] } bytes = "1" diff --git a/theseus/src/launcher/mod.rs b/theseus/src/launcher/mod.rs index 8bf104060..649918754 100644 --- a/theseus/src/launcher/mod.rs +++ b/theseus/src/launcher/mod.rs @@ -1,4 +1,5 @@ use daedalus::minecraft::{ArgumentType, VersionInfo}; +use serde::{Deserialize, Serialize}; use std::path::Path; use std::process::{Command, Stdio}; use thiserror::Error; @@ -65,7 +66,8 @@ pub async fn fetch_metadata() -> Result< Ok((game?, forge?, fabric?)) } -#[derive(Debug, Eq, PartialEq, Clone, Copy)] +#[derive(Debug, Eq, PartialEq, Clone, Copy, Deserialize, Serialize)] +#[serde(rename_all = "lowercase")] pub enum ModLoader { Vanilla, Forge, diff --git a/theseus/src/modpack/mod.rs b/theseus/src/modpack/mod.rs index f9a2f4e2c..8c3ad45da 100644 --- a/theseus/src/modpack/mod.rs +++ b/theseus/src/modpack/mod.rs @@ -4,18 +4,23 @@ use daedalus::download_file; use fs_extra::dir::CopyOptions; use serde::Deserialize; use std::{convert::TryFrom, env, io, path::Path}; -use tokio::fs; +use tokio::{fs, try_join}; use uuid::Uuid; use zip::ZipArchive; -use self::{manifest::Manifest, pack::Modpack}; +use self::{ + manifest::Manifest, + pack::{Modpack, ModpackGame}, +}; -pub mod pack; pub mod manifest; pub mod modrinth_api; +pub mod pack; +pub const COMPILED_PATH: &'static str = "compiled/"; pub const MANIFEST_PATH: &'static str = "index.json"; pub const OVERRIDES_PATH: &'static str = "overrides/"; +pub const PACK_JSON5_PATH: &'static str = "modpack.json5"; #[derive(thiserror::Error, Debug)] pub enum ModpackError { @@ -43,6 +48,9 @@ pub enum ModpackError { #[error("Error parsing json: {0}")] JsonError(#[from] serde_json::Error), + #[error("Error parsing json5: {0}")] + Json5Error(#[from] json5::Error), + #[error("Error joining futures: {0}")] JoinError(#[from] tokio::task::JoinError), @@ -50,7 +58,7 @@ pub enum ModpackError { VersionError(String), #[error("Error downloading file: {0}")] - FetchError(#[from] reqwest::Error) + FetchError(#[from] reqwest::Error), } type ModpackResult = Result; @@ -121,3 +129,57 @@ pub async fn realise_modpack( modpack.download_files(dest, side).await?; Ok(()) } + +pub fn to_pack_json5(pack: &Modpack) -> ModpackResult { + let json5 = json5::to_string(pack)?; + Ok(format!("// This modpack is managed using Theseus. It can be edited using either a Theseus-compatible launcher or manually.\n{}", json5)) +} + +lazy_static::lazy_static! { + static ref PACK_GITIGNORE: String = format!(r#" + {0} + "#, COMPILED_PATH); +} + +pub async fn create_modpack( + name: &str, + game: ModpackGame, + summary: Option<&str>, +) -> ModpackResult<()> { + let output_dir = Path::new("./").join(name); + let pack = Modpack::new(game, "0.1.0", name, summary); + + try_join!( + fs::create_dir(&output_dir), + fs::create_dir(output_dir.join(OVERRIDES_PATH)), + fs::write(output_dir.join(".gitignore"), PACK_GITIGNORE.as_str()), + fs::write(output_dir.join(PACK_JSON5_PATH), to_pack_json5(&pack)?), + )?; + + Ok(()) +} + +pub async fn compile_modpack(dir: &Path) -> ModpackResult<()> { + let result_dir = dir.join(COMPILED_PATH); + let pack: Modpack = json5::from_str(&fs::read_to_string(dir.join(PACK_JSON5_PATH)).await?)?; + + if dir.join(OVERRIDES_PATH).exists() { + fs_extra::dir::copy( + dir.join(OVERRIDES_PATH), + result_dir.join(OVERRIDES_PATH), + &CopyOptions::new(), + )?; + } + let manifest = Manifest::try_from(&pack)?; + + try_join!( + fs::create_dir(&result_dir), + fs::write( + result_dir.join(MANIFEST_PATH), + serde_json::to_string(&manifest)? + ), + )?; + + Ok(()) +} + diff --git a/theseus/src/modpack/pack.rs b/theseus/src/modpack/pack.rs index 367ea0a9e..b8e726a7c 100644 --- a/theseus/src/modpack/pack.rs +++ b/theseus/src/modpack/pack.rs @@ -1,5 +1,6 @@ use daedalus::download_file_mirrors; use futures::future; +use serde::{Deserialize, Serialize}; use std::{ collections::HashSet, hash::Hash, @@ -13,7 +14,7 @@ use super::{ }; use crate::launcher::ModLoader; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)] pub struct Modpack { pub game: ModpackGame, pub version: String, @@ -96,14 +97,14 @@ impl Modpack { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)] pub enum ModpackGame { // TODO: Currently, the launcher does not support specifying mod loader versions, so I just // store the loader here. Minecraft(String, ModLoader), } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)] pub struct ModpackFile { pub path: PathBuf, pub hashes: ModpackFileHashes, @@ -145,7 +146,8 @@ impl ModpackFile { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] pub enum ModpackEnv { ClientOnly, ServerOnly, @@ -168,7 +170,7 @@ impl ModpackEnv { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)] pub struct ModpackFileHashes { pub sha1: String, }