Beispiel #1
0
def test_decode_old_version(permalink: str, version: int):
    with pytest.raises(ValueError) as exp:
        Permalink.from_str(permalink)
    assert str(
        exp.value) == ("Given permalink has version {}, but this Randovania "
                       "support only permalink of version {}.".format(
                           version, Permalink.current_version()))
Beispiel #2
0
def distribute_command_logic(args):
    def status_update(s):
        if args.status_update:
            print(s)

    if args.permalink is not None:
        permalink = Permalink.from_str(args.permalink)
    else:
        permalink = asyncio.run(_create_permalink(args))
        print(f"Permalink: {permalink.as_base64_str}")

    if permalink.spoiler:
        debug.set_level(args.debug)

    extra_args = {}
    if args.no_retry:
        extra_args["attempts"] = 0

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

    layout_description.save_to_file(args.output_file)
Beispiel #3
0
    async def on_request_presets(self, ctx: ComponentContext):
        try:
            title = ctx.origin_message.embeds[0].title
            # Trim leading and trailing `s
            permalink = Permalink.from_str(title[1:-1])

        except (IndexError, ValueError, UnsupportedPermalink) as e:
            logging.exception("Unable to find permalink on message that sent attach_presets_of_permalink")
            permalink = None

        files = []

        if permalink is not None:
            for player, preset in enumerate(permalink.parameters.presets):
                data = io.BytesIO()
                VersionedPreset.with_preset(preset).save_to_io(data)
                data.seek(0)
                files.append(
                    discord.File(data, filename=f"Player {player + 1}'s Preset.{VersionedPreset.file_extension()}")
                )

        await ctx.edit_origin(
            components=[],
            files=files,
        )
def test_decode(mock_dictionary_byte_hash: MagicMock):
    mock_dictionary_byte_hash.return_value = 120
    # We're mocking the database hash to avoid breaking tests every single time we change the database

    # This test should break whenever we change how permalinks are created
    # When this happens, we must bump the permalink version and change the tests
    encoded = "gAAAfReLCAAC4wAAAOaANg=="

    expected = Permalink(
        seed_number=1000,
        spoiler=True,
        patcher_configuration=PatcherConfiguration(
            menu_mod=True,
            warp_to_start=False,
        ),
        layout_configuration=LayoutConfiguration.from_params(
            trick_level_configuration=TrickLevelConfiguration(
                LayoutTrickLevel.HARD),
            elevators=LayoutElevators.RANDOMIZED,
        ),
    )

    # Uncomment this line to quickly get the new encoded permalink
    # assert expected.as_str == ""
    # print(expected.as_str)

    # Run
    link = Permalink.from_str(encoded)

    # Assert
    assert link == expected
def batch_distribute_command_logic(args):
    finished_count = 0

    validate: bool = args.validate

    output_dir: Path = args.output_dir
    output_dir.mkdir(parents=True, exist_ok=True)

    base_permalink = Permalink.from_str(args.permalink)

    def callback(result):
        nonlocal finished_count
        finished_count += 1
        print("Finished seed in {} seconds. At {} of {} seeds.".format(result, finished_count, args.seed_count))

    def error_callback(e):
        nonlocal finished_count
        finished_count += 1
        print("Failed to generate seed: {}".format(e))

    with multiprocessing.Pool() as pool:
        for seed_number in range(base_permalink.seed_number, base_permalink.seed_number + args.seed_count):
            pool.apply_async(
                func=batch_distribute_helper,
                args=(base_permalink, seed_number, output_dir, validate),
                callback=callback,
                error_callback=error_callback,
            )
        pool.close()
        pool.join()
