Compare commits

..
1 Commits
Author SHA1 Message Date
Koloszár Gergely 73eae30395 Merge dccdffe761 into 23fb10f48e 2025-10-24 14:15:24 +00:00
16 changed files with 635 additions and 893 deletions

No files matched your search

-4
View File
@@ -5,10 +5,6 @@ labels: ["bug"]
assignees: assignees:
- aclist - aclist
body: body:
- type: markdown
attributes:
value: |
### WARNING: When posting log/config data, redact any sensitive information like API keys.
- type: checkboxes - type: checkboxes
id: kb id: kb
attributes: attributes:
-4
View File
@@ -5,10 +5,6 @@ labels: ["troubleshooting"]
assignees: assignees:
- aclist - aclist
body: body:
- type: markdown
attributes:
value: |
### WARNING: When posting log/config data, redact any sensitive information like API keys.
- type: dropdown - type: dropdown
id: device id: device
attributes: attributes:
+1 -1
View File
@@ -8,7 +8,7 @@ jobs:
mirror-to-codeberg: mirror-to-codeberg:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: pixta-dev/repository-mirroring-action@v1 - uses: pixta-dev/repository-mirroring-action@v1
-3
View File
@@ -1,4 +1 @@
tags tags
__pycache__
pre-commit
tests/
+5 -116
View File
@@ -1,125 +1,14 @@
# Changelog # Changelog
## [6.0.6-beta.1] 2026-05-17 ## [6.0.0-beta.8] XXXX-XX-XX
## Changed
- Update symlink hash logic to align with DZGUI 7 beta
- Update A2S helper to latest commit
## [6.0.5-beta.2] 2026-04-17
## Changed
- During server connection attempts, only download mods local to specific server
## [6.0.5-beta.1] 2026-04-13
## Fixed
- Update A2S module checksums to support servers with missing description field
## Changed
- Bump version to reflect hotfixes on stable branch
## [6.0.2-beta.5] 2026-04-12
## Fixed
- Use conditional logic when testing GLibUnix namespaces
## [6.0.2-beta.4] 2026-04-10
## Fixed
- Use GLibUnix namespace when setting up SIGINT handling
## [6.0.2-beta.3] 2026-01-26
## Fixed
- Raise error correctly from module
## [6.0.2-beta.2] 2026-01-18
## Fixed
- Helpers not being downloaded (uncommented boilerplate)
## [6.0.2-beta.1] 2026-01-18
## Fixed
- Do not spawn dialogs from inside of thread when fetching prereqs
## Changed
- Optimize time complexity of startup checks
- Optimize coordinate calculation by using local records first
- Rename some internal functions
- Wrap entire startup process in dialog
## [6.0.1-beta.1] 2026-01-16
## Fixed
- Explicitly use Python 3.13 when calling subprocesses
## [6.0.0-beta.20] 2026-01-14
## Fixed
- Write cache file after wiping
## [6.0.0-beta.19] 2026-01-14
## Fixed
- Steam Deck compatible script path expansion
## [6.0.0-beta.18] 2026-01-14
## Fixed
- Update UI checksums
## [6.0.0-beta.17] 2026-01-14
## Added ## Added
- More descriptive help dialogs when initial dependencies are missing - Flag to disable branch toggle for distro-packaged releases
## [6.0.0-beta.16] 2026-01-04
## Fixed
- Regression introduced in previous hotfix
## [6.0.0-beta.15] 2025-12-27
## Fixed
- Path discovery during first-time setup when parsing filepaths with whitespaces
- First-time setup dialog continuously triggering when DayZ install path had whitespaces in it
## [6.0.0-beta.14] 2025-12-09
## Fixed
- Dialogs with newlines breaking output in log table
## [6.0.0-beta.13] 2025-11-29
## Fixed
- Server filter panel not being hidden when entering other page contexts
- Path to remote changelog being constructed incorrectly
## [6.0.0-beta.12] 2025-11-14
## Changed
- Require Python 3.13
## Fixed
- Normalize beta version
## [6.0.0-beta.11] 2025-11-13
## Added
- Support sandboxed flatpak
## Changed
- Preferred client from radio toggle to combobox
## Fixed ## Fixed
- Config file erroneously getting updated when populating settings menu - ESC key destroying wait dialogs while thread is pending
- Tooltip signals being processed on main menu
## [6.0.0-beta.10] 2025-11-10 - Leaky variable name in dialog title
## Fixed
- Python 3.14 error in finally block
- Require Python version between 3.11 and 3.12
## [6.0.0-beta.9] 2025-11-05
## Fixed
- UI not being constructed correctly if CHANGELOG.md was missing
## [6.0.0-beta.8] 2025-11-04
## Added
- Internal flag to allow distribution packaged releases to disable in-app updates
- Pre-boot check to test whether script was invoked directly
- Commandline usage help text
## Fixed
- ESC key destroying wait dialogs while a thread is pending
- Tooltip signals being erroneously drawn on main menu
- Leaky variable name in dialog titles
- Prevent extraneous signals from propagating when column width is adjusted - Prevent extraneous signals from propagating when column width is adjusted
- Script failing to start when remote endpoints are unavailable (GaryBlackbourne)
## Changed
- Optimize time complexity of pre-boot checks (GaryBlackbourne)
- Moved location of notes file
- Rewrote distance calculation module (GaryBlackbourne)
## [6.0.0-beta.7] 2025-09-20 ## [6.0.0-beta.7] 2025-09-20
## Added ## Added
+33 -38
View File
@@ -1,15 +1,16 @@
# Introduction # Introduction
Thank you for your interest in DZGUI! Thank you for considering contributing to DZGUI!
This guide goes over development conventions and best practices for contributors. This is a guide for contributors containing conventions best practices and rules
If you are a developer, you can skip to the end. of development and aims to ease the beginning of a contribution.
# Requesting help # How can I request help?
If you encounter a problem with DZGUI, you can submit tickets on the GitHub There is an issue tracker on github
[issue tracker](https://github.com/aclist/dztui/issues) under the (link)[https://github.com/aclist/dzgui-devel/issues] where you can submit
`troubleshooting` tag. tickets. If you need help troubleshooting a problem, submit a ticket. There is a
category called `troubleshooting` which is exactly what you need.
# How can I help the project? # How can I help the project?
@@ -22,50 +23,44 @@ There are several ways to help this project.
Before making any contribution, please read the `CODE_OF_CONDUCT.md` and act Before making any contribution, please read the `CODE_OF_CONDUCT.md` and act
accordingly. accordingly.
## Submitting a ticket ## How to report
Navigate to the GitHub [issue tracker](https://github.com/aclist/dztui/issues).
From there, follow the onscreen prompts. You will be asked questions such as:
Reproduce the bug that you found. Navigate to the issue tracker on github:
(link)[https://github.com/aclist/dzgui-devel/issues]
Create a ticket where you give us the following:
- What version are you using? - What version are you using?
- What distribution are you using? - What distribution are you using?
- What is the issue that you found? - What is the issue that you found?
- How can we reproduce the issue? - How can we reproduce the issue?
- Possible screenshots, logs, other data that can help us
You can also attach screenshots, logs, or other data that can help us. ## How to request a feature
## Requesting a feature Navigate to the projects issue tracker
(link)[https://github.com/aclist/dzgui-devel/issues]. Search for your idea to
see if anyone had posted a simmilar idea. If not, open a ticket where you
describe your feature and its possible benefits. Please note, that this is a
community project, it takes time for us to develop features, having a feature
request doesn't mean that it will be implemented, but we do our best to support
as many cool ideas as possible.
You can also request features via the same issue tracker. It is good practice to ## How to contribute code or documentation
first search for your idea to see if a similar one has already been posted.
If not, open a ticket where you describe your feature and its possible benefits.
Please note that this is a community project, so it takes time for us to develop If you have an issue of interest in the issue tracker or any other change that
features. Putting in a feature request does not mean that it will be implemented, you would like to have implemented you can contribute code to this project. For
but we will do our best to support as many cool ideas as possible. this, you may fork this repository and checkout to
`prerelease/<latest-version>`. All new features and fixes will be merged to this
## Contributing code or documentation branch, so open your branch from here. Name it according to our convention:
- fix/<your-fix> - fix branches
If you would like to take ownership or assist with an issue on the issue tracker, or
contribute a new change, please follow the guidelines below.
Fork this repository and check out the code up to `prerelease/<latest-version>`.
If there is no pending prerelease PR, you can base your changes off of
the `testing` branch. You may later be asked to retarget your PR to the prerelease
branch once a new one is made. This is because out-of-band PRs are consolidated into
the next release before merging.
The following naming conventions apply for PRs:
- fix/<your-fix> - patch/hotfix branches
- feat/<your-feature> - feature branches - feat/<your-feature> - feature branches
- doc/<your-doc-branch> - documentation branches - doc/<your-doc-branch> - documentation branches
- infra/<your-infra-branch> - infrastructure branches - infra/<your-infra-branch> - infrastructure branches
Implement your changes and test them locally. If they work you may Now you can implement your changes and test it locally. If they work you may
open a merge request, and then we review your changes. If everything is OK, it open a merge request, and then we review your changes. If everything is OK, it
will be merged to `prerelease/<latest-version>`, then `testing`, and after a live will be merged to `prerelease/<latest-version>`, then `testing` and after a live
testing phase, to `stable` as well. testing phase, to `stable` as well.
It is recommended to follow During development, it is recommended to follow (conventional
[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), as this commits)[https://www.conventionalcommits.org/en/v1.0.0/], it helps the project
integrates well with tooling and helps the project review your code. maintainers when they review your code.
+1 -9
View File
@@ -11,15 +11,6 @@ This overcomes certain limitations in the Linux client and helps prepare the gam
- Concatenate launch options to pass to Steam - Concatenate launch options to pass to Steam
- Connect to mod-enabled LAN servers - Connect to mod-enabled LAN servers
## Design philosophy
DZGUI follows a specific set of guidelines, enumerated below:
- Provide a free and open-source tool under GPL
- Do not use undocumented, third-party servers to aggregate data; no central "DZGUI" server
- All connections go to Steam and DayZ servers directly; the tool should function in perpetuity without intermediary endpoints
- Target Linux desktop and Steam deck specifically; not a cross-platform tool
## Setup and usage ## Setup and usage
Refer to the documentation for installation and setup instructions: Refer to the documentation for installation and setup instructions:
@@ -27,6 +18,7 @@ Refer to the documentation for installation and setup instructions:
- [GitHub](https://aclist.github.io/dzgui/index.html) - [GitHub](https://aclist.github.io/dzgui/index.html)
- [Mirror (Codeberg)](https://aclist.codeberg.page) - [Mirror (Codeberg)](https://aclist.codeberg.page)
![A screenshot of DZGUI](/images/example.png) ![A screenshot of DZGUI](/images/example.png)
## Attribution ## Attribution
+115 -269
View File
@@ -1,8 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -o pipefail set -o pipefail
src_path="$(readlink -e "$0")" version=6.0.0.beta-8
version=6.0.6-beta.1
#CONSTANTS #CONSTANTS
aid=221100 aid=221100
@@ -40,7 +39,6 @@ cols_file="$state_path/$prefix.cols.json"
#CACHE FILES #CACHE FILES
coords_file="$cache_path/$prefix.coords" coords_file="$cache_path/$prefix.coords"
src_path_file="$cache_path/$prefix.src"
#legacy paths #legacy paths
hist_file="$config_path/history" hist_file="$config_path/history"
@@ -65,14 +63,17 @@ testing_url="$url_prefix/testing"
releases_url="https://github.com/$author/$repo/releases/download/browser" releases_url="https://github.com/$author/$repo/releases/download/browser"
km_helper_url="$releases_url/latlon" km_helper_url="$releases_url/latlon"
set_im_module(){ set_im_module(){
#TODO: drop pending SteamOS changes #TODO: drop pending SteamOS changes
if pgrep -a gamescope | grep -q "generate-drm-mode"; then pgrep -a gamescope | grep -q "generate-drm-mode"
unset GTK_IM_MODULE if [[ $? -eq 0 ]]; then
GTK_IM_MODULE=""
logger INFO "Detected Steam Deck (Game Mode), unsetting GTK_IM_MODULE" logger INFO "Detected Steam Deck (Game Mode), unsetting GTK_IM_MODULE"
else
return
fi fi
} }
redact(){ redact(){
sed 's@\(/home/\)[^/]*@\1REDACTED@g' sed 's@\(/home/\)[^/]*@\1REDACTED@g'
} }
@@ -86,21 +87,13 @@ logger(){
printf "%s␞%s␞%s::%s()::%s␞%s\n" "$date" "$tag" "$self" "$caller" "$line" "$string" \ printf "%s␞%s␞%s::%s()::%s␞%s\n" "$date" "$tag" "$self" "$caller" "$line" "$string" \
| redact >> "$debug_log" | redact >> "$debug_log"
} }
setup_dirs(){ setup_dirs(){
directories=() for dir in "$state_path" "$cache_path" "$share_path" "$helpers_path" "$freedesktop_path" "$config_path" "$log_path"; do
directories+=("$state_path") if [[ ! -d $dir ]]; then
directories+=("$cache_path")
directories+=("$share_path")
directories+=("$helpers_path")
directories+=("$freedesktop_path")
directories+=("$config_path")
directories+=("$log_path")
for dir in "${directories[@]}"; do
mkdir -p "$dir" mkdir -p "$dir"
fi
done done
} }
setup_state_files(){ setup_state_files(){
if [[ -f "$debug_log" ]]; then if [[ -f "$debug_log" ]]; then
rm "$debug_log" && touch $debug_log rm "$debug_log" && touch $debug_log
@@ -111,13 +104,13 @@ setup_state_files(){
logger INFO "Migrating legacy version file" logger INFO "Migrating legacy version file"
fi fi
# wipe cache files # wipe cache files
if [[ $(ls -A "$cache_path") ]]; then local path="$cache_path"
for file in "$cache_path"/*; do if find "$path" -mindepth 1 -maxdepth 1 | read; then
for file in $path/*; do
rm "$file" rm "$file"
done done
logger INFO "Wiped cache files" logger INFO "Wiped cache files"
fi fi
echo "$src_path" > "$src_path_file"
} }
print_config_vals(){ print_config_vals(){
local keys=( local keys=(
@@ -139,10 +132,10 @@ print_config_vals(){
} }
test_gobject(){ test_gobject(){
python3.13 -c "import gi" python3 -c "import gi"
if [[ ! $? -eq 0 ]]; then if [[ ! $? -eq 0 ]]; then
logger CRITICAL "Missing PyGObject" logger CRITICAL "Missing PyGObject"
quit_with_help_dialog "Requires PyGObject (python-gobject)" fdialog "Requires PyGObject (python-gobject)"
exit 1 exit 1
fi fi
logger INFO "Found PyGObject in Python env" logger INFO "Found PyGObject in Python env"
@@ -221,14 +214,17 @@ default_steam_path="$default_steam_path"
#Preferred Steam launch command (for Flatpak support) #Preferred Steam launch command (for Flatpak support)
preferred_client="$preferred_client" preferred_client="$preferred_client"
#DZGUI source path
src_path="$src_path"
END END
} }
depcheck(){ depcheck(){
for dep in "${!deps[@]}"; do for dep in "${!deps[@]}"; do
if ! command -v "$dep" &> /dev/null; then command -v "$dep" 2>&1>/dev/null
if [[ $? -eq 1 ]]; then
local msg="Requires $dep >= ${deps[$dep]}" local msg="Requires $dep >= ${deps[$dep]}"
echo "$msg" raise_error_and_quit "$msg"
exit 1
fi fi
done done
local jqmsg="jq must be compiled with support for oniguruma" local jqmsg="jq must be compiled with support for oniguruma"
@@ -237,31 +233,14 @@ depcheck(){
[[ $? -ne 0 ]] && raise_error_and_quit "$jqmsg" [[ $? -ne 0 ]] && raise_error_and_quit "$jqmsg"
logger INFO "Initial dependencies satisfied" logger INFO "Initial dependencies satisfied"
} }
open_url(){
url="$1"
if [[ -n "$BROWSER" ]]; then
logger INFO "Opening '$url' in '$BROWSER'"
"$BROWSER" "$url"
else
logger INFO "Opening '$url' with xdg-open"
xdg-open "$url"
fi
}
quit_with_help_dialog(){
local sel
msg="$1"
help_button="Open help page"
url="https://aclist.github.io/dzgui/installation.html"
logger CRITICAL "$msg"
sel=$(zenity --info --extra-button="$help_button" --text="$msg")
[[ $sel == "$help_button" ]] && open_url "$url" &
exit 1
}
check_pyver(){ check_pyver(){
if [[ ! $(python3.13 --version) ]]; then local pyver=$(python3 --version | awk '{print $2}')
local msg="Requires Python 3.13" local minor=$(<<< $pyver awk -F. '{print $2}')
quit_with_help_dialog "$msg" if [[ -z $pyver ]] || [[ ${pyver:0:1} -lt 3 ]] || [[ $minor -lt 10 ]]; then
local msg="Requires Python >=3.10"
raise_error_and_quit "$msg"
fi fi
logger INFO "Found Python version: $pyver"
} }
watcher_deps(){ watcher_deps(){
if [[ ! $(command -v wmctrl) ]] && [[ ! $(command -v xdotool) ]]; then if [[ ! $(command -v wmctrl) ]] && [[ ! $(command -v xdotool) ]]; then
@@ -335,8 +314,6 @@ check_unmerged(){
fi fi
} }
check_version(){ check_version(){
echo "# Checking version"
[[ -n $reference_branch ]] && return
local version_url=$(format_version_url) local version_url=$(format_version_url)
local upstream=$(curl -Ls "$version_url" | awk -F= '/^version=/ {print $2}') local upstream=$(curl -Ls "$version_url" | awk -F= '/^version=/ {print $2}')
local res=$(get_response_code "$version_url") local res=$(get_response_code "$version_url")
@@ -400,11 +377,10 @@ prompt_dl(){
dl_changelog(){ dl_changelog(){
local mdbranch local mdbranch
local md local md
source "$config_file"
[[ $branch == "stable" ]] && mdbranch="dzgui" [[ $branch == "stable" ]] && mdbranch="dzgui"
[[ $branch == "testing" ]] && mdbranch="testing" [[ $branch == "testing" ]] && mdbranch="testing"
local changelog="$url_prefix/${mdbranch}/CHANGELOG.md" local md="$url_prefix/${mdbranch}/$file"
curl -Ls "$changelog" > "$state_path/CHANGELOG.md" curl -Ls "$md" > "$state_path/CHANGELOG.md"
} }
test_display_mode(){ test_display_mode(){
pgrep -a gamescope | grep -q "generate-drm-mode" pgrep -a gamescope | grep -q "generate-drm-mode"
@@ -415,7 +391,6 @@ test_display_mode(){
fi fi
} }
check_architecture(){ check_architecture(){
echo "# Checking system architecture"
local cpu=$(< /proc/cpuinfo awk -F": " '/AMD Custom APU [0-9]{4}$/ {print $2; exit}') local cpu=$(< /proc/cpuinfo awk -F": " '/AMD Custom APU [0-9]{4}$/ {print $2; exit}')
read -a APU_MODEL <<< "$cpu" read -a APU_MODEL <<< "$cpu"
if [[ ${APU_MODEL[3]} != "0932" ]] && [[ ${APU_MODEL[3]} != "0405" ]]; then if [[ ${APU_MODEL[3]} != "0932" ]] && [[ ${APU_MODEL[3]} != "0405" ]]; then
@@ -432,7 +407,6 @@ check_architecture(){
logger INFO "Setting architecture to 'Steam Deck'" logger INFO "Setting architecture to 'Steam Deck'"
} }
check_map_count(){ check_map_count(){
echo "# Checking map count"
[[ $is_steam_deck -gt 0 ]] && return 0 [[ $is_steam_deck -gt 0 ]] && return 0
local map_count_file="/proc/sys/vm/max_map_count" local map_count_file="/proc/sys/vm/max_map_count"
local min_count=1048576 local min_count=1048576
@@ -482,7 +456,6 @@ tdialog(){
$steamsafe_zenity --info --text="$1" "${zenity_flags[@]}" $steamsafe_zenity --info --text="$1" "${zenity_flags[@]}"
} }
steam_deps(){ steam_deps(){
echo "# Checking Steam"
local flatpak local flatpak
local steam local steam
[[ $(command -v flatpak) ]] && flatpak=$(flatpak list | grep valvesoftware.Steam) [[ $(command -v flatpak) ]] && flatpak=$(flatpak list | grep valvesoftware.Steam)
@@ -491,64 +464,50 @@ steam_deps(){
local msg="Found neither Steam nor Flatpak Steam" local msg="Found neither Steam nor Flatpak Steam"
raise_error_and_quit "$msg" raise_error_and_quit "$msg"
exit 1 exit 1
elif [[ -n "$steam" ]] && [[ -n "$flatpak" ]]; then
[[ -n $preferred_client ]] && return 0
if [[ -z $preferred_client ]]; then
preferred_client="steam"
fi fi
elif [[ -n "$steam" ]]; then
preferred_client="steam"
else
preferred_client="flatpak"
fi
update_config
logger INFO "Preferred client set to '$preferred_client'"
}
migrate_files(){
if [[ ! -f $config_path/dztuirc.oldapi ]]; then
cp $config_file $config_path/dztuirc.oldapi
logger INFO "Migrated old API file"
fi
[[ ! -f $hist_file ]] && return
rm $hist_file
logger INFO "Wiped old history file"
} }
stale_symlinks(){ stale_symlinks(){
echo "# Cleaning stale symlinks"
local game_dir="$steam_path/steamapps/common/DayZ" local game_dir="$steam_path/steamapps/common/DayZ"
readarray -t links < <(find "$game_dir" -xtype l) for l in $(find "$game_dir" -xtype l); do
for link in "${links[@]}"; do logger DEBUG "Updating stale symlink '$l'"
logger DEBUG "Updating stale symlink '$link'" unlink "$l"
unlink "$link"
done done
} }
check_availability() {
if [[ -z $1 ]]; then
return 1
fi
local url=$1
local timeout_sec="1"
if [[ $2 ]]; then
timeout_sec=$2
fi
if ! ping -w "$timeout_sec" "$url" > /dev/null 2>&1; then
logger WARN "Failed to reach $url, service may be down."
return 1
fi
}
local_latlon(){ local_latlon(){
echo "# Checking coordinates"
if [[ -z $(command -v dig) ]]; then if [[ -z $(command -v dig) ]]; then
local url_ipecho="https://ipecho.net/plain" local local_ip=$(curl -Ls "https://ipecho.net/plain")
if ! check_availability "ipecho.net"; then
logger WARN "Failed to get external ip address, ipecho.net service may be down."
return 1
fi
local local_ip=$(curl -Ls "$url_ipecho")
else else
# TODO : implement checking remote
local local_ip=$(dig -4 +short myip.opendns.com @resolver1.opendns.com) local local_ip=$(dig -4 +short myip.opendns.com @resolver1.opendns.com)
fi fi
if calc_local_coords "$local_ip"; then local url="http://ip-api.com/json/$local_ip"
return 0 local res=$(curl -Ls "$url" | jq -r '"\(.lat)\n\(.lon)"')
fi
local url_ip_api="http://ip-api.com/json/$local_ip"
if ! check_availability "ip-api.com"; then
logger WARN "Failed to get local coordinates, ip-api.com service may be down."
return 1
fi
local res=$(curl -Ls "$url_ip_api" | jq -r '"\(.lat)\n\(.lon)"')
if [[ -z "$res" ]]; then if [[ -z "$res" ]]; then
logger WARN "Failed to get local coordinates" logger WARN "Failed to get local coordinates"
return 1 return 1
fi fi
echo "$res" > "$coords_file" echo "$res" > "$coords_file"
} }
lock(){ lock(){
echo "# Setting up lock file"
[[ ! -f $lock_file ]] && touch $lock_file [[ ! -f $lock_file ]] && touch $lock_file
local pid=$(cat $lock_file) local pid=$(cat $lock_file)
ps -p $pid -o pid= >/dev/null 2>&1 ps -p $pid -o pid= >/dev/null 2>&1
@@ -567,12 +526,9 @@ get_hash(){
md5sum "$1" | awk '{print $1}' md5sum "$1" | awk '{print $1}'
} }
fetch_a2s(){ fetch_a2s(){
sum="d26da2fe03df2b95436f823f0496887b" # this file is currently monolithic
if [[ -d $helpers_path/a2s ]] && [[ $(get_hash "$helpers_path/a2s/info.py") == $sum ]]; then [[ -d $helpers_path/a2s ]] && { logger INFO "A2S helper is current"; return 0; }
logger INFO "A2S helper is current" local sha=c7590ffa9a6d0c6912e17ceeab15b832a1090640
return 0
fi
local sha=b40eb24cdbb06ebd08272f224257fe5a81610e86
local author="yepoleb" local author="yepoleb"
local repo="python-a2s" local repo="python-a2s"
local url="https://github.com/$author/$repo/tarball/$sha" local url="https://github.com/$author/$repo/tarball/$sha"
@@ -586,13 +542,13 @@ fetch_a2s(){
logger INFO "Updated A2S helper to sha '$sha'" logger INFO "Updated A2S helper to sha '$sha'"
} }
fetch_dzq(){ fetch_dzq(){
local sum="0d316badbb8d943c5f078e73862cac3e" local sum="0a334e1e144e76e560419d155435c91e"
local file="$helpers_path/a2s/dayzquery.py" local file="$helpers_path/a2s/dayzquery.py"
if [[ -f $file ]] && [[ $(get_hash "$file") == $sum ]]; then if [[ -f $file ]] && [[ $(get_hash "$file") == $sum ]]; then
logger INFO "DZQ is current" logger INFO "DZQ is current"
return 0 return 0
fi fi
local sha="07483b88ed096327ebca752f5e177011b604d7fe" local sha=a22a9f428cbe075d7dda62f78000296955eea92a
local author="yepoleb" local author="yepoleb"
local repo="dayzquery" local repo="dayzquery"
local url="https://raw.githubusercontent.com/$author/$repo/$sha/dayzquery.py" local url="https://raw.githubusercontent.com/$author/$repo/$sha/dayzquery.py"
@@ -629,12 +585,12 @@ fetch_helpers_by_sum(){
[[ -f "$config_file" ]] && source "$config_file" [[ -f "$config_file" ]] && source "$config_file"
declare -A sums declare -A sums
sums=( sums=(
["funcs"]="1cd26343cd68a5382e3377a9757497fa" ["funcs"]="65134f86b7c9e14173e05d8e3ebea101"
["query_v2.py"]="55d339ba02512ac69de288eb3be41067" ["query_v2.py"]="55d339ba02512ac69de288eb3be41067"
["servers.py"]="ed442c3aecf33f777d59dcf53650d263" ["servers.py"]="ed442c3aecf33f777d59dcf53650d263"
["ui.py"]="cf52a3d5883afe02a013d8673df494fa" ["ui.py"]="f03879f088ad074f2ce3c92607c30202"
["vdf2json.py"]="2f49f6f5d3af919bebaab2e9c220f397" ["vdf2json.py"]="2f49f6f5d3af919bebaab2e9c220f397"
["pefile.py"]="b452974a84bff1d821872fcebf59e380" ["pefile.py"]="21531f2c0d9dfa5f110cf6779f9d22c0"
) )
local author="aclist" local author="aclist"
local repo="dztui" local repo="dztui"
@@ -653,10 +609,6 @@ fetch_helpers_by_sum(){
realbranch="dzgui" realbranch="dzgui"
fi fi
if [[ -n $reference_branch ]]; then
realbranch="$reference_branch"
fi
for i in "${!sums[@]}"; do for i in "${!sums[@]}"; do
file="$i" file="$i"
sum="${sums[$i]}" sum="${sums[$i]}"
@@ -694,12 +646,15 @@ fetch_km_helper(){
} }
get_response_code(){ get_response_code(){
local url="$1" local url="$1"
curl --connect-timeout 3 -Ls -I -o /dev/null -w "%{http_code}" "$url" curl -Ls -I -o /dev/null -w "%{http_code}" "$url"
}
raise_error_and_quit(){
echo "$1"
exit 1
} }
fetch_ip_db(){ fetch_ip_db(){
parse_dl_url(){ parse_dl_url(){
curl --connect-timeout 3 -Ls "$url" \ curl -Ls "$url" \
| grep "csv.gz" \ | grep "csv.gz" \
| awk -F"['']" '{print $2}' | awk -F"['']" '{print $2}'
} }
@@ -715,9 +670,6 @@ fetch_ip_db(){
fetch(){ fetch(){
logger INFO "Triggering fetch routine" logger INFO "Triggering fetch routine"
local url="$1" local url="$1"
local ip_file="${state_path}/ips.csv"
local base_file="${state_path}/${this_month}.csv.gz"
local extracted_file="${state_path}/${this_month}.csv"
curl -Ls "$url" > "$base_file" curl -Ls "$url" > "$base_file"
if [[ $? -ne 0 ]]; then if [[ $? -ne 0 ]]; then
@@ -737,7 +689,7 @@ fetch_ip_db(){
logger WARN "Abnormal exit while parsing IPs" logger WARN "Abnormal exit while parsing IPs"
return return
fi fi
rm "${state_path}/${this_month}.csv" rm "${state_path}/${month}.csv"
readarray -t records < <(cat $ip_file | awk -F, 'NR==1 {print $1} END {print $1}') readarray -t records < <(cat $ip_file | awk -F, 'NR==1 {print $1} END {print $1}')
if [[ ${records[0]} != "0.0.0.0" ]] && [[ ${records[1]} != "224.0.0.0" ]]; then if [[ ${records[0]} != "0.0.0.0" ]] && [[ ${records[1]} != "224.0.0.0" ]]; then
@@ -752,13 +704,16 @@ fetch_ip_db(){
rm "$ip_file" rm "$ip_file"
fi fi
echo "$this_month" > "$month_file" echo "$month" > "$month_file"
logger INFO "Wrote '$this_month' to stub '$month_file'" logger INFO "Wrote '$month' to stub '$month_file'"
logger INFO "Updated '$ip_file'" logger INFO "Updated '$ip_file'"
} }
check_remote(){ local url="https://db-ip.com/db/download/ip-to-city-lite"
local url="$1" local month_file="${state_path}/.month"
local ip_file="${state_path}/ips.csv"
# test main url
local res=$(get_response_code "$url") local res=$(get_response_code "$url")
if [[ $res -ne 200 ]]; then if [[ $res -ne 200 ]]; then
logger WARN "Failed to retrieve remote resource: '$url' ($res)" logger WARN "Failed to retrieve remote resource: '$url' ($res)"
@@ -774,35 +729,30 @@ fetch_ip_db(){
return return
fi fi
logger INFO "Resolved download URL: '$dl_url'" logger INFO "Resolved download URL: '$dl_url'"
fetch "$dl_url"
}
local url="https://db-ip.com/db/download/ip-to-city-lite" local month=$(parse_dl_url_date "$dl_url")
month_file="${state_path}/.month" local base_file="${state_path}/${month}.csv.gz"
this_month=$(date +%Y-%m) local extracted_file="${state_path}/${month}.csv"
# no stub file # no stub file
if [[ ! -f $month_file ]]; then if [[ ! -f $month_file ]]; then
logger WARN "No stub file '$month_file' present" logger WARN "No stub file '$month_file' present"
check_remote "$url" fetch "$dl_url"
return
fi
# if stub is same date, abort
local last_month=$(< "$month_file")
if [[ $last_month == "$this_month" ]]; then
logger INFO "Local stub '$last_month' is identical to remote, skipping"
return return
fi fi
# local needs update # local needs update
logger WARN "Local stub '$last_month' does not match '$this_month'" local last_month=$(< "$month_file")
check_remote "$url" if [[ $last_month != "$month" ]]; then
logger WARN "Local stub '$last_month' does not match remote stub '$month'"
fetch "$dl_url"
return return
fi
# if stub is same date, abort
logger INFO "Local stub '$last_month' is identical to remote, skipping"
} }
fetch_helpers(){ fetch_helpers(){
echo "# Checking helper files"
fetch_a2s fetch_a2s
fetch_dzq fetch_dzq
fetch_km_helper fetch_km_helper
@@ -901,26 +851,12 @@ file_picker(){
} }
find_library_folder(){ find_library_folder(){
local search_path="$1" local search_path="$1"
readarray -t paths < <(python3.13 "$helpers_path/vdf2json.py" -i "$1/steamapps/libraryfolders.vdf" \ steam_path="$(python3 "$helpers_path/vdf2json.py" -i "$1/steamapps/libraryfolders.vdf" \
| jq -r '.libraryfolders[]|select(.apps|has("221100")).path') | jq -r '.libraryfolders[]|select(.apps|has("221100")).path')"
if [[ ! $? -eq 0 ]]; then if [[ ! $? -eq 0 ]] || [[ -z $steam_path ]]; then
logger WARN "Failed to parse Steam path using '$search_path'" logger WARN "Failed to parse Steam path using '$search_path'"
return 1 return 1
fi fi
if [[ ${#paths[@]} -eq 0 ]]; then
logger WARN "No valid VDF paths in '$search_path'"
return 1
fi
if [[ ${#paths[@]} -gt 1 ]]; then
for path in "${paths[@]}"; do
if [[ -d "$path" ]]; then
steam_path="$path"
break
fi
done
else
steam_path="${paths[0]}"
fi
logger INFO "Steam path resolved to: $steam_path" logger INFO "Steam path resolved to: $steam_path"
} }
create_config(){ create_config(){
@@ -929,7 +865,6 @@ create_config(){
unset default_steam_path unset default_steam_path
unset steam_path unset steam_path
preferred_client="steam"
while true; do while true; do
local player_input="$($steamsafe_zenity \ local player_input="$($steamsafe_zenity \
--forms \ --forms \
@@ -990,7 +925,6 @@ create_config(){
done done
} }
varcheck(){ varcheck(){
echo "# Checking config file"
local msg="Config file '$config_file' missing. Start first-time setup now?" local msg="Config file '$config_file' missing. Start first-time setup now?"
local msg2="The Steam paths set in your config file appear to be invalid (DayZ was moved or uninstalled). Restart first-time setup now?" local msg2="The Steam paths set in your config file appear to be invalid (DayZ was moved or uninstalled). Restart first-time setup now?"
if [[ ! -f $config_file ]]; then if [[ ! -f $config_file ]]; then
@@ -1004,7 +938,7 @@ varcheck(){
source "$config_file" source "$config_file"
local workshop_dir="$steam_path/steamapps/workshop/content/$aid" local workshop_dir="$steam_path/steamapps/workshop/content/$aid"
local game_dir="$steam_path/steamapps/common/DayZ" local game_dir="$steam_path/steamapps/common/DayZ"
if [[ ! -d $steam_path ]] || [[ ! -d $game_dir ]] || [[ ! $(find "$game_dir" -type f) ]]; then if [[ ! -d $steam_path ]] || [[ ! -d $game_dir ]] || [[ ! $(find $game_dir -type f) ]]; then
logger WARN "DayZ path resolved to '$game_dir'" logger WARN "DayZ path resolved to '$game_dir'"
logger WARN "Workshop path resolved to '$workshop_dir'" logger WARN "Workshop path resolved to '$workshop_dir'"
qdialog "$msg2" "Yes" "Exit" qdialog "$msg2" "Yes" "Exit"
@@ -1015,16 +949,18 @@ varcheck(){
create_config create_config
return 0 return 0
fi fi
if [[ $src_path != $(realpath "$0") ]]; then
src_path=$(realpath "$0")
update_config
fi
} }
is_dzg_downloading(){ is_dzg_downloading(){
echo "# Checking DayZ"
if [[ -d $steam_path ]] && [[ -d $steam_path/downloading/$aid ]]; then if [[ -d $steam_path ]] && [[ -d $steam_path/downloading/$aid ]]; then
logger WARN "DayZ may be scheduling updates" logger WARN "DayZ may be scheduling updates"
return 0 return 0
fi fi
} }
is_steam_running(){ is_steam_running(){
echo "# Checking Steam"
local res=$(ps aux | grep "steamwebhelper" | grep -v grep) local res=$(ps aux | grep "steamwebhelper" | grep -v grep)
if [[ -z $res ]]; then if [[ -z $res ]]; then
logger WARN "Steam may not be running" logger WARN "Steam may not be running"
@@ -1037,7 +973,6 @@ get_response_code(){
curl -Ls -I -o /dev/null -w "%{http_code}" "$url" curl -Ls -I -o /dev/null -w "%{http_code}" "$url"
} }
test_connection(){ test_connection(){
echo "# Testing connection"
declare -A hr declare -A hr
local res1 local res1
local res2 local res2
@@ -1071,7 +1006,6 @@ legacy_cols(){
mv $cols_file.new $cols_file mv $cols_file.new $cols_file
} }
stale_mod_signatures(){ stale_mod_signatures(){
echo "# Checking stale mods"
[[ ! -f "$versions_file" ]] && return [[ ! -f "$versions_file" ]] && return
local workshop_dir="$steam_path/steamapps/workshop/content/$aid" local workshop_dir="$steam_path/steamapps/workshop/content/$aid"
if [[ -d $workshop_dir ]]; then if [[ -d $workshop_dir ]]; then
@@ -1085,48 +1019,18 @@ stale_mod_signatures(){
} }
create_new_links(){ create_new_links(){
echo "# Setting up symlinks"
"$func_helper" "update_symlinks" "$func_helper" "update_symlinks"
} }
calc_local_coords(){
local ip="$1"
[[ ! -f "$geo_helper" ]] && return 1
IFS="." read -ra split <<< "$ip"
[[ ${#split[@]} -ne 4 ]] && return 1
prefix="^${split[0]}.${split[1]}."
readarray -t res < <(grep "$prefix" "$geo_helper")
for address in "${res[@]}"; do
IFS="," read -ra fields <<< "$address"
upper="${fields[1]}"
IFS="." read -ra upper_digits <<< "$upper"
if [[ ${split[2]} -gt ${upper_digits[2]} ]]; then
continue
fi
if [[ ${split[2]} -eq ${upper_digits[2]} ]]; then
if [[ ${split[3]} -gt ${upper_digits[3]} ]]; then
continue
fi
if [[ ${split[3]} -eq ${upper_digits[3]} ]]; then
echo "${fields[-2]}" >> "$coords_file"
echo "${fields[-1]}" >> "$coords_file"
return 0
fi
fi
if [[ ${split[2]} -lt ${upper_digits[2]} ]]; then
echo "${fields[-2]}" >> "$coords_file"
echo "${fields[-1]}" >> "$coords_file"
return 0
fi
done
return 1
}
initial_setup(){ initial_setup(){
setup_dirs
setup_state_files
depcheck
check_pyver
test_gobject
watcher_deps
check_architecture check_architecture
test_connection test_connection
fetch_helpers fetch_helpers > >(pdialog "Checking helper files")
varcheck varcheck
source "$config_file" source "$config_file"
lock lock
@@ -1135,12 +1039,14 @@ initial_setup(){
check_version check_version
check_map_count check_map_count
steam_deps steam_deps
migrate_files
stale_symlinks stale_symlinks
stale_mod_signatures stale_mod_signatures
create_new_links create_new_links
local_latlon local_latlon
is_steam_running is_steam_running
is_dzg_downloading is_dzg_downloading
print_config_vals
} }
uninstall(){ uninstall(){
_full(){ _full(){
@@ -1184,82 +1090,22 @@ uninstall(){
rm "$self" rm "$self"
echo "Uninstall routine complete" echo "Uninstall routine complete"
} }
usage(){
cat <<- EOM
DZGUI - Free and Open Source DayZ launcher
Usage:
dzgui.sh [options]
Description:
When no option is provided, the script launches DZGUI.
Options:
-u, --uninstall:
Uninstalls the software
-v, --version:
Prints the version
-h, --help:
Prints this message
EOM
}
main(){ main(){
# setup zenity environment local zenv=$(zenity --version 2>/dev/null)
local zenv=""
zenv=$(zenity --version 2>/dev/null)
[[ -z $zenv ]] && { echo "Requires zenity >= ${deps[$steamsafe_zenity]}"; exit 1; } [[ -z $zenv ]] && { echo "Requires zenity >= ${deps[$steamsafe_zenity]}"; exit 1; }
if [[ $1 == "--uninstall" ]] || [[ $1 == "-u" ]]; then
# parse arguments uninstall &&
while [[ $# -gt 0 ]]; do
case $1 in
"--uninstall" | "-u")
uninstall
exit 0 exit 0
# shift fi
;;
"--version" | "-v")
echo $version
exit 0
# shift
;;
"--help" | "-h")
usage
exit 0
# shift
;;
*)
echo "Unrecognized command!"
return 1
esac
done
set_im_module set_im_module
printf "Checking dependencies...\n" printf "Initializing setup...\n"
setup_dirs initial_setup
setup_state_files
depcheck
check_pyver
test_gobject
watcher_deps
initial_setup > >(pdialog "Initializing setup")
printf "All OK. Kicking off UI...\n" printf "All OK. Kicking off UI...\n"
python3.13 "$ui_helper" "--init-ui" "$version" "$is_steam_deck" python3 "$ui_helper" "--init-ui" "$version" "$is_steam_deck"
} }
main "$@"
if [[ $(basename "$0") == "dzgui.sh" ]]; then #TODO: tech debt: cruddy handling for steam forking
main "$@" [[ $? -eq 1 ]] && pkill -f dzgui.sh
#TODO: tech debt: cruddy handling for steam forking
[[ $? -eq 1 ]] && pkill -f dzgui.sh
fi
+153 -148
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -o pipefail set -o pipefail
version="6.0.5-beta.2" version="6.0.0-beta.8"
#CONSTANTS #CONSTANTS
aid=221100 aid=221100
@@ -51,7 +51,6 @@ _cache_binary="$cache_dir/$prefix.binary"
_cache_coords="$cache_path/$prefix.coords" _cache_coords="$cache_path/$prefix.coords"
_cache_cooldown="$cache_path/$prefix.cooldown" _cache_cooldown="$cache_path/$prefix.cooldown"
_cache_lan="$cache_path/$prefix.lan" _cache_lan="$cache_path/$prefix.lan"
_cache_src_path="$cache_path/$prefix.src"
#XDG #XDG
freedesktop_path="$HOME/.local/share/applications" freedesktop_path="$HOME/.local/share/applications"
@@ -88,14 +87,17 @@ steam_api_url="https://steamcommunity.com/dev/apikey"
battlemetrics_api_url="https://www.battlemetrics.com/developers" battlemetrics_api_url="https://www.battlemetrics.com/developers"
bm_api="https://api.battlemetrics.com/servers" bm_api="https://api.battlemetrics.com/servers"
steam_cmd="$preferred_client" if [[ $preferred_client == "steam" ]]; then
steam_cmd="steam"
else
steam_cmd="flatpak run com.valvesoftware.Steam"
fi
declare -A funcs=( declare -A funcs=(
["Highlight stale"]="find_stale_mods" ["Highlight stale"]="find_stale_mods"
["Change player name"]="update_config_val" ["Change player name"]="update_config_val"
["Change Steam API key"]="update_config_val" ["Change Steam API key"]="update_config_val"
["Change Battlemetrics API key"]="update_config_val" ["Change Battlemetrics API key"]="update_config_val"
["Change client"]="update_config_val"
["Change favorite server"]="add_record" ["Change favorite server"]="add_record"
["Add server by IP"]="add_record" ["Add server by IP"]="add_record"
["Add server by ID"]="add_record" ["Add server by ID"]="add_record"
@@ -286,7 +288,7 @@ a2s(){
local mode="$3" local mode="$3"
logger INFO "Querying '$ip:$qport' with mode '$mode'" logger INFO "Querying '$ip:$qport' with mode '$mode'"
local res local res
res=$(python3.13 "$query_helper" "$ip" "$qport" "$mode") res=$(python3 "$query_helper" "$ip" "$qport" "$mode")
if [[ $? -eq 1 ]]; then if [[ $? -eq 1 ]]; then
res=$(try_fallback "$ip" "$qport" "$mode") res=$(try_fallback "$ip" "$qport" "$mode")
if [[ $? -eq 1 ]]; then if [[ $? -eq 1 ]]; then
@@ -309,7 +311,7 @@ is_in_favs(){
find_id(){ find_id(){
local file="$default_steam_path/config/loginusers.vdf" local file="$default_steam_path/config/loginusers.vdf"
[[ ! -f $file ]] && return 1 [[ ! -f $file ]] && return 1
local res=$(python3.13 "$helpers_path/vdf2json.py" \ local res=$(python3 "$helpers_path/vdf2json.py" \
-i "$file" \ -i "$file" \
| jq -r '.users | jq -r '.users
|to_entries[] |to_entries[]
@@ -338,7 +340,7 @@ list_mods(){
name=$(awk -F\" '/name/ {print $2}' "${dir}/meta.cpp") name=$(awk -F\" '/name/ {print $2}' "${dir}/meta.cpp")
base_dir=$(basename $(readlink -f $game_dir/$symlink)) base_dir=$(basename $(readlink -f $game_dir/$symlink))
size=$(du -s "$(readlink -f "$game_dir/$symlink")" | awk '{print $1}') size=$(du -s "$(readlink -f "$game_dir/$symlink")" | awk '{print $1}')
size=$(python3.13 -c "n=($size/1024) +.005; print(round(n,4))") size=$(python3 -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" LC_NUMERIC=C printf "%s$sep%s$sep%s$sep%3.3f\n" "$name" "$symlink" "$base_dir" "$size"
done | sort -k1) done | sort -k1)
# user may have manually pruned mods out-of-band # user may have manually pruned mods out-of-band
@@ -367,10 +369,6 @@ local_latlon(){
get_dist(){ get_dist(){
shift shift
local given_ip="$1" local given_ip="$1"
if [[ ! -f $_cache_coords ]]; then
printf "Unknown"
return
fi
readarray -t coords < "$_cache_coords" readarray -t coords < "$_cache_coords"
readarray -t n < <(<<< "$given_ip" awk 'BEGIN{RS="."}{$1=$1}1') readarray -t n < <(<<< "$given_ip" awk 'BEGIN{RS="."}{$1=$1}1')
@@ -397,7 +395,6 @@ get_dist(){
logger INFO "Distance: $dist km" logger INFO "Distance: $dist km"
fi fi
} }
query_config(){ query_config(){
[[ -n $2 ]] && local key=$2 [[ -n $2 ]] && local key=$2
keys=( keys=(
@@ -423,7 +420,6 @@ query_config(){
echo "${!i}" echo "${!i}"
done done
} }
align_versions_file(){ align_versions_file(){
shift shift
local mod="$1" local mod="$1"
@@ -589,6 +585,9 @@ default_steam_path="$default_steam_path"
#Preferred Steam launch command (for Flatpak support) #Preferred Steam launch command (for Flatpak support)
preferred_client="$preferred_client" preferred_client="$preferred_client"
#DZGUI source path
src_path="$src_path"
END END
} }
format_version_url(){ format_version_url(){
@@ -642,7 +641,6 @@ download_new_version(){
return 1 return 1
fi fi
local version_url="$(format_version_url)" local version_url="$(format_version_url)"
local src_path="$(< "$_cache_src_path")"
mv "$src_path" "$src_path.old" mv "$src_path" "$src_path.old"
curl -Ls "$version_url" > "$src_path" curl -Ls "$version_url" > "$src_path"
rc=$? rc=$?
@@ -690,7 +688,6 @@ toggle(){
auto_install="1" auto_install="1"
else else
auto_install="" auto_install=""
rm "$versions_file"
fi fi
;; ;;
Toggle[[:space:]]debug[[:space:]]mode) Toggle[[:space:]]debug[[:space:]]mode)
@@ -700,6 +697,13 @@ toggle(){
debug="" debug=""
fi fi
;; ;;
Toggle[[:space:]]Steam/Flatpak)
if [[ $preferred_client == "steam" ]]; then
preferred_client="flatpak"
else
preferred_client="steam"
fi
;;
Toggle[[:space:]]DZGUI[[:space:]]fullscreen[[:space:]]boot) Toggle[[:space:]]DZGUI[[:space:]]fullscreen[[:space:]]boot)
if [[ $fullscreen == "true" ]]; then if [[ $fullscreen == "true" ]]; then
fullscreen="false" fullscreen="false"
@@ -798,9 +802,6 @@ update_config_val(){
local context="$1" local context="$1"
local value="$2" local value="$2"
case $1 in case $1 in
"Change client")
key="preferred_client"
;;
"Change player name") "Change player name")
key="name" key="name"
if [[ -z "${value// }" ]]; then if [[ -z "${value// }" ]]; then
@@ -826,6 +827,7 @@ update_config_val(){
declare -n nr=$key declare -n nr=$key
nr="$value" nr="$value"
update_config update_config
echo "Updated the key '$key' to '$value'"
return 80 return 80
} }
show_log(){ show_log(){
@@ -930,19 +932,13 @@ query_defunct(){
<<< "$result" awk '{print $2}' <<< "$result" awk '{print $2}'
} }
encode(){ encode(){
echo -n "$1" | md5sum | cut -c -8 echo "$1" | md5sum | cut -c -8
} }
compare(){ compare(){
local modlist="$@" local modlist="$@"
diff=$(comm -23 <(<<< "$modlist" sort -u) <(installed_mods | sort)) diff=$(comm -23 <(<<< "$modlist" sort -u) <(installed_mods | sort))
echo "$diff" 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(){ legacy_symlinks(){
logger INFO "Removing legacy symlinks" logger INFO "Removing legacy symlinks"
@@ -1033,10 +1029,7 @@ update_symlinks(){
if [[ ! -d "$workshop_dir" ]]; then if [[ ! -d "$workshop_dir" ]]; then
return return
fi fi
if [[ ! -d "$game_dir" ]]; then legacy_symlinks
return
fi
unlink_all
symlinks symlinks
} }
try_fallback(){ try_fallback(){
@@ -1087,42 +1080,41 @@ try_connect(){
local appid="$2" local appid="$2"
local binary="$3" local binary="$3"
IFS=":" read -ra address <<< "$record" local ip=$(<<< $record awk -F: '{print $1}')
local ip local gameport=$(<<< $record awk -F: '{print $2}')
local gameport local qport=$(<<< $record awk -F: '{print $3}')
local qport
ip=${address[0]}
gameport=${address[1]}
qport=${address[2]}
[[ $appid -eq $exp ]] && echo "$binary" > "$_cache_binary" [[ $appid -eq $exp ]] && echo "$binary" > $_cache_binary
local remote_mods local remote_mods
remote_mods=$(a2s "$ip" "$qport" rules) remote_mods=$(a2s $ip $qport rules)
if [[ $? -eq 1 ]]; then if [[ $? -eq 1 ]]; then
printf "Failed to fetch server modlist, possibly timed out" printf "Failed to fetch server modlist, possibly timed out"
return 1 return 1
fi fi
logger INFO "Server returned modlist: $(<<< "$remote_mods" tr '\n' ' ')" logger INFO "Server returned modlist: $(<<< $remote_mods tr '\n' ' ')"
local sanitized_mods=$(query_defunct "$remote_mods")
local sanitized_mods local diff=$(compare "$sanitized_mods")
sanitized_mods=$(query_defunct "$remote_mods")
logger INFO "Connection attempt for $ip:$qport" logger INFO "Connection attempt for $ip:$qport"
update_history "$record" update_history "$record"
if [[ -n $auto_install ]]; then
logger INFO "Merging modlists"
diff=$(merge_modlists "$diff")
diff=$(query_defunct "$diff")
fi
if [[ -n $diff ]]; then
if [[ $(check_architecture) -eq 1 ]] && [[ $(test_display_mode) == "gm" ]]; then
printf "Use Desktop Mode to download mods on Steam Deck"
return 1
fi
case $auto_install in case $auto_install in
"") install_mods "$sanitized_mods" "manual" ;; "") manual_mod_install "$ip" "$gameport" "$diff" "$sanitized_mods" "" "$appid";;
1|2) install_mods "$sanitized_mods" "auto" ;; 1|2) manual_mod_install "$ip" "$gameport" "$diff" "$sanitized_mods" "auto" "$appid" ;;
esac 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 else
printf "User aborted download process, or some mods may have failed to download. Try connecting again to resync." launch "$ip" "$gameport" "$sanitized_mods" "$appid"
exit 1
fi fi
} }
check_architecture(){ check_architecture(){
@@ -1138,15 +1130,13 @@ force_update(){
printf "Only available when mod auto-install is ON" printf "Only available when mod auto-install is ON"
return 1 return 1
fi fi
# NOTE: forcibly regenerate signatures by removing file
rm "$versions_file" rm "$versions_file"
local mods local update=$(check_timestamps)
mods=$(ls -1 "$workshop_dir") manual_mod_install "null" "null" "$update" "null" "force" "null"
install_mods "$mods" "force"
echo "Finished requesting mod updates." echo "Finished requesting mod updates."
return 0 return 0
} }
get_timestamps(){ get_local_stamps(){
readarray -t modlist < <(printf "%s\n" "$@") readarray -t modlist < <(printf "%s\n" "$@")
local max="${#modlist[@]}" local max="${#modlist[@]}"
_concat(){ _concat(){
@@ -1171,6 +1161,67 @@ update_stamps(){
printf "%s\n" "${stamps[$i]}" >> $versions_file printf "%s\n" "${stamps[$i]}" >> $versions_file
done done
} }
check_timestamps(){
readarray -t local_modlist < <(ls -1 $workshop_dir)
local max=${#local_modlist[@]}
logger INFO "Local mod count: $max"
[[ $max -eq 0 ]] && return 1
local local_stamps=$(get_local_stamps "${local_modlist[@]}")
if [[ -z $local_stamps ]]; then
logger WARN "Timestamp query returned empty response"
return 1
fi
local aligned=$(<<< "$local_stamps" jq -r '.response.publishedfiledetails[]|"\(.publishedfileid),\(.time_updated)"')
readarray -t remote_ids < <(<<< "$aligned" awk -F, '{print $1}')
readarray -t remote_times < <(<<< "$aligned" awk -F, '{print $2}')
if [[ ! -f $versions_file ]]; then
logger INFO "No prior versions file found, creating"
update_stamps "$aligned"
#force refresh all mods if versions file was missing
printf "%s\n" "${remote_ids[@]}"
return 0
fi
readarray -t old_ids < <(< $versions_file awk -F, '{print $1}')
readarray -t old_times < <(< $versions_file awk -F, '{print $2}')
declare -A remote_version
declare -A local_version
for((i = 0; i < ${#remote_ids[@]}; ++i)); do
remote_version[${remote_ids[$i]}]=${remote_times[$i]}
done
needs_update=()
for((i=0;i<${#old_ids[@]};i++)); do
local id=${old_ids[$i]}
local time=${old_times[$i]}
if [[ $time != ${remote_version[$id]} ]]; then
logger WARN "Mod '$id' timestamp '$time' != '${remote_version[$id]}'"
needs_update+=($id)
fi
done
printf "%s\n" "${needs_update[@]}"
}
merge_modlists(){
local diff="$1"
_sort(){
printf "%s\n" "$diff"
printf "%s\n" "${needs_update[@]}"
}
readarray -t needs_update < <(check_timestamps)
if [[ -z ${needs_update[@]} ]]; then
echo "$diff"
return 0
fi
if [[ -z "$diff" ]] && [[ ${#needs_update[@]} -gt 0 ]]; then
printf "%s\n" "${needs_update[@]}"
else
# remove duplicates
_sort | sort -u
fi
}
concat_mods(){ concat_mods(){
readarray -t concat_arr <<< "$@" readarray -t concat_arr <<< "$@"
local id local id
@@ -1212,7 +1263,7 @@ launch(){
if [[ $debug -eq 1 ]]; then if [[ $debug -eq 1 ]]; then
local launch_options="$steam_cmd -applaunch $appid -connect=$ip:$gameport -nolauncher -nosplash -name=$name -skipintro -mod=$concat" 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" printf "Debug mode: these options would have been used to launch the game: $launch_options"
return 0 return 0
fi fi
echo "$concat" > "$_cache_launch" echo "$concat" > "$_cache_launch"
@@ -1248,121 +1299,75 @@ final_handshake(){
printf "\n" printf "\n"
return 6 return 6
} }
watcher(){ manual_mod_install(){
local -n to_download="$1" local ip="$1"
local -n signatures="$2" local gameport="$2"
local mode="$3" local diff="$3"
local sanitized_mods="$4"
local mode="$5"
local appid="$6"
local ex="$state_path/dzg.watcher"
declare -i i=0 readarray -t stage_mods <<< "$diff"
local remote_time [[ -f $ex ]] && rm $ex
local length
length=${#to_download[@]} _watcher(){
for mod in "${!to_download[@]}"; do for((i=0;i<${#stage_mods[@]};i++)); do
remote_time=${to_download[$mod]}
[[ -f $ex ]] && return 1 [[ -f $ex ]] && return 1
case $mode in
"auto" | "force") if [[ $mode == "auto" ]] || [[ $mode == "force" ]]; then
$steam_cmd "steam://url/CommunityFilePage/${mod}+workshop_download_item ${aid} ${mod}" $steam_cmd "steam://url/CommunityFilePage/${stage_mods[$i]}+workshop_download_item $aid ${stage_mods[$i]}"
echo "# Opening workshop page for ${mod}. If you see no progress, you may be out of disk space." echo "# Opening workshop page for ${stage_mods[$i]}. If you see no progress, you may be out of disk space."
;; else
*) $steam_cmd "steam://url/CommunityFilePage/${stage_mods[$i]}"
$steam_cmd "steam://url/CommunityFilePage/${mod}" echo "# Opening workshop page for ${stage_mods[$i]}. If you see no progress after subscribing, try unsubscribing and resubscribing again until the download commences."
echo "# Opening workshop page for ${mod}. If you see no progress after subscribing, try unsubscribing and resubscribing again until the download commences." fi
;;
esac
sleep 1s sleep 1s
foreground foreground
until [[ -d $downloads_dir/${mod} ]]; do until [[ -d $downloads_dir/${stage_mods[$i]} ]]; do
[[ -f $ex ]] && return 1 [[ -f $ex ]] && return 1
sleep 0.1s sleep 0.1s
# NOTE: fallback for instantaneous downloads if [[ -d $workshop_dir/${stage_mods[$i]} ]]; then
if [[ $mode == "auto" || $mode == "force" ]]; then
if [[ -d $workshop_dir/${mod} ]]; then
break break
fi fi
fi
done done
foreground foreground
case $mode in if [[ $mode == "auto" ]] || [[ $mode == "force" ]]; then
"auto" | "force") echo "# Steam is downloading ${stage_mods[$i]} (mod $((i+1)) of ${#stage_mods[@]}). You do not need to manually Subscribe."
echo "# Steam is downloading ${mod} (mod $((i+1)) of ${length}). You do not need to manually Subscribe." else
;; echo "# Steam is downloading ${stage_mods[$i]} (mod $((i+1)) of ${#stage_mods[@]})"
*) fi
echo "# Steam is downloading ${mod} (mod $((i+1)) of ${length})" until [[ -d $workshop_dir/${stage_mods[$i]} ]]; do
;;
esac
until [[ -d $workshop_dir/${mod} ]]; do
[[ -f $ex ]] && return 1 [[ -f $ex ]] && return 1
sleep 0.1s sleep 0.1s
done done
foreground foreground
if [[ ! -v signatures["${mod}"] ]]; then echo "# ${stage_mods[$i]} moved to mods dir"
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 done
echo "100" echo "100"
} }
install_mods(){ _watcher > >($steamsafe_zenity --pulsate --progress --auto-close --title="DZG Watcher" --width=500 2>/dev/null; rc=$?; [[ $rc -eq 1 ]] && touch $ex)
local sanitized_mods="$1"
local mode="$2"
local ex="$state_path/dzg.watcher"
[[ -f $ex ]] && rm "$ex" if [[ $mode == "force" ]]; then
rm "$versions_file"
check_timestamps
return 0
fi
if [[ -f "$versions_file" ]]; then local diff=$(compare "$sanitized_mods")
readarray -t local_mods < "$versions_file" if [[ -z $diff ]]; then
if [[ $mode == "auto" ]]; then
rm "$versions_file"
check_timestamps
fi
launch "$ip" "$gameport" "$sanitized_mods" "$appid"
else else
local_mods=() printf "User aborted download process, or some mods may have failed to download. Try connecting again to resync."
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 exit 1
fi 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(){ test_display_mode(){
pgrep -a gamescope | grep -q "generate-drm-mode" pgrep -a gamescope | grep -q "generate-drm-mode"
+36
View File
@@ -0,0 +1,36 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define R 6371
#define TO_RAD (3.1415926536 / 180)
double dist(double th1, double ph1, double th2, double ph2)
{
double dx, dy, dz;
ph1 -= ph2;
ph1 *= TO_RAD, th1 *= TO_RAD, th2 *= TO_RAD;
dz = sin(th1) - sin(th2);
dx = cos(ph1) * cos(th1) - cos(th2);
dy = sin(ph1) * cos(th1);
return asin(sqrt(dx * dx + dy * dy + dz * dz) / 2) * 2 * R;
}
int main(int argc, const char * argv[])
{
if(argc < 5 || argc > 5){
return 1;
}
float coords[4];
for(int i=1;i<5;i++){
if(atof(argv[i]) == 0){
return 1;
}
coords[i] = atof(argv[i]);
}
double d = dist(coords[1], coords[2], coords[3], coords[4]);
printf("%.1f\n", d);
return 0;
}
+1 -1
View File
@@ -295,7 +295,7 @@ def get_dayz_version(file: Path) -> DayZVersion | Exception:
try: try:
version = get_version(file) version = get_version(file)
except Exception as e: except Exception as e:
raise e return e
patch = str(version.build) + str(version.revision) patch = str(version.build) + str(version.revision)
dz_vers = DayZVersion(version.major, version.minor, int(patch)) dz_vers = DayZVersion(version.major, version.minor, int(patch))
return dz_vers return dz_vers
-1
View File
@@ -1 +0,0 @@
latlon
-24
View File
@@ -1,24 +0,0 @@
bin := latlon
CC = gcc
CFLAGS += -Wall
CFLAGS += -Wextra
CFLAGS += -Wpedantic
CFLAGS += -std=c23
CFLAGS += -O3
LDFLAGS += -lm
PREFIX ?= $(HOME)/.local/share/dzgui
all: $(bin)
install: $(bin)
install -Dm 755 $(bin) $(PREFIX)
$(bin): latlon.c
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
clean:
rm -f $(bin)
-77
View File
@@ -1,77 +0,0 @@
// This small program computes the distance between two points on the Earths
// surface. The algorithm used for computing is called haversine formula.
// Source for the algorithm can be found here:
// https://en.wikipedia.org/wiki/Haversine_formula
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
// Earth's radius in meters.
#define RADIUS ((12756L / 2.0L) * 1000L)
// PI natural constant
#define PI 3.1415926536L
// coordinates struct for storing points
struct Coordinate {
double latitude;
double longitude;
};
/**
* @brief Calculates a radian value from an angle
*/
double radian_from(const double angle) {
return (angle * PI) / 180;
}
/**
* @brief Calculate the haversine function from an angle in radian
*/
double haversine(const double angle) { return ((1.0L - cos(angle)) / 2); }
/**
* @brief Calculate the distance between two coordinates
*/
double great_circle_distance(const struct Coordinate a,
const struct Coordinate b) {
// calculate longitude and latitude differences
struct Coordinate diff_coord = {
.longitude = a.longitude - b.longitude,
.latitude = a.latitude - b.latitude,
};
// calculate haversine(theta) value, where theta is the angle between the
// two coordinates
double hav_theta = haversine(diff_coord.latitude)
+ cos(a.latitude) * cos(b.latitude) * haversine(diff_coord.longitude);
// calculate distance from radius, and haversine(theta) values
double distance = 2 * RADIUS * asin(sqrt(hav_theta));
return distance;
}
int main(int argc, const char* argv[]) {
if (argc < 5 || argc > 5) {
return 1;
}
const struct Coordinate a = {
.latitude = radian_from(atof(argv[1])),
.longitude = radian_from(atof(argv[2])),
};
const struct Coordinate b = {
.latitude = radian_from(atof(argv[3])),
.longitude = radian_from(atof(argv[4])),
};
// Returns the distance in meters.
// To pretty-print the result in a consumer of this module, it is
// recommended to divide the result by 1,000 and round it.
printf("%.1f\n", great_circle_distance(a, b));
return 0;
}
+290 -198
View File
File diff suppressed because it is too large. Load diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB