コード例 #1
0
def apply_layout(
    description: LayoutDescription,
    players_config: PlayersConfiguration,
    cosmetic_patches: CosmeticPatches,
    backup_files_path: Optional[Path],
    progress_update: ProgressUpdateCallable,
    game_root: Path,
):
    """
    Applies the modifications listed in the given LayoutDescription to the game in game_root.
    :param description:
    :param players_config:
    :param cosmetic_patches:
    :param game_root:
    :param backup_files_path: Path to use as pak backup, to remove/add menu mod.
    :param progress_update:
    :return:
    """
    description.save_to_file(
        game_root.joinpath("files",
                           f"randovania.{description.file_extension()}"))

    apply_patcher_file(
        game_root, backup_files_path,
        patcher_file.create_patcher_file(description, players_config,
                                         cosmetic_patches),
        description.all_patches[players_config.player_index].game_specific,
        progress_update)
コード例 #2
0
def export_layout(
    layout: LayoutDescription,
    options: Options,
):
    """
    Creates a seed log file for the given layout and saves it to the configured path
    :param layout:
    :param options:
    :return:
    """

    output_json = options.output_directory.joinpath("{}.json".format(
        _output_name_for(layout)))

    if debug.debug_level() > 0:
        patcher = options.output_directory.joinpath("{}-patcher.json".format(
            _output_name_for(layout)))
        with patcher.open("w") as out_file:
            json.dump(patcher_file.create_patcher_file(
                layout, options.cosmetic_patches),
                      out_file,
                      indent=4,
                      separators=(',', ': '))

    # Save the layout to a file
    layout.save_to_file(output_json)
コード例 #3
0
def persist_layout(data_dir: Path, description: LayoutDescription):
    history_dir = data_dir.joinpath("game_history")
    history_dir.mkdir(parents=True, exist_ok=True)

    date_format = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
    file_path = history_dir.joinpath(
        f"{date_format}-{description.shareable_word_hash}.{description.file_extension()}")
    description.save_to_file(file_path)
コード例 #4
0
def persist_layout(history_dir: Path, description: LayoutDescription):
    history_dir.mkdir(parents=True, exist_ok=True)

    games = "-".join(sorted(game.short_name for game in description.all_games))

    date_format = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
    file_path = history_dir.joinpath(
        f"{date_format}_{games}_{description.shareable_word_hash}.{description.file_extension()}")
    description.save_to_file(file_path)
コード例 #5
0
def _create_patch_data(test_files_dir, mocker, in_file, out_file, cosmetic):
    # Setup
    f = test_files_dir.joinpath("log_files", "cave_story",
                                f"{in_file}.rdvgame")
    description = LayoutDescription.from_file(f)
    players_config = PlayersConfiguration(0, {0: "Cave Story"})

    mocker.patch(
        "randovania.layout.layout_description.LayoutDescription.shareable_hash_bytes",
        new_callable=PropertyMock,
        return_value=b'\x00\x00\x00\x00\x00')

    # Run
    data = CSPatchDataFactory(description, players_config,
                              cosmetic).create_data()

    # Expected Result

    # strip mychar to just the filename rather than full path
    if data["mychar"] is not None:
        mychar = Path(data["mychar"])
        data["mychar"] = mychar.name

    # Uncomment the following lines to update:
    # with test_files_dir.joinpath("caver_expected_data", f"{out_file}.json").open("w") as f:
    #     json.dump(data, f)

    with test_files_dir.joinpath("caver_expected_data",
                                 f"{out_file}.json").open("r") as f:
        expected_data = json.load(f)

    assert data == expected_data