Beispiel #6
0
def test_round_trip(spoiler: bool, layout: dict, default_preset, mocker):
    # Setup
    random_uuid = uuid.uuid4()
    mocker.patch("uuid.uuid4", return_value=random_uuid)

    preset = Preset(
        name="{} Custom".format(default_preset.name),
        description="A customized preset.",
        uuid=random_uuid,
        base_preset_uuid=default_preset.uuid,
        game=default_preset.game,
        configuration=dataclasses.replace(default_preset.configuration,
                                          **layout),
    )

    link = Permalink(
        seed_number=1000,
        spoiler=spoiler,
        presets={0: preset},
    )

    # Run
    after = Permalink.from_str(link.as_base64_str)

    # Assert
    assert link == after
Beispiel #7
0
def test_decode_mock_other(encoded, num_players, mocker):
    preset = MagicMock()

    def read_values(decoder: BitPackDecoder, metadata):
        decoder.decode(100, 100)
        return preset

    mock_preset_unpack: MagicMock = mocker.patch(
        "randovania.layout.preset.Preset.bit_pack_unpack",
        side_effect=read_values)

    expected = Permalink(
        seed_number=1000,
        spoiler=True,
        presets={i: preset
                 for i in range(num_players)},
    )
    preset.bit_pack_encode.return_value = [(0, 100), (5, 100)]

    # Uncomment this line to quickly get the new encoded permalink
    # assert expected.as_base64_str == ""
    # print(expected.as_base64_str)

    # Run
    round_trip = expected.as_base64_str
    link = Permalink.from_str(encoded)

    # Assert
    assert link == expected
    assert round_trip == encoded
    mock_preset_unpack.assert_called_once_with(ANY, {"manager": ANY})
def test_decode_mock_other(
    mock_packer_unpack: MagicMock,
    mock_layout_unpack: MagicMock,
):

    encoded = "gAAAfRggLQ=="
    patcher_configuration = mock_packer_unpack.return_value
    layout_configuration = mock_layout_unpack.return_value

    expected = Permalink(
        seed_number=1000,
        spoiler=True,
        patcher_configuration=patcher_configuration,
        layout_configuration=layout_configuration,
    )
    patcher_configuration.bit_pack_encode.return_value = []
    layout_configuration.bit_pack_encode.return_value = []
    mock_layout_unpack.return_value.game_data = {"test": True}

    # Uncomment this line to quickly get the new encoded permalink
    # assert expected.as_str == ""
    # print(expected.as_str)

    # Run
    link = Permalink.from_str(encoded)
    round_trip = expected.as_str

    # Assert
    assert link == expected
    assert encoded == round_trip
    mock_packer_unpack.assert_called_once()
    mock_layout_unpack.assert_called_once()
    patcher_configuration.bit_pack_encode.assert_called_once_with({})
    layout_configuration.bit_pack_encode.assert_called_once_with({})
async def permalink_command_body(args):
    from randovania.interface_common.preset_manager import PresetManager

    preset_manager = PresetManager(None)
    versioned_preset = preset_manager.included_preset_with(
        RandovaniaGame(args.game), args.preset)
    if versioned_preset is None:
        raise ValueError(f"Unknown preset: {args.preset}")

    seed = args.seed
    if seed is None:
        seed = random.randint(0, 2**31)

    preset = versioned_preset.get_preset()
    permalink = Permalink(
        seed_number=seed,
        spoiler=not args.race,
        presets={i: preset
                 for i in range(args.player_count)})
    print(permalink.as_base64_str)

    Permalink.from_str(permalink.as_base64_str)
Beispiel #10
0
def test_round_trip(spoiler: bool, patcher: PatcherConfiguration,
                    layout: LayoutConfiguration):
    # Setup
    link = Permalink(
        seed_number=1000,
        spoiler=spoiler,
        patcher_configuration=patcher,
        layout_configuration=layout,
    )

    # Run
    after = Permalink.from_str(link.as_str)

    # Assert
    assert link == after
