示例#1
0
    def import_preset_file(self, path: Path):
        preset = VersionedPreset.from_file_sync(path)
        try:
            preset.get_preset()
        except InvalidPreset:
            QtWidgets.QMessageBox.critical(
                self._window_manager, "Error loading preset",
                "The file at '{}' contains an invalid preset.".format(path))
            return

        existing_preset = self._window_manager.preset_manager.preset_for_uuid(
            preset.uuid)
        if existing_preset is not None:
            user_response = QtWidgets.QMessageBox.warning(
                self._window_manager, "Preset ID conflict",
                "The new preset '{}' has the same ID as existing '{}'. Do you want to overwrite it?"
                .format(
                    preset.name,
                    existing_preset.name,
                ), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
                | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel)
            if user_response == QtWidgets.QMessageBox.Cancel:
                return
            elif user_response == QtWidgets.QMessageBox.No:
                preset = VersionedPreset.with_preset(
                    dataclasses.replace(preset.get_preset(),
                                        uuid=uuid.uuid4()))

        self._add_new_preset(preset)
示例#2
0
def _get_preset(preset_json: dict) -> VersionedPreset:
    try:
        preset = VersionedPreset(preset_json)
        preset.get_preset()  # test if valid
        return preset
    except Exception as e:
        raise InvalidAction(f"invalid preset: {e}")
示例#3
0
    def add_new_preset(self, new_preset: VersionedPreset) -> bool:
        """
        Adds a new custom preset.
        :param: new_preset
        :return True, if there wasn't any preset with that name
        """
        assert new_preset.uuid not in self.included_presets
        existed_before = new_preset.uuid in self.custom_presets
        self.custom_presets[new_preset.uuid] = new_preset

        path = self._file_name_for_preset(new_preset)
        new_preset.save_to_file(path)
        _commit(f"Update preset '{new_preset.name}'", path, self._data_dir.parent, False)

        return not existed_before
示例#4
0
    def add_new_preset(self, new_preset: VersionedPreset) -> bool:
        """
        Adds a new custom preset.
        :param: new_preset
        :return True, if there wasn't any preset with that name
        """
        if self._included_preset_with_name(new_preset.name) is not None:
            raise ValueError("A default preset with name '{}' already exists.".format(new_preset.name))

        existed_before = new_preset.name in self.custom_presets
        self.custom_presets[new_preset.name] = new_preset

        path = self._file_name_for_preset(new_preset)
        new_preset.save_to_file(path)
        return not existed_before
示例#5
0
    def _on_import_preset(self):
        path = common_qt_lib.prompt_user_for_preset_file(self._window_manager,
                                                         new_file=False)
        if path is None:
            return

        preset = VersionedPreset.from_file_sync(path)
        try:
            preset.get_preset()
        except (ValueError, KeyError):
            QMessageBox.critical(
                self._window_manager, "Error loading preset",
                "The file at '{}' contains an invalid preset.".format(path))
            return

        if self._window_manager.preset_manager.preset_for_name(
                preset.name) is not None:
            user_response = QMessageBox.warning(
                self._window_manager, "Preset name conflict",
                "A preset named '{}' already exists. Do you want to overwrite it?"
                .format(preset.name), QMessageBox.Yes | QMessageBox.No,
                QMessageBox.No)
            if user_response == QMessageBox.No:
                return

        self._add_new_preset(preset)
示例#6
0
    def _add_new_preset(self, preset: VersionedPreset):
        with self._options as options:
            options.selected_preset_name = preset.name

        if self._window_manager.preset_manager.add_new_preset(preset):
            self._create_button_for_preset(preset)
        self.on_preset_changed(preset.get_preset())
示例#7
0
    def from_json_dict(cls, json_dict: dict) -> "LayoutDescription":
        json_dict = migrate_description(json_dict)

        has_spoiler = "game_modifications" in json_dict
        if not has_spoiler:
            raise ValueError(
                "Unable to read details of seed log with spoiler disabled")

        permalink = Permalink(
            seed_number=json_dict["info"]["seed"],
            spoiler=has_spoiler,
            presets={
                index: VersionedPreset(preset).get_preset()
                for index, preset in enumerate(json_dict["info"]["presets"])
            },
        )

        return LayoutDescription(
            version=json_dict["info"]["version"],
            permalink=permalink,
            all_patches=game_patches_serializer.decode(
                json_dict["game_modifications"], {
                    index: preset.configuration
                    for index, preset in permalink.presets.items()
                }),
            item_order=json_dict["item_order"],
        )
