async def test_patches_for_pickup(connector: EchoesRemoteConnector, version: EchoesDolVersion, mocker, generic_item_category): # Setup mock_item_patch: MagicMock = mocker.patch( "randovania.patching.prime.all_prime_dol_patches.adjust_item_amount_and_capacity_patch" ) db = connector.game.resource_database pickup = PickupEntry("Pickup", 0, generic_item_category, generic_item_category, progression=tuple(), extra_resources=( (db.energy_tank, db.energy_tank.max_capacity), (db.item_percentage, 1), )) inventory = { db.multiworld_magic_item: InventoryItem(0, 0), db.energy_tank: InventoryItem(1, 1), } # Run patches, message = await connector._patches_for_pickup( "Someone", pickup, inventory) # Assert mock_item_patch.assert_called_once_with( version.powerup_functions, RandovaniaGame.METROID_PRIME_ECHOES, db.energy_tank.extra["item_id"], db.energy_tank.max_capacity) assert patches == [mock_item_patch.return_value] assert message == "Received Pickup from Someone."
async def test_patches_for_pickup(backend, mocker): # Setup mock_item_patch: MagicMock = mocker.patch( "randovania.games.prime.all_prime_dol_patches.adjust_item_amount_and_capacity_patch") backend.patches = dol_patcher.ALL_VERSIONS_PATCHES[0] db = backend.game.resource_database pickup = PickupEntry("Pickup", 0, ItemCategory.MISSILE, ItemCategory.MISSILE, ( ConditionalResources(None, None, ((db.energy_tank, db.energy_tank.max_capacity), (db.item_percentage, 1)), ), )) backend._inventory = { db.multiworld_magic_item: InventoryItem(0, 0), db.energy_tank: InventoryItem(1, 1), } # Run patches, message = await backend._patches_for_pickup("Someone", pickup) # Assert mock_item_patch.assert_called_once_with(backend.patches.powerup_functions, db.energy_tank.index, db.energy_tank.max_capacity) assert patches == [mock_item_patch.return_value] assert message == "Received Pickup from Someone."
def _update_tracker_from_hook(self, inventory: Dict[ItemResourceInfo, InventoryItem]): for element in self._tracker_elements: if len(element.labels) > 1: satisfied = False for i, resource in reversed(list(enumerate( element.resources))): current = inventory.get(resource, InventoryItem(0, 0)) fields = { "amount": current.amount, "capacity": current.capacity, "max_capacity": resource.max_capacity } if satisfied: element.labels[i].setVisible(False) elif fields[element.field_to_check. value] >= element.minimum_to_check: # This tier is satisfied satisfied = True element.labels[i].setVisible(True) element.labels[i].set_checked(True) else: element.labels[i].setVisible(False) if not satisfied: element.labels[0].setVisible(True) element.labels[0].set_checked(False) else: label = element.labels[0] amount = 0 capacity = 0 max_capacity = 0 for resource in element.resources: current = inventory.get(resource, InventoryItem(0, 0)) amount += current.amount capacity += current.capacity max_capacity += resource.max_capacity if isinstance(label, ClickableLabel): fields = { "amount": amount, "capacity": capacity, "max_capacity": max_capacity } value_target = element.minimum_to_check value = fields[element.field_to_check.value] label.set_checked(max_capacity == 0 or value >= value_target) else: label.setText( element.text_template.format( amount=amount, capacity=capacity, max_capacity=max_capacity, ))
async def test_update_inventory_label(backend, echoes_resource_database): backend._inventory = { echoes_resource_database.energy_tank: InventoryItem(4, 4), echoes_resource_database.multiworld_magic_item: InventoryItem(0, 2), } backend._update_inventory_label() assert "Energy Tank x 4/4" in backend.inventory_label.text() assert "Multiworld Magic Identifier x 0/2" in backend.inventory_label.text( )
async def test_patches_for_pickup(connector: Prime1RemoteConnector, mocker, artifact: bool, generic_item_category): # Setup mock_item_patch: MagicMock = mocker.patch( "randovania.patching.prime.all_prime_dol_patches.adjust_item_amount_and_capacity_patch" ) mock_increment_capacity: MagicMock = mocker.patch( "randovania.patching.prime.all_prime_dol_patches.increment_item_capacity_patch" ) mock_artifact_layer: MagicMock = mocker.patch( "randovania.games.prime1.patcher.prime1_dol_patches.set_artifact_layer_active_patch" ) db = connector.game.resource_database if artifact: extra = (db.get_item("Strength"), 1) else: extra = (db.energy_tank, db.energy_tank.max_capacity) pickup = PickupEntry("Pickup", 0, generic_item_category, generic_item_category, progression=tuple(), extra_resources=(extra, )) inventory = { db.multiworld_magic_item: InventoryItem(0, 0), db.energy_tank: InventoryItem(1, 1), } # Run patches, message = await connector._patches_for_pickup( "Someone", pickup, inventory) # Assert expected_patches = [] if artifact: mock_artifact_layer.assert_called_once_with(connector.version, 2, True) expected_patches.append(mock_artifact_layer.return_value) used_patch, unused_patch = mock_increment_capacity, mock_item_patch else: mock_artifact_layer.assert_not_called() used_patch, unused_patch = mock_item_patch, mock_increment_capacity expected_patches.insert(0, used_patch.return_value) used_patch.assert_called_once_with(connector.version.powerup_functions, RandovaniaGame.METROID_PRIME, extra[0].extra["item_id"], extra[1]) unused_patch.assert_not_called() assert patches == expected_patches assert message == "Received Pickup from Someone."
async def test_get_inventory_invalid_capacity(backend): # Setup custom_inventory = {5: InventoryItem(0, 50)} backend.patches = dol_patcher.ALL_VERSIONS_PATCHES[0] backend._perform_memory_operations.side_effect = lambda ops: { op: struct.pack(">II", *custom_inventory.get(item.index, InventoryItem(item.max_capacity, item.max_capacity))) for op, item in zip(ops, backend.game.resource_database.item) } # Run msg = "Received InventoryItem(amount=0, capacity=50) for Darkburst, which is an invalid state." with pytest.raises(MemoryOperationException, match=re.escape(msg)): await backend._get_inventory()
async def known_collected_locations( self, executor: MemoryOperationExecutor, ) -> Tuple[Set[PickupIndex], List[DolRemotePatch]]: """Fetches pickup indices that have been collected. The list may return less than all collected locations, depending on implementation details. This function also returns a list of remote patches that must be performed via `execute_remote_patches`. """ multiworld_magic_item = self.game.resource_database.multiworld_magic_item if multiworld_magic_item is None: return set(), [] memory_ops = await self._memory_op_for_items(executor, [multiworld_magic_item]) op_result = await executor.perform_single_memory_operation(*memory_ops) magic_inv = InventoryItem(*struct.unpack(">II", op_result)) if magic_inv.amount > 0: self.logger.info( f"magic item was at {magic_inv.amount}/{magic_inv.capacity}") locations = {PickupIndex(magic_inv.amount - 1)} patches = [ DolRemotePatch([], all_prime_dol_patches. adjust_item_amount_and_capacity_patch( self.version.powerup_functions, self.game.game, multiworld_magic_item.extra["item_id"], -magic_inv.amount, )) ] return locations, patches else: return set(), []
def _update_tracker_from_hook(self, inventory: Dict[ItemResourceInfo, InventoryItem]): for item, label in self._item_to_label.items(): current = inventory.get(item, InventoryItem(0, 0)) label.set_checked(current.capacity > 0) energy_tank = inventory.get(self._energy_tank_item, InventoryItem(0, 0)) self._energy_tank_label.setText("x {}/{}".format( energy_tank.capacity, self._energy_tank_item.max_capacity)) for label, keys in self._labels_for_keys: num_keys = sum( inventory.get(key, InventoryItem(0, 0)).capacity for key in keys) label.setText("x {}/{}".format(num_keys, len(keys)))
async def test_update_magic_item_give_pickup(backend, mocker, on_cooldown): # Setup mock_item_patch: MagicMock = mocker.patch( "randovania.games.prime.all_prime_dol_patches.increment_item_capacity_patch") backend.patches = dol_patcher.ALL_VERSIONS_PATCHES[0] backend._emit_location_collected = AsyncMock() backend._execute_remote_patches = AsyncMock() pickup_patches = MagicMock() backend._patches_for_pickup = AsyncMock(return_value=([pickup_patches, pickup_patches], "The Message")) backend._inventory = {backend.game.resource_database.multiworld_magic_item: InventoryItem(0, 0)} backend._permanent_pickups = [ ("A", MagicMock()), ("B", MagicMock()), ] backend.message_cooldown = 1 if on_cooldown else 0 # Run await backend._update_magic_item() # Assert backend._emit_location_collected.assert_not_awaited() if on_cooldown: backend._execute_remote_patches.assert_not_awaited() else: mock_item_patch.assert_called_once_with(backend.patches.powerup_functions, backend.game.resource_database.multiworld_magic_item.index) backend._patches_for_pickup.assert_awaited_once_with(*backend._permanent_pickups[0]) backend._execute_remote_patches.assert_awaited_once_with( [pickup_patches, pickup_patches, mock_item_patch.return_value], "The Message")
async def test_receive_required_missile_launcher( connector: EchoesRemoteConnector, echoes_item_database, echoes_resource_database): pickup = pickup_creator.create_major_item( echoes_item_database.major_items["Missile Launcher"], MajorItemState(included_ammo=(5, )), True, echoes_resource_database, echoes_item_database.ammo["Missile Expansion"], True, ) executor = AsyncMock() permanent_pickups = (("Received Missile Launcher from Someone Else", pickup), ) inventory = { echoes_resource_database.multiworld_magic_item: InventoryItem(0, 0), } # Run patches, has_message = await connector.find_missing_remote_pickups( executor, inventory, permanent_pickups, False, ) assert has_message assert len(patches) == 5 await connector.execute_remote_patches(executor, patches)
async def test_find_missing_remote_pickups_give_pickup( connector: EchoesRemoteConnector, version: EchoesDolVersion, mocker, in_cooldown): # Setup mock_item_patch: MagicMock = mocker.patch( "randovania.patching.prime.all_prime_dol_patches.increment_item_capacity_patch" ) mock_call_display_hud_patch: MagicMock = mocker.patch( "randovania.patching.prime.all_prime_dol_patches.call_display_hud_patch" ) pickup_patches = MagicMock() connector._write_string_to_game_buffer = MagicMock() connector._patches_for_pickup = AsyncMock( return_value=([pickup_patches, pickup_patches], "The Message")) executor = AsyncMock() inventory = { connector.game.resource_database.multiworld_magic_item: InventoryItem(0, 0) } permanent_pickups = [ ("A", MagicMock()), ("B", MagicMock()), ] # Run patches, has_message = await connector.find_missing_remote_pickups( executor, inventory, permanent_pickups, in_cooldown) # Assert if in_cooldown: assert patches == [] assert not has_message mock_item_patch.assert_not_called() connector._patches_for_pickup.assert_not_called() connector._write_string_to_game_buffer.assert_not_called() mock_call_display_hud_patch.assert_not_called() return mock_item_patch.assert_called_once_with( version.powerup_functions, RandovaniaGame.METROID_PRIME_ECHOES, connector.game.resource_database.multiworld_magic_item.extra["item_id"] ) connector._patches_for_pickup.assert_awaited_once_with( permanent_pickups[0][0], permanent_pickups[0][1], inventory) assert has_message assert patches == [ DolRemotePatch([], pickup_patches), DolRemotePatch([], pickup_patches), DolRemotePatch([], mock_item_patch.return_value), DolRemotePatch( [connector._write_string_to_game_buffer.return_value], mock_call_display_hud_patch.return_value, ), ] connector._write_string_to_game_buffer.assert_called_once_with( "The Message") mock_call_display_hud_patch.assert_called_once_with(version.string_display)
def test_update_tracker_from_hook(window, echoes_resource_database): # Setup inventory = { item: InventoryItem(item.index % 3, item.index % 3) for item in echoes_resource_database.item } # Run window._update_tracker_from_hook(inventory)
async def test_get_inventory_invalid_amount(connector: EchoesRemoteConnector): # Setup custom_inventory = {"Darkburst": InventoryItem(1, 0)} executor = AsyncMock() executor.perform_memory_operations.side_effect = lambda ops: { op: struct.pack( ">II", *custom_inventory.get( item.short_name, InventoryItem(item.max_capacity, item.max_capacity))) for op, item in zip(ops, connector.game.resource_database.item) } # Run msg = "Received InventoryItem(amount=1, capacity=0) for Darkburst, which is an invalid state." with pytest.raises(MemoryOperationException, match=re.escape(msg)): await connector.get_inventory(executor)
def test_update_tracker_from_hook(window, echoes_resource_database): # Setup items = echoes_resource_database.item inventory = { items[i]: InventoryItem(i % 3, i % 3) for i in range(len(items)) } # Run window._update_tracker_from_hook(inventory)
async def test_check_for_collected_index_location_collected_no_tracking(backend): # Setup backend.patches = dol_patcher.ALL_VERSIONS_PATCHES[0] backend.tracking_inventory = False backend._inventory = None backend._read_item = AsyncMock(return_value=InventoryItem(10, 10)) backend._write_item = AsyncMock() backend._update_inventory = AsyncMock() backend._emit_location_collected = AsyncMock() # Run await backend._check_for_collected_index() # Assert backend._emit_location_collected.assert_awaited_once_with(9) backend._update_inventory.assert_not_awaited() backend._read_item.assert_awaited_once_with(backend.game.resource_database.multiworld_magic_item) backend._write_item.assert_awaited_once_with(backend.game.resource_database.multiworld_magic_item, InventoryItem(0, 0))
async def _read_item(self, item: ItemResourceInfo) -> InventoryItem: player_state_pointer = self._get_player_state_pointer() op_result = await self._perform_single_memory_operations( MemoryOperation( address=player_state_pointer, read_byte_count=8, offset=_powerup_offset(item.index), )) return InventoryItem(*struct.unpack(">II", op_result))
async def test_check_for_collected_index_nothing(backend): # Setup backend.patches = dol_patcher.ALL_VERSIONS_PATCHES[0] backend._inventory = { backend.game.resource_database.multiworld_magic_item: InventoryItem(0, 0) } backend._update_inventory = AsyncMock() # Run await backend._check_for_collected_index() # Assert backend._update_inventory.assert_not_awaited()
async def test_check_for_collected_index_receive_items(backend, tracking: bool): # Setup backend.patches = dol_patcher.ALL_VERSIONS_PATCHES[0] backend.tracking_inventory = tracking backend._update_inventory = AsyncMock() backend._read_item = AsyncMock(return_value=InventoryItem(0, 0)) backend._write_item = AsyncMock() resource = backend.game.resource_database.energy_tank pickup = PickupEntry("Pickup", 0, ItemCategory.MISSILE, ItemCategory.MISSILE, ( ConditionalResources(None, None, ((resource, resource.max_capacity),), ), )) inventory = { backend.game.resource_database.multiworld_magic_item: InventoryItem(0, 0), backend.game.resource_database.energy_tank: InventoryItem(1, 1), } backend._get_inventory = AsyncMock(return_value=inventory) if tracking: backend._inventory = inventory else: backend._inventory = None backend._permanent_pickups = [pickup] # Run await backend._check_for_collected_index() # Assert if tracking: backend._read_item.assert_not_awaited() else: backend._read_item.assert_awaited_once_with(backend.game.resource_database.multiworld_magic_item) backend._update_inventory.assert_awaited_once_with({ backend.game.resource_database.multiworld_magic_item: InventoryItem(0, 1), backend.game.resource_database.energy_tank: InventoryItem(resource.max_capacity, resource.max_capacity), }) backend._write_item.assert_not_awaited()
async def test_perform_write_inventory_dark_suit(backend, echoes_game_description, has_light_suit): # Setup backend.patches = dol_patcher.ALL_VERSIONS_PATCHES[0] backend._perform_memory_operations = AsyncMock() dark_suit = echoes_game_description.resource_database.get_item(13) light_suit = echoes_game_description.resource_database.get_item(14) changed_items = {dark_suit: InventoryItem(1, 1)} if has_light_suit: backend._inventory = {light_suit: InventoryItem(1, 1)} else: backend._inventory = {} # Run await backend._perform_write_inventory(changed_items) # Assert backend._perform_memory_operations.assert_awaited_once() write_op = backend._perform_memory_operations.mock_calls[0].args[0] assert write_op[1] == MemoryOperation( address=2151533548, offset=84, write_bytes=b"\x00\x00\x00\x02" if has_light_suit else b"\x00\x00\x00\x01", )
async def test_update_inventory_with_change(backend, item): # Setup backend.patches = dol_patcher.ALL_VERSIONS_PATCHES[0] backend._perform_memory_operations = AsyncMock() backend._inventory = { item: InventoryItem(0, 0) for item in backend.game.resource_database.item } new_inventory = copy.copy(backend._inventory) new_inventory[backend.game.resource_database.multiworld_magic_item] = InventoryItem(1, 15) # Run await backend._update_inventory(new_inventory) # Assert backend._perform_memory_operations.assert_awaited_once_with([ MemoryOperation( address=backend._get_player_state_pointer(), write_bytes=struct.pack(">II", 1, 15), read_byte_count=8, offset=connection_backend._powerup_offset(backend.game.resource_database.multiworld_magic_item.index), ) ])
async def test_update_inventory_no_change(backend): # Setup backend.patches = dol_patcher.ALL_VERSIONS_PATCHES[0] backend._perform_memory_operations = AsyncMock() backend._inventory = { item: InventoryItem(0, 0) for item in backend.game.resource_database.item } # Run await backend._update_inventory(backend._inventory) # Assert backend._perform_memory_operations.assert_not_awaited()
async def test_update_magic_item_nothing(backend): # Setup backend.patches = dol_patcher.ALL_VERSIONS_PATCHES[0] backend._inventory = { backend.game.resource_database.multiworld_magic_item: InventoryItem(0, 0) } backend._emit_location_collected = AsyncMock() backend._execute_remote_patches = AsyncMock() # Run await backend._update_magic_item() # Assert backend._emit_location_collected.assert_not_awaited() backend._execute_remote_patches.assert_not_awaited()
async def test_session_self_update(client: NetworkClient): client._emit_with_result = AsyncMock() client._current_game_session_meta = MagicMock() client._current_game_session_meta.id = 1234 inventory: Inventory = { ItemResourceInfo(33, "None", "None", 1): InventoryItem(1, 1) } await client.session_self_update(inventory, GameConnectionStatus.InGame, MemoryExecutorChoice.DOLPHIN) client._emit_with_result.assert_awaited_once_with( "game_session_self_update", (1234, b'\x01None\x00\x01\x01', "In-game (Dolphin)"))
async def test_get_inventory(backend): # Setup backend.patches = dol_patcher.ALL_VERSIONS_PATCHES[0] backend._perform_memory_operations.return_value = [ struct.pack(">II", item.index, item.index) for item in backend.game.resource_database.item ] # Run inventory = await backend._get_inventory() # Assert assert inventory == { item: InventoryItem(item.index, item.index) for item in backend.game.resource_database.item }
async def test_get_inventory_valid(connector: EchoesRemoteConnector): # Setup executor = AsyncMock() executor.perform_memory_operations.side_effect = lambda ops: { op: struct.pack(">II", item.max_capacity, item.max_capacity) for op, item in zip(ops, connector.game.resource_database.item) } # Run inventory = await connector.get_inventory(executor) # Assert assert inventory == { item: InventoryItem(item.max_capacity, item.max_capacity) for item in connector.game.resource_database.item }
async def test_get_inventory(backend): # Setup backend.patches = dol_patcher.ALL_VERSIONS_PATCHES[0] backend._perform_memory_operations.side_effect = lambda ops: { op: struct.pack(">II", item.index, item.index) for op, item in zip(ops, backend.game.resource_database.item) } # Run inventory = await backend._get_inventory() # Assert assert inventory == { item: InventoryItem(item.index, item.index) for item in backend.game.resource_database.item }
async def test_find_missing_remote_pickups_pending_location( connector: EchoesRemoteConnector): # Setup executor = AsyncMock() inventory = { connector.game.resource_database.multiworld_magic_item: InventoryItem(5, 15) } # Run patches, has_message = await connector.find_missing_remote_pickups( executor, inventory, [], False) # Assert assert patches == [] assert not has_message
def _update_tracker_from_hook(self, inventory: Dict[ItemResourceInfo, InventoryItem]): for element in self._tracker_elements: amount = 0 capacity = 0 max_capacity = 0 for resource in element.resources: current = inventory.get(resource, InventoryItem(0, 0)) amount += current.amount capacity += current.capacity max_capacity += resource.max_capacity if isinstance(element.label, ClickableLabel): element.label.set_checked(max_capacity == 0 or capacity > 0) else: element.label.setText(element.text_template.format( amount=amount, capacity=capacity, max_capacity=max_capacity, ))
async def test_update_magic_item_collected_location(backend, mocker): # Setup mock_item_patch: MagicMock = mocker.patch( "randovania.games.prime.all_prime_dol_patches.adjust_item_amount_and_capacity_patch") backend.patches = dol_patcher.ALL_VERSIONS_PATCHES[0] backend._inventory = { backend.game.resource_database.multiworld_magic_item: InventoryItem(10, 10) } backend._emit_location_collected = AsyncMock() backend._execute_remote_patches = AsyncMock() # Run await backend._update_magic_item() # Assert mock_item_patch.assert_called_once_with(backend.patches.powerup_functions, backend.game.resource_database.multiworld_magic_item.index, -10) backend._emit_location_collected.assert_awaited_once_with(9) backend._execute_remote_patches.assert_awaited_once_with([mock_item_patch.return_value], None)
async def _get_inventory(self) -> Dict[ItemResourceInfo, InventoryItem]: player_state_pointer = self._get_player_state_pointer() memory_ops = [ MemoryOperation( address=player_state_pointer, read_byte_count=8, offset=_powerup_offset(item.index), ) for item in self.game.resource_database.item ] ops_result = await self._perform_memory_operations(memory_ops) inventory = {} for item, memory_result in zip(self.game.resource_database.item, ops_result): inventory[item] = InventoryItem( *struct.unpack(">II", memory_result)) return inventory