Compare commits

..

No commits in common. "d305f47bb941d86f61952574145606b917b5970e" and "b8230cbf43fe9083c199e6f788f58b07f248a4c1" have entirely different histories.

12 changed files with 34 additions and 82 deletions

View File

@ -12,7 +12,6 @@ from dzgui.api.servers import get_rules, fqip_to_record
from dzgui.const.constants import ( from dzgui.const.constants import (
APP_NAME, APP_NAME,
APPID_DAYZ, APPID_DAYZ,
DAYZ_COMMUNITY_ROOT,
LIBRARYFOLDERS_PATH, LIBRARYFOLDERS_PATH,
WORKSHOP_PATH, WORKSHOP_PATH,
) )
@ -51,9 +50,6 @@ def get_local_mods(workshop_path: Path) -> list[Path]:
def is_mission(path: Path) -> bool: def is_mission(path: Path) -> bool:
# TODO: parse integrity of other files # TODO: parse integrity of other files
parent = path.parent.name
if parent != DAYZ_COMMUNITY_ROOT:
return False
file = path / "init.c" file = path / "init.c"
return file.exists() return file.exists()
@ -85,7 +81,6 @@ def get_mod_size(path: Path) -> float:
def get_custom_mods(path: Path) -> list[Any]: def get_custom_mods(path: Path) -> list[Any]:
# TODO: rename this function for clarity ("get local vs. get custom")
mods = get_local_mods(path) mods = get_local_mods(path)
# TODO: error handling # TODO: error handling
return parse_mods(mods, use_custom=True) return parse_mods(mods, use_custom=True)

View File

@ -73,9 +73,14 @@ def get_local_signatures(version_file: Path) -> dict[str, int]:
return hashes return hashes
def enqueue_mod(client: str, mod: str, appid: int) -> None: def enqueue_mod(mod: str, appid: int) -> None:
client_args = concat_bash_args(client) args = [
subprocess.Popen([*client_args, "+workshop_download_item", str(appid), mod]) "steam",
f"steam://url/CommunityFilePage/{mod}+workshop_download_item",
str(appid),
mod,
]
subprocess.Popen(["/usr/bin/env", "bash", *args])
def get_needs_update( def get_needs_update(

View File

@ -83,5 +83,3 @@ LOG_FILTERS = ("CRITICAL", "WARNING", "INFO", "DEBUG")
TMP_PATH = "/tmp" TMP_PATH = "/tmp"
TMP_TARBALL = "/tmp/dzgui.tar.gz" TMP_TARBALL = "/tmp/dzgui.tar.gz"
TMP_EXE = "/tmp/dzgui/dzgui" TMP_EXE = "/tmp/dzgui/dzgui"
DAYZ_COMMUNITY_ROOT = "Missions"

View File

@ -88,7 +88,6 @@ class ConnectionManager:
self.record: Record self.record: Record
self.workshop: Path self.workshop: Path
self.client: str
self.remote_mod_ids: list[str] = [] self.remote_mod_ids: list[str] = []
self.missing_mods: list[tuple[str, str, int, int]] = [] self.missing_mods: list[tuple[str, str, int, int]] = []
@ -172,7 +171,6 @@ class ConnectionManager:
client = self.controller.query_config(Preferences.CLIENT) client = self.controller.query_config(Preferences.CLIENT)
running = is_steam_running(client) running = is_steam_running(client)
steam_proc = SteamProcess(client_name, running) steam_proc = SteamProcess(client_name, running)
self.client = client
game_mode = prefs.is_game_mode game_mode = prefs.is_game_mode
@ -265,10 +263,11 @@ class ConnectionManager:
def _connect_steam(self, menu_only: bool) -> 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)
if menu_only: if menu_only:
rc = load_to_menu(self.client, self.appid, playername, self.remote_mod_ids) rc = load_to_menu(client, self.appid, playername, self.remote_mod_ids)
else: else:
rc = connect(self.client, addr, self.appid, playername, self.remote_mod_ids) 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)
@ -300,8 +299,7 @@ class ConnectionManager:
for title, mod, stamp, size in self.missing_mods: for title, mod, stamp, size in self.missing_mods:
if self.controller.is_cancel_pending(): if self.controller.is_cancel_pending():
return return
enqueue_mod(self.client, mod, self.appid) enqueue_mod(mod, self.appid)
# NOTE: prevents rate limiting
time.sleep(3) time.sleep(3)
if raise_window is True: if raise_window is True:

View File

@ -66,9 +66,6 @@ class ModManager:
func = StoredFunc(self._on_mods_loaded) func = StoredFunc(self._on_mods_loaded)
self.thread_man.set_cleanup_func(func) self.thread_man.set_cleanup_func(func)
def set_store(self, store: "FastInsertListStore") -> None:
self.store = store
def _on_mods_loaded(self) -> None: def _on_mods_loaded(self) -> None:
self.treeview.set_model(self.store) self.treeview.set_model(self.store)
if self.store is None: if self.store is None:

View File

@ -95,7 +95,6 @@ class OfflineManager:
) )
combined_mods.extend(new_symlinks) combined_mods.extend(new_symlinks)
relative_link = ""
if len(mission) > 0: if len(mission) > 0:
relative_link = symlink_mission(Path(steam_path), mission) relative_link = symlink_mission(Path(steam_path), mission)

View File

@ -10,7 +10,6 @@ custom_button = "Set custom mod folder"
local_frame = "Installed mods" local_frame = "Installed mods"
no_local_mods = "No local mods found"
no_mods = "No valid mods found" no_mods = "No valid mods found"
no_mission = "Not a valid mission" no_mission = "Not a valid mission"

View File

@ -2,7 +2,7 @@ import logging
from pathlib import Path from pathlib import Path
from dzgui.api.mods import get_local_mod_path, get_local_mod_ids, _hash from dzgui.api.mods import get_local_mod_path, get_local_mod_ids, _hash
from dzgui.const.constants import APPID_DAYZ, APPID_DAYZ_EXP, APP_NAME, DAYZ_COMMUNITY_ROOT from dzgui.const.constants import APPID_DAYZ, APPID_DAYZ_EXP, APP_NAME
from dzgui.const.enum import Preferences from dzgui.const.enum import Preferences
from dzgui.config.query import lookup from dzgui.config.query import lookup
@ -11,23 +11,18 @@ import dzgui.api.pefile as PeFile
logger = logging.getLogger(APP_NAME) logger = logging.getLogger(APP_NAME)
def expunge_link(file: Path) -> None:
if not file.is_symlink():
return
# NOTE: unlink stale symlinks
if file.exists() is False:
file.unlink()
if str(file.stem)[:2] == "@C":
file.unlink()
def rebuild_symlinks(config: Path) -> None: def rebuild_symlinks(config: Path) -> None:
# TODO: pass direct path as argument # TODO: pass direct path as argument
path = lookup(config, Preferences.DEFAULT) path = lookup(config, Preferences.DEFAULT)
steam_path = Path(path) steam_path = Path(path)
dayz_path = PeFile.get_nested_app_path(steam_path, APPID_DAYZ) dayz_path = PeFile.get_nested_app_path(steam_path, APPID_DAYZ)
for file in dayz_path.iterdir(): for file in dayz_path.iterdir():
expunge_link(file) # NOTE: unlink stale symlinks
if file.is_symlink() and file.exists() is False:
file.unlink()
# NOTE: expunge ephemeral (custom) symlinks
if file.is_symlink() and str(file.stem)[:2] == "@C":
file.unlink()
workshop = get_local_mod_path(steam_path) workshop = get_local_mod_path(steam_path)
# NOTE: create symlinks for missing mods # NOTE: create symlinks for missing mods
for mod_id in get_local_mod_ids(steam_path): for mod_id in get_local_mod_ids(steam_path):
@ -54,20 +49,16 @@ def create_custom_symlinks(
clone_symlinks(steam_path) clone_symlinks(steam_path)
return links return links
def symlink_mission(steam_path: Path, target: str) -> None:
def symlink_mission(steam_path: Path, target: str) -> str:
dayz_path = PeFile.get_nested_app_path(steam_path, APPID_DAYZ) dayz_path = PeFile.get_nested_app_path(steam_path, APPID_DAYZ)
for file in dayz_path.iterdir(): for file in dayz_path.iterdir():
expunge_link(file) if file.is_symlink() and str(file.stem)[:2] == "@C":
path = Path(target) file.unlink()
stem = path.name stem = Path(target).name
parent = path.parent suffix = _hash(stem, use_custom=True)
source = dayz_path.joinpath(DAYZ_COMMUNITY_ROOT) source = dayz_path.joinpath(suffix)
try: source.symlink_to(Path(target))
source.symlink_to(parent) return suffix
except Exception:
return ""
return f"{DAYZ_COMMUNITY_ROOT}/{stem}"
def clone_symlinks(steam_path: Path) -> None: def clone_symlinks(steam_path: Path) -> None:

View File

@ -25,8 +25,6 @@ class Statusbar(Gtk.Grid):
self.controller.register_widget("statusbar", self) self.controller.register_widget("statusbar", self)
self.emitter = controller.get_emitter() self.emitter = controller.get_emitter()
self.prior_enum: NotebookPage
self.prior_status: str
self.playercount = "" self.playercount = ""
self.statusbar = Gtk.Statusbar() self.statusbar = Gtk.Statusbar()
@ -73,7 +71,6 @@ class Statusbar(Gtk.Grid):
NotebookPage.CONNECTION, NotebookPage.CONNECTION,
NotebookPage.OFFLINE, NotebookPage.OFFLINE,
): ):
self.prior_enum = enum
self.set_by_context(enum, esc_to_return) self.set_by_context(enum, esc_to_return)
return return
@ -83,9 +80,8 @@ class Statusbar(Gtk.Grid):
case NotebookPage.KEYS: case NotebookPage.KEYS:
bar = question_to_return bar = question_to_return
case _: case _:
bar = self.prior_status return
self.prior_enum = enum
self.set_by_context(enum, bar) self.set_by_context(enum, bar)
def _on_server_row_changed(self, statusbar: Self) -> None: def _on_server_row_changed(self, statusbar: Self) -> None:
@ -101,10 +97,6 @@ class Statusbar(Gtk.Grid):
context: Union["ServerTab", NotebookPage], context: Union["ServerTab", NotebookPage],
) -> None: ) -> None:
self.spinner.stop() self.spinner.stop()
if type(context) is NotebookPage:
self.prior_enum = context
self.prior_status = ""
# FIXME: CalcDist is being called when table is not loaded # FIXME: CalcDist is being called when table is not loaded
if dist is None: if dist is None:
self.set_by_context(context, "") self.set_by_context(context, "")
@ -130,8 +122,6 @@ class Statusbar(Gtk.Grid):
def set_by_context( def set_by_context(
self, context: Union[NotebookPage, "ServerTab"], string: str self, context: Union[NotebookPage, "ServerTab"], string: str
) -> None: ) -> None:
if context != NotebookPage.KEYS:
self.prior_status = string
meta = self.statusbar.get_context_id(str(context)) meta = self.statusbar.get_context_id(str(context))
self.statusbar.push(meta, string) self.statusbar.push(meta, string)
self.set_cache(string) self.set_cache(string)

