from __future__ import annotations import json import socket import time import unittest from client_session import ClientSession from fake_player import FakePlayerClient, STATUS_CONNECTED from server_core import FalloutTogetherServer class CombatProtocolTests(unittest.TestCase): def setUp(self) -> None: self.sender_server, self.sender_peer = socket.socketpair() self.target_server, self.target_peer = socket.socketpair() self.server = FalloutTogetherServer() self.sender = ClientSession( self.sender_server, ("local", 1), 1, time.time(), ) self.target = ClientSession( self.target_server, ("local", 2), 2, time.time(), ) self.sender.mark_gameplay_active(1) self.target.mark_gameplay_active(1) self.server._init_rate_state(self.sender, time.monotonic()) self.server._init_rate_state(self.target, time.monotonic()) self.server._clients = { self.sender_server: self.sender, self.target_server: self.target, } def tearDown(self) -> None: for connection in ( self.sender_server, self.sender_peer, self.target_server, self.target_peer, ): connection.close() def test_combat_hit_is_authoritative_and_targeted(self) -> None: self.server._handle_line( self.sender, json.dumps( { "type": "combatHit", "playerId": 999, "sequence": 7, "targetPlayerId": 2, "damage": 12.5, "weaponFormId": "1f4a6", } ), ) routed = json.loads(self.target_peer.recv(4096)) self.assertEqual(routed["playerId"], 1) self.assertEqual(routed["targetPlayerId"], 2) self.assertEqual(routed["weaponFormId"], "0001F4A6") self.sender_peer.setblocking(False) with self.assertRaises(BlockingIOError): self.sender_peer.recv(1) def test_invalid_damage_is_not_routed(self) -> None: self.server._handle_line( self.sender, '{"type":"combatHit","sequence":8,"targetPlayerId":2,"damage":NaN}', ) self.target_peer.setblocking(False) with self.assertRaises(BlockingIOError): self.target_peer.recv(1) class FakePlayerCombatHelperTests(unittest.TestCase): def setUp(self) -> None: self.fake_socket, self.peer_socket = socket.socketpair() self.fake = FakePlayerClient(1, "test") self.fake._socket = self.fake_socket self.fake._status = STATUS_CONNECTED def tearDown(self) -> None: self.fake_socket.close() self.peer_socket.close() def test_ranged_fire_action_helper(self) -> None: self.fake.send_ranged_fire_action() packet = json.loads(self.peer_socket.recv(4096)) self.assertTrue(packet["weaponDrawn"]) self.assertEqual(packet["actionEvents"][0]["type"], 3) self.assertEqual(packet["actionEvents"][0]["eventName"], "fireSingle") def test_combat_hit_helper(self) -> None: self.fake.send_combat_hit(2, 9.5, 0x1F4A6) packet = json.loads(self.peer_socket.recv(4096)) self.assertEqual(packet["type"], "combatHit") self.assertEqual(packet["targetPlayerId"], 2) self.assertEqual(packet["weaponFormId"], "0001F4A6") if __name__ == "__main__": unittest.main()