12 Commits

Author SHA1 Message Date
v4n 09c40aa25f bump: version 2025-02-05 23:32:50 +02:00
Matt Cavanagh 1a01034816 feat: added self update mechanism (#15)
* feat: Added self update mechanism

* fix: Formatting

* fix: Formatting again

* fix: Arghhh INDENTATION

* feat: Updated readme with new command

* feat: Added shortcut command

* feat: Added more detail to the readme

* feat: Removed the check for the last update file. It seems a bit pointless to delay checking for it, it's curling github so it's not like it's a DDOS concern, and it could be the tool is getting updated often, why delay their updates? It's also just a curl, it's very inexpensive.

* feat: Removed REPO_URL, it was unused

* feat: Reverted version testing change (oops)

* feat: checking for updates timed at 1 hour

* fix: info messages formatting

---------

Co-authored-by: v4n <105587619+v4n00@users.noreply.github.com>
2025-02-05 23:29:21 +02:00
v4n 83e2161456 code: removed unnecessary parameter passing 2025-02-05 17:09:15 +02:00
Luca Saalfeld a2b904caa2 feat: prevent the same mod from being installed multiple times (#14)
* prevent the same mod from being installed multiple times

* use flag instead of variable for exit check
2025-02-05 16:59:00 +02:00
v4n 9f622ca130 bump: version 2025-02-05 16:27:04 +02:00
v4n 0ab273977e fix: function return typo, trailing slashes removed from paths (#13) 2025-02-05 16:23:32 +02:00
Luca Saalfeld 388afe69b7 fix: bash date comparison (#12) 2025-02-05 15:22:10 +02:00
v4n 3f0305c741 feat: steam deck install 2025-02-03 02:08:50 +02:00
v4n 1a6a13a621 fix: enable/disable logic 2025-02-03 01:49:41 +02:00
Harsh Shandilya 23cb0646bf Use portable shebangs (#7)
* h2mm: use portable shebang

* install.sh: use portable shebang
2025-01-28 11:49:51 +02:00
v4n 6768cab391 fix: disabling/enabling correctly downgrades/upgrades 2025-01-27 00:10:44 +02:00
v4n 35902f1f8f fix: downgrade logic 2025-01-23 09:29:48 +02:00
4 changed files with 1418 additions and 1322 deletions
+13 -4
View File
@@ -4,9 +4,9 @@ Helldivers 2 Mod Manager CLI is a command line interface for managing Helldivers
This script is complete, the version will always [remain at 0.x.x](https://0ver.org/)
## Installation and updating
## Installation
To install/update Helldivers 2 Mod Manager CLI run the following command in your terminal:
To install Helldivers 2 Mod Manager CLI run the following command in your terminal:
```bash
bash -c "$(curl -fsSL https://raw.githubusercontent.com/v4n00/h2mm-cli/refs/heads/master/install.sh)"
@@ -16,7 +16,8 @@ bash -c "$(curl -fsSL https://raw.githubusercontent.com/v4n00/h2mm-cli/refs/head
> Running this script will require sudo permissions. **DO NOT TRUST** random scripts from the internet. If you want to review the script before running it, check out the mod repository for yourself.
If for some reason, the installation command doesn't work you can:
1. Go to https://raw.githubusercontent.com/v4n00/h2mm-cli/refs/heads/master/install.sh
1. Go to <https://raw.githubusercontent.com/v4n00/h2mm-cli/refs/heads/master/install.sh>
1. Right click -> Save page as...
1. Go to your downloads folders `cd ~/Downloads`
1. Give the script execution permissions `chmod +x install.sh`
@@ -46,6 +47,7 @@ h2mm
- `modpack-overwrite` - Overwrite a modpack by name (or index).
- `modpack-reset` - Reset all installed modpacks.
- `reset` - Reset all installed mods.
- `update` - Update h2mm to latest version.
- `help` - Display this help message.
### Basic usage
@@ -85,6 +87,12 @@ h2mm disable -i 1 # disable mod with index 1
h2mm list
```
#### Updating the script
```bash
h2mm update
```
## Compatibility
The script is developed and tested on Arch Linux, but it should work on other Linux distributions as well. If you encounter any issues, please open an issue on the repository.
@@ -92,7 +100,7 @@ The script is developed and tested on Arch Linux, but it should work on other Li
Status of platforms:
- Linux :white_check_mark:
- Steam Deck - untested (should work) :grey_question:
- Steam Deck :white_check_mark:
- Windows - WSL :white_check_mark:
> The script works on WSL, but you need to specify the path to the Helldivers 2 mods directory manually, to find your Windows partition head to `/mnt/` and from there go to your Helldivers 2 data directory, on a typical install it should be on `/mnt/c/Program\ Files\ \(x86\)/Steam/steamapps/common/Helldivers\ 2/data`. You also need to have `unzip` installed, which can be done by running `sudo apt install unzip`.
@@ -117,6 +125,7 @@ You can use the short form of commands to save some time. The shortcuts are:
- `mo` for `modpack-overwrite`
- `mr` for `modpack-reset`
- `r` for `reset`
- `up` for `update`
### Modpacks support
+184 -114
View File
@@ -1,6 +1,6 @@
#!/bin/bash
#!/usr/bin/env bash
VERSION="0.3.0"
VERSION="0.3.5"
# --- Globals ---
@@ -17,7 +17,6 @@ MODPACKS_DB_FILE=""
LAST_CHECKED_UPDATE_FILE="${HOME}/.config/h2mm/last_update"
VERSION_URL="https://raw.githubusercontent.com/v4n00/h2mm-cli/refs/heads/master/version"
REPO_URL="https://github.com/v4n00/h2mm-cli"
# --- Utility Functions ---
@@ -25,11 +24,6 @@ function get_version_major() {
echo "$1" | awk -F. '{print $2}'
}
BREAKING_CHANGES_DB_FILE_PATCHES=(
["2"]='sed -i "s/^\([0-9]\+\),/\1,ENABLED,/" $1'
["3"]='sed -i "1 i\\3" "$1"'
)
function get_filename_without_path() {
echo "$1" | awk -F/ '{print $NF}'
}
@@ -38,10 +32,6 @@ function get_basename() {
get_filename_without_path "$1" | sed -E 's/\.+.*//'
}
function get_basename_with_patch_without_extension() {
echo "$1" | sed -E "s/(.*[0-9]+).*/\1/"
}
function get_extension() {
get_filename_without_path "$1" | sed -E 's/.*patch_[0-9]+//'
}
@@ -54,6 +44,14 @@ function disable_all_modpacks() {
sed -i 's/ENABLED/DISABLED/' "$MODPACKS_DB_FILE"
}
function remove_disabled_prefix() {
local _file="$1"
while [[ "$_file" == disabled_* ]]; do
_file=$(echo "$_file" | sed 's/^disabled_//')
done
echo "$_file"
}
function get_mod_name_and_index() {
if [[ -n "$mod_index" ]]; then # if mod index exists
entry=$(grep "^${mod_index}," "$DB_FILE")
@@ -64,9 +62,13 @@ function get_mod_name_and_index() {
fi
if [[ -z "$entry" || -z "$mod_index" || -z "$mod_name" ]]; then
if [[ "$1" == "--do-not-exit" ]]; then
mod_index=-1
else
echo -e "${RED}Error${NC}: Mod not found." >&2
exit 1
fi
fi
status=$(echo "$entry" | awk -F, '{print $2}')
}
@@ -97,33 +99,30 @@ function find_game_directory() {
echo "$saved_dir"
return
else
echo "Saved game directory is invalid."
echo -e "${RED}Error${NC}: Saved game directory is invalid. Proceeding to get a new directory." >&2
fi
fi
# first time setup, or directory is not valid anymore
echo "Searching for the Helldivers 2 data directory..." >&2
game_dir=$(find "$search_dir" -type d -path "*/$target_dir" 2>/dev/null | head -n 1)
echo "Searching for the Helldivers 2 data directory... (20 seconds timeout)" >&2
game_dir=$(timeout 20 find "$search_dir" -type d -path "*/$target_dir" 2>/dev/null | head -n 1)
if [[ -z "$game_dir" ]]; then
echo "Could not find the Helldivers 2 data directory automatically." >&2
IFS= read -ep "Please enter the path to the Helldivers 2 data directory: " game_dir
if [[ ! -d "$game_dir" ]]; then
echo -e "${RED}Error${NC}: Provided path is not a valid directory." >&2
exit 1
fi
game_dir="$(realpath "${game_dir/#\~/$HOME}")"
[[ ! -d "$game_dir" ]] && { echo -e "${RED}Error${NC}: Provided path is not a valid directory." >&2; exit 1; }
fi
# save path
mkdir -p "$(dirname "$H2PATH")"
echo "$game_dir" > "$H2PATH"
if [[ $? -eq 0 ]]; then
[[ $? -ne 0 ]] && { echo -e "${RED}Error${NC}: Could not save game directory." >&2; exit 1; }
echo -e "Game directory ${GREEN}saved${NC}: $game_dir" >&2
else
echo -e "${RED}Error${NC}: Could not save game directory." >&2
exit 1
fi
# return the directory
echo "$game_dir"
}
@@ -135,6 +134,7 @@ function initialize_directories() {
touch "$DB_FILE"
[[ $? -ne 0 ]] && { echo -e "${RED}Error${NC}: Could not create database file." >&2; exit 1; }
echo "$VERSION" | awk -F. '{print $2}' > "$DB_FILE"
echo -e "Database file ${GREEN}created${NC}: $DB_FILE" &>2
fi
@@ -173,6 +173,7 @@ function display_help() {
echo " modpack-overwrite Overwrite a modpack by name (or index)."
echo " modpack-reset Reset all installed modpacks."
echo " reset Reset all installed mods."
echo " update Update h2mm to the latest version."
echo " help Display this help message."
echo "For more information on usage, use h2mm [command] --help."
echo "Basic Usage:"
@@ -182,7 +183,7 @@ function display_help() {
}
function display_install_help() {
echo "Usage: h2mm install [options] <mod_files|mod_dir|mod_zip>"
echo "Usage: h2mm install [options] <mod_files|mod_dirs|mod_zips>"
echo "Short form: h2mm i"
echo "Options:"
echo " -n \"<mod_name>\" Name the mod yourself, inside double quotes."
@@ -309,11 +310,11 @@ function display_modpack_overwrite_help() {
function check_for_updates() {
if [[ -f "$LAST_CHECKED_UPDATE_FILE" ]]; then
last_update=$(cat "$LAST_CHECKED_UPDATE_FILE")
if [[ $(date +%Y-%m-%d) -gt $(date +%Y-%m-%d -d "$last_update + 3 days") ]]; then
if [[ "$(date +%s)" -lt "$(date +%s -d "$last_update + 1 hour")" ]]; then
return
fi
else
echo $(date +%Y-%m-%d) > "$LAST_CHECKED_UPDATE_FILE"
echo "$(date +%Y-%m-%dT%H:%M:%S)" > "$LAST_CHECKED_UPDATE_FILE"
exit 0
fi
@@ -324,11 +325,108 @@ function check_for_updates() {
fi
if [[ "$latest_version" != "$VERSION" ]]; then
echo -e "${RED}!${NC} A new version of h2mm is available: ${ORANGE}$VERSION${NC} -> ${GREEN}$latest_version${NC}" >&2
echo -e "${RED}!${NC} You can download it from: $REPO_URL" >&2
echo -e "${ORANGE}Info:${NC} A new version of h2mm is available: ${ORANGE}$VERSION${NC} -> ${GREEN}$latest_version${NC}" >&2
echo -e "${ORANGE}Info:${NC} Run \"h2mm update\" to update." >&2
fi
echo $(date +%Y-%m-%d) > "$LAST_CHECKED_UPDATE_FILE"
echo "$(date +%Y-%m-%dT%H:%M:%S)" > "$LAST_CHECKED_UPDATE_FILE"
}
# Upgrade/downgrade logic
function downgrade_mods() {
local files="$1"
declare -A downgrades_versions
declare -A downgrades_to_apply
for file in $files; do
# save the basename for the files that were deleted into a hash table, so we can downgrade mods with greater version number
# also depending on how many patches the mod has, we need to downgrade with more versions
current_version=$(echo "$file" | grep -oP '(?<=patch_)\d+')
base_name=$(get_basename "$file")
downgrades_versions["$base_name"]=$current_version
# count how many unique patches the mod has
[[ -z "${downgrades_to_apply["$base_name"]+unset}" ]] && downgrades_to_apply["$base_name"]=$(echo $files | tr ' ' '\n' | grep "$base_name" | sed -E "s/(.*_[0-9]+).*/\1/" | sort -u | wc -l)
done
# downgrade any necessary mods - it takes ~20 minutes to re-understand this code so I'm writing a comment here
# for each base name, find all files that have the same base name, and are greater than the current version, and downgrade them
# the downgrades_versions hash table stores the current version, so we can compare it with the version of the files
# the downgrades_to_apply hash table stores the number of patches the mod has, so we can downgrade with more versions
# for example, if we have:
# mod 1: AAA.patch_0 AAA.patch_0.stream AAA.patch_1 AAA.patch_1.stream
# mod 2: AAA.patch_2 AAA.patch_2.stream AAA.patch_3 AAA.patch_3.stream
# mod 3: AAA.patch_4 AAA.patch_4.stream AAA.patch_5 AAA.patch_5.stream
# if we remove mod with index 2, we need to downgrade mod with index 3 (which has patch version 4 and 5 > 3 (biggest last patch removed)) by 2 versions,
# the number 2 we get by counting the number of unique base names (without extensions like .stream, but with the .patch_[0-9]) in the files
for base_name in "${!downgrades_to_apply[@]}"; do
# find all files that have the same base name, and are greater than the current version, and downgrade them
IFS=$'\n' mods_to_downgrade=($(ls "$MODS_DIR/$base_name"* 2>/dev/null | sort -V)); unset IFS
for mod in "${mods_to_downgrade[@]}"; do
mod=$(get_filename_without_path "$mod")
patch_version=$(echo $mod | grep -oP '(?<=patch_)\d+')
if [[ $patch_version -gt ${downgrades_versions[$base_name]} ]]; then
new_version=$((patch_version - downgrades_to_apply["$base_name"]))
extension=$(get_extension "$mod")
new_patch="${base_name}.patch_${new_version}${extension}"
mv "$MODS_DIR/$mod" "$MODS_DIR/$new_patch"
[[ $? -ne 0 ]] && { echo -e "${RED}Error${NC}: Could not downgrade mod file $mod." >&2; exit 1; }
echo -e "Downgraded ${ORANGE}$mod${NC} to ${GREEN}\$MODS_DIR/$new_patch${NC}." >&2
# save changes in database as well
sed -i "s/\(\b$mod\b\)/$new_patch/" "$DB_FILE"
fi
done
done
}
function upgrade_mods() {
local files="$1"
declare -A upgrade_versions
declare -A upgrades_to_apply
# opposite of downgrade_mods
for file in $files; do
# remove disabled_ prefix if it exists
file=$(remove_disabled_prefix "$file")
current_version=$(echo "$file" | grep -oP '(?<=patch_)\d+')
base_name=$(get_basename "$file")
# basically save the lowest number, by limiting the setting of the key to the first time we see it
[[ -z "${upgrade_versions["$base_name"]+unset}" ]] && upgrade_versions["$base_name"]=$current_version
[[ -z "${upgrades_to_apply["$base_name"]+unset}" ]] && upgrades_to_apply["$base_name"]=$(echo $files | tr ' ' '\n' | grep "$base_name" | sed -E "s/(.*_[0-9]+).*/\1/" | sort -u | wc -l)
done
for base_name in "${!upgrades_to_apply[@]}"; do
IFS=$'\n' mods_to_upgrade=($(ls "$MODS_DIR/$base_name"* 2>/dev/null | sort -V)); unset IFS
for mod in "${mods_to_upgrade[@]}"; do
mod=$(get_filename_without_path "$mod")
patch_version=$(echo $mod | grep -oP '(?<=patch_)\d+')
if [[ $patch_version -ge ${upgrade_versions[$base_name]} ]]; then
new_version=$((patch_version + upgrades_to_apply["$base_name"]))
extension=$(get_extension "$mod")
new_patch="${base_name}.patch_${new_version}${extension}"
mv "$MODS_DIR/$mod" "$MODS_DIR/$new_patch"
[[ $? -ne 0 ]] && { echo -e "${RED}Error${NC}: Could not upgrade mod file $mod." >&2; exit 1; }
echo -e "Upgraded ${ORANGE}$mod${NC} to ${GREEN}\$MODS_DIR/$new_patch${NC}." >&2
sed -i "s/\(\b$mod\b\)/$new_patch/" "$DB_FILE"
fi
done
done
}
# Mod management
@@ -360,7 +458,7 @@ function mod_disable() {
fi
# find mod files
get_mod_name_and_index "$mod_name" "$mod_index"
get_mod_name_and_index
if [[ "$status" == "DISABLED" ]]; then
echo -e "${RED}Error${NC}: Mod $mod_name is already disabled." >&2
@@ -370,25 +468,30 @@ function mod_disable() {
# disable each mod file by adding disabled_ to the start of the filename
files=$(get_files_by_entry_from_db "$entry")
for file in $files; do
if [[ ! -f "$MODS_DIR/$file" ]]; then
echo -e "${RED}Error${NC}: Mod file $file does not exist." >&2
exit 1
else
[[ ! -f "$MODS_DIR/$file" ]] && { echo -e "${RED}Error${NC}: Mod file $file does not exist." >&2; exit 1; }
disabled_file="disabled_$file"
mv "$MODS_DIR/$file" "$MODS_DIR/$disabled_file"
echo -e "Disabled ${ORANGE}$file${NC} (changed to ${GREEN}\$MODS_DIR/$disabled_file${NC})." >&2
fi
while [[ -f "$MODS_DIR/$disabled_file" ]]; do
disabled_file="disabled_$disabled_file"
done
mv "$MODS_DIR/$file" "$MODS_DIR/$disabled_file"
[[ $? -ne 0 ]] && { echo -e "${RED}Error${NC}: Could not disable mod file $file." >&2; exit 1; }
echo -e "Disabled ${ORANGE}$file${NC} (changed to ${GREEN}\$MODS_DIR/$disabled_file${NC})." >&2
# save change to db
sed -i "/^$mod_index,/ s/\(\b$file\b\)/$disabled_file/" "$DB_FILE"
done
# downgrade mods with greater version number
downgrade_mods "$files"
# update the database
sed -i "/^$mod_index,/s/ENABLED/DISABLED/" "$DB_FILE"
if [[ $? -eq 0 ]]; then
[[ $? -ne 0 ]] && { echo -e "${RED}Error${NC}: Could not disable mod." >&2; exit 1; }
echo -e "Mod $mod_name ${ORANGE}disabled${NC} successfully." >&2
else
echo -e "${RED}Error${NC}: Failed to disable mod." >&2
exit 1
fi
}
function mod_enable() {
@@ -415,35 +518,37 @@ function mod_enable() {
[[ -z "$mod_name" && -z "$mod_index" ]] && { echo -e "${RED}Error${NC}: Mod name or index is required to enable." >&2; exit 1; }
# find mod files
get_mod_name_and_index "$mod_name" "$mod_index"
get_mod_name_and_index
[[ "$status" == "ENABLED" ]] && { echo -e "${RED}Error${NC}: Mod $mod_name is already enabled." >&2; exit 1; }
files=$(get_files_by_entry_from_db "$entry")
# upgrade mods with lower version number
upgrade_mods "$files"
# enable each mod file by removing disabled_ from the start of the filename
for file in $files; do
disabled_file="disabled_$file"
enabled_file=$(remove_disabled_prefix "$file")
# check if the files exists
[[ -f "$MODS_DIR/$disabled_file" ]] || { echo -e "${RED}Error${NC}: Mod file $file does not exist." >&2; exit 1; }
[[ -f "$MODS_DIR/$file" ]] || { echo -e "${RED}Error${NC}: Mod file $file does not exist." >&2; exit 1; }
mv "$MODS_DIR/$disabled_file" "$MODS_DIR/$file"
mv "$MODS_DIR/$file" "$MODS_DIR/$enabled_file"
# check if the file was moved successfully
[[ $? -ne 0 ]] && { echo -e "${RED}Error${NC}: Could not enable mod file $disabled_file." >&2; exit 1; }
echo -e "Enabled ${ORANGE}$disabled_file${NC} (changed to ${GREEN}\$MODS_DIR/$file${NC})." >&2
[[ $? -ne 0 ]] && { echo -e "${RED}Error${NC}: Could not enable mod file $file." >&2; exit 1; }
echo -e "Enabled ${ORANGE}$file${NC} (changed to ${GREEN}\$MODS_DIR/$enabled_file${NC})." >&2
# save change to db
sed -i "/^$mod_index,/ s/\(\b$file\b\)/$enabled_file/" "$DB_FILE"
done
# update the database
sed -i "/^$mod_index,/s/DISABLED/ENABLED/" "$DB_FILE"
if [[ $? -eq 0 ]]; then
[[ $? -ne 0 ]] && { echo -e "${RED}Error${NC}: Could not enable mod." >&2; exit 1; }
echo -e "Mod $mod_name ${GREEN}enabled${NC} successfully." >&2
else
echo -e "${RED}Error${NC}: Failed to enable mod." >&2
exit 1
fi
}
function modpack_reset() {
@@ -561,6 +666,10 @@ function mod_install() {
# verify minimum information required
[[ -z "$mod_name" || ${#mod_files[@]} -eq 0 ]] && { echo -e "${RED}Error${NC}: Mod name and files are required." >&2; exit 1; }
# verify duplicate mod names
get_mod_name_and_index --do-not-exit
[[ $mod_index -ne -1 ]] && { echo -e "${RED}Error${NC}: The mod '$mod_name' is already installed."; exit 1; }
# verify mod files exist
for file in "${mod_files[@]}"; do
[[ ! -f "$file" ]] && { echo -e "${RED}Error${NC}: Mod file $file does not exist." >&2; exit 1; }
@@ -635,12 +744,10 @@ function mod_uninstall() {
[[ -z "$mod_name" && -z "$mod_index" ]] && { echo -e "${RED}Error${NC}: Mod name or index is required to uninstall." >&2; exit 1; }
# find mod files
get_mod_name_and_index "$mod_name" "$mod_index"
get_mod_name_and_index
# delete mod files
files=$(get_files_by_entry_from_db "$entry")
declare -A downgrades_versions
declare -A downgrades_to_remove
for file in $files; do
[[ ! -f "$MODS_DIR/$file" ]] && { echo -e "${RED}Error${NC}: Mod file $file does not exist." >&2; exit 1; }
@@ -648,49 +755,10 @@ function mod_uninstall() {
rm "$MODS_DIR/$file"
[[ $? -ne 0 ]] && { echo -e "${RED}Error${NC}: Could not remove mod file $file." >&2; exit 1; }
# save the basename for the files that were deleted into a hash table, so we can downgrade mods with greater version number
# also depending on how many patches the mod has, we need to downgrade with more versions
current_version=$(echo $file | grep -oP '(?<=patch_)\d+')
base_name_with_patch=$(get_basename_with_patch_without_extension "$file")
base_name=$(get_basename "$file")
downgrades_versions["$base_name_with_patch"]=$current_version
[[ -z "${downgrades_to_remove["$base_name"]+unset}" ]] && downgrades_to_remove["$base_name"]=$(echo $files | sed -E "s/(.*[0-9]+).*/\1/" | wc -l)
done
# downgrade any necessary mods - it takes ~20 minutes to re-understand this code so I'm writing a comment here
# for each base name, find all files that have the same base name, and are greater than the current version, and downgrade them
# the downgrades_versions hash table stores the current version, so we can compare it with the version of the files
# the downgrades_to_remove hash table stores the number of patches the mod has, so we can downgrade with more versions
# for example, if we have:
# mod 1: AAA.patch_0 AAA.patch_0.stream AAA.patch_1 AAA.patch_1.stream
# mod 2: AAA.patch_2 AAA.patch_2.stream AAA.patch_3 AAA.patch_3.stream
# mod 3: AAA.patch_4 AAA.patch_4.stream AAA.patch_5 AAA.patch_5.stream
# if we remove mod 2, we need to downgrade mod 3 (which has version 4 and 5 > 3 (last patch removed)) by 2 versions,
# the number 2 we get by counting the number of unique base names (without extensions like .stream, but with the .patch_[0-9]) in the files
for base_name in "${!downgrades_to_remove[@]}"; do
# find all files that have the same base name, and are greater than the current version, and downgrade them
IFS=$'\n' mods_to_remove=($(ls "$MODS_DIR/$base_name"* 2>/dev/null | sort -V)); unset IFS
base_name_with_patch=$(get_basename_with_patch_without_extension "$file")
for mod in "${mods_to_remove[@]}"; do
mod=$(get_filename_without_path "$mod")
patch_version=$(echo $mod | grep -oP '(?<=patch_)\d+')
if [[ $patch_version -gt ${downgrades_versions[$base_name_with_patch]} ]]; then
new_version=$((patch_version - downgrades_to_remove["$base_name"] - 1))
extension=$(get_extension "$mod")
new_patch="${base_name}.patch_${new_version}${extension}"
mv "$MODS_DIR/$mod" "$MODS_DIR/$new_patch"
[[ $? -ne 0 ]] && { echo -e "${RED}Error${NC}: Could not downgrade mod file $mod." >&2; exit 1; }
echo -e "Downgraded ${ORANGE}$mod${NC} to ${GREEN}\$MODS_DIR/$new_patch${NC}." >&2
# save changes in database as well
sed -i "s/$mod/$new_patch/" "$DB_FILE"
fi
done
done
# downgrade mods with greater version number, only if the mod is enabled
[[ $disabled == false ]] && downgrade_mods "$files"
# remove entry from database
sed -i "/^$mod_index,/d" "$DB_FILE"
@@ -729,7 +797,7 @@ function mod_export() {
[[ $(wc -l < "$DB_FILE") -le 1 ]] && { echo "No modpacks saved."; exit 1; }
if [[ silent == false ]]; then
if [[ $modpack_export == false ]]; then
echo -ne "Archive file will be saved to ${save_dir}/${archive_name}. Make? (Y/n): "
read -r confirm
fi
@@ -784,21 +852,6 @@ function mod_import() {
[[ $? -ne 0 ]] && { echo -e "${RED}Error${NC}: Failed to import mods." >&2; exit 1; }
# handle breaking chanegs
# version 0.2.x has no version number in the database file, so if there's no version number, add it
if [[ $(head -n 1 "$DB_FILE") =~ "[0-9]+," ]]; then
mods_db_current_version=$(head -n 1 "$DB_FILE")
else
mods_db_current_version="2"
fi
mods_db_new_version=$(get_version_major $VERSION)
for ((i = mods_db_current_version + 1; i <= mods_db_new_version; i++)); do
eval $(echo "${BREAKING_CHANGES_DB_FILE_PATCHES[$i]}" | sed "s:\$1:$DB_FILE:")
[[ $? -ne 0 ]] && { echo -e "${RED}Error${NC}: Failed to apply breaking changes patch $i." >&2; exit 1; }
done
echo -e "Mods imported ${GREEN}successfully${NC}." >&2
}
@@ -968,6 +1021,20 @@ function modpack_overwrite() {
sed -i "/^$modpack_index,/s/DISABLED/ENABLED/" "$MODPACKS_DB_FILE"
}
function self_update() {
latest_version=$(curl -sS "$VERSION_URL")
if [[ "$latest_version" == "$VERSION" ]]; then
echo -e "h2mm is already up-to-date." >&2
exit 0
fi
echo -e "Starting update script..." >&2
# Run the installer for the latest version
bash -c "$(curl -fsSL https://raw.githubusercontent.com/v4n00/h2mm-cli/refs/heads/master/install.sh)"
}
# --- Main ---
function main() {
@@ -1025,6 +1092,9 @@ function main() {
version|v|-v|--version)
echo "${VERSION}"
;;
update|up)
self_update
;;
help|--help|-h|h)
display_help
;;
+26 -9
View File
@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -e
RED='\033[0;31m'
@@ -58,8 +58,7 @@ if [[ -x "$(command -v $SCRIPT_NAME)" ]]; then
if [[ $latest_major -gt $installed_major ]]; then
echo -e "${ORANGE}Warning:${NC} Major version upgrade detected."
echo "Check out the changelogs here:"
echo "https://github.com/v4n00/h2mm-cli/releases"
echo "${ORANGE}Info${NC}: Check out the changelogs here -> https://github.com/v4n00/h2mm-cli/releases"
echo "The script will proceed to upgrade ${SCRIPT_NAME} to avoid breaking changes."
# find hd2 path
@@ -80,13 +79,11 @@ if [[ -x "$(command -v $SCRIPT_NAME)" ]]; then
[[ ! -f "$game_dir/mods.csv" ]] && { echo -e "${RED}Error:${NC} mods.csv not found in $game_dir."; exit 1; }
# make backup of mods in case something goes wrong
echo "Creating a backup of mods.csv."
echo "${ORANGE}V${NC} It is advised to make a backup before proceeding."
h2mm export
# iterate from installed major number to latest major number
for ((i = installed_major + 1; i <= latest_major; i++)); do
echo -e "Applying breaking changes patch for version $i."
if [[ -n "${breaking_changes_patches[$i]}" ]]; then
eval $(echo "${breaking_changes_patches[$i]}" | sed "s:\$1:$game_dir:")
else
@@ -107,13 +104,33 @@ fi
# Install
# if steam deck, set destination path to ~/.local/bin
IFS= read -ep "Are you installing on a Steam Deck? (y/N): " response_sd
if [[ "$response_sd" == "y" || "$response_sd" == "Y" ]]; then
# steam deck
DESTINATION_PATH="$HOME/.local/bin"
mkdir -p "$DESTINATION_PATH"
# check if ~/.local/bin is in PATH
if [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then
# add ~/.local/bin to PATH
echo -e "${ORANGE}Warning:${NC} Installing the script on a Steam Deck means adding $DESTINATION_PATH to your \$PATH."
echo -e "${ORANGE}Warning:${NC} If you're using a different shell, you may need to add it manually."
IFS= read -ep "Do you want to add $DESTINATION_PATH to your \$PATH in ~/.bashrc? (Y/n): " response
if [[ "$response" == "y" || "$response" = "Y" || -z "$response" ]]; then
echo "export PATH=\"\$HOME/.local/bin:\$PATH\"" >> "$HOME/.bashrc"
echo -e "${GREEN}Success:${NC} Added $DESTINATION_PATH to your \$PATH in ~/.bashrc."
fi
fi
else
# not steam deck
# set another path if needed
IFS= read -ep "Install the script to $DESTINATION_PATH or specify another path (must be included in \$PATH)? (Y/path): " response
if [[ "$response" != "y" && "$response" != "Y" && -n "$response" ]]; then
DESTINATION_PATH="$response"
if [[ ! -d "$DESTINATION_PATH" ]]; then
echo -e "${RED}Error:${NC} Path $DESTINATION_PATH does not exist."
exit 1
[[ ! -d "$DESTINATION_PATH" ]] && { echo -e "${RED}Error:${NC} Path $DESTINATION_PATH does not exist."; exit 1; }
fi
fi
+1 -1
View File
@@ -1 +1 @@
0.3.0
0.3.5