コード例 #6
0
def _test_preset(rdvgame_file, expected_results_file, mocker):
    # Setup
    description = LayoutDescription.from_file(rdvgame_file)
    players_config = PlayersConfiguration(0, {0: "Prime", 1: "Echoes"})
    cosmetic_patches = PrimeCosmeticPatches(use_hud_color=True,
                                            hud_color=(255, 0, 0),
                                            suit_color_rotations=(0, 40, 350,
                                                                  12))

    mocker.patch(
        "randovania.layout.layout_description.LayoutDescription.shareable_hash_bytes",
        new_callable=PropertyMock,
        return_value=b"\x00\x00\x00\x00\x00")

    # Run
    data = PrimePatchDataFactory(description, players_config,
                                 cosmetic_patches).create_data()

    # Expected Result
    with expected_results_file.open("r") as file:
        expected_data = json.load(file)

    # Uncomment to easily view diff of failed test
    # with expected_results_file.open("w") as file:
    #     file.write(json.dumps(data, indent=4, separators=(',', ': ')))

    # Ignore the part of the main menu message which has the randovania version in it
    data["gameConfig"]["mainMenuMessage"] = data["gameConfig"][
        "mainMenuMessage"].split("\n")[1]
    expected_data["gameConfig"]["mainMenuMessage"] = expected_data[
        "gameConfig"]["mainMenuMessage"].split("\n")[1]

    assert data == expected_data
コード例 #7
0
def test_apply_layout(mock_patch_game_name_and_id: MagicMock,
                      mock_change_starting_spawn: MagicMock,
                      mock_claris_apply_layout: MagicMock, empty_patches):
    # Setup
    layout = MagicMock(spec=LayoutDescription(version="0.15.0",
                                              permalink=MagicMock(),
                                              patches=empty_patches,
                                              solver_path=()))
    progress_update = MagicMock()
    options: Options = MagicMock()

    # Run
    simplified_patcher.apply_layout(layout, options, progress_update)

    # Assert
    mock_patch_game_name_and_id.assert_called_once_with(
        options.game_files_path,
        "Metroid Prime 2: Randomizer - {}".format(layout.shareable_hash))
    mock_change_starting_spawn.assert_called_once_with(
        options.game_files_path, layout.patches.starting_location)
    mock_claris_apply_layout.assert_called_once_with(
        description=layout,
        cosmetic_patches=options.cosmetic_patches,
        game_root=options.game_files_path,
        backup_files_path=options.backup_files_path,
        progress_update=progress_update)
コード例 #8
0
def export_layout(
    layout: LayoutDescription,
    options: Options,
):
    """
    Creates a seed log file for the given layout and saves it to the configured path
    :param layout:
    :param options:
    :return:
    """

    output_json = options.output_directory.joinpath("{}.json".format(
        _output_name_for(layout)))

    # Save the layout to a file
    layout.save_to_file(output_json)
コード例 #9
0
def test_internal_patch_iso(mock_apply_layout: MagicMock,
                            mock_pack_iso: MagicMock,
                            mock_debug_level: MagicMock, empty_patches):
    # Setup
    mock_debug_level.return_value = 0
    layout = MagicMock(spec=LayoutDescription(version="0.15.0",
                                              permalink=MagicMock(),
                                              patches=empty_patches,
                                              solver_path=()))
    layout.shareable_hash = "layout"
    options = MagicMock()
    options.output_directory = Path("fun")

    name = "Echoes Randomizer - layout"
    output_iso = Path("fun", name + ".iso")
    output_json = Path("fun", name + ".json")
    updaters = [MagicMock(), MagicMock()]

    # Run
    simplified_patcher._internal_patch_iso(updaters, layout, options)

    # Assert
    mock_apply_layout.assert_called_once_with(layout=layout,
                                              options=options,
                                              progress_update=updaters[0])
    mock_pack_iso.assert_called_once_with(output_iso=output_iso,
                                          options=options,
                                          progress_update=updaters[1])
    layout.save_to_file.assert_called_once_with(output_json)
コード例 #10
0
def _create_description_mock(permalink: Permalink, empty_patches: GamePatches):
    return MagicMock(spec=LayoutDescription(
        version=randovania.VERSION,
        permalink=permalink,
        patches=empty_patches,
        solver_path=()
    ))
