Пример #1
0
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."
Пример #2
0
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."
Пример #3
0
    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,
                        ))
Пример #4
0
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(
    )
Пример #5
0
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."
Пример #6
0
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()
Пример #7
0
    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(), []
Пример #8
0
    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)))
Пример #9
0
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)
Пример #12
0
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))
Пример #16
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()
Пример #22
0
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()
Пример #23
0
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)"))
Пример #24
0
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
Пример #28
0
    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,
                ))
Пример #29
0
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)
Пример #30
0
    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