示例#8
0
 def from_json(cls, data) -> "GameSessionEntry":
     player_entries = [
         PlayerSessionEntry.from_json(player_json)
         for player_json in data["players"]
     ]
     return GameSessionEntry(
         id=data["id"],
         name=data["name"],
         presets=[
             VersionedPreset(preset_json) for preset_json in data["presets"]
         ],
         players={
             player_entry.id: player_entry
             for player_entry in player_entries
         },
         actions=[
             GameSessionAction.from_json(item) for item in data["actions"]
         ],
         seed_hash=data["seed_hash"],
         word_hash=data["word_hash"],
         spoiler=data["spoiler"],
         permalink=data["permalink"],
         state=GameSessionState(data["state"]),
         generation_in_progress=data["generation_in_progress"],
     )
示例#9
0
 async def load_user_presets(self):
     all_files = self._data_dir.glob(f"*.{VersionedPreset.file_extension()}")
     user_presets = await asyncio.gather(*[VersionedPreset.from_file(f) for f in all_files])
     for preset in user_presets:
         if preset.name in self.custom_presets or self._included_preset_with_name(preset.name) is not None:
             continue
         self.custom_presets[preset.name] = preset
    def from_json_dict(cls, json_dict: dict) -> "LayoutDescription":
        version = json_dict.get("schema_version")
        if version != cls.schema_version():
            raise RuntimeError("Unsupported log file version '{}'. Expected {}.".format(version, cls.schema_version()))

        has_spoiler = "game_modifications" in json_dict
        if not has_spoiler:
            raise ValueError("Unable to read details of seed log with spoiler disabled")

        permalink = Permalink(
            seed_number=json_dict["info"]["seed"],
            spoiler=has_spoiler,
            presets={
                index: VersionedPreset(preset).get_preset()
                for index, preset in enumerate(json_dict["info"]["presets"])
            },
        )

        return LayoutDescription(
            version=json_dict["info"]["version"],
            permalink=permalink,
            all_patches=game_patches_serializer.decode(
                json_dict["game_modifications"], {
                    index: preset.layout_configuration
                    for index, preset in permalink.presets.items()
                }),
            item_order=json_dict["item_order"],
        )
示例#11
0
    def dropEvent(self, event: QtGui.QDropEvent) -> None:
        item: QtWidgets.QTreeWidgetItem = self.itemAt(event.pos())
        if not item:
            return event.setDropAction(Qt.IgnoreAction)

        source = self.preset_for_item(self.currentItem())
        target = self.preset_for_item(item)

        if source is None or target is None:
            return event.setDropAction(Qt.IgnoreAction)

        if source.game != target.game or source.base_preset_uuid is None:
            return event.setDropAction(Qt.IgnoreAction)

        try:
            source_preset = source.get_preset()
        except InvalidPreset:
            return event.setDropAction(Qt.IgnoreAction)

        self.window_manager.preset_manager.add_new_preset(
            VersionedPreset.with_preset(
                dataclasses.replace(source_preset,
                                    base_preset_uuid=target.uuid)))

        return super().dropEvent(event)
示例#12
0
    def __init__(self, data_dir: Optional[Path]):
        self.included_presets = [VersionedPreset.from_file_sync(f) for f in read_preset_list()]

        self.custom_presets = {}
        if data_dir is not None:
            self._data_dir = data_dir.joinpath("presets")
        else:
            self._data_dir = None