コード例 #11
0
def test_GameSession_create_session_entry(clean_database, has_description, test_files_dir, mocker):
    # Setup
    description = LayoutDescription.from_file(test_files_dir.joinpath("log_files", "seed_a.rdvgame"))
    someone = database.User.create(name="Someone")
    s = database.GameSession.create(name="Debug", num_teams=1, creator=someone)
    game_details = None
    if has_description:
        s.layout_description = description
        s.save()
        game_details = {
            'seed_hash': '5IENQWDS',
            'spoiler': True,
            'word_hash': 'Biostorage Cavern Watch',
        }

    # Run
    session = database.GameSession.get_by_id(1)
    result = session.create_session_entry()
    readable_result = construct_lib.convert_to_raw_python(BinaryGameSessionEntry.parse(result))

    # Assert
    assert readable_result == {
        'allowed_games': ['prime1', 'prime2'],
        'game_details': game_details,
        'generation_in_progress': None,
        'id': 1,
        'name': 'Debug',
        'players': [],
        'presets': [],
        'state': 'setup',
    }
コード例 #12
0
async def _create_description(generator_params: GeneratorParameters,
                              status_update: Callable[[str], None],
                              attempts: int,
                              ) -> LayoutDescription:
    """
    :param generator_params:
    :param status_update:
    :return:
    """
    rng = Random(generator_params.as_bytes)

    presets = [
        generator_params.get_preset(i)
        for i in range(generator_params.player_count)
    ]

    retrying = tenacity.AsyncRetrying(
        stop=tenacity.stop_after_attempt(attempts),
        retry=tenacity.retry_if_exception_type(UnableToGenerate),
        reraise=True
    )

    filler_results = await retrying(_create_pools_and_fill, rng, presets, status_update)

    all_patches = _distribute_remaining_items(rng, filler_results.player_results)
    return LayoutDescription.create_new(
        generator_parameters=generator_params,
        all_patches=all_patches,
        item_order=filler_results.action_log,
    )
コード例 #13
0
ファイル: generator.py プロジェクト: 00mjk/randovania
def _async_create_description(
    permalink: Permalink,
    status_update: Callable[[str], None],
    attempts: int,
) -> LayoutDescription:
    """
    :param permalink:
    :param status_update:
    :return:
    """
    rng = Random(permalink.as_bytes)

    presets = {
        i: permalink.get_preset(i)
        for i in range(permalink.player_count)
    }

    retrying = tenacity.Retrying(
        stop=tenacity.stop_after_attempt(attempts),
        retry=tenacity.retry_if_exception_type(UnableToGenerate),
        reraise=True)

    filler_results = retrying(_create_pools_and_fill, rng, presets,
                              status_update)
    all_patches = _distribute_remaining_items(rng,
                                              filler_results.player_results)
    return LayoutDescription(
        permalink=permalink,
        version=VERSION,
        all_patches=all_patches,
        item_order=filler_results.action_log,
    )
コード例 #14
0
ファイル: generator.py プロジェクト: JaggerTSG/confusino
def _async_create_description(
    permalink: Permalink,
    status_update: Callable[[str], None],
) -> LayoutDescription:
    """
    :param permalink:
    :param status_update:
    :return:
    """
    rng = Random(permalink.as_str)

    presets = {
        i: permalink.get_preset(i)
        for i in range(permalink.player_count)
    }

    filler_results = _retryable_create_patches(rng, presets, status_update)
    all_patches = _distribute_remaining_items(rng,
                                              filler_results.player_results)
    return LayoutDescription(
        permalink=permalink,
        version=VERSION,
        all_patches=all_patches,
        item_order=filler_results.action_log,
    )
