async def _setup_locations_combo(self):
        network_client = common_qt_lib.get_network_client()
        game_session = network_client.current_game_session
        user = network_client.current_user

        game = self.game
        index_to_name = {
            node.pickup_index.index: game.world_list.area_name(area)
            for world, area, node in game.world_list.all_worlds_areas_nodes
            if isinstance(node, PickupNode)
        }

        if game_session is None:
            names = index_to_name
        else:
            patcher_data = await network_client.session_admin_player(
                user.id, SessionAdminUserAction.CREATE_PATCHER_FILE,
                CosmeticPatches().as_json)
            names = {
                pickup["pickup_index"]:
                "{}: {}".format(index_to_name[pickup["pickup_index"]],
                                pickup["hud_text"][0])
                for pickup in patcher_data["pickups"]
            }

        self.collect_location_combo.clear()
        for index, name in sorted(names.items()):
            self.collect_location_combo.addItem(name, index)

        self.collect_location_button.setEnabled(True)
        self.collect_location_combo.setVisible(True)
        self.setup_collect_location_combo_button.deleteLater()
Example #2
0
def test_distribute_command_logic(
    mock_generate_description: MagicMock,
    mock_from_str: MagicMock,
    mock_write_patcher_file_to_disk: MagicMock,
):
    # Setup
    args = MagicMock()
    args.output_file = Path("asdfasdf/qwerqwerqwer/zxcvzxcv.json")
    patcher_json = Path("asdfasdf/qwerqwerqwer/zxcvzxcv.patcher-json")

    # Run
    randovania.cli.commands.distribute.distribute_command_logic(args)

    # Assert
    mock_from_str.assert_called_once_with(args.permalink)

    mock_generate_description.assert_called_once_with(
        permalink=mock_from_str.return_value,
        status_update=ANY,
        validate_after_generation=args.validate,
        timeout=None,
    )

    save_file_mock: MagicMock = mock_generate_description.return_value.save_to_file
    save_file_mock.assert_called_once_with(args.output_file)

    mock_write_patcher_file_to_disk.assert_called_once_with(
        patcher_json,
        mock_generate_description.return_value,
        CosmeticPatches.default(),
    )
Example #3
0
def test_game_session_admin_player_patcher_file(mock_layout_description: PropertyMock,
                                                mock_create_patcher_file: MagicMock,
                                                flask_app,
                                                clean_database):
    user1 = database.User.create(id=1234, name="The Name")
    user2 = database.User.create(id=1235, name="Brother")
    session = database.GameSession.create(id=1, name="Debug", state=GameSessionState.IN_PROGRESS, creator=user1)
    database.GameSessionPreset.create(session=session, row=0, preset="{}")
    database.GameSessionPreset.create(session=session, row=1, preset="{}")
    database.GameSessionPreset.create(session=session, row=2, preset="{}")
    database.GameSessionMembership.create(user=user1, session=session, row=2, admin=False)
    database.GameSessionMembership.create(user=user2, session=session, row=1, admin=False)
    sio = MagicMock()
    sio.get_current_user.return_value = user1

    cosmetic = CosmeticPatches(open_map=False)

    # Run
    with flask_app.test_request_context():
        result = game_session.game_session_admin_player(sio, 1, 1234, "create_patcher_file", cosmetic.as_json)

    # Assert
    mock_create_patcher_file.assert_called_once_with(
        mock_layout_description.return_value,
        PlayersConfiguration(2, {
            0: "Player 1",
            1: "Brother",
            2: "The Name",
        }),
        cosmetic
    )
    assert result is mock_create_patcher_file.return_value
Example #4
0
def test_change_hud_lag(skip_qtbot):
    preferences = CosmeticPatches(user_preferences=EchoesUserPreferences(
        hud_lag=False))

    dialog = EchoesUserPreferencesDialog(None, preferences)
    skip_qtbot.addWidget(dialog)

    dialog.hud_lag_check.setChecked(True)

    assert dialog.preferences == EchoesUserPreferences(hud_lag=True)
