From 8838827bff9bb0c3d1a675444e9f99e9e1b48e9c Mon Sep 17 00:00:00 2001 From: aclist Date: Tue, 11 Oct 2022 23:18:46 +0900 Subject: [PATCH 001/144] Update readme --- README.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index ad182b8..b7c262a 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,26 @@ ## What this is -- BETA TESTING BRANCH - +A GUI version of [DZTUI](https://githubom/aclist/dztui) for Linux. Used to list official and community server details and quick connect to preferred servers by staging mods and concatenating launch options automatically. -A GUI version of [DZTUI](https://github.com/aclist/dztui) for Linux. Used to list community server details and quick connect to preferred servers by staging mods and concatenating launch options automatically. +DZGUI allows you to connect to both official and modded/community DayZ servers on Linux and provides a graphical interface for doing so. This overcomes certain limitations in the Linux client and helps prepare the game to launch by doing the following: -DZGUI allows you to connect to modded/community DayZ servers on Linux and provides a graphical interface for doing so. This overcomes certain limitations in the Linux client and helps prepare the game to launch by doing the following: - -1. Display server metadata in a table (server data and ID, player count, ping, current gametime) -2. Find and prepare mods being requested by the server +1. Display server metadata in a table (server name, player count, ping, current gametime, distance, IP) +2. Find and prepare mods being requested by the server (choose from manual or automatic installation) 3. Concatenate launch options to pass to Steam ## Setup and usage Refer to the [manual](https://aclist.github.io/dzgui/dzgui.html) for installation and setup instructions, a feature-by-feature breakdown, and Steam integration tutorials. -![Alt text](/images/example.png) +![Alt text](example.png) + +## Attribution +A subset of geolocation data was sourced from DB-IP under a Creative Commons Attribution 4.0 International License. + +## Acknowledgments +Special thanks to the following users for going above and beyond in beta testing and finding obscure bugs. + +- fossean +- scandalouss +- SteveIDusa +- The_Axelrod From 6a8566fb4a53f55e950925e475a39dc56147aa6e Mon Sep 17 00:00:00 2001 From: aclist Date: Tue, 11 Oct 2022 23:29:19 +0900 Subject: [PATCH 002/144] Remove stray logging --- dzgui.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/dzgui.sh b/dzgui.sh index 20dffd8..a8d9a7a 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -857,7 +857,6 @@ delete_or_connect(){ local qport_res=$(< $file jq -r --arg port $lookup_port '.response.servers[]|select(.gameport==($port|tonumber)).addr') local qport=$(echo "$qport_res" | awk -F: '{print $2}') qport_list="$lookup_ip%%$qport" - echo "$response" > resp connect "$qport_list" "ip" fi From 15aad8b49f64285979008c74bcc2aed5a89f94db Mon Sep 17 00:00:00 2001 From: aclist Date: Wed, 12 Oct 2022 00:18:04 +0900 Subject: [PATCH 003/144] Increment major version --- changelog.md | 4 ++++ dzgui.sh | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index eb8253c..0072385 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,10 @@ - Custom query API - Standardize dialogs +## [3.0.0-rc.1] 2022-10-12 +### Changed +- Increment major version + ## [2.8.0-rc.9] 2022-10-11 ### Changed - Reordered main menu elements diff --git a/dzgui.sh b/dzgui.sh index a8d9a7a..6a84ce1 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=2.8.0-rc.9 +version=3.0.0-testing aid=221100 game="dayz" From 3d196d6f1198737e0326c724d9fe27633e1dc011 Mon Sep 17 00:00:00 2001 From: aclist Date: Wed, 12 Oct 2022 00:19:46 +0900 Subject: [PATCH 004/144] Update news --- news | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/news b/news index d65553e..fbdf413 100644 --- a/news +++ b/news @@ -1 +1 @@ -NEWS: If you are on the Testing branch and using Steam Deck Game Mode, advise using Stable branch or Desktop Mode until experimental features are finished. +NEWS: Safe to use Steam Deck game mode on Testing branch again. From 698edf420b4f46fb1789a5166ec3f27c34fcfbda Mon Sep 17 00:00:00 2001 From: aclist Date: Wed, 12 Oct 2022 00:20:08 +0900 Subject: [PATCH 005/144] Update changelog --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 0072385..0ff7367 100644 --- a/changelog.md +++ b/changelog.md @@ -5,7 +5,7 @@ - Custom query API - Standardize dialogs -## [3.0.0-rc.1] 2022-10-12 +## [3.0.0-testing] 2022-10-12 ### Changed - Increment major version From 0ce5f1e200f940ee012fa3c7e826ce71114a046b Mon Sep 17 00:00:00 2001 From: aclist Date: Wed, 12 Oct 2022 00:29:50 +0900 Subject: [PATCH 006/144] List mod count --- dzgui.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dzgui.sh b/dzgui.sh index 6a84ce1..4de0346 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1294,9 +1294,9 @@ server_browser(){ } mods_disk_size(){ - printf "Total size on disk: %s |" $(du -sh "$game_dir" | awk '{print $1}') - printf "Mods location: $steam_path/steamapps/workshop/content/221100" - + printf "Total size on disk: %s | " $(du -sh "$game_dir" | awk '{print $1}') + printf "%s mods | " $(ls -1 "$game_dir" | wc -l) + printf "Location: %s/steamapps/workshop/content/221100" "$steam_path" } main_menu(){ From 7830d93e8554a75fa236cdebc59b3d8034701b52 Mon Sep 17 00:00:00 2001 From: aclist Date: Wed, 12 Oct 2022 03:14:21 +0900 Subject: [PATCH 007/144] Fix Deck popup size --- dzgui.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dzgui.sh b/dzgui.sh index 4de0346..191aa57 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1661,7 +1661,9 @@ deprecation_warning(){ } if [[ -z $steam_api ]]; then echo "100" - zenity --info --text="$(warn)" + local big_prompt + [[ $is_steam_deck -eq 1 ]] && big_prompt="--width=800" + zenity --info --text="$(warn)" $big_prompt key_setup_url="https://aclist.github.io/dzgui/dzgui.html#_api_key_server_ids" browser "$key_setup_url" 2>/dev/null & while true; do From 586c154bd42330790a5940ca1151a04af8be3217 Mon Sep 17 00:00:00 2001 From: aclist Date: Sun, 16 Oct 2022 17:50:48 +0900 Subject: [PATCH 008/144] Include /run in search path --- dzgui.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dzgui.sh b/dzgui.sh index 191aa57..ccee5fb 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.0.0-testing +version=3.1.0-rc.1 aid=221100 game="dayz" @@ -245,7 +245,7 @@ guess_path(){ steam_path="$clean_path" else echo "# Searching for alternate DayZ path. This may take some time." - path=$(find / -type d \( -path "/proc" -o -path "*/timeshift" -o -path "/tmp" -o -path "/usr" -o -path "/boot" -o -path "/proc" -o -path "/root" -o -path "/run" -o -path "/sys" -o -path "/etc" -o -path "/var" -o -path "/run" -o -path "/lost+found" \) -prune -o -regex ".*/steamapps/common/DayZ$" -print -quit 2>/dev/null) + path=$(find / -type d \( -path "/proc" -o -path "*/timeshift" -o -path "/tmp" -o -path "/usr" -o -path "/boot" -o -path "/proc" -o -path "/root" -o -path "/sys" -o -path "/etc" -o -path "/var" -o -path "/lost+found" \) -prune -o -regex ".*/steamapps/common/DayZ$" -print -quit 2>/dev/null) clean_path=$(echo -e "$path" | awk -F"/steamapps" '{print $1}') steam_path="$clean_path" fi From d019cc27650353496211651bd35c0a62acb90dad Mon Sep 17 00:00:00 2001 From: aclist Date: Sun, 16 Oct 2022 20:45:37 +0900 Subject: [PATCH 009/144] Fix Steam Deck popup width --- dzgui.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dzgui.sh b/dzgui.sh index ccee5fb..5ddf58d 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.1.0-rc.1 +version=3.1.0-rc.2 aid=221100 game="dayz" @@ -679,7 +679,7 @@ add_steam_api(){ steam_api="steam_api=\"$steam_api\"" awk -v "var=$steam_api" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > ${config_path}dztuirc echo "[DZGUI] Added Steam API key" - zenity --info --title="DZGUI" --text="Added Steam API key to:\n\n${config_path}dztuirc\nIf errors occur, you can restore the file:\n${config_path}dztuirc.old" 2>/dev/null + zenity --info --title="DZGUI" --text="Added Steam API key to:\n\n${config_path}dztuirc\nIf errors occur, you can restore the file:\n${config_path}dztuirc.old" --width=500 2>/dev/null source $config_file } check_steam_api(){ From 258a5ad10a982538c6f14095b89b3ac713ec1b8f Mon Sep 17 00:00:00 2001 From: aclist Date: Sun, 16 Oct 2022 21:15:19 +0900 Subject: [PATCH 010/144] Update changelog --- changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/changelog.md b/changelog.md index 0ff7367..07aace9 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,10 @@ - Custom query API - Standardize dialogs +## [3.1.0-rc.1-2] 2022-10-16 +### Fixed +- Steam Deck path discovery + ## [3.0.0-testing] 2022-10-12 ### Changed - Increment major version From 3503857cfdeb6db1c66925807b52947e1b529a15 Mon Sep 17 00:00:00 2001 From: aclist <92275929+aclist@users.noreply.github.com> Date: Sun, 23 Oct 2022 02:22:39 +0900 Subject: [PATCH 011/144] Automod v.2 backend preparation --- dzgui.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dzgui.sh b/dzgui.sh index 5ddf58d..41c004f 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.1.0-rc.2 +version=3.1.0-rc.3 aid=221100 game="dayz" @@ -194,6 +194,9 @@ auto_install="$auto_install" #Automod staging directory staging_dir="$staging_dir" + +#Path to default Steam client +default_steam_path=$default_steam_path END } write_desktop_file(){ From 8a7656c9a7a633d65bf131c7e8a43f237ead18e5 Mon Sep 17 00:00:00 2001 From: aclist Date: Mon, 24 Oct 2022 03:09:35 +0900 Subject: [PATCH 012/144] Automods v.2 --- changelog.md | 8 +++ dzgui.sh | 184 ++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 155 insertions(+), 37 deletions(-) diff --git a/changelog.md b/changelog.md index 07aace9..a6d71f6 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,14 @@ - Custom query API - Standardize dialogs +## [3.1.0-rc.4] 2022-10-23 +### Added +- Automod v.2 method + +## [3.1.0-rc.3] 2022-10-16 +### Added +- Backend preparation + ## [3.1.0-rc.1-2] 2022-10-16 ### Fixed - Steam Deck path discovery diff --git a/dzgui.sh b/dzgui.sh index 5ddf58d..b7464e7 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.1.0-rc.2 +version=3.1.0-rc.4 aid=221100 game="dayz" @@ -194,6 +194,9 @@ auto_install="$auto_install" #Automod staging directory staging_dir="$staging_dir" + +#Path to default Steam client +default_steam_path="$default_steam_path" END } write_desktop_file(){ @@ -403,7 +406,7 @@ term_params(){ terminator|xterm|tilix|xfce4-terminal) $term -e "bash $helpers_path/scmd.sh $totalmodsize $1";; esac } -auto_mod_install(){ +headless_mod_install(){ cmd=$(printf "%q " "$@") if [[ -z "$term" ]]; then if [[ $is_steam_deck -eq 1 ]]; then @@ -445,7 +448,6 @@ manual_mod_install(){ steam_deck_mods else local ex="/tmp/dzc.tmp" - [[ -f $ex ]] && rm $ex watcher(){ readarray -t stage_mods <<< "$diff" for((i=0;i<${#stage_mods[@]};i++)); do @@ -457,7 +459,6 @@ manual_mod_install(){ sleep 1s foreground until [[ -d $downloads_dir/${stage_mods[$i]} ]]; do - [[ -f $ex ]] && return 1 sleep 0.1s if [[ -d $workshop_dir/${stage_mods[$i]} ]]; then break @@ -466,7 +467,6 @@ manual_mod_install(){ foreground echo "# Steam is downloading ${stage_mods[$i]} (mod $((i+1)) of ${#stage_mods[@]})" until [[ -d $workshop_dir/${stage_mods[$i]} ]]; do - [[ -f $ex ]] && return 1 sleep 0.1s done foreground @@ -484,24 +484,6 @@ manual_mod_install(){ fi fi } -# if [[ $is_steam_deck -eq 0 ]]; then -# open_mod_links -# until [[ -z $diff ]]; do -# zenity --question --title="DZGUI" --ok-label="Next" --cancel-label="Cancel" --text="Opened mod links in browser.\nClick [Next] when all mods have been subscribed to.\nThis dialog may reappear if clicking [Next] too soon\nbefore mods are synchronized in the background." --width=500 2>/dev/null -# rc=$? -# if [[ $rc -eq 0 ]]; then -# compare -# open_mod_links -# else -# return -# fi -# done -# else -# steam_deck_mods -# rc=$? -# [[ $rc -eq 1 ]] && return 1 -# fi -# passed_mod_check > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) encode(){ echo "$1" | md5sum | cut -c -8 } @@ -547,6 +529,39 @@ passed_mod_check(){ echo "100" } +auto_mod_install(){ + popup 300 + rc=$? + if [[ $rc -eq 0 ]]; then + calc_mod_sizes + local total_size=$(numfmt --to=iec $totalmodsize) + depot_watcher "$diff" & + console_dl "$diff" && + steam steam://open/minigameslist + until [[ -d $staging_dir/app_$aid ]]; do + sleep 0.1s + done + until [[ ! -d $staging_dir/app_$aid ]]; do + until [[ "$fmt_size" == "$total_size" ]]; do + local cur_size=$(du -sb "$staging_dir/app_$aid" | awk '{print $1}') + local fmt_size=$(numfmt --to=iec $cur_size) + sleep 0.1s + [[ ${#modids[@]} -gt 1 ]] && s=s + echo "# Downloading ${#modids[@]} mod$s ($fmt_size of $total_size)" + done + echo "# Waiting for hangup signal from Steam" + done | zenity --progress --pulsate --text="Downloading mods" --auto-close --no-cancel --width=500 --title=DZGUI 2>/dev/null + compare + if [[ -z $diff ]]; then + passed_mod_check > >(zenity --pulsate --progress --title=DZGUI --auto-close --width=500 2>/dev/null) + launch + else + manual_mod_install + fi + else + manual_mod_install + fi +} connect(){ #TODO: sanitize/validate input readarray -t qport_arr <<< "$qport_list" @@ -571,9 +586,11 @@ connect(){ compare if [[ -n $diff ]]; then if [[ $auto_install -eq 1 ]]; then - auto_mod_install "$diff" + headless_mod_install "$diff" rc=$? [[ $rc -eq 1 ]] && manual_mod_install + elif [[ $auto_install -eq 2 ]]; then + auto_mod_install else manual_mod_install fi @@ -851,10 +868,8 @@ delete_or_connect(){ local lookup_ip=$(echo "$sel" | awk -F%% '{print $1}') local lookup_port=$(echo "$lookup_ip" | awk -F: '{print $2}') source $config_file - file=$(mktemp) url="https://api.steampowered.com/IGameServersService/GetServerList/v1/?filter=\appid\221100\gameaddr\\$lookup_ip&key=$steam_api" - curl -Ls "$url" > $file - local qport_res=$(< $file jq -r --arg port $lookup_port '.response.servers[]|select(.gameport==($port|tonumber)).addr') + local qport_res=$(curl -Ls "$url" | jq -r --arg port $lookup_port '.response.servers[]|select(.gameport==($port|tonumber)).addr') local qport=$(echo "$qport_res" | awk -F: '{print $2}') qport_list="$lookup_ip%%$qport" connect "$qport_list" "ip" @@ -911,7 +926,8 @@ connect_to_fav(){ } set_header(){ - [[ $auto_install -eq 1 ]] && install_mode=auto + [[ $auto_install -eq 2 ]] && install_mode="auto" + [[ $auto_install -eq 1 ]] && install_mode="headless" [[ $auto_install -eq 0 ]] && install_mode=manual if [[ $1 == "delete" ]]; then sel=$(cat $tmp | zenity $sd_res --list $cols --title="DZGUI" --text="DZGUI $version | Mode: $mode | Branch: $branch | Mods: $install_mode | Fav: $fav_label" \ @@ -952,7 +968,7 @@ generate_log(){ automods_prompt(){ cat <<- HERE -Auto-mod installation set to ON. This method is NOT supported in Game Mode (Steam Deck). +Headless installation set to ON. READ THIS FIRST: With this setting on, DZGUI will attempt to download and prepare mods using Valve's steamcmd tool. @@ -968,7 +984,9 @@ NOTE: it can take some time for large mods to download, and steamcmd will not in If your distribution is unsupported, you don't have enough disk space to stage all of the mods, or there are other problems, DZGUI will warn you and write a report to $HOME/.local/share/dzgui/helpers/SCMD.log. You can attach this file to a bug report. HERE } -toggle_automods(){ +toggle_headless(){ + [[ $is_steam_deck -eq 1 ]] && test_display_mode + [[ $gamemode -eq 1 ]] && { popup 200; return; } mv $config_file ${config_path}dztuirc.old local nr=$(awk '/auto_install=/ {print NR}' ${config_path}dztuirc.old) if [[ $auto_install == "1" ]]; then @@ -978,18 +996,108 @@ toggle_automods(){ fi local flip_state="auto_install=\"$auto_install\"" awk -v "var=$flip_state" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > $config_file - printf "[DZGUI] Toggled auto-mod install to '$auto_install'\n" + printf "[DZGUI] Set mod install to '$auto_install'\n" source $config_file local big_prompt [[ $is_steam_deck -eq 1 ]] && big_prompt="--width=800" [[ $auto_install == "1" ]] && zenity --info --text="$(automods_prompt)" $big_prompt 2>/dev/null } +depot_watcher(){ + local last=$(echo "${@: -1}") + payload(){ + echo -e "itemcount=1&publishedfileids[0]=$last" + } + post(){ + curl -s -X POST -H "Content-Type:application/x-www-form-urlencoded" -d "$(payload)" 'https://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1/?format=json' + } + depot_id=$(post | jq -r '.response.publishedfiledetails[].hcontent_file') + + local dir="$default_steam_path/ubuntu12_32/steamapps/content" + local alt="$default_steam_path/ubuntu12_32/steamapps/_content" + local log="$default_steam_path/logs/content_log.txt" + [[ -d $dir ]] && { mv "$dir" $alt; } + [[ -h "$dir" ]] && unlink "$dir" + [[ -d $staging_dir/app_$aid ]] && rm -rf $staging_dir/app_$aid + ln -s $staging_dir "$dir" + echo "" > "$log" + + until [[ "$(tail -n1 $log)" =~ "/depot/$aid/manifest/$depot_id" ]]; do + sleep 0.1s + done + until [[ "$(tail -n1 $log)" =~ "Closing connection" ]]; do + sleep 0.1s + done + readarray -t modids <<< "$@" + echo "[DZGUI] Moving mods" + for i in "${modids[@]}"; do + mv $staging_dir/app_$aid/item_$i "$workshop_dir/$i" + done + echo "[DZGUI] Cleanup" + rmdir $staging_dir/app_$aid + unlink "$dir" + [[ -d "$alt" ]] && { mv "$alt" "$dir"; } +} +console_dl(){ + readarray -t modids <<< "$@" + steam steam://open/console + sleep 1s + for i in "${modids[@]}"; do + xdotool type --delay 15 "download_item $aid $i" + sleep 0.2s + xdotool key Return + sleep 0.2s + done +} +find_default_path(){ + [[ -n $default_steam_path ]] && return + default_steam_path=$(find / -type d \( -path "/proc" -o -path "*/timeshift" -o -path \ + "/tmp" -o -path "/usr" -o -path "/boot" -o -path "/proc" -o -path "/root" \ + -o -path "/sys" -o -path "/etc" -o -path "/var" -o -path "/lost+found" \) -prune \ + -o -regex ".*/Steam/ubuntu12_32$" -print -quit 2>/dev/null | sed 's@/ubuntu12_32@@') + echo "$default_steam_path" >> logs + default_steam_path="default_steam_path=\"$default_steam_path\"" + mv $config_file ${config_path}dztuirc.old + local nr=$(awk '/default_steam_path=/ {print NR}' ${config_path}dztuirc.old) + awk -v "var=$default_steam_path" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > $config_file + printf "[DZGUI] Set default Steam path to $default_steam_path\n" + source $config_file +} +popup(){ + [[ $1 -eq 100 ]] && zenity --info --text="This feature requires xdotool" --title=DZGUI --width=500 + [[ $1 -eq 200 ]] && zenity --info --text="This feature is not supported on Gaming Mode" --title=DZGUI --width=500 + [[ $1 -eq 300 ]] && zenity --info --text="\nThe Steam console will now open and briefly issue commands to\ndownload the workshop files, then return to the small Steam client\nwhile they download.\n\nEnsure that the Steam console has keyboard and mouse focus\n(keep hands off keyboard) while the commands are being issued.\n\nIt will only take a few seconds to queue the downloads, but if a\npopup or notification window steals focus, it could obstruct\nthe process." --title=DZGUI --width=500 +} +requires_xdo(){ + zenity --info --text="This feature requires xdojool" --title=DZGUI --width=500 +} +toggle_console_dl(){ + [[ $is_steam_deck -eq 1 ]] && test_display_mode + [[ $gamemode -eq 1 ]] && { popup 200; return; } + [[ ! $(command -v xdotool) ]] && { popup 100; return; } + mv $config_file ${config_path}dztuirc.old + local nr=$(awk '/auto_install=/ {print NR}' ${config_path}dztuirc.old) + if [[ $auto_install == "2" ]]; then + auto_install="0" + else + auto_install="2" + fi + local flip_state="auto_install=\"$auto_install\"" + awk -v "var=$flip_state" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > $config_file + printf "[DZGUI] Set mod install to '$auto_install'\n" + source $config_file +} options_menu(){ + case "$auto_install" in + 0) auto_hr="OFF"; headless_hr="OFF" ;; + 1) auto_hr="OFF"; headless_hr="ON" ;; + 2) auto_hr="ON"; headless_hr="OFF" ;; + esac debug_list=( "Toggle branch" "Toggle debug mode" "Generate debug log" - "Toggle auto-mod install (experimental)" + "Toggle auto mod install [$auto_hr]" + "Toggle headless mod install [$headless_hr]" "Set auto-mod staging directory [$staging_dir]" ) debug_sel=$(zenity --list --width=1280 --height=800 --column="Options" --title="DZGUI" --hide-header "${debug_list[@]}" 2>/dev/null) @@ -1006,8 +1114,10 @@ options_menu(){ printf "[DZGUI] Wrote log file to %s/log\n" "$source_dir" zenity --info --width 500 --title="DZGUI" --text="Wrote log file to \n$source_dir/log" 2>/dev/null elif [[ $debug_sel == "${debug_list[3]}" ]]; then - toggle_automods + toggle_console_dl elif [[ $debug_sel == "${debug_list[4]}" ]]; then + toggle_headless + elif [[ $debug_sel == "${debug_list[5]}" ]]; then file_picker fi } @@ -1268,10 +1378,9 @@ server_browser(){ #TODO: some error handling here fetch(){ echo "# Getting server list" - curl -Ls "$url" > $file + response=$(curl -Ls "$url" | jq -r '.response.servers') } fetch > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) - response=$(< $file jq -r '.response.servers') total_servers=$(echo "$response" | jq 'length') players_online=$(echo "$response" | jq '.[].players' | awk '{s+=$1}END{print s}') debug_log="$HOME/.local/share/dzgui/DEBUG.log" @@ -1638,7 +1747,7 @@ fetch_scmd_helper(){ [[ ! -f "$helpers_path/d.webp" ]] && curl -Ls "$notify_img_url" > "$helpers_path/d.webp" } deprecation_warning(){ - warn(){ + warn_big(){ cat <<- HERE IMPORTANT ANNOUNCEMENT (Steam API key not found) @@ -1663,7 +1772,7 @@ deprecation_warning(){ echo "100" local big_prompt [[ $is_steam_deck -eq 1 ]] && big_prompt="--width=800" - zenity --info --text="$(warn)" $big_prompt + zenity --info --text="$(warn_big)" $big_prompt key_setup_url="https://aclist.github.io/dzgui/dzgui.html#_api_key_server_ids" browser "$key_setup_url" 2>/dev/null & while true; do @@ -1681,6 +1790,7 @@ initial_setup(){ check_version check_map_count config + find_default_path fetch_scmd_helper run_varcheck init_items From 6c3542c93b3751d320b7720abaedd936d33279f6 Mon Sep 17 00:00:00 2001 From: aclist Date: Wed, 26 Oct 2022 09:22:19 +0900 Subject: [PATCH 013/144] Automod versioning --- changelog.md | 4 +++ dzgui.sh | 88 +++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/changelog.md b/changelog.md index a6d71f6..8365236 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,10 @@ - Custom query API - Standardize dialogs +## [3.1.0-rc.5] 2022-10-25 +### Added +- Versioning for automods + ## [3.1.0-rc.4] 2022-10-23 ### Added - Automod v.2 method diff --git a/dzgui.sh b/dzgui.sh index b7464e7..7b74426 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.1.0-rc.4 +version=3.1.0-rc.5 aid=221100 game="dayz" @@ -34,6 +34,7 @@ scmd_url="$testing_url/helpers/scmd.sh" notify_url="$testing_url/helpers/d.html" notify_img_url="$testing_url/helpers/d.webp" forum_url="https://github.com/aclist/dztui/discussions" +version_file="$config_path/versions" update_last_seen(){ mv $config_file ${config_path}dztuirc.old @@ -448,6 +449,7 @@ manual_mod_install(){ steam_deck_mods else local ex="/tmp/dzc.tmp" + [[ -f $ex ]] && rm $ex watcher(){ readarray -t stage_mods <<< "$diff" for((i=0;i<${#stage_mods[@]};i++)); do @@ -459,6 +461,7 @@ manual_mod_install(){ sleep 1s foreground until [[ -d $downloads_dir/${stage_mods[$i]} ]]; do + [[ -f $ex ]] && return 1 sleep 0.1s if [[ -d $workshop_dir/${stage_mods[$i]} ]]; then break @@ -467,6 +470,7 @@ manual_mod_install(){ foreground echo "# Steam is downloading ${stage_mods[$i]} (mod $((i+1)) of ${#stage_mods[@]})" until [[ -d $workshop_dir/${stage_mods[$i]} ]]; do + [[ -f $ex ]] && return 1 sleep 0.1s done foreground @@ -543,7 +547,7 @@ auto_mod_install(){ done until [[ ! -d $staging_dir/app_$aid ]]; do until [[ "$fmt_size" == "$total_size" ]]; do - local cur_size=$(du -sb "$staging_dir/app_$aid" | awk '{print $1}') + local cur_size=$(du -s -B1 "$staging_dir/app_$aid" | awk '{print $1}') local fmt_size=$(numfmt --to=iec $cur_size) sleep 0.1s [[ ${#modids[@]} -gt 1 ]] && s=s @@ -553,6 +557,7 @@ auto_mod_install(){ done | zenity --progress --pulsate --text="Downloading mods" --auto-close --no-cancel --width=500 --title=DZGUI 2>/dev/null compare if [[ -z $diff ]]; then + check_timestamps passed_mod_check > >(zenity --pulsate --progress --title=DZGUI --auto-close --width=500 2>/dev/null) launch else @@ -562,6 +567,64 @@ auto_mod_install(){ manual_mod_install fi } +get_local_stamps(){ + concat(){ + for ((i=0;i<$max;i++)); do + echo "publishedfileids[$i]=${local_modlist[$i]}&" + done | awk '{print}' ORS='' + } + payload(){ + echo -e "itemcount=${max}&$(concat)" + } + post(){ + curl -s -X POST -H "Content-Type:application/x-www-form-urlencoded" \ + -d "$(payload)" 'https://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1/?format=json' + } + post +} +update_stamps(){ + for((i=0;i<${#local_modlist[@]};i++)); do + mod=${local_modlist[$i]} + stamp=${stamps[$i]} + printf "%s\t%s\n" "$mod" "$stamp" >> $version_file + done +} +check_timestamps(){ + readarray -t local_modlist < <(ls -1 $workshop_dir) + max=${#local_modlist[@]} + readarray -t stamps < <(get_local_stamps | jq -r '.response.publishedfiledetails[].time_updated') + if [[ ! -f $version_file ]]; then + update_stamps + return + else + needs_update=() + for((i=0;i<${#local_modlist[@]};i++)); do + mod=${local_modlist[$i]} + stamp=${stamps[$i]} + if [[ ! $(awk -v var=$mod '$1 == var' $version_file) ]]; then + echo -e "$mod\t$stamp" >> $version_file + elif [[ $(awk -v var=$mod -v var2=$stamp '$1 == var && $2 == var2' $version_file) ]]; then + : + else + awk -v var=$mod -v var2=$stamp '$1 == var {$2=var2;print $1"\t"$2; next;};{print}' $version_file > $version_file.new + mv $version_file.new $version_file + needs_update+=($mod) + fi + done + fi +} +merge_modlists(){ + check_timestamps + if [[ -z "$diff" ]] && [[ ${#needs_update[@]} -gt 0 ]]; then + diff=$(printf "%s\n" "${needs_update[@]}") + elif [[ -z "$diff" ]] && [[ ${#needs_update[@]} -eq 0 ]]; then + diff= + elif [[ -n "$diff" ]] && [[ ${#needs_update[@]} -eq 0 ]]; then + : + else + diff="$(echo -e "$diff\n${needs_update[@]}")" + fi +} connect(){ #TODO: sanitize/validate input readarray -t qport_arr <<< "$qport_list" @@ -584,6 +647,7 @@ connect(){ rc=$? [[ $rc -eq 1 ]] && return compare + merge_modlists if [[ -n $diff ]]; then if [[ $auto_install -eq 1 ]]; then headless_mod_install "$diff" @@ -1030,7 +1094,8 @@ depot_watcher(){ readarray -t modids <<< "$@" echo "[DZGUI] Moving mods" for i in "${modids[@]}"; do - mv $staging_dir/app_$aid/item_$i "$workshop_dir/$i" + rsync -a --delete "$staging_dir/app_$aid/item_$i/" "$workshop_dir/$i/" && + rm -rf "$staging_dir/app_$aid/item_$i" done echo "[DZGUI] Cleanup" rmdir $staging_dir/app_$aid @@ -1050,11 +1115,18 @@ console_dl(){ } find_default_path(){ [[ -n $default_steam_path ]] && return - default_steam_path=$(find / -type d \( -path "/proc" -o -path "*/timeshift" -o -path \ - "/tmp" -o -path "/usr" -o -path "/boot" -o -path "/proc" -o -path "/root" \ - -o -path "/sys" -o -path "/etc" -o -path "/var" -o -path "/lost+found" \) -prune \ - -o -regex ".*/Steam/ubuntu12_32$" -print -quit 2>/dev/null | sed 's@/ubuntu12_32@@') - echo "$default_steam_path" >> logs + if [[ $is_steam_deck -eq 1 ]]; then + default_steam_path="$HOME/.local/share/Steam" + else + if [[ -d "$HOME/.local/share/Steam" ]]; then + default_steam_path="$HOME/.local/share/Steam" + else + default_steam_path=$(find / -type d \( -path "/proc" -o -path "*/timeshift" -o -path \ + "/tmp" -o -path "/usr" -o -path "/boot" -o -path "/run" -o -path "/proc" -o -path "/root" \ + -o -path "/sys" -o -path "/etc" -o -path "/var" -o -path "/lost+found" \) -prune \ + -o -regex ".*/Steam/ubuntu12_32$" -print -quit 2>/dev/null | sed 's@/ubuntu12_32@@') + fi + fi default_steam_path="default_steam_path=\"$default_steam_path\"" mv $config_file ${config_path}dztuirc.old local nr=$(awk '/default_steam_path=/ {print NR}' ${config_path}dztuirc.old) From 2411dcd0d6ed8ea7217f589aee7c76343b7c75cb Mon Sep 17 00:00:00 2001 From: aclist Date: Wed, 26 Oct 2022 09:22:51 +0900 Subject: [PATCH 014/144] Update changelog --- changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog.md b/changelog.md index 8365236..2935528 100644 --- a/changelog.md +++ b/changelog.md @@ -8,6 +8,8 @@ ## [3.1.0-rc.5] 2022-10-25 ### Added - Versioning for automods +### Fixed +- Restore manual mode watcher file ## [3.1.0-rc.4] 2022-10-23 ### Added From 079e20e49f64e65899e994964ffa9fabc36e1559 Mon Sep 17 00:00:00 2001 From: aclist Date: Wed, 26 Oct 2022 11:38:52 +0900 Subject: [PATCH 015/144] Download mod metadata --- changelog.md | 4 +++ dzgui.sh | 69 +++++++++++++++------------------------------------- 2 files changed, 24 insertions(+), 49 deletions(-) diff --git a/changelog.md b/changelog.md index 2935528..d423f46 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,10 @@ - Custom query API - Standardize dialogs +## [3.1.0-rc.6] 2022-10-25 +### Fixed +- Download mod metadata + ## [3.1.0-rc.5] 2022-10-25 ### Added - Versioning for automods diff --git a/dzgui.sh b/dzgui.sh index 7b74426..9213a60 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.1.0-rc.5 +version=3.1.0-rc.6 aid=221100 game="dayz" @@ -539,22 +539,15 @@ auto_mod_install(){ if [[ $rc -eq 0 ]]; then calc_mod_sizes local total_size=$(numfmt --to=iec $totalmodsize) + log="$default_steam_path/logs/content_log.txt" + [[ -f "/tmp/dz.status" ]] && rm "/tmp/dz.status" + touch "/tmp/dz.status" depot_watcher "$diff" & console_dl "$diff" && - steam steam://open/minigameslist - until [[ -d $staging_dir/app_$aid ]]; do + steam steam://open/downloads 2>/dev/null 1>&2 + until [[ $(tail -n1 "/tmp/dz.status") =~ "Finished" ]]; do sleep 0.1s done - until [[ ! -d $staging_dir/app_$aid ]]; do - until [[ "$fmt_size" == "$total_size" ]]; do - local cur_size=$(du -s -B1 "$staging_dir/app_$aid" | awk '{print $1}') - local fmt_size=$(numfmt --to=iec $cur_size) - sleep 0.1s - [[ ${#modids[@]} -gt 1 ]] && s=s - echo "# Downloading ${#modids[@]} mod$s ($fmt_size of $total_size)" - done - echo "# Waiting for hangup signal from Steam" - done | zenity --progress --pulsate --text="Downloading mods" --auto-close --no-cancel --width=500 --title=DZGUI 2>/dev/null compare if [[ -z $diff ]]; then check_timestamps @@ -1067,47 +1060,25 @@ toggle_headless(){ [[ $auto_install == "1" ]] && zenity --info --text="$(automods_prompt)" $big_prompt 2>/dev/null } depot_watcher(){ - local last=$(echo "${@: -1}") - payload(){ - echo -e "itemcount=1&publishedfileids[0]=$last" - } - post(){ - curl -s -X POST -H "Content-Type:application/x-www-form-urlencoded" -d "$(payload)" 'https://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1/?format=json' - } - depot_id=$(post | jq -r '.response.publishedfiledetails[].hcontent_file') - - local dir="$default_steam_path/ubuntu12_32/steamapps/content" - local alt="$default_steam_path/ubuntu12_32/steamapps/_content" - local log="$default_steam_path/logs/content_log.txt" - [[ -d $dir ]] && { mv "$dir" $alt; } - [[ -h "$dir" ]] && unlink "$dir" - [[ -d $staging_dir/app_$aid ]] && rm -rf $staging_dir/app_$aid - ln -s $staging_dir "$dir" - echo "" > "$log" - - until [[ "$(tail -n1 $log)" =~ "/depot/$aid/manifest/$depot_id" ]]; do - sleep 0.1s + readarray -t watched_mods <<< "$@" + for i in "${watched_mods[@]}"; do + echo "[DZGUI] Downloading mod $i" + until [[ -d "$workshop_dir/$i" ]]; do + sleep 0.1s + done + until [[ "$(tail -n1 $log)" =~ AppID[[:space:]]221100[[:space:]]scheduler[[:space:]]finished ]]; do + sleep 0.1s + done done - until [[ "$(tail -n1 $log)" =~ "Closing connection" ]]; do - sleep 0.1s - done - readarray -t modids <<< "$@" - echo "[DZGUI] Moving mods" - for i in "${modids[@]}"; do - rsync -a --delete "$staging_dir/app_$aid/item_$i/" "$workshop_dir/$i/" && - rm -rf "$staging_dir/app_$aid/item_$i" - done - echo "[DZGUI] Cleanup" - rmdir $staging_dir/app_$aid - unlink "$dir" - [[ -d "$alt" ]] && { mv "$alt" "$dir"; } + echo "Finished" >> "/tmp/dz.status" + return 0 } console_dl(){ readarray -t modids <<< "$@" - steam steam://open/console + steam steam://open/console 2>/dev/null 1>&2 sleep 1s for i in "${modids[@]}"; do - xdotool type --delay 15 "download_item $aid $i" + xdotool type --delay 15 "workshop_download_item $aid $i" sleep 0.2s xdotool key Return sleep 0.2s @@ -1137,7 +1108,7 @@ find_default_path(){ popup(){ [[ $1 -eq 100 ]] && zenity --info --text="This feature requires xdotool" --title=DZGUI --width=500 [[ $1 -eq 200 ]] && zenity --info --text="This feature is not supported on Gaming Mode" --title=DZGUI --width=500 - [[ $1 -eq 300 ]] && zenity --info --text="\nThe Steam console will now open and briefly issue commands to\ndownload the workshop files, then return to the small Steam client\nwhile they download.\n\nEnsure that the Steam console has keyboard and mouse focus\n(keep hands off keyboard) while the commands are being issued.\n\nIt will only take a few seconds to queue the downloads, but if a\npopup or notification window steals focus, it could obstruct\nthe process." --title=DZGUI --width=500 + [[ $1 -eq 300 ]] && zenity --info --text="\nThe Steam console will now open and briefly issue commands to\ndownload the workshop files, then return to the small Steam client\nwhile they download.\n\nEnsure that the Steam console has keyboard and mouse focus\n(keep hands off keyboard) while the commands are being issued.\n\nIt will only take a few seconds to queue the downloads, but if a\npopup or notification window steals focus, it could obstruct\nthe process." --title=DZGUI --width=500 2>/dev/null } requires_xdo(){ zenity --info --text="This feature requires xdojool" --title=DZGUI --width=500 From 77bd6bdf9cf9dddbf191ebf8dcfbd77340602679 Mon Sep 17 00:00:00 2001 From: aclist Date: Wed, 26 Oct 2022 14:26:06 +0900 Subject: [PATCH 016/144] Streamline progress notifier --- changelog.md | 4 ++++ dzgui.sh | 26 ++++++-------------------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/changelog.md b/changelog.md index d423f46..172d85c 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,10 @@ - Custom query API - Standardize dialogs +## [3.1.0-rc.7] 2022-10-25 +### Changed +- Simplify mod progress watcher + ## [3.1.0-rc.6] 2022-10-25 ### Fixed - Download mod metadata diff --git a/dzgui.sh b/dzgui.sh index 9213a60..f36e0a8 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.1.0-rc.6 +version=3.1.0-rc.7 aid=221100 game="dayz" @@ -542,12 +542,12 @@ auto_mod_install(){ log="$default_steam_path/logs/content_log.txt" [[ -f "/tmp/dz.status" ]] && rm "/tmp/dz.status" touch "/tmp/dz.status" - depot_watcher "$diff" & console_dl "$diff" && - steam steam://open/downloads 2>/dev/null 1>&2 - until [[ $(tail -n1 "/tmp/dz.status") =~ "Finished" ]]; do - sleep 0.1s - done + steam steam://open/downloads && 2>/dev/null 1>&2 + until [[ -z $(comm -23 <(printf "%s\n" "${modids[@]}" | sort) <(ls -1 $workshop_dir | sort)) ]]; do + local missing=$(comm -23 <(printf "%s\n" "${modids[@]}" | sort) <(ls -1 $workshop_dir | sort) | wc -l) + echo "# Downloaded $((${#modids[@]}-missing)) of ${#modids[@]} mods" + done | zenity --pulsate --progress --title=DZGUI --auto-close --no-cancel --width=500 2>/dev/null compare if [[ -z $diff ]]; then check_timestamps @@ -1059,20 +1059,6 @@ toggle_headless(){ [[ $is_steam_deck -eq 1 ]] && big_prompt="--width=800" [[ $auto_install == "1" ]] && zenity --info --text="$(automods_prompt)" $big_prompt 2>/dev/null } -depot_watcher(){ - readarray -t watched_mods <<< "$@" - for i in "${watched_mods[@]}"; do - echo "[DZGUI] Downloading mod $i" - until [[ -d "$workshop_dir/$i" ]]; do - sleep 0.1s - done - until [[ "$(tail -n1 $log)" =~ AppID[[:space:]]221100[[:space:]]scheduler[[:space:]]finished ]]; do - sleep 0.1s - done - done - echo "Finished" >> "/tmp/dz.status" - return 0 -} console_dl(){ readarray -t modids <<< "$@" steam steam://open/console 2>/dev/null 1>&2 From 9c4ba1ff7b559811aac3f1f0a3406f8c4ec27094 Mon Sep 17 00:00:00 2001 From: aclist Date: Wed, 26 Oct 2022 22:23:38 +0900 Subject: [PATCH 017/144] Foreground window --- changelog.md | 4 + dzgui.sh | 8 +- dzgui.sh.old | 1698 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1707 insertions(+), 3 deletions(-) create mode 100755 dzgui.sh.old diff --git a/changelog.md b/changelog.md index 172d85c..b2237e4 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,10 @@ - Custom query API - Standardize dialogs +## [3.1.0-rc.8] 2022-10-26 +### Changed +- Foreground mod progress watcher + ## [3.1.0-rc.7] 2022-10-25 ### Changed - Simplify mod progress watcher diff --git a/dzgui.sh b/dzgui.sh index f36e0a8..ba0eb3c 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.1.0-rc.7 +version=3.1.0-rc.8 aid=221100 game="dayz" @@ -545,9 +545,11 @@ auto_mod_install(){ console_dl "$diff" && steam steam://open/downloads && 2>/dev/null 1>&2 until [[ -z $(comm -23 <(printf "%s\n" "${modids[@]}" | sort) <(ls -1 $workshop_dir | sort)) ]]; do + win=$(xdotool search --name "DZG Watcher") + [[ ! $(xdotool getwindowfocus) -eq $win ]] && xdotool windowraise $win local missing=$(comm -23 <(printf "%s\n" "${modids[@]}" | sort) <(ls -1 $workshop_dir | sort) | wc -l) echo "# Downloaded $((${#modids[@]}-missing)) of ${#modids[@]} mods" - done | zenity --pulsate --progress --title=DZGUI --auto-close --no-cancel --width=500 2>/dev/null + done | zenity --pulsate --progress --title="DZG Watcher" --auto-close --no-cancel --width=500 2>/dev/null compare if [[ -z $diff ]]; then check_timestamps @@ -1094,7 +1096,7 @@ find_default_path(){ popup(){ [[ $1 -eq 100 ]] && zenity --info --text="This feature requires xdotool" --title=DZGUI --width=500 [[ $1 -eq 200 ]] && zenity --info --text="This feature is not supported on Gaming Mode" --title=DZGUI --width=500 - [[ $1 -eq 300 ]] && zenity --info --text="\nThe Steam console will now open and briefly issue commands to\ndownload the workshop files, then return to the small Steam client\nwhile they download.\n\nEnsure that the Steam console has keyboard and mouse focus\n(keep hands off keyboard) while the commands are being issued.\n\nIt will only take a few seconds to queue the downloads, but if a\npopup or notification window steals focus, it could obstruct\nthe process." --title=DZGUI --width=500 2>/dev/null + [[ $1 -eq 300 ]] && zenity --info --text="\nThe Steam console will now open and briefly issue commands to\ndownload the workshop files, then return to the download progress page.\n\nEnsure that the Steam console has keyboard and mouse focus\n(keep hands off keyboard) while the commands are being issued.\n\nIt will only take a few seconds to queue the downloads, but if a\npopup or notification window steals focus, it could obstruct\nthe process." --title=DZGUI --width=500 2>/dev/null } requires_xdo(){ zenity --info --text="This feature requires xdojool" --title=DZGUI --width=500 diff --git a/dzgui.sh.old b/dzgui.sh.old new file mode 100755 index 0000000..1934f87 --- /dev/null +++ b/dzgui.sh.old @@ -0,0 +1,1698 @@ +#!/bin/bash + +set -o pipefail +version=2.7.1 + +aid=221100 +game="dayz" +workshop="steam://url/CommunityFilePage/" +api="https://api.battlemetrics.com/servers" +sd_res="--width=1280 --height=800" +config_path="$HOME/.config/dztui/" +config_file="${config_path}dztuirc" +tmp=/tmp/dzgui.tmp +fifo=/tmp/table.tmp +separator="%%" +check_config_msg="Check config values and restart." +issues_url="https://github.com/aclist/dztui/issues" +url_prefix="https://raw.githubusercontent.com/aclist/dztui" +stable_url="$url_prefix/dzgui" +testing_url="$url_prefix/testing" +releases_url="https://github.com/aclist/dztui/releases/download/browser" +help_url="https://aclist.github.io/dzgui/dzgui" +freedesktop_path="$HOME/.local/share/applications" +sd_install_path="$HOME/.local/share/dzgui" +helpers_path="$sd_install_path/helpers" +geo_file="$helpers_path/ips.csv" +km_helper="$helpers_path/latlon" +sums_path="$helpers_path/sums.md5" +scmd_file="$helpers_path/scmd.sh" +km_helper_url="$releases_url/latlon" +db_file="$releases_url/ips.csv.gz" +sums_url="$stable_url/helpers/sums.md5" +scmd_url="$stable_url/helpers/scmd.sh" +notify_url="$stable_url/helpers/d.html" +notify_img_url="$stable_url/helpers/d.webp" +forum_url="https://github.com/aclist/dztui/discussions" + +update_last_seen(){ + mv $config_file ${config_path}dztuirc.old + nr=$(awk '/seen_news=/ {print NR}' ${config_path}dztuirc.old) + seen_news="seen_news=\"$sum\"" + awk -v "var=$seen_news" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > $config_file + printf "[DZGUI] Updated last seen news item to '$sum'\n" + source $config_file +} +check_news(){ + [[ $branch == "stable" ]] && news_url="$stable_url/news" + [[ $branch == "testing" ]] && news_url="$testing_url/news" + result=$(curl -Ls "$news_url") + sum=$(echo -n "$result" | md5sum | awk '{print $1}') +} +print_news(){ + check_news + if [[ $sum == $seen_news || -z $result ]]; then + hchar="" + news="" + else + hchar="─" + news="$result\n$(awk -v var="$hchar" 'BEGIN{for(c=0;c<90;c++) printf var;}')\n" + update_last_seen + fi +} +#TODO: prevent connecting to offline servers +#TODO: abstract zenity title params and dimensions + +declare -A deps +deps=([awk]="5.1.1" [curl]="7.80.0" [jq]="1.6" [tr]="9.0" [zenity]="3.42.1" [steam]="1.0.0") +changelog(){ + if [[ $branch == "stable" ]]; then + md="https://raw.githubusercontent.com/aclist/dztui/dzgui/changelog.md" + else + md="https://raw.githubusercontent.com/aclist/dztui/testing/changelog.md" + fi + prefix="This window can be scrolled." + echo $prefix + echo "" + curl -Ls "$md" | awk '/Unreleased/ {flag=1}flag' +} + +depcheck(){ + for dep in "${!deps[@]}"; do + command -v $dep 2>&1>/dev/null || (printf "Requires %s >=%s\n" $dep ${deps[$dep]}; exit 1) + done +} +watcher_deps(){ + if [[ ! $(command -v wmctrl) ]] && [[ ! $(command -v xdotool) ]]; then + echo "100" + warn "Missing dependency: requires 'wmctrl' or 'xdotool'.\nInstall from your system's package manager." + exit 1 + fi +} +init_items(){ + #array order determines menu selector; this is destructive +items=( + "[Connect]" + " Server browser" + " My servers" + " Quick connect to favorite server" + "[Manage servers]" + " Connect by IP" + " Add server by ID" + " Add favorite server" + " Delete server" + "[Options]" + " List installed mods" + " View changelog" + " Advanced options" + "[Help]" + " Help file ⧉" + " Report bug ⧉" + " Become a beta tester ⧉" + ) +} +warn_and_exit(){ + zenity --info --title="DZGUI" --text="$1" --width=500 --icon-name="dialog-warning" 2>/dev/null + printf "[DZGUI] %s\n" "$check_config_msg" + exit +} +warn(){ + zenity --info --title="DZGUI" --text="$1" --width=500 --icon-name="dialog-warning" 2>/dev/null +} +info(){ + zenity --info --title="DZGUI" --text="$1" --width=500 2>/dev/null +} +set_api_params(){ + response=$(curl -s "$api" -H "Authorization: Bearer "$api_key"" -G -d "sort=-players" \ + -d "filter[game]=$game" -d "filter[ids][whitelist]=$list_of_ids") + list_response=$response + first_entry=1 +} +query_api(){ + #TODO: prevent drawing list if null values returned without API error + if [[ $one_shot_launch -eq 1 ]]; then + list_of_ids="$fav" + else + if [[ -n $fav ]]; then + list_of_ids="$whitelist,$fav" + else + list_of_ids="$whitelist" + fi + fi + set_api_params + if [[ "$(jq -r 'keys[]' <<< "$response")" == "errors" ]]; then + code=$(jq -r '.errors[] .status' <<< $response) + #TODO: fix granular api codes + if [[ $code -eq 401 ]]; then + warn "Error $code: malformed API key" + return + elif [[ $code -eq 500 ]]; then + warn "Error $code: malformed server list" + return + fi + + fi + if [[ -z $(echo $response | jq '.data[]') ]]; then + warn "95: API returned empty response. Check config file." + return + fi +} +write_config(){ +cat <<-END +#Path to DayZ installation +steam_path="$steam_path" + +#Your unique API key +api_key="$api_key" + +#Comma-separated list of server IDs +whitelist="$whitelist" + +#Favorite server to fast-connect to (limit one) +fav="$fav" + +#Custom player name (optional, required by some servers) +name="$name" + +#Set to 1 to perform dry-run and print launch options +debug="$debug" + +#Toggle stable/testing branch +branch="$branch" + +#Last seen news item +seen_news="$seen_news" + +#Steam API key +steam_api="$steam_api" + +#Terminal emulator +term="$term" + +#Auto-install mods +auto_install="$auto_install" + +#Automod staging directory +staging_dir="$staging_dir" + END +} +write_desktop_file(){ +cat <<-END +[Desktop Entry] +Version=1.0 +Type=Application +Terminal=false +Exec=$sd_install_path/dzgui.sh +Name=DZGUI +Comment=dzgui +Icon=$sd_install_path/dzgui +Categories=Game + END +} +freedesktop_dirs(){ + mkdir -p "$sd_install_path" + mkdir -p "$freedesktop_path" + #TODO: update url + curl -s "$version_url" > "$sd_install_path/dzgui.sh" + chmod +x "$sd_install_path/dzgui.sh" + img_url="https://raw.githubusercontent.com/aclist/dztui/testing/images" + for i in dzgui grid.png hero.png logo.png; do + curl -s "$img_url/$i" > "$sd_install_path/$i" + done + write_desktop_file > "$freedesktop_path/dzgui.desktop" + if [[ $is_steam_deck -eq 1 ]]; then + write_desktop_file > "$HOME/Desktop/dzgui.desktop" + fi +} +file_picker(){ + while true; do + local path=$(zenity --file-selection --directory 2>/dev/null) + if [[ -z "$path" ]]; then + return + else + echo "[DZGUI]" Set mod staging path to "$path" + staging_dir="$path" + write_config > $config_file + return + fi + done +} +guess_path(){ + echo "# Checking for default DayZ path" + path=$(find $HOME -type d -regex ".*/steamapps/common/DayZ$" -print -quit) + if [[ -n "$path" ]]; then + clean_path=$(echo -e "$path" | awk -F"/steamapps" '{print $1}') + steam_path="$clean_path" + else + echo "# Searching for alternate DayZ path. This may take some time." + path=$(find / -type d \( -path "/proc" -o -path "*/timeshift" -o -path "/tmp" -o -path "/usr" -o -path "/boot" -o -path "/proc" -o -path "/root" -o -path "/run" -o -path "/sys" -o -path "/etc" -o -path "/var" -o -path "/run" -o -path "/lost+found" \) -prune -o -regex ".*/steamapps/common/DayZ$" -print -quit 2>/dev/null) + clean_path=$(echo -e "$path" | awk -F"/steamapps" '{print $1}') + steam_path="$clean_path" + fi +} +create_config(){ + while true; do + player_input="$(zenity --forms --add-entry="Player name (required for some servers)" --add-entry="API key" --add-entry="Server 1 (you can add more later)" --title="DZGUI" --text="DZGUI" --add-entry="Server 2" --add-entry="Server 3" --add-entry="Server 4" $sd_res --separator="│" 2>/dev/null)" + #explicitly setting IFS crashes zenity in loop + #and mapfile does not support high ascii delimiters + #so split fields with newline + readarray -t args < <(echo "$player_input" | sed 's/│/\n/g') + name="${args[0]}" + api_key="${args[1]}" + server_1="${args[2]}" + server_2="${args[3]}" + server_3="${args[4]}" + server_4="${args[5]}" + + [[ -z $player_input ]] && exit + if [[ -z $api_key ]]; then + warn "API key: invalid value" + elif [[ -z $server_1 ]]; then + warn "Server 1: cannot be empty" + elif [[ ! $server_1 =~ ^[0-9]+$ ]]; then + warn "Server 1: only numeric IDs" + elif [[ -n $server_2 ]] && [[ ! $server_2 =~ ^[0-9]+$ ]]; then + warn "Server 2: only numeric IDs" + elif [[ -n $server_3 ]] && [[ ! $server_3 =~ ^[0-9]+$ ]]; then + warn "Server 3: only numeric IDs" + elif [[ -n $server_4 ]] && [[ ! $server_3 =~ ^[0-9]+$ ]]; then + warn "Server 4: only numeric IDs" + else + whitelist=$(echo "$player_input" | awk -F"│" '{OFS=","}{print $3,$4,$5,$6}' | sed 's/,*$//g' | sed 's/^,*//g') + guess_path > >(zenity --width 500 --progress --auto-close --pulsate 2>/dev/null) && + echo "[DZGUI] Set path to $steam_path" + #FIXME: tech debt: gracefully exit if user cancels search process + mkdir -p $config_path; write_config > $config_file + info "Config file created at $config_file." + return + fi + done + +} +err(){ + printf "[ERROR] %s\n" "$1" +} +varcheck(){ + [[ -z $api_key ]] && (err "Error in key: 'api_key'") + [[ -z $whitelist ]] && (err "Error in key: 'whitelist'") + [[ ! -d "$game_dir" ]] && (err "Malformed game path") + [[ $whitelist =~ [[:space:]] ]] && (err "Separate whitelist values with commas") +} +run_depcheck(){ + if [[ -z $(depcheck) ]]; then + : + else + echo "100" + zenity --warning --ok-label="Exit" --title="DZGUI" --text="$(depcheck)" + exit + fi +} +run_varcheck(){ + source $config_file + workshop_dir="$steam_path/steamapps/workshop/content/$aid" + game_dir="$steam_path/steamapps/common/DayZ" + if [[ -z $(varcheck) ]]; then + : + else + zenity --warning --width 500 --text="$(varcheck)" 2>/dev/null + printf "[DZGUI] %s\n" "$check_config_msg" + zenity --question --cancel-label="Exit" --text="Malformed config file. This is probably user error.\nStart first-time setup process again?" --width=500 2>/dev/null + code=$? + if [[ $code -eq 1 ]]; then + exit + else + echo "100" + create_config + fi + fi +} +config(){ + if [[ ! -f $config_file ]]; then + zenity --width 500 --info --text="Config file not found. Click OK to proceed to first-time setup." 2>/dev/null + code=$? + #prevent progress if user hits ESC + if [[ $code -eq 1 ]]; then + exit + else + create_config + fi + else + source $config_file + fi + +} +open_mod_links(){ + link_file=$(mktemp) + echo "" > $link_file + echo "DZGUI" >> $link_file + echo "

