mirror of
https://github.com/WarrenHood/MCModpackManager.git
synced 2025-04-30 00:04:59 +01:00
Properly added support for local files in the modpack
This commit is contained in:
parent
d079f448ae
commit
9df380ad18
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -148,6 +148,9 @@ name = "anyhow"
|
||||||
version = "1.0.86"
|
version = "1.0.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "approx"
|
name = "approx"
|
||||||
|
|
|
@ -5,7 +5,7 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.86"
|
anyhow = { version = "1.0.86", features = ["backtrace"] }
|
||||||
clap = { version = "4.5.15", features = ["derive"] }
|
clap = { version = "4.5.15", features = ["derive"] }
|
||||||
git2 = "0.19.0"
|
git2 = "0.19.0"
|
||||||
home = "0.5.9"
|
home = "0.5.9"
|
||||||
|
|
|
@ -1,11 +1,44 @@
|
||||||
use crate::providers::DownloadSide;
|
use crate::providers::DownloadSide;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::path::{Path, PathBuf};
|
use std::{fmt::Display, path::{Path, PathBuf}, str::FromStr};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Hash)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Hash)]
|
||||||
pub struct FileMeta {
|
pub struct FileMeta {
|
||||||
|
/// Relative path of file in the instance folder
|
||||||
pub target_path: String,
|
pub target_path: String,
|
||||||
|
/// Which side the files should be applied on
|
||||||
pub side: DownloadSide,
|
pub side: DownloadSide,
|
||||||
|
/// When to apply the files to the instance
|
||||||
|
pub apply_policy: FileApplyPolicy
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)]
|
||||||
|
pub enum FileApplyPolicy {
|
||||||
|
/// Always ensure the file or folder exactly matches that defined in the pack
|
||||||
|
Always,
|
||||||
|
/// Only apply the file or folder if it doesn't already exist in the pack
|
||||||
|
Once
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for FileApplyPolicy {
|
||||||
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s.to_ascii_lowercase().as_str() {
|
||||||
|
"always" => Ok(Self::Always),
|
||||||
|
"once" => Ok(Self::Once),
|
||||||
|
_ => anyhow::bail!("Invalid apply policy {}. Expected one of: always, once", s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for FileApplyPolicy {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Always => write!(f, "Always"),
|
||||||
|
Self::Once => write!(f, "Once"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for FileMeta {
|
impl PartialEq for FileMeta {
|
||||||
|
|
|
@ -7,7 +7,7 @@ mod resolver;
|
||||||
|
|
||||||
use anyhow::{Error, Result};
|
use anyhow::{Error, Result};
|
||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Args, Parser, Subcommand};
|
||||||
use file_meta::{get_normalized_relative_path, FileMeta};
|
use file_meta::{get_normalized_relative_path, FileApplyPolicy, FileMeta};
|
||||||
use mod_meta::{ModMeta, ModProvider};
|
use mod_meta::{ModMeta, ModProvider};
|
||||||
use modpack::ModpackMeta;
|
use modpack::ModpackMeta;
|
||||||
use profiles::{PackSource, Profile};
|
use profiles::{PackSource, Profile};
|
||||||
|
@ -134,10 +134,13 @@ enum FileCommands {
|
||||||
local_path: PathBuf,
|
local_path: PathBuf,
|
||||||
/// Target path to copy the file/folder to relative to the MC instance directory
|
/// Target path to copy the file/folder to relative to the MC instance directory
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
target_path: Option<PathBuf>,
|
target_path: Option<String>,
|
||||||
/// Side to copy the file/folder to
|
/// Side to copy the file/folder to
|
||||||
#[arg(long, default_value_t = DownloadSide::Server)]
|
#[arg(long, default_value_t = DownloadSide::Server)]
|
||||||
side: DownloadSide,
|
side: DownloadSide,
|
||||||
|
/// File apply policy - whether to always apply the file or just apply it once (if the file doesn't exist)
|
||||||
|
#[arg(long, default_value_t = FileApplyPolicy::Always)]
|
||||||
|
apply_policy: FileApplyPolicy,
|
||||||
},
|
},
|
||||||
/// Show metadata about a file in the pack
|
/// Show metadata about a file in the pack
|
||||||
Show {
|
Show {
|
||||||
|
@ -437,15 +440,19 @@ async fn main() -> anyhow::Result<()> {
|
||||||
local_path,
|
local_path,
|
||||||
target_path,
|
target_path,
|
||||||
side,
|
side,
|
||||||
|
apply_policy,
|
||||||
} => {
|
} => {
|
||||||
let mut modpack_meta = ModpackMeta::load_from_current_directory()?;
|
let mut modpack_meta = ModpackMeta::load_from_current_directory()?;
|
||||||
let current_dir = &std::env::current_dir()?;
|
let current_dir = &std::env::current_dir()?;
|
||||||
|
let target_path = if let Some(target_path) = target_path {
|
||||||
|
target_path
|
||||||
|
} else {
|
||||||
|
get_normalized_relative_path(&local_path, ¤t_dir)?
|
||||||
|
};
|
||||||
let file_meta = FileMeta {
|
let file_meta = FileMeta {
|
||||||
target_path: get_normalized_relative_path(
|
target_path,
|
||||||
&target_path.unwrap_or(local_path.clone()),
|
|
||||||
current_dir,
|
|
||||||
)?,
|
|
||||||
side,
|
side,
|
||||||
|
apply_policy,
|
||||||
};
|
};
|
||||||
|
|
||||||
modpack_meta.add_file(&local_path, &file_meta, current_dir)?;
|
modpack_meta.add_file(&local_path, &file_meta, current_dir)?;
|
||||||
|
@ -512,7 +519,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
};
|
};
|
||||||
println!("Profile name : {name}");
|
println!("Profile name : {name}");
|
||||||
println!("Instance folder : {}", profile.instance_folder.display());
|
println!("Instance folder : {}", profile.instance_folder.display());
|
||||||
println!("Modpack source: {}", profile.pack_source);
|
println!("Modpack source : {}", profile.pack_source);
|
||||||
println!("Side : {}", profile.side);
|
println!("Side : {}", profile.side);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
file_meta::{get_normalized_relative_path, FileMeta},
|
file_meta::{get_normalized_relative_path, FileApplyPolicy, FileMeta},
|
||||||
mod_meta::{ModMeta, ModProvider},
|
mod_meta::{ModMeta, ModProvider},
|
||||||
|
providers::DownloadSide,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -130,16 +131,6 @@ impl ModpackMeta {
|
||||||
pack_root.display()
|
pack_root.display()
|
||||||
))?
|
))?
|
||||||
};
|
};
|
||||||
|
|
||||||
let target_path = PathBuf::from(&file_meta.target_path);
|
|
||||||
if !target_path.is_relative() {
|
|
||||||
anyhow::bail!(
|
|
||||||
"Target path {} for file {} is not relative!",
|
|
||||||
file_meta.target_path,
|
|
||||||
file_path.display()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let full_path = pack_root.join(relative_path);
|
let full_path = pack_root.join(relative_path);
|
||||||
|
|
||||||
// Make sure this path is consistent across platforms
|
// Make sure this path is consistent across platforms
|
||||||
|
@ -189,6 +180,86 @@ impl ModpackMeta {
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Installs all the manual files from the pack into the specified directory
|
||||||
|
///
|
||||||
|
/// Files/Folders are added if they don't exist if the policy is set to `FileApplyPolicy::Once`.
|
||||||
|
/// Otherwise, files/folders are always overwritten.
|
||||||
|
///
|
||||||
|
/// Files/Folders, when applied, will ensure that the exact contents of that file or folder match in the instance folder
|
||||||
|
/// Ie. If a folder is being applied, any files in that folder not in the modpack will be removed
|
||||||
|
pub fn install_files(
|
||||||
|
&self,
|
||||||
|
pack_dir: &Path,
|
||||||
|
instance_dir: &Path,
|
||||||
|
side: DownloadSide,
|
||||||
|
) -> Result<()> {
|
||||||
|
println!(
|
||||||
|
"Applying modpack files: {} -> {}...",
|
||||||
|
pack_dir.display(),
|
||||||
|
instance_dir.display()
|
||||||
|
);
|
||||||
|
if let Some(files) = &self.files {
|
||||||
|
for (rel_path, file_meta) in files {
|
||||||
|
let source_path = pack_dir.join(rel_path);
|
||||||
|
let target_path = instance_dir.join(&file_meta.target_path);
|
||||||
|
if !side.contains(file_meta.side) {
|
||||||
|
println!(
|
||||||
|
"Skipping apply of {} -> {}. (Applies for side={}, current side={})",
|
||||||
|
source_path.display(),
|
||||||
|
target_path.display(),
|
||||||
|
file_meta.side.to_string(),
|
||||||
|
side.to_string()
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if target_path.exists() && file_meta.apply_policy == FileApplyPolicy::Once {
|
||||||
|
println!(
|
||||||
|
"Skipping apply of {} -> {}. (Already applied once)",
|
||||||
|
source_path.display(),
|
||||||
|
target_path.display(),
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, this file/folder needs to be applied
|
||||||
|
if source_path.is_dir() {
|
||||||
|
// Sync a folder
|
||||||
|
if target_path.exists() {
|
||||||
|
println!(
|
||||||
|
"Syncing and overwriting existing directory {} -> {}",
|
||||||
|
source_path.display(),
|
||||||
|
target_path.display(),
|
||||||
|
);
|
||||||
|
std::fs::remove_dir_all(&target_path)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.copy_files(&source_path, &target_path)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy_files(&self, src: &Path, dst: &Path) -> Result<()> {
|
||||||
|
if src.is_dir() {
|
||||||
|
std::fs::create_dir_all(dst)?;
|
||||||
|
for entry in std::fs::read_dir(src)? {
|
||||||
|
let entry = entry?;
|
||||||
|
let src_path = entry.path();
|
||||||
|
let dst_path = dst.join(entry.file_name());
|
||||||
|
self.copy_files(&src_path, &dst_path)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let parent_dir = dst.parent();
|
||||||
|
if let Some(parent_dir) = parent_dir {
|
||||||
|
std::fs::create_dir_all(parent_dir)?;
|
||||||
|
}
|
||||||
|
println!("Syncing file {} -> {}", src.display(), dst.display());
|
||||||
|
std::fs::copy(src, dst)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init_project(&self, directory: &Path) -> Result<()> {
|
pub fn init_project(&self, directory: &Path) -> Result<()> {
|
||||||
let modpack_meta_file_path = directory.join(PathBuf::from(MODPACK_FILENAME));
|
let modpack_meta_file_path = directory.join(PathBuf::from(MODPACK_FILENAME));
|
||||||
if modpack_meta_file_path.exists() {
|
if modpack_meta_file_path.exists() {
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::{
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{providers::DownloadSide, resolver::PinnedPackMeta};
|
use crate::{modpack::ModpackMeta, providers::DownloadSide, resolver::PinnedPackMeta};
|
||||||
|
|
||||||
const CONFIG_DIR_NAME: &str = "mcmpmgr";
|
const CONFIG_DIR_NAME: &str = "mcmpmgr";
|
||||||
const DATA_FILENAME: &str = "data.toml";
|
const DATA_FILENAME: &str = "data.toml";
|
||||||
|
@ -58,16 +58,20 @@ impl Profile {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn install(&self) -> Result<()> {
|
pub async fn install(&self) -> Result<()> {
|
||||||
let (pack_lock, temp_dir) = match &self.pack_source {
|
let (pack_lock, pack_directory, _temp_dir) = match &self.pack_source {
|
||||||
PackSource::Git { url } => {
|
PackSource::Git { url } => {
|
||||||
let (pack_lock, packdir) = PinnedPackMeta::load_from_git_repo(&url, true).await?;
|
let (pack_lock, packdir) = PinnedPackMeta::load_from_git_repo(&url, true).await?;
|
||||||
(pack_lock, Some(packdir))
|
let pack_path = packdir.path().to_path_buf();
|
||||||
|
(pack_lock, pack_path, Some(packdir))
|
||||||
}
|
}
|
||||||
PackSource::Local { path } => (
|
PackSource::Local { path } => (
|
||||||
PinnedPackMeta::load_from_directory(&path, true).await?,
|
PinnedPackMeta::load_from_directory(&path, true).await?,
|
||||||
|
path.to_path_buf(),
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
let modpack_meta = ModpackMeta::load_from_directory(&pack_directory)?;
|
||||||
|
modpack_meta.install_files(&pack_directory, &self.instance_folder, self.side)?;
|
||||||
|
|
||||||
pack_lock
|
pack_lock
|
||||||
.download_mods(&self.instance_folder.join("mods"), self.side)
|
.download_mods(&self.instance_folder.join("mods"), self.side)
|
||||||
|
|
|
@ -28,6 +28,12 @@ pub enum DownloadSide {
|
||||||
Client,
|
Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DownloadSide {
|
||||||
|
pub fn contains(self, side: Self) -> bool {
|
||||||
|
self == Self::Both || side == Self::Both || self == side
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromStr for DownloadSide {
|
impl FromStr for DownloadSide {
|
||||||
type Err = anyhow::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.86"
|
anyhow = { version = "1.0.86", features = ["backtrace"] }
|
||||||
iced = { version = "0.12.1", features = ["tokio"] }
|
iced = { version = "0.12.1", features = ["tokio"] }
|
||||||
mcmpmgr = { version = "0.1.0", path = "../mcmpmgr" }
|
mcmpmgr = { version = "0.1.0", path = "../mcmpmgr" }
|
||||||
rfd = "0.14.1"
|
rfd = "0.14.1"
|
||||||
|
|
Loading…
Reference in a new issue