Example #5
0
def test_change_sfx_volume(skip_qtbot):
    preferences = CosmeticPatches(user_preferences=EchoesUserPreferences(
        sfx_volume=15))

    dialog = EchoesUserPreferencesDialog(None, preferences)
    skip_qtbot.addWidget(dialog)

    dialog.sfx_volume_slider.setValue(50)

    assert dialog.preferences == EchoesUserPreferences(sfx_volume=50)
Example #6
0
def test_change_sound_mode(skip_qtbot):
    preferences = CosmeticPatches(user_preferences=EchoesUserPreferences(
        sound_mode=SoundMode.MONO))

    dialog = EchoesUserPreferencesDialog(None, preferences)
    skip_qtbot.addWidget(dialog)

    dialog.sound_mode_combo.setCurrentIndex(2)

    assert dialog.preferences == EchoesUserPreferences(
        sound_mode=SoundMode.SURROUND)
Example #7
0
def test_create_claris_patcher_file(test_files_dir, randomizer_data):
    # Setup
    description = LayoutDescription.from_file(
        test_files_dir.joinpath("log_files", "seed_a.rdvgame"))
    player_index = 0
    preset = description.permalink.get_preset(player_index)
    cosmetic_patches = CosmeticPatches()

    # Run
    result = claris_patcher_file.create_patcher_file(
        description, PlayersConfiguration(player_index, {0: "you"}),
        cosmetic_patches, randomizer_data)

    # Assert
    assert isinstance(result["spawn_point"], dict)

    assert isinstance(result["pickups"], list)
    assert len(result["pickups"]) == 119

    assert isinstance(result["elevators"], list)
    assert len(result["elevators"]) == 22

    assert isinstance(result["translator_gates"], list)
    assert len(result["translator_gates"]) == 17

    assert isinstance(result["string_patches"], list)
    assert len(result["string_patches"]) == 60

    assert result["specific_patches"] == {
        "hive_chamber_b_post_state": True,
        "intro_in_post_state": True,
        "warp_to_start": preset.configuration.warp_to_start,
        "speed_up_credits": cosmetic_patches.speed_up_credits,
        "disable_hud_popup": cosmetic_patches.disable_hud_popup,
        "pickup_map_icons": cosmetic_patches.pickup_markers,
        "full_map_at_start": cosmetic_patches.open_map,
        "dark_world_varia_suit_damage": preset.configuration.varia_suit_damage,
        "dark_world_dark_suit_damage": preset.configuration.dark_suit_damage,
        "always_up_gfmc_compound": True,
        "always_up_torvus_temple": True,
        "always_up_great_temple": False,
    }
    # TODO: check all fields?
    assert result["dol_patches"]["default_items"] == {
        "visor": "Combat Visor",
        "beam": "Power Beam",
    }
Example #8
0
def test_randomize_command_logic(mocker, with_permalink):
    mock_from_str = mocker.patch(
        "randovania.layout.permalink.Permalink.from_str")
    mock_from_file = mocker.patch(
        "randovania.layout.layout_description.LayoutDescription.from_file")
    mock_generate = mocker.patch(
        "randovania.generator.generator.generate_description")
    mock_apply = mocker.patch(
        "randovania.games.prime.claris_randomizer.apply_layout")

    args = MagicMock()
    if with_permalink:
        layout_description = mock_generate.return_value
    else:
        layout_description = mock_from_file.return_value
        args.permalink = None

    cosmetic_patches = CosmeticPatches(
        disable_hud_popup=args.disable_hud_popup,
        speed_up_credits=args.speed_up_credits)

    # Run
    randomize_command.randomize_command_logic(args)

    # Assert
    mock_apply.assert_called_once_with(description=layout_description,
                                       cosmetic_patches=cosmetic_patches,
                                       backup_files_path=args.backup_files,
                                       progress_update=ANY,
                                       game_root=args.game_files,
                                       players_config=PlayersConfiguration(
                                           0, {0: "You"}))

    if with_permalink:
        mock_from_str.assert_called_once_with(args.permalink)
        mock_generate.assert_called_once_with(
            permalink=mock_from_str.return_value,
            status_update=ANY,
            validate_after_generation=True)
        mock_from_file.assert_not_called()
    else:
        mock_from_str.assert_not_called()
        mock_generate.assert_not_called()
        mock_from_file.assert_called_once_with(args.log_file)
