Prevent stale player action replay
This commit is contained in:
+32
-2
@@ -769,8 +769,16 @@ class FalloutTogetherServer:
|
|||||||
return False
|
return False
|
||||||
normalized["playerId"] = client.player_id
|
normalized["playerId"] = client.player_id
|
||||||
normalized["serverTime"] = time.time()
|
normalized["serverTime"] = time.time()
|
||||||
|
durable_fields = ("equippedItems", "appearance", "characterName")
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self._last_player_state_by_player_id[client.player_id] = dict(normalized)
|
if any(field in normalized for field in durable_fields):
|
||||||
|
cached = dict(self._last_player_state_by_player_id.get(client.player_id, {}))
|
||||||
|
cached.update({"type": "playerState", "playerId": client.player_id, "serverTime": normalized["serverTime"]})
|
||||||
|
for field in durable_fields:
|
||||||
|
if field in normalized:
|
||||||
|
cached[field] = normalized[field]
|
||||||
|
cached.pop("actionEvents", None)
|
||||||
|
self._last_player_state_by_player_id[client.player_id] = cached
|
||||||
self._stats["playerStatePacketsReceived"] += 1
|
self._stats["playerStatePacketsReceived"] += 1
|
||||||
self._broadcast_player_state(client, normalized)
|
self._broadcast_player_state(client, normalized)
|
||||||
return True
|
return True
|
||||||
@@ -1177,7 +1185,29 @@ class FalloutTogetherServer:
|
|||||||
self._disconnect_client(recipient)
|
self._disconnect_client(recipient)
|
||||||
|
|
||||||
def _broadcast_player_state(self, sender: ClientSession, packet: dict[str, Any]) -> None:
|
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:
|
||||||
|
recipients = [
|
||||||
|
client
|
||||||
|
for client in self._active_clients_locked()
|
||||||
|
if client.connection != sender.connection and client.protocol_version >= PROTOCOL_VERSION
|
||||||
|
]
|
||||||
|
successful = 0
|
||||||
|
failed: list[ClientSession] = []
|
||||||
|
durable_fields = ("equippedItems", "appearance", "characterName")
|
||||||
|
sender_scope_known = _scope_from_state(sender.last_transform) is not None
|
||||||
|
for recipient in recipients:
|
||||||
|
relay_packet = dict(packet)
|
||||||
|
if "actionEvents" in relay_packet:
|
||||||
|
recipient_scope_known = _scope_from_state(recipient.last_transform) is not None
|
||||||
|
if not sender_scope_known or not recipient_scope_known or not states_share_interest(sender.last_transform, recipient.last_transform):
|
||||||
|
relay_packet.pop("actionEvents", None)
|
||||||
|
if "actionEvents" not in relay_packet and not any(field in relay_packet for field in durable_fields):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
self._send_packet(recipient, relay_packet, broadcast=True)
|
||||||
|
successful += 1
|
||||||
|
except (OSError, ValueError):
|
||||||
|
failed.append(recipient)
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self._stats["playerStatePacketsBroadcast"] += successful
|
self._stats["playerStatePacketsBroadcast"] += successful
|
||||||
for client in failed:
|
for client in failed:
|
||||||
|
|||||||
Reference in New Issue
Block a user