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
This commit is contained in:
aclist authored and GitHub committed 2026-05-29 01:09:25 +09:00
commit dac02d75f6
22 files changed
+184 -50

No files matched your search

+2 -1
View File
@@ -36,6 +36,7 @@
- Preconnect warnings/failsafes like filesize - Preconnect warnings/failsafes like filesize
- Save filters per server context between sessions - Save filters per server context between sessions
- Preboot progress dialog - Preboot progress dialog
- Choose to jump into splash screen instead of server
## Changed ## Changed
@@ -52,6 +53,7 @@
- Embed Workshop link in Options menu - Embed Workshop link in Options menu
- Disable overlay scrollbars on server tables - Disable overlay scrollbars on server tables
- Reduce size of geolocation DB on disk (~100MB) - Reduce size of geolocation DB on disk (~100MB)
- Enable LAN page Empty/Full filters on first run of app
## Dropped ## Dropped
- Debug mode - Debug mode
@@ -67,7 +69,6 @@
## Unreleased ## Unreleased
- Load offline mods - Load offline mods
- Choose to jump into splash screen instead of server
- Local documentation - Local documentation
- Raw debug command in context menu - Raw debug command in context menu
+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) vers = get_dayz_version(pe_file_path)
dayz_version = dayz_version_to_str(vers) dayz_version = dayz_version_to_str(vers)
return dayz_version return dayz_version
except Exception as e: except Exception:
print(e)
return None return None
+6
View File
@@ -61,6 +61,8 @@ class Details:
@dataclass(slots=True) @dataclass(slots=True)
class Record: 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 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])) 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: def response_to_fqip(res: dict) -> str:
ip = res["addr"].split(":")[0] ip = res["addr"].split(":")[0]
gameport = res["gameport"] 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 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: def find_user_id(path: Path) -> str | None:
resolved_path = path / "config" / "loginusers.vdf" resolved_path = path / "config" / "loginusers.vdf"
try: try:
+8 -2
View File
@@ -409,9 +409,12 @@ class Controller(GObject.GObject):
saved_tree = self.get_servers().get_saved() saved_tree = self.get_servers().get_saved()
ServerModelManager(self, saved_tree).remove_by_record(record) 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() 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: def remove_from_history(self, record: "Record") -> None:
# NOTE: remove action is only possible from history tree context menu, # 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() ind = self.config_man.get_start_tab()
self.get_servers().notebook.set_current_page(ind) 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: def update_and_connect(self, raise_window: bool) -> None:
self.connection_man.update_and_connect(raise_window) self.connection_man.update_and_connect(raise_window)
+3 -1
View File
@@ -36,6 +36,7 @@
- Preconnect warnings/failsafes like filesize - Preconnect warnings/failsafes like filesize
- Save filters per server context between sessions - Save filters per server context between sessions
- Preboot progress dialog - Preboot progress dialog
- Choose to jump into splash screen instead of server
## Changed ## Changed
@@ -51,6 +52,8 @@
- Suppress log messages from imported modules - Suppress log messages from imported modules
- Embed Workshop link in Options menu - Embed Workshop link in Options menu
- Disable overlay scrollbars on server tables - 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 ## Dropped
- Debug mode - Debug mode
@@ -66,7 +69,6 @@
## Unreleased ## Unreleased
- Load offline mods - Load offline mods
- Choose to jump into splash screen instead of server
- Local documentation - Local documentation
- Raw debug command in context menu - Raw debug command in context menu
+19 -4
View File
@@ -1,6 +1,7 @@
import logging import logging
import traceback import traceback
from collections import deque
from typing import Any, TYPE_CHECKING from typing import Any, TYPE_CHECKING
from dzgui.const.constants import ( from dzgui.const.constants import (
@@ -52,11 +53,25 @@ class ConfigManager:
ips.append(record) ips.append(record)
self.update_config(Preferences.IP_LIST, ips) 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: with open(self.prefs.paths.history, "w") as f:
for record in records: for record in unique:
addr = f"{record[7]}:{record[8]}" f.write(f"{record}\n")
f.write(f"{addr}\n")
def remove_saved_server(self, record: str) -> None: def remove_saved_server(self, record: str) -> None:
ips = self.lookup(Preferences.IP_LIST) ips = self.lookup(Preferences.IP_LIST)
+27 -8
View File
@@ -15,6 +15,7 @@ from dzgui.api.steam import (
enqueue_mod, enqueue_mod,
get_remote_signatures, get_remote_signatures,
get_needs_update, get_needs_update,
load_to_menu,
) )
from dzgui.api.mods import ( from dzgui.api.mods import (
@@ -74,6 +75,7 @@ class Prerequisites:
steam_proc: SteamProcess steam_proc: SteamProcess
mods: list[list[str]] mods: list[list[str]]
game_mode: bool game_mode: bool
is_last_server: bool
class ConnectionManager: class ConnectionManager:
@@ -172,6 +174,8 @@ class ConnectionManager:
game_mode = prefs.is_game_mode game_mode = prefs.is_game_mode
is_last = self.is_last_server()
prereqs = Prerequisites( prereqs = Prerequisites(
name=info.server_name, name=info.server_name,
appid=info.game_id, appid=info.game_id,
@@ -186,11 +190,23 @@ class ConnectionManager:
steam_proc=steam_proc, steam_proc=steam_proc,
mods=remote_mods, mods=remote_mods,
game_mode=game_mode, game_mode=game_mode,
is_last_server=is_last,
) )
func = StoredFunc(self.controller.open_connection_assistant, prereqs) func = StoredFunc(self.controller.open_connection_assistant, prereqs)
self.thread_man.set_cleanup_func(func, destroy_first=True) 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) @call_on_thread(dialog.querying)
def query_details(self, record: Servers.Record) -> None: def query_details(self, record: Servers.Record) -> None:
details = Servers.get_details(record) details = Servers.get_details(record)
@@ -245,11 +261,14 @@ class ConnectionManager:
dialog = ExceptionDialog(self.controller, server_timeout) dialog = ExceptionDialog(self.controller, server_timeout)
dialog.run() dialog.run()
def _connect_steam(self) -> None: def _connect_steam(self, menu_only: bool) -> None:
addr = f"{self.record.ip}:{self.record.gameport}" addr = f"{self.record.ip}:{self.record.gameport}"
playername = self.controller.query_config(Preferences.NAME) playername = self.controller.query_config(Preferences.NAME)
client = self.controller.query_config(Preferences.CLIENT) 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: if rc != 0:
# TODO: log/pop the error # TODO: log/pop the error
func = StoredFunc(self.controller.update_status) func = StoredFunc(self.controller.update_status)
@@ -271,10 +290,10 @@ class ConnectionManager:
self.thread_man.set_cleanup_func(func) self.thread_man.set_cleanup_func(func)
def _add_to_history_and_return(self) -> None: 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) 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 # NOTE: fast enqueue all mods in auto mode
prefs = self.controller.get_prefs() prefs = self.controller.get_prefs()
@@ -312,11 +331,11 @@ class ConnectionManager:
update_signatures(self.missing_mods, prefs.paths.version) update_signatures(self.missing_mods, prefs.paths.version)
# TODO: just push steam path directly # TODO: just push steam path directly
rebuild_symlinks(prefs.paths.config) rebuild_symlinks(prefs.paths.config)
self._connect_steam() self._connect_steam(menu_only)
@call_on_thread(waiting_for_mods, show_cancel=True) @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: if len(self.missing_mods) > 0:
self._update_mods(raise_window) self._update_mods(raise_window, menu_only)
else: else:
self._connect_steam() self._connect_steam(menu_only)
+3 -3
View File
@@ -9,7 +9,7 @@ if TYPE_CHECKING:
class FilterManager: 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() self.map_store = ModelFactory().make_map_store()
@@ -17,10 +17,10 @@ class FilterManager:
self.default_filters = { self.default_filters = {
strings.filter_1pp: True, strings.filter_1pp: True,
strings.filter_day: True, strings.filter_day: True,
strings.filter_empty: False, strings.filter_empty: show_empty,
strings.filter_3pp: True, strings.filter_3pp: True,
strings.filter_night: True, strings.filter_night: True,
strings.filter_full: False, strings.filter_full: show_full,
strings.filter_lowpop: True, strings.filter_lowpop: True,
strings.filter_nonascii: False, strings.filter_nonascii: False,
strings.filter_duplicate: False, strings.filter_duplicate: False,
+3 -2
View File
@@ -85,8 +85,9 @@ class ProxyModelManager:
if found is False: if found is False:
self.control_model.append(history) 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) 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.const.enum import FilterMode, Preferences, ServerTab
from dzgui.managers.threading import call_on_thread, StoredFunc, ThreadingManager 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.util.strings import api_warn_msg, dialog
from dzgui.views.dialogs.generic import ExceptionDialog from dzgui.views.dialogs.generic import ExceptionDialog
@@ -23,7 +24,7 @@ gi.require_version("Gtk", "3.0")
from gi.repository import Gtk # noqa E402 from gi.repository import Gtk # noqa E402
if TYPE_CHECKING: if TYPE_CHECKING:
from dzgui.api.servers import A2SInfo from dzgui.api.servers import A2SInfo, Record
from dzgui.controllers.mc import Controller from dzgui.controllers.mc import Controller
from dzgui.model.proxy_model import ProxyModelManager from dzgui.model.proxy_model import ProxyModelManager
from dzgui.views.trees.tree_servers import ServerTreeView from dzgui.views.trees.tree_servers import ServerTreeView
@@ -130,7 +131,7 @@ class ServerModelManager:
servers = [] servers = []
ports = range(1, 256) ports = range(1, 256)
failure_func = StoredFunc(self._cleanup_on_failure) failure_func = StoredFunc(self._cleanup_on_lan_failure)
event = threading.Event() event = threading.Event()
with ThreadPoolExecutor() as executor: with ThreadPoolExecutor() as executor:
@@ -225,15 +226,12 @@ class ServerModelManager:
) = None, ) = None,
) -> None: ) -> None:
proxy_man = self._get_proxy_man() proxy_man = self._get_proxy_man()
config_man = self.controller.get_config_man()
if rows is not None: if rows is not None:
records = rows records = rows
else: else:
control_model = proxy_man.get_control() control_model = proxy_man.get_control()
records = control_model records = control_model
config_man.update_history_file(records)
config_man.update_history_file(records)
self._sort_unique_maps(records) self._sort_unique_maps(records)
proxy = proxy_man.get_proxy_model() proxy = proxy_man.get_proxy_model()
self.tv.set_model(proxy) self.tv.set_model(proxy)
@@ -249,15 +247,20 @@ class ServerModelManager:
self.emitter.emit("load_maps", store) self.emitter.emit("load_maps", store)
# self.emitter.emit("servers_loaded_init") # 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() proxy_man = self._get_proxy_man()
rows = Servers.parse_json([record]) rows = Servers.parse_json([row])
try: try:
proxy_man.append_row_to_history(rows[0]) proxy_man.append_row_to_history(rows[0])
self.update_history()
except Exception: except Exception:
self.update_history(rows) 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: def remove_from_history(self, record: Servers.Record) -> None:
proxy_man = self._get_proxy_man() proxy_man = self._get_proxy_man()
@@ -403,6 +406,16 @@ class ServerModelManager:
if self.first_iteration: if self.first_iteration:
self._update_maps() 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: def _cleanup_on_failure(self, show_dialog: bool = True) -> None:
# TODO: disable map, keyword, and filter widgets if model is None # TODO: disable map, keyword, and filter widgets if model is None
# signal driven (servers_empty, servers_failed_to_load) # 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" fetching_update = "Fetching update"
failed_to_update = "Failed to update DZGUI executable" failed_to_update = "Failed to update DZGUI executable"
update_success = "Updated successfully. Please exit and relaunch." 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" update_mods = "Update mods and connect"
connect = "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" back = "Back"
cancel = "Cancel" cancel = "Cancel"
warnings = "Warnings" 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", keys_tooltip="Toggles the keybindings dialog",
) )
gtk_theme_missing = "No GTK theme provided"
steam_icon_missing = "Steam icon not found in IconTheme" steam_icon_missing = "Steam icon not found in IconTheme"
missing_changelog = "Error: failed to read changelog" missing_changelog = "Error: failed to read changelog"
+9 -5
View File
@@ -323,8 +323,12 @@ class App(Gtk.Application):
MainController = Controller() MainController = Controller()
theme = Gtk.IconTheme.get_default() try:
icons = theme.list_icons(None) theme = Gtk.IconTheme.get_default()
if STEAM_ICON not in icons: icons = theme.list_icons(None)
logger.warn(strings.steam_icon_missing) if STEAM_ICON not in icons:
warnings.warn(strings.steam_icon_missing, stacklevel=2) 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 logging
import os import os
from pathlib import Path
from typing import Literal, TYPE_CHECKING from typing import Literal, TYPE_CHECKING
from dzgui.const.constants import ( from dzgui.const.constants import (
@@ -13,6 +14,7 @@ from dzgui.const.constants import (
from dzgui.const.enum import ServerTab from dzgui.const.enum import ServerTab
from dzgui.managers.update import UpdateManager from dzgui.managers.update import UpdateManager
from dzgui.util.clip import copy_clipboard 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.buttonbox import ButtonBox
from dzgui.views.components.filter_panel import FilterPanel from dzgui.views.components.filter_panel import FilterPanel
from dzgui.views.components.mod_panel import ModSelectionPanel from dzgui.views.components.mod_panel import ModSelectionPanel
@@ -87,11 +89,12 @@ class RightPanel(Gtk.Box):
) )
exe_path = os.getenv("PYAPP") exe_path = os.getenv("PYAPP")
if exe_path and update is not None: if exe_path and update is not None:
self.update_button.set_halign(Gtk.Align.END) if is_writeable(Path(exe_path)) is True:
self.gutter_box.add(self.update_button) self.update_button.set_halign(Gtk.Align.END)
self.update_button.connect( self.gutter_box.add(self.update_button)
"clicked", self._on_update_button_clicked, exe_path, update self.update_button.connect(
) "clicked", self._on_update_button_clicked, exe_path, update
)
self.gutter_box.add(eb) self.gutter_box.add(eb)
self.pack_start(self.gutter_box, NO_EXPAND, FILL, NO_PADDING) self.pack_start(self.gutter_box, NO_EXPAND, FILL, NO_PADDING)
@@ -108,7 +111,7 @@ class RightPanel(Gtk.Box):
return return
self.filters_vbox.set_sensitive(False) self.filters_vbox.set_sensitive(False)
# TODO: unless it is lan page # 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: def _on_lan_page_init(self, emitter: "Emitter") -> None:
self.filters_vbox.set_sensitive(False) 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 label=preconnect.back, halign=Gtk.Align.END, hexpand=True
) )
self.ok = Gtk.Button(label=preconnect.update_mods, halign=Gtk.Align.END) 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 # TODO: abstract
self.raise_window = Gtk.CheckButton( self.raise_window = Gtk.CheckButton(
@@ -126,13 +129,14 @@ class PreConnectionAssistant(Gtk.ScrolledWindow):
spacing=5, spacing=5,
) )
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, 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) box.add(button)
for el in self.raise_window, box: for el in self.raise_window, box:
self.button_box.add(el) self.button_box.add(el)
self.back.connect("clicked", self._on_back_clicked) self.back.connect("clicked", self._on_back_clicked)
self.ok.connect("clicked", self._on_ok_clicked) self.ok.connect("clicked", self._on_ok_clicked)
self.connect_last.connect("clicked", self._on_connect_last_clicked)
self.title = Gtk.Label(label="") self.title = Gtk.Label(label="")
add_class(self.title, "preconnect-heading") add_class(self.title, "preconnect-heading")
@@ -213,6 +217,9 @@ class PreConnectionAssistant(Gtk.ScrolledWindow):
if event.keyval == Gdk.KEY_u: if event.keyval == Gdk.KEY_u:
self.ok.emit("clicked") 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: def _on_ok_clicked(self, button: Gtk.Button) -> None:
self.controller.update_and_connect(self.raise_window.get_active()) self.controller.update_and_connect(self.raise_window.get_active())
@@ -308,6 +315,11 @@ class PreConnectionAssistant(Gtk.ScrolledWindow):
prefix = preconnect.total_mods prefix = preconnect.total_mods
self.mod_count.set_text(f"{prefix}{str(total_mods)}.{suffix}") 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) self._process_warnings(prereqs)
if self.tree.is_visible(): if self.tree.is_visible():
self.tree.grab_focus() self.tree.grab_focus()
+7 -1
View File
@@ -47,7 +47,13 @@ class ServerNotebook(Gtk.ScrolledWindow):
self.recent = ServerTreeView( self.recent = ServerTreeView(
controller, ServerTab.RECENT, ContextMenuGroup.RECENT 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 = [ tabs = [
(self.browser, server_labels.browser), (self.browser, server_labels.browser),
+7 -2
View File
@@ -38,7 +38,12 @@ QUEUE_CHECK_DELAY = 200
class ServerTreeView(ContextMixin, TreeView): # type: ignore class ServerTreeView(ContextMixin, TreeView): # type: ignore
def __init__( 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: ) -> None:
super().__init__(controller, menu=menu) super().__init__(controller, menu=menu)
@@ -48,7 +53,7 @@ class ServerTreeView(ContextMixin, TreeView): # type: ignore
self.loaded = False self.loaded = False
self.filter_man = FilterManager() self.filter_man = FilterManager(show_empty, show_full)
self.proxy_man = ProxyModelManager(self.filter_man) self.proxy_man = ProxyModelManager(self.filter_man)
model = self.proxy_man.get_proxy_model() model = self.proxy_man.get_proxy_model()
self.set_model(model) self.set_model(model)
+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.0b7" version = "7.0.0b8"
license = "GPL-3.0-or-later" license = "GPL-3.0-or-later"
license-files = ["LICENSE"] license-files = ["LICENSE"]
readme = "README.md" readme = "README.md"
+5 -2
View File
@@ -53,7 +53,8 @@ def untar(file: str, glob: str, renamed: str) -> None:
def install_deps() -> None: def install_deps() -> None:
pip = build_dir.joinpath(f"{CPYTHON_BUILD}/bin/pip") 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: def repackage() -> None:
@@ -66,7 +67,9 @@ def repackage() -> None:
def rebuild_cpython() -> None: def rebuild_cpython() -> None:
if cpython_dir.is_dir() is False: if cpython_dir.is_dir() is False:
get_cpython() get_cpython()
install_deps() code = install_deps()
if code != 0:
return
repackage() repackage()
return get_packaged_version() return get_packaged_version()