示例#13
0
def test_game_session_admin_session_change_layout_description(
        clean_database, preset_manager, mock_emit_session_update, mocker,
        flask_app):
    mock_verify_no_layout_description = mocker.patch(
        "randovania.server.game_session._verify_no_layout_description",
        autospec=True)
    mock_from_json_dict: MagicMock = mocker.patch(
        "randovania.layout.layout_description.LayoutDescription.from_json_dict"
    )

    preset_as_json = json.dumps(preset_manager.default_preset.as_json)
    user1 = database.User.create(id=1234, name="The Name")
    session = database.GameSession.create(id=1,
                                          name="Debug",
                                          state=GameSessionState.SETUP,
                                          creator=user1,
                                          generation_in_progress=user1)
    database.GameSessionPreset.create(session=session,
                                      row=0,
                                      preset=preset_as_json)
    database.GameSessionPreset.create(session=session,
                                      row=1,
                                      preset=preset_as_json)
    database.GameSessionMembership.create(user=user1,
                                          session=session,
                                          row=None,
                                          admin=True)

    new_preset = preset_manager.default_preset.get_preset()
    new_preset = dataclasses.replace(new_preset,
                                     configuration=dataclasses.replace(
                                         new_preset.configuration,
                                         menu_mod=False))

    sio = MagicMock()
    sio.get_current_user.return_value = user1
    layout_description = mock_from_json_dict.return_value
    layout_description.as_json = "some_json_string"
    layout_description.permalink.player_count = 2
    layout_description.permalink.presets = {i: new_preset for i in (0, 1)}

    # Run
    with flask_app.test_request_context():
        game_session.game_session_admin_session(
            sio, 1, SessionAdminGlobalAction.CHANGE_LAYOUT_DESCRIPTION.value,
            "layout_description_json")

    # Assert
    mock_emit_session_update.assert_called_once_with(session)
    mock_verify_no_layout_description.assert_called_once_with(session)
    assert database.GameSession.get_by_id(
        1).layout_description_json == '"some_json_string"'
    assert database.GameSession.get_by_id(1).generation_in_progress is None

    new_session = database.GameSession.get_by_id(1)
    new_json = json.dumps(VersionedPreset.with_preset(new_preset).as_json)
    assert [preset.preset for preset in new_session.presets] == [new_json] * 2
示例#14
0
文件: bot.py 项目: dyceron/randovania
    async def on_message(self, message: discord.Message):
        if message.author == self.user:
            return

        if message.guild.id != self.configuration["guild"]:
            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)

        channel: discord.TextChannel = message.channel
        if self.configuration["channel_name_filter"] in channel.name:
            await look_for_permalinks(message.content, channel)
示例#15
0
def refresh_presets_command_logic(args):
    base_path = get_data_path().joinpath("presets")

    with base_path.joinpath("presets.json").open() as presets_file:
        preset_list = json.load(presets_file)["presets"]

    for preset_relative_path in preset_list:
        preset_path = base_path.joinpath(preset_relative_path["path"])
        preset = VersionedPreset.from_file_sync(preset_path)
        preset.ensure_converted()
        preset.save_to_file(preset_path)
示例#16
0
    def __init__(self, data_dir: Optional[Path]):
        self.included_presets = {
            preset.uuid: preset
            for preset in [VersionedPreset.from_file_sync(f) for f in read_preset_list()]
        }

        self.custom_presets = {}
        if data_dir is not None:
            self._data_dir = data_dir
        else:
            self._data_dir = None
示例#17
0
文件: bot.py 项目: dyceron/randovania
async def reply_for_preset(message: discord.Message,
                           versioned_preset: VersionedPreset):
    try:
        preset = versioned_preset.get_preset()
    except ValueError as e:
        logging.info("Invalid preset '{}' from {}: {}".format(
            versioned_preset.name, message.author.display_name, e))
        return

    embed = discord.Embed(title=preset.name, description=preset.description)
    for category, items in preset_describer.describe(preset):
        embed.add_field(name=category, value="\n".join(items), inline=True)
    await message.reply(embed=embed)
