diff --git a/CHANGELOG.md b/CHANGELOG.md index a85404b..0a443f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,56 +1,110 @@ # Changelog -## [6.0.1] 2026-01-16 +## [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] 2026-01-14 +## [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 -- Support DayZ Experimental -- Ping display on server tables -- Save descriptive text notes on a per-server basis -- Speed up load time and navigation of server tables -- More robust threading and cache system when filtering servers -- Show additional DayZ client information in Options menu -- Warn user of DayZ client version mismatches -- Support clickable hyperlinks in dialogs -- Rich server metadata details dialog (right-click on server table) -- Filter by modded servers -- Breadcrumbs showing current menu context -- Dedicated changelog page -- Dedicated keybindings page -- Dedicated settings page ("Options") -- Support sandboxed version of Flatpak -- Vim-style navigation keybindings -- Added "Return to main menu" button to dialog windows when failing to load server table -- Dynamic context menus for modded servers -- Additional keybindings for new filter toggles -- If a server is locked, prompt user when connecting -- Internal flag to allow distro-packaged releases to disable in-app updates -- Pre-boot checks to test whether script was invoked directly -- Commandline usage help text (GaryBlackbourne) - More descriptive help dialogs when initial dependencies are missing +## [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 +- Config file erroneously getting updated when populating settings menu + +## [6.0.0-beta.10] 2025-11-10 +## 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 - Script failing to start when remote endpoints are unavailable (GaryBlackbourne) -- Key stickiness when quickly navigating through entries in tables -- Servers returning malformed A2S_INFO blocking server browser from loading -- Normalized buttons in dialogs and restored proper padding -- GTK errors being emitted to stdout when inserting debug table -- Entry dialog sensitivity when validating API keys -- Additional validation on entry dialogs to prevent submitting empty text -- Suppress errors during pre-boot checks when mods are not installed -- Centered filter checkboxes within panel -- Do not pop unhighlight/select stale buttons if no stale mods exist -- Improved keybinding interaction with side panels -- Suppress typeahead search in mod dialogs -- Do not trigger global API cooldown if no LAN/favorite servers are found -- Fix table column expansion in server mod dialogs -- Missing parameters when closing application via window decorations -- Statusbar contents not updating on certain pages -- Prevent debug button from activating when in certain input fields -- Window size not updating correctly when unmaximizing window after changing "fullscreen at boot" setting -- Branch toggle signal being emitted when entering Options menu from other contexts + +## 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 +## Added +- Users can save text notes to describe servers + +## Changed +- State file serialization methods + +## [6.0.0-beta.6] 2025-09-12 +## Added +- Support DayZ Experimental +- Show additional client information in Options menu +- Warn user of client version mismatches +- Support clickable hyperlinks + +## Fixed - Mods rarely not appearing in local mod list if download completed too quickly - Statusbar not updating when clicking a row after spamming keyboard input - Extraneous logs being generated when subscribing to mods @@ -58,188 +112,342 @@ - Newline terminators in history file - Floating point number calculation - Window resizing too small if no prior resolution was set -- ESC key destroying wait dialogs while a thread is pending -- Tooltip signals being erroneously emitted on main menu -- UI not being constructed correctly if CHANGELOG.md was missing -- Config file erroneously getting updated when populating settings menu -- Leaky variable name in dialog titles -- Prevent extraneous signals from propagating when column width is adjusted -- Server filter panel not being hidden when entering other page contexts -- Path to remote changelog being constructed incorrectly -- Dialogs with newlines breaking output in log table -- 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.5] 2025-08-20 +## Fixed +- Servers returning malformed A2S_INFO blocking server browser from loading + +## [6.0.0-beta.4] 2025-08-13 +## Fixed +- Branch toggle signal being emitted when entering Options menu from other contexts + +## [6.0.0-beta.3] 2025-08-08 +## Added +- Additional server metadata to details dialog +- Prompt user when connecting if a server is locked +- Dedicated settings page ("Options") + +## Fixed +- Statusbar contents not updating on certain pages +- Breadcrumbs not updating when returning from keybindings page +- Wrap long descriptions in server details +- Prevent debug button from activating when in certain input fields +- Window size not updating correctly when unmaximizing window after changing "fullscreen at boot" setting + +## Dropped +- Extraneous information from right statusbar + +## [6.0.0-beta.2] 2025-07-29 +## Fixed +- Missing parameters when closing application via window decorations + +## [6.0.0-beta.1] 2025-07-29 +## Added +- Ping column to server browsing contexts +- Server details dialog to server browsing contexts +- Filter by modded servers +- Breadcrumbs showing current menu context to top of window +- Speed up load time and navigation of server tables +- More robust threading and cache system when filtering servers +- Dedicated changelog page +- Dedicated keybindings page +- Vim-style navigation keybindings +- Additional validation on entry dialogs to prevent submitting empty text +- Added "Return to main menu" button to dialog windows when failing to load server table +- Dynamic context menus for modded servers +- Additional keybindings for filter toggles + +## Fixed +- Entry dialog sensitivity when validating API keys +- Normalized buttons in dialogs and restored proper padding +- GTK errors being emitted to stdout when inserting debug table +- Suppress errors during pre-boot checks when mods are not installed +- Centered filter checkboxes within panel +- Do not pop unhighlight/select stale buttons if no stale mods exist +- Improved keybinding interaction with side panels +- Key stickiness when quickly navigating through entries in tables +- Suppress typeahead search in mod dialogs +- Do not trigger global API cooldown if no LAN/favorite servers are found +- Fix table column expansion in server mod dialogs ## Changed -- Require Python 3.13 - Reduced global API cooldown from 60s to 30s - Clarify dialog messages when DayZ path could not be found - Auto-focus first item when opening context menus - Opacity setting on side buttons when in a different context - Refactored BM API key validation to account for new key format -- State file serialization methods -- Optimize time complexity of pre-boot checks (GaryBlackbourne) -- Rewrote distance calculation module (GaryBlackbourne) -- Changed preferred client setting from radio toggle to combobox ## Dropped - Ping readout in statusbar -- Extraneous information from right statusbar -## [5.8.3] 2026-01-04 -## Fixed -- Normalize checksum numbers and dates - -## [5.8.2] 2026-01-04 -## Fixed -- Fix regression in symlink routine introduced by previous hotfix - -## [5.8.1] 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 - -## [5.8.0] 2025-07-06 +## [5.8.0-beta.2] 2025-07-06 ## Added - Filter servers by official/unofficial status -- Automatically fetch geolocation records ## Changed - Updated internal versioning of helper files + +## [5.8.0-beta.1] 2025-06-05 +## Added +- Automatically fetch geolocation records ## Fixed - Corrected erroneous 2024 date in prior changelog entries -## [5.7.0] 2025-04-17 -## Added -- Save application dimensions when quitting and restore on subsequent boot -## Fixed -- Issues with the window exceeding the bounds of the screen when displaying some table contexts +## [5.7.1-beta.1] 2025-04-17 ## Changed -- More direct server ping query method that should return marginally more accurate times +- Updated geolocation records -## [5.6.7] 2025-04-04 +## [5.7.0-beta.13] 2025-04-17 +## Fixed +- Updated checksum for UI helper file + +## [5.7.0-beta.12] 2025-04-04 ## Dropped - Removed extraneous pre-boot API checks that could cause error messages to be printed if the user had not set up an API key yet -## [5.6.6] 2025-03-16 -## Changed -- Update IP database records for 2025-03 +## [5.7.0-beta.11] 2025-03-20 +## Fixed +- Reduce startup time when testing remote APIs -## [5.6.5] 2025-03-04 +## [5.7.0-beta.10] 2025-03-15 +## Added +- Restore application size on subsequent startup +## Fixed +- Grid scaling causes table to exceeds bounds of display viewport +- Window exceeds taskbar on Steam Deck +- Reduce possibility of timeouts when pinging servers +## Changed +- Packed filter checkbox buttons into a 3x3 grid +- Updated IP database to 2025-03 records + +## [5.7.0-beta.9] 2025-03-04 ## Fixed - Livonia server results being dropped from batch queries -## [5.6.4] 2025-02-10 -### Changed +## [5.7.0-beta.8] 2025-02-10 +## Changed - Drop launch flag and check for invocation through Steam automatically -## [5.6.3] 2025-02-09 -### Changed +## [5.7.0-beta.7] 2025-02-09 +## Changed - Update IP database records for 2025-02 -## [5.6.2] 2025-01-22 -### Fixed -- Resolve regression introduced with IP resolution feature in 5.6.0 (restores functionality of right-click action: Add to My Servers) +## [5.7.0-beta.6] 2025-01-10 +## Fixed +- Resolve regression introduced with IP resolution feature in 13c6813 -## [5.6.1] 2025-01-10 -### Fixed -- Add fallback support for jq 1.6 +## [5.7.0-beta.5] 2025-01-10 +## Changed +- Stricter redacting of usernames (again) -## [5.6.0] 2025-01-06 -### Added -- Application header bar and controls -- Menu context subtitle in header bar -- Bulk delete mods (via 'List installed mods' list). Not compatible with Manual Mod install mode -- Highlight stale mods in mods list -- Added --steam launch parameter -- Open Steam workshop subscriptions dialog when switching from Manual to Auto mode -- Hover tooltips to most buttons -- Add in-app documentation link to Codeberg mirror -- Additional logging -### Fixed -- Avoid sudo escalation if system map count is sufficient (jiriks74) -- Duplicate dialog title on Steam Deck -- Resolve remote IP when saving records for game servers with multiple hosts -- More performant symlink traversal when checking for legacy links -- Only iterate on missing symlinks at boot (400% performance uplift) -- Fix for server list truncation causing some servers to not appear in results -- Empty dialog popups if user manually deletes local mods while application is running -- Update statusbar when removing servers from list/repopulating -- Abort DayZ path discovery if Steam files are not synched -- Race condition when checking for installed mods -- Favorite server message not updating correctly -- Improved handling for cases where there are no locally installed mods -- Set up mod symlinks at boot, rather than only on server connect -- Prevent context menus from opening when table is empty -- When reloading table in-place, prevent duplicate panel elements from being added if already present -- Clean up local mod signatures from versions file when deleting mods -- Focus first row when opening mods list +## [5.7.0-beta.4] 2025-01-10 +## Fixed +- Clerical hotfix + +## [5.7.0-beta.3] 2025-01-10 +## Changed +- Support legacy jq syntax for Ubuntu variants + +## [5.7.0-beta.2] 2025-01-07 +## Fixed +- Stricter redacting of usernames +- Omit nested directories when traversing symlinks + +## [5.7.0-beta.1] 2025-01-07 ### Changed -- Redact usernames in log files +- Normalize version number + +## [5.6.0-beta.21] 2025-01-06 +### Added +- Add in-app documentation link to Codeberg mirror +- Hover tooltips to most buttons +### Fixed - Prevent ArrowUp/ArrowDown input when inside keyword field -- Admonish user to restart Steam in error dialog if DayZ path could not be found -- Refactor control flow for more robust contextual parsing -- Stop sending modal dialog hints to outer window -- Clean up signal emission -- Clarify some error messages and normalize text formatting +### Changed - Update forum URL +- Reword Help section links to include destination - Update README.md - Update IP database to 2025-01 - Reduce IP database size by 50% - Update documentation to 5.6.x standard ### Dropped -- Hall of Fame section from button links, to be moved inside documentation -- Unused imports +- Removed temporary mod ID output in debug logs +- Removed Hall of Fame section from button links, moved inside documentation +- Remove unused imports -## [5.5.3] 2024-12-13 +## [5.6.0-beta.20] 2024-12-23 +### Added +- Output real and resolved mod ids to logs (temporary) +- Added -steam launch parameter +### Fixed +- Only iterate on missing symlinks +- Move logging up + +## [5.6.0-beta.19] 2024-12-18 +### Added +- Redact usernames in log files +### Fixed +- More performant symlink traversal when checking for legacy links + +## [5.6.0-beta.18] 2024-12-14 +### Added +- Open Steam workshop subscriptions dialog +- Additional logging +### Fixed +- Empty dialog popups if user manually deletes local mods while application is running +- Abort DayZ path discovery if VDF if Steam files are not synched +- Avoid sudo escalation if system map count is sufficient (jiriks74) +### Changed +- Admonish user to restart Steam in error dialog if DayZ path could not be found + +## [5.6.0-beta.17] 2024-12-14 +### Added +- Additional logging + +## [5.6.0-beta.16] 2024-12-13 ### Fixed - Add remote resource health checks before downloading updates ### Added - Add fallback repository -## [5.5.1] 2024-12-03 +## [5.6.0-beta.11] 2024-12-07 ### Fixed -- Support localized decimal separators when parsing installed mod sizes +- Add missing function definition -## [5.5.0] 2024-11-10 +## [5.6.0-beta.10] 2024-12-04 +### Fixed +- Untoggle highlight button when repopulating mod list +- Resolve remote IP when saving records for game servers with multiple hosts +- Update statusbar when removing servers from list/repopulating +### Added: +- "Select stale" button to bulk select mods marked as obsolete + +## [5.6.0-beta.9] 2024-12-03 +### Fixed +- Normalize user locale when parsing floats + +## [5.6.0-beta.8] 2024-11-28 +### Fixed +- Normalize user locale when parsing floats + +## [5.6.0-beta.7] 2024-11-28 +### Changed +- Add additional logging when fetching installed mods + +## [5.6.0-beta.6] 2024-11-28 +### Fixed +- Race condition when checking for installed mods + +## [5.6.0-beta.5] 2024-11-21 ### Added -- Support servers running DLC content (fixes Frostline servers) -- Expose a toggle setting for whether to launch the application in fullscreen -- Text autocompletion in maps search field (partial search) -- Add disk space warning to popup dialog when downloading mods +- Highlight stale mods in mods list +### Fixed +- Duplicate dialog title on Steam Deck + +## [5.6.0-beta.4] 2024-11-20 +### Added +- Application header bar and controls +- Menu context subtitle in header bar +### Changed +- Refactor control flow for more robust contextual parsing +- Stop sending modal dialog hints to outer window +### Fixed +- Favorite server message not updating correctly + +## [5.6.0-beta.3] 2024-11-18 +### Fixed +- Improved handling for cases where there are no locally installed mods +- Set up mod symlinks at boot, rather than only on server connect +- Prevent context menus from opening when table is empty +- When reloading table in-place, prevent duplicate panel elements from being added if already present +- Clean up signal emission + +## [5.6.0-beta.2] 2024-11-15 +### Fixed +- Clean up local mod signatures from versions file when deleting mods + +## [5.6.0-beta.1] 2024-11-12 +### Added +- Bulk delete mods (via 'List installed mods' list). Not compatible with Manual Mod install mode +### Fixed +- Fix for server list truncation causing some servers to not appear in results +- Suppress signal emission when switching menu contexts +- Focus first row when opening mods list +### Changed +- Clarify some error messages and normalize text formatting + +## [5.5.0-beta.5] 2024-11-03 +### Changed +- Use updated A2S_RULES logic ### Fixed - Servers in saved servers list would populate context menu with same option when right-clicking in server browser + +## [5.5.0-beta.4] 2024-10-31 +### Added +- Expose a toggle setting for whether to launch the application in fullscreen +### Fixed - Enable adding/removing servers to/from My Saved Servers when in Recent Servers context + +## [5.5.0-beta.3] 2024-10-31 +### Fixed - Prevent maps combobox from duplicating contents + +## [5.5.0-beta.2] 2024-10-31 +### Fixed - Restore keyboard input to keyword entry field -### Changed + +## [5.5.0-beta.1] 2024-10-30 +### Added +- Support servers running DLC content (fixes Frostline servers) +- Text autocompletion in maps search field +- Add disk space warning to popup dialog +### Fixed - Abort fallback query method if DLC is required -## [5.4.2] 2024-10-05 +## [5.4.2-beta.1] 2024-10-05 ### Fixed - Sanitize third-party API IDs to remove UGC collisions -## [5.4.1] 2024-09-25 +## [5.4.1-beta.2] 2024-09-12 +### Fixed +- Use fallback logic for modlist queries when user traverses networks + +## [5.4.1-beta.2] 2024-09-10 +### Fixed +- Fix signal handling control flow for checkbox toggles + +## [5.4.1-beta.1] 2024-09-10 ### Added - Pre-boot validation check for users with self-compiled version of jq ### Fixed -- Use fallback logic for modlist queries when user traverses networks -- Fix signal handling control flow for checkbox toggles - When reloading the server browser, the map combobox selection would revert to the last selected map instead of All Maps - Server filter toggle signals were accessible from the main menu when switching between menu contexts - Global cooldown dialog could sometimes block filter toggles after cooldown reset - Normalized minor version number due to a previous clerical error -## [5.4.0] 2024-08-27 +## [5.4.0-beta.5] 2024-08-27 ### Added -- Scan local area network for DayZ servers - Freedesktop application icons for system taskbar, tray, and other dialogs -- Emit CPU model name when exporting system debug log ### Fixed - Errors being printed to the console when Exit button was explicitly clicked + +## [5.4.0-beta.4] 2024-08-21 +### Added +- Emit CPU model name when exporting system debug log +### Fixed - Detect Steam Deck OLED APU variant during initial setup -- Encapsulate player names correctly so that names with whitespace in them are supported + +## [5.4.0-beta.3] 2024-08-04 +### Added +- Scan local area network for DayZ servers + +## [5.4.0-beta.2] 2024-08-03 +### Fixed +- Clerical hotfix for previous player names fix - Test if DayZ directory is empty at startup, implying that the game was moved to a new library collection -- Report WM_CLASS name to the window manager + +## [5.4.0-beta.1] 2024-07-16 +### Fixed +- Encapsulate player names correctly so that names with whitespace in them are supported ## [5.3.2] 2024-07-02 ### Fixed diff --git a/dzgui.sh b/dzgui.sh index daa4098..00b480f 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -2,7 +2,7 @@ set -o pipefail src_path="$(readlink -e "$0")" -version=6.0.1 +version=6.0.2-beta.1 #CONSTANTS aid=221100 @@ -142,7 +142,7 @@ test_gobject(){ python3.13 -c "import gi" if [[ ! $? -eq 0 ]]; then logger CRITICAL "Missing PyGObject" - quit_with_pdialog "Requires PyGObject (python-gobject)" + quit_with_help_dialog "Requires PyGObject (python-gobject)" exit 1 fi logger INFO "Found PyGObject in Python env" @@ -247,7 +247,7 @@ open_url(){ xdg-open "$url" fi } -quit_with_pdialog(){ +quit_with_help_dialog(){ local sel msg="$1" help_button="Open help page" @@ -260,7 +260,7 @@ quit_with_pdialog(){ check_pyver(){ if [[ ! $(python3.13 --version) ]]; then local msg="Requires Python 3.13" - quit_with_pdialog "$msg" + quit_with_help_dialog "$msg" fi } watcher_deps(){ @@ -335,6 +335,7 @@ check_unmerged(){ fi } check_version(){ + echo "# Checking version" [[ -n $reference_branch ]] && return local version_url=$(format_version_url) local upstream=$(curl -Ls "$version_url" | awk -F= '/^version=/ {print $2}') @@ -414,6 +415,7 @@ test_display_mode(){ fi } check_architecture(){ + echo "# Checking system architecture" local cpu=$(< /proc/cpuinfo awk -F": " '/AMD Custom APU [0-9]{4}$/ {print $2; exit}') read -a APU_MODEL <<< "$cpu" if [[ ${APU_MODEL[3]} != "0932" ]] && [[ ${APU_MODEL[3]} != "0405" ]]; then @@ -430,6 +432,7 @@ check_architecture(){ logger INFO "Setting architecture to 'Steam Deck'" } check_map_count(){ + echo "# Checking map count" [[ $is_steam_deck -gt 0 ]] && return 0 local map_count_file="/proc/sys/vm/max_map_count" local min_count=1048576 @@ -479,6 +482,7 @@ tdialog(){ $steamsafe_zenity --info --text="$1" "${zenity_flags[@]}" } steam_deps(){ + echo "# Checking Steam" local flatpak local steam [[ $(command -v flatpak) ]] && flatpak=$(flatpak list | grep valvesoftware.Steam) @@ -489,16 +493,8 @@ steam_deps(){ exit 1 fi } -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(){ + echo "# Cleaning stale symlinks" local game_dir="$steam_path/steamapps/common/DayZ" readarray -t links < <(find "$game_dir" -xtype l) for link in "${links[@]}"; do @@ -512,7 +508,7 @@ check_availability() { return 1 fi local url=$1 - local timeout_sec="3" + local timeout_sec="1" if [[ $2 ]]; then timeout_sec=$2 fi @@ -523,6 +519,7 @@ check_availability() { } local_latlon(){ + echo "# Checking coordinates" if [[ -z $(command -v dig) ]]; then local url_ipecho="https://ipecho.net/plain" if ! check_availability "ipecho.net"; then @@ -534,6 +531,9 @@ local_latlon(){ # TODO : implement checking remote local local_ip=$(dig -4 +short myip.opendns.com @resolver1.opendns.com) fi + if calc_local_coords "$local_ip"; then + return 0 + 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." @@ -548,6 +548,7 @@ local_latlon(){ } lock(){ + echo "# Setting up lock file" [[ ! -f $lock_file ]] && touch $lock_file local pid=$(cat $lock_file) ps -p $pid -o pid= >/dev/null 2>&1 @@ -625,10 +626,10 @@ fetch_helpers_by_sum(){ [[ -f "$config_file" ]] && source "$config_file" declare -A sums sums=( - ["funcs"]="5e35a9812c03fc64d75843a23d024f0d" + ["funcs"]="69e83db00cbc7674e241565c88aed83d" ["query_v2.py"]="55d339ba02512ac69de288eb3be41067" ["servers.py"]="ed442c3aecf33f777d59dcf53650d263" - ["ui.py"]="3d67e5e8e85a23dde1fd0e85a9be62a9" + ["ui.py"]="d09b9d8bed3854efd51377289786c5ac" ["vdf2json.py"]="2f49f6f5d3af919bebaab2e9c220f397" ["pefile.py"]="21531f2c0d9dfa5f110cf6779f9d22c0" ) @@ -690,12 +691,12 @@ fetch_km_helper(){ } get_response_code(){ local url="$1" - curl -Ls -I -o /dev/null -w "%{http_code}" "$url" + curl --connect-timeout 3 -Ls -I -o /dev/null -w "%{http_code}" "$url" } fetch_ip_db(){ parse_dl_url(){ - curl -Ls "$url" \ + curl --connect-timeout 3 -Ls "$url" \ | grep "csv.gz" \ | awk -F"['']" '{print $2}' } @@ -711,6 +712,9 @@ fetch_ip_db(){ fetch(){ logger INFO "Triggering fetch routine" 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" if [[ $? -ne 0 ]]; then @@ -730,7 +734,7 @@ fetch_ip_db(){ logger WARN "Abnormal exit while parsing IPs" return fi - rm "${state_path}/${month}.csv" + rm "${state_path}/${this_month}.csv" 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 @@ -745,55 +749,57 @@ fetch_ip_db(){ rm "$ip_file" fi - echo "$month" > "$month_file" - logger INFO "Wrote '$month' to stub '$month_file'" + echo "$this_month" > "$month_file" + logger INFO "Wrote '$this_month' to stub '$month_file'" logger INFO "Updated '$ip_file'" } + check_remote(){ + local url="$1" + local res=$(get_response_code "$url") + if [[ $res -ne 200 ]]; then + logger WARN "Failed to retrieve remote resource: '$url' ($res)" + return + fi + logger INFO "Resolved remote URL: '$url'" + + # test dl url + local dl_url="$(parse_dl_url)" + local res=$(get_response_code "$dl_url") + if [[ $res -ne 200 ]]; then + logger WARN "Remote resource unavailable: '$dl_url' ($res)" + return + fi + logger INFO "Resolved download URL: '$dl_url'" + fetch "$dl_url" + } + local url="https://db-ip.com/db/download/ip-to-city-lite" - local month_file="${state_path}/.month" - local ip_file="${state_path}/ips.csv" - - # test main url - local res=$(get_response_code "$url") - if [[ $res -ne 200 ]]; then - logger WARN "Failed to retrieve remote resource: '$url' ($res)" - return - fi - logger INFO "Resolved remote URL: '$url'" - - # test dl url - local dl_url="$(parse_dl_url)" - local res=$(get_response_code "$dl_url") - if [[ $res -ne 200 ]]; then - logger WARN "Remote resource unavailable: '$dl_url' ($res)" - return - fi - logger INFO "Resolved download URL: '$dl_url'" - - local month=$(parse_dl_url_date "$dl_url") - local base_file="${state_path}/${month}.csv.gz" - local extracted_file="${state_path}/${month}.csv" + month_file="${state_path}/.month" + this_month=$(date +%Y-%m) # no stub file if [[ ! -f $month_file ]]; then logger WARN "No stub file '$month_file' present" - fetch "$dl_url" - return - fi - - # local needs update - local last_month=$(< "$month_file") - if [[ $last_month != "$month" ]]; then - logger WARN "Local stub '$last_month' does not match remote stub '$month'" - fetch "$dl_url" + check_remote "$url" return fi # if stub is same date, abort - logger INFO "Local stub '$last_month' is identical to remote, skipping" + local last_month=$(< "$month_file") + if [[ $last_month == "$this_month" ]]; then + logger INFO "Local stub '$last_month' is identical to remote, skipping" + return + fi + + # local needs update + logger WARN "Local stub '$last_month' does not match '$this_month'" + check_remote "$url" + return + } fetch_helpers(){ + echo "# Checking helper files" fetch_a2s fetch_dzq fetch_km_helper @@ -981,6 +987,7 @@ create_config(){ done } varcheck(){ + echo "# Checking config file" 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?" if [[ ! -f $config_file ]]; then @@ -1007,12 +1014,14 @@ varcheck(){ fi } is_dzg_downloading(){ + echo "# Checking DayZ" if [[ -d $steam_path ]] && [[ -d $steam_path/downloading/$aid ]]; then logger WARN "DayZ may be scheduling updates" return 0 fi } is_steam_running(){ + echo "# Checking Steam" local res=$(ps aux | grep "steamwebhelper" | grep -v grep) if [[ -z $res ]]; then logger WARN "Steam may not be running" @@ -1025,6 +1034,7 @@ get_response_code(){ curl -Ls -I -o /dev/null -w "%{http_code}" "$url" } test_connection(){ + echo "# Testing connection" declare -A hr local res1 local res2 @@ -1058,6 +1068,7 @@ legacy_cols(){ mv $cols_file.new $cols_file } stale_mod_signatures(){ + echo "# Checking stale mods" [[ ! -f "$versions_file" ]] && return local workshop_dir="$steam_path/steamapps/workshop/content/$aid" if [[ -d $workshop_dir ]]; then @@ -1071,18 +1082,48 @@ stale_mod_signatures(){ } create_new_links(){ + echo "# Setting up 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(){ - setup_dirs - setup_state_files - depcheck - check_pyver - test_gobject - watcher_deps check_architecture test_connection - fetch_helpers > >(pdialog "Checking helper files") + #fetch_helpers varcheck source "$config_file" lock @@ -1091,14 +1132,12 @@ initial_setup(){ check_version check_map_count steam_deps - migrate_files stale_symlinks stale_mod_signatures create_new_links local_latlon is_steam_running is_dzg_downloading - print_config_vals } uninstall(){ _full(){ @@ -1200,8 +1239,15 @@ main(){ set_im_module - printf "Initializing setup...\n" - initial_setup + printf "Checking dependencies...\n" + setup_dirs + setup_state_files + depcheck + check_pyver + test_gobject + watcher_deps + + initial_setup > >(pdialog "Initializing setup") printf "All OK. Kicking off UI...\n" python3.13 "$ui_helper" "--init-ui" "$version" "$is_steam_deck" diff --git a/helpers/funcs b/helpers/funcs index 9ec0edb..94f1697 100755 --- a/helpers/funcs +++ b/helpers/funcs @@ -1,6 +1,6 @@ #!/usr/bin/env bash set -o pipefail -version="6.0.1" +version="6.0.2-beta.1" #CONSTANTS aid=221100 diff --git a/helpers/ui.py b/helpers/ui.py index 81aaf3e..bff57a0 100644 --- a/helpers/ui.py +++ b/helpers/ui.py @@ -19,7 +19,7 @@ from collections.abc import Callable from concurrent.futures import wait from concurrent.futures import ThreadPoolExecutor from pathlib import Path -from typing import Literal, Self, Any +from typing import Literal, Self, Any, TYPE_CHECKING import servers as Servers # noqa E402 import pefile as PeFile # noqa E402 @@ -32,10 +32,12 @@ from pefile import ( ) from pefile import VersionMatch +if TYPE_CHECKING: + from servers import Prereqs + locale.setlocale(locale.LC_ALL, "") import gi # noqa E402 - gi.require_version("Gtk", "3.0") from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa E402 @@ -1027,13 +1029,7 @@ def process_tree_option(choice: RowType) -> None: return record = str_to_record(record) - thread_new_with_dialog( - App.treeview.prepare_connection, - parse_shell_output, - "Querying server", - command, - [record], - ) + prepare_connection(command, record) return match command.dict["type"]: @@ -1061,48 +1057,54 @@ def parse_shell_output(proc: subprocess.CompletedProcess, row: RowType): process_shell_return_code(msg, proc.returncode, row) -def thread_new_with_dialog( - func: Callable, - callback: Callable | None, - msg: str, - row: RowType | None, - args: list, +def prepare_connection(rowtype: RowType, record: Record) -> None: + def background(rowtype: RowType, record: Record) -> None: + def cleanup() -> None: + App.treeview.wait_dialog.destroy() + # NOTE: When using RowType.CONN_BY_IP, the gameport needs to be interpolated + record.gameport = prereqs.gameport + addr = record_to_str(record) + + if proceed is False: + spawn_dialog(msg, Popup.NOTIFY) + return + if msg != "": + res = spawn_dialog(msg, Popup.CONFIRM) + if res is False: + try_connect(addr, str(prereqs.appid), str(pefile_path), rowtype) + else: + try_connect(addr, str(prereqs.appid), str(pefile_path), rowtype) + + proceed, msg, pefile_path, prereqs = App.treeview.get_prereqs(record) + GLib.idle_add(cleanup) + + msg = "Checking prerequisites" + App.treeview.dialog_show(msg) + thread = threading.Thread(target=background, args=(rowtype, record)) + thread.start() + +def try_connect( + addr: str, + appid: str, + path: str, + row: RowType ) -> None: - """ - Pop a GenericDialog transient to App.treeview and - call a function on a thread, with optional callback. - Chiefly used for connection-related subprocesses. - - After completion, the dialog is destroyed in the main event loop - and additional exception handling occurs. - - This is intended as a bridge between legacy shell methods and the UI. - A more abstracted version of call_on_thread() for when extra threaded - processing occurs before calls to shell subprocesses. - """ - - def background(*args): + def background(addr, appid, path): def cleanup(): App.treeview.dialog_hide() - if exception is not None: - spawn_dialog(str(exception), Popup.NOTIFY) - process_user_input(row) - return - if callback is not None and proc is not None: - callback(proc, row) + parse_shell_output(proc, row) - exception = None - proc = None try: - proc = func(*args) + proc = call_out("try_connect", addr, appid, path) except Exception as e: - exception = e + logger.critical(e) GLib.idle_add(cleanup) return GLib.idle_add(cleanup) + msg = "Querying server" App.treeview.dialog_show(msg) - thread = threading.Thread(target=background, args=(args)) + thread = threading.Thread(target=background, args=(addr, appid, path)) thread.start() @@ -1142,27 +1144,31 @@ def record_to_str(record: Record) -> str: def connect_by_ip(enum: RowType, response: str) -> None: - def _prep(response: str) -> None: + try: record = Servers.validate_ip(response) - proc = App.treeview.prepare_connection(record) - return proc - - thread_new_with_dialog( - _prep, parse_shell_output, "Querying IP", enum, [response] - ) - return - + except Exception as e: + spawn_dialog(str(e), Popup.NOTIFY) + return + prepare_connection(enum, record) def connect_by_id(enum: RowType, response: str, key: str) -> None: def _prep(key: str, response: str) -> None: - record = Servers.query_bm_api(key, response) - proc = App.treeview.prepare_connection(record) - return proc + def cleanup() -> None: + App.treeview.dialog_hide() + prepare_connection(enum, record) - thread_new_with_dialog( - _prep, parse_shell_output, "Querying API", enum, [key, response] - ) - return + try: + record = Servers.query_bm_api(key, response) + except Exception as e: + spawn_dialog(str(e), Popup.NOTIFY) + App.treeview.dialog_hide() + return + GLib.idle_add(cleanup) + + msg = "Validating ID" + App.treeview.dialog_show(msg) + thread = threading.Thread(target=_prep, args=(key, response)) + thread.start() def process_user_input(enum: RowType) -> None: @@ -2493,6 +2499,8 @@ class TreeView(Gtk.TreeView): def _dump_servers(self, ips: list) -> list | None: if len(ips) == 0: return [] + # NOTE: block malformed records + ips = [ip for ip in ips if len(ip.split(":")) == 3 and ip.split(":")[2] != "" ] with ThreadPoolExecutor() as executor: futures = [ executor.submit( @@ -2943,9 +2951,9 @@ class TreeView(Gtk.TreeView): def get_view(self): return self.view - def prepare_connection( + def get_prereqs( self, record: Record - ) -> subprocess.CompletedProcess | None: + ) -> tuple[bool, str, str|None, "Prereqs"]: """ Always called on a thread with a dialog on the transient parent window """ @@ -2953,8 +2961,7 @@ class TreeView(Gtk.TreeView): if prereqs.appid is None: logger.warning(f"Query to '{record.ip}:{record.qport}' timed out") msg = "Timed out when querying server, check IP or try again later" - spawn_dialog(msg, Popup.NOTIFY) - return None + return (False, msg, None, prereqs) build = "DayZ" if prereqs.appid == APPID_DAYZ else "DayZ Experimental" steam_path = query_config("default_steam_path")[0] @@ -2964,8 +2971,7 @@ class TreeView(Gtk.TreeView): "Config file has no value set for 'default_steam_path'" ) msg = "Local Steam installation is not set, possibly malformed config file." - spawn_dialog(msg, Popup.NOTIFY) - return None + return (False, msg, None, prereqs) try: pefile_path = PeFile.get_pefile_path(steam_path, prereqs.appid) @@ -2979,8 +2985,7 @@ class TreeView(Gtk.TreeView): f"If you recently installed {build} or moved it to a different drive, " "restart Steam to allow these changes to synchronize, then try again." ) - spawn_dialog(msg, Popup.NOTIFY) - return None + return (False, msg, None, prereqs) except AppMovedError: logger.critical( f"Library folder synch error for '{prereqs.appid}'" @@ -2990,13 +2995,11 @@ class TreeView(Gtk.TreeView): f"If you recently installed {build} or moved it to a different drive, " "restart Steam to allow these changes to synchronize, then try again." ) - spawn_dialog(msg, Popup.NOTIFY) - return None + return (False, msg, None, prereqs) except (VDFLoadError, PeFileError, Exception) as e: logger.critical(e) msg = "Steam settings or DayZ installation may be corrupted. Try restarting Steam." - spawn_dialog(msg, Popup.NOTIFY) - return None + return (False, msg, None, prereqs) try: local_vers = PeFile.get_dayz_version(pefile_path) @@ -3008,7 +3011,7 @@ class TreeView(Gtk.TreeView): local_vers = None try: - remote_vers = PeFile.dayz_version_from_str(prereqs.version) + remote_vers = PeFile.dayz_version_from_str(prereqs) except Exception: remote_vers = None @@ -3021,18 +3024,15 @@ class TreeView(Gtk.TreeView): f"This server is running a newer build ({prereqs.version}) of {build} than " f"your local version. You may be unable to connect. Proceed anyway?" ) - res = spawn_dialog(msg, Popup.CONFIRM) - if res is True: - return None + return (True, msg, pefile_path, prereqs) case VersionMatch.LOCAL_NEWER: msg = ( f"This server is running an out-of-date build ({prereqs.version}) of {build}. " "You may be unable to connect. Proceed anyway?" ) - res = spawn_dialog(msg, Popup.CONFIRM) - if res is True: - return None + return (True, msg, pefile_path, prereqs) case VersionMatch.SAME_VERSION: + return (True, "", pefile_path, prereqs) pass if prereqs.password is True: @@ -3040,19 +3040,10 @@ class TreeView(Gtk.TreeView): "This server is password-protected and you will be " "prompted when connecting. Do you want to proceed?" ) - res = spawn_dialog(msg, Popup.CONFIRM) - if res is True: - return None + return (True, msg, pefile_path, prereqs) + + return (True, "", pefile_path, prereqs) - """ - When using RowType.CONN_BY_IP, the gameport needs to be interpolated - """ - record.gameport = prereqs.gameport - addr = record_to_str(record) - proc = call_out( - "try_connect", addr, str(prereqs.appid), str(pefile_path) - ) - return proc @signal_emission @update_window_labels @@ -3122,13 +3113,7 @@ class TreeView(Gtk.TreeView): record = self.get_record() if record is None: return - thread_new_with_dialog( - self.prepare_connection, - parse_shell_output, - "Querying server", - None, - [record], - ) + prepare_connection(None, record) case _: # any other non-server option from the main menu process_tree_option(output)