mirror of
https://github.com/aclist/dztui.git
synced 2026-08-26 01:37:18 +02:00
1396 lines
40 KiB
Bash
Executable File
1396 lines
40 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -o pipefail
|
|
version="6.0.5-beta.2"
|
|
|
|
#CONSTANTS
|
|
aid=221100
|
|
exp=1024020
|
|
game="dayz"
|
|
app_name="dzgui"
|
|
app_name_upper="DZGUI"
|
|
workshop="steam://url/CommunityFilePage/"
|
|
sd_res="--width=1280 --height=800"
|
|
steamsafe_zenity="/usr/bin/zenity"
|
|
separator="␞"
|
|
|
|
##CONFIG
|
|
config_path="$HOME/.config/dztui"
|
|
config_file="$config_path/dztuirc"
|
|
source $config_file
|
|
|
|
#PATHS
|
|
state_path="$HOME/.local/state/$app_name"
|
|
cache_path="$HOME/.cache/$app_name"
|
|
share_path="$HOME/.local/share/$app_name"
|
|
script_path="$share_path/dzgui.sh"
|
|
helpers_path="$share_path/helpers"
|
|
prefix="dzg"
|
|
|
|
#LOGS
|
|
log_path="$state_path/logs"
|
|
debug_log="$log_path/DZGUI_DEBUG.log"
|
|
system_log="$log_path/DZGUI_SYSTEM.log"
|
|
mod_log="$log_path/DZGUI_MODIDS.log"
|
|
|
|
#STATE FILES
|
|
history_file="$state_path/$prefix.history"
|
|
versions_file="$state_path/$prefix.versions"
|
|
lock_file="$state_path/$prefix.lock"
|
|
|
|
#CACHE
|
|
cache_dir="$HOME/.cache/$app_name"
|
|
_cache_servers="$cache_dir/$prefix.servers"
|
|
_cache_mods_temp="$cache_dir/$prefix.mods_temp"
|
|
_cache_stale_mods_temp="$cache_dir/$prefix.stale_mods_temp"
|
|
_cache_temp="$cache_dir/$prefix.temp"
|
|
_cache_my_servers="$cache_dir/$prefix.my_servers"
|
|
_cache_history="$cache_dir/$prefix.history"
|
|
_cache_launch="$cache_dir/$prefix.launch_mods"
|
|
_cache_address="$cache_dir/$prefix.launch_address"
|
|
_cache_binary="$cache_dir/$prefix.binary"
|
|
_cache_coords="$cache_path/$prefix.coords"
|
|
_cache_cooldown="$cache_path/$prefix.cooldown"
|
|
_cache_lan="$cache_path/$prefix.lan"
|
|
_cache_src_path="$cache_path/$prefix.src"
|
|
|
|
#XDG
|
|
freedesktop_path="$HOME/.local/share/applications"
|
|
|
|
#HELPERS
|
|
ui_helper="$helpers_path/ui.py"
|
|
geo_file="$helpers_path/ips.csv"
|
|
km_helper="$helpers_path/latlon"
|
|
sums_path="$helpers_path/sums.md5"
|
|
query_helper="$helpers_path/query_v2.py"
|
|
lan_helper="$helpers_path/lan"
|
|
|
|
#STEAM PATHS
|
|
workshop_path="$steam_path/steamapps/workshop"
|
|
workshop_dir="$workshop_path/content/$aid"
|
|
downloads_dir="$workshop_path/downloads/$aid"
|
|
game_dir="$steam_path/steamapps/common/DayZ"
|
|
|
|
#URLS
|
|
author="aclist"
|
|
repo="dztui"
|
|
gh_prefix="https://github.com"
|
|
issues_url="$gh_prefix/$author/$repo/issues"
|
|
url_prefix="https://raw.githubusercontent.com/$author/$repo"
|
|
stable_url="$url_prefix/dzgui"
|
|
testing_url="$url_prefix/testing"
|
|
releases_url="$gh_prefix/$author/$repo/releases/download/browser"
|
|
help_url="https://$author.github.io/dzgui/index"
|
|
help_url2="https://$author.codeberg.page"
|
|
forum_url="https://old.reddit.com/r/dzgui"
|
|
sponsor_url="$gh_prefix/sponsors/$author"
|
|
battlemetrics_server_url="https://www.battlemetrics.com/servers/dayz"
|
|
steam_api_url="https://steamcommunity.com/dev/apikey"
|
|
battlemetrics_api_url="https://www.battlemetrics.com/developers"
|
|
bm_api="https://api.battlemetrics.com/servers"
|
|
|
|
steam_cmd="$preferred_client"
|
|
|
|
declare -A funcs=(
|
|
["Highlight stale"]="find_stale_mods"
|
|
["Change player name"]="update_config_val"
|
|
["Change Steam API key"]="update_config_val"
|
|
["Change Battlemetrics API key"]="update_config_val"
|
|
["Change client"]="update_config_val"
|
|
["Change favorite server"]="add_record"
|
|
["Add server by IP"]="add_record"
|
|
["Add server by ID"]="add_record"
|
|
["try_connect"]="try_connect"
|
|
["find_id"]="find_id"
|
|
["toggle"]="toggle"
|
|
["Open link"]="open_link"
|
|
["get_dist"]="get_dist"
|
|
["test_cooldown"]="test_cooldown"
|
|
["query_config"]="query_config"
|
|
["query_favorites"]="query_favorites"
|
|
["start_cooldown"]="start_cooldown"
|
|
["List installed mods"]="list_mods"
|
|
["Delete selected mods"]="delete_local_mod"
|
|
["align_local"]="align_versions_file"
|
|
["show_server_modlist"]="show_server_modlist"
|
|
["test_ping"]="test_ping"
|
|
["is_in_favs"]="is_in_favs"
|
|
["show_log"]="show_log"
|
|
["Output system info to log file"]="generate_log"
|
|
["open_user_workshop"]="open_user_workshop"
|
|
["open_workshop_page"]="open_workshop_page"
|
|
["Add to my servers"]="update_favs_from_table"
|
|
["Remove from my servers"]="update_favs_from_table"
|
|
["Remove from history"]="remove_from_history"
|
|
["Force update local mods"]="force_update"
|
|
["Resolve IP"]="resolve_ip"
|
|
["Handshake"]="handshake"
|
|
["Handshake_EXP"]="handshake_exp"
|
|
["get_player_count"]="get_player_count"
|
|
["update_symlinks"]="update_symlinks"
|
|
)
|
|
|
|
clone_symlinks(){
|
|
local path="$(< $_cache_binary)"
|
|
path=$(dirname "$path")
|
|
for dir in $(find $game_dir -type l); do
|
|
local link=$(basename $dir)
|
|
ln -sf "${dir}" "${path}/${link}"
|
|
done
|
|
}
|
|
handshake(){
|
|
final_handshake "$aid"
|
|
}
|
|
handshake_exp(){
|
|
final_handshake "$exp"
|
|
}
|
|
query_favorites(){
|
|
if [[ -z "${ip_list[@]}" ]]; then
|
|
return 1
|
|
fi
|
|
printf "%s\n" "${ip_list[@]}"
|
|
}
|
|
find_stale_mods(){
|
|
local res
|
|
local mods=()
|
|
> $_cache_stale_mods_temp
|
|
for i in "${ip_list[@]}"; do
|
|
local ip=$(<<< "$i" awk -F: '{print $1}')
|
|
local qport=$(<<< "$i" awk -F: '{print $3}')
|
|
res=$(a2s $ip $qport rules)
|
|
if [[ -n $res ]]; then
|
|
printf "%s\n" "$res" >> $_cache_stale_mods_temp
|
|
fi
|
|
done
|
|
printf ""
|
|
return 99
|
|
}
|
|
resolve_ip(){
|
|
shift
|
|
local res
|
|
local record="$1"
|
|
local ip=$(<<< "$record" awk -F: '{print $1}')
|
|
local qport=$(<<< "$record" awk -F: '{print $3}')
|
|
res=$(a2s $ip $qport info)
|
|
if [[ ! $? -eq 0 ]]; then
|
|
printf "Server timed out \n"
|
|
return 1
|
|
fi
|
|
local gport=$(<<< "$res" jq -r '.[].gameport')
|
|
if [[ -z $gport ]]; then
|
|
printf "Failed to resolve server metadata\n"
|
|
return 1
|
|
fi
|
|
|
|
# incoming input can only be 'remove' or 'add'
|
|
# record is in favs => implies deletion
|
|
# record not in favs => implies addition
|
|
resolved_record="$ip:$gport:$qport"
|
|
if [[ ${ip_list[*]} =~ $resolved_record ]]; then
|
|
remove_from_favs "$resolved_record"
|
|
else
|
|
add_to_favs "$resolved_record"
|
|
fi
|
|
}
|
|
get_player_count(){
|
|
shift
|
|
local res
|
|
local ip="$1"
|
|
local qport="$2"
|
|
res=$(a2s $ip $qport info)
|
|
[[ ! $? -eq 0 ]] && return 1
|
|
local players="$(<<< $res jq -r '.[].players')"
|
|
local queue="$(<<< $res jq -r '.[].gametype|split("lqs")[1]|split(",")[0]')"
|
|
printf "%s\n%s" "$players" "$queue"
|
|
}
|
|
|
|
map_id_to_ip(){
|
|
local id="$1"
|
|
local res=$(curl -s "$bm_api" -H "Authorization: Bearer "$api_key"" \
|
|
-G -d "sort=-players" \
|
|
-d "filter[game]=$game" \
|
|
-d "filter[ids][whitelist]=$id")
|
|
local len=$(<<< "$res" jq '.data|length')
|
|
[[ $len -eq 0 ]] && return 1
|
|
local record=$(<<< "$res" jq -r '.data[].attributes|"\(.ip):\(.port):\(.portQuery)"')
|
|
echo "$record"
|
|
}
|
|
add_record(){
|
|
local context="$1"
|
|
local addr="$2"
|
|
local record
|
|
if [[ $context != "Add server by ID" ]] && [[ $(validate_ip "$addr") -eq 1 ]]; then
|
|
printf "Not a valid IP format. Supply IP:Queryport"
|
|
return 2
|
|
fi
|
|
local ip=$(<<< $addr awk -F: '{print $1}')
|
|
local qport=$(<<< $addr awk -F: '{print $2}')
|
|
local res
|
|
res=$(a2s $ip $qport info)
|
|
if [[ ! $? -eq 0 ]]; then
|
|
printf "Timed out when querying the server. Is this a valid server?"
|
|
return 2
|
|
fi
|
|
local gameport="$(<<< $res jq -r '.[].gameport')"
|
|
record="${ip}:${gameport}:${qport}"
|
|
|
|
case "$context" in
|
|
"Add server by IP")
|
|
if [[ ${ip_list[*]} =~ $record ]]; then
|
|
printf "Already in favorites list"
|
|
return 1
|
|
fi
|
|
add_to_favs "$record"
|
|
;;
|
|
"Change favorite server")
|
|
fav_label=$(<<< "$res" jq -r '.[].name')
|
|
fav_server="$record"
|
|
update_config
|
|
echo "Updated favorite server to '$fav_server' ($fav_label)"
|
|
return 90
|
|
;;
|
|
"Add server by ID")
|
|
if [[ -z "$api_key" ]]; then
|
|
printf "No Battlemetrics API key set"
|
|
return 4
|
|
fi
|
|
record=$(map_id_to_ip "$addr")
|
|
if [[ $? -eq 1 ]]; then
|
|
logger WARN "Not a valid record: '$addr'"
|
|
printf "Not a valid ID"
|
|
return 2
|
|
fi
|
|
logger INFO "Battlemetrics ID resolved to IP $record"
|
|
if [[ ${ip_list[*]} =~ $record ]]; then
|
|
printf "Already in favorites list"
|
|
return 1
|
|
fi
|
|
add_to_favs "$record"
|
|
;;
|
|
esac
|
|
}
|
|
start_cooldown(){
|
|
logger WARN "API response empty. Started 60s cooldown at $(date +%s)"
|
|
date +%s > $_cache_cooldown
|
|
}
|
|
is_dlc(){
|
|
local dlc
|
|
local ip="$1"
|
|
local gport="$2"
|
|
local res="$(curl -Ls "https://api.steampowered.com/IGameServersService/GetServerList/v1/?filter=\gameaddr\\${ip}:${gport}\appid\221100&key=$steam_api")"
|
|
dlc=$(<<< "$res" jq '.response.servers[].gametype|contains("isDLC")')
|
|
printf "%s\n" "$dlc"
|
|
}
|
|
a2s(){
|
|
local ip="$1"
|
|
local qport="$2"
|
|
local mode="$3"
|
|
logger INFO "Querying '$ip:$qport' with mode '$mode'"
|
|
local res
|
|
res=$(python3.13 "$query_helper" "$ip" "$qport" "$mode")
|
|
if [[ $? -eq 1 ]]; then
|
|
res=$(try_fallback "$ip" "$qport" "$mode")
|
|
if [[ $? -eq 1 ]]; then
|
|
return 1
|
|
fi
|
|
fi
|
|
printf "%s\n" "$res"
|
|
}
|
|
is_in_favs(){
|
|
shift
|
|
local record="$1"
|
|
for (( i = 0; i < ${#ip_list[@]}; i++ )); do
|
|
if [[ ${ip_list[$i]} == "$record" ]]; then
|
|
logger INFO "'$record' is in favorites list"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
find_id(){
|
|
local file="$default_steam_path/config/loginusers.vdf"
|
|
[[ ! -f $file ]] && return 1
|
|
local res=$(python3.13 "$helpers_path/vdf2json.py" \
|
|
-i "$file" \
|
|
| jq -r '.users
|
|
|to_entries[]
|
|
|select(.value.MostRecent=="1")
|
|
|.key'
|
|
)
|
|
[[ -z $res ]] && return 1
|
|
printf "%s" "$res"
|
|
return 0
|
|
}
|
|
list_mods(){
|
|
local symlink
|
|
local sep
|
|
local name
|
|
local base_dir
|
|
local size
|
|
local mods
|
|
if [[ -z $(installed_mods) ]] || [[ -z $(find $workshop_dir -maxdepth 2 -name "*.cpp" | grep .cpp) ]]; then
|
|
printf "No mods currently installed or incorrect path set."
|
|
logger WARN "Found no locally installed mods"
|
|
return 1
|
|
else
|
|
mods=$(for dir in $(find $game_dir/* -maxdepth 1 -type l); do
|
|
symlink=$(basename $dir)
|
|
sep="␞"
|
|
name=$(awk -F\" '/name/ {print $2}' "${dir}/meta.cpp")
|
|
base_dir=$(basename $(readlink -f $game_dir/$symlink))
|
|
size=$(du -s "$(readlink -f "$game_dir/$symlink")" | awk '{print $1}')
|
|
size=$(python3.13 -c "n=($size/1024) +.005; print(round(n,4))")
|
|
LC_NUMERIC=C printf "%s$sep%s$sep%s$sep%3.3f\n" "$name" "$symlink" "$base_dir" "$size"
|
|
done | sort -k1)
|
|
# user may have manually pruned mods out-of-band
|
|
# handle directory detritus but no actual mods
|
|
if [[ -z $mods ]]; then
|
|
printf "No mods currently installed or incorrect path set."
|
|
return 1
|
|
fi
|
|
echo "$mods"
|
|
fi
|
|
}
|
|
installed_mods(){
|
|
find "$workshop_dir" -maxdepth 1 -mindepth 1 -printf "%f\n"
|
|
}
|
|
local_latlon(){
|
|
local url="http://ip-api.com/json/$local_ip"
|
|
local local_ip
|
|
|
|
if [[ -z $(command -v dig) ]]; then
|
|
local_ip=$(curl -Ls "https://ipecho.net/plain")
|
|
else
|
|
local_ip=$(dig -4 +short myip.opendns.com @resolver1.opendns.com)
|
|
fi
|
|
curl -Ls "$url" | jq -r '"\(.lat)\n\(.lon)"'
|
|
}
|
|
get_dist(){
|
|
shift
|
|
local given_ip="$1"
|
|
if [[ ! -f $_cache_coords ]]; then
|
|
printf "Unknown"
|
|
return
|
|
fi
|
|
readarray -t coords < "$_cache_coords"
|
|
readarray -t n < <(<<< "$given_ip" awk 'BEGIN{RS="."}{$1=$1}1')
|
|
|
|
local local_lat="${coords[0]}"
|
|
local local_lon="${coords[1]}"
|
|
local network="^${n[0]}.${n[1]}\."
|
|
local three="${n[2]}"
|
|
local host="${n[3]}"
|
|
|
|
local binary=$(grep -E "$network" $geo_file)
|
|
local res=$(<<< "$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=$(<<< "$res" awk '{print $1}')
|
|
local remote_lon=$(<<< "$res" awk '{print $2}')
|
|
if [[ -z $remote_lat ]]; then
|
|
logger WARN "Failed to find geolocation candidate in IP database"
|
|
local dist="Unknown"
|
|
printf "Unknown"
|
|
else
|
|
logger INFO "Resolved remote server geolocation to '$remote_lat, $remote_lon'"
|
|
local dist=$($km_helper "$local_lat" "$local_lon" "$remote_lat" "$remote_lon")
|
|
LC_NUMERIC=C printf "%.0f" "$dist"
|
|
logger INFO "Distance: $dist km"
|
|
fi
|
|
}
|
|
|
|
query_config(){
|
|
[[ -n $2 ]] && local key=$2
|
|
keys=(
|
|
"branch"
|
|
"debug"
|
|
"auto_install"
|
|
"name"
|
|
"fav_label"
|
|
"preferred_client"
|
|
"fullscreen"
|
|
"default_steam_path"
|
|
)
|
|
if [[ -n $key ]]; then
|
|
if [[ -n ${!key} ]]; then
|
|
echo "${!key}"
|
|
return 0
|
|
else
|
|
echo ""
|
|
return 1
|
|
fi
|
|
fi
|
|
for i in "${keys[@]}"; do
|
|
echo "${!i}"
|
|
done
|
|
}
|
|
|
|
align_versions_file(){
|
|
shift
|
|
local mod="$1"
|
|
[[ ! -f $versions_file ]] && return
|
|
< "$versions_file" awk -F, -v var="$mod" '$1 != var' > $versions_file.new &&
|
|
mv $versions_file.new $versions_file
|
|
logger INFO "Removed local signatures for the mod '$mod'"
|
|
}
|
|
pluralize(){
|
|
local plural="$1"
|
|
local count="$2"
|
|
local mod
|
|
local suffix
|
|
local base
|
|
local ct
|
|
local s
|
|
|
|
if [[ "${plural: -2}" == "es" ]]; then
|
|
base="${plural::-2}"
|
|
suffix="${plural: -2}"
|
|
ct=$((count^2))
|
|
[[ $ct -ne 3 ]] && s="$suffix"
|
|
else
|
|
base="${plural::-1}"
|
|
suffix="${plural: -1}"
|
|
ct=$((count^1))
|
|
[[ $ct -gt 0 ]] && s="$suffix"
|
|
fi
|
|
|
|
printf "%s%s" "$base" "$s"
|
|
}
|
|
delete_local_mod(){
|
|
shift
|
|
readarray -t symlinks < <(awk '{print $1}' $_cache_mods_temp)
|
|
readarray -t ids < <(awk '{print $2}' $_cache_mods_temp)
|
|
rm "$_cache_mods_temp"
|
|
for ((i=0; i<${#symlinks[@]}; i++)); do
|
|
[[ ! -d $workshop_dir/${ids[$i]} ]] && return 1
|
|
[[ ! -L $game_dir/${symlinks[$i]} ]] && return 1
|
|
#SC2115
|
|
rm -rf "${workshop_dir:?}/${ids[$i]}" && unlink "$game_dir/${symlinks[$i]}" || return 1
|
|
align_versions_file "align" "${ids[$i]}"
|
|
done
|
|
printf "Successfully deleted %s %s." "${#symlinks[@]}" "$(pluralize "mods" ${#symlinks[@]})"
|
|
return 95
|
|
}
|
|
test_cooldown(){
|
|
[[ ! -f $_cache_cooldown ]] && return 0
|
|
local old_time=$(< $_cache_cooldown)
|
|
local cur_time=$(date +%s)
|
|
local delta=$(($cur_time - $old_time))
|
|
if [[ $delta -lt 30 ]]; then
|
|
local remains=$((30 - $delta))
|
|
local suffix=$(pluralize "seconds" $remains)
|
|
printf "Global API cooldown in effect. Please wait %s %s." "$remains" "$suffix"
|
|
exit 1
|
|
fi
|
|
}
|
|
redact(){
|
|
sed 's@\(/home/\)[^/]*@\1REDACTED@g'
|
|
}
|
|
logger(){
|
|
local date="$(date "+%F %T,%3N")"
|
|
local tag="$1"
|
|
local string="$2"
|
|
local self="${BASH_SOURCE[0]}"
|
|
local caller="${FUNCNAME[1]}"
|
|
local line="${BASH_LINENO[0]}"
|
|
printf "%s␞%s␞%s::%s()::%s␞%s\n" "$date" "$tag" "$self" "$caller" "$line" "$string" \
|
|
| redact >> "$debug_log"
|
|
}
|
|
test_ping(){
|
|
shift
|
|
local time
|
|
local res
|
|
local ip="$1"
|
|
local qport="$2"
|
|
res=$(a2s $ip $qport info)
|
|
if [[ -z $res ]]; then
|
|
time="Timed out"
|
|
else
|
|
time=$(<<< "$res" jq -r '.[].ping')
|
|
fi
|
|
printf "%s" "$time"
|
|
}
|
|
show_server_modlist(){
|
|
shift
|
|
local ip="$1"
|
|
local qport="$2"
|
|
local res=$(a2s $ip $qport names)
|
|
[[ -z $res ]] && return 1
|
|
[[ $(<<< $res jq '.ids|length') -lt 1 ]] && return 1
|
|
local names=$(<<< "$res" jq -r '.names[]')
|
|
local ids=$(<<< "$res" jq -r '.ids[]')
|
|
local icon
|
|
local flag
|
|
local label
|
|
readarray -t names <<< "$names"
|
|
readarray -t ids <<< "$ids"
|
|
readarray -t mods < <(installed_mods)
|
|
for ((i=0; i<${#ids[@]}; ++i)); do
|
|
icon=
|
|
flag="WARN"
|
|
label="MISSING"
|
|
for j in "${mods[@]}"; do
|
|
if [[ $j == "${ids[$i]}" ]]; then
|
|
icon=✓
|
|
flag="INFO"
|
|
label="installed"
|
|
break
|
|
fi
|
|
done
|
|
logger $flag "Mod '${names[$i]}' is $label"
|
|
printf "%s␞%s␞%s\n" "${names[$i]}" "${ids[i]}" "$icon"
|
|
done | sort -k1
|
|
}
|
|
print_ip_list(){
|
|
[[ ${#ip_list[@]} -eq 0 ]] && return 1
|
|
printf "\t\"%s\"\n" "${ip_list[@]}"
|
|
}
|
|
write_config(){
|
|
cat <<-END
|
|
#Path to DayZ installation
|
|
steam_path="$steam_path"
|
|
|
|
#Battlemetrics API key
|
|
api_key="$api_key"
|
|
|
|
#Favorited server IP:PORT array
|
|
ip_list=(
|
|
$(print_ip_list)
|
|
)
|
|
|
|
#Favorite server to fast-connect to (limit one)
|
|
fav_server="$fav_server"
|
|
|
|
#Favorite server label (human readable)
|
|
fav_label="$fav_label"
|
|
|
|
#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"
|
|
|
|
#Start in fullscreen
|
|
fullscreen="$fullscreen"
|
|
|
|
#Steam API key
|
|
steam_api="$steam_api"
|
|
|
|
#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
|
|
}
|
|
format_version_url(){
|
|
case "$branch" in
|
|
"stable")
|
|
version_url="$stable_url/dzgui.sh"
|
|
;;
|
|
"testing")
|
|
version_url="$testing_url/dzgui.sh"
|
|
;;
|
|
esac
|
|
echo "$version_url"
|
|
}
|
|
get_response_code(){
|
|
local url="$1"
|
|
curl -Ls -I -o /dev/null -w "%{http_code}" "$url"
|
|
}
|
|
test_connection(){
|
|
source "$config_file"
|
|
declare -A hr
|
|
local res1
|
|
local res2
|
|
local str="No connection could be established to the remote server"
|
|
hr=(
|
|
["github.com"]="https://github.com/$author"
|
|
["codeberg.org"]="https://codeberg.org/$author"
|
|
)
|
|
res=$(get_response_code "${hr["github.com"]}")
|
|
if [[ $res -ne 200 ]]; then
|
|
logger WARN "Remote host '${hr["github.com"]}' unreachable', trying fallback"
|
|
remote_host=cb
|
|
logger INFO "Set remote host to '${hr["codeberg.org"]}'"
|
|
res=$(get_response_code "${hr["codeberg.org"]}")
|
|
if [[ $res -ne 200 ]]; then
|
|
return 1
|
|
fi
|
|
fi
|
|
if [[ $remote_host == "cb" ]]; then
|
|
url_prefix="https://codeberg.org/$author/$repo/raw/branch"
|
|
releases_url="https://codeberg.org/$author/$repo/releases/download/browser"
|
|
stable_url="$url_prefix/dzgui"
|
|
testing_url="$url_prefix/testing"
|
|
fi
|
|
}
|
|
download_new_version(){
|
|
test_connection
|
|
rc=$?
|
|
if [[ $rc -eq 1 ]]; then
|
|
printf "Remote resource unavailable. Aborting"
|
|
logger WARN "Remote resource unavailable"
|
|
return 1
|
|
fi
|
|
local version_url="$(format_version_url)"
|
|
local src_path="$(< "$_cache_src_path")"
|
|
mv "$src_path" "$src_path.old"
|
|
curl -Ls "$version_url" > "$src_path"
|
|
rc=$?
|
|
if [[ $rc -eq 0 ]]; then
|
|
dl_changelog
|
|
logger INFO "Wrote new version to $src_path"
|
|
chmod +x "$src_path"
|
|
touch "${config_path}.unmerged"
|
|
printf "New version downloaded. Please exit to apply changes"
|
|
logger INFO "User exited after version upgrade"
|
|
return 255
|
|
else
|
|
mv "$src_path.old" "$src_path"
|
|
printf "Failed to fetch new version. Rolling back"
|
|
logger WARN "curl failed to fetch new version. Rolling back"
|
|
return 1
|
|
fi
|
|
}
|
|
dl_changelog(){
|
|
local mdbranch
|
|
local file="CHANGELOG.md"
|
|
[[ $branch == "stable" ]] && mdbranch="dzgui"
|
|
[[ $branch == "testing" ]] && mdbranch="testing"
|
|
local md="$url_prefix/${mdbranch}/$file"
|
|
curl -Ls "$md" > "$state_path/$file"
|
|
}
|
|
toggle(){
|
|
shift
|
|
local context="$1"
|
|
case "$context" in
|
|
Toggle[[:space:]]release[[:space:]]branch)
|
|
if [[ $branch == "stable" ]]; then
|
|
branch="testing"
|
|
else
|
|
branch="stable"
|
|
fi
|
|
update_config
|
|
download_new_version
|
|
[[ $? -eq 1 ]] && return 1
|
|
return 255
|
|
;;
|
|
Toggle[[:space:]]mod[[:space:]]install[[:space:]]mode)
|
|
if [[ -z $auto_install ]]; then
|
|
staging_dir="/tmp"
|
|
auto_install="1"
|
|
else
|
|
auto_install=""
|
|
rm "$versions_file"
|
|
fi
|
|
;;
|
|
Toggle[[:space:]]debug[[:space:]]mode)
|
|
if [[ -z $debug ]]; then
|
|
debug="1"
|
|
else
|
|
debug=""
|
|
fi
|
|
;;
|
|
Toggle[[:space:]]DZGUI[[:space:]]fullscreen[[:space:]]boot)
|
|
if [[ $fullscreen == "true" ]]; then
|
|
fullscreen="false"
|
|
else
|
|
fullscreen="true"
|
|
fi
|
|
;;
|
|
esac
|
|
update_config
|
|
return 90
|
|
|
|
}
|
|
add_to_favs(){
|
|
local record="$1"
|
|
ip_list+=("$record")
|
|
update_config
|
|
logger INFO "Added the record $record to saved servers"
|
|
printf "Added %s to saved servers" $record
|
|
return 90
|
|
}
|
|
remove_from_history(){
|
|
shift
|
|
local record="$1"
|
|
# remove ip from history file
|
|
local hist_cache=$(< "$history_file")
|
|
<<< "$hist_cache" grep -v "$record" > "$history_file"
|
|
# update cache
|
|
local cache=$(< "$_cache_history")
|
|
local r=$(<<< "$record" awk -F: '{print $1":"$2"␞"$3}')
|
|
<<< "$cache" grep -v -P "$r$" > "$_cache_history"
|
|
}
|
|
remove_from_favs(){
|
|
local record="$1"
|
|
for (( i=0; i<${#ip_list[@]}; ++i )); do
|
|
if [[ ${ip_list[$i]} == "$record" ]]; then
|
|
unset ip_list[$i]
|
|
break
|
|
fi
|
|
done
|
|
if [[ ${#ip_list[@]} -gt 0 ]]; then
|
|
readarray -t ip_list < <(printf "%s\n" "${ip_list[@]}")
|
|
fi
|
|
update_config
|
|
local r=$(<<< "$record" awk -F: '{print $1":"$2"␞"$3}')
|
|
local cache="$(< "$_cache_my_servers")"
|
|
<<< "$cache" grep -v -P "$r$" > $_cache_my_servers
|
|
logger INFO "Removed the record $record from saved servers"
|
|
echo "Removed $record from saved servers"
|
|
return 90
|
|
}
|
|
update_favs_from_table(){
|
|
local context="$1"
|
|
local record="$2"
|
|
if [[ $context =~ Remove ]]; then
|
|
remove_from_favs "$record"
|
|
echo "Removed $record from saved servers"
|
|
else
|
|
add_to_favs "$record"
|
|
fi
|
|
return 0
|
|
}
|
|
update_config(){
|
|
# handling for legacy files
|
|
[[ -z $branch ]] && branch="stable"
|
|
mv $config_file ${config_file}.old
|
|
write_config > $config_file
|
|
logger INFO "Updated config file at '$config_file'"
|
|
}
|
|
validate_ip(){
|
|
local ip="$1"
|
|
local res
|
|
<<< "$ip" grep -qP '^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}:[0-9]+$' && echo 0 || echo 1
|
|
}
|
|
test_steam_api(){
|
|
local key="$1"
|
|
[[ -z $key ]] && return 1
|
|
local url="https://api.steampowered.com/IGameServersService/GetServerList/v1/?filter=\appid\221100&limit=10&key=$key"
|
|
local code=$(curl -ILs "$url" | grep -E "^HTTP")
|
|
[[ $code =~ 403 ]] && echo 1
|
|
[[ $code =~ 200 ]] && echo 0
|
|
}
|
|
test_bm_api(){
|
|
local key="$1"
|
|
if [[ -z $key ]]; then
|
|
echo 1
|
|
return
|
|
fi
|
|
local code=$(curl -ILs "$bm_api" \
|
|
-H "Authorization: Bearer "$key"" -G \
|
|
-d "filter[game]=$game" \
|
|
| grep -E "^HTTP")
|
|
[[ $code =~ 401 ]] && echo 1
|
|
[[ $code =~ 200 ]] && echo 0
|
|
}
|
|
update_config_val(){
|
|
local context="$1"
|
|
local value="$2"
|
|
case $1 in
|
|
"Change client")
|
|
key="preferred_client"
|
|
;;
|
|
"Change player name")
|
|
key="name"
|
|
if [[ -z "${value// }" ]]; then
|
|
printf "Invalid name"
|
|
return 2
|
|
fi
|
|
;;
|
|
"Change Steam API key")
|
|
key="steam_api"
|
|
if [[ ${#value} -lt 32 ]] || [[ $(test_steam_api "$value") -eq 1 ]]; then
|
|
printf "Invalid API key"
|
|
return 78
|
|
fi
|
|
;;
|
|
"Change Battlemetrics API key")
|
|
key="api_key"
|
|
if [[ $(test_bm_api "$value") -eq 1 ]]; then
|
|
printf "Invalid API key"
|
|
return 79
|
|
fi
|
|
;;
|
|
esac
|
|
declare -n nr=$key
|
|
nr="$value"
|
|
update_config
|
|
return 80
|
|
}
|
|
show_log(){
|
|
< "$debug_log" sed 's/Keyword␞/Keyword/'
|
|
}
|
|
open_user_workshop(){
|
|
shift
|
|
local id="$1"
|
|
url="https://steamcommunity.com/profiles/$id/myworkshopfiles/?appid=$aid&browsefilter=mysubscriptions"
|
|
$steam_cmd steam://openurl/$url &
|
|
}
|
|
open_workshop_page(){
|
|
shift
|
|
local id="$1"
|
|
local workshop_uri="steam://url/CommunityFilePage/$id"
|
|
$steam_cmd "$workshop_uri" $id &
|
|
}
|
|
open_link(){
|
|
shift
|
|
local destination="$1"
|
|
local url
|
|
case "$destination" in
|
|
"Open Battlemetrics")
|
|
url="$battlemetrics_server_url"
|
|
;;
|
|
"Open Steam API page")
|
|
url="$steam_api_url"
|
|
;;
|
|
"Open Battlemetrics API page")
|
|
url="$battlemetrics_api_url"
|
|
;;
|
|
"Documentation/help files (GitHub) ⧉")
|
|
url="$help_url"
|
|
;;
|
|
"Documentation/help files (Codeberg mirror) ⧉")
|
|
url="$help_url2"
|
|
;;
|
|
"Report a bug (GitHub) ⧉")
|
|
url="$issues_url"
|
|
;;
|
|
"DZGUI Subreddit ⧉")
|
|
url="$forum_url"
|
|
;;
|
|
"Sponsor (GitHub) ⧉")
|
|
url="$sponsor_url"
|
|
;;
|
|
esac
|
|
|
|
#if [[ $is_steam_deck -eq 1 ]]; then
|
|
#$steam_cmd steam://openurl/"$1" 2>/dev/null
|
|
if [[ -n "$BROWSER" ]]; then
|
|
logger INFO "Opening '$url' in '$BROWSER'"
|
|
"$BROWSER" "$url"
|
|
else
|
|
logger INFO "Opening '$url' with xdg-open"
|
|
xdg-open "$url"
|
|
fi
|
|
}
|
|
generate_log(){
|
|
source $config_file
|
|
cat <<-DOC > $system_log
|
|
Distro: $(< /etc/os-release grep -w NAME | awk -F\" '{print $2}')
|
|
Kernel: $(uname -mrs)
|
|
CPU: $(< /proc/cpuinfo awk -F": " '/model name/ {print $2; exit}')
|
|
Version: $version
|
|
Branch: $branch
|
|
Mode: $(if [[ -z $debug ]]; then echo normal; else echo debug; fi)
|
|
Auto: $(if [[ -z $auto_install ]]; then echo normal; else echo auto; fi)
|
|
Steam path: $steam_path
|
|
Workshop path: $workshop_dir
|
|
Game path: $game_dir
|
|
Servers:
|
|
$(print_ip_list | sed 's/"//g')
|
|
Mods:
|
|
$(list_mods | sed 's/^/\t/g')
|
|
DOC
|
|
printf "Wrote system log to %s" "$system_log"
|
|
return 0
|
|
}
|
|
query_defunct(){
|
|
readarray -t modlist <<< "$@"
|
|
local 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'
|
|
}
|
|
local result=$(post | jq -r '
|
|
.[].publishedfiledetails[]
|
|
| select(.result==1)
|
|
| select(.filename|contains("screenshot")|not)
|
|
| "\(.file_size) \(.publishedfileid)"')
|
|
<<< "$result" awk '{print $2}'
|
|
}
|
|
encode(){
|
|
echo -n "$1" | md5sum | cut -c -8
|
|
}
|
|
compare(){
|
|
local modlist="$@"
|
|
diff=$(comm -23 <(<<< "$modlist" sort -u) <(installed_mods | sort))
|
|
echo "$diff"
|
|
}
|
|
unlink_all(){
|
|
readarray -t links < <(find "$game_dir" -maxdepth 1 -mindepth 1 -type l)
|
|
for link in "${links[@]}"; do
|
|
unlink "$link"
|
|
done
|
|
}
|
|
legacy_symlinks(){
|
|
logger INFO "Removing legacy symlinks"
|
|
|
|
readarray -t stale_mod_dirs1 < <(find "$workshop_dir" -maxdepth 1 -mindepth 1 -type l -name '@?*-*')
|
|
logger INFO "Read local mods into array 1 with length: ${#stale_mod_dirs[@]}"
|
|
if [[ ${#stale_mod_dirs1} -ne 0 ]]; then
|
|
for d in "${stale_mod_dirs1[@]}"; do
|
|
unlink "$game_dir/$d"
|
|
done
|
|
fi
|
|
|
|
readarray -t stale_mod_dirs2 < <(find "$workshop_dir" -maxdepth 1 -mindepth 1 -type l -name '@??')
|
|
logger INFO "Read local mods into array 2 with length: ${#stale_mod_dirs[@]}"
|
|
if [[ ${#stale_mod_dirs2} -eq 0 ]]; then
|
|
for d in "${stale_mod_dirs2[@]}"; do
|
|
unlink "$game_dir/$d"
|
|
done
|
|
fi
|
|
}
|
|
symlinks(){
|
|
_merge(){
|
|
comm -23 <(printf "%s\n" "${mods[@]}" | sort) <(printf "%s\n" "${targets[@]}" | sort)
|
|
}
|
|
_pulse(){
|
|
zenity --pulsate --progress --auto-close --no-cancel --title="DZGUI"
|
|
}
|
|
_create_links(){
|
|
local arr=("$@")
|
|
local encoded_id
|
|
local link
|
|
local mod
|
|
for ((i=0; i<${#arr[@]}; i++)); do
|
|
encoded_id=$(encode "${arr[$i]}")
|
|
link="@$encoded_id"
|
|
mod="${arr[$i]}"
|
|
logger INFO "Creating link '$game_dir/$link' for '$workshop_dir/$mod'"
|
|
[[ -n $STEAMSCRIPT ]] && echo "# Creating mod link $((i+1))/${#arr[@]}"
|
|
ln -s "$workshop_dir/$mod" "$game_dir/$link"
|
|
done
|
|
}
|
|
|
|
readarray -t mods < <(find "$workshop_dir" -mindepth 1 -name meta.cpp | awk -F/ 'NF=NF-1{print $NF}')
|
|
readarray -t links < <(find "$game_dir" -maxdepth 1 -type l)
|
|
|
|
if [[ ${#mods[@]} -eq 0 ]]; then
|
|
logger INFO "No mods present, aborting"
|
|
return
|
|
fi
|
|
|
|
if [[ ${#links[@]} -eq 0 ]]; then
|
|
logger INFO "No symlinks present in '$game_dir', creating them"
|
|
if [[ -n $STEAMSCRIPT ]]; then
|
|
_create_links "${mods[@]}" > >(_pulse)
|
|
else
|
|
_create_links "${mods[@]}"
|
|
fi
|
|
return
|
|
fi
|
|
|
|
readarray -t targets < <(printf "%s\n" "${links[@]}" | xargs readlink -f | awk -F/ '{print $NF}')
|
|
readarray -t hits < <(_merge)
|
|
|
|
if [[ ${#hits[@]} -eq 0 ]]; then
|
|
logger INFO "Symlinks are up to date, skipping"
|
|
return
|
|
fi
|
|
|
|
# update missing targets
|
|
logger INFO "Found ${#hits[@]} unlinked mods"
|
|
if [[ -n $STEAMSCRIPT ]]; then
|
|
_create_links "${hits[@]}" > >(_pulse)
|
|
else
|
|
_create_links "${hits[@]}"
|
|
fi
|
|
}
|
|
update_history(){
|
|
local record="$1"
|
|
local old
|
|
[[ -n $(grep "$record" $history_file) ]] && return
|
|
if [[ -f $history_file ]]; then
|
|
old=$(tail -n9 "$history_file")
|
|
rm $history_file
|
|
echo "$old" > "$history_file"
|
|
fi
|
|
echo "$record" >> "$history_file"
|
|
}
|
|
update_symlinks(){
|
|
if [[ ! -d "$workshop_dir" ]]; then
|
|
return
|
|
fi
|
|
if [[ ! -d "$game_dir" ]]; then
|
|
return
|
|
fi
|
|
unlink_all
|
|
symlinks
|
|
}
|
|
try_fallback(){
|
|
local ip="$1"
|
|
local qport="$2"
|
|
local mode="$3"
|
|
if [[ $mode != "rules" ]] && [[ $mode != "names" ]]; then
|
|
logger WARN "Fallback query API called with an invalid mode: '$mode'"
|
|
return 1
|
|
fi
|
|
[[ -z $api_key ]] && return 1
|
|
logger INFO "Using fallback query API on '$ip:$qport' with mode: '$mode'"
|
|
local res=$(curl -s "$bm_api" -H "Authorization: Bearer "$api_key"" \
|
|
-G -d "filter[game]=$game" \
|
|
-d "filter[search]=%22${ip}:${qport}%22")
|
|
[[ -z $res ]] && return 1
|
|
local len=$(<<< "$res" jq '.data|length')
|
|
[[ $len -eq 0 ]] && return 1
|
|
|
|
# cull defunct entries
|
|
local record=$(<<< "$res" jq -r '
|
|
.data[].attributes
|
|
|select(.status != "removed")')
|
|
|
|
# reverse lookup gport
|
|
local dlc
|
|
local gport=$(<<< "$record" jq -r '.port')
|
|
dlc=$(is_dlc "$ip" "$gport")
|
|
if [[ $dlc == "true" ]]; then
|
|
logger FAIL "Fallback query API called on DLC-enabled server"
|
|
return 1
|
|
fi
|
|
|
|
case "$mode" in
|
|
"rules")
|
|
<<< "$record" jq '.details.modIds[]'
|
|
;;
|
|
"names")
|
|
<<< "$record" jq '
|
|
.details|[.modNames, .modIds] as [$n, $i]
|
|
| {names: $n, ids: $i}'
|
|
;;
|
|
esac
|
|
}
|
|
try_connect(){
|
|
shift
|
|
local record="$1"
|
|
local appid="$2"
|
|
local binary="$3"
|
|
|
|
IFS=":" read -ra address <<< "$record"
|
|
local ip
|
|
local gameport
|
|
local qport
|
|
ip=${address[0]}
|
|
gameport=${address[1]}
|
|
qport=${address[2]}
|
|
|
|
[[ $appid -eq $exp ]] && echo "$binary" > "$_cache_binary"
|
|
|
|
local remote_mods
|
|
remote_mods=$(a2s "$ip" "$qport" rules)
|
|
if [[ $? -eq 1 ]]; then
|
|
printf "Failed to fetch server modlist, possibly timed out"
|
|
return 1
|
|
fi
|
|
logger INFO "Server returned modlist: $(<<< "$remote_mods" tr '\n' ' ')"
|
|
|
|
local sanitized_mods
|
|
sanitized_mods=$(query_defunct "$remote_mods")
|
|
|
|
logger INFO "Connection attempt for $ip:$qport"
|
|
update_history "$record"
|
|
|
|
case $auto_install in
|
|
"") install_mods "$sanitized_mods" "manual" ;;
|
|
1|2) install_mods "$sanitized_mods" "auto" ;;
|
|
esac
|
|
|
|
# NOTE: clean diff indicates that all requisite mods were downloaded
|
|
diff=$(compare "$sanitized_mods")
|
|
if [[ -z $diff ]]; then
|
|
launch "$ip" "$gameport" "$sanitized_mods" "$appid"
|
|
else
|
|
printf "User aborted download process, or some mods may have failed to download. Try connecting again to resync."
|
|
exit 1
|
|
fi
|
|
}
|
|
check_architecture(){
|
|
local cpu=$(< /proc/cpuinfo grep "AMD Custom APU 0405")
|
|
if [[ -n "$cpu" ]]; then
|
|
echo 1
|
|
else
|
|
echo 0
|
|
fi
|
|
}
|
|
force_update(){
|
|
if [[ ! $auto_install -eq 1 ]]; then
|
|
printf "Only available when mod auto-install is ON"
|
|
return 1
|
|
fi
|
|
# NOTE: forcibly regenerate signatures by removing file
|
|
rm "$versions_file"
|
|
local mods
|
|
mods=$(ls -1 "$workshop_dir")
|
|
install_mods "$mods" "force"
|
|
echo "Finished requesting mod updates."
|
|
return 0
|
|
}
|
|
get_timestamps(){
|
|
readarray -t modlist < <(printf "%s\n" "$@")
|
|
local max="${#modlist[@]}"
|
|
_concat(){
|
|
for ((i=0;i<$max;i++)); do
|
|
printf "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'
|
|
}
|
|
|
|
_post
|
|
}
|
|
update_stamps(){
|
|
readarray -t stamps <<< "$1"
|
|
for((i=0;i<${#stamps[@]};i++)); do
|
|
printf "%s\n" "${stamps[$i]}" >> $versions_file
|
|
done
|
|
}
|
|
concat_mods(){
|
|
readarray -t concat_arr <<< "$@"
|
|
local id
|
|
local encoded_id
|
|
local link
|
|
for i in "${concat_arr[@]}"; do
|
|
until [[ -f $workshop_dir/$i/meta.cpp ]]; do
|
|
sleep 0.1s
|
|
done
|
|
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'
|
|
}
|
|
is_dayz_running(){
|
|
local proc=$(ps aux | grep "DayZ_x64.exe" | grep -v grep)
|
|
if [[ -n $proc ]]; then
|
|
echo 1
|
|
else
|
|
echo 0
|
|
fi
|
|
}
|
|
launch(){
|
|
local ip="$1"
|
|
local gameport="$2"
|
|
local mods="$3"
|
|
local appid="$4"
|
|
|
|
local concat
|
|
if [[ -n $mods ]]; then
|
|
concat=$(concat_mods "$mods")
|
|
else
|
|
concat=""
|
|
fi
|
|
|
|
update_symlinks
|
|
[[ $appid -eq $exp ]] && clone_symlinks
|
|
|
|
if [[ $debug -eq 1 ]]; then
|
|
local launch_options="$steam_cmd -applaunch $appid -connect=$ip:$gameport -nolauncher -nosplash -name=$name -skipintro -mod=$concat"
|
|
printf "Debug mode: these options would have been used to launch the game: %s" "$launch_options"
|
|
return 0
|
|
fi
|
|
echo "$concat" > "$_cache_launch"
|
|
echo "$ip:$gameport" > "$_cache_address"
|
|
logger INFO "Saved launch params: '$concat'"
|
|
printf "Launch conditions satisfied. DayZ will launch after you confirm this dialog."
|
|
[[ $appid == "$aid" ]] && return 100
|
|
[[ $appid == "$exp" ]] && return 101
|
|
}
|
|
final_handshake(){
|
|
local appid="$1"
|
|
local saved_mods=$(< "$_cache_launch")
|
|
local saved_address=$(< "$_cache_address")
|
|
local res=$(is_dayz_running)
|
|
if [[ $res -eq 1 ]]; then
|
|
logger WARN "DayZ appears to already be running"
|
|
printf "Is DayZ already running? DZGUI cannot launch DayZ if another process is using it."
|
|
return 1
|
|
fi
|
|
logger INFO "Kicking off Steam launch"
|
|
local params=()
|
|
params+=("-connect=$saved_address")
|
|
params+=("-nolauncher")
|
|
params+=("-nosplash")
|
|
params+=("-skipintro")
|
|
params+=("-name=$name")
|
|
params+=("-mod=$saved_mods")
|
|
$steam_cmd -applaunch $appid "${params[@]}" &
|
|
until [[ $(is_dayz_running) -eq 1 ]]; do
|
|
sleep 0.1s
|
|
done
|
|
logger INFO "Caught DayZ process"
|
|
printf "\n"
|
|
return 6
|
|
}
|
|
watcher(){
|
|
local -n to_download="$1"
|
|
local -n signatures="$2"
|
|
local mode="$3"
|
|
|
|
declare -i i=0
|
|
local remote_time
|
|
local length
|
|
length=${#to_download[@]}
|
|
for mod in "${!to_download[@]}"; do
|
|
remote_time=${to_download[$mod]}
|
|
[[ -f $ex ]] && return 1
|
|
case $mode in
|
|
"auto" | "force")
|
|
$steam_cmd "steam://url/CommunityFilePage/${mod}+workshop_download_item ${aid} ${mod}"
|
|
echo "# Opening workshop page for ${mod}. If you see no progress, you may be out of disk space."
|
|
;;
|
|
*)
|
|
$steam_cmd "steam://url/CommunityFilePage/${mod}"
|
|
echo "# Opening workshop page for ${mod}. If you see no progress after subscribing, try unsubscribing and resubscribing again until the download commences."
|
|
;;
|
|
esac
|
|
sleep 1s
|
|
foreground
|
|
|
|
until [[ -d $downloads_dir/${mod} ]]; do
|
|
[[ -f $ex ]] && return 1
|
|
sleep 0.1s
|
|
# NOTE: fallback for instantaneous downloads
|
|
if [[ $mode == "auto" || $mode == "force" ]]; then
|
|
if [[ -d $workshop_dir/${mod} ]]; then
|
|
break
|
|
fi
|
|
fi
|
|
done
|
|
|
|
foreground
|
|
case $mode in
|
|
"auto" | "force")
|
|
echo "# Steam is downloading ${mod} (mod $((i+1)) of ${length}). You do not need to manually Subscribe."
|
|
;;
|
|
*)
|
|
echo "# Steam is downloading ${mod} (mod $((i+1)) of ${length})"
|
|
;;
|
|
esac
|
|
|
|
until [[ -d $workshop_dir/${mod} ]]; do
|
|
[[ -f $ex ]] && return 1
|
|
sleep 0.1s
|
|
done
|
|
|
|
foreground
|
|
if [[ ! -v signatures["${mod}"] ]]; then
|
|
echo "$mod,$remote_time" >> "$versions_file"
|
|
else
|
|
sed -i "s/$mod,${signatures[$mod]}/$mod,$remote_time/" "$versions_file"
|
|
fi
|
|
echo "# ${mod} moved to mods dir"
|
|
i+=1
|
|
done
|
|
echo "100"
|
|
}
|
|
install_mods(){
|
|
local sanitized_mods="$1"
|
|
local mode="$2"
|
|
local ex="$state_path/dzg.watcher"
|
|
|
|
[[ -f $ex ]] && rm "$ex"
|
|
|
|
if [[ -f "$versions_file" ]]; then
|
|
readarray -t local_mods < "$versions_file"
|
|
else
|
|
local_mods=()
|
|
fi
|
|
|
|
declare -A mod_signatures
|
|
local local_id
|
|
local local_time
|
|
for mod in "${local_mods[@]}"; do
|
|
IFS="," read -ra fields <<< "$mod"
|
|
local_id="${fields[0]}"
|
|
local_time="${fields[1]}"
|
|
mod_signatures[$local_id]=$local_time
|
|
done
|
|
|
|
readarray -t remote_mods <<< "$sanitized_mods"
|
|
declare -A download
|
|
local remote_timestamps
|
|
local remote_id
|
|
local remote_time
|
|
remote_timestamps=$(get_timestamps "${remote_mods[@]}")
|
|
for mod in $(<<< "$remote_timestamps" jq -r '.response.publishedfiledetails[]|"\(.publishedfileid),\(.time_updated)"'); do
|
|
IFS="," read -ra fields <<< "$mod"
|
|
remote_id="${fields[0]}"
|
|
remote_time="${fields[1]}"
|
|
|
|
# NOTE: mod is not present in versions file
|
|
if [[ ! -v mod_signatures["$remote_id"] ]]; then
|
|
download["$remote_id"]=$remote_time
|
|
continue
|
|
fi
|
|
# NOTE: mod timestamp is out of date
|
|
local_time="${mod_signatures["$remote_id"]}"
|
|
if [[ $remote_time != "$local_time" ]]; then
|
|
download["$remote_id"]=$remote_time
|
|
continue
|
|
fi
|
|
done
|
|
|
|
[[ ${#download[@]} -lt 1 ]] && return
|
|
if [[ $(check_architecture) -eq 1 ]] && [[ $(test_display_mode) == "gm" ]]; then
|
|
printf "Use Desktop Mode to download mods on Steam Deck"
|
|
exit 1
|
|
fi
|
|
watcher download mod_signatures "$mode" > >($steamsafe_zenity --pulsate --progress --auto-close --title="DZG Watcher" --width=500 2>/dev/null; rc=$?; [[ $rc -eq 1 ]] && touch "$ex")
|
|
}
|
|
test_display_mode(){
|
|
pgrep -a gamescope | grep -q "generate-drm-mode"
|
|
if [[ $? -eq 0 ]]; then
|
|
echo gm
|
|
else
|
|
echo dm
|
|
fi
|
|
}
|
|
foreground(){
|
|
if [[ $(command -v wmctrl) ]]; then
|
|
wmctrl -a "DZG Watcher"
|
|
else
|
|
local wid=$(xdotool search --name "DZG Watcher")
|
|
xdotool windowactivate $wid
|
|
fi
|
|
}
|
|
main(){
|
|
local params="$(printf '"%s", ' "$@")"
|
|
logger INFO "Received request from UI with params [${params::-2}]"
|
|
func=${funcs["$1"]}
|
|
[[ -z $func ]] && return 1
|
|
if [[ -z $2 ]]; then
|
|
shift
|
|
$func
|
|
else
|
|
$func "$@"
|
|
fi
|
|
}
|
|
main "$@"
|