diff --git a/dzgui/api/servers.py b/dzgui/api/servers.py index 728bbda..06e0b05 100644 --- a/dzgui/api/servers.py +++ b/dzgui/api/servers.py @@ -6,6 +6,7 @@ import re import requests import socket import subprocess +import threading import typing # noqa from dataclasses import dataclass @@ -56,7 +57,10 @@ def get_netmask() -> str: return netmask -def test_ip(suffix: int, port: int) -> dict | None: +# TODO: rename +def test_ip(suffix: int, port: int, event: threading.Event) -> dict | None: + if event.is_set(): + return netmask = get_netmask() hostname = f"{netmask}.{str(suffix)}" ping = ["ping", "-c1", "-i", "0.1", "-w", "1"] diff --git a/dzgui/controllers/mc.py b/dzgui/controllers/mc.py index 93fcdc0..b18e9e0 100644 --- a/dzgui/controllers/mc.py +++ b/dzgui/controllers/mc.py @@ -436,34 +436,45 @@ class Controller(GObject.GObject): parsed = Servers.parse_json(serv) self.push_data_success(parsed, FilterMode.INITIAL) - def dump_lan(self, port: int) -> None: + # TODO: strings + @call_on_thread("scanning LAN ports") + def dump_lan(self, port: int, early_abort: bool) -> None: serv = [] + event = threading.Event() with ThreadPoolExecutor() as executor: - futures = [executor.submit(Servers.test_ip, i, port) for i in range(1, 256)] + futures = [ + executor.submit(Servers.test_ip, i, port, event) for i in range(1, 256) + ] for future in as_completed(futures): try: - res = future.result(timeout=3) + res = future.result(timeout=0.5) + if res is not None and early_abort is True: + event.set() + self.cleanup_func = StoredFunc(self.cleanup_on_failure) + return if res is None: continue - serv.panned(res) - if len(serv) == 0: + serv.append(res) + except Exception as e: + logger.critical(e) self.cleanup_func = StoredFunc(self.cleanup_on_failure) - return + if len(serv) == 0: + self.cleanup_func = StoredFunc(self.cleanup_on_failure) + return parsed = Servers.parse_json(serv) self.push_data_success(parsed, FilterMode.INITIAL) - def dump_api(self) -> None: key = self.query_config(Preferences.STEAM) job = Servers.query_api params = Servers.params serv = [] - #i = 0 + # i = 0 with ThreadPoolExecutor() as executor: futures = [executor.submit(job, key, APPID_DAYZ, param) for param in params] for future in as_completed(futures): try: - #i += 1 + # i += 1 GLib.idle_add(lambda: self.wait_dialog.increment()) res = future.result(timeout=3) if res.status != 200 or not res.parsed: @@ -477,6 +488,7 @@ class Controller(GObject.GObject): return # NOTE: This step is allowed to fail, since this metadata is incidental + print("now checking exp servers") res = Servers.query_api(key, APPID_DAYZ_EXP, "") if res.status == 200 and res.parsed is True: j = res.json diff --git a/dzgui/views/components/connect_panel.py b/dzgui/views/components/connect_panel.py index daf641e..a5d0077 100644 --- a/dzgui/views/components/connect_panel.py +++ b/dzgui/views/components/connect_panel.py @@ -31,32 +31,52 @@ class LanPanel(Gtk.Frame): label = BoldLabel(lan_panel.heading) self.set_label_widget(label) - radio1 = Gtk.RadioButton.new_with_label(None, lan_panel.default_button) + self.radio1 = Gtk.RadioButton.new_with_label(None, lan_panel.default_button) radio2 = Gtk.RadioButton.new_with_label_from_widget( - radio1, lan_panel.custom_button + self.radio1, lan_panel.custom_button ) self.entry = PortEntry(controller) self.scan = Gtk.Button(label=lan_panel.scan_button) + self.scan.connect("clicked", self._on_scan_clicked) + # TODO: strings + self.early_abort = Gtk.CheckButton("Stop scanning on first hit") + self.early_abort.set_active(True) + self.early_abort.set_tooltip_text( + "Unless you have multiple DayZ servers on your LAN,\nleave this checked to get results faster" + ) self.entry.connect("string_validated", self._on_port_validated) self.emitter.connect( "request_lan_entry_focus", lambda _: self.entry.grab_focus() ) - radio1.connect("toggled", self._on_radio_toggled) + self.radio1.connect("toggled", self._on_radio_toggled) self.grid = Gtk.Grid(margin=10, vexpand=False, column_spacing=15, row_spacing=5) - self.grid.attach(radio1, 0, 0, COLS, ROWS) - self.grid.attach_next_to(radio2, radio1, Gtk.PositionType.RIGHT, COLS, ROWS) + self.grid.attach(self.radio1, 0, 0, COLS, ROWS) + self.grid.attach_next_to( + radio2, self.radio1, Gtk.PositionType.RIGHT, COLS, ROWS + ) self.grid.attach_next_to(self.entry, radio2, Gtk.PositionType.RIGHT, COLS, ROWS) self.grid.attach_next_to( self.scan, self.entry, Gtk.PositionType.RIGHT, COLS, ROWS ) + self.grid.attach_next_to( + self.early_abort, self.scan, Gtk.PositionType.RIGHT, COLS, ROWS + ) self.add(self.grid) self.entry.set_sensitive(False) + def _on_scan_clicked(self, button: Gtk.Button) -> None: + if self.radio1.get_active(): + port = 27016 + else: + port = self.entry.get_text() + abort = self.early_abort.get_active() + self.controller.dump_lan(int(port), abort) + def _on_radio_toggled(self, button: Gtk.RadioButton) -> None: state = button.get_active() self.entry.set_sensitive(not state)