Compare commits

..
1 Commits
Author SHA1 Message Date
Koloszár Gergely f46cea0ab8 Merge 506c82e86a into 7e45cb0fa2 2025-10-25 02:52:17 +00:00
14 changed files with 575 additions and 606 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:
-3
View File
@@ -1,4 +1 @@
tags tags
__pycache__
pre-commit
tests/
+285 -162
View File
@@ -1,67 +1,36 @@
# Changelog # Changelog
## [6.0.2] 2026-01-26 ## [6.0.0-beta.8] XXXX-XX-XX
## Added
- Flag to disable branch toggle for distro-packaged releases
- Test whether script was sourced inline
- Commandline usage help text
## Fixed ## Fixed
- Explicitly use Python 3.13 when calling subprocesses - ESC key destroying wait dialogs while thread is pending
- Raise error correctly from module - Tooltip signals being processed on main menu
- Do not spawn dialogs from inside of thread when fetching prereqs - Leaky variable name in dialog title
- Prevent extraneous signals from propagating when column width is adjusted
- Script failing to start when remote endpoints are unavailable
## Changed ## Changed
- Optimize time complexity of startup checks - Optimize time cost of setup checks
- Optimize coordinate calculation by using local records first
- Rename some internal functions
- Wrap entire startup process in dialog
## [6.0.1] 2026-01-16 ## [6.0.0-beta.7] 2025-09-20
## Fixed ## Added
- Explicitly use Python 3.13 when calling subprocesses - Users can save text notes to describe servers
## [6.0.0] 2026-01-14 ## Changed
- State file serialization methods
## [6.0.0-beta.6] 2025-09-12
## Added ## Added
- Support DayZ Experimental - Support DayZ Experimental
- Ping display on server tables - Show additional client information in Options menu
- Save descriptive text notes on a per-server basis - Warn user of client version mismatches
- Speed up load time and navigation of server tables - Support clickable hyperlinks
- 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
## Fixed ## Fixed
- 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
- Mods rarely not appearing in local mod list if download completed too quickly - Mods rarely not appearing in local mod list if download completed too quickly
- Statusbar not updating when clicking a row after spamming keyboard input - Statusbar not updating when clicking a row after spamming keyboard input
- Extraneous logs being generated when subscribing to mods - Extraneous logs being generated when subscribing to mods
@@ -69,188 +38,342 @@
- Newline terminators in history file - Newline terminators in history file
- Floating point number calculation - Floating point number calculation
- Window resizing too small if no prior resolution was set - 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 ## [6.0.0-beta.5] 2025-08-20
- UI not being constructed correctly if CHANGELOG.md was missing ## Fixed
- Config file erroneously getting updated when populating settings menu - Servers returning malformed A2S_INFO blocking server browser from loading
- Leaky variable name in dialog titles
- Prevent extraneous signals from propagating when column width is adjusted ## [6.0.0-beta.4] 2025-08-13
- Server filter panel not being hidden when entering other page contexts ## Fixed
- Path to remote changelog being constructed incorrectly - Branch toggle signal being emitted when entering Options menu from other contexts
- Dialogs with newlines breaking output in log table
- Path discovery during first-time setup when parsing filepaths with whitespaces ## [6.0.0-beta.3] 2025-08-08
- First-time setup dialog continuously triggering when DayZ install path had whitespaces in it ## 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 ## Changed
- Require Python 3.13
- Reduced global API cooldown from 60s to 30s - Reduced global API cooldown from 60s to 30s
- Clarify dialog messages when DayZ path could not be found - Clarify dialog messages when DayZ path could not be found
- Auto-focus first item when opening context menus - Auto-focus first item when opening context menus
- Opacity setting on side buttons when in a different context - Opacity setting on side buttons when in a different context
- Refactored BM API key validation to account for new key format - 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 ## Dropped
- Ping readout in statusbar - Ping readout in statusbar
- Extraneous information from right statusbar
## [5.8.3] 2026-01-04 ## [5.8.0-beta.2] 2025-07-06
## 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
## Added ## Added
- Filter servers by official/unofficial status - Filter servers by official/unofficial status
- Automatically fetch geolocation records
## Changed ## Changed
- Updated internal versioning of helper files - Updated internal versioning of helper files
## [5.8.0-beta.1] 2025-06-05
## Added
- Automatically fetch geolocation records
## Fixed ## Fixed
- Corrected erroneous 2024 date in prior changelog entries - Corrected erroneous 2024 date in prior changelog entries
## [5.7.0] 2025-04-17 ## [5.7.1-beta.1] 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
## Changed ## 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 ## 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 - 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 ## [5.7.0-beta.11] 2025-03-20
## Changed ## Fixed
- Update IP database records for 2025-03 - 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 ## Fixed
- Livonia server results being dropped from batch queries - Livonia server results being dropped from batch queries
## [5.6.4] 2025-02-10 ## [5.7.0-beta.8] 2025-02-10
### Changed ## Changed
- Drop launch flag and check for invocation through Steam automatically - Drop launch flag and check for invocation through Steam automatically
## [5.6.3] 2025-02-09 ## [5.7.0-beta.7] 2025-02-09
### Changed ## Changed
- Update IP database records for 2025-02 - Update IP database records for 2025-02
## [5.6.2] 2025-01-22 ## [5.7.0-beta.6] 2025-01-10
### Fixed ## Fixed
- Resolve regression introduced with IP resolution feature in 5.6.0 (restores functionality of right-click action: Add to My Servers) - Resolve regression introduced with IP resolution feature in 13c6813
## [5.6.1] 2025-01-10 ## [5.7.0-beta.5] 2025-01-10
### Fixed ## Changed
- Add fallback support for jq 1.6 - Stricter redacting of usernames (again)
## [5.6.0] 2025-01-06 ## [5.7.0-beta.4] 2025-01-10
### Added ## Fixed
- Application header bar and controls - Clerical hotfix
- Menu context subtitle in header bar
- Bulk delete mods (via 'List installed mods' list). Not compatible with Manual Mod install mode ## [5.7.0-beta.3] 2025-01-10
- Highlight stale mods in mods list ## Changed
- Added --steam launch parameter - Support legacy jq syntax for Ubuntu variants
- Open Steam workshop subscriptions dialog when switching from Manual to Auto mode
- Hover tooltips to most buttons ## [5.7.0-beta.2] 2025-01-07
- Add in-app documentation link to Codeberg mirror ## Fixed
- Additional logging - Stricter redacting of usernames
### Fixed - Omit nested directories when traversing symlinks
- Avoid sudo escalation if system map count is sufficient (jiriks74)
- Duplicate dialog title on Steam Deck ## [5.7.0-beta.1] 2025-01-07
- 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
### Changed ### 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 - Prevent ArrowUp/ArrowDown input when inside keyword field
- Admonish user to restart Steam in error dialog if DayZ path could not be found ### Changed
- 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
- Update forum URL - Update forum URL
- Reword Help section links to include destination
- Update README.md - Update README.md
- Update IP database to 2025-01 - Update IP database to 2025-01
- Reduce IP database size by 50% - Reduce IP database size by 50%
- Update documentation to 5.6.x standard - Update documentation to 5.6.x standard
### Dropped ### Dropped
- Hall of Fame section from button links, to be moved inside documentation - Removed temporary mod ID output in debug logs
- Unused imports - 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 ### Fixed
- Add remote resource health checks before downloading updates - Add remote resource health checks before downloading updates
### Added ### Added
- Add fallback repository - Add fallback repository
## [5.5.1] 2024-12-03 ## [5.6.0-beta.11] 2024-12-07
### Fixed ### 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 ### Added
- Support servers running DLC content (fixes Frostline servers) - Highlight stale mods in mods list
- Expose a toggle setting for whether to launch the application in fullscreen ### Fixed
- Text autocompletion in maps search field (partial search) - Duplicate dialog title on Steam Deck
- Add disk space warning to popup dialog when downloading mods
## [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 ### Fixed
- Servers in saved servers list would populate context menu with same option when right-clicking in server browser - 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 - 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 - Prevent maps combobox from duplicating contents
## [5.5.0-beta.2] 2024-10-31
### Fixed
- Restore keyboard input to keyword entry field - 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 - Abort fallback query method if DLC is required
## [5.4.2] 2024-10-05 ## [5.4.2-beta.1] 2024-10-05
### Fixed ### Fixed
- Sanitize third-party API IDs to remove UGC collisions - 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 ### Added
- Pre-boot validation check for users with self-compiled version of jq - Pre-boot validation check for users with self-compiled version of jq
### Fixed ### 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 - 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 - 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 - Global cooldown dialog could sometimes block filter toggles after cooldown reset
- Normalized minor version number due to a previous clerical error - 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 ### Added
- Scan local area network for DayZ servers
- Freedesktop application icons for system taskbar, tray, and other dialogs - Freedesktop application icons for system taskbar, tray, and other dialogs
- Emit CPU model name when exporting system debug log
### Fixed ### Fixed
- Errors being printed to the console when Exit button was explicitly clicked - 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 - 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 - 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 ## [5.3.2] 2024-07-02
### Fixed ### Fixed
+3 -5
View File
@@ -50,10 +50,8 @@ If you would like to take ownership or assist with an issue on the issue tracker
contribute a new change, please follow the guidelines below. contribute a new change, please follow the guidelines below.
Fork this repository and check out the code up to `prerelease/<latest-version>`. 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 All new features and fixes will be merged to this branch, so open your branch
the `testing` branch. You may later be asked to retarget your PR to the prerelease from here.
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: The following naming conventions apply for PRs:
- fix/<your-fix> - patch/hotfix branches - fix/<your-fix> - patch/hotfix branches
@@ -63,7 +61,7 @@ The following naming conventions apply for PRs:
Implement your changes and test them locally. If they work you may Implement your changes and test them 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 It is recommended to follow
+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
+103 -171
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.2
#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"
@@ -117,7 +115,6 @@ setup_state_files(){
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 +136,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 +218,16 @@ 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 if ! command -v "$dep" &> /dev/null; 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 +236,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 +317,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 +380,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 +394,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 +410,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 +459,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,15 +467,33 @@ 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
elif [[ -n "$steam" ]]; then
preferred_client="steam"
else
preferred_client="flatpak"
fi 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
} }
@@ -508,7 +502,7 @@ check_availability() {
return 1 return 1
fi fi
local url=$1 local url=$1
local timeout_sec="2" local timeout_sec="3"
if [[ $2 ]]; then if [[ $2 ]]; then
timeout_sec=$2 timeout_sec=$2
fi fi
@@ -519,7 +513,6 @@ check_availability() {
} }
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 url_ipecho="https://ipecho.net/plain"
if ! check_availability "ipecho.net"; then if ! check_availability "ipecho.net"; then
@@ -531,9 +524,6 @@ local_latlon(){
# TODO : implement checking remote # 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
return 0
fi
local url_ip_api="http://ip-api.com/json/$local_ip" local url_ip_api="http://ip-api.com/json/$local_ip"
if ! check_availability "ip-api.com"; then if ! check_availability "ip-api.com"; then
logger WARN "Failed to get local coordinates, ip-api.com service may be down." logger WARN "Failed to get local coordinates, ip-api.com service may be down."
@@ -548,7 +538,6 @@ local_latlon(){
} }
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
@@ -626,12 +615,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"]="5523d933e9f0668fc14b3faf0d4aafd8" ["funcs"]="6ae3ead7034dc8e7543472bddee75c63"
["query_v2.py"]="55d339ba02512ac69de288eb3be41067" ["query_v2.py"]="55d339ba02512ac69de288eb3be41067"
["servers.py"]="ed442c3aecf33f777d59dcf53650d263" ["servers.py"]="ed442c3aecf33f777d59dcf53650d263"
["ui.py"]="d09b9d8bed3854efd51377289786c5ac" ["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"
@@ -650,10 +639,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]}"
@@ -691,12 +676,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}'
} }
@@ -712,9 +700,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
@@ -734,7 +719,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
@@ -749,57 +734,55 @@ 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="$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 url="https://db-ip.com/db/download/ip-to-city-lite"
month_file="${state_path}/.month" local month_file="${state_path}/.month"
this_month=$(date +%Y-%m) 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"
# 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
return logger WARN "Local stub '$last_month' does not match remote stub '$month'"
fetch "$dl_url"
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
@@ -898,26 +881,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(){
@@ -926,7 +895,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 \
@@ -987,7 +955,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
@@ -1001,7 +968,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"
@@ -1012,16 +979,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"
@@ -1034,7 +1003,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
@@ -1068,7 +1036,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
@@ -1082,48 +1049,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
@@ -1132,12 +1069,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(){
@@ -1239,18 +1178,11 @@ main(){
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"
} }
+20 -13
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -o pipefail set -o pipefail
version="6.0.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
@@ -397,7 +399,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 +424,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 +589,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 +645,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=$?
@@ -699,6 +701,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"
@@ -797,9 +806,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
@@ -825,6 +831,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(){
+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;
}
+126 -132
View File
@@ -18,8 +18,7 @@ from enum import Enum
from collections.abc import Callable from collections.abc import Callable
from concurrent.futures import wait from concurrent.futures import wait
from concurrent.futures import ThreadPoolExecutor 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 servers as Servers # noqa E402
import pefile as PeFile # noqa E402 import pefile as PeFile # noqa E402
@@ -32,12 +31,10 @@ from pefile import (
) )
from pefile import VersionMatch from pefile import VersionMatch
if TYPE_CHECKING:
from servers import Prereqs
locale.setlocale(locale.LC_ALL, "") locale.setlocale(locale.LC_ALL, "")
import gi # noqa E402 import gi # noqa E402
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa E402 from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa E402
@@ -81,7 +78,7 @@ servers_path = f"{cache_path}/{app_name_abbr}.servers"
config_path = f"{user_path}/.config/dztui" config_path = f"{user_path}/.config/dztui"
config_file = f"{config_path}/dztuirc" config_file = f"{config_path}/dztuirc"
history_file = f"{state_path}/{app_name_abbr}.history" history_file = f"{state_path}/{app_name_abbr}.history"
notes_file = f"{config_path}/{app_name_abbr}.notes.json" notes_file = f"{state_path}/{app_name_abbr}.notes.json"
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
log_file = f"{log_path}/{app_name}_DEBUG.log" log_file = f"{log_path}/{app_name}_DEBUG.log"
@@ -826,7 +823,8 @@ def query_history() -> list | None:
rows = [row.rstrip("\n") for row in f] rows = [row.rstrip("\n") for row in f]
except OSError: except OSError:
rows = None rows = None
return rows finally:
return rows
def query_favorites() -> None | list: def query_favorites() -> None | list:
@@ -868,14 +866,13 @@ def spawn_dialog(msg: str, mode: Popup) -> bool:
dialog = GenericDialog(msg, mode) dialog = GenericDialog(msg, mode)
response = dialog.run() response = dialog.run()
dialog.destroy() dialog.destroy()
clean_msg = msg.replace("\n", " ")
match response: match response:
case Gtk.ResponseType.OK: case Gtk.ResponseType.OK:
logger.info(f"User confirmed dialog with message '{clean_msg}'") logger.info(f"User confirmed dialog with message '{msg}'")
return False return False
case Gtk.ResponseType.CANCEL | Gtk.ResponseType.DELETE_EVENT: case Gtk.ResponseType.CANCEL | Gtk.ResponseType.DELETE_EVENT:
logger.info(f"User aborted dialog with message '{clean_msg}'") logger.info(f"User aborted dialog with message '{msg}'")
return True return True
return False return False
@@ -1019,7 +1016,7 @@ def process_tree_option(choice: RowType) -> None:
return return
if command == RowType.CHANGELOG: if command == RowType.CHANGELOG:
App.grid.notebook.open_changelog() App.grid.notebook.set_page_by_enum(NotebookPage.CHANGELOG)
return return
if command == RowType.QUICK_CONNECT: if command == RowType.QUICK_CONNECT:
@@ -1029,7 +1026,13 @@ def process_tree_option(choice: RowType) -> None:
return return
record = str_to_record(record) record = str_to_record(record)
prepare_connection(command, record) thread_new_with_dialog(
App.treeview.prepare_connection,
parse_shell_output,
"Querying server",
command,
[record],
)
return return
match command.dict["type"]: match command.dict["type"]:
@@ -1057,54 +1060,48 @@ def parse_shell_output(proc: subprocess.CompletedProcess, row: RowType):
process_shell_return_code(msg, proc.returncode, row) process_shell_return_code(msg, proc.returncode, row)
def prepare_connection(rowtype: RowType, record: Record) -> None: def thread_new_with_dialog(
def background(rowtype: RowType, record: Record) -> None: func: Callable,
def cleanup() -> None: callback: Callable | None,
App.treeview.wait_dialog.destroy() msg: str,
# NOTE: When using RowType.CONN_BY_IP, the gameport needs to be interpolated row: RowType | None,
record.gameport = prereqs.gameport args: list,
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: ) -> None:
def background(addr, appid, path): """
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 cleanup(): def cleanup():
App.treeview.dialog_hide() App.treeview.dialog_hide()
parse_shell_output(proc, row) 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)
exception = None
proc = None
try: try:
proc = call_out("try_connect", addr, appid, path) proc = func(*args)
except Exception as e: except Exception as e:
logger.critical(e) exception = e
GLib.idle_add(cleanup) GLib.idle_add(cleanup)
return return
GLib.idle_add(cleanup) GLib.idle_add(cleanup)
msg = "Querying server"
App.treeview.dialog_show(msg) App.treeview.dialog_show(msg)
thread = threading.Thread(target=background, args=(addr, appid, path)) thread = threading.Thread(target=background, args=(args))
thread.start() thread.start()
@@ -1144,31 +1141,27 @@ def record_to_str(record: Record) -> str:
def connect_by_ip(enum: RowType, response: str) -> None: def connect_by_ip(enum: RowType, response: str) -> None:
try: def _prep(response: str) -> None:
record = Servers.validate_ip(response) record = Servers.validate_ip(response)
except Exception as e: proc = App.treeview.prepare_connection(record)
spawn_dialog(str(e), Popup.NOTIFY) return proc
return
prepare_connection(enum, record) thread_new_with_dialog(
_prep, parse_shell_output, "Querying IP", enum, [response]
)
return
def connect_by_id(enum: RowType, response: str, key: str) -> None: def connect_by_id(enum: RowType, response: str, key: str) -> None:
def _prep(key: str, response: str) -> None: def _prep(key: str, response: str) -> None:
def cleanup() -> None: record = Servers.query_bm_api(key, response)
App.treeview.dialog_hide() proc = App.treeview.prepare_connection(record)
prepare_connection(enum, record) return proc
try: thread_new_with_dialog(
record = Servers.query_bm_api(key, response) _prep, parse_shell_output, "Querying API", enum, [key, response]
except Exception as e: )
spawn_dialog(str(e), Popup.NOTIFY) return
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: def process_user_input(enum: RowType) -> None:
@@ -1493,8 +1486,6 @@ class ButtonBox(Gtk.Box):
context = button.type context = button.type
logger.info(f"User clicked '{context}'") logger.info(f"User clicked '{context}'")
App.grid.right_panel.filters_vbox.set_visible(False)
if context == ButtonType.EXIT: if context == ButtonType.EXIT:
logger.info("Normal user exit") logger.info("Normal user exit")
save_res_and_quit() save_res_and_quit()
@@ -2499,8 +2490,6 @@ class TreeView(Gtk.TreeView):
def _dump_servers(self, ips: list) -> list | None: def _dump_servers(self, ips: list) -> list | None:
if len(ips) == 0: if len(ips) == 0:
return [] return []
# NOTE: block malformed records
ips = [ip for ip in ips if len(ip.split(":")) == 3 and ip.split(":")[2] != "" ]
with ThreadPoolExecutor() as executor: with ThreadPoolExecutor() as executor:
futures = [ futures = [
executor.submit( executor.submit(
@@ -2951,9 +2940,9 @@ class TreeView(Gtk.TreeView):
def get_view(self): def get_view(self):
return self.view return self.view
def get_prereqs( def prepare_connection(
self, record: Record self, record: Record
) -> tuple[bool, str, str|None, "Prereqs"]: ) -> subprocess.CompletedProcess | None:
""" """
Always called on a thread with a dialog on the transient parent window Always called on a thread with a dialog on the transient parent window
""" """
@@ -2961,7 +2950,8 @@ class TreeView(Gtk.TreeView):
if prereqs.appid is None: if prereqs.appid is None:
logger.warning(f"Query to '{record.ip}:{record.qport}' timed out") logger.warning(f"Query to '{record.ip}:{record.qport}' timed out")
msg = "Timed out when querying server, check IP or try again later" msg = "Timed out when querying server, check IP or try again later"
return (False, msg, None, prereqs) spawn_dialog(msg, Popup.NOTIFY)
return None
build = "DayZ" if prereqs.appid == APPID_DAYZ else "DayZ Experimental" build = "DayZ" if prereqs.appid == APPID_DAYZ else "DayZ Experimental"
steam_path = query_config("default_steam_path")[0] steam_path = query_config("default_steam_path")[0]
@@ -2971,7 +2961,8 @@ class TreeView(Gtk.TreeView):
"Config file has no value set for 'default_steam_path'" "Config file has no value set for 'default_steam_path'"
) )
msg = "Local Steam installation is not set, possibly malformed config file." msg = "Local Steam installation is not set, possibly malformed config file."
return (False, msg, None, prereqs) spawn_dialog(msg, Popup.NOTIFY)
return None
try: try:
pefile_path = PeFile.get_pefile_path(steam_path, prereqs.appid) pefile_path = PeFile.get_pefile_path(steam_path, prereqs.appid)
@@ -2985,7 +2976,8 @@ class TreeView(Gtk.TreeView):
f"If you recently installed {build} or moved it to a different drive, " f"If you recently installed {build} or moved it to a different drive, "
"restart Steam to allow these changes to synchronize, then try again." "restart Steam to allow these changes to synchronize, then try again."
) )
return (False, msg, None, prereqs) spawn_dialog(msg, Popup.NOTIFY)
return None
except AppMovedError: except AppMovedError:
logger.critical( logger.critical(
f"Library folder synch error for '{prereqs.appid}'" f"Library folder synch error for '{prereqs.appid}'"
@@ -2995,11 +2987,13 @@ class TreeView(Gtk.TreeView):
f"If you recently installed {build} or moved it to a different drive, " f"If you recently installed {build} or moved it to a different drive, "
"restart Steam to allow these changes to synchronize, then try again." "restart Steam to allow these changes to synchronize, then try again."
) )
return (False, msg, None, prereqs) spawn_dialog(msg, Popup.NOTIFY)
return None
except (VDFLoadError, PeFileError, Exception) as e: except (VDFLoadError, PeFileError, Exception) as e:
logger.critical(e) logger.critical(e)
msg = "Steam settings or DayZ installation may be corrupted. Try restarting Steam." msg = "Steam settings or DayZ installation may be corrupted. Try restarting Steam."
return (False, msg, None, prereqs) spawn_dialog(msg, Popup.NOTIFY)
return None
try: try:
local_vers = PeFile.get_dayz_version(pefile_path) local_vers = PeFile.get_dayz_version(pefile_path)
@@ -3011,7 +3005,7 @@ class TreeView(Gtk.TreeView):
local_vers = None local_vers = None
try: try:
remote_vers = PeFile.dayz_version_from_str(prereqs) remote_vers = PeFile.dayz_version_from_str(prereqs.version)
except Exception: except Exception:
remote_vers = None remote_vers = None
@@ -3024,15 +3018,18 @@ class TreeView(Gtk.TreeView):
f"This server is running a newer build ({prereqs.version}) of {build} than " 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?" f"your local version. You may be unable to connect. Proceed anyway?"
) )
return (True, msg, pefile_path, prereqs) res = spawn_dialog(msg, Popup.CONFIRM)
if res is True:
return None
case VersionMatch.LOCAL_NEWER: case VersionMatch.LOCAL_NEWER:
msg = ( msg = (
f"This server is running an out-of-date build ({prereqs.version}) of {build}. " f"This server is running an out-of-date build ({prereqs.version}) of {build}. "
"You may be unable to connect. Proceed anyway?" "You may be unable to connect. Proceed anyway?"
) )
return (True, msg, pefile_path, prereqs) res = spawn_dialog(msg, Popup.CONFIRM)
if res is True:
return None
case VersionMatch.SAME_VERSION: case VersionMatch.SAME_VERSION:
return (True, "", pefile_path, prereqs)
pass pass
if prereqs.password is True: if prereqs.password is True:
@@ -3040,10 +3037,19 @@ class TreeView(Gtk.TreeView):
"This server is password-protected and you will be " "This server is password-protected and you will be "
"prompted when connecting. Do you want to proceed?" "prompted when connecting. Do you want to proceed?"
) )
return (True, msg, pefile_path, prereqs) res = spawn_dialog(msg, Popup.CONFIRM)
if res is True:
return (True, "", pefile_path, prereqs) return None
"""
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 @signal_emission
@update_window_labels @update_window_labels
@@ -3113,7 +3119,13 @@ class TreeView(Gtk.TreeView):
record = self.get_record() record = self.get_record()
if record is None: if record is None:
return return
prepare_connection(None, record) thread_new_with_dialog(
self.prepare_connection,
parse_shell_output,
"Querying server",
None,
[record],
)
case _: # any other non-server option from the main menu case _: # any other non-server option from the main menu
process_tree_option(output) process_tree_option(output)
@@ -3789,22 +3801,12 @@ class Options(Gtk.Box):
"Always fullscreen", "Always fullscreen",
Preferences.WINDOW, Preferences.WINDOW,
) )
self.steam_toggle = self.make_binary_radio(
# TODO: gray out options if not available on system "Steam", "Flatpak (experimental)", Preferences.CLIENT
client_store = Gtk.ListStore(str, str) )
client_store.append(["steam", "steam"])
client_store.append(["flatpak", "flatpak run com.valvesoftware.Steam"])
client_store.append(["flatpak (container)", "flatpak-spawn --host flatpak run com.valvesoftware.Steam"])
renderer_text = Gtk.CellRendererText(ellipsize=Pango.EllipsizeMode.END)
self.client_combo = Gtk.ComboBox.new_with_model(client_store)
self.client_combo.set_halign(Gtk.Align.START)
self.client_combo.pack_start(renderer_text, True)
self.client_combo.connect("changed", self._on_client_changed)
self.client_combo.add_attribute(renderer_text, "text", 0)
pref_rows = [ pref_rows = [
[LeftLabel("Steam client"), self.client_combo], [LeftLabel("Steam client"), self.steam_toggle],
[LeftLabel("Window size at boot"), self.fullscreen_toggle], [LeftLabel("Window size at boot"), self.fullscreen_toggle],
[LeftLabel("Player name"), self.player_box], [LeftLabel("Player name"), self.player_box],
] ]
@@ -3989,15 +3991,6 @@ class Options(Gtk.Box):
show_wait_dialog = True show_wait_dialog = True
call_on_thread(show_wait_dialog, cmd, wait_msg, "") call_on_thread(show_wait_dialog, cmd, wait_msg, "")
def _on_client_changed(self, combo: Gtk.ComboBox) -> None:
# prevent triggering on initial init
if App.treeview.subpage is not RowType.OPTIONS:
return
ind = combo.get_active()
mod = combo.get_model()
client = mod[ind][1]
call_bash_func("Change client", client)
def _on_branch_changed(self, combo: Gtk.ComboBoxText) -> None: def _on_branch_changed(self, combo: Gtk.ComboBoxText) -> None:
# prevent triggering on initial init # prevent triggering on initial init
if App.treeview.subpage is not RowType.OPTIONS: if App.treeview.subpage is not RowType.OPTIONS:
@@ -4007,9 +4000,11 @@ class Options(Gtk.Box):
def _on_radio_toggled( def _on_radio_toggled(
self, button: Gtk.RadioButton, context: Preferences self, button: Gtk.RadioButton, context: Preferences
) -> None: ) -> None:
if App.treeview.subpage is not RowType.OPTIONS: if App.treeview.subpage is None:
return return
match context: match context:
case Preferences.CLIENT:
toggle = RowType.TGL_STEAM
case Preferences.INSTALL: case Preferences.INSTALL:
toggle = RowType.TGL_INSTALL toggle = RowType.TGL_INSTALL
state = button.get_group()[0].get_active() state = button.get_group()[0].get_active()
@@ -4131,9 +4126,11 @@ class Options(Gtk.Box):
radio = 0 radio = 0
self.mod_install_toggle.get_children()[radio].set_active(True) self.mod_install_toggle.get_children()[radio].set_active(True)
for ind, row in enumerate(self.client_combo.get_model()): if client == "flatpak":
if row[1] == client: radio = 1
self.client_combo.set_active(ind) else:
radio = 0
self.steam_toggle.get_children()[radio].set_active(True)
if fullscreen == "true": if fullscreen == "true":
radio = 1 radio = 1
@@ -4328,20 +4325,22 @@ class KeybindingsDialog(Gtk.Box):
class Changelog(Gtk.Box): class Changelog(Gtk.Box):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.changelog_label = Gtk.Label()
self.add(self.changelog_label)
def open_changelog(self, path: Path) -> None:
try: try:
changelog = path.read_text() with open(changelog_path, "r") as f:
changelog = f.read()
changelog_label = Gtk.Label()
changelog = self.format_pango(changelog)
changelog_label.set_markup(changelog)
self.add(changelog_label)
except FileNotFoundError:
msg = f"Failed to find CHANGELOG.md at {changelog_path}"
logger.critical(msg)
spawn_dialog(msg, Popup.WARN)
return
except OSError as e: except OSError as e:
spawn_dialog(f"Something went wrong: {e}", Popup.NOTIFY) spawn_dialog(f"Something went wrong: {e}", Popup.WARN)
logger.critical(e) logger.critical(e)
return Exception return
formatted = self.format_pango(changelog)
self.changelog_label.set_markup(formatted)
App.grid.notebook.set_page_by_enum(NotebookPage.CHANGELOG)
def format_pango(self, text: str) -> str: def format_pango(self, text: str) -> str:
medium = '<span size="medium"><b>' medium = '<span size="medium"><b>'
@@ -4358,8 +4357,7 @@ class Notebook(Gtk.Notebook):
def __init__(self): def __init__(self):
super().__init__(show_tabs=False, show_border=False) super().__init__(show_tabs=False, show_border=False)
self.changelog = Changelog() self.clog = ScrollableNote(Changelog())
self.clog = ScrollableNote(self.changelog)
self.clog.type = RowType.CHANGELOG self.clog.type = RowType.CHANGELOG
self.clog.show_all() self.clog.show_all()
self.append_page(self.clog) self.append_page(self.clog)
@@ -4378,10 +4376,6 @@ class Notebook(Gtk.Notebook):
self.connect("switch-page", self._on_page_changed) self.connect("switch-page", self._on_page_changed)
self.connect("key-press-event", self._on_keypress) self.connect("key-press-event", self._on_keypress)
def open_changelog(self) -> None:
path = Path(changelog_path)
self.changelog.open_changelog(path)
def _set_adjustment(self, adjustment: VAdjustment) -> None: def _set_adjustment(self, adjustment: VAdjustment) -> None:
INCREMENT = 50 INCREMENT = 50
page = self.get_page() page = self.get_page()