2022-05-09 13:42:24 +02:00
#!/bin/bash
2022-06-04 22:29:30 +02:00
set -o pipefail
2022-06-08 16:49:26 +02:00
version = 1.1.2
2022-05-09 13:42:24 +02:00
aid = 221100
game = "dayz"
workshop = "https://steamcommunity.com/sharedfiles/filedetails/?id="
api = "https://api.battlemetrics.com/servers"
2022-05-22 21:20:34 +02:00
sd_res = "--width=1280 --height=800"
config_path = " $HOME /.config/dztui/ "
config_file = " ${ config_path } dztuirc "
tmp = /tmp/dztui.tmp
2022-05-29 07:41:56 +02:00
separator = "%%"
2022-06-04 22:29:30 +02:00
git_url = "https://github.com/aclist/dztui/issues"
version_url = "https://raw.githubusercontent.com/aclist/dztui/dzgui/dzgui.sh"
2022-05-09 13:42:24 +02:00
declare -A deps
2022-05-22 21:20:34 +02:00
deps = ( [ awk] = "5.1.1" [ curl] = "7.80.0" [ jq] = "1.6" [ tr] = "9.0" [ zenity] = "3.42.1" )
2022-05-09 13:42:24 +02:00
depcheck( ) {
for dep in " ${ !deps[@] } " ; do
command -v $dep 2>& 1>/dev/null || ( printf "[ERROR] Requires %s >= %s\n" $dep ${ deps [ $dep ] } ; exit 1)
done
}
2022-05-22 21:20:34 +02:00
items = (
"Launch server list"
2022-06-04 22:29:30 +02:00
"Quick connect to favorite server"
"Add server by ID"
"List mods"
"Report bug"
2022-05-22 21:20:34 +02:00
)
2022-06-04 22:29:30 +02:00
warn_and_exit( ) {
zenity --info --title= "DZGUI" --text= " $1 " --icon-name= "dialog-warning" 2>/dev/null
exit
}
2022-05-22 21:20:34 +02:00
warn( ) {
zenity --info --title= "DZGUI" --text= " $1 " --icon-name= "dialog-warning" 2>/dev/null
2022-05-09 13:42:24 +02:00
}
2022-05-22 21:20:34 +02:00
info( ) {
zenity --info --title= "DZGUI" --text= " $1 " --icon-name= "network-wireless" 2>/dev/null
}
2022-05-09 13:42:24 +02:00
query_api( ) {
2022-05-22 21:20:34 +02:00
response = $( curl -s " $api " -H "Authorization: Bearer " $api_key "" -G -d "sort=-players" \
2022-05-09 13:42:24 +02:00
-d " filter[game]= $game " -d " filter[ids][whitelist]= $whitelist " )
if [ [ " $( jq -r 'keys[]' <<< " $response " ) " = = "errors" ] ] ; then
code = $( jq -r '.errors[] .status' <<< $response )
#TODO: fix granular api codes
2022-06-04 22:29:30 +02:00
warn_and_exit " Error $code : malformed API key "
2022-05-09 13:42:24 +02:00
fi
2022-05-22 21:20:34 +02:00
}
write_config( ) {
cat <<-'END'
2022-06-04 22:29:30 +02:00
#Path to DayZ installation (change if using multiple SD cards/drives)
2022-05-29 07:02:39 +02:00
steam_path = "/home/deck/.local/share/Steam"
2022-05-22 21:20:34 +02:00
workshop_dir = " $steam_path /steamapps/workshop/content/ $aid "
game_dir = " $steam_path /steamapps/common/DayZ "
#Your unique API key
api_key = ""
#Comma-separated list of server IDs
whitelist = ""
#Favorite server to fast-connect to (limit one)
fav = ""
#Custom player name (optional, required by some servers)
name = "player"
#Set to 1 to perform dry-run and print launch options
2022-06-04 22:29:30 +02:00
debug = "0"
2022-05-22 21:20:34 +02:00
#(Not implemented) Set to 0 to suppress ping attempt
2022-06-04 22:29:30 +02:00
ping = "1"
2022-05-22 21:20:34 +02:00
END
}
create_config( ) {
mkdir -p $config_path ; write_config > $config_file
info " Config file created at $config_file .\nFill in values and relaunch. "
exit
}
err( ) {
printf "[ERROR] %s\n" " $1 "
}
varcheck( ) {
[ [ -z $api_key ] ] && ( err "Error in key: 'api_key'" )
[ [ -z $whitelist ] ] && ( err "Error in key: 'whitelist'" )
[ [ ! -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( ) {
if [ [ -z $( depcheck) ] ] ; then
:
else
2022-06-04 22:29:30 +02:00
zenity --warning --ok-label= "Exit" --text= " $( depcheck) "
exit
2022-05-22 21:20:34 +02:00
fi
if [ [ -z $( varcheck) ] ] ; then
:
else
2022-06-04 22:29:30 +02:00
zenity --warning --ok-label= "Exit" --text= " $( varcheck) "
exit
2022-05-22 21:20:34 +02:00
fi
}
config( ) {
if [ [ ! -f $config_file ] ] ; then
2022-06-04 22:29:30 +02:00
zenity --question --cancel-label= "Exit" --text= "Config file not found. Should DZGUI create one for you?" 2>/dev/null
2022-05-22 21:20:34 +02:00
code = $?
if [ [ $code -eq 1 ] ] ; then
exit
else
create_config
fi
else
source $config_file
fi
}
2022-06-04 22:29:30 +02:00
manual_mod_install( ) {
l = 0
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
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." 2>/dev/null
else
return
fi
compare
done
passed_mod_check
}
symlinks( ) {
for d in " $workshop_dir " /*; do
id = $( awk -F"= " '/publishedid/ {print $2}' " $d " /meta.cpp | awk -F\; '{print $1}' )
mod = $( awk -F\" '/name/ {print $2}' " $d " /meta.cpp | sed -E 's/[^[:alpha:]0-9]+/_/g; s/^_|_$//g' )
link = " @ $id - $mod "
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"
symlinks
launch
2022-05-22 21:20:34 +02:00
}
2022-06-04 22:29:30 +02:00
connect( ) {
#TODO: sanitize/validate input
ip = $( echo " $1 " | awk -F" $separator " '{print $1}' )
bid = $( echo " $1 " | awk -F" $separator " '{print $2}' )
fetch_mods " $bid "
validate_mods
rc = $?
[ [ $rc -eq 1 ] ] && return
compare
if [ [ -n $diff ] ] ; then
manual_mod_install
else
passed_mod_check
fi
}
2022-05-22 21:20:34 +02:00
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[]' )
2022-05-09 13:42:24 +02:00
}
2022-06-04 22:29:30 +02:00
check_workshop( ) {
curl -Ls " $url ${ modlist [ $i ] } " | grep data-appid | awk -F\" '{print $8}'
}
validate_mods( ) {
url = "https://steamcommunity.com/sharedfiles/filedetails/?id="
newlist = ( )
readarray -t modlist <<< $remote_mods
l = 1
tc = $(( ${# modlist [@] } + 1 ))
echo "[DZGUI] Verifying server modlist integrity"
for ( ( i = 0; i<= ${# modlist [@] } ; i++) ) ; do
echo " $l "
echo " # Verifying mod $l / $tc "
if [ [ $( check_workshop) -eq $aid ] ] ; then
newlist += ( " ${ modlist [ $i ] } " )
fi
sleep 2s
let l++
#array gets lost in pipe without process substitution
done > >( zenity --progress --auto-close --pulsate --title= "DZGUI" 2>/dev/null)
}
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 "
}
2022-05-09 13:42:24 +02:00
concat_mods( ) {
2022-06-08 16:49:26 +02:00
readarray -t serv <<< " $( server_modlist) "
2022-05-09 13:42:24 +02:00
for i in " ${ serv [@] } " ; do
id = $( awk -F"= " '/publishedid/ {print $2}' " $workshop_dir " /$i /meta.cpp | awk -F\; '{print $1}' )
mod = $( awk -F\" '/name/ {print $2}' " $workshop_dir " /$i /meta.cpp | sed -E 's/[^[:alpha:]0-9]+/_/g; s/^_|_$//g' )
link = " @ $id - $mod ; "
echo -e " $link "
done | tr -d '\n' | perl -ple 'chop'
}
launch( ) {
mods = $( concat_mods)
if [ [ $debug -eq 1 ] ] ; then
2022-06-04 22:29:30 +02:00
zenity --warning --title= "DZGUI" \
--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
2022-05-09 13:42:24 +02:00
else
steam -applaunch $aid -connect= $ip -nolauncher -nosplash -skipintro -name= $name \" -mod= $mods \"
exit
fi
}
2022-06-04 22:29:30 +02:00
report_bug( ) {
echo "[DZGUI] Opening issues page"
steam steam://openurl/$git_url 2>/dev/null
2022-05-22 21:20:34 +02:00
}
set_mode( ) {
if [ [ $debug -eq 1 ] ] ; then
2022-06-04 22:29:30 +02:00
mode = debug
2022-05-22 21:20:34 +02:00
else
mode = normal
fi
}
2022-06-04 22:29:30 +02:00
populate( ) {
2022-05-22 21:20:34 +02:00
while true; do
2022-06-04 22:29:30 +02:00
#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""
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)
rc = $?
if [ [ $rc -eq 0 ] ] ; then if [ [ -z $sel ] ] ; then
warn "No item was selected."
else
connect $sel
fi
else
return
fi
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)}'
}
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 "
}
main_menu( ) {
2022-05-22 21:20:34 +02:00
set_mode
2022-06-04 22:29:30 +02:00
set_fav
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)
rc = $?
if [ [ $rc -eq 0 ] ] ; then
if [ [ -z $sel ] ] ; then
warn "No item was selected."
elif [ [ $sel = = "Launch server list" ] ] ; then
query_api
parse_json <<< " $response "
#TODO: create logger function
echo "[DZGUI] Checking response time of servers"
create_array | zenity --progress --pulsate --title= "DZGUI" --auto-close 2>/dev/null
rc = $?
if [ [ $rc -eq 1 ] ] ; then
:
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
connect_to_fav
else
warn "This feature is not yet implemented."
fi
2022-05-22 21:20:34 +02:00
else
2022-06-04 22:29:30 +02:00
return
2022-05-22 21:20:34 +02:00
fi
done
}
parse_json( ) {
list = $( jq -r '.data[] .attributes | "\(.name)\t\(.ip):\(.port)\t\(.players)/\(.maxPlayers)\t\(.status)\t\(.id)"' )
echo -e " $list " > $tmp
2022-05-09 13:42:24 +02:00
}
check_ping( ) {
ping_ip = $( echo " $1 " | awk -F'\t' '{print $2}' | awk -F: '{print $1}' )
2022-06-04 22:29:30 +02:00
ms = $( ping -c 1 -W 1 " $ping_ip " | awk -Ftime= '/time=/ {print $2}' )
2022-05-22 21:20:34 +02:00
if [ [ -z $ms ] ] ; then
echo "Timeout"
else
echo " $ms "
fi
}
2022-05-09 13:42:24 +02:00
create_array( ) {
list = $( cat $tmp )
#TODO: improve error handling for null values
2022-05-29 08:33:14 +02:00
lc = 1
2022-05-09 13:42:24 +02:00
while read line; do
name = $( echo " $line " | awk -F'\t' '{print $1}' )
2022-05-22 21:20:34 +02:00
#truncate names
2022-05-09 13:42:24 +02:00
if [ [ $( echo " $name " | wc -m) -gt 30 ] ] ; then
name = " $( echo $name | awk '{print substr($0,1,30) "..."}' ) "
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}' )
2022-06-04 22:29:30 +02:00
2022-05-22 21:20:34 +02:00
#yad only
#[[ $stat == "online" ]] && stat="<span color='#77ff33'>online</span>" || :
2022-06-04 22:29:30 +02:00
2022-05-09 13:42:24 +02:00
#TODO: probe offline return codes
id = $( echo " $line " | awk -F'\t' '{print $5}' )
2022-05-29 08:33:14 +02:00
tc = $( awk 'END{print NR}' $tmp )
echo " $lc / $tc "
echo " # Checking ping: $lc / $tc "
2022-05-09 13:42:24 +02:00
ping = $( check_ping " $line " )
declare -g -a rows = ( " ${ rows [@] } " " $name " " $ip " " $players " " $stat " " $id " " $ping " )
2022-05-29 08:33:14 +02:00
let lc++
done <<< " $list "
for i in " ${ rows [@] } " ; do echo -e " $i " ; done > $tmp
2022-05-09 13:42:24 +02:00
}
2022-06-04 22:29:30 +02:00
set_fav( ) {
#whitelist=$fav
#TODO: this is redundant; merge these functions
query_api
echo "[DZGUI] Setting favorite server"
[ [ $( awk -F\" '/fav=/ {print $2}' $config_file ) ] ] &&
#TODO: test API key here and return errors
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' )
}
download_new_version( ) {
source_dir = $( dirname -- " $( readlink -f -- " $0 " ; ) " )
mv $source_dir /dzgui.sh $source_dir /dzgui.old
curl -Ls " $version_url " > $source_dir /dzgui.sh
rc = $?
if [ [ $rc -eq 0 ] ] ; then
echo " [DZGUI] Wrote $upstream to $source_dir /dzgui.sh "
2022-06-04 22:39:20 +02:00
chmod +x $source_dir /dzgui.sh
2022-06-04 22:29:30 +02:00
zenity --info --title= "DZGUI" --text " DZGUI $upstream successfully downloaded.\nExit and restart to use the new version. " 2>/dev/null
exit
else
mv $source_dir /dzgui.old $source_dir /dzgui.sh
zenity --info --title= "DZGUI" --text "Failed to download new version." 2>/dev/null
return
fi
}
check_version( ) {
2022-06-04 22:39:20 +02:00
upstream = $( curl -Ls " $version_url " | awk -F= '/^version=/ {print $2}' )
2022-06-04 22:29:30 +02:00
if [ [ $version = = $upstream ] ] ; then
:
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
rc = $?
if [ [ $rc -eq 1 ] ] ; then
return
else
download_new_version
fi
fi
}
add_by_id( ) {
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"
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 occurred, you can restore the file:\n ${ config_path } dztuirc.old " 2>/dev/null
source $config_file
return
fi
fi
done
}
2022-05-09 13:42:24 +02:00
main( ) {
2022-06-04 22:29:30 +02:00
check_version
2022-05-22 21:20:34 +02:00
config
2022-05-09 13:42:24 +02:00
checks
2022-05-22 21:20:34 +02:00
main_menu
2022-05-09 13:42:24 +02:00
}
main