mirror of
https://github.com/WarrenHood/MCModpackManager.git
synced 2025-04-30 00:44:58 +01:00
Compare commits
2 commits
042bfb8baa
...
d079f448ae
Author | SHA1 | Date | |
---|---|---|---|
|
d079f448ae | ||
|
db4b2bd694 |
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -2067,6 +2067,7 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"git2",
|
"git2",
|
||||||
"home",
|
"home",
|
||||||
|
"pathdiff",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -2618,6 +2619,12 @@ version = "1.0.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pathdiff"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
|
|
|
@ -9,6 +9,7 @@ anyhow = "1.0.86"
|
||||||
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"
|
||||||
|
pathdiff = "0.2.1"
|
||||||
reqwest = { version = "0.12.5", features = ["json"] }
|
reqwest = { version = "0.12.5", features = ["json"] }
|
||||||
semver = { version = "1.0.23", features = ["serde"] }
|
semver = { version = "1.0.23", features = ["serde"] }
|
||||||
serde = { version = "1.0.207", features = ["derive"] }
|
serde = { version = "1.0.207", features = ["derive"] }
|
||||||
|
|
64
mcmpmgr/src/file_meta.rs
Normal file
64
mcmpmgr/src/file_meta.rs
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
use crate::providers::DownloadSide;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Hash)]
|
||||||
|
pub struct FileMeta {
|
||||||
|
pub target_path: String,
|
||||||
|
pub side: DownloadSide,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for FileMeta {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.target_path == other.target_path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for FileMeta {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
self.target_path.partial_cmp(&other.target_path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for FileMeta {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.target_path.cmp(&other.target_path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for FileMeta {}
|
||||||
|
|
||||||
|
/// Get a normalized relative path string in a consistent way across platforms
|
||||||
|
/// TODO: Make a nice struct for this maybe
|
||||||
|
pub fn get_normalized_relative_path(
|
||||||
|
path_to_normalize: &Path,
|
||||||
|
base_path: &Path,
|
||||||
|
) -> anyhow::Result<String> {
|
||||||
|
if path_to_normalize.is_absolute() {
|
||||||
|
anyhow::bail!(
|
||||||
|
"Absolute paths are not supported! Will not normalise {}",
|
||||||
|
path_to_normalize.display()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let base_path = base_path.canonicalize()?;
|
||||||
|
let full_path = base_path.join(path_to_normalize).canonicalize()?;
|
||||||
|
let relative_path = pathdiff::diff_paths(&full_path, &base_path).ok_or(anyhow::format_err!(
|
||||||
|
"Cannot normalize path {} relative to {}",
|
||||||
|
&path_to_normalize.display(),
|
||||||
|
&base_path.display()
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let mut normalized_path = String::new();
|
||||||
|
for (i, component) in relative_path.components().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
normalized_path.push('/');
|
||||||
|
}
|
||||||
|
normalized_path.push_str(&component.as_os_str().to_string_lossy());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !normalized_path.starts_with("./") && !normalized_path.starts_with("/") {
|
||||||
|
normalized_path.insert_str(0, "./");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(normalized_path)
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod mod_meta;
|
pub mod mod_meta;
|
||||||
|
pub mod file_meta;
|
||||||
pub mod modpack;
|
pub mod modpack;
|
||||||
pub mod profiles;
|
pub mod profiles;
|
||||||
pub mod providers;
|
pub mod providers;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
mod file_meta;
|
||||||
mod mod_meta;
|
mod mod_meta;
|
||||||
mod modpack;
|
mod modpack;
|
||||||
mod profiles;
|
mod profiles;
|
||||||
|
@ -6,6 +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 mod_meta::{ModMeta, ModProvider};
|
use mod_meta::{ModMeta, ModProvider};
|
||||||
use modpack::ModpackMeta;
|
use modpack::ModpackMeta;
|
||||||
use profiles::{PackSource, Profile};
|
use profiles::{PackSource, Profile};
|
||||||
|
@ -74,7 +76,7 @@ enum Commands {
|
||||||
modloader: Option<modpack::ModLoader>,
|
modloader: Option<modpack::ModLoader>,
|
||||||
/// Side override
|
/// Side override
|
||||||
#[arg(long, short)]
|
#[arg(long, short)]
|
||||||
side: Option<DownloadSide>
|
side: Option<DownloadSide>,
|
||||||
},
|
},
|
||||||
/// Remove a mod from the modpack
|
/// Remove a mod from the modpack
|
||||||
Remove {
|
Remove {
|
||||||
|
@ -109,10 +111,46 @@ enum Commands {
|
||||||
#[arg(long, short, action)]
|
#[arg(long, short, action)]
|
||||||
locked: bool,
|
locked: bool,
|
||||||
},
|
},
|
||||||
|
/// Manage local files in the modpack
|
||||||
|
File(FileArgs),
|
||||||
/// Manage mcmpmgr profiles
|
/// Manage mcmpmgr profiles
|
||||||
Profile(ProfileArgs),
|
Profile(ProfileArgs),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Args)]
|
||||||
|
#[command(args_conflicts_with_subcommands = true)]
|
||||||
|
struct FileArgs {
|
||||||
|
#[command(subcommand)]
|
||||||
|
command: Option<FileCommands>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
enum FileCommands {
|
||||||
|
/// List all files/folders in the pack
|
||||||
|
List,
|
||||||
|
/// Add new files/folder to the pack
|
||||||
|
Add {
|
||||||
|
/// Local path to file/folder to include in the pack (must be in the pack root)
|
||||||
|
local_path: PathBuf,
|
||||||
|
/// Target path to copy the file/folder to relative to the MC instance directory
|
||||||
|
#[arg(short, long)]
|
||||||
|
target_path: Option<PathBuf>,
|
||||||
|
/// Side to copy the file/folder to
|
||||||
|
#[arg(long, default_value_t = DownloadSide::Server)]
|
||||||
|
side: DownloadSide,
|
||||||
|
},
|
||||||
|
/// Show metadata about a file in the pack
|
||||||
|
Show {
|
||||||
|
/// Local path of the file/folder to show
|
||||||
|
local_path: String,
|
||||||
|
},
|
||||||
|
/// Remove a file/folder from the pack
|
||||||
|
Remove {
|
||||||
|
/// local path to file/folder to remove
|
||||||
|
local_path: PathBuf,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
#[command(args_conflicts_with_subcommands = true)]
|
#[command(args_conflicts_with_subcommands = true)]
|
||||||
struct ProfileArgs {
|
struct ProfileArgs {
|
||||||
|
@ -134,9 +172,9 @@ enum ProfileCommands {
|
||||||
/// A local file path to a modpack directory or a git repo url prefixed with 'git+'
|
/// A local file path to a modpack directory or a git repo url prefixed with 'git+'
|
||||||
#[arg(long, short)]
|
#[arg(long, short)]
|
||||||
pack_source: PackSource,
|
pack_source: PackSource,
|
||||||
/// Mods directory
|
/// Instance directory (containing a mods folder)
|
||||||
#[arg(long, short)]
|
#[arg(long, short)]
|
||||||
mods_directory: PathBuf,
|
instance_directory: PathBuf,
|
||||||
},
|
},
|
||||||
/// Install a profile
|
/// Install a profile
|
||||||
Install {
|
Install {
|
||||||
|
@ -228,7 +266,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
locked,
|
locked,
|
||||||
mc_version,
|
mc_version,
|
||||||
modloader,
|
modloader,
|
||||||
side
|
side,
|
||||||
} => {
|
} => {
|
||||||
let mut modpack_meta = ModpackMeta::load_from_current_directory()?;
|
let mut modpack_meta = ModpackMeta::load_from_current_directory()?;
|
||||||
let old_modpack_meta = modpack_meta.clone();
|
let old_modpack_meta = modpack_meta.clone();
|
||||||
|
@ -251,15 +289,15 @@ async fn main() -> anyhow::Result<()> {
|
||||||
DownloadSide::Both => {
|
DownloadSide::Both => {
|
||||||
mod_meta.server_side = Some(true);
|
mod_meta.server_side = Some(true);
|
||||||
mod_meta.client_side = Some(true);
|
mod_meta.client_side = Some(true);
|
||||||
},
|
}
|
||||||
DownloadSide::Server => {
|
DownloadSide::Server => {
|
||||||
mod_meta.server_side = Some(true);
|
mod_meta.server_side = Some(true);
|
||||||
mod_meta.client_side = Some(false);
|
mod_meta.client_side = Some(false);
|
||||||
},
|
}
|
||||||
DownloadSide::Client => {
|
DownloadSide::Client => {
|
||||||
mod_meta.server_side = Some(false);
|
mod_meta.server_side = Some(false);
|
||||||
mod_meta.client_side = Some(true);
|
mod_meta.client_side = Some(true);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for provider in providers.into_iter() {
|
for provider in providers.into_iter() {
|
||||||
|
@ -391,6 +429,37 @@ async fn main() -> anyhow::Result<()> {
|
||||||
pack_lock.init(&modpack_meta, !locked).await?;
|
pack_lock.init(&modpack_meta, !locked).await?;
|
||||||
pack_lock.save_current_dir_lock()?;
|
pack_lock.save_current_dir_lock()?;
|
||||||
}
|
}
|
||||||
|
Commands::File(FileArgs { command }) => {
|
||||||
|
if let Some(command) = command {
|
||||||
|
match command {
|
||||||
|
FileCommands::List => todo!(),
|
||||||
|
FileCommands::Add {
|
||||||
|
local_path,
|
||||||
|
target_path,
|
||||||
|
side,
|
||||||
|
} => {
|
||||||
|
let mut modpack_meta = ModpackMeta::load_from_current_directory()?;
|
||||||
|
let current_dir = &std::env::current_dir()?;
|
||||||
|
let file_meta = FileMeta {
|
||||||
|
target_path: get_normalized_relative_path(
|
||||||
|
&target_path.unwrap_or(local_path.clone()),
|
||||||
|
current_dir,
|
||||||
|
)?,
|
||||||
|
side,
|
||||||
|
};
|
||||||
|
|
||||||
|
modpack_meta.add_file(&local_path, &file_meta, current_dir)?;
|
||||||
|
modpack_meta.save_current_dir_project()?;
|
||||||
|
}
|
||||||
|
FileCommands::Show { local_path } => todo!(),
|
||||||
|
FileCommands::Remove { local_path } => {
|
||||||
|
let mut modpack_meta = ModpackMeta::load_from_current_directory()?;
|
||||||
|
modpack_meta.remove_file(&local_path, &std::env::current_dir()?)?;
|
||||||
|
modpack_meta.save_current_dir_project()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Commands::Profile(ProfileArgs { command }) => {
|
Commands::Profile(ProfileArgs { command }) => {
|
||||||
if let Some(command) = command {
|
if let Some(command) = command {
|
||||||
match command {
|
match command {
|
||||||
|
@ -405,10 +474,10 @@ async fn main() -> anyhow::Result<()> {
|
||||||
name,
|
name,
|
||||||
side,
|
side,
|
||||||
pack_source,
|
pack_source,
|
||||||
mods_directory,
|
instance_directory,
|
||||||
} => {
|
} => {
|
||||||
let mut userdata = profiles::Data::load()?;
|
let mut userdata = profiles::Data::load()?;
|
||||||
let profile = Profile::new(&mods_directory, pack_source, side);
|
let profile = Profile::new(&instance_directory, pack_source, side);
|
||||||
userdata.add_profile(&name, profile);
|
userdata.add_profile(&name, profile);
|
||||||
userdata.save()?;
|
userdata.save()?;
|
||||||
println!("Saved profile '{name}'");
|
println!("Saved profile '{name}'");
|
||||||
|
@ -442,7 +511,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
anyhow::bail!("Profile '{name}' does not exist")
|
anyhow::bail!("Profile '{name}' does not exist")
|
||||||
};
|
};
|
||||||
println!("Profile name : {name}");
|
println!("Profile name : {name}");
|
||||||
println!("Mods folder : {}", profile.mods_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,4 +1,7 @@
|
||||||
use crate::mod_meta::{ModMeta, ModProvider};
|
use crate::{
|
||||||
|
file_meta::{get_normalized_relative_path, FileMeta},
|
||||||
|
mod_meta::{ModMeta, ModProvider},
|
||||||
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -38,11 +41,19 @@ impl std::str::FromStr for ModLoader {
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct ModpackMeta {
|
pub struct ModpackMeta {
|
||||||
|
/// The name of the modpack
|
||||||
pub pack_name: String,
|
pub pack_name: String,
|
||||||
|
/// The intended minecraft version on which this pack should run
|
||||||
pub mc_version: String,
|
pub mc_version: String,
|
||||||
|
/// The default modloader for the modpack
|
||||||
pub modloader: ModLoader,
|
pub modloader: ModLoader,
|
||||||
|
/// Map of mod name -> mod metadata
|
||||||
pub mods: BTreeMap<String, ModMeta>,
|
pub mods: BTreeMap<String, ModMeta>,
|
||||||
|
/// Mapping of relative paths to files to copy over from the modpack
|
||||||
|
pub files: Option<BTreeMap<String, FileMeta>>,
|
||||||
|
/// Default provider for newly added mods in the modpack
|
||||||
pub default_providers: Vec<ModProvider>,
|
pub default_providers: Vec<ModProvider>,
|
||||||
|
/// A set of forbidden mods in the modpack
|
||||||
pub forbidden_mods: BTreeSet<String>,
|
pub forbidden_mods: BTreeSet<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,6 +114,81 @@ impl ModpackMeta {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add local files or folders to the pack. These should be committed to version control
|
||||||
|
pub fn add_file(
|
||||||
|
&mut self,
|
||||||
|
file_path: &Path,
|
||||||
|
file_meta: &FileMeta,
|
||||||
|
pack_root: &Path,
|
||||||
|
) -> Result<&mut Self> {
|
||||||
|
let relative_path = if file_path.is_relative() {
|
||||||
|
file_path
|
||||||
|
} else {
|
||||||
|
&pathdiff::diff_paths(file_path, pack_root).ok_or(anyhow::format_err!(
|
||||||
|
"Cannot get relative path of {} in {}",
|
||||||
|
file_path.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);
|
||||||
|
|
||||||
|
// Make sure this path is consistent across platforms
|
||||||
|
let relative_path = get_normalized_relative_path(relative_path, &pack_root)?;
|
||||||
|
|
||||||
|
if !full_path
|
||||||
|
.canonicalize()?
|
||||||
|
.starts_with(pack_root.canonicalize()?)
|
||||||
|
{
|
||||||
|
anyhow::bail!(
|
||||||
|
"You cannot add local files to the modpack from outside the pack source directory. {} is not contained in {}",
|
||||||
|
full_path.canonicalize()?.display(),
|
||||||
|
pack_root.canonicalize()?.display()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
match &mut self.files {
|
||||||
|
Some(files) => {
|
||||||
|
files.insert(relative_path.clone(), file_meta.clone());
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.files
|
||||||
|
.insert(BTreeMap::new())
|
||||||
|
.insert(relative_path.clone(), file_meta.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Added file '{relative_path}' -> '{}' to modpack...",
|
||||||
|
file_meta.target_path
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_file(&mut self, file_path: &PathBuf, pack_root: &Path) -> Result<&mut Self> {
|
||||||
|
let relative_path = get_normalized_relative_path(&file_path, pack_root)?;
|
||||||
|
if let Some(files) = &mut self.files {
|
||||||
|
let removed = files.remove(&relative_path);
|
||||||
|
if let Some(removed) = removed {
|
||||||
|
println!(
|
||||||
|
"Removed file '{relative_path}' -> '{}' from modpack...",
|
||||||
|
removed.target_path
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
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() {
|
||||||
|
@ -140,6 +226,7 @@ impl std::default::Default for ModpackMeta {
|
||||||
mc_version: "1.20.1".into(),
|
mc_version: "1.20.1".into(),
|
||||||
modloader: ModLoader::Forge,
|
modloader: ModLoader::Forge,
|
||||||
mods: Default::default(),
|
mods: Default::default(),
|
||||||
|
files: Default::default(),
|
||||||
default_providers: vec![ModProvider::Modrinth],
|
default_providers: vec![ModProvider::Modrinth],
|
||||||
forbidden_mods: Default::default(),
|
forbidden_mods: Default::default(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,15 +43,15 @@ impl Display for PackSource {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Profile {
|
pub struct Profile {
|
||||||
pub mods_folder: PathBuf,
|
pub instance_folder: PathBuf,
|
||||||
pub pack_source: PackSource,
|
pub pack_source: PackSource,
|
||||||
pub side: DownloadSide,
|
pub side: DownloadSide,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Profile {
|
impl Profile {
|
||||||
pub fn new(mods_folder: &Path, pack_source: PackSource, side: DownloadSide) -> Self {
|
pub fn new(instance_folder: &Path, pack_source: PackSource, side: DownloadSide) -> Self {
|
||||||
Self {
|
Self {
|
||||||
mods_folder: mods_folder.into(),
|
instance_folder: instance_folder.into(),
|
||||||
pack_source,
|
pack_source,
|
||||||
side,
|
side,
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ impl Profile {
|
||||||
};
|
};
|
||||||
|
|
||||||
pack_lock
|
pack_lock
|
||||||
.download_mods(&self.mods_folder, self.side)
|
.download_mods(&self.instance_folder.join("mods"), self.side)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -118,8 +118,7 @@ impl Data {
|
||||||
|
|
||||||
if let Some(home_dir) = home_dir {
|
if let Some(home_dir) = home_dir {
|
||||||
Ok(home_dir)
|
Ok(home_dir)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
anyhow::bail!("Unable to locate home directory")
|
anyhow::bail!("Unable to locate home directory")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub enum FileSource {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Hash)]
|
||||||
pub enum DownloadSide {
|
pub enum DownloadSide {
|
||||||
Both,
|
Both,
|
||||||
Server,
|
Server,
|
||||||
|
|
|
@ -25,7 +25,7 @@ struct ManagerGUI {
|
||||||
previous_view: ManagerView,
|
previous_view: ManagerView,
|
||||||
profile_edit_settings: ProfileSettings,
|
profile_edit_settings: ProfileSettings,
|
||||||
profile_save_error: Option<String>,
|
profile_save_error: Option<String>,
|
||||||
current_install_status: ProfileInstallStatus
|
current_install_status: ProfileInstallStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -41,7 +41,7 @@ enum ManagerView {
|
||||||
/// The current application view
|
/// The current application view
|
||||||
struct ProfileSettings {
|
struct ProfileSettings {
|
||||||
name: String,
|
name: String,
|
||||||
mods_dir: Option<PathBuf>,
|
instance_dir: Option<PathBuf>,
|
||||||
pack_source: String,
|
pack_source: String,
|
||||||
side: DownloadSide,
|
side: DownloadSide,
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ impl Default for ProfileSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: Default::default(),
|
name: Default::default(),
|
||||||
mods_dir: Default::default(),
|
instance_dir: Default::default(),
|
||||||
pack_source: Default::default(),
|
pack_source: Default::default(),
|
||||||
side: DownloadSide::Client,
|
side: DownloadSide::Client,
|
||||||
}
|
}
|
||||||
|
@ -60,12 +60,15 @@ impl Default for ProfileSettings {
|
||||||
impl TryFrom<ProfileSettings> for profiles::Profile {
|
impl TryFrom<ProfileSettings> for profiles::Profile {
|
||||||
type Error = String;
|
type Error = String;
|
||||||
fn try_from(value: ProfileSettings) -> Result<Self, Self::Error> {
|
fn try_from(value: ProfileSettings) -> Result<Self, Self::Error> {
|
||||||
let mods_dir = value
|
let instance_dir = value
|
||||||
.mods_dir
|
.instance_dir
|
||||||
.ok_or(format!("A mods directory is required"))?;
|
.ok_or(format!("An instance directory is required"))?;
|
||||||
|
if !instance_dir.join("mods").exists() {
|
||||||
|
return Err(format!("Instance folder {} does not seem to contain a mods directory. Are you sure this is a valid instance directory?", instance_dir.display()));
|
||||||
|
}
|
||||||
let pack_source = value.pack_source;
|
let pack_source = value.pack_source;
|
||||||
Ok(profiles::Profile::new(
|
Ok(profiles::Profile::new(
|
||||||
&mods_dir,
|
&instance_dir,
|
||||||
profiles::PackSource::from_str(&pack_source)?,
|
profiles::PackSource::from_str(&pack_source)?,
|
||||||
value.side,
|
value.side,
|
||||||
))
|
))
|
||||||
|
@ -81,13 +84,13 @@ impl Default for ManagerView {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum Message {
|
enum Message {
|
||||||
SwitchView(ManagerView),
|
SwitchView(ManagerView),
|
||||||
BrowseModsDir,
|
BrowseInstanceDir,
|
||||||
EditProfileName(String),
|
EditProfileName(String),
|
||||||
EditPackSource(String),
|
EditPackSource(String),
|
||||||
SaveProfile,
|
SaveProfile,
|
||||||
DeleteProfile(String),
|
DeleteProfile(String),
|
||||||
InstallProfile(String),
|
InstallProfile(String),
|
||||||
ProfileInstalled(ProfileInstallStatus)
|
ProfileInstalled(ProfileInstallStatus),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -95,7 +98,7 @@ enum ProfileInstallStatus {
|
||||||
NotStarted,
|
NotStarted,
|
||||||
Installing,
|
Installing,
|
||||||
Success,
|
Success,
|
||||||
Error(String)
|
Error(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ProfileInstallStatus {
|
impl Default for ProfileInstallStatus {
|
||||||
|
@ -158,8 +161,8 @@ impl Application for ManagerGUI {
|
||||||
self.profile_edit_settings.name = profile.trim().into();
|
self.profile_edit_settings.name = profile.trim().into();
|
||||||
if let Some(loaded_profile) = loaded_profile {
|
if let Some(loaded_profile) = loaded_profile {
|
||||||
self.profile_edit_settings.name = profile.into();
|
self.profile_edit_settings.name = profile.into();
|
||||||
self.profile_edit_settings.mods_dir =
|
self.profile_edit_settings.instance_dir =
|
||||||
Some(loaded_profile.mods_folder.clone());
|
Some(loaded_profile.instance_folder.clone());
|
||||||
self.profile_edit_settings.pack_source =
|
self.profile_edit_settings.pack_source =
|
||||||
loaded_profile.pack_source.to_string();
|
loaded_profile.pack_source.to_string();
|
||||||
self.profile_edit_settings.side = loaded_profile.side;
|
self.profile_edit_settings.side = loaded_profile.side;
|
||||||
|
@ -172,9 +175,9 @@ impl Application for ManagerGUI {
|
||||||
self.current_view = view;
|
self.current_view = view;
|
||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
Message::BrowseModsDir => {
|
Message::BrowseInstanceDir => {
|
||||||
self.profile_edit_settings.mods_dir = rfd::FileDialog::new()
|
self.profile_edit_settings.instance_dir = rfd::FileDialog::new()
|
||||||
.set_title("Select your mods folder")
|
.set_title("Select your instance folder")
|
||||||
.pick_folder();
|
.pick_folder();
|
||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
|
@ -235,24 +238,21 @@ impl Application for ManagerGUI {
|
||||||
let result = profile.install().await;
|
let result = profile.install().await;
|
||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
ProfileInstallStatus::Error(format!("{}", err))
|
ProfileInstallStatus::Error(format!("{}", err))
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
ProfileInstallStatus::Success
|
ProfileInstallStatus::Success
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
ProfileInstallStatus::Error(format!("Profile '{}' doesn't exist", name))
|
ProfileInstallStatus::Error(format!("Profile '{}' doesn't exist", name))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Message::ProfileInstalled
|
Message::ProfileInstalled,
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
Message::ProfileInstalled(result) => {
|
Message::ProfileInstalled(result) => {
|
||||||
self.current_install_status = result;
|
self.current_install_status = result;
|
||||||
|
|
||||||
Command::none()
|
Command::none()
|
||||||
},
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,8 +325,11 @@ impl ManagerGUI {
|
||||||
]
|
]
|
||||||
.spacing(5),
|
.spacing(5),
|
||||||
row![
|
row![
|
||||||
"Mods directory",
|
"Instance folder",
|
||||||
text_input("Mods directory", &profile.mods_folder.display().to_string()),
|
text_input(
|
||||||
|
"Instance folder",
|
||||||
|
&profile.instance_folder.display().to_string()
|
||||||
|
),
|
||||||
]
|
]
|
||||||
.spacing(20),
|
.spacing(20),
|
||||||
row!["Mods to download", text(profile.side),].spacing(5),
|
row!["Mods to download", text(profile.side),].spacing(5),
|
||||||
|
@ -354,16 +357,17 @@ impl ManagerGUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
match &self.current_install_status {
|
match &self.current_install_status {
|
||||||
ProfileInstallStatus::NotStarted => {},
|
ProfileInstallStatus::NotStarted => {}
|
||||||
ProfileInstallStatus::Installing => {
|
ProfileInstallStatus::Installing => {
|
||||||
profile_view = profile_view.push(text("Installing..."));
|
profile_view = profile_view.push(text("Installing..."));
|
||||||
},
|
}
|
||||||
ProfileInstallStatus::Success => {
|
ProfileInstallStatus::Success => {
|
||||||
profile_view = profile_view.push(text("Installed"));
|
profile_view = profile_view.push(text("Installed"));
|
||||||
},
|
}
|
||||||
ProfileInstallStatus::Error(err) => {
|
ProfileInstallStatus::Error(err) => {
|
||||||
profile_view = profile_view.push(text(format!("Failed to install profile: {}", err)));
|
profile_view =
|
||||||
},
|
profile_view.push(text(format!("Failed to install profile: {}", err)));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
profile_view
|
profile_view
|
||||||
|
@ -379,8 +383,8 @@ impl ManagerGUI {
|
||||||
previous_view: ManagerView,
|
previous_view: ManagerView,
|
||||||
can_edit_name: bool,
|
can_edit_name: bool,
|
||||||
) -> Element<Message> {
|
) -> Element<Message> {
|
||||||
let current_mods_directory_display = match &self.profile_edit_settings.mods_dir {
|
let current_instance_directory_display = match &self.profile_edit_settings.instance_dir {
|
||||||
Some(mods_dir) => mods_dir.display().to_string(),
|
Some(instance_dir) => instance_dir.display().to_string(),
|
||||||
None => String::from(""),
|
None => String::from(""),
|
||||||
};
|
};
|
||||||
let mut profile_editor = column![
|
let mut profile_editor = column![
|
||||||
|
@ -405,12 +409,12 @@ impl ManagerGUI {
|
||||||
]
|
]
|
||||||
.spacing(5),
|
.spacing(5),
|
||||||
row![
|
row![
|
||||||
"Mods directory",
|
"Instance directory",
|
||||||
text_input(
|
text_input(
|
||||||
"Browse for your MC instance's mods directory",
|
"Browse for your MC instance directory (contains your mods folder)",
|
||||||
¤t_mods_directory_display
|
¤t_instance_directory_display
|
||||||
),
|
),
|
||||||
button("Browse").on_press(Message::BrowseModsDir)
|
button("Browse").on_press(Message::BrowseInstanceDir)
|
||||||
]
|
]
|
||||||
.spacing(5),
|
.spacing(5),
|
||||||
row![
|
row![
|
||||||
|
|
Loading…
Reference in a new issue