DZGUI

" >> $link_file + echo "

Open these links and subscribe to them on the Steam Workshop, then continue with the application prompts.
Note: it may take some time for mods to synchronize before DZGUI can see them.
It can help to have Steam in an adjacent window so that you can see the downloads completing.

" >> $link_file + n=1 + for i in $diff; do + echo "$n. ${workshop}$i
" + let n++ + done >> $link_file + echo "" >> $link_file + browser "$link_file" 2>/dev/null & + +} +steam_deck_mods(){ + until [[ -z $diff ]]; do + next=$(echo -e "$diff" | head -n1) + zenity --question --ok-label="Open" --cancel-label="Cancel" --title="DZGUI" --text="Missing mods. Click [Open] to open mod $next in Steam Workshop and subscribe to it by clicking the green Subscribe button. After the mod is downloaded, return to this menu to continue validation." --width=500 2>/dev/null + rc=$? + if [[ $rc -eq 0 ]]; then + echo "[DZGUI] Opening ${workshop}$next" + steam steam://url/CommunityFilePage/$next 2>/dev/null & + zenity --info --title="DZGUI" --ok-label="Next" --text="Click [Next] to continue mod check." --width=500 2>/dev/null + else + return 1 + fi + compare + done +} +set_term(){ + local term="$1" + local tterm="term=\"$term\"" + mv $config_file ${config_path}dztuirc.old + nr=$(awk '/term=/ {print NR}' ${config_path}dztuirc.old) + awk -v "var=$tterm" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > $config_file + printf "[DZGUI] Set term to '$term'\n" + source $config_file +} +sel_term(){ + #only terminals known to support -e flag + for i in "$TERMINAL" urxvt alacritty konsole gnome-terminal terminator xfce4-terminal xterm tilix; do + [[ $(command -v $i) ]] && terms+=($i) + done + #FIXME: if no terms, error + local terms=$(printf "%s\n" "${terms[@]}" | sort -u) + term=$(echo "$terms" | zenity --list --column=Terminal --height=800 --width=1200 --text="Select your preferred terminal emulator to run steamcmd (setting will be saved)" --title=DZGUI 2>/dev/null) +} +calc_mod_sizes(){ + for i in "$diff"; do + local mods+=$(grep -w "$i" /tmp/modsizes | awk '{print $1}') + done + totalmodsize=$(echo -e "${mods[@]}" | awk '{s+=$1}END{print s}') +} +term_params(){ + case $term in + konsole) $term --hold -e "bash $helpers_path/scmd.sh $totalmodsize $1";; + urxvt) $term -e bash -c "/$helpers_path/scmd.sh $totalmodsize $1";; + alacritty) $term -e bash -c "/$helpers_path/scmd.sh $totalmodsize $1";; + terminator|xterm|tilix|xfce4-terminal) $term -e "bash $helpers_path/scmd.sh $totalmodsize $1";; + esac +} +auto_mod_install(){ + cmd=$(printf "%q " "$@") + if [[ -z "$term" ]]; then + if [[ $is_steam_deck -eq 1 ]]; then + set_term konsole + return 0 + else + sel_term && set_term "$term" + fi + fi + [[ -z "$term" ]] && return 1 + echo "[DZGUI] Kicking off auto mod script" + calc_mod_sizes + term_params "$cmd" + compare + if [[ -z $diff ]]; then + passed_mod_check > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) + launch + else + warn "Auto mod installation failed or some mods missing.\nReverting to manual mode." + return 1 + fi +} +test_display_mode(){ + pgrep -a gamescope | grep -q "generate-drm-mode" + [[ $? -eq 0 ]] && gamemode=1 +} +foreground(){ + if [[ $(command -v wmctrl) ]]; then + wmctrl -a "DZG Watcher" + else + + local window_id=$(xdotool search --name "DZG Watcher") + xdotool windowactivate $window_id + fi +} +manual_mod_install(){ + [[ $is_steam_deck -eq 1 ]] && test_display_mode + if [[ $gamemode -eq 1 ]]; then + steam_deck_mods + else + local ex="/tmp/dzc.tmp" + [[ -f $ex ]] && rm $ex + watcher(){ + readarray -t stage_mods <<< "$diff" + for((i=0;i<${#stage_mods[@]};i++)); do + [[ -f $ex ]] && return 1 + local downloads_dir="$steam_path/steamapps/workshop/downloads/$aid" + local workshop_dir="$steam_path/steamapps/workshop/content/$aid" + steam "steam://url/CommunityFilePage/${stage_mods[$i]}" + echo "# Opening workshop page for ${stage_mods[$i]}. If you see no progress after subscribing, try unsubscribing and resubscribing again until the download commences." + sleep 1s + foreground + until [[ -d $downloads_dir/${stage_mods[$i]} ]]; do + [[ -f $ex ]] && return 1 + sleep 0.1s + if [[ -d $workshop_dir/${stage_mods[$i]} ]]; then + break + fi + done + foreground + echo "# Steam is downloading ${stage_mods[$i]} (mod $((i+1)) of ${#stage_mods[@]})" + until [[ -d $workshop_dir/${stage_mods[$i]} ]]; do + [[ -f $ex ]] && return 1 + sleep 0.1s + done + foreground + echo "# ${stage_mods[$i]} moved to mods dir" + done + echo "100" + } + watcher > >(zenity --pulsate --progress --auto-close --title="DZG Watcher" --width=500 2>/dev/null; rc=$?; [[ $rc -eq 1 ]] && touch $ex) + compare + if [[ -z $diff ]]; then + passed_mod_check > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) + launch + else + return 1 + fi + fi +} +# if [[ $is_steam_deck -eq 0 ]]; then +# open_mod_links +# until [[ -z $diff ]]; do +# zenity --question --title="DZGUI" --ok-label="Next" --cancel-label="Cancel" --text="Opened mod links in browser.\nClick [Next] when all mods have been subscribed to.\nThis dialog may reappear if clicking [Next] too soon\nbefore mods are synchronized in the background." --width=500 2>/dev/null +# rc=$? +# if [[ $rc -eq 0 ]]; then +# compare +# open_mod_links +# else +# return +# fi +# done +# else +# steam_deck_mods +# rc=$? +# [[ $rc -eq 1 ]] && return 1 +# fi +# passed_mod_check > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) +encode(){ + echo "$1" | md5sum | cut -c -8 +} +stale_symlinks(){ + for l in $(find "$game_dir" -xtype l); do + unlink $l + done +} +legacy_symlinks(){ + for d in "$game_dir"/*; do + if [[ $d =~ @[0-9]+-.+ ]]; then + unlink "$d" + fi + done + for d in "$workshop_dir"/*; do + 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') + if [[ -h "$game_dir/@$encoded_id" ]]; then + unlink "$game_dir/@$encoded_id" + fi + done +} +symlinks(){ + for d in "$workshop_dir"/*; do + id=$(awk -F"= " '/publishedid/ {print $2}' "$d"/meta.cpp | awk -F\; '{print $1}') + encoded_id=$(encode "$id") + mod=$(awk -F\" '/name/ {print $2}' "$d"/meta.cpp | sed -E 's/[^[:alpha:]0-9]+/_/g; s/^_|_$//g') + link="@$encoded_id" + if [[ -h "$game_dir/$link" ]]; then + : + else + printf "[DZGUI] Creating symlink for $mod\n" + ln -fs "$d" "$game_dir/$link" + fi + done +} +passed_mod_check(){ + echo "[DZGUI] Passed mod check" + echo "# Preparing symlinks" + stale_symlinks + legacy_symlinks + symlinks + echo "100" + +} +connect(){ + #TODO: sanitize/validate input + readarray -t qport_arr <<< "$qport_list" + if [[ -z ${qport_arr[@]} ]]; then + err "98: Failed to obtain query ports" + return + fi + ip=$(echo "$1" | awk -F"$separator" '{print $1}') + bid=$(echo "$1" | awk -F"$separator" '{print $2}') + if [[ $2 == "ip" ]]; then + fetch_mods_sa "$ip" > >(zenity --pulsate --progress --auto-close --no-cancel --width=500 2>/dev/null) + else + fetch_mods "$bid" + fi + if [[ $ret -eq 96 ]]; then + unset ret + return + fi + validate_mods + rc=$? + [[ $rc -eq 1 ]] && return + compare + if [[ -n $diff ]]; then + if [[ $auto_install -eq 1 ]]; then + auto_mod_install "$diff" + rc=$? + [[ $rc -eq 1 ]] && manual_mod_install + else + manual_mod_install + fi + else + passed_mod_check > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) + launch + fi +} +fetch_mods(){ + remote_mods=$(curl -s "$api" -H "Authorization: Bearer "$api_key"" -G -d filter[ids][whitelist]="$1" -d "sort=-players" \ + | jq -r '.data[] .attributes .details .modIds[]') +} +fetch_mods_sa(){ + sa_ip=$(echo "$1" | awk -F: '{print $1}') + for i in ${qport_arr[@]}; do + if [[ -n $(echo "$i" | awk -v ip=$ip '$0 ~ ip') ]]; then + sa_port=$(echo $i | awk -v ip=$ip -F$separator '$0 ~ ip {print $2}') + fi + done + echo "[DZGUI] Querying modlist on ${sa_ip}:${sa_port}" + echo "# Querying modlist on ${sa_ip}:${sa_port}" + local response=$(curl -Ls "https://dayzsalauncher.com/api/v1/query/$sa_ip/$sa_port") + local status=$(echo "$response" | jq '.status') + if [[ $status -eq 1 ]]; then + echo "100" + err "97: Failed to fetch modlist" + zenity --error --title="DZGUI" --width=500 --text="[ERROR] 97: Failed to fetch modlist" 2>/dev/null && + ret=96 + return + fi + remote_mods=$(echo "$response" | jq -r '.result.mods[].steamWorkshopId') + qport_arr=() +} +prepare_ip_list(){ + ct=$(< "$1" jq '[.response.servers[]]|length') + for((i=0;i<$ct;i++));do + name=$(< $json jq --arg i $i -r '[.servers[]][($i|tonumber)].name') + addr=$(< $json jq --arg i $i -r '[.servers[]][($i|tonumber)].addr') + ip=$(echo "$addr" | awk -F: '{print $1}') + players=$(< $json jq --arg i $i -r '[.servers[]][($i|tonumber)].players') + max_players=$(< $json jq --arg i $i -r '[.servers[]][($i|tonumber)].max_players') + gameport=$(< $json jq --arg i $i -r '[.servers[]][($i|tonumber)].gameport') + ip_port=$(echo "$ip:$gameport") + time=$(< $json jq --arg i $i -r '[.servers[]][($i|tonumber)].gametype' | grep -oP '(?/dev/null) + if [[ $? -eq 1 ]]; then + return_from_table=1 + return + fi + if [[ -z $sel ]]; then + echo "No selection" + else + local gameport="$(echo "$sel" | awk -F: '{print $2}')" + local ip="$(echo "$sel" | awk -F: '{print $1}')" + local addr=$(< $json jq -r --arg gameport $gameport '.servers[]|select(.gameport == ($gameport|tonumber)).addr') + local qport=$(echo "$addr" | awk -F: '{print $2}') + local sa_ip=$(echo "$ip:$gameport%%$qport") + qport_list="$sa_ip" + connect "$sel" "ip" + fi + done +} +fetch_ip_metadata(){ + local meta_file=$(mktemp) + source $config_file + url="https://api.steampowered.com/IGameServersService/GetServerList/v1/?filter=\appid\221100\gameaddr\\$ip&key=$steam_api" + curl -Ls "$url" > $meta_file + json=$(mktemp) + < $meta_file jq '.response' > $json + res=$(< $meta_file jq -er '.response.servers[]' 2>/dev/null) + if [[ ! $? -eq 0 ]]; then + warn "[ERROR] 96: Failed to retrieve IP metadata. Check IP or API key and try again." + echo "[DZGUI] 96: Failed to retrieve IP metadata" + + else + ip_table + fi +} + +#TODO: local servers +#local_ip(){ +#(^127\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.) +#} +test_steam_api(){ + local code=$(curl -ILs "https://api.steampowered.com/IGameServersService/GetServerList/v1/?filter=\appid\221100&limit=10&key=$steam_api" \ + | grep -E "^HTTP") + [[ $code =~ 403 ]] && echo 1 + [[ $code =~ 200 ]] && echo 0 +} +add_steam_api(){ + [[ $(test_steam_api) -eq 1 ]] && return 1 + mv $config_file ${config_path}dztuirc.old + nr=$(awk '/steam_api=/ {print NR}' ${config_path}dztuirc.old) + steam_api="steam_api=\"$steam_api\"" + awk -v "var=$steam_api" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > ${config_path}dztuirc + echo "[DZGUI] Added Steam API key" + zenity --info --title="DZGUI" --text="Added Steam API key to:\n\n${config_path}dztuirc\nIf errors occur, you can restore the file:\n${config_path}dztuirc.old" 2>/dev/null + source $config_file +} +check_steam_api(){ + if [[ -z $steam_api ]]; then + steam_api=$(zenity --entry --text="Key 'steam_api' not present in config file. Enter Steam API key:" --title="DZGUI" 2>/dev/null) + if [[ $? -eq 1 ]] ; then + return + elif [[ ${#steam_api} -lt 32 ]] || [[ $(test_steam_api) -eq 1 ]]; then + zenity --warning --title="DZGUI" --text="Check API key and try again." 2>/dev/null + return 1 + else + add_steam_api + fi + fi +} +validate_ip(){ + echo "$1" | grep -qP '^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$' +} +connect_by_ip(){ + source $config_file + check_steam_api + [[ $? -eq 1 ]] && return + while true; do + if [[ $return_from_table -eq 1 ]]; then + return_from_table=0 + return + fi + ip=$(zenity --entry --text="Enter server IP (omit port)" --title="DZGUI" 2>/dev/null) + [[ $? -eq 1 ]] && return + if validate_ip "$ip"; then + fetch_ip_metadata + else + continue + fi + done +} +fetch_mods(){ + remote_mods=$(curl -s "$api" -H "Authorization: Bearer "$api_key"" -G -d filter[ids][whitelist]="$1" -d "sort=-players" \ + | jq -r '.data[] .attributes .details .modIds[]') +} +query_defunct(){ + max=${#modlist[@]} + concat(){ + for ((i=0;i<$max;i++)); do + echo "publishedfileids[$i]=${modlist[$i]}&" + done | awk '{print}' ORS='' + } + payload(){ + echo -e "itemcount=${max}&$(concat)" + } + post(){ + curl -s -X POST -H "Content-Type:application/x-www-form-urlencoded" -d "$(payload)" 'https://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1/?format=json' + } + result=$(post | jq -r '.[].publishedfiledetails[] | select(.result==1) | "\(.file_size) \(.publishedfileid)"') + echo "$result" > /tmp/modsizes + readarray -t newlist <<< $(echo -e "$result" | awk '{print $2}') +} +validate_mods(){ + url="https://steamcommunity.com/sharedfiles/filedetails/?id=" + newlist=() + readarray -t modlist <<< $remote_mods + query_defunct +} +server_modlist(){ + for i in "${newlist[@]}"; do + printf "$i\n" + done +} +compare(){ + diff=$(comm -23 <(server_modlist | sort) <(installed_mods | sort)) +} + +installed_mods(){ + ls -1 "$workshop_dir" +} +concat_mods(){ + if [[ -z ${remote_mods[@]} ]]; then + return 1 + else + readarray -t serv <<< "$(server_modlist)" + for i in "${serv[@]}"; do + id=$(awk -F"= " '/publishedid/ {print $2}' "$workshop_dir"/$i/meta.cpp | awk -F\; '{print $1}') + encoded_id=$(encode $id) + link="@$encoded_id;" + echo -e "$link" + done | tr -d '\n' | perl -ple 'chop' + fi +} +launch(){ + mods=$(concat_mods) + if [[ $debug -eq 1 ]]; then + launch_options="steam -applaunch $aid -connect=$ip -nolauncher -nosplash -name=$name -skipintro \"-mod=$mods\"" + print_launch_options="$(printf "This is a dry run.\nThese options would have been used to launch the game:\n\n$launch_options\n" | fold -w 60)" + zenity --question --title="DZGUI" --ok-label="Write to file" --cancel-label="Back"\ + --text="$print_launch_options" 2>/dev/null + if [[ $? -eq 0 ]]; then + source_script=$(realpath "$0") + source_dir=$(dirname "$source_script") + echo "$launch_options" > "$source_dir"/options.log + echo "[DZGUI] Wrote launch options to $source_dir/options.log" + zenity --info --width 500 --title="DZGUI" --text="Wrote launch options to \n$source_dir/options.log" 2>/dev/null + fi + + else + echo "[DZGUI] All OK. Launching DayZ" + zenity --width 500 --title="DZGUI" --info --text="Launch conditions satisfied.\nDayZ will now launch after clicking [OK]." 2>/dev/null + steam -applaunch $aid -connect=$ip -nolauncher -nosplash -skipintro -name=$name \"-mod=$mods\" + exit + fi + one_shot_launch=0 +} +browser(){ + if [[ -n "$BROWSER" ]]; then + "$BROWSER" "$1" 2>/dev/null + else + xdg-open "$1" 2>/dev/null + fi +} +report_bug(){ + echo "[DZGUI] Opening issues page in browser" + if [[ $is_steam_deck -eq 1 ]]; then + steam steam://openurl/"$issues_url" 2>/dev/null + elif [[ $is_steam_deck -eq 0 ]]; then + browser "$issues_url" 2>/dev/null & + fi +} +forum(){ + echo "[DZGUI] Opening forum in browser" + if [[ $is_steam_deck -eq 1 ]]; then + steam steam://openurl/"$forum_url" 2>/dev/null + elif [[ $is_steam_deck -eq 0 ]]; then + browser "$forum_url" 2>/dev/null & + fi +} +help_file(){ + echo "[DZGUI] Opening help file in browser" + if [[ $is_steam_deck -eq 1 ]]; then + steam steam://openurl/"$help_url" 2>/dev/null + elif [[ $is_steam_deck -eq 0 ]]; then + browser "$help_url" 2>/dev/null & + fi +} +set_mode(){ + if [[ $debug -eq 1 ]]; then + mode=debug + else + mode=normal + fi +} +delete_by_id(){ + new_whitelist="whitelist=\"$(echo "$whitelist" | sed "s/,$server_id$//;s/^$server_id,//;s/,$server_id,/,/")\"" + mv $config_file ${config_path}dztuirc.old + nr=$(awk '/whitelist=/ {print NR}' ${config_path}dztuirc.old) + awk -v "var=$new_whitelist" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > ${config_path}dztuirc + echo "[DZGUI] Removed $server_id from key 'whitelist'" + zenity --info --title="DZGUI" --text="Removed "$server_id" from:\n${config_path}dztuirc\nIf errors occur, you can restore the file:\n${config_path}dztuirc.old" --width=500 2>/dev/null + source $config_file +} +delete_or_connect(){ + if [[ $delete -eq 1 ]]; then + server_name=$(echo "$sel" | awk -F"%%" '{print $1}') + server_id=$(echo "$sel" | awk -F"%%" '{print $2}') + zenity --question --text="Delete this server? \n$server_name" + if [[ $? -eq 0 ]]; then + delete_by_id $server_id + fi + else + #hotfix for bug #36 + local lookup_ip=$(echo "$sel" | awk -F%% '{print $1}') + local lookup_port=$(echo "$lookup_ip" | awk -F: '{print $2}') + source $config_file + file=$(mktemp) + url="https://api.steampowered.com/IGameServersService/GetServerList/v1/?filter=\appid\221100\gameaddr\\$lookup_ip&key=$steam_api" + curl -Ls "$url" > $file + local qport_res=$(< $file jq -r --arg port $lookup_port '.response.servers[]|select(.gameport==($port|tonumber)).addr') + local qport=$(echo "$qport_res" | awk -F: '{print $2}') + qport_list="$lookup_ip%%$qport" + connect "$qport_list" "ip" + + fi +} +populate(){ + while true; do + if [[ $delete -eq 1 ]]; then + cols="--column="Server" --column="ID"" + set_header "delete" + else + cols="--column="Server" --column="IP" --column="Players" --column="Gametime" --column="Status" --column="ID" --column="Ping"" + set_header ${FUNCNAME[0]} + fi + rc=$? + if [[ $rc -eq 0 ]]; then + if [[ -z $sel ]]; then + warn "No item was selected." + else + delete_or_connect + return + fi + else + delete=0 + return + fi + done +} +list_mods(){ + if [[ -z $(installed_mods) ]] || [[ -z $(find $workshop_dir -maxdepth 2 -name "*.cpp" | grep .cpp) ]]; then + zenity --info --text="94: No mods currently installed or incorrect path given" $sd_res 2>/dev/null + else + for d in $(find $game_dir/* -maxdepth 1 -type l); do + dir=$(basename $d) + awk -v d=$dir -F\" '/name/ {printf "%s\t%s\n", $2,d}' "$gamedir"/$d/meta.cpp + done | sort + fi +} +fetch_query_ports(){ + qport_list=$(echo "$response" | jq -r '.data[] .attributes | "\(.ip):\(.port)%%\(.portQuery)"') +} +connect_to_fav(){ + if [[ -n $fav ]]; then + one_shot_launch=1 + query_api + fetch_query_ports + echo "[DZGUI] Attempting connection to $fav_label" + connect "$qport_list" "ip" + one_shot_launch=0 + else + warn "93: No fav server configured" + fi + +} +set_header(){ + [[ $auto_install -eq 1 ]] && install_mode=auto + [[ $auto_install -eq 0 ]] && install_mode=manual + if [[ $1 == "delete" ]]; then + sel=$(cat $tmp | zenity $sd_res --list $cols --title="DZGUI" --text="DZGUI $version | Mode: $mode | Branch: $branch | Mods: $install_mode | Fav: $fav_label" \ + --separator="$separator" --print-column=1,2 --ok-label="Delete" 2>/dev/null) + elif [[ $1 == "populate" ]]; then + sel=$(cat $tmp | zenity $sd_res --list $cols --title="DZGUI" --text="DZGUI $version | Mode: $mode | Branch: $branch | Mods: $install_mode | Fav: $fav_label" \ + --separator="$separator" --print-column=2,6 2>/dev/null) + elif [[ $1 == "main_menu" ]]; then + sel=$(zenity $sd_res --list --title="DZGUI" --text="${news}DZGUI $version | Mode: $mode | Branch: $branch | Mods: $install_mode | Fav: $fav_label" \ + --cancel-label="Exit" --ok-label="Select" --column="Select launch option" --hide-header "${items[@]}" 2>/dev/null) + fi +} +toggle_branch(){ + mv $config_file ${config_path}dztuirc.old + nr=$(awk '/branch=/ {print NR}' ${config_path}dztuirc.old) + if [[ $branch == "stable" ]]; then + branch="testing" + else + branch="stable" + fi + flip_branch="branch=\"$branch\"" + awk -v "var=$flip_branch" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > $config_file + printf "[DZGUI] Toggled branch to '$branch'\n" + source $config_file +} +generate_log(){ + cat <<-DOC + Version: $version + Branch: $branch + Whitelist: $whitelist + Path: $steam_path + Linux: $(uname -mrs) + + Mods: + $(list_mods) + DOC +} +automods_prompt(){ +cat <<- HERE + +Auto-mod installation set to ON. This method is NOT supported in Game Mode (Steam Deck). + +READ THIS FIRST: +With this setting on, DZGUI will attempt to download and prepare mods using Valve's steamcmd tool. + +The first time this process is run, DZGUI will ask you to select a terminal emulator of your preference to spawn the installation routine. If you don't have a preference or don't know, you can pick any. + +Installation will kick off in a separate window and may ask you for input such as your sudo password in order to install system packages and create the steamcmd user. + +steamcmd itself will ask for your Steam credentials. This information is used directly by Valve's steamcmd tool to authenticate your account and let you download mods headlessly. steamcmd is an official program created by Valve and communicates only with their servers. + +NOTE: it can take some time for large mods to download, and steamcmd will not inform you of activity until each one is finished downloading. + +If your distribution is unsupported, you don't have enough disk space to stage all of the mods, or there are other problems, DZGUI will warn you and write a report to $HOME/.local/share/dzgui/helpers/SCMD.log. You can attach this file to a bug report. +HERE +} +toggle_automods(){ + mv $config_file ${config_path}dztuirc.old + local nr=$(awk '/auto_install=/ {print NR}' ${config_path}dztuirc.old) + if [[ $auto_install == "1" ]]; then + auto_install="0" + else + auto_install="1" + fi + local flip_state="auto_install=\"$auto_install\"" + awk -v "var=$flip_state" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > $config_file + printf "[DZGUI] Toggled auto-mod install to '$auto_install'\n" + source $config_file + local big_prompt + [[ $is_steam_deck -eq 1 ]] && big_prompt="--width=800" + [[ $auto_install == "1" ]] && zenity --info --text="$(automods_prompt)" $big_prompt 2>/dev/null +} +options_menu(){ + debug_list=( + "Toggle branch" + "Toggle debug mode" + "Generate debug log" + "Toggle auto-mod install (experimental)" + "Set auto-mod staging directory [$staging_dir]" + ) + debug_sel=$(zenity --list --width=1280 --height=800 --column="Options" --title="DZGUI" --hide-header "${debug_list[@]}" 2>/dev/null) + if [[ $debug_sel == "${debug_list[0]}" ]]; then + enforce_dl=1 + toggle_branch && + check_version + elif [[ $debug_sel == "${debug_list[1]}" ]]; then + toggle_debug + elif [[ $debug_sel == "${debug_list[2]}" ]]; then + source_script=$(realpath "$0") + source_dir=$(dirname "$source_script") + generate_log > "$source_dir/log" + printf "[DZGUI] Wrote log file to %s/log\n" "$source_dir" + zenity --info --width 500 --title="DZGUI" --text="Wrote log file to \n$source_dir/log" 2>/dev/null + elif [[ $debug_sel == "${debug_list[3]}" ]]; then + toggle_automods + elif [[ $debug_sel == "${debug_list[4]}" ]]; then + file_picker + fi +} +query_and_connect(){ + query_api + parse_json + #TODO: create logger function + if [[ ! $delete -eq 1 ]]; then + echo "[DZGUI] Checking response time of servers" + create_array | zenity --width 500 --progress --pulsate --title="DZGUI" --auto-close 2>/dev/null + else + create_array + fi + rc=$? + if [[ $rc -eq 1 ]]; then + : + else + populate + fi +} +exclude_full(){ + response=$(echo "$response" | jq '[.[]|select(.players!=.max_players)]') +} +exclude_empty(){ + response=$(echo "$response" | jq '[.[]|select(.players!=0)]') +} +filter_maps(){ + echo "# Filtering maps" + [[ $ret -eq 98 ]] && return + local maps=$(echo "$response" | jq -r '.[].map//empty|ascii_downcase' | sort -u) + local map_ct=$(echo "$maps" | wc -l) + local map_sel=$(echo "$maps" | zenity --list --column="Check" --width=1200 --height=800 2>/dev/null --title="DZGUI" --text="Found $map_ct map types") + echo "[DZGUI] Selected '$map_sel'" + if [[ -z $map_sel ]]; then + ret=97 + return + fi + echo "100" + response=$(echo "$response" | jq --arg map "$map_sel" '[.[]|select(.map)//empty|select(.map|ascii_downcase == $map)]') +} +exclude_daytime(){ + response=$(echo "$response" | jq '[.[]|select(.gametype|test(",[0][6-9]:|,[1][0-6]:")|not)]') +} +exclude_nighttime(){ + response=$(echo "$response" | jq '[.[]|select(.gametype|test(",[1][7-9]:|,[2][0-4]:|[0][0-5]:")|not)]') +} +keyword_filter(){ + response=$(echo "$response" | jq --arg search "$search" '[.[]|select(.name|ascii_downcase | contains($search))]') +} +exclude_lowpop(){ + response=$(echo "$response" | jq '[.[]|select(.players > 9)]') +} +exclude_nonascii(){ + response=$(echo "$response" | jq -r '[.[]|select(.name|test("^([[:ascii:]])*$"))]') +} +strip_null(){ + response=$(echo "$response" | jq -r '[.[]|select(.map//empty)]') +} +local_latlon(){ + if [[ -z $(command -v dig) ]]; then + local local_ip=$(curl -Ls "https://ipecho.net/plain") + else + local local_ip=$(dig +short myip.opendns.com @resolver1.opendns.com) + fi + local url="http://ip-api.com/json/$local_ip" + local res=$(curl -Ls "$url" | jq -r '"\(.lat),\(.lon)"') + local_lat=$(echo "$res" | awk -F, '{print $1}') + local_lon=$(echo "$res" | awk -F, '{print $2}') +} +disabled(){ + if [[ -z ${disabled[@]} ]]; then + printf "%s" "-" + else + for((i=0;i<${#disabled[@]};i++)); do + if [[ $i < $((${#disabled[@]}-1)) ]]; then + printf "%s, " "${disabled[$i]}" + else + printf "%s" "${disabled[$i]}" + fi + + done + fi +} +pagination(){ + if [[ ${#qport[@]} -eq 1 ]]; then + entry=server + else + entry=servers + fi + printf "DZGUI %s | " "$version" + printf "Mode: %s |" "$mode" + printf "Fav: %s " "$fav_label" + printf "\nIncluded: %s | " "$filters" + printf "Excluded: %s " "$(disabled)" + if [[ -n $search ]]; then + printf "| Keyword: %s " "$search" + fi + printf "\nReturned: %s %s of %s | " "${#qport[@]}" "$entry" "$total_servers" + printf "Players online: %s" "$players_online" +} +check_geo_file(){ + local gzip="$helpers_path/ips.csv.gz" + curl -Ls "$sums_url" > "$sums_path" + cd "$helpers_path" + md5sum -c "$sums_path" 2>/dev/null 1>&2 + local res=$? + cd $OLDPWD + if [[ $res -eq 1 ]]; then + run(){ + mkdir -p "$helpers_path" + echo "# Fetching new geolocation DB" + curl -Ls "$db_file" > "$gzip" + echo "# Extracting coordinates" + #force overwrite + gunzip -f "$gzip" + echo "# Preparing helper file" + curl -Ls "$km_helper_url" > "$km_helper" + chmod +x $km_helper + echo "100" + } + run > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) + fi +} +choose_filters(){ + if [[ $is_steam_deck -eq 0 ]]; then + sd_res="--width=1920 --height=1080" + fi + sels=$(zenity --title="DZGUI" --text="Server search" --list --checklist --column "Check" --column "Option" --hide-header TRUE "All maps (untick to select from map list)" TRUE "Daytime" TRUE "Nighttime" False "Empty" False "Full" False "Low population" FALSE "Non-ASCII titles" FALSE "Keyword search" $sd_res 2>/dev/null) + if [[ $sels =~ Keyword ]]; then + search=$(zenity --entry --text="Search (case insensitive)" --width=500 --title="DZGUI" 2>/dev/null | awk '{print tolower($0)}') + [[ -z $search ]] && { ret=97; return; } + fi + [[ -z $sels ]] && return + filters=$(echo "$sels" | sed 's/|/, /g;s/ (untick to select from map list)//') + echo "[DZGUI] Filters: $filters" +} +get_dist(){ + local given_ip="$1" + local network="$(echo "$given_ip" | awk -F. '{OFS="."}{print $1"."$2}')" + local binary=$(grep -E "^$network\." $geo_file) + local three=$(echo $given_ip | awk -F. '{print $3}') + local host=$(echo $given_ip | awk -F. '{print $4}') + local res=$(echo "$binary" | awk -F[.,] -v three=$three -v host=$host '$3 <=three && $7 >= three{if($3>three || ($3==three && $4 > host) || $7 < three || ($7==three && $8 < host)){next}{print}}' | awk -F, '{print $7,$8}') + local remote_lat=$(echo "$res" | awk '{print $1}') + local remote_lon=$(echo "$res" | awk '{print $2}') + if [[ -z $remote_lat ]]; then + local dist="Unknown" + echo "$dist" + else + local dist=$($km_helper "$local_lat" "$local_lon" "$remote_lat" "$remote_lon") + printf "%05.0f %s" "$dist" "km" + fi +} +prepare_filters(){ + echo "# Filtering list" + [[ ! "$sels" =~ "Full" ]] && { exclude_full; disabled+=("Full") ; } + [[ ! "$sels" =~ "Empty" ]] && { exclude_empty; disabled+=("Empty") ; } + [[ ! "$sels" =~ "Daytime" ]] && { exclude_daytime; disabled+=("Daytime") ; } + [[ ! "$sels" =~ "Nighttime" ]] && { exclude_nighttime; disabled+=("Nighttime") ; } + [[ ! "$sels" =~ "Low population" ]] && { exclude_lowpop; disabled+=("Low-pop") ; } + [[ ! "$sels" =~ "Non-ASCII titles" ]] && { exclude_nonascii; disabled+=("Non-ASCII") ; } + [[ -n "$search" ]] && keyword_filter + strip_null + echo "100" +} +write_fifo(){ + [[ -p $fifo ]] && rm $fifo + mkfifo $fifo + for((i=0;i<${#qport[@]};i++)); do + printf "%s\n%s\n%s\n%03d\n%03d\n%s\n%s:%s\n%s\n" "${map[$i]}" "${name[$i]}" "${gametime[$i]}" \ + "${players[$i]}" "${max[$i]}" "$(get_dist ${addr[$i]})" "${addr[$i]}" "${gameport[$i]}" "${qport[$i]}" >> $fifo + done +} +munge_servers(){ + if [[ ! "$sels" =~ "All maps" ]]; then + filter_maps > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) + disabled+=("All maps") + fi + [[ $ret -eq 97 ]] && return + prepare_filters > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) + if [[ $(echo "$response" | jq 'length') -eq 0 ]]; then + zenity --error --text="No matching servers" 2>/dev/null + return + fi + local addr=$(echo "$response" | jq -r '.[].addr' | awk -F: '{print $1}') + local gameport=$(echo "$response" | jq -r '.[].gameport') + local qport=$(echo "$response" | jq -r '.[].addr' | awk -F: '{print $2}') + #jq bug #1788, raw output cannot be used with ASCII + local name=$(echo "$response" | jq -a '.[].name' | sed 's/\\u[0-9a-z]\{4\}//g;s/^"//;s/"$//') + local players=$(echo "$response" | jq -r '.[].players') + local max=$(echo "$response" | jq -r '.[].max_players') + local map=$(echo "$response" | jq -r '.[].map|ascii_downcase') + local gametime=$(echo "$response" | jq -r '.[].gametype' | grep -oE '[0-9]{2}:[0-9]{2}$') + readarray -t qport <<< $qport + readarray -t gameport <<< $gameport + readarray -t addr <<< $addr + readarray -t name <<< $name + readarray -t players <<< $players + readarray -t map <<< $map + readarray -t max <<< $max + readarray -t gametime <<< $gametime + if [[ $is_steam_deck -eq 0 ]]; then + sd_res="--width=1920 --height=1080" + fi + + write_fifo & + pid=$! + local sel=$(zenity --text="$(pagination)" --title="DZGUI" --list --column=Map --column=Name --column=Gametime --column=Players --column=Max --column=Distance --column=IP --column=Qport $sd_res --print-column=7,8 --separator=%% 2>/dev/null < <(while true; do cat $fifo; done)) + if [[ -z $sel ]]; then + rm $fifo + kill -9 $pid + else + rm $fifo + kill -9 $pid + echo $sel + fi +} +debug_servers(){ + [[ -f $debug_log ]] && rm $debug_log + if [[ -n $steam_api ]]; then + exists=true + else + exists=false + fi + key_len=${#steam_api} + first_char=${steam_api:0:1} + last_char=${steam_api:0-1} + debug_res=$(curl -Ls "https://api.steampowered.com/IGameServersService/GetServerList/v1/?filter=\appid\221100&limit=10&key=$steam_api") + debug_len=$(echo "$debug_res" | jq '[.response.servers[]]|length') + [[ -z $debug_len ]] && debug_len=0 + cat <<-DOC > $debug_log + ======START DEBUG====== + Key exists: $exists + First char: $first_char + Last char: $last_char + Key length: $key_len + ======Short query====== + Expected: 10 + Found: $debug_len + Response follows----> + $debug_res + ======END DEBUG======= + DOC +} +server_browser(){ + check_steam_api + [[ $? -eq 1 ]] && return + + unset ret + file=$(mktemp) + local limit=20000 + local url="https://api.steampowered.com/IGameServersService/GetServerList/v1/?filter=\appid\221100&limit=$limit&key=$steam_api" + check_geo_file + local_latlon + choose_filters + [[ -z $sels ]] && return + [[ $ret -eq 97 ]] && return + #TODO: some error handling here + fetch(){ + echo "# Getting server list" + curl -Ls "$url" > $file + } + fetch > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) + response=$(< $file jq -r '.response.servers') + total_servers=$(echo "$response" | jq 'length') + players_online=$(echo "$response" | jq '.[].players' | awk '{s+=$1}END{print s}') + debug_log="$HOME/.local/share/dzgui/DEBUG.log" + debug_servers + local sel=$(munge_servers) + if [[ -z $sel ]]; then + unset filters + unset search + ret=98 + return + fi + local sel_ip=$(echo "$sel" | awk -F%% '{print $1}') + local sel_port=$(echo "$sel" | awk -F%% '{print $2}') + qport_list="$sel_ip%%$sel_port" + if [[ -n "$sel_ip" ]]; then + connect "$sel_ip" "ip" + else + return + fi +} + +mods_disk_size(){ + printf "Total size on disk: %s |" $(du -sh "$game_dir" | awk '{print $1}') + printf "Mods location: $steam_path/steamapps/workshop/content/221100" + +} + +main_menu(){ + print_news + set_mode + if [[ -n $fav ]]; then + items[7]=" Change favorite server" + fi + while true; do + set_header ${FUNCNAME[0]} + rc=$? + if [[ $rc -eq 0 ]]; then + if [[ -z $sel ]]; then + warn "No item was selected." + elif [[ $sel == "${items[0]}" ]]; then + : + elif [[ $sel == "${items[1]}" ]]; then + server_browser + elif [[ $sel == "${items[2]}" ]]; then + query_and_connect + elif [[ $sel == "${items[3]}" ]]; then + connect_to_fav + elif [[ $sel == "${items[4]}" ]]; then + : + elif [[ $sel == "${items[5]}" ]]; then + connect_by_ip + elif [[ $sel == "${items[6]}" ]]; then + add_by_id + elif [[ $sel == "${items[7]}" ]]; then + add_by_fav + elif [[ $sel == "${items[8]}" ]]; then + delete=1 + query_and_connect + elif [[ $sel == "${items[9]}" ]]; then + : + elif [[ $sel == "${items[10]}" ]]; then + list_mods | sed 's/\t/\n/g' | zenity --list --column="Mod" --column="Symlink" \ + --title="DZGUI" $sd_res --text="$(mods_disk_size)" \ + --print-column="" 2>/dev/null + elif [[ $sel == "${items[11]}" ]]; then + changelog | zenity --text-info $sd_res --title="DZGUI" 2>/dev/null + elif [[ $sel == "${items[12]}" ]]; then + options_menu + main_menu + return + elif [[ $sel == "${items[13]}" ]]; then + : + elif [[ $sel == "${items[14]}" ]]; then + help_file + elif [[ $sel == "${items[15]}" ]]; then + report_bug + elif [[ $sel == "${items[16]}" ]]; then + forum + else + warn "This feature is not yet implemented." + fi + else + return + fi + done +} +page_through(){ + list_response=$(curl -s "$page") + list=$(echo "$list_response" | jq -r '.data[] .attributes | "\(.name)\t\(.ip):\(.port)\t\(.players)/\(.maxPlayers)\t\(.details.time)\t\(.status)\t\(.id)"') + idarr+=("$list") + parse_json +} +parse_json(){ + page=$(echo "$list_response" | jq -r '.links.next?') + if [[ $first_entry -eq 1 ]]; then + local list=$(echo "$list_response" | jq -r '.data[] .attributes | "\(.name)\t\(.ip):\(.port)\t\(.players)/\(.maxPlayers)\t\(.details.time)\t\(.status)\t\(.id)"') + idarr+=("$list") + first_entry=0 + fi + if [[ "$page" != "null" ]]; then + local list=$(echo "$list_response" | jq -r '.data[] .attributes | "\(.name)\t\(.ip):\(.port)\t\(.players)/\(.maxPlayers)\t\(.details.time)\t\(.status)\t\(.id)"') + idarr+=("$list") + page_through + else + printf "%s\n" "${idarr[@]}" > $tmp + idarr=() + fetch_query_ports + fi +} +check_ping(){ + ping_ip=$(echo "$1" | awk -F'\t' '{print $2}' | awk -F: '{print $1}') + ms=$(ping -c 1 -W 1 "$ping_ip" | awk -Ftime= '/time=/ {print $2}') + if [[ -z $ms ]]; then + echo "Timeout" + else + echo "$ms" + fi +} +create_array(){ + rows=() + list=$(cat $tmp) + #TODO: improve error handling for null values + lc=1 + while read line; do + name=$(echo "$line" | awk -F'\t' '{print $1}') + #truncate names + if [[ $(echo "$name" | wc -m) -gt 50 ]]; then + name="$(echo "$name" | awk '{print substr($0,1,50) "..."}')" + else + : + fi + ip=$(echo "$line" | awk -F'\t' '{print $2}') + players=$(echo "$line" | awk -F'\t' '{print $3}') + time=$(echo "$line" | awk -F'\t' '{print $4}') + stat=$(echo "$line" | awk -F'\t' '{print $5}') + + #yad only + #[[ $stat == "online" ]] && stat="online" || : + + #TODO: probe offline return codes + id=$(echo "$line" | awk -F'\t' '{print $6}') + tc=$(awk 'END{print NR}' $tmp) + if [[ $delete -eq 1 ]]; then + declare -g -a rows=("${rows[@]}" "$name" "$id") + else + echo "# Checking ping: $lc/$tc" + ping=$(check_ping "$line") + declare -g -a rows=("${rows[@]}" "$name" "$ip" "$players" "$time" "$stat" "$id" "$ping") + fi + let lc++ + done <<< "$list" + + for i in "${rows[@]}"; do echo -e "$i"; done > $tmp +} +set_fav(){ + #TODO: test API key here and return errors + echo "[DZGUI] Querying favorite server" + query_api + fav_label=$(curl -s "$api" -H "Authorization: Bearer "$api_key"" -G -d "filter[game]=$game" -d "filter[ids][whitelist]=$fav" \ + | jq -r '.data[] .attributes .name') + if [[ -z $fav_label ]]; then + fav_label=null + fi + echo "[DZGUI] Setting favorite server to '$fav_label'" +} +check_unmerged(){ + if [[ -f ${config_path}.unmerged ]]; then + printf "[DZGUI] Found new config format, merging changes\n" + merge_config + rm ${config_path}.unmerged + fi +} +merge_config(){ + source $config_file + mv $config_file ${config_path}dztuirc.old + [[ -z $staging_dir ]] && staging_dir="/tmp" + write_config > $config_file + printf "[DZGUI] Wrote new config file to %sdztuirc\n" $config_path + zenity --info --width 500 --title="DZGUI" --text="Wrote new config format to \n${config_path}dztuirc\nIf errors occur, you can restore the file:\n${config_path}dztuirc.old" 2>/dev/null + +} +download_new_version(){ + if [[ $is_steam_deck -eq 1 ]]; then + freedesktop_dirs + fi + source_script=$(realpath "$0") + source_dir=$(dirname "$source_script") + mv $source_script $source_script.old + echo "# Downloading version $upstream" + curl -Ls "$version_url" > $source_script + rc=$? + if [[ $rc -eq 0 ]]; then + echo "[DZGUI] Wrote $upstream to $source_script" + chmod +x $source_script + touch ${config_path}.unmerged + echo "100" + zenity --question --width 500 --title="DZGUI" --text "DZGUI $upstream successfully downloaded.\nTo view the changelog, select Changelog.\nTo use the new version, select Exit and restart." --ok-label="Changelog" --cancel-label="Exit" 2>/dev/null + code=$? + if [[ $code -eq 0 ]]; then + changelog | zenity --text-info $sd_res --title="DZGUI" 2>/dev/null + exit + elif [[ $code -eq 1 ]]; then + exit + fi + else + echo "100" + mv $source_script.old $source_script + zenity --info --title="DZGUI" --text "[ERROR] 99: Failed to download new version." 2>/dev/null + return + fi + +} +check_branch(){ + if [[ $branch == "stable" ]]; then + version_url="$stable_url/dzgui.sh" + elif [[ $branch == "testing" ]]; then + version_url="$testing_url/dzgui.sh" + fi + upstream=$(curl -Ls "$version_url" | awk -F= '/^version=/ {print $2}') +} +enforce_dl(){ + download_new_version > >(zenity --progress --pulsate --auto-close --no-cancel --width=500) +} +prompt_dl(){ + zenity --question --title="DZGUI" --text "Version conflict.\n\nYour branch:\t\t\t$branch\nYour version\t\t\t$version\nUpstream version:\t\t$upstream\n\nVersion updates introduce important bug fixes and are encouraged.\n\nAttempt to download latest version?" --width=500 --ok-label="Yes" --cancel-label="No" 2>/dev/null + rc=$? + if [[ $rc -eq 1 ]]; then + return + else + echo "100" + download_new_version > >(zenity --progress --pulsate --auto-close --no-cancel --width=500) + fi +} +check_version(){ + [[ -f $config_file ]] && source $config_file + [[ -z $branch ]] && branch="stable" + check_branch + [[ ! -d "$freedesktop_path" ]] && freedesktop_dirs + if [[ $version == $upstream ]]; then + check_unmerged + else +# echo "100" + echo "[DZGUI] Upstream ($upstream) != local ($version)" + if [[ $enforce_dl -eq 1 ]]; then + enforce_dl + else + prompt_dl + fi + fi +} +check_architecture(){ + cpu=$(cat /proc/cpuinfo | grep "AMD Custom APU 0405") + if [[ -n "$cpu" ]]; then + is_steam_deck=1 + echo "[DZGUI] Setting architecture to 'Steam Deck'" + else + is_steam_deck=0 + echo "[DZGUI] Setting architecture to 'desktop'" + fi +} +add_by_id(){ + #FIXME: prevent redundant creation of existent IDs (for neatness) + while true; do + id=$(zenity --entry --text="Enter server ID" --title="DZGUI" 2>/dev/null) + rc=$? + if [[ $rc -eq 1 ]]; then + return + else + if [[ ! $id =~ ^[0-9]+$ ]]; then + zenity --warning --title="DZGUI" --text="Invalid ID" 2>/dev/null + else + new_whitelist="whitelist=\"$whitelist,$id\"" + mv $config_file ${config_path}dztuirc.old + nr=$(awk '/whitelist=/ {print NR}' ${config_path}dztuirc.old) + awk -v "var=$new_whitelist" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > ${config_path}dztuirc + echo "[DZGUI] Added $id to key 'whitelist'" + zenity --info --title="DZGUI" --text="Added "$id" to:\n${config_path}dztuirc\nIf errors occur, you can restore the file:\n${config_path}dztuirc.old" --width=500 2>/dev/null + source $config_file + return + fi + fi + done +} +toggle_debug(){ + mv $config_file ${config_path}dztuirc.old + nr=$(awk '/debug=/ {print NR}' ${config_path}dztuirc.old) + if [[ $debug -eq 1 ]]; then + debug=0 + else + debug=1 + fi + flip_debug="debug=\"$debug\"" + awk -v "var=$flip_debug" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > $config_file + printf "[DZGUI] Toggled debug flag to '$debug'\n" + source $config_file + +} +setup(){ + if [[ -n $fav ]]; then + set_fav + items[7]=" Change favorite server" + fi +} +check_map_count(){ + count=1048576 + echo "[DZGUI] Checking system map count" + if [[ $(sysctl -q vm.max_map_count | awk -F"= " '{print $2}') -lt $count ]]; then + echo "100" + map_warning=$(zenity --question --width 500 --title="DZGUI" --text "System map count must be $count or higher to run DayZ with Wine. Increase map count and make this change permanent? (will prompt for sudo password)" 2>/dev/null) + if [[ $? -eq 0 ]]; then + pass=$(zenity --password) + sudo -S <<< "$pass" sh -c "echo 'vm.max_map_count=1048576' > /etc/sysctl.d/dayz.conf" + echo "" + + fi + fi +} +add_by_fav(){ +while true; do + fav_id=$(zenity --entry --text="Enter server ID" --title="DZGUI" 2>/dev/null) + rc=$? + if [[ $rc -eq 1 ]]; then + return + else + if [[ ! $fav_id =~ ^[0-9]+$ ]]; then + zenity --warning --title="DZGUI" --text="Invalid ID" + else + new_fav="fav=\"$fav_id\"" + mv $config_file ${config_path}dztuirc.old + nr=$(awk '/fav=/ {print NR}' ${config_path}dztuirc.old) + awk -v "var=$new_fav" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > ${config_path}dztuirc + echo "[DZGUI] Added $fav_id to key 'fav'" + zenity --info --title="DZGUI" --text="Added "$fav_id" to:\n${config_path}dztuirc\nIf errors occurred, you can restore the file:\n${config_path}dztuirc.old" 2>/dev/null + source $config_file + set_fav + items[7]=" Change favorite server" + return + fi + fi +done +} +lock(){ + if [[ ! -f $config_path/.lockfile ]]; then + touch $config_path/.lockfile + fi + pid=$(cat $config_path/.lockfile) + ps -p $pid -o pid= >/dev/null 2>&1 + res=$? + if [[ $res -eq 0 ]]; then + echo "[DZGUI] Already running ($pid)" + zenity --info --text="DZGUI already running (pid $pid)" --width=500 --title="DZGUI" 2>/dev/null + exit + elif [[ $pid == $$ ]]; then + : + else + echo $$ > $config_path/.lockfile + fi +} +fetch_scmd_helper(){ + mkdir -p "$helpers_path" + curl -Ls "$scmd_url" > "$helpers_path/scmd.sh" + chmod +x "$helpers_path/scmd.sh" + [[ ! -f "$helpers_path/d.html" ]] && curl -Ls "$notify_url" > "$helpers_path/d.html" + [[ ! -f "$helpers_path/d.webp" ]] && curl -Ls "$notify_img_url" > "$helpers_path/d.webp" +} +deprecation_warning(){ + warn(){ + cat <<- HERE + IMPORTANT ANNOUNCEMENT + (Steam API key not found) + + A Steam API key is now mandatory to run the app. + The BM API returns incorrect mod data on some servers + and cannot be relied upon for up to date information. + + Going forward, we will only use the BM API as a convenience + function to manage server names and your favorite servers list, + and migrate to indexing servers on an IP basis. + + This is a backend change. You can continue adding servers by ID, + but we will retrieve information from Valve instead, as we do for the + server browser and connect-by-ip methods. + + Click [OK] to open the help page describing how to set up your key. + After you input a valid key, the app will resume. + HERE + } + if [[ -z $steam_api ]]; then + echo "100" + zenity --info --text="$(warn)" + key_setup_url="https://aclist.github.io/dzgui/dzgui.html#_api_key_server_ids" + browser "$key_setup_url" 2>/dev/null & + while true; do + if [[ $(check_steam_api) ]]; then + break + fi + done + fi +} +initial_setup(){ + echo "# Initial setup" + run_depcheck + watcher_deps + check_architecture + check_version + check_map_count + config + fetch_scmd_helper + run_varcheck + init_items + setup + deprecation_warning + echo "100" +} +main(){ + lock + initial_setup > >(zenity --pulsate --progress --auto-close --title="DZGUI" --width=500 2>/dev/null) + main_menu + #cruddy handling for steam forking + [[ $? -eq 1 ]] && pkill -f dzgui.sh +} + +main +#trap cleanup EXIT From 2760f38f6f8e40c4a11527e166c30b77b7533386 Mon Sep 17 00:00:00 2001 From: aclist Date: Thu, 27 Oct 2022 13:40:42 +0900 Subject: [PATCH 018/144] Propagate .desktop file --- dzgui.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dzgui.sh b/dzgui.sh index ba0eb3c..e396e89 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1649,7 +1649,7 @@ check_version(){ [[ -f $config_file ]] && source $config_file [[ -z $branch ]] && branch="stable" check_branch - [[ ! -d "$freedesktop_path" ]] && freedesktop_dirs + [[ ! -f "$freedesktop_path/dzgui.desktop" ]] && freedesktop_dirs if [[ $version == $upstream ]]; then check_unmerged else From 0f89b49f4c9bdce897f4ac8ce352f5ee5979c440 Mon Sep 17 00:00:00 2001 From: aclist Date: Thu, 27 Oct 2022 13:41:36 +0900 Subject: [PATCH 019/144] Bump version --- dzgui.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dzgui.sh b/dzgui.sh index e396e89..35eea57 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.1.0-rc.8 +version=3.1.0-rc.9 aid=221100 game="dayz" From 4769b6741a097d0cd79026f269b34830edf39a28 Mon Sep 17 00:00:00 2001 From: aclist Date: Wed, 9 Nov 2022 12:07:56 +0900 Subject: [PATCH 020/144] More verbose logs --- dzgui.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dzgui.sh b/dzgui.sh index 35eea57..12ece84 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1017,7 +1017,10 @@ generate_log(){ Version: $version Branch: $branch Whitelist: $whitelist - Path: $steam_path + Steam path: $steam_path + Workshop path: $workshop_dir + Game path: $game_dir + Linux: $(uname -mrs) Mods: From f9c00f3522bfdfe1182eecfaf35d1d0fb2fbf237 Mon Sep 17 00:00:00 2001 From: aclist Date: Wed, 9 Nov 2022 12:38:42 +0900 Subject: [PATCH 021/144] Bump version --- changelog.md | 5 +++++ dzgui.sh | 7 +++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index b2237e4..7efee14 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,11 @@ - Custom query API - Standardize dialogs +## [3.1.0-rc.10] 2022-11-09 +### Changed +- More verbose logs +- Rename log file for clarity + ## [3.1.0-rc.8] 2022-10-26 ### Changed - Foreground mod progress watcher diff --git a/dzgui.sh b/dzgui.sh index 12ece84..01cc2f5 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.1.0-rc.9 +version=3.1.0-rc.10 aid=221100 game="dayz" @@ -1014,14 +1014,13 @@ toggle_branch(){ } generate_log(){ cat <<-DOC + Linux: $(uname -mrs) Version: $version Branch: $branch Whitelist: $whitelist Steam path: $steam_path Workshop path: $workshop_dir Game path: $game_dir - - Linux: $(uname -mrs) Mods: $(list_mods) @@ -1144,7 +1143,7 @@ options_menu(){ elif [[ $debug_sel == "${debug_list[2]}" ]]; then source_script=$(realpath "$0") source_dir=$(dirname "$source_script") - generate_log > "$source_dir/log" + generate_log > "$source_dir/DZGUI.log" printf "[DZGUI] Wrote log file to %s/log\n" "$source_dir" zenity --info --width 500 --title="DZGUI" --text="Wrote log file to \n$source_dir/log" 2>/dev/null elif [[ $debug_sel == "${debug_list[3]}" ]]; then From 7ad6c13b52786a26faffbd6917d6362440d84d5e Mon Sep 17 00:00:00 2001 From: aclist Date: Tue, 22 Nov 2022 04:54:23 +0900 Subject: [PATCH 022/144] Recent connect history --- changelog.md | 9 ++++ dzgui.sh | 145 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 115 insertions(+), 39 deletions(-) diff --git a/changelog.md b/changelog.md index 7efee14..cc97565 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,15 @@ - Custom query API - Standardize dialogs +## [3.1.0-rc.10] 2022-11-21 +### Added +- Recent connect history +- Force refresh local mods +### Changed +- More verbose messages regarding automod installation +### Dropped +- Headless mode + ## [3.1.0-rc.10] 2022-11-09 ### Changed - More verbose logs diff --git a/dzgui.sh b/dzgui.sh index 01cc2f5..2f1eac3 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.1.0-rc.10 +version=3.1.0-rc.11 aid=221100 game="dayz" @@ -10,6 +10,7 @@ api="https://api.battlemetrics.com/servers" sd_res="--width=1280 --height=800" config_path="$HOME/.config/dztui/" config_file="${config_path}dztuirc" +hist_file="${config_path}/history" tmp=/tmp/dzgui.tmp fifo=/tmp/table.tmp separator="%%" @@ -97,8 +98,9 @@ items=( " Server browser" " My servers" " Quick connect to favorite server" - "[Manage servers]" " Connect by IP" + " History (last 10 connected)" + "[Manage servers]" " Add server by ID" " Add favorite server" " Delete server" @@ -527,7 +529,6 @@ symlinks(){ passed_mod_check(){ echo "[DZGUI] Passed mod check" echo "# Preparing symlinks" - stale_symlinks legacy_symlinks symlinks echo "100" @@ -551,6 +552,7 @@ auto_mod_install(){ echo "# Downloaded $((${#modids[@]}-missing)) of ${#modids[@]} mods" done | zenity --pulsate --progress --title="DZG Watcher" --auto-close --no-cancel --width=500 2>/dev/null compare + [[ $force_update -eq 1 ]] && { unset force_update; return; } if [[ -z $diff ]]; then check_timestamps passed_mod_check > >(zenity --pulsate --progress --title=DZGUI --auto-close --width=500 2>/dev/null) @@ -609,6 +611,7 @@ check_timestamps(){ fi } merge_modlists(){ + [[ $force_update -eq 1 ]] && echo "# Checking mod versions" check_timestamps if [[ -z "$diff" ]] && [[ ${#needs_update[@]} -gt 0 ]]; then diff=$(printf "%s\n" "${needs_update[@]}") @@ -619,6 +622,15 @@ merge_modlists(){ else diff="$(echo -e "$diff\n${needs_update[@]}")" fi + [[ $force_update -eq 1 ]] && echo "100" +} +update_history(){ + [[ -n $(grep "$ip" $hist_file) ]] && return + if [[ -f $hist_file ]]; then + old=$(tail -n9 "$hist_file") + old="$old\n" + fi + echo -e "${old}${ip}" > "$hist_file" } connect(){ #TODO: sanitize/validate input @@ -642,7 +654,7 @@ connect(){ rc=$? [[ $rc -eq 1 ]] && return compare - merge_modlists + [[ $auto_install -eq 2 ]] && merge_modlists if [[ -n $diff ]]; then if [[ $auto_install -eq 1 ]]; then headless_mod_install "$diff" @@ -655,6 +667,7 @@ connect(){ fi else passed_mod_check > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) + update_history launch fi } @@ -700,6 +713,41 @@ prepare_ip_list(){ echo "$time" done } +history_table(){ + for i in $(cat $hist_file); do + echo "# Getting metadata for $i" + local meta_file=$(mktemp) + source $config_file + local url="https://api.steampowered.com/IGameServersService/GetServerList/v1/?filter=\appid\221100\gameaddr\\$i&key=$steam_api" + curl -Ls "$url" > $meta_file + json=$(mktemp) + < $meta_file jq '.response' > $json + res=$(< $meta_file jq -er '.response.servers[]' 2>/dev/null) + prepare_ip_list "$meta_file" >> /tmp/dz.hist + sleep 1s + done > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) + while true; do + sel=$(cat /tmp/dz.hist | zenity --width 1200 --height 800 --list --column=Name --column=IP --column=Players --column=Gametime --print-column=2 --separator=%% 2>/dev/null) + if [[ $? -eq 1 ]]; then + return_from_table=1 + rm /tmp/dz.hist + return + fi + if [[ -z $sel ]]; then + echo "No selection" + else + local gameport="$(echo "$sel" | awk -F: '{print $2}')" + local ip="$(echo "$sel" | awk -F: '{print $1}')" + local addr=$(< $json jq -r --arg gameport $gameport '.servers[]|select(.gameport == ($gameport|tonumber)).addr') + local qport=$(echo "$addr" | awk -F: '{print $2}') + local sa_ip=$(echo "$ip:$gameport%%$qport") + qport_list="$sa_ip" + connect "$sel" "ip" + fi + done + rm /tmp/dz.hist + +} ip_table(){ while true; do @@ -1065,8 +1113,11 @@ toggle_headless(){ } console_dl(){ readarray -t modids <<< "$@" - steam steam://open/console 2>/dev/null 1>&2 + steam steam://open/console 2>/dev/null 1>&2 && sleep 1s + #https://github.com/jordansissel/xdotool/issues/67 + local wid=$(xdotool search --sync --onlyvisible --desktop "$(xdotool get_desktop)" --name Steam) + xdotool windowactivate $wid for i in "${modids[@]}"; do xdotool type --delay 15 "workshop_download_item $aid $i" sleep 0.2s @@ -1082,6 +1133,7 @@ find_default_path(){ if [[ -d "$HOME/.local/share/Steam" ]]; then default_steam_path="$HOME/.local/share/Steam" else + # ubuntu .steam default_steam_path=$(find / -type d \( -path "/proc" -o -path "*/timeshift" -o -path \ "/tmp" -o -path "/usr" -o -path "/boot" -o -path "/run" -o -path "/proc" -o -path "/root" \ -o -path "/sys" -o -path "/etc" -o -path "/var" -o -path "/lost+found" \) -prune \ @@ -1096,12 +1148,19 @@ find_default_path(){ source $config_file } popup(){ - [[ $1 -eq 100 ]] && zenity --info --text="This feature requires xdotool" --title=DZGUI --width=500 - [[ $1 -eq 200 ]] && zenity --info --text="This feature is not supported on Gaming Mode" --title=DZGUI --width=500 - [[ $1 -eq 300 ]] && zenity --info --text="\nThe Steam console will now open and briefly issue commands to\ndownload the workshop files, then return to the download progress page.\n\nEnsure that the Steam console has keyboard and mouse focus\n(keep hands off keyboard) while the commands are being issued.\n\nIt will only take a few seconds to queue the downloads, but if a\npopup or notification window steals focus, it could obstruct\nthe process." --title=DZGUI --width=500 2>/dev/null + pop(){ + zenity --info --text="$1" --title=DZGUI --width=500 2>/dev/null + } + case "$1" in + 100) pop "This feature requires xdotool.";; + 200) pop "This feature is not supported on Gaming Mode.";; + 300) pop "\nThe Steam console will now open and briefly issue commands to\ndownload the workshop files, then return to the download progress page.\n\nEnsure that the Steam console has keyboard and mouse focus\n(keep hands off keyboard) while the commands are being issued.\n\nDepending on the number if mods, it may take some time to queue the downloads,\nbut if a popup or notification window steals focus, it could obstruct\nthe process." ;; + 400) pop "Automod install enabled. Auto-downloaded mods will not appear\nin your Steam Workshop subscriptions, but DZGUI will\ntrack the version number of downloaded mods internally\nand trigger an update if necessary." ;; + 500) pop "Automod install disabled.\nSwitched to manual mode." ;; + esac } requires_xdo(){ - zenity --info --text="This feature requires xdojool" --title=DZGUI --width=500 + zenity --info --text="This feature requires xdotool" --title=DZGUI --width=500 } toggle_console_dl(){ [[ $is_steam_deck -eq 1 ]] && test_display_mode @@ -1111,14 +1170,22 @@ toggle_console_dl(){ local nr=$(awk '/auto_install=/ {print NR}' ${config_path}dztuirc.old) if [[ $auto_install == "2" ]]; then auto_install="0" + popup 500 else auto_install="2" + popup 400 fi local flip_state="auto_install=\"$auto_install\"" awk -v "var=$flip_state" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > $config_file printf "[DZGUI] Set mod install to '$auto_install'\n" source $config_file } +force_update_mods(){ + if [[ -f $version_file ]]; then + awk '{OFS="\t"}{$2="000"}1' $version_file > /tmp/versions + mv /tmp/versions $version_file + fi +} options_menu(){ case "$auto_install" in 0) auto_hr="OFF"; headless_hr="OFF" ;; @@ -1130,8 +1197,9 @@ options_menu(){ "Toggle debug mode" "Generate debug log" "Toggle auto mod install [$auto_hr]" - "Toggle headless mod install [$headless_hr]" - "Set auto-mod staging directory [$staging_dir]" + "Force update local mods" +# "Toggle headless mod install [$headless_hr]" +# "Set auto-mod staging directory [$staging_dir]" ) debug_sel=$(zenity --list --width=1280 --height=800 --column="Options" --title="DZGUI" --hide-header "${debug_list[@]}" 2>/dev/null) if [[ $debug_sel == "${debug_list[0]}" ]]; then @@ -1149,9 +1217,10 @@ options_menu(){ elif [[ $debug_sel == "${debug_list[3]}" ]]; then toggle_console_dl elif [[ $debug_sel == "${debug_list[4]}" ]]; then - toggle_headless - elif [[ $debug_sel == "${debug_list[5]}" ]]; then - file_picker + force_update=1 + force_update_mods + merge_modlists > >(zenity --pulsate --progress --auto-close --title=DZGUI --width=500 2>/dev/null) + auto_mod_install fi } query_and_connect(){ @@ -1389,7 +1458,7 @@ debug_servers(){ Key length: $key_len ======Short query====== Expected: 10 - Found: $debug_len + Found: $debug_len Response follows----> $debug_res ======END DEBUG======= @@ -1403,7 +1472,7 @@ server_browser(){ file=$(mktemp) local limit=20000 local url="https://api.steampowered.com/IGameServersService/GetServerList/v1/?filter=\appid\221100&limit=$limit&key=$steam_api" - check_geo_file + check_geo_file local_latlon choose_filters [[ -z $sels ]] && return @@ -1440,13 +1509,12 @@ mods_disk_size(){ printf "%s mods | " $(ls -1 "$game_dir" | wc -l) printf "Location: %s/steamapps/workshop/content/221100" "$steam_path" } - main_menu(){ print_news set_mode - if [[ -n $fav ]]; then - items[7]=" Change favorite server" - fi +# if [[ -n $fav ]]; then +# items[8]=" Change favorite server" +# fi while true; do set_header ${FUNCNAME[0]} rc=$? @@ -1462,35 +1530,37 @@ main_menu(){ elif [[ $sel == "${items[3]}" ]]; then connect_to_fav elif [[ $sel == "${items[4]}" ]]; then - : - elif [[ $sel == "${items[5]}" ]]; then connect_by_ip + elif [[ $sel == "${items[5]}" ]]; then + history_table elif [[ $sel == "${items[6]}" ]]; then - add_by_id + : elif [[ $sel == "${items[7]}" ]]; then - add_by_fav + add_by_id elif [[ $sel == "${items[8]}" ]]; then + add_by_fav + elif [[ $sel == "${items[9]}" ]]; then delete=1 query_and_connect - elif [[ $sel == "${items[9]}" ]]; then - : elif [[ $sel == "${items[10]}" ]]; then + : + elif [[ $sel == "${items[11]}" ]]; then list_mods | sed 's/\t/\n/g' | zenity --list --column="Mod" --column="Symlink" \ --title="DZGUI" $sd_res --text="$(mods_disk_size)" \ --print-column="" 2>/dev/null - elif [[ $sel == "${items[11]}" ]]; then - changelog | zenity --text-info $sd_res --title="DZGUI" 2>/dev/null elif [[ $sel == "${items[12]}" ]]; then + changelog | zenity --text-info $sd_res --title="DZGUI" 2>/dev/null + elif [[ $sel == "${items[13]}" ]]; then options_menu main_menu return - elif [[ $sel == "${items[13]}" ]]; then - : elif [[ $sel == "${items[14]}" ]]; then - help_file + : elif [[ $sel == "${items[15]}" ]]; then - report_bug + help_file elif [[ $sel == "${items[16]}" ]]; then + report_bug + elif [[ $sel == "${items[17]}" ]]; then forum else warn "This feature is not yet implemented." @@ -1534,7 +1604,7 @@ check_ping(){ } create_array(){ rows=() - list=$(cat $tmp) + list=$(cat $tmp) #TODO: improve error handling for null values lc=1 while read line; do @@ -1550,9 +1620,6 @@ create_array(){ time=$(echo "$line" | awk -F'\t' '{print $4}') stat=$(echo "$line" | awk -F'\t' '{print $5}') - #yad only - #[[ $stat == "online" ]] && stat="online" || : - #TODO: probe offline return codes id=$(echo "$line" | awk -F'\t' '{print $6}') tc=$(awk 'END{print NR}' $tmp) @@ -1714,7 +1781,7 @@ toggle_debug(){ setup(){ if [[ -n $fav ]]; then set_fav - items[7]=" Change favorite server" + items[8]=" Change favorite server" fi } check_map_count(){ @@ -1749,7 +1816,7 @@ while true; do zenity --info --title="DZGUI" --text="Added "$fav_id" to:\n${config_path}dztuirc\nIf errors occurred, you can restore the file:\n${config_path}dztuirc.old" 2>/dev/null source $config_file set_fav - items[7]=" Change favorite server" + items[8]=" Change favorite server" return fi fi @@ -1826,6 +1893,7 @@ initial_setup(){ find_default_path fetch_scmd_helper run_varcheck + stale_symlinks init_items setup deprecation_warning @@ -1840,4 +1908,3 @@ main(){ } main -#trap cleanup EXIT From 159a9bc900dff948f9b9f7106748822323ed1f07 Mon Sep 17 00:00:00 2001 From: aclist Date: Wed, 23 Nov 2022 00:12:28 +0900 Subject: [PATCH 023/144] Update labels --- changelog.md | 6 +++++- dzgui.sh | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index cc97565..bfb3d68 100644 --- a/changelog.md +++ b/changelog.md @@ -5,7 +5,11 @@ - Custom query API - Standardize dialogs -## [3.1.0-rc.10] 2022-11-21 +## [3.1.0-rc.12] 2022-11-22 +### Changed +- Updated menu labels + +## [3.1.0-rc.11] 2022-11-21 ### Added - Recent connect history - Force refresh local mods diff --git a/dzgui.sh b/dzgui.sh index 2f1eac3..42e0aba 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.1.0-rc.11 +version=3.1.0-rc.12 aid=221100 game="dayz" @@ -725,9 +725,9 @@ history_table(){ res=$(< $meta_file jq -er '.response.servers[]' 2>/dev/null) prepare_ip_list "$meta_file" >> /tmp/dz.hist sleep 1s - done > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) + done > >(zenity --pulsate --progress --auto-close --title=DZGUI --width=500 2>/dev/null) while true; do - sel=$(cat /tmp/dz.hist | zenity --width 1200 --height 800 --list --column=Name --column=IP --column=Players --column=Gametime --print-column=2 --separator=%% 2>/dev/null) + sel=$(cat /tmp/dz.hist | zenity --width 1200 --height 800 --title=DZGUI --text="Recent servers" --list --column=Name --column=IP --column=Players --column=Gametime --print-column=2 --separator=%% 2>/dev/null) if [[ $? -eq 1 ]]; then return_from_table=1 rm /tmp/dz.hist From 3fdb6c4d7f0c2d9b2549c9268d53181959602c3a Mon Sep 17 00:00:00 2001 From: aclist Date: Thu, 24 Nov 2022 01:50:34 +0900 Subject: [PATCH 024/144] Block Steam from opening (#39) --- changelog.md | 5 +++++ dzgui.sh | 11 ++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index bfb3d68..5806221 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,11 @@ - Custom query API - Standardize dialogs +## [3.1.0-rc.13] 2022-11-24 +### Fixed +- Prevent downloading mods if Steam is not running +- Suppress "force refresh mods" option if automods is disabled + ## [3.1.0-rc.12] 2022-11-22 ### Changed - Updated menu labels diff --git a/dzgui.sh b/dzgui.sh index 42e0aba..94281a3 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.1.0-rc.12 +version=3.1.0-rc.13 aid=221100 game="dayz" @@ -632,6 +632,9 @@ update_history(){ fi echo -e "${old}${ip}" > "$hist_file" } +is_steam_running(){ + xdotool search --desktop "$(xdotool get_desktop)" --name "Steam" +} connect(){ #TODO: sanitize/validate input readarray -t qport_arr <<< "$qport_list" @@ -656,6 +659,7 @@ connect(){ compare [[ $auto_install -eq 2 ]] && merge_modlists if [[ -n $diff ]]; then + [[ -z $(is_steam_running) ]] && { zenity --info --text "Steam must be running on the current desktop to use this feature."; return; } if [[ $auto_install -eq 1 ]]; then headless_mod_install "$diff" rc=$? @@ -1116,7 +1120,7 @@ console_dl(){ steam steam://open/console 2>/dev/null 1>&2 && sleep 1s #https://github.com/jordansissel/xdotool/issues/67 - local wid=$(xdotool search --sync --onlyvisible --desktop "$(xdotool get_desktop)" --name Steam) + local wid=$(xdotool search --desktop "$(xdotool get_desktop)" --name Steam) xdotool windowactivate $wid for i in "${modids[@]}"; do xdotool type --delay 15 "workshop_download_item $aid $i" @@ -1197,10 +1201,10 @@ options_menu(){ "Toggle debug mode" "Generate debug log" "Toggle auto mod install [$auto_hr]" - "Force update local mods" # "Toggle headless mod install [$headless_hr]" # "Set auto-mod staging directory [$staging_dir]" ) + [[ $auto_install -eq 2 ]] && debug_list+=("Force update local mods") debug_sel=$(zenity --list --width=1280 --height=800 --column="Options" --title="DZGUI" --hide-header "${debug_list[@]}" 2>/dev/null) if [[ $debug_sel == "${debug_list[0]}" ]]; then enforce_dl=1 @@ -1220,6 +1224,7 @@ options_menu(){ force_update=1 force_update_mods merge_modlists > >(zenity --pulsate --progress --auto-close --title=DZGUI --width=500 2>/dev/null) + [[ -z $(is_steam_running) ]] && { zenity --info --text "Steam must be running on the current desktop to use this feature."; return; } auto_mod_install fi } From b4aa8fbb6dcba80cb8b4aa14b2d7eb23005e048f Mon Sep 17 00:00:00 2001 From: aclist Date: Thu, 24 Nov 2022 02:15:11 +0900 Subject: [PATCH 025/144] Rename history --- changelog.md | 4 ++++ dzgui.sh | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 5806221..a901df0 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,10 @@ - Custom query API - Standardize dialogs +## [3.1.0-rc.14] 2022-11-24 +### Changed +- Rename "History" to "Recent" + ## [3.1.0-rc.13] 2022-11-24 ### Fixed - Prevent downloading mods if Steam is not running diff --git a/dzgui.sh b/dzgui.sh index 94281a3..150e6b9 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.1.0-rc.13 +version=3.1.0-rc.14 aid=221100 game="dayz" @@ -99,7 +99,7 @@ items=( " My servers" " Quick connect to favorite server" " Connect by IP" - " History (last 10 connected)" + " Recent servers (last 10)" "[Manage servers]" " Add server by ID" " Add favorite server" From 913140c22cbb67355ba5776e947fcea0a7cdd282 Mon Sep 17 00:00:00 2001 From: aclist Date: Fri, 25 Nov 2022 05:53:35 +0900 Subject: [PATCH 026/144] Faster path discovery --- changelog.md | 15 +++ dzgui.sh | 259 ++++++++++++++------------------------------ helpers/vdf2json.py | 63 +++++++++++ 3 files changed, 160 insertions(+), 177 deletions(-) create mode 100644 helpers/vdf2json.py diff --git a/changelog.md b/changelog.md index a901df0..6d0c678 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,21 @@ - Custom query API - Standardize dialogs +## [3.1.0-rc.15] 2022-11-24 +### Fixed +- Faster path discovery on initial setup +- Handle whitelist deletion when only one entry present +- Return to main menu from recent servers list +- Unset delete menu flags after deletion +### Added +- Add python to deps +- Add Steam API key requirement to initial setup +### Dropped +- Drop mandatory server IDs on initial setup +- Old functions and files pertaining to headless mode +### Changed +- Allow My Servers list to be initially empty + ## [3.1.0-rc.14] 2022-11-24 ### Changed - Rename "History" to "Recent" diff --git a/dzgui.sh b/dzgui.sh index 150e6b9..e258b32 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -32,11 +32,15 @@ km_helper_url="$releases_url/latlon" db_file="$releases_url/ips.csv.gz" sums_url="$testing_url/helpers/sums.md5" scmd_url="$testing_url/helpers/scmd.sh" +vdf2json_url="$testing_url/helpers/vdf2json.py" notify_url="$testing_url/helpers/d.html" notify_img_url="$testing_url/helpers/d.webp" forum_url="https://github.com/aclist/dztui/discussions" version_file="$config_path/versions" +#TODO: prevent connecting to offline servers +#TODO: abstract zenity title params and dimensions + update_last_seen(){ mv $config_file ${config_path}dztuirc.old nr=$(awk '/seen_news=/ {print NR}' ${config_path}dztuirc.old) @@ -53,7 +57,7 @@ check_news(){ } print_news(){ check_news - if [[ $sum == $seen_news || -z $result ]]; then + if [[ $sum == $seen_news || -z $result ]]; then hchar="" news="" else @@ -62,11 +66,9 @@ print_news(){ update_last_seen fi } -#TODO: prevent connecting to offline servers -#TODO: abstract zenity title params and dimensions declare -A deps -deps=([awk]="5.1.1" [curl]="7.80.0" [jq]="1.6" [tr]="9.0" [zenity]="3.42.1" [steam]="1.0.0") +deps=([awk]="5.1.1" [curl]="7.80.0" [jq]="1.6" [tr]="9.0" [zenity]="3.42.1" [steam]="1.0.0" [python]="3.0") changelog(){ if [[ $branch == "stable" ]]; then md="https://raw.githubusercontent.com/aclist/dztui/dzgui/changelog.md" @@ -111,7 +113,7 @@ items=( "[Help]" " Help file ⧉" " Report bug ⧉" - " Become a beta tester ⧉" + " Forum ⧉" ) } warn_and_exit(){ @@ -136,11 +138,7 @@ query_api(){ if [[ $one_shot_launch -eq 1 ]]; then list_of_ids="$fav" else - if [[ -n $fav ]]; then - list_of_ids="$whitelist,$fav" - else - list_of_ids="$whitelist" - fi + list_of_ids="$whitelist" fi set_api_params if [[ "$(jq -r 'keys[]' <<< "$response")" == "errors" ]]; then @@ -230,68 +228,57 @@ freedesktop_dirs(){ write_desktop_file > "$HOME/Desktop/dzgui.desktop" fi } +find_library_folder(){ + steam_path=$(python $helpers_path/vdf2json.py -i $default_steam_path/steamapps/libraryfolders.vdf | jq -r '.libraryfolders[]|select(.apps|has("221100")).path') +} file_picker(){ while true; do local path=$(zenity --file-selection --directory 2>/dev/null) if [[ -z "$path" ]]; then return else - echo "[DZGUI]" Set mod staging path to "$path" - staging_dir="$path" - write_config > $config_file + default_steam_path="$path" + find_library_folder + fi + if [[ -z $steam_path ]]; then + warn "DayZ not found at this path." + else return fi done } -guess_path(){ - echo "# Checking for default DayZ path" - path=$(find $HOME -type d -regex ".*/steamapps/common/DayZ$" -print -quit) - if [[ -n "$path" ]]; then - clean_path=$(echo -e "$path" | awk -F"/steamapps" '{print $1}') - steam_path="$clean_path" - else - echo "# Searching for alternate DayZ path. This may take some time." - path=$(find / -type d \( -path "/proc" -o -path "*/timeshift" -o -path "/tmp" -o -path "/usr" -o -path "/boot" -o -path "/proc" -o -path "/root" -o -path "/sys" -o -path "/etc" -o -path "/var" -o -path "/lost+found" \) -prune -o -regex ".*/steamapps/common/DayZ$" -print -quit 2>/dev/null) - clean_path=$(echo -e "$path" | awk -F"/steamapps" '{print $1}') - steam_path="$clean_path" - fi -} create_config(){ while true; do - player_input="$(zenity --forms --add-entry="Player name (required for some servers)" --add-entry="API key" --add-entry="Server 1 (you can add more later)" --title="DZGUI" --text="DZGUI" --add-entry="Server 2" --add-entry="Server 3" --add-entry="Server 4" $sd_res --separator="│" 2>/dev/null)" - #explicitly setting IFS crashes zenity in loop - #and mapfile does not support high ascii delimiters - #so split fields with newline - readarray -t args < <(echo "$player_input" | sed 's/│/\n/g') - name="${args[0]}" - api_key="${args[1]}" - server_1="${args[2]}" - server_2="${args[3]}" - server_3="${args[4]}" - server_4="${args[5]}" + player_input="$(zenity --forms --add-entry="Player name (required for some servers)" --add-entry="BattleMetrics API key" --add-entry="Steam API key" --title="DZGUI" --text="DZGUI" $sd_res --separator="│" 2>/dev/null)" + #explicitly setting IFS crashes zenity in loop + #and mapfile does not support high ascii delimiters + #so split fields with newline + readarray -t args < <(echo "$player_input" | sed 's/│/\n/g') + name="${args[0]}" + api_key="${args[1]}" + steam_api="${args[2]}" - [[ -z $player_input ]] && exit - if [[ -z $api_key ]]; then - warn "API key: invalid value" - elif [[ -z $server_1 ]]; then - warn "Server 1: cannot be empty" - elif [[ ! $server_1 =~ ^[0-9]+$ ]]; then - warn "Server 1: only numeric IDs" - elif [[ -n $server_2 ]] && [[ ! $server_2 =~ ^[0-9]+$ ]]; then - warn "Server 2: only numeric IDs" - elif [[ -n $server_3 ]] && [[ ! $server_3 =~ ^[0-9]+$ ]]; then - warn "Server 3: only numeric IDs" - elif [[ -n $server_4 ]] && [[ ! $server_3 =~ ^[0-9]+$ ]]; then - warn "Server 4: only numeric IDs" - else - whitelist=$(echo "$player_input" | awk -F"│" '{OFS=","}{print $3,$4,$5,$6}' | sed 's/,*$//g' | sed 's/^,*//g') - guess_path > >(zenity --width 500 --progress --auto-close --pulsate 2>/dev/null) && - echo "[DZGUI] Set path to $steam_path" - #FIXME: tech debt: gracefully exit if user cancels search process - mkdir -p $config_path; write_config > $config_file - info "Config file created at $config_file." - return - fi + [[ -z $player_input ]] && exit + if [[ -z $api_key ]] || [[ -z $steam_api ]]; then + warn "API key: invalid value" + #TODO: test BM key + elif [[ $(test_steam_api) -eq 1 ]]; then + warn "Invalid Steam API key" + else + while true; do + find_default_path + find_library_folder + if [[ -z $steam_path ]]; then + return + else + mkdir -p $config_path + write_config > $config_file + info "Config file created at $config_file." + source $config_file + return + fi + done + fi done } @@ -300,9 +287,7 @@ err(){ } varcheck(){ [[ -z $api_key ]] && (err "Error in key: 'api_key'") - [[ -z $whitelist ]] && (err "Error in key: 'whitelist'") [[ ! -d "$game_dir" ]] && (err "Malformed game path") - [[ $whitelist =~ [[:space:]] ]] && (err "Separate whitelist values with commas") } run_depcheck(){ if [[ -z $(depcheck) ]]; then @@ -327,13 +312,14 @@ run_varcheck(){ if [[ $code -eq 1 ]]; then exit else - echo "100" + #echo "100" create_config fi fi } config(){ if [[ ! -f $config_file ]]; then + #echo "100" zenity --width 500 --info --text="Config file not found. Click OK to proceed to first-time setup." 2>/dev/null code=$? #prevent progress if user hits ESC @@ -377,61 +363,12 @@ steam_deck_mods(){ compare done } -set_term(){ - local term="$1" - local tterm="term=\"$term\"" - mv $config_file ${config_path}dztuirc.old - nr=$(awk '/term=/ {print NR}' ${config_path}dztuirc.old) - awk -v "var=$tterm" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > $config_file - printf "[DZGUI] Set term to '$term'\n" - source $config_file -} -sel_term(){ - #only terminals known to support -e flag - for i in "$TERMINAL" urxvt alacritty konsole gnome-terminal terminator xfce4-terminal xterm tilix; do - [[ $(command -v $i) ]] && terms+=($i) - done - #FIXME: if no terms, error - local terms=$(printf "%s\n" "${terms[@]}" | sort -u) - term=$(echo "$terms" | zenity --list --column=Terminal --height=800 --width=1200 --text="Select your preferred terminal emulator to run steamcmd (setting will be saved)" --title=DZGUI 2>/dev/null) -} calc_mod_sizes(){ for i in "$diff"; do local mods+=$(grep -w "$i" /tmp/modsizes | awk '{print $1}') done totalmodsize=$(echo -e "${mods[@]}" | awk '{s+=$1}END{print s}') } -term_params(){ - case $term in - konsole) $term --hold -e "bash $helpers_path/scmd.sh $totalmodsize $1";; - urxvt) $term -e bash -c "/$helpers_path/scmd.sh $totalmodsize $1";; - alacritty) $term -e bash -c "/$helpers_path/scmd.sh $totalmodsize $1";; - terminator|xterm|tilix|xfce4-terminal) $term -e "bash $helpers_path/scmd.sh $totalmodsize $1";; - esac -} -headless_mod_install(){ - cmd=$(printf "%q " "$@") - if [[ -z "$term" ]]; then - if [[ $is_steam_deck -eq 1 ]]; then - set_term konsole - return 0 - else - sel_term && set_term "$term" - fi - fi - [[ -z "$term" ]] && return 1 - echo "[DZGUI] Kicking off auto mod script" - calc_mod_sizes - term_params "$cmd" - compare - if [[ -z $diff ]]; then - passed_mod_check > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) - launch - else - warn "Auto mod installation failed or some mods missing.\nReverting to manual mode." - return 1 - fi -} test_display_mode(){ pgrep -a gamescope | grep -q "generate-drm-mode" [[ $? -eq 0 ]] && gamemode=1 @@ -719,7 +656,7 @@ prepare_ip_list(){ } history_table(){ for i in $(cat $hist_file); do - echo "# Getting metadata for $i" + echo "# Getting metadata for $i" local meta_file=$(mktemp) source $config_file local url="https://api.steampowered.com/IGameServersService/GetServerList/v1/?filter=\appid\221100\gameaddr\\$i&key=$steam_api" @@ -727,9 +664,10 @@ history_table(){ json=$(mktemp) < $meta_file jq '.response' > $json res=$(< $meta_file jq -er '.response.servers[]' 2>/dev/null) - prepare_ip_list "$meta_file" >> /tmp/dz.hist - sleep 1s - done > >(zenity --pulsate --progress --auto-close --title=DZGUI --width=500 2>/dev/null) + prepare_ip_list "$meta_file" >> /tmp/dz.hist + sleep 0.5s + done | zenity --pulsate --progress --auto-close --title=DZGUI --width=500 --no-cancel 2>/dev/null + [[ $? -eq 1 ]] && return while true; do sel=$(cat /tmp/dz.hist | zenity --width 1200 --height 800 --title=DZGUI --text="Recent servers" --list --column=Name --column=IP --column=Players --column=Gametime --print-column=2 --separator=%% 2>/dev/null) if [[ $? -eq 1 ]]; then @@ -750,7 +688,6 @@ history_table(){ fi done rm /tmp/dz.hist - } ip_table(){ @@ -958,7 +895,7 @@ set_mode(){ fi } delete_by_id(){ - new_whitelist="whitelist=\"$(echo "$whitelist" | sed "s/,$server_id$//;s/^$server_id,//;s/,$server_id,/,/")\"" + new_whitelist="whitelist=\"$(echo "$whitelist" | sed "s/,$server_id$//;s/^$server_id,//;s/,$server_id,/,/;s/^$server_id$//")\"" mv $config_file ${config_path}dztuirc.old nr=$(awk '/whitelist=/ {print NR}' ${config_path}dztuirc.old) awk -v "var=$new_whitelist" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > ${config_path}dztuirc @@ -970,10 +907,12 @@ delete_or_connect(){ if [[ $delete -eq 1 ]]; then server_name=$(echo "$sel" | awk -F"%%" '{print $1}') server_id=$(echo "$sel" | awk -F"%%" '{print $2}') - zenity --question --text="Delete this server? \n$server_name" + zenity --question --text="Delete this server? \n$server_name" --title=DZGUI --width=500 2>/dev/null if [[ $? -eq 0 ]]; then delete_by_id $server_id fi + source $config_file + unset delete else #hotfix for bug #36 local lookup_ip=$(echo "$sel" | awk -F%% '{print $1}') @@ -1130,26 +1069,31 @@ console_dl(){ done } find_default_path(){ - [[ -n $default_steam_path ]] && return + discover(){ + echo "# Searching for Steam" + default_steam_path=$(find / -type d \( -path "/proc" -o -path "*/timeshift" -o -path \ + "/tmp" -o -path "/usr" -o -path "/boot" -o -path "/run" -o -path "/proc" -o -path "/root" \ + -o -path "/sys" -o -path "/etc" -o -path "/var" -o -path "/lost+found" \) -prune \ + -o -regex ".*/Steam/ubuntu12_32$" -print -quit 2>/dev/null | sed 's@/ubuntu12_32@@') + echo "100" + } if [[ $is_steam_deck -eq 1 ]]; then default_steam_path="$HOME/.local/share/Steam" else + #default if [[ -d "$HOME/.local/share/Steam" ]]; then default_steam_path="$HOME/.local/share/Steam" + #ubuntu + elif [[ -d "$HOME/.steam/steam" ]]; then + default_steam_path="$HOME/.steam/steam" else - # ubuntu .steam - default_steam_path=$(find / -type d \( -path "/proc" -o -path "*/timeshift" -o -path \ - "/tmp" -o -path "/usr" -o -path "/boot" -o -path "/run" -o -path "/proc" -o -path "/root" \ - -o -path "/sys" -o -path "/etc" -o -path "/var" -o -path "/lost+found" \) -prune \ - -o -regex ".*/Steam/ubuntu12_32$" -print -quit 2>/dev/null | sed 's@/ubuntu12_32@@') + local res=$(echo -e "Let DZGUI auto-discover Steam path (accurate, slower)\nSelect the Steam path manually (less accurate, faster)" | zenity --list --column="Choice" --title=DZGUI --hide-header --text="Steam is not installed in a standard location." $sd_res) + case "$res" in + *auto*) discover > >(zenity --width 500 --progress --auto-close --title="DZGUI" --pulsate --no-cancel 2>/dev/null) ;; + *manual*) file_picker ;; + esac fi fi - default_steam_path="default_steam_path=\"$default_steam_path\"" - mv $config_file ${config_path}dztuirc.old - local nr=$(awk '/default_steam_path=/ {print NR}' ${config_path}dztuirc.old) - awk -v "var=$default_steam_path" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > $config_file - printf "[DZGUI] Set default Steam path to $default_steam_path\n" - source $config_file } popup(){ pop(){ @@ -1161,6 +1105,7 @@ popup(){ 300) pop "\nThe Steam console will now open and briefly issue commands to\ndownload the workshop files, then return to the download progress page.\n\nEnsure that the Steam console has keyboard and mouse focus\n(keep hands off keyboard) while the commands are being issued.\n\nDepending on the number if mods, it may take some time to queue the downloads,\nbut if a popup or notification window steals focus, it could obstruct\nthe process." ;; 400) pop "Automod install enabled. Auto-downloaded mods will not appear\nin your Steam Workshop subscriptions, but DZGUI will\ntrack the version number of downloaded mods internally\nand trigger an update if necessary." ;; 500) pop "Automod install disabled.\nSwitched to manual mode." ;; + 600) pop "No preferred servers set." ;; esac } requires_xdo(){ @@ -1229,6 +1174,7 @@ options_menu(){ fi } query_and_connect(){ + [[ -z $whitelist ]] && { popup 600; return; } query_api parse_json #TODO: create logger function @@ -1641,7 +1587,6 @@ create_array(){ for i in "${rows[@]}"; do echo -e "$i"; done > $tmp } set_fav(){ - #TODO: test API key here and return errors echo "[DZGUI] Querying favorite server" query_api fav_label=$(curl -s "$api" -H "Authorization: Bearer "$api_key"" -G -d "filter[game]=$game" -d "filter[ids][whitelist]=$fav" \ @@ -1757,7 +1702,8 @@ add_by_id(){ if [[ ! $id =~ ^[0-9]+$ ]]; then zenity --warning --title="DZGUI" --text="Invalid ID" 2>/dev/null else - new_whitelist="whitelist=\"$whitelist,$id\"" + [[ -z $whitelist ]] && new_whitelist="whitelist=\"$id\"" + [[ -n $whitelist ]] && new_whitelist="whitelist=\"$whitelist,$id\"" mv $config_file ${config_path}dztuirc.old nr=$(awk '/whitelist=/ {print NR}' ${config_path}dztuirc.old) awk -v "var=$new_whitelist" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > ${config_path}dztuirc @@ -1844,48 +1790,9 @@ lock(){ echo $$ > $config_path/.lockfile fi } -fetch_scmd_helper(){ +fetch_helpers(){ mkdir -p "$helpers_path" - curl -Ls "$scmd_url" > "$helpers_path/scmd.sh" - chmod +x "$helpers_path/scmd.sh" - [[ ! -f "$helpers_path/d.html" ]] && curl -Ls "$notify_url" > "$helpers_path/d.html" - [[ ! -f "$helpers_path/d.webp" ]] && curl -Ls "$notify_img_url" > "$helpers_path/d.webp" -} -deprecation_warning(){ - warn_big(){ - cat <<- HERE - IMPORTANT ANNOUNCEMENT - (Steam API key not found) - - A Steam API key is now mandatory to run the app. - The BM API returns incorrect mod data on some servers - and cannot be relied upon for up to date information. - - Going forward, we will only use the BM API as a convenience - function to manage server names and your favorite servers list, - and migrate to indexing servers on an IP basis. - - This is a backend change. You can continue adding servers by ID, - but we will retrieve information from Valve instead, as we do for the - server browser and connect-by-ip methods. - - Click [OK] to open the help page describing how to set up your key. - After you input a valid key, the app will resume. - HERE - } - if [[ -z $steam_api ]]; then - echo "100" - local big_prompt - [[ $is_steam_deck -eq 1 ]] && big_prompt="--width=800" - zenity --info --text="$(warn_big)" $big_prompt - key_setup_url="https://aclist.github.io/dzgui/dzgui.html#_api_key_server_ids" - browser "$key_setup_url" 2>/dev/null & - while true; do - if [[ $(check_steam_api) ]]; then - break - fi - done - fi + [[ ! -f "$helpers_path/vdf2json.py" ]] && curl -Ls "$vdf2json_url" > "$helpers_path/vdf2json.py" } initial_setup(){ echo "# Initial setup" @@ -1894,19 +1801,17 @@ initial_setup(){ check_architecture check_version check_map_count + fetch_helpers config - find_default_path - fetch_scmd_helper run_varcheck stale_symlinks init_items setup - deprecation_warning echo "100" } main(){ lock - initial_setup > >(zenity --pulsate --progress --auto-close --title="DZGUI" --width=500 2>/dev/null) + initial_setup > >(zenity --pulsate --progress --auto-close --title="DZGUI" --no-cancel --width=500 2>/dev/null) main_menu #cruddy handling for steam forking [[ $? -eq 1 ]] && pkill -f dzgui.sh diff --git a/helpers/vdf2json.py b/helpers/vdf2json.py new file mode 100644 index 0000000..279b513 --- /dev/null +++ b/helpers/vdf2json.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python + +from shlex import shlex + +def vdf2json(stream): + + """ + Read a Steam vdf file and return a string in json format + """ + + def _istr(ident, string): + return (ident * ' ') + string + + jbuf = '{\n' + lex = shlex(stream) + indent = 1 + + while True: + tok = lex.get_token() + if not tok: + return jbuf + '}\n' + if tok == '}': + indent -= 1 + jbuf += _istr(indent, '}') + ntok = lex.get_token() + lex.push_token(ntok) + if ntok and ntok != '}': + jbuf += ',' + jbuf += '\n' + else: + ntok = lex.get_token() + if ntok == '{': + jbuf += _istr(indent, tok + ': {\n') + indent += 1 + else: + jbuf += _istr(indent, tok + ': ' + ntok) + ntok = lex.get_token() + lex.push_token(ntok) + if ntok != '}': + jbuf += ',' + jbuf += '\n' +def main(): + """ + Read Steam vdf and write json compatible conversion + """ + import sys + import argparse + + parser = argparse.ArgumentParser(prog='vdf2json', description=main.__doc__) + parser.add_argument('-i', '--input', + default=sys.stdin, + type=argparse.FileType('r'), + help='input vdf file (stdin if not specified)') + parser.add_argument('-o', '--output', + default=sys.stdout, + type=argparse.FileType('w'), + help='output json file (stdout if not specified)') + + args = parser.parse_args() + args.output.write(vdf2json(args.input)) + +if __name__ == '__main__': + main() From c160d321cb17370bd9a3dc084d422995a05bb921 Mon Sep 17 00:00:00 2001 From: aclist Date: Fri, 25 Nov 2022 05:55:19 +0900 Subject: [PATCH 027/144] Bump version --- dzgui.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dzgui.sh b/dzgui.sh index e258b32..4a18d6c 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.1.0-rc.14 +version=3.1.0-rc.15 aid=221100 game="dayz" From 5824f971bb262490159ff9f914248dd7264922dc Mon Sep 17 00:00:00 2001 From: aclist Date: Fri, 25 Nov 2022 14:34:16 +0900 Subject: [PATCH 028/144] Hotfix for #40 --- changelog.md | 4 ++++ dzgui.sh | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 6d0c678..44d2fd9 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,10 @@ - Custom query API - Standardize dialogs +## [3.1.0-rc.16] 2022-11-25 +### Fixed +- Hotfix for server modlists returning multiples of same mod + ## [3.1.0-rc.15] 2022-11-24 ### Fixed - Faster path discovery on initial setup diff --git a/dzgui.sh b/dzgui.sh index 4a18d6c..a373d11 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.1.0-rc.15 +version=3.1.0-rc.16 aid=221100 game="dayz" @@ -814,7 +814,7 @@ server_modlist(){ done } compare(){ - diff=$(comm -23 <(server_modlist | sort) <(installed_mods | sort)) + diff=$(comm -23 <(server_modlist | sort -u) <(installed_mods | sort)) } installed_mods(){ From 09cb67134bf1c908add892f5470a0c09812a9ee7 Mon Sep 17 00:00:00 2001 From: aclist Date: Tue, 29 Nov 2022 04:52:25 +0900 Subject: [PATCH 029/144] Check python explicitly --- changelog.md | 4 ++++ dzgui.sh | 12 ++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 44d2fd9..685c45f 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,10 @@ - Custom query API - Standardize dialogs +## [3.1.0-rc.17] 2022-11-28 +### Changed +- Explicitly check Python version + ## [3.1.0-rc.16] 2022-11-25 ### Fixed - Hotfix for server modlists returning multiples of same mod diff --git a/dzgui.sh b/dzgui.sh index a373d11..6df5751 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.1.0-rc.16 +version=3.1.0-rc.17 aid=221100 game="dayz" @@ -68,7 +68,7 @@ print_news(){ } declare -A deps -deps=([awk]="5.1.1" [curl]="7.80.0" [jq]="1.6" [tr]="9.0" [zenity]="3.42.1" [steam]="1.0.0" [python]="3.0") +deps=([awk]="5.1.1" [curl]="7.80.0" [jq]="1.6" [tr]="9.0" [zenity]="3.42.1" [steam]="1.0.0") changelog(){ if [[ $branch == "stable" ]]; then md="https://raw.githubusercontent.com/aclist/dztui/dzgui/changelog.md" @@ -248,6 +248,7 @@ file_picker(){ done } create_config(){ + check_pyver while true; do player_input="$(zenity --forms --add-entry="Player name (required for some servers)" --add-entry="BattleMetrics API key" --add-entry="Steam API key" --title="DZGUI" --text="DZGUI" $sd_res --separator="│" 2>/dev/null)" #explicitly setting IFS crashes zenity in loop @@ -298,6 +299,13 @@ run_depcheck(){ exit fi } +check_pyver(){ + pyver=$(python --version | awk '{print $2}') + if [[ -z $pyver ]] || [[ ${pyver:0:1} -lt 3 ]]; then + warn "Requires python >=3.0" && + exit + fi +} run_varcheck(){ source $config_file workshop_dir="$steam_path/steamapps/workshop/content/$aid" From fa72d84179c3f92a7e667f5b0985124ff4b656d4 Mon Sep 17 00:00:00 2001 From: aclist Date: Thu, 1 Dec 2022 18:34:40 +0900 Subject: [PATCH 030/144] Add thousands separator --- changelog.md | 6 ++++ dzgui.sh | 80 +++++----------------------------------------------- 2 files changed, 13 insertions(+), 73 deletions(-) diff --git a/changelog.md b/changelog.md index 685c45f..e40c68a 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,12 @@ - Custom query API - Standardize dialogs +## [3.1.0-rc.18] 2022-12-01 +### Added +- Thousands separator to player counts +### Dropped +- Removed deprecated functions + ## [3.1.0-rc.17] 2022-11-28 ### Changed - Explicitly check Python version diff --git a/dzgui.sh b/dzgui.sh index 6df5751..84b5ccb 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.1.0-rc.17 +version=3.1.0-rc.18 aid=221100 game="dayz" @@ -116,11 +116,6 @@ items=( " Forum ⧉" ) } -warn_and_exit(){ - zenity --info --title="DZGUI" --text="$1" --width=500 --icon-name="dialog-warning" 2>/dev/null - printf "[DZGUI] %s\n" "$check_config_msg" - exit -} warn(){ zenity --info --title="DZGUI" --text="$1" --width=500 --icon-name="dialog-warning" 2>/dev/null } @@ -291,9 +286,7 @@ varcheck(){ [[ ! -d "$game_dir" ]] && (err "Malformed game path") } run_depcheck(){ - if [[ -z $(depcheck) ]]; then - : - else + if [[ -n $(depcheck) ]]; then echo "100" zenity --warning --ok-label="Exit" --title="DZGUI" --text="$(depcheck)" exit @@ -310,11 +303,8 @@ run_varcheck(){ source $config_file workshop_dir="$steam_path/steamapps/workshop/content/$aid" game_dir="$steam_path/steamapps/common/DayZ" - if [[ -z $(varcheck) ]]; then - : - else + if [[ -n $(varcheck) ]]; then zenity --warning --width 500 --text="$(varcheck)" 2>/dev/null - printf "[DZGUI] %s\n" "$check_config_msg" zenity --question --cancel-label="Exit" --text="Malformed config file. This is probably user error.\nStart first-time setup process again?" --width=500 2>/dev/null code=$? if [[ $code -eq 1 ]]; then @@ -330,7 +320,7 @@ config(){ #echo "100" zenity --width 500 --info --text="Config file not found. Click OK to proceed to first-time setup." 2>/dev/null code=$? - #prevent progress if user hits ESC + #TODO: prevent progress if user hits ESC if [[ $code -eq 1 ]]; then exit else @@ -340,21 +330,6 @@ config(){ source $config_file fi -} -open_mod_links(){ - link_file=$(mktemp) - echo "" > $link_file - echo "DZGUI" >> $link_file - echo "

DZGUI

" >> $link_file - echo "

Open these links and subscribe to them on the Steam Workshop, then continue with the application prompts.
Note: it may take some time for mods to synchronize before DZGUI can see them.
It can help to have Steam in an adjacent window so that you can see the downloads completing.

" >> $link_file - n=1 - for i in $diff; do - echo "$n. ${workshop}$i
" - let n++ - done >> $link_file - echo "" >> $link_file - browser "$link_file" 2>/dev/null & - } steam_deck_mods(){ until [[ -z $diff ]]; do @@ -605,6 +580,7 @@ connect(){ [[ $auto_install -eq 2 ]] && merge_modlists if [[ -n $diff ]]; then [[ -z $(is_steam_running) ]] && { zenity --info --text "Steam must be running on the current desktop to use this feature."; return; } + #TODO: headless is deprecated if [[ $auto_install -eq 1 ]]; then headless_mod_install "$diff" rc=$? @@ -1025,43 +1001,6 @@ generate_log(){ $(list_mods) DOC } -automods_prompt(){ -cat <<- HERE - -Headless installation set to ON. - -READ THIS FIRST: -With this setting on, DZGUI will attempt to download and prepare mods using Valve's steamcmd tool. - -The first time this process is run, DZGUI will ask you to select a terminal emulator of your preference to spawn the installation routine. If you don't have a preference or don't know, you can pick any. - -Installation will kick off in a separate window and may ask you for input such as your sudo password in order to install system packages and create the steamcmd user. - -steamcmd itself will ask for your Steam credentials. This information is used directly by Valve's steamcmd tool to authenticate your account and let you download mods headlessly. steamcmd is an official program created by Valve and communicates only with their servers. - -NOTE: it can take some time for large mods to download, and steamcmd will not inform you of activity until each one is finished downloading. - -If your distribution is unsupported, you don't have enough disk space to stage all of the mods, or there are other problems, DZGUI will warn you and write a report to $HOME/.local/share/dzgui/helpers/SCMD.log. You can attach this file to a bug report. -HERE -} -toggle_headless(){ - [[ $is_steam_deck -eq 1 ]] && test_display_mode - [[ $gamemode -eq 1 ]] && { popup 200; return; } - mv $config_file ${config_path}dztuirc.old - local nr=$(awk '/auto_install=/ {print NR}' ${config_path}dztuirc.old) - if [[ $auto_install == "1" ]]; then - auto_install="0" - else - auto_install="1" - fi - local flip_state="auto_install=\"$auto_install\"" - awk -v "var=$flip_state" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > $config_file - printf "[DZGUI] Set mod install to '$auto_install'\n" - source $config_file - local big_prompt - [[ $is_steam_deck -eq 1 ]] && big_prompt="--width=800" - [[ $auto_install == "1" ]] && zenity --info --text="$(automods_prompt)" $big_prompt 2>/dev/null -} console_dl(){ readarray -t modids <<< "$@" steam steam://open/console 2>/dev/null 1>&2 && @@ -1116,9 +1055,6 @@ popup(){ 600) pop "No preferred servers set." ;; esac } -requires_xdo(){ - zenity --info --text="This feature requires xdotool" --title=DZGUI --width=500 -} toggle_console_dl(){ [[ $is_steam_deck -eq 1 ]] && test_display_mode [[ $gamemode -eq 1 ]] && { popup 200; return; } @@ -1154,8 +1090,6 @@ options_menu(){ "Toggle debug mode" "Generate debug log" "Toggle auto mod install [$auto_hr]" -# "Toggle headless mod install [$headless_hr]" -# "Set auto-mod staging directory [$staging_dir]" ) [[ $auto_install -eq 2 ]] && debug_list+=("Force update local mods") debug_sel=$(zenity --list --width=1280 --height=800 --column="Options" --title="DZGUI" --hide-header "${debug_list[@]}" 2>/dev/null) @@ -1442,8 +1376,8 @@ server_browser(){ response=$(curl -Ls "$url" | jq -r '.response.servers') } fetch > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) - total_servers=$(echo "$response" | jq 'length') - players_online=$(echo "$response" | jq '.[].players' | awk '{s+=$1}END{print s}') + total_servers=$(echo "$response" | jq 'length' | numfmt --grouping) + players_online=$(echo "$response" | jq '.[].players' | awk '{s+=$1}END{print s}' | numfmt --grouping) debug_log="$HOME/.local/share/dzgui/DEBUG.log" debug_servers local sel=$(munge_servers) From e0733c7d483d49fafcb62401d3ff161e64971feb Mon Sep 17 00:00:00 2001 From: aclist Date: Fri, 2 Dec 2022 16:25:08 +0900 Subject: [PATCH 031/144] Explicitly use system zenity --- dzgui.sh | 148 +++++++++++++++++++++++++++---------------------------- 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/dzgui.sh b/dzgui.sh index 84b5ccb..4eb0a51 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.1.0-rc.18 +version=3.1.0-rc.19 aid=221100 game="dayz" @@ -37,6 +37,7 @@ notify_url="$testing_url/helpers/d.html" notify_img_url="$testing_url/helpers/d.webp" forum_url="https://github.com/aclist/dztui/discussions" version_file="$config_path/versions" +steamsafe_zenity="/usr/bin/zenity" #TODO: prevent connecting to offline servers #TODO: abstract zenity title params and dimensions @@ -68,7 +69,7 @@ print_news(){ } declare -A deps -deps=([awk]="5.1.1" [curl]="7.80.0" [jq]="1.6" [tr]="9.0" [zenity]="3.42.1" [steam]="1.0.0") +deps=([awk]="5.1.1" [curl]="7.80.0" [jq]="1.6" [tr]="9.0" [$steamsafe_zenity]="3.42.1" [steam]="1.0.0") changelog(){ if [[ $branch == "stable" ]]; then md="https://raw.githubusercontent.com/aclist/dztui/dzgui/changelog.md" @@ -117,10 +118,10 @@ items=( ) } warn(){ - zenity --info --title="DZGUI" --text="$1" --width=500 --icon-name="dialog-warning" 2>/dev/null + $steamsafe_zenity --info --title="DZGUI" --text="$1" --width=500 --icon-name="dialog-warning" 2>/dev/null } info(){ - zenity --info --title="DZGUI" --text="$1" --width=500 2>/dev/null + $steamsafe_zenity --info --title="DZGUI" --text="$1" --width=500 2>/dev/null } set_api_params(){ response=$(curl -s "$api" -H "Authorization: Bearer "$api_key"" -G -d "sort=-players" \ @@ -228,7 +229,7 @@ find_library_folder(){ } file_picker(){ while true; do - local path=$(zenity --file-selection --directory 2>/dev/null) + local path=$($steamsafe_zenity --file-selection --directory 2>/dev/null) if [[ -z "$path" ]]; then return else @@ -245,8 +246,8 @@ file_picker(){ create_config(){ check_pyver while true; do - player_input="$(zenity --forms --add-entry="Player name (required for some servers)" --add-entry="BattleMetrics API key" --add-entry="Steam API key" --title="DZGUI" --text="DZGUI" $sd_res --separator="│" 2>/dev/null)" - #explicitly setting IFS crashes zenity in loop + player_input="$($steamsafe_zenity --forms --add-entry="Player name (required for some servers)" --add-entry="BattleMetrics API key" --add-entry="Steam API key" --title="DZGUI" --text="DZGUI" $sd_res --separator="│" 2>/dev/null)" + #explicitly setting IFS crashes $steamsafe_zenity in loop #and mapfile does not support high ascii delimiters #so split fields with newline readarray -t args < <(echo "$player_input" | sed 's/│/\n/g') @@ -288,7 +289,7 @@ varcheck(){ run_depcheck(){ if [[ -n $(depcheck) ]]; then echo "100" - zenity --warning --ok-label="Exit" --title="DZGUI" --text="$(depcheck)" + $steamsafe_zenity --warning --ok-label="Exit" --title="DZGUI" --text="$(depcheck)" exit fi } @@ -304,8 +305,8 @@ run_varcheck(){ workshop_dir="$steam_path/steamapps/workshop/content/$aid" game_dir="$steam_path/steamapps/common/DayZ" if [[ -n $(varcheck) ]]; then - zenity --warning --width 500 --text="$(varcheck)" 2>/dev/null - zenity --question --cancel-label="Exit" --text="Malformed config file. This is probably user error.\nStart first-time setup process again?" --width=500 2>/dev/null + $steamsafe_zenity --warning --width 500 --text="$(varcheck)" 2>/dev/null + $steamsafe_zenity --question --cancel-label="Exit" --text="Malformed config file. This is probably user error.\nStart first-time setup process again?" --width=500 2>/dev/null code=$? if [[ $code -eq 1 ]]; then exit @@ -318,7 +319,7 @@ run_varcheck(){ config(){ if [[ ! -f $config_file ]]; then #echo "100" - zenity --width 500 --info --text="Config file not found. Click OK to proceed to first-time setup." 2>/dev/null + $steamsafe_zenity --width 500 --info --text="Config file not found. Click OK to proceed to first-time setup." 2>/dev/null code=$? #TODO: prevent progress if user hits ESC if [[ $code -eq 1 ]]; then @@ -334,12 +335,12 @@ config(){ steam_deck_mods(){ until [[ -z $diff ]]; do next=$(echo -e "$diff" | head -n1) - zenity --question --ok-label="Open" --cancel-label="Cancel" --title="DZGUI" --text="Missing mods. Click [Open] to open mod $next in Steam Workshop and subscribe to it by clicking the green Subscribe button. After the mod is downloaded, return to this menu to continue validation." --width=500 2>/dev/null + $steamsafe_zenity --question --ok-label="Open" --cancel-label="Cancel" --title="DZGUI" --text="Missing mods. Click [Open] to open mod $next in Steam Workshop and subscribe to it by clicking the green Subscribe button. After the mod is downloaded, return to this menu to continue validation." --width=500 2>/dev/null rc=$? if [[ $rc -eq 0 ]]; then echo "[DZGUI] Opening ${workshop}$next" steam steam://url/CommunityFilePage/$next 2>/dev/null & - zenity --info --title="DZGUI" --ok-label="Next" --text="Click [Next] to continue mod check." --width=500 2>/dev/null + $steamsafe_zenity --info --title="DZGUI" --ok-label="Next" --text="Click [Next] to continue mod check." --width=500 2>/dev/null else return 1 fi @@ -400,10 +401,10 @@ manual_mod_install(){ done echo "100" } - watcher > >(zenity --pulsate --progress --auto-close --title="DZG Watcher" --width=500 2>/dev/null; rc=$?; [[ $rc -eq 1 ]] && touch $ex) + watcher > >($steamsafe_zenity --pulsate --progress --auto-close --title="DZG Watcher" --width=500 2>/dev/null; rc=$?; [[ $rc -eq 1 ]] && touch $ex) compare if [[ -z $diff ]]; then - passed_mod_check > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) + passed_mod_check > >($steamsafe_zenity --pulsate --progress --auto-close --width=500 2>/dev/null) launch else return 1 @@ -470,12 +471,12 @@ auto_mod_install(){ [[ ! $(xdotool getwindowfocus) -eq $win ]] && xdotool windowraise $win local missing=$(comm -23 <(printf "%s\n" "${modids[@]}" | sort) <(ls -1 $workshop_dir | sort) | wc -l) echo "# Downloaded $((${#modids[@]}-missing)) of ${#modids[@]} mods" - done | zenity --pulsate --progress --title="DZG Watcher" --auto-close --no-cancel --width=500 2>/dev/null + done | $steamsafe_zenity --pulsate --progress --title="DZG Watcher" --auto-close --no-cancel --width=500 2>/dev/null compare [[ $force_update -eq 1 ]] && { unset force_update; return; } if [[ -z $diff ]]; then check_timestamps - passed_mod_check > >(zenity --pulsate --progress --title=DZGUI --auto-close --width=500 2>/dev/null) + passed_mod_check > >($steamsafe_zenity --pulsate --progress --title=DZGUI --auto-close --width=500 2>/dev/null) launch else manual_mod_install @@ -565,7 +566,7 @@ connect(){ ip=$(echo "$1" | awk -F"$separator" '{print $1}') bid=$(echo "$1" | awk -F"$separator" '{print $2}') if [[ $2 == "ip" ]]; then - fetch_mods_sa "$ip" > >(zenity --pulsate --progress --auto-close --no-cancel --width=500 2>/dev/null) + fetch_mods_sa "$ip" > >($steamsafe_zenity --pulsate --progress --auto-close --no-cancel --width=500 2>/dev/null) else fetch_mods "$bid" fi @@ -579,7 +580,7 @@ connect(){ compare [[ $auto_install -eq 2 ]] && merge_modlists if [[ -n $diff ]]; then - [[ -z $(is_steam_running) ]] && { zenity --info --text "Steam must be running on the current desktop to use this feature."; return; } + [[ -z $(is_steam_running) ]] && { $steamsafe_zenity --info --text "Steam must be running on the current desktop to use this feature."; return; } #TODO: headless is deprecated if [[ $auto_install -eq 1 ]]; then headless_mod_install "$diff" @@ -591,7 +592,7 @@ connect(){ manual_mod_install fi else - passed_mod_check > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) + passed_mod_check > >($steamsafe_zenity --pulsate --progress --auto-close --width=500 2>/dev/null) update_history launch fi @@ -614,7 +615,7 @@ fetch_mods_sa(){ if [[ $status -eq 1 ]]; then echo "100" err "97: Failed to fetch modlist" - zenity --error --title="DZGUI" --width=500 --text="[ERROR] 97: Failed to fetch modlist" 2>/dev/null && + $steamsafe_zenity --error --title="DZGUI" --width=500 --text="[ERROR] 97: Failed to fetch modlist" 2>/dev/null && ret=96 return fi @@ -650,10 +651,10 @@ history_table(){ res=$(< $meta_file jq -er '.response.servers[]' 2>/dev/null) prepare_ip_list "$meta_file" >> /tmp/dz.hist sleep 0.5s - done | zenity --pulsate --progress --auto-close --title=DZGUI --width=500 --no-cancel 2>/dev/null + done | $steamsafe_zenity --pulsate --progress --auto-close --title=DZGUI --width=500 --no-cancel 2>/dev/null [[ $? -eq 1 ]] && return while true; do - sel=$(cat /tmp/dz.hist | zenity --width 1200 --height 800 --title=DZGUI --text="Recent servers" --list --column=Name --column=IP --column=Players --column=Gametime --print-column=2 --separator=%% 2>/dev/null) + sel=$(cat /tmp/dz.hist | $steamsafe_zenity --width 1200 --height 800 --title=DZGUI --text="Recent servers" --list --column=Name --column=IP --column=Players --column=Gametime --print-column=2 --separator=%% 2>/dev/null) if [[ $? -eq 1 ]]; then return_from_table=1 rm /tmp/dz.hist @@ -676,7 +677,7 @@ history_table(){ ip_table(){ while true; do - sel=$(prepare_ip_list "$meta_file" | zenity --width 1200 --height 800 --list --column=Name --column=IP --column=Players --column=Gametime --print-column=2 --separator=%% 2>/dev/null) + sel=$(prepare_ip_list "$meta_file" | $steamsafe_zenity --width 1200 --height 800 --list --column=Name --column=IP --column=Players --column=Gametime --print-column=2 --separator=%% 2>/dev/null) if [[ $? -eq 1 ]]; then return_from_table=1 return @@ -728,16 +729,16 @@ add_steam_api(){ steam_api="steam_api=\"$steam_api\"" awk -v "var=$steam_api" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > ${config_path}dztuirc echo "[DZGUI] Added Steam API key" - zenity --info --title="DZGUI" --text="Added Steam API key to:\n\n${config_path}dztuirc\nIf errors occur, you can restore the file:\n${config_path}dztuirc.old" --width=500 2>/dev/null + $steamsafe_zenity --info --title="DZGUI" --text="Added Steam API key to:\n\n${config_path}dztuirc\nIf errors occur, you can restore the file:\n${config_path}dztuirc.old" --width=500 2>/dev/null source $config_file } check_steam_api(){ if [[ -z $steam_api ]]; then - steam_api=$(zenity --entry --text="Key 'steam_api' not present in config file. Enter Steam API key:" --title="DZGUI" 2>/dev/null) + steam_api=$($steamsafe_zenity --entry --text="Key 'steam_api' not present in config file. Enter Steam API key:" --title="DZGUI" 2>/dev/null) if [[ $? -eq 1 ]] ; then return elif [[ ${#steam_api} -lt 32 ]] || [[ $(test_steam_api) -eq 1 ]]; then - zenity --warning --title="DZGUI" --text="Check API key and try again." 2>/dev/null + $steamsafe_zenity --warning --title="DZGUI" --text="Check API key and try again." 2>/dev/null return 1 else add_steam_api @@ -756,7 +757,7 @@ connect_by_ip(){ return_from_table=0 return fi - ip=$(zenity --entry --text="Enter server IP (omit port)" --title="DZGUI" 2>/dev/null) + ip=$($steamsafe_zenity --entry --text="Enter server IP (omit port)" --title="DZGUI" 2>/dev/null) [[ $? -eq 1 ]] && return if validate_ip "$ip"; then fetch_ip_metadata @@ -822,19 +823,19 @@ launch(){ if [[ $debug -eq 1 ]]; then launch_options="steam -applaunch $aid -connect=$ip -nolauncher -nosplash -name=$name -skipintro \"-mod=$mods\"" print_launch_options="$(printf "This is a dry run.\nThese options would have been used to launch the game:\n\n$launch_options\n" | fold -w 60)" - zenity --question --title="DZGUI" --ok-label="Write to file" --cancel-label="Back"\ + $steamsafe_zenity --question --title="DZGUI" --ok-label="Write to file" --cancel-label="Back"\ --text="$print_launch_options" 2>/dev/null if [[ $? -eq 0 ]]; then source_script=$(realpath "$0") source_dir=$(dirname "$source_script") echo "$launch_options" > "$source_dir"/options.log echo "[DZGUI] Wrote launch options to $source_dir/options.log" - zenity --info --width 500 --title="DZGUI" --text="Wrote launch options to \n$source_dir/options.log" 2>/dev/null + $steamsafe_zenity --info --width 500 --title="DZGUI" --text="Wrote launch options to \n$source_dir/options.log" 2>/dev/null fi else echo "[DZGUI] All OK. Launching DayZ" - zenity --width 500 --title="DZGUI" --info --text="Launch conditions satisfied.\nDayZ will now launch after clicking [OK]." 2>/dev/null + $steamsafe_zenity --width 500 --title="DZGUI" --info --text="Launch conditions satisfied.\nDayZ will now launch after clicking [OK]." 2>/dev/null steam -applaunch $aid -connect=$ip -nolauncher -nosplash -skipintro -name=$name \"-mod=$mods\" exit fi @@ -884,14 +885,14 @@ delete_by_id(){ nr=$(awk '/whitelist=/ {print NR}' ${config_path}dztuirc.old) awk -v "var=$new_whitelist" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > ${config_path}dztuirc echo "[DZGUI] Removed $server_id from key 'whitelist'" - zenity --info --title="DZGUI" --text="Removed "$server_id" from:\n${config_path}dztuirc\nIf errors occur, you can restore the file:\n${config_path}dztuirc.old" --width=500 2>/dev/null + $steamsafe_zenity --info --title="DZGUI" --text="Removed "$server_id" from:\n${config_path}dztuirc\nIf errors occur, you can restore the file:\n${config_path}dztuirc.old" --width=500 2>/dev/null source $config_file } delete_or_connect(){ if [[ $delete -eq 1 ]]; then server_name=$(echo "$sel" | awk -F"%%" '{print $1}') server_id=$(echo "$sel" | awk -F"%%" '{print $2}') - zenity --question --text="Delete this server? \n$server_name" --title=DZGUI --width=500 2>/dev/null + $steamsafe_zenity --question --text="Delete this server? \n$server_name" --title=DZGUI --width=500 2>/dev/null if [[ $? -eq 0 ]]; then delete_by_id $server_id fi @@ -935,7 +936,7 @@ populate(){ } list_mods(){ if [[ -z $(installed_mods) ]] || [[ -z $(find $workshop_dir -maxdepth 2 -name "*.cpp" | grep .cpp) ]]; then - zenity --info --text="94: No mods currently installed or incorrect path given" $sd_res 2>/dev/null + $steamsafe_zenity --info --text="94: No mods currently installed or incorrect path given" $sd_res 2>/dev/null else for d in $(find $game_dir/* -maxdepth 1 -type l); do dir=$(basename $d) @@ -964,13 +965,13 @@ set_header(){ [[ $auto_install -eq 1 ]] && install_mode="headless" [[ $auto_install -eq 0 ]] && install_mode=manual if [[ $1 == "delete" ]]; then - sel=$(cat $tmp | zenity $sd_res --list $cols --title="DZGUI" --text="DZGUI $version | Mode: $mode | Branch: $branch | Mods: $install_mode | Fav: $fav_label" \ + sel=$(cat $tmp | $steamsafe_zenity $sd_res --list $cols --title="DZGUI" --text="DZGUI $version | Mode: $mode | Branch: $branch | Mods: $install_mode | Fav: $fav_label" \ --separator="$separator" --print-column=1,2 --ok-label="Delete" 2>/dev/null) elif [[ $1 == "populate" ]]; then - sel=$(cat $tmp | zenity $sd_res --list $cols --title="DZGUI" --text="DZGUI $version | Mode: $mode | Branch: $branch | Mods: $install_mode | Fav: $fav_label" \ + sel=$(cat $tmp | $steamsafe_zenity $sd_res --list $cols --title="DZGUI" --text="DZGUI $version | Mode: $mode | Branch: $branch | Mods: $install_mode | Fav: $fav_label" \ --separator="$separator" --print-column=2,6 2>/dev/null) elif [[ $1 == "main_menu" ]]; then - sel=$(zenity $sd_res --list --title="DZGUI" --text="${news}DZGUI $version | Mode: $mode | Branch: $branch | Mods: $install_mode | Fav: $fav_label" \ + sel=$($steamsafe_zenity $sd_res --list --title="DZGUI" --text="${news}DZGUI $version | Mode: $mode | Branch: $branch | Mods: $install_mode | Fav: $fav_label" \ --cancel-label="Exit" --ok-label="Select" --column="Select launch option" --hide-header "${items[@]}" 2>/dev/null) fi } @@ -1034,9 +1035,9 @@ find_default_path(){ elif [[ -d "$HOME/.steam/steam" ]]; then default_steam_path="$HOME/.steam/steam" else - local res=$(echo -e "Let DZGUI auto-discover Steam path (accurate, slower)\nSelect the Steam path manually (less accurate, faster)" | zenity --list --column="Choice" --title=DZGUI --hide-header --text="Steam is not installed in a standard location." $sd_res) + local res=$(echo -e "Let DZGUI auto-discover Steam path (accurate, slower)\nSelect the Steam path manually (less accurate, faster)" | $steamsafe_zenity --list --column="Choice" --title=DZGUI --hide-header --text="Steam is not installed in a standard location." $sd_res) case "$res" in - *auto*) discover > >(zenity --width 500 --progress --auto-close --title="DZGUI" --pulsate --no-cancel 2>/dev/null) ;; + *auto*) discover > >($steamsafe_zenity --width 500 --progress --auto-close --title="DZGUI" --pulsate --no-cancel 2>/dev/null) ;; *manual*) file_picker ;; esac fi @@ -1044,7 +1045,7 @@ find_default_path(){ } popup(){ pop(){ - zenity --info --text="$1" --title=DZGUI --width=500 2>/dev/null + $steamsafe_zenity --info --text="$1" --title=DZGUI --width=500 2>/dev/null } case "$1" in 100) pop "This feature requires xdotool.";; @@ -1092,7 +1093,7 @@ options_menu(){ "Toggle auto mod install [$auto_hr]" ) [[ $auto_install -eq 2 ]] && debug_list+=("Force update local mods") - debug_sel=$(zenity --list --width=1280 --height=800 --column="Options" --title="DZGUI" --hide-header "${debug_list[@]}" 2>/dev/null) + debug_sel=$($steamsafe_zenity --list --width=1280 --height=800 --column="Options" --title="DZGUI" --hide-header "${debug_list[@]}" 2>/dev/null) if [[ $debug_sel == "${debug_list[0]}" ]]; then enforce_dl=1 toggle_branch && @@ -1104,14 +1105,14 @@ options_menu(){ source_dir=$(dirname "$source_script") generate_log > "$source_dir/DZGUI.log" printf "[DZGUI] Wrote log file to %s/log\n" "$source_dir" - zenity --info --width 500 --title="DZGUI" --text="Wrote log file to \n$source_dir/log" 2>/dev/null + $steamsafe_zenity --info --width 500 --title="DZGUI" --text="Wrote log file to \n$source_dir/log" 2>/dev/null elif [[ $debug_sel == "${debug_list[3]}" ]]; then toggle_console_dl elif [[ $debug_sel == "${debug_list[4]}" ]]; then force_update=1 force_update_mods - merge_modlists > >(zenity --pulsate --progress --auto-close --title=DZGUI --width=500 2>/dev/null) - [[ -z $(is_steam_running) ]] && { zenity --info --text "Steam must be running on the current desktop to use this feature."; return; } + merge_modlists > >($steamsafe_zenity --pulsate --progress --auto-close --title=DZGUI --width=500 2>/dev/null) + [[ -z $(is_steam_running) ]] && { $steamsafe_zenity --info --text "Steam must be running on the current desktop to use this feature."; return; } auto_mod_install fi } @@ -1122,7 +1123,7 @@ query_and_connect(){ #TODO: create logger function if [[ ! $delete -eq 1 ]]; then echo "[DZGUI] Checking response time of servers" - create_array | zenity --width 500 --progress --pulsate --title="DZGUI" --auto-close 2>/dev/null + create_array | $steamsafe_zenity --width 500 --progress --pulsate --title="DZGUI" --auto-close 2>/dev/null else create_array fi @@ -1144,7 +1145,7 @@ filter_maps(){ [[ $ret -eq 98 ]] && return local maps=$(echo "$response" | jq -r '.[].map//empty|ascii_downcase' | sort -u) local map_ct=$(echo "$maps" | wc -l) - local map_sel=$(echo "$maps" | zenity --list --column="Check" --width=1200 --height=800 2>/dev/null --title="DZGUI" --text="Found $map_ct map types") + local map_sel=$(echo "$maps" | $steamsafe_zenity --list --column="Check" --width=1200 --height=800 2>/dev/null --title="DZGUI" --text="Found $map_ct map types") echo "[DZGUI] Selected '$map_sel'" if [[ -z $map_sel ]]; then ret=97 @@ -1233,16 +1234,16 @@ check_geo_file(){ chmod +x $km_helper echo "100" } - run > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) + run > >($steamsafe_zenity --pulsate --progress --auto-close --width=500 2>/dev/null) fi } choose_filters(){ if [[ $is_steam_deck -eq 0 ]]; then sd_res="--width=1920 --height=1080" fi - sels=$(zenity --title="DZGUI" --text="Server search" --list --checklist --column "Check" --column "Option" --hide-header TRUE "All maps (untick to select from map list)" TRUE "Daytime" TRUE "Nighttime" False "Empty" False "Full" False "Low population" FALSE "Non-ASCII titles" FALSE "Keyword search" $sd_res 2>/dev/null) + sels=$($steamsafe_zenity --title="DZGUI" --text="Server search" --list --checklist --column "Check" --column "Option" --hide-header TRUE "All maps (untick to select from map list)" TRUE "Daytime" TRUE "Nighttime" False "Empty" False "Full" False "Low population" FALSE "Non-ASCII titles" FALSE "Keyword search" $sd_res 2>/dev/null) if [[ $sels =~ Keyword ]]; then - search=$(zenity --entry --text="Search (case insensitive)" --width=500 --title="DZGUI" 2>/dev/null | awk '{print tolower($0)}') + search=$($steamsafe_zenity --entry --text="Search (case insensitive)" --width=500 --title="DZGUI" 2>/dev/null | awk '{print tolower($0)}') [[ -z $search ]] && { ret=97; return; } fi [[ -z $sels ]] && return @@ -1288,13 +1289,13 @@ write_fifo(){ } munge_servers(){ if [[ ! "$sels" =~ "All maps" ]]; then - filter_maps > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) + filter_maps > >($steamsafe_zenity --pulsate --progress --auto-close --width=500 2>/dev/null) disabled+=("All maps") fi [[ $ret -eq 97 ]] && return - prepare_filters > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) + prepare_filters > >($steamsafe_zenity --pulsate --progress --auto-close --width=500 2>/dev/null) if [[ $(echo "$response" | jq 'length') -eq 0 ]]; then - zenity --error --text="No matching servers" 2>/dev/null + $steamsafe_zenity --error --text="No matching servers" 2>/dev/null return fi local addr=$(echo "$response" | jq -r '.[].addr' | awk -F: '{print $1}') @@ -1320,7 +1321,7 @@ munge_servers(){ write_fifo & pid=$! - local sel=$(zenity --text="$(pagination)" --title="DZGUI" --list --column=Map --column=Name --column=Gametime --column=Players --column=Max --column=Distance --column=IP --column=Qport $sd_res --print-column=7,8 --separator=%% 2>/dev/null < <(while true; do cat $fifo; done)) + local sel=$($steamsafe_zenity --text="$(pagination)" --title="DZGUI" --list --column=Map --column=Name --column=Gametime --column=Players --column=Max --column=Distance --column=IP --column=Qport $sd_res --print-column=7,8 --separator=%% 2>/dev/null < <(while true; do cat $fifo; done)) if [[ -z $sel ]]; then rm $fifo kill -9 $pid @@ -1375,7 +1376,7 @@ server_browser(){ echo "# Getting server list" response=$(curl -Ls "$url" | jq -r '.response.servers') } - fetch > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) + fetch > >($steamsafe_zenity --pulsate --progress --auto-close --width=500 2>/dev/null) total_servers=$(echo "$response" | jq 'length' | numfmt --grouping) players_online=$(echo "$response" | jq '.[].players' | awk '{s+=$1}END{print s}' | numfmt --grouping) debug_log="$HOME/.local/share/dzgui/DEBUG.log" @@ -1438,11 +1439,11 @@ main_menu(){ elif [[ $sel == "${items[10]}" ]]; then : elif [[ $sel == "${items[11]}" ]]; then - list_mods | sed 's/\t/\n/g' | zenity --list --column="Mod" --column="Symlink" \ + list_mods | sed 's/\t/\n/g' | $steamsafe_zenity --list --column="Mod" --column="Symlink" \ --title="DZGUI" $sd_res --text="$(mods_disk_size)" \ --print-column="" 2>/dev/null elif [[ $sel == "${items[12]}" ]]; then - changelog | zenity --text-info $sd_res --title="DZGUI" 2>/dev/null + changelog | $steamsafe_zenity --text-info $sd_res --title="DZGUI" 2>/dev/null elif [[ $sel == "${items[13]}" ]]; then options_menu main_menu @@ -1551,7 +1552,7 @@ merge_config(){ [[ -z $staging_dir ]] && staging_dir="/tmp" write_config > $config_file printf "[DZGUI] Wrote new config file to %sdztuirc\n" $config_path - zenity --info --width 500 --title="DZGUI" --text="Wrote new config format to \n${config_path}dztuirc\nIf errors occur, you can restore the file:\n${config_path}dztuirc.old" 2>/dev/null + $steamsafe_zenity --info --width 500 --title="DZGUI" --text="Wrote new config format to \n${config_path}dztuirc\nIf errors occur, you can restore the file:\n${config_path}dztuirc.old" 2>/dev/null } download_new_version(){ @@ -1569,10 +1570,10 @@ download_new_version(){ chmod +x $source_script touch ${config_path}.unmerged echo "100" - zenity --question --width 500 --title="DZGUI" --text "DZGUI $upstream successfully downloaded.\nTo view the changelog, select Changelog.\nTo use the new version, select Exit and restart." --ok-label="Changelog" --cancel-label="Exit" 2>/dev/null + $steamsafe_zenity --question --width 500 --title="DZGUI" --text "DZGUI $upstream successfully downloaded.\nTo view the changelog, select Changelog.\nTo use the new version, select Exit and restart." --ok-label="Changelog" --cancel-label="Exit" 2>/dev/null code=$? if [[ $code -eq 0 ]]; then - changelog | zenity --text-info $sd_res --title="DZGUI" 2>/dev/null + changelog | $steamsafe_zenity --text-info $sd_res --title="DZGUI" 2>/dev/null exit elif [[ $code -eq 1 ]]; then exit @@ -1580,7 +1581,7 @@ download_new_version(){ else echo "100" mv $source_script.old $source_script - zenity --info --title="DZGUI" --text "[ERROR] 99: Failed to download new version." 2>/dev/null + $steamsafe_zenity --info --title="DZGUI" --text "[ERROR] 99: Failed to download new version." 2>/dev/null return fi @@ -1594,16 +1595,16 @@ check_branch(){ upstream=$(curl -Ls "$version_url" | awk -F= '/^version=/ {print $2}') } enforce_dl(){ - download_new_version > >(zenity --progress --pulsate --auto-close --no-cancel --width=500) + download_new_version > >($steamsafe_zenity --progress --pulsate --auto-close --no-cancel --width=500) } prompt_dl(){ - zenity --question --title="DZGUI" --text "Version conflict.\n\nYour branch:\t\t\t$branch\nYour version\t\t\t$version\nUpstream version:\t\t$upstream\n\nVersion updates introduce important bug fixes and are encouraged.\n\nAttempt to download latest version?" --width=500 --ok-label="Yes" --cancel-label="No" 2>/dev/null + $steamsafe_zenity --question --title="DZGUI" --text "Version conflict.\n\nYour branch:\t\t\t$branch\nYour version\t\t\t$version\nUpstream version:\t\t$upstream\n\nVersion updates introduce important bug fixes and are encouraged.\n\nAttempt to download latest version?" --width=500 --ok-label="Yes" --cancel-label="No" 2>/dev/null rc=$? if [[ $rc -eq 1 ]]; then return else echo "100" - download_new_version > >(zenity --progress --pulsate --auto-close --no-cancel --width=500) + download_new_version > >($steamsafe_zenity --progress --pulsate --auto-close --no-cancel --width=500) fi } check_version(){ @@ -1636,13 +1637,13 @@ check_architecture(){ add_by_id(){ #FIXME: prevent redundant creation of existent IDs (for neatness) while true; do - id=$(zenity --entry --text="Enter server ID" --title="DZGUI" 2>/dev/null) + id=$($steamsafe_zenity --entry --text="Enter server ID" --title="DZGUI" 2>/dev/null) rc=$? if [[ $rc -eq 1 ]]; then return else if [[ ! $id =~ ^[0-9]+$ ]]; then - zenity --warning --title="DZGUI" --text="Invalid ID" 2>/dev/null + $steamsafe_zenity --warning --title="DZGUI" --text="Invalid ID" 2>/dev/null else [[ -z $whitelist ]] && new_whitelist="whitelist=\"$id\"" [[ -n $whitelist ]] && new_whitelist="whitelist=\"$whitelist,$id\"" @@ -1650,7 +1651,7 @@ add_by_id(){ nr=$(awk '/whitelist=/ {print NR}' ${config_path}dztuirc.old) awk -v "var=$new_whitelist" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > ${config_path}dztuirc echo "[DZGUI] Added $id to key 'whitelist'" - zenity --info --title="DZGUI" --text="Added "$id" to:\n${config_path}dztuirc\nIf errors occur, you can restore the file:\n${config_path}dztuirc.old" --width=500 2>/dev/null + $steamsafe_zenity --info --title="DZGUI" --text="Added "$id" to:\n${config_path}dztuirc\nIf errors occur, you can restore the file:\n${config_path}dztuirc.old" --width=500 2>/dev/null source $config_file return fi @@ -1682,9 +1683,9 @@ check_map_count(){ echo "[DZGUI] Checking system map count" if [[ $(sysctl -q vm.max_map_count | awk -F"= " '{print $2}') -lt $count ]]; then echo "100" - map_warning=$(zenity --question --width 500 --title="DZGUI" --text "System map count must be $count or higher to run DayZ with Wine. Increase map count and make this change permanent? (will prompt for sudo password)" 2>/dev/null) + map_warning=$($steamsafe_zenity --question --width 500 --title="DZGUI" --text "System map count must be $count or higher to run DayZ with Wine. Increase map count and make this change permanent? (will prompt for sudo password)" 2>/dev/null) if [[ $? -eq 0 ]]; then - pass=$(zenity --password) + pass=$($steamsafe_zenity --password) sudo -S <<< "$pass" sh -c "echo 'vm.max_map_count=1048576' > /etc/sysctl.d/dayz.conf" echo "" @@ -1693,20 +1694,20 @@ check_map_count(){ } add_by_fav(){ while true; do - fav_id=$(zenity --entry --text="Enter server ID" --title="DZGUI" 2>/dev/null) + fav_id=$($steamsafe_zenity --entry --text="Enter server ID" --title="DZGUI" 2>/dev/null) rc=$? if [[ $rc -eq 1 ]]; then return else if [[ ! $fav_id =~ ^[0-9]+$ ]]; then - zenity --warning --title="DZGUI" --text="Invalid ID" + $steamsafe_zenity --warning --title="DZGUI" --text="Invalid ID" else new_fav="fav=\"$fav_id\"" mv $config_file ${config_path}dztuirc.old nr=$(awk '/fav=/ {print NR}' ${config_path}dztuirc.old) awk -v "var=$new_fav" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > ${config_path}dztuirc echo "[DZGUI] Added $fav_id to key 'fav'" - zenity --info --title="DZGUI" --text="Added "$fav_id" to:\n${config_path}dztuirc\nIf errors occurred, you can restore the file:\n${config_path}dztuirc.old" 2>/dev/null + $steamsafe_zenity --info --title="DZGUI" --text="Added "$fav_id" to:\n${config_path}dztuirc\nIf errors occurred, you can restore the file:\n${config_path}dztuirc.old" 2>/dev/null source $config_file set_fav items[8]=" Change favorite server" @@ -1724,7 +1725,7 @@ lock(){ res=$? if [[ $res -eq 0 ]]; then echo "[DZGUI] Already running ($pid)" - zenity --info --text="DZGUI already running (pid $pid)" --width=500 --title="DZGUI" 2>/dev/null + $steamsafe_zenity --info --text="DZGUI already running (pid $pid)" --width=500 --title="DZGUI" 2>/dev/null exit elif [[ $pid == $$ ]]; then : @@ -1753,10 +1754,9 @@ initial_setup(){ } main(){ lock - initial_setup > >(zenity --pulsate --progress --auto-close --title="DZGUI" --no-cancel --width=500 2>/dev/null) + initial_setup > >($steamsafe_zenity --pulsate --progress --auto-close --title="DZGUI" --no-cancel --width=500 2>/dev/null) main_menu #cruddy handling for steam forking [[ $? -eq 1 ]] && pkill -f dzgui.sh } - main From 9ffa148534371c873d30374aac5daf7cb698a797 Mon Sep 17 00:00:00 2001 From: aclist Date: Sat, 3 Dec 2022 13:05:12 +0900 Subject: [PATCH 032/144] Hotfix for /run path --- dzgui.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dzgui.sh b/dzgui.sh index 4eb0a51..c499c8c 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=3.1.0-rc.19 +version=3.1.0-rc.20 aid=221100 game="dayz" @@ -1020,7 +1020,7 @@ find_default_path(){ discover(){ echo "# Searching for Steam" default_steam_path=$(find / -type d \( -path "/proc" -o -path "*/timeshift" -o -path \ - "/tmp" -o -path "/usr" -o -path "/boot" -o -path "/run" -o -path "/proc" -o -path "/root" \ + "/tmp" -o -path "/usr" -o -path "/boot" -o -path "/proc" -o -path "/root" \ -o -path "/sys" -o -path "/etc" -o -path "/var" -o -path "/lost+found" \) -prune \ -o -regex ".*/Steam/ubuntu12_32$" -print -quit 2>/dev/null | sed 's@/ubuntu12_32@@') echo "100" @@ -1498,7 +1498,6 @@ check_ping(){ } create_array(){ rows=() - list=$(cat $tmp) #TODO: improve error handling for null values lc=1 while read line; do @@ -1525,7 +1524,7 @@ create_array(){ declare -g -a rows=("${rows[@]}" "$name" "$ip" "$players" "$time" "$stat" "$id" "$ping") fi let lc++ - done <<< "$list" + done < "$tmp" for i in "${rows[@]}"; do echo -e "$i"; done > $tmp } From 978e8a36b26264db2897c2637ecf307c6749730b Mon Sep 17 00:00:00 2001 From: aclist Date: Sat, 3 Dec 2022 14:13:35 +0900 Subject: [PATCH 033/144] Update changelog --- changelog.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/changelog.md b/changelog.md index e40c68a..1c82973 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,15 @@ - Custom query API - Standardize dialogs + +## [3.1.0-rc.20] 2022-12-02 +### Fixed +- Include /run in auto-discovery routine + +## [3.1.0-rc.19] 2022-12-02 +### Fixed +- Use Steam-safe local zenity version + ## [3.1.0-rc.18] 2022-12-01 ### Added - Thousands separator to player counts From 5c17531654fe4ebb462e2893436a1d96bfac1de4 Mon Sep 17 00:00:00 2001 From: aclist Date: Sat, 3 Dec 2022 14:16:24 +0900 Subject: [PATCH 034/144] Clean up stale files --- dzgui.sh.old | 1698 ----------------------------------------------- helpers/d.html | 5 - helpers/d.webp | Bin 3067676 -> 0 bytes helpers/scmd.sh | 361 ---------- 4 files changed, 2064 deletions(-) delete mode 100755 dzgui.sh.old delete mode 100644 helpers/d.html delete mode 100644 helpers/d.webp delete mode 100644 helpers/scmd.sh diff --git a/dzgui.sh.old b/dzgui.sh.old deleted file mode 100755 index 1934f87..0000000 --- a/dzgui.sh.old +++ /dev/null @@ -1,1698 +0,0 @@ -#!/bin/bash - -set -o pipefail -version=2.7.1 - -aid=221100 -game="dayz" -workshop="steam://url/CommunityFilePage/" -api="https://api.battlemetrics.com/servers" -sd_res="--width=1280 --height=800" -config_path="$HOME/.config/dztui/" -config_file="${config_path}dztuirc" -tmp=/tmp/dzgui.tmp -fifo=/tmp/table.tmp -separator="%%" -check_config_msg="Check config values and restart." -issues_url="https://github.com/aclist/dztui/issues" -url_prefix="https://raw.githubusercontent.com/aclist/dztui" -stable_url="$url_prefix/dzgui" -testing_url="$url_prefix/testing" -releases_url="https://github.com/aclist/dztui/releases/download/browser" -help_url="https://aclist.github.io/dzgui/dzgui" -freedesktop_path="$HOME/.local/share/applications" -sd_install_path="$HOME/.local/share/dzgui" -helpers_path="$sd_install_path/helpers" -geo_file="$helpers_path/ips.csv" -km_helper="$helpers_path/latlon" -sums_path="$helpers_path/sums.md5" -scmd_file="$helpers_path/scmd.sh" -km_helper_url="$releases_url/latlon" -db_file="$releases_url/ips.csv.gz" -sums_url="$stable_url/helpers/sums.md5" -scmd_url="$stable_url/helpers/scmd.sh" -notify_url="$stable_url/helpers/d.html" -notify_img_url="$stable_url/helpers/d.webp" -forum_url="https://github.com/aclist/dztui/discussions" - -update_last_seen(){ - mv $config_file ${config_path}dztuirc.old - nr=$(awk '/seen_news=/ {print NR}' ${config_path}dztuirc.old) - seen_news="seen_news=\"$sum\"" - awk -v "var=$seen_news" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > $config_file - printf "[DZGUI] Updated last seen news item to '$sum'\n" - source $config_file -} -check_news(){ - [[ $branch == "stable" ]] && news_url="$stable_url/news" - [[ $branch == "testing" ]] && news_url="$testing_url/news" - result=$(curl -Ls "$news_url") - sum=$(echo -n "$result" | md5sum | awk '{print $1}') -} -print_news(){ - check_news - if [[ $sum == $seen_news || -z $result ]]; then - hchar="" - news="" - else - hchar="─" - news="$result\n$(awk -v var="$hchar" 'BEGIN{for(c=0;c<90;c++) printf var;}')\n" - update_last_seen - fi -} -#TODO: prevent connecting to offline servers -#TODO: abstract zenity title params and dimensions - -declare -A deps -deps=([awk]="5.1.1" [curl]="7.80.0" [jq]="1.6" [tr]="9.0" [zenity]="3.42.1" [steam]="1.0.0") -changelog(){ - if [[ $branch == "stable" ]]; then - md="https://raw.githubusercontent.com/aclist/dztui/dzgui/changelog.md" - else - md="https://raw.githubusercontent.com/aclist/dztui/testing/changelog.md" - fi - prefix="This window can be scrolled." - echo $prefix - echo "" - curl -Ls "$md" | awk '/Unreleased/ {flag=1}flag' -} - -depcheck(){ - for dep in "${!deps[@]}"; do - command -v $dep 2>&1>/dev/null || (printf "Requires %s >=%s\n" $dep ${deps[$dep]}; exit 1) - done -} -watcher_deps(){ - if [[ ! $(command -v wmctrl) ]] && [[ ! $(command -v xdotool) ]]; then - echo "100" - warn "Missing dependency: requires 'wmctrl' or 'xdotool'.\nInstall from your system's package manager." - exit 1 - fi -} -init_items(){ - #array order determines menu selector; this is destructive -items=( - "[Connect]" - " Server browser" - " My servers" - " Quick connect to favorite server" - "[Manage servers]" - " Connect by IP" - " Add server by ID" - " Add favorite server" - " Delete server" - "[Options]" - " List installed mods" - " View changelog" - " Advanced options" - "[Help]" - " Help file ⧉" - " Report bug ⧉" - " Become a beta tester ⧉" - ) -} -warn_and_exit(){ - zenity --info --title="DZGUI" --text="$1" --width=500 --icon-name="dialog-warning" 2>/dev/null - printf "[DZGUI] %s\n" "$check_config_msg" - exit -} -warn(){ - zenity --info --title="DZGUI" --text="$1" --width=500 --icon-name="dialog-warning" 2>/dev/null -} -info(){ - zenity --info --title="DZGUI" --text="$1" --width=500 2>/dev/null -} -set_api_params(){ - response=$(curl -s "$api" -H "Authorization: Bearer "$api_key"" -G -d "sort=-players" \ - -d "filter[game]=$game" -d "filter[ids][whitelist]=$list_of_ids") - list_response=$response - first_entry=1 -} -query_api(){ - #TODO: prevent drawing list if null values returned without API error - if [[ $one_shot_launch -eq 1 ]]; then - list_of_ids="$fav" - else - if [[ -n $fav ]]; then - list_of_ids="$whitelist,$fav" - else - list_of_ids="$whitelist" - fi - fi - set_api_params - if [[ "$(jq -r 'keys[]' <<< "$response")" == "errors" ]]; then - code=$(jq -r '.errors[] .status' <<< $response) - #TODO: fix granular api codes - if [[ $code -eq 401 ]]; then - warn "Error $code: malformed API key" - return - elif [[ $code -eq 500 ]]; then - warn "Error $code: malformed server list" - return - fi - - fi - if [[ -z $(echo $response | jq '.data[]') ]]; then - warn "95: API returned empty response. Check config file." - return - fi -} -write_config(){ -cat <<-END -#Path to DayZ installation -steam_path="$steam_path" - -#Your unique API key -api_key="$api_key" - -#Comma-separated list of server IDs -whitelist="$whitelist" - -#Favorite server to fast-connect to (limit one) -fav="$fav" - -#Custom player name (optional, required by some servers) -name="$name" - -#Set to 1 to perform dry-run and print launch options -debug="$debug" - -#Toggle stable/testing branch -branch="$branch" - -#Last seen news item -seen_news="$seen_news" - -#Steam API key -steam_api="$steam_api" - -#Terminal emulator -term="$term" - -#Auto-install mods -auto_install="$auto_install" - -#Automod staging directory -staging_dir="$staging_dir" - END -} -write_desktop_file(){ -cat <<-END -[Desktop Entry] -Version=1.0 -Type=Application -Terminal=false -Exec=$sd_install_path/dzgui.sh -Name=DZGUI -Comment=dzgui -Icon=$sd_install_path/dzgui -Categories=Game - END -} -freedesktop_dirs(){ - mkdir -p "$sd_install_path" - mkdir -p "$freedesktop_path" - #TODO: update url - curl -s "$version_url" > "$sd_install_path/dzgui.sh" - chmod +x "$sd_install_path/dzgui.sh" - img_url="https://raw.githubusercontent.com/aclist/dztui/testing/images" - for i in dzgui grid.png hero.png logo.png; do - curl -s "$img_url/$i" > "$sd_install_path/$i" - done - write_desktop_file > "$freedesktop_path/dzgui.desktop" - if [[ $is_steam_deck -eq 1 ]]; then - write_desktop_file > "$HOME/Desktop/dzgui.desktop" - fi -} -file_picker(){ - while true; do - local path=$(zenity --file-selection --directory 2>/dev/null) - if [[ -z "$path" ]]; then - return - else - echo "[DZGUI]" Set mod staging path to "$path" - staging_dir="$path" - write_config > $config_file - return - fi - done -} -guess_path(){ - echo "# Checking for default DayZ path" - path=$(find $HOME -type d -regex ".*/steamapps/common/DayZ$" -print -quit) - if [[ -n "$path" ]]; then - clean_path=$(echo -e "$path" | awk -F"/steamapps" '{print $1}') - steam_path="$clean_path" - else - echo "# Searching for alternate DayZ path. This may take some time." - path=$(find / -type d \( -path "/proc" -o -path "*/timeshift" -o -path "/tmp" -o -path "/usr" -o -path "/boot" -o -path "/proc" -o -path "/root" -o -path "/run" -o -path "/sys" -o -path "/etc" -o -path "/var" -o -path "/run" -o -path "/lost+found" \) -prune -o -regex ".*/steamapps/common/DayZ$" -print -quit 2>/dev/null) - clean_path=$(echo -e "$path" | awk -F"/steamapps" '{print $1}') - steam_path="$clean_path" - fi -} -create_config(){ - while true; do - player_input="$(zenity --forms --add-entry="Player name (required for some servers)" --add-entry="API key" --add-entry="Server 1 (you can add more later)" --title="DZGUI" --text="DZGUI" --add-entry="Server 2" --add-entry="Server 3" --add-entry="Server 4" $sd_res --separator="│" 2>/dev/null)" - #explicitly setting IFS crashes zenity in loop - #and mapfile does not support high ascii delimiters - #so split fields with newline - readarray -t args < <(echo "$player_input" | sed 's/│/\n/g') - name="${args[0]}" - api_key="${args[1]}" - server_1="${args[2]}" - server_2="${args[3]}" - server_3="${args[4]}" - server_4="${args[5]}" - - [[ -z $player_input ]] && exit - if [[ -z $api_key ]]; then - warn "API key: invalid value" - elif [[ -z $server_1 ]]; then - warn "Server 1: cannot be empty" - elif [[ ! $server_1 =~ ^[0-9]+$ ]]; then - warn "Server 1: only numeric IDs" - elif [[ -n $server_2 ]] && [[ ! $server_2 =~ ^[0-9]+$ ]]; then - warn "Server 2: only numeric IDs" - elif [[ -n $server_3 ]] && [[ ! $server_3 =~ ^[0-9]+$ ]]; then - warn "Server 3: only numeric IDs" - elif [[ -n $server_4 ]] && [[ ! $server_3 =~ ^[0-9]+$ ]]; then - warn "Server 4: only numeric IDs" - else - whitelist=$(echo "$player_input" | awk -F"│" '{OFS=","}{print $3,$4,$5,$6}' | sed 's/,*$//g' | sed 's/^,*//g') - guess_path > >(zenity --width 500 --progress --auto-close --pulsate 2>/dev/null) && - echo "[DZGUI] Set path to $steam_path" - #FIXME: tech debt: gracefully exit if user cancels search process - mkdir -p $config_path; write_config > $config_file - info "Config file created at $config_file." - return - fi - done - -} -err(){ - printf "[ERROR] %s\n" "$1" -} -varcheck(){ - [[ -z $api_key ]] && (err "Error in key: 'api_key'") - [[ -z $whitelist ]] && (err "Error in key: 'whitelist'") - [[ ! -d "$game_dir" ]] && (err "Malformed game path") - [[ $whitelist =~ [[:space:]] ]] && (err "Separate whitelist values with commas") -} -run_depcheck(){ - if [[ -z $(depcheck) ]]; then - : - else - echo "100" - zenity --warning --ok-label="Exit" --title="DZGUI" --text="$(depcheck)" - exit - fi -} -run_varcheck(){ - source $config_file - workshop_dir="$steam_path/steamapps/workshop/content/$aid" - game_dir="$steam_path/steamapps/common/DayZ" - if [[ -z $(varcheck) ]]; then - : - else - zenity --warning --width 500 --text="$(varcheck)" 2>/dev/null - printf "[DZGUI] %s\n" "$check_config_msg" - zenity --question --cancel-label="Exit" --text="Malformed config file. This is probably user error.\nStart first-time setup process again?" --width=500 2>/dev/null - code=$? - if [[ $code -eq 1 ]]; then - exit - else - echo "100" - create_config - fi - fi -} -config(){ - if [[ ! -f $config_file ]]; then - zenity --width 500 --info --text="Config file not found. Click OK to proceed to first-time setup." 2>/dev/null - code=$? - #prevent progress if user hits ESC - if [[ $code -eq 1 ]]; then - exit - else - create_config - fi - else - source $config_file - fi - -} -open_mod_links(){ - link_file=$(mktemp) - echo "" > $link_file - echo "DZGUI" >> $link_file - echo "

DZGUI

" >> $link_file - echo "

Open these links and subscribe to them on the Steam Workshop, then continue with the application prompts.
Note: it may take some time for mods to synchronize before DZGUI can see them.
It can help to have Steam in an adjacent window so that you can see the downloads completing.

" >> $link_file - n=1 - for i in $diff; do - echo "$n. ${workshop}$i
" - let n++ - done >> $link_file - echo "" >> $link_file - browser "$link_file" 2>/dev/null & - -} -steam_deck_mods(){ - until [[ -z $diff ]]; do - next=$(echo -e "$diff" | head -n1) - zenity --question --ok-label="Open" --cancel-label="Cancel" --title="DZGUI" --text="Missing mods. Click [Open] to open mod $next in Steam Workshop and subscribe to it by clicking the green Subscribe button. After the mod is downloaded, return to this menu to continue validation." --width=500 2>/dev/null - rc=$? - if [[ $rc -eq 0 ]]; then - echo "[DZGUI] Opening ${workshop}$next" - steam steam://url/CommunityFilePage/$next 2>/dev/null & - zenity --info --title="DZGUI" --ok-label="Next" --text="Click [Next] to continue mod check." --width=500 2>/dev/null - else - return 1 - fi - compare - done -} -set_term(){ - local term="$1" - local tterm="term=\"$term\"" - mv $config_file ${config_path}dztuirc.old - nr=$(awk '/term=/ {print NR}' ${config_path}dztuirc.old) - awk -v "var=$tterm" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > $config_file - printf "[DZGUI] Set term to '$term'\n" - source $config_file -} -sel_term(){ - #only terminals known to support -e flag - for i in "$TERMINAL" urxvt alacritty konsole gnome-terminal terminator xfce4-terminal xterm tilix; do - [[ $(command -v $i) ]] && terms+=($i) - done - #FIXME: if no terms, error - local terms=$(printf "%s\n" "${terms[@]}" | sort -u) - term=$(echo "$terms" | zenity --list --column=Terminal --height=800 --width=1200 --text="Select your preferred terminal emulator to run steamcmd (setting will be saved)" --title=DZGUI 2>/dev/null) -} -calc_mod_sizes(){ - for i in "$diff"; do - local mods+=$(grep -w "$i" /tmp/modsizes | awk '{print $1}') - done - totalmodsize=$(echo -e "${mods[@]}" | awk '{s+=$1}END{print s}') -} -term_params(){ - case $term in - konsole) $term --hold -e "bash $helpers_path/scmd.sh $totalmodsize $1";; - urxvt) $term -e bash -c "/$helpers_path/scmd.sh $totalmodsize $1";; - alacritty) $term -e bash -c "/$helpers_path/scmd.sh $totalmodsize $1";; - terminator|xterm|tilix|xfce4-terminal) $term -e "bash $helpers_path/scmd.sh $totalmodsize $1";; - esac -} -auto_mod_install(){ - cmd=$(printf "%q " "$@") - if [[ -z "$term" ]]; then - if [[ $is_steam_deck -eq 1 ]]; then - set_term konsole - return 0 - else - sel_term && set_term "$term" - fi - fi - [[ -z "$term" ]] && return 1 - echo "[DZGUI] Kicking off auto mod script" - calc_mod_sizes - term_params "$cmd" - compare - if [[ -z $diff ]]; then - passed_mod_check > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) - launch - else - warn "Auto mod installation failed or some mods missing.\nReverting to manual mode." - return 1 - fi -} -test_display_mode(){ - pgrep -a gamescope | grep -q "generate-drm-mode" - [[ $? -eq 0 ]] && gamemode=1 -} -foreground(){ - if [[ $(command -v wmctrl) ]]; then - wmctrl -a "DZG Watcher" - else - - local window_id=$(xdotool search --name "DZG Watcher") - xdotool windowactivate $window_id - fi -} -manual_mod_install(){ - [[ $is_steam_deck -eq 1 ]] && test_display_mode - if [[ $gamemode -eq 1 ]]; then - steam_deck_mods - else - local ex="/tmp/dzc.tmp" - [[ -f $ex ]] && rm $ex - watcher(){ - readarray -t stage_mods <<< "$diff" - for((i=0;i<${#stage_mods[@]};i++)); do - [[ -f $ex ]] && return 1 - local downloads_dir="$steam_path/steamapps/workshop/downloads/$aid" - local workshop_dir="$steam_path/steamapps/workshop/content/$aid" - steam "steam://url/CommunityFilePage/${stage_mods[$i]}" - echo "# Opening workshop page for ${stage_mods[$i]}. If you see no progress after subscribing, try unsubscribing and resubscribing again until the download commences." - sleep 1s - foreground - until [[ -d $downloads_dir/${stage_mods[$i]} ]]; do - [[ -f $ex ]] && return 1 - sleep 0.1s - if [[ -d $workshop_dir/${stage_mods[$i]} ]]; then - break - fi - done - foreground - echo "# Steam is downloading ${stage_mods[$i]} (mod $((i+1)) of ${#stage_mods[@]})" - until [[ -d $workshop_dir/${stage_mods[$i]} ]]; do - [[ -f $ex ]] && return 1 - sleep 0.1s - done - foreground - echo "# ${stage_mods[$i]} moved to mods dir" - done - echo "100" - } - watcher > >(zenity --pulsate --progress --auto-close --title="DZG Watcher" --width=500 2>/dev/null; rc=$?; [[ $rc -eq 1 ]] && touch $ex) - compare - if [[ -z $diff ]]; then - passed_mod_check > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) - launch - else - return 1 - fi - fi -} -# if [[ $is_steam_deck -eq 0 ]]; then -# open_mod_links -# until [[ -z $diff ]]; do -# zenity --question --title="DZGUI" --ok-label="Next" --cancel-label="Cancel" --text="Opened mod links in browser.\nClick [Next] when all mods have been subscribed to.\nThis dialog may reappear if clicking [Next] too soon\nbefore mods are synchronized in the background." --width=500 2>/dev/null -# rc=$? -# if [[ $rc -eq 0 ]]; then -# compare -# open_mod_links -# else -# return -# fi -# done -# else -# steam_deck_mods -# rc=$? -# [[ $rc -eq 1 ]] && return 1 -# fi -# passed_mod_check > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) -encode(){ - echo "$1" | md5sum | cut -c -8 -} -stale_symlinks(){ - for l in $(find "$game_dir" -xtype l); do - unlink $l - done -} -legacy_symlinks(){ - for d in "$game_dir"/*; do - if [[ $d =~ @[0-9]+-.+ ]]; then - unlink "$d" - fi - done - for d in "$workshop_dir"/*; do - 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') - if [[ -h "$game_dir/@$encoded_id" ]]; then - unlink "$game_dir/@$encoded_id" - fi - done -} -symlinks(){ - for d in "$workshop_dir"/*; do - id=$(awk -F"= " '/publishedid/ {print $2}' "$d"/meta.cpp | awk -F\; '{print $1}') - encoded_id=$(encode "$id") - mod=$(awk -F\" '/name/ {print $2}' "$d"/meta.cpp | sed -E 's/[^[:alpha:]0-9]+/_/g; s/^_|_$//g') - link="@$encoded_id" - if [[ -h "$game_dir/$link" ]]; then - : - else - printf "[DZGUI] Creating symlink for $mod\n" - ln -fs "$d" "$game_dir/$link" - fi - done -} -passed_mod_check(){ - echo "[DZGUI] Passed mod check" - echo "# Preparing symlinks" - stale_symlinks - legacy_symlinks - symlinks - echo "100" - -} -connect(){ - #TODO: sanitize/validate input - readarray -t qport_arr <<< "$qport_list" - if [[ -z ${qport_arr[@]} ]]; then - err "98: Failed to obtain query ports" - return - fi - ip=$(echo "$1" | awk -F"$separator" '{print $1}') - bid=$(echo "$1" | awk -F"$separator" '{print $2}') - if [[ $2 == "ip" ]]; then - fetch_mods_sa "$ip" > >(zenity --pulsate --progress --auto-close --no-cancel --width=500 2>/dev/null) - else - fetch_mods "$bid" - fi - if [[ $ret -eq 96 ]]; then - unset ret - return - fi - validate_mods - rc=$? - [[ $rc -eq 1 ]] && return - compare - if [[ -n $diff ]]; then - if [[ $auto_install -eq 1 ]]; then - auto_mod_install "$diff" - rc=$? - [[ $rc -eq 1 ]] && manual_mod_install - else - manual_mod_install - fi - else - passed_mod_check > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) - launch - fi -} -fetch_mods(){ - remote_mods=$(curl -s "$api" -H "Authorization: Bearer "$api_key"" -G -d filter[ids][whitelist]="$1" -d "sort=-players" \ - | jq -r '.data[] .attributes .details .modIds[]') -} -fetch_mods_sa(){ - sa_ip=$(echo "$1" | awk -F: '{print $1}') - for i in ${qport_arr[@]}; do - if [[ -n $(echo "$i" | awk -v ip=$ip '$0 ~ ip') ]]; then - sa_port=$(echo $i | awk -v ip=$ip -F$separator '$0 ~ ip {print $2}') - fi - done - echo "[DZGUI] Querying modlist on ${sa_ip}:${sa_port}" - echo "# Querying modlist on ${sa_ip}:${sa_port}" - local response=$(curl -Ls "https://dayzsalauncher.com/api/v1/query/$sa_ip/$sa_port") - local status=$(echo "$response" | jq '.status') - if [[ $status -eq 1 ]]; then - echo "100" - err "97: Failed to fetch modlist" - zenity --error --title="DZGUI" --width=500 --text="[ERROR] 97: Failed to fetch modlist" 2>/dev/null && - ret=96 - return - fi - remote_mods=$(echo "$response" | jq -r '.result.mods[].steamWorkshopId') - qport_arr=() -} -prepare_ip_list(){ - ct=$(< "$1" jq '[.response.servers[]]|length') - for((i=0;i<$ct;i++));do - name=$(< $json jq --arg i $i -r '[.servers[]][($i|tonumber)].name') - addr=$(< $json jq --arg i $i -r '[.servers[]][($i|tonumber)].addr') - ip=$(echo "$addr" | awk -F: '{print $1}') - players=$(< $json jq --arg i $i -r '[.servers[]][($i|tonumber)].players') - max_players=$(< $json jq --arg i $i -r '[.servers[]][($i|tonumber)].max_players') - gameport=$(< $json jq --arg i $i -r '[.servers[]][($i|tonumber)].gameport') - ip_port=$(echo "$ip:$gameport") - time=$(< $json jq --arg i $i -r '[.servers[]][($i|tonumber)].gametype' | grep -oP '(?/dev/null) - if [[ $? -eq 1 ]]; then - return_from_table=1 - return - fi - if [[ -z $sel ]]; then - echo "No selection" - else - local gameport="$(echo "$sel" | awk -F: '{print $2}')" - local ip="$(echo "$sel" | awk -F: '{print $1}')" - local addr=$(< $json jq -r --arg gameport $gameport '.servers[]|select(.gameport == ($gameport|tonumber)).addr') - local qport=$(echo "$addr" | awk -F: '{print $2}') - local sa_ip=$(echo "$ip:$gameport%%$qport") - qport_list="$sa_ip" - connect "$sel" "ip" - fi - done -} -fetch_ip_metadata(){ - local meta_file=$(mktemp) - source $config_file - url="https://api.steampowered.com/IGameServersService/GetServerList/v1/?filter=\appid\221100\gameaddr\\$ip&key=$steam_api" - curl -Ls "$url" > $meta_file - json=$(mktemp) - < $meta_file jq '.response' > $json - res=$(< $meta_file jq -er '.response.servers[]' 2>/dev/null) - if [[ ! $? -eq 0 ]]; then - warn "[ERROR] 96: Failed to retrieve IP metadata. Check IP or API key and try again." - echo "[DZGUI] 96: Failed to retrieve IP metadata" - - else - ip_table - fi -} - -#TODO: local servers -#local_ip(){ -#(^127\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.) -#} -test_steam_api(){ - local code=$(curl -ILs "https://api.steampowered.com/IGameServersService/GetServerList/v1/?filter=\appid\221100&limit=10&key=$steam_api" \ - | grep -E "^HTTP") - [[ $code =~ 403 ]] && echo 1 - [[ $code =~ 200 ]] && echo 0 -} -add_steam_api(){ - [[ $(test_steam_api) -eq 1 ]] && return 1 - mv $config_file ${config_path}dztuirc.old - nr=$(awk '/steam_api=/ {print NR}' ${config_path}dztuirc.old) - steam_api="steam_api=\"$steam_api\"" - awk -v "var=$steam_api" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > ${config_path}dztuirc - echo "[DZGUI] Added Steam API key" - zenity --info --title="DZGUI" --text="Added Steam API key to:\n\n${config_path}dztuirc\nIf errors occur, you can restore the file:\n${config_path}dztuirc.old" 2>/dev/null - source $config_file -} -check_steam_api(){ - if [[ -z $steam_api ]]; then - steam_api=$(zenity --entry --text="Key 'steam_api' not present in config file. Enter Steam API key:" --title="DZGUI" 2>/dev/null) - if [[ $? -eq 1 ]] ; then - return - elif [[ ${#steam_api} -lt 32 ]] || [[ $(test_steam_api) -eq 1 ]]; then - zenity --warning --title="DZGUI" --text="Check API key and try again." 2>/dev/null - return 1 - else - add_steam_api - fi - fi -} -validate_ip(){ - echo "$1" | grep -qP '^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$' -} -connect_by_ip(){ - source $config_file - check_steam_api - [[ $? -eq 1 ]] && return - while true; do - if [[ $return_from_table -eq 1 ]]; then - return_from_table=0 - return - fi - ip=$(zenity --entry --text="Enter server IP (omit port)" --title="DZGUI" 2>/dev/null) - [[ $? -eq 1 ]] && return - if validate_ip "$ip"; then - fetch_ip_metadata - else - continue - fi - done -} -fetch_mods(){ - remote_mods=$(curl -s "$api" -H "Authorization: Bearer "$api_key"" -G -d filter[ids][whitelist]="$1" -d "sort=-players" \ - | jq -r '.data[] .attributes .details .modIds[]') -} -query_defunct(){ - max=${#modlist[@]} - concat(){ - for ((i=0;i<$max;i++)); do - echo "publishedfileids[$i]=${modlist[$i]}&" - done | awk '{print}' ORS='' - } - payload(){ - echo -e "itemcount=${max}&$(concat)" - } - post(){ - curl -s -X POST -H "Content-Type:application/x-www-form-urlencoded" -d "$(payload)" 'https://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1/?format=json' - } - result=$(post | jq -r '.[].publishedfiledetails[] | select(.result==1) | "\(.file_size) \(.publishedfileid)"') - echo "$result" > /tmp/modsizes - readarray -t newlist <<< $(echo -e "$result" | awk '{print $2}') -} -validate_mods(){ - url="https://steamcommunity.com/sharedfiles/filedetails/?id=" - newlist=() - readarray -t modlist <<< $remote_mods - query_defunct -} -server_modlist(){ - for i in "${newlist[@]}"; do - printf "$i\n" - done -} -compare(){ - diff=$(comm -23 <(server_modlist | sort) <(installed_mods | sort)) -} - -installed_mods(){ - ls -1 "$workshop_dir" -} -concat_mods(){ - if [[ -z ${remote_mods[@]} ]]; then - return 1 - else - readarray -t serv <<< "$(server_modlist)" - for i in "${serv[@]}"; do - id=$(awk -F"= " '/publishedid/ {print $2}' "$workshop_dir"/$i/meta.cpp | awk -F\; '{print $1}') - encoded_id=$(encode $id) - link="@$encoded_id;" - echo -e "$link" - done | tr -d '\n' | perl -ple 'chop' - fi -} -launch(){ - mods=$(concat_mods) - if [[ $debug -eq 1 ]]; then - launch_options="steam -applaunch $aid -connect=$ip -nolauncher -nosplash -name=$name -skipintro \"-mod=$mods\"" - print_launch_options="$(printf "This is a dry run.\nThese options would have been used to launch the game:\n\n$launch_options\n" | fold -w 60)" - zenity --question --title="DZGUI" --ok-label="Write to file" --cancel-label="Back"\ - --text="$print_launch_options" 2>/dev/null - if [[ $? -eq 0 ]]; then - source_script=$(realpath "$0") - source_dir=$(dirname "$source_script") - echo "$launch_options" > "$source_dir"/options.log - echo "[DZGUI] Wrote launch options to $source_dir/options.log" - zenity --info --width 500 --title="DZGUI" --text="Wrote launch options to \n$source_dir/options.log" 2>/dev/null - fi - - else - echo "[DZGUI] All OK. Launching DayZ" - zenity --width 500 --title="DZGUI" --info --text="Launch conditions satisfied.\nDayZ will now launch after clicking [OK]." 2>/dev/null - steam -applaunch $aid -connect=$ip -nolauncher -nosplash -skipintro -name=$name \"-mod=$mods\" - exit - fi - one_shot_launch=0 -} -browser(){ - if [[ -n "$BROWSER" ]]; then - "$BROWSER" "$1" 2>/dev/null - else - xdg-open "$1" 2>/dev/null - fi -} -report_bug(){ - echo "[DZGUI] Opening issues page in browser" - if [[ $is_steam_deck -eq 1 ]]; then - steam steam://openurl/"$issues_url" 2>/dev/null - elif [[ $is_steam_deck -eq 0 ]]; then - browser "$issues_url" 2>/dev/null & - fi -} -forum(){ - echo "[DZGUI] Opening forum in browser" - if [[ $is_steam_deck -eq 1 ]]; then - steam steam://openurl/"$forum_url" 2>/dev/null - elif [[ $is_steam_deck -eq 0 ]]; then - browser "$forum_url" 2>/dev/null & - fi -} -help_file(){ - echo "[DZGUI] Opening help file in browser" - if [[ $is_steam_deck -eq 1 ]]; then - steam steam://openurl/"$help_url" 2>/dev/null - elif [[ $is_steam_deck -eq 0 ]]; then - browser "$help_url" 2>/dev/null & - fi -} -set_mode(){ - if [[ $debug -eq 1 ]]; then - mode=debug - else - mode=normal - fi -} -delete_by_id(){ - new_whitelist="whitelist=\"$(echo "$whitelist" | sed "s/,$server_id$//;s/^$server_id,//;s/,$server_id,/,/")\"" - mv $config_file ${config_path}dztuirc.old - nr=$(awk '/whitelist=/ {print NR}' ${config_path}dztuirc.old) - awk -v "var=$new_whitelist" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > ${config_path}dztuirc - echo "[DZGUI] Removed $server_id from key 'whitelist'" - zenity --info --title="DZGUI" --text="Removed "$server_id" from:\n${config_path}dztuirc\nIf errors occur, you can restore the file:\n${config_path}dztuirc.old" --width=500 2>/dev/null - source $config_file -} -delete_or_connect(){ - if [[ $delete -eq 1 ]]; then - server_name=$(echo "$sel" | awk -F"%%" '{print $1}') - server_id=$(echo "$sel" | awk -F"%%" '{print $2}') - zenity --question --text="Delete this server? \n$server_name" - if [[ $? -eq 0 ]]; then - delete_by_id $server_id - fi - else - #hotfix for bug #36 - local lookup_ip=$(echo "$sel" | awk -F%% '{print $1}') - local lookup_port=$(echo "$lookup_ip" | awk -F: '{print $2}') - source $config_file - file=$(mktemp) - url="https://api.steampowered.com/IGameServersService/GetServerList/v1/?filter=\appid\221100\gameaddr\\$lookup_ip&key=$steam_api" - curl -Ls "$url" > $file - local qport_res=$(< $file jq -r --arg port $lookup_port '.response.servers[]|select(.gameport==($port|tonumber)).addr') - local qport=$(echo "$qport_res" | awk -F: '{print $2}') - qport_list="$lookup_ip%%$qport" - connect "$qport_list" "ip" - - fi -} -populate(){ - while true; do - if [[ $delete -eq 1 ]]; then - cols="--column="Server" --column="ID"" - set_header "delete" - else - cols="--column="Server" --column="IP" --column="Players" --column="Gametime" --column="Status" --column="ID" --column="Ping"" - set_header ${FUNCNAME[0]} - fi - rc=$? - if [[ $rc -eq 0 ]]; then - if [[ -z $sel ]]; then - warn "No item was selected." - else - delete_or_connect - return - fi - else - delete=0 - return - fi - done -} -list_mods(){ - if [[ -z $(installed_mods) ]] || [[ -z $(find $workshop_dir -maxdepth 2 -name "*.cpp" | grep .cpp) ]]; then - zenity --info --text="94: No mods currently installed or incorrect path given" $sd_res 2>/dev/null - else - for d in $(find $game_dir/* -maxdepth 1 -type l); do - dir=$(basename $d) - awk -v d=$dir -F\" '/name/ {printf "%s\t%s\n", $2,d}' "$gamedir"/$d/meta.cpp - done | sort - fi -} -fetch_query_ports(){ - qport_list=$(echo "$response" | jq -r '.data[] .attributes | "\(.ip):\(.port)%%\(.portQuery)"') -} -connect_to_fav(){ - if [[ -n $fav ]]; then - one_shot_launch=1 - query_api - fetch_query_ports - echo "[DZGUI] Attempting connection to $fav_label" - connect "$qport_list" "ip" - one_shot_launch=0 - else - warn "93: No fav server configured" - fi - -} -set_header(){ - [[ $auto_install -eq 1 ]] && install_mode=auto - [[ $auto_install -eq 0 ]] && install_mode=manual - if [[ $1 == "delete" ]]; then - sel=$(cat $tmp | zenity $sd_res --list $cols --title="DZGUI" --text="DZGUI $version | Mode: $mode | Branch: $branch | Mods: $install_mode | Fav: $fav_label" \ - --separator="$separator" --print-column=1,2 --ok-label="Delete" 2>/dev/null) - elif [[ $1 == "populate" ]]; then - sel=$(cat $tmp | zenity $sd_res --list $cols --title="DZGUI" --text="DZGUI $version | Mode: $mode | Branch: $branch | Mods: $install_mode | Fav: $fav_label" \ - --separator="$separator" --print-column=2,6 2>/dev/null) - elif [[ $1 == "main_menu" ]]; then - sel=$(zenity $sd_res --list --title="DZGUI" --text="${news}DZGUI $version | Mode: $mode | Branch: $branch | Mods: $install_mode | Fav: $fav_label" \ - --cancel-label="Exit" --ok-label="Select" --column="Select launch option" --hide-header "${items[@]}" 2>/dev/null) - fi -} -toggle_branch(){ - mv $config_file ${config_path}dztuirc.old - nr=$(awk '/branch=/ {print NR}' ${config_path}dztuirc.old) - if [[ $branch == "stable" ]]; then - branch="testing" - else - branch="stable" - fi - flip_branch="branch=\"$branch\"" - awk -v "var=$flip_branch" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > $config_file - printf "[DZGUI] Toggled branch to '$branch'\n" - source $config_file -} -generate_log(){ - cat <<-DOC - Version: $version - Branch: $branch - Whitelist: $whitelist - Path: $steam_path - Linux: $(uname -mrs) - - Mods: - $(list_mods) - DOC -} -automods_prompt(){ -cat <<- HERE - -Auto-mod installation set to ON. This method is NOT supported in Game Mode (Steam Deck). - -READ THIS FIRST: -With this setting on, DZGUI will attempt to download and prepare mods using Valve's steamcmd tool. - -The first time this process is run, DZGUI will ask you to select a terminal emulator of your preference to spawn the installation routine. If you don't have a preference or don't know, you can pick any. - -Installation will kick off in a separate window and may ask you for input such as your sudo password in order to install system packages and create the steamcmd user. - -steamcmd itself will ask for your Steam credentials. This information is used directly by Valve's steamcmd tool to authenticate your account and let you download mods headlessly. steamcmd is an official program created by Valve and communicates only with their servers. - -NOTE: it can take some time for large mods to download, and steamcmd will not inform you of activity until each one is finished downloading. - -If your distribution is unsupported, you don't have enough disk space to stage all of the mods, or there are other problems, DZGUI will warn you and write a report to $HOME/.local/share/dzgui/helpers/SCMD.log. You can attach this file to a bug report. -HERE -} -toggle_automods(){ - mv $config_file ${config_path}dztuirc.old - local nr=$(awk '/auto_install=/ {print NR}' ${config_path}dztuirc.old) - if [[ $auto_install == "1" ]]; then - auto_install="0" - else - auto_install="1" - fi - local flip_state="auto_install=\"$auto_install\"" - awk -v "var=$flip_state" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > $config_file - printf "[DZGUI] Toggled auto-mod install to '$auto_install'\n" - source $config_file - local big_prompt - [[ $is_steam_deck -eq 1 ]] && big_prompt="--width=800" - [[ $auto_install == "1" ]] && zenity --info --text="$(automods_prompt)" $big_prompt 2>/dev/null -} -options_menu(){ - debug_list=( - "Toggle branch" - "Toggle debug mode" - "Generate debug log" - "Toggle auto-mod install (experimental)" - "Set auto-mod staging directory [$staging_dir]" - ) - debug_sel=$(zenity --list --width=1280 --height=800 --column="Options" --title="DZGUI" --hide-header "${debug_list[@]}" 2>/dev/null) - if [[ $debug_sel == "${debug_list[0]}" ]]; then - enforce_dl=1 - toggle_branch && - check_version - elif [[ $debug_sel == "${debug_list[1]}" ]]; then - toggle_debug - elif [[ $debug_sel == "${debug_list[2]}" ]]; then - source_script=$(realpath "$0") - source_dir=$(dirname "$source_script") - generate_log > "$source_dir/log" - printf "[DZGUI] Wrote log file to %s/log\n" "$source_dir" - zenity --info --width 500 --title="DZGUI" --text="Wrote log file to \n$source_dir/log" 2>/dev/null - elif [[ $debug_sel == "${debug_list[3]}" ]]; then - toggle_automods - elif [[ $debug_sel == "${debug_list[4]}" ]]; then - file_picker - fi -} -query_and_connect(){ - query_api - parse_json - #TODO: create logger function - if [[ ! $delete -eq 1 ]]; then - echo "[DZGUI] Checking response time of servers" - create_array | zenity --width 500 --progress --pulsate --title="DZGUI" --auto-close 2>/dev/null - else - create_array - fi - rc=$? - if [[ $rc -eq 1 ]]; then - : - else - populate - fi -} -exclude_full(){ - response=$(echo "$response" | jq '[.[]|select(.players!=.max_players)]') -} -exclude_empty(){ - response=$(echo "$response" | jq '[.[]|select(.players!=0)]') -} -filter_maps(){ - echo "# Filtering maps" - [[ $ret -eq 98 ]] && return - local maps=$(echo "$response" | jq -r '.[].map//empty|ascii_downcase' | sort -u) - local map_ct=$(echo "$maps" | wc -l) - local map_sel=$(echo "$maps" | zenity --list --column="Check" --width=1200 --height=800 2>/dev/null --title="DZGUI" --text="Found $map_ct map types") - echo "[DZGUI] Selected '$map_sel'" - if [[ -z $map_sel ]]; then - ret=97 - return - fi - echo "100" - response=$(echo "$response" | jq --arg map "$map_sel" '[.[]|select(.map)//empty|select(.map|ascii_downcase == $map)]') -} -exclude_daytime(){ - response=$(echo "$response" | jq '[.[]|select(.gametype|test(",[0][6-9]:|,[1][0-6]:")|not)]') -} -exclude_nighttime(){ - response=$(echo "$response" | jq '[.[]|select(.gametype|test(",[1][7-9]:|,[2][0-4]:|[0][0-5]:")|not)]') -} -keyword_filter(){ - response=$(echo "$response" | jq --arg search "$search" '[.[]|select(.name|ascii_downcase | contains($search))]') -} -exclude_lowpop(){ - response=$(echo "$response" | jq '[.[]|select(.players > 9)]') -} -exclude_nonascii(){ - response=$(echo "$response" | jq -r '[.[]|select(.name|test("^([[:ascii:]])*$"))]') -} -strip_null(){ - response=$(echo "$response" | jq -r '[.[]|select(.map//empty)]') -} -local_latlon(){ - if [[ -z $(command -v dig) ]]; then - local local_ip=$(curl -Ls "https://ipecho.net/plain") - else - local local_ip=$(dig +short myip.opendns.com @resolver1.opendns.com) - fi - local url="http://ip-api.com/json/$local_ip" - local res=$(curl -Ls "$url" | jq -r '"\(.lat),\(.lon)"') - local_lat=$(echo "$res" | awk -F, '{print $1}') - local_lon=$(echo "$res" | awk -F, '{print $2}') -} -disabled(){ - if [[ -z ${disabled[@]} ]]; then - printf "%s" "-" - else - for((i=0;i<${#disabled[@]};i++)); do - if [[ $i < $((${#disabled[@]}-1)) ]]; then - printf "%s, " "${disabled[$i]}" - else - printf "%s" "${disabled[$i]}" - fi - - done - fi -} -pagination(){ - if [[ ${#qport[@]} -eq 1 ]]; then - entry=server - else - entry=servers - fi - printf "DZGUI %s | " "$version" - printf "Mode: %s |" "$mode" - printf "Fav: %s " "$fav_label" - printf "\nIncluded: %s | " "$filters" - printf "Excluded: %s " "$(disabled)" - if [[ -n $search ]]; then - printf "| Keyword: %s " "$search" - fi - printf "\nReturned: %s %s of %s | " "${#qport[@]}" "$entry" "$total_servers" - printf "Players online: %s" "$players_online" -} -check_geo_file(){ - local gzip="$helpers_path/ips.csv.gz" - curl -Ls "$sums_url" > "$sums_path" - cd "$helpers_path" - md5sum -c "$sums_path" 2>/dev/null 1>&2 - local res=$? - cd $OLDPWD - if [[ $res -eq 1 ]]; then - run(){ - mkdir -p "$helpers_path" - echo "# Fetching new geolocation DB" - curl -Ls "$db_file" > "$gzip" - echo "# Extracting coordinates" - #force overwrite - gunzip -f "$gzip" - echo "# Preparing helper file" - curl -Ls "$km_helper_url" > "$km_helper" - chmod +x $km_helper - echo "100" - } - run > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) - fi -} -choose_filters(){ - if [[ $is_steam_deck -eq 0 ]]; then - sd_res="--width=1920 --height=1080" - fi - sels=$(zenity --title="DZGUI" --text="Server search" --list --checklist --column "Check" --column "Option" --hide-header TRUE "All maps (untick to select from map list)" TRUE "Daytime" TRUE "Nighttime" False "Empty" False "Full" False "Low population" FALSE "Non-ASCII titles" FALSE "Keyword search" $sd_res 2>/dev/null) - if [[ $sels =~ Keyword ]]; then - search=$(zenity --entry --text="Search (case insensitive)" --width=500 --title="DZGUI" 2>/dev/null | awk '{print tolower($0)}') - [[ -z $search ]] && { ret=97; return; } - fi - [[ -z $sels ]] && return - filters=$(echo "$sels" | sed 's/|/, /g;s/ (untick to select from map list)//') - echo "[DZGUI] Filters: $filters" -} -get_dist(){ - local given_ip="$1" - local network="$(echo "$given_ip" | awk -F. '{OFS="."}{print $1"."$2}')" - local binary=$(grep -E "^$network\." $geo_file) - local three=$(echo $given_ip | awk -F. '{print $3}') - local host=$(echo $given_ip | awk -F. '{print $4}') - local res=$(echo "$binary" | awk -F[.,] -v three=$three -v host=$host '$3 <=three && $7 >= three{if($3>three || ($3==three && $4 > host) || $7 < three || ($7==three && $8 < host)){next}{print}}' | awk -F, '{print $7,$8}') - local remote_lat=$(echo "$res" | awk '{print $1}') - local remote_lon=$(echo "$res" | awk '{print $2}') - if [[ -z $remote_lat ]]; then - local dist="Unknown" - echo "$dist" - else - local dist=$($km_helper "$local_lat" "$local_lon" "$remote_lat" "$remote_lon") - printf "%05.0f %s" "$dist" "km" - fi -} -prepare_filters(){ - echo "# Filtering list" - [[ ! "$sels" =~ "Full" ]] && { exclude_full; disabled+=("Full") ; } - [[ ! "$sels" =~ "Empty" ]] && { exclude_empty; disabled+=("Empty") ; } - [[ ! "$sels" =~ "Daytime" ]] && { exclude_daytime; disabled+=("Daytime") ; } - [[ ! "$sels" =~ "Nighttime" ]] && { exclude_nighttime; disabled+=("Nighttime") ; } - [[ ! "$sels" =~ "Low population" ]] && { exclude_lowpop; disabled+=("Low-pop") ; } - [[ ! "$sels" =~ "Non-ASCII titles" ]] && { exclude_nonascii; disabled+=("Non-ASCII") ; } - [[ -n "$search" ]] && keyword_filter - strip_null - echo "100" -} -write_fifo(){ - [[ -p $fifo ]] && rm $fifo - mkfifo $fifo - for((i=0;i<${#qport[@]};i++)); do - printf "%s\n%s\n%s\n%03d\n%03d\n%s\n%s:%s\n%s\n" "${map[$i]}" "${name[$i]}" "${gametime[$i]}" \ - "${players[$i]}" "${max[$i]}" "$(get_dist ${addr[$i]})" "${addr[$i]}" "${gameport[$i]}" "${qport[$i]}" >> $fifo - done -} -munge_servers(){ - if [[ ! "$sels" =~ "All maps" ]]; then - filter_maps > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) - disabled+=("All maps") - fi - [[ $ret -eq 97 ]] && return - prepare_filters > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) - if [[ $(echo "$response" | jq 'length') -eq 0 ]]; then - zenity --error --text="No matching servers" 2>/dev/null - return - fi - local addr=$(echo "$response" | jq -r '.[].addr' | awk -F: '{print $1}') - local gameport=$(echo "$response" | jq -r '.[].gameport') - local qport=$(echo "$response" | jq -r '.[].addr' | awk -F: '{print $2}') - #jq bug #1788, raw output cannot be used with ASCII - local name=$(echo "$response" | jq -a '.[].name' | sed 's/\\u[0-9a-z]\{4\}//g;s/^"//;s/"$//') - local players=$(echo "$response" | jq -r '.[].players') - local max=$(echo "$response" | jq -r '.[].max_players') - local map=$(echo "$response" | jq -r '.[].map|ascii_downcase') - local gametime=$(echo "$response" | jq -r '.[].gametype' | grep -oE '[0-9]{2}:[0-9]{2}$') - readarray -t qport <<< $qport - readarray -t gameport <<< $gameport - readarray -t addr <<< $addr - readarray -t name <<< $name - readarray -t players <<< $players - readarray -t map <<< $map - readarray -t max <<< $max - readarray -t gametime <<< $gametime - if [[ $is_steam_deck -eq 0 ]]; then - sd_res="--width=1920 --height=1080" - fi - - write_fifo & - pid=$! - local sel=$(zenity --text="$(pagination)" --title="DZGUI" --list --column=Map --column=Name --column=Gametime --column=Players --column=Max --column=Distance --column=IP --column=Qport $sd_res --print-column=7,8 --separator=%% 2>/dev/null < <(while true; do cat $fifo; done)) - if [[ -z $sel ]]; then - rm $fifo - kill -9 $pid - else - rm $fifo - kill -9 $pid - echo $sel - fi -} -debug_servers(){ - [[ -f $debug_log ]] && rm $debug_log - if [[ -n $steam_api ]]; then - exists=true - else - exists=false - fi - key_len=${#steam_api} - first_char=${steam_api:0:1} - last_char=${steam_api:0-1} - debug_res=$(curl -Ls "https://api.steampowered.com/IGameServersService/GetServerList/v1/?filter=\appid\221100&limit=10&key=$steam_api") - debug_len=$(echo "$debug_res" | jq '[.response.servers[]]|length') - [[ -z $debug_len ]] && debug_len=0 - cat <<-DOC > $debug_log - ======START DEBUG====== - Key exists: $exists - First char: $first_char - Last char: $last_char - Key length: $key_len - ======Short query====== - Expected: 10 - Found: $debug_len - Response follows----> - $debug_res - ======END DEBUG======= - DOC -} -server_browser(){ - check_steam_api - [[ $? -eq 1 ]] && return - - unset ret - file=$(mktemp) - local limit=20000 - local url="https://api.steampowered.com/IGameServersService/GetServerList/v1/?filter=\appid\221100&limit=$limit&key=$steam_api" - check_geo_file - local_latlon - choose_filters - [[ -z $sels ]] && return - [[ $ret -eq 97 ]] && return - #TODO: some error handling here - fetch(){ - echo "# Getting server list" - curl -Ls "$url" > $file - } - fetch > >(zenity --pulsate --progress --auto-close --width=500 2>/dev/null) - response=$(< $file jq -r '.response.servers') - total_servers=$(echo "$response" | jq 'length') - players_online=$(echo "$response" | jq '.[].players' | awk '{s+=$1}END{print s}') - debug_log="$HOME/.local/share/dzgui/DEBUG.log" - debug_servers - local sel=$(munge_servers) - if [[ -z $sel ]]; then - unset filters - unset search - ret=98 - return - fi - local sel_ip=$(echo "$sel" | awk -F%% '{print $1}') - local sel_port=$(echo "$sel" | awk -F%% '{print $2}') - qport_list="$sel_ip%%$sel_port" - if [[ -n "$sel_ip" ]]; then - connect "$sel_ip" "ip" - else - return - fi -} - -mods_disk_size(){ - printf "Total size on disk: %s |" $(du -sh "$game_dir" | awk '{print $1}') - printf "Mods location: $steam_path/steamapps/workshop/content/221100" - -} - -main_menu(){ - print_news - set_mode - if [[ -n $fav ]]; then - items[7]=" Change favorite server" - fi - while true; do - set_header ${FUNCNAME[0]} - rc=$? - if [[ $rc -eq 0 ]]; then - if [[ -z $sel ]]; then - warn "No item was selected." - elif [[ $sel == "${items[0]}" ]]; then - : - elif [[ $sel == "${items[1]}" ]]; then - server_browser - elif [[ $sel == "${items[2]}" ]]; then - query_and_connect - elif [[ $sel == "${items[3]}" ]]; then - connect_to_fav - elif [[ $sel == "${items[4]}" ]]; then - : - elif [[ $sel == "${items[5]}" ]]; then - connect_by_ip - elif [[ $sel == "${items[6]}" ]]; then - add_by_id - elif [[ $sel == "${items[7]}" ]]; then - add_by_fav - elif [[ $sel == "${items[8]}" ]]; then - delete=1 - query_and_connect - elif [[ $sel == "${items[9]}" ]]; then - : - elif [[ $sel == "${items[10]}" ]]; then - list_mods | sed 's/\t/\n/g' | zenity --list --column="Mod" --column="Symlink" \ - --title="DZGUI" $sd_res --text="$(mods_disk_size)" \ - --print-column="" 2>/dev/null - elif [[ $sel == "${items[11]}" ]]; then - changelog | zenity --text-info $sd_res --title="DZGUI" 2>/dev/null - elif [[ $sel == "${items[12]}" ]]; then - options_menu - main_menu - return - elif [[ $sel == "${items[13]}" ]]; then - : - elif [[ $sel == "${items[14]}" ]]; then - help_file - elif [[ $sel == "${items[15]}" ]]; then - report_bug - elif [[ $sel == "${items[16]}" ]]; then - forum - else - warn "This feature is not yet implemented." - fi - else - return - fi - done -} -page_through(){ - list_response=$(curl -s "$page") - list=$(echo "$list_response" | jq -r '.data[] .attributes | "\(.name)\t\(.ip):\(.port)\t\(.players)/\(.maxPlayers)\t\(.details.time)\t\(.status)\t\(.id)"') - idarr+=("$list") - parse_json -} -parse_json(){ - page=$(echo "$list_response" | jq -r '.links.next?') - if [[ $first_entry -eq 1 ]]; then - local list=$(echo "$list_response" | jq -r '.data[] .attributes | "\(.name)\t\(.ip):\(.port)\t\(.players)/\(.maxPlayers)\t\(.details.time)\t\(.status)\t\(.id)"') - idarr+=("$list") - first_entry=0 - fi - if [[ "$page" != "null" ]]; then - local list=$(echo "$list_response" | jq -r '.data[] .attributes | "\(.name)\t\(.ip):\(.port)\t\(.players)/\(.maxPlayers)\t\(.details.time)\t\(.status)\t\(.id)"') - idarr+=("$list") - page_through - else - printf "%s\n" "${idarr[@]}" > $tmp - idarr=() - fetch_query_ports - fi -} -check_ping(){ - ping_ip=$(echo "$1" | awk -F'\t' '{print $2}' | awk -F: '{print $1}') - ms=$(ping -c 1 -W 1 "$ping_ip" | awk -Ftime= '/time=/ {print $2}') - if [[ -z $ms ]]; then - echo "Timeout" - else - echo "$ms" - fi -} -create_array(){ - rows=() - list=$(cat $tmp) - #TODO: improve error handling for null values - lc=1 - while read line; do - name=$(echo "$line" | awk -F'\t' '{print $1}') - #truncate names - if [[ $(echo "$name" | wc -m) -gt 50 ]]; then - name="$(echo "$name" | awk '{print substr($0,1,50) "..."}')" - else - : - fi - ip=$(echo "$line" | awk -F'\t' '{print $2}') - players=$(echo "$line" | awk -F'\t' '{print $3}') - time=$(echo "$line" | awk -F'\t' '{print $4}') - stat=$(echo "$line" | awk -F'\t' '{print $5}') - - #yad only - #[[ $stat == "online" ]] && stat="online" || : - - #TODO: probe offline return codes - id=$(echo "$line" | awk -F'\t' '{print $6}') - tc=$(awk 'END{print NR}' $tmp) - if [[ $delete -eq 1 ]]; then - declare -g -a rows=("${rows[@]}" "$name" "$id") - else - echo "# Checking ping: $lc/$tc" - ping=$(check_ping "$line") - declare -g -a rows=("${rows[@]}" "$name" "$ip" "$players" "$time" "$stat" "$id" "$ping") - fi - let lc++ - done <<< "$list" - - for i in "${rows[@]}"; do echo -e "$i"; done > $tmp -} -set_fav(){ - #TODO: test API key here and return errors - echo "[DZGUI] Querying favorite server" - query_api - fav_label=$(curl -s "$api" -H "Authorization: Bearer "$api_key"" -G -d "filter[game]=$game" -d "filter[ids][whitelist]=$fav" \ - | jq -r '.data[] .attributes .name') - if [[ -z $fav_label ]]; then - fav_label=null - fi - echo "[DZGUI] Setting favorite server to '$fav_label'" -} -check_unmerged(){ - if [[ -f ${config_path}.unmerged ]]; then - printf "[DZGUI] Found new config format, merging changes\n" - merge_config - rm ${config_path}.unmerged - fi -} -merge_config(){ - source $config_file - mv $config_file ${config_path}dztuirc.old - [[ -z $staging_dir ]] && staging_dir="/tmp" - write_config > $config_file - printf "[DZGUI] Wrote new config file to %sdztuirc\n" $config_path - zenity --info --width 500 --title="DZGUI" --text="Wrote new config format to \n${config_path}dztuirc\nIf errors occur, you can restore the file:\n${config_path}dztuirc.old" 2>/dev/null - -} -download_new_version(){ - if [[ $is_steam_deck -eq 1 ]]; then - freedesktop_dirs - fi - source_script=$(realpath "$0") - source_dir=$(dirname "$source_script") - mv $source_script $source_script.old - echo "# Downloading version $upstream" - curl -Ls "$version_url" > $source_script - rc=$? - if [[ $rc -eq 0 ]]; then - echo "[DZGUI] Wrote $upstream to $source_script" - chmod +x $source_script - touch ${config_path}.unmerged - echo "100" - zenity --question --width 500 --title="DZGUI" --text "DZGUI $upstream successfully downloaded.\nTo view the changelog, select Changelog.\nTo use the new version, select Exit and restart." --ok-label="Changelog" --cancel-label="Exit" 2>/dev/null - code=$? - if [[ $code -eq 0 ]]; then - changelog | zenity --text-info $sd_res --title="DZGUI" 2>/dev/null - exit - elif [[ $code -eq 1 ]]; then - exit - fi - else - echo "100" - mv $source_script.old $source_script - zenity --info --title="DZGUI" --text "[ERROR] 99: Failed to download new version." 2>/dev/null - return - fi - -} -check_branch(){ - if [[ $branch == "stable" ]]; then - version_url="$stable_url/dzgui.sh" - elif [[ $branch == "testing" ]]; then - version_url="$testing_url/dzgui.sh" - fi - upstream=$(curl -Ls "$version_url" | awk -F= '/^version=/ {print $2}') -} -enforce_dl(){ - download_new_version > >(zenity --progress --pulsate --auto-close --no-cancel --width=500) -} -prompt_dl(){ - zenity --question --title="DZGUI" --text "Version conflict.\n\nYour branch:\t\t\t$branch\nYour version\t\t\t$version\nUpstream version:\t\t$upstream\n\nVersion updates introduce important bug fixes and are encouraged.\n\nAttempt to download latest version?" --width=500 --ok-label="Yes" --cancel-label="No" 2>/dev/null - rc=$? - if [[ $rc -eq 1 ]]; then - return - else - echo "100" - download_new_version > >(zenity --progress --pulsate --auto-close --no-cancel --width=500) - fi -} -check_version(){ - [[ -f $config_file ]] && source $config_file - [[ -z $branch ]] && branch="stable" - check_branch - [[ ! -d "$freedesktop_path" ]] && freedesktop_dirs - if [[ $version == $upstream ]]; then - check_unmerged - else -# echo "100" - echo "[DZGUI] Upstream ($upstream) != local ($version)" - if [[ $enforce_dl -eq 1 ]]; then - enforce_dl - else - prompt_dl - fi - fi -} -check_architecture(){ - cpu=$(cat /proc/cpuinfo | grep "AMD Custom APU 0405") - if [[ -n "$cpu" ]]; then - is_steam_deck=1 - echo "[DZGUI] Setting architecture to 'Steam Deck'" - else - is_steam_deck=0 - echo "[DZGUI] Setting architecture to 'desktop'" - fi -} -add_by_id(){ - #FIXME: prevent redundant creation of existent IDs (for neatness) - while true; do - id=$(zenity --entry --text="Enter server ID" --title="DZGUI" 2>/dev/null) - rc=$? - if [[ $rc -eq 1 ]]; then - return - else - if [[ ! $id =~ ^[0-9]+$ ]]; then - zenity --warning --title="DZGUI" --text="Invalid ID" 2>/dev/null - else - new_whitelist="whitelist=\"$whitelist,$id\"" - mv $config_file ${config_path}dztuirc.old - nr=$(awk '/whitelist=/ {print NR}' ${config_path}dztuirc.old) - awk -v "var=$new_whitelist" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > ${config_path}dztuirc - echo "[DZGUI] Added $id to key 'whitelist'" - zenity --info --title="DZGUI" --text="Added "$id" to:\n${config_path}dztuirc\nIf errors occur, you can restore the file:\n${config_path}dztuirc.old" --width=500 2>/dev/null - source $config_file - return - fi - fi - done -} -toggle_debug(){ - mv $config_file ${config_path}dztuirc.old - nr=$(awk '/debug=/ {print NR}' ${config_path}dztuirc.old) - if [[ $debug -eq 1 ]]; then - debug=0 - else - debug=1 - fi - flip_debug="debug=\"$debug\"" - awk -v "var=$flip_debug" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > $config_file - printf "[DZGUI] Toggled debug flag to '$debug'\n" - source $config_file - -} -setup(){ - if [[ -n $fav ]]; then - set_fav - items[7]=" Change favorite server" - fi -} -check_map_count(){ - count=1048576 - echo "[DZGUI] Checking system map count" - if [[ $(sysctl -q vm.max_map_count | awk -F"= " '{print $2}') -lt $count ]]; then - echo "100" - map_warning=$(zenity --question --width 500 --title="DZGUI" --text "System map count must be $count or higher to run DayZ with Wine. Increase map count and make this change permanent? (will prompt for sudo password)" 2>/dev/null) - if [[ $? -eq 0 ]]; then - pass=$(zenity --password) - sudo -S <<< "$pass" sh -c "echo 'vm.max_map_count=1048576' > /etc/sysctl.d/dayz.conf" - echo "" - - fi - fi -} -add_by_fav(){ -while true; do - fav_id=$(zenity --entry --text="Enter server ID" --title="DZGUI" 2>/dev/null) - rc=$? - if [[ $rc -eq 1 ]]; then - return - else - if [[ ! $fav_id =~ ^[0-9]+$ ]]; then - zenity --warning --title="DZGUI" --text="Invalid ID" - else - new_fav="fav=\"$fav_id\"" - mv $config_file ${config_path}dztuirc.old - nr=$(awk '/fav=/ {print NR}' ${config_path}dztuirc.old) - awk -v "var=$new_fav" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > ${config_path}dztuirc - echo "[DZGUI] Added $fav_id to key 'fav'" - zenity --info --title="DZGUI" --text="Added "$fav_id" to:\n${config_path}dztuirc\nIf errors occurred, you can restore the file:\n${config_path}dztuirc.old" 2>/dev/null - source $config_file - set_fav - items[7]=" Change favorite server" - return - fi - fi -done -} -lock(){ - if [[ ! -f $config_path/.lockfile ]]; then - touch $config_path/.lockfile - fi - pid=$(cat $config_path/.lockfile) - ps -p $pid -o pid= >/dev/null 2>&1 - res=$? - if [[ $res -eq 0 ]]; then - echo "[DZGUI] Already running ($pid)" - zenity --info --text="DZGUI already running (pid $pid)" --width=500 --title="DZGUI" 2>/dev/null - exit - elif [[ $pid == $$ ]]; then - : - else - echo $$ > $config_path/.lockfile - fi -} -fetch_scmd_helper(){ - mkdir -p "$helpers_path" - curl -Ls "$scmd_url" > "$helpers_path/scmd.sh" - chmod +x "$helpers_path/scmd.sh" - [[ ! -f "$helpers_path/d.html" ]] && curl -Ls "$notify_url" > "$helpers_path/d.html" - [[ ! -f "$helpers_path/d.webp" ]] && curl -Ls "$notify_img_url" > "$helpers_path/d.webp" -} -deprecation_warning(){ - warn(){ - cat <<- HERE - IMPORTANT ANNOUNCEMENT - (Steam API key not found) - - A Steam API key is now mandatory to run the app. - The BM API returns incorrect mod data on some servers - and cannot be relied upon for up to date information. - - Going forward, we will only use the BM API as a convenience - function to manage server names and your favorite servers list, - and migrate to indexing servers on an IP basis. - - This is a backend change. You can continue adding servers by ID, - but we will retrieve information from Valve instead, as we do for the - server browser and connect-by-ip methods. - - Click [OK] to open the help page describing how to set up your key. - After you input a valid key, the app will resume. - HERE - } - if [[ -z $steam_api ]]; then - echo "100" - zenity --info --text="$(warn)" - key_setup_url="https://aclist.github.io/dzgui/dzgui.html#_api_key_server_ids" - browser "$key_setup_url" 2>/dev/null & - while true; do - if [[ $(check_steam_api) ]]; then - break - fi - done - fi -} -initial_setup(){ - echo "# Initial setup" - run_depcheck - watcher_deps - check_architecture - check_version - check_map_count - config - fetch_scmd_helper - run_varcheck - init_items - setup - deprecation_warning - echo "100" -} -main(){ - lock - initial_setup > >(zenity --pulsate --progress --auto-close --title="DZGUI" --width=500 2>/dev/null) - main_menu - #cruddy handling for steam forking - [[ $? -eq 1 ]] && pkill -f dzgui.sh -} - -main -#trap cleanup EXIT diff --git a/helpers/d.html b/helpers/d.html deleted file mode 100644 index e5bbbda..0000000 --- a/helpers/d.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/helpers/d.webp b/helpers/d.webp deleted file mode 100644 index 37129b795dc8e436b06db33d501e136a204dcfe2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3067676 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%0D? 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%0AnEatzJVgbkU+UtI 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