Example #9
0
def distribute_command_logic(args):
    debug._DEBUG_LEVEL = args.debug

    def status_update(s):
        pass

    permalink = Permalink.from_str(args.permalink)

    before = time.perf_counter()
    layout_description = generator.generate_description(permalink=permalink, status_update=status_update,
                                                        validate_after_generation=args.validate, timeout=None)
    after = time.perf_counter()
    print("Took {} seconds. Hash: {}".format(after - before, layout_description.shareable_hash))

    layout_description.save_to_file(args.output_file)
    simplified_patcher.write_patcher_file_to_disk(
        args.output_file.with_suffix(".patcher-json"),
        layout_description,
        CosmeticPatches.default(),
    )
Example #10
0
def randomize_command_logic(args):
    def status_update(s):
        if args.verbose:
            print(s)

    if args.permalink is not None:
        layout_description = generator.generate_description(permalink=Permalink.from_str(args.permalink),
                                                            status_update=status_update,
                                                            validate_after_generation=True)
    else:
        layout_description = LayoutDescription.from_file(args.log_file)

    cosmetic_patches = CosmeticPatches(
        disable_hud_popup=args.disable_hud_popup,
        speed_up_credits=args.speed_up_credits)

    claris_randomizer.apply_layout(description=layout_description,
                                   cosmetic_patches=cosmetic_patches,
                                   backup_files_path=args.backup_files,
                                   progress_update=lambda x, _: status_update(x),
                                   game_root=args.game_files,
                                   )
Example #11
0
async def randomize_command_logic_async(args):
    from randovania.games.patcher_provider import PatcherProvider
    from randovania.generator import generator
    from randovania.interface_common.cosmetic_patches import CosmeticPatches
    from randovania.interface_common.players_configuration import PlayersConfiguration
    from randovania.layout.layout_description import LayoutDescription
    from randovania.layout.permalink import Permalink
    from randovania.interface_common.options import Options

    def status_update(s):
        if args.verbose:
            print(s)

    if args.permalink is not None:
        permalink = Permalink.from_str(args.permalink)
        layout_description = await generator.generate_and_validate_description(
            permalink=permalink,
            status_update=status_update,
            validate_after_generation=True,
        )
    else:
        layout_description = LayoutDescription.from_file(args.log_file)

    cosmetic_patches = CosmeticPatches(
        disable_hud_popup=args.disable_hud_popup,
        speed_up_credits=args.speed_up_credits)

    players_config = PlayersConfiguration(args.player_index,
                                          {i: f"Player {i + 1}"
                                           for i in range(layout_description.permalink.player_count)})
    preset = layout_description.permalink.get_preset(players_config.player_index)

    game_files_path = Options.with_default_data_dir().game_files_path
    patcher_provider = PatcherProvider()
    patcher = patcher_provider.patcher_for_game(preset.game)

    patch_data = patcher.create_patch_data(layout_description, players_config, cosmetic_patches)
    patcher.patch_game(args.input_file, args.output_file, patch_data, game_files_path, lambda x, _: status_update(x))
