mirror of
https://github.com/aclist/dztui.git
synced 2025-01-01 15:12:05 +01:00
fix: error handling for no local mods
This commit is contained in:
parent
32a43cd787
commit
8b9f751ff1
4 changed files with 108 additions and 50 deletions
|
@ -1,5 +1,13 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [5.6.0-beta.3] 2024-11-18
|
||||||
|
### Fixed
|
||||||
|
- Improved handling for cases where there are no locally installed mods
|
||||||
|
- Set up mod symlinks at boot, rather than only on server connect
|
||||||
|
- Prevent context menus from opening when table is empty
|
||||||
|
- When reloading table in-place, prevent duplicate panel elements from being added if already present
|
||||||
|
- Clean up signal emission
|
||||||
|
|
||||||
## [5.6.0-beta.2] 2024-11-15
|
## [5.6.0-beta.2] 2024-11-15
|
||||||
### Fixed
|
### Fixed
|
||||||
- Clean up local mod signatures from versions file when deleting mods
|
- Clean up local mod signatures from versions file when deleting mods
|
||||||
|
|
8
dzgui.sh
8
dzgui.sh
|
@ -569,10 +569,10 @@ fetch_helpers_by_sum(){
|
||||||
[[ -f "$config_file" ]] && source "$config_file"
|
[[ -f "$config_file" ]] && source "$config_file"
|
||||||
declare -A sums
|
declare -A sums
|
||||||
sums=(
|
sums=(
|
||||||
["ui.py"]="680ff0e4071681f26409fa3592a41e46"
|
["ui.py"]="4663cdda7bf91a0c594103d6f4382f15"
|
||||||
["query_v2.py"]="55d339ba02512ac69de288eb3be41067"
|
["query_v2.py"]="55d339ba02512ac69de288eb3be41067"
|
||||||
["vdf2json.py"]="2f49f6f5d3af919bebaab2e9c220f397"
|
["vdf2json.py"]="2f49f6f5d3af919bebaab2e9c220f397"
|
||||||
["funcs"]="acd5d85b27141082b25e07138f8b5b54"
|
["funcs"]="5eae515ea2cac2ab38212a529415e86b"
|
||||||
["lan"]="c62e84ddd1457b71a85ad21da662b9af"
|
["lan"]="c62e84ddd1457b71a85ad21da662b9af"
|
||||||
)
|
)
|
||||||
local author="aclist"
|
local author="aclist"
|
||||||
|
@ -874,6 +874,9 @@ stale_mod_signatures(){
|
||||||
fi
|
fi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
create_new_links(){
|
||||||
|
"$HOME/.local/share/$app_name/helpers/funcs" "update_symlinks"
|
||||||
|
}
|
||||||
initial_setup(){
|
initial_setup(){
|
||||||
setup_dirs
|
setup_dirs
|
||||||
setup_state_files
|
setup_state_files
|
||||||
|
@ -895,6 +898,7 @@ initial_setup(){
|
||||||
migrate_files
|
migrate_files
|
||||||
stale_symlinks
|
stale_symlinks
|
||||||
stale_mod_signatures
|
stale_mod_signatures
|
||||||
|
create_new_links
|
||||||
local_latlon
|
local_latlon
|
||||||
is_steam_running
|
is_steam_running
|
||||||
is_dzg_downloading
|
is_dzg_downloading
|
||||||
|
|
|
@ -116,7 +116,7 @@ declare -A funcs=(
|
||||||
["query_config"]="query_config"
|
["query_config"]="query_config"
|
||||||
["start_cooldown"]="start_cooldown"
|
["start_cooldown"]="start_cooldown"
|
||||||
["list_mods"]="list_mods"
|
["list_mods"]="list_mods"
|
||||||
["delete"]="delete_local_mod"
|
["Delete selected mods"]="delete_local_mod"
|
||||||
["align_local"]="align_versions_file"
|
["align_local"]="align_versions_file"
|
||||||
["show_server_modlist"]="show_server_modlist"
|
["show_server_modlist"]="show_server_modlist"
|
||||||
["test_ping"]="test_ping"
|
["test_ping"]="test_ping"
|
||||||
|
@ -131,6 +131,7 @@ declare -A funcs=(
|
||||||
["Handshake"]="final_handshake"
|
["Handshake"]="final_handshake"
|
||||||
["get_player_count"]="get_player_count"
|
["get_player_count"]="get_player_count"
|
||||||
["lan_scan"]="lan_scan"
|
["lan_scan"]="lan_scan"
|
||||||
|
["update_symlinks"]="update_symlinks"
|
||||||
)
|
)
|
||||||
|
|
||||||
lan_scan(){
|
lan_scan(){
|
||||||
|
@ -588,19 +589,34 @@ align_versions_file(){
|
||||||
mv $versions_file.new $versions_file
|
mv $versions_file.new $versions_file
|
||||||
logger INFO "Removed local signatures for the mod '$mod'"
|
logger INFO "Removed local signatures for the mod '$mod'"
|
||||||
}
|
}
|
||||||
|
pluralize(){
|
||||||
|
local plural="$1"
|
||||||
|
local count="$2"
|
||||||
|
local mod
|
||||||
|
local suffix
|
||||||
|
local base
|
||||||
|
local ct
|
||||||
|
local s
|
||||||
|
|
||||||
|
if [[ "${plural: -2}" == "es" ]]; then
|
||||||
|
base="${plural::-2}"
|
||||||
|
suffix="${plural: -2}"
|
||||||
|
ct=$((count^2))
|
||||||
|
[[ $ct -ne 3 ]] && s="$suffix"
|
||||||
|
else
|
||||||
|
base="${plural::-1}"
|
||||||
|
suffix="${plural: -1}"
|
||||||
|
ct=$((count^1))
|
||||||
|
[[ $ct -gt 0 ]] && s="$suffix"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "%s%s" "$base" "$s"
|
||||||
|
}
|
||||||
delete_local_mod(){
|
delete_local_mod(){
|
||||||
shift
|
shift
|
||||||
if [[ -z $1 ]]; then
|
|
||||||
# use multi mode
|
|
||||||
readarray -t symlinks < <(awk '{print $1}' $_cache_mods_temp)
|
readarray -t symlinks < <(awk '{print $1}' $_cache_mods_temp)
|
||||||
readarray -t ids < <(awk '{print $2}' $_cache_mods_temp)
|
readarray -t ids < <(awk '{print $2}' $_cache_mods_temp)
|
||||||
rm "$_cache_mods_temp"
|
rm "$_cache_mods_temp"
|
||||||
else
|
|
||||||
local symlink="$1"
|
|
||||||
local dir="$2"
|
|
||||||
readarray -t symlinks <<< "$symlink"
|
|
||||||
readarray -t ids <<< "$dir"
|
|
||||||
fi
|
|
||||||
for ((i=0; i<${#symlinks[@]}; i++)); do
|
for ((i=0; i<${#symlinks[@]}; i++)); do
|
||||||
[[ ! -d $workshop_dir/${ids[$i]} ]] && return 1
|
[[ ! -d $workshop_dir/${ids[$i]} ]] && return 1
|
||||||
[[ ! -L $game_dir/${symlinks[$i]} ]] && return 1
|
[[ ! -L $game_dir/${symlinks[$i]} ]] && return 1
|
||||||
|
@ -608,16 +624,17 @@ delete_local_mod(){
|
||||||
rm -rf "${workshop_dir:?}/${ids[$i]}" && unlink "$game_dir/${symlinks[$i]}" || return 1
|
rm -rf "${workshop_dir:?}/${ids[$i]}" && unlink "$game_dir/${symlinks[$i]}" || return 1
|
||||||
align_versions_file "align" "${ids[$i]}"
|
align_versions_file "align" "${ids[$i]}"
|
||||||
done
|
done
|
||||||
|
printf "Successfully deleted %s %s." "${#symlinks[@]}" "$(pluralize "mods" ${#symlinks[@]})"
|
||||||
|
return 95
|
||||||
}
|
}
|
||||||
test_cooldown(){
|
test_cooldown(){
|
||||||
[[ ! -f $_cache_cooldown ]] && return 0
|
[[ ! -f $_cache_cooldown ]] && return 0
|
||||||
local old_time=$(< $_cache_cooldown)
|
local old_time=$(< $_cache_cooldown)
|
||||||
local cur_time=$(date +%s)
|
local cur_time=$(date +%s)
|
||||||
local delta=$(($cur_time - $old_time))
|
local delta=$(($cur_time - $old_time))
|
||||||
local suffix="seconds"
|
|
||||||
if [[ $delta -lt 60 ]]; then
|
if [[ $delta -lt 60 ]]; then
|
||||||
local remains=$((60 - $delta))
|
local remains=$((60 - $delta))
|
||||||
[[ $remains -eq 1 ]] && suffix="second"
|
local suffix=$(pluralize "seconds" $remains)
|
||||||
printf "Global API cooldown in effect. Please wait %s %s." "$remains" "$suffix"
|
printf "Global API cooldown in effect. Please wait %s %s." "$remains" "$suffix"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
@ -1104,7 +1121,11 @@ legacy_symlinks(){
|
||||||
unlink "$d"
|
unlink "$d"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
for d in "$workshop_dir"/*; do
|
readarray -t mod_dirs < <(find "$workshop_dir" -maxdepth 1 -mindepth 1 -type d)
|
||||||
|
[[ ${#mod_dirs[@]} -eq 0 ]] && return
|
||||||
|
for d in "${mod_dirs[@]}"; do
|
||||||
|
# suppress errors if mods are downloading at boot
|
||||||
|
[[ ! -f "$d/meta.cpp" ]] && continue
|
||||||
local id=$(awk -F"= " '/publishedid/ {print $2}' "$d"/meta.cpp | awk -F\; '{print $1}')
|
local id=$(awk -F"= " '/publishedid/ {print $2}' "$d"/meta.cpp | awk -F\; '{print $1}')
|
||||||
local encoded_id=$(echo "$id" | awk '{printf("%c",$1)}' | base64 | sed 's/\//_/g; s/=//g; s/+/]/g')
|
local encoded_id=$(echo "$id" | awk '{printf("%c",$1)}' | base64 | sed 's/\//_/g; s/=//g; s/+/]/g')
|
||||||
if [[ -h "$game_dir/@$encoded_id" ]]; then
|
if [[ -h "$game_dir/@$encoded_id" ]]; then
|
||||||
|
@ -1113,7 +1134,11 @@ legacy_symlinks(){
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
symlinks(){
|
symlinks(){
|
||||||
for d in "$workshop_dir"/*; do
|
readarray -t mod_dirs < <(find "$workshop_dir" -maxdepth 1 -mindepth 1 -type d)
|
||||||
|
[[ ${#mod_dirs[@]} -eq 0 ]] && return
|
||||||
|
for d in "${mod_dirs[@]}"; do
|
||||||
|
# suppress errors if mods are downloading at boot
|
||||||
|
[[ ! -f "$d/meta.cpp" ]] && continue
|
||||||
id=$(awk -F"= " '/publishedid/ {print $2}' "$d"/meta.cpp | awk -F\; '{print $1}')
|
id=$(awk -F"= " '/publishedid/ {print $2}' "$d"/meta.cpp | awk -F\; '{print $1}')
|
||||||
encoded_id=$(encode "$id")
|
encoded_id=$(encode "$id")
|
||||||
link="@$encoded_id"
|
link="@$encoded_id"
|
||||||
|
|
|
@ -330,8 +330,8 @@ def spawn_dialog(transient_parent, msg, mode):
|
||||||
|
|
||||||
|
|
||||||
def process_shell_return_code(transient_parent, msg, code, original_input):
|
def process_shell_return_code(transient_parent, msg, code, original_input):
|
||||||
|
logger.info("Processing return code '%s' for the input '%s', returned message '%s'" %(code, original_input, msg))
|
||||||
match code:
|
match code:
|
||||||
#TODO: add logger output to each
|
|
||||||
case 0:
|
case 0:
|
||||||
# success with notice popup
|
# success with notice popup
|
||||||
spawn_dialog(transient_parent, msg, Popup.NOTIFY)
|
spawn_dialog(transient_parent, msg, Popup.NOTIFY)
|
||||||
|
@ -369,6 +369,13 @@ def process_shell_return_code(transient_parent, msg, code, original_input):
|
||||||
transient_parent.grid.update_statusbar(tooltip)
|
transient_parent.grid.update_statusbar(tooltip)
|
||||||
spawn_dialog(transient_parent, msg, Popup.NOTIFY)
|
spawn_dialog(transient_parent, msg, Popup.NOTIFY)
|
||||||
return
|
return
|
||||||
|
case 95:
|
||||||
|
# reload mods list
|
||||||
|
spawn_dialog(transient_parent, msg, Popup.NOTIFY)
|
||||||
|
treeview = transient_parent.grid.scrollable_treelist.treeview
|
||||||
|
# re-block this signal before redrawing table contents
|
||||||
|
toggle_signal(treeview, treeview, '_on_keypress', False)
|
||||||
|
treeview.update_quad_column("List installed mods")
|
||||||
case 100:
|
case 100:
|
||||||
# final handoff before launch
|
# final handoff before launch
|
||||||
final_conf = spawn_dialog(transient_parent, msg, Popup.CONFIRM)
|
final_conf = spawn_dialog(transient_parent, msg, Popup.CONFIRM)
|
||||||
|
@ -427,6 +434,8 @@ def process_tree_option(input, treeview):
|
||||||
else:
|
else:
|
||||||
# non-blocking subprocess
|
# non-blocking subprocess
|
||||||
subprocess.Popen(['/usr/bin/env', 'bash', funcs, "Open link", command])
|
subprocess.Popen(['/usr/bin/env', 'bash', funcs, "Open link", command])
|
||||||
|
case "Delete selected mods":
|
||||||
|
call_on_thread(True, context, "Deleting mods", command)
|
||||||
case "Handshake":
|
case "Handshake":
|
||||||
call_on_thread(True, context, "Waiting for DayZ", command)
|
call_on_thread(True, context, "Waiting for DayZ", command)
|
||||||
case _:
|
case _:
|
||||||
|
@ -605,6 +614,8 @@ class ButtonBox(Gtk.Box):
|
||||||
|
|
||||||
# only applicable when returning from mod list
|
# only applicable when returning from mod list
|
||||||
grid = widgets["grid"]
|
grid = widgets["grid"]
|
||||||
|
grid_last_child = grid.right_panel.get_children()[-1]
|
||||||
|
if isinstance(grid_last_child, ModSelectionPanel):
|
||||||
grid.right_panel.remove(grid.sel_panel)
|
grid.right_panel.remove(grid.sel_panel)
|
||||||
right_panel = self.get_parent()
|
right_panel = self.get_parent()
|
||||||
right_panel.set_filter_visibility(False)
|
right_panel.set_filter_visibility(False)
|
||||||
|
@ -780,15 +791,15 @@ class TreeView(Gtk.TreeView):
|
||||||
success_msg = "Successfully deleted the mod '%s'." %(value)
|
success_msg = "Successfully deleted the mod '%s'." %(value)
|
||||||
fail_msg = "An error occurred during deletion. Aborting."
|
fail_msg = "An error occurred during deletion. Aborting."
|
||||||
res = spawn_dialog(parent, conf_msg, Popup.CONFIRM)
|
res = spawn_dialog(parent, conf_msg, Popup.CONFIRM)
|
||||||
|
if res == 0:
|
||||||
|
mods = []
|
||||||
symlink = self.get_column_at_index(1)
|
symlink = self.get_column_at_index(1)
|
||||||
dir = self.get_column_at_index(2)
|
dir = self.get_column_at_index(2)
|
||||||
if res == 0:
|
concat = symlink + " " + dir + "\n"
|
||||||
proc = call_out(parent, "delete", symlink, dir)
|
mods.append(concat)
|
||||||
if proc.returncode == 0:
|
with open(mods_temp_file, "w") as outfile:
|
||||||
spawn_dialog(parent, success_msg, Popup.NOTIFY)
|
outfile.writelines(mods)
|
||||||
self.update_quad_column("List installed mods")
|
process_tree_option(["Delete selected mods", ""], self)
|
||||||
else:
|
|
||||||
spawn_dialog(parent, fail_msg, Popup.NOTIFY)
|
|
||||||
case "Open in Steam Workshop":
|
case "Open in Steam Workshop":
|
||||||
record = self.get_column_at_index(2)
|
record = self.get_column_at_index(2)
|
||||||
call_out(parent, "open_workshop_page", record)
|
call_out(parent, "open_workshop_page", record)
|
||||||
|
@ -851,6 +862,11 @@ class TreeView(Gtk.TreeView):
|
||||||
self.menu.show_all()
|
self.menu.show_all()
|
||||||
|
|
||||||
if event.type is Gdk.EventType.KEY_PRESS and event.keyval is Gdk.KEY_l:
|
if event.type is Gdk.EventType.KEY_PRESS and event.keyval is Gdk.KEY_l:
|
||||||
|
sel = self.get_selection()
|
||||||
|
sels = sel.get_selected_rows()
|
||||||
|
(model, pathlist) = sels
|
||||||
|
if len(pathlist) < 1:
|
||||||
|
return
|
||||||
self.menu.popup_at_widget(widget, Gdk.Gravity.CENTER, Gdk.Gravity.WEST)
|
self.menu.popup_at_widget(widget, Gdk.Gravity.CENTER, Gdk.Gravity.WEST)
|
||||||
else:
|
else:
|
||||||
self.menu.popup_at_pointer(event)
|
self.menu.popup_at_pointer(event)
|
||||||
|
@ -973,8 +989,11 @@ class TreeView(Gtk.TreeView):
|
||||||
|
|
||||||
def _focus_first_row(self):
|
def _focus_first_row(self):
|
||||||
path = Gtk.TreePath(0)
|
path = Gtk.TreePath(0)
|
||||||
|
try:
|
||||||
it = mod_store.get_iter(path)
|
it = mod_store.get_iter(path)
|
||||||
self.get_selection().select_path(path)
|
self.get_selection().select_path(path)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
def get_column_at_index(self, index):
|
def get_column_at_index(self, index):
|
||||||
select = self.get_selection()
|
select = self.get_selection()
|
||||||
|
@ -1056,9 +1075,14 @@ class TreeView(Gtk.TreeView):
|
||||||
def _background_quad(self, dialog, mode):
|
def _background_quad(self, dialog, mode):
|
||||||
def load():
|
def load():
|
||||||
dialog.destroy()
|
dialog.destroy()
|
||||||
|
# detach button panel if store is empty
|
||||||
|
if isinstance(panel_last_child, ModSelectionPanel):
|
||||||
|
if total_mods == 0:
|
||||||
|
right_panel.remove(grid.sel_panel)
|
||||||
|
grid.show_all()
|
||||||
|
right_panel.set_filter_visibility(False)
|
||||||
self.set_model(mod_store)
|
self.set_model(mod_store)
|
||||||
self.grab_focus()
|
self.grab_focus()
|
||||||
if abort is False:
|
|
||||||
size = locale.format_string('%.3f', total_size, grouping=True)
|
size = locale.format_string('%.3f', total_size, grouping=True)
|
||||||
pretty = pluralize("mods", total_mods)
|
pretty = pluralize("mods", total_mods)
|
||||||
grid.update_statusbar(f"Found {total_mods:n} {pretty} taking up {size} MiB")
|
grid.update_statusbar(f"Found {total_mods:n} {pretty} taking up {size} MiB")
|
||||||
|
@ -1066,21 +1090,27 @@ class TreeView(Gtk.TreeView):
|
||||||
toggle_signal(self, self.selected_row, '_on_tree_selection_changed', True)
|
toggle_signal(self, self.selected_row, '_on_tree_selection_changed', True)
|
||||||
toggle_signal(self, self, '_on_keypress', True)
|
toggle_signal(self, self, '_on_keypress', True)
|
||||||
self._focus_first_row()
|
self._focus_first_row()
|
||||||
|
if total_mods == 0:
|
||||||
|
spawn_dialog(self.get_outer_window(), data.stdout, Popup.NOTIFY)
|
||||||
|
|
||||||
grid = self.get_outer_grid()
|
widgets = relative_widget(self)
|
||||||
|
grid = widgets["grid"]
|
||||||
right_panel = grid.right_panel
|
right_panel = grid.right_panel
|
||||||
|
|
||||||
abort = False
|
|
||||||
right_panel.set_filter_visibility(False)
|
|
||||||
data = call_out(self, "list_mods", mode)
|
data = call_out(self, "list_mods", mode)
|
||||||
|
panel_last_child = right_panel.get_children()[-1]
|
||||||
|
|
||||||
# suppress errors if no mods available on system
|
# suppress errors if no mods available on system
|
||||||
if data.returncode == 1:
|
if data.returncode == 1:
|
||||||
abort = True
|
total_mods = 0
|
||||||
|
total_size = 0
|
||||||
GLib.idle_add(load)
|
GLib.idle_add(load)
|
||||||
spawn_dialog(self.get_outer_window(), data.stdout, Popup.NOTIFY)
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
# attach button panel only if missing (prevents duplication when reloading in-place)
|
||||||
|
if not isinstance(panel_last_child, ModSelectionPanel):
|
||||||
|
right_panel.pack_start(grid.sel_panel, False, False, 0)
|
||||||
|
grid.show_all()
|
||||||
|
right_panel.set_filter_visibility(False)
|
||||||
result = parse_mod_rows(data)
|
result = parse_mod_rows(data)
|
||||||
total_size = result[0]
|
total_size = result[0]
|
||||||
total_mods = result[1]
|
total_mods = result[1]
|
||||||
|
@ -1214,10 +1244,6 @@ class TreeView(Gtk.TreeView):
|
||||||
if mode == "List installed mods":
|
if mode == "List installed mods":
|
||||||
cols = mod_cols
|
cols = mod_cols
|
||||||
self.set_model(mod_store)
|
self.set_model(mod_store)
|
||||||
# attach button panel
|
|
||||||
grid = self.get_parent().get_parent()
|
|
||||||
grid.right_panel.pack_start(grid.sel_panel, False, False, 0)
|
|
||||||
grid.show_all()
|
|
||||||
else:
|
else:
|
||||||
cols = log_cols
|
cols = log_cols
|
||||||
self.set_model(log_store)
|
self.set_model(log_store)
|
||||||
|
@ -1988,12 +2014,7 @@ class ModSelectionPanel(Gtk.Box):
|
||||||
mods.append(concat)
|
mods.append(concat)
|
||||||
with open(mods_temp_file, "w") as outfile:
|
with open(mods_temp_file, "w") as outfile:
|
||||||
outfile.writelines(mods)
|
outfile.writelines(mods)
|
||||||
proc = call_out(parent, "delete")
|
process_tree_option(["Delete selected mods", ""], treeview)
|
||||||
if proc.returncode == 0:
|
|
||||||
spawn_dialog(parent, success_msg, Popup.NOTIFY)
|
|
||||||
treeview.update_quad_column("List installed mods")
|
|
||||||
else:
|
|
||||||
spawn_dialog(parent, fail_msg, Popup.NOTIFY)
|
|
||||||
|
|
||||||
class FilterPanel(Gtk.Box):
|
class FilterPanel(Gtk.Box):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
Loading…
Reference in a new issue