1
0
Fork 0
mirror of https://github.com/aclist/dztui.git synced 2025-04-03 02:53:00 +02:00

Compare commits

...

123 commits

Author SHA1 Message Date
aclist
2ba07bbba8
Merge pull request #197 from aclist/dzgui
All checks were successful
Mirror to Codeberg / mirror-to-codeberg (push) Successful in 28s
chore: merge stable onto archival branch
2025-02-09 19:55:27 +09:00
aclist
65eda05d9f
Merge pull request #200 from aclist/chore/templates-stable
All checks were successful
Mirror to Codeberg / mirror-to-codeberg (push) Successful in 22s
chore: back up templates from stable
2025-02-09 19:54:17 +09:00
aclist
f3b3f79bfb chore: back up templates from main 2025-02-09 19:53:25 +09:00
aclist
28c7d7f949
Merge pull request #198 from aclist/fix/drop-files
chore: drop extraneous file
2025-02-09 19:02:02 +09:00
aclist
d072c8107e chore: drop extraneous file 2025-02-09 19:01:30 +09:00
aclist
0629c8a6ba
Merge pull request #196 from aclist/release/5.6.3
chore: update IP db
2025-02-09 18:56:13 +09:00
aclist
8aea4af687 chore: update IP db 2025-02-09 18:55:33 +09:00
aclist
0562d8230d
Merge pull request #195 from aclist/release/5.6.2
All checks were successful
Mirror to Codeberg / mirror-to-codeberg (push) Successful in 10s
chore: prepare release
2025-01-24 16:40:07 +09:00
aclist
759327e932 chore: prepare release 2025-01-23 12:02:27 +09:00
aclist
36753e02f1
Merge pull request #193 from aclist/release/5.6.1
All checks were successful
Mirror to Codeberg / mirror-to-codeberg (push) Successful in 17s
Release/5.6.1
2025-01-10 22:48:54 +09:00
aclist
2ab2e23736 docs: update readme 2025-01-10 22:45:56 +09:00
aclist
34e7b2e480 fix: jq 1.6 support 2025-01-10 22:45:25 +09:00
aclist
3ab666a807
Merge pull request #190 from aclist/release/5.6.0
All checks were successful
Mirror to Codeberg / mirror-to-codeberg (push) Successful in 21s
Release/5.6.0
2025-01-07 21:46:53 +09:00
aclist
c8f7a32284 chore: update db checksum
All checks were successful
Mirror to Codeberg / mirror-to-codeberg (push) Successful in 25s
2025-01-07 21:39:16 +09:00
aclist
a070e9fc65 chore: drop unused log file 2025-01-07 20:54:03 +09:00
aclist
5f55e3c664 Merge branch 'release/5.6.0' of github.com:aclist/dztui into release/5.6.0 2025-01-07 20:52:58 +09:00
aclist
07606d8852 docs: update changelog 2025-01-07 20:51:57 +09:00
aclist
3f1d8cb24f
Merge branch 'dzgui' into release/5.6.0 2025-01-07 00:59:57 +09:00
aclist
4df12605fc chore: update changelog 2025-01-07 00:55:58 +09:00
aclist
d7b1d72042
Merge pull request #184 from aclist/release/5.6.0-beta.21
All checks were successful
Mirror to Codeberg / mirror-to-codeberg (push) Successful in 23s
feat: add tooltips
2025-01-07 00:45:46 +09:00
aclist
83fb021aa9 docs: update changelog 2025-01-07 00:44:20 +09:00
aclist
54a7c7976c change: update URL 2025-01-07 00:33:58 +09:00
aclist
0e18839024 fix: dict, not list 2025-01-06 23:30:14 +09:00
aclist
c9c8337c3b chore: update image 2025-01-06 22:58:01 +09:00
aclist
2d7ab3aec6 docs: update readme 2025-01-06 22:16:21 +09:00
aclist
adb921dc93 chore: update changelog 2025-01-06 22:09:18 +09:00
aclist
2b4649ac6e chore: drop docs 2025-01-06 22:08:48 +09:00
aclist
5da4d16ed8 feat: add tooltips 2025-01-06 17:23:38 +09:00
aclist
2158635e64 chore: add gitignore
All checks were successful
Mirror to Codeberg / mirror-to-codeberg (push) Successful in 10s
2025-01-06 17:04:12 +09:00
aclist
8817a5db23 chore: add YAML file
All checks were successful
Mirror to Codeberg / mirror-to-codeberg (push) Successful in 10s
2024-12-25 14:22:39 +09:00
aclist
f60a6edf2b
Merge pull request #182 from aclist/patch/yaml
All checks were successful
Mirror to Codeberg / mirror-to-codeberg (push) Successful in 26s
Patch/yaml
2024-12-25 14:21:49 +09:00
aclist
8c33a1b371 chore: drop branch logic 2024-12-25 14:21:24 +09:00
aclist
e5f7391b89 chore: add YAML file 2024-12-25 14:20:36 +09:00
aclist
7d2bb7ddc5
Merge pull request #181 from aclist/release/5.6.0-beta.20
fix: more performant link traversal
2024-12-23 21:39:17 +09:00
aclist
3b5960cb16 fix: enclose paths 2024-12-23 21:38:15 +09:00
aclist
3cd99911a6 chore: remove stray line 2024-12-23 21:00:18 +09:00
aclist
4e968e63e5 fix: more performant link traversal 2024-12-23 20:58:21 +09:00
aclist
21e9e140d1
Merge pull request #180 from aclist/release/5.6.0-beta.19
Release/5.6.0-beta.19
2024-12-20 10:35:43 +09:00
aclist
973e2dc100 fix: shorten symlink check time 2024-12-19 10:21:44 +09:00
aclist
bdea37ddc4 chore: cleanup 2024-12-18 20:57:09 +09:00
aclist
9b2ec79aa5 feat: redact usernames 2024-12-18 20:56:14 +09:00
aclist
4a95316412 feat: redact usernames 2024-12-18 20:55:01 +09:00
aclist
99d964bf44 chore: bump version 2024-12-18 18:22:41 +09:00
aclist
471cfec94b
Merge pull request #174 from jiriks74/testing
fix(checks): Check `vm.max_map_count` using `cat`
2024-12-18 18:19:48 +09:00
aclist
717070ec40
Merge pull request #177 from aclist/release/5.6.0-beta.18
feat: open workshop subscriptions
2024-12-18 17:29:29 +09:00
aclist
a18d68776f chore: add logging 2024-12-18 17:26:31 +09:00
c73120295a
fix(checks): Check vm.max_map_count using cat
This solves multiple issues:
- We don't need `sudo` to check the value anymore
- Some systems may not have `sysctl` available
  - IDK about desktops, but my Debian server doesn't have this command
  (for whatever reason)
