Compare commits

...
30 Commits
Author SHA1 Message Date
aclist 0c0a6ade4b Merge 41b0e86fda into 8eb0a17b78 2026-06-03 10:35:36 +00:00
aclist 41b0e86fda docs: update DEVELOPERS.md 2026-06-03 19:35:30 +09:00
aclist fa486c2b76 chore: build musl release target 2026-06-03 18:20:35 +09:00
aclist 8eb0a17b78 Merge pull request #327 from aclist/docs/dzgui7-onboarding
Mirror to Codeberg / mirror-to-codeberg (push) Waiting to run
docs: add dzgui7 onboarding page
2026-06-03 15:17:51 +09:00
aclist 9e2d528436 docs: add dzgui7 onboarding page 2026-06-03 14:01:38 +09:00
aclist dac02d75f6 Merge pull request #319 from aclist/feat/test-writeable
Mirror to Codeberg / mirror-to-codeberg (push) Has been cancelled
feat: disallow updates if binary path is not writeable
2026-05-29 01:09:25 +09:00
aclist 3a5c061dbf chore: update docstring 2026-05-29 01:03:29 +09:00
aclist 3851b76ca0 docs: update changelog 2026-05-29 00:44:35 +09:00
aclist 387930b147 chore: update strings file 2026-05-29 00:44:17 +09:00
aclist c0c3ee8c64 fix: catch error if no GTK theme is available (headless installs) 2026-05-29 00:44:00 +09:00
aclist 1212c09110 feat: add tooltip to load menu button 2026-05-29 00:43:34 +09:00
aclist 86e2249a23 change: enable empty/full filters on first run of LAN page 2026-05-29 00:43:01 +09:00
aclist 302a43e65e change: enable empty/full filters on first run of LAN page 2026-05-29 00:42:43 +09:00
aclist 3dc58c1a69 fix: abort if deps could not be installed in cpython distribution 2026-05-29 00:41:47 +09:00
aclist 9af2145ee8 fix: use dedicated LAN error message 2026-05-29 00:41:24 +09:00
aclist 7b4382b178 chore: bump version 2026-05-28 05:52:46 +09:00
aclist ea3631072b chore: add typehint ignore 2026-05-28 05:49:16 +09:00
aclist 67bae3c3e0 chore: uncomment connection logic 2026-05-28 05:48:25 +09:00
aclist 1ea30270ce fix: break after 10th record 2026-05-28 05:47:59 +09:00
aclist 2c9b36ba84 fix: append history queue (WIP) 2026-05-28 05:24:23 +09:00
aclist fd9eb33714 feat: load direct to menu 2026-05-28 03:34:11 +09:00
aclist 15d928026a feat: add file module 2026-05-28 02:41:41 +09:00
aclist 24d3bc8209 chore: drop comment 2026-05-28 02:41:26 +09:00
aclist 881847cee9 feat: disallow updates if binary path is not writeable 2026-05-28 02:40:13 +09:00
aclist d3a06cf970 Merge pull request #318 from aclist/docs/MTU
docs: update DZG-001 (#315)
2026-05-28 02:18:43 +09:00
aclist 741344e75e docs: update DZG-001 (#315) 2026-05-28 02:18:08 +09:00
aclist a8a7aba338 Merge pull request #317 from aclist/chore/update-actions
chore: update checkout action to v5
2026-05-28 02:11:48 +09:00
aclist fafd4c6000 chore: update checkout action to v5 2026-05-28 02:10:49 +09:00
aclist 7e5e326ebe Merge pull request #316 from aclist/chore/geofile-stripping
chore: add geofile test
2026-05-28 02:09:45 +09:00
aclist e7644b49f5 chore: add geofile test 2026-05-28 02:07:39 +09:00
31 changed files with 399 additions and 169 deletions

No files matched your search

+1 -1
View File
@@ -8,7 +8,7 @@ jobs:
mirror-to-codeberg:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: pixta-dev/repository-mirroring-action@v1
+2 -1
View File
@@ -36,6 +36,7 @@
- Preconnect warnings/failsafes like filesize
- Save filters per server context between sessions
- Preboot progress dialog
- Choose to jump into splash screen instead of server
## Changed
@@ -52,6 +53,7 @@
- Embed Workshop link in Options menu
- Disable overlay scrollbars on server tables
- Reduce size of geolocation DB on disk (~100MB)
- Enable LAN page Empty/Full filters on first run of app
## Dropped
- Debug mode
@@ -67,7 +69,6 @@
## Unreleased
- Load offline mods
- Choose to jump into splash screen instead of server
- Local documentation
- Raw debug command in context menu
+3 -8
View File
@@ -13,11 +13,6 @@ git submodule update --recursive --init
sphinx-build -M html source build -a
# Building standalone DZGUI release binaries
1. Run `./scripts/dl_pyapp.sh`
# TODO: secondary script that packs this archive
2. Download cpython 3.13, linux, gnu, v3
https://github.com/astral-sh/python-build-standalone/releases/download/20251014/cpython-3.13.9%2B20251014-x86_64_v3-unknown-linux-gnu-install_only_stripped.tar.gz
3. Explicitly install dependencies into site-packages
4. Pack back into a tarball
5. Run `python3 scripts/release.py`
6. Generate a release and tag against the bundled `dzgui` executable
1. Set up the build target: `rustup target add x86_64-unknown-linux-musl`
2. Run `python3 scripts/release.py`
3. Generate a release and tag against the bundled `dzgui` executable
+2 -2
View File
@@ -36,5 +36,5 @@ html_show_copyright = False
html_show_sourcelink = False
today_fmt = "%Y-%m-%d"
release = "7.x.x"
version = "7.x.x"
release = "6.x.x - 7.x.x"
version = "6.x.x - 7.x.x"
+110
View File
@@ -0,0 +1,110 @@
Installation (DZGUI 6 legacy)
==================
Basic runtime 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.
- curl
- jq
- steam
- wmctrl or xdotool
- zenity
.. note::
All dependencies are installed out of the box on Steam Deck.
UI dependencies (Python and PyGObject)
---------------------------------------
.. important::
As of version 6.0.0, you must have a valid Python 3.13 and matching PyGObject environment.
Depending on your distribution, this may require additional manual intervention.
Arch Linux (vanilla variants)
^^^^^^^^^^^^^^^^^^^^^^
You will need the ``python3.13`` package, if it is not already installed.
Install the following build dependencies:
.. code:: console
sudo pacman -S cmake gcc pkgconf python-cairo
.. include:: venv.rst
CachyOS (Arch)
^^^^^^^^^^^^^^^^^^^^^^^
Refer to the build instructions for Arch Linux first.
The ``python3.13`` package is not available on CachyOS, so it is recommended to
use ``uv`` to manage Python versions.
.. code:: console
sudo pacman -S uv
.. include:: venv.rst
Manjaro 26 (Arch)
^^^^^^^^^^^^^^^^^^^^^^^
If Python 3.13 is not already available out of the box, refer to the build instructions for :ref:`Arch Linux (vanilla variants)`.
Debian 13 variants
^^^^^^^^^^^^^^^^^^^^^^^
Python 3.13 and PyGObject are available out of the box. No additional steps are necessary.
You can proceed to the :ref:`Installscript`.
Fedora 43 variants
^^^^^^^^^^^^^^^^^^^^^^^
Fedora 43 ships with a newer version of Python (3.14), so you must set up Python 3.13 in its own
virtual environment (venv) to prevent it from conflicting with the system package.
Install the following build dependencies:
.. code:: console
sudo dnf install cairo cairo-devel cairo-gobject-devel gcc python3.13 python3.13-devel
.. include:: venv.rst
Ubuntu 24 variants
^^^^^^^^^^^^^^^^^^^^^^
Ubuntu ships with an older version of Python (3.12). You must install additional dependencies.
Add the PPA below to add Python 3.13 to your sources:
.. code:: console
sudo add-apt-repository ppa:deadsnakes/ppa
Install the following build dependencies:
.. code:: console
sudo apt install python3.13 python3.13-venv python3.13-dev build-essential python3-gi-cairo pkg-config libgirepository-2.0-dev libcairo2-dev
.. include:: venv.rst
Installscript
-----------------------
Invoke the command below from a terminal. This script fetches the main DZGUI script from the repository,
makes it executable, and launches DZGUI's first-time setup. After running this installscript, you will
have the file ``dzgui.sh`` wherever you invoked the command.
.. code:: console
curl -s "https://codeberg.org/aclist/dztui/raw/branch/dzgui/install.sh" | bash
nixOS installation
----------------------
Two packages are independently maintained by other contributors. Each takes a slightly different approach.
nixOS packages are standalone and do not require cloning the git repository.
- https://github.com/lelgenio/dzgui-nix
- https://github.com/jiriks74/dzgui.flake
Next steps
---------------
Proceed to the :doc:`setup` section for setup instructions.
+33
View File
@@ -0,0 +1,33 @@
Installation (DZGUI 7 beta)
==================
In order to make the first-time setup experience easier for end-users, DZGUI 7 no longer requires
the tool to be installed from source code into a working Python environment. All necessary dependencies and the
runtime environment are built-in.
Turnkey installer
------------------------
- Visit the project's `Releases page <https://github.com/aclist/dztui/releases>`_.
- Extract the DZGUI tarball from the top of the **Assets** list.
- From a terminal, run the command ``./dzgui`` or double click on the extracted file in
a file explorer.
- Once the setup wizard opens, follow the steps at the bottom of this page.
UI dependencies (Python and PyGObject)
---------------------------------------
On most major distributions, GObject introspection libraries should be available by default.
If you receive an error about a missing ``libgirepository-2.0`` dependency, install it via your system's package manager.
On Debian-based distributions (Ubuntu, etc.), this package is not explicitly installed on the system.
You can install it via the following set of commands:
.. code:: console
sudo apt update
sudo apt install libgirepository-2.0
Next steps
---------------
Proceed to the :doc:`setup` section for setup instructions.
+16 -98
View File
@@ -1,110 +1,28 @@
Installation
==================
Basic runtime dependencies
---------------------------------
As of this writing, you have a choice of two DZGUI versions:
If not already installed, the below can be found in your system’s package manager.
- DZGUI 6: Legacy stable version, more complicated to install
- DZGUI 7: Unstable beta version, much easier to install
If any dependencies are missing when the application starts, it will warn you, so you need not take any preemptive measures here.
DZGUI 6
--------------------
This version is no longer receiving feature updates, but will receive security updates until it is replaced by DZGUI 7.
- curl
- jq
- steam
- wmctrl or xdotool
- zenity
DZGUI 6 must be installed from the source code and has many more package dependencies and prerequisites for it to function correctly.
If you are looking for a one-click, self-contained installer, use :ref:`DZGUI 7` instead.
.. note::
All dependencies are installed out of the box on Steam Deck.
To install **DZGUI 6**, visit :doc:`dzgui6`.
UI dependencies (Python and PyGObject)
---------------------------------------
.. important::
As of version 6.0.0, you must have a valid Python 3.13 and matching PyGObject environment.
Depending on your distribution, this may require additional manual intervention.
Arch Linux (vanilla variants)
^^^^^^^^^^^^^^^^^^^^^^
You will need the ``python3.13`` package, if it is not already installed.
DZGUI 7
--------------------
This version is currently in beta testing and has all of the features found in DZGUI 6, but may be unstable.
Install the following build dependencies:
DZGUI 7 has an all-in-one installer and setup wizard, and many new features not found in DZGUI 6.
However, this codebase is newer and is provided as a pre-release beta candidate.
.. code:: console
If you want the simplest installer and don't mind experiencing the occasional bug, use this version.
sudo pacman -S cmake gcc pkgconf python-cairo
.. include:: venv.rst
CachyOS (Arch)
^^^^^^^^^^^^^^^^^^^^^^^
Refer to the build instructions for Arch Linux first.
The ``python3.13`` package is not available on CachyOS, so it is recommended to
use ``uv`` to manage Python versions.
.. code:: console
sudo pacman -S uv
.. include:: venv.rst
Manjaro 26 (Arch)
^^^^^^^^^^^^^^^^^^^^^^^
If Python 3.13 is not already available out of the box, refer to the build instructions for :ref:`Arch Linux (vanilla variants)`.
Debian 13 variants
^^^^^^^^^^^^^^^^^^^^^^^
Python 3.13 and PyGObject are available out of the box. No additional steps are necessary.
You can proceed to the :ref:`Installscript`.
Fedora 43 variants
^^^^^^^^^^^^^^^^^^^^^^^
Fedora 43 ships with a newer version of Python (3.14), so you must set up Python 3.13 in its own
virtual environment (venv) to prevent it from conflicting with the system package.
Install the following build dependencies:
.. code:: console
sudo dnf install cairo cairo-devel cairo-gobject-devel gcc python3.13 python3.13-devel
.. include:: venv.rst
Ubuntu 24 variants
^^^^^^^^^^^^^^^^^^^^^^
Ubuntu ships with an older version of Python (3.12). You must install additional dependencies.
Add the PPA below to add Python 3.13 to your sources:
.. code:: console
sudo add-apt-repository ppa:deadsnakes/ppa
Install the following build dependencies:
.. code:: console
sudo apt install python3.13 python3.13-venv python3.13-dev build-essential python3-gi-cairo pkg-config libgirepository-2.0-dev libcairo2-dev
.. include:: venv.rst
Installscript
-----------------------
Invoke the command below from a terminal. This script fetches the main DZGUI script from the repository,
makes it executable, and launches DZGUI's first-time setup. After running this installscript, you will
have the file ``dzgui.sh`` wherever you invoked the command.
.. code:: console
curl -s "https://codeberg.org/aclist/dztui/raw/branch/dzgui/install.sh" | bash
nixOS installation
----------------------
Two packages are independently maintained by other contributors. Each takes a slightly different approach.
nixOS packages are standalone and do not require cloning the git repository.
- https://github.com/lelgenio/dzgui-nix
- https://github.com/jiriks74/dzgui.flake
Next steps
---------------
Proceed to the next section for setup instructions.
To install **DZGUI 7**, visit :doc:`dzgui7`.
+11 -6
View File
@@ -3,16 +3,21 @@ Knowledge Base
.. _DZG-001:
DZG-001: Periodically getting dropped from servers, or servers appear unreachable
DZG-001: Periodically getting dropped from servers, or servers time out in DZGUI
------------------------------------------------------------------------------------
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.
DayZ opens a large number of connections while querying/connected to servers.
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.
In addition, packets sent from server responses are expected to be a standard size (see warning below).
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.
If your network does not have enough headroom or has settings departing from defaults, this may lead to getting dropped from servers,
unresponsiveness, or a timeout.
This issue is frequently seen on Steam Deck, due to the tendency for users to use it in an untethered Wi-Fi setup.
If you are on Wi-Fi, try switching to a wired connection and see if the problem resolves itself. Consumer Wi-Fi routers
tend to have less headroom than their wired counterparts.
.. important::
Ensure that MTU (maximum tranmission unit) on your network is set to the standard size of 1,500 bytes.
If your network preferences or router have "jumbo" frames enabled, packets may be dropped, causing server queries to time out.
.. _DZG-002:
+1 -2
View File
@@ -456,6 +456,5 @@ def get_pretty_version(steam_path: Path, appid: int) -> str | None:
vers = get_dayz_version(pe_file_path)
dayz_version = dayz_version_to_str(vers)
return dayz_version
except Exception as e:
print(e)
except Exception:
return None
+6
View File
@@ -61,6 +61,8 @@ class Details:
@dataclass(slots=True)
class Record:
# TODO: investigate whether this enum is still being used;
# can this be frozen?
"""
The gameport field is manipulated by the RowType.CONN_BY_IP method
"""
@@ -509,6 +511,10 @@ def fqip_to_record(addr: str) -> Optional[Record]:
return Record(r[0], int(r[1]), int(r[2]))
def record_to_fqip(record: Record) -> str:
return f"{record.ip}:{record.gameport}:{record.qport}"
def response_to_fqip(res: dict) -> str:
ip = res["addr"].split(":")[0]
gameport = res["gameport"]
+17
View File
@@ -146,6 +146,23 @@ def connect(client: str, addr: str, appid: int, name: str, mods: list[str]) -> i
return proc.returncode
def load_to_menu(client: str, addr: str, appid: int, name: str, mods: list[str]) -> int:
"""Loads to the menu screen with the selected mods; used for AFK/pre-joining"""
concat = concat_mods(mods)
client_args = concat_bash_args(client)
params = [
"-applaunch",
str(appid),
"-nolauncher",
"-nosplash",
"-skipintro",
f"-name={name}",
f"-mod={concat}",
]
proc = subprocess.run([*client_args, *params])
return proc.returncode
def find_user_id(path: Path) -> str | None:
resolved_path = path / "config" / "loginusers.vdf"
try:
+8 -2
View File
@@ -409,9 +409,12 @@ class Controller(GObject.GObject):
saved_tree = self.get_servers().get_saved()
ServerModelManager(self, saved_tree).remove_by_record(record)
def add_to_history(self, record: dict[str, Any]) -> None:
def add_to_history(self, row: dict[str, Any], record: "Record") -> None:
tv = self.get_servers().get_recent()
ServerModelManager(self, tv).add_to_history(record)
ServerModelManager(self, tv).add_to_history((row, record))
# def append_to_history(self, record: "Record") -> None:
# self.config_man.append_to_history_file(record)
def remove_from_history(self, record: "Record") -> None:
# NOTE: remove action is only possible from history tree context menu,
@@ -508,6 +511,9 @@ class Controller(GObject.GObject):
ind = self.config_man.get_start_tab()
self.get_servers().notebook.set_current_page(ind)
def update_and_load_to_menu(self, raise_window: bool) -> None:
self.connection_man.update_and_connect(raise_window, menu_only=True)
def update_and_connect(self, raise_window: bool) -> None:
self.connection_man.update_and_connect(raise_window)
+3 -1
View File
@@ -36,6 +36,7 @@
- Preconnect warnings/failsafes like filesize
- Save filters per server context between sessions
- Preboot progress dialog
- Choose to jump into splash screen instead of server
## Changed
@@ -51,6 +52,8 @@
- Suppress log messages from imported modules
- Embed Workshop link in Options menu
- Disable overlay scrollbars on server tables
- Reduce size of geolocation DB on disk (~100MB)
- Enable LAN page Empty/Full filters on first run of app
## Dropped
- Debug mode
@@ -66,7 +69,6 @@
## Unreleased
- Load offline mods
- Choose to jump into splash screen instead of server
- Local documentation
- Raw debug command in context menu
+19 -4
View File
@@ -1,6 +1,7 @@
import logging
import traceback
from collections import deque
from typing import Any, TYPE_CHECKING
from dzgui.const.constants import (
@@ -52,11 +53,25 @@ class ConfigManager:
ips.append(record)
self.update_config(Preferences.IP_LIST, ips)
def update_history_file(self, records: list[Any]) -> None:
def update_history_file(self, fqip: str) -> None:
with open(self.prefs.paths.history, "r") as f:
ips = [line.rstrip() for line in f.readlines()]
seen = set()
ips.append(fqip)
# NOTE: Preserve linear order of records while deduplicating
unique = deque(maxlen=10) # type: ignore
for i in range(len(ips) - 1, -1, -1):
if ips[i] not in seen:
seen.add(ips[i])
unique.appendleft(ips[i])
# NOTE: appendleft will push 11th item off right edge
if len(unique) == 10:
break
with open(self.prefs.paths.history, "w") as f:
for record in records:
addr = f"{record[7]}:{record[8]}"
f.write(f"{addr}\n")
for record in unique:
f.write(f"{record}\n")
def remove_saved_server(self, record: str) -> None:
ips = self.lookup(Preferences.IP_LIST)
+27 -8
View File
@@ -15,6 +15,7 @@ from dzgui.api.steam import (
enqueue_mod,
get_remote_signatures,
get_needs_update,
load_to_menu,
)
from dzgui.api.mods import (
@@ -74,6 +75,7 @@ class Prerequisites:
steam_proc: SteamProcess
mods: list[list[str]]
game_mode: bool
is_last_server: bool
class ConnectionManager:
@@ -172,6 +174,8 @@ class ConnectionManager:
game_mode = prefs.is_game_mode
is_last = self.is_last_server()
prereqs = Prerequisites(
name=info.server_name,
appid=info.game_id,
@@ -186,11 +190,23 @@ class ConnectionManager:
steam_proc=steam_proc,
mods=remote_mods,
game_mode=game_mode,
is_last_server=is_last,
)
func = StoredFunc(self.controller.open_connection_assistant, prereqs)
self.thread_man.set_cleanup_func(func, destroy_first=True)
def is_last_server(self) -> bool:
prefs = self.controller.get_prefs()
history = prefs.paths.history
try:
lines = history.read_text().splitlines()
last = lines[-1]
current = Servers.record_to_fqip(self.record)
return last == current
except Exception:
return False
@call_on_thread(dialog.querying)
def query_details(self, record: Servers.Record) -> None:
details = Servers.get_details(record)
@@ -245,11 +261,14 @@ class ConnectionManager:
dialog = ExceptionDialog(self.controller, server_timeout)
dialog.run()
def _connect_steam(self) -> None:
def _connect_steam(self, menu_only: bool) -> None:
addr = f"{self.record.ip}:{self.record.gameport}"
playername = self.controller.query_config(Preferences.NAME)
client = self.controller.query_config(Preferences.CLIENT)
rc = connect(client, addr, self.appid, playername, self.remote_mod_ids)
if menu_only:
rc = load_to_menu(client, addr, self.appid, playername, self.remote_mod_ids)
else:
rc = connect(client, addr, self.appid, playername, self.remote_mod_ids)
if rc != 0:
# TODO: log/pop the error
func = StoredFunc(self.controller.update_status)
@@ -271,10 +290,10 @@ class ConnectionManager:
self.thread_man.set_cleanup_func(func)
def _add_to_history_and_return(self) -> None:
self.controller.add_to_history(self.history)
self.controller.add_to_history(self.history, self.record)
self.controller.open_page(NotebookPage.SERVERS)
def _update_mods(self, raise_window: bool) -> None:
def _update_mods(self, raise_window: bool, menu_only: bool = False) -> None:
# NOTE: fast enqueue all mods in auto mode
prefs = self.controller.get_prefs()
@@ -312,11 +331,11 @@ class ConnectionManager:
update_signatures(self.missing_mods, prefs.paths.version)
# TODO: just push steam path directly
rebuild_symlinks(prefs.paths.config)
self._connect_steam()
self._connect_steam(menu_only)
@call_on_thread(waiting_for_mods, show_cancel=True)
def update_and_connect(self, raise_window: bool) -> None:
def update_and_connect(self, raise_window: bool, menu_only: bool = False) -> None:
if len(self.missing_mods) > 0:
self._update_mods(raise_window)
self._update_mods(raise_window, menu_only)
else:
self._connect_steam()
self._connect_steam(menu_only)
+3 -3
View File
@@ -9,7 +9,7 @@ if TYPE_CHECKING:
class FilterManager:
def __init__(self) -> None:
def __init__(self, show_empty: bool = False, show_full: bool = False) -> None:
self.map_store = ModelFactory().make_map_store()
@@ -17,10 +17,10 @@ class FilterManager:
self.default_filters = {
strings.filter_1pp: True,
strings.filter_day: True,
strings.filter_empty: False,
strings.filter_empty: show_empty,
strings.filter_3pp: True,
strings.filter_night: True,
strings.filter_full: False,
strings.filter_full: show_full,
strings.filter_lowpop: True,
strings.filter_nonascii: False,
strings.filter_duplicate: False,
+3 -2
View File
@@ -85,8 +85,9 @@ class ProxyModelManager:
if found is False:
self.control_model.append(history)
if len(self.control_model) == 11:
del self.control_model[0]
# if len(self.control_model) == 11:
# del self.control_model[0]
self.filter(FilterMode.INITIAL, skip_cache=True)
+22 -9
View File
@@ -14,6 +14,7 @@ from dzgui.const.constants import (
)
from dzgui.const.enum import FilterMode, Preferences, ServerTab
from dzgui.managers.threading import call_on_thread, StoredFunc, ThreadingManager
from dzgui.strings import dialogs
from dzgui.util.strings import api_warn_msg, dialog
from dzgui.views.dialogs.generic import ExceptionDialog
@@ -23,7 +24,7 @@ gi.require_version("Gtk", "3.0")
from gi.repository import Gtk # noqa E402
if TYPE_CHECKING:
from dzgui.api.servers import A2SInfo
from dzgui.api.servers import A2SInfo, Record
from dzgui.controllers.mc import Controller
from dzgui.model.proxy_model import ProxyModelManager
from dzgui.views.trees.tree_servers import ServerTreeView
@@ -130,7 +131,7 @@ class ServerModelManager:
servers = []
ports = range(1, 256)
failure_func = StoredFunc(self._cleanup_on_failure)
failure_func = StoredFunc(self._cleanup_on_lan_failure)
event = threading.Event()
with ThreadPoolExecutor() as executor:
@@ -225,15 +226,12 @@ class ServerModelManager:
) = None,
) -> None:
proxy_man = self._get_proxy_man()
config_man = self.controller.get_config_man()
if rows is not None:
records = rows
else:
control_model = proxy_man.get_control()
records = control_model
config_man.update_history_file(records)
config_man.update_history_file(records)
self._sort_unique_maps(records)
proxy = proxy_man.get_proxy_model()
self.tv.set_model(proxy)
@@ -249,15 +247,20 @@ class ServerModelManager:
self.emitter.emit("load_maps", store)
# self.emitter.emit("servers_loaded_init")
def add_to_history(self, record: dict[str, Any]) -> None:
# TODO: dataclass for record rows; check for other dict annotations
def add_to_history(self, data: tuple[dict[str, Any], "Record"]) -> None:
row, record = data
proxy_man = self._get_proxy_man()
rows = Servers.parse_json([record])
rows = Servers.parse_json([row])
try:
proxy_man.append_row_to_history(rows[0])
self.update_history()
except Exception:
self.update_history(rows)
return
self.update_history()
fqip = Servers.record_to_fqip(record)
config_man = self.controller.get_config_man()
config_man.update_history_file(fqip)
def remove_from_history(self, record: Servers.Record) -> None:
proxy_man = self._get_proxy_man()
@@ -403,6 +406,16 @@ class ServerModelManager:
if self.first_iteration:
self._update_maps()
def _cleanup_on_lan_failure(self, show_dialog: bool = True) -> None:
if self.preserve_on_fail is False:
self.tv.set_model(None)
filter_man = self.tv.get_filter_man()
filter_man.set_unique_maps([])
if show_dialog:
dialog = ExceptionDialog(self.controller, dialogs.load_error_lan)
dialog.run()
def _cleanup_on_failure(self, show_dialog: bool = True) -> None:
# TODO: disable map, keyword, and filter widgets if model is None
# signal driven (servers_empty, servers_failed_to_load)
+2
View File
@@ -4,3 +4,5 @@ waiting_for_mods = "Waiting for Steam to update mods"
fetching_update = "Fetching update"
failed_to_update = "Failed to update DZGUI executable"
update_success = "Updated successfully. Please exit and relaunch."
load_error_lan = "Failed to find any servers on your network.\nCheck the server query port or your firewall settings."
+4
View File
@@ -1,5 +1,9 @@
update_mods = "Update mods and connect"
connect = "Connect"
connect_last = "Load to menu screen"
connect_last_tooltip = (
"Launch DayZ with these mods (if any) and\ngo to the menu screen without connecting."
)
back = "Back"
cancel = "Cancel"
warnings = "Warnings"
+15
View File
@@ -0,0 +1,15 @@
from datetime import datetime
from pathlib import Path
def is_writeable(path: "Path") -> bool:
now = int(datetime.now().timestamp())
suffix = ".dzgtmp"
file = str(now) + suffix
filepath = Path(str(path) + "_" + file)
try:
filepath.touch()
filepath.unlink()
return True
except OSError:
return False
+1
View File
@@ -611,6 +611,7 @@ atomic_buttons = AtomicButton(
keys_tooltip="Toggles the keybindings dialog",
)
gtk_theme_missing = "No GTK theme provided"
steam_icon_missing = "Steam icon not found in IconTheme"
missing_changelog = "Error: failed to read changelog"
+9 -5
View File
@@ -323,8 +323,12 @@ class App(Gtk.Application):
MainController = Controller()
theme = Gtk.IconTheme.get_default()
icons = theme.list_icons(None)
if STEAM_ICON not in icons:
logger.warn(strings.steam_icon_missing)
warnings.warn(strings.steam_icon_missing, stacklevel=2)
try:
theme = Gtk.IconTheme.get_default()
icons = theme.list_icons(None)
if STEAM_ICON not in icons:
logger.warn(strings.steam_icon_missing)
warnings.warn(strings.steam_icon_missing, stacklevel=2)
except Exception as e:
logger.warning(e)
warnings.warn(strings.gtk_theme_missing, stacklevel=2)
+9 -6
View File
@@ -1,6 +1,7 @@
import logging
import os
from pathlib import Path
from typing import Literal, TYPE_CHECKING
from dzgui.const.constants import (
@@ -13,6 +14,7 @@ from dzgui.const.constants import (
from dzgui.const.enum import ServerTab
from dzgui.managers.update import UpdateManager
from dzgui.util.clip import copy_clipboard
from dzgui.util.file import is_writeable
from dzgui.views.components.buttonbox import ButtonBox
from dzgui.views.components.filter_panel import FilterPanel
from dzgui.views.components.mod_panel import ModSelectionPanel
@@ -87,11 +89,12 @@ class RightPanel(Gtk.Box):
)
exe_path = os.getenv("PYAPP")
if exe_path and update is not None:
self.update_button.set_halign(Gtk.Align.END)
self.gutter_box.add(self.update_button)
self.update_button.connect(
"clicked", self._on_update_button_clicked, exe_path, update
)
if is_writeable(Path(exe_path)) is True:
self.update_button.set_halign(Gtk.Align.END)
self.gutter_box.add(self.update_button)
self.update_button.connect(
"clicked", self._on_update_button_clicked, exe_path, update
)
self.gutter_box.add(eb)
self.pack_start(self.gutter_box, NO_EXPAND, FILL, NO_PADDING)
@@ -108,7 +111,7 @@ class RightPanel(Gtk.Box):
return
self.filters_vbox.set_sensitive(False)
# TODO: unless it is lan page
#self.refresh_button.set_sensitive(False)
# self.refresh_button.set_sensitive(False)
def _on_lan_page_init(self, emitter: "Emitter") -> None:
self.filters_vbox.set_sensitive(False)
+13 -1
View File
@@ -106,6 +106,9 @@ class PreConnectionAssistant(Gtk.ScrolledWindow):
label=preconnect.back, halign=Gtk.Align.END, hexpand=True
)
self.ok = Gtk.Button(label=preconnect.update_mods, halign=Gtk.Align.END)
self.connect_last = Gtk.Button(
label=preconnect.connect_last, halign=Gtk.Align.END, tooltip_text=preconnect.connect_last_tooltip
)
# TODO: abstract
self.raise_window = Gtk.CheckButton(
@@ -126,13 +129,14 @@ class PreConnectionAssistant(Gtk.ScrolledWindow):
spacing=5,
)
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
for button in self.back, self.ok:
for button in self.back, self.ok, self.connect_last:
box.add(button)
for el in self.raise_window, box:
self.button_box.add(el)
self.back.connect("clicked", self._on_back_clicked)
self.ok.connect("clicked", self._on_ok_clicked)
self.connect_last.connect("clicked", self._on_connect_last_clicked)
self.title = Gtk.Label(label="")
add_class(self.title, "preconnect-heading")
@@ -213,6 +217,9 @@ class PreConnectionAssistant(Gtk.ScrolledWindow):
if event.keyval == Gdk.KEY_u:
self.ok.emit("clicked")
def _on_connect_last_clicked(self, button: Gtk.Button) -> None:
self.controller.update_and_load_to_menu(self.raise_window.get_active())
def _on_ok_clicked(self, button: Gtk.Button) -> None:
self.controller.update_and_connect(self.raise_window.get_active())
@@ -308,6 +315,11 @@ class PreConnectionAssistant(Gtk.ScrolledWindow):
prefix = preconnect.total_mods
self.mod_count.set_text(f"{prefix}{str(total_mods)}.{suffix}")
if prereqs.is_last_server:
self.connect_last.show()
else:
self.connect_last.hide()
self._process_warnings(prereqs)
if self.tree.is_visible():
self.tree.grab_focus()
+7 -1
View File
@@ -47,7 +47,13 @@ class ServerNotebook(Gtk.ScrolledWindow):
self.recent = ServerTreeView(
controller, ServerTab.RECENT, ContextMenuGroup.RECENT
)
self.lan = ServerTreeView(controller, ServerTab.LAN, ContextMenuGroup.SCAN_LAN)
self.lan = ServerTreeView(
controller,
ServerTab.LAN,
ContextMenuGroup.SCAN_LAN,
show_empty=True,
show_full=True,
)
tabs = [
(self.browser, server_labels.browser),
+7 -2
View File
@@ -38,7 +38,12 @@ QUEUE_CHECK_DELAY = 200
class ServerTreeView(ContextMixin, TreeView): # type: ignore
def __init__(
self, controller: "Controller", enum: ServerTab, menu: ContextMenuGroup
self,
controller: "Controller",
enum: ServerTab,
menu: ContextMenuGroup,
show_empty: bool = False,
show_full: bool = False,
) -> None:
super().__init__(controller, menu=menu)
@@ -48,7 +53,7 @@ class ServerTreeView(ContextMixin, TreeView): # type: ignore
self.loaded = False
self.filter_man = FilterManager()
self.filter_man = FilterManager(show_empty, show_full)
self.proxy_man = ProxyModelManager(self.filter_man)
model = self.proxy_man.get_proxy_model()
self.set_model(model)
+1 -1
View File
@@ -4,7 +4,7 @@ description = "DayZ server browser and mod manager for Linux"
authors = [
{name = "aclist"}
]
version = "7.0.0b7"
version = "7.0.0b8"
license = "GPL-3.0-or-later"
license-files = ["LICENSE"]
readme = "README.md"
+5 -2
View File
@@ -53,7 +53,8 @@ def untar(file: str, glob: str, renamed: str) -> None:
def install_deps() -> None:
pip = build_dir.joinpath(f"{CPYTHON_BUILD}/bin/pip")
subprocess.run([pip, "install", root])
proc = subprocess.run([pip, "install", root])
return proc.returncode
def repackage() -> None:
@@ -66,7 +67,9 @@ def repackage() -> None:
def rebuild_cpython() -> None:
if cpython_dir.is_dir() is False:
get_cpython()
install_deps()
code = install_deps()
if code != 0:
return
repackage()
return get_packaged_version()
+12 -4
View File
@@ -4,10 +4,8 @@ import subprocess
import tarfile
from pathlib import Path
from prebuild import get_pyapp, rebuild_cpython
root = Path(__file__).resolve().parents[1]
output = root.joinpath("dist")
build_dir = root.joinpath("build")
@@ -17,6 +15,7 @@ wheel = builder.build("wheel", output_directory=output)
stem = Path(wheel).stem
tarname = f"{stem}.tar.gz"
metadata = stem.split("-")
appname = metadata[0]
version = metadata[1]
@@ -44,9 +43,18 @@ env["PYAPP_FULL_ISOLATION"] = "true"
env["PYAPP_DISTRIBUTION_PATH"] = str(cpython)
env["PYAPP_DISTRIBUTION_PYTHON_PATH"] = "python/bin/python3"
proc = subprocess.run(["cargo", "build", "--release"], env=env, cwd=pyapp_dir)
target = "x86_64-unknown-linux-musl"
build_params = [ "cargo", "build", "--release", "--target", target]
proc = subprocess.run(
build_params,
env=env,
cwd=pyapp_dir,
)
# TODO: set proper release tags on tarfile
if proc.returncode == 0:
output_exe = build_dir.joinpath("pyapp-latest/target/release/pyapp")
output_exe = build_dir.joinpath(f"pyapp-latest/target/{target}/release/pyapp")
release_exe = output.joinpath(appname)
output_exe.rename(release_exe)
tarpath = output.joinpath(tarname)
+27
View File
@@ -0,0 +1,27 @@
from dzgui.config.ipdb import get_ipdb
from pathlib import Path
import os
import pytest
import tempfile
@pytest.mark.slow
def test_stripping():
d = tempfile.TemporaryDirectory()
path = Path(d.name).joinpath("ips.csv")
get_ipdb(path)
with open(path, "rb") as f:
first = f.readline().decode().rstrip()
try:
f.seek(-2, os.SEEK_END)
while f.read(1) != b"\n":
f.seek(-2, os.SEEK_CUR)
except OSError:
f.seek(0)
last = f.read().decode().rstrip()
first_els = first.split(",")
last_els = last.split(",")
assert first_els[0] == "0.0.0.0"
assert last_els[0] == "224.0.0.0"