mirror of
https://github.com/aclist/dztui.git
synced 2026-11-03 00:22:04 +01:00
Compare commits
41
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b47cb482f5 | ||
|
|
152999d728 | ||
|
|
cdcb3b794f | ||
|
|
cd23f403c9 | ||
|
|
a40bef8f82 | ||
|
|
5c07a77b63 | ||
|
|
91773c933c | ||
|
|
6a30ed44fc | ||
|
|
bbaec35a5f | ||
|
|
c46477f0a7 | ||
|
|
f47446a2e1 | ||
|
|
cdb311cb1f | ||
|
|
11cdcb62b2 | ||
|
|
1913f69e7b | ||
|
|
8c2cea0fdc | ||
|
|
8c29b9b35a | ||
|
|
d8239f1c34 | ||
|
|
8c3ec4512a | ||
|
|
1a18c8ff02 | ||
|
|
92c23ec609 | ||
|
|
e9d4054e76 | ||
|
|
1c9e026f70 | ||
|
|
eb076b7dcd | ||
|
|
8629e02670 | ||
|
|
3ba38f30ad | ||
|
|
7c37fd8b6e | ||
|
|
1a065123bb | ||
|
|
2586306323 | ||
|
|
1ccb99d935 | ||
|
|
054fe968d1 | ||
|
|
1760e6e928 | ||
|
|
a2a39e9927 | ||
|
|
e664f68624 | ||
|
|
2e41856348 | ||
|
|
27d444ea6f | ||
|
|
26a2362172 | ||
|
|
50cf347164 | ||
|
|
dd69bc88b1 | ||
|
|
5c84b5ae0a | ||
|
|
e92df8c062 | ||
|
|
494311e8df |
No files matched your search
+29
-28
@@ -1,6 +1,7 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## Added
|
## [7.0.0] Unreleased
|
||||||
|
### Added
|
||||||
- Setup wizard
|
- Setup wizard
|
||||||
- Changelog text wrapping and formatting
|
- Changelog text wrapping and formatting
|
||||||
- Changelog ships with source
|
- Changelog ships with source
|
||||||
@@ -44,7 +45,7 @@
|
|||||||
- Generate Steam shortcuts and cover art
|
- Generate Steam shortcuts and cover art
|
||||||
- Warn user if background downloads are disabled
|
- Warn user if background downloads are disabled
|
||||||
|
|
||||||
## Changed
|
### Changed
|
||||||
- Conform to PEP 440 versioning for beta versions
|
- Conform to PEP 440 versioning for beta versions
|
||||||
- Reduce padding on keys button
|
- Reduce padding on keys button
|
||||||
- Boldface breadcrumbs
|
- Boldface breadcrumbs
|
||||||
@@ -61,58 +62,58 @@
|
|||||||
- Enable LAN page Empty/Full filters on first run of app
|
- Enable LAN page Empty/Full filters on first run of app
|
||||||
- Propagate subscribed mods to Steam client
|
- Propagate subscribed mods to Steam client
|
||||||
|
|
||||||
## Dropped
|
### Dropped
|
||||||
- Debug mode
|
- Debug mode
|
||||||
- Branch switching
|
- Branch switching
|
||||||
- Manual mod install mode (describe rationale)
|
- Manual mod install mode (describe rationale)
|
||||||
- Force update mods
|
- Force update mods
|
||||||
|
|
||||||
## Fixed
|
### Fixed
|
||||||
- Longstanding issue with left clicks not registering as tree selection changes after spamming keyboard input
|
- Longstanding issue with left clicks not registering as tree selection changes after spamming keyboard input
|
||||||
- Center server title text on server dialogs
|
- Center server title text on server dialogs
|
||||||
- Rare segfaults when changing maps (threading)
|
- Rare segfaults when changing maps (threading)
|
||||||
- Moved dialogs out of threads
|
- Moved dialogs out of threads
|
||||||
|
|
||||||
## Unreleased
|
### Unreleased
|
||||||
- Load offline mods
|
- Load offline mods
|
||||||
- Local documentation
|
- Local documentation
|
||||||
- Raw debug command in context menu
|
- Raw debug command in context menu
|
||||||
|
|
||||||
## Developer-facing
|
### Developer-facing
|
||||||
- Add pyproject.toml file
|
- Add pyproject.toml file
|
||||||
- Show deprecation warnings
|
- Show deprecation warnings
|
||||||
- Options -> Dev page
|
- Options -> Dev page
|
||||||
|
|
||||||
## [6.0.5] 2026-05-17
|
## [6.0.5] 2026-05-17
|
||||||
## Changed
|
### Changed
|
||||||
- Update symlink hash logic to align with DZGUI 7 beta
|
- Update symlink hash logic to align with DZGUI 7 beta
|
||||||
- Update A2S helper to latest commit
|
- Update A2S helper to latest commit
|
||||||
|
|
||||||
## [6.0.4] 2026-04-13
|
## [6.0.4] 2026-04-13
|
||||||
## Fixed
|
### Fixed
|
||||||
- Update A2S module checksums to support servers with missing description field
|
- Update A2S module checksums to support servers with missing description field
|
||||||
|
|
||||||
## [6.0.3] 2026-04-12
|
## [6.0.3] 2026-04-12
|
||||||
## Fixed
|
### Fixed
|
||||||
- Use GLibUnix namespace where available when setting up SIGINT callback
|
- Use GLibUnix namespace where available when setting up SIGINT callback
|
||||||
|
|
||||||
## [6.0.2] 2026-01-26
|
## [6.0.2] 2026-01-26
|
||||||
## Fixed
|
### Fixed
|
||||||
- Explicitly use Python 3.13 when calling subprocesses
|
- Explicitly use Python 3.13 when calling subprocesses
|
||||||
- Raise error correctly from module
|
- Raise error correctly from module
|
||||||
- Do not spawn dialogs from inside of thread when fetching prereqs
|
- Do not spawn dialogs from inside of thread when fetching prereqs
|
||||||
## Changed
|
### Changed
|
||||||
- Optimize time complexity of startup checks
|
- Optimize time complexity of startup checks
|
||||||
- Optimize coordinate calculation by using local records first
|
- Optimize coordinate calculation by using local records first
|
||||||
- Rename some internal functions
|
- Rename some internal functions
|
||||||
- Wrap entire startup process in dialog
|
- Wrap entire startup process in dialog
|
||||||
|
|
||||||
## [6.0.1] 2026-01-16
|
## [6.0.1] 2026-01-16
|
||||||
## Fixed
|
### Fixed
|
||||||
- Explicitly use Python 3.13 when calling subprocesses
|
- Explicitly use Python 3.13 when calling subprocesses
|
||||||
|
|
||||||
## [6.0.0] 2026-01-14
|
## [6.0.0] 2026-01-14
|
||||||
## Added
|
### Added
|
||||||
- Support DayZ Experimental
|
- Support DayZ Experimental
|
||||||
- Ping display on server tables
|
- Ping display on server tables
|
||||||
- Save descriptive text notes on a per-server basis
|
- Save descriptive text notes on a per-server basis
|
||||||
@@ -138,7 +139,7 @@
|
|||||||
- Commandline usage help text (GaryBlackbourne)
|
- Commandline usage help text (GaryBlackbourne)
|
||||||
- More descriptive help dialogs when initial dependencies are missing
|
- More descriptive help dialogs when initial dependencies are missing
|
||||||
|
|
||||||
## Fixed
|
### Fixed
|
||||||
- Script failing to start when remote endpoints are unavailable (GaryBlackbourne)
|
- Script failing to start when remote endpoints are unavailable (GaryBlackbourne)
|
||||||
- Key stickiness when quickly navigating through entries in tables
|
- Key stickiness when quickly navigating through entries in tables
|
||||||
- Servers returning malformed A2S_INFO blocking server browser from loading
|
- Servers returning malformed A2S_INFO blocking server browser from loading
|
||||||
@@ -177,7 +178,7 @@
|
|||||||
- Path discovery during first-time setup when parsing filepaths with whitespaces
|
- Path discovery during first-time setup when parsing filepaths with whitespaces
|
||||||
- First-time setup dialog continuously triggering when DayZ install path had whitespaces in it
|
- First-time setup dialog continuously triggering when DayZ install path had whitespaces in it
|
||||||
|
|
||||||
## Changed
|
### Changed
|
||||||
- Require Python 3.13
|
- Require Python 3.13
|
||||||
- Reduced global API cooldown from 60s to 30s
|
- Reduced global API cooldown from 60s to 30s
|
||||||
- Clarify dialog messages when DayZ path could not be found
|
- Clarify dialog messages when DayZ path could not be found
|
||||||
@@ -189,50 +190,50 @@
|
|||||||
- Rewrote distance calculation module (GaryBlackbourne)
|
- Rewrote distance calculation module (GaryBlackbourne)
|
||||||
- Changed preferred client setting from radio toggle to combobox
|
- Changed preferred client setting from radio toggle to combobox
|
||||||
|
|
||||||
## Dropped
|
### Dropped
|
||||||
- Ping readout in statusbar
|
- Ping readout in statusbar
|
||||||
- Extraneous information from right statusbar
|
- Extraneous information from right statusbar
|
||||||
|
|
||||||
## [5.8.3] 2026-01-04
|
## [5.8.3] 2026-01-04
|
||||||
## Fixed
|
### Fixed
|
||||||
- Normalize checksum numbers and dates
|
- Normalize checksum numbers and dates
|
||||||
|
|
||||||
## [5.8.2] 2026-01-04
|
## [5.8.2] 2026-01-04
|
||||||
## Fixed
|
### Fixed
|
||||||
- Fix regression in symlink routine introduced by previous hotfix
|
- Fix regression in symlink routine introduced by previous hotfix
|
||||||
|
|
||||||
## [5.8.1] 2025-12-27
|
## [5.8.1] 2025-12-27
|
||||||
## Fixed
|
### Fixed
|
||||||
- Path discovery during first-time setup when parsing filepaths with whitespaces
|
- Path discovery during first-time setup when parsing filepaths with whitespaces
|
||||||
- First-time setup dialog continuously triggering when DayZ install path had whitespaces in it
|
- First-time setup dialog continuously triggering when DayZ install path had whitespaces in it
|
||||||
|
|
||||||
## [5.8.0] 2025-07-06
|
## [5.8.0] 2025-07-06
|
||||||
## Added
|
### Added
|
||||||
- Filter servers by official/unofficial status
|
- Filter servers by official/unofficial status
|
||||||
- Automatically fetch geolocation records
|
- Automatically fetch geolocation records
|
||||||
## Changed
|
### Changed
|
||||||
- Updated internal versioning of helper files
|
- Updated internal versioning of helper files
|
||||||
## Fixed
|
### Fixed
|
||||||
- Corrected erroneous 2024 date in prior changelog entries
|
- Corrected erroneous 2024 date in prior changelog entries
|
||||||
|
|
||||||
## [5.7.0] 2025-04-17
|
## [5.7.0] 2025-04-17
|
||||||
## Added
|
### Added
|
||||||
- Save application dimensions when quitting and restore on subsequent boot
|
- Save application dimensions when quitting and restore on subsequent boot
|
||||||
## Fixed
|
### Fixed
|
||||||
- Issues with the window exceeding the bounds of the screen when displaying some table contexts
|
- Issues with the window exceeding the bounds of the screen when displaying some table contexts
|
||||||
## Changed
|
### Changed
|
||||||
- More direct server ping query method that should return marginally more accurate times
|
- More direct server ping query method that should return marginally more accurate times
|
||||||
|
|
||||||
## [5.6.7] 2025-04-04
|
## [5.6.7] 2025-04-04
|
||||||
## Dropped
|
### Dropped
|
||||||
- Removed extraneous pre-boot API checks that could cause error messages to be printed if the user had not set up an API key yet
|
- Removed extraneous pre-boot API checks that could cause error messages to be printed if the user had not set up an API key yet
|
||||||
|
|
||||||
## [5.6.6] 2025-03-16
|
## [5.6.6] 2025-03-16
|
||||||
## Changed
|
### Changed
|
||||||
- Update IP database records for 2025-03
|
- Update IP database records for 2025-03
|
||||||
|
|
||||||
## [5.6.5] 2025-03-04
|
## [5.6.5] 2025-03-04
|
||||||
## Fixed
|
### Fixed
|
||||||
- Livonia server results being dropped from batch queries
|
- Livonia server results being dropped from batch queries
|
||||||
|
|
||||||
## [5.6.4] 2025-02-10
|
## [5.6.4] 2025-02-10
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@ If you are a developer, you can skip to the end.
|
|||||||
# Requesting help
|
# Requesting help
|
||||||
|
|
||||||
If you encounter a problem with DZGUI, you can submit tickets on the GitHub
|
If you encounter a problem with DZGUI, you can submit tickets on the GitHub
|
||||||
(issue tracker)[https://github.com/aclist/dztui/issues] under the
|
[issue tracker](https://github.com/aclist/dztui/issues) under the
|
||||||
`troubleshooting` tag.
|
`troubleshooting` tag.
|
||||||
|
|
||||||
# How can I help the project?
|
# How can I help the project?
|
||||||
@@ -24,7 +24,7 @@ accordingly.
|
|||||||
|
|
||||||
## Submitting a ticket
|
## Submitting a ticket
|
||||||
|
|
||||||
Navigate to the GitHub (issue tracker)[https://github.com/aclist/dztui/issues].
|
Navigate to the GitHub [issue tracker](https://github.com/aclist/dztui/issues).
|
||||||
From there, follow the onscreen prompts. You will be asked questions such as:
|
From there, follow the onscreen prompts. You will be asked questions such as:
|
||||||
|
|
||||||
- What version are you using?
|
- What version are you using?
|
||||||
|
|||||||
@@ -86,7 +86,6 @@ class WorkshopACF(ACF):
|
|||||||
def __init__(self, file: Path) -> None:
|
def __init__(self, file: Path) -> None:
|
||||||
super().__init__(file)
|
super().__init__(file)
|
||||||
|
|
||||||
# TODO: break into workshop parser and generic acf parser
|
|
||||||
def unpack(self, d: dict, lines: list[Any] = []) -> str:
|
def unpack(self, d: dict, lines: list[Any] = []) -> str:
|
||||||
t1 = "AppWorkshop"
|
t1 = "AppWorkshop"
|
||||||
t2 = ("WorkshopItemsInstalled", "WorkshopItemDetails")
|
t2 = ("WorkshopItemsInstalled", "WorkshopItemDetails")
|
||||||
|
|||||||
+27
-12
@@ -12,6 +12,7 @@ from typing import Any
|
|||||||
from dzgui.api.steam import find_user_id_32
|
from dzgui.api.steam import find_user_id_32
|
||||||
from dzgui.const.constants import APP_NAME, APP_NAME_LOWER, IMAGES_PATH
|
from dzgui.const.constants import APP_NAME, APP_NAME_LOWER, IMAGES_PATH
|
||||||
from dzgui.util.dirs import copy_dzgui_to_xdg_data
|
from dzgui.util.dirs import copy_dzgui_to_xdg_data
|
||||||
|
from dzgui.util.strings import unknown
|
||||||
|
|
||||||
logger = logging.getLogger(APP_NAME)
|
logger = logging.getLogger(APP_NAME)
|
||||||
|
|
||||||
@@ -46,6 +47,25 @@ class Shortcuts:
|
|||||||
wrapped_exe = f'"{exe}"'
|
wrapped_exe = f'"{exe}"'
|
||||||
return appname + wrapped_exe
|
return appname + wrapped_exe
|
||||||
|
|
||||||
|
def find_appname_by_unsigned_id(self, appid: int) -> str:
|
||||||
|
# NOTE: bitmask signed int back to 32-bit CRC
|
||||||
|
# varying client versions treat case sensitivity differently
|
||||||
|
try:
|
||||||
|
for key in self.shortcuts["shortcuts"].keys():
|
||||||
|
if self.shortcuts["shortcuts"][key]["appid"] & 0xFFFFFFFF == appid:
|
||||||
|
try:
|
||||||
|
return str(self.shortcuts["shortcuts"][key]["appname"])
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(e)
|
||||||
|
try:
|
||||||
|
return str(self.shortcuts["shortcuts"][key]["AppName"])
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(e)
|
||||||
|
return unknown
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(e)
|
||||||
|
return unknown
|
||||||
|
|
||||||
def get_shortcuts(self) -> Any:
|
def get_shortcuts(self) -> Any:
|
||||||
return self.shortcuts
|
return self.shortcuts
|
||||||
|
|
||||||
@@ -59,7 +79,7 @@ class Shortcuts:
|
|||||||
logger.critical(e)
|
logger.critical(e)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def find_shortcuts_path(self, steam_path: Path) -> None:
|
def find_shortcuts_path(self, steam_path: Path) -> Path:
|
||||||
uid = find_user_id_32(steam_path)
|
uid = find_user_id_32(steam_path)
|
||||||
self.user_config_path = steam_path.joinpath(f"userdata/{uid}/config")
|
self.user_config_path = steam_path.joinpath(f"userdata/{uid}/config")
|
||||||
return self.user_config_path.joinpath("shortcuts.vdf")
|
return self.user_config_path.joinpath("shortcuts.vdf")
|
||||||
@@ -111,22 +131,18 @@ class Shortcuts:
|
|||||||
https://developer.valvesoftware.com/wiki/Add_Non-Steam_Game
|
https://developer.valvesoftware.com/wiki/Add_Non-Steam_Game
|
||||||
https://developer.valvesoftware.com/wiki/Steam_Library_Shortcuts
|
https://developer.valvesoftware.com/wiki/Steam_Library_Shortcuts
|
||||||
|
|
||||||
Wiki variously lists keys with title case and lowercase, but keys actually do
|
Key case is not internally consistent and varies between client versions
|
||||||
not seem to be case-sensitive. Some entries generated by Steam do not match the wiki
|
Most keys use Pascal case, but some do not
|
||||||
|
|
||||||
Keys are entered into the dictionary in a linear insertion order
|
|
||||||
|
|
||||||
# TODO: try to replicate key case sensitivity as it is created by Steam with generic shortcut
|
|
||||||
|
|
||||||
appid: signed int CRC
|
appid: signed int CRC
|
||||||
exe: absolute path to the executable, must be wrapped in literal quotes
|
Exe: absolute path to the executable, must be wrapped in literal quotes
|
||||||
StartDir: directory the executable starts in, generally the parent
|
StartDir: directory the executable starts in, generally the parent
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NEW_ENTRY: dict[str, Any] = {}
|
NEW_ENTRY: dict[str, Any] = {}
|
||||||
NEW_ENTRY["appid"] = meta.appid
|
NEW_ENTRY["appid"] = meta.appid
|
||||||
NEW_ENTRY["appname"] = meta.appname
|
NEW_ENTRY["AppName"] = meta.appname
|
||||||
NEW_ENTRY["exe"] = f'"{meta.exe_path}"'
|
NEW_ENTRY["Exe"] = f'"{meta.exe_path}"'
|
||||||
NEW_ENTRY["StartDir"] = f"{meta.start_dir}"
|
NEW_ENTRY["StartDir"] = f"{meta.start_dir}"
|
||||||
NEW_ENTRY["icon"] = meta.icon
|
NEW_ENTRY["icon"] = meta.icon
|
||||||
NEW_ENTRY["ShortcutPath"] = ""
|
NEW_ENTRY["ShortcutPath"] = ""
|
||||||
@@ -134,7 +150,7 @@ class Shortcuts:
|
|||||||
NEW_ENTRY["IsHidden"] = 0
|
NEW_ENTRY["IsHidden"] = 0
|
||||||
NEW_ENTRY["AllowDesktopConfig"] = 1
|
NEW_ENTRY["AllowDesktopConfig"] = 1
|
||||||
NEW_ENTRY["AllowOverlay"] = 1
|
NEW_ENTRY["AllowOverlay"] = 1
|
||||||
NEW_ENTRY["openvr"] = 0
|
NEW_ENTRY["OpenVR"] = 0
|
||||||
NEW_ENTRY["Devkit"] = 0
|
NEW_ENTRY["Devkit"] = 0
|
||||||
NEW_ENTRY["DevkitGameID"] = ""
|
NEW_ENTRY["DevkitGameID"] = ""
|
||||||
NEW_ENTRY["DevkitOverrideAppID"] = 0
|
NEW_ENTRY["DevkitOverrideAppID"] = 0
|
||||||
@@ -162,7 +178,6 @@ def add_steam_shortcut(steam_path: Path, exe_path: Path) -> None:
|
|||||||
images = Path(str(traversable))
|
images = Path(str(traversable))
|
||||||
icon = images.joinpath("icon.png")
|
icon = images.joinpath("icon.png")
|
||||||
|
|
||||||
# TEST: assert that record (start dir and exe combo) is in shortcuts after creation
|
|
||||||
shortcuts = Shortcuts(Path(steam_path))
|
shortcuts = Shortcuts(Path(steam_path))
|
||||||
shortcuts.add_shortcut(APP_NAME, start_dir, exe_path, icon)
|
shortcuts.add_shortcut(APP_NAME, start_dir, exe_path, icon)
|
||||||
shortcuts.save_shortcuts()
|
shortcuts.save_shortcuts()
|
||||||
|
|||||||
+50
-26
@@ -15,6 +15,7 @@ from dzgui.const.constants import (
|
|||||||
APPID_DAYZ,
|
APPID_DAYZ,
|
||||||
APPID_DAYZ_EXP,
|
APPID_DAYZ_EXP,
|
||||||
APP_NAME,
|
APP_NAME,
|
||||||
|
DAYZ_BINARY,
|
||||||
DEBIAN_STEAM_PATH,
|
DEBIAN_STEAM_PATH,
|
||||||
DEFAULT_STEAM_PATH,
|
DEFAULT_STEAM_PATH,
|
||||||
FLATPAK_STEAM_PATH,
|
FLATPAK_STEAM_PATH,
|
||||||
@@ -31,7 +32,6 @@ from dzgui.const.endpoints import (
|
|||||||
)
|
)
|
||||||
from dzgui.strings import wizard
|
from dzgui.strings import wizard
|
||||||
from dzgui.util.bash import concat_bash_args
|
from dzgui.util.bash import concat_bash_args
|
||||||
from dzgui.util.strings import unknown
|
|
||||||
|
|
||||||
logger = logging.getLogger(APP_NAME)
|
logger = logging.getLogger(APP_NAME)
|
||||||
|
|
||||||
@@ -205,20 +205,28 @@ def launch_offline(
|
|||||||
proc = subprocess.run([*client_args, *params])
|
proc = subprocess.run([*client_args, *params])
|
||||||
return proc.returncode
|
return proc.returncode
|
||||||
|
|
||||||
|
def find_loginusers(path: Path) -> Path:
|
||||||
|
return path / "config" / "loginusers.vdf"
|
||||||
|
|
||||||
def find_user_id(path: Path) -> str | None:
|
def find_user_id(path: Path) -> str | None:
|
||||||
resolved_path = path / "config" / "loginusers.vdf"
|
resolved_path = find_loginusers(path)
|
||||||
try:
|
try:
|
||||||
with open(resolved_path, "r") as f:
|
with open(resolved_path, "r") as f:
|
||||||
v = vdf.load(f)
|
v = vdf.load(f)
|
||||||
|
# NOTE: beta client
|
||||||
|
# /package/beta
|
||||||
|
if len(v["users"]) == 1:
|
||||||
|
return str(list(v["users"].keys())[0])
|
||||||
for user in v["users"]:
|
for user in v["users"]:
|
||||||
if v["users"][user]["MostRecent"] == "1":
|
if v["users"][user]["MostRecent"] == "1":
|
||||||
return str(user)
|
return str(user)
|
||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warn(e)
|
logger.debug(e)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def find_user_id_32(path: Path) -> int:
|
def find_user_id_32(path: Path) -> int:
|
||||||
uid = find_user_id(path)
|
uid = find_user_id(path)
|
||||||
if uid is None:
|
if uid is None:
|
||||||
@@ -301,28 +309,34 @@ def get_running_app() -> int | None:
|
|||||||
PROC_NAME = "steam"
|
PROC_NAME = "steam"
|
||||||
SUBPROC_NAME = "reaper"
|
SUBPROC_NAME = "reaper"
|
||||||
FLAG = "AppId"
|
FLAG = "AppId"
|
||||||
|
# NOTE: may cause conflicts if multiple apps are running
|
||||||
for proc in psutil.process_iter():
|
try:
|
||||||
if proc.name() == PROC_NAME:
|
for proc in psutil.process_iter():
|
||||||
subprocs = proc.children()
|
if proc.name() == PROC_NAME:
|
||||||
filtered = (proc for proc in subprocs if proc.name() == SUBPROC_NAME)
|
subprocs = proc.children()
|
||||||
try:
|
filtered = (proc for proc in subprocs if proc.name() == SUBPROC_NAME)
|
||||||
proc = next(filtered)
|
try:
|
||||||
except StopIteration:
|
proc = next(filtered)
|
||||||
return None
|
except StopIteration:
|
||||||
args = proc.cmdline()
|
return None
|
||||||
appid = (row for row in args if FLAG in row)
|
args = proc.cmdline()
|
||||||
try:
|
appid = (row for row in args if FLAG in row)
|
||||||
return int(next(appid).split("=")[1])
|
try:
|
||||||
except StopIteration:
|
return int(next(appid).split("=")[1])
|
||||||
return None
|
except StopIteration:
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(e)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_app_allows_downloads(path: Path, appid: int) -> bool:
|
def get_app_allows_downloads(path: Path, appid: int) -> bool:
|
||||||
root_path = get_app_path(path, appid)
|
try:
|
||||||
acf = root_path.joinpath(f"steamapps/appmanifest_{appid}.acf")
|
root_path = get_app_path(path, appid)
|
||||||
flag = ACF(acf).get_allows_downloads()
|
acf = root_path.joinpath(f"steamapps/appmanifest_{appid}.acf")
|
||||||
|
flag = ACF(acf).get_allows_downloads()
|
||||||
|
except AppNotInstalledError:
|
||||||
|
flag = 0
|
||||||
match flag:
|
match flag:
|
||||||
# NOTE: adheres to global client setting
|
# NOTE: adheres to global client setting
|
||||||
case 0:
|
case 0:
|
||||||
@@ -337,9 +351,11 @@ def get_app_allows_downloads(path: Path, appid: int) -> bool:
|
|||||||
case _:
|
case _:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def get_config(path: Path) -> Path:
|
def get_config(path: Path) -> Path:
|
||||||
return path.joinpath("config/config.vdf")
|
return path.joinpath("config/config.vdf")
|
||||||
|
|
||||||
|
|
||||||
def get_client_allows_downloads(path: Path) -> bool:
|
def get_client_allows_downloads(path: Path) -> bool:
|
||||||
config = get_config(path)
|
config = get_config(path)
|
||||||
try:
|
try:
|
||||||
@@ -360,8 +376,16 @@ def get_client_allows_downloads(path: Path) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def is_dayz_running() -> bool:
|
def is_dayz_running() -> bool:
|
||||||
appid = get_running_app()
|
"""Subprocesses spawned from Steam will not show up in regular process tree"""
|
||||||
return appid in (APPID_DAYZ, APPID_DAYZ_EXP)
|
procs = []
|
||||||
|
substring = DAYZ_BINARY
|
||||||
|
for proc in psutil.process_iter():
|
||||||
|
try:
|
||||||
|
procs.append(proc.cmdline())
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(e)
|
||||||
|
continue
|
||||||
|
return any(substring in item for sublist in procs for item in sublist)
|
||||||
|
|
||||||
|
|
||||||
def get_app_path(folders_path: Path, appid: int) -> Path:
|
def get_app_path(folders_path: Path, appid: int) -> Path:
|
||||||
@@ -391,13 +415,13 @@ def get_app_path(folders_path: Path, appid: int) -> Path:
|
|||||||
return Path(app_path)
|
return Path(app_path)
|
||||||
|
|
||||||
|
|
||||||
def get_app_name(appid: int) -> str:
|
def get_app_name(appid: int) -> str | None:
|
||||||
payload = {"appids": [appid]}
|
payload = {"appids": [appid]}
|
||||||
res = requests.get(APP_DETAILS, params=payload)
|
res = requests.get(APP_DETAILS, params=payload)
|
||||||
if res.status_code != 200:
|
if res.status_code != 200:
|
||||||
return unknown
|
return None
|
||||||
try:
|
try:
|
||||||
return str(res.json()[str(appid)]["data"]["name"])
|
return str(res.json()[str(appid)]["data"]["name"])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(e)
|
logger.debug(e)
|
||||||
return unknown
|
return None
|
||||||
+29
-28
@@ -1,6 +1,7 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## Added
|
## [7.0.0] Unreleased
|
||||||
|
### Added
|
||||||
- Setup wizard
|
- Setup wizard
|
||||||
- Changelog text wrapping and formatting
|
- Changelog text wrapping and formatting
|
||||||
- Changelog ships with source
|
- Changelog ships with source
|
||||||
@@ -44,7 +45,7 @@
|
|||||||
- Generate Steam shortcuts and cover art
|
- Generate Steam shortcuts and cover art
|
||||||
- Warn user if background downloads are disabled
|
- Warn user if background downloads are disabled
|
||||||
|
|
||||||
## Changed
|
### Changed
|
||||||
- Conform to PEP 440 versioning for beta versions
|
- Conform to PEP 440 versioning for beta versions
|
||||||
- Reduce padding on keys button
|
- Reduce padding on keys button
|
||||||
- Boldface breadcrumbs
|
- Boldface breadcrumbs
|
||||||
@@ -61,58 +62,58 @@
|
|||||||
- Enable LAN page Empty/Full filters on first run of app
|
- Enable LAN page Empty/Full filters on first run of app
|
||||||
- Propagate subscribed mods to Steam client
|
- Propagate subscribed mods to Steam client
|
||||||
|
|
||||||
## Dropped
|
### Dropped
|
||||||
- Debug mode
|
- Debug mode
|
||||||
- Branch switching
|
- Branch switching
|
||||||
- Manual mod install mode (describe rationale)
|
- Manual mod install mode (describe rationale)
|
||||||
- Force update mods
|
- Force update mods
|
||||||
|
|
||||||
## Fixed
|
### Fixed
|
||||||
- Longstanding issue with left clicks not registering as tree selection changes after spamming keyboard input
|
- Longstanding issue with left clicks not registering as tree selection changes after spamming keyboard input
|
||||||
- Center server title text on server dialogs
|
- Center server title text on server dialogs
|
||||||
- Rare segfaults when changing maps (threading)
|
- Rare segfaults when changing maps (threading)
|
||||||
- Moved dialogs out of threads
|
- Moved dialogs out of threads
|
||||||
|
|
||||||
## Unreleased
|
### Unreleased
|
||||||
- Load offline mods
|
- Load offline mods
|
||||||
- Local documentation
|
- Local documentation
|
||||||
- Raw debug command in context menu
|
- Raw debug command in context menu
|
||||||
|
|
||||||
## Developer-facing
|
### Developer-facing
|
||||||
- Add pyproject.toml file
|
- Add pyproject.toml file
|
||||||
- Show deprecation warnings
|
- Show deprecation warnings
|
||||||
- Options -> Dev page
|
- Options -> Dev page
|
||||||
|
|
||||||
## [6.0.5] 2026-05-17
|
## [6.0.5] 2026-05-17
|
||||||
## Changed
|
### Changed
|
||||||
- Update symlink hash logic to align with DZGUI 7 beta
|
- Update symlink hash logic to align with DZGUI 7 beta
|
||||||
- Update A2S helper to latest commit
|
- Update A2S helper to latest commit
|
||||||
|
|
||||||
## [6.0.4] 2026-04-13
|
## [6.0.4] 2026-04-13
|
||||||
## Fixed
|
### Fixed
|
||||||
- Update A2S module checksums to support servers with missing description field
|
- Update A2S module checksums to support servers with missing description field
|
||||||
|
|
||||||
## [6.0.3] 2026-04-12
|
## [6.0.3] 2026-04-12
|
||||||
## Fixed
|
### Fixed
|
||||||
- Use GLibUnix namespace where available when setting up SIGINT callback
|
- Use GLibUnix namespace where available when setting up SIGINT callback
|
||||||
|
|
||||||
## [6.0.2] 2026-01-26
|
## [6.0.2] 2026-01-26
|
||||||
## Fixed
|
### Fixed
|
||||||
- Explicitly use Python 3.13 when calling subprocesses
|
- Explicitly use Python 3.13 when calling subprocesses
|
||||||
- Raise error correctly from module
|
- Raise error correctly from module
|
||||||
- Do not spawn dialogs from inside of thread when fetching prereqs
|
- Do not spawn dialogs from inside of thread when fetching prereqs
|
||||||
## Changed
|
### Changed
|
||||||
- Optimize time complexity of startup checks
|
- Optimize time complexity of startup checks
|
||||||
- Optimize coordinate calculation by using local records first
|
- Optimize coordinate calculation by using local records first
|
||||||
- Rename some internal functions
|
- Rename some internal functions
|
||||||
- Wrap entire startup process in dialog
|
- Wrap entire startup process in dialog
|
||||||
|
|
||||||
## [6.0.1] 2026-01-16
|
## [6.0.1] 2026-01-16
|
||||||
## Fixed
|
### Fixed
|
||||||
- Explicitly use Python 3.13 when calling subprocesses
|
- Explicitly use Python 3.13 when calling subprocesses
|
||||||
|
|
||||||
## [6.0.0] 2026-01-14
|
## [6.0.0] 2026-01-14
|
||||||
## Added
|
### Added
|
||||||
- Support DayZ Experimental
|
- Support DayZ Experimental
|
||||||
- Ping display on server tables
|
- Ping display on server tables
|
||||||
- Save descriptive text notes on a per-server basis
|
- Save descriptive text notes on a per-server basis
|
||||||
@@ -138,7 +139,7 @@
|
|||||||
- Commandline usage help text (GaryBlackbourne)
|
- Commandline usage help text (GaryBlackbourne)
|
||||||
- More descriptive help dialogs when initial dependencies are missing
|
- More descriptive help dialogs when initial dependencies are missing
|
||||||
|
|
||||||
## Fixed
|
### Fixed
|
||||||
- Script failing to start when remote endpoints are unavailable (GaryBlackbourne)
|
- Script failing to start when remote endpoints are unavailable (GaryBlackbourne)
|
||||||
- Key stickiness when quickly navigating through entries in tables
|
- Key stickiness when quickly navigating through entries in tables
|
||||||
- Servers returning malformed A2S_INFO blocking server browser from loading
|
- Servers returning malformed A2S_INFO blocking server browser from loading
|
||||||
@@ -177,7 +178,7 @@
|
|||||||
- Path discovery during first-time setup when parsing filepaths with whitespaces
|
- Path discovery during first-time setup when parsing filepaths with whitespaces
|
||||||
- First-time setup dialog continuously triggering when DayZ install path had whitespaces in it
|
- First-time setup dialog continuously triggering when DayZ install path had whitespaces in it
|
||||||
|
|
||||||
## Changed
|
### Changed
|
||||||
- Require Python 3.13
|
- Require Python 3.13
|
||||||
- Reduced global API cooldown from 60s to 30s
|
- Reduced global API cooldown from 60s to 30s
|
||||||
- Clarify dialog messages when DayZ path could not be found
|
- Clarify dialog messages when DayZ path could not be found
|
||||||
@@ -189,50 +190,50 @@
|
|||||||
- Rewrote distance calculation module (GaryBlackbourne)
|
- Rewrote distance calculation module (GaryBlackbourne)
|
||||||
- Changed preferred client setting from radio toggle to combobox
|
- Changed preferred client setting from radio toggle to combobox
|
||||||
|
|
||||||
## Dropped
|
### Dropped
|
||||||
- Ping readout in statusbar
|
- Ping readout in statusbar
|
||||||
- Extraneous information from right statusbar
|
- Extraneous information from right statusbar
|
||||||
|
|
||||||
## [5.8.3] 2026-01-04
|
## [5.8.3] 2026-01-04
|
||||||
## Fixed
|
### Fixed
|
||||||
- Normalize checksum numbers and dates
|
- Normalize checksum numbers and dates
|
||||||
|
|
||||||
## [5.8.2] 2026-01-04
|
## [5.8.2] 2026-01-04
|
||||||
## Fixed
|
### Fixed
|
||||||
- Fix regression in symlink routine introduced by previous hotfix
|
- Fix regression in symlink routine introduced by previous hotfix
|
||||||
|
|
||||||
## [5.8.1] 2025-12-27
|
## [5.8.1] 2025-12-27
|
||||||
## Fixed
|
### Fixed
|
||||||
- Path discovery during first-time setup when parsing filepaths with whitespaces
|
- Path discovery during first-time setup when parsing filepaths with whitespaces
|
||||||
- First-time setup dialog continuously triggering when DayZ install path had whitespaces in it
|
- First-time setup dialog continuously triggering when DayZ install path had whitespaces in it
|
||||||
|
|
||||||
## [5.8.0] 2025-07-06
|
## [5.8.0] 2025-07-06
|
||||||
## Added
|
### Added
|
||||||
- Filter servers by official/unofficial status
|
- Filter servers by official/unofficial status
|
||||||
- Automatically fetch geolocation records
|
- Automatically fetch geolocation records
|
||||||
## Changed
|
### Changed
|
||||||
- Updated internal versioning of helper files
|
- Updated internal versioning of helper files
|
||||||
## Fixed
|
### Fixed
|
||||||
- Corrected erroneous 2024 date in prior changelog entries
|
- Corrected erroneous 2024 date in prior changelog entries
|
||||||
|
|
||||||
## [5.7.0] 2025-04-17
|
## [5.7.0] 2025-04-17
|
||||||
## Added
|
### Added
|
||||||
- Save application dimensions when quitting and restore on subsequent boot
|
- Save application dimensions when quitting and restore on subsequent boot
|
||||||
## Fixed
|
### Fixed
|
||||||
- Issues with the window exceeding the bounds of the screen when displaying some table contexts
|
- Issues with the window exceeding the bounds of the screen when displaying some table contexts
|
||||||
## Changed
|
### Changed
|
||||||
- More direct server ping query method that should return marginally more accurate times
|
- More direct server ping query method that should return marginally more accurate times
|
||||||
|
|
||||||
## [5.6.7] 2025-04-04
|
## [5.6.7] 2025-04-04
|
||||||
## Dropped
|
### Dropped
|
||||||
- Removed extraneous pre-boot API checks that could cause error messages to be printed if the user had not set up an API key yet
|
- Removed extraneous pre-boot API checks that could cause error messages to be printed if the user had not set up an API key yet
|
||||||
|
|
||||||
## [5.6.6] 2025-03-16
|
## [5.6.6] 2025-03-16
|
||||||
## Changed
|
### Changed
|
||||||
- Update IP database records for 2025-03
|
- Update IP database records for 2025-03
|
||||||
|
|
||||||
## [5.6.5] 2025-03-04
|
## [5.6.5] 2025-03-04
|
||||||
## Fixed
|
### Fixed
|
||||||
- Livonia server results being dropped from batch queries
|
- Livonia server results being dropped from batch queries
|
||||||
|
|
||||||
## [5.6.4] 2025-02-10
|
## [5.6.4] 2025-02-10
|
||||||
|
|||||||
@@ -55,8 +55,11 @@ class ConfigManager:
|
|||||||
self.update_config(Preferences.IP_LIST, ips)
|
self.update_config(Preferences.IP_LIST, ips)
|
||||||
|
|
||||||
def update_history_file(self, fqip: str) -> None:
|
def update_history_file(self, fqip: str) -> None:
|
||||||
with open(self.prefs.paths.history, "r") as f:
|
try:
|
||||||
ips = [line.rstrip() for line in f.readlines()]
|
with open(self.prefs.paths.history, "r") as f:
|
||||||
|
ips = [line.rstrip() for line in f.readlines()]
|
||||||
|
except FileNotFoundError:
|
||||||
|
ips = []
|
||||||
|
|
||||||
seen = set()
|
seen = set()
|
||||||
ips.append(fqip)
|
ips.append(fqip)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from typing import TYPE_CHECKING
|
|||||||
|
|
||||||
import dzgui.api.pefile as PeFile
|
import dzgui.api.pefile as PeFile
|
||||||
import dzgui.api.servers as Servers
|
import dzgui.api.servers as Servers
|
||||||
|
from dzgui.api.shortcuts import Shortcuts
|
||||||
|
|
||||||
from dzgui.api.steam import (
|
from dzgui.api.steam import (
|
||||||
connect,
|
connect,
|
||||||
@@ -175,6 +176,12 @@ class ConnectionManager:
|
|||||||
if running_app is not None:
|
if running_app is not None:
|
||||||
allows_dl = get_app_allows_downloads(steam_path, running_app)
|
allows_dl = get_app_allows_downloads(steam_path, running_app)
|
||||||
running_app_name = get_app_name(running_app)
|
running_app_name = get_app_name(running_app)
|
||||||
|
# NOTE: for out-of-range appid, assume NSG.
|
||||||
|
# dzgui.api.steam.get_app_name() will return None
|
||||||
|
if running_app_name is None:
|
||||||
|
s = Shortcuts(steam_path)
|
||||||
|
# NOTE: returns "Unknown" if unparseable
|
||||||
|
running_app_name = s.find_appname_by_unsigned_id(running_app)
|
||||||
allows_downloads = (allows_dl, running_app_name)
|
allows_downloads = (allows_dl, running_app_name)
|
||||||
else:
|
else:
|
||||||
allows_downloads = (True, "")
|
allows_downloads = (True, "")
|
||||||
|
|||||||
@@ -440,7 +440,8 @@ class Assistant(Gtk.Assistant):
|
|||||||
def __init__(self, is_deck: bool, XDG: "Xdg"):
|
def __init__(self, is_deck: bool, XDG: "Xdg"):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
if is_deck:
|
if is_deck:
|
||||||
self.fullscreen()
|
# NOTE: deemed to be "safe" dimensions that exclude taskbar size
|
||||||
|
self.set_default_size(1085, 670)
|
||||||
else:
|
else:
|
||||||
self.set_default_size(1500, 900)
|
self.set_default_size(1500, 900)
|
||||||
|
|
||||||
@@ -484,8 +485,6 @@ class Assistant(Gtk.Assistant):
|
|||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
# NOTE: disabled for now on system-provided packages
|
# NOTE: disabled for now on system-provided packages
|
||||||
# TODO: drop, for testing purposes
|
|
||||||
os.environ["PYAPP"] = str(Path.home().joinpath("dzgui/dzgui"))
|
|
||||||
if isinstance(page, ShortcutCreationPage) and os.getenv("PYAPP") is None:
|
if isinstance(page, ShortcutCreationPage) and os.getenv("PYAPP") is None:
|
||||||
continue
|
continue
|
||||||
self._add_page(page, page.get_page_type())
|
self._add_page(page, page.get_page_type())
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from importlib import resources
|
from importlib import resources
|
||||||
@@ -32,15 +33,63 @@ class Changelog(HelpMenuMixin, ScrollableMixin, Gtk.ScrolledWindow): # type: ig
|
|||||||
|
|
||||||
# TODO: should long text be wrapped?
|
# TODO: should long text be wrapped?
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
formatted = format_pango(changelog)
|
self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5, margin_top=10)
|
||||||
self.changelog_label = Gtk.Label(valign=Gtk.Align.START, margin=15)
|
|
||||||
self.changelog_label.set_markup(formatted)
|
|
||||||
self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
|
||||||
self.box.add(self.changelog_label)
|
|
||||||
self.add(self.box)
|
self.add(self.box)
|
||||||
|
|
||||||
self.connect("key-press-event", self._on_keypress)
|
self.connect("key-press-event", self._on_keypress)
|
||||||
self.connect("key-press-event", self._on_esc_keypress)
|
self.connect("key-press-event", self._on_esc_keypress)
|
||||||
|
|
||||||
|
changes = self._parse(changelog)
|
||||||
|
self._generate_nodes(changes)
|
||||||
|
self.show_all()
|
||||||
|
|
||||||
def grab_content_area(self) -> None:
|
def grab_content_area(self) -> None:
|
||||||
self.grab_focus()
|
self.grab_focus()
|
||||||
|
|
||||||
|
def _parse_version(self, version: str) -> tuple[str, str]:
|
||||||
|
pattern = r"(^##\s\[)(.+)(\] )(.+$)"
|
||||||
|
match = re.match(pattern, version)
|
||||||
|
if match:
|
||||||
|
version = match.group(2)
|
||||||
|
date = match.group(4)
|
||||||
|
return version, date
|
||||||
|
raise ValueError("Changelog parse error")
|
||||||
|
|
||||||
|
|
||||||
|
def _generate_nodes(self, releases: list[tuple[str, list[str]]]) -> None:
|
||||||
|
for header, changes in releases:
|
||||||
|
for ind in (0, -1):
|
||||||
|
if changes[ind] == "":
|
||||||
|
del changes[ind]
|
||||||
|
text = "\n".join(changes)
|
||||||
|
formatted = format_pango(text)
|
||||||
|
|
||||||
|
label = Gtk.Label(valign=Gtk.Align.START, margin=15, halign=Gtk.Align.START)
|
||||||
|
label.set_markup(formatted)
|
||||||
|
|
||||||
|
version, date = self._parse_version(header)
|
||||||
|
button_text = f"{version} ({date})"
|
||||||
|
button = Gtk.Button(label=button_text, margin_start=5)
|
||||||
|
|
||||||
|
expander = Gtk.Expander()
|
||||||
|
expander.set_label_widget(button)
|
||||||
|
expander.add(label)
|
||||||
|
self.box.add(expander)
|
||||||
|
|
||||||
|
|
||||||
|
def _parse(self, changelog: str) -> list[tuple[str, list[str]]]:
|
||||||
|
release = ""
|
||||||
|
releases: list[tuple[str, list[str]]] = []
|
||||||
|
release_notes: list[str] = []
|
||||||
|
for line in changelog.splitlines():
|
||||||
|
if line.startswith("# "):
|
||||||
|
continue
|
||||||
|
if line.startswith("## "):
|
||||||
|
if release != "":
|
||||||
|
releases.append((release, release_notes))
|
||||||
|
release_notes = []
|
||||||
|
release = ""
|
||||||
|
release = line
|
||||||
|
continue
|
||||||
|
release_notes.append(line.rstrip())
|
||||||
|
return releases
|
||||||
@@ -273,7 +273,7 @@ class PreConnectionAssistant(Gtk.Box):
|
|||||||
)
|
)
|
||||||
|
|
||||||
allows_dl, running_app = prereqs.allows_downloads
|
allows_dl, running_app = prereqs.allows_downloads
|
||||||
if allows_dl is False:
|
if len(prereqs.mods) > 0 and allows_dl is False:
|
||||||
msg = (
|
msg = (
|
||||||
f"The game '{running_app}' is currently running in Steam, but background downloads are not enabled.\n"
|
f"The game '{running_app}' is currently running in Steam, but background downloads are not enabled.\n"
|
||||||
"Either stop the game first, or update your global Steam settings or the game's local settings.\n"
|
"Either stop the game first, or update your global Steam settings or the game's local settings.\n"
|
||||||
|
|||||||
+2
-2
@@ -4,7 +4,7 @@ description = "DayZ server browser and mod manager for Linux"
|
|||||||
authors = [
|
authors = [
|
||||||
{name = "aclist"}
|
{name = "aclist"}
|
||||||
]
|
]
|
||||||
version = "7.0.0b15"
|
version = "7.0.0b19"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
license-files = ["LICENSE"]
|
license-files = ["LICENSE"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -76,7 +76,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
line-length = 88
|
line-length = 88
|
||||||
indent-width = 4
|
indent-width = 4
|
||||||
extend-exclude = ["*lib/a2s"]
|
extend-exclude = ["*lib/a2s"]
|
||||||
lint.unfixable = ["F401"]
|
lint.unfixable = ["F401", "I001"]
|
||||||
|
|
||||||
[tool.ruff.format]
|
[tool.ruff.format]
|
||||||
quote-style = "double"
|
quote-style = "double"
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ This file includes the combined licenses to the following components:
|
|||||||
- psutil
|
- psutil
|
||||||
- PyGObject
|
- PyGObject
|
||||||
- python-a2s
|
- python-a2s
|
||||||
|
- vdf
|
||||||
|
|
||||||
When shipped as a binary installer, these components are collectively bundled
|
When shipped as a binary installer, these components are collectively bundled
|
||||||
as a turnkey application and used to run the DZGUI utility.\n\n
|
as a turnkey application and used to run the DZGUI utility.\n\n
|
||||||
|
|||||||
Vendored
File renamed without changes.
Vendored
File renamed without changes.
+13
@@ -0,0 +1,13 @@
|
|||||||
|
"users"
|
||||||
|
{
|
||||||
|
"0"
|
||||||
|
{
|
||||||
|
"AccountName" "STEAMUSER"
|
||||||
|
"PersonaName" "STEAMUSER"
|
||||||
|
"RememberPassword" "0"
|
||||||
|
"WantsOfflineMode" "0"
|
||||||
|
"SkipOfflineModeWarning" "0"
|
||||||
|
"AutoLogin" "1"
|
||||||
|
"Timestamp" "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
"users"
|
||||||
|
{
|
||||||
|
"0"
|
||||||
|
{
|
||||||
|
"AccountName" "STEAMUSER"
|
||||||
|
"PersonaName" "STEAMUSER"
|
||||||
|
"RememberPassword" "0"
|
||||||
|
"WantsOfflineMode" "0"
|
||||||
|
"SkipOfflineModeWarning" "0"
|
||||||
|
"AllowAutoLogin" "1"
|
||||||
|
"MostRecent" "1"
|
||||||
|
"Timestamp" "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
"users"
|
||||||
|
{
|
||||||
|
"0"
|
||||||
|
{
|
||||||
|
"AccountName" "STEAMUSER"
|
||||||
|
"PersonaName" "STEAMUSER"
|
||||||
|
"RememberPassword" "0"
|
||||||
|
"WantsOfflineMode" "0"
|
||||||
|
"SkipOfflineModeWarning" "0"
|
||||||
|
"AllowAutoLogin" "1"
|
||||||
|
"MostRecent" "0"
|
||||||
|
"Timestamp" "0"
|
||||||
|
}
|
||||||
|
"1"
|
||||||
|
{
|
||||||
|
"AccountName" "STEAMUSER2"
|
||||||
|
"PersonaName" "STEAMUSER2"
|
||||||
|
"RememberPassword" "0"
|
||||||
|
"WantsOfflineMode" "0"
|
||||||
|
"SkipOfflineModeWarning" "0"
|
||||||
|
"AllowAutoLogin" "1"
|
||||||
|
"MostRecent" "1"
|
||||||
|
"Timestamp" "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
File renamed without changes.
Vendored
BIN
Binary file not shown.
File renamed without changes.
@@ -0,0 +1,25 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from dzgui.api.steam import find_user_id
|
||||||
|
from tests.fixtures import fixture_path
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.apitest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"fixture, expect",
|
||||||
|
[
|
||||||
|
("api/loginusers_legacy_client.vdf", 0),
|
||||||
|
("api/loginusers_legacy_client_multiple.vdf", 1),
|
||||||
|
("api/loginusers_beta_client.vdf", 0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_loginusers(monkeypatch, fixture: str, expect: int) -> None:
|
||||||
|
def mock_loginusers(path: Path) -> Path:
|
||||||
|
return fixture_path(fixture)
|
||||||
|
|
||||||
|
monkeypatch.setattr("dzgui.api.steam.find_loginusers", mock_loginusers)
|
||||||
|
uid = find_user_id(Path(""))
|
||||||
|
assert int(uid) == expect
|
||||||
+26
-12
@@ -1,8 +1,14 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
import dzgui.api.pefile as PeFile
|
import dzgui.api.pefile as PeFile
|
||||||
from dzgui.api.steam import VDFLoadError, AppNotInstalledError, AppMovedError, get_app_path
|
from dzgui.api.steam import (
|
||||||
|
VDFLoadError,
|
||||||
|
AppNotInstalledError,
|
||||||
|
AppMovedError,
|
||||||
|
get_app_path
|
||||||
|
)
|
||||||
|
|
||||||
from dzgui.config.query import get_config
|
from dzgui.config.query import get_config
|
||||||
from dzgui.config.xdg import get_xdg_paths, parse_filepaths
|
from dzgui.config.xdg import get_xdg_paths, parse_filepaths
|
||||||
@@ -10,12 +16,7 @@ from dzgui.const.constants import APPID_DAYZ
|
|||||||
|
|
||||||
from tests.fixtures import fixture_path
|
from tests.fixtures import fixture_path
|
||||||
|
|
||||||
pytestmark = pytest.mark.pefile
|
pytestmark = pytest.mark.apitest
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def second_drive():
|
|
||||||
return fixture_path("in_library_on_second_drive.vdf")
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@@ -26,6 +27,8 @@ def default_steam_path():
|
|||||||
return conf["default_steam_path"]
|
return conf["default_steam_path"]
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: split into separate test file
|
||||||
|
# FIXME: use fixtures
|
||||||
def test_pefile_length(default_steam_path):
|
def test_pefile_length(default_steam_path):
|
||||||
try:
|
try:
|
||||||
pe_file_path = PeFile.get_pefile_path(Path(default_steam_path), APPID_DAYZ)
|
pe_file_path = PeFile.get_pefile_path(Path(default_steam_path), APPID_DAYZ)
|
||||||
@@ -52,15 +55,26 @@ def test_invalid_pefile_path():
|
|||||||
("malformed.vdf", VDFLoadError),
|
("malformed.vdf", VDFLoadError),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_not_in_library(fixture, exception):
|
def test_not_in_library(monkeypatch, fixture, exception):
|
||||||
fixture = fixture_path(fixture)
|
import dzgui.api.steam as steam
|
||||||
|
monkeypatch.setattr(steam, "LIBRARYFOLDERS_PATH", fixture)
|
||||||
|
|
||||||
|
folder_path = Path(fixture_path("api"))
|
||||||
with pytest.raises(exception):
|
with pytest.raises(exception):
|
||||||
try:
|
try:
|
||||||
get_app_path(fixture, APPID_DAYZ)
|
get_app_path(folder_path, APPID_DAYZ)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def test_on_second_drive(second_drive):
|
@pytest.fixture
|
||||||
path = get_app_path(second_drive, APPID_DAYZ)
|
def second_drive():
|
||||||
|
return "in_library_on_second_drive.vdf"
|
||||||
|
|
||||||
|
|
||||||
|
def test_on_second_drive(monkeypatch, second_drive):
|
||||||
|
import dzgui.api.steam as steam
|
||||||
|
monkeypatch.setattr(steam, "LIBRARYFOLDERS_PATH", second_drive)
|
||||||
|
folder_path = Path(fixture_path("api"))
|
||||||
|
path = get_app_path(folder_path, APPID_DAYZ)
|
||||||
assert path == Path("/tmp")
|
assert path == Path("/tmp")
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import pytest
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from _pytest.monkeypatch import MonkeyPatch
|
||||||
|
|
||||||
|
from dzgui.api.shortcuts import Shortcuts
|
||||||
|
from tests.fixtures import fixture_path
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.apitest
|
||||||
|
|
||||||
|
|
||||||
|
def mock_find_shortcuts(self, steam_path: Path) -> Path:
|
||||||
|
return Path(fixture_path("api/no_shortcuts.vdf"))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module", autouse=True)
|
||||||
|
def patch_api() -> None:
|
||||||
|
mp = MonkeyPatch()
|
||||||
|
mp.setattr("dzgui.api.shortcuts.Shortcuts.find_shortcuts_path", mock_find_shortcuts)
|
||||||
|
yield
|
||||||
|
mp.undo()
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_shortcuts(monkeypatch) -> None:
|
||||||
|
s = Shortcuts(Path(""))
|
||||||
|
assert len(s.shortcuts["shortcuts"]) == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def dummy_app() -> None:
|
||||||
|
d = {
|
||||||
|
"AppName": "TEST APP",
|
||||||
|
"StartDir": "TEST_DIR",
|
||||||
|
"Exe": "TEST_DIR/TEST_EXE.EXE",
|
||||||
|
"icon": "IMAGES_DIR/TEST_IMAGE.PNG",
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
def test_wrap_exe(dummy_app) -> None:
|
||||||
|
s = Shortcuts(Path(""))
|
||||||
|
s.add_shortcut(*dummy_app.values())
|
||||||
|
assert s.shortcuts["shortcuts"]["0"]["Exe"][0] == '"'
|
||||||
|
assert s.shortcuts["shortcuts"]["0"]["Exe"][-1] == '"'
|
||||||
|
|
||||||
|
|
||||||
|
def test_add_shortcut(dummy_app) -> None:
|
||||||
|
s = Shortcuts(Path(""))
|
||||||
|
s.add_shortcut(*dummy_app.values())
|
||||||
|
new = s.shortcuts["shortcuts"]
|
||||||
|
ind = str(len(new) - 1)
|
||||||
|
for k, v in dummy_app.items():
|
||||||
|
if k == "Exe":
|
||||||
|
v = f'"{v}"'
|
||||||
|
assert new[ind][k] == v
|
||||||
|
|
||||||
|
|
||||||
|
def test_save_shortcut(dummy_app) -> None:
|
||||||
|
s = Shortcuts(Path(""))
|
||||||
|
s.add_shortcut(*dummy_app.values())
|
||||||
|
with tempfile.NamedTemporaryFile() as f:
|
||||||
|
tmp = Path(f.name)
|
||||||
|
s.shortcuts_path = tmp
|
||||||
|
s.save_shortcuts()
|
||||||
|
s._load_shortcuts(tmp)
|
||||||
|
assert len(s.shortcuts["shortcuts"]) == 1
|
||||||
Reference in new issue
Block a user