2024-12-17 15:54:17 +01:00
aclist
6e3746e9b4 fix: abort path discovery 2024-12-16 15:21:57 +09:00
aclist
34b3d3bc8c chore: drop unused var 2024-12-16 14:49:55 +09:00
aclist
4d3d9bf0bb feat: open workshop subscriptions 2024-12-15 09:08:35 +09:00
aclist
e6b5e40bb2 chore: add logging 2024-12-15 08:57:25 +09:00
aclist
c11511abff Merge pull request 'chore: backports into stable' (#5) from release/testing-backports into dzgui
Reviewed-on: https://codeberg.org/aclist/dztui/pulls/5
2024-12-13 01:50:01 +00:00
aclist
bda7223ac8
Merge pull request #176 from aclist/release/testing-backports
Release/testing backports
2024-12-13 10:47:54 +09:00
aclist
6b48ee127d chore: backports into stable 2024-12-13 10:45:39 +09:00
aclist
822db9edfe
Merge pull request #175 from aclist/release/cb-backports
Release/cb backports
2024-12-13 10:30:20 +09:00
aclist
3fce2cc3ad fix: return codes 2024-12-13 10:18:56 +09:00
aclist
4da78d33f3 fix: clean up URLs 2024-12-13 09:58:44 +09:00
aclist
5d6cbaa1a5 chore: bump version 2024-12-13 09:51:31 +09:00
aclist
d4d0593927 fix: add fallback logic to installscript 2024-12-13 09:49:39 +09:00
aclist
cbecf569ad fix: add fallbacks and remote resource checks 2024-12-13 09:40:46 +09:00
aclist
aa8a52bfc0 Merge pull request 'release/readme' (#4) from release/readme into dzgui
Reviewed-on: https://codeberg.org/aclist/dztui/pulls/4
2024-12-12 09:33:32 +00:00
aclist
8ea62434ee docs: update docs link 2024-12-12 18:32:44 +09:00
aclist
c5785d1857 docs: update docs link 2024-12-12 18:31:58 +09:00
aclist
baeaeb1b64 docs: update README 2024-12-12 17:57:27 +09:00
aclist
32b424da5c fix: update installscript 2024-12-12 12:25:26 +09:00
aclist
d71110d0a1 fix: url syntax 2024-12-12 12:10:04 +09:00
aclist
62e6da2abb fix: url syntax 2024-12-12 12:08:57 +09:00
aclist
bc9178d2de fix: triage hotfix from testing 2024-12-12 11:56:10 +09:00
aclist
12b1c392df fix: initial setup 2024-12-12 11:50:18 +09:00
aclist
1be68e7be3 fix: enable redirects 2024-12-12 11:38:16 +09:00
aclist
33b2aa85df Merge pull request 'fix: Set up alternate host' (#2) from release/triage into testing
Reviewed-on: https://codeberg.org/aclist/dztui/pulls/2
2024-12-12 02:33:22 +00:00
aclist
4b95033c19 chore: add comments 2024-12-12 11:31:32 +09:00
aclist
ea0d94a2fc fix: interpolate old vars 2024-12-12 11:25:50 +09:00
aclist
7c5bd76e8f fix: test connection control flow 2024-12-12 11:16:46 +09:00
aclist
a079d554bc chore: bump version 2024-12-12 11:13:48 +09:00
aclist
0197e0f921 fix: conditional remote url logic 2024-12-12 11:08:07 +09:00
aclist
01081a29e7 Merge branch 'release/triage' of codeberg.org:aclist/dzgui into release/triage 2024-12-12 10:27:52 +09:00
aclist
1da2e72928 fix: update installscript 2024-12-12 10:27:38 +09:00
aclist
f503c89c2b fix: line wrapping 2024-12-12 00:58:56 +00:00
aclist
eaf9d5ad3e fix: change remote URLs 2024-12-12 09:54:44 +09:00
aclist
8dc9ad3313 fix: add missing function definition 2024-12-07 14:02:50 +09:00
aclist
3adac2a211
Merge pull request #173 from aclist/release/5.6.0-beta.10
Release/5.6.0-beta.10
2024-12-05 08:25:19 +09:00
aclist
8fdc29d47d chore: bump version 2024-12-05 08:23:51 +09:00
aclist
336b988282 fix: untoggle highlight button (#169, 170) 2024-12-05 08:15:45 +09:00
aclist
6d332f613e chore: guard clause 2024-12-05 08:13:29 +09:00
aclist
a46c2e17ab chore: remove unused values 2024-12-05 08:12:20 +09:00
aclist
13c6813c8e feat: resolve ip when saving records 2024-12-05 08:11:39 +09:00
aclist
43a639005f chore: move comments 2024-12-05 08:10:31 +09:00
aclist
75e247b831 fix: remove return values from parse_server_rows() 2024-12-05 08:10:08 +09:00
aclist
5619d55840 fix: update statusbar when removing rows 2024-12-05 08:09:25 +09:00
aclist
a4f076fbdb chore: clarify comments 2024-12-05 08:07:16 +09:00
aclist
eaa6c11543 fix: typo 2024-12-05 08:06:46 +09:00
aclist
ef3fffca47 change: use printf 2024-12-05 08:04:29 +09:00
aclist
ac3055ce39 fix: non blocking workshop open 2024-12-05 08:04:00 +09:00
aclist
0fdc132873 fix: array syntax 2024-12-05 08:03:04 +09:00
aclist
6b669e5c21 fix: reset window context on cooldown 2024-12-03 21:41:54 +09:00
aclist
a94859a157 change: use LC_NUMERIC 2024-12-03 20:12:18 +09:00
aclist
1533febf9a
Merge pull request #172 from aclist/release/5.5.1
fix: backport hotfix (#168)
2024-12-03 20:10:29 +09:00
aclist
c3c8872fad fix: backport hotfix (#168) 2024-12-03 20:09:19 +09:00
aclist
d5162d6d9e fix: normalize user locale (#168) 2024-11-28 23:25:28 +09:00
aclist
94f9ac52c5 chore: add logging 2024-11-28 22:37:15 +09:00
aclist
e28a75cfd9 fix: installed mods race condition (#167) 2024-11-28 17:43:03 +09:00
aclist
885c3bc7e7 chore: update checksums 2024-11-21 23:58:47 +09:00
aclist
6e375d3f24 fix: drop unused parameter 2024-11-21 23:58:38 +09:00
aclist
08af994361 feat: highlight stale mods (#162) 2024-11-21 23:53:10 +09:00
aclist
e9cd457233 fix: set window context earlier 2024-11-21 19:06:01 +09:00
aclist
fbb133eeb2 chore: uncomment 2024-11-21 17:34:47 +09:00
aclist
9fdc123e2c feat: header bar 2024-11-21 17:28:36 +09:00
aclist
3e9c5a96d9 chore: bump version 2024-11-19 21:51:59 +09:00
aclist
651b50ade2 chore: unify time delta methods 2024-11-19 21:51:43 +09:00
aclist
8b9f751ff1 fix: error handling for no local mods 2024-11-19 21:49:59 +09:00
aclist
32a43cd787 chore: update changelog 2024-11-16 11:37:02 +09:00
aclist
732bc918a7 fix: align local mod signatures 2024-11-16 07:42:52 +09:00
aclist
1b7752588c
Merge pull request #165 from aclist/release/5.6.0.beta-1
Release/5.6.0-beta.1
2024-11-13 09:55:45 +09:00
aclist
2087f4ffa8 chore: update changelog 2024-11-13 09:52:49 +09:00
aclist
d6d8dde2aa feat: iterate mod deletion 2024-11-13 09:43:23 +09:00
aclist
c254de2aec chore: add comments 2024-11-13 09:42:01 +09:00
aclist
fbc500066f fix: focus first row 2024-11-13 09:34:25 +09:00
aclist
365b455b3c feat: bulk mod deletion 2024-11-13 09:34:05 +09:00
aclist
50d6bdf74f feat: add pluralize func 2024-11-13 06:19:37 +09:00
aclist
1eed01623a chore: clarify timeout message 2024-11-13 05:53:44 +09:00
aclist
031df3a62f fix: batch MSQ queries 2024-11-13 05:35:50 +09:00
aclist
0dfd727222 change: use enum for popup mode 2024-11-13 05:33:39 +09:00
23 changed files with 1606 additions and 2343 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
tags

View file

@ -1,5 +1,73 @@
# Changelog
## [5.6.6] 2024-02-09
### Changed
- Update IP database records for 2025-02
## [5.6.2] 2024-01-22
### Fixed
- Resolve regression introduced with IP resolution feature in 5.6.0 (restores functionality of right-click action: Add to My Servers)
## [5.6.1] 2024-01-10
### Fixed
- Add fallback support for jq 1.6
## [5.6.0] 2024-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
- 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 README.md
- Update IP database to 2025-01
- Reduce IP database size by 50%
- Update documentation to 5.6.x standard
### Dropped
- Hall of Fame section from button links, to be moved inside documentation
- Unused imports
## [5.5.3] 2024-12-13
### Fixed
- Add remote resource health checks before downloading updates
### Added
- Add fallback repository
## [5.5.1] 2024-12-03
### Fixed
- Support localized decimal separators when parsing installed mod sizes
## [5.5.0] 2024-11-10
### Added
- Support servers running DLC content (fixes Frostline servers)

View file

@ -1,22 +1,25 @@
## What this is
DZGUI is a GUI version of [DZTUI](https://github.com/aclist/dztui/tree/dztui) for Linux.
DZGUI allows you to connect to both official and modded/community DayZ servers on Linux and provides a graphical interface for doing so.
Note: development of DZTUI has stopped and has been replaced with DZGUI.
This overcomes certain limitations in the Linux client and helps prepare the game to launch by providing features like:
DZGUI allows you to connect to both official and modded/community DayZ servers on Linux and provides a graphical interface for doing so. This overcomes certain limitations in the Linux client and helps prepare the game to launch by doing the following:
1. Search for and display server metadata in a table (server name, player count, ping, current gametime, distance, IP)
2. Add/delete/manage favorite servers by IP or ID
3. Find and prepare mods being requested by the server (choose from manual or automatic installation)
4. Concatenate launch options to pass to Steam
Other options include the ability to connect by IP or ID or set a favorite server.
- Search for and display server metadata in a table (server name, player count, ping, queue size, current gametime, distance, IP)
- Add/delete/manage favorite servers by IP or ID
- Quick-connect to favorite/recent servers
- Find and prepare mods being requested by servers (choose from manual or automatic installation)
- Bulk delete/update local mods
- Concatenate launch options to pass to Steam
- Connect to mod-enabled LAN servers
## Setup and usage
Refer to the [manual](https://aclist.github.io/dzgui/dzgui.html) for installation and setup instructions, a feature-by-feature breakdown, and Steam integration tutorials.
Refer to the documentation for installation and setup instructions:
![Alt text](/images/example.png)
- [GitHub](https://aclist.github.io/dzgui/dzgui.html)
- [Mirror (Codeberg)](https://aclist.codeberg.page)
![A screenshot of DZGUI](/images/example.png)
## Attribution

View file

@ -1,427 +0,0 @@
/*! Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
/* Uncomment the following line when using as a custom stylesheet */
/* @import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"; */
html{font-family:sans-serif;-webkit-text-size-adjust:100%}
h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 {color: #8a1414 !important}
a{background:none}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
b,strong{font-weight:bold}
abbr{font-size:.9em}
abbr[title]{cursor:help;border-bottom:1px dotted #dddddf;text-decoration:none}
dfn{font-style:italic}
hr{height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
audio,video{display:inline-block}
audio:not([controls]){display:none;height:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type=checkbox],input[type=radio]{padding:0}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,::before,::after{box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;line-height:1;position:relative;cursor:auto;-moz-tab-size:4;-o-tab-size:4;tab-size:4;word-wrap:anywhere;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:0}
p{line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0}
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
ul.square{list-style-type:square}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}
table{background:#fff;margin-bottom:1.25em;border:1px solid #dedede;word-wrap:normal}
table thead,table tfoot{background:#f7f8f7}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt{background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{line-height:1.6}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.center{margin-left:auto;margin-right:auto}
.stretch{width:100%}
.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table}
.clearfix::after,.float-group::after{clear:both}
:not(pre).nobreak{word-wrap:normal}
:not(pre).nowrap{white-space:nowrap}
:not(pre).pre-wrap{white-space:pre-wrap}
:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed}
pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}
pre>code{display:block}
pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}
em em{font-style:normal}
strong strong{font-weight:400}
.keyseq{color:rgba(51,51,51,.8)}
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 0 rgba(0,0,0,.2),inset 0 0 0 .1em #fff;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menuref{color:#000}
.menuseq b:not(.caret),.menuref{font-weight:inherit}
.menuseq{word-spacing:-.02em}
.menuseq b.caret{font-size:1.25em;line-height:.8}
.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}
b.button::before,b.button::after{position:relative;top:-1px;font-weight:400}
b.button::before{content:"[";padding:0 3px 0 2px}
b.button::after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin:0 auto;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table}
#header::after,#content::after,#footnotes::after,#footer::after{clear:both}
#content{margin-top:1.25em}
#content::before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:flex;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span::before{content:"\00a0\2013\00a0"}
#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark::before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber::after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc li{line-height:1.3334;margin-top:.3334em}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}}
@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}
#content #toc{border:1px solid #e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:none;background:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:hsla(0,0%,100%,.8);line-height:1.44}
#content{margin-bottom:.625em}
.sect1{padding-bottom:.625em}
@media screen and (min-width:768px){#content{margin-bottom:1.25em}
.sect1{padding-bottom:1.25em}}
.sect1:last-child{padding-bottom:0}
.sect1+.sect1{border-top:1px solid #e7e7e9}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
details{margin-left:1.25rem}
details>summary{cursor:pointer;display:block;position:relative;line-height:1.6;margin-bottom:.625rem;outline:none;-webkit-tap-highlight-color:transparent}
details>summary::-webkit-details-marker{display:none}
details>summary::before{content:"";border:solid transparent;border-left:solid;border-width:.3em 0 .3em .5em;position:absolute;top:.5em;left:-1.25rem;transform:translateX(15%)}
details[open]>summary::before{border:solid transparent;border-top:solid;border-width:.5em .3em 0;transform:translateY(15%)}
details>summary::after{content:"";width:1.25rem;height:1em;position:absolute;top:.3em;left:-1.25rem}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
.paragraph.lead>p,#preamble>.sectionbody>[class=paragraph]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6);word-wrap:anywhere}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border:1px solid #e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;border-radius:4px}
.exampleblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child{margin-bottom:0}
.sidebarblock{border:1px solid #dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;border-radius:4px}
.sidebarblock>:first-child{margin-top:0}
.sidebarblock>:last-child{margin-bottom:0}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock>.content>pre{border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em}
@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}}
@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}}
.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class=highlight],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8}
.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)}
.listingblock>.content{position:relative}
.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5}
.listingblock:hover code[data-lang]::before{display:block}
.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5}
.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.prettyprint{background:#f7f7f8}
pre.prettyprint .linenums{line-height:1.45;margin-left:2em}
pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0}
pre.prettyprint li code[data-lang]::before{opacity:1}
pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none}
table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal}
table.linenotable td.code{padding-left:.75em}
table.linenotable td.linenos,pre.pygments .linenos{border-right:1px solid;opacity:.35;padding-right:.5em;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
pre.pygments span.linenos{display:inline-block;margin-right:.75em}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right}
.verseblock{margin:0 1em 1.25em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans-serif;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}
.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0}
.quoteblock.abstract{margin:0 1em 1.25em;display:block}
.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}
.quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf}
.quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0}
.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem}
.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;font-size:.85rem;text-align:left;margin-right:0}
p.tableblock:last-child{margin-bottom:0}
td.tableblock>.content{margin-bottom:1.25em;word-wrap:anywhere}
td.tableblock>.content>:last-child{margin-bottom:-1.25em}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all>*>tr>*{border-width:1px}
table.grid-cols>*>tr>*{border-width:0 1px}
table.grid-rows>*>tr>*{border-width:1px 0}
table.frame-all{border-width:1px}
table.frame-ends{border-width:1px 0}
table.frame-sides{border-width:0 1px}
table.frame-none>colgroup+*>:first-child>*,table.frame-sides>colgroup+*>:first-child>*{border-top-width:0}
table.frame-none>:last-child>:last-child>*,table.frame-sides>:last-child>:last-child>*{border-bottom-width:0}
table.frame-none>*>tr>:first-child,table.frame-ends>*>tr>:first-child{border-left-width:0}
table.frame-none>*>tr>:last-child,table.frame-ends>*>tr>:last-child{border-right-width:0}
table.stripes-all>*>tr,table.stripes-odd>*>tr:nth-of-type(odd),table.stripes-even>*>tr:nth-of-type(even),table.stripes-hover>*>tr:hover{background:#f8f8f7}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}
ul.unstyled,ol.unstyled{margin-left:0}
li>p:empty:only-child::before{content:"";display:inline-block}
ul.checklist>li>p:first-child{margin-left:-1em}
ul.checklist>li>p:first-child>.fa-square-o:first-child,ul.checklist>li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
ul.checklist>li>p:first-child>input[type=checkbox]:first-child{margin-right:.25em}
ul.inline{display:flex;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}
ul.inline>li{margin-left:1.25em}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
td.hdlist2{word-wrap:anywhere}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}
.colist td:not([class]):first-child img{max-width:none}
.colist td:not([class]):last-child{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:4px solid #fff;box-shadow:0 0 0 1px #ddd}
.imageblock.left{margin:.25em .625em 1.25em 0}
.imageblock.right{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background:#00fafa}
.black{color:#000}
.black-background{background:#000}
.blue{color:#0000bf}
.blue-background{background:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background:#fa00fa}
.gray{color:#606060}
.gray-background{background:#7d7d7d}
.green{color:#006000}
.green-background{background:#007d00}
.lime{color:#00bf00}
.lime-background{background:#00fa00}
.maroon{color:#600000}
.maroon-background{background:#7d0000}
.navy{color:#000060}
.navy-background{background:#00007d}
.olive{color:#606000}
.olive-background{background:#7d7d00}
.purple{color:#600060}
.purple-background{background:#7d007d}
.red{color:#bf0000}
.red-background{background:#fa0000}
.silver{color:#909090}
.silver-background{background:#bcbcbc}
.teal{color:#006060}
.teal-background{background:#007d7d}
.white{color:#bfbfbf}
.white-background{background:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background:#fafa00}
span.icon>.fa{cursor:default}
a span.icon>.fa{cursor:inherit}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);border-radius:50%;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]::after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt,summary{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,span.alt,summary{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@page{margin:1.25cm .75cm}
@media print{*{box-shadow:none!important;text-shadow:none!important}
html{font-size:80%}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]{border-bottom:1px dotted}
abbr[title]::after{content:" (" attr(title) ")"}
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
svg{max-width:100%}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#header,#content,#footnotes,#footer{max-width:none}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span::before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]::before{display:block}
#footer{padding:0 .9375em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
@media amzn-kf8,print{#header>h1:first-child{margin-top:1.25rem}
.sect1{padding:0!important}
.sect1+.sect1{border:0}
#footer{background:none}
#footer-text{color:rgba(0,0,0,.6);font-size:.9em}}
@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}}

View file

@ -1,120 +0,0 @@
/* Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
@import url(//fonts.googleapis.com/css?family=Noto+Sans);
@import url(https://cdn.jsdelivr.net/gh/asciidoctor/asciidoctor@2.0/data/stylesheets/asciidoctor-default.css); /* Default asciidoc style framework - important */
/* CUSTOMISATIONS */
/* Change the values in root for quick customisation. If you want even more fine grain... venture further. */
:root{
--maincolor:#222222;
--primarycolor:#aaa;
--secondarycolor:#aaa;
--tertiarycolor:#aaa;
--sidebarbackground:#222222;
--linkcolor:#ecc89e;
--linkcoloralternate:#cbcbcb;
--white:#777777;
--codebg:#111;
--codefg:#ffffff;
--linkhover:#eb862f;
}
/* Text styles */
body{font-family: "Noto Sans",sans-serif;background-color: var(--maincolor);color:var(--white);}
h1{color:var(--primarycolor) !important;font-family:"Noto Sans",sans-serif;}
h2,h3,h4,h5,h6{color:var(--secondarycolor) !important;font-family:"Noto Sans",sans-serif;}
.title{color:var(--white) !important;font-family:"Noto Sans",sans-serif;font-style: normal; font-weight: normal;}
p{font-family: "Noto Sans",sans-serif ! important}
#toc.toc2 a:link{color:var(--linkcolor);}
#toc.toc2 {border-right: 1px solid #8e8e96}
blockquote{color:var(--tertiarycolor) !important}
.quoteblock{color:var(--white)}
code{color:var(--codefg);background-color: var(--codebg) !important}
td.tableblock{border:0 solid #a9a9a9}
/* Table styles */
th{background-color: var(--maincolor);color:var(--primarycolor) !important;}
td{background-color: var(--maincolor);color: var(--primarycolor) !important}
#toc.toc2{background-color:var(--sidebarbackground);}
#toctitle{color:var(--white);}
/* Responsiveness fixes */
video {
max-width: 100%;
}
@media all and (max-width: 600px) {
table {
width: 55vw!important;
font-size: 3vw;
}
}
.exampleblock > .content {
background-color: var(--maincolor);
}
a {
color: var(--linkcolor);
}
a:hover,#toc.toc2 a:hover{
color: var(--linkhover);
}
.admonitionblock td.icon .icon-tip::before {
text-shadow: none;
color: var(--white);
}
.admonitionblock td.icon .icon-note::before {
color: var(--tertiarycolor);
}
.admonitionblock td.icon .icon-important::before {
color: var(--linkcolor);
}
/*.admonitionblock td.icon .icon-caution::before {
color: var(--linkcoloralternate);
}*/
.admonitionblock td.icon .icon-warning::before {
color: var(--primarycolor);
}
#preamble > .sectionbody > .paragraph:first-of-type p {
color: var(--white);
}
.quoteblock blockquote::before {
color: var(--primarycolor);
}
.quoteblock .attribution cite, .verseblock .attribution cite {
color: var(--white);
}
.verseblock pre {
color: var(--white);
}
.quoteblock blockquote, .quoteblock blockquote p {
color: var(--white);
}
.sidebarblock {
background: var(--sidebarbackground);
}
.literalblock pre, .listingblock pre:not(.highlight), .listingblock pre[class="highlight"], .listingblock pre[class^="highlight "], .listingblock pre.CodeRay, .listingblock pre.prettyprint {
background: var(--codebg);
color: var(--white);
}
.literalblock pre, .listingblock>.content>pre:not(.highlight), .listingblock>.content>pre[class=highlight], .listingblock>.content>pre[class^="highlight "] {
background: var(--codebg);
}
#header .details {
color: var(--white);
}
#header .details span.email a {
color: var(--linkcoloralternate);
}

View file

@ -1,608 +0,0 @@
:nofooter:
:toc: left
:stylesheet: custom.css
= DZGUI documentation (v5.x.x)
DayZ server browser and mod manager for Linux | Last updated: {d}
Click https://aclist.github.io/dzgui/dzgui_dark.html[here] for dark mode
Looking for the DZGUI Knowledge Base? Click https://aclist.github.io/dzgui/kb.html[here]
== What this is
A GUI version of https://github.com/aclist/dztui[DZTUI] for Linux.
Used to list official and community server details and quick connect to preferred servers
by staging mods and concatenating launch options automatically.
Development on DZTUI (terminal client) has stopped at this time.
Instead, DZGUI brings numerous functionality and security improvements and is intended to be a more user-friendly,
turnkey solution for graphical desktop environments, and can also be used on the Steam Deck or similar devices.
== Setup
=== Dependencies
If not already installed, the below can be found in your system's package manager.
If any dependencies are missing when the application starts, it will warn you, so you need not take any preemptive measures here.
All dependencies are installed out of the box on Steam Deck.
- `curl`
- `jq`
- `zenity`
- `steam`
- `wmctrl` or `xdotool`
- `PyGObject` (`python-gobject`)
[NOTE]
If you are using a self-compiled version of jq (e.g. gentoo), it must be configured with support for oniguruma (this is the default setting on most distributions).
=== Preparation
==== Step 1: Download DZGUI and make it executable
**Automatic method: generic OS**
Invoke the command below from a terminal:
```
curl -s "https://raw.githubusercontent.com/aclist/dztui/dzgui/install.sh" | bash
```
**Automatic method: nix-based systems (contributed by lelgenio)**
Follow the instructions at https://github.com/lelgenio/dzgui-nix to ingest the package and dependencies
into your system using flakes.
**Manual method**
```
git clone https://github.com/aclist/dztui.git
chmod +x dzgui.sh
./dzgui.sh
```
==== Step 2: update the vm.max_map_count value
On most modern distributions, it will seldom be necessary to update this value anymore, since it is set to a sufficiently large number for performance-intensive applications.
**Automatic method:**
This is handled automatically by DZGUI if you just choose to run the application out of the box.
You will be prompted for your sudo password in order to check whether the system map count is too small.
This is a one-time check that will not be triggered again once the map count is updated.
[NOTE]
If you are using a Steam Deck, this step is not necessary.
The process writes the count to the file `/etc/sysctl.d/dayz.conf`.
If the system map count was lower than the threshold, it is updated to `1048576`.
If the system map count was already higher, that value is interpolated into this file for redundancy purposes and to avoid sudo escalation on subsequent launches of the application.
[NOTE]
If, for reasons unrelated to DayZ, you choose at a later time to raise your system map count higher than it originally was and
you find that the count is not sticking, check for the presence of the `dayz.conf` file to see if it is taking precedence and delete it accordingly.
If you used the automatic method, you can skip to <<Step 3: Prepare a Steam account with a DayZ license, Step 3>> below.
**Manual method:**
If you wish to update this value yourself without intervention from DZGUI, you have two options:
Invoke the command below for an **ephemeral change**. Note that if changing the map count on a one-time basis, it will revert to the old value after the system is rebooted.
```
sudo sysctl -w vm.max_map_count=1048576
```
Invoke the command below for a **persistent change**:
```
echo 'vm.max_map_count=1048576' | sudo tee /etc/sysctl.d/dayz.conf
```
==== Step 3: Prepare a Steam account with a DayZ license
Enable a Proton version ≥ `6.8` (or use Proton Experimental) in the `Compatibility` field of the game's right-click options. As of this writing, any recent version of Proton should work, and it is encouraged to use the most recent one.
=== API key & server IDs
==== Steam Web API key (required)
1. Register for a https://steamcommunity.com/dev/apikey[Steam Web API key] (free) using your Steam account. You will be asked for a unique URL for your app when registering.
2. Since this key is for a personal use application and does not actually call back anywhere, set a generic local identifier here like "127.0.0.1" or some other name that is meaningful to you.
3. Once configured, you can insert this key in the app when launching it for the first time.
[NOTE]
If you are confused about this requirement, please refer to DZGUI Knowledge Base article https://aclist.github.io/dzgui/kb.html#DZG-007[DZG-007] for additional information.
==== BattleMetrics API key (optional)
This key is optional. Using this key in conjunction with the above allows you to also connect to and query servers by numerical ID instead of by IP. See <<Manage > Add server by ID, Add server by ID>>.
1. Register for an API key at https://www.battlemetrics.com/account/register?after=%2Fdevelopers[BattleMetrics] (free).
2. From the **Personal Access Tokens** area, Select **New Token**.
3. Give the token any name in the field at the top.
4. Leave all options **unchecked** and scroll to the bottom, select **Create Token**.
5. Once configured, you can insert this key in the app when launching it for the first time (optional), or later on when using the connect/query by ID methods in the app for the first time.
=== First-time launch
It is always advised to have Steam running in the background. DZGUI is meant to run "on top of" Steam, and will warn you if Steam appears to not be running.
DZGUI can be launched one of two ways.
**From a terminal:**
```
./dzgui.sh
```
Launching from a terminal may give more verbose information in the event of a crash, and can be a good way of troubleshooting problems.
**From the shortcut shipped with the application**:
If you are using a desktop environment (DE) based on the Freedesktop specification, shortcuts will be installed for you.
- One shortcut is located under the "Games" category of your system's applications list.
- The other is accessed via the "DZGUI" desktop icon (Steam Deck only)
After launching the app, follow the menu prompts onscreen. You will be asked to provide the following:
- Player name (a handle name that identifies your character; required by some servers)
- Steam API key (required)
- BM API key (optional)
==== Steam path discovery
DZGUI will now attempt to locate your default Steam installation and DayZ path. You *must* have DayZ installed in your Steam library in order to proceed. (It can be installed to any drive of your choosing.)
If DZGUI cannot find Steam or cannot find DayZ installed at the detected Steam path, it will prompt you to manually specify the path to your Steam installation.
Specify the root, top-level entry point to Steam, not DayZ. E.g.,
`/media/mydrive/Steam`, not `/media/mydrive/Steam/steamapps/common/DayZ`
If your Steam installation is in a hidden folder but the file picker dialog does not show hidden folders, ensure that your GTK settings are set to show hidden files.
For GTK 2, update the file `$HOME/.config/gtk-2.0/gtkfilechooser.ini` to contain this line:
`ShowHidden=true`
For GTK 3, invoke the command:
`gsettings set org.gtk.Settings.FileChooser show-hidden true`
=== Steam integration & artwork
==== Adding DZGUI as a third-party app
DZGUI can be added to Steam as a "non-Steam game" in order to facilitate integration with Steam Deck or desktop environments.
1. Launch Steam in the "Large" (default) view.
[NOTE]
Steam Deck: you must switch to "Desktop Mode" and launch Steam from the desktop. Steam Deck's Game Mode view has limited support for configuration of custom games.
[start=2]
2. Select **Add a Game** > **Add a Non-Steam Game** from the lower left-hand corner of the Steam client.
image::https://github.com/aclist/dztui/raw/dzgui/images/tutorial/01.png[01,500]
[start=3]
3. Navigate to `$HOME/.local/share/applications/` and select `dzgui.desktop`
4. Select **Add Selected Programs** to add a shortcut to DZGUI.
==== Artwork
DZGUI also ships with Steam cover artwork. It is located under:
```
$HOME/.local/share/dzgui
```
The artwork consists of five parts:
[%autowidth]
|===
|Name|Description
|Hero|a large horizontal banner used on the app's details page, and on landscape-orientation covers in the Recent Games section
|Icon|a square icon used for the tree/list view of the Steam library
|Grid|a vertical "box art" cover used on Steam library pages
|Logo|a transparent icon used to remove Steam's default app text
|dzgui|used by freedesktop shortcut to generate a desktop icon; not intended for manual use by the user
|===
===== Updating the artwork
1. From the main library view, navigate to the app's details page and right-click the blank image header at the top.
image::https://github.com/aclist/dztui/raw/dzgui/images/tutorial/03.png[03,700]
[start=2]
2. Select **Set Custom Background**
3. Select to display **All Files** from the File type dropdown
4. Navigate to the artwork path described above and select `hero.png`.
5. Next, right-click the image background and select **Set Custom Logo**.
image::https://github.com/aclist/dztui/raw/dzgui/images/tutorial/04.png[04,700]
[start=5]
5. Navigate to the same path and select `logo.png`. Notice that this removes the redundant app name that occluded the image.
image::https://github.com/aclist/dztui/raw/dzgui/images/tutorial/05.png[05,700]
[start=6]
6. Next, navigate to your Library index (looks like a bookshelf of cover art) and find the DZGUI app.
[start=7]
7. Right-click its cover and select **Manage** > **Set custom artwork**.
image::https://github.com/aclist/dztui/raw/dzgui/images/tutorial/06.png[06,700]
[start=8]
8. Navigate to the same path and select `grid.png`. The final result:
image::https://github.com/aclist/dztui/raw/dzgui/images/tutorial/07.png[07,700]
[start=9]
9. Right-click the DZGUI entry and select Properties to open the properties dialog. Next to the **Shortcut** field, you will see a small square box which represents the game's icon. Click this to open a file explorer and select `icon.png` from the path above. This will add a small icon to the list view.
image::https://github.com/aclist/dztui/raw/dzgui/images/tutorial/08.png[08,700]
[start=10]
10. After you launch DZGUI for the first time, you should return to the library view and select the Recent Games dropdown on the right-hand side. Steam shows a collection of box art based on categories like "Play Next", "Recent Games", etc. Look for a downward-pointing caret icon and click it, then select the "Recent Games" category. If DZGUI was the last item played, it will be shown with a landscape, rather than portrait, orientation cover, which is initially blank. To customize this cover, right click it and select `Manage > Set custom artwork`, then select the `hero.png` image again for this area.
image::https://github.com/aclist/dztui/raw/dzgui/images/tutorial/09.png[09,700]
==== Controller layout
A controller layout for Steam Deck is available in the Steam community layouts section. Search for "DZGUI Official Config" to download it. This layout provides modal layers intended to facilitate interaction with the DZGUI interface, but does not attempt to customize in-game DayZ controls in a detailed fashion.
Long-press the View button and Select button (☰) to toggle D-pad navigation. This creates an additional layer that lets you navigate through menus using the D-pad and A/B to respectively confirm selections and go back. Remember to toggle this layer off again after launching your game to revert back to the master layer.
=== Updating the app
If DZGUI detects a new upstream version, it will prompt you to download it automatically.
It backs up the original version before fetching the new one, then updates your config file with your existing values. Once finished, it will ask you to relaunch the app.
If you decline to upgrade to the new version, DZGUI will continue to the main menu with the current version.
[NOTE]
New versions may include changes to bugs that could prevent you from playing on certain servers.
Upgrading is always advised.
If you experience a problem or need to restore the prior version of DZGUI and/or your configs,
it is enough to simply replace the new version with the old one and relaunch the app.
The file can be found at:
```
<path to script>/<script name>.old
```
E.g., if DZGUI is named `dzgui.sh`, in the path `$HOME/bin`, the backup will be located at
```
$HOME/bin/dzgui.sh.old
```
If launching DZGUI via its system shortcut, the backup file will be located under the path:
```
$HOME/.local/share/dzgui
```
Similarly, a backup config file will be located at:
```
$HOME/.config/dztui/dztuirc.old
```
=== Locale support
For internationalization purposes, DZGUI will inherit the default locale setting on the system when displaying numbers. This is used for thousands separators in long numbers and decimal separators in fractional numbers.
If you wish to use a specific regional numbering preference while retaining a different base system language (e.g., English language with German-style numbering), pass the desired locale as a variable before launching DZGUI:
```
LC_ALL=de_DE.UTF-8 ./dzgui.sh
```
If you intend to use this frequently, you could wrap the above in a script or alias.
== Uninstallation
To uninstall DZGUI and its associated files, invoke `./dzgui.sh` with the `-u` or `--uninstall` flag.
Follow the prompts to choose from full uninstall (removes the application and all user-defined configuration files) or partial install (removes the application, but keeps user-defined configuration files).
If you intend to reinstall DZGUI again, keeping your configuration files is recommended.
== Basic usage
Select from among the <<Menu contexts and options>> below.
Connecting to a server consists of fetching metadata for the server you are searching for.
DZGUI will check the server's modset against your local mods. If you are missing any,
it will prompt you to download them through the Steam Workshop.
[NOTE]
You must be logged into Steam for mod changes to take effect.
It can take some time for the mods to download and update.
Once all of the mods are downloaded and staged, DZGUI will notify you that it is ready to connect.
The app hands the launch parameters to Steam, after which point DZGUI is not involved in the operation of DayZ.
== Menu contexts and options
The main view displays a series of contexts, visualized as buttons, on the right-hand side. You can click these to navigate between different areas.
A footer at the bottom of the application lists an explanatory tooltip for the currently focused option. In the case of server browsing contexts, this tooltip will be updated to show the distance in kilometers to the server and an estimated ping (round-trip latency).
The right-hand side of the footer displays the branch of DZGUI you are using (stable/testing), the mod install mode (normal/auto), and the version number. For example, if you are on the stable branch, using the default install mode, and using DZGUI v5.0.0, the footer will read:
`STABLE | NORMAL | DZGUI 5.0.0`
=== Main menu
The main menu context is the central entry point to server navigation, letting you browse the list of publicly available servers, check your list of favorited servers, connect to recent servers, et cetera.
==== Main menu > Server browser
The server browser retrieves and lists all publicly broadcasting servers (including official ones) in a table.
These servers can be filtered by various parameters in order to display a more granular result.
After you select a server from the list and choose to connect, the application continues to the mod validation step.
For details on interacting with server browsing contexts, which are uniform across different menus, refer to <<Server table and filters>>.
==== Main menu > My saved servers
Prints metadata for the servers saved in your servers list. Servers can be added via the <<Manage>> context or via the <<Contextual options>> of server table views.
For details on interacting with server browsing contexts, which are uniform across different menus, refer to <<Server table and filters>>.
==== Main menu > Quick connect to favorite server
Bypasses the server list and quick-connects to a single favorite server specified in advance using the <<Manage > Add (change) favorite server, Add (change) favorite server>> option.
==== Main menu > Recent servers
This feature queries the history file for the last 10 servers connected to by any means
(server browser, favorite servers, connect-by-IP, etc.), and presents them in a table.
For details on interacting with server browsing contexts, which are uniform across different menus, refer to <<Server table and filters>>.
==== Main menu > Connect by IP
Provide the IP and query port in `<IP>:<PORT>` format. Depending on the mod installation method you have selected, DZGUI will proceed to stage and download mods and prompt you before connecting.
==== Main menu > Connect by ID
Requires a <<API key & server IDs, BM API key>>. Use the https://www.battlemetrics.com/servers/dayz[BattleMetrics site] to find servers of interest (proximity, player count, rules, etc.)
Each server has a unique ID. This is the string of numbers at the end of the URL. Copy these IDs.
For example, in the URL https://www.battlemetrics.com/servers/dayz/8039514, the ID is `8039514`.
Enter the ID of a server to have it translated to an IP. After this step, the process continues as though you were connecting by IP.
=== Manage
==== Manage > Add server by IP
Add a server to your saved servers list by IP.
Provide the IP and query port in `<IP>:<PORT>` format.
Servers you add will be saved and listed when using the <<Main menu > My saved servers, My saved servers>> option.
==== Manage > Add server by ID
Requires a <<API key & server IDs, BM API key>>. Use the https://www.battlemetrics.com/servers/dayz[BattleMetrics site] to find servers of interest (proximity, player count, rules, etc.)
Each server has a unique ID. This is the string of numbers at the end of the URL. Copy these IDs.
For example, in the URL https://www.battlemetrics.com/servers/dayz/8039514, the ID is `8039514`.
Servers you add will be saved and listed when using the <<Main menu > My saved servers, My saved servers>> option.
==== Manage > Add (change) favorite server
Prompts you to add/change a favorite server (limit one). The name of the server will be updated in the footer of the app when focused on the <<Main menu > Quick connect to favorite server, Quick connect to favorite server>> option in the <<Main menu>> context. If a favorite server is already enabled, this option switches to "Change favorite server."
=== Options
The options context is chiefly used to toggle settings, update API keys, or perform changes to mods.
==== Options > List installed mods
Prints a scrollable dialog containing all locally-installed mods and their corresponding symlink IDs and directory names.
If you right-click on an entry in the list, you have the option of opening the Workshop page for the mod in Steam or deleting the local mod.
==== Options > Toggle release branch
Used to toggle the branch to fetch DZGUI from between `stable` and `testing`.
The app ships with the stable branch enabled, with the testing branch being used to elaborate various experimental features.
==== Options > Toggle mod install mode
This feature is experimental. It attempts to queue the mods requested for download automatically, rather than prompting the user to subscribe to each one.
[NOTE]
When using auto mod installation, DZGUI will track the latest version of your installed mods and periodically synchronize their signatures next time you attempt to connect to a server. This means that if you satisfy the mods needed for a server, but updates to other mods you already have are found, an update will be triggered until all of your local mods are refreshed. Provided you tend to keep auto mod install on, these updates should be atomic and ensure that mods are always up to date.
==== Options > Toggle Steam/Flatpak
This feature should be used if there are concurrent installations of Steam on the local system. Toggle between using Steam or Flatpak Steam to launch DayZ.
==== Options > Change player name
Used to change the in-game player name that is broadcast when on servers.
==== Options > Change Steam API key
Used to change or update the Steam API key; can be used if the old key needs to be revoked and updated with a new one.
==== Options > Change Battlemetrics API key
Identical to the option above, only for Battlemetrics.
==== Options > Force update local mods
Attempts to update the signatures of all local mods and synchronize them with the latest versions available on Steam Workshop. This option should be used when <<Options > Toggle mod install mode, Toggle mod install mode>> is set to AUTO.
This can be used in the event of mod corruption or linkage errors.
==== Output system info to log file
Writes a list of your current settings and system configuration to a local file that can be pasted into bug reports. This process may take some time.
The file is written to `$HOME/.local/state/dzgui/logs/DZGUI_SYSTEM.log`
=== Help
The help context chiefly opens external documentation and support pages in the system-defined web browser or internal dialog.
==== Options > View changelog
Prints the entire changelog up to the current version in-app.
==== Options > Show debug log
Prints the DZGUI log file generated since the last session in a tabled format in-app.
==== Help > Help file
Uses xdg-open to open this documentation in the system browser.
==== Help > Report a bug
Uses xdg-open to open the https://github.com/aclist/dztui/issues[bug tracker] in the system browser.
==== Help > Forum
Uses xdg-open to open the https://github.com/aclist/dztui/discussions[discussion forum] in the system browser.
==== Help > Sponsor
Uses xdg-open to open the https://github.com/sponsors/aclist[sponsor page], where you can help fund development, in the system browser.
==== Help > Hall of fame
Uses xdg-open to open the https://aclist.github.io/dzgui/dzgui#_hall_of_fame[Hall of Fame], which recognizes those who actively contributed to the betterment of the application through bug reports and suggestions, in the system browser.
=== Exit
Exits the DZGUI application.
=== Keybindings
The "?" button opens a dialog that provides additional information about keybindings and navigation throughout the application.
[NOTE]
Due to size constraints, this button is not currently available on Steam Deck.
== Server table and filters
=== Table metadata
[%autowidth]
All server browser contexts (<<Main menu > My saved servers, My saved servers>>, <<Main menu > Server browser, Server browser>>, <<Main menu > Recent servers, Recent servers>>) share the same table layout.
The table lists servers in columnar fashion according to:
- **Name**: the server name and the menu context you are currently on
- **Map**: the map name
- **Perspective**: first-person (1PP) or third-person (3PP)
- **Gametime**: in-game time on the 24-hour clock
- **Players**: total players currently in-game
- **Maximum**: total players the server supports
- **Queue**: total players in the waiting queue (for full servers)
- **Address**: the address in IP:Port format
- **Qport**: the query port of the listening server
Columns are width-adjustable, and the positions they are set to will be saved and persist across sessions. The saved column width is used uniformly across server browsing contexts.
=== Contextual options
When in a server browsing context, right click on any server in the list to expose a series of contextual options. These are:
* **Add to (Remove from) saved servers**: add or remove the server to/from your saved servers list
* **Copy IP to clipboard**: copy the game server IP to the system clipboard
* **Show server-side mods**: opens a dialog with details about the name, Workshop ID, and local installation status of mods on the server. Select a mod to open its Workshop page in Steam
* **Refresh player count**: refresh the player count for this server in-place in the table. Useful if some time has passed since you loaded the list and you want to see if the queue is open
=== Filters
The filter panel on the right-hand side of the table allows you to search by keyword, map type, and toggle different filters. These filters can be combined.
==== Keyword search
A text entry field where you can search for hits in any column of the table by partial string match. The field does not support regex, but does support basic backslash escapes for special characters, e.g., `\[My string\]`.
Clear the keyword search and submit again to remove any keyword filter.
==== Map search
Opens a combobox that lets you filter the table by specific map.
A variety of navigation bindings for interacting with the table and toggling filters can be found via the <<Keybindings>> dialog.
==== Toggle filters
A series of filters that restrict the results by game parameters. Also toggleable via the number keys.
[%autowidth]
|===
|Filter|Usage
|1PP|Include servers in first-person perspective
|3PP|Include servers in third-person perspective
|Day|Include servers with gametime between 0600 and 1659
|Night|Include servers with gametime between 1700 and 0559
|Empty|Include servers with 0 current players
|Full|Include servers at maximum capacity
|Low pop|Include servers with a current player count < 30% of the allowable capacity
|Non-ASCII titles|Include servers with special symbols, Unicode, or text in the title.
Note: unticking this filter will also exclude CJK languages, Cyrillic, and other special character sets
|Duplicate|Include servers with duplicated server titles (there may be large numbers of misconfigured or generic servers with the same boilerplate title)
|===
[NOTE]
It is possible to filter the list to 0 results if you combine or exclude logically opposed filter pairs, such as excluding both 'Day' and 'Night' servers, effectively restricting the list to no possible servers. If you expect to see results but do not, double-check your filters (and keywords) to make sure your search is not too restrictive.
== Debug mode
Debug mode can be enabled via the button toggle on the right panel. When debug mode is enabled, any attempt to connect to a server will function as before, excluding the final connection step. DZGUI will attempt to synchronize and download mods and then print a dialog showing what options would have actually been run when connecting to the server.
This can be used to diagnose problems and test functionality without actually launching the game.
== Troubleshooting/FAQ
Please refer to the https://aclist.github.io/dzgui/kb.html[DZGUI Knowledge Base] for common issues.
== Reference
=== Config file key/value pairs
The config file is located at `$HOME/.config/dztui/dztuirc`.
Under normal usage, these values are populated and toggled automatically in-app. Deprecated keys are not listed.
[%autowidth]
|===
|Key|Value
|`api_key`|an optional BattleMetrics API key generated at BattleMetrics. See <<API key & server IDs>>
|`auto_install`|permissible values are 0, 1, and 2. These are set internally depending on if the user enabled auto mod installation
|`branch`|by default, set to `stable`; set to `testing` to fetch the testing branch
|`debug`|by default, unset; when set to `1`, the launch options that would have been run are printed as a dry-run, instead of actually connecting (used for troubleshooting and submitting bug reports)
|`default_steam_path`|the path to the default Steam client installation
|`ip_list`|an array of server addresses; each record is a concatenation of the IP/GamePort/Queryport
|`fav_server`|a single server to quick-connect to; a concatenation of the IP/Gameport/Queryport
|`fav_label`|the human-readable name of the fav server set above
|`name`|a custom "handle" name used to identify the player on a server (required by some servers)
|`preferred_client`|whether the user prefers native Steam or Flatpak. This value is only set if concurrent versions of Steam are found on the system
|`src_path`|the path to where DZGUI is currently being stored
|`staging_dir`|a directory used to stage changes between updates
|`steam_path`|the path to Steam detected/set during first-time setup
|`steam_api`|the Steam API key. See <<API key & server IDs>>
|===
=== Hall of fame
This section recognizes users who have gone above and beyond in submitting useful bug reports that helped in tracking down critical issues
or resulted in the elaboration of important features. This list is not exhaustive, and any missing parties are errors of omission, but this does
not diminish the gratitude I have for their contributions.
.bongjutsu
Consistently one of the first to report emergent bugs and provides clear, detailed ways of replicating the issue.
.dj3hac
Provided extensive debug information that was instrumental in solving issues with Flatpak Steam.
.jiriks74
Gives highly relevant information about edge cases, particularly as they concern the Steam beta client, Wayland, desktop environments, and experimental features.
.MatheusLasserr
Consistently provides constructive, straightforward suggestions about UI and readability improvements.
.scandalouss
Tracked down several highly obscure but key bugs in the early development of the application that were breaking discovery of mods.
.StevelDusa
Played a critical role in the elaboration of many of the features we now take for granted by being an early beta tester who not only reported bugs, but
helped workshop and brainstorm various ideas that turned into QOL features.
.Thoughtduck216
Contributed extensive beta testing of Steam Deck builds and provided ongoing troubleshooting for Steam Deck users

View file

@ -1,608 +0,0 @@
:nofooter:
:toc: left
:stylesheet: dark.css
= DZGUI documentation (v5.x.x)
DayZ server browser and mod manager for Linux | Last updated: {d}
Click https://aclist.github.io/dzgui/dzgui.html[here] for light mode
Looking for the DZGUI Knowledge Base? Click https://aclist.github.io/dzgui/kb_dark.html[here]
== What this is
A GUI version of https://github.com/aclist/dztui[DZTUI] for Linux.
Used to list official and community server details and quick connect to preferred servers
by staging mods and concatenating launch options automatically.
Development on DZTUI (terminal client) has stopped at this time.
Instead, DZGUI brings numerous functionality and security improvements and is intended to be a more user-friendly,
turnkey solution for graphical desktop environments, and can also be used on the Steam Deck or similar devices.
== Setup
=== Dependencies
If not already installed, the below can be found in your system's package manager.
If any dependencies are missing when the application starts, it will warn you, so you need not take any preemptive measures here.
All dependencies are installed out of the box on Steam Deck.
- `curl`
- `jq`
- `zenity`
- `steam`
- `wmctrl` or `xdotool`
- `PyGObject` (`python-gobject`)
[NOTE]
If you are using a self-compiled version of jq (e.g. gentoo), it must be configured with support for oniguruma (this is the default setting on most distributions).
=== Preparation
==== Step 1: Download DZGUI and make it executable
**Automatic method: generic OS**
Invoke the command below from a terminal:
```
curl -s "https://raw.githubusercontent.com/aclist/dztui/dzgui/install.sh" | bash
```
**Automatic method: nix-based systems (contributed by lelgenio)**
Follow the instructions at https://github.com/lelgenio/dzgui-nix to ingest the package and dependencies
into your system using flakes.
**Manual method**
```
git clone https://github.com/aclist/dztui.git
chmod +x dzgui.sh
./dzgui.sh
```
==== Step 2: update the vm.max_map_count value
On most modern distributions, it will seldom be necessary to update this value anymore, since it is set to a sufficiently large number for performance-intensive applications.
**Automatic method:**
This is handled automatically by DZGUI if you just choose to run the application out of the box.
You will be prompted for your sudo password in order to check whether the system map count is too small.
This is a one-time check that will not be triggered again once the map count is updated.
[NOTE]
If you are using a Steam Deck, this step is not necessary.
The process writes the count to the file `/etc/sysctl.d/dayz.conf`.
If the system map count was lower than the threshold, it is updated to `1048576`.
If the system map count was already higher, that value is interpolated into this file for redundancy purposes and to avoid sudo escalation on subsequent launches of the application.
[NOTE]
If, for reasons unrelated to DayZ, you choose at a later time to raise your system map count higher than it originally was and
you find that the count is not sticking, check for the presence of the `dayz.conf` file to see if it is taking precedence and delete it accordingly.
If you used the automatic method, you can skip to <<Step 3: Prepare a Steam account with a DayZ license, Step 3>> below.
**Manual method:**
If you wish to update this value yourself without intervention from DZGUI, you have two options:
Invoke the command below for an **ephemeral change**. Note that if changing the map count on a one-time basis, it will revert to the old value after the system is rebooted.
```
sudo sysctl -w vm.max_map_count=1048576
```
Invoke the command below for a **persistent change**:
```
echo 'vm.max_map_count=1048576' | sudo tee /etc/sysctl.d/dayz.conf
```
==== Step 3: Prepare a Steam account with a DayZ license
Enable a Proton version ≥ `6.8` (or use Proton Experimental) in the `Compatibility` field of the game's right-click options. As of this writing, any recent version of Proton should work, and it is encouraged to use the most recent one.
=== API key & server IDs
==== Steam Web API key (required)
1. Register for a https://steamcommunity.com/dev/apikey[Steam Web API key] (free) using your Steam account. You will be asked for a unique URL for your app when registering.
2. Since this key is for a personal use application and does not actually call back anywhere, set a generic local identifier here like "127.0.0.1" or some other name that is meaningful to you.
3. Once configured, you can insert this key in the app when launching it for the first time.
[NOTE]
If you are confused about this requirement, please refer to DZGUI Knowledge Base article https://aclist.github.io/dzgui/kb.html#DZG-007[DZG-007] for additional information.
==== BattleMetrics API key (optional)
This key is optional. Using this key in conjunction with the above allows you to also connect to and query servers by numerical ID instead of by IP. See <<Manage > Add server by ID, Add server by ID>>.
1. Register for an API key at https://www.battlemetrics.com/account/register?after=%2Fdevelopers[BattleMetrics] (free).
2. From the **Personal Access Tokens** area, Select **New Token**.
3. Give the token any name in the field at the top.
4. Leave all options **unchecked** and scroll to the bottom, select **Create Token**.
5. Once configured, you can insert this key in the app when launching it for the first time (optional), or later on when using the connect/query by ID methods in the app for the first time.
=== First-time launch
It is always advised to have Steam running in the background. DZGUI is meant to run "on top of" Steam, and will warn you if Steam appears to not be running.
DZGUI can be launched one of two ways.
**From a terminal:**
```
./dzgui.sh
```
Launching from a terminal may give more verbose information in the event of a crash, and can be a good way of troubleshooting problems.
**From the shortcut shipped with the application**:
If you are using a desktop environment (DE) based on the Freedesktop specification, shortcuts will be installed for you.
- One shortcut is located under the "Games" category of your system's applications list.
- The other is accessed via the "DZGUI" desktop icon (Steam Deck only)
After launching the app, follow the menu prompts onscreen. You will be asked to provide the following:
- Player name (a handle name that identifies your character; required by some servers)
- Steam API key (required)
- BM API key (optional)
==== Steam path discovery
DZGUI will now attempt to locate your default Steam installation and DayZ path. You *must* have DayZ installed in your Steam library in order to proceed. (It can be installed to any drive of your choosing.)
If DZGUI cannot find Steam or cannot find DayZ installed at the detected Steam path, it will prompt you to manually specify the path to your Steam installation.
Specify the root, top-level entry point to Steam, not DayZ. E.g.,
`/media/mydrive/Steam`, not `/media/mydrive/Steam/steamapps/common/DayZ`
If your Steam installation is in a hidden folder but the file picker dialog does not show hidden folders, ensure that your GTK settings are set to show hidden files.
For GTK 2, update the file `$HOME/.config/gtk-2.0/gtkfilechooser.ini` to contain this line:
`ShowHidden=true`
For GTK 3, invoke the command:
`gsettings set org.gtk.Settings.FileChooser show-hidden true`
=== Steam integration & artwork
==== Adding DZGUI as a third-party app
DZGUI can be added to Steam as a "non-Steam game" in order to facilitate integration with Steam Deck or desktop environments.
1. Launch Steam in the "Large" (default) view.
[NOTE]
Steam Deck: you must switch to "Desktop Mode" and launch Steam from the desktop. Steam Deck's Game Mode view has limited support for configuration of custom games.
[start=2]
2. Select **Add a Game** > **Add a Non-Steam Game** from the lower left-hand corner of the Steam client.
image::https://github.com/aclist/dztui/raw/dzgui/images/tutorial/01.png[01,500]
[start=3]
3. Navigate to `$HOME/.local/share/applications/` and select `dzgui.desktop`
4. Select **Add Selected Programs** to add a shortcut to DZGUI.
==== Artwork
DZGUI also ships with Steam cover artwork. It is located under:
```
$HOME/.local/share/dzgui
```
The artwork consists of five parts:
[%autowidth]
|===
|Name|Description
|Hero|a large horizontal banner used on the app's details page, and on landscape-orientation covers in the Recent Games section
|Icon|a square icon used for the tree/list view of the Steam library
|Grid|a vertical "box art" cover used on Steam library pages
|Logo|a transparent icon used to remove Steam's default app text
|dzgui|used by freedesktop shortcut to generate a desktop icon; not intended for manual use by the user
|===
===== Updating the artwork
1. From the main library view, navigate to the app's details page and right-click the blank image header at the top.
image::https://github.com/aclist/dztui/raw/dzgui/images/tutorial/03.png[03,700]
[start=2]
2. Select **Set Custom Background**
3. Select to display **All Files** from the File type dropdown
4. Navigate to the artwork path described above and select `hero.png`.
5. Next, right-click the image background and select **Set Custom Logo**.
image::https://github.com/aclist/dztui/raw/dzgui/images/tutorial/04.png[04,700]
[start=5]
5. Navigate to the same path and select `logo.png`. Notice that this removes the redundant app name that occluded the image.
image::https://github.com/aclist/dztui/raw/dzgui/images/tutorial/05.png[05,700]
[start=6]
6. Next, navigate to your Library index (looks like a bookshelf of cover art) and find the DZGUI app.
[start=7]
7. Right-click its cover and select **Manage** > **Set custom artwork**.
image::https://github.com/aclist/dztui/raw/dzgui/images/tutorial/06.png[06,700]
[start=8]
8. Navigate to the same path and select `grid.png`. The final result:
image::https://github.com/aclist/dztui/raw/dzgui/images/tutorial/07.png[07,700]
[start=9]
9. Right-click the DZGUI entry and select Properties to open the properties dialog. Next to the **Shortcut** field, you will see a small square box which represents the game's icon. Click this to open a file explorer and select `icon.png` from the path above. This will add a small icon to the list view.
image::https://github.com/aclist/dztui/raw/dzgui/images/tutorial/08.png[08,700]
[start=10]
10. After you launch DZGUI for the first time, you should return to the library view and select the Recent Games dropdown on the right-hand side. Steam shows a collection of box art based on categories like "Play Next", "Recent Games", etc. Look for a downward-pointing caret icon and click it, then select the "Recent Games" category. If DZGUI was the last item played, it will be shown with a landscape, rather than portrait, orientation cover, which is initially blank. To customize this cover, right click it and select `Manage > Set custom artwork`, then select the `hero.png` image again for this area.
image::https://github.com/aclist/dztui/raw/dzgui/images/tutorial/09.png[09,700]
==== Controller layout
A controller layout for Steam Deck is available in the Steam community layouts section. Search for "DZGUI Official Config" to download it. This layout provides modal layers intended to facilitate interaction with the DZGUI interface, but does not attempt to customize in-game DayZ controls in a detailed fashion.
Long-press the View button and Select button (☰) to toggle D-pad navigation. This creates an additional layer that lets you navigate through menus using the D-pad and A/B to respectively confirm selections and go back. Remember to toggle this layer off again after launching your game to revert back to the master layer.
=== Updating the app
If DZGUI detects a new upstream version, it will prompt you to download it automatically.
It backs up the original version before fetching the new one, then updates your config file with your existing values. Once finished, it will ask you to relaunch the app.
If you decline to upgrade to the new version, DZGUI will continue to the main menu with the current version.
[NOTE]
New versions may include changes to bugs that could prevent you from playing on certain servers.
Upgrading is always advised.
If you experience a problem or need to restore the prior version of DZGUI and/or your configs,
it is enough to simply replace the new version with the old one and relaunch the app.
The file can be found at:
```
<path to script>/<script name>.old
```
E.g., if DZGUI is named `dzgui.sh`, in the path `$HOME/bin`, the backup will be located at
```
$HOME/bin/dzgui.sh.old
```
If launching DZGUI via its system shortcut, the backup file will be located under the path:
```
$HOME/.local/share/dzgui
```
Similarly, a backup config file will be located at:
```
$HOME/.config/dztui/dztuirc.old
```
=== Locale support
For internationalization purposes, DZGUI will inherit the default locale setting on the system when displaying numbers. This is used for thousands separators in long numbers and decimal separators in fractional numbers.
If you wish to use a specific regional numbering preference while retaining a different base system language (e.g., English language with German-style numbering), pass the desired locale as a variable before launching DZGUI:
```
LC_ALL=de_DE.UTF-8 ./dzgui.sh
```
If you intend to use this frequently, you could wrap the above in a script or alias.
== Uninstallation
To uninstall DZGUI and its associated files, invoke `./dzgui.sh` with the `-u` or `--uninstall` flag.
Follow the prompts to choose from full uninstall (removes the application and all user-defined configuration files) or partial install (removes the application, but keeps user-defined configuration files).
If you intend to reinstall DZGUI again, keeping your configuration files is recommended.
== Basic usage
Select from among the <<Menu contexts and options>> below.
Connecting to a server consists of fetching metadata for the server you are searching for.
DZGUI will check the server's modset against your local mods. If you are missing any,
it will prompt you to download them through the Steam Workshop.
[NOTE]
You must be logged into Steam for mod changes to take effect.
It can take some time for the mods to download and update.
Once all of the mods are downloaded and staged, DZGUI will notify you that it is ready to connect.
The app hands the launch parameters to Steam, after which point DZGUI is not involved in the operation of DayZ.
== Menu contexts and options
The main view displays a series of contexts, visualized as buttons, on the right-hand side. You can click these to navigate between different areas.
A footer at the bottom of the application lists an explanatory tooltip for the currently focused option. In the case of server browsing contexts, this tooltip will be updated to show the distance in kilometers to the server and an estimated ping (round-trip latency).
The right-hand side of the footer displays the branch of DZGUI you are using (stable/testing), the mod install mode (normal/auto), and the version number. For example, if you are on the stable branch, using the default install mode, and using DZGUI v5.0.0, the footer will read:
`STABLE | NORMAL | DZGUI 5.0.0`
=== Main menu
The main menu context is the central entry point to server navigation, letting you browse the list of publicly available servers, check your list of favorited servers, connect to recent servers, et cetera.
==== Main menu > Server browser
The server browser retrieves and lists all publicly broadcasting servers (including official ones) in a table.
These servers can be filtered by various parameters in order to display a more granular result.
After you select a server from the list and choose to connect, the application continues to the mod validation step.
For details on interacting with server browsing contexts, which are uniform across different menus, refer to <<Server table and filters>>.
==== Main menu > My saved servers
Prints metadata for the servers saved in your servers list. Servers can be added via the <<Manage>> context or via the <<Contextual options>> of server table views.
For details on interacting with server browsing contexts, which are uniform across different menus, refer to <<Server table and filters>>.
==== Main menu > Quick connect to favorite server
Bypasses the server list and quick-connects to a single favorite server specified in advance using the <<Manage > Add (change) favorite server, Add (change) favorite server>> option.
==== Main menu > Recent servers
This feature queries the history file for the last 10 servers connected to by any means
(server browser, favorite servers, connect-by-IP, etc.), and presents them in a table.
For details on interacting with server browsing contexts, which are uniform across different menus, refer to <<Server table and filters>>.
==== Main menu > Connect by IP
Provide the IP and query port in `<IP>:<PORT>` format. Depending on the mod installation method you have selected, DZGUI will proceed to stage and download mods and prompt you before connecting.
==== Main menu > Connect by ID
Requires a <<API key & server IDs, BM API key>>. Use the https://www.battlemetrics.com/servers/dayz[BattleMetrics site] to find servers of interest (proximity, player count, rules, etc.)
Each server has a unique ID. This is the string of numbers at the end of the URL. Copy these IDs.
For example, in the URL https://www.battlemetrics.com/servers/dayz/8039514, the ID is `8039514`.
Enter the ID of a server to have it translated to an IP. After this step, the process continues as though you were connecting by IP.
=== Manage
==== Manage > Add server by IP
Add a server to your saved servers list by IP.
Provide the IP and query port in `<IP>:<PORT>` format.
Servers you add will be saved and listed when using the <<Main menu > My saved servers, My saved servers>> option.
==== Manage > Add server by ID
Requires a <<API key & server IDs, BM API key>>. Use the https://www.battlemetrics.com/servers/dayz[BattleMetrics site] to find servers of interest (proximity, player count, rules, etc.)
Each server has a unique ID. This is the string of numbers at the end of the URL. Copy these IDs.
For example, in the URL https://www.battlemetrics.com/servers/dayz/8039514, the ID is `8039514`.
Servers you add will be saved and listed when using the <<Main menu > My saved servers, My saved servers>> option.
==== Manage > Add (change) favorite server
Prompts you to add/change a favorite server (limit one). The name of the server will be updated in the footer of the app when focused on the <<Main menu > Quick connect to favorite server, Quick connect to favorite server>> option in the <<Main menu>> context. If a favorite server is already enabled, this option switches to "Change favorite server."
=== Options
The options context is chiefly used to toggle settings, update API keys, or perform changes to mods.
==== Options > List installed mods
Prints a scrollable dialog containing all locally-installed mods and their corresponding symlink IDs and directory names.
If you right-click on an entry in the list, you have the option of opening the Workshop page for the mod in Steam or deleting the local mod.
==== Options > Toggle release branch
Used to toggle the branch to fetch DZGUI from between `stable` and `testing`.
The app ships with the stable branch enabled, with the testing branch being used to elaborate various experimental features.
==== Options > Toggle mod install mode
This feature is experimental. It attempts to queue the mods requested for download automatically, rather than prompting the user to subscribe to each one.
[NOTE]
When using auto mod installation, DZGUI will track the latest version of your installed mods and periodically synchronize their signatures next time you attempt to connect to a server. This means that if you satisfy the mods needed for a server, but updates to other mods you already have are found, an update will be triggered until all of your local mods are refreshed. Provided you tend to keep auto mod install on, these updates should be atomic and ensure that mods are always up to date.
==== Options > Toggle Steam/Flatpak
This feature should be used if there are concurrent installations of Steam on the local system. Toggle between using Steam or Flatpak Steam to launch DayZ.
==== Options > Change player name
Used to change the in-game player name that is broadcast when on servers.
==== Options > Change Steam API key
Used to change or update the Steam API key; can be used if the old key needs to be revoked and updated with a new one.
==== Options > Change Battlemetrics API key
Identical to the option above, only for Battlemetrics.
==== Options > Force update local mods
Attempts to update the signatures of all local mods and synchronize them with the latest versions available on Steam Workshop. This option should be used when <<Options > Toggle mod install mode, Toggle mod install mode>> is set to AUTO.
This can be used in the event of mod corruption or linkage errors.
==== Output system info to log file
Writes a list of your current settings and system configuration to a local file that can be pasted into bug reports. This process may take some time.
The file is written to `$HOME/.local/state/dzgui/logs/DZGUI_SYSTEM.log`
=== Help
The help context chiefly opens external documentation and support pages in the system-defined web browser or internal dialog.
==== Options > View changelog
Prints the entire changelog up to the current version in-app.
==== Options > Show debug log
Prints the DZGUI log file generated since the last session in a tabled format in-app.
==== Help > Help file
Uses xdg-open to open this documentation in the system browser.
==== Help > Report a bug
Uses xdg-open to open the https://github.com/aclist/dztui/issues[bug tracker] in the system browser.
==== Help > Forum
Uses xdg-open to open the https://github.com/aclist/dztui/discussions[discussion forum] in the system browser.
==== Help > Sponsor
Uses xdg-open to open the https://github.com/sponsors/aclist[sponsor page], where you can help fund development, in the system browser.
==== Help > Hall of fame
Uses xdg-open to open the https://aclist.github.io/dzgui/dzgui#_hall_of_fame[Hall of Fame], which recognizes those who actively contributed to the betterment of the application through bug reports and suggestions, in the system browser.
=== Exit
Exits the DZGUI application.
=== Keybindings
The "?" button opens a dialog that provides additional information about keybindings and navigation throughout the application.
[NOTE]
Due to size constraints, this button is not currently available on Steam Deck.
== Server table and filters
=== Table metadata
[%autowidth]
All server browser contexts (<<Main menu > My saved servers, My saved servers>>, <<Main menu > Server browser, Server browser>>, <<Main menu > Recent servers, Recent servers>>) share the same table layout.
The table lists servers in columnar fashion according to:
- **Name**: the server name and the menu context you are currently on
- **Map**: the map name
- **Perspective**: first-person (1PP) or third-person (3PP)
- **Gametime**: in-game time on the 24-hour clock
- **Players**: total players currently in-game
- **Maximum**: total players the server supports
- **Queue**: total players in the waiting queue (for full servers)
- **Address**: the address in IP:Port format
- **Qport**: the query port of the listening server
Columns are width-adjustable, and the positions they are set to will be saved and persist across sessions. The saved column width is used uniformly across server browsing contexts.
=== Contextual options
When in a server browsing context, right click on any server in the list to expose a series of contextual options. These are:
* **Add to (Remove from) saved servers**: add or remove the server to/from your saved servers list
* **Copy IP to clipboard**: copy the game server IP to the system clipboard
* **Show server-side mods**: opens a dialog with details about the name, Workshop ID, and local installation status of mods on the server. Select a mod to open its Workshop page in Steam
* **Refresh player count**: refresh the player count for this server in-place in the table. Useful if some time has passed since you loaded the list and you want to see if the queue is open
=== Filters
The filter panel on the right-hand side of the table allows you to search by keyword, map type, and toggle different filters. These filters can be combined.
==== Keyword search
A text entry field where you can search for hits in any column of the table by partial string match. The field does not support regex, but does support basic backslash escapes for special characters, e.g., `\[My string\]`.
Clear the keyword search and submit again to remove any keyword filter.
==== Map search
Opens a combobox that lets you filter the table by specific map.
A variety of navigation bindings for interacting with the table and toggling filters can be found via the <<Keybindings>> dialog.
==== Toggle filters
A series of filters that restrict the results by game parameters. Also toggleable via the number keys.
[%autowidth]
|===
|Filter|Usage
|1PP|Include servers in first-person perspective
|3PP|Include servers in third-person perspective
|Day|Include servers with gametime between 0600 and 1659
|Night|Include servers with gametime between 1700 and 0559
|Empty|Include servers with 0 current players
|Full|Include servers at maximum capacity
|Low pop|Include servers with a current player count < 30% of the allowable capacity
|Non-ASCII titles|Include servers with special symbols, Unicode, or text in the title.
Note: unticking this filter will also exclude CJK languages, Cyrillic, and other special character sets
|Duplicate|Include servers with duplicated server titles (there may be large numbers of misconfigured or generic servers with the same boilerplate title)
|===
[NOTE]
It is possible to filter the list to 0 results if you combine or exclude logically opposed filter pairs, such as excluding both 'Day' and 'Night' servers, effectively restricting the list to no possible servers. If you expect to see results but do not, double-check your filters (and keywords) to make sure your search is not too restrictive.
== Debug mode
Debug mode can be enabled via the button toggle on the right panel. When debug mode is enabled, any attempt to connect to a server will function as before, excluding the final connection step. DZGUI will attempt to synchronize and download mods and then print a dialog showing what options would have actually been run when connecting to the server.
This can be used to diagnose problems and test functionality without actually launching the game.
== Troubleshooting/FAQ
Please refer to the https://aclist.github.io/dzgui/kb.html[DZGUI Knowledge Base] for common issues.
== Reference
=== Config file key/value pairs
The config file is located at `$HOME/.config/dztui/dztuirc`.
Under normal usage, these values are populated and toggled automatically in-app. Deprecated keys are not listed.
[%autowidth]
|===
|Key|Value
|`api_key`|an optional BattleMetrics API key generated at BattleMetrics. See <<API key & server IDs>>
|`auto_install`|permissible values are 0, 1, and 2. These are set internally depending on if the user enabled auto mod installation
|`branch`|by default, set to `stable`; set to `testing` to fetch the testing branch
|`debug`|by default, unset; when set to `1`, the launch options that would have been run are printed as a dry-run, instead of actually connecting (used for troubleshooting and submitting bug reports)
|`default_steam_path`|the path to the default Steam client installation
|`ip_list`|an array of server addresses; each record is a concatenation of the IP/GamePort/Queryport
|`fav_server`|a single server to quick-connect to; a concatenation of the IP/Gameport/Queryport
|`fav_label`|the human-readable name of the fav server set above
|`name`|a custom "handle" name used to identify the player on a server (required by some servers)
|`preferred_client`|whether the user prefers native Steam or Flatpak. This value is only set if concurrent versions of Steam are found on the system
|`src_path`|the path to where DZGUI is currently being stored
|`staging_dir`|a directory used to stage changes between updates
|`steam_path`|the path to Steam detected/set during first-time setup
|`steam_api`|the Steam API key. See <<API key & server IDs>>
|===
=== Hall of fame
This section recognizes users who have gone above and beyond in submitting useful bug reports that helped in tracking down critical issues
or resulted in the elaboration of important features. This list is not exhaustive, and any missing parties are errors of omission, but this does
not diminish the gratitude I have for their contributions.
.bongjutsu
Consistently one of the first to report emergent bugs and provides clear, detailed ways of replicating the issue.
.dj3hac
Provided extensive debug information that was instrumental in solving issues with Flatpak Steam.
.jiriks74
Gives highly relevant information about edge cases, particularly as they concern the Steam beta client, Wayland, desktop environments, and experimental features.
.MatheusLasserr
Consistently provides constructive, straightforward suggestions about UI and readability improvements.
.scandalouss
Tracked down several highly obscure but key bugs in the early development of the application that were breaking discovery of mods.
.StevelDusa
Played a critical role in the elaboration of many of the features we now take for granted by being an early beta tester who not only reported bugs, but
helped workshop and brainstorm various ideas that turned into QOL features.
.Thoughtduck216
Contributed extensive beta testing of Steam Deck builds and provided ongoing troubleshooting for Steam Deck users

View file

@ -1,28 +0,0 @@
:nofooter:
:toc: left
:stylesheet: custom.css
= DZGUI Knowledge Base (v5.x.x)
FAQs for DZGUI
Click https://aclist.github.io/dzgui/kb_dark.html[here] for dark mode
Looking for the main DZGUI documentation? Click https://aclist.github.io/dzgui/dzgui.html[here]
include::kb_sections/dzg001.adoc[]
include::kb_sections/dzg002.adoc[]
include::kb_sections/dzg003.adoc[]
include::kb_sections/dzg004.adoc[]
include::kb_sections/dzg005.adoc[]
include::kb_sections/dzg006.adoc[]
include::kb_sections/dzg007.adoc[]
include::kb_sections/dzg008.adoc[]
include::kb_sections/dzg009.adoc[]

View file

@ -1,28 +0,0 @@
:nofooter:
:toc: left
:stylesheet: dark.css
= DZGUI Knowledge Base (v5.x.x)
FAQs for DZGUI
Click https://aclist.github.io/dzgui/kb.html[here] for light mode
Looking for the main DZGUI documentation? Click https://aclist.github.io/dzgui/dzgui_dark.html[here]
include::kb_sections/dzg001.adoc[]
include::kb_sections/dzg002.adoc[]
include::kb_sections/dzg003.adoc[]
include::kb_sections/dzg004.adoc[]
include::kb_sections/dzg005.adoc[]
include::kb_sections/dzg006.adoc[]
include::kb_sections/dzg007.adoc[]
include::kb_sections/dzg008.adoc[]
include::kb_sections/dzg009.adoc[]

View file

@ -1,11 +0,0 @@
[[DZG-001, DZG-001]]
== DZG-001: Periodically getting dropped from servers, or servers appear unreachable
Last updated: {d}
It is a longstanding issue in DayZ that the game opens a large number of connections when querying servers and/or while connected to servers. This can result in excess traffic on the user's PC which, depending on how much headroom your network has, can lead to getting dropped, unresponsiveness, or a timeout.
If you are on Wi-Fi, try switching to a wired connection and see if the problem resolves itself. If it does, your wireless router settings do not have enough headroom for max simultaneous connections. Use a wired connection, or update your network settings to a more permissive setup.
Bohemia has acknowledged this issue going back 10+ years and stated that DayZ has a heavy impact on the network, but there is as yet no proposed solution on the DayZ side.
This issue is frequently seen on Steam Deck, due to the tendency for users to use it in an untethered Wi-Fi setup.

View file

@ -1,5 +0,0 @@
[[DZG-002,DZG-002]]
== DZG-002: Some servers appear locked in the official DayZ client, and are unreachable in DZGUI
Last updated: {d}
This is a variant of <<DZG-001>>.

View file

@ -1,7 +0,0 @@
[[DZG-003, DZG-003]]
== DZG-003: On Steam Deck, DayZ becomes unresponsive/sluggish over time
Last updated: {d}
When DayZ is open for 1+ hours, a gradual loss in performance and FPS may occur on the Steam Deck.
A solution that seems to work for most users is to install https://github.com/CryoByte33/steam-deck-utilities[Cryo Utilities], a third-party performance management application.

View file

@ -1,7 +0,0 @@
[[DZG-004, DZG-004]]
== DZG-004: On Steam Deck, some mods in the Workshop show a black screen when DZGUI attempts to open them
Last updated: {d}
This is a bug in the Steam client that is being tracked at Valve's Steam for Linux issue tracker https://github.com/ValveSoftware/steam-for-linux/issues/9598[here].
To resolve this issue, manually intervene in the Steam client by selecting a different context (e.g., Store, Library), waiting for it to load, then navigating back to the Workshop context. This should clear the blockage and allow the contents to render.

View file

@ -1,7 +0,0 @@
[[DZG-005,DZG-005]]
== DZG-005: Rendering problems with objects in the Winter Chernarus v2 mod
Last updated: {d}
This mod has LOD (level of detail) bugs that may cause objects near the player, such as leaves, to render incorrectly, or cause distant trees to pop in abruptly. This is an acknowledged issue with the mod itself, not with DayZ or DZGUI.
There is no user-side fix for this issue; it is a problem solely on the mod side.

View file

@ -1,11 +0,0 @@
[[DZG-006,DZG-006]]
== DZG-006: After moving DayZ to another drive, DZGUI fails to locate it on initial setup
Last updated: {d}
If you recently moved the location of DayZ using Steam's internal dialogs, it may take some time for this information to update internally on Steam's side.
Steam stores the location of installed games in a unified file, and DZGUI checks this file during initial setup to determine where Steam claims DayZ is installed.
If you recently moved DayZ to a different drive or partition but did not restart Steam, this information may be out of date, causing Steam to report the wrong location.
Try restarting the Steam client and starting the DZGUI initial setup again.

View file

@ -1,17 +0,0 @@
[[DZG-007,DZG-007]]
== DZG-007: Why do I need a Steam Web API key? Is it safe?
Last updated: {d}
In order to provide a server browser showing a searchable list of all available servers, DZGUI utilizes the Steam Web API.
Actual connections and queries to individual servers are performed directly between the computer and the DayZ server.
DZGUI gets its server information directly from the most authoritative source: Steam. It does this by letting the user be solely in control of their own API key and the application in an authenticated way. Users explicitly get permission to use a Web API key instead of scraping DayZ server info from third-party sites.
Everything that happens between DZGUI and the Steam Web API endpoint takes place solely on the user's computer, using a GET request (fetch server list), and no information gets sent back to the developer. DZGUI does not scrape third party DayZ APIs without permission.
There is some misconception that a Steam Web API key could be used to gain information about a user's account or control their account. Not only is this not possible, but the Web API key is used solely by the user on their own computer and is protected by Steam Guard.
A Steam Web API key is the most strict way of getting authentic, reliable, and consistent server information in a zero-trust model.
You are responsible for the creation, storage, management, and revocation of your Web API key.

View file

@ -1,7 +0,0 @@
[[DZG-008,DZG-008]]
== DZG-008: DZGUI server browser stops working, and Steam API key is seemingly malformed
Last updated: {d}
In order to use a Steam API key, you will have to enable Steam Guard (2FA) protection on your account. If you previously generated an API key and enabled Steam Guard, but then disabled Steam Guard for some other reason, this will also disable any prior API keys you had.
If, after setting up DZGUI, you inadvertently turned off Steam Guard for some reason, check to see if your API key has been disabled, and reapply Steam Guard accordingly.

View file

@ -1,5 +0,0 @@
[[DZG-009,DZG-009]]
== DZG-009: Livonia map is missing from maps dropdown in DZGUI
Last updated: {d}
The Livonia map was iterated off of a map used for the Arma 3 Contact DLC, and is internally called Enoch. Servers running the Livonia map thus use the internal name. Search for Enoch in the list.

132
dzgui.sh
View file

@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -o pipefail
version=5.5.0
version=5.6.3
#CONSTANTS
aid=221100
@ -54,7 +54,8 @@ km_helper="$helpers_path/latlon"
sums_path="$helpers_path/sums.md5"
func_helper="$helpers_path/funcs"
#URLS
#REMOTE
remote_host=gh
author="aclist"
repo="dztui"
url_prefix="https://raw.githubusercontent.com/$author/$repo"
@ -64,6 +65,7 @@ releases_url="https://github.com/$author/$repo/releases/download/browser"
km_helper_url="$releases_url/latlon"
geo_file_url="$releases_url/ips.csv.gz"
set_im_module(){
#TODO: drop pending SteamOS changes
pgrep -a gamescope | grep -q "generate-drm-mode"
@ -74,6 +76,9 @@ set_im_module(){
return
fi
}
redact(){
sed 's@\(/home/\)[^/]*@\1REDACTED@g'
}
logger(){
local date="$(date "+%F %T,%3N")"
local tag="$1"
@ -81,7 +86,8 @@ logger(){
local self="${BASH_SOURCE[0]}"
local caller="${FUNCNAME[1]}"
local line="${BASH_LINENO[0]}"
printf "%s␞%s␞%s::%s()::%s␞%s\n" "$date" "$tag" "$self" "$caller" "$line" "$string" >> "$debug_log"
printf "%s␞%s␞%s::%s()::%s␞%s\n" "$date" "$tag" "$self" "$caller" "$line" "$string" \
| redact >> "$debug_log"
}
setup_dirs(){
for dir in "$state_path" "$cache_path" "$share_path" "$helpers_path" "$freedesktop_path" "$config_path" "$log_path"; do
@ -312,13 +318,14 @@ check_unmerged(){
check_version(){
local version_url=$(format_version_url)
local upstream=$(curl -Ls "$version_url" | awk -F= '/^version=/ {print $2}')
local res=$(get_response_code "$version_url")
[[ $res -ne 200 ]] && raise_error_and_quit "Remote resource unavailable: '$version_url'"
logger INFO "Local branch: '$branch', local version: $version"
if [[ $branch == "stable" ]]; then
version_url="$stable_url/dzgui.sh"
elif [[ $branch == "testing" ]]; then
version_url="$testing_url/dzgui.sh"
fi
local upstream=$(curl -Ls "$version_url" | awk -F= '/^version=/ {print $2}')
[[ ! -f "$freedesktop_path/$app_name.desktop" ]] && freedesktop_dirs
if [[ $version == $upstream ]]; then
logger INFO "Local version is same as upstream"
@ -371,9 +378,10 @@ prompt_dl(){
}
dl_changelog(){
local mdbranch
local md
[[ $branch == "stable" ]] && mdbranch="dzgui"
[[ $branch == "testing" ]] && mdbranch="testing"
local md="https://raw.githubusercontent.com/$author/dztui/${mdbranch}/CHANGELOG.md"
local md="$url_prefix/${mdbranch}/$file"
curl -Ls "$md" > "$state_path/CHANGELOG.md"
}
test_display_mode(){
@ -402,13 +410,20 @@ check_architecture(){
}
check_map_count(){
[[ $is_steam_deck -gt 0 ]] && return 0
local count=1048576
local map_count_file="/proc/sys/vm/max_map_count"
local min_count=1048576
local conf_file="/etc/sysctl.d/dayz.conf"
if [[ -f $conf_file ]]; then
logger DEBUG "System map count is already $count or higher"
local current_count
if [[ ! -f ${map_count_file} ]]; then
logger WARN "File '${map_count_file}' doesn't exist!"
return 1
fi
current_count=$(cat ${map_count_file})
if [[ $current_count -ge $min_count ]]; then
logger DEBUG "System map count is set to ${current_count}"
return 0
fi
qdialog "sudo password required to check system vm map count." "OK" "Cancel"
qdialog "sudo password required to set system vm map count." "OK" "Cancel"
if [[ $? -eq 0 ]]; then
local pass
logger INFO "Prompting user for sudo escalation"
@ -417,13 +432,11 @@ check_map_count(){
logger WARN "User aborted password prompt"
return 1
fi
local ct=$(sudo -S <<< "$pass" sh -c "sysctl -q vm.max_map_count | awk -F'= ' '{print \$2}'")
logger DEBUG "Old map count is $ct"
local new_ct
[[ $ct -lt $count ]] && ct=$count
sudo -S <<< "$pass" sh -c "echo 'vm.max_map_count=$ct' > $conf_file"
logger DEBUG "Old map count is $current_count"
[[ $current_count -lt $min_count ]] && current_count=$min_count
sudo -S <<< "$pass" sh -c "echo 'vm.max_map_count=${current_count}' > $conf_file"
sudo sysctl -p "$conf_file"
logger DEBUG "Updated map count to $count"
logger DEBUG "Updated map count to $min_count"
else
logger WARN "User aborted map count prompt"
return 1
@ -479,7 +492,7 @@ stale_symlinks(){
local game_dir="$steam_path/steamapps/common/DayZ"
for l in $(find "$game_dir" -xtype l); do
logger DEBUG "Updating stale symlink '$l'"
unlink $l
unlink "$l"
done
}
local_latlon(){
@ -515,6 +528,7 @@ get_hash(){
md5sum "$1" | awk '{print $1}'
}
fetch_a2s(){
# this file is currently monolithic
[[ -d $helpers_path/a2s ]] && { logger INFO "A2S helper is current"; return 0; }
local sha=c7590ffa9a6d0c6912e17ceeab15b832a1090640
local author="yepoleb"
@ -522,6 +536,8 @@ fetch_a2s(){
local url="https://github.com/$author/$repo/tarball/$sha"
local prefix="${author^}-$repo-${sha:0:7}"
local file="$prefix.tar.gz"
local res=$(get_response_code "$url")
[[ $res -ne 200 ]] && raise_error_and_quit "Remote resource unavailable: '$file'"
curl -Ls "$url" > "$helpers_path/$file"
tar xf "$helpers_path/$file" -C "$helpers_path" "$prefix/a2s" --strip=1
rm "$helpers_path/$file"
@ -535,9 +551,11 @@ fetch_dzq(){
return 0
fi
local sha=3088bbfb147b77bc7b6a9425581b439889ff3f7f
local author="aclist"
local author="yepoleb"
local repo="dayzquery"
local url="https://raw.githubusercontent.com/$author/$repo/$sha/dayzquery.py"
local res=$(get_response_code "$url")
[[ $res -ne 200 ]] && raise_error_and_quit "Remote resource unavailable: 'dayzquery.py'"
curl -Ls "$url" > "$file"
logger INFO "Updated DZQ to sha '$sha'"
}
@ -569,10 +587,10 @@ fetch_helpers_by_sum(){
[[ -f "$config_file" ]] && source "$config_file"
declare -A sums
sums=(
["ui.py"]="dd7aa34df1d374739127cca3033a3f67"
["ui.py"]="5a876efacf208d12b5fe761996425412"
["query_v2.py"]="55d339ba02512ac69de288eb3be41067"
["vdf2json.py"]="2f49f6f5d3af919bebaab2e9c220f397"
["funcs"]="d8ae2662fbc3c62bdb5a51dec1935705"
["funcs"]="417bd5eaffbefc905a843985c691dc64"
["lan"]="c62e84ddd1457b71a85ad21da662b9af"
)
local author="aclist"
@ -596,11 +614,15 @@ fetch_helpers_by_sum(){
file="$i"
sum="${sums[$i]}"
full_path="$helpers_path/$file"
url="https://raw.githubusercontent.com/$author/$repo/$realbranch/helpers/$file"
url="${url_prefix}/$realbranch/helpers/$file"
if [[ -f "$full_path" ]] && [[ $(get_hash "$full_path") == $sum ]]; then
logger INFO "$file is current"
else
logger WARN "File '$full_path' checksum != '$sum'"
local res=$(get_response_code "$url")
[[ $res -ne 200 ]] && raise_error_and_quit "Remote resource unavailable: '$url'"
curl -Ls "$url" > "$full_path"
if [[ ! $? -eq 0 ]]; then
raise_error_and_quit "Failed to fetch the file '$file'. Possible timeout?"
@ -617,15 +639,19 @@ fetch_helpers_by_sum(){
}
fetch_geo_file(){
# for binary releases
local geo_sum="7b6668eb4535bb432acb42016ba9cc47"
local geo_sum="9824e9b9a75a4830a2423932cc188b06"
local km_sum="b038fdb8f655798207bd28de3a004706"
local gzip="$helpers_path/ips.csv.gz"
if [[ ! -f $geo_file ]] || [[ $(get_hash $geo_file) != $geo_sum ]]; then
local res=$(get_response_code "$geo_file_url")
[[ $res -ne 200 ]] && raise_error_and_quit "Remote resource unavailable: '$geo_file_url'"
curl -Ls "$geo_file_url" > "$gzip"
#force overwrite
gunzip -f "$gzip"
fi
if [[ ! -f $km_helper ]] || [[ $(get_hash $km_helper) != $km_sum ]]; then
local res=$(get_response_code "$km_helper_url")
[[ $res -ne 200 ]] && raise_error_and_quit "Remote resource unavailable: '$km_helper_url'"
curl -Ls "$km_helper_url" > "$km_helper"
chmod +x "$km_helper"
fi
@ -730,7 +756,7 @@ find_library_folder(){
local search_path="$1"
steam_path="$(python3 "$helpers_path/vdf2json.py" -i "$1/steamapps/libraryfolders.vdf" \
| jq -r '.libraryfolders[]|select(.apps|has("221100")).path')"
if [[ ! $? -eq 0 ]]; then
if [[ ! $? -eq 0 ]] || [[ -z $steam_path ]]; then
logger WARN "Failed to parse Steam path using '$search_path'"
return 1
fi
@ -783,7 +809,7 @@ create_config(){
find_library_folder "$default_steam_path"
if [[ -z $steam_path ]]; then
logger raise_error "Steam path was empty"
zenity --question --text="DayZ not found or not installed at the Steam library given." --ok-label="Choose path manually" --cancel-label="Exit"
zenity --question --text="DayZ not found or not installed at the Steam library given. NOTE: if you recently installed DayZ or moved its location, you MUST restart Steam first for these changes to synch." --ok-label="Choose path manually" --cancel-label="Exit"
if [[ $? -eq 0 ]]; then
logger INFO "User selected file picker"
file_picker
@ -845,14 +871,42 @@ is_steam_running(){
return 0
fi
}
get_response_code(){
local url="$1"
curl -Ls -I -o /dev/null -w "%{http_code}" "$url"
}
test_connection(){
ping -c1 -4 github.com 1>/dev/null 2>&1
if [[ ! $? -eq 0 ]]; then
raise_error_and_quit "No connection could be established to the remote server (github.com)."
source "$config_file"
declare -A hr
local res1
local res2
local str="No connection could be established to the remote server"
hr=(
["steampowered.com"]="https://api.steampowered.com/IGameServersService/GetServerList/v1/?key=$steam_api"
["github.com"]="https://github.com/$author"
["codeberg.org"]="https://codeberg.org/$author"
)
# steam API is mandatory, except on initial setup
if [[ -n $steam_api ]]; then
res=$(get_response_code "${hr["steampowered.com"]}")
[[ $res -ne 200 ]] && raise_error_and_quit "$str ("steampowered.com")"
fi
ping -c1 -4 api.steampowered.com 1>/dev/null 2>&1
if [[ ! $? -eq 0 ]]; then
raise_error_and_quit "No connection could be established to the remote server (steampowered.com)."
res=$(get_response_code "${hr["github.com"]}")
if [[ $res -ne 200 ]]; then
logger WARN "Remote host '${hr["github.com"]}' unreachable', trying fallback"
remote_host=cb
logger INFO "Set remote host to '${hr["codeberg.org"]}'"
res=$(get_response_code "${hr["codeberg.org"]}")
[[ $res -ne 200 ]] && raise_error_and_quit "$str (${hr["codeberg.org"]})"
fi
if [[ $remote_host == "cb" ]]; then
url_prefix="https://codeberg.org/$author/$repo/raw/branch"
releases_url="https://codeberg.org/$author/$repo/releases/download/browser"
stable_url="$url_prefix/dzgui"
testing_url="$url_prefix/testing"
km_helper_url="$releases_url/latlon"
geo_file_url="$releases_url/ips.csv.gz"
fi
}
legacy_cols(){
@ -862,6 +916,21 @@ legacy_cols(){
< $cols_file jq '.cols += { "Queue": 120 }' > $cols_file.new &&
mv $cols_file.new $cols_file
}
stale_mod_signatures(){
local workshop_dir="$steam_path/steamapps/workshop/content/$aid"
if [[ -d $workshop_dir ]]; then
readarray -t old_mod_ids < <(awk -F, '{print $1}' $versions_file)
for ((i=0; i<${#old_mod_ids[@]}; ++i)); do
if [[ ! -d $workshop_dir/${old_mod_ids[$i]} ]]; then
"$HOME/.local/share/$app_name/helpers/funcs" "align_local" "${old_mod_ids[$i]}"
fi
done
fi
}
create_new_links(){
"$HOME/.local/share/$app_name/helpers/funcs" "update_symlinks"
}
initial_setup(){
setup_dirs
setup_state_files
@ -882,6 +951,8 @@ initial_setup(){
steam_deps
migrate_files
stale_symlinks
stale_mod_signatures
create_new_links
local_latlon
is_steam_running
is_dzg_downloading
@ -936,6 +1007,9 @@ main(){
uninstall &&
exit 0
fi
if [[ $1 == "--steam" ]] || [[ $1 == "-s" ]]; then
export STEAM_LAUNCH=1
fi
set_im_module

View file

@ -1,6 +1,6 @@
#!/usr/bin/env bash
set -o pipefail
version=5.5.0
version=5.6.0
#CONSTANTS
aid=221100
@ -29,6 +29,7 @@ prefix="dzg"
log_path="$state_path/logs"
debug_log="$log_path/DZGUI_DEBUG.log"
system_log="$log_path/DZGUI_SYSTEM.log"
mod_log="$log_path/DZGUI_MODIDS.log"
#STATE FILES
history_file="$state_path/$prefix.history"
@ -38,6 +39,9 @@ lock_file="$state_path/$prefix.lock"
#CACHE
cache_dir="$HOME/.cache/$app_name"
_cache_servers="$cache_dir/$prefix.servers"
_cache_mods_temp="$cache_dir/$prefix.mods_temp"
_cache_stale_mods_temp="$cache_dir/$prefix.stale_mods_temp"
_cache_temp="$cache_dir/$prefix.temp"
_cache_my_servers="$cache_dir/$prefix.my_servers"
_cache_history="$cache_dir/$prefix.history"
_cache_launch="$cache_dir/$prefix.launch_mods"
@ -73,12 +77,9 @@ url_prefix="https://raw.githubusercontent.com/$author/$repo"
stable_url="$url_prefix/dzgui"
testing_url="$url_prefix/testing"
releases_url="$gh_prefix/$author/$repo/releases/download/browser"
km_helper_url="$releases_url/latlon"
db_file="$releases_url/ips.csv.gz"
sums_url="$stable_url/helpers/sums.md5"
#TODO: move adoc to index
help_url="https://$author.github.io/dzgui/dzgui"
forum_url="$gh_prefix/$author/$repo/discussions"
help_url="https://$author.github.io/dzgui/index"
help_url2="https://$author.codeberg.page"
forum_url="https://old.reddit.com/r/dzgui"
sponsor_url="$gh_prefix/sponsors/$author"
battlemetrics_server_url="https://www.battlemetrics.com/servers/dayz"
steam_api_url="https://steamcommunity.com/dev/apikey"
@ -93,6 +94,7 @@ else
fi
declare -A funcs=(
["Highlight stale"]="find_stale_mods"
["My servers"]="dump_servers"
["Change player name"]="update_config_val"
["Change Steam API key"]="update_config_val"
@ -104,6 +106,7 @@ declare -A funcs=(
["Connect by IP"]="validate_and_connect"
["Connect by ID"]="validate_and_connect"
["Connect from table"]="connect_from_table"
["find_id"]="find_id"
["toggle"]="toggle"
["Open link"]="open_link"
["filter"]="dump_servers"
@ -113,21 +116,25 @@ declare -A funcs=(
["test_cooldown"]="test_cooldown"
["query_config"]="query_config"
["start_cooldown"]="start_cooldown"
["list_mods"]="list_mods"
["delete"]="delete_local_mod"
["List installed mods"]="list_mods"
["Delete selected mods"]="delete_local_mod"
["align_local"]="align_versions_file"
["show_server_modlist"]="show_server_modlist"
["test_ping"]="test_ping"
["is_in_favs"]="is_in_favs"
["show_log"]="show_log"
["gen_log"]="generate_log"
["Output system info to log file"]="generate_log"
["open_user_workshop"]="open_user_workshop"
["open_workshop_page"]="open_workshop_page"
["Add to my servers"]="update_favs_from_table"
["Remove from my servers"]="update_favs_from_table"
["Remove from history"]="remove_from_history"
["force_update"]="force_update"
["Force update local mods"]="force_update"
["Resolve IP"]="resolve_ip"
["Handshake"]="final_handshake"
["get_player_count"]="get_player_count"
["lan_scan"]="lan_scan"
["update_symlinks"]="update_symlinks"
)
lan_scan(){
@ -140,6 +147,48 @@ lan_scan(){
printf "%s\n" "$res"
fi
}
find_stale_mods(){
local res
local mods=()
> $_cache_stale_mods_temp
for i in "${ip_list[@]}"; do
local ip=$(<<< "$i" awk -F: '{print $1}')
local qport=$(<<< "$i" awk -F: '{print $3}')
res=$(a2s $ip $qport rules)
if [[ -n $res ]]; then
printf "%s\n" "$res" >> $_cache_stale_mods_temp
fi
done
printf ""
return 99
}
resolve_ip(){
shift
local res
local record="$1"
local ip=$(<<< "$record" awk -F: '{print $1}')
local qport=$(<<< "$record" awk -F: '{print $3}')
res=$(a2s $ip $qport info)
if [[ ! $? -eq 0 ]]; then
printf "Server timed out \n"
return 1
fi
local gport=$(<<< "$res" jq -r '.[].gameport')
if [[ -z $gport ]]; then
printf "Failed to resolve server metadata\n"
return 1
fi
# incoming input can only be 'remove' or 'add'
# record is in favs => implies deletion
# record not in favs => implies addition
resolved_record="$ip:$gport:$qport"
if [[ ${ip_list[*]} =~ $resolved_record ]]; then
remove_from_favs "$resolved_record"
else
add_to_favs "$resolved_record"
fi
}
get_player_count(){
shift
local res
@ -232,6 +281,7 @@ add_record(){
fav_label=$(<<< "$res" jq -r '.[].name')
fav_server="$record"
update_config
echo "Updated favorite server to '$fav_server' ($fav_label)"
return 90
;;
"Add server by ID")
@ -314,30 +364,52 @@ is_in_favs(){
done
return 1
}
find_id(){
local file="$default_steam_path/config/loginusers.vdf"
[[ ! -f $file ]] && return 1
local res=$(python3 "$helpers_path/vdf2json.py" \
-i "$file" \
| jq -r '.users
|to_entries[]
|select(.value.MostRecent=="1")
|.key'
)
[[ -z $res ]] && return 1
printf "%s" "$res"
return 0
}
list_mods(){
local symlink
local sep
local name
local base_dir
local size
local mods
if [[ -z $(installed_mods) ]] || [[ -z $(find $workshop_dir -maxdepth 2 -name "*.cpp" | grep .cpp) ]]; then
echo "No mods currently installed or incorrect path set."
printf "No mods currently installed or incorrect path set."
logger WARN "Found no locally installed mods"
return 1
else
for dir in $(find $game_dir/* -maxdepth 1 -type l); do
mods=$(for dir in $(find $game_dir/* -maxdepth 1 -type l); do
symlink=$(basename $dir)
sep="␞"
name=$(awk -F\" '/name/ {print $2}' "${dir}/meta.cpp")
base_dir=$(basename $(readlink -f $game_dir/$symlink))
size=$(du -s "$(readlink -f "$game_dir/$symlink")" | awk '{print $1}')
size=$(python3 -c "n=($size/1024) +.005; print(round(n,4))")
printf "%s$sep%s$sep%s$sep%3.3f\n" "$name" "$symlink" "$base_dir" "$size"
done | sort -k1
LC_NUMERIC=C printf "%s$sep%s$sep%s$sep%3.3f\n" "$name" "$symlink" "$base_dir" "$size"
done | sort -k1)
# user may have manually pruned mods out-of-band
# handle directory detritus but no actual mods
if [[ -z $mods ]]; then
printf "No mods currently installed or incorrect path set."
return 1
fi
echo "$mods"
fi
}
installed_mods(){
ls -1 "$workshop_dir"
find "$workshop_dir" -maxdepth 1 -mindepth 1 -printf "%f\n"
}
local_latlon(){
local url="http://ip-api.com/json/$local_ip"
@ -380,9 +452,32 @@ get_dist(){
fi
}
get_remote_servers(){
local limit=20000
local url="https://api.steampowered.com/IGameServersService/GetServerList/v1/?filter=\appid\221100&limit=$limit&key=$steam_api"
curl -Ls "$url" | jq -r '.response.servers'
params=(
"\\nor\1\map\chernarusplus\\nor\1\map\sakhal"
"\map\chernarusplus\empty\1"
"\map\chernarusplus\noplayers\1"
"\map\\sakhal"
)
local limit=10000
local url="https://api.steampowered.com/IGameServersService/GetServerList/v1/?"
_fetch(){
local param="$1"
curl -LsG "$url" \
-d filter="\appid\221100${param}" \
-d limit=$limit \
-d key=$steam_api \
| jq -M -r '.response.servers'
}
for ((i=0; i <${#params[@]}; i++ )); do
_fetch "${params[$i]}" > $_cache_temp.${i}
done
# ubuntu variants do not support jq 1.7 chained operators
# https://github.com/jqlang/jq/releases/tag/jq-1.7
jq -n '[ [inputs]|add ][]' $_cache_temp.* && rm $_cache_temp.*
}
get_unique_maps(){
shift
@ -556,24 +651,60 @@ parse_server_json(){
"\(.addr|split(":")[1])"
' | sort -k1
}
align_versions_file(){
shift
local mod="$1"
[[ ! -f $versions_file ]] && return
< "$versions_file" awk -F, -v var="$mod" '$1 != var' > $versions_file.new &&
mv $versions_file.new $versions_file
logger INFO "Removed local signatures for the mod '$mod'"
}
pluralize(){
local plural="$1"
local count="$2"
local mod
local suffix
local base
local ct
local s
if [[ "${plural: -2}" == "es" ]]; then
base="${plural::-2}"
suffix="${plural: -2}"
ct=$((count^2))
[[ $ct -ne 3 ]] && s="$suffix"
else
base="${plural::-1}"
suffix="${plural: -1}"
ct=$((count^1))
[[ $ct -gt 0 ]] && s="$suffix"
fi
printf "%s%s" "$base" "$s"
}
delete_local_mod(){
shift
local symlink="$1"
local dir="$2"
[[ ! -d $workshop_dir/$dir ]] && return 1
[[ ! -L $game_dir/$symlink ]] && return 1
#SC2115
rm -rf "${workshop_dir:?}/$dir" && unlink "$game_dir/$symlink" || return 1
readarray -t symlinks < <(awk '{print $1}' $_cache_mods_temp)
readarray -t ids < <(awk '{print $2}' $_cache_mods_temp)
rm "$_cache_mods_temp"
for ((i=0; i<${#symlinks[@]}; i++)); do
[[ ! -d $workshop_dir/${ids[$i]} ]] && return 1
[[ ! -L $game_dir/${symlinks[$i]} ]] && return 1
#SC2115
rm -rf "${workshop_dir:?}/${ids[$i]}" && unlink "$game_dir/${symlinks[$i]}" || return 1
align_versions_file "align" "${ids[$i]}"
done
printf "Successfully deleted %s %s." "${#symlinks[@]}" "$(pluralize "mods" ${#symlinks[@]})"
return 95
}
test_cooldown(){
[[ ! -f $_cache_cooldown ]] && return 0
local old_time=$(< $_cache_cooldown)
local cur_time=$(date +%s)
local delta=$(($cur_time - $old_time))
local suffix="seconds"
if [[ $delta -lt 60 ]]; then
local remains=$((60 - $delta))
[[ $remains -eq 1 ]] && suffix="second"
local suffix=$(pluralize "seconds" $remains)
printf "Global API cooldown in effect. Please wait %s %s." "$remains" "$suffix"
exit 1
fi
@ -634,6 +765,9 @@ dump_servers(){
logger INFO "Server context is '$subcontext', reading from file '$file'"
filter_servers "$file" "$@"
}
redact(){
sed 's@\(/home/\)[^/]*@\1REDACTED@g'
}
logger(){
local date="$(date "+%F %T,%3N")"
local tag="$1"
@ -641,7 +775,8 @@ logger(){
local self="${BASH_SOURCE[0]}"
local caller="${FUNCNAME[1]}"
local line="${BASH_LINENO[0]}"
printf "%s␞%s␞%s::%s()::%s␞%s\n" "$date" "$tag" "$self" "$caller" "$line" "$string" >> "$debug_log"
printf "%s␞%s␞%s::%s()::%s␞%s\n" "$date" "$tag" "$self" "$caller" "$line" "$string" \
| redact >> "$debug_log"
}
test_ping(){
shift
@ -649,7 +784,7 @@ test_ping(){
local qport="$2"
local res
res=$(ping -c1 -4 -W0.5 $1 | grep time= | awk -F= '{print $4}')
[[ ! $? -eq 0 ]] && res="Unreachable"
[[ ! $? -eq 0 ]] && res="Timed out"
printf "%s" "$res"
}
show_server_modlist(){
@ -748,7 +883,45 @@ format_version_url(){
esac
echo "$version_url"
}
get_response_code(){
local url="$1"
curl -Ls -I -o /dev/null -w "%{http_code}" "$url"
}
test_connection(){
source "$config_file"
declare -A hr
local res1
local res2
local str="No connection could be established to the remote server"
hr=(
["github.com"]="https://github.com/$author"
["codeberg.org"]="https://codeberg.org/$author"
)
res=$(get_response_code "${hr["github.com"]}")
if [[ $res -ne 200 ]]; then
logger WARN "Remote host '${hr["github.com"]}' unreachable', trying fallback"
remote_host=cb
logger INFO "Set remote host to '${hr["codeberg.org"]}'"
res=$(get_response_code "${hr["codeberg.org"]}")
if [[ $res -ne 200 ]]; then
return 1
fi
fi
if [[ $remote_host == "cb" ]]; then
url_prefix="https://codeberg.org/$author/$repo/raw/branch"
releases_url="https://codeberg.org/$author/$repo/releases/download/browser"
stable_url="$url_prefix/dzgui"
testing_url="$url_prefix/testing"
fi
}
download_new_version(){
test_connection
rc=$?
if [[ $rc -eq 1 ]]; then
printf "Remote resource unavailable. Aborting"
logger WARN "Remote resource unavailable"
return 1
fi
local version_url="$(format_version_url)"
mv "$src_path" "$src_path.old"
curl -Ls "$version_url" > "$src_path"
@ -773,7 +946,7 @@ dl_changelog(){
local file="CHANGELOG.md"
[[ $branch == "stable" ]] && mdbranch="dzgui"
[[ $branch == "testing" ]] && mdbranch="testing"
local md="https://raw.githubusercontent.com/$author/$repo/${mdbranch}/$file"
local md="$url_prefix/${mdbranch}/$file"
curl -Ls "$md" > "$state_path/$file"
}
toggle(){
@ -788,6 +961,7 @@ toggle(){
fi
update_config
download_new_version
[[ $? -eq 1 ]] && return 1
return 255
;;
Toggle[[:space:]]mod[[:space:]]install[[:space:]]mode)
@ -851,7 +1025,7 @@ remove_from_favs(){
break
fi
done
if [[ ${#ip_list} -gt 0 ]]; then
if [[ ${#ip_list[@]} -gt 0 ]]; then
readarray -t ip_list < <(printf "%s\n" "${ip_list[@]}")
fi
update_config
@ -859,6 +1033,7 @@ remove_from_favs(){
local cache="$(< "$_cache_my_servers")"
<<< "$cache" grep -v -P "$r$" > $_cache_my_servers
logger INFO "Removed the record $record from saved servers"
echo "Removed $record from saved servers"
return 90
}
update_favs_from_table(){
@ -936,11 +1111,17 @@ update_config_val(){
show_log(){
< "$debug_log" sed 's/Keyword␞/Keyword/'
}
open_user_workshop(){
shift
local id="$1"
url="https://steamcommunity.com/profiles/$id/myworkshopfiles/?appid=$aid&browsefilter=mysubscriptions"
$steam_cmd steam://openurl/$url &
}
open_workshop_page(){
shift
local id="$1"
local workshop_uri="steam://url/CommunityFilePage/$id"
$steam_cmd "$workshop_uri" $id
$steam_cmd "$workshop_uri" $id &
}
open_link(){
shift
@ -956,21 +1137,21 @@ open_link(){
"Open Battlemetrics API page")
url="$battlemetrics_api_url"
;;
"Help file ⧉")
"Documentation/help files (GitHub) ⧉")
url="$help_url"
;;
"Report a bug ⧉")
"Documentation/help files (Codeberg mirror) ⧉")
url="$help_url2"
;;
"Report a bug (GitHub) ⧉")
url="$issues_url"
;;
"Forum ⧉")
"DZGUI Subreddit ⧉")
url="$forum_url"
;;
"Sponsor ⧉")
"Sponsor (GitHub) ⧉")
url="$sponsor_url"
;;
"Hall of fame ⧉")
url="${help_url}#_hall_of_fame"
;;
esac
#if [[ $is_steam_deck -eq 1 ]]; then
@ -1055,31 +1236,79 @@ compare(){
echo "$diff"
}
legacy_symlinks(){
for d in "$game_dir"/*; do
if [[ $d =~ @[0-9]+-.+ ]]; then
unlink "$d"
fi
done
for d in "$workshop_dir"/*; do
local id=$(awk -F"= " '/publishedid/ {print $2}' "$d"/meta.cpp | awk -F\; '{print $1}')
local encoded_id=$(echo "$id" | awk '{printf("%c",$1)}' | base64 | sed 's/\//_/g; s/=//g; s/+/]/g')
if [[ -h "$game_dir/@$encoded_id" ]]; then
unlink "$game_dir/@$encoded_id"
fi
done
logger INFO "Removing legacy symlinks"
readarray -t stale_mod_dirs1 < <(find "$workshop_dir" -maxdepth 1 -mindepth 1 -type l -name '@?*-*')
logger INFO "Read local mods into array 1 with length: ${#stale_mod_dirs[@]}"
if [[ ${#stale_mod_dirs1} -ne 0 ]]; then
for d in "${stale_mod_dirs1[@]}"; do
unlink "$game_dir/$d"
done
fi
readarray -t stale_mod_dirs2 < <(find "$workshop_dir" -maxdepth 1 -mindepth 1 -type l -name '@??')
logger INFO "Read local mods into array 2 with length: ${#stale_mod_dirs[@]}"
if [[ ${#stale_mod_dirs2} -eq 0 ]]; then
for d in "${stale_mod_dirs2[@]}"; do
unlink "$game_dir/$d"
done
fi
}
symlinks(){
for d in "$workshop_dir"/*; do
id=$(awk -F"= " '/publishedid/ {print $2}' "$d"/meta.cpp | awk -F\; '{print $1}')
encoded_id=$(encode "$id")
link="@$encoded_id"
if [[ -h "$game_dir/$link" ]]; then
logger INFO "Symlink already exists: '$link' for mod '$id'"
continue
_merge(){
comm -23 <(printf "%s\n" "${mods[@]}" | sort) <(printf "%s\n" "${targets[@]}" | sort)
}
_pulse(){
zenity --pulsate --progress --auto-close --no-cancel --title="DZGUI"
}
_create_links(){
local arr=("$@")
local encoded_id
local link
local mod
for ((i=0; i<${#arr[@]}; i++)); do
encoded_id=$(encode "${arr[$i]}")
link="@$encoded_id"
mod="${arr[$i]}"
logger INFO "Creating link '$game_dir/$link' for '$workshop_dir/$mod'"
[[ $STEAM_LAUNCH -eq 1 ]] && echo "# Creating mod link $((i+1))/${#arr[@]}"
ln -s "$workshop_dir/$mod" "$game_dir/$link"
done
}
readarray -t mods < <(find "$workshop_dir" -mindepth 1 -name meta.cpp | awk -F/ 'NF=NF-1{print $NF}')
readarray -t links < <(find "$game_dir" -maxdepth 1 -type l)
if [[ ${#mods[@]} -eq 0 ]]; then
logger INFO "No mods present, aborting"
return
fi
if [[ ${#links[@]} -eq 0 ]]; then
logger INFO "No symlinks present in '$game_dir', creating them"
if [[ $STEAM_LAUNCH -eq 1 ]]; then
_create_links "${mods[@]}" > >(_pulse)
else
_create_links "${mods[@]}"
fi
ln -fs "$d" "$game_dir/$link"
logger INFO "Created symlink '$link' for mod '$id'"
done
return
fi
readarray -t targets < <(printf "%s\n" "${links[@]}" | xargs readlink -f | awk -F/ '{print $NF}')
readarray -t hits < <(_merge)
if [[ ${#hits[@]} -eq 0 ]]; then
logger INFO "Symlinks are up to date, skipping"
return
fi
# update missing targets
logger INFO "Found ${#hits[@]} unlinked mods"
if [[ $STEAM_LAUNCH -eq 1 ]]; then
_create_links "${hits[@]}" > >(_pulse)
else
_create_links "${hits[@]}"
fi
}
update_history(){
local record="$1"

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 KiB

After

Width:  |  Height:  |  Size: 130 KiB

47
install.sh Normal file → Executable file
View file

@ -1,8 +1,41 @@
#!/usr/bin/env bash
curl "https://raw.githubusercontent.com/aclist/dztui/dzgui/dzgui.sh" > dzgui.sh
chmod +x dzgui.sh
xdg_file="$HOME/.local/share/applications/dzgui.desktop"
share="$HOME/.local/share/dzgui"
[[ -f $xdg_file ]] && rm $xdg_file
[[ -d $share ]] && rm -rf "$share"
./dzgui.sh
get_response_code(){
local url="$1"
curl -Ls -I -o /dev/null -w "%{http_code}" "$url"
}
abort(){
printf "Remote resource not available. Try again later.\n"
exit 1
}
fetch(){
local file="dzgui.sh"
local author="aclist"
local repo="dztui"
local branch="dzgui"
local url
local res
gh_url="https://raw.githubusercontent.com/$author/$repo/$branch/$file"
cb_url="https://codeberg.org/$author/$repo/raw/branch/$branch/$file"
url="$gh_url"
printf "Checking the remote resource at '%s'\n" "$url"
res=$(get_response_code "$url")
if [[ $res -ne 200 ]]; then
url="$cb_url"
printf "Checking the remote resource at '%s'\n" "$url"
res=$(get_response_code "$url")
if [[ $res -ne 200 ]]; then
abort
fi
fi
curl -L "$url" > dzgui.sh
chmod +x dzgui.sh
xdg_file="$HOME/.local/share/applications/dzgui.desktop"
share="$HOME/.local/share/dzgui"
[[ -f $xdg_file ]] && rm $xdg_file
[[ -d $share ]] && rm -rf "$share"
./dzgui.sh
}
fetch