Sync from GitHub main #1
@@ -18,6 +18,16 @@ def recv_packet(sock: socket.socket, timeout: float = 2.0) -> dict:
|
||||
return json.loads(line.decode("utf-8"))
|
||||
|
||||
|
||||
def recv_until(sock: socket.socket, packet_type: str, timeout: float = 2.0) -> dict:
|
||||
deadline = time.monotonic() + timeout
|
||||
while time.monotonic() < deadline:
|
||||
remaining = max(0.01, deadline - time.monotonic())
|
||||
packet = recv_packet(sock, timeout=remaining)
|
||||
if packet.get("type") == packet_type:
|
||||
return packet
|
||||
raise AssertionError(f"did not receive packet type {packet_type}")
|
||||
|
||||
|
||||
def send_packet(sock: socket.socket, packet: dict) -> None:
|
||||
sock.sendall(json.dumps(packet, separators=(",", ":")).encode() + b"\n")
|
||||
|
||||
@@ -29,10 +39,16 @@ def connect(server: FalloutTogetherServer) -> tuple[socket.socket, dict]:
|
||||
|
||||
def hello(sock: socket.socket) -> dict:
|
||||
send_packet(sock, {"type": "hello", "protocolVersion": PROTOCOL_VERSION})
|
||||
return recv_packet(sock)
|
||||
return recv_until(sock, "sessionReady")
|
||||
|
||||
|
||||
def transform(cell: str, x: float, y: float, world: str = "0000003C") -> dict:
|
||||
def transform(
|
||||
cell: str,
|
||||
x: float,
|
||||
y: float,
|
||||
world: str = "0000003C",
|
||||
movement_type: str = "normal",
|
||||
) -> dict:
|
||||
return {
|
||||
"type": "transform",
|
||||
"x": x,
|
||||
@@ -41,6 +57,7 @@ def transform(cell: str, x: float, y: float, world: str = "0000003C") -> dict:
|
||||
"angleZ": 0.0,
|
||||
"cellId": cell,
|
||||
"worldspaceId": world,
|
||||
"movementType": movement_type,
|
||||
}
|
||||
|
||||
|
||||
@@ -139,6 +156,89 @@ def test_transform_interest_filters_distant_peer():
|
||||
server.stop()
|
||||
|
||||
|
||||
def test_rejected_normal_teleport_is_corrected_and_never_relayed():
|
||||
server = start_server()
|
||||
a, a_welcome = connect(server)
|
||||
hello(a)
|
||||
b, _ = connect(server)
|
||||
hello(b)
|
||||
|
||||
send_packet(b, transform("00000010", 64.0, 0.0))
|
||||
recv_until(a, "transform")
|
||||
send_packet(a, transform("00000010", 0.0, 0.0))
|
||||
first_relay = recv_until(b, "transform")
|
||||
assert first_relay["x"] == 0.0
|
||||
|
||||
send_packet(a, transform("00000010", 100000.0, 0.0))
|
||||
correction = recv_until(a, "positionCorrection")
|
||||
assert correction["x"] == 0.0
|
||||
assert correction["cellId"] == "00000010"
|
||||
|
||||
b.settimeout(0.2)
|
||||
with pytest.raises(socket.timeout):
|
||||
b.recv(4096)
|
||||
|
||||
client = server._find_client_by_player_id(a_welcome["playerId"])
|
||||
assert client is not None
|
||||
assert client.last_transform is not None
|
||||
assert client.last_transform["x"] == 0.0
|
||||
stats = server.get_stats()
|
||||
assert stats["movementPacketsRejected"] >= 1
|
||||
assert stats["movementCorrectionsSent"] >= 1
|
||||
|
||||
a.close()
|
||||
b.close()
|
||||
server.stop()
|
||||
|
||||
|
||||
def test_scope_change_requires_explicit_transition():
|
||||
server = start_server()
|
||||
sock, welcome = connect(server)
|
||||
hello(sock)
|
||||
|
||||
send_packet(sock, transform("00000010", 0.0, 0.0))
|
||||
time.sleep(0.05)
|
||||
send_packet(sock, transform("00000011", 10.0, 0.0))
|
||||
correction = recv_until(sock, "positionCorrection")
|
||||
assert correction["cellId"] == "00000010"
|
||||
|
||||
client = server._find_client_by_player_id(welcome["playerId"])
|
||||
assert client is not None
|
||||
assert client.last_transform is not None
|
||||
assert client.last_transform["cellId"] == "00000010"
|
||||
|
||||
send_packet(sock, transform("00000011", 10.0, 0.0, movement_type="cell_change"))
|
||||
deadline = time.monotonic() + 1.0
|
||||
while time.monotonic() < deadline:
|
||||
if client.last_transform is not None and client.last_transform.get("cellId") == "00000011":
|
||||
break
|
||||
time.sleep(0.01)
|
||||
assert client.last_transform is not None
|
||||
assert client.last_transform["cellId"] == "00000011"
|
||||
|
||||
sock.close()
|
||||
server.stop()
|
||||
|
||||
|
||||
def test_zero_elapsed_burst_cannot_bypass_movement_envelope():
|
||||
server = start_server()
|
||||
sock, welcome = connect(server)
|
||||
hello(sock)
|
||||
|
||||
send_packet(sock, transform("00000010", 0.0, 0.0))
|
||||
send_packet(sock, transform("00000010", 50000.0, 0.0))
|
||||
correction = recv_until(sock, "positionCorrection")
|
||||
assert correction["x"] == 0.0
|
||||
|
||||
client = server._find_client_by_player_id(welcome["playerId"])
|
||||
assert client is not None
|
||||
assert client.last_transform is not None
|
||||
assert client.last_transform["x"] == 0.0
|
||||
|
||||
sock.close()
|
||||
server.stop()
|
||||
|
||||
|
||||
def test_oversized_unterminated_packet_closes_session():
|
||||
server = start_server()
|
||||
sock, _ = connect(server)
|
||||
|
||||
Reference in New Issue
Block a user