diff options
author | Daniel Schadt <kingdread@gmx.de> | 2021-11-06 23:01:52 +0100 |
---|---|---|
committer | Daniel Schadt <kingdread@gmx.de> | 2021-11-06 23:01:52 +0100 |
commit | b7d13549d4ce961b9384611844e62a7156b321c7 (patch) | |
tree | 9ff39ad4e5f16598794598f1b35c00c48661476a /src | |
parent | 20c8574ab6b9db586ccc88e453cdb84fe44f1004 (diff) | |
download | modderbaas-b7d13549d4ce961b9384611844e62a7156b321c7.tar.gz modderbaas-b7d13549d4ce961b9384611844e62a7156b321c7.tar.bz2 modderbaas-b7d13549d4ce961b9384611844e62a7156b321c7.zip |
support installing modpacks
Diffstat (limited to 'src')
-rw-r--r-- | src/download.rs | 8 | ||||
-rw-r--r-- | src/main.rs | 35 | ||||
-rw-r--r-- | src/minemod.rs | 36 |
3 files changed, 62 insertions, 17 deletions
diff --git a/src/download.rs b/src/download.rs index 8a372ad..75bea57 100644 --- a/src/download.rs +++ b/src/download.rs @@ -10,7 +10,7 @@ use zip::ZipArchive; use super::{ contentdb::{ContentDb, ContentId}, error::{Error, Result}, - minemod::{MineMod, ModId}, + minemod::{self, ModContainer, ModId}, }; /// A source determines where a mod should be loaded from. @@ -73,7 +73,7 @@ impl Downloader { }) } - pub fn download(&self, source: &Source) -> Result<MineMod> { + pub fn download(&self, source: &Source) -> Result<Box<dyn ModContainer>> { match *source { Source::Http(ref url) => self.download_http(url), Source::ContentDb((ref user, ref package)) => { @@ -96,7 +96,7 @@ impl Downloader { /// /// The mod is extracted to a temporary directory and has to be copied using /// [`MineMod::copy_to`]. - pub fn download_http(&self, url: &Url) -> Result<MineMod> { + pub fn download_http(&self, url: &Url) -> Result<Box<dyn ModContainer>> { let mut reader = ureq::request_url("GET", url).call()?.into_reader(); let mut data = Vec::new(); reader.read_to_end(&mut data)?; @@ -115,6 +115,6 @@ impl Downloader { archive.extract(self.temp_dir.path())?; let extracted_path = self.temp_dir.path().join(name); - MineMod::open(&extracted_path) + minemod::open_mod_or_pack(&extracted_path) } } diff --git a/src/main.rs b/src/main.rs index bff0fe0..da04f9c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,9 @@ use itertools::Itertools; use log::debug; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; -use modderbaas::{Baas, ContentDb, Downloader, MineMod, Snapshot, Source, World}; +use modderbaas::{ + minemod::ModContainer, Baas, ContentDb, Downloader, MineMod, Snapshot, Source, World, +}; fn main() -> Result<()> { stderrlog::new() @@ -200,7 +202,7 @@ fn install_mods( .map(|&s| Source::from_str(s)) .collect::<Result<Vec<_>, _>>()?; - let mut to_install = Vec::<MineMod>::new(); + let mut to_install = Vec::<Box<dyn ModContainer>>::new(); let mut to_enable = Vec::<MineMod>::new(); let game = snapshot @@ -213,7 +215,7 @@ fn install_mods( .map(|m| m.mod_id()) .collect::<Result<Vec<_>, _>>()?; - while !wanted.is_empty() { + 'modloop: while !wanted.is_empty() { let next_mod = wanted.remove(0); // Special handling for mods specified by their ID, as those could already exist. @@ -229,9 +231,11 @@ fn install_mods( } // Is this a mod that is already queued for installation? - for m in &to_install { - if &m.mod_id()? == id { - continue; + for mod_or_pack in &to_install { + for m in mod_or_pack.mods()? { + if &m.name()? == id { + continue 'modloop; + } } } @@ -264,7 +268,9 @@ fn install_mods( let downloaded = downloader .download(&next_mod) .context("Failed to download mod")?; - wanted.extend(downloaded.dependencies()?.into_iter().map(Source::ModId)); + for m in downloaded.mods()? { + wanted.extend(m.dependencies()?.into_iter().map(Source::ModId)); + } to_install.push(downloaded); } } @@ -276,20 +282,25 @@ fn install_mods( if dry_run { for m in to_install { - let mod_id = m.mod_id()?; + let mod_id = m.name()?; let mod_dir = target_dir.join(&mod_id); - writeln!(output, "Installing {} to {:?}", mod_id, mod_dir)?; + writeln!(output, "Installing {} to {:?}", m, mod_dir)?; + to_enable.extend(m.mods()?); + } + for m in to_enable { + let mod_id = m.mod_id()?; + writeln!(output, "Enabling {}", mod_id)?; } return Ok(()); } for m in to_install { - let mod_id = m.mod_id()?; + let mod_id = m.name()?; writeln!(output, "Installing {}", mod_id)?; let installed = m - .copy_to(target_dir) + .install_to(target_dir) .context(format!("Error installing '{}'", mod_id))?; - to_enable.push(installed); + to_enable.extend(installed.mods()?); } for m in to_enable { diff --git a/src/minemod.rs b/src/minemod.rs index 6af50e3..d90605c 100644 --- a/src/minemod.rs +++ b/src/minemod.rs @@ -126,15 +126,39 @@ impl Modpack { } Ok(mods) } + + /// Copies the modpack to the given path. + /// + /// Note that the path should not include the modpack directory, that will be appended + /// automatically. + /// + /// Returns a new [`Modpack`] object pointing to the copy. + pub fn copy_to<P: AsRef<Path>>(&self, path: P) -> Result<Modpack> { + let mut options = CopyOptions::new(); + options.content_only = true; + let path = path.as_ref().join(self.name()?); + fs::create_dir_all(&path)?; + dir::copy(&self.path, &path, &options)?; + Modpack::open(&path) + } +} + +impl fmt::Display for Modpack { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} (pack)", self.name().map_err(|_| fmt::Error)?) + } } /// A thing that can contain mods. -pub trait ModContainer: Any { +pub trait ModContainer: Any + fmt::Display { /// Returns the name of the mod container. fn name(&self) -> Result<String>; /// Return all contained mods. fn mods(&self) -> Result<Vec<MineMod>>; + + /// Copies the content to the given directory. + fn install_to(&self, path: &Path) -> Result<Box<dyn ModContainer>>; } impl ModContainer for MineMod { @@ -145,6 +169,11 @@ impl ModContainer for MineMod { fn mods(&self) -> Result<Vec<MineMod>> { Ok(vec![self.clone()]) } + + fn install_to(&self, path: &Path) -> Result<Box<dyn ModContainer>> { + self.copy_to(path) + .map(|x| Box::new(x) as Box<dyn ModContainer>) + } } impl ModContainer for Modpack { @@ -155,6 +184,11 @@ impl ModContainer for Modpack { fn mods(&self) -> Result<Vec<MineMod>> { self.mods() } + + fn install_to(&self, path: &Path) -> Result<Box<dyn ModContainer>> { + self.copy_to(path) + .map(|x| Box::new(x) as Box<dyn ModContainer>) + } } /// Attempts to open the given path as either a single mod or a modpack. |