コード例 #15
0
ファイル: test_generator.py プロジェクト: xisi/randovania
def _create_test_layout_description(
        configuration: LayoutConfiguration,
        pickup_mapping: Iterable[int],
) -> LayoutDescription:
    """
    Creates a LayoutDescription for the given configuration, with the patches being for the given pickup_mapping
    :param configuration:
    :param pickup_mapping:
    :return:
    """
    game = data_reader.decode_data(configuration.game_data)
    pickup_database = game.pickup_database

    return LayoutDescription(
        version=VERSION,
        permalink=Permalink(
            seed_number=0,
            spoiler=True,
            patcher_configuration=PatcherConfiguration.default(),
            layout_configuration=configuration,
        ),
        patches=GamePatches.with_game(game).assign_new_pickups([
            (PickupIndex(i), pickup_database.original_pickup_mapping[PickupIndex(new_index)])
            for i, new_index in enumerate(pickup_mapping)
        ]),
        solver_path=())
コード例 #16
0
def test_update_content(skip_qtbot, test_files_dir):
    # Setup
    description = LayoutDescription.from_file(test_files_dir.joinpath("log_files", "seed_a.rdvgame"))
    tab = TranslatorGateDetailsTab(None, RandovaniaGame.METROID_PRIME_ECHOES)

    # Run
    tab.update_content(
        description.get_preset(0).configuration,
        description.all_patches,
        PlayersConfiguration(0, {0: "You"}),
    )

    # Assert
    counts = {}
    for i in range(tab.tree_widget.topLevelItemCount()):
        item = tab.tree_widget.topLevelItem(i)
        counts[item.text(0)] = item.childCount()

    assert counts == {
        'Agon Wastes': 2,
        'Great Temple': 3,
        'Sanctuary Fortress': 2,
        'Temple Grounds': 7,
        'Torvus Bog': 3,
    }
コード例 #17
0
ファイル: validate.py プロジェクト: randovania/randovania
def validate_command_logic(args):
    debug.set_level(args.debug)

    description = LayoutDescription.from_file(args.layout_file)
    if description.player_count != 1:
        raise ValueError(
            f"Validator does not support layouts with more than 1 player.")

    configuration = description.get_preset(0).configuration
    patches = description.all_patches[0]
    total_times = []

    final_state_by_resolve = None
    for _ in range(args.repeat):
        before = time.perf_counter()
        final_state_by_resolve = asyncio.run(
            resolver.resolve(configuration=configuration, patches=patches))
        after = time.perf_counter()
        total_times.append(after - before)
        print("Took {:.3f} seconds. Game is {}.".format(
            total_times[-1], "possible"
            if final_state_by_resolve is not None else "impossible"))
    if args.repeat > 1:
        cli_lib.print_report_multiple_times(total_times)

    if args.repeat < 1:
        raise ValueError("Expected at least 1 repeat")
    return 0 if final_state_by_resolve is not None else 1
コード例 #18
0
async def reply_for_layout_description(message: discord.Message, description: LayoutDescription):
    embed = discord.Embed(
        title="Spoiler file for Randovania {}".format(description.randovania_version_text),
    )

    if description.player_count == 1:
        preset = description.get_preset(0)
        embed.description = "{}, with preset {}.\nSeed Hash: {}\nPermalink: {}".format(
            preset.game.long_name, preset.name,
            description.shareable_word_hash,
            description.permalink.as_base64_str,
        )
        _add_preset_description_to_embed(embed, preset)
    else:
        games = {preset.game.long_name for preset in description.all_presets}
        game_names = sorted(games)

        last_game = game_names.pop()
        games_text = ", ".join(game_names)
        if games_text:
            games_text += " and "
        games_text += last_game

        embed.description = "{} player multiworld for {}.\nSeed Hash: {}\nPermalink: {}".format(
            description.player_count,
            games_text,
            description.shareable_word_hash,
            description.permalink.as_base64_str,
        )

    await message.reply(embed=embed, mention_author=False)
