Merge pull request #16 from shenjackyuanjie/feature/dr-sdk
This commit is contained in:
commit
0a7bfe0a94
15
.github/workflows/nuitka.yml
vendored
15
.github/workflows/nuitka.yml
vendored
@ -5,9 +5,7 @@ name: Build
|
||||
on:
|
||||
# 触发条件
|
||||
push:
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
workflow_dispatch:
|
||||
|
||||
# 主任务
|
||||
@ -80,18 +78,6 @@ jobs:
|
||||
Write-Output $infos >> $env:GITHUB_ENV
|
||||
python .github/workflows/get_info.py
|
||||
|
||||
# 这堆构建脚本全都是我自己写的
|
||||
- name: DR-rs build
|
||||
shell: pwsh
|
||||
run: |
|
||||
cd libs/Difficult_Rocket_rs/src
|
||||
python setup.py build
|
||||
python post_build.py
|
||||
python setup.py clean
|
||||
cd ..
|
||||
cd ..
|
||||
cd ..
|
||||
|
||||
# 还是得我自己写脚本
|
||||
- name: Build on Windows
|
||||
if: runner.os == 'Windows'
|
||||
@ -165,7 +151,6 @@ jobs:
|
||||
Copy-Item libs/fonts build\DR.dist\libs\fonts -Recurse
|
||||
# Copy-Item libs\pyglet\ build\DR.dist -Recurse
|
||||
|
||||
# Copy-Item -Path "libs\Difficult_Rocket_rs\lib\*" -Destination "build\DR.dist\libs\Difficult_Rocket_rs\lib" -Recurse
|
||||
Rename-Item build/DR.dist Difficult-Rocket
|
||||
python ./.github/workflows/post_compile.py
|
||||
|
||||
|
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@ -2,5 +2,9 @@
|
||||
"rust-analyzer.linkedProjects": [
|
||||
"libs/Difficult_Rocket_rs/src/Cargo.toml",
|
||||
"libs/pyglet_rs/src/Cargo.toml",
|
||||
]
|
||||
],
|
||||
"python.analysis.extraPaths": [
|
||||
"./libs"
|
||||
],
|
||||
"python.analysis.typeCheckingMode": "basic"
|
||||
}
|
||||
|
23
DR.py
23
DR.py
@ -7,6 +7,7 @@ import sys
|
||||
import time
|
||||
import cProfile
|
||||
import traceback
|
||||
import threading
|
||||
|
||||
from io import StringIO
|
||||
|
||||
@ -39,7 +40,7 @@ def print_path() -> None:
|
||||
# 输出一遍大部分文件位置相关信息 以后可能会加到logs里
|
||||
|
||||
|
||||
def main() -> None:
|
||||
def main() -> int:
|
||||
print(hi) # hi!
|
||||
start_time_ns = time.time_ns()
|
||||
start_time_perf_ns = time.perf_counter_ns()
|
||||
@ -57,7 +58,7 @@ def main() -> None:
|
||||
print('pyglet_rs available:', get_version_str())
|
||||
print('trying to patch pyglet_rs')
|
||||
patch_vector()
|
||||
except ImportError as e:
|
||||
except ImportError:
|
||||
print('pyglet_rs import error')
|
||||
traceback.print_exc()
|
||||
try:
|
||||
@ -82,7 +83,8 @@ def main() -> None:
|
||||
if DR_option.crash_report_test:
|
||||
raise TestError('debugging') # debug 嘛,试试crash
|
||||
except Exception as exp: # 出毛病了
|
||||
print(error_format['error.happen']) #
|
||||
# 解析错误信息
|
||||
print(error_format['error.happen'])
|
||||
error = traceback.format_exc()
|
||||
name = type(exp).__name__
|
||||
if name in error_format:
|
||||
@ -90,6 +92,7 @@ def main() -> None:
|
||||
else:
|
||||
print(error_format['error.unknown'])
|
||||
print(error)
|
||||
# 输出 crash 信息
|
||||
crash.create_crash_report(error)
|
||||
cache_steam = StringIO()
|
||||
crash.write_info_to_cache(cache_steam)
|
||||
@ -99,8 +102,20 @@ def main() -> None:
|
||||
crash.record_thread = False
|
||||
print(crash.all_thread)
|
||||
print(crash.all_process)
|
||||
# join all thread
|
||||
for thread in threading.enumerate():
|
||||
print(thread)
|
||||
if thread.name == 'MainThread' or thread == threading.main_thread() or thread == threading.current_thread():
|
||||
continue
|
||||
if thread.daemon:
|
||||
continue
|
||||
thread.join()
|
||||
# stop pyglet
|
||||
import pyglet
|
||||
pyglet.app.exit()
|
||||
print("Difficult_Rocket 已关闭")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
sys.exit(main())
|
||||
|
@ -4,7 +4,6 @@
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
import importlib
|
||||
@ -15,22 +14,19 @@ from pathlib import Path
|
||||
from typing import Optional, List, Tuple
|
||||
|
||||
from Difficult_Rocket.api.types import Options
|
||||
from Difficult_Rocket.utils.new_thread import new_thread
|
||||
|
||||
from libs.MCDR.version import Version
|
||||
|
||||
game_version = Version("0.7.2.2") # 游戏版本
|
||||
build_version = Version("1.2.1.0") # 编译文件版本(与游戏本体无关)
|
||||
Api_version = Version("0.0.2.0") # API 版本
|
||||
Api_version = Version("0.1.0.0") # API 版本
|
||||
__version__ = game_version
|
||||
|
||||
# TODO 解耦 DR SDK 与 DR_mod 和 DR_rs
|
||||
DR_rust_version = Version("0.2.6.1") # DR 的 Rust 编写部分的版本
|
||||
# 后面会移除的 DR_rs 相关信息
|
||||
# DR_rs和 DR_mod 的部分正在和 DR SDK 解耦
|
||||
|
||||
long_version: int = 14
|
||||
long_version: int = 15
|
||||
"""
|
||||
long_version: 一个用于标记内部协议的整数
|
||||
15: 完全移除 DR_rust 相关内容 解耦完成
|
||||
14: BaseScreen 的每一个函数都添加了一个参数: window: "ClientWindow"
|
||||
13: 为 DR_runtime 添加 API_version
|
||||
12: 去除 DR_runtime 的 global_logger
|
||||
@ -66,36 +62,15 @@ class _DR_option(Options):
|
||||
DR_rust_available: bool = False
|
||||
use_cProfile: bool = False
|
||||
use_local_logging: bool = False
|
||||
use_DR_rust: bool = True
|
||||
|
||||
# tests
|
||||
playing: bool = False
|
||||
debugging: bool = False
|
||||
crash_report_test: bool = True
|
||||
crash_report_test: bool = False
|
||||
|
||||
# window option
|
||||
gui_scale: float = 1.0 # default 1.0 2.0 -> 2x 3 -> 3x
|
||||
|
||||
def init(self, **kwargs):
|
||||
try:
|
||||
from libs.Difficult_Rocket_rs import test_call, get_version_str
|
||||
test_call(self)
|
||||
print(f'DR_rust available: {get_version_str()}')
|
||||
except ImportError:
|
||||
if __name__ != '__main__':
|
||||
traceback.print_exc()
|
||||
self.DR_rust_available = False
|
||||
self.use_DR_rust = self.use_DR_rust and self.DR_rust_available
|
||||
self.flush_option()
|
||||
|
||||
def test_rust(self):
|
||||
if self.DR_rust_available:
|
||||
from libs.Difficult_Rocket_rs import part_list_read_test
|
||||
part_list_read_test("./configs/PartList.xml")
|
||||
|
||||
def draw(self):
|
||||
self.DR_rust_available = True
|
||||
|
||||
@property
|
||||
def std_font_size(self) -> int:
|
||||
return round(12 * self.gui_scale)
|
||||
@ -114,9 +89,6 @@ class _DR_runtime(Options):
|
||||
DR_long_version: int = long_version # DR SDK 内部协议版本 (不要问我为什么不用 Version,我也在考虑)
|
||||
|
||||
DR_Mod_List: List[Tuple[str, Version]] = [] # DR Mod 列表 (name, version)
|
||||
|
||||
DR_Rust_version: Version = DR_rust_version # 后面要去掉的 DR_rs 版本
|
||||
DR_Rust_get_version: Optional[Version] = None # 后面也要去掉的 DR_rs 版本
|
||||
|
||||
# run status
|
||||
running: bool = False
|
||||
@ -132,15 +104,6 @@ class _DR_runtime(Options):
|
||||
language: str = 'zh-CN'
|
||||
default_language: str = 'zh-CN'
|
||||
|
||||
def init(self, **kwargs) -> None:
|
||||
with contextlib.suppress(ImportError):
|
||||
from libs.Difficult_Rocket_rs import get_version_str
|
||||
self.DR_Rust_get_version = Version(get_version_str())
|
||||
if self.DR_Rust_get_version != self.DR_Rust_version:
|
||||
relationship = 'larger' if self.DR_Rust_version > self.DR_Rust_get_version else 'smaller'
|
||||
warnings.warn(f'DR_rust builtin version is {self.DR_Rust_version} but true version is {get_version_str()}.\n'
|
||||
f'Builtin version {relationship} than true version')
|
||||
|
||||
def load_file(self) -> bool:
|
||||
with contextlib.suppress(FileNotFoundError):
|
||||
with open('./configs/main.toml', 'r', encoding='utf-8') as f:
|
||||
@ -151,33 +114,34 @@ class _DR_runtime(Options):
|
||||
return True
|
||||
return False
|
||||
|
||||
def load_mods(self) -> None:
|
||||
mod_list = self.find_mods()
|
||||
|
||||
def find_mods(self) -> List[str]:
|
||||
mods = []
|
||||
paths = Path(self.mod_path).iterdir()
|
||||
mod_path = Path(self.mod_path)
|
||||
if not mod_path.exists():
|
||||
mod_path.mkdir()
|
||||
return []
|
||||
paths = mod_path.iterdir()
|
||||
sys.path.append(self.mod_path)
|
||||
for mod_path in paths:
|
||||
try:
|
||||
if mod_path.is_dir() and mod_path.name != '__pycache__': # 处理文件夹 mod
|
||||
if importlib.util.find_spec(mod_path.name) is not None:
|
||||
module = importlib.import_module(mod_path.name)
|
||||
mods.append(mod_path.name)
|
||||
else:
|
||||
print(f'can not import mod {mod_path} because importlib can not find spec')
|
||||
elif mod_path.suffix in ('.pyz', '.zip'): # 处理压缩包 mod
|
||||
if importlib.util.find_spec(mod_path.name) is not None:
|
||||
module = importlib.import_module(mod_path.name)
|
||||
mods.append(mod_path.name)
|
||||
elif mod_path.suffix == '.pyd': # pyd 扩展 mod
|
||||
if importlib.util.find_spec(mod_path.name) is not None:
|
||||
mods.append(mod_path.name)
|
||||
elif mod_path.suffix == '.py': # 处理单文件 mod
|
||||
print(f'importing mod {mod_path=} {mod_path.stem}')
|
||||
module = importlib.import_module(mod_path.stem)
|
||||
mods.append(mod_path.stem)
|
||||
if importlib.util.find_spec(mod_path.stem) is not None:
|
||||
mods.append(mod_path.stem)
|
||||
except ImportError:
|
||||
print(f'ImportError when loading mod {mod_path}')
|
||||
traceback.print_exc()
|
||||
self.DR_Mod_List = [(mod, Version('0.0.0-unknown')) for mod in mods]
|
||||
return mods
|
||||
|
||||
|
||||
@ -185,9 +149,6 @@ DR_option = _DR_option()
|
||||
DR_runtime = _DR_runtime()
|
||||
|
||||
if DR_option.playing:
|
||||
from Difficult_Rocket.utils import new_thread
|
||||
|
||||
|
||||
def think_it(something):
|
||||
return something
|
||||
|
||||
|
@ -11,7 +11,7 @@ github: @shenjackyuanjie
|
||||
gitee: @shenjackyuanjie
|
||||
"""
|
||||
|
||||
# 单独导入的(或者就这一个有用的)
|
||||
# from .delivery import Delivery
|
||||
|
||||
# lazy之后之前全部导入的(太多了写不动__all__了
|
||||
# from Difficult_Rocket.api import screen, mod, exception
|
||||
|
||||
__all__ = ['screen', 'mod', 'exception']
|
||||
|
@ -1,28 +0,0 @@
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
"""
|
||||
writen by shenjackyuanjie
|
||||
mail: 3695888@qq.com
|
||||
github: @shenjackyuanjie
|
||||
gitee: @shenjackyuanjie
|
||||
"""
|
||||
|
||||
|
||||
class Delivery:
|
||||
def __init__(self):
|
||||
# bool
|
||||
self.using = False
|
||||
self.read = False
|
||||
self.key_board_get = False
|
||||
# dic
|
||||
self.ship_info = {}
|
||||
self.planet_system = {}
|
||||
self.main_ship_parts = {}
|
||||
self.this_planet_info = {}
|
||||
self.back_ground_element = {}
|
||||
# value
|
||||
self.back_ground_image = ''
|
@ -10,3 +10,10 @@ mail: 3695888@qq.com
|
||||
github: @shenjackyuanjie
|
||||
gitee: @shenjackyuanjie
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from Difficult_Rocket.api.exception import command, logger, main, threading, unsupport
|
||||
|
||||
__all__ = ['command', 'logger', 'main', 'threading', 'unsupport']
|
||||
|
81
Difficult_Rocket/api/mod/__init__.py
Normal file
81
Difficult_Rocket/api/mod/__init__.py
Normal file
@ -0,0 +1,81 @@
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
# system function
|
||||
from typing import Tuple, List, Optional, TypeVar, TYPE_CHECKING
|
||||
|
||||
# from libs
|
||||
from libs.MCDR.version import Version
|
||||
|
||||
# from DR
|
||||
if TYPE_CHECKING:
|
||||
from Difficult_Rocket.main import Game
|
||||
from Difficult_Rocket.client import ClientWindow
|
||||
else:
|
||||
Game = TypeVar("Game")
|
||||
ClientWindow = TypeVar("ClientWindow")
|
||||
|
||||
from Difficult_Rocket import DR_runtime
|
||||
from ..types import Options
|
||||
|
||||
RequireVersion = Tuple[Version, Version]
|
||||
# 第一个是最低兼容版本,第二个是最高兼容版本
|
||||
# 例如: ("1.0.0", "1.1.0") 表示从1.0.0版本开始兼容,到1.1.0版本结束兼容
|
||||
ForceRequire = bool
|
||||
|
||||
|
||||
class ModInfo(Options):
|
||||
"""
|
||||
加载mod时候的参数
|
||||
"""
|
||||
"""基本信息"""
|
||||
mod_id: str # mod id
|
||||
name: str # mod 名称
|
||||
version: Version # mod 版本
|
||||
|
||||
"""作者、描述"""
|
||||
writer: str # 作者
|
||||
link: str = "" # 作者链接
|
||||
description: str = "" # 描述 (务必简洁明了)
|
||||
info: str = "" # 其他信息 (可以很多很多)
|
||||
|
||||
"""版本相关信息"""
|
||||
DR_version: RequireVersion = (DR_runtime.DR_version, DR_runtime.DR_version) # DR SDK 兼容版本
|
||||
DR_Api_version: RequireVersion = (DR_runtime.API_version, DR_runtime.API_version) # DR Api版本
|
||||
Mod_Require_version: List[Tuple[str, ForceRequire, RequireVersion]] = [] # mod 依赖版本
|
||||
|
||||
"""mod 状态"""
|
||||
is_enable: bool = True # 是否启用
|
||||
is_loaded: bool = False # 是否加载
|
||||
|
||||
"""mod 配置"""
|
||||
config: Options = Options() # mod 配置存储
|
||||
old_mod: Optional["ModInfo"] = None # 旧的mod实例
|
||||
|
||||
def on_load(self, game: Game, old_self: Optional["ModInfo"] = None) -> bool:
|
||||
""" 加载时调用 """
|
||||
print(f'Mod {self.mod_id} loaded')
|
||||
return True
|
||||
|
||||
def on_client_start(self, game: Game, client: ClientWindow):
|
||||
""" 客户端启动时调用 """
|
||||
print(f'Mod {self.mod_id} client start')
|
||||
|
||||
def on_client_stop(self, game: Game, client: ClientWindow, source: str = 'window'):
|
||||
""" 客户端停止时调用 """
|
||||
print(f'Mod {self.mod_id} client stop')
|
||||
|
||||
def on_server_start(self, game: Game):
|
||||
""" 服务器启动时调用 """
|
||||
print(f'Mod {self.mod_id} server start')
|
||||
|
||||
def on_server_stop(self, game: Game):
|
||||
""" 服务器停止时调用 """
|
||||
print(f'Mod {self.mod_id} server stop')
|
||||
|
||||
def on_unload(self, game: Game):
|
||||
""" 卸载时调用 """
|
||||
print(f'Mod {self.mod_id} unloaded')
|
@ -5,15 +5,14 @@
|
||||
# -------------------------------
|
||||
|
||||
import math
|
||||
from typing import Dict, Union, List, Optional
|
||||
from typing import Dict, Union, Optional
|
||||
from dataclasses import dataclass
|
||||
|
||||
# pyglet
|
||||
# import pyglet
|
||||
from pyglet.image import load, AbstractImage
|
||||
|
||||
# Difficult Rocket
|
||||
from Difficult_Rocket.api.types import Options
|
||||
from Difficult_Rocket.utils.options import Options
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -30,7 +29,6 @@ class SR1PartData:
|
||||
flip_y: bool
|
||||
explode: bool
|
||||
textures: Optional[str] = None
|
||||
connections: Optional[List[int]] = None
|
||||
|
||||
|
||||
class SR1Textures(Options):
|
||||
@ -145,23 +143,3 @@ def xml_bool(bool_like: Union[str, int, bool, None]) -> bool:
|
||||
if isinstance(bool_like, int):
|
||||
return bool_like != 0
|
||||
return False if bool_like == '0' else bool_like.lower() != 'false'
|
||||
|
||||
#
|
||||
#
|
||||
# from xml.etree.ElementTree import Element, ElementTree
|
||||
# from defusedxml.ElementTree import parse
|
||||
#
|
||||
# part_list = parse("../../../../textures/PartList.xml")
|
||||
# part_list_root: Element = part_list.getroot()
|
||||
# print(part_list_root.tag, part_list_root.attrib)
|
||||
#
|
||||
# part_types = part_list_root.find('PartTypes')
|
||||
#
|
||||
# for x in list(part_list_root):
|
||||
# print(f'tag: {x.tag} attr: {x.attrib}')
|
||||
#
|
||||
# for part_type in list(part_list_root):
|
||||
# part_type: Element
|
||||
# print(f'\'{part_type.attrib.get("id")}\': \'{part_type.attrib.get("sprite")}\'')
|
||||
#
|
||||
#
|
||||
|
@ -4,195 +4,24 @@
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
"""
|
||||
writen by shenjackyuanjie
|
||||
mail: 3695888@qq.com
|
||||
github: @shenjackyuanjie
|
||||
gitee: @shenjackyuanjie
|
||||
"""
|
||||
from Difficult_Rocket.utils.options import Options, FontData, Fonts, \
|
||||
OptionsError, OptionNameNotDefined, OptionNotFound, \
|
||||
get_type_hints_
|
||||
|
||||
import traceback
|
||||
from dataclasses import dataclass
|
||||
from typing import get_type_hints, Type, List, Union, Dict, Any, Callable, Tuple, Optional, TYPE_CHECKING
|
||||
__all__ = [
|
||||
# main class
|
||||
'Options',
|
||||
|
||||
# from Difficult Rocket
|
||||
# data class
|
||||
'FontData',
|
||||
'Fonts',
|
||||
|
||||
__all__ = ['get_type_hints_',
|
||||
'Options',
|
||||
'Fonts',
|
||||
'FontData',
|
||||
'OptionsError',
|
||||
'OptionNotFound',
|
||||
'OptionNameNotDefined']
|
||||
# exception
|
||||
'OptionsError',
|
||||
'OptionNameNotDefined',
|
||||
'OptionNotFound',
|
||||
|
||||
# other
|
||||
'get_type_hints_',
|
||||
]
|
||||
|
||||
def get_type_hints_(cls: Type):
|
||||
try:
|
||||
return get_type_hints(cls)
|
||||
except ValueError:
|
||||
return get_type_hints(cls, globalns={})
|
||||
|
||||
|
||||
class OptionsError(Exception):
|
||||
""" option 的错误基类"""
|
||||
|
||||
|
||||
class OptionNameNotDefined(OptionsError):
|
||||
""" 向初始化的 option 里添加了一个不存在于选项里的选项 """
|
||||
|
||||
|
||||
class OptionNotFound(OptionsError):
|
||||
""" 某个选项没有找到 """
|
||||
|
||||
|
||||
class Options:
|
||||
"""
|
||||
Difficult Rocket 的游戏配置的存储基类
|
||||
"""
|
||||
name = 'Option Base'
|
||||
cached_options: Dict[str, Union[str, Any]] = {}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""
|
||||
创建一个新的 Options 的时候的配置
|
||||
如果存在 init 方法 会在设置完 kwargs 之后运行子类的 init 方法
|
||||
:param kwargs:
|
||||
"""
|
||||
self.flush_option()
|
||||
for option, value in kwargs.items():
|
||||
if option not in self.cached_options:
|
||||
raise OptionNameNotDefined(f"option: {option} with value: {value} is not defined")
|
||||
setattr(self, option, value)
|
||||
if hasattr(self, 'init'):
|
||||
self.init(**kwargs)
|
||||
if hasattr(self, 'load_file'):
|
||||
try:
|
||||
self.load_file()
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
self.flush_option()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
def init(self, **kwargs) -> None:
|
||||
""" 如果子类定义了这个函数,则会在 __init__ 之后调用这个函数 """
|
||||
|
||||
def load_file(self) -> bool:
|
||||
"""如果子类定义了这个函数,则会在 __init__ 和 init 之后再调用这个函数
|
||||
|
||||
请注意,这个函数请尽量使用 try 包裹住可能出现错误的部分
|
||||
否则会在控制台输出你的报错"""
|
||||
|
||||
def option(self) -> Dict[str, Any]:
|
||||
"""
|
||||
获取配置类的所有配置
|
||||
:return: 自己的所有配置
|
||||
"""
|
||||
values = {}
|
||||
for ann in self.__annotations__: # 获取类型注释
|
||||
values[ann] = getattr(self, ann, None)
|
||||
if values[ann] is None:
|
||||
values[ann] = self.__annotations__[ann]
|
||||
|
||||
if not hasattr(self, 'options'):
|
||||
self.options: Dict[str, Union[Callable, object]] = {}
|
||||
for option, a_fun in self.options.items(): # 获取额外内容
|
||||
values[option] = a_fun
|
||||
|
||||
for option, a_fun in values.items(): # 检查是否为 property
|
||||
if a_fun is bool and getattr(self, option, None) is not None:
|
||||
values[option] = False
|
||||
if isinstance(a_fun, property):
|
||||
try:
|
||||
values[option] = getattr(self, option)
|
||||
except AttributeError:
|
||||
raise OptionNotFound(f'Option {option} is not found in {self.name}') from None
|
||||
return values
|
||||
|
||||
def format(self, text: str) -> str:
|
||||
"""
|
||||
使用自己的选项给输入的字符串替换内容
|
||||
:param text: 想替换的内容
|
||||
:return: 替换之后的内容
|
||||
"""
|
||||
cache_option = self.flush_option()
|
||||
for option, value in cache_option.items():
|
||||
text = text.replace(f'{{{option}}}', str(value))
|
||||
return text
|
||||
|
||||
def flush_option(self) -> Dict[str, Any]:
|
||||
"""
|
||||
刷新缓存 options 的内容
|
||||
:return: 刷新过的 options
|
||||
"""
|
||||
self.cached_options = self.option()
|
||||
return self.cached_options
|
||||
|
||||
def option_with_len(self) -> List[Union[List[Tuple[str, Any, Any]], int, Any]]:
|
||||
options = self.flush_option()
|
||||
max_len_key = 1
|
||||
max_len_value = 1
|
||||
max_len_value_t = 1
|
||||
option_list = []
|
||||
for key, value in options.items():
|
||||
value_t = value if isinstance(value, Type) else type(value)
|
||||
max_len_key = max(max_len_key, len(key))
|
||||
max_len_value = max(max_len_value, len(str(value)))
|
||||
max_len_value_t = max(max_len_value_t, len(str(value_t)))
|
||||
option_list.append((key, value, value_t))
|
||||
return [option_list, max_len_key, max_len_value, max_len_value_t]
|
||||
|
||||
@classmethod
|
||||
def add_option(cls, name: str, value: Union[Callable, object]) -> Dict:
|
||||
if not hasattr(cls, 'options'):
|
||||
cls.options: Dict[str, Union[Callable, object]] = {}
|
||||
cls.options[name] = value
|
||||
return cls.options
|
||||
|
||||
@staticmethod
|
||||
def init_option(options_class: 'Options'.__class__, init_value: Optional[dict] = None) -> 'Options':
|
||||
return options_class(**init_value if init_value is not None else {})
|
||||
|
||||
|
||||
class Fonts(Options):
|
||||
# font's value
|
||||
|
||||
HOS: str = 'HarmonyOS Sans'
|
||||
HOS_S: str = 'HarmonyOS Sans SC'
|
||||
HOS_T: str = 'HarmonyOS Sans TC'
|
||||
HOS_C: str = 'HarmonyOS Sans Condensed'
|
||||
|
||||
鸿蒙字体: str = HOS
|
||||
鸿蒙简体: str = HOS_S
|
||||
鸿蒙繁体: str = HOS_T
|
||||
鸿蒙窄体: str = HOS_C
|
||||
|
||||
CC: str = 'Cascadia Code'
|
||||
CM: str = 'Cascadia Mono'
|
||||
CCPL: str = 'Cascadia Code PL'
|
||||
CMPL: str = 'Cascadia Mono PL'
|
||||
|
||||
微软等宽: str = CC
|
||||
微软等宽无线: str = CM
|
||||
微软等宽带电线: str = CCPL
|
||||
微软等宽带电线无线: str = CMPL
|
||||
|
||||
得意黑: str = '得意黑'
|
||||
# SS = smiley-sans
|
||||
SS: str = 得意黑
|
||||
|
||||
|
||||
@dataclass
|
||||
class FontData:
|
||||
""" 用于保存字体的信息 """
|
||||
font_name: str = Fonts.鸿蒙简体
|
||||
font_size: int = 13
|
||||
bold: bool = False
|
||||
italic: bool = False
|
||||
stretch: bool = False
|
||||
|
||||
def dict(self) -> Dict[str, Union[str, int, bool]]:
|
||||
return dict(font_name=self.font_name,
|
||||
font_size=self.font_size,
|
||||
bold=self.bold,
|
||||
italic=self.italic,
|
||||
stretch=self.stretch)
|
||||
|
@ -1,30 +0,0 @@
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
"""
|
||||
writen by shenjackyuanjie
|
||||
mail: 3695888@qq.com
|
||||
github: @shenjackyuanjie
|
||||
gitee: @shenjackyuanjie
|
||||
"""
|
||||
|
||||
# system function
|
||||
import ctypes
|
||||
|
||||
# Difficult_Rocket function
|
||||
|
||||
# libs function
|
||||
# from MCDR.serializer import Serializable
|
||||
|
||||
"""
|
||||
DR 内部数据传输格式类型
|
||||
"""
|
||||
|
||||
|
||||
class datas(ctypes.Structure):
|
||||
_fields_: list = [("name", ctypes.c_char_p),
|
||||
("uuid", ctypes.c_char_p),
|
||||
]
|
@ -5,21 +5,15 @@
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
"""
|
||||
writen by shenjackyuanjie
|
||||
mail: 3695888@qq.com
|
||||
github: @shenjackyuanjie
|
||||
gitee: @shenjackyuanjie
|
||||
"""
|
||||
import inspect
|
||||
# system function
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import logging
|
||||
import inspect
|
||||
import functools
|
||||
import traceback
|
||||
|
||||
from typing import List, Callable
|
||||
from typing import Callable, Dict, List, TYPE_CHECKING
|
||||
from decimal import Decimal
|
||||
|
||||
# third function
|
||||
@ -32,6 +26,8 @@ from pyglet.window import Window
|
||||
from pyglet.window import key, mouse
|
||||
|
||||
# Difficult_Rocket function
|
||||
if TYPE_CHECKING:
|
||||
from Difficult_Rocket.main import Game
|
||||
from Difficult_Rocket.utils import tools
|
||||
from Difficult_Rocket.api.types import Options
|
||||
from Difficult_Rocket.command import line, tree
|
||||
@ -43,7 +39,6 @@ from Difficult_Rocket.client.fps.fps_log import FpsLogger
|
||||
from Difficult_Rocket.client.guis.widgets import InputBox
|
||||
from Difficult_Rocket.exception.command import CommandError
|
||||
from Difficult_Rocket.exception.language import LanguageNotFound
|
||||
from Difficult_Rocket.client.render.sr1_ship import SR1ShipRender
|
||||
from Difficult_Rocket.client.screen import DRScreen, DRDEBUGScreen
|
||||
|
||||
|
||||
@ -59,7 +54,7 @@ class ClientOption(Options):
|
||||
caption: str = "Difficult Rocket v{DR_version}|DR_rs v{DR_Rust_get_version}"
|
||||
|
||||
def load_file(self) -> None:
|
||||
file = tools.load_file('./configs/main.toml')
|
||||
file: dict = tools.load_file('./configs/main.toml')
|
||||
self.fps = int(file['runtime']['fps'])
|
||||
self.width = int(file['window']['width'])
|
||||
self.height = int(file['window']['height'])
|
||||
@ -70,7 +65,7 @@ class ClientOption(Options):
|
||||
|
||||
|
||||
class Client:
|
||||
def __init__(self, net_mode='local'):
|
||||
def __init__(self, game: "Game", net_mode='local'):
|
||||
start_time = time.time_ns()
|
||||
# logging
|
||||
self.logger = logging.getLogger('client')
|
||||
@ -82,20 +77,14 @@ class Client:
|
||||
self.process_name = 'Client process'
|
||||
self.process_pid = os.getpid()
|
||||
self.net_mode = net_mode
|
||||
file_drop = bool(
|
||||
pyglet.compat_platform != 'darwin' or DR_option.pyglet_macosx_dev_test
|
||||
)
|
||||
self.window = ClientWindow(net_mode=self.net_mode, width=self.config.width, height=self.config.height,
|
||||
self.game = game
|
||||
self.window = ClientWindow(game=game, net_mode=self.net_mode,
|
||||
width=self.config.width, height=self.config.height,
|
||||
fullscreen=self.config.fullscreen, caption=self.config.caption,
|
||||
resizable=self.config.resizeable, visible=self.config.visible,
|
||||
file_drops=file_drop)
|
||||
file_drops=True)
|
||||
end_time = time.time_ns()
|
||||
self.use_time = end_time - start_time
|
||||
if DR_option.use_DR_rust:
|
||||
from libs.Difficult_Rocket_rs import read_ship_test, part_list_read_test
|
||||
# part_list_read_test()
|
||||
# read_ship_test()
|
||||
|
||||
self.logger.info(tr().client.setup.use_time().format(Decimal(self.use_time) / 1000000000))
|
||||
self.logger.debug(tr().client.setup.use_time_ns().format(self.use_time))
|
||||
|
||||
@ -119,7 +108,7 @@ def _call_screen_after(func: Callable) -> Callable:
|
||||
@functools.wraps(func)
|
||||
def warped(self: "ClientWindow", *args, **kwargs):
|
||||
result = func(self, *args, **kwargs)
|
||||
for a_screen in self.screen_list:
|
||||
for title, a_screen in self.screen_list.items():
|
||||
a_screen.window_pointer = self
|
||||
# 提前帮子窗口设置好指针
|
||||
if hasattr(a_screen, func.__name__):
|
||||
@ -136,7 +125,7 @@ def _call_screen_after(func: Callable) -> Callable:
|
||||
def _call_screen_before(func: Callable) -> Callable:
|
||||
@functools.wraps(func)
|
||||
def warped(self: "ClientWindow", *args, **kwargs):
|
||||
for a_screen in self.screen_list:
|
||||
for title, a_screen in self.screen_list.items():
|
||||
a_screen.window_pointer = self
|
||||
# 提前帮子窗口设置好指针
|
||||
if hasattr(a_screen, func.__name__):
|
||||
@ -153,7 +142,7 @@ def _call_screen_before(func: Callable) -> Callable:
|
||||
|
||||
class ClientWindow(Window):
|
||||
|
||||
def __init__(self, net_mode='local', *args, **kwargs):
|
||||
def __init__(self, game: "Game", net_mode='local', *args, **kwargs):
|
||||
"""
|
||||
|
||||
@param net_mode:
|
||||
@ -166,8 +155,10 @@ class ClientWindow(Window):
|
||||
self.logger = logging.getLogger('client')
|
||||
self.logger.info(tr().window.setup.start())
|
||||
# value
|
||||
self.game = game
|
||||
self.net_mode = net_mode
|
||||
self.run_input = False
|
||||
self.command_list: List[str] = []
|
||||
# configs
|
||||
self.main_config = tools.load_file('./configs/main.toml')
|
||||
self.game_config = tools.load_file('./configs/game.config')
|
||||
@ -181,7 +172,7 @@ class ClientWindow(Window):
|
||||
# frame
|
||||
self.frame = pyglet.gui.Frame(self, order=20)
|
||||
self.M_frame = pyglet.gui.MovableFrame(self, modifier=key.LCTRL)
|
||||
self.screen_list: List[BaseScreen] = []
|
||||
self.screen_list: Dict[str, BaseScreen] = {}
|
||||
# setup
|
||||
self.setup()
|
||||
# 命令显示
|
||||
@ -194,7 +185,7 @@ class ClientWindow(Window):
|
||||
self.set_handlers(self.input_box)
|
||||
self.input_box.enabled = True
|
||||
# 设置刷新率
|
||||
pyglet.clock.schedule_interval(self.draw_update, float(self.SPF))
|
||||
# pyglet.clock.schedule_interval(self.draw_update, float(self.SPF))
|
||||
# 完成设置后的信息输出
|
||||
self.logger.info(tr().window.os.pid_is().format(os.getpid(), os.getppid()))
|
||||
end_time = time.time_ns()
|
||||
@ -208,9 +199,9 @@ class ClientWindow(Window):
|
||||
self.set_icon(pyglet.image.load('./textures/icon.png'))
|
||||
self.load_fonts()
|
||||
# TODO 读取配置文件,加载不同的屏幕,解耦
|
||||
self.screen_list.append(DRDEBUGScreen(self))
|
||||
self.screen_list.append(DRScreen(self))
|
||||
self.screen_list.append(SR1ShipRender(self))
|
||||
self.screen_list['DR_debug'] = DRDEBUGScreen(self)
|
||||
self.screen_list['DR_main'] = DRScreen(self)
|
||||
self.game.dispatch_event('on_client_start', game=self.game, client=self)
|
||||
|
||||
def load_fonts(self) -> None:
|
||||
fonts_folder_path = self.main_config['runtime']['fonts_folder']
|
||||
@ -222,27 +213,35 @@ class ClientWindow(Window):
|
||||
self.set_icon(pyglet.image.load('./textures/icon.png'))
|
||||
self.run_input = True
|
||||
self.read_input()
|
||||
pyglet.app.event_loop.run(1 / self.main_config['runtime']['fps'])
|
||||
try:
|
||||
pyglet.app.event_loop.run(1 / self.main_config['runtime']['fps'])
|
||||
except KeyboardInterrupt:
|
||||
print("==========client stop. KeyboardInterrupt info==========")
|
||||
traceback.print_exc()
|
||||
print("==========client stop. KeyboardInterrupt info end==========")
|
||||
self.dispatch_event("on_close")
|
||||
sys.exit(0)
|
||||
|
||||
@new_thread('window read_input', daemon=True)
|
||||
def read_input(self):
|
||||
self.logger.debug('read_input start')
|
||||
while self.run_input:
|
||||
get = input(">")
|
||||
try:
|
||||
get = input(">")
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
self.run_input = False
|
||||
break
|
||||
if get in ('', ' ', '\n', '\r'):
|
||||
continue
|
||||
if get == 'stop':
|
||||
self.run_input = False
|
||||
try:
|
||||
self.on_command(line.CommandText(get))
|
||||
except CommandError:
|
||||
self.logger.error(traceback.format_exc())
|
||||
self.command_list.append(get)
|
||||
self.logger.debug('read_input end')
|
||||
|
||||
@new_thread('window save_info')
|
||||
def save_info(self):
|
||||
self.logger.info(tr().client.config.save.start())
|
||||
config_file = tools.load_file('./configs/main.toml')
|
||||
config_file: dict = tools.load_file('./configs/main.toml')
|
||||
config_file['window']['width'] = self.width
|
||||
config_file['window']['height'] = self.height
|
||||
config_file['runtime']['language'] = DR_runtime.language
|
||||
@ -253,8 +252,8 @@ class ClientWindow(Window):
|
||||
client api
|
||||
"""
|
||||
|
||||
def add_sub_screen(self, sub_screen: BaseScreen.__class__):
|
||||
self.screen_list.append(sub_screen(self))
|
||||
def add_sub_screen(self, title: str, sub_screen: type(BaseScreen)):
|
||||
self.screen_list[title] = sub_screen(self)
|
||||
|
||||
"""
|
||||
draws and some event
|
||||
@ -268,9 +267,13 @@ class ClientWindow(Window):
|
||||
|
||||
@_call_screen_after
|
||||
def on_draw(self, *dt):
|
||||
# self.logger.debug('on_draw call dt: {}'.format(dt))
|
||||
if self.command_list:
|
||||
for command in self.command_list:
|
||||
self.on_command(line.CommandText(command))
|
||||
self.command_list.pop(0)
|
||||
pyglet.gl.glClearColor(0.1, 0, 0, 0.0)
|
||||
self.clear()
|
||||
self.draw_update(float(self.SPF))
|
||||
self.draw_batch()
|
||||
|
||||
@_call_screen_after
|
||||
@ -434,6 +437,7 @@ class ClientWindow(Window):
|
||||
|
||||
@_call_screen_before
|
||||
def on_close(self, source: str = 'window') -> None:
|
||||
self.game.dispatch_event('on_close', game=self.game, client=self, source=source)
|
||||
self.logger.info(tr().window.game.stop_get().format(tr().game[source]()))
|
||||
self.logger.info(tr().window.game.stop())
|
||||
self.fps_log.check_list = False
|
||||
|
@ -15,17 +15,22 @@ import os
|
||||
import sys
|
||||
import time
|
||||
import logging
|
||||
import importlib
|
||||
import importlib.util
|
||||
import logging.config
|
||||
import multiprocessing
|
||||
|
||||
from io import StringIO
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if __name__ == '__main__': # been start will not run this
|
||||
sys.path.append('/bin/libs')
|
||||
sys.path.append('/bin')
|
||||
|
||||
from Difficult_Rocket import client, server, DR_option
|
||||
|
||||
from Difficult_Rocket import client, server, DR_option, DR_runtime
|
||||
if TYPE_CHECKING:
|
||||
from Difficult_Rocket.api.mod import ModInfo
|
||||
from Difficult_Rocket.crash import write_info_to_cache
|
||||
from Difficult_Rocket.utils import tools
|
||||
from Difficult_Rocket.utils.translate import tr
|
||||
@ -58,6 +63,9 @@ class Game:
|
||||
# version check
|
||||
self.log_env()
|
||||
self.python_version_check()
|
||||
self.loaded_mods = []
|
||||
# self.client = client.Client
|
||||
# self.server = server.Server
|
||||
self.setup()
|
||||
|
||||
def log_env(self) -> None:
|
||||
@ -66,8 +74,67 @@ class Game:
|
||||
text = cache_steam.getvalue()
|
||||
self.logger.info(text)
|
||||
|
||||
def load_mods(self) -> None:
|
||||
mods = []
|
||||
mod_path = Path(DR_runtime.mod_path)
|
||||
if not mod_path.exists():
|
||||
self.logger.info(tr().main.mod.find.faild.no_mod_folder())
|
||||
return
|
||||
# 寻找有效 mod
|
||||
paths = mod_path.iterdir()
|
||||
sys.path.append(DR_runtime.mod_path)
|
||||
for mod_path in paths:
|
||||
try:
|
||||
if mod_path.name == '__pycache__':
|
||||
continue
|
||||
self.logger.info(tr().main.mod.find.start().format(mod_path))
|
||||
if mod_path.is_dir():
|
||||
if importlib.util.find_spec(mod_path.name) is not None:
|
||||
mods.append(mod_path.name)
|
||||
else:
|
||||
self.logger.warning(tr().main.mod.load.faild.info().format(mod_path.name, tr().main.mod.find.faild.no_spec()))
|
||||
elif mod_path.suffix in ('.pyz', '.zip', '.pyd', '.py'):
|
||||
if importlib.util.find_spec(mod_path.name) is not None:
|
||||
mods.append(mod_path.name)
|
||||
except ImportError as e:
|
||||
self.logger.warning(tr().main.mod.find.faild().format(mod_path, e))
|
||||
self.logger.info(tr().main.mod.find.done())
|
||||
# 加载有效 mod
|
||||
module = []
|
||||
for mod in mods:
|
||||
try:
|
||||
self.logger.info(tr().main.mod.load.start().format(mod))
|
||||
mod_module = importlib.import_module(mod)
|
||||
if not hasattr(mod_module, "mod_class"):
|
||||
self.logger.warning(tr().main.mod.load.faild.info().format(mod, tr().main.mod.load.faild.no_mod_class()))
|
||||
del mod_module # 释放内存
|
||||
continue
|
||||
mod_class: type(ModInfo) = mod_module.mod_class
|
||||
mod_class = mod_class()
|
||||
module.append(mod_class)
|
||||
self.logger.info(tr().main.mod.load.info().format(mod_class.mod_id, mod_class.version))
|
||||
except ImportError as e:
|
||||
self.logger.warning(tr().main.mod.load.faild().format(mod, e))
|
||||
self.logger.info(tr().main.mod.load.done())
|
||||
self.loaded_mods = module
|
||||
mod_list = []
|
||||
for mod in module:
|
||||
mod_list.append((mod.mod_id, mod.version))
|
||||
# 调用 on_load
|
||||
self.dispatch_event('on_load', game=self)
|
||||
DR_runtime.DR_Mod_List = mod_list
|
||||
|
||||
def dispatch_event(self, event_name: str, *args, **kwargs) -> None:
|
||||
for mod in self.loaded_mods:
|
||||
if hasattr(mod, event_name):
|
||||
try:
|
||||
getattr(mod, event_name)(*args, **kwargs)
|
||||
except Exception as e:
|
||||
self.logger.error(tr().main.mod.event.error().format(event_name, e, mod.mod_id))
|
||||
|
||||
def setup(self) -> None:
|
||||
self.client = client.Client(net_mode='local')
|
||||
self.load_mods()
|
||||
self.client = client.Client(game=self, net_mode='local')
|
||||
self.server = server.Server(net_mode='local')
|
||||
|
||||
def python_version_check(self) -> None: # best 3.8+ and write at 3.8.10
|
||||
|
@ -4,68 +4,3 @@
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
"""
|
||||
writen by shenjackyuanjie
|
||||
mail: 3695888@qq.com
|
||||
github: @shenjackyuanjie
|
||||
gitee: @shenjackyuanjie
|
||||
"""
|
||||
|
||||
# system function
|
||||
from typing import Tuple, List
|
||||
|
||||
# from libs
|
||||
from MCDR.version import Version
|
||||
from MCDR.serializer import Serializable
|
||||
|
||||
# from DR
|
||||
from Difficult_Rocket import DR_runtime, Options
|
||||
|
||||
"""
|
||||
mod系统参数
|
||||
"""
|
||||
MOD_loader_version = "0.1.0.0" # mod系统版本 版本号遵守 semver ++
|
||||
semver_loader_version = Version(MOD_loader_version)
|
||||
|
||||
"""
|
||||
加载mod时会更改的参数
|
||||
这里的只是范例,实际加载时会根据mod配置修改
|
||||
"""
|
||||
|
||||
RequireVersion = Tuple[Version, Version]
|
||||
# 第一个是最低兼容版本,第二个是最高兼容版本
|
||||
# 例如: ("1.0.0", "1.1.0") 表示从1.0.0版本开始兼容,到1.1.0版本结束兼容
|
||||
ForceRequire = bool
|
||||
|
||||
|
||||
# TODO 完善中
|
||||
class MODInfo(Serializable):
|
||||
"""
|
||||
加载mod时候的参数
|
||||
"""
|
||||
"""基本信息"""
|
||||
name: str # mod 名称
|
||||
version: Version # mod 版本
|
||||
|
||||
"""作者、描述"""
|
||||
writer: str # 作者
|
||||
link: str = "" # 作者链接
|
||||
description: str = "" # 描述 (务必简洁明了)
|
||||
info: str = "" # 其他信息 (可以很多很多)
|
||||
|
||||
"""版本相关信息"""
|
||||
DR_version: RequireVersion = (DR_runtime.DR_version, DR_runtime.DR_version) # DR SDK 兼容版本
|
||||
DR_Api_version: RequireVersion = (DR_runtime.API_version, DR_runtime.API_version) # DR Api版本
|
||||
Mod_Require_version: List[Tuple[str, ForceRequire, RequireVersion]] = [] # mod 依赖版本
|
||||
|
||||
"""mod 状态"""
|
||||
is_enable: bool = True # 是否启用
|
||||
is_loaded: bool = False # 是否加载
|
||||
|
||||
"""mod 配置"""
|
||||
config: Options = Options() # mod 配置存储
|
||||
|
||||
|
||||
"""
|
||||
一些重置用函数
|
||||
"""
|
||||
|
@ -4,9 +4,8 @@
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
"""
|
||||
writen by shenjackyuanjie
|
||||
mail: 3695888@qq.com
|
||||
github: @shenjackyuanjie
|
||||
gitee: @shenjackyuanjie
|
||||
"""
|
||||
from Difficult_Rocket.api.screen import BaseScreen
|
||||
from Difficult_Rocket import DR_option, DR_runtime
|
||||
|
||||
|
||||
|
||||
|
@ -4,17 +4,3 @@
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
"""
|
||||
writen by shenjackyuanjie
|
||||
mail: 3695888@qq.com
|
||||
github: @shenjackyuanjie
|
||||
gitee: @shenjackyuanjie
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .new_thread import new_thread
|
||||
|
||||
__all__ = ['new_thread']
|
||||
|
||||
|
@ -7,8 +7,7 @@
|
||||
import functools
|
||||
import inspect
|
||||
import threading
|
||||
from Difficult_Rocket import crash, DR_option
|
||||
from typing import Optional, Callable
|
||||
from typing import Optional, Callable, Union, List
|
||||
|
||||
"""
|
||||
This part of code come from MCDReforged(https://github.com/Fallen-Breath/MCDReforged)
|
||||
@ -17,6 +16,15 @@ GNU Lesser General Public License v3.0(GNU LGPL v3)
|
||||
(have some changes)
|
||||
"""
|
||||
|
||||
__all__ = [
|
||||
'new_thread',
|
||||
'FunctionThread'
|
||||
]
|
||||
|
||||
|
||||
record_thread = False
|
||||
record_destination: List[Callable[['FunctionThread'], None]] = []
|
||||
|
||||
|
||||
def copy_signature(target: Callable, origin: Callable) -> Callable:
|
||||
"""
|
||||
@ -28,10 +36,13 @@ def copy_signature(target: Callable, origin: Callable) -> Callable:
|
||||
|
||||
|
||||
class FunctionThread(threading.Thread):
|
||||
"""
|
||||
A Thread subclass which is used in decorator :func:`new_thread` to wrap a synchronized function call
|
||||
"""
|
||||
__NONE = object()
|
||||
|
||||
def __init__(self, target, name, args, kwargs):
|
||||
super().__init__(target=target, args=args, kwargs=kwargs, name=name)
|
||||
def __init__(self, target, name, args, kwargs, daemon):
|
||||
super().__init__(target=target, args=args, kwargs=kwargs, name=name, daemon=daemon)
|
||||
self.__return_value = self.__NONE
|
||||
self.__error = None
|
||||
|
||||
@ -40,12 +51,33 @@ class FunctionThread(threading.Thread):
|
||||
self.__return_value = target(*args_, **kwargs_)
|
||||
except Exception as e:
|
||||
self.__error = e
|
||||
print(e)
|
||||
raise e from None
|
||||
|
||||
self._target = wrapped_target
|
||||
|
||||
def get_return_value(self, block: bool = False, timeout: Optional[float] = None):
|
||||
"""
|
||||
Get the return value of the original function
|
||||
|
||||
If an exception has occurred during the original function call, the exception will be risen again here
|
||||
|
||||
Examples::
|
||||
|
||||
>>> import time
|
||||
>>> @new_thread
|
||||
... def do_something(text: str):
|
||||
... time.sleep(1)
|
||||
... return text
|
||||
|
||||
>>> do_something('task').get_return_value(block=True)
|
||||
'task'
|
||||
|
||||
:param block: If it should join the thread before getting the return value to make sure the function invocation finishes
|
||||
:param timeout: The maximum timeout for the thread join
|
||||
:raise RuntimeError: If the thread is still alive when getting return value. Might be caused by ``block=False``
|
||||
while the thread is still running, or thread join operation times out
|
||||
:return: The return value of the original function
|
||||
"""
|
||||
if block:
|
||||
self.join(timeout)
|
||||
if self.__return_value is self.__NONE:
|
||||
@ -54,30 +86,57 @@ class FunctionThread(threading.Thread):
|
||||
raise self.__error
|
||||
return self.__return_value
|
||||
|
||||
def join(self, timeout: Optional[float] = None) -> None:
|
||||
super().join(timeout)
|
||||
|
||||
def start(self) -> None:
|
||||
super().start()
|
||||
|
||||
|
||||
def new_thread(thread_name: Optional[str or Callable] = None,
|
||||
def new_thread(arg: Optional[Union[str, Callable]] = None,
|
||||
daemon: bool = False,
|
||||
log_thread: bool = True):
|
||||
"""
|
||||
Use a new thread to execute the decorated function
|
||||
The function return value will be set to the thread instance that executes this function
|
||||
The name of the thread can be specified in parameter
|
||||
This is a one line solution to make your function executes in parallels.
|
||||
When decorated with this decorator, functions will be executed in a new daemon thread
|
||||
|
||||
This decorator only changes the return value of the function to the created ``Thread`` object.
|
||||
Beside the return value, it reserves all signatures of the decorated function,
|
||||
so you can safely use the decorated function as if there's no decorating at all
|
||||
|
||||
It's also a simple compatible upgrade method for old MCDR 0.x plugins
|
||||
|
||||
The return value of the decorated function is changed to the ``Thread`` object that executes this function
|
||||
|
||||
The decorated function has 1 extra field:
|
||||
|
||||
* ``original`` field: The original undecorated function
|
||||
|
||||
Examples::
|
||||
|
||||
>>> import time
|
||||
|
||||
>>> @new_thread('My Plugin Thread')
|
||||
... def do_something(text: str):
|
||||
... time.sleep(1)
|
||||
... print(threading.current_thread().name)
|
||||
>>> callable(do_something.original)
|
||||
True
|
||||
>>> t = do_something('foo')
|
||||
>>> isinstance(t, FunctionThread)
|
||||
True
|
||||
>>> t.join()
|
||||
My Plugin Thread
|
||||
|
||||
:param arg: A :class:`str`, the name of the thread. It's recommend to specify the thread name, so when you
|
||||
log something by ``server.logger``, a meaningful thread name will be displayed
|
||||
instead of a plain and meaningless ``Thread-3``
|
||||
:param daemon: If the thread should be a daemon thread
|
||||
:param log_thread: If the thread should be logged to callback defined in record_destination
|
||||
"""
|
||||
|
||||
def wrapper(func):
|
||||
@functools.wraps(func) # to preserve the origin function information
|
||||
def wrap(*args, **kwargs):
|
||||
thread = FunctionThread(target=func, args=args, kwargs=kwargs, name=thread_name)
|
||||
thread.daemon = daemon
|
||||
thread = FunctionThread(target=func, args=args, kwargs=kwargs, name=thread_name, daemon=daemon)
|
||||
if record_thread:
|
||||
for destination in record_destination:
|
||||
destination(thread)
|
||||
thread.start()
|
||||
if log_thread and DR_option.record_threads:
|
||||
crash.all_thread.append(thread)
|
||||
return thread
|
||||
|
||||
# bring the signature of the func to the wrap function
|
||||
@ -86,10 +145,11 @@ def new_thread(thread_name: Optional[str or Callable] = None,
|
||||
wrap.original = func # access this field to get the original function
|
||||
return wrap
|
||||
|
||||
# Directly use @on_new_thread without ending brackets case
|
||||
if isinstance(thread_name, Callable):
|
||||
this_is_a_function = thread_name
|
||||
# Directly use @new_thread without ending brackets case, e.g. @new_thread
|
||||
if isinstance(arg, Callable):
|
||||
thread_name = None
|
||||
return wrapper(this_is_a_function)
|
||||
# Use @on_new_thread with ending brackets case
|
||||
return wrapper
|
||||
return wrapper(arg)
|
||||
# Use @new_thread with ending brackets case, e.g. @new_thread('A'), @new_thread()
|
||||
else:
|
||||
thread_name = arg
|
||||
return wrapper
|
||||
|
194
Difficult_Rocket/utils/options.py
Normal file
194
Difficult_Rocket/utils/options.py
Normal file
@ -0,0 +1,194 @@
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
import traceback
|
||||
from dataclasses import dataclass
|
||||
from typing import get_type_hints, Type, List, Union, Dict, Any, Callable, Tuple, Optional, TYPE_CHECKING
|
||||
|
||||
__all__ = ['get_type_hints_',
|
||||
'Options',
|
||||
'Fonts',
|
||||
'FontData',
|
||||
'OptionsError',
|
||||
'OptionNotFound',
|
||||
'OptionNameNotDefined']
|
||||
|
||||
|
||||
def get_type_hints_(cls: Type):
|
||||
try:
|
||||
return get_type_hints(cls)
|
||||
except ValueError:
|
||||
return get_type_hints(cls, globalns={})
|
||||
|
||||
|
||||
class OptionsError(Exception):
|
||||
""" option 的错误基类"""
|
||||
|
||||
|
||||
class OptionNameNotDefined(OptionsError):
|
||||
""" 向初始化的 option 里添加了一个不存在于选项里的选项 """
|
||||
|
||||
|
||||
class OptionNotFound(OptionsError):
|
||||
""" 某个选项没有找到 """
|
||||
|
||||
|
||||
class Options:
|
||||
"""
|
||||
Difficult Rocket 的游戏配置的存储基类
|
||||
"""
|
||||
name = 'Option Base'
|
||||
cached_options: Dict[str, Union[str, Any]] = {}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""
|
||||
创建一个新的 Options 的时候的配置
|
||||
如果存在 init 方法 会在设置完 kwargs 之后运行子类的 init 方法
|
||||
:param kwargs:
|
||||
"""
|
||||
if TYPE_CHECKING:
|
||||
self.options: Dict[str, Union[Callable, object]] = {}
|
||||
self.flush_option()
|
||||
for option, value in kwargs.items():
|
||||
if option not in self.cached_options:
|
||||
raise OptionNameNotDefined(f"option: {option} with value: {value} is not defined")
|
||||
setattr(self, option, value)
|
||||
if hasattr(self, 'init'):
|
||||
self.init(**kwargs)
|
||||
if hasattr(self, 'load_file'):
|
||||
try:
|
||||
self.load_file()
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
self.flush_option()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
options: Dict[str, Union[Callable, object]] = {}
|
||||
|
||||
def init(self, **kwargs) -> None:
|
||||
""" 如果子类定义了这个函数,则会在 __init__ 之后调用这个函数 """
|
||||
|
||||
def load_file(self) -> bool:
|
||||
"""如果子类定义了这个函数,则会在 __init__ 和 init 之后再调用这个函数
|
||||
|
||||
请注意,这个函数请尽量使用 try 包裹住可能出现错误的部分
|
||||
否则会在控制台输出你的报错"""
|
||||
return True
|
||||
|
||||
def option(self) -> Dict[str, Any]:
|
||||
"""
|
||||
获取配置类的所有配置
|
||||
:return: 自己的所有配置
|
||||
"""
|
||||
values = {}
|
||||
for ann in self.__annotations__: # 获取类型注释
|
||||
values[ann] = getattr(self, ann, None)
|
||||
if values[ann] is None:
|
||||
values[ann] = self.__annotations__[ann]
|
||||
|
||||
if not hasattr(self, 'options'):
|
||||
self.options: Dict[str, Union[Callable, object]] = {}
|
||||
for option, a_fun in self.options.items(): # 获取额外内容
|
||||
values[option] = a_fun
|
||||
|
||||
for option, a_fun in values.items(): # 检查是否为 property
|
||||
if a_fun is bool and getattr(self, option, None) is not None:
|
||||
values[option] = False
|
||||
if isinstance(a_fun, property):
|
||||
try:
|
||||
values[option] = getattr(self, option)
|
||||
except AttributeError:
|
||||
raise OptionNotFound(f'Option {option} is not found in {self.name}') from None
|
||||
return values
|
||||
|
||||
def format(self, text: str) -> str:
|
||||
"""
|
||||
使用自己的选项给输入的字符串替换内容
|
||||
:param text: 想替换的内容
|
||||
:return: 替换之后的内容
|
||||
"""
|
||||
cache_option = self.flush_option()
|
||||
for option, value in cache_option.items():
|
||||
text = text.replace(f'{{{option}}}', str(value))
|
||||
return text
|
||||
|
||||
def flush_option(self) -> Dict[str, Any]:
|
||||
"""
|
||||
刷新缓存 options 的内容
|
||||
:return: 刷新过的 options
|
||||
"""
|
||||
self.cached_options = self.option()
|
||||
return self.cached_options
|
||||
|
||||
def option_with_len(self) -> List[Union[List[Tuple[str, Any, Any]], int, Any]]:
|
||||
options = self.flush_option()
|
||||
max_len_key = 1
|
||||
max_len_value = 1
|
||||
max_len_value_t = 1
|
||||
option_list = []
|
||||
for key, value in options.items():
|
||||
value_t = value if isinstance(value, Type) else type(value)
|
||||
max_len_key = max(max_len_key, len(key))
|
||||
max_len_value = max(max_len_value, len(str(value)))
|
||||
max_len_value_t = max(max_len_value_t, len(str(value_t)))
|
||||
option_list.append((key, value, value_t))
|
||||
return [option_list, max_len_key, max_len_value, max_len_value_t]
|
||||
|
||||
@classmethod
|
||||
def add_option(cls, name: str, value: Union[Callable, object]) -> Dict:
|
||||
if not hasattr(cls, 'options'):
|
||||
cls.options: Dict[str, Union[Callable, object]] = {}
|
||||
cls.options[name] = value
|
||||
return cls.options
|
||||
|
||||
@staticmethod
|
||||
def init_option(options_class: Type['Options'], init_value: Optional[dict] = None) -> 'Options':
|
||||
return options_class(**init_value if init_value is not None else {})
|
||||
|
||||
|
||||
class Fonts(Options):
|
||||
# font's value
|
||||
|
||||
HOS: str = 'HarmonyOS Sans'
|
||||
HOS_S: str = 'HarmonyOS Sans SC'
|
||||
HOS_T: str = 'HarmonyOS Sans TC'
|
||||
HOS_C: str = 'HarmonyOS Sans Condensed'
|
||||
|
||||
鸿蒙字体: str = HOS
|
||||
鸿蒙简体: str = HOS_S
|
||||
鸿蒙繁体: str = HOS_T
|
||||
鸿蒙窄体: str = HOS_C
|
||||
|
||||
CC: str = 'Cascadia Code'
|
||||
CM: str = 'Cascadia Mono'
|
||||
CCPL: str = 'Cascadia Code PL'
|
||||
CMPL: str = 'Cascadia Mono PL'
|
||||
|
||||
微软等宽: str = CC
|
||||
微软等宽无线: str = CM
|
||||
微软等宽带电线: str = CCPL
|
||||
微软等宽带电线无线: str = CMPL
|
||||
|
||||
得意黑: str = '得意黑'
|
||||
# SS = smiley-sans
|
||||
SS: str = 得意黑
|
||||
|
||||
|
||||
@dataclass
|
||||
class FontData:
|
||||
""" 用于保存字体的信息 """
|
||||
font_name: str = Fonts.鸿蒙简体
|
||||
font_size: int = 13
|
||||
bold: bool = False
|
||||
italic: bool = False
|
||||
stretch: bool = False
|
||||
|
||||
def dict(self) -> Dict[str, Union[str, int, bool]]:
|
||||
return dict(font_name=self.font_name,
|
||||
font_size=self.font_size,
|
||||
bold=self.bold,
|
||||
italic=self.italic,
|
||||
stretch=self.stretch)
|
@ -121,8 +121,8 @@ class Translates:
|
||||
self._config.is_final = True
|
||||
return self
|
||||
|
||||
def __call__(self, *args, **kwargs) -> Union[dict, list, int, str]:
|
||||
return self.__str__()
|
||||
def __call__(self, *args, **kwargs) -> str:
|
||||
return str(self)
|
||||
|
||||
def copy(self):
|
||||
return self.__copy__()
|
||||
|
20
biggit.ps1
Normal file
20
biggit.ps1
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
$objects = git verify-pack -v .git/objects/pack/pack-aba4bfc55979194c86dbd466c86e57d8199ae7ad.idx | Select-String -Pattern 'chain' -NotMatch | ForEach-Object {$_.Line} | Sort-Object -Property @{Expression={$_.Split(" ")[2]}; Ascending=$false} | Select-Object -First 50
|
||||
|
||||
Write-Output "All sizes are in kB. The pack column is the size of the object, compressed, inside the pack file."
|
||||
|
||||
$output = "size,pack,SHA,location"
|
||||
foreach ($y in $objects) {
|
||||
# extract the size in bytes
|
||||
$size = [int]($y.Split(" ")[4]/1024)
|
||||
# extract the compressed size in bytes
|
||||
$compressedSize = [int]($y.Split(" ")[5]/1024)
|
||||
# extract the SHA
|
||||
$sha = $y.Split(" ")[0]
|
||||
# find the objects location in the repository tree
|
||||
$other = git rev-list --all --objects | Select-String $sha
|
||||
$output += "`n${size},${compressedSize},${other}"
|
||||
}
|
||||
|
||||
Write-Output $output | ConvertFrom-Csv -Delimiter "," | Format-Table
|
||||
Pause
|
@ -1,4 +1,4 @@
|
||||
Set-Location .\libs\Difficult_Rocket_rs\src
|
||||
Set-Location .\mods\dr_game\Difficult_Rocket_rs\src
|
||||
|
||||
Write-Output $args[0]
|
||||
|
||||
@ -23,6 +23,6 @@ if ($do -or "311" -in $args) {
|
||||
python3.11 setup.py build
|
||||
}
|
||||
|
||||
python3 post_build.py
|
||||
python3.8 post_build.py
|
||||
|
||||
Set-Location ..\..\..\
|
||||
Set-Location ..\..\..\..\
|
||||
|
@ -1,4 +0,0 @@
|
||||
[command]
|
||||
log = 1000
|
||||
show = 20
|
||||
size = 12
|
@ -20,7 +20,16 @@ logger.logfile_name = "Log file name : "
|
||||
logger.logfile_level = "Log file record level : "
|
||||
logger.logfile_fmt = "Log file record format : "
|
||||
logger.logfile_datefmt = "Log file date format : "
|
||||
game_start.at = "The main thread of the game starts with : "
|
||||
game_start.at = "Game MainThread start at: {}"
|
||||
mod.find.start = "Checking Mod: {}"
|
||||
mod.find.faild.no_spec = "importlib can't find spec"
|
||||
mod.find.faild.no_mod_folder = "Can't find mod folder"
|
||||
mod.find.done = "All Mod checked"
|
||||
mod.load.start = "Loading Mod: {}"
|
||||
mod.load.info = "mod id: {} version: {}"
|
||||
mod.load.faild.info = "Mod load failed: {} error info: {}"
|
||||
mod.load.faild.no_mod_class = "Can't find Mod class"
|
||||
mod.load.done = "All Mod loaded"
|
||||
|
||||
[client]
|
||||
setup.start = "Client start loading"
|
||||
|
@ -21,6 +21,16 @@ logger.logfile_level = "日志文件记录级别:"
|
||||
logger.logfile_fmt = "日志文件记录格式:"
|
||||
logger.logfile_datefmt = "日志文件日期格式:"
|
||||
game_start.at = "游戏主线程开始于:"
|
||||
mod.find.start = "正在校验 Mod: {}"
|
||||
mod.find.faild.no_spec = "importlib 无法找到 spec"
|
||||
mod.find.faild.no_mod_folder = "没有找到 Mod 文件夹"
|
||||
mod.find.done = "所有 Mod 校验完成"
|
||||
mod.load.start = "正在加载 Mod: {}"
|
||||
mod.load.info = "mod id: {} 版本号: {}"
|
||||
mod.load.faild.info = "Mod 加载失败: {} 错误信息: {}"
|
||||
mod.load.faild.no_mod_class = "没有找到 Mod 类"
|
||||
mod.load.done = "所有 Mod 加载完成"
|
||||
mod.event.error = "Mod 事件 {} 发生错误 {} Mod: {}"
|
||||
|
||||
[client]
|
||||
setup.start = "客户端加载开始"
|
||||
|
@ -7,8 +7,8 @@ fonts_folder = "libs/fonts"
|
||||
|
||||
[window]
|
||||
style = "None"
|
||||
width = 1406
|
||||
height = 900
|
||||
width = 2571
|
||||
height = 1510
|
||||
visible = true
|
||||
gui_scale = 1
|
||||
caption = "Difficult Rocket v{DR_version}|DR_rs v{DR_Rust_get_version}"
|
||||
@ -19,3 +19,5 @@ full_screen = false
|
||||
width = 1024
|
||||
height = 768
|
||||
gui_scale = 1
|
||||
[game.mods]
|
||||
path = "mods"
|
||||
|
@ -22,15 +22,54 @@
|
||||
- [![Readme-gitee](https://img.shields.io/badge/Readme-中文(点我!)-blue.svg?style=flat-square)](../../README.md)
|
||||
- Using [SemVer 2.0.0](https://semver.org/) to manage version
|
||||
|
||||
## 202305 DR `0.8.0.0` + DR_api `0.1.0.0`
|
||||
## 202305 DR `0.8.0.0` + DR_api `0.1.0.0` + DR_rs `0.2.7.0` + 15
|
||||
|
||||
> 啊哈! mod 加载来啦!
|
||||
|
||||
> 啊啊啊啊啊 大重构 api
|
||||
|
||||
### DR_api `0.1.0.0`
|
||||
|
||||
- 大概是一个可用的版本了
|
||||
- `ModInfo`
|
||||
- `on_load(game: Game, old_self: Optional[ModInfo]) -> bool`
|
||||
- `game`: Game 对象 用于存储 DR SDK 的信息
|
||||
- `old_self`: 旧的 ModInfo 对象, 可以用于从上次加载中恢复信息
|
||||
- 返回值: 是否加载成功
|
||||
- `on_client_start(game: Game, client: ClientWindow) -> None`
|
||||
- `game`: Game 对象 用于存储 DR SDK 的信息
|
||||
- `client`: ClientWindow 对象 用于传递客户端状态
|
||||
- `on_client_stop(game: Game, client: ClientWindow, source: str = 'window')`
|
||||
- `game`: Game 对象 用于存储 DR SDK 的信息
|
||||
- `client`: ClientWindow 对象 用于传递客户端状态
|
||||
- `source`: 关闭调用的来源
|
||||
|
||||
### DR_rs `0.2.7.0`
|
||||
|
||||
- `__init__.py`
|
||||
- 添加了 `SR1Ship_rs` 的 typing
|
||||
- `name`
|
||||
- `description`
|
||||
- `lift_off`
|
||||
- `touch_ground`
|
||||
- `img_pos() -> Tuple[int, int, int, int]`
|
||||
- 导出了 `SR1Ship_rs`
|
||||
- Exported `SR1Ship_rs`
|
||||
- `types::SR1PartData`
|
||||
- `get_box(&self, part_type: &SR1PartType) -> (f64, f64, f64, f64)`
|
||||
- `types::SR1Ship`
|
||||
- `from_file`
|
||||
|
||||
### Remove
|
||||
|
||||
- `game.config`
|
||||
- 已删除
|
||||
- Removed
|
||||
- `DR_option` & `DR_runtime`(`long_version` `15`)
|
||||
- 完全移除 `DR_rust` 部分
|
||||
- Completely removed the `DR_rust` part
|
||||
- 现在 `client` 不会在 `setup()` 中调用 `DR_runtime` 的 `find_mods()` 方法
|
||||
- Now `client` will not call the `find_mods()` method of `DR_runtime` in `setup()`
|
||||
|
||||
### Changes
|
||||
|
||||
@ -53,6 +92,14 @@
|
||||
- 现在游戏崩溃时会自动在 stdio 中输出崩溃日志 内容跟 crash report 中的基本相同
|
||||
- Now when the game crashes, it will automatically output the crash log in stdio
|
||||
- The content of the crash log is basically the same as the crash report
|
||||
- `utils.new_thread`
|
||||
- 跟随 MCDR 的更新
|
||||
- 将记录线程的方式改成 函数回调
|
||||
- Follow the update of MCDR
|
||||
- Change the way to record threads to function callbacks
|
||||
- `Difficult_Rocket.api`
|
||||
- 大重构,移除定义,改为引用
|
||||
- Big refactoring, remove definition, change to reference
|
||||
|
||||
### Docs
|
||||
|
||||
@ -67,6 +114,13 @@
|
||||
### Mod Loader
|
||||
|
||||
- `ModInfo`
|
||||
- `on_load(game: Game, old_self: Optional[ModInfo]) -> bool`
|
||||
- `game`: Game 对象 用于存储 DR SDK 的信息
|
||||
- `old_self`: 旧的 ModInfo 对象, 可以用于从上次加载中恢复信息
|
||||
- 返回值: 是否加载成功
|
||||
- `game`: Game object used to store information about the DR SDK
|
||||
- `old_self`: Old ModInfo object, can be used to restore information from the last load
|
||||
- Return value: Whether the load is successful
|
||||
|
||||
## 20230422 DR `0.7.2.2` + DR_rs `0.2.6.1` + DR_api `0.0.2.0` + 14
|
||||
|
||||
@ -147,6 +201,13 @@
|
||||
- 修改了中文语言的翻译,在中英混杂的中间加入空格
|
||||
- Modified the Chinese translation, and added a space in the middle of Chinese and English
|
||||
|
||||
### Fix
|
||||
|
||||
- `mods` 路径不存在时 游戏会崩溃的 bug
|
||||
- Bug that the game will crash when the `mods` path does not exist
|
||||
- 游戏崩溃 / 从控制台退出时 `WerFault.exe` 会跳出来卡住程序的 bug
|
||||
- Bug that `WerFault.exe` will pop up and block the program when the game crashes / exits from the console
|
||||
|
||||
## 20230405 DR `0.7.2.1`
|
||||
|
||||
### Changes
|
||||
|
BIN
libs/Difficult_Rocket_rs/lib/Difficult_Rocket_rs.cp38-win_amd64.pyd
(Stored with Git LFS)
BIN
libs/Difficult_Rocket_rs/lib/Difficult_Rocket_rs.cp38-win_amd64.pyd
(Stored with Git LFS)
Binary file not shown.
@ -3,7 +3,6 @@ from .base import Display, Screen, ScreenMode, Canvas
|
||||
from pyglet.libs.win32 import _user32
|
||||
from pyglet.libs.win32.constants import *
|
||||
from pyglet.libs.win32.types import *
|
||||
from pyglet.libs.win32.context_managers import device_context
|
||||
|
||||
|
||||
class Win32Display(Display):
|
||||
@ -31,13 +30,13 @@ class Win32Screen(Screen):
|
||||
self._handle = handle
|
||||
|
||||
def get_matching_configs(self, template):
|
||||
with device_context(None) as hdc:
|
||||
canvas = Win32Canvas(self.display, 0, hdc)
|
||||
configs = template.match(canvas)
|
||||
# XXX deprecate config's being screen-specific
|
||||
for config in configs:
|
||||
config.screen = self
|
||||
|
||||
hdc = _user32.GetDC(0)
|
||||
canvas = Win32Canvas(self.display, 0, hdc)
|
||||
configs = template.match(canvas)
|
||||
# XXX deprecate config's being screen-specific
|
||||
for config in configs:
|
||||
config.screen = self
|
||||
_user32.ReleaseDC(0, hdc)
|
||||
return configs
|
||||
|
||||
def get_device_name(self):
|
||||
|
@ -9,7 +9,6 @@ from pyglet.font import base
|
||||
from pyglet.font import win32query
|
||||
import pyglet.image
|
||||
from pyglet.libs.win32.constants import *
|
||||
from pyglet.libs.win32.context_managers import device_context
|
||||
from pyglet.libs.win32.types import *
|
||||
from pyglet.libs.win32 import _gdi32 as gdi32, _user32 as user32
|
||||
from pyglet.libs.win32 import _kernel32 as kernel32
|
||||
@ -196,13 +195,14 @@ class Win32Font(base.Font):
|
||||
self.hfont = gdi32.CreateFontIndirectW(byref(self.logfont))
|
||||
|
||||
# Create a dummy DC for coordinate mapping
|
||||
with device_context(None) as dc:
|
||||
metrics = TEXTMETRIC()
|
||||
gdi32.SelectObject(dc, self.hfont)
|
||||
gdi32.GetTextMetricsA(dc, byref(metrics))
|
||||
self.ascent = metrics.tmAscent
|
||||
self.descent = -metrics.tmDescent
|
||||
self.max_glyph_width = metrics.tmMaxCharWidth
|
||||
dc = user32.GetDC(0)
|
||||
metrics = TEXTMETRIC()
|
||||
gdi32.SelectObject(dc, self.hfont)
|
||||
gdi32.GetTextMetricsA(dc, byref(metrics))
|
||||
self.ascent = metrics.tmAscent
|
||||
self.descent = -metrics.tmDescent
|
||||
self.max_glyph_width = metrics.tmMaxCharWidth
|
||||
user32.ReleaseDC(0, dc)
|
||||
|
||||
def __del__(self):
|
||||
gdi32.DeleteObject(self.hfont)
|
||||
@ -210,22 +210,22 @@ class Win32Font(base.Font):
|
||||
@staticmethod
|
||||
def get_logfont(name, size, bold, italic, dpi):
|
||||
# Create a dummy DC for coordinate mapping
|
||||
with device_context(None) as dc:
|
||||
if dpi is None:
|
||||
dpi = 96
|
||||
logpixelsy = dpi
|
||||
|
||||
logfont = LOGFONTW()
|
||||
# Conversion of point size to device pixels
|
||||
logfont.lfHeight = int(-size * logpixelsy // 72)
|
||||
if bold:
|
||||
logfont.lfWeight = FW_BOLD
|
||||
else:
|
||||
logfont.lfWeight = FW_NORMAL
|
||||
logfont.lfItalic = italic
|
||||
logfont.lfFaceName = name
|
||||
logfont.lfQuality = ANTIALIASED_QUALITY
|
||||
dc = user32.GetDC(0)
|
||||
if dpi is None:
|
||||
dpi = 96
|
||||
logpixelsy = dpi
|
||||
|
||||
logfont = LOGFONTW()
|
||||
# Conversion of point size to device pixels
|
||||
logfont.lfHeight = int(-size * logpixelsy // 72)
|
||||
if bold:
|
||||
logfont.lfWeight = FW_BOLD
|
||||
else:
|
||||
logfont.lfWeight = FW_NORMAL
|
||||
logfont.lfItalic = italic
|
||||
logfont.lfFaceName = name
|
||||
logfont.lfQuality = ANTIALIASED_QUALITY
|
||||
user32.ReleaseDC(0, dc)
|
||||
return logfont
|
||||
|
||||
@classmethod
|
||||
|
@ -71,7 +71,6 @@ appropriate typeface name and create the font using CreateFont or
|
||||
CreateFontIndirect.
|
||||
|
||||
"""
|
||||
from pyglet.libs.win32.context_managers import device_context
|
||||
|
||||
DEBUG = False
|
||||
|
||||
@ -386,33 +385,36 @@ def query(charset=DEFAULT_CHARSET):
|
||||
global FONTDB
|
||||
|
||||
# 1. Get device context of the entire screen
|
||||
with device_context(None) as hdc:
|
||||
hdc = user32.GetDC(None)
|
||||
|
||||
# 2. Call EnumFontFamiliesExA (ANSI version)
|
||||
# 2. Call EnumFontFamiliesExA (ANSI version)
|
||||
|
||||
# 2a. Call with empty font name to query all available fonts
|
||||
# (or fonts for the specified charset)
|
||||
#
|
||||
# NOTES:
|
||||
#
|
||||
# * there are fonts that don't support ANSI charset
|
||||
# * for DEFAULT_CHARSET font is passed to callback function as
|
||||
# many times as charsets it supports
|
||||
# 2a. Call with empty font name to query all available fonts
|
||||
# (or fonts for the specified charset)
|
||||
#
|
||||
# NOTES:
|
||||
#
|
||||
# * there are fonts that don't support ANSI charset
|
||||
# * for DEFAULT_CHARSET font is passed to callback function as
|
||||
# many times as charsets it supports
|
||||
|
||||
# [ ] font name should be less than 32 symbols with terminating \0
|
||||
# [ ] check double purpose - enumerate all available font names
|
||||
# - enumerate all available charsets for a single font
|
||||
# - other params?
|
||||
# [ ] font name should be less than 32 symbols with terminating \0
|
||||
# [ ] check double purpose - enumerate all available font names
|
||||
# - enumerate all available charsets for a single font
|
||||
# - other params?
|
||||
|
||||
logfont = LOGFONTW(0, 0, 0, 0, 0, 0, 0, 0, charset, 0, 0, 0, 0, '')
|
||||
FONTDB = [] # clear cached FONTDB for enum_font_names callback
|
||||
res = gdi32.EnumFontFamiliesExW(
|
||||
hdc, # handle to device context
|
||||
ctypes.byref(logfont),
|
||||
enum_font_names, # pointer to callback function
|
||||
0, # lParam - application-supplied data
|
||||
0) # dwFlags - reserved = 0
|
||||
# res here is the last value returned by callback function
|
||||
logfont = LOGFONTW(0, 0, 0, 0, 0, 0, 0, 0, charset, 0, 0, 0, 0, '')
|
||||
FONTDB = [] # clear cached FONTDB for enum_font_names callback
|
||||
res = gdi32.EnumFontFamiliesExW(
|
||||
hdc, # handle to device context
|
||||
ctypes.byref(logfont),
|
||||
enum_font_names, # pointer to callback function
|
||||
0, # lParam - application-supplied data
|
||||
0) # dwFlags - reserved = 0
|
||||
# res here is the last value returned by callback function
|
||||
|
||||
# 3. Release DC
|
||||
user32.ReleaseDC(None, hdc)
|
||||
|
||||
return FONTDB
|
||||
|
||||
|
@ -1,57 +0,0 @@
|
||||
"""
|
||||
Win32 resources as handy context managers.
|
||||
|
||||
These are intended to help keep loader code clean & stable at the price
|
||||
of a tiny bit of execution speed. Performance-critical code should avoid
|
||||
using these helpers in favor of direct win32 API calls.
|
||||
|
||||
Before::
|
||||
|
||||
def loader_function(arg):
|
||||
context_handle = user32.GetResource(None)
|
||||
|
||||
result = calculation(arg)
|
||||
|
||||
# Easily forgotten!
|
||||
user32.ReleaseResource(context_handle)
|
||||
|
||||
return result
|
||||
|
||||
After::
|
||||
|
||||
def loader_function(arg):
|
||||
|
||||
with resource_context() as context_handle:
|
||||
result = calculation(arg)
|
||||
|
||||
return result
|
||||
|
||||
"""
|
||||
from contextlib import contextmanager
|
||||
from typing import Optional, Generator
|
||||
from ctypes.wintypes import HANDLE
|
||||
from ctypes import WinError
|
||||
from pyglet.libs.win32 import _user32 as user32
|
||||
|
||||
|
||||
@contextmanager
|
||||
def device_context(window_handle: Optional[int] = None) -> Generator[HANDLE, None, None]:
|
||||
"""
|
||||
A Windows device context wrapped as a context manager.
|
||||
|
||||
Args:
|
||||
window_handle: A specific window handle to use, if any.
|
||||
Raises:
|
||||
WinError: Raises if a device context cannot be acquired or released
|
||||
Yields:
|
||||
HANDLE: the managed drawing context handle to auto-close.
|
||||
|
||||
"""
|
||||
if not (_dc := user32.GetDC(window_handle)):
|
||||
raise WinError()
|
||||
|
||||
try:
|
||||
yield _dc
|
||||
finally:
|
||||
if not user32.ReleaseDC(None, _dc):
|
||||
raise WinError()
|
@ -1,5 +0,0 @@
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
@ -40,22 +40,16 @@ if TYPE_CHECKING:
|
||||
|
||||
@property
|
||||
def dx(self) -> float: ...
|
||||
|
||||
@property
|
||||
def dy(self) -> float: ...
|
||||
|
||||
@property
|
||||
def zoom(self) -> float: ...
|
||||
|
||||
@property
|
||||
def position(self) -> Tuple[float, float]: ...
|
||||
|
||||
@dx.setter
|
||||
def dx(self, value: float) -> None: ...
|
||||
|
||||
@dy.setter
|
||||
def dy(self, value: float) -> None: ...
|
||||
|
||||
@zoom.setter
|
||||
def zoom(self, value: float) -> None: ...
|
||||
|
||||
@ -93,3 +87,17 @@ if TYPE_CHECKING:
|
||||
def as_dict(self) -> Dict[str, SR1PartType_rs]: ...
|
||||
|
||||
def get_part_type(self, name: str) -> SR1PartType_rs: ...
|
||||
|
||||
class SR1Ship_rs:
|
||||
""" 用于高效且省内存的读取 SR1Ship """
|
||||
@property
|
||||
def name(self) -> str: ...
|
||||
@property
|
||||
def description(self) -> str: ...
|
||||
@property
|
||||
def lift_off(self) -> bool: ...
|
||||
@property
|
||||
def touch_ground(self) -> bool: ...
|
||||
@property
|
||||
def img_pos(self) -> Tuple[int, int, int, int]: ...
|
||||
""" -x -y +x +y 左下右上 """
|
@ -3,22 +3,16 @@
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
from setuptools import setup
|
||||
from setuptools_rust import Binding, RustExtension
|
||||
|
||||
sys.path.append('../../../')
|
||||
sys.path.append(os.curdir)
|
||||
if '../../../' in sys.path:
|
||||
from Difficult_Rocket import DR_runtime
|
||||
|
||||
package_path = 'Difficult_Rocket_rs'
|
||||
|
||||
setup(
|
||||
name='Difficult_Rocket_rs',
|
||||
version=DR_runtime.DR_Rust_version.__str__(),
|
||||
version="0.2.7.0",
|
||||
author='shenjackyuanjie',
|
||||
author_email='3695888@qq.com',
|
||||
rust_extensions=[RustExtension(target="Difficult_Rocket_rs.Difficult_Rocket_rs",
|
@ -17,7 +17,7 @@ mod types;
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[pyfunction]
|
||||
fn get_version_str() -> String { "0.2.6.1".to_string() }
|
||||
fn get_version_str() -> String { "0.2.7.0".to_string() }
|
||||
|
||||
#[pyfunction]
|
||||
fn test_call(py_obj: &PyAny) -> PyResult<bool> {
|
||||
@ -39,6 +39,7 @@ fn module_init(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
|
||||
m.add_class::<render::camera::CameraRs>()?;
|
||||
m.add_class::<render::camera::CenterCameraRs>()?;
|
||||
m.add_class::<render::screen::PartFrame>()?;
|
||||
m.add_class::<python::data::PySR1Ship>()?;
|
||||
m.add_class::<python::data::PySR1PartList>()?;
|
||||
m.add_class::<python::data::PySR1PartType>()?;
|
||||
Ok(())
|
@ -15,11 +15,10 @@
|
||||
pub mod plugin_trait {
|
||||
pub struct ModInfo {
|
||||
pub name: String,
|
||||
pub version: String
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
pub trait ModInfoTrait {
|
||||
fn info() -> ModInfo;
|
||||
}
|
||||
|
||||
}
|
@ -13,8 +13,8 @@ pub mod data {
|
||||
|
||||
use crate::sr1_data::part_list::RawPartList;
|
||||
use crate::sr1_data::ship::RawShip;
|
||||
use crate::types::sr1::{SR1PartData, SR1PartListTrait};
|
||||
use crate::types::sr1::{SR1PartList, SR1PartType, SR1Ship};
|
||||
use crate::types::sr1::{SR1PartListTrait, SR1ShipTrait};
|
||||
|
||||
#[pyclass]
|
||||
#[pyo3(name = "SR1PartType_rs")]
|
||||
@ -70,6 +70,12 @@ pub mod data {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
#[pyo3(name = "SR1PartData_rs")]
|
||||
pub struct PySR1PartData {
|
||||
pub data: SR1PartData,
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
#[pyo3(name = "SR1Ship_rs")]
|
||||
#[pyo3(text_signature = "(file_path = './configs/dock1.xml', part_list = './configs/PartList.xml', ship_name = 'NewShip')")]
|
||||
@ -82,11 +88,29 @@ pub mod data {
|
||||
impl PySR1Ship {
|
||||
#[new]
|
||||
fn new(file_path: String, part_list: String, ship_name: String) -> Self {
|
||||
let raw_ship: RawShip = RawShip::from_file(file_path).unwrap();
|
||||
let ship = raw_ship.to_sr_ship(Some(ship_name));
|
||||
let ship = SR1Ship::from_file(file_path, Some(ship_name)).unwrap();
|
||||
let part_list = SR1PartList::from_file(part_list).unwrap();
|
||||
Self { ship, part_list }
|
||||
}
|
||||
|
||||
fn get_img_pos(&self) -> (i64, i64, i64, i64) {
|
||||
let mut img_pos = (0, 0, 0, 0);
|
||||
// -x, -y, +x, +y
|
||||
// 左下角,右上角
|
||||
for part in self.ship.types.iter() {
|
||||
// let part_box = part
|
||||
todo!("get_img_pos")
|
||||
}
|
||||
img_pos
|
||||
}
|
||||
|
||||
fn get_name(&self) -> String { self.ship.name.clone() }
|
||||
|
||||
fn get_description(&self) -> String { self.ship.description.clone() }
|
||||
|
||||
fn get_lift_off(&self) -> bool { self.ship.lift_off }
|
||||
|
||||
fn get_touch_ground(&self) -> bool { self.ship.touch_ground }
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,6 +118,12 @@ pub mod translate {
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyDict;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum BoolString {
|
||||
Bool(bool),
|
||||
String(String),
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
#[pyo3(name = "TranslateConfig_rs")]
|
||||
#[pyo3(text_signature = "(language, raise_error = False, replace_normal = False, add_error = False, is_result = False, keep_get = False)")]
|
||||
@ -121,6 +151,13 @@ pub mod translate {
|
||||
language: language.unwrap_or(default_language),
|
||||
}
|
||||
}
|
||||
|
||||
// fn set(&self, py_: Python, item: String, value: BoolString) -> &Self {
|
||||
// match item.as_str() {
|
||||
// "raise_error" => self,
|
||||
// _ => self,
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
#[pyclass]
|
@ -9,10 +9,9 @@
|
||||
use pyo3::prelude::*;
|
||||
use rapier2d_f64::prelude::*;
|
||||
|
||||
|
||||
#[pyfunction]
|
||||
#[pyo3(name = "simluation")]
|
||||
pub fn simluation() -> PyResult<()> {
|
||||
pub fn simluation() -> () {
|
||||
let mut rigid_body_set = RigidBodySet::new();
|
||||
let mut collider_set = ColliderSet::new();
|
||||
|
||||
@ -62,6 +61,4 @@ pub fn simluation() -> PyResult<()> {
|
||||
let ball_body = &rigid_body_set[ball_body_handle];
|
||||
println!("Ball altitude: {} {}", ball_body.translation().x, ball_body.translation().y);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -8,7 +8,9 @@
|
||||
|
||||
pub mod sr1 {
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
|
||||
use super::math::{Edge, Shape};
|
||||
use crate::sr1_data::part_list::Damage as RawDamage;
|
||||
use crate::sr1_data::part_list::{AttachPoint, AttachPoints, Engine, Lander, Rcs, Shape as RawShape, Solar, Tank};
|
||||
use crate::sr1_data::part_list::{RawPartList, RawPartType, SR1PartTypeEnum};
|
||||
@ -179,31 +181,49 @@ pub mod sr1 {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SR1PartList {
|
||||
pub types: Vec<SR1PartType>,
|
||||
pub cache: Option<HashMap<String, SR1PartType>>,
|
||||
pub cache: HashMap<String, SR1PartType>,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl SR1PartList {
|
||||
#[inline]
|
||||
pub fn new(name: String, types: Vec<SR1PartType>) -> SR1PartList {
|
||||
let mut map = HashMap::new();
|
||||
for part in types.iter() {
|
||||
map.insert(part.id.clone(), part.clone());
|
||||
}
|
||||
SR1PartList {
|
||||
types,
|
||||
cache: map,
|
||||
name,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_file(file_name: String) -> Option<SR1PartList> {
|
||||
if let Some(raw_list) = RawPartList::from_file(file_name) {
|
||||
return Some(raw_list.to_sr_part_list(None));
|
||||
let sr_list = raw_list.to_sr_part_list(None);
|
||||
let mut map = HashMap::new();
|
||||
for part in sr_list.types.iter() {
|
||||
map.insert(part.id.clone(), part.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hash_map(&mut self) -> HashMap<String, SR1PartType> {
|
||||
if let Some(map) = &self.cache {
|
||||
return map.clone();
|
||||
pub fn get_part_type(self, type_name: String) -> Option<SR1PartType> {
|
||||
if let Some(part) = self.cache.get(&type_name) {
|
||||
return Some(part.clone());
|
||||
}
|
||||
let mut map = HashMap::new();
|
||||
for part in self.types.iter() {
|
||||
map.insert(part.id.clone(), part.clone());
|
||||
}
|
||||
self.cache = Some(map.clone());
|
||||
map
|
||||
None
|
||||
}
|
||||
|
||||
pub fn part_types_new(part_types: Vec<SR1PartType>, name: Option<String>) -> Self {
|
||||
SR1PartList::new(name.unwrap_or("NewPartList".to_string()), part_types)
|
||||
}
|
||||
|
||||
pub fn insert_part(&mut self, part: SR1PartType) -> () { self.types.insert(0, part); }
|
||||
}
|
||||
|
||||
pub trait SR1PartTypeData {
|
||||
@ -226,17 +246,6 @@ pub mod sr1 {
|
||||
fn to_raw_ship(&self) -> RawShip;
|
||||
}
|
||||
|
||||
impl SR1PartList {
|
||||
#[inline]
|
||||
pub fn new(name: String, types: Vec<SR1PartType>) -> Self { SR1PartList { name, cache: None, types } }
|
||||
|
||||
pub fn part_types_new(part_types: Vec<SR1PartType>, name: Option<String>) -> Self {
|
||||
SR1PartList::new(name.unwrap_or("NewPartList".to_string()), part_types)
|
||||
}
|
||||
|
||||
pub fn insert_part(&mut self, part: SR1PartType) -> () { self.types.insert(0, part); }
|
||||
}
|
||||
|
||||
impl SR1PartListTrait for SR1PartList {
|
||||
fn to_sr_part_list(&self, name: Option<String>) -> SR1PartList {
|
||||
return if let Some(name) = name {
|
||||
@ -491,6 +500,32 @@ pub mod sr1 {
|
||||
pub explode: bool,
|
||||
}
|
||||
|
||||
impl SR1PartData {
|
||||
pub fn get_box(&self, part_type: &SR1PartType) -> (f64, f64, f64, f64) {
|
||||
let width = part_type.width;
|
||||
let height = part_type.height;
|
||||
let radius = self.angle;
|
||||
let mut shape = Shape::new_width_height(width as f64, height as f64, Some(radius));
|
||||
shape.move_xy(Some(self.x), Some(self.y));
|
||||
let mut pos_box = (0_f64, 0_f64, 0_f64, 0_f64);
|
||||
match shape.bounds[0] {
|
||||
Edge::OneTimeLine(line) => {
|
||||
pos_box.0 = line.start.x;
|
||||
pos_box.1 = line.start.y;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
match shape.bounds[2] {
|
||||
Edge::OneTimeLine(line) => {
|
||||
pos_box.2 = line.start.x;
|
||||
pos_box.3 = line.start.y;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
pos_box
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SR1PartDataAttr {
|
||||
Tank {
|
||||
@ -532,6 +567,18 @@ pub mod sr1 {
|
||||
pub disconnected: Option<Vec<(Vec<SR1PartData>, Option<Vec<Connection>>)>>,
|
||||
}
|
||||
|
||||
impl SR1Ship {
|
||||
pub fn from_file(file_name: String, ship_name: Option<String>) -> Option<Self> {
|
||||
// 首先验证文件是否存在 不存在则返回None
|
||||
if !std::path::Path::new(&file_name).exists() {
|
||||
return None;
|
||||
}
|
||||
// 解析为 RawShip
|
||||
let ship: RawShip = RawShip::from_file(file_name).unwrap();
|
||||
Some(ship.to_sr_ship(ship_name))
|
||||
}
|
||||
}
|
||||
|
||||
impl SR1ShipTrait for SR1Ship {
|
||||
#[inline]
|
||||
fn to_sr_ship(&self, name: Option<String>) -> SR1Ship {
|
||||
@ -582,6 +629,14 @@ pub mod sr1 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_max_box(parts: &Vec<SR1PartData>, part_list: &SR1PartList) -> (f64, f64, f64, f64) {
|
||||
let mut max_box = (0_f64, 0_f64, 0_f64, 0_f64);
|
||||
for part in parts.iter() {
|
||||
let part_type = part_list.get_part_type(part.part_type);
|
||||
}
|
||||
todo!("get_max_box")
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
@ -686,7 +741,7 @@ pub mod math {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_width_height(width: f64, height: f64, angle: Option<f64>) -> Self {
|
||||
pub fn new_width_height(width: f64, height: f64, radius: Option<f64>) -> Self {
|
||||
let d_width = width / 2.0;
|
||||
let d_height = height / 2.0;
|
||||
let mut edges: Vec<Edge> = vec![
|
||||
@ -695,17 +750,17 @@ pub mod math {
|
||||
Edge::OneTimeLine(OneTimeLine::pos_new(d_width, d_height, -d_width, d_height)),
|
||||
Edge::OneTimeLine(OneTimeLine::pos_new(-d_width, d_height, -d_width, -d_height)),
|
||||
];
|
||||
if let Some(angle) = angle {
|
||||
if let Some(radius) = radius {
|
||||
edges = edges
|
||||
.iter()
|
||||
.map(|edge| match edge {
|
||||
Edge::OneTimeLine(line) => {
|
||||
let start = line.start.rotate(angle);
|
||||
let end = line.end.rotate(angle);
|
||||
let start = line.start.rotate_radius(radius);
|
||||
let end = line.end.rotate_radius(radius);
|
||||
Edge::OneTimeLine(OneTimeLine::point_new(&start, &end))
|
||||
}
|
||||
Edge::CircularArc(arc) => {
|
||||
let pos = arc.pos.rotate(angle);
|
||||
let pos = arc.pos.rotate_radius(radius);
|
||||
Edge::CircularArc(CircularArc {
|
||||
r: arc.r,
|
||||
pos,
|
||||
@ -722,6 +777,25 @@ pub mod math {
|
||||
bounds: edges,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_xy(&mut self, x: Option<f64>, y: Option<f64>) {
|
||||
let x = x.unwrap_or(0.0);
|
||||
let y = y.unwrap_or(0.0);
|
||||
for edge in self.bounds.iter() {
|
||||
match edge {
|
||||
Edge::OneTimeLine(mut line) => {
|
||||
line.start.x += x;
|
||||
line.start.y += y;
|
||||
line.end.x += x;
|
||||
line.end.y += y;
|
||||
}
|
||||
Edge::CircularArc(mut arc) => {
|
||||
arc.pos.x += x;
|
||||
arc.pos.y += y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OneTimeLine {
|
84
mods/dr_game/__init__.py
Normal file
84
mods/dr_game/__init__.py
Normal file
@ -0,0 +1,84 @@
|
||||
# -------------------------------
|
||||
# Difficult Rocket Mod
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
import warnings
|
||||
import traceback
|
||||
|
||||
from typing import Optional
|
||||
|
||||
|
||||
from libs.MCDR.version import Version
|
||||
from Difficult_Rocket.main import Game
|
||||
from Difficult_Rocket.api.mod import ModInfo
|
||||
from Difficult_Rocket.api.types import Options
|
||||
from Difficult_Rocket.client import ClientWindow
|
||||
|
||||
DR_rust_version = Version("0.2.6.2") # DR_mod 的 Rust 编写部分的兼容版本
|
||||
|
||||
|
||||
class _DR_mod_runtime(Options):
|
||||
|
||||
use_DR_rust: bool = True
|
||||
DR_rust_available: bool = False
|
||||
DR_rust_version: Version = DR_rust_version
|
||||
DR_rust_get_version: Optional[Version] = None
|
||||
|
||||
def init(self) -> None:
|
||||
try:
|
||||
from .Difficult_Rocket_rs import get_version_str
|
||||
self.DR_rust_get_version = Version(get_version_str())
|
||||
self.DR_rust_available = True
|
||||
if self.DR_rust_get_version != self.DR_rust_version:
|
||||
relationship = 'larger' if self.DR_rust_version > self.DR_rust_version else 'smaller'
|
||||
warnings.warn(f'DR_rust builtin version is {self.DR_rust_version} but true version is {get_version_str()}.\n'
|
||||
f'Builtin version {relationship} than true version')
|
||||
self.use_DR_rust = self.use_DR_rust and self.DR_rust_available
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
self.DR_rust_available = False
|
||||
self.use_DR_rust = False
|
||||
self.flush_option()
|
||||
|
||||
|
||||
DR_mod_runtime = _DR_mod_runtime()
|
||||
|
||||
|
||||
class DR_mod(ModInfo):
|
||||
|
||||
mod_id = "difficult_rocket_mod"
|
||||
name = "Difficult Rocket mod"
|
||||
version = Version("0.7.2.2")
|
||||
|
||||
writer = "shenjackyuanjie"
|
||||
link = "shenjack.top"
|
||||
description = "Difficult Rocket mod (where the game implement)"
|
||||
info = "Difficult Rocket mod (where the game implement)"
|
||||
|
||||
config = DR_mod_runtime
|
||||
|
||||
# DR_version = # DR SDK 兼容版本
|
||||
# 反正是内置 mod 跟着最新版本的 DR 走就行了
|
||||
# DR_Api_version = # DR Api版本
|
||||
# 同理 不管 API 版本 这东西要是不兼容了才是大问题
|
||||
|
||||
def on_load(self, game: Game, old_self: Optional["DR_mod"] = None) -> bool:
|
||||
if not DR_mod_runtime.DR_rust_available:
|
||||
return False
|
||||
if old_self:
|
||||
game.client.window.add_sub_screen("SR1_ship", old_self.screen)
|
||||
else:
|
||||
self.config.flush_option()
|
||||
print("DR_mod: on_load")
|
||||
return True
|
||||
|
||||
def on_client_start(self, game: Game, client: ClientWindow):
|
||||
from .sr1_ship import SR1ShipRender
|
||||
self.screen = SR1ShipRender
|
||||
print('DR_mod: on_client_start')
|
||||
client.add_sub_screen("SR1_ship", SR1ShipRender)
|
||||
|
||||
|
||||
mod_class = DR_mod
|
@ -19,8 +19,10 @@ from pyglet.math import Vec4
|
||||
from pyglet.text import Label
|
||||
from pyglet.shapes import Line
|
||||
from pyglet.sprite import Sprite
|
||||
from pyglet.image import Texture
|
||||
from pyglet.graphics import Batch, Group
|
||||
from pyglet.image import load as load_image
|
||||
|
||||
from . import DR_mod_runtime
|
||||
|
||||
# Difficult Rocket
|
||||
from Difficult_Rocket import DR_option
|
||||
@ -33,8 +35,8 @@ from Difficult_Rocket.api.types.SR1 import SR1Textures, SR1PartTexture, SR1PartD
|
||||
if TYPE_CHECKING:
|
||||
from Difficult_Rocket.client import ClientWindow
|
||||
|
||||
if DR_option.use_DR_rust:
|
||||
from libs.Difficult_Rocket_rs import CenterCamera_rs, SR1PartList_rs
|
||||
if DR_mod_runtime.use_DR_rust:
|
||||
from .Difficult_Rocket_rs import CenterCamera_rs, SR1PartList_rs
|
||||
|
||||
|
||||
logger = logging.getLogger('client')
|
||||
@ -128,7 +130,7 @@ class SR1ShipRender(BaseScreen):
|
||||
load_end_time = time.time_ns()
|
||||
logger.info(tr().client.sr1_render.setup.use_time().format(
|
||||
(load_end_time - load_start_time) / 1000000000))
|
||||
if DR_option.use_DR_rust:
|
||||
if DR_mod_runtime.use_DR_rust:
|
||||
self.camera_rs = CenterCamera_rs(main_window,
|
||||
min_zoom=(1 / 2) ** 10, max_zoom=10)
|
||||
self.rust_parts = None
|
||||
@ -194,7 +196,7 @@ class SR1ShipRender(BaseScreen):
|
||||
self.part_data: Dict[int, SR1PartData] = {}
|
||||
self.parts_sprite: Dict[int, Sprite] = {}
|
||||
self.camera_rs.zoom = 1.0
|
||||
if DR_option.use_DR_rust:
|
||||
if DR_mod_runtime.use_DR_rust:
|
||||
self.camera_rs.dx = 0
|
||||
self.camera_rs.dy = 0
|
||||
parts = self.xml_root.find('Parts')
|
||||
@ -213,13 +215,13 @@ class SR1ShipRender(BaseScreen):
|
||||
self.drawing = False
|
||||
self.need_draw = False
|
||||
full_mass = 0
|
||||
if DR_option.use_DR_rust:
|
||||
if DR_mod_runtime.use_DR_rust:
|
||||
for part in self.part_data:
|
||||
full_mass += self.part_list_rs.get_part_type(self.part_data[part].p_type).mass * 500
|
||||
logger.info(tr().client.sr1_render.ship.load_time().format(
|
||||
(time.perf_counter_ns() - start_time) / 1000000000))
|
||||
logger.info(tr().client.sr1_render.ship.info().format(
|
||||
len(self.part_data), f'{full_mass}kg' if DR_option.use_DR_rust else tr().game.require_DR_rs()))
|
||||
len(self.part_data), f'{full_mass}kg' if DR_mod_runtime.use_DR_rust else tr().game.require_DR_rs()))
|
||||
self.rendered = True
|
||||
|
||||
def get_ship_size(self) -> (int, int):
|
||||
@ -359,6 +361,12 @@ class SR1ShipRender(BaseScreen):
|
||||
|
||||
image_data = screenshot(self.window_pointer)
|
||||
image_data.save('test.png')
|
||||
elif command.re_match('gen_img'):
|
||||
if not self.rendered:
|
||||
return
|
||||
# ship_size = self.ship.size
|
||||
base_textures = Texture.create(100, 100)
|
||||
...
|
||||
|
||||
def on_mouse_drag(self, x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int, window: "ClientWindow"):
|
||||
if not self.focus:
|
Loading…
Reference in New Issue
Block a user