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()))
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)
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()
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
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)
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
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)
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)
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")
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})
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)
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()
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(), )
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
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()
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
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, )
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_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
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
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
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)
def test_decode_invalid(invalid: str): with pytest.raises(ValueError): Permalink.from_str(invalid)
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())