Skip to content

Commit 7d71d36

Browse files
Add support for user added mods
1 parent 9a39508 commit 7d71d36

File tree

8 files changed

+98
-51
lines changed

8 files changed

+98
-51
lines changed

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ jobs:
107107
files: ./out/*
108108
name: Ferium ${{ github.ref_name }}
109109
body: |
110-
Compiled binaries for Ferium version `${{ github.ref_name }}` ([changelog](https://github.com/theRookieCoder/ferium/blob/main/CHANGELOG.md#$MD_HEADER))
110+
Compiled binaries for Ferium version `${{ github.ref_name }}` ([changelog](https://github.com/theRookieCoder/ferium/blob/main/CHANGELOG.md#${{ $MD_HEADER }}))
111111
112112
The provided binaries are for:
113113

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Changelog for Ferium
22

3+
## `v3.27.0`
4+
### 07.05.2022
5+
6+
- Added a `.old` directory in the output directory to store 'deleted' mods
7+
- Update the output directory checking code so that the backup is only requested when there are files (because directories will not be deleted)
8+
- Added support for user installed mods
9+
- These are read from `<output_dir>/user`
10+
- There are now download and install messages when upgrading
11+
312
## `v3.26.0`
413
### 05.05.2022
514

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[package]
22
name = "ferium"
3-
version = "3.26.0"
3+
version = "3.27.0"
44
edition = "2021"
5-
authors = ["Ilesh Thiada (theRookieCoder) <[email protected]>", "Daniel Hauck (SolidTux)"]
5+
authors = ["Ilesh Thiada (theRookieCoder) <[email protected]>", "薛詠謙 (KyleUltimate)", "Daniel Hauck (SolidTux)"]
66
description = "Ferium is a CLI program for managing Minecraft mods from Modrinth, CurseForge, and Github Releases"
77
exclude = ["tests"] # Don't need tests for installing using crates.io
88
repository = "https://github.com/theRookieCoder/ferium"

README.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,12 @@ When you first start up, you will have to create a new profile by running `feri
7373
- You can find these at the top left part of the repository's page as a big 'owner / name'
7474
- So to add [Sodium](https://github.com/CaffeineMC/sodium-fabric), you should run `ferium add-github CaffeineMC sodium-fabric` (again, case-insensitive)
7575
- Note: The GitHub repository has to release JAR files in their Releases for Ferium to download, or else it will refuse to be added
76+
- External mods
77+
- If you want to add files that are not on Modrinth, CurseForge, or GitHub Release, place them in a `user` folder in the output directory. Files here will be copied to the output directory when upgrading
7678

7779
### Upgrading Mods
7880

79-
> _Warning: Upgrading will empty the output directory before downloading your mods!_
80-
> If your output directory is not empty when setting it, Ferium will offer to create a backup
81+
> If your output directory is not empty when setting it, Ferium will offer to create a backup. Please do so if it contains any files you would like to keep
8182
8283
Now after adding all your mods, run `ferium upgrade` to download all of them to your output directory.
8384
This defaults to `.minecraft/mods`, where `.minecraft` is the default Minecraft resources directory. You don't need to worry about this if you play with Mojang's launcher (unless you changed the resources directory).
@@ -86,14 +87,16 @@ You can choose to pick a custom output directory during profile creation or [cha
8687
If Ferium fails to find a compatible version of a mod, it will print it's name in red and give a reason.It will continue downloading the rest of the mods and will exit with an error.
8788
See the [advanced section](#advanced) for more information.
8889

90+
**WARNING:** _When upgrading, any files not downloaded by Ferium will be moved to the `.old` folder in the output directory_
91+
8992
### Managing Mods
9093

9194
You can see all the mods in your current profile by running `ferium list`. If you want to see more information about them, you can run `ferium list -v` or `ferium list --verbose`.
9295

9396
You can remove any of your mod by runnning `ferium remove`, selecting the ones you would like to remove by using the space key, and pressing enter once you're done.
9497
You can also provide the names of the mods to remove as arguments. Mod names with spaces have to be given in quotes (`ferium remove "ok zoomer"`) or the spaces should be escaped (`ferium remove ok\ zoomer`).
9598

96-
#### Advanced
99+
#### Check Overrides
97100

98101
If some mod is compatible with your profile but Ferium does not download it, [create an issue]((https://github.com/theRookieCoder/ferium/issues/new)) if you think it's a bug. Or else, you can disable the game version or mod loader checks by setting `check_game_version` or `check_mod_loader` to false for the specific mod.
99102

@@ -121,7 +124,7 @@ You can create a profile by running `ferium profile create` and configuring the
121124
- Mod loader
122125

123126
You can also provide these settings as flags to the create command.
124-
If you want to copy the mods from another profile, provide the `--import` flag. You can also provide the profile name to the `--import` flag, if you don't a profile picker will be shown.
127+
If you want to copy the mods from another profile, provide the `--import` flag. You can also provide the profile name to the `--import` flag if you don't a profile picker will be shown.
125128
Finally, Ferium will automatically switch to the newly created profile.
126129

127130
#### Configure
@@ -148,7 +151,7 @@ Firstly, you need the Rust toolchain which includes `cargo`, `rustup`, etc. You
148151
You'll also need the [Just](https://github.com/casey/just#installation) command runner, its basically a much better version of `make`.
149152

150153
If you want to build Ferium without cloning the repo, set the `CURSEFORGE_API_KEY` environment variable, then run `cargo install ferium`.
151-
If you don't have a CurseForge API key you can set the variable to an empty value, however anything using the CurseForge API will not work.
154+
If you don't have a CurseForge API key, you can set the variable to an empty value. However anything using the CurseForge API will not work.
152155

153156
To build the project and install it to your Cargo binary directory, clone the project and run `just install`. If you want to install it for testing a developement version, run `just` (alias for `just install-dev`).
154157

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ async fn actual_main() -> Result<()> {
199199
SubCommands::Upgrade => {
200200
check_internet().await?;
201201
check_empty_profile(profile)?;
202-
create_dir_all(&profile.output_dir).await?;
202+
create_dir_all(&profile.output_dir.join(".old")).await?;
203203
upgrade(&modrinth, &curseforge, &github, profile).await?;
204204
},
205205
};

src/subcommands/profile/mod.rs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,18 @@ mod list;
55
pub use configure::configure;
66
pub use create::create;
77
pub use delete::delete;
8-
use fs_extra::dir::{copy, CopyOptions};
98
pub use list::list;
109

1110
use crate::THEME;
1211
use anyhow::{bail, Result};
1312
use colored::Colorize;
1413
use dialoguer::{Confirm, Select};
14+
use fs_extra::dir::{copy, CopyOptions};
1515
use libium::{
1616
config::{self, structs::ModLoader},
1717
file_picker, misc, HOME,
1818
};
1919
use std::path::PathBuf;
20-
use tokio::fs::read_dir;
2120

2221
fn pick_mod_loader(default: Option<&ModLoader>) -> Result<ModLoader> {
2322
let mut picker = Select::with_theme(&*THEME);
@@ -66,13 +65,29 @@ async fn check_output_directory(output_dir: &PathBuf) -> Result<()> {
6665
if output_dir.file_name().unwrap() != "mods" {
6766
println!("{}", "Warning! The output directory is not called `mods`. Most mod loaders will load a directory called `mods`.".bright_yellow());
6867
}
69-
if output_dir.exists()
70-
&& read_dir(output_dir).await?.next_entry().await?.is_some()
71-
&& Confirm::with_theme(&*THEME)
72-
.with_prompt("Your output directory is not empty and it will be erased when upgrading. Would like to create a backup of it?")
73-
.interact()? {
74-
let backup_dir = file_picker::pick_folder(&*HOME, "Where should the backup be made?").await.unwrap();
75-
copy(output_dir, backup_dir, &CopyOptions::new())?;
68+
let mut backup = false;
69+
if output_dir.exists() {
70+
for file in std::fs::read_dir(output_dir)? {
71+
let file = file?;
72+
if file.path().is_file() && file.file_name() != ".DS_Store" {
73+
backup = true;
74+
break;
75+
}
76+
}
77+
}
78+
if backup {
79+
println!(
80+
"There are files in your output directory, these will be deleted when you upgrade."
81+
);
82+
if Confirm::with_theme(&*THEME)
83+
.with_prompt("Would like to create a backup?")
84+
.interact()?
85+
{
86+
let backup_dir = file_picker::pick_folder(&*HOME, "Where should the backup be made?")
87+
.await
88+
.unwrap();
89+
copy(output_dir, backup_dir, &CopyOptions::new())?;
90+
}
7691
}
7792
Ok(())
7893
}

src/subcommands/upgrade.rs

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ use crate::{CROSS, TICK, YELLOW_TICK};
22
use anyhow::{bail, Result};
33
use colored::Colorize;
44
use ferinth::Ferinth;
5+
use fs_extra::file::{move_file, CopyOptions};
56
use furse::Furse;
67
use itertools::Itertools;
78
use libium::config;
89
use libium::upgrade;
910
use octocrab::Octocrab;
10-
use tokio::fs::remove_file;
11+
use std::fs::read_dir;
12+
use tokio::fs::copy;
1113

1214
struct Downloadable {
1315
filename: String,
@@ -183,41 +185,59 @@ pub async fn upgrade(
183185
);
184186
}
185187

186-
eprint!("\n{}", "Downloading Mod Files... ".bold());
187-
for file in std::fs::read_dir(&profile.output_dir)? {
188+
let mut to_install = Vec::new();
189+
if profile.output_dir.join("user").exists() {
190+
for file in read_dir(&profile.output_dir.join("user"))? {
191+
let file = file?;
192+
let path = file.path();
193+
if path.is_file() {
194+
to_install.push((file.file_name(), path));
195+
}
196+
}
197+
}
198+
199+
println!("\n{}\n", "Downloading Mod Files".bold());
200+
201+
for file in read_dir(&profile.output_dir)? {
188202
let file = file?;
189-
let path = file.path();
190-
if path.is_file() {
191-
let mut index = None;
192-
// If a file is already downloaded
193-
if let Some(downloadable) = to_download
203+
if file.file_type()?.is_file() {
204+
let filename = file.file_name();
205+
let filename = filename.to_str().unwrap();
206+
if let Some((index, _)) = to_download
194207
.iter()
195-
.find_position(|thing| file.file_name().to_str().unwrap() == thing.filename)
208+
.find_position(|thing| filename == thing.filename)
196209
{
197-
index = Some(downloadable.0);
198-
}
199-
match index {
200-
// Then don't download the file
201-
Some(index) => {
202-
to_download.swap_remove(index);
203-
},
204-
// Or else delete the file
205-
None => remove_file(path).await?,
210+
to_download.swap_remove(index);
211+
} else if let Some((index, _)) =
212+
to_install.iter().find_position(|thing| filename == thing.0)
213+
{
214+
to_install.swap_remove(index);
215+
} else {
216+
let _ = move_file(
217+
file.path(),
218+
profile.output_dir.join(".old").join(filename),
219+
&CopyOptions::new(),
220+
);
206221
}
207222
}
208223
}
209-
match {
210-
for downloadable in to_download {
211-
let contents = reqwest::get(downloadable.download_url)
212-
.await?
213-
.bytes()
214-
.await?;
215-
upgrade::write_mod_file(profile, contents, &downloadable.filename).await?;
216-
}
217-
Ok::<(), anyhow::Error>(())
218-
} {
219-
Ok(_) => println!("{}", *TICK),
220-
Err(_) => bail!("{}", CROSS),
224+
225+
for downloadable in to_download {
226+
eprint!("Downloading {}... ", downloadable.filename.dimmed());
227+
let contents = reqwest::get(downloadable.download_url)
228+
.await?
229+
.bytes()
230+
.await?;
231+
upgrade::write_mod_file(profile, contents, &downloadable.filename).await?;
232+
println!("{}", &*TICK);
233+
}
234+
for installable in to_install {
235+
eprint!(
236+
"Installing {}... ",
237+
installable.0.to_string_lossy().dimmed()
238+
);
239+
copy(installable.1, profile.output_dir.join(installable.0)).await?;
240+
println!("{}", &*TICK);
221241
}
222242

223243
if error {

0 commit comments

Comments
 (0)