change: simplify routes between query API

This commit is contained in:
aclist committed 2026-05-07 00:19:01 +09:00
1 parent a9863ff86b
commit 7523bffbd8
7 files changed
+94 -110

No files matched your search

+68 -88
View File
@@ -20,6 +20,7 @@ import a2s
import dayzquery import dayzquery
if TYPE_CHECKING: if TYPE_CHECKING:
from a2s import SourceInfo
from dayzquery import DayzMod from dayzquery import DayzMod
logger = logging.getLogger(APP_NAME) logger = logging.getLogger(APP_NAME)
@@ -40,18 +41,54 @@ params = [
] ]
#class BmAPIError(Exception):
# pass
#
#
#class BmIdError(Exception):
# pass
class InvalidIpError(Exception): class InvalidIpError(Exception):
pass pass
@dataclass(slots=True, frozen=True)
class Res:
status: int
parsed: bool
json: Union[dict[str, Any], None]
@dataclass(slots=True, frozen=True)
class Details:
data: Union[list, None]
description: str
success: bool
@dataclass(slots=True)
class Record:
"""
The gameport field is manipulated by the RowType.CONN_BY_IP method
"""
ip: str
gameport: int
qport: int
class A2SInfo:
def __init__(self, record: Record, info: a2s.SourceInfo | None) -> None:
self.record = record
self.info = info
def get_record(self) -> Record:
return self.record
def get_info(self) -> a2s.SourceInfo | None:
return self.info
def as_row(self) -> dict[str, Any] | None:
if self.info is None:
return None
ip = self.record.ip
qport = self.record.qport
return source_info_to_dict(ip, qport, self.info)
def get_netmask() -> str: def get_netmask() -> str:
hostname = os.uname()[1] hostname = os.uname()[1]
i = socket.gethostbyname(hostname) i = socket.gethostbyname(hostname)
@@ -191,11 +228,8 @@ def parse_json(json: list) -> list:
return rows return rows
def source_info_to_dict(ip: str, qport: int, info: "SourceInfo") -> dict[str, Any]:
def query_direct(ip: str, qport: int, timeout: float = 3.0) -> dict | None:
try: try:
info = a2s.info((ip, qport), timeout)
name = info.server_name name = info.server_name
mapname = info.map_name mapname = info.map_name
address = ip + ":" + str(qport) address = ip + ":" + str(qport)
@@ -221,61 +255,18 @@ def query_direct(ip: str, qport: int, timeout: float = 3.0) -> dict | None:
res["ping"] = ping res["ping"] = ping
return res return res
except Exception as e: except Exception as e:
# TODO: generalized function
logger.critical(f"{type(e).__name__}: {e} ({ip}:{qport})") logger.critical(f"{type(e).__name__}: {e} ({ip}:{qport})")
return None return None
@dataclass(slots=True, frozen=True) def query_direct(ip: str, qport: int, timeout: float = 3.0) -> dict[str, Any] | None:
class Res: try:
status: int info = a2s.info((ip, qport), timeout)
parsed: bool return source_info_to_dict(ip, qport, info)
json: Union[dict[str, Any], None] except Exception as e:
logger.critical(e)
return None
@dataclass(slots=True, frozen=True)
class Details:
data: Union[list, None]
description: str
success: bool
#@dataclass(slots=True, frozen=True)
#class Prereqs:
# password: bool
# gameport: int
# appid: Union[int, None]
# version: Union[str, None]
@dataclass(slots=True)
class Record:
"""
The gameport field is manipulated by the RowType.CONN_BY_IP method
"""
ip: str
gameport: int
qport: int
@dataclass(frozen=True, slots=True)
class PreReqs:
record: Record
source: a2s.SourceInfo
#def get_prereqs(ip: str, qport: int) -> Prereqs:
# try:
# info = a2s.info((ip, qport))
# except TimeoutError:
# return Prereqs(False, 0, None, None)
#
# gameport = getattr(info, "port", 0)
# is_password = getattr(info, "password_protected", False)
# appid = getattr(info, "game_id", None)
# version = getattr(info, "version", None)
#
# return Prereqs(is_password, gameport, appid, version)
def get_details(record: Record) -> Details: def get_details(record: Record) -> Details:
@@ -443,18 +434,6 @@ def get_rules(record: Record) -> list["DayzMod"]:
return [mod for mod in mods] return [mod for mod in mods]
def query_by_id(server_id: int, key: str, full: bool = False) -> dict[Any] | None:
"""
Used with numeric Battlemetrics IDs
"""
try:
resolved = map_id_to_record(key, server_id)
return query_direct(resolved.ip, resolved.qport, full)
except Exception as e:
logger.critical(e)
return None
def query_playercount(record: Record) -> tuple[int, int] | None: def query_playercount(record: Record) -> tuple[int, int] | None:
try: try:
res = query_direct(record.ip, record.qport) res = query_direct(record.ip, record.qport)
@@ -470,24 +449,25 @@ def query_playercount(record: Record) -> tuple[int, int] | None:
return None return None
def query_by_ip(addr: str, full: bool = False) -> dict[str, Any] | PreReqs | None: def query_by_ip(addr: str) -> A2SInfo:
record = short_ip_to_record(addr) record = short_ip_to_record(addr)
return query_by_record(record, full) return query_by_record(record)
def query_by_record(record: Record, full: bool = False) -> dict[str, Any] | PreReqs | None: def query_by_id(server_id: int, key: str) -> dict[Any] | None:
if full: """
try: Used with numeric Battlemetrics IDs
info = a2s.info((record.ip, record.qport), 3.0) """
return PreReqs(record, info) record = map_id_to_record(key, server_id)
except Exception as e: return query_by_record(record)
logger.critical(e)
return None
def query_by_record(record: Record) -> A2SInfo:
try: try:
return query_direct(record.ip, record.qport) info = a2s.info((record.ip, record.qport), 3.0)
return A2SInfo(record, info)
except Exception as e: except Exception as e:
logger.critical(e) logger.warning(e)
return None return A2SInfo(record, None)
def short_ip_to_record(addr: str) -> Optional[Record]: def short_ip_to_record(addr: str) -> Optional[Record]:
+2 -2
View File
@@ -37,7 +37,7 @@ logger = logging.getLogger(APP_NAME)
if TYPE_CHECKING: if TYPE_CHECKING:
from dayzquery import DayzMod from dayzquery import DayzMod
from dzgui.api.servers import PreReqs, Record from dzgui.api.servers import A2SInfo, Record
from dzgui.const.enum import ServerTab from dzgui.const.enum import ServerTab
from dzgui.managers.filter import FilterManager from dzgui.managers.filter import FilterManager
from dzgui.util.dist import Haversine from dzgui.util.dist import Haversine
@@ -458,7 +458,7 @@ class Controller(GObject.GObject):
def set_exit_event(self) -> None: def set_exit_event(self) -> None:
self.exit_event.set() self.exit_event.set()
def open_connection_assistant(self, res: "PreReqs", mods: list["DayzMod"]) -> None: def open_connection_assistant(self, res: "A2SInfo", mods: list["DayzMod"]) -> None:
self.open_page(NotebookPage.CONNECTION) self.open_page(NotebookPage.CONNECTION)
self.mediator.preconnect.populate(res, mods) self.mediator.preconnect.populate(res, mods)
+9 -9
View File
@@ -2,7 +2,7 @@ import logging
import shutil import shutil
from pathlib import Path from pathlib import Path
from typing import Union, TYPE_CHECKING from typing import TYPE_CHECKING
import dzgui.api.pefile as PeFile import dzgui.api.pefile as PeFile
import dzgui.api.servers as Servers import dzgui.api.servers as Servers
@@ -22,7 +22,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 PreReqs from dzgui.api.servers import A2SInfo
from dzgui.controllers.mc import Controller from dzgui.controllers.mc import Controller
logger = logging.getLogger(APP_NAME) logger = logging.getLogger(APP_NAME)
@@ -36,26 +36,26 @@ class ConnectionManager:
@call_on_thread(dialog.querying) @call_on_thread(dialog.querying)
def connect_by_id(self, _id: int, key: str) -> None: def connect_by_id(self, _id: int, key: str) -> None:
res = Servers.query_by_id(_id, key, full=True) res = Servers.query_by_id(_id, key)
self._prepare_connection(res) self._prepare_connection(res)
@call_on_thread(dialog.querying) @call_on_thread(dialog.querying)
def connect_by_ip(self, addr: str) -> None: def connect_by_ip(self, addr: str) -> None:
res = Servers.query_by_ip(addr, full=True) res = Servers.query_by_ip(addr)
self._prepare_connection(res) self._prepare_connection(res)
@call_on_thread(dialog.querying) @call_on_thread(dialog.querying)
def connect_by_record(self, record: Servers.Record) -> None: def connect_by_record(self, record: Servers.Record) -> None:
res = Servers.query_by_record(record, full=True) res = Servers.query_by_record(record)
self._prepare_connection(res) self._prepare_connection(res)
def _prepare_connection(self, res: Union["PreReqs", None]) -> None: def _prepare_connection(self, res: "A2SInfo") -> None:
failure_func = StoredFunc(self._server_timeout) failure_func = StoredFunc(self._server_timeout)
if res is None: if res.get_info() is None:
self.thread_man.set_cleanup_func(failure_func, destroy_first=True) self.thread_man.set_cleanup_func(failure_func, destroy_first=True)
return return
record = res.record record = res.get_record()
try: try:
remote_mods = self._query_modlist(record) remote_mods = self._query_modlist(record)
remote_mod_ids = [mod[1] for mod in remote_mods] remote_mod_ids = [mod[1] for mod in remote_mods]
@@ -79,7 +79,7 @@ class ConnectionManager:
# TODO: when downloading mods, create symlinks if missing # TODO: when downloading mods, create symlinks if missing
# TODO: get missing mod sizes, warn if not enough space # TODO: get missing mod sizes, warn if not enough space
info = res.source info = res.get_info()
try: try:
dayz_path = PeFile.get_pefile_path(steam_path, info.game_id) dayz_path = PeFile.get_pefile_path(steam_path, info.game_id)
# TODO: handle missing path; do not calculate size if appid is missing # TODO: handle missing path; do not calculate size if appid is missing
+8 -5
View File
@@ -23,6 +23,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.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
@@ -39,6 +40,7 @@ class NewPlayerCount:
queue: int queue: int
class ServerModelManager: class ServerModelManager:
def __init__(self, controller: "Controller", tv: "ServerTreeView") -> None: def __init__(self, controller: "Controller", tv: "ServerTreeView") -> None:
@@ -256,23 +258,24 @@ class ServerModelManager:
proxy_man.update_playercount(self.playercount) proxy_man.update_playercount(self.playercount)
def _parse_single_record( def _parse_single_record(
self, response: dict[Any, Any] | None, delete: bool = False self, response: "A2SInfo", delete: bool = False
) -> None: ) -> None:
self.preserve_on_fail = True self.preserve_on_fail = True
if response is None: row = response.as_row()
if row is None:
self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_on_failure)) self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_on_failure))
return return
# NOTE: expected to only contain one item # NOTE: expected to only contain one item
records = Servers.parse_json([response]) records = Servers.parse_json([row])
server = records[0] server = records[0]
if server is None: if server is None:
return return
proxy_man = self._get_proxy_man() proxy_man = self._get_proxy_man()
config_man = self.controller.get_config_man() config_man = self.controller.get_config_man()
fqip = Servers.response_to_fqip(response) fqip = Servers.response_to_fqip(row)
record = Servers.response_to_record(response) record = response.get_record() #Servers.response_to_record(row)
# TODO: less convoluted # TODO: less convoluted
if delete: if delete:
+1 -1
View File
@@ -57,7 +57,7 @@ copy_ip = "Copy IP to clipboard"
copy_log = "Copy record(s) to clipboard" copy_log = "Copy record(s) to clipboard"
add = "Add to Saved Servers" add = "Add to Saved Servers"
add_fav = "Set as favorite" add_fav = "Set as favorite"
remove = "Remove from my servers" remove = "Remove from Saved Servers"
remove_history = "Remove from history" remove_history = "Remove from history"
connect = "Connect" connect = "Connect"
+5 -4
View File
@@ -22,7 +22,7 @@ from gi.repository import Gdk, Gtk # type: ignore # noqa E402
if TYPE_CHECKING: if TYPE_CHECKING:
from dzgui.api.servers import PreReqs from dzgui.api.servers import A2SInfo
from dzgui.controllers.mc import Controller from dzgui.controllers.mc import Controller
@@ -142,7 +142,7 @@ class PreConnectionAssistant(Gtk.ScrolledWindow):
self.add(self.box) self.add(self.box)
# self.connect("key-press-event", self._on_keypress) self.connect("key-press-event", self._on_keypress)
self.connect("map", self._on_map) self.connect("map", self._on_map)
def _on_map(self, widget: Self) -> None: def _on_map(self, widget: Self) -> None:
@@ -162,13 +162,14 @@ class PreConnectionAssistant(Gtk.ScrolledWindow):
page = self.controller.get_prior_page() page = self.controller.get_prior_page()
self.controller.open_page(page) self.controller.open_page(page)
def populate(self, res: "PreReqs", mods: list["DayzMod"]) -> None: def populate(self, res: "A2SInfo", mods: list["DayzMod"]) -> None:
self.tree.populate(mods) self.tree.populate(mods)
total = len(mods) total = len(mods)
self._set_warnings() self._set_warnings()
name = res.source.server_name info = res.get_info()
name = info.server_name
self.title.set_text(name) self.title.set_text(name)
if total < 1: if total < 1:
self.tree_frame.set_visible(False) self.tree_frame.set_visible(False)
+1 -1
View File
@@ -144,7 +144,7 @@ class TreeView(CursorMixin, Gtk.TreeView): # type: ignore
path = pathlist[0] path = pathlist[0]
tree_iter = model.get_iter(path) tree_iter = model.get_iter(path)
value = model.get_value(tree_iter, index) value = model.get_value(tree_iter, index)
return str(value) return value
def get_name(self) -> str: def get_name(self) -> str:
name = self.get_value_at_index(0) name = self.get_value_at_index(0)