Sync from GitHub main #1

Open
nomad wants to merge 145 commits from sync/from-github into main
Showing only changes of commit 8c352260a3 - Show all commits
+56
View File
@@ -14,6 +14,7 @@ from ban_store import BanStore
from client_session import ClientSession
from lan_discovery import DISCOVERY_PORT, LanDiscoveryResponder
from npc_authority import NpcAuthorityManager, ScopeKey, scope_from_transform
from player_state import normalize_player_state_packet
from world_state_presets import normalize_fw_console_arg, relay_weather_form_id
HOST = "0.0.0.0"
@@ -222,6 +223,7 @@ class FalloutTogetherServer:
self._server_world_state: dict[str, str] = {}
self._last_npc_state: dict[str, Any] | None = None
self._last_npc_state_by_scope: dict[ScopeKey, dict[str, Any]] = {}
self._last_player_state_by_player_id: dict[int, dict[str, Any]] = {}
self._npc_authority = NpcAuthorityManager()
self._connect_attempts: dict[str, deque[float]] = {}
@@ -237,6 +239,8 @@ class FalloutTogetherServer:
"transformPacketsReceived": 0,
"transformPacketsBroadcast": 0,
"transformPacketsInterestFiltered": 0,
"playerStatePacketsReceived": 0,
"playerStatePacketsBroadcast": 0,
"movementPacketsRejected": 0,
"movementCorrectionsSent": 0,
"worldStatePacketsReceived": 0,
@@ -443,6 +447,7 @@ class FalloutTogetherServer:
self._world_state_host_player_id = None
self._last_npc_state = None
self._last_npc_state_by_scope.clear()
self._last_player_state_by_player_id.clear()
self._npc_authority.clear()
self._connect_attempts.clear()
@@ -568,6 +573,7 @@ class FalloutTogetherServer:
},
)
self._send_existing_transforms_to_client(client)
self._send_existing_player_states_to_client(client)
self._send_existing_npc_state_to_client(client)
if became_host:
self._broadcast_world_state_host_assignment(client.player_id)
@@ -612,6 +618,7 @@ class FalloutTogetherServer:
"rate-limit-v1",
"movement-correction-v1",
"npc-authority-epoch-v1",
"player-state-v1",
],
},
)
@@ -728,6 +735,9 @@ class FalloutTogetherServer:
if normalized is None:
self._reject_packet(client, "Malformed transform")
return False
if client.protocol_version >= PROTOCOL_VERSION:
for reliable_field in ("equippedItems", "appearance", "actionEvents", "characterName"):
normalized.pop(reliable_field, None)
accepted_monotonic = time.monotonic()
movement_valid, movement_reason = self._validate_transform_movement(client, normalized, accepted_monotonic)
if not movement_valid:
@@ -749,6 +759,22 @@ class FalloutTogetherServer:
self._send_npc_authority_for_client(client, current_scope)
return True
if packet_type == "playerState":
if client.protocol_version < PROTOCOL_VERSION:
self._reject_packet(client, "playerState requires Protocol V2", warning=False)
return False
normalized = normalize_player_state_packet(packet, _normalize_action_events)
if normalized is None:
self._reject_packet(client, "Malformed playerState")
return False
normalized["playerId"] = client.player_id
normalized["serverTime"] = time.time()
with self._lock:
self._last_player_state_by_player_id[client.player_id] = dict(normalized)
self._stats["playerStatePacketsReceived"] += 1
self._broadcast_player_state(client, normalized)
return True
if packet_type == "worldState":
if not self._is_world_host(client):
self._reject_packet(client, "worldState from non-authority client", warning=False)
@@ -1108,6 +1134,27 @@ class FalloutTogetherServer:
with self._lock:
self._stats["transformPacketsBroadcast"] += successful
def _send_existing_player_states_to_client(self, new_client: ClientSession) -> None:
if new_client.protocol_version < PROTOCOL_VERSION:
return
with self._lock:
snapshots = [
dict(packet)
for player_id, packet in self._last_player_state_by_player_id.items()
if player_id != new_client.player_id
]
successful = 0
for packet in snapshots:
packet["serverTime"] = time.time()
try:
self._send_packet(new_client, packet, broadcast=True)
successful += 1
except (OSError, ValueError):
self._disconnect_client(new_client)
break
with self._lock:
self._stats["playerStatePacketsBroadcast"] += successful
def _broadcast_transform(self, sender: ClientSession, packet: dict[str, Any]) -> None:
with self._lock:
recipients = [client for client in self._active_clients_locked() if client.connection != sender.connection]
@@ -1129,6 +1176,13 @@ class FalloutTogetherServer:
for recipient in failed:
self._disconnect_client(recipient)
def _broadcast_player_state(self, sender: ClientSession, packet: dict[str, Any]) -> None:
successful, failed = self._broadcast_to_active(packet, exclude=sender, v2_only=True)
with self._lock:
self._stats["playerStatePacketsBroadcast"] += successful
for client in failed:
self._disconnect_client(client)
def _broadcast_world_state(self, sender: ClientSession, packet: dict[str, Any]) -> None:
successful, failed = self._broadcast_to_active(packet, exclude=sender)
with self._lock:
@@ -1280,6 +1334,8 @@ class FalloutTogetherServer:
def _remove_client(self, client: ClientSession) -> bool:
with self._lock:
removed = self._clients.pop(client.connection, None) is not None
if removed:
self._last_player_state_by_player_id.pop(client.player_id, None)
if removed and client.gameplay_active:
self._stats["clientsDisconnected"] += 1
return removed