コード例 #19
0
    def __init__(self, description: LayoutDescription,
                 players_config: PlayersConfiguration,
                 cosmetic_patches: BaseCosmeticPatches):
        self.description = description
        self.players_config = players_config
        self.cosmetic_patches = cosmetic_patches

        self.item_db = default_database.item_database_for_game(
            self.game_enum())

        self.patches = description.all_patches[players_config.player_index]
        self.configuration = description.get_preset(
            players_config.player_index).configuration
        self.rng = Random(
            description.get_seed_for_player(players_config.player_index))
        self.game = filtered_database.game_description_for_layout(
            self.configuration)
コード例 #20
0
def apply_layout(description: LayoutDescription,
                 cosmetic_patches: CosmeticPatches, backup_files_path: Path,
                 progress_update: ProgressUpdateCallable, game_root: Path):
    """
    Applies the modifications listed in the given LayoutDescription to the game in game_root.
    :param cosmetic_patches:
    :param description:
    :param game_root:
    :param backup_files_path: Path to use as pak backup, to remove/add menu mod.
    :param progress_update:
    :return:
    """

    patcher_configuration = description.permalink.patcher_configuration
    args = _base_args(
        game_root, hud_memo_popup_removal=cosmetic_patches.disable_hud_popup)

    status_update = status_update_lib.create_progress_update_from_successive_messages(
        progress_update, 400 if patcher_configuration.menu_mod else 100)

    _ensure_no_menu_mod(game_root, backup_files_path, status_update)
    _create_pak_backups(game_root, backup_files_path, status_update)

    indices = _calculate_indices(description)

    args += [
        "-s",
        str(description.permalink.seed_number),
        "-p",
        ",".join(str(index) for index in indices),
    ]
    layout_configuration = description.permalink.layout_configuration
    if not is_vanilla_starting_location(layout_configuration):
        args.append("-i")
    if layout_configuration.elevators == LayoutRandomizedFlag.RANDOMIZED:
        args.append("-v")
    if cosmetic_patches.speed_up_credits:
        args.append("-c")
    if description.permalink.patcher_configuration.warp_to_start:
        args.append("-t")

    description.save_to_file(game_root.joinpath("files", "randovania.json"))
    _run_with_args(args, "Randomized!", status_update)

    if patcher_configuration.menu_mod:
        _add_menu_mod_to_files(game_root, status_update)
コード例 #21
0
    async def on_message(self, message: discord.Message):
        if message.author == self.bot.user:
            return

        for attachment in message.attachments:
            filename: str = attachment.filename
            if filename.endswith(VersionedPreset.file_extension()):
                data = await attachment.read()
                versioned_preset = VersionedPreset(json.loads(data.decode("utf-8")))
                await reply_for_preset(message, versioned_preset)

            elif filename.endswith(LayoutDescription.file_extension()):
                data = await attachment.read()
                description = LayoutDescription.from_json_dict(json.loads(data.decode("utf-8")))
                await reply_for_layout_description(message, description)

        await look_for_permalinks(message)
コード例 #22
0
def test_apply_layout(
    mock_ensure_no_menu_mod: MagicMock,
    mock_create_pak_backups: MagicMock,
    mock_add_menu_mod_to_files: MagicMock,
    mock_modern_api: MagicMock,
    mock_create_progress_update_from_successive_messages: MagicMock,
    mock_save_to_file: MagicMock,
    include_menu_mod: bool,
    has_backup_path: bool,
):
    # Setup
    cosmetic_patches = MagicMock()
    description = LayoutDescription(
        version=randovania.VERSION,
        permalink=Permalink(seed_number=1,
                            spoiler=False,
                            patcher_configuration=PatcherConfiguration(
                                menu_mod=include_menu_mod,
                                warp_to_start=MagicMock(),
                            ),
                            layout_configuration=MagicMock()),
        patches=MagicMock(),
        solver_path=(),
    )

    game_root = MagicMock(spec=Path())
    backup_files_path = MagicMock() if has_backup_path else None
    progress_update = MagicMock()
    status_update = mock_create_progress_update_from_successive_messages.return_value

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

    # Assert
    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)
    if has_backup_path:
        mock_create_pak_backups.assert_called_once_with(
            game_root, backup_files_path, status_update)
    else:
        mock_create_pak_backups.assert_not_called()
    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_modern_api.assert_called_once_with(game_root, status_update,
                                            description, cosmetic_patches)

    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()