def test_create_patcher_file(test_files_dir):
    # Setup
    description = LayoutDescription.from_file(test_files_dir.joinpath("log_files", "seed_a.json"))
    cosmetic_patches = CosmeticPatches()
    translator_gates = description.permalink.layout_configuration.translator_configuration

    # Run
    result = patcher_file.create_patcher_file(description, cosmetic_patches)

    # Assert
    assert isinstance(result["spawn_point"], dict)

    assert isinstance(result["pickups"], list)
    assert len(result["pickups"]) == 119

    assert isinstance(result["elevators"], list)
    assert len(result["elevators"]) == 22

    assert isinstance(result["translator_gates"], list)
    assert len(result["translator_gates"]) == 17

    assert isinstance(result["string_patches"], list)
    assert len(result["string_patches"]) == 40

    assert result["specific_patches"] == {
        "hive_chamber_b_post_state": True,
        "intro_in_post_state": True,
        "warp_to_start": description.permalink.patcher_configuration.warp_to_start,
        "speed_up_credits": cosmetic_patches.speed_up_credits,
        "disable_hud_popup": cosmetic_patches.disable_hud_popup,
        "pickup_map_icons": cosmetic_patches.pickup_markers,
        "full_map_at_start": cosmetic_patches.open_map,
        "dark_world_varia_suit_damage": description.permalink.patcher_configuration.varia_suit_damage,
        "dark_world_dark_suit_damage": description.permalink.patcher_configuration.dark_suit_damage,
        "always_up_gfmc_compound": translator_gates.fixed_gfmc_compound,
        "always_up_torvus_temple": translator_gates.fixed_torvus_temple,
        "always_up_great_temple": translator_gates.fixed_great_temple,
    }
Example #13
0
def game_session_admin_player(sio: ServerApp, session_id: int, user_id: int,
                              action: str, arg):
    _verify_has_admin(sio, session_id, user_id)
    action: SessionAdminUserAction = SessionAdminUserAction(action)

    session: GameSession = database.GameSession.get_by_id(session_id)
    membership = GameSessionMembership.get_by_ids(user_id, session_id)

    if action == SessionAdminUserAction.KICK:
        membership.delete_instance()
        if not list(session.players):
            session.delete_instance(recursive=True)
            logger().info(
                f"Session {session_id}. Kicking user {user_id} and deleting session."
            )
        else:
            logger().info(f"Session {session_id}. Kicking user {user_id}.")

    elif action == SessionAdminUserAction.MOVE:
        offset: int = arg
        if membership.is_observer is None:
            raise InvalidAction("Player is an observer")

        new_row = membership.row + offset
        if new_row < 0:
            raise InvalidAction("New position is negative")
        if new_row >= session.num_rows:
            raise InvalidAction("New position is beyond num of rows")

        team_members = [None] * session.num_rows
        for member in GameSessionMembership.non_observer_members(session):
            team_members[member.row] = member

        while (0 <= new_row <
               session.num_rows) and team_members[new_row] is not None:
            new_row += offset

        if new_row < 0 or new_row >= session.num_rows:
            raise InvalidAction("No empty slots found in this direction")

        with database.db.atomic():
            logger().info(
                f"Session {session_id}, User {user_id}. "
                f"Performing {action}, new row is {new_row}, from {membership.row}."
            )
            membership.row = new_row
            membership.save()

    elif action == SessionAdminUserAction.SWITCH_IS_OBSERVER:
        if membership.is_observer:
            membership.row = _find_empty_row(session)
        else:
            membership.row = None
        logger().info(
            f"Session {session_id}, User {user_id}. Performing {action}, new row is {membership.row}."
        )
        membership.save()

    elif action == SessionAdminUserAction.SWITCH_ADMIN:
        # Must be admin for this
        _verify_has_admin(sio, session_id, None, allow_when_no_admins=True)
        num_admins = GameSessionMembership.select().where(
            GameSessionMembership.session == session_id,
            GameSessionMembership.admin == True).count()

        if membership.admin and num_admins <= 1:
            raise InvalidAction("can't demote the only admin")

        membership.admin = not membership.admin
        logger().info(
            f"Session {session_id}, User {user_id}. Performing {action}, new status is {membership.admin}."
        )
        membership.save()

    elif action == SessionAdminUserAction.CREATE_PATCHER_FILE:
        cosmetic_patches = CosmeticPatches.from_json_dict(arg)
        player_names = {i: f"Player {i + 1}" for i in range(session.num_rows)}

        for member in GameSessionMembership.non_observer_members(session):
            player_names[member.row] = member.effective_name

        players_config = PlayersConfiguration(
            player_index=membership.row,
            player_names=player_names,
        )
        return patcher_file.create_patcher_file(session.layout_description,
                                                players_config,
                                                cosmetic_patches)

    elif action == SessionAdminUserAction.ABANDON:
        # FIXME
        raise InvalidAction("Abandon is NYI")

    _emit_session_update(session)
