feat: refresh player count

This commit is contained in:
aclist 2026-04-18 18:15:27 +09:00
parent 06450ddadc
commit e1f54bd75d
6 changed files with 105 additions and 32 deletions

View File

@ -453,11 +453,24 @@ def query_by_id(addr: str, key: str) -> Optional[dict]:
return None
def query_playercount(record: Record) -> Optional[tuple[int]]:
try:
res = query_direct(record.ip, record.qport)
players = int(res["players"])
r = res["gametype"].split("lqs")
try:
queue = int(r[1].split(",")[0])
except IndexError:
queue = 0
return (players, queue)
except Exception as e:
logger.critical(e)
return None
def query_by_ip(addr: str) -> Optional[dict]:
record = short_ip_to_record(addr)
return query_by_record(record)
def query_by_record(record: Record) -> Optional[dict]:
try:
return query_direct(record.ip, record.qport)

View File

@ -604,6 +604,10 @@ class Controller(GObject.GObject):
saved_tree = self.get_servers().get_saved()
ServerModelManager(self, saved_tree).add_by_record(record)
def remove_by_record(self, record: "Record") -> None:
saved_tree = self.get_servers().get_saved()
ServerModelManager(self, saved_tree).remove_by_record(record)
def connect_by_str(self, addr: str) -> None:
if addr.isdigit():
config_man = self.get_config_man()
@ -652,3 +656,8 @@ class Controller(GObject.GObject):
tv = self.get_active_treeview()
record = tv.get_record_string()
self.notes_man.delete_note(record)
def refresh_players(self, record: "Record") -> None:
treeview = self.get_active_treeview()
model, treeiter = treeview.get_selection().get_selected()
ServerModelManager(self, treeview).update_playercount(treeiter, record)

View File

@ -48,6 +48,11 @@ class ConfigManager:
ips.append(record)
self.update_config(Preferences.IP_LIST, ips)
def remove_saved_server(self, record: str) -> None:
ips = self.lookup(Preferences.IP_LIST)
ips.remove(record)
self.update_config(Preferences.IP_LIST, ips)
# TODO: drop/reroute
def update_config(self, key: Preferences, value: str) -> None:
self.write_config(key, value)

View File

@ -66,7 +66,10 @@ class ContextMenuManager:
case ContextMenu.REFRESH_PLAYERS:
print("DEBUG: unimplemented")
# get record
record = self.treeview.get_record()
self.controller.refresh_players(record)
# TODO: just updates proxy model, this info is ephemeral
# call a2s on thread
pass
case ContextMenu.REMOVE_HISTORY:
@ -75,9 +78,8 @@ class ContextMenuManager:
# see dq.py
pass
case ContextMenu.REMOVE_SERVER:
print("DEBUG: unimplemented")
# reverse of ADD_SERVER
pass
record = self.treeview.get_record()
self.controller.remove_by_record(record)
case ContextMenu.SHOW_DETAILS:
record = self.treeview.get_record()
self.controller.get_details(record)

View File

