From 42c9da2d25c9a70beb23215499b90403eb22bda2 Mon Sep 17 00:00:00 2001 From: shenjackyuanjie <3695888@qq.com> Date: Tue, 8 Feb 2022 17:15:09 +0800 Subject: [PATCH] add some --- Difficult_Rocket/__init__.py | 4 +- Difficult_Rocket/api/new_thread.py | 1 + Difficult_Rocket/api/serializer.py | 179 +++++++++++++++++++++++++++ Difficult_Rocket/command/tree.py | 4 +- Difficult_Rocket/mods/__init__.py | 60 +++++++++ Difficult_Rocket/mods/client.py | 34 +++++ docs/howto/client.md | 4 + libs/pyglet/graphics/vertexdomain.py | 8 +- 8 files changed, 286 insertions(+), 8 deletions(-) create mode 100644 Difficult_Rocket/api/serializer.py create mode 100644 Difficult_Rocket/mods/__init__.py create mode 100644 Difficult_Rocket/mods/client.py create mode 100644 docs/howto/client.md diff --git a/Difficult_Rocket/__init__.py b/Difficult_Rocket/__init__.py index d276276..adc7aa4 100644 --- a/Difficult_Rocket/__init__.py +++ b/Difficult_Rocket/__init__.py @@ -11,8 +11,8 @@ github: @shenjackyuanjie gitee: @shenjackyuanjie """ -version = '0.6.2' -__version__ = version +game_version = '0.6.2' +__version__ = game_version playing = False diff --git a/Difficult_Rocket/api/new_thread.py b/Difficult_Rocket/api/new_thread.py index 38c6611..27badac 100644 --- a/Difficult_Rocket/api/new_thread.py +++ b/Difficult_Rocket/api/new_thread.py @@ -3,6 +3,7 @@ # Copyright © 2021-2022 by shenjackyuanjie # All rights reserved # ------------------------------- +# 本文件以 GNU Lesser General Public License v3.0(GNU LGPL v3) 开源协议进行授权 (谢谢狐狸写出这么好的MCDR) import functools import inspect diff --git a/Difficult_Rocket/api/serializer.py b/Difficult_Rocket/api/serializer.py new file mode 100644 index 0000000..40f7f03 --- /dev/null +++ b/Difficult_Rocket/api/serializer.py @@ -0,0 +1,179 @@ +# ------------------------------- +# Difficult Rocket +# Copyright © 2021-2022 by shenjackyuanjie +# All rights reserved +# ------------------------------- +# 本文件以 GNU Lesser General Public License v3.0(GNU LGPL v3) 开源协议进行授权 (谢谢狐狸写出这么好的MCDR) +# 顺便说一句,我把所有的tab都改成了空格,因为我觉得空格比tab更好看(草,后半句是github copilot自动填充的) + +import copy +from abc import ABC +from enum import EnumMeta +from threading import Lock +from typing import Union, TypeVar, List, Dict, Type, get_type_hints, Any + +""" +This part of code come from MCDReforged(https://github.com/Fallen-Breath/MCDReforged) +Very thanks to Fallen_Breath and other coder who helped MCDR worked better +GNU Lesser General Public License v3.0(GNU LGPL v3) +(have some changes) +""" + +__all__ = [ + 'serialize', + 'deserialize', + 'Serializable' +] + +T = TypeVar('T') + + +def _get_type_hints(cls: Type): + try: + return get_type_hints(cls) + except: + return get_type_hints(cls, globalns={}) + + +def _get_origin(cls: Type): + return getattr(cls, '__origin__', None) + + +def _get_args(cls: Type) -> tuple: + return getattr(cls, '__args__', ()) + + +def serialize(obj) -> Union[None, int, float, str, list, dict]: + if type(obj) in (type(None), int, float, str, bool): + return obj + elif isinstance(obj, list) or isinstance(obj, tuple): + return list(map(serialize, obj)) + elif isinstance(obj, dict): + return dict(map(lambda t: (t[0], serialize(t[1])), obj.items())) + elif isinstance(obj.__class__, EnumMeta): + return obj.name + try: + attr_dict = vars(obj).copy() + # don't serialize protected fields + for attr_name in list(attr_dict.keys()): + if attr_name.startswith('_'): + attr_dict.pop(attr_name) + except: + raise TypeError('Unsupported input type {}'.format(type(obj))) from None + else: + return serialize(attr_dict) + + +_BASIC_CLASSES = (type(None), bool, int, float, str, list, dict) + + +def deserialize(data, cls: Type[T], *, error_at_missing=False, error_at_redundancy=False) -> T: + # in case None instead of NoneType is passed + if cls is None: + cls = type(None) + # if its type is Any, then simply return the data + if cls is Any: + return data + # Union + # Unpack Union first since the target class is not confirmed yet + elif _get_origin(cls) == Union: + for possible_cls in _get_args(cls): + try: + return deserialize(data, possible_cls, error_at_missing=error_at_missing, error_at_redundancy=error_at_redundancy) + except (TypeError, ValueError): + pass + raise TypeError('Data in type {} cannot match any candidate of target class {}'.format(type(data), cls)) + # Element (None, int, float, str, list, dict) + # For list and dict, since it doesn't have any type hint, we choose to simply return the data + elif cls in _BASIC_CLASSES and type(data) is cls: + return data + # float thing + elif cls is float and isinstance(data, int): + return float(data) + # List + elif _get_origin(cls) == List[int].__origin__ and isinstance(data, list): + element_type = _get_args(cls)[0] + return list(map(lambda e: deserialize(e, element_type, error_at_missing=error_at_missing, error_at_redundancy=error_at_redundancy), data)) + # Dict + elif _get_origin(cls) == Dict[int, int].__origin__ and isinstance(data, dict): + key_type = _get_args(cls)[0] + val_type = _get_args(cls)[1] + instance = {} + for key, value in data.items(): + deserialized_key = deserialize(key, key_type, error_at_missing=error_at_missing, error_at_redundancy=error_at_redundancy) + deserialized_value = deserialize(value, val_type, error_at_missing=error_at_missing, error_at_redundancy=error_at_redundancy) + instance[deserialized_key] = deserialized_value + return instance + # Enum + elif isinstance(cls, EnumMeta) and isinstance(data, str): + return cls[data] + # Object + elif cls not in _BASIC_CLASSES and isinstance(cls, type) and isinstance(data, dict): + try: + result = cls() + except: + raise TypeError('Failed to construct instance of class {}'.format(type(cls))) + input_key_set = set(data.keys()) + for attr_name, attr_type in _get_type_hints(cls).items(): + if not attr_name.startswith('_'): + if attr_name in data: + result.__setattr__(attr_name, deserialize(data[attr_name], attr_type, error_at_missing=error_at_missing, + error_at_redundancy=error_at_redundancy)) + input_key_set.remove(attr_name) + elif error_at_missing: + raise ValueError('Missing attribute {} for class {} in input object {}'.format(attr_name, cls, data)) + elif hasattr(cls, attr_name): + result.__setattr__(attr_name, copy.copy(getattr(cls, attr_name))) + if error_at_redundancy and len(input_key_set) > 0: + raise ValueError('Redundancy attributes {} for class {} in input object {}'.format(input_key_set, cls, data)) + if isinstance(result, Serializable): + result.on_deserialization() + return result + else: + raise TypeError('Unsupported input type: expected class {} but found data with class {}'.format(cls, type(data))) + + +class Serializable(ABC): + __annotations_cache: dict = None + __annotations_lock = Lock() + + def __init__(self, **kwargs): + for key in kwargs.keys(): + if key not in self.get_annotations_fields(): + raise KeyError('Unknown key received in __init__ of class {}: {}'.format(self.__class__, key)) + vars(self).update(kwargs) + + @classmethod + def __get_annotation_dict(cls) -> dict: + public_fields = {} + for attr_name, attr_type in _get_type_hints(cls).items(): + if not attr_name.startswith('_'): + public_fields[attr_name] = attr_type + return public_fields + + @classmethod + def get_annotations_fields(cls) -> Dict[str, Type]: + with cls.__annotations_lock: + if cls.__annotations_cache is None: + cls.__annotations_cache = cls.__get_annotation_dict() + return cls.__annotations_cache + + def serialize(self) -> dict: + return serialize(self) + + @classmethod + def deserialize(cls, data: dict, **kwargs): + return deserialize(data, cls, **kwargs) + + def update_from(self, data: dict): + vars(self).update(vars(self.deserialize(data))) + + @classmethod + def get_default(cls): + return cls.deserialize({}) + + def on_deserialization(self): + """ + Invoked after being deserialized + """ + pass diff --git a/Difficult_Rocket/command/tree.py b/Difficult_Rocket/command/tree.py index 0a52068..20fc3e8 100644 --- a/Difficult_Rocket/command/tree.py +++ b/Difficult_Rocket/command/tree.py @@ -11,14 +11,14 @@ github: @shenjackyuanjie gitee: @shenjackyuanjie """ -from Difficult_Rocket import version +from Difficult_Rocket import game_version from Difficult_Rocket.command import line command_tree = { 'name': 'DR-root', - 'version': version, + 'version': game_version, 'information': 'DR这一部分的代码还TM是复制我之前写的屑Census', 'commands': { 'info': '啊啊啊啊', diff --git a/Difficult_Rocket/mods/__init__.py b/Difficult_Rocket/mods/__init__.py new file mode 100644 index 0000000..82858f2 --- /dev/null +++ b/Difficult_Rocket/mods/__init__.py @@ -0,0 +1,60 @@ +# ------------------------------- +# Difficult Rocket +# Copyright © 2021-2022 by shenjackyuanjie +# All rights reserved +# ------------------------------- + +""" +writen by shenjackyuanjie +mail: 3695888@qq.com +github: @shenjackyuanjie +gitee: @shenjackyuanjie +""" + +from typing import Tuple + +from Difficult_Rocket import game_version +from Difficult_Rocket.api.serializer import Serializable + +""" +mod系统参数 +""" +MOD_loader_version = "0.0.1" # mod系统版本 版本号遵守semver2.0.0 + + +""" +加载mod时会更改的参数 +这里的只是范例,实际加载时会根据mod配置修改 +""" + + +class MODInfo(Serializable): + """ + 加载mod时候的参数 + """ + """基本信息""" + name: str # mod名称 + version: str # mod版本 + dependencies: list = [] # mod依赖 + + """作者、描述""" + writer: str # 作者 + description: str = "" # 描述 + + """版本兼容信息""" + write_version: str # mod编写版本 + compatible_version: Tuple[str, str] = (game_version, game_version) # mod兼容版本 + # 第一个是最低兼容版本,第二个是最高兼容版本 + # 例如: ("1.0.0", "1.1.0") + + +MOD_info = MODInfo( + name="Difficult_Rocket", + version="0.0.1", + writer="shenjackyuanjie" +) + +""" +一些重置用函数 +""" + diff --git a/Difficult_Rocket/mods/client.py b/Difficult_Rocket/mods/client.py new file mode 100644 index 0000000..fc29504 --- /dev/null +++ b/Difficult_Rocket/mods/client.py @@ -0,0 +1,34 @@ +# ------------------------------- +# Difficult Rocket +# Copyright © 2021-2022 by shenjackyuanjie +# All rights reserved +# ------------------------------- + +""" +writen by shenjackyuanjie +mail: 3695888@qq.com +github: @shenjackyuanjie +gitee: @shenjackyuanjie +""" + +""" +这里是所有客户端 mod 加载器的装饰器实现 +可以实现类似 + +from Difficult_Rocket.mods.client import KeyBinding + + +@KeyBinding() + +""" + + +def KeyBinding(func): + """ + 客户端键盘事件传递装饰器 + """ + + def wrapper(*args, **kwargs): + func(*args, **kwargs) + + return wrapper diff --git a/docs/howto/client.md b/docs/howto/client.md new file mode 100644 index 0000000..e1f9017 --- /dev/null +++ b/docs/howto/client.md @@ -0,0 +1,4 @@ +# 设计文档 + +20220208 + diff --git a/libs/pyglet/graphics/vertexdomain.py b/libs/pyglet/graphics/vertexdomain.py index 59fde1c..61618dc 100644 --- a/libs/pyglet/graphics/vertexdomain.py +++ b/libs/pyglet/graphics/vertexdomain.py @@ -389,10 +389,10 @@ class VertexList: """dynamic access to vertex attributes, for backwards compatibility. """ domain = self.domain - if self._cache_versions.get(name, None) != domain.version: + if self._cache_versions.get(name, None) != domain.game_version: attribute = domain.attribute_names[name] self._caches[name] = attribute.get_region(attribute.buffer, self.start, self.count) - self._cache_versions[name] = domain.version + self._cache_versions[name] = domain.game_version region = self._caches[name] region.invalidate() @@ -638,10 +638,10 @@ class IndexedVertexList(VertexList): @property def indices(self): """Array of index data.""" - if self._indices_cache_version != self.domain.version: + if self._indices_cache_version != self.domain.game_version: domain = self.domain self._indices_cache = domain.get_index_region(self.index_start, self.index_count) - self._indices_cache_version = domain.version + self._indices_cache_version = domain.game_version region = self._indices_cache region.invalidate()