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()
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(), )
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
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)
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)
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)
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", }
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)
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 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_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, }
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())