@ -10,7 +10,12 @@ from dzgui.util import strings
if TYPE_CHECKING:
from dzgui.managers.filter_man import FilterManager
from dzgui.model.model_factory import FastInsertListStore
from dzgui.model.servers import NewPlayerCount
import gi
gi.require_version("Gtk", "3.0")
from gi.repository.Gtk import TreeIter
class ProxyModelManager:
"""
@ -35,9 +40,27 @@ class ProxyModelManager:
self.filtered: list = None
self.success = True
def has_control_model(self) -> bool:
pass
def append_row(self, row: list) -> None:
self.proxy_model.append(row)
def append_row_to_control(self, row: list) -> None:
self.control_model.append(row)
self.filter(FilterMode.INITIAL, skip_cache=True)
def remove_row_from_control(self, row: list) -> None:
for record in self.control_model:
if row[7] == record[7] and row[8] == record[8]:
self.control_model.remove(record)
self.filter(FilterMode.INITIAL, skip_cache=True)
def update_playercount(self, playercount: "NewPlayerCount") -> None:
treeiter = playercount.treeiter
self.proxy_model[treeiter][4] = playercount.players
self.proxy_model[treeiter][6] = playercount.queue
def clear_proxy_model(self) -> None:
self.proxy_model.clear()

View File

@ -1,7 +1,9 @@
import logging
import threading
from concurrent.futures import as_completed
from concurrent.futures import ThreadPoolExecutor
from dataclasses import dataclass
from typing import TYPE_CHECKING
import dzgui.api.servers as Servers
@ -29,9 +31,15 @@ if TYPE_CHECKING:
logger = logging.getLogger(__name__)
# TODO: failure: spawns error dialog
# TODO: failure should spawn error dialog
# TODO: non failure with empty model: updates statusbar with help text
@dataclass
class NewPlayerCount:
treeiter: Gtk.TreeIter
players: int
queue: int
class ServerModelManager:
def __init__(self, controller: "Controller", tv: Gtk.TreeView) -> None:
@ -207,50 +215,65 @@ class ServerModelManager:
# FIXME: use "adding server" string
@call_on_thread(dialog.querying)
def add_by_record(self, record: Servers.Record) -> None:
"""
Record as shown in server browser may resolve to a different IP
"""
res = Servers.query_by_record(record)
self._parse_single_record(res)
@call_on_thread(dialog.querying)
def remove_by_record(self, record: Servers.Record) -> None:
res = Servers.query_by_record(record)
self._parse_single_record(res, delete=True)
def add_by_str(self, addr: str) -> None:
if addr.isdigit():
self.add_by_id(addr)
else:
self.add_by_ip(addr)
def _parse_single_record(self, response: dict) -> None:
@call_on_thread(dialog.querying)
def update_playercount(self, treeiter: Gtk.TreeIter, record: Servers.Record) -> None:
proxy_man = self._get_proxy_man()
res = Servers.query_playercount(record)
if res is None:
return
players, queue = res
self.playercount = NewPlayerCount(treeiter, players, queue)
self.thread_man.set_cleanup_func(StoredFunc(self._push_playercount))
def _push_playercount(self) -> None:
proxy_man = self._get_proxy_man()
proxy_man.update_playercount(self.playercount)
def _parse_single_record(self, response: dict, delete: bool = False) -> None:
if response is None:
self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_on_failure))
return
# NOTE: expected to only contain one item
records = Servers.parse_json([response])
record = records[0]
proxy_man = self._get_proxy_man()
raw_model = proxy_man.get_control()
fqip = Servers.response_to_fq_ip(response)
config_man = self.controller.get_config_man()
config_man.add_saved_server(fqip)
# NOTE: abort early if Saved Servers tab was not loaded yet
if raw_model is None:
return
if delete:
# NOTE: abort early if Saved Servers tab was not loaded yet
config_man.remove_saved_server(fqip)
if proxy_man.has_control_model() is False:
return
proxy_man.remove_row_from_control(record)
else:
config_man.add_saved_server(fqip)
if proxy_man.has_control_model() is False:
return
proxy_man.append_row_to_control(record)
# NOTE: expected to only contain one item
raw_model.append(record)
# TODO: if all filters are already applied, strange behavior may occur
# -> need to insert and reupdate tree per current filters
# for example, non-empty will only show up in empty because it is not cached
# FIXME: new results are not being shown when tabbing over to Saved Servers
proxy_man.filter(FilterMode.INITIAL, skip_cache=True)
filter_man = self.tv.get_filter_man()
old_maps = filter_man.get_unique_maps()
cur_map = record[1]
if cur_map not in old_maps:
old_maps.append(cur_map)
self._set_new_maps(old_maps)
control_model = proxy_man.get_control()
self._sort_unique_maps(control_model)
self.thread_man.set_cleanup_func(StoredFunc(self._cleanup_single_ip))
def _dump_history(self) -> None:
@ -295,8 +318,8 @@ class ServerModelManager:
# TODO: animate saved servers tab if we are on other tab
self.emitter.emit("servers_loaded", self.enum)
# TODO: consolidate methods and handle multi/single map addition
filter_man = self.tv.get_filter_man()
# NOTE: maps are set outside of thread because it triggers map changed signals
maps = self._get_new_maps()
filter_man.set_unique_maps(maps)
@ -314,8 +337,6 @@ class ServerModelManager:
self.tv.set_model(proxy)
# TODO: make sure control model len is N + 1
# inserting a row serializes file on disk, updates control model for that tab, and updates model
# NOTE: when inserting new rows, the entire control model is wiped and rebuilt, then proxy model is swapped in
# TODO: signals or other approach to deferring map
# model insertion after thread closes
# cf. servers_loaded signal