mirror of
https://github.com/aclist/dztui.git
synced 2024-12-28 21:32:36 +01:00
1929 lines
61 KiB
Bash
Executable file
1929 lines
61 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
set -o pipefail
|
|
version=3.3.13
|
|
|
|
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"
|
|
hist_file="${config_path}/history"
|
|
tmp=/tmp/dzgui.tmp
|
|
fifo=/tmp/table.tmp
|
|
debug_log="$PWD/DZGUI_DEBUG.log"
|
|
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"
|
|
sponsor_url="https://github.com/sponsors/aclist"
|
|
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"
|
|
vdf2json_url="$stable_url/helpers/vdf2json.py"
|
|
notify_url="$stable_url/helpers/d.html"
|
|
notify_img_url="$stable_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
|
|
|
|
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(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
echo "# Checking 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}')
|
|
logger INFO "News: $result"
|
|
}
|
|
print_news(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
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
|
|
}
|
|
|
|
declare -A deps
|
|
deps=([awk]="5.1.1" [curl]="7.80.0" [jq]="1.6" [tr]="9.0" [$steamsafe_zenity]="3.42.1")
|
|
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"
|
|
}
|
|
|
|
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(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
if [[ ! $(command -v wmctrl) ]] && [[ ! $(command -v xdotool) ]]; then
|
|
echo "100"
|
|
warn "Missing dependency: requires 'wmctrl' or 'xdotool'.\nInstall from your system's package manager."
|
|
logger ERROR "Missing watcher dependencies"
|
|
exit 1
|
|
fi
|
|
}
|
|
init_items(){
|
|
#array order determines menu selector; this is destructive
|
|
items=(
|
|
"[Connect]"
|
|
" Server browser"
|
|
" My servers"
|
|
" Quick connect to favorite server"
|
|
" Connect by IP"
|
|
" Recent servers (last 10)"
|
|
"[Manage servers]"
|
|
" Add server by ID"
|
|
" Add favorite server"
|
|
" Delete server"
|
|
"[Options]"
|
|
" List installed mods"
|
|
" View changelog"
|
|
" Advanced options"
|
|
"[Help]"
|
|
" Help file ⧉"
|
|
" Report bug ⧉"
|
|
" Forum ⧉"
|
|
" Sponsor ⧉"
|
|
" Hall of fame ⧉"
|
|
)
|
|
}
|
|
warn(){
|
|
$steamsafe_zenity --info --title="DZGUI" --text="$1" --width=500 --icon-name="dialog-warning" 2>/dev/null
|
|
}
|
|
info(){
|
|
$steamsafe_zenity --info --title="DZGUI" --text="$1" --width=500 2>/dev/null
|
|
}
|
|
set_api_params(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
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(){
|
|
logger INFO ${FUNCNAME[0]}
|
|
echo "# Querying API"
|
|
#TODO: prevent drawing list if null values returned without API error
|
|
if [[ $one_shot_launch -eq 1 ]]; then
|
|
list_of_ids="$fav"
|
|
else
|
|
list_of_ids="$whitelist"
|
|
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"
|
|
|
|
#Path to default Steam client
|
|
default_steam_path="$default_steam_path"
|
|
|
|
#Preferred Steam launch command (for Flatpak support)
|
|
preferred_client="$preferred_client"
|
|
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="$stable_url/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
|
|
}
|
|
find_library_folder(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
logger INFO "User picked directory: '$1'"
|
|
steam_path="$(python3 "$helpers_path/vdf2json.py" -i "$1/steamapps/libraryfolders.vdf" | jq -r '.libraryfolders[]|select(.apps|has("221100")).path')"
|
|
logger INFO "Steam path resolved to: $steam_path"
|
|
}
|
|
file_picker(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
local path=$($steamsafe_zenity --file-selection --directory 2>/dev/null)
|
|
logger INFO "File picker path resolve to: $path"
|
|
if [[ -z "$path" ]]; then
|
|
logger INFO "Path was empty"
|
|
return
|
|
else
|
|
default_steam_path="$path"
|
|
find_library_folder "$default_steam_path"
|
|
fi
|
|
}
|
|
create_config(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
check_pyver
|
|
write_to_config(){
|
|
mkdir -p $config_path
|
|
write_config > $config_file
|
|
info "Config file created at $config_file."
|
|
source $config_file
|
|
return
|
|
}
|
|
while true; do
|
|
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')
|
|
name="${args[0]}"
|
|
api_key="${args[1]}"
|
|
steam_api="${args[2]}"
|
|
|
|
[[ -z $player_input ]] && exit
|
|
if [[ -z $api_key ]] || [[ -z $steam_api ]]; then
|
|
warn "API key cannot be empty"
|
|
#TODO: test BM key
|
|
elif [[ $(test_steam_api) -eq 1 ]]; then
|
|
warn "Invalid Steam API key"
|
|
elif [[ $(test_bm_api) -eq 1 ]]; then
|
|
warn "Invalid BM API key"
|
|
else
|
|
while true; do
|
|
logger INFO "steamsafe_zenity is $steamsafe_zenity"
|
|
[[ -n $steam_path ]] && { write_to_config; return; }
|
|
find_default_path
|
|
find_library_folder "$default_steam_path"
|
|
if [[ -z $steam_path ]]; then
|
|
logger WARN "Steam path was empty"
|
|
zenity --question --text="DayZ not found or not installed at the chosen path." --ok-label="Choose path manually" --cancel-label="Exit"
|
|
if [[ $? -eq 0 ]]; then
|
|
logger INFO "User selected file picker"
|
|
file_picker
|
|
else
|
|
exit
|
|
fi
|
|
else
|
|
write_to_config
|
|
fi
|
|
done
|
|
fi
|
|
done
|
|
|
|
}
|
|
err(){
|
|
printf "[ERROR] %s\n" "$1"
|
|
}
|
|
varcheck(){
|
|
if [[ -z $api_key ]] || [[ ! -d $steam_path ]] || [[ ! -d $game_dir ]]; then
|
|
echo 1
|
|
fi
|
|
}
|
|
run_depcheck(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
if [[ -n $(depcheck) ]]; then
|
|
echo "100"
|
|
logger ERROR "Missing dependencies, quitting"
|
|
$steamsafe_zenity --warning --ok-label="Exit" --title="DZGUI" --text="$(depcheck)"
|
|
exit
|
|
fi
|
|
}
|
|
logger(){
|
|
local date="$(date "+%F %T")"
|
|
local tag="$1"
|
|
local string="$2"
|
|
printf "[%s] [%s] %s\n" "$date" "$tag" "$string" >> "$debug_log"
|
|
}
|
|
check_pyver(){
|
|
pyver=$(python3 --version | awk '{print $2}')
|
|
if [[ -z $pyver ]] || [[ ${pyver:0:1} -lt 3 ]]; then
|
|
warn "Requires python >=3.0" &&
|
|
exit
|
|
fi
|
|
}
|
|
run_varcheck(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
source $config_file
|
|
workshop_dir="$steam_path/steamapps/workshop/content/$aid"
|
|
game_dir="$steam_path/steamapps/common/DayZ"
|
|
if [[ $(varcheck) -eq 1 ]]; then
|
|
$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
|
|
logger ERROR "Malformed config vars"
|
|
exit
|
|
else
|
|
create_config
|
|
fi
|
|
fi
|
|
}
|
|
config(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
if [[ ! -f $config_file ]]; then
|
|
logger WARN "Config file missing"
|
|
logger INFO "steamsafe_zenity is $steamsafe_zenity"
|
|
$steamsafe_zenity --width=500 --info --text="Config file not found. Click OK to proceed to first-time setup." 2>/dev/null
|
|
code=$?
|
|
logger INFO "Return code $code"
|
|
#TODO: prevent progress if user hits ESC
|
|
if [[ $code -eq 1 ]]; then
|
|
exit
|
|
else
|
|
create_config
|
|
fi
|
|
else
|
|
source $config_file
|
|
fi
|
|
|
|
}
|
|
steam_deck_mods(){
|
|
until [[ -z $diff ]]; do
|
|
next=$(echo -e "$diff" | head -n1)
|
|
$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_cmd steam://url/CommunityFilePage/$next 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
|
|
compare
|
|
done
|
|
}
|
|
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}')
|
|
}
|
|
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_cmd "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 > >($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 > >($steamsafe_zenity --pulsate --progress --auto-close --width=500 2>/dev/null)
|
|
launch
|
|
else
|
|
return 1
|
|
fi
|
|
fi
|
|
}
|
|
encode(){
|
|
echo "$1" | md5sum | cut -c -8
|
|
}
|
|
stale_symlinks(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
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"
|
|
legacy_symlinks
|
|
symlinks
|
|
echo "100"
|
|
|
|
}
|
|
auto_mod_install(){
|
|
#[[ -z $(is_steam_running) ]] && { $steamsafe_zenity --info --text "Steam must be running to use this feature."; return; }
|
|
popup 300
|
|
rc=$?
|
|
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"
|
|
console_dl "$diff" &&
|
|
$steam_cmd steam://open/downloads && 2>/dev/null 1>&2
|
|
win=$(xdotool search --name "DZG Watcher")
|
|
xdotool windowactivate $win
|
|
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. ESC cancels"
|
|
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 > >($steamsafe_zenity --pulsate --progress --title="DZGUI" --auto-close --width=500 2>/dev/null)
|
|
launch
|
|
else
|
|
manual_mod_install
|
|
fi
|
|
else
|
|
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[@]}
|
|
[[ $max -eq 0 ]] && return
|
|
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(){
|
|
[[ $force_update -eq 1 ]] && echo "# Checking mod versions"
|
|
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="$(printf "%s\n%s\n" "$diff" "${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"
|
|
}
|
|
is_steam_running(){
|
|
xdotool search --onlyvisible --name "Steam"
|
|
}
|
|
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" > >($steamsafe_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
|
|
[[ $auto_install -eq 2 ]] && merge_modlists
|
|
if [[ -n $diff ]]; then
|
|
case $auto_install in
|
|
1|2) auto_mod_install ;;
|
|
*) manual_mod_install ;;
|
|
esac
|
|
else
|
|
passed_mod_check > >($steamsafe_zenity --pulsate --progress --auto-close --width=500 2>/dev/null)
|
|
update_history
|
|
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"
|
|
$steamsafe_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' 2>/dev/null)
|
|
#old servers may become stale and return nothing
|
|
if [[ -n $ct ]]; then
|
|
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}')
|
|
local qport=$(awk -F: '{print $2}' <<< $addr)
|
|
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 '(?<!\d)\d{2}:\d{2}(?!\d)')
|
|
echo "$name"
|
|
echo "$ip_port"
|
|
echo "$players/$max_players"
|
|
echo "$time"
|
|
echo "$qport"
|
|
done
|
|
fi
|
|
}
|
|
history_table(){
|
|
[[ -f /tmp/dz.hist ]] && rm /tmp/dz.hist
|
|
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 0.5s
|
|
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 | $steamsafe_zenity --width 1200 --height 800 --title="DZGUI" --text="Recent servers" --list --column=Name --column=IP --column=Players --column=Gametime --column=Qport --print-column=2,5 --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 addr="$(echo "$sel" | awk -F"%%" '{print $1}')"
|
|
local qport="$(echo "$sel" | awk -F"%%" '{print $2}')"
|
|
local ip=$(awk -F: '{print $1}' <<< $addr)
|
|
local gameport=$(awk -F: '{print $2}' <<< $addr)
|
|
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
|
|
sel=$(prepare_ip_list "$meta_file" | $steamsafe_zenity --width 1200 --height 800 --text="One or more maps found at this server. Select map from the list below" --title="DZGUI" --list --column=Name --column=IP --column=Players --column=Gametime --column=Qport --print-column=2 --separator=%% 2>/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(){
|
|
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)
|
|
}
|
|
|
|
#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
|
|
}
|
|
test_bm_api(){
|
|
local code=$(curl -ILs "$api" -H "Authorization: Bearer "$api_key"" -G \
|
|
-d "filter[game]=$game" | grep -E "^HTTP")
|
|
[[ $code =~ 401 ]] && 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"
|
|
$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=$($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
|
|
$steamsafe_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=$($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
|
|
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
|
|
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 -u) <(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_cmd -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)"
|
|
$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"
|
|
$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"
|
|
$steamsafe_zenity --width=500 --title="DZGUI" --info --text="Launch conditions satisfied.\nDayZ will now launch after clicking [OK]." 2>/dev/null
|
|
$steam_cmd -applaunch $aid -connect=$ip -nolauncher -nosplash -skipintro -name=$name \"-mod=$mods\"
|
|
exit
|
|
fi
|
|
one_shot_launch=0
|
|
}
|
|
browser(){
|
|
if [[ $is_steam_deck -eq 1 ]]; then
|
|
steam steam://openurl/"$1" 2>/dev/null
|
|
elif [[ $is_steam_deck -eq 0 ]]; then
|
|
if [[ -n "$BROWSER" ]]; then
|
|
"$BROWSER" "$1" 2>/dev/null
|
|
else
|
|
xdg-open "$1" 2>/dev/null
|
|
fi
|
|
fi
|
|
}
|
|
report_bug(){
|
|
browser "$issues_url"
|
|
}
|
|
forum(){
|
|
browser "$forum_url"
|
|
}
|
|
help_file(){
|
|
browser "$help_url"
|
|
}
|
|
sponsor(){
|
|
browser "$sponsor_url"
|
|
}
|
|
hof(){
|
|
browser "${help_url}#_hall_of_fame"
|
|
}
|
|
set_mode(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
if [[ $debug -eq 1 ]]; then
|
|
mode=debug
|
|
else
|
|
mode=normal
|
|
fi
|
|
logger INFO "Mode is $mode"
|
|
}
|
|
delete_by_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
|
|
echo "[DZGUI] Removed $server_id from key 'whitelist'"
|
|
$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}')
|
|
$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
|
|
source $config_file
|
|
unset delete
|
|
else
|
|
local lookup_ip=$(echo "$sel" | awk -F: '{print $1}')
|
|
ip=$lookup_ip
|
|
fetch_ip_metadata
|
|
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
|
|
local jad=$(echo "$res" | jq -r '.addr')
|
|
if [[ $(<<< "$jad" wc -l ) -gt 1 ]]; then
|
|
ip_table
|
|
elif [[ $(<<< "$jad" wc -l ) -eq 1 ]]; then
|
|
local gameport="$(echo "$res" | jq -r '.gameport')"
|
|
local ip="$(echo "$jad" | awk -F: '{print $1}')"
|
|
local qport=$(echo "$jad" | awk -F: '{print $2}')
|
|
local sa_ip=$(echo "$ip:$gameport%%$qport")
|
|
qport_list="$sa_ip"
|
|
local sel="$ip:$gameport%%$qport"
|
|
connect "$sel" "ip"
|
|
fi
|
|
fi
|
|
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
|
|
$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)
|
|
awk -v d=$dir -F\" '/name/ {printf "%s\t%s\t", $2,d}' "$gamedir"/$d/meta.cpp
|
|
printf "%s\n" "$(basename $(readlink -f $game_dir/$dir))"
|
|
done | sort -k1
|
|
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(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
logger INFO "Header mode is $1"
|
|
print_news
|
|
[[ $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 | $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 | $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=$($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
|
|
}
|
|
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
|
|
Linux: $(uname -mrs)
|
|
Version: $version
|
|
Branch: $branch
|
|
Whitelist: $whitelist
|
|
Steam path: $steam_path
|
|
Workshop path: $workshop_dir
|
|
Game path: $game_dir
|
|
|
|
Mods:
|
|
$(list_mods)
|
|
DOC
|
|
}
|
|
focus_beta_client(){
|
|
steam steam://open/console 2>/dev/null 1>&2 &&
|
|
sleep 1s
|
|
wid(){
|
|
#wmctrl -ilx | awk 'tolower($3) == "steam.steam"' | grep 'Steam$' | awk '{print $1}'
|
|
wmctrl -ilx | awk '$3 == "steamwebhelper.steam" {print $1}'
|
|
}
|
|
until [[ -n $(wid) ]]; do
|
|
:
|
|
done
|
|
wmctrl -ia $(wid)
|
|
sleep 0.1s
|
|
wid=$(xdotool getactivewindow)
|
|
echo wid is $wid
|
|
local geo=$(xdotool getwindowgeometry $wid)
|
|
local pos=$(<<< "$geo" awk 'NR==2 {print $2}' | sed 's/,/ /')
|
|
local dim=$(<<< "$geo" awk 'NR==3 {print $2}' | sed 's/x/ /')
|
|
local pos1=$(<<< "$pos" awk '{print $1}')
|
|
local pos2=$(<<< "$pos" awk '{print $2}')
|
|
local dim1=$(<<< "$dim" awk '{print $1}')
|
|
local dim2=$(<<< "$dim" awk '{print $2}')
|
|
local dim1=$(((dim1/2)+pos1))
|
|
local dim2=$(((dim2/2)+pos2))
|
|
echo moving mouse
|
|
xdotool mousemove $dim1 $dim2
|
|
xdotool click 1
|
|
sleep 0.5s
|
|
xdotool key Tab
|
|
}
|
|
console_dl(){
|
|
readarray -t modids <<< "$@"
|
|
focus_beta_client
|
|
# steam steam://open/console 2>/dev/null 1>&2 &&
|
|
# sleep 1s
|
|
#https://github.com/jordansissel/xdotool/issues/67
|
|
#https://dwm.suckless.org/patches/current_desktop/
|
|
# local wid=$(xdotool search --onlyvisible --name Steam)
|
|
#xdotool windowactivate $wid
|
|
sleep 1.5s
|
|
for i in "${modids[@]}"; do
|
|
xdotool type --delay 0 "workshop_download_item $aid $i"
|
|
sleep 0.5s
|
|
xdotool key Return
|
|
sleep 0.5s
|
|
done
|
|
}
|
|
find_default_path(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
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 "/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@@')
|
|
}
|
|
if [[ $is_steam_deck -eq 1 ]]; then
|
|
default_steam_path="$HOME/.local/share/Steam"
|
|
else
|
|
local def_path
|
|
local ub_path
|
|
local flat_path
|
|
def_path="$HOME/.local/share/Steam"
|
|
ub_path="$HOME/.steam/steam"
|
|
flat_path="$HOME/.var/app/com.valvesoftware.Steam/data/Steam"
|
|
|
|
if [[ -d "$def_path" ]]; then
|
|
default_steam_path="$def_path"
|
|
elif [[ -d "$ub_path" ]]; then
|
|
default_steam_path="$ub_path"
|
|
elif [[ -d $flat_path ]]; then
|
|
default_steam_path="$flat_path"
|
|
else
|
|
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 ;;
|
|
*manual*)
|
|
zenity --info --text="\nSelect the top-level entry point to the location where Steam (not DayZ)\nis installed and before entering the \"steamapps\" path.\n\nE.g., if Steam is installed at:\n\"/media/mydrive/Steam\"\n\nCorrect:\n- \"/media/mydrive/Steam\"\n\nIncorrect:\n- \"/media/mydrive/Steam/steamapps/common/DayZ\"\n- \"/media/mydrive/\"" --width=500 &&
|
|
file_picker ;;
|
|
esac
|
|
fi
|
|
fi
|
|
}
|
|
popup(){
|
|
pop(){
|
|
$steamsafe_zenity --info --text="$1" --title="DZGUI" --width=500 2>/dev/null
|
|
}
|
|
case "$1" in
|
|
100) pop "This feature requires xdotool and wmctrl.";;
|
|
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." ;;
|
|
600) pop "No preferred servers set." ;;
|
|
700) pop "Toggled to Flatpak Steam." ;;
|
|
800) pop "Toggled to native Steam." ;;
|
|
900) pop "This feature is not supported on Steam Deck." ;;
|
|
1000) pop "No recent history."
|
|
esac
|
|
}
|
|
toggle_console_dl(){
|
|
[[ $is_steam_deck -eq 1 ]] && { popup 900; return; }
|
|
[[ ! $(command -v xdotool) ]] && { popup 100; return; }
|
|
[[ ! $(command -v wmctrl) ]] && { 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"
|
|
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
|
|
}
|
|
toggle_steam_binary(){
|
|
case "$steam_cmd" in
|
|
steam)
|
|
steam_cmd="flatpak run com.valvesoftware.Steam"
|
|
update_steam_cmd
|
|
popup 700
|
|
;;
|
|
flatpak*)
|
|
steam_cmd="steam"
|
|
update_steam_cmd
|
|
popup 800;;
|
|
esac
|
|
}
|
|
options_menu(){
|
|
case "$auto_install" in
|
|
0|1|"") auto_hr="OFF"; ;;
|
|
2) auto_hr="ON"; ;;
|
|
esac
|
|
debug_list=(
|
|
"Toggle branch"
|
|
"Toggle debug mode"
|
|
"Output system info"
|
|
"Toggle auto mod install [$auto_hr]"
|
|
)
|
|
#TODO: tech debt: drop old flags
|
|
[[ $auto_install -eq 2 ]] || [[ $auto_install -eq 1 ]] && debug_list+=("Force update local mods")
|
|
case "$steam_cmd" in
|
|
steam) steam_hr=Steam ;;
|
|
flatpak*) steam_hr=Flatpak ;;
|
|
esac
|
|
[[ $toggle_steam -eq 1 ]] && debug_list+=("Toggle native Steam or Flatpak [$steam_hr]")
|
|
debug_sel=$($steamsafe_zenity --list --width=1280 --height=800 --column="Options" --title="DZGUI" --hide-header "${debug_list[@]}" 2>/dev/null)
|
|
[[ -z $debug_sel ]] && return
|
|
case "$debug_sel" in
|
|
"Toggle branch")
|
|
enforce_dl=1
|
|
toggle_branch &&
|
|
check_version
|
|
;;
|
|
"Toggle debug mode") toggle_debug ;;
|
|
"Output system info")
|
|
source_script=$(realpath "$0")
|
|
source_dir=$(dirname "$source_script")
|
|
generate_log > "$source_dir/DZGUI.log"
|
|
$steamsafe_zenity --info --width=500 --title="DZGUI" --text="Wrote log file to \n$source_dir/log" 2>/dev/null
|
|
printf "[DZGUI] Wrote log file to %s/log\n" "$source_dir"
|
|
;;
|
|
Toggle[[:space:]]auto*) toggle_console_dl ;;
|
|
"Force update local mods")
|
|
force_update=1
|
|
force_update_mods
|
|
merge_modlists > >($steamsafe_zenity --pulsate --progress --no-cancel --auto-close --title="DZGUI" --width=500 2>/dev/null)
|
|
auto_mod_install
|
|
;;
|
|
Toggle[[:space:]]native*) toggle_steam_binary ;;
|
|
esac
|
|
}
|
|
query_and_connect(){
|
|
[[ -z $whitelist ]] && { popup 600; return; }
|
|
q(){
|
|
query_api
|
|
parse_json
|
|
create_array
|
|
}
|
|
q | $steamsafe_zenity --width=500 --progress --pulsate --title="DZGUI" --auto-close 2>/dev/null
|
|
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" | $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
|
|
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 in-game: %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 > >($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=$($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" TRUE "Low population" FALSE "Non-ASCII titles" FALSE "Keyword search" $sd_res 2>/dev/null)
|
|
if [[ $sels =~ Keyword ]]; then
|
|
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
|
|
filters=$(echo "$sels" | sed 's/|/, /g;s/ (untick to select from map list)//')
|
|
}
|
|
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")
|
|
LC_NUMERIC=C 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 > >($steamsafe_zenity --pulsate --progress --auto-close --width=500 2>/dev/null)
|
|
disabled+=("All maps")
|
|
fi
|
|
[[ $ret -eq 97 ]] && return
|
|
prepare_filters > >($steamsafe_zenity --pulsate --progress --auto-close --width=500 2>/dev/null)
|
|
if [[ $(echo "$response" | jq 'length') -eq 0 ]]; then
|
|
$steamsafe_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=$($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
|
|
else
|
|
rm $fifo
|
|
kill -9 $pid
|
|
echo $sel
|
|
fi
|
|
}
|
|
debug_servers(){
|
|
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
|
|
}
|
|
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"
|
|
response=$(curl -Ls "$url" | jq -r '.response.servers')
|
|
}
|
|
fetch > >($steamsafe_zenity --pulsate --progress --auto-close --width=500 2>/dev/null)
|
|
total_servers=$(echo "$response" | jq 'length' | numfmt --grouping)
|
|
players_online=$(curl -Ls "https://api.steampowered.com/ISteamUserStats/GetNumberOfCurrentPlayers/v1/?appid=$aid" \
|
|
| jq '.response.player_count' | numfmt --grouping)
|
|
debug_servers
|
|
local sel=$(munge_servers)
|
|
if [[ -z $sel ]]; then
|
|
unset filters
|
|
unset search
|
|
ret=98
|
|
sd_res="--width=1280 --height=800"
|
|
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"
|
|
sd_res="--width=1280 --height=800"
|
|
else
|
|
sd_res="--width=1280 --height=800"
|
|
return
|
|
fi
|
|
}
|
|
mods_disk_size(){
|
|
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(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
logger INFO "Setting mode"
|
|
set_mode
|
|
while true; do
|
|
set_header ${FUNCNAME[0]}
|
|
rc=$?
|
|
logger INFO "set_header rc is $rc"
|
|
if [[ $rc -eq 0 ]]; then
|
|
case "$sel" in
|
|
"") warn "No item was selected." ;;
|
|
" Server browser") server_browser ;;
|
|
" My servers") query_and_connect ;;
|
|
" Quick connect to favorite server") connect_to_fav ;;
|
|
" Connect by IP") connect_by_ip ;;
|
|
" Recent servers (last 10)") history_table ;;
|
|
" Add server by ID") add_by_id ;;
|
|
" Add favorite server") add_by_fav ;;
|
|
" Change favorite server") add_by_fav ;;
|
|
" Delete server") delete=1; query_and_connect ;;
|
|
" List installed mods")
|
|
list_mods | sed 's/\t/\n/g' | $steamsafe_zenity --list --column="Mod" --column="Symlink" --column="Dir" \
|
|
--title="DZGUI" $sd_res --text="$(mods_disk_size)" \
|
|
--print-column="" 2>/dev/null
|
|
;;
|
|
" View changelog") changelog | $steamsafe_zenity --text-info $sd_res --title="DZGUI" 2>/dev/null ;;
|
|
" Advanced options")
|
|
options_menu
|
|
main_menu
|
|
return
|
|
;;
|
|
" Help file ⧉") help_file ;;
|
|
" Report bug ⧉") report_bug ;;
|
|
" Forum ⧉") forum ;;
|
|
" NEW: Sponsor ⧉") sponsor ;;
|
|
" NEW: Hall of fame ⧉") hof ;;
|
|
esac
|
|
else
|
|
logger INFO "Returning from main menu"
|
|
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(){
|
|
echo "# Parsing servers"
|
|
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
|
|
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=()
|
|
#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}')
|
|
|
|
#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 < <(cat "$tmp" | sort -k1)
|
|
|
|
for i in "${rows[@]}"; do echo -e "$i"; done > $tmp
|
|
}
|
|
set_fav(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
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
|
|
else
|
|
fav_label="'$fav_label'"
|
|
fi
|
|
logger INFO "Fav label is $fav_label"
|
|
}
|
|
check_unmerged(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
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
|
|
$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(){
|
|
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"
|
|
$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 | $steamsafe_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
|
|
$steamsafe_zenity --info --title="DZGUI" --text "[ERROR] 99: Failed to download new version." 2>/dev/null
|
|
return
|
|
fi
|
|
|
|
}
|
|
check_branch(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
if [[ $branch == "stable" ]]; then
|
|
version_url="$stable_url/dzgui.sh"
|
|
elif [[ $branch == "testing" ]]; then
|
|
version_url="$testing_url/dzgui.sh"
|
|
fi
|
|
logger INFO "Branch is $branch"
|
|
upstream=$(curl -Ls "$version_url" | awk -F= '/^version=/ {print $2}')
|
|
logger INFO "Upstream version is $version"
|
|
}
|
|
enforce_dl(){
|
|
download_new_version > >($steamsafe_zenity --progress --pulsate --auto-close --no-cancel --width=500)
|
|
}
|
|
prompt_dl(){
|
|
$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 > >($steamsafe_zenity --progress --pulsate --auto-close --no-cancel --width=500)
|
|
fi
|
|
}
|
|
check_version(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
[[ -f $config_file ]] && source $config_file
|
|
[[ -z $branch ]] && branch="stable"
|
|
check_branch
|
|
[[ ! -f "$freedesktop_path/dzgui.desktop" ]] && freedesktop_dirs
|
|
if [[ $version == $upstream ]]; then
|
|
logger INFO "Local version is same as upstream"
|
|
check_unmerged
|
|
else
|
|
# echo "100"
|
|
echo "[DZGUI] Upstream ($upstream) != local ($version)"
|
|
logger INFO "Local and remote version mismatch"
|
|
if [[ $enforce_dl -eq 1 ]]; then
|
|
enforce_dl
|
|
else
|
|
prompt_dl
|
|
fi
|
|
fi
|
|
}
|
|
check_architecture(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
cpu=$(cat /proc/cpuinfo | grep "AMD Custom APU 0405")
|
|
if [[ -n "$cpu" ]]; then
|
|
is_steam_deck=1
|
|
logger INFO "Setting architecture to 'Steam Deck'"
|
|
echo "[DZGUI] Setting architecture to 'Steam Deck'"
|
|
else
|
|
is_steam_deck=0
|
|
logger INFO "Setting architecture to 'desktop'"
|
|
echo "[DZGUI] Setting architecture to 'desktop'"
|
|
fi
|
|
}
|
|
add_by_id(){
|
|
#FIXME: prevent redundant creation of existent IDs (for neatness)
|
|
while true; do
|
|
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
|
|
$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\""
|
|
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
|
|
$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
|
|
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(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
if [[ -n $fav ]]; then
|
|
set_fav
|
|
items[8]=" Change favorite server"
|
|
fi
|
|
}
|
|
check_map_count(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
[[ $is_steam_deck -eq 1 ]] && return
|
|
local count=1048576
|
|
logger INFO "Checking system map count"
|
|
echo "[DZGUI] Checking system map count"
|
|
if [[ ! -f /etc/sysctl.d/dayz.conf ]]; then
|
|
$steamsafe_zenity --question --width=500 --title="DZGUI" --cancel-label="Cancel" --ok-label="OK" --text "sudo password required to check system vm map count."
|
|
local rc=$?
|
|
logger INFO "Return code is $rc"
|
|
if [[ $rc -eq 0 ]]; then
|
|
local pass
|
|
logger INFO "Prompting user for sudo escalation"
|
|
pass=$($steamsafe_zenity --password)
|
|
local rc
|
|
logger INFO "Return code is $rc"
|
|
[[ $rc -eq 1 ]] && exit 1
|
|
local ct=$(sudo -S <<< "$pass" sh -c "sysctl -q vm.max_map_count | awk -F'= ' '{print \$2}'")
|
|
local new_ct
|
|
[[ $ct -lt $count ]] && ct=$count
|
|
logger INFO "Updating map count"
|
|
sudo -S <<< "$pass" sh -c "echo 'vm.max_map_count=$ct' > /etc/sysctl.d/dayz.conf"
|
|
sudo sysctl -p /etc/sysctl.d/dayz.conf
|
|
else
|
|
logger INFO "Zenity dialog failed or user exit"
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
add_by_fav(){
|
|
while true; do
|
|
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
|
|
$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'"
|
|
$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"
|
|
return
|
|
fi
|
|
fi
|
|
done
|
|
}
|
|
lock(){
|
|
[[ ! -d $config_path ]] && return
|
|
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)"
|
|
$steamsafe_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_helpers(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
mkdir -p "$helpers_path"
|
|
[[ ! -f "$helpers_path/vdf2json.py" ]] && curl -Ls "$vdf2json_url" > "$helpers_path/vdf2json.py"
|
|
}
|
|
update_steam_cmd(){
|
|
local new_cmd
|
|
preferred_client="$steam_cmd"
|
|
new_cmd="preferred_client=\"$preferred_client\""
|
|
mv $config_file ${config_path}dztuirc.old
|
|
nr=$(awk '/preferred_client=/ {print NR}' ${config_path}dztuirc.old)
|
|
awk -v "var=$new_cmd" -v "nr=$nr" 'NR==nr {$0=var}{print}' ${config_path}dztuirc.old > ${config_path}dztuirc
|
|
}
|
|
steam_deps(){
|
|
logger INFO "${FUNCNAME[0]}"
|
|
local flatpak steam
|
|
[[ $(command -v flatpak) ]] && flatpak=$(flatpak list | grep valvesoftware.Steam)
|
|
steam=$(command -v steam)
|
|
if [[ -z "$steam" ]] && [[ -z "$flatpak" ]]; then
|
|
warn "Requires Steam or Flatpak Steam"
|
|
logger ERROR "Steam was missing"
|
|
exit
|
|
elif [[ -n "$steam" ]] && [[ -n "$flatpak" ]]; then
|
|
toggle_steam=1
|
|
steam_cmd="steam"
|
|
[[ -n $preferred_client ]] && steam_cmd="$preferred_client"
|
|
[[ -z $preferred_client ]] && update_steam_cmd
|
|
elif [[ -n "$steam" ]]; then
|
|
steam_cmd="steam"
|
|
else
|
|
steam_cmd="flatpak run com.valvesoftware.Steam"
|
|
fi
|
|
logger INFO "steam_cmd set to $steam_cmd"
|
|
}
|
|
initial_setup(){
|
|
echo "# Initial setup"
|
|
run_depcheck
|
|
watcher_deps
|
|
check_architecture
|
|
check_version
|
|
check_map_count
|
|
fetch_helpers
|
|
config
|
|
steam_deps
|
|
run_varcheck
|
|
stale_symlinks
|
|
init_items
|
|
setup
|
|
check_news
|
|
echo "100"
|
|
}
|
|
test_zenity_version(){
|
|
local current="$1"
|
|
local cutoff="3.99.1"
|
|
if [[ "$(printf '%s\n' "$cutoff" "$current" | sort -V | head -n1)" == "$cutoff" ]]; then
|
|
logger INFO "zenity version greater than or equal to $cutoff"
|
|
echo greater
|
|
else
|
|
logger INFO "zenity version lesser than $cutoff"
|
|
echo lesser
|
|
fi
|
|
}
|
|
main(){
|
|
lock
|
|
local zenv=$(zenity --version 2>/dev/null)
|
|
[[ -z $zenv ]] && { logger "Missing zenity"; exit; }
|
|
local res=$(test_zenity_version $zenv)
|
|
initial_setup > >($steamsafe_zenity --pulsate --progress --auto-close --title="DZGUI" --no-cancel --width=500 2>/dev/null)
|
|
main_menu
|
|
#TODO: tech debt: cruddy handling for steam forking
|
|
[[ $? -eq 1 ]] && pkill -f dzgui.sh
|
|
}
|
|
parent=$(cat /proc/$PPID/comm)
|
|
[[ -f "$debug_log" ]] && rm "$debug_log"
|
|
main
|