Merge pull request #468 from aclist/fix/wizard-config-exit
Mirror to Codeberg / mirror-to-codeberg (push) Has been cancelled

fix: write config later in install wizard
This commit is contained in:
aclist authored and GitHub committed 2026-08-29 16:21:57 +09:00
commit 3c92108ac3
17 files changed
+140 -72

No files matched your search

+1
View File
@@ -90,6 +90,7 @@
- Rare segfaults when changing maps (threading) - Rare segfaults when changing maps (threading)
- Moved dialogs out of threads - Moved dialogs out of threads
- History table not being in lock-step with state file - History table not being in lock-step with state file
- Current keyword filter not being applied when refreshing table
### Unreleased ### Unreleased
- Load offline mods - Load offline mods
+9 -4
View File
@@ -346,10 +346,15 @@ def get_details(record: Record) -> Details:
try: try:
dlc = rules.dlc_flags dlc = rules.dlc_flags
if dlc == 0: match dlc:
dlc = strings.none case 0:
if dlc == 2: dlc = strings.dlc_none
dlc = strings.dlc_frostline case 2:
dlc = strings.dlc_frostline
case 3:
dlc = strings.dlc_badlands
case _:
dlc = strings.unspecified
except AttributeError: except AttributeError:
dlc = strings.unspecified dlc = strings.unspecified
+16 -9
View File
@@ -151,7 +151,9 @@ def get_remote_signatures(mods: list[str]) -> list[tuple[str, str, int, int]]:
return hashes return hashes
def connect_debug(client: str, addr: str, appid: int, name: str, mods: list[str]) -> str: def connect_debug(
client: str, addr: str, appid: int, name: str, mods: list[str]
) -> str:
concat = concat_mods(mods) concat = concat_mods(mods)
client_args = concat_bash_args(client) client_args = concat_bash_args(client)
params = [ params = [
@@ -167,6 +169,7 @@ def connect_debug(client: str, addr: str, appid: int, name: str, mods: list[str]
client_args.extend(params) client_args.extend(params)
return " ".join(client_args) return " ".join(client_args)
# TODO: set config to name=user, use official server and no mods, # TODO: set config to name=user, use official server and no mods,
# ensure that formatted string is identical to fixture with same hash # ensure that formatted string is identical to fixture with same hash
def connect(client: str, addr: str, appid: int, name: str, mods: list[str]) -> int: def connect(client: str, addr: str, appid: int, name: str, mods: list[str]) -> int:
@@ -235,14 +238,18 @@ def find_user_id(path: Path) -> str | None:
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 users = v["users"]
# /package/beta # NOTE: beta client: /package/beta
if len(v["users"]) == 1: last_user = str(next(iter(users)))
return str(list(v["users"].keys())[0]) if len(users) == 1:
for user in v["users"]: return last_user
if v["users"][user]["MostRecent"] == "1": last_stamp = int(users[last_user]["Timestamp"])
return str(user) for user in users:
return None stamp = int(users[user]["Timestamp"])
if stamp > last_stamp:
last_stamp = stamp
last_user = user
return last_user
except Exception as e: except Exception as e:
logger.debug(e) logger.debug(e)
return None return None
+1
View File
@@ -523,6 +523,7 @@ class Controller(GObject.GObject):
def set_start_tab(self) -> None: def set_start_tab(self) -> None:
ind = self.config_man.get_start_tab() ind = self.config_man.get_start_tab()
logger.info(f"Setting start tab to index {ind}")
self.get_servers().notebook.set_current_page(ind) self.get_servers().notebook.set_current_page(ind)
def get_debug_args(self) -> str: def get_debug_args(self) -> str:
+1
View File
@@ -90,6 +90,7 @@
- Rare segfaults when changing maps (threading) - Rare segfaults when changing maps (threading)
- Moved dialogs out of threads - Moved dialogs out of threads
- History table not being in lock-step with state file - History table not being in lock-step with state file
- Current keyword filter not being applied when refreshing table
### Unreleased ### Unreleased
- Load offline mods - Load offline mods
+3 -3
View File
@@ -1,3 +1,4 @@
import json
import shutil import shutil
from pathlib import Path from pathlib import Path
@@ -7,11 +8,10 @@ from dzgui.config.convert import rc2json
from dzgui.util._json import read_json, write_json from dzgui.util._json import read_json, write_json
def migrate_legacy_conf(config: Path) -> None: def migrate_legacy_conf(config: Path) -> Any:
old_conf = Path.home() / LEGACY_CONFIG_PATH old_conf = Path.home() / LEGACY_CONFIG_PATH
j = rc2json(old_conf) j = rc2json(old_conf)
config.parent.mkdir(parents=True, exist_ok=True) return json.loads(j)
config.write_text(j)
def has_new_config(config: Path) -> bool: def has_new_config(config: Path) -> bool:
+3 -5
View File
@@ -259,10 +259,6 @@ class ProxyModelManager:
def set_cache(self, filters: tuple[str], filtered_rows: list[tuple]) -> None: def set_cache(self, filters: tuple[str], filtered_rows: list[tuple]) -> None:
self.filter_cache[filters] = filtered_rows self.filter_cache[filters] = filtered_rows
# @deprecated("Currently unused")
# def convert_model_to_list(self, model: "FastInsertListStore") -> list:
# return [[el for el in row] for row in model]
def set_filtered(self, rows: list | None) -> None: def set_filtered(self, rows: list | None) -> None:
if rows is None: if rows is None:
self.filtered = [] self.filtered = []
@@ -298,4 +294,6 @@ class ProxyModelManager:
def push(self, data: list[Any]) -> None: def push(self, data: list[Any]) -> None:
self.wipe_cache() self.wipe_cache()
self.set_control(data) self.set_control(data)
self.filter(FilterMode.INITIAL) cur_keyword = self.filter_man.get_active_keyword()
mode = FilterMode.TOGGLE_ON if len(cur_keyword) > 0 else FilterMode.INITIAL
self.filter(mode)
-1
View File
@@ -84,6 +84,5 @@ class CalcDist:
except Exception: except Exception:
return None return None
# TODO: handle failed remote dist
haversine = Haversine(local.lat, local.lon, remote.lat, remote.lon) haversine = Haversine(local.lat, local.lon, remote.lat, remote.lon)
return haversine return haversine
+1 -9
View File
@@ -2,14 +2,13 @@ import a2s
import requests import requests
import shutil import shutil
import subprocess import subprocess
from warnings import deprecated
from dataclasses import dataclass from dataclasses import dataclass
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from dzgui.api.servers import Record from dzgui.api.servers import Record
from dzgui.const.constants import REQUEST_TIMEOUT from dzgui.const.constants import REQUEST_TIMEOUT
from dzgui.const.endpoints import COORDS_API, IP_ECHO from dzgui.const.endpoints import IP_ECHO
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -127,10 +126,3 @@ def is_valid_port(port: str) -> bool:
if not port.isdigit() or int(port) == 0 or int(port[0]) == 0 or int(port) > 65535: if not port.isdigit() or int(port) == 0 or int(port[0]) == 0 or int(port) > 65535:
return True return True
return False return False
@deprecated("use ips.csv")
def get_local_coords(ip: str) -> str:
url = COORDS_API + "/" + ip
# local res=$(curl -Ls "$url" | jq -r '"\(.lat)\n\(.lon)"')
return url
+2 -1
View File
@@ -168,11 +168,12 @@ none_provided = "None provided"
disabled = "Disabled" disabled = "Disabled"
enabled = "Enabled" enabled = "Enabled"
null = "-" null = "-"
none = "None"
unspecified = "not specified" unspecified = "not specified"
# DLC # DLC
dlc_frostline = "Frostline" dlc_frostline = "Frostline"
dlc_badlands = "Badlands"
dlc_none = "None"
# platform # platform
windows = "Windows" windows = "Windows"
+40 -38
View File
@@ -1,4 +1,3 @@
import os
import textwrap import textwrap
from enum import Enum from enum import Enum
@@ -17,9 +16,7 @@ from dzgui.const.constants import (
) )
from dzgui.const.boilerplate import config_boilerplate from dzgui.const.boilerplate import config_boilerplate
from dzgui.const.endpoints import STEAM_API_SETUP from dzgui.const.endpoints import STEAM_API_SETUP
from dzgui.const.enum import Preferences
from dzgui.config import freedesktop from dzgui.config import freedesktop
from dzgui.config.query import lookup
from dzgui.init.migrate import migrate_legacy_conf from dzgui.init.migrate import migrate_legacy_conf
from dzgui.managers.threading import call_on_thread, StoredFunc, ThreadingManager from dzgui.managers.threading import call_on_thread, StoredFunc, ThreadingManager
from dzgui.strings import wizard from dzgui.strings import wizard
@@ -305,6 +302,7 @@ class ConfigMigrationPage(EnumeratedWizardPage):
) )
self.migrated = False self.migrated = False
self.migrated_conf: dict[str, Any] = {}
self.config = config self.config = config
self.page_type = Gtk.AssistantPageType.INTRO self.page_type = Gtk.AssistantPageType.INTRO
@@ -344,8 +342,7 @@ class ConfigMigrationPage(EnumeratedWizardPage):
def _on_import_clicked(self, button: Gtk.Button) -> None: def _on_import_clicked(self, button: Gtk.Button) -> None:
self.grid.set_sensitive(False) self.grid.set_sensitive(False)
try: try:
# TODO: this could be deferred to the final page (prevents accidental destruction of dialog via ESC) self.migrated_conf = migrate_legacy_conf(self.config)
migrate_legacy_conf(self.config)
self.migrated = True self.migrated = True
self.success_box.set_visible(True) self.success_box.set_visible(True)
except Exception: except Exception:
@@ -437,20 +434,18 @@ class Assistant(Gtk.Assistant):
else: else:
self.set_default_size(1500, 900) self.set_default_size(1500, 900)
self.is_binary = False if os.getenv("PYAPP") is None else True
self.config_path = XDG.config self.config_path = XDG.config
self.config_values: dict[str, Any] = config_boilerplate self.config_values: dict[str, Any] = config_boilerplate
self.setup_complete = False self.setup_complete = False
self.page1 = IntroductionPage() self.page_intro = IntroductionPage()
self.page2 = ConfigMigrationPage(XDG.config) self.page_migration = ConfigMigrationPage(XDG.config)
self.page3 = SteamPathPage() self.page_paths = SteamPathPage()
self.page4 = SteamValidationPage() self.page_api = SteamValidationPage()
self.page5 = PreferencesPage() self.page_prefs = PreferencesPage()
self.page6 = ShortcutCreationPage(XDG.shortcut) self.page_shortcuts = ShortcutCreationPage(XDG.shortcut)
self.page7 = CompletionPage() self.page_completion = CompletionPage()
self.set_forward_page_func(self._advance_page) self.set_forward_page_func(self._advance_page)
@@ -458,16 +453,18 @@ class Assistant(Gtk.Assistant):
EMITTER.connect("step_pending", self._mark_page_incomplete) EMITTER.connect("step_pending", self._mark_page_incomplete)
EMITTER.connect("config", self._set_config_state) EMITTER.connect("config", self._set_config_state)
self.pages: dict[EnumeratedWizardPage, int] = {}
legacy_path = Path.home().joinpath(LEGACY_CONFIG_PATH) legacy_path = Path.home().joinpath(LEGACY_CONFIG_PATH)
self.has_legacy_config = legacy_path.is_file() self.has_legacy_config = legacy_path.is_file()
for page in ( for page in (
self.page1, self.page_intro,
self.page2, self.page_migration,
self.page3, self.page_paths,
self.page4, self.page_api,
self.page5, self.page_prefs,
self.page6, self.page_shortcuts,
self.page7, self.page_completion,
): ):
# NOTE: skip config migration page if no legacy config file # NOTE: skip config migration page if no legacy config file
if ( if (
@@ -475,10 +472,8 @@ class Assistant(Gtk.Assistant):
and self.has_legacy_config is False and self.has_legacy_config is False
): ):
continue continue
# NOTE: disabled for now on system-provided packages ind = self._add_page(page, page.get_page_type())
if isinstance(page, ShortcutCreationPage) and not self.is_binary: self.pages[page] = ind
continue
self._add_page(page, page.get_page_type())
self.connect("prepare", self._on_page_prepare) self.connect("prepare", self._on_page_prepare)
self.connect("cancel", self.destroy_and_quit) self.connect("cancel", self.destroy_and_quit)
@@ -486,8 +481,11 @@ class Assistant(Gtk.Assistant):
self.show_all() self.show_all()
load_css() load_css()
def write_config(self) -> None: def write_config(self, config: dict[str, Any]) -> None:
write_json(self.config_values, self.config_path) write_json(config, self.config_path)
def get_final_page(self) -> int:
return self.pages[self.page_shortcuts]
def _advance_page(self, index: int) -> int: def _advance_page(self, index: int) -> int:
page = self.get_nth_page(index) page = self.get_nth_page(index)
@@ -496,24 +494,23 @@ class Assistant(Gtk.Assistant):
pass pass
case ConfigMigrationPage(): case ConfigMigrationPage():
if page.is_migrated(): if page.is_migrated():
steam_path = lookup(self.config_path, Preferences.DEFAULT) sp = page.migrated_conf["default_steam_path"]
self.page6.set_steam_path(steam_path) self.page_shortcuts.set_steam_path(sp)
offset = 1 if not self.is_binary else 2 self.config_values = self.page_migration.migrated_conf
self.setup_complete = True return self.get_final_page()
return self.get_n_pages() - offset
case SteamPathPage(): case SteamPathPage():
self.config_values["default_steam_path"] = page.get_path_from_radio() self.config_values["default_steam_path"] = page.get_path_from_radio()
case SteamValidationPage(): case SteamValidationPage():
self.config_values["steam_api"] = page.get_api_key() self.config_values["steam_api"] = page.get_api_key()
case PreferencesPage(): case PreferencesPage():
# NOTE: collects config values before advancing to last page # NOTE: collects config values before advancing to last page
name, use_miles, client = self.page5.get_prefs() name, use_miles, client = self.page_prefs.get_prefs()
self.config_values["name"] = name self.config_values["name"] = name
self.config_values["use_miles"] = use_miles self.config_values["use_miles"] = use_miles
self.config_values["client"] = client self.config_values["client"] = client
self.write_config() self.page_shortcuts.set_steam_path(
self.page6.set_steam_path(self.config_values["default_steam_path"]) self.config_values["default_steam_path"]
self.setup_complete = True )
case ShortcutCreationPage(): case ShortcutCreationPage():
page.create_shortcuts() page.create_shortcuts()
case _: case _:
@@ -543,11 +540,12 @@ class Assistant(Gtk.Assistant):
def _add_page( def _add_page(
self, page: EnumeratedWizardPage, ptype: Gtk.AssistantPageType self, page: EnumeratedWizardPage, ptype: Gtk.AssistantPageType
) -> None: ) -> int:
self.append_page(page) ind = self.append_page(page)
self.set_page_type(page, ptype) self.set_page_type(page, ptype)
self.set_page_title(page, page.get_title()) self.set_page_title(page, page.get_title())
self.set_page_complete(page, True) self.set_page_complete(page, True)
return ind
def _set_config_state(self, emitter: "Emitter", state: bool) -> None: def _set_config_state(self, emitter: "Emitter", state: bool) -> None:
self.config = state self.config = state
@@ -564,6 +562,10 @@ class Assistant(Gtk.Assistant):
if not isinstance(page, IntroductionPage): if not isinstance(page, IntroductionPage):
EMITTER.emit("step_pending") EMITTER.emit("step_pending")
if page == self.page_completion:
self.write_config(self.config_values)
self.setup_complete = True
class CheckboxWithLabel(Gtk.Box): class CheckboxWithLabel(Gtk.Box):
def __init__(self, text: str, blurb_text: str) -> None: def __init__(self, text: str, blurb_text: str) -> None:
+1 -1
View File
@@ -4,7 +4,7 @@ description = "DayZ server browser and mod manager for Linux"
authors = [ authors = [
{name = "aclist"} {name = "aclist"}
] ]
version = "7.0.0b22" version = "7.0.0b23"
license = "GPL-3.0-or-later" license = "GPL-3.0-or-later"
license-files = ["LICENSE"] license-files = ["LICENSE"]
readme = "README.md" readme = "README.md"
+23
View File
@@ -0,0 +1,23 @@
"users"
{
"0"
{
"AccountName" "STEAMUSER"
"PersonaName" "STEAMUSER"
"RememberPassword" "0"
"WantsOfflineMode" "0"
"SkipOfflineModeWarning" "0"
"AutoLogin" "1"
"Timestamp" "1787904411"
}
"1"
{
"AccountName" "STEAMUSER2"
"PersonaName" "STEAMUSER2"
"RememberPassword" "0"
"WantsOfflineMode" "0"
"SkipOfflineModeWarning" "0"
"AutoLogin" "1"
"Timestamp" "1787904647"
}
}
@@ -0,0 +1,23 @@
"users"
{
"0"
{
"AccountName" "STEAMUSER"
"PersonaName" "STEAMUSER"
"RememberPassword" "0"
"WantsOfflineMode" "0"
"SkipOfflineModeWarning" "0"
"AutoLogin" "1"
"Timestamp" "1797904411"
}
"1"
{
"AccountName" "STEAMUSER2"
"PersonaName" "STEAMUSER2"
"RememberPassword" "0"
"WantsOfflineMode" "0"
"SkipOfflineModeWarning" "0"
"AutoLogin" "1"
"Timestamp" "1787904647"
}
}
+1 -1
View File
@@ -20,6 +20,6 @@
"SkipOfflineModeWarning" "0" "SkipOfflineModeWarning" "0"
"AllowAutoLogin" "1" "AllowAutoLogin" "1"
"MostRecent" "1" "MostRecent" "1"
"Timestamp" "0" "Timestamp" "10"
} }
} }
+2
View File
@@ -14,6 +14,8 @@ pytestmark = pytest.mark.apitest
("api/loginusers_legacy_client.vdf", 0), ("api/loginusers_legacy_client.vdf", 0),
("api/loginusers_legacy_client_multiple.vdf", 1), ("api/loginusers_legacy_client_multiple.vdf", 1),
("api/loginusers_beta_client.vdf", 0), ("api/loginusers_beta_client.vdf", 0),
("api/loginusers_beta_client_multiple.vdf", 1),
("api/loginusers_beta_client_multiple_2.vdf", 0),
], ],
) )
def test_loginusers(monkeypatch, fixture: str, expect: int) -> None: def test_loginusers(monkeypatch, fixture: str, expect: int) -> None:
+13
View File
@@ -0,0 +1,13 @@
from pathlib import Path
from dzgui.config.xdg import get_xdg_paths
def test_path_fallback(monkeypatch) -> None:
envs = ["XDG_DATA_HOME", "XDG_STATE_HOME", "XDG_CONFIG_HOME", "XDG_CACHE_HOME"]
for env in envs:
monkeypatch.setenv(env, "/etc/dzgui")
paths = get_xdg_paths()
assert paths["XDG_CONFIG_HOME"] == Path.home().joinpath(".config/dzgui")
assert paths["XDG_STATE_HOME"] == Path.home().joinpath(".local/state/dzgui")
assert paths["XDG_DATA_HOME"] == Path.home().joinpath(".local/share/dzgui")
assert paths["XDG_CACHE_HOME"] == Path.home().joinpath(".cache/dzgui")