Beispiel #11
0
async def permalink_command_body(args):
    from randovania.interface_common import persistence
    from randovania.interface_common.preset_manager import PresetManager

    preset_manager = PresetManager(persistence.user_data_dir())
    await preset_manager.load_user_presets()

    versioned_preset = preset_manager.preset_for_name(args.preset)
    if versioned_preset is None:
        raise ValueError(f"Unknown preset: {args.preset}")

    seed = args.seed
    if seed is None:
        seed = random.randint(0, 2**31)

    preset = versioned_preset.get_preset()
    permalink = Permalink(
        seed_number=seed,
        spoiler=not args.race,
        presets={i: preset
                 for i in range(args.player_count)})
    print(permalink.as_base64_str)

    Permalink.from_str(permalink.as_base64_str)
Beispiel #12
0
def distribute_command_logic(args):
    from randovania.layout.permalink import Permalink
    from randovania.generator import generator

    async def _create_permalink(args_) -> Permalink:
        from randovania.interface_common import persistence
        from randovania.interface_common.preset_manager import PresetManager

        preset_manager = PresetManager(persistence.user_data_dir())
        await preset_manager.load_user_presets()
        preset = preset_manager.preset_for_name(args_.preset_name).get_preset()

        return Permalink(
            args_.seed_number,
            spoiler=True,
            presets={i: preset
                     for i in range(args_.player_count)},
        )

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

    if args.permalink is not None:
        permalink = Permalink.from_str(args.permalink)
    else:
        permalink = asyncio.run(_create_permalink(args))
        print(f"Permalink: {permalink.as_base64_str}")

    if permalink.spoiler:
        debug.set_level(args.debug)

    extra_args = {}
    if args.no_retry:
        extra_args["attempts"] = 0

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

    layout_description.save_to_file(args.output_file)
Beispiel #13
0
def test_round_trip(seed_hash, fake_generator_parameters, mocker):
    mock_from_bytes: MagicMock = mocker.patch(
        "randovania.layout.generator_parameters.GeneratorParameters.from_bytes",
        autospec=True,
        return_value=fake_generator_parameters)
    # Setup
    link = Permalink(parameters=fake_generator_parameters,
                     seed_hash=seed_hash,
                     randovania_version=b"0123")

    # Run
    after = Permalink.from_str(link.as_base64_str)

    # Assert
    assert link == after
    mock_from_bytes.assert_called_once_with(b"\xA0\xB0\xC0")
Beispiel #14
0
async def look_for_permalinks(message: str, channel: discord.TextChannel):
    for word in possible_links_re.finditer(message):
        try:
            permalink = Permalink.from_str(word.group(1))
        except ValueError:
            continue

        embed = discord.Embed(title=word.group(1),
                              description="{} player multiworld permalink".format(permalink.player_count))

        if permalink.player_count == 1:
            preset = permalink.get_preset(0)
            embed.description = "{} permalink for Randovania {}".format(_PRETTY_GAME_NAME[preset.game],
                                                                        randovania.VERSION)
            for category, items in preset_describer.describe(preset):
                embed.add_field(name=category, value="\n".join(items), inline=True)

        await channel.send(embed=embed)
def test_decode_mock_other(
    mock_packer_unpack: MagicMock,
    mock_layout_unpack: MagicMock,
    default_preset,
    encoded,
    num_players,
):
    patcher_configuration = mock_packer_unpack.return_value
    layout_configuration = mock_layout_unpack.return_value
    preset = Preset(
        name="{} Custom".format(default_preset.name),
        description="A customized preset.",
        base_preset_name=default_preset.name,
        patcher_configuration=patcher_configuration,
        layout_configuration=layout_configuration,
    )

    expected = Permalink(
        seed_number=1000,
        spoiler=True,
        presets={i: preset
                 for i in range(num_players)},
    )
    patcher_configuration.bit_pack_encode.return_value = []
    layout_configuration.bit_pack_encode.return_value = []
    mock_layout_unpack.return_value.game_data = {"test": True}

    # Uncomment this line to quickly get the new encoded permalink
    # assert expected.as_str == ""
    # print(expected.as_str)

    # Run
    round_trip = expected.as_str
    link = Permalink.from_str(encoded)

    # Assert
    assert link == expected
    assert encoded == round_trip
    mock_packer_unpack.assert_called_once()
    mock_layout_unpack.assert_called_once()
    patcher_configuration.bit_pack_encode.assert_called_once_with(
        {"reference": default_preset.patcher_configuration})
    layout_configuration.bit_pack_encode.assert_called_once_with(
        {"reference": default_preset.layout_configuration})