View File

@ -11,7 +11,6 @@ from dzgui.const.constants import (
EDIT_DELETE, EDIT_DELETE,
ERROR, ERROR,
FOLDER, FOLDER,
WARNING,
) )
from dzgui.const.enum import ContextMenuGroup, NotebookPage from dzgui.const.enum import ContextMenuGroup, NotebookPage
from dzgui.managers.offline import OfflineManager from dzgui.managers.offline import OfflineManager
@ -174,7 +173,6 @@ class FolderHBox(HBox):
self.pop.set_label(error, msg) self.pop.set_label(error, msg)
self.pop.popup() self.pop.popup()
class ModFrame(HeadingFrame): class ModFrame(HeadingFrame):
def __init__( def __init__(
self, parent: OfflineLoader, controller: "Controller", label: str self, parent: OfflineLoader, controller: "Controller", label: str
@ -200,39 +198,20 @@ class ModFrame(HeadingFrame):
self.tree_vbox = VBox() self.tree_vbox = VBox()
self.tree_vbox.extend([self.scrolled, self.status]) self.tree_vbox.extend([self.scrolled, self.status])
self.no_mods = HBox(spacing=10)
no_mods_icon = Icon(WARNING, margin_start=10)
no_mods_label = Gtk.Label(label=offline.no_local_mods, halign=Gtk.Align.START)
self.no_mods.extend([no_mods_icon, no_mods_label])
self.vbox.pack_end(self.no_mods, expand=True, fill=True, padding=3)
self.vbox.pack_end(self.tree_vbox, expand=True, fill=True, padding=3) self.vbox.pack_end(self.tree_vbox, expand=True, fill=True, padding=3)
self.frame.add(self.vbox) self.frame.add(self.vbox)
sel = self.tree.get_selection() sel = self.tree.get_selection()
sel.connect("changed", self._on_selection_changed) sel.connect("changed", self._on_selection_changed)
self.connect("map", self._on_map)
self.connect("unmap", self._on_unmap) self.connect("unmap", self._on_unmap)
def start_empty(self) -> None:
self.tree_vbox.hide()
self.no_mods.show()
def _on_map(self, widget: Self) -> None:
self.no_mods.hide()
self.tree.set_model(None)
self.tree_vbox.hide()
def _on_unmap(self, widget: Self) -> None: def _on_unmap(self, widget: Self) -> None:
self.tree.set_model(None) self.tree.set_model(None)
self.tree_vbox.hide() self.tree_vbox.hide()
self.no_mods.hide()
def start(self, store: "FastInsertListStore") -> None: def start(self, store: "FastInsertListStore") -> None:
# TODO: this manipulates mod man store out of band
self.tree.set_model(store) self.tree.set_model(store)
self.tree.mod_man.set_store(store)
self.show_tree() self.show_tree()
def get_mods(self) -> list[str]: def get_mods(self) -> list[str]:
@ -240,7 +219,6 @@ class ModFrame(HeadingFrame):
if model is None: if model is None:
return [] return []
if type(self) is CustomModFrame: if type(self) is CustomModFrame:
# NOTE: custom mod dirs to be hashed
return [model[_iter][2] for _iter in treeiters] return [model[_iter][2] for _iter in treeiters]
else: else:
# NOTE: pre-existing, canonical symlinks to published mods # NOTE: pre-existing, canonical symlinks to published mods
@ -316,7 +294,6 @@ class CustomModFrame(ModFrame):
self.custom_hbox.hide_label() self.custom_hbox.hide_label()
self.tree.set_model(None) self.tree.set_model(None)
self.tree_vbox.hide() self.tree_vbox.hide()
self.no_mods.hide()
def present_error(self, folder: str) -> None: def present_error(self, folder: str) -> None:
self.hide_tree() self.hide_tree()
@ -482,12 +459,12 @@ class OfflineLoader(Gtk.Box):
self.ok.set_sensitive(True) self.ok.set_sensitive(True)
def _on_keypress(self, widget: Self, event: Gdk.EventKey) -> None: def _on_keypress(self, widget: Self, event: Gdk.EventKey) -> None:
# FIXME: widget is not always in focus
if event.keyval == Gdk.KEY_Escape: if event.keyval == Gdk.KEY_Escape:
self.back.emit("clicked") self.back.emit("clicked")
def populate(self, store: Union["FastInsertListStore", None]) -> None: def populate(self, store: Union["FastInsertListStore", None]) -> None:
if store is None: if store is None:
self.local_frame.start_empty()
return return
self.local_frame.start(store) self.local_frame.start(store)

View File

@ -333,7 +333,10 @@ class PreConnectionAssistant(Gtk.Box):
self.connect_last.hide() self.connect_last.hide()
self._process_warnings(prereqs) self._process_warnings(prereqs)
if self.tree.is_visible():
self.tree.grab_focus() self.tree.grab_focus()
else:
self.grab_focus()
def mark_finished(self) -> None: def mark_finished(self) -> None:
self.mod_count.set_label(preconnect.all_updated) self.mod_count.set_label(preconnect.all_updated)

View File

@ -4,7 +4,7 @@ description = "DayZ server browser and mod manager for Linux"
authors = [ authors = [
{name = "aclist"} {name = "aclist"}
] ]
version = "7.0.0b11" version = "7.0.0b12"
license = "GPL-3.0-or-later" license = "GPL-3.0-or-later"
license-files = ["LICENSE"] license-files = ["LICENSE"]
readme = "README.md" readme = "README.md"