diff --git a/dzgui.sh b/dzgui.sh index 7392449..9015dad 100644 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/bin/bash set -o pipefail -version=1.2.1 +version=2.0.0 aid=221100 game="dayz" workshop="https://steamcommunity.com/sharedfiles/filedetails/?id=" @@ -13,7 +13,9 @@ tmp=/tmp/dztui.tmp separator="%%" git_url="https://github.com/aclist/dztui/issues" version_url="https://raw.githubusercontent.com/aclist/dztui/dzgui/dzgui.sh" +help_url="https://github.com/aclist/dztui/blob/dzgui/README.md" upstream=$(curl -Ls "$version_url" | awk -F= '/^version=/ {print $2}') +check_config_msg="Check config values and restart." declare -A deps @@ -28,7 +30,7 @@ changelog(){ depcheck(){ for dep in "${!deps[@]}"; do - command -v $dep 2>&1>/dev/null || (printf "[ERROR] Requires %s >= %s\n" $dep ${deps[$dep]} ; exit 1) + command -v $dep 2>&1>/dev/null || (printf "[ERROR] Requires %s >= %s\nCheck your system package manager." $dep ${deps[$dep]}; exit 1) done } @@ -36,61 +38,113 @@ items=( "Launch server list" "Quick connect to favorite server" "Add server by ID" - "List mods" - "Report bug" + "Add favorite server" + "List installed mods" + #"Toggle debug mode" + "Report bug (opens in browser)" + "Help file (opens in browser)" + "View changelog" ) +#exit_and_cleanup(){ +#rm $tmp +#rm $link_file +#} warn_and_exit(){ zenity --info --title="DZGUI" --text="$1" --icon-name="dialog-warning" 2>/dev/null + printf "[DZGUI] %s\n" "$check_config_msg" exit } warn(){ zenity --info --title="DZGUI" --text="$1" --icon-name="dialog-warning" 2>/dev/null } info(){ - zenity --info --title="DZGUI" --text="$1" --icon-name="network-wireless" 2>/dev/null + zenity --info --title="DZGUI" --text="$1" 2>/dev/null } 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 response=$(curl -s "$api" -H "Authorization: Bearer "$api_key"" -G -d "sort=-players" \ - -d "filter[game]=$game" -d "filter[ids][whitelist]=$whitelist") + -d "filter[game]=$game" -d "filter[ids][whitelist]=$list_of_ids") if [[ "$(jq -r 'keys[]' <<< "$response")" == "errors" ]]; then code=$(jq -r '.errors[] .status' <<< $response) #TODO: fix granular api codes - warn_and_exit "Error $code: malformed API key" + if [[ $code -eq 401 ]]; then + echo "$code" >> outfile + warn_and_exit "Error $code: malformed API key" + elif [[ $code -eq 500 ]]; then + warn_and_exit "Error $code: malformed server list" + fi + + fi + if [[ -z $(echo $response | jq '.data[]') ]]; then + warn_and_exit "API returned empty response. Check config file." fi } write_config(){ -cat <<-'END' +cat <<-END -#Path to DayZ installation (change if using multiple SD cards/drives) -steam_path="/home/deck/.local/share/Steam" -workshop_dir="$steam_path/steamapps/workshop/content/$aid" -game_dir="$steam_path/steamapps/common/DayZ" +#Path to DayZ installation +steam_path="$steam_path" #Your unique API key -api_key="" +api_key="$api_key" #Comma-separated list of server IDs -whitelist="" +whitelist="$whitelist" #Favorite server to fast-connect to (limit one) -fav="" +fav="$fav" #Custom player name (optional, required by some servers) -name="player" +name="$name" #Set to 1 to perform dry-run and print launch options debug="0" -#(Not implemented) Set to 0 to suppress ping attempt -ping="1" - END } +guess_path(){ + if [[ $is_steam_deck -eq 1 ]]; then + steam_path="/home/deck/.local/share/Steam" + else + echo "# Checking for default DayZ path" + path=$(find $HOME -path "*.local/share/Steam/steamapps/common/DayZ" | wc -c) + if [[ ! $path -eq 0 ]]; then + steam_path="$HOME/.local/share/Steam/steamapps/common/DayZ" + else + echo "# Searching for alternate DayZ path" + path=$(find / -path "*/steamapps/common/DayZ" 2>/dev/null) + if [[ $(echo "$path" | wc -l) -gt 1 ]]; then + path_sel=$(echo -e "$path" | zenity --list --title="DZGUI" --text="Multiple paths found. Select correct DayZ path" --column="Paths" --width 1200 --height 800) + clean_path=$(echo -e "$path_sel" | awk -F"/steamapps" '{print $1}') + steam_path="$clean_path" + elif [[ ! $(echo $path | wc -c) -eq 0 ]]; then + clean_path=$(echo -e "$path" | awk -F"/steamapps" '{print $1}') + steam_path="$clean_path" + else + steam_path="" + fi + fi + fi + echo "[DZGUI] Set Steam path to $steam_path" +} create_config(){ + 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="│")" + name=$(echo "$player_input" | awk -F│ '{print $1}') + api_key=$(echo "$player_input" | awk -F│ '{print $2}') + whitelist=$(echo "$player_input" | awk -F"│" '{OFS=","}{print $3,$4,$5}' | sed 's/,*$//g' | sed 's/^,*//g') + guess_path > >(zenity --progress --auto-close --pulsate) mkdir -p $config_path; write_config > $config_file - info "Config file created at $config_file.\nFill in values and relaunch." - exit + info "Config file created at $config_file." } err(){ @@ -102,21 +156,24 @@ varcheck(){ [[ ! -d $workshop_dir ]] && (err "Malformed workshop path") [[ ! -d $game_dir ]] && (err "Malformed game path") [[ $whitelist =~ [[:space:]] ]] && (err "Separate whitelist values with commas") - IFS=, - [[ ! "${whitelist[*]}" =~ ${fav} ]] && (err "Fav key value not in whitelist") - unset IFS } -checks() { +run_depcheck() { if [[ -z $(depcheck) ]]; then : else zenity --warning --ok-label="Exit" --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 --ok-label="Exit" --text="$(varcheck)" + zenity --warning --ok-label="Exit" --text="$(varcheck)" 2>/dev/null + printf "[DZGUI] %s\n" "$check_config_msg" exit fi } @@ -133,9 +190,35 @@ 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 + for i in $diff; do + echo "${workshop}$i
" + done >> $link_file + echo "" >> $link_file + browser "$link_file" + } manual_mod_install(){ l=0 + 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. Click [Next] when all mods have been subscribed to. This dialog may reappear if clicking [Next] too soon before mods are synchronized in the background." 2>/dev/null + rc=$? + if [[ $rc -eq 0 ]]; then + compare + else + return + fi + done + else + 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." 2>/dev/null @@ -149,6 +232,7 @@ manual_mod_install(){ fi compare done + fi passed_mod_check } symlinks(){ @@ -242,14 +326,35 @@ launch(){ --text="$(printf "[DEBUG] This is a dry run. These options would have been used to launch the game:\n\nsteam -applaunch $aid -connect=$ip -nolauncher -nosplash -skipintro \"-mod=$mods\"\n")" 2>/dev/null else echo "[DZGUI] All OK. Launching DayZ" - zenity --title="DZGUI" --info --text="Launch conditions satisfied.\nDayZ will launch after clicking [OK]." 2>/dev/null + zenity --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 + #reset whitelist if canceling connect + 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" - steam steam://openurl/$git_url 2>/dev/null + echo "[DZGUI] Opening issues page in browser" + if [[ $is_steam_deck -eq 1 ]]; then + steam steam://openurl/"$git_url" 2>/dev/null + elif [[ $is_steam_deck -eq 0 ]]; then + browser "$git_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 @@ -261,9 +366,9 @@ set_mode(){ populate(){ while true; do #TODO: add boolean statement for ping flag; affects all column ordinal output - cols="--column="Server" --column="IP" --column="Players" --column="Status" --column="ID" --column="Ping"" + cols="--column="Server" --column="IP" --column="Players" --column="Gametime" --column="Status" --column="ID" --column="Ping"" sel=$(cat $tmp | zenity $sd_res --list $cols --title="DZGUI" --text="DZGUI $version | Mode: $mode | Fav: $fav_label" \ - --separator="$separator" --print-column=2,5 2>/dev/null) + --separator="$separator" --print-column=2,6 2>/dev/null) rc=$? if [[ $rc -eq 0 ]]; then if [[ -z $sel ]]; then warn "No item was selected." @@ -276,21 +381,32 @@ populate(){ done } list_mods(){ - for d in $(installed_mods); do - awk -F\" '/name/ {print $2}' "$workshop_dir"/$d/meta.cpp - done | sort | awk 'NR > 1 { printf(", ") } {printf("%s",$0)}' + if [[ -z $(installed_mods) || -z $(find $workshop_dir -maxdepth 2 -name "*.cpp" | grep .cpp) ]]; then + zenity --info --text="No mods currently installed or incorrect path given" $sd_res 2>/dev/null + else + for d in $(installed_mods); do + awk -F\" '/name/ {print $2}' "$workshop_dir"/$d/meta.cpp + done | sort | zenity --text-info --title="DZGUI" $sd_res 2>/dev/null + fi } connect_to_fav(){ - whitelist=$fav - query_api - sel=$(jq -r '.data[] .attributes | "\(.ip):\(.port)%%\(.id)"' <<< $response) - echo "[DZGUI] Attempting connection to $fav_label" - connect "$sel" + if [[ -n $fav ]]; then + one_shot_launch=1 + query_api + sel=$(jq -r '.data[] .attributes | "\(.ip):\(.port)%%\(.id)"' <<< $response) + echo "[DZGUI] Attempting connection to $fav_label" + connect "$sel" + else + warn "No fav server configured" + fi } main_menu(){ set_mode - set_fav + if [[ -n $fav ]]; then + set_fav + items[3]="Change favorite server" + fi while true; do sel=$(zenity --width=1280 --height=800 --list --title="DZGUI" --text="DZGUI $version | Mode: $mode | Fav: $fav_label" \ --cancel-label="Exit" --ok-label="Select" --column="Select launch option" "${items[@]}" 2>/dev/null) @@ -298,7 +414,7 @@ main_menu(){ if [[ $rc -eq 0 ]]; then if [[ -z $sel ]]; then warn "No item was selected." - elif [[ $sel == "Launch server list" ]]; then + elif [[ $sel == "${items[0]}" ]]; then query_api parse_json <<< "$response" #TODO: create logger function @@ -310,15 +426,20 @@ main_menu(){ else populate fi - elif [[ $sel == "Report bug" ]]; then - report_bug - elif [[ $sel == "List mods" ]]; then - echo "[DZGUI] Listing installed mods" - zenity --info --icon-name="" --title="DZGUI" --text="$(list_mods)" 2>/dev/null - elif [[ $sel == "Add server by ID" ]]; then - add_by_id - elif [[ $sel == "Quick connect to favorite server" ]]; then + elif [[ $sel == "${items[1]}" ]]; then connect_to_fav + elif [[ $sel == "${items[2]}" ]]; then + add_by_id + elif [[ $sel == "${items[3]}" ]]; then + add_by_fav + elif [[ $sel == "${items[4]}" ]]; then + list_mods + elif [[ $sel == "${items[5]}" ]]; then + report_bug + elif [[ $sel == "${items[6]}" ]]; then + help_file + elif [[ $sel == "${items[7]}" ]]; then + changelog | zenity --text-info $sd_res --title="DZGUI" 2>/dev/null else warn "This feature is not yet implemented." fi @@ -328,7 +449,7 @@ main_menu(){ done } parse_json(){ - list=$(jq -r '.data[] .attributes | "\(.name)\t\(.ip):\(.port)\t\(.players)/\(.maxPlayers)\t\(.status)\t\(.id)"') + list=$(jq -r '.data[] .attributes | "\(.name)\t\(.ip):\(.port)\t\(.players)/\(.maxPlayers)\t\(.details.time)\t\(.status)\t\(.id)"') echo -e "$list" > $tmp } check_ping(){ @@ -347,25 +468,26 @@ create_array(){ while read line; do name=$(echo "$line" | awk -F'\t' '{print $1}') #truncate names - if [[ $(echo "$name" | wc -m) -gt 30 ]]; then - name="$(echo $name | awk '{print substr($0,1,30) "..."}')" + 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}') - stat=$(echo "$line" | awk -F'\t' '{print $4}') + 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 $5}') + id=$(echo "$line" | awk -F'\t' '{print $6}') tc=$(awk 'END{print NR}' $tmp) echo "$lc/$tc" echo "# Checking ping: $lc/$tc" ping=$(check_ping "$line") - declare -g -a rows=("${rows[@]}" "$name" "$ip" "$players" "$stat" "$id" "$ping") + declare -g -a rows=("${rows[@]}" "$name" "$ip" "$players" "$time" "$stat" "$id" "$ping") let lc++ done <<< "$list" @@ -374,14 +496,28 @@ create_array(){ set_fav(){ #TODO: test API key here and return errors query_api - if [[ -z $fav ]]; then - fav_label=null - else 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 + write_config > $config_file + printf "[DZGUI] Wrote new config file to %sdztuirc\n" $config_path + zenity --info --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(){ source_dir=$(dirname -- "$(readlink -f -- "$0";)") mv $source_dir/dzgui.sh $source_dir/dzgui.old @@ -390,6 +526,7 @@ download_new_version(){ if [[ $rc -eq 0 ]]; then echo "[DZGUI] Wrote $upstream to $source_dir/dzgui.sh" chmod +x $source_dir/dzgui.sh + touch ${config_path}.unmerged zenity --question --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 @@ -407,7 +544,7 @@ download_new_version(){ } check_version(){ if [[ $version == $upstream ]]; then - : + check_unmerged else echo "[DZGUI] Upstream ($upstream) is > local ($version)" zenity --question --title="DZGUI" --text "Newer version available.\n\nYour version:\t\t\t$version\nUpstream version:\t\t$upstream\n\nAttempt to download latest version?" --width=500 --ok-label="Yes" --cancel-label="No" 2>/dev/null @@ -419,32 +556,69 @@ check_version(){ fi fi } +check_architecture(){ + os_release=$(awk '/steamdeck/' "/etc/os-release") + if [[ -f "/etc/os-release" ]] && [[ -n $os_releasec ]]; 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(){ + #TODO: prevent redundant creation of existent IDs + 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" 2>/dev/null + source $config_file + return + fi + fi + done +} +add_by_fav(){ while true; do - id=$(zenity --entry --text="Enter server ID" --title="DZGUI" 2>/dev/null) + fav_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 + if [[ ! $fav_id =~ ^[0-9]+$ ]]; then zenity --warning --title="DZGUI" --text="Invalid ID" else - new_whitelist="whitelist=\"$whitelist,$id\"" + new_fav="fav=\"$fav_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 occurred, you can restore the file:\n${config_path}dztuirc.old" 2>/dev/null + 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[3]="Change favorite server" return fi fi done } main(){ + run_depcheck check_version + check_architecture config - checks + run_varcheck main_menu }