mirror of
https://github.com/aclist/dztui.git
synced 2026-11-05 17:45:10 +01:00
Compare commits
229
Commits
f46cea0ab8
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2f6abd667 | ||
|
|
f76985c79d | ||
|
|
3d3cd19892 | ||
|
|
c14fb10d23 | ||
|
|
a8417d27d7 | ||
|
|
2d58bb491e | ||
|
|
2f123da63a | ||
|
|
7016edf7cd | ||
|
|
b630cf4080 | ||
|
|
1034a5ac04 | ||
|
|
ebc62b0b1b | ||
|
|
4b47ff1fe0 | ||
|
|
e3155ad7ee | ||
|
|
c45d9fb234 | ||
|
|
22e92e67a7 | ||
|
|
cb45ce7b9a | ||
|
|
9678d41b4c | ||
|
|
0daf567111 | ||
|
|
b75e99ffd7 | ||
|
|
5562c44320 | ||
|
|
236a5dd0a3 | ||
|
|
4c10958d10 | ||
|
|
23f7140c66 | ||
|
|
fbcfa1fb34 | ||
|
|
52f36e0d0a | ||
|
|
d90c069a73 | ||
|
|
d2f3eebd42 | ||
|
|
af64ebb12a | ||
|
|
203ca0d388 | ||
|
|
34ee8c6645 | ||
|
|
0cd561b62a | ||
|
|
b9c30b4f52 | ||
|
|
4bcf894704 | ||
|
|
c37800795b | ||
|
|
929f497b9f | ||
|
|
60aebe5eeb | ||
|
|
aef9a10c42 | ||
|
|
c6034bc6b9 | ||
|
|
005288371b | ||
|
|
53e50a5716 | ||
|
|
a50b11633f | ||
|
|
9c4541fe76 | ||
|
|
ac3e7ee128 | ||
|
|
1495ad386d | ||
|
|
4edf71aa0a | ||
|
|
113e5f885b | ||
|
|
a3092b1bf3 | ||
|
|
e28929bb70 | ||
|
|
4a820e3083 | ||
|
|
d866b4992b | ||
|
|
376c5294d9 | ||
|
|
71651bab6c | ||
|
|
1dba57af4b | ||
|
|
dabf5d4ef0 | ||
|
|
75f8c841df | ||
|
|
5bce8709af | ||
|
|
a418bcfb90 | ||
|
|
90d59cec97 | ||
|
|
a8ae07931d | ||
|
|
a4ce65fe57 | ||
|
|
d7e94eab90 | ||
|
|
a08c588df3 | ||
|
|
385b1a5af2 | ||
|
|
be02f086c7 | ||
|
|
cc35b2cd85 | ||
|
|
a419aad160 | ||
|
|
21d4601e41 | ||
|
|
adf8a62e8a | ||
|
|
a1d14669c7 | ||
|
|
46a54cef12 | ||
|
|
a94f8f0e32 | ||
|
|
0da098afb6 | ||
|
|
c8e9f8f1fc | ||
|
|
661329877f | ||
|
|
cc73f23c1c | ||
|
|
cbc131028f | ||
|
|
239a82876e | ||
|
|
e73617c2f4 | ||
|
|
67b0559058 | ||
|
|
dfe65aacd4 | ||
|
|
6aa1ec4201 | ||
|
|
d6223e54b0 | ||
|
|
83552f833c | ||
|
|
f0657be289 | ||
|
|
321384dc7b | ||
|
|
e02f02b775 | ||
|
|
de524bd2a5 | ||
|
|
edee9b6234 | ||
|
|
cefd041e08 | ||
|
|
881b0b3008 | ||
|
|
2a59441dbc | ||
|
|
5ab18d0796 | ||
|
|
23e3d34947 | ||
|
|
b86dd37b50 | ||
|
|
59fdd30236 | ||
|
|
a35bfda5f1 | ||
|
|
5393bf1f5f | ||
|
|
90b3aa8663 | ||
|
|
728e4daf7c | ||
|
|
7a210c7f39 | ||
|
|
459dd69359 | ||
|
|
fbd1a355d5 | ||
|
|
82d2afb1f2 | ||
|
|
8193980878 | ||
|
|
acdb34d36a | ||
|
|
d6d0b241e0 | ||
|
|
f75ef947c9 | ||
|
|
6dc9a719ee | ||
|
|
785df28f66 | ||
|
|
66e114a400 | ||
|
|
2ba07bbba8 | ||
|
|
65eda05d9f | ||
|
|
f3b3f79bfb | ||
|
|
28c7d7f949 | ||
|
|
d072c8107e | ||
|
|
0629c8a6ba | ||
|
|
8aea4af687 | ||
|
|
0562d8230d | ||
|
|
759327e932 | ||
|
|
36753e02f1 | ||
|
|
2ab2e23736 | ||
|
|
34e7b2e480 | ||
|
|
540d0ad7e4 | ||
|
|
47bea45111 | ||
|
|
d03346b56a | ||
|
|
54e0762882 | ||
|
|
0834a86d33 | ||
|
|
c6a8841614 | ||
|
|
df92acb7dc | ||
|
|
a333571f94 | ||
|
|
05f88b6019 | ||
|
|
1f31c43fe4 | ||
|
|
0763416903 | ||
|
|
3b384c5a4d | ||
|
|
3ab666a807 | ||
|
|
c8f7a32284 | ||
|
|
a070e9fc65 | ||
|
|
5f55e3c664 | ||
|
|
07606d8852 | ||
|
|
afffc90abc | ||
|
|
70db7b45b3 | ||
|
|
aac8558079 | ||
|
|
ac8ff2febf | ||
|
|
6369d895fd | ||
|
|
7413f7c029 | ||
|
|
3f1d8cb24f | ||
|
|
4df12605fc | ||
|
|
2c5da88e06 | ||
|
|
3cb6175436 | ||
|
|
3739f19ab2 | ||
|
|
2da4d001d6 | ||
|
|
5d61a1f513 | ||
|
|
56c80d187b | ||
|
|
e08f66cdcb | ||
|
|
48fee301bb | ||
|
|
20511f5661 | ||
|
|
d2e8333ba9 | ||
|
|
5d37039064 | ||
|
|
ce44cdbc54 | ||
|
|
5b2738420e | ||
|
|
1e29288e2a | ||
|
|
745cc06091 | ||
|
|
0311cc8a95 | ||
|
|
7c3521cf97 | ||
|
|
bb0eefadd2 | ||
|
|
7eb5a4fac5 | ||
|
|
ebce329c3d | ||
|
|
23464d3b51 | ||
|
|
602a7b8edc | ||
|
|
59c58a1df5 | ||
|
|
830318d98e | ||
|
|
271c3e6197 | ||
|
|
1ab9634069 | ||
|
|
8666b0055a | ||
|
|
406554e011 | ||
|
|
5334d07f7f | ||
|
|
6cab96d1f8 | ||
|
|
bc94d989d4 | ||
|
|
e4fff5f242 | ||
|
|
53f2fe922a | ||
|
|
800f68a1b7 | ||
|
|
c6c377e8b9 | ||
|
|
7a68d7e097 | ||
|
|
f60a6edf2b | ||
|
|
8c33a1b371 | ||
|
|
e5f7391b89 | ||
|
|
a8f51280e8 | ||
|
|
c19346529b | ||
|
|
85451f765b | ||
|
|
5a144f7815 | ||
|
|
2370e81403 | ||
|
|
c11511abff | ||
|
|
bda7223ac8 | ||
|
|
6b48ee127d | ||
|
|
aa8a52bfc0 | ||
|
|
8ea62434ee | ||
|
|
baeaeb1b64 | ||
|
|
32b424da5c | ||
|
|
d71110d0a1 | ||
|
|
bc9178d2de | ||
|
|
1533febf9a | ||
|
|
c3c8872fad | ||
|
|
bae6a57e1e | ||
|
|
3bdc0057a2 | ||
|
|
5496f914f2 | ||
|
|
cb9db55bcb | ||
|
|
c0ab8111fd | ||
|
|
10c6a79b17 | ||
|
|
2b4b533a5b | ||
|
|
dcb8be9132 | ||
|
|
278aa8c28a | ||
|
|
32e5bafb33 | ||
|
|
59e19510e3 | ||
|
|
45ab112970 | ||
|
|
ed07fd0343 | ||
|
|
c2c7b37b90 | ||
|
|
23798c91d3 | ||
|
|
659cbb83a3 | ||
|
|
32c467fc6d | ||
|
|
d01add3573 | ||
|
|
0de89a3ef1 | ||
|
|
5686ea76c2 | ||
|
|
5fda598173 | ||
|
|
049fb47019 | ||
|
|
81f269b71b | ||
|
|
c97a7a260f | ||
|
|
b715eb5a3e | ||
|
|
8e6cf7e792 | ||
|
|
98868073a5 |
No files matched your search
@@ -5,6 +5,10 @@ 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:
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ labels: ["troubleshooting"]
|
|||||||
assignees:
|
assignees:
|
||||||
- aclist
|
- aclist
|
||||||
body:
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
### WARNING: When posting log/config data, redact any sensitive information like API keys.
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
id: device
|
id: device
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
@@ -1 +1,4 @@
|
|||||||
tags
|
tags
|
||||||
|
__pycache__
|
||||||
|
pre-commit
|
||||||
|
tests/
|
||||||
+168
-291
@@ -1,36 +1,67 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## [6.0.0-beta.8] XXXX-XX-XX
|
## [6.0.2] 2026-01-26
|
||||||
## Added
|
|
||||||
- Flag to disable branch toggle for distro-packaged releases
|
|
||||||
- Test whether script was sourced inline
|
|
||||||
- Commandline usage help text
|
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
- ESC key destroying wait dialogs while thread is pending
|
- Explicitly use Python 3.13 when calling subprocesses
|
||||||
- Tooltip signals being processed on main menu
|
- Raise error correctly from module
|
||||||
- Leaky variable name in dialog title
|
- Do not spawn dialogs from inside of thread when fetching prereqs
|
||||||
- Prevent extraneous signals from propagating when column width is adjusted
|
|
||||||
- Script failing to start when remote endpoints are unavailable
|
|
||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
- Optimize time cost of setup checks
|
- 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.0-beta.7] 2025-09-20
|
## [6.0.1] 2026-01-16
|
||||||
## Added
|
## Fixed
|
||||||
- Users can save text notes to describe servers
|
- Explicitly use Python 3.13 when calling subprocesses
|
||||||
|
|
||||||
## Changed
|
## [6.0.0] 2026-01-14
|
||||||
- State file serialization methods
|
|
||||||
|
|
||||||
## [6.0.0-beta.6] 2025-09-12
|
|
||||||
## Added
|
## Added
|
||||||
- Support DayZ Experimental
|
- Support DayZ Experimental
|
||||||
- Show additional client information in Options menu
|
- Ping display on server tables
|
||||||
- Warn user of client version mismatches
|
- Save descriptive text notes on a per-server basis
|
||||||
- Support clickable hyperlinks
|
- 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
|
||||||
|
|
||||||
## 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
|
||||||
@@ -38,342 +69,188 @@
|
|||||||
- 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
|
||||||
## [6.0.0-beta.5] 2025-08-20
|
- Tooltip signals being erroneously emitted on main menu
|
||||||
## Fixed
|
- UI not being constructed correctly if CHANGELOG.md was missing
|
||||||
- Servers returning malformed A2S_INFO blocking server browser from loading
|
- Config file erroneously getting updated when populating settings menu
|
||||||
|
- Leaky variable name in dialog titles
|
||||||
## [6.0.0-beta.4] 2025-08-13
|
- Prevent extraneous signals from propagating when column width is adjusted
|
||||||
## Fixed
|
- Server filter panel not being hidden when entering other page contexts
|
||||||
- Branch toggle signal being emitted when entering Options menu from other contexts
|
- Path to remote changelog being constructed incorrectly
|
||||||
|
- Dialogs with newlines breaking output in log table
|
||||||
## [6.0.0-beta.3] 2025-08-08
|
- Path discovery during first-time setup when parsing filepaths with whitespaces
|
||||||
## Added
|
- First-time setup dialog continuously triggering when DayZ install path had whitespaces in it
|
||||||
- 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.0-beta.2] 2025-07-06
|
## [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
|
||||||
## 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.1-beta.1] 2025-04-17
|
## [5.7.0] 2025-04-17
|
||||||
## Changed
|
## Added
|
||||||
- Updated geolocation records
|
- Save application dimensions when quitting and restore on subsequent boot
|
||||||
|
|
||||||
## [5.7.0-beta.13] 2025-04-17
|
|
||||||
## Fixed
|
## Fixed
|
||||||
- Updated checksum for UI helper file
|
- Issues with the window exceeding the bounds of the screen when displaying some table contexts
|
||||||
|
## Changed
|
||||||
|
- More direct server ping query method that should return marginally more accurate times
|
||||||
|
|
||||||
## [5.7.0-beta.12] 2025-04-04
|
## [5.6.7] 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.7.0-beta.11] 2025-03-20
|
## [5.6.6] 2025-03-16
|
||||||
## Fixed
|
|
||||||
- Reduce startup time when testing remote APIs
|
|
||||||
|
|
||||||
## [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
|
## Changed
|
||||||
- Packed filter checkbox buttons into a 3x3 grid
|
- Update IP database records for 2025-03
|
||||||
- Updated IP database to 2025-03 records
|
|
||||||
|
|
||||||
## [5.7.0-beta.9] 2025-03-04
|
## [5.6.5] 2025-03-04
|
||||||
## Fixed
|
## Fixed
|
||||||
- Livonia server results being dropped from batch queries
|
- Livonia server results being dropped from batch queries
|
||||||
|
|
||||||
## [5.7.0-beta.8] 2025-02-10
|
## [5.6.4] 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.7.0-beta.7] 2025-02-09
|
## [5.6.3] 2025-02-09
|
||||||
## Changed
|
### Changed
|
||||||
- Update IP database records for 2025-02
|
- Update IP database records for 2025-02
|
||||||
|
|
||||||
## [5.7.0-beta.6] 2025-01-10
|
## [5.6.2] 2025-01-22
|
||||||
## Fixed
|
|
||||||
- Resolve regression introduced with IP resolution feature in 13c6813
|
|
||||||
|
|
||||||
## [5.7.0-beta.5] 2025-01-10
|
|
||||||
## Changed
|
|
||||||
- Stricter redacting of usernames (again)
|
|
||||||
|
|
||||||
## [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
|
|
||||||
- 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
|
### Fixed
|
||||||
- Prevent ArrowUp/ArrowDown input when inside keyword field
|
- Resolve regression introduced with IP resolution feature in 5.6.0 (restores functionality of right-click action: Add to My Servers)
|
||||||
|
|
||||||
|
## [5.6.1] 2025-01-10
|
||||||
|
### Fixed
|
||||||
|
- Add fallback support for jq 1.6
|
||||||
|
|
||||||
|
## [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
|
||||||
### Changed
|
### Changed
|
||||||
|
- Redact usernames in log files
|
||||||
|
- 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
|
||||||
- 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
|
||||||
- Removed temporary mod ID output in debug logs
|
- Hall of Fame section from button links, to be moved inside documentation
|
||||||
- Removed Hall of Fame section from button links, moved inside documentation
|
- Unused imports
|
||||||
- Remove unused imports
|
|
||||||
|
|
||||||
## [5.6.0-beta.20] 2024-12-23
|
## [5.5.3] 2024-12-13
|
||||||
### 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.6.0-beta.11] 2024-12-07
|
## [5.5.1] 2024-12-03
|
||||||
### Fixed
|
### Fixed
|
||||||
- Add missing function definition
|
- Support localized decimal separators when parsing installed mod sizes
|
||||||
|
|
||||||
## [5.6.0-beta.10] 2024-12-04
|
## [5.5.0] 2024-11-10
|
||||||
### 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
|
|
||||||
- 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
|
|
||||||
|
|
||||||
## [5.5.0-beta.1] 2024-10-30
|
|
||||||
### Added
|
### Added
|
||||||
- Support servers running DLC content (fixes Frostline servers)
|
- Support servers running DLC content (fixes Frostline servers)
|
||||||
- Text autocompletion in maps search field
|
- Expose a toggle setting for whether to launch the application in fullscreen
|
||||||
- Add disk space warning to popup dialog
|
- Text autocompletion in maps search field (partial search)
|
||||||
|
- Add disk space warning to popup dialog when downloading mods
|
||||||
### Fixed
|
### Fixed
|
||||||
|
- Servers in saved servers list would populate context menu with same option when right-clicking in server browser
|
||||||
|
- Enable adding/removing servers to/from My Saved Servers when in Recent Servers context
|
||||||
|
- Prevent maps combobox from duplicating contents
|
||||||
|
- Restore keyboard input to keyword entry field
|
||||||
|
### Changed
|
||||||
- Abort fallback query method if DLC is required
|
- Abort fallback query method if DLC is required
|
||||||
|
|
||||||
## [5.4.2-beta.1] 2024-10-05
|
## [5.4.2] 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-beta.2] 2024-09-12
|
## [5.4.1] 2024-09-25
|
||||||
### 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-beta.5] 2024-08-27
|
## [5.4.0] 2024-08-27
|
||||||
### Added
|
|
||||||
- Freedesktop application icons for system taskbar, tray, and other dialogs
|
|
||||||
### 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
|
|
||||||
|
|
||||||
## [5.4.0-beta.3] 2024-08-04
|
|
||||||
### Added
|
### Added
|
||||||
- Scan local area network for DayZ servers
|
- Scan local area network for DayZ servers
|
||||||
|
- Freedesktop application icons for system taskbar, tray, and other dialogs
|
||||||
## [5.4.0-beta.2] 2024-08-03
|
- Emit CPU model name when exporting system debug log
|
||||||
### 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
|
|
||||||
|
|
||||||
## [5.4.0-beta.1] 2024-07-16
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
- Errors being printed to the console when Exit button was explicitly clicked
|
||||||
|
- Detect Steam Deck OLED APU variant during initial setup
|
||||||
- Encapsulate player names correctly so that names with whitespace in them are supported
|
- Encapsulate player names correctly so that names with whitespace in them are supported
|
||||||
|
- 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.3.2] 2024-07-02
|
## [5.3.2] 2024-07-02
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
+5
-3
@@ -50,8 +50,10 @@ 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>`.
|
||||||
All new features and fixes will be merged to this branch, so open your branch
|
If there is no pending prerelease PR, you can base your changes off of
|
||||||
from here.
|
the `testing` branch. You may later be asked to retarget your PR to the prerelease
|
||||||
|
branch once a new one is made. This is because out-of-band PRs are consolidated into
|
||||||
|
the next release before merging.
|
||||||
|
|
||||||
The following naming conventions apply for PRs:
|
The following naming conventions apply for PRs:
|
||||||
- fix/<your-fix> - patch/hotfix branches
|
- fix/<your-fix> - patch/hotfix branches
|
||||||
@@ -61,7 +63,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
|
||||||
|
|||||||
@@ -11,6 +11,15 @@ 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:
|
||||||
@@ -18,7 +27,6 @@ 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)
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Attribution
|
## Attribution
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
version=6.0.0.beta-8
|
src_path="$(readlink -e "$0")"
|
||||||
|
version=6.0.2
|
||||||
|
|
||||||
#CONSTANTS
|
#CONSTANTS
|
||||||
aid=221100
|
aid=221100
|
||||||
@@ -39,6 +40,7 @@ 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"
|
||||||
@@ -115,6 +117,7 @@ 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=(
|
||||||
@@ -136,10 +139,10 @@ print_config_vals(){
|
|||||||
|
|
||||||
}
|
}
|
||||||
test_gobject(){
|
test_gobject(){
|
||||||
python3 -c "import gi"
|
python3.13 -c "import gi"
|
||||||
if [[ ! $? -eq 0 ]]; then
|
if [[ ! $? -eq 0 ]]; then
|
||||||
logger CRITICAL "Missing PyGObject"
|
logger CRITICAL "Missing PyGObject"
|
||||||
fdialog "Requires PyGObject (python-gobject)"
|
quit_with_help_dialog "Requires PyGObject (python-gobject)"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
logger INFO "Found PyGObject in Python env"
|
logger INFO "Found PyGObject in Python env"
|
||||||
@@ -218,16 +221,14 @@ 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]}"
|
||||||
raise_error_and_quit "$msg"
|
echo "$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"
|
||||||
@@ -236,14 +237,31 @@ 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(){
|
||||||
local pyver=$(python3 --version | awk '{print $2}')
|
if [[ ! $(python3.13 --version) ]]; then
|
||||||
local minor=$(<<< $pyver awk -F. '{print $2}')
|
local msg="Requires Python 3.13"
|
||||||
if [[ -z $pyver ]] || [[ ${pyver:0:1} -lt 3 ]] || [[ $minor -lt 10 ]]; then
|
quit_with_help_dialog "$msg"
|
||||||
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
|
||||||
@@ -317,6 +335,8 @@ 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")
|
||||||
@@ -380,10 +400,11 @@ 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 md="$url_prefix/${mdbranch}/$file"
|
local changelog="$url_prefix/${mdbranch}/CHANGELOG.md"
|
||||||
curl -Ls "$md" > "$state_path/CHANGELOG.md"
|
curl -Ls "$changelog" > "$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"
|
||||||
@@ -394,6 +415,7 @@ 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
|
||||||
@@ -410,6 +432,7 @@ 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
|
||||||
@@ -459,6 +482,7 @@ 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)
|
||||||
@@ -467,33 +491,15 @@ steam_deps(){
|
|||||||
local msg="Found neither Steam nor Flatpak Steam"
|
local msg="Found neither Steam nor Flatpak Steam"
|
||||||
raise_error_and_quit "$msg"
|
raise_error_and_quit "$msg"
|
||||||
exit 1
|
exit 1
|
||||||
elif [[ -n "$steam" ]] && [[ -n "$flatpak" ]]; then
|
|
||||||
[[ -n $preferred_client ]] && return 0
|
|
||||||
if [[ -z $preferred_client ]]; then
|
|
||||||
preferred_client="steam"
|
|
||||||
fi
|
fi
|
||||||
elif [[ -n "$steam" ]]; then
|
|
||||||
preferred_client="steam"
|
|
||||||
else
|
|
||||||
preferred_client="flatpak"
|
|
||||||
fi
|
|
||||||
update_config
|
|
||||||
logger INFO "Preferred client set to '$preferred_client'"
|
|
||||||
}
|
|
||||||
migrate_files(){
|
|
||||||
if [[ ! -f $config_path/dztuirc.oldapi ]]; then
|
|
||||||
cp $config_file $config_path/dztuirc.oldapi
|
|
||||||
logger INFO "Migrated old API file"
|
|
||||||
fi
|
|
||||||
[[ ! -f $hist_file ]] && return
|
|
||||||
rm $hist_file
|
|
||||||
logger INFO "Wiped old history file"
|
|
||||||
}
|
}
|
||||||
stale_symlinks(){
|
stale_symlinks(){
|
||||||
|
echo "# Cleaning stale symlinks"
|
||||||
local game_dir="$steam_path/steamapps/common/DayZ"
|
local game_dir="$steam_path/steamapps/common/DayZ"
|
||||||
for l in $(find "$game_dir" -xtype l); do
|
readarray -t links < <(find "$game_dir" -xtype l)
|
||||||
logger DEBUG "Updating stale symlink '$l'"
|
for link in "${links[@]}"; do
|
||||||
unlink "$l"
|
logger DEBUG "Updating stale symlink '$link'"
|
||||||
|
unlink "$link"
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,7 +508,7 @@ check_availability() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
local url=$1
|
local url=$1
|
||||||
local timeout_sec="3"
|
local timeout_sec="2"
|
||||||
if [[ $2 ]]; then
|
if [[ $2 ]]; then
|
||||||
timeout_sec=$2
|
timeout_sec=$2
|
||||||
fi
|
fi
|
||||||
@@ -513,6 +519,7 @@ 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
|
||||||
@@ -524,6 +531,9 @@ 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."
|
||||||
@@ -538,6 +548,7 @@ 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
|
||||||
@@ -615,12 +626,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"]="6ae3ead7034dc8e7543472bddee75c63"
|
["funcs"]="5523d933e9f0668fc14b3faf0d4aafd8"
|
||||||
["query_v2.py"]="55d339ba02512ac69de288eb3be41067"
|
["query_v2.py"]="55d339ba02512ac69de288eb3be41067"
|
||||||
["servers.py"]="ed442c3aecf33f777d59dcf53650d263"
|
["servers.py"]="ed442c3aecf33f777d59dcf53650d263"
|
||||||
["ui.py"]="f03879f088ad074f2ce3c92607c30202"
|
["ui.py"]="d09b9d8bed3854efd51377289786c5ac"
|
||||||
["vdf2json.py"]="2f49f6f5d3af919bebaab2e9c220f397"
|
["vdf2json.py"]="2f49f6f5d3af919bebaab2e9c220f397"
|
||||||
["pefile.py"]="21531f2c0d9dfa5f110cf6779f9d22c0"
|
["pefile.py"]="b452974a84bff1d821872fcebf59e380"
|
||||||
)
|
)
|
||||||
local author="aclist"
|
local author="aclist"
|
||||||
local repo="dztui"
|
local repo="dztui"
|
||||||
@@ -639,6 +650,10 @@ 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]}"
|
||||||
@@ -676,15 +691,12 @@ fetch_km_helper(){
|
|||||||
}
|
}
|
||||||
get_response_code(){
|
get_response_code(){
|
||||||
local url="$1"
|
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"
|
||||||
}
|
|
||||||
raise_error_and_quit(){
|
|
||||||
echo "$1"
|
|
||||||
exit 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch_ip_db(){
|
fetch_ip_db(){
|
||||||
parse_dl_url(){
|
parse_dl_url(){
|
||||||
curl -Ls "$url" \
|
curl --connect-timeout 3 -Ls "$url" \
|
||||||
| grep "csv.gz" \
|
| grep "csv.gz" \
|
||||||
| awk -F"['']" '{print $2}'
|
| awk -F"['']" '{print $2}'
|
||||||
}
|
}
|
||||||
@@ -700,6 +712,9 @@ 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
|
||||||
@@ -719,7 +734,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}/${month}.csv"
|
rm "${state_path}/${this_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
|
||||||
@@ -734,16 +749,13 @@ fetch_ip_db(){
|
|||||||
rm "$ip_file"
|
rm "$ip_file"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "$month" > "$month_file"
|
echo "$this_month" > "$month_file"
|
||||||
logger INFO "Wrote '$month' to stub '$month_file'"
|
logger INFO "Wrote '$this_month' to stub '$month_file'"
|
||||||
logger INFO "Updated '$ip_file'"
|
logger INFO "Updated '$ip_file'"
|
||||||
}
|
}
|
||||||
|
|
||||||
local url="https://db-ip.com/db/download/ip-to-city-lite"
|
check_remote(){
|
||||||
local month_file="${state_path}/.month"
|
local url="$1"
|
||||||
local ip_file="${state_path}/ips.csv"
|
|
||||||
|
|
||||||
# test main url
|
|
||||||
local res=$(get_response_code "$url")
|
local res=$(get_response_code "$url")
|
||||||
if [[ $res -ne 200 ]]; then
|
if [[ $res -ne 200 ]]; then
|
||||||
logger WARN "Failed to retrieve remote resource: '$url' ($res)"
|
logger WARN "Failed to retrieve remote resource: '$url' ($res)"
|
||||||
@@ -759,30 +771,35 @@ fetch_ip_db(){
|
|||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
logger INFO "Resolved download URL: '$dl_url'"
|
logger INFO "Resolved download URL: '$dl_url'"
|
||||||
|
fetch "$dl_url"
|
||||||
|
}
|
||||||
|
|
||||||
local month=$(parse_dl_url_date "$dl_url")
|
local url="https://db-ip.com/db/download/ip-to-city-lite"
|
||||||
local base_file="${state_path}/${month}.csv.gz"
|
month_file="${state_path}/.month"
|
||||||
local extracted_file="${state_path}/${month}.csv"
|
this_month=$(date +%Y-%m)
|
||||||
|
|
||||||
# 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"
|
||||||
fetch "$dl_url"
|
check_remote "$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"
|
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# if stub is same date, abort
|
# 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"
|
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(){
|
fetch_helpers(){
|
||||||
|
echo "# Checking helper files"
|
||||||
fetch_a2s
|
fetch_a2s
|
||||||
fetch_dzq
|
fetch_dzq
|
||||||
fetch_km_helper
|
fetch_km_helper
|
||||||
@@ -881,12 +898,26 @@ file_picker(){
|
|||||||
}
|
}
|
||||||
find_library_folder(){
|
find_library_folder(){
|
||||||
local search_path="$1"
|
local search_path="$1"
|
||||||
steam_path="$(python3 "$helpers_path/vdf2json.py" -i "$1/steamapps/libraryfolders.vdf" \
|
readarray -t paths < <(python3.13 "$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 ]] || [[ -z $steam_path ]]; then
|
if [[ ! $? -eq 0 ]]; 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(){
|
||||||
@@ -895,6 +926,7 @@ 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 \
|
||||||
@@ -955,6 +987,7 @@ 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
|
||||||
@@ -968,7 +1001,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"
|
||||||
@@ -979,18 +1012,16 @@ 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"
|
||||||
@@ -1003,6 +1034,7 @@ 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
|
||||||
@@ -1036,6 +1068,7 @@ 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
|
||||||
@@ -1049,18 +1082,48 @@ 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 > >(pdialog "Checking helper files")
|
fetch_helpers
|
||||||
varcheck
|
varcheck
|
||||||
source "$config_file"
|
source "$config_file"
|
||||||
lock
|
lock
|
||||||
@@ -1069,14 +1132,12 @@ 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(){
|
||||||
@@ -1178,11 +1239,18 @@ main(){
|
|||||||
|
|
||||||
set_im_module
|
set_im_module
|
||||||
|
|
||||||
printf "Initializing setup...\n"
|
printf "Checking dependencies...\n"
|
||||||
initial_setup
|
setup_dirs
|
||||||
|
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 "$ui_helper" "--init-ui" "$version" "$is_steam_deck"
|
python3.13 "$ui_helper" "--init-ui" "$version" "$is_steam_deck"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-20
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
version="6.0.0-beta.8"
|
version="6.0.2"
|
||||||
|
|
||||||
#CONSTANTS
|
#CONSTANTS
|
||||||
aid=221100
|
aid=221100
|
||||||
@@ -51,6 +51,7 @@ _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"
|
||||||
@@ -87,17 +88,14 @@ 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"
|
||||||
|
|
||||||
if [[ $preferred_client == "steam" ]]; then
|
steam_cmd="$preferred_client"
|
||||||
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"
|
||||||
@@ -288,7 +286,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 "$query_helper" "$ip" "$qport" "$mode")
|
res=$(python3.13 "$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
|
||||||
@@ -311,7 +309,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 "$helpers_path/vdf2json.py" \
|
local res=$(python3.13 "$helpers_path/vdf2json.py" \
|
||||||
-i "$file" \
|
-i "$file" \
|
||||||
| jq -r '.users
|
| jq -r '.users
|
||||||
|to_entries[]
|
|to_entries[]
|
||||||
@@ -340,7 +338,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 -c "n=($size/1024) +.005; print(round(n,4))")
|
size=$(python3.13 -c "n=($size/1024) +.005; print(round(n,4))")
|
||||||
LC_NUMERIC=C printf "%s$sep%s$sep%s$sep%3.3f\n" "$name" "$symlink" "$base_dir" "$size"
|
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
|
||||||
@@ -399,6 +397,7 @@ 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=(
|
||||||
@@ -424,6 +423,7 @@ query_config(){
|
|||||||
echo "${!i}"
|
echo "${!i}"
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
align_versions_file(){
|
align_versions_file(){
|
||||||
shift
|
shift
|
||||||
local mod="$1"
|
local mod="$1"
|
||||||
@@ -589,9 +589,6 @@ 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(){
|
||||||
@@ -645,6 +642,7 @@ 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=$?
|
||||||
@@ -701,13 +699,6 @@ 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"
|
||||||
@@ -806,6 +797,9 @@ 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
|
||||||
@@ -831,7 +825,6 @@ 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(){
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
#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
@@ -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:
|
||||||
return e
|
raise 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
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
latlon
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
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)
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
// 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;
|
||||||
|
}
|
||||||
+129
-123
@@ -18,7 +18,8 @@ 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 typing import Literal, Self, Any
|
from pathlib import Path
|
||||||
|
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
|
||||||
@@ -31,10 +32,12 @@ 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
|
||||||
|
|
||||||
@@ -78,7 +81,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"{state_path}/{app_name_abbr}.notes.json"
|
notes_file = f"{config_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"
|
||||||
@@ -823,7 +826,6 @@ 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
|
||||||
finally:
|
|
||||||
return rows
|
return rows
|
||||||
|
|
||||||
|
|
||||||
@@ -866,13 +868,14 @@ 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 '{msg}'")
|
logger.info(f"User confirmed dialog with message '{clean_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 '{msg}'")
|
logger.info(f"User aborted dialog with message '{clean_msg}'")
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -1016,7 +1019,7 @@ def process_tree_option(choice: RowType) -> None:
|
|||||||
return
|
return
|
||||||
|
|
||||||
if command == RowType.CHANGELOG:
|
if command == RowType.CHANGELOG:
|
||||||
App.grid.notebook.set_page_by_enum(NotebookPage.CHANGELOG)
|
App.grid.notebook.open_changelog()
|
||||||
return
|
return
|
||||||
|
|
||||||
if command == RowType.QUICK_CONNECT:
|
if command == RowType.QUICK_CONNECT:
|
||||||
@@ -1026,13 +1029,7 @@ def process_tree_option(choice: RowType) -> None:
|
|||||||
return
|
return
|
||||||
|
|
||||||
record = str_to_record(record)
|
record = str_to_record(record)
|
||||||
thread_new_with_dialog(
|
prepare_connection(command, record)
|
||||||
App.treeview.prepare_connection,
|
|
||||||
parse_shell_output,
|
|
||||||
"Querying server",
|
|
||||||
command,
|
|
||||||
[record],
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
match command.dict["type"]:
|
match command.dict["type"]:
|
||||||
@@ -1060,48 +1057,54 @@ 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 thread_new_with_dialog(
|
def prepare_connection(rowtype: RowType, record: Record) -> None:
|
||||||
func: Callable,
|
def background(rowtype: RowType, record: Record) -> None:
|
||||||
callback: Callable | None,
|
def cleanup() -> None:
|
||||||
msg: str,
|
App.treeview.wait_dialog.destroy()
|
||||||
row: RowType | None,
|
# NOTE: When using RowType.CONN_BY_IP, the gameport needs to be interpolated
|
||||||
args: list,
|
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:
|
) -> 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()
|
||||||
if exception is not None:
|
parse_shell_output(proc, row)
|
||||||
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 = func(*args)
|
proc = call_out("try_connect", addr, appid, path)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
exception = e
|
logger.critical(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=(args))
|
thread = threading.Thread(target=background, args=(addr, appid, path))
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
|
|
||||||
@@ -1141,27 +1144,31 @@ 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:
|
||||||
def _prep(response: str) -> None:
|
try:
|
||||||
record = Servers.validate_ip(response)
|
record = Servers.validate_ip(response)
|
||||||
proc = App.treeview.prepare_connection(record)
|
except Exception as e:
|
||||||
return proc
|
spawn_dialog(str(e), Popup.NOTIFY)
|
||||||
|
|
||||||
thread_new_with_dialog(
|
|
||||||
_prep, parse_shell_output, "Querying IP", enum, [response]
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
prepare_connection(enum, record)
|
||||||
|
|
||||||
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:
|
||||||
record = Servers.query_bm_api(key, response)
|
def cleanup() -> None:
|
||||||
proc = App.treeview.prepare_connection(record)
|
App.treeview.dialog_hide()
|
||||||
return proc
|
prepare_connection(enum, record)
|
||||||
|
|
||||||
thread_new_with_dialog(
|
try:
|
||||||
_prep, parse_shell_output, "Querying API", enum, [key, response]
|
record = Servers.query_bm_api(key, response)
|
||||||
)
|
except Exception as e:
|
||||||
|
spawn_dialog(str(e), Popup.NOTIFY)
|
||||||
|
App.treeview.dialog_hide()
|
||||||
return
|
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:
|
||||||
@@ -1486,6 +1493,8 @@ 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()
|
||||||
@@ -2490,6 +2499,8 @@ 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(
|
||||||
@@ -2940,9 +2951,9 @@ class TreeView(Gtk.TreeView):
|
|||||||
def get_view(self):
|
def get_view(self):
|
||||||
return self.view
|
return self.view
|
||||||
|
|
||||||
def prepare_connection(
|
def get_prereqs(
|
||||||
self, record: Record
|
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
|
Always called on a thread with a dialog on the transient parent window
|
||||||
"""
|
"""
|
||||||
@@ -2950,8 +2961,7 @@ 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"
|
||||||
spawn_dialog(msg, Popup.NOTIFY)
|
return (False, msg, None, prereqs)
|
||||||
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]
|
||||||
@@ -2961,8 +2971,7 @@ 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."
|
||||||
spawn_dialog(msg, Popup.NOTIFY)
|
return (False, msg, None, prereqs)
|
||||||
return None
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
pefile_path = PeFile.get_pefile_path(steam_path, prereqs.appid)
|
pefile_path = PeFile.get_pefile_path(steam_path, prereqs.appid)
|
||||||
@@ -2976,8 +2985,7 @@ 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."
|
||||||
)
|
)
|
||||||
spawn_dialog(msg, Popup.NOTIFY)
|
return (False, msg, None, prereqs)
|
||||||
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}'"
|
||||||
@@ -2987,13 +2995,11 @@ 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."
|
||||||
)
|
)
|
||||||
spawn_dialog(msg, Popup.NOTIFY)
|
return (False, msg, None, prereqs)
|
||||||
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."
|
||||||
spawn_dialog(msg, Popup.NOTIFY)
|
return (False, msg, None, prereqs)
|
||||||
return None
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
local_vers = PeFile.get_dayz_version(pefile_path)
|
local_vers = PeFile.get_dayz_version(pefile_path)
|
||||||
@@ -3005,7 +3011,7 @@ class TreeView(Gtk.TreeView):
|
|||||||
local_vers = None
|
local_vers = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
remote_vers = PeFile.dayz_version_from_str(prereqs.version)
|
remote_vers = PeFile.dayz_version_from_str(prereqs)
|
||||||
except Exception:
|
except Exception:
|
||||||
remote_vers = None
|
remote_vers = None
|
||||||
|
|
||||||
@@ -3018,18 +3024,15 @@ 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?"
|
||||||
)
|
)
|
||||||
res = spawn_dialog(msg, Popup.CONFIRM)
|
return (True, msg, pefile_path, prereqs)
|
||||||
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?"
|
||||||
)
|
)
|
||||||
res = spawn_dialog(msg, Popup.CONFIRM)
|
return (True, msg, pefile_path, prereqs)
|
||||||
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:
|
||||||
@@ -3037,19 +3040,10 @@ 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?"
|
||||||
)
|
)
|
||||||
res = spawn_dialog(msg, Popup.CONFIRM)
|
return (True, msg, pefile_path, prereqs)
|
||||||
if res is True:
|
|
||||||
return None
|
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
|
@signal_emission
|
||||||
@update_window_labels
|
@update_window_labels
|
||||||
@@ -3119,13 +3113,7 @@ class TreeView(Gtk.TreeView):
|
|||||||
record = self.get_record()
|
record = self.get_record()
|
||||||
if record is None:
|
if record is None:
|
||||||
return
|
return
|
||||||
thread_new_with_dialog(
|
prepare_connection(None, record)
|
||||||
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)
|
||||||
|
|
||||||
@@ -3801,12 +3789,22 @@ class Options(Gtk.Box):
|
|||||||
"Always fullscreen",
|
"Always fullscreen",
|
||||||
Preferences.WINDOW,
|
Preferences.WINDOW,
|
||||||
)
|
)
|
||||||
self.steam_toggle = self.make_binary_radio(
|
|
||||||
"Steam", "Flatpak (experimental)", Preferences.CLIENT
|
# TODO: gray out options if not available on system
|
||||||
)
|
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.steam_toggle],
|
[LeftLabel("Steam client"), self.client_combo],
|
||||||
[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],
|
||||||
]
|
]
|
||||||
@@ -3991,6 +3989,15 @@ 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:
|
||||||
@@ -4000,11 +4007,9 @@ 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 None:
|
if App.treeview.subpage is not RowType.OPTIONS:
|
||||||
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()
|
||||||
@@ -4126,11 +4131,9 @@ 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)
|
||||||
|
|
||||||
if client == "flatpak":
|
for ind, row in enumerate(self.client_combo.get_model()):
|
||||||
radio = 1
|
if row[1] == client:
|
||||||
else:
|
self.client_combo.set_active(ind)
|
||||||
radio = 0
|
|
||||||
self.steam_toggle.get_children()[radio].set_active(True)
|
|
||||||
|
|
||||||
if fullscreen == "true":
|
if fullscreen == "true":
|
||||||
radio = 1
|
radio = 1
|
||||||
@@ -4325,22 +4328,20 @@ 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:
|
||||||
with open(changelog_path, "r") as f:
|
changelog = path.read_text()
|
||||||
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.WARN)
|
spawn_dialog(f"Something went wrong: {e}", Popup.NOTIFY)
|
||||||
logger.critical(e)
|
logger.critical(e)
|
||||||
return
|
return Exception
|
||||||
|
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>'
|
||||||
@@ -4357,7 +4358,8 @@ 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.clog = ScrollableNote(Changelog())
|
self.changelog = 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)
|
||||||
@@ -4376,6 +4378,10 @@ 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()
|
||||||
|
|||||||
Reference in new issue
Block a user