示例#18
0
    async def _on_customize_button(self):
        if self._logic_settings_window is not None:
            self._logic_settings_window.raise_()
            return

        editor = PresetEditor(self._current_preset_data.get_preset())
        self._logic_settings_window = LogicSettingsWindow(self._window_manager, editor)

        self._logic_settings_window.on_preset_changed(editor.create_custom_preset_with())
        editor.on_changed = lambda: self._logic_settings_window.on_preset_changed(editor.create_custom_preset_with())

        result = await async_dialog.execute_dialog(self._logic_settings_window)
        self._logic_settings_window = None

        if result == QDialog.Accepted:
            self._add_new_preset(VersionedPreset.with_preset(editor.create_custom_preset_with()))
示例#19
0
    def dragEnterEvent(self, event: QtGui.QDragEnterEvent):
        from randovania.layout.preset_migration import VersionedPreset

        valid_extensions = [
            LayoutDescription.file_extension(),
            VersionedPreset.file_extension(),
        ]
        valid_extensions_with_dot = {
            f".{extension}"
            for extension in valid_extensions
        }

        for url in event.mimeData().urls():
            ext = os.path.splitext(url.toLocalFile())[1]
            if ext in valid_extensions_with_dot:
                event.acceptProposedAction()
                return
示例#20
0
    def as_json(self) -> dict:
        result = {
            "schema_version": CURRENT_DESCRIPTION_SCHEMA_VERSION,
            "info": {
                "version": self.version,
                "permalink": self.permalink.as_base64_str,
                "seed": self.permalink.seed_number,
                "presets": [
                    VersionedPreset.with_preset(preset).as_json
                    for preset in self.permalink.presets.values()
                ],
            }
        }

        if self.permalink.spoiler:
            result["game_modifications"] = self._serialized_patches
            result["item_order"] = self.item_order

        return result
示例#21
0
def _change_layout_description(sio: ServerApp, session: GameSession,
                               description_json: Optional[dict]):
    _verify_has_admin(sio, session.id, None)
    _verify_in_setup(session)
    rows_to_update = []

    if description_json is None:
        description = None
    else:
        if session.generation_in_progress != sio.get_current_user():
            if session.generation_in_progress is None:
                raise InvalidAction(f"Not waiting for a layout.")
            else:
                raise InvalidAction(
                    f"Waiting for a layout from {session.generation_in_progress.name}."
                )

        _verify_no_layout_description(session)
        description = LayoutDescription.from_json_dict(description_json)
        permalink = description.permalink
        if permalink.player_count != session.num_rows:
            raise InvalidAction(
                f"Description is for a {permalink.player_count} players,"
                f" while the session is for {session.num_rows}.")

        for permalink_preset, preset_row in zip(permalink.presets.values(),
                                                session.presets):
            preset_row = typing.cast(GameSessionPreset, preset_row)
            if _get_preset(json.loads(
                    preset_row.preset)).get_preset() != permalink_preset:
                preset = VersionedPreset.with_preset(permalink_preset)
                if preset.game != RandovaniaGame.PRIME2:
                    raise InvalidAction("Only Prime 2 presets allowed.")
                preset_row.preset = json.dumps(preset.as_json)
                rows_to_update.append(preset_row)

    with database.db.atomic():
        for preset_row in rows_to_update:
            preset_row.save()

        session.generation_in_progress = None
        session.layout_description = description
        session.save()
示例#22
0
 def _file_name_for_preset(self, preset: VersionedPreset) -> Path:
     return self._data_dir.joinpath("{}.{}".format(preset.slug_name, preset.file_extension()))
示例#23
0
 async def load_user_presets(self):
     all_files = self._data_dir.glob(f"*.{VersionedPreset.file_extension()}")
     user_presets = await asyncio.gather(*[VersionedPreset.from_file(f) for f in all_files])
     for preset in typing.cast(List[VersionedPreset], user_presets):
         self.custom_presets[preset.uuid] = preset
示例#24
0
def test_included_presets_are_valid(f):
    VersionedPreset.from_file_sync(f).get_preset()
示例#25
0
 def all_presets(self) -> List[Preset]:
     return [
         VersionedPreset(json.loads(preset.preset)).get_preset()
         for preset in sorted(self.presets, key=lambda it: it.row)
     ]
示例#26
0
 def _on_duplicate_preset(self):
     old_preset = self._current_preset_data
     self._add_new_preset(
         VersionedPreset.with_preset(old_preset.get_preset().fork()))