def test_apply_layout(mock_run_with_args: MagicMock,
                      mock_base_args: MagicMock,
                      mock_ensure_no_menu_mod: MagicMock,
                      mock_create_pak_backups: MagicMock,
                      mock_calculate_indices: MagicMock,
                      mock_add_menu_mod_to_files: MagicMock,
                      mock_create_progress_update_from_successive_messages: MagicMock,
                      mock_save_to_file: MagicMock,
                      seed_number: int,
                      item_loss: bool,
                      elevators: bool,
                      warp_to_start: bool,
                      include_menu_mod: bool,
                      speed_up_credits: bool,
                      ):
    # Setup
    hud_memo_popup_removal: bool = MagicMock()
    cosmetic_patches = CosmeticPatches(disable_hud_popup=hud_memo_popup_removal,
                                       speed_up_credits=speed_up_credits,
                                       )
    description = LayoutDescription(
        version=randovania.VERSION,
        permalink=Permalink(
            seed_number=seed_number,
            spoiler=False,
            patcher_configuration=PatcherConfiguration(
                menu_mod=include_menu_mod,
                warp_to_start=warp_to_start,
            ),
            layout_configuration=LayoutConfiguration.from_params(
                trick_level=MagicMock(),
                sky_temple_keys=MagicMock(),
                elevators=LayoutRandomizedFlag.RANDOMIZED if elevators else LayoutRandomizedFlag.VANILLA,
                pickup_quantities={},
                starting_location=StartingLocation.default(),
                starting_resources=StartingResources.from_item_loss(item_loss),
            )
        ),
        patches=None,
        solver_path=(),
    )

    game_root = MagicMock(spec=Path())
    backup_files_path = MagicMock()
    progress_update = MagicMock()
    status_update = mock_create_progress_update_from_successive_messages.return_value

    mock_calculate_indices.return_value = [10, 25, 1, 2, 5, 1]
    mock_base_args.return_value = []
    expected_args = [
        "-s", str(seed_number),
        "-p", "10,25,1,2,5,1"
    ]
    if not item_loss:
        expected_args.append("-i")
    if elevators:
        expected_args.append("-v")
    if speed_up_credits:
        expected_args.append("-c")
    if warp_to_start:
        expected_args.append("-t")

    # Run
    claris_randomizer.apply_layout(description, cosmetic_patches, backup_files_path, progress_update, game_root)

    # Assert
    mock_base_args.assert_called_once_with(game_root, hud_memo_popup_removal=hud_memo_popup_removal)
    mock_create_progress_update_from_successive_messages.assert_called_once_with(
        progress_update,
        400 if include_menu_mod else 100
    )
    mock_ensure_no_menu_mod.assert_called_once_with(game_root, backup_files_path, status_update)
    mock_create_pak_backups.assert_called_once_with(game_root, backup_files_path, status_update)
    mock_calculate_indices.assert_called_once_with(description)
    game_root.joinpath.assert_called_once_with("files", "randovania.json")
    mock_save_to_file.assert_called_once_with(description, game_root.joinpath.return_value)
    mock_run_with_args.assert_called_once_with(expected_args, "Randomized!", status_update)
    if include_menu_mod:
        mock_add_menu_mod_to_files.assert_called_once_with(game_root, status_update)
    else:
        mock_add_menu_mod_to_files.assert_not_called()
 def reset(self):
     self.on_new_cosmetic_patches(CosmeticPatches())