Beispiel #16
0
def distribute_command_logic(args):
    def status_update(s):
        pass

    permalink = Permalink.from_str(args.permalink)
    if permalink.spoiler:
        debug.set_level(args.debug)

    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)
Beispiel #17
0
    async def attempt_join(self):
        if not self.races:
            return

        race = self.selected_race

        permalink = None

        for word in race.info.split(" "):
            try:
                permalink = Permalink.from_str(word)
            except ValueError:
                continue

        if permalink is None:
            await async_dialog.warning(self, "Missing permalink",
                                       "Unable to get a valid Permalink from this race's info.")
        else:
            self.permalink = permalink
            return self.accept()
Beispiel #18
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(),
    )
Beispiel #19
0
def test_decode(mocker, invalid):
    games = (RandovaniaGame.METROID_PRIME_ECHOES, RandovaniaGame.METROID_DREAD)
    mock_from_bytes: MagicMock = mocker.patch(
        "randovania.layout.generator_parameters.GeneratorParameters.from_bytes",
        autospec=True)
    parameters = mock_from_bytes.return_value
    parameters.as_bytes = bitpacking.pack_results_and_bit_count(
        generator_parameters.encode_game_list(games))[0]
    if invalid:
        mock_from_bytes.side_effect = ValueError("Invalid Permalink")

    # This test should break whenever we change how permalinks are created
    # When this happens, we must bump the permalink version and change the tests
    encoded = "DQYwMTIzAkwMABlW"

    expected = Permalink(
        parameters=parameters,
        seed_hash=None,
        randovania_version=b"0123",
    )

    # Uncomment this line to quickly get the new encoded permalink
    # assert expected.as_base64_str == ""
    # print(expected.as_base64_str)

    if invalid:
        expectation = pytest.raises(UnsupportedPermalink)
    else:
        expectation = contextlib.nullcontext()

    # Run
    with expectation as exp:
        link = Permalink.from_str(encoded)

    # Assert
    mock_from_bytes.assert_called_once_with(b'F\x00')
    if invalid:
        assert exp.value.games == games
    else:
        assert link == expected
Beispiel #20
0
def batch_distribute_command_logic(args):
    from randovania.layout.permalink import Permalink

    finished_count = 0

    timeout: int = args.timeout
    validate: bool = args.validate

    output_dir: Path = args.output_dir
    output_dir.mkdir(parents=True, exist_ok=True)

    seed_count = args.seed_count
    num_digits = math.ceil(math.log10(seed_count + 1))
    number_format = "[{0:" + str(num_digits) + "d}/{1}] "
    base_permalink = Permalink.from_str(args.permalink)

    def report_update(msg: str):
        nonlocal finished_count
        finished_count += 1
        print(number_format.format(finished_count, seed_count) + msg)

    def callback(result):
        report_update(f"Finished seed in {result} seconds.")

    def error_callback(e):
        report_update(f"Failed to generate seed: {e}")

    with multiprocessing.Pool(processes=args.process_count
                              ) as pool, sleep_inhibitor.get_inhibitor():
        for seed_number in range(base_permalink.seed_number,
                                 base_permalink.seed_number + args.seed_count):
            pool.apply_async(
                func=batch_distribute_helper,
                args=(base_permalink, seed_number, timeout, validate,
                      output_dir),
                callback=callback,
                error_callback=error_callback,
            )
        pool.close()
        pool.join()