コード例 #23
0
def prompt_user_for_input_game_log(window: QMainWindow) -> Optional[Path]:
    """
    Shows an QFileDialog asking the user for a Randovania seed log
    :param window:
    :return: A string if the user selected a file, None otherwise
    """
    return _prompt_user_for_file(window, caption="Select a Randovania seed log.",
                                 filter="Randovania Game, *.{}".format(LayoutDescription.file_extension()),
                                 new_file=False)
コード例 #24
0
ファイル: qt.py プロジェクト: BigChilly95/randovania
def show_game_details(app: QApplication, options, game: Path):
    from randovania.layout.layout_description import LayoutDescription
    from randovania.gui.seed_details_window import SeedDetailsWindow

    layout = LayoutDescription.from_file(game)
    details_window = SeedDetailsWindow(None, options)
    details_window.update_layout_description(layout)
    details_window.show()
    app.details_window = details_window
コード例 #25
0
def show_game_details(app: QtWidgets.QApplication, options, game: Path):
    from randovania.layout.layout_description import LayoutDescription
    from randovania.gui.game_details.game_details_window import GameDetailsWindow

    layout = LayoutDescription.from_file(game)
    details_window = GameDetailsWindow(None, options)
    details_window.update_layout_description(layout)
    logger.info("Displaying game details")
    details_window.show()
    app.details_window = details_window
コード例 #26
0
async def test_create_patches(
    mock_random: MagicMock,
    mock_distribute_remaining_items: MagicMock,
    mock_create_player_pool: MagicMock,
    mock_validate_item_pool_size: MagicMock,
    mocker,
):
    # Setup
    filler_result = MagicMock()
    mock_run_filler: AsyncMock = mocker.patch(
        "randovania.generator.generator.run_filler",
        new_callable=AsyncMock,
        return_value=filler_result)
    mock_dock_weakness_distributor: AsyncMock = mocker.patch(
        "randovania.generator.dock_weakness_distributor.distribute_post_fill_weaknesses",
        new_callable=AsyncMock,
        return_value=filler_result)
    mock_distribute_remaining_items.return_value = filler_result

    num_players = 1
    rng = mock_random.return_value
    status_update: Union[MagicMock, Callable[[str], None]] = MagicMock()
    player_pools = [MagicMock() for _ in range(num_players)]
    presets = [MagicMock() for _ in range(num_players)]

    mock_create_player_pool.side_effect = player_pools

    generator_parameters = MagicMock()
    generator_parameters.get_preset.side_effect = lambda i: presets[i]

    # Run
    result = await generator._create_description(generator_parameters,
                                                 status_update, 0)

    # Assert
    mock_random.assert_called_once_with(generator_parameters.as_bytes)
    mock_create_player_pool.assert_has_calls([
        call(rng, presets[i].configuration, i, num_players)
        for i in range(num_players)
    ])
    mock_validate_item_pool_size.assert_has_calls([
        call(player_pools[i].pickups, player_pools[i].game,
             player_pools[i].configuration) for i in range(num_players)
    ])
    mock_run_filler.assert_awaited_once_with(
        rng, [player_pools[i] for i in range(num_players)], status_update)
    mock_distribute_remaining_items.assert_called_once_with(rng, filler_result)
    mock_dock_weakness_distributor.assert_called_once_with(
        rng, filler_result, status_update)

    assert result == LayoutDescription.create_new(
        generator_parameters=generator_parameters,
        all_patches={},
        item_order=filler_result.action_log,
    )
