|
7 | 7 | import logging |
8 | 8 | import os |
9 | 9 | import time |
| 10 | +import netifaces |
| 11 | + |
10 | 12 | from subprocess import Popen |
11 | 13 | from typing import Any |
12 | 14 |
|
@@ -44,15 +46,18 @@ def __init__( |
44 | 46 | suppress_logs: bool = False, |
45 | 47 | use_conventional_port: bool = False, |
46 | 48 | automate_referee: bool = False, |
| 49 | + parallelized: bool = False, |
47 | 50 | ) -> None: |
48 | 51 | """Run Gamecontroller |
49 | 52 |
|
50 | 53 | :param suppress_logs: True if logs should be suppressed |
51 | 54 | :param use_conventional_port: True when using static referee port. False for dynamic port assignments. |
52 | | - :param automate_referee: True if referee commands should be automated |
| 55 | + :param automate_referee: True if referee commands should be automated. |
| 56 | + :param parallelized: True when this is one of many Gamecontrollers running at once. |
53 | 57 | """ |
54 | 58 | self.suppress_logs = suppress_logs |
55 | 59 | self.automate_referee = automate_referee |
| 60 | + self.parallelized = parallelized |
56 | 61 |
|
57 | 62 | self.use_conventional_port = use_conventional_port |
58 | 63 | self.referee_port = None |
@@ -109,17 +114,20 @@ def __enter__(self) -> Gamecontroller: |
109 | 114 |
|
110 | 115 | command += ["-publishAddress", f"{self.REFEREE_IP}:{self.referee_port}"] |
111 | 116 | command += ["-ciAddress", f"localhost:{self.ci_port}"] |
112 | | - command += [ |
113 | | - "-address", |
114 | | - "localhost:0", |
115 | | - "-autorefAddress", |
116 | | - "localhost:0", |
117 | | - "-remoteControlAddress", |
118 | | - "localhost:0", |
119 | | - "-teamAddress", |
120 | | - "localhost:0", |
121 | | - "-backendOnly", |
122 | | - ] |
| 117 | + if self.parallelized: |
| 118 | + # One of many GCs running at once: no web UI and all-dynamic ports so |
| 119 | + # instances don't collide on the fixed UI / autoref ports. |
| 120 | + command += [ |
| 121 | + "-address", |
| 122 | + "localhost:0", |
| 123 | + "-autorefAddress", |
| 124 | + "localhost:0", |
| 125 | + "-remoteControlAddress", |
| 126 | + "localhost:0", |
| 127 | + "-teamAddress", |
| 128 | + "localhost:0", |
| 129 | + "-backendOnly", |
| 130 | + ] |
123 | 131 |
|
124 | 132 | if self.suppress_logs: |
125 | 133 | with open(os.devnull, "w") as fp: |
@@ -245,15 +253,10 @@ def __send_referee_command(data: Referee) -> None: |
245 | 253 | if autoref_proto_unix_io is not None: |
246 | 254 | autoref_proto_unix_io.send_proto(Referee, data) |
247 | 255 |
|
248 | | - if is_current_platform_macos(): |
249 | | - loopback_iface = "en0" |
250 | | - else: |
251 | | - loopback_iface = "lo" |
252 | | - |
253 | 256 | self.receive_referee_command = tbots_cpp.SSLRefereeProtoListener( |
254 | 257 | Gamecontroller.REFEREE_IP, |
255 | 258 | self.referee_port, |
256 | | - loopback_iface, |
| 259 | + self.__get_referee_multicast_interface(), |
257 | 260 | __send_referee_command, |
258 | 261 | True, |
259 | 262 | ) |
@@ -630,3 +633,19 @@ def __update_robot_count( |
630 | 633 | robot_states[removed_robot_ids.get_nowait()].CopyFrom(place_state) |
631 | 634 | except queue.Empty: |
632 | 635 | return |
| 636 | + |
| 637 | + @staticmethod |
| 638 | + def __get_referee_multicast_interface() -> str: |
| 639 | + """Determine the network interface to join the referee multicast group on. |
| 640 | +
|
| 641 | + :return: the name of the interface to receive referee multicast on |
| 642 | + """ |
| 643 | + if not is_current_platform_macos(): |
| 644 | + return "lo" |
| 645 | + |
| 646 | + default = netifaces.gateways().get("default", {}).get(netifaces.AF_INET) |
| 647 | + if default: |
| 648 | + gateway_ip, interface_name = default |
| 649 | + return gateway_ip |
| 650 | + |
| 651 | + raise RuntimeError("Could not determine the default network interface on macOS") |
0 commit comments