mcdreforged.minecraft.rtext.click_event 源代码

import dataclasses
import json
from abc import ABC, abstractmethod
from typing import Optional, Type, TypeVar, Generic, Union, ClassVar

from typing_extensions import override, Self, final

from mcdreforged.minecraft.rtext._registry import RRegistry, NamedObject
from mcdreforged.minecraft.rtext.schema import RTextJsonFormat
from mcdreforged.utils import class_utils


class __RClickActionMeta(RRegistry['RAction']):
	pass


_RCE_co = TypeVar('_RCE_co', bound='RClickEvent', covariant=True)


[文档] class RClickAction(NamedObject, ABC, Generic[_RCE_co], metaclass=__RClickActionMeta): """ Minecraft text click event actions .. versionadded:: v2.15.0 .. note:: For versions before v2.15.0, use :attr:`RAction` instead. You can stick to :attr:`RAction` for maximum compatibility """ suggest_command: ClassVar['RClickAction[RClickSuggestCommand]'] """Fill the chat bar with given text""" run_command: ClassVar['RClickAction[RClickRunCommand]'] """ Run the given text as command (Minecraft <1.19.1) If the given text doesn't start with ``"/"``, the given text will be considered as a chat message and sent to the server, so it can be used to automatically execute MCDR command after the player click the decorated text .. attention:: In vanilla Minecraft >=1.19.1, only strings starting with ``"/"``, i.e. command strings, can be used as the text value of :attr:`run_command` action For other strings that don't start with ``"/"``, the client will reject to send the chat message See `Issue #203 <https://github.com/MCDReforged/MCDReforged/issues/203>`__ """ open_url: ClassVar['RClickAction[RClickOpenUrl]'] """Open given url""" open_file: ClassVar['RClickAction[RClickOpenFile]'] """ Open file from given path .. note:: Actually vanilla Minecraft doesn't allow texts sent by command contain :attr:`open_file` actions, so don't be surprised if this :attr:`open_file` doesn't work """ copy_to_clipboard: ClassVar['RClickAction[RClickCopyToClipboard]'] """ Copy given text to clipboard .. note:: Available in Minecraft 1.15+ """ change_page: ClassVar['RClickAction[RClickChangePage]'] """ Change to the specified page of the current book .. note:: Only work in written books """ show_dialog: ClassVar['RClickAction[RClickShowDialog]'] """ Open specified dialog .. note:: Available in Minecraft 1.21.6+ .. versionadded:: v2.15.0 """ custom: ClassVar['RClickAction[RClickCustom]'] """ Send a custom event to the server, which has no effect in vanilla servers .. note:: Available in Minecraft 1.21.6+ .. versionadded:: v2.15.0 """ @property @abstractmethod def event_class(self) -> Type[_RCE_co]: raise NotImplementedError()
# The historical alias RAction = RClickAction """ Alias of :class:`RClickAction` .. versionadded:: v1.0.0 """ class _RClickActionImpl(RClickAction): def __init__(self, name: str, event_class: Type['RClickEvent']): self.__name = name self.__event_class = event_class @property @override def name(self) -> str: return self.__name @property @override def event_class(self) -> Type['RClickEvent']: return self.__event_class def __repr__(self) -> str: return class_utils.represent(self, {'action': self.name, 'class': self.__event_class})
[文档] @dataclasses.dataclass(frozen=True) class RClickEvent(ABC): """ An abstract base class of Minecraft click event component .. versionadded:: v2.15.0 """ @property @abstractmethod def action(self) -> RClickAction: """ Return the click action type of this component """ raise NotImplementedError()
[文档] @final def to_json_object(self, json_format: RTextJsonFormat) -> dict: """ Serialize itself into a json dict :param json_format: The target json format """ data = self._to_json_object(json_format) data.pop('action', None) return {'action': self.action.name, **data}
@abstractmethod def _to_json_object(self, json_format: RTextJsonFormat) -> dict: raise NotImplementedError()
[文档] @classmethod @final def from_json_object(cls, click_event: dict, json_format: RTextJsonFormat) -> 'RClickEvent': """ Deserialize a json dict to a click event component :param click_event: The json dict to deserialize from :param json_format: The json format of the provided data """ action: str = class_utils.check_type(click_event['action'], str) for rca in RClickAction: if rca.name == action: event_class: Type[RClickEvent] = rca.event_class return event_class._from_json_object(click_event, json_format) raise ValueError(f'Unknown click event action {action!r}')
@classmethod @abstractmethod def _from_json_object(cls, click_event: dict, json_format: RTextJsonFormat) -> Self: raise NotImplementedError()
_JsonValueType = TypeVar('_JsonValueType')
[文档] class RClickEventSingleValue(RClickEvent, ABC, Generic[_JsonValueType]): """ An abstract base class for those click event components that contain only 1 value .. seealso: :meth:`RTextBase.set_click_event(action, value) <mcdreforged.minecraft.rtext.text.RTextBase.set_click_event>` .. versionadded:: v2.15.0 """ @override @final def _to_json_object(self, json_format: RTextJsonFormat) -> dict: return {self._get_json_key(json_format): self.get_json_value(json_format)} @classmethod @override @final def _from_json_object(cls, click_event: dict, json_format: RTextJsonFormat) -> Self: return cls.from_json_value(click_event[cls._get_json_key(json_format)]) @classmethod @abstractmethod def _get_json_key(cls, json_format: RTextJsonFormat) -> str: raise NotImplementedError() @abstractmethod def get_json_value(self, json_format: RTextJsonFormat) -> _JsonValueType: raise NotImplementedError() @classmethod @abstractmethod def from_json_value(cls, v: _JsonValueType) -> Self: raise NotImplementedError()
[文档] @dataclasses.dataclass(frozen=True) class RClickSuggestCommand(RClickEventSingleValue[str]): """ The click event component for :attr:`RClickAction.suggest_command` action .. versionadded:: v2.15.0 """ command: str """The command to suggest""" @property @override def action(self) -> RClickAction: return RClickAction.suggest_command @classmethod @override def _get_json_key(cls, json_format: RTextJsonFormat) -> str: return 'command' if json_format == RTextJsonFormat.V_1_21_5 else 'value' @override def get_json_value(self, json_format: RTextJsonFormat) -> str: return self.command @classmethod @override def from_json_value(cls, s: str) -> Self: return cls(command=class_utils.check_type(s, str))
[文档] @dataclasses.dataclass(frozen=True) class RClickRunCommand(RClickEventSingleValue[str]): """ The click event component for :attr:`RClickAction.run_command` action .. versionadded:: v2.15.0 """ command: str """The command to run""" @property @override def action(self) -> RClickAction: return RClickAction.run_command @classmethod @override def _get_json_key(cls, json_format: RTextJsonFormat) -> str: return 'command' if json_format == RTextJsonFormat.V_1_21_5 else 'value' @override def get_json_value(self, json_format: RTextJsonFormat) -> str: return self.command @classmethod @override def from_json_value(cls, s: str) -> Self: return cls(command=class_utils.check_type(s, str))
[文档] @dataclasses.dataclass(frozen=True) class RClickOpenUrl(RClickEventSingleValue[str]): """ The click event component for :attr:`RClickAction.open_url` action .. versionadded:: v2.15.0 """ url: str """The url to open""" @property @override def action(self) -> RClickAction: return RClickAction.open_url @classmethod @override def _get_json_key(cls, json_format: RTextJsonFormat) -> str: return 'url' if json_format == RTextJsonFormat.V_1_21_5 else 'value' @override def get_json_value(self, json_format: RTextJsonFormat) -> str: return self.url @classmethod @override def from_json_value(cls, s: str) -> Self: return cls(url=class_utils.check_type(s, str))
[文档] @dataclasses.dataclass(frozen=True) class RClickOpenFile(RClickEventSingleValue[str]): """ The click event component for :attr:`RClickAction.open_file` action .. versionadded:: v2.15.0 """ path: str """The file path to open""" @property @override def action(self) -> RClickAction: return RClickAction.open_file @classmethod @override def _get_json_key(cls, json_format: RTextJsonFormat) -> str: return 'path' if json_format == RTextJsonFormat.V_1_21_5 else 'value' @override def get_json_value(self, json_format: RTextJsonFormat) -> str: return self.path @classmethod @override def from_json_value(cls, s: str) -> Self: return cls(path=class_utils.check_type(s, str))
[文档] @dataclasses.dataclass(frozen=True) class RClickCopyToClipboard(RClickEventSingleValue[str]): """ The click event component for :attr:`RClickAction.copy_to_clipboard` action .. versionadded:: v2.15.0 """ value: str """The content to copy to clipboard""" @property @override def action(self) -> RClickAction: return RClickAction.copy_to_clipboard @classmethod @override def _get_json_key(cls, json_format: RTextJsonFormat) -> str: return 'value' @override def get_json_value(self, json_format: RTextJsonFormat) -> str: return self.value @classmethod @override def from_json_value(cls, s: str) -> Self: return cls(value=class_utils.check_type(s, str))
[文档] @dataclasses.dataclass(frozen=True) class RClickShowDialog(RClickEventSingleValue[Union[str, dict]]): """ The click event component for :attr:`RClickAction.show_dialog` action .. note:: Available in Minecraft 1.21.6+ .. versionadded:: v2.15.0 """ dialog: Union[str, dict] """ The data of the dialog. It can be either: 1. A dialog identifier, e.g., ``minecraft:server_links`` 2. A full dialog description NBT tag """ @property @override def action(self) -> RClickAction: return RClickAction.show_dialog @classmethod @override def _get_json_key(cls, json_format: RTextJsonFormat) -> str: return 'dialog' if json_format == RTextJsonFormat.V_1_21_5 else 'value' @override def get_json_value(self, json_format: RTextJsonFormat) -> Union[str, dict]: if json_format == RTextJsonFormat.V_1_7: # This was introduced in mc1.21.5 and RTextJsonFormat.V_1_7 does not support this # just return something safe for the server (in case it only expects str for the `value` field) return self.dialog if isinstance(self.dialog, str) else json.dumps(self.dialog, ensure_ascii=False) else: return self.dialog if isinstance(self.dialog, str) else self.dialog.copy() # XXX: deepcopy? @classmethod @override def from_json_value(cls, v: Union[str, dict]) -> Self: return cls(dialog=class_utils.check_type(v, (str, dict)))
# The json value type is # str, in RTextJsonFormat.V_1_7 # int, in RTextJsonFormat.V_1_21_5 # Just use `Union[int, str]` here to make it simple
[文档] @dataclasses.dataclass(frozen=True) class RClickChangePage(RClickEventSingleValue[Union[int, str]]): """ The click event component for :attr:`RClickAction.change_page` action .. versionadded:: v2.15.0 """ page: int """The page number to change to""" @property @override def action(self) -> RClickAction: return RClickAction.change_page @classmethod @override def _get_json_key(cls, json_format: RTextJsonFormat) -> str: return 'page' if json_format == RTextJsonFormat.V_1_21_5 else 'value' @override def get_json_value(self, json_format: RTextJsonFormat) -> Union[int, str]: return self.page if json_format == RTextJsonFormat.V_1_21_5 else str(self.page) @classmethod @override def from_json_value(cls, v: Union[int, str]) -> Self: return cls(page=int(v))
[文档] @dataclasses.dataclass(frozen=True) class RClickCustom(RClickEvent): """ The click event component for :attr:`RClickAction.custom` action .. note:: Available in Minecraft 1.21.6+ .. versionadded:: v2.15.0 """ id: str """The identifier""" payload: Optional[Union[str, dict]] = None """(Optional) The payload""" @property @override def action(self) -> RClickAction: return RClickAction.custom @override def _to_json_object(self, json_format: RTextJsonFormat) -> dict: # introduced in mc1.21.6, so no need for handling RTextJsonFormat < V_1_21_5 return { 'id': self.id, **({'payload': self.payload} if self.payload is not None else {}), } @classmethod @override def _from_json_object(cls, click_event: dict, json_format: RTextJsonFormat) -> Self: # introduced in mc1.21.6, so no need for handling RTextJsonFormat < V_1_21_5 return cls( id=class_utils.check_type(click_event['id'], str), payload=class_utils.check_type(click_event.get('payload'), (str, dict, None)), )
def __register_click_events(): def register(name: str, event_class: Type['RClickEvent']): # noinspection PyProtectedMember RClickAction._register_item(name, _RClickActionImpl(name, event_class)) register('suggest_command', RClickSuggestCommand) register('run_command', RClickRunCommand) register('open_url', RClickOpenUrl) register('open_file', RClickOpenFile) register('copy_to_clipboard', RClickCopyToClipboard) register('change_page', RClickChangePage) register('show_dialog', RClickShowDialog) register('custom', RClickCustom) # noinspection PyProtectedMember RClickAction._ensure_registration_done() __register_click_events()