コード例 #27
0
def test_round_trip_default(permalink: Permalink,
                            item_locations: Dict[str, Dict[str, str]],
                            solver_path: Tuple[SolverPath, ...]):

    game = data_reader.decode_data(permalink.layout_configuration.game_data)
    original = LayoutDescription(
        version=randovania.VERSION,
        permalink=permalink,
        patches=GamePatches(
            _item_locations_to_pickup_assignment(game, item_locations),
            claris_randomizer.elevator_connections_for_seed_number(
                permalink.seed_number), {}, {}, (), game.starting_location),
        solver_path=solver_path,
    )

    # Run
    decoded = LayoutDescription.from_json_dict(original.as_json)

    # Assert
    assert decoded == original
コード例 #28
0
    def __init__(self, json_path: Path):
        super().__init__()
        self.setupUi(self)
        set_default_window_icon(self)
        self.layout_description = LayoutDescription.from_file(json_path)

        # Keep the Layout Description visualizer ready, but invisible.
        self._create_pickup_spoilers()

        # And update
        self.update_layout_description(self.layout_description)
コード例 #29
0
def generate_list(permalink: Permalink,
                  status_update: Optional[Callable[[str], None]],
                  timeout: Optional[int] = 120) -> LayoutDescription:
    if status_update is None:
        status_update = id

    data = permalink.layout_configuration.game_data

    create_patches_params = {
        "permalink": permalink,
        "game": data_reader.decode_data(data, False),
        "status_update": status_update
    }
    resolver_game = data_reader.decode_data(data)

    def create_failure(message: str):
        return GenerationFailure(message, permalink=permalink)

    new_patches = None
    final_state_by_resolve = None

    with multiprocessing.dummy.Pool(1) as dummy_pool:
        patches_async = dummy_pool.apply_async(func=_create_patches,
                                               kwds=create_patches_params)
        try:
            new_patches = patches_async.get(timeout)
        except multiprocessing.TimeoutError:
            raise create_failure("Timeout reached when generating patches.")

        resolve_params = {
            "configuration": permalink.layout_configuration,
            "game": resolver_game,
            "patches": new_patches,
            "status_update": status_update,
        }
        final_state_async = dummy_pool.apply_async(func=resolver.resolve,
                                                   kwds=resolve_params)
        try:
            final_state_by_resolve = final_state_async.get(60)
        except multiprocessing.TimeoutError:
            raise create_failure("Timeout reached when validating possibility")

    if final_state_by_resolve is None:
        # Why is final_state_by_distribution not OK?
        raise create_failure(
            "Generated seed was considered impossible by the solver")
    else:
        solver_path = _state_to_solver_path(final_state_by_resolve,
                                            resolver_game)

    return LayoutDescription(permalink=permalink,
                             version=VERSION,
                             patches=new_patches,
                             solver_path=solver_path)
コード例 #30
0
def apply_layout(
    description: LayoutDescription,
    players_config: PlayersConfiguration,
    cosmetic_patches: CosmeticPatches,
    backup_files_path: Optional[Path],
    progress_update: ProgressUpdateCallable,
    game_root: Path,
):
    """
    Applies the modifications listed in the given LayoutDescription to the game in game_root.
    :param description:
    :param players_config:
    :param cosmetic_patches:
    :param game_root:
    :param backup_files_path: Path to use as pak backup, to remove/add menu mod.
    :param progress_update:
    :return:
    """

    patcher_configuration = description.permalink.get_preset(
        players_config.player_index).patcher_configuration

    status_update = status_update_lib.create_progress_update_from_successive_messages(
        progress_update, 400 if patcher_configuration.menu_mod else 100)

    _ensure_no_menu_mod(game_root, backup_files_path, status_update)
    if backup_files_path is not None:
        _create_pak_backups(game_root, backup_files_path, status_update)
    description.save_to_file(
        game_root.joinpath("files",
                           f"randovania.{description.file_extension()}"))

    _modern_api(game_root, status_update, description, players_config,
                cosmetic_patches)
    dol_patcher.apply_patches(
        game_root, description.all_patches[players_config.player_index],
        cosmetic_patches)

    if patcher_configuration.menu_mod:
        _add_menu_mod_to_files(game_root, status_update)