feat: command to change load order of mods (#27)

* progress

* progress

* feat: progress

* progress

* done, needs more testing, pr -> change all functions to require -n before specifying name -> v0.4 (reindex db)

* testing done
This commit is contained in:
v4n
2025-03-18 16:18:48 +02:00
committed by GitHub
parent 6b968172ea
commit a8e94c19fb
+192 -9
View File
@@ -304,6 +304,19 @@ Import mods and database from an archive file (coming from h2mm).
EOF
}
function display_order_help() {
cat << EOF
Usage: h2mm order [OPTIONS] [MOD_NAME|MOD_INDEX] <NEW_INDEX>
Change order of a mod by name or index.
Options:
-i <index> Index of the mod to order.
-n <index> New index of the mod.
Usage:
h2mm order -n "Example mod" 1
h2mm order -i 1 2
EOF
}
function display_modpack_list_help() {
cat << EOF
Usage: h2mm modpack-list
@@ -519,8 +532,8 @@ function mod_disable() {
fi
# 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
current_mod_files=$(get_files_by_entry_from_db "$entry")
for file in $current_mod_files; do
[[ ! -f "$MODS_DIR/$file" ]] && { log ERROR "Mod file $file does not exist."; exit 1; }
disabled_file="disabled_$file"
@@ -538,7 +551,7 @@ function mod_disable() {
done
# downgrade mods with greater version number
downgrade_mods "$files"
downgrade_mods "$current_mod_files"
# update the database
sed -i "/^$mod_index,/s/ENABLED/DISABLED/" "$DB_FILE"
@@ -575,13 +588,13 @@ function mod_enable() {
[[ "$status" == "ENABLED" ]] && { log ERROR "Mod $mod_name is already enabled."; exit 1; }
files=$(get_files_by_entry_from_db "$entry")
current_mod_files=$(get_files_by_entry_from_db "$entry")
# upgrade mods with lower version number
upgrade_mods "$files"
upgrade_mods "$current_mod_files"
# enable each mod file by removing disabled_ from the start of the filename
for file in $files; do
for file in $current_mod_files; do
enabled_file=$(remove_disabled_prefix "$file")
# check if the files exists
@@ -836,9 +849,9 @@ function mod_uninstall() {
get_mod_name_and_index
# delete mod files
files=$(get_files_by_entry_from_db "$entry")
current_mod_files=$(get_files_by_entry_from_db "$entry")
for file in $files; do
for file in $current_mod_files; do
[[ ! -f "$MODS_DIR/$file" ]] && { log ERROR "Mod file $file does not exist."; exit 1; }
log INFO "Removing ${ORANGE}\$MODS_DIR/$file${NC}."
@@ -848,7 +861,7 @@ function mod_uninstall() {
done
# downgrade mods with greater version number, only if the mod is enabled
[[ "$status" == "ENABLED" ]] && downgrade_mods "$files"
[[ "$status" == "ENABLED" ]] && downgrade_mods "$current_mod_files"
# remove entry from database
sed -i "/^$mod_index,/d" "$DB_FILE"
@@ -985,6 +998,173 @@ function mod_import() {
log INFO "Mods ${GREEN}successfully${NC} imported."
}
function mod_order {
[[ "$1" == "--help" || "$1" == "-h" ]] && { display_order_help; exit 0; }
local mod_name=""
local mod_index=""
local new_index=""
# parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
-i)
mod_index="$2"; shift 2
;;
-n)
mod_name="$2"; shift 2
;;
--help|-h)
display_order_help; exit 0
;;
*)
new_index="$1"; shift 1
;;
esac
done
[[ -z "$mod_name" && -z "$mod_index" ]] && { log ERROR "Mod name or index is required to change order."; exit 1; }
# find mod files
get_mod_name_and_index
# get the number of mods
mod_count=$(($(cat "$DB_FILE" | wc -l) - 1))
# check if the mod is already at the desired index and if new index is valid
[[ "$mod_index" == "$new_index" ]] && { log ERROR "Mod $mod_name is already at index $new_index."; exit 1; }
[[ $new_index -lt 1 ]] && { log ERROR "Index can not be less than 1."; exit 1; }
[[ $new_index -gt $mod_count ]] && { log ERROR "Index can not be more than mod count (${mod_count})."; exit 1; }
# assert ascending or descending order
ascending_order=true
[[ $mod_index -gt $new_index ]] && ascending_order=false
# get entries between the indexes
if [[ $ascending_order == true ]]; then
entries=$(awk -v mod_index="$mod_index" -v new_index="$new_index" -F, 'NR > 1 && $1 > mod_index && $1 <= new_index {print $0}' "$DB_FILE")
else
entries=$(awk -v mod_index="$mod_index" -v new_index="$new_index" -F, 'NR > 1 && $1 < mod_index && $1 >= new_index {print $0}' "$DB_FILE")
fi
# get files for the current mod
IFS= current_mod_files=$(get_files_by_entry_from_db "$entry"); unset IFS
# step 1 - count how many replaces need to be done in hash table
declare -A replace_count
for file in $current_mod_files; do
extension=$(get_extension "$file")
basename=$(get_basename "$file")
# if the file has no extension, add the basename to the hash table or increment the count
if [[ -z "$extension" ]]; then
replace_count["$basename"]=$(( ${replace_count["$basename"]:-0} + 1 ))
fi
done
# step 2.1 - iterate over the basenames, find and store the files with the same basenames as the ones we want to upgrade/downgrade
declare -A files_to_replace
for basename in "${!replace_count[@]}"; do
IFS= files_to_replace["$basename"]=$(echo "$entries" | awk -F, '{print $4}' | grep -o "$basename\.patch_[^ ]*"); unset IFS
done
# step 2.2 - reverse sort the files_to_replace if we are in descending order
[[ $ascending_order == false ]] && for basename in "${!files_to_replace[@]}"; do
files_to_replace["$basename"]=$(echo "${files_to_replace["$basename"]}" | sort -rV)
done
# move current mod files to "t_$file" so we can move the new files to the correct index
for file in $current_mod_files; do
mv "$MODS_DIR/$file" "$MODS_DIR/t_$file"
[[ $? -ne 0 ]] && { log ERROR "Could not move mod file $file."; exit 1; }
done
# step 3 - for every basename in files_to_replace, add replace_count to each file's patch number and move
for basename in "${!files_to_replace[@]}"; do
files=$(echo "${files_to_replace["$basename"]}" | tr ' ' '\n')
for file in $files; do
basename=$(get_basename "$file")
patch_number=$(get_patch_number "$file")
extension=$(get_extension "$file")
# if ascending order, subtract replace_count to the patch number, otherwise add
operation="-"; [[ $ascending_order == false ]] && operation="+"
new_file="${basename}.patch_$(($patch_number $operation ${replace_count["$basename"]}))${extension}"
mv "$MODS_DIR/$file" "$MODS_DIR/$new_file"
[[ $? -ne 0 ]] && { log ERROR "Could not move mod file $file."; exit 1; }
log INFO "Reindexing ${ORANGE}$file${NC} to ${GREEN}$new_file${NC}."
# update db
sed -i "s/\(\b$file\b\)/$new_file/" "$DB_FILE"
done
done
# step 4 - for every file in current_mod_files, subtract the basename's array size of files_to_replace from the patch number
for file in $current_mod_files; do
basename=$(get_basename "$file")
patch_number=$(get_patch_number "$file")
extension=$(get_extension "$file")
# get size of only files without an extension
size=$(echo "${files_to_replace["$basename"]}" | grep -E "${basename}.patch_[0-9]+$" | tr ' ' '\n' | wc -l)
# in case size is 0, move t_file back to file later
new_file=${file}
if [[ $size -gt 0 ]]; then
# if ascending order, add size from the patch number, otherwise subtract
operation="+"; [[ $ascending_order == false ]] && operation="-"
new_file="${basename}.patch_$(($patch_number $operation $size))${extension}"
log INFO "Reindexing ${ORANGE}$file${NC} to ${GREEN}$new_file${NC}."
# dont forget current mod files are prefixed with t_
mv "$MODS_DIR/t_$file" "$MODS_DIR/t_$new_file"
[[ $? -ne 0 ]] && { log ERROR "Could not move mod file $file."; exit 1; }
# update file names
entry=$(echo "$entry" | sed "s/\(\b$file\b\)/$new_file/")
fi
# move back temp file
mv "$MODS_DIR/t_$new_file" "$MODS_DIR/$new_file"
# update index (will be replaced in db later)
entry=$(echo "$entry" | sed "s/^$mod_index,/$new_index,/")
done
# step 5.1 - change current mod to t_index so we can move the other indexes
sed -i "s/^$mod_index,/t_$mod_index,/" "$DB_FILE"
# step 5.2 - reindex the rest of the mods
idxs=$(echo "$entries" | awk -F, '{print $1}' | tr ' ' '\n')
# step 5.3 - reverse sort the idxs if we are in descending order
[[ $ascending_order == false ]] && idxs=$(echo "$idxs" | sort -r)
for idx in $idxs; do
# if ascending order, subtract 1 from the index, otherwise add 1
operation="-"; [[ $ascending_order == false ]] && operation="+"
sed -i "s/^$idx,/$(($idx $operation 1)),/" "$DB_FILE"
done
# step 6 - move the entry to the new index
sed -i "/^t_$mod_index,/d" "$DB_FILE"
# weird edge case where the new index is the last index
if [[ $((new_index + 1)) -gt $(wc -l < "$DB_FILE") ]]; then
echo "$entry" >> "$DB_FILE"
else
sed -i "$((new_index + 1))i $entry" "$DB_FILE"
fi
log INFO "Mod ${GREEN}successfully${NC} reindexed: $mod_name from $mod_index to $new_index."
}
# --- Modpacks management ---
function modpack_list() {
@@ -1241,6 +1421,9 @@ function main() {
"import"|"im")
mod_import "$@"
;;
"order"|"o")
mod_order "$@"
;;
"modpack-list"|"ml")
modpack_list "$@"
;;