Beispiel #21
0
def test_round_trip(spoiler: bool, layout: dict, default_preset):
    # Setup
    preset = Preset(
        name="{} Custom".format(default_preset.name),
        description="A customized preset.",
        base_preset_name=default_preset.name,
        game=default_preset.game,
        configuration=dataclasses.replace(default_preset.configuration,
                                          **layout),
    )

    link = Permalink(
        seed_number=1000,
        spoiler=spoiler,
        presets={0: preset},
    )

    # Run
    after = Permalink.from_str(link.as_base64_str)

    # Assert
    assert link == after
Beispiel #22
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,
                                   )
Beispiel #23
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))
Beispiel #24
0
def test_decode(mock_dictionary_byte_hash: MagicMock, default_preset):
    mock_dictionary_byte_hash.return_value = 120
    # We're mocking the database hash to avoid breaking tests every single time we change the database

    # This test should break whenever we change how permalinks are created
    # When this happens, we must bump the permalink version and change the tests
    encoded = "wDhwJfQnUIg4"

    expected = Permalink(
        seed_number=1000,
        spoiler=True,
        presets={0: default_preset},
    )

    # Uncomment this line to quickly get the new encoded permalink
    # assert expected.as_base64_str == ""
    # print(expected.as_base64_str)

    # Run
    link = Permalink.from_str(encoded)

    # Assert
    assert link == expected
Beispiel #25
0
def test_decode_v1(mock_dictionary_byte_hash: MagicMock):
    mock_dictionary_byte_hash.return_value = 120
    # We're mocking the database hash to avoid breaking tests every single time we change the database

    # This test should break whenever we change how permalinks are created
    # When this happens, we must bump the permalink version and change the tests
    encoded = "IAAAfReNPArRHMClxLYgIID3"

    expected = Permalink(
        seed_number=1000,
        spoiler=True,
        patcher_configuration=PatcherConfiguration(
            disable_hud_popup=True,
            menu_mod=True,
            speed_up_credits=False,
        ),
        layout_configuration=LayoutConfiguration.from_params(
            trick_level=LayoutTrickLevel.HARD,
            sky_temple_keys=LayoutSkyTempleKeyMode.FULLY_RANDOM,
            elevators=LayoutRandomizedFlag.RANDOMIZED,
            pickup_quantities={
                "Missile Expansion": 10,
                "Light Suit": 9,
            },
            starting_location=StartingLocation.default(),
            starting_resources=StartingResources.default(),
        ),
    )

    # Uncomment this line to quickly get the new encoded permalink
    # assert expected.as_str == ""

    # Run
    link = Permalink.from_str(encoded)

    # Assert
    assert link == expected
Beispiel #26
0
def test_round_trip(spoiler: bool,
                    patcher: dict,
                    layout: dict,
                    preset_manager):
    # Setup
    preset = Preset(
        name="{} Custom".format(preset_manager.default_preset.name),
        description="A customized preset.",
        base_preset_name=preset_manager.default_preset.name,
        patcher_configuration=dataclasses.replace(preset_manager.default_preset.patcher_configuration, **patcher),
        layout_configuration=dataclasses.replace(preset_manager.default_preset.layout_configuration, **layout),
    )

    link = Permalink(
        seed_number=1000,
        spoiler=spoiler,
        preset=preset,
    )

    # Run
    after = Permalink.from_str(link.as_str)

    # Assert
    assert link == after
Beispiel #27
0
async def permalink_command_body(args):
    from randovania.layout.permalink import Permalink
    permalink = create_permalink(args)
    print(permalink.as_base64_str)
    Permalink.from_str(permalink.as_base64_str)
Beispiel #28
0
def test_decode_invalid(invalid: str):
    with pytest.raises(ValueError):
        Permalink.from_str(invalid)
Beispiel #29
0
def generate_from_permalink_logic(args):
    from randovania.layout.permalink import Permalink
    permalink = Permalink.from_str(args.permalink)
    common_generate_logic(args, permalink)
 def _get_permalink_from_field(self) -> Permalink:
     return Permalink.from_str(self.permalink_edit.text())