Persist GNS transport configuration

This commit is contained in:
Nomads_Reach
2026-08-16 00:49:10 -04:00
parent 2f4ddaaa26
commit d24e8c58e3
+56
View File
@@ -20,6 +20,25 @@ MAX_PLAYERS_HARD_LIMIT = 256
DEFAULT_ADMIN_PORT = 7779 DEFAULT_ADMIN_PORT = 7779
def _parse_bool(value: Any, *, field_name: str, default: bool = False) -> bool:
if value is None:
return default
if isinstance(value, bool):
return value
if isinstance(value, int) and not isinstance(value, bool) and value in (0, 1):
return bool(value)
if isinstance(value, str):
text = value.strip().lower()
if text in {"1", "true", "yes", "on"}:
return True
if text in {"0", "false", "no", "off", ""}:
return False
raise ValueError(
f"{field_name} must be a boolean or one of true/false, yes/no, on/off, 1/0"
)
@dataclass @dataclass
class Config: class Config:
"""Server configuration model.""" """Server configuration model."""
@@ -30,6 +49,8 @@ class Config:
max_players: int = 16 max_players: int = 16
log_verbosity: str = "info" log_verbosity: str = "info"
admin_port: int = DEFAULT_ADMIN_PORT admin_port: int = DEFAULT_ADMIN_PORT
enable_gns_transport: bool = False
gns_bridge_path: str | None = None
def to_dict(self) -> dict[str, Any]: def to_dict(self) -> dict[str, Any]:
"""Convert config to dictionary.""" """Convert config to dictionary."""
@@ -41,6 +62,8 @@ class Config:
"max_players": self.max_players, "max_players": self.max_players,
"log_verbosity": self.log_verbosity, "log_verbosity": self.log_verbosity,
"admin_port": self.admin_port, "admin_port": self.admin_port,
"enable_gns_transport": self.enable_gns_transport,
"gns_bridge_path": self.gns_bridge_path,
} }
@classmethod @classmethod
@@ -53,6 +76,20 @@ class Config:
except (TypeError, ValueError) as error: except (TypeError, ValueError) as error:
raise ValueError(f"Invalid numeric config field: {error}") from error raise ValueError(f"Invalid numeric config field: {error}") from error
enable_gns_transport = _parse_bool(
data.get("enable_gns_transport"),
field_name="enable_gns_transport",
default=False,
)
raw_bridge_path = data.get("gns_bridge_path")
if raw_bridge_path is None:
gns_bridge_path = None
elif isinstance(raw_bridge_path, str):
normalized_bridge_path = raw_bridge_path.strip()
gns_bridge_path = normalized_bridge_path or None
else:
raise ValueError("gns_bridge_path must be a string or null")
return cls( return cls(
host=str(data.get("host", "0.0.0.0")), host=str(data.get("host", "0.0.0.0")),
port=port, port=port,
@@ -61,6 +98,8 @@ class Config:
max_players=max_players, max_players=max_players,
log_verbosity=str(data.get("log_verbosity", "info")), log_verbosity=str(data.get("log_verbosity", "info")),
admin_port=admin_port, admin_port=admin_port,
enable_gns_transport=enable_gns_transport,
gns_bridge_path=gns_bridge_path,
) )
@@ -145,6 +184,18 @@ def _is_valid_bind_host(host: str) -> bool:
return False return False
def _is_valid_gns_bind_host(host: str) -> bool:
"""Return True for the strict IPv4 bind form used by the native GNS bridge."""
text = host.strip()
if not text:
return False
try:
socket.inet_pton(socket.AF_INET, text)
return True
except OSError:
return False
def validate_config(config: Config) -> tuple[bool, list[str]]: def validate_config(config: Config) -> tuple[bool, list[str]]:
""" """
Validate configuration values. Validate configuration values.
@@ -163,6 +214,11 @@ def validate_config(config: Config) -> tuple[bool, list[str]]:
errors.append( errors.append(
f"host '{config.host}' is not a valid IPv4 address or resolvable hostname" f"host '{config.host}' is not a valid IPv4 address or resolvable hostname"
) )
elif config.enable_gns_transport and not _is_valid_gns_bind_host(str(config.host)):
errors.append(
"enable_gns_transport requires host to be an explicit IPv4 bind address "
"such as 0.0.0.0 or 127.0.0.1"
)
if config.port < 1 or config.port > 65535: if config.port < 1 or config.port > 65535:
errors.append(f"port must be 1-65535, got {config.port}") errors.append(f"port must be 1-65535, got {config.port}")