DR -> DR SDK #16
15
.github/workflows/nuitka.yml
vendored
15
.github/workflows/nuitka.yml
vendored
@ -5,9 +5,7 @@ name: Build
|
|||||||
on:
|
on:
|
||||||
# 触发条件
|
# 触发条件
|
||||||
push:
|
push:
|
||||||
branches: ["main"]
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: ["main"]
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
# 主任务
|
# 主任务
|
||||||
@ -80,18 +78,6 @@ jobs:
|
|||||||
Write-Output $infos >> $env:GITHUB_ENV
|
Write-Output $infos >> $env:GITHUB_ENV
|
||||||
python .github/workflows/get_info.py
|
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
|
- name: Build on Windows
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
@ -165,7 +151,6 @@ jobs:
|
|||||||
Copy-Item libs/fonts build\DR.dist\libs\fonts -Recurse
|
Copy-Item libs/fonts build\DR.dist\libs\fonts -Recurse
|
||||||
# Copy-Item libs\pyglet\ build\DR.dist -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
|
Rename-Item build/DR.dist Difficult-Rocket
|
||||||
python ./.github/workflows/post_compile.py
|
python ./.github/workflows/post_compile.py
|
||||||
|
|
||||||
|
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@ -2,5 +2,9 @@
|
|||||||
"rust-analyzer.linkedProjects": [
|
"rust-analyzer.linkedProjects": [
|
||||||
"libs/Difficult_Rocket_rs/src/Cargo.toml",
|
"libs/Difficult_Rocket_rs/src/Cargo.toml",
|
||||||
"libs/pyglet_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 time
|
||||||
import cProfile
|
import cProfile
|
||||||
import traceback
|
import traceback
|
||||||
|
import threading
|
||||||
|
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ def print_path() -> None:
|
|||||||
# 输出一遍大部分文件位置相关信息 以后可能会加到logs里
|
# 输出一遍大部分文件位置相关信息 以后可能会加到logs里
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> int:
|
||||||
print(hi) # hi!
|
print(hi) # hi!
|
||||||
start_time_ns = time.time_ns()
|
start_time_ns = time.time_ns()
|
||||||
start_time_perf_ns = time.perf_counter_ns()
|
start_time_perf_ns = time.perf_counter_ns()
|
||||||
@ -57,7 +58,7 @@ def main() -> None:
|
|||||||
print('pyglet_rs available:', get_version_str())
|
print('pyglet_rs available:', get_version_str())
|
||||||
print('trying to patch pyglet_rs')
|
print('trying to patch pyglet_rs')
|
||||||
patch_vector()
|
patch_vector()
|
||||||
except ImportError as e:
|
except ImportError:
|
||||||
print('pyglet_rs import error')
|
print('pyglet_rs import error')
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
try:
|
try:
|
||||||
@ -82,7 +83,8 @@ def main() -> None:
|
|||||||
if DR_option.crash_report_test:
|
if DR_option.crash_report_test:
|
||||||
raise TestError('debugging') # debug 嘛,试试crash
|
raise TestError('debugging') # debug 嘛,试试crash
|
||||||
except Exception as exp: # 出毛病了
|
except Exception as exp: # 出毛病了
|
||||||
print(error_format['error.happen']) #
|
# 解析错误信息
|
||||||
|
print(error_format['error.happen'])
|
||||||
error = traceback.format_exc()
|
error = traceback.format_exc()
|
||||||
name = type(exp).__name__
|
name = type(exp).__name__
|
||||||
if name in error_format:
|
if name in error_format:
|
||||||
@ -90,6 +92,7 @@ def main() -> None:
|
|||||||
else:
|
else:
|
||||||
print(error_format['error.unknown'])
|
print(error_format['error.unknown'])
|
||||||
print(error)
|
print(error)
|
||||||
|
# 输出 crash 信息
|
||||||
crash.create_crash_report(error)
|
crash.create_crash_report(error)
|
||||||
cache_steam = StringIO()
|
cache_steam = StringIO()
|
||||||
crash.write_info_to_cache(cache_steam)
|
crash.write_info_to_cache(cache_steam)
|
||||||
@ -99,8 +102,20 @@ def main() -> None:
|
|||||||
crash.record_thread = False
|
crash.record_thread = False
|
||||||
print(crash.all_thread)
|
print(crash.all_thread)
|
||||||
print(crash.all_process)
|
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 已关闭")
|
print("Difficult_Rocket 已关闭")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
sys.exit(main())
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
# All rights reserved
|
# All rights reserved
|
||||||
# -------------------------------
|
# -------------------------------
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
import importlib
|
import importlib
|
||||||
@ -15,22 +14,19 @@ from pathlib import Path
|
|||||||
from typing import Optional, List, Tuple
|
from typing import Optional, List, Tuple
|
||||||
|
|
||||||
from Difficult_Rocket.api.types import Options
|
from Difficult_Rocket.api.types import Options
|
||||||
|
from Difficult_Rocket.utils.new_thread import new_thread
|
||||||
|
|
||||||
from libs.MCDR.version import Version
|
from libs.MCDR.version import Version
|
||||||
|
|
||||||
game_version = Version("0.7.2.2") # 游戏版本
|
game_version = Version("0.7.2.2") # 游戏版本
|
||||||
build_version = Version("1.2.1.0") # 编译文件版本(与游戏本体无关)
|
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
|
__version__ = game_version
|
||||||
|
|
||||||
# TODO 解耦 DR SDK 与 DR_mod 和 DR_rs
|
long_version: int = 15
|
||||||
DR_rust_version = Version("0.2.6.1") # DR 的 Rust 编写部分的版本
|
|
||||||
# 后面会移除的 DR_rs 相关信息
|
|
||||||
# DR_rs和 DR_mod 的部分正在和 DR SDK 解耦
|
|
||||||
|
|
||||||
long_version: int = 14
|
|
||||||
"""
|
"""
|
||||||
long_version: 一个用于标记内部协议的整数
|
long_version: 一个用于标记内部协议的整数
|
||||||
|
15: 完全移除 DR_rust 相关内容 解耦完成
|
||||||
14: BaseScreen 的每一个函数都添加了一个参数: window: "ClientWindow"
|
14: BaseScreen 的每一个函数都添加了一个参数: window: "ClientWindow"
|
||||||
13: 为 DR_runtime 添加 API_version
|
13: 为 DR_runtime 添加 API_version
|
||||||
12: 去除 DR_runtime 的 global_logger
|
12: 去除 DR_runtime 的 global_logger
|
||||||
@ -66,36 +62,15 @@ class _DR_option(Options):
|
|||||||
DR_rust_available: bool = False
|
DR_rust_available: bool = False
|
||||||
use_cProfile: bool = False
|
use_cProfile: bool = False
|
||||||
use_local_logging: bool = False
|
use_local_logging: bool = False
|
||||||
use_DR_rust: bool = True
|
|
||||||
|
|
||||||
# tests
|
# tests
|
||||||
playing: bool = False
|
playing: bool = False
|
||||||
debugging: bool = False
|
debugging: bool = False
|
||||||
crash_report_test: bool = True
|
crash_report_test: bool = False
|
||||||
|
|
||||||
# window option
|
# window option
|
||||||
gui_scale: float = 1.0 # default 1.0 2.0 -> 2x 3 -> 3x
|
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
|
@property
|
||||||
def std_font_size(self) -> int:
|
def std_font_size(self) -> int:
|
||||||
return round(12 * self.gui_scale)
|
return round(12 * self.gui_scale)
|
||||||
@ -114,9 +89,6 @@ class _DR_runtime(Options):
|
|||||||
DR_long_version: int = long_version # DR SDK 内部协议版本 (不要问我为什么不用 Version,我也在考虑)
|
DR_long_version: int = long_version # DR SDK 内部协议版本 (不要问我为什么不用 Version,我也在考虑)
|
||||||
|
|
||||||
DR_Mod_List: List[Tuple[str, Version]] = [] # DR Mod 列表 (name, 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
|
# run status
|
||||||
running: bool = False
|
running: bool = False
|
||||||
@ -132,15 +104,6 @@ class _DR_runtime(Options):
|
|||||||
language: str = 'zh-CN'
|
language: str = 'zh-CN'
|
||||||
default_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:
|
def load_file(self) -> bool:
|
||||||
with contextlib.suppress(FileNotFoundError):
|
with contextlib.suppress(FileNotFoundError):
|
||||||
with open('./configs/main.toml', 'r', encoding='utf-8') as f:
|
with open('./configs/main.toml', 'r', encoding='utf-8') as f:
|
||||||
@ -151,33 +114,34 @@ class _DR_runtime(Options):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def load_mods(self) -> None:
|
|
||||||
mod_list = self.find_mods()
|
|
||||||
|
|
||||||
def find_mods(self) -> List[str]:
|
def find_mods(self) -> List[str]:
|
||||||
mods = []
|
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)
|
sys.path.append(self.mod_path)
|
||||||
for mod_path in paths:
|
for mod_path in paths:
|
||||||
try:
|
try:
|
||||||
if mod_path.is_dir() and mod_path.name != '__pycache__': # 处理文件夹 mod
|
if mod_path.is_dir() and mod_path.name != '__pycache__': # 处理文件夹 mod
|
||||||
if importlib.util.find_spec(mod_path.name) is not None:
|
if importlib.util.find_spec(mod_path.name) is not None:
|
||||||
module = importlib.import_module(mod_path.name)
|
|
||||||
mods.append(mod_path.name)
|
mods.append(mod_path.name)
|
||||||
else:
|
else:
|
||||||
print(f'can not import mod {mod_path} because importlib can not find spec')
|
print(f'can not import mod {mod_path} because importlib can not find spec')
|
||||||
elif mod_path.suffix in ('.pyz', '.zip'): # 处理压缩包 mod
|
elif mod_path.suffix in ('.pyz', '.zip'): # 处理压缩包 mod
|
||||||
if importlib.util.find_spec(mod_path.name) is not None:
|
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)
|
mods.append(mod_path.name)
|
||||||
elif mod_path.suffix == '.py': # 处理单文件 mod
|
elif mod_path.suffix == '.py': # 处理单文件 mod
|
||||||
print(f'importing mod {mod_path=} {mod_path.stem}')
|
print(f'importing mod {mod_path=} {mod_path.stem}')
|
||||||
module = importlib.import_module(mod_path.stem)
|
if importlib.util.find_spec(mod_path.stem) is not None:
|
||||||
mods.append(mod_path.stem)
|
mods.append(mod_path.stem)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print(f'ImportError when loading mod {mod_path}')
|
print(f'ImportError when loading mod {mod_path}')
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
self.DR_Mod_List = [(mod, Version('0.0.0-unknown')) for mod in mods]
|
|
||||||
return mods
|
return mods
|
||||||
|
|
||||||
|
|
||||||
@ -185,9 +149,6 @@ DR_option = _DR_option()
|
|||||||
DR_runtime = _DR_runtime()
|
DR_runtime = _DR_runtime()
|
||||||
|
|
||||||
if DR_option.playing:
|
if DR_option.playing:
|
||||||
from Difficult_Rocket.utils import new_thread
|
|
||||||
|
|
||||||
|
|
||||||
def think_it(something):
|
def think_it(something):
|
||||||
return something
|
return something
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ github: @shenjackyuanjie
|
|||||||
gitee: @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
|
github: @shenjackyuanjie
|
||||||
gitee: @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
|
import math
|
||||||
from typing import Dict, Union, List, Optional
|
from typing import Dict, Union, Optional
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
# pyglet
|
# pyglet
|
||||||
# import pyglet
|
|
||||||
from pyglet.image import load, AbstractImage
|
from pyglet.image import load, AbstractImage
|
||||||
|
|
||||||
# Difficult Rocket
|
# Difficult Rocket
|
||||||
from Difficult_Rocket.api.types import Options
|
from Difficult_Rocket.utils.options import Options
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -30,7 +29,6 @@ class SR1PartData:
|
|||||||
flip_y: bool
|
flip_y: bool
|
||||||
explode: bool
|
explode: bool
|
||||||
textures: Optional[str] = None
|
textures: Optional[str] = None
|
||||||
connections: Optional[List[int]] = None
|
|
||||||
|
|
||||||
|
|
||||||
class SR1Textures(Options):
|
class SR1Textures(Options):
|
||||||
@ -145,23 +143,3 @@ def xml_bool(bool_like: Union[str, int, bool, None]) -> bool:
|
|||||||
if isinstance(bool_like, int):
|
if isinstance(bool_like, int):
|
||||||
return bool_like != 0
|
return bool_like != 0
|
||||||
return False if bool_like == '0' else bool_like.lower() != 'false'
|
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
|
# All rights reserved
|
||||||
# -------------------------------
|
# -------------------------------
|
||||||
|
|
||||||
"""
|
from Difficult_Rocket.utils.options import Options, FontData, Fonts, \
|
||||||
writen by shenjackyuanjie
|
OptionsError, OptionNameNotDefined, OptionNotFound, \
|
||||||
mail: 3695888@qq.com
|
get_type_hints_
|
||||||
github: @shenjackyuanjie
|
|
||||||
gitee: @shenjackyuanjie
|
|
||||||
"""
|
|
||||||
|
|
||||||
import traceback
|
__all__ = [
|
||||||
from dataclasses import dataclass
|
# main class
|
||||||
from typing import get_type_hints, Type, List, Union, Dict, Any, Callable, Tuple, Optional, TYPE_CHECKING
|
'Options',
|
||||||
|
|
||||||
# from Difficult Rocket
|
# data class
|
||||||
|
'FontData',
|
||||||
|
'Fonts',
|
||||||
|
|
||||||
__all__ = ['get_type_hints_',
|
# exception
|
||||||
'Options',
|
'OptionsError',
|
||||||
'Fonts',
|
'OptionNameNotDefined',
|
||||||
'FontData',
|
'OptionNotFound',
|
||||||
'OptionsError',
|
|
||||||
'OptionNotFound',
|
|
||||||
'OptionNameNotDefined']
|
|
||||||
|
|
||||||
|
# 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
|
# All rights reserved
|
||||||
# -------------------------------
|
# -------------------------------
|
||||||
|
|
||||||
"""
|
|
||||||
writen by shenjackyuanjie
|
|
||||||
mail: 3695888@qq.com
|
|
||||||
github: @shenjackyuanjie
|
|
||||||
gitee: @shenjackyuanjie
|
|
||||||
"""
|
|
||||||
import inspect
|
|
||||||
# system function
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
|
import inspect
|
||||||
import functools
|
import functools
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from typing import List, Callable
|
from typing import Callable, Dict, List, TYPE_CHECKING
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
# third function
|
# third function
|
||||||
@ -32,6 +26,8 @@ from pyglet.window import Window
|
|||||||
from pyglet.window import key, mouse
|
from pyglet.window import key, mouse
|
||||||
|
|
||||||
# Difficult_Rocket function
|
# Difficult_Rocket function
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from Difficult_Rocket.main import Game
|
||||||
from Difficult_Rocket.utils import tools
|
from Difficult_Rocket.utils import tools
|
||||||
from Difficult_Rocket.api.types import Options
|
from Difficult_Rocket.api.types import Options
|
||||||
from Difficult_Rocket.command import line, tree
|
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.client.guis.widgets import InputBox
|
||||||
from Difficult_Rocket.exception.command import CommandError
|
from Difficult_Rocket.exception.command import CommandError
|
||||||
from Difficult_Rocket.exception.language import LanguageNotFound
|
from Difficult_Rocket.exception.language import LanguageNotFound
|
||||||
from Difficult_Rocket.client.render.sr1_ship import SR1ShipRender
|
|
||||||
from Difficult_Rocket.client.screen import DRScreen, DRDEBUGScreen
|
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}"
|
caption: str = "Difficult Rocket v{DR_version}|DR_rs v{DR_Rust_get_version}"
|
||||||
|
|
||||||
def load_file(self) -> None:
|
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.fps = int(file['runtime']['fps'])
|
||||||
self.width = int(file['window']['width'])
|
self.width = int(file['window']['width'])
|
||||||
self.height = int(file['window']['height'])
|
self.height = int(file['window']['height'])
|
||||||
@ -70,7 +65,7 @@ class ClientOption(Options):
|
|||||||
|
|
||||||
|
|
||||||
class Client:
|
class Client:
|
||||||
def __init__(self, net_mode='local'):
|
def __init__(self, game: "Game", net_mode='local'):
|
||||||
start_time = time.time_ns()
|
start_time = time.time_ns()
|
||||||
# logging
|
# logging
|
||||||
self.logger = logging.getLogger('client')
|
self.logger = logging.getLogger('client')
|
||||||
@ -82,20 +77,14 @@ class Client:
|
|||||||
self.process_name = 'Client process'
|
self.process_name = 'Client process'
|
||||||
self.process_pid = os.getpid()
|
self.process_pid = os.getpid()
|
||||||
self.net_mode = net_mode
|
self.net_mode = net_mode
|
||||||
file_drop = bool(
|
self.game = game
|
||||||
pyglet.compat_platform != 'darwin' or DR_option.pyglet_macosx_dev_test
|
self.window = ClientWindow(game=game, net_mode=self.net_mode,
|
||||||
)
|
width=self.config.width, height=self.config.height,
|
||||||
self.window = ClientWindow(net_mode=self.net_mode, width=self.config.width, height=self.config.height,
|
|
||||||
fullscreen=self.config.fullscreen, caption=self.config.caption,
|
fullscreen=self.config.fullscreen, caption=self.config.caption,
|
||||||
resizable=self.config.resizeable, visible=self.config.visible,
|
resizable=self.config.resizeable, visible=self.config.visible,
|
||||||
file_drops=file_drop)
|
file_drops=True)
|
||||||
end_time = time.time_ns()
|
end_time = time.time_ns()
|
||||||
self.use_time = end_time - start_time
|
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.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))
|
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)
|
@functools.wraps(func)
|
||||||
def warped(self: "ClientWindow", *args, **kwargs):
|
def warped(self: "ClientWindow", *args, **kwargs):
|
||||||
result = func(self, *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
|
a_screen.window_pointer = self
|
||||||
# 提前帮子窗口设置好指针
|
# 提前帮子窗口设置好指针
|
||||||
if hasattr(a_screen, func.__name__):
|
if hasattr(a_screen, func.__name__):
|
||||||
@ -136,7 +125,7 @@ def _call_screen_after(func: Callable) -> Callable:
|
|||||||
def _call_screen_before(func: Callable) -> Callable:
|
def _call_screen_before(func: Callable) -> Callable:
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def warped(self: "ClientWindow", *args, **kwargs):
|
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
|
a_screen.window_pointer = self
|
||||||
# 提前帮子窗口设置好指针
|
# 提前帮子窗口设置好指针
|
||||||
if hasattr(a_screen, func.__name__):
|
if hasattr(a_screen, func.__name__):
|
||||||
@ -153,7 +142,7 @@ def _call_screen_before(func: Callable) -> Callable:
|
|||||||
|
|
||||||
class ClientWindow(Window):
|
class ClientWindow(Window):
|
||||||
|
|
||||||
def __init__(self, net_mode='local', *args, **kwargs):
|
def __init__(self, game: "Game", net_mode='local', *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@param net_mode:
|
@param net_mode:
|
||||||
@ -166,8 +155,10 @@ class ClientWindow(Window):
|
|||||||
self.logger = logging.getLogger('client')
|
self.logger = logging.getLogger('client')
|
||||||
self.logger.info(tr().window.setup.start())
|
self.logger.info(tr().window.setup.start())
|
||||||
# value
|
# value
|
||||||
|
self.game = game
|
||||||
self.net_mode = net_mode
|
self.net_mode = net_mode
|
||||||
self.run_input = False
|
self.run_input = False
|
||||||
|
self.command_list: List[str] = []
|
||||||
# configs
|
# configs
|
||||||
self.main_config = tools.load_file('./configs/main.toml')
|
self.main_config = tools.load_file('./configs/main.toml')
|
||||||
self.game_config = tools.load_file('./configs/game.config')
|
self.game_config = tools.load_file('./configs/game.config')
|
||||||
@ -181,7 +172,7 @@ class ClientWindow(Window):
|
|||||||
# frame
|
# frame
|
||||||
self.frame = pyglet.gui.Frame(self, order=20)
|
self.frame = pyglet.gui.Frame(self, order=20)
|
||||||
self.M_frame = pyglet.gui.MovableFrame(self, modifier=key.LCTRL)
|
self.M_frame = pyglet.gui.MovableFrame(self, modifier=key.LCTRL)
|
||||||
self.screen_list: List[BaseScreen] = []
|
self.screen_list: Dict[str, BaseScreen] = {}
|
||||||
# setup
|
# setup
|
||||||
self.setup()
|
self.setup()
|
||||||
# 命令显示
|
# 命令显示
|
||||||
@ -194,7 +185,7 @@ class ClientWindow(Window):
|
|||||||
self.set_handlers(self.input_box)
|
self.set_handlers(self.input_box)
|
||||||
self.input_box.enabled = True
|
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()))
|
self.logger.info(tr().window.os.pid_is().format(os.getpid(), os.getppid()))
|
||||||
end_time = time.time_ns()
|
end_time = time.time_ns()
|
||||||
@ -208,9 +199,9 @@ class ClientWindow(Window):
|
|||||||
self.set_icon(pyglet.image.load('./textures/icon.png'))
|
self.set_icon(pyglet.image.load('./textures/icon.png'))
|
||||||
self.load_fonts()
|
self.load_fonts()
|
||||||
# TODO 读取配置文件,加载不同的屏幕,解耦
|
# TODO 读取配置文件,加载不同的屏幕,解耦
|
||||||
self.screen_list.append(DRDEBUGScreen(self))
|
self.screen_list['DR_debug'] = DRDEBUGScreen(self)
|
||||||
self.screen_list.append(DRScreen(self))
|
self.screen_list['DR_main'] = DRScreen(self)
|
||||||
self.screen_list.append(SR1ShipRender(self))
|
self.game.dispatch_event('on_client_start', game=self.game, client=self)
|
||||||
|
|
||||||
def load_fonts(self) -> None:
|
def load_fonts(self) -> None:
|
||||||
fonts_folder_path = self.main_config['runtime']['fonts_folder']
|
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.set_icon(pyglet.image.load('./textures/icon.png'))
|
||||||
self.run_input = True
|
self.run_input = True
|
||||||
self.read_input()
|
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)
|
@new_thread('window read_input', daemon=True)
|
||||||
def read_input(self):
|
def read_input(self):
|
||||||
self.logger.debug('read_input start')
|
self.logger.debug('read_input start')
|
||||||
while self.run_input:
|
while self.run_input:
|
||||||
get = input(">")
|
try:
|
||||||
|
get = input(">")
|
||||||
|
except (EOFError, KeyboardInterrupt):
|
||||||
|
self.run_input = False
|
||||||
|
break
|
||||||
if get in ('', ' ', '\n', '\r'):
|
if get in ('', ' ', '\n', '\r'):
|
||||||
continue
|
continue
|
||||||
if get == 'stop':
|
if get == 'stop':
|
||||||
self.run_input = False
|
self.run_input = False
|
||||||
try:
|
self.command_list.append(get)
|
||||||
self.on_command(line.CommandText(get))
|
|
||||||
except CommandError:
|
|
||||||
self.logger.error(traceback.format_exc())
|
|
||||||
self.logger.debug('read_input end')
|
self.logger.debug('read_input end')
|
||||||
|
|
||||||
@new_thread('window save_info')
|
@new_thread('window save_info')
|
||||||
def save_info(self):
|
def save_info(self):
|
||||||
self.logger.info(tr().client.config.save.start())
|
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']['width'] = self.width
|
||||||
config_file['window']['height'] = self.height
|
config_file['window']['height'] = self.height
|
||||||
config_file['runtime']['language'] = DR_runtime.language
|
config_file['runtime']['language'] = DR_runtime.language
|
||||||
@ -253,8 +252,8 @@ class ClientWindow(Window):
|
|||||||
client api
|
client api
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def add_sub_screen(self, sub_screen: BaseScreen.__class__):
|
def add_sub_screen(self, title: str, sub_screen: type(BaseScreen)):
|
||||||
self.screen_list.append(sub_screen(self))
|
self.screen_list[title] = sub_screen(self)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
draws and some event
|
draws and some event
|
||||||
@ -268,9 +267,13 @@ class ClientWindow(Window):
|
|||||||
|
|
||||||
@_call_screen_after
|
@_call_screen_after
|
||||||
def on_draw(self, *dt):
|
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)
|
pyglet.gl.glClearColor(0.1, 0, 0, 0.0)
|
||||||
self.clear()
|
self.clear()
|
||||||
|
self.draw_update(float(self.SPF))
|
||||||
self.draw_batch()
|
self.draw_batch()
|
||||||
|
|
||||||
@_call_screen_after
|
@_call_screen_after
|
||||||
@ -434,6 +437,7 @@ class ClientWindow(Window):
|
|||||||
|
|
||||||
@_call_screen_before
|
@_call_screen_before
|
||||||
def on_close(self, source: str = 'window') -> None:
|
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_get().format(tr().game[source]()))
|
||||||
self.logger.info(tr().window.game.stop())
|
self.logger.info(tr().window.game.stop())
|
||||||
self.fps_log.check_list = False
|
self.fps_log.check_list = False
|
||||||
|
@ -15,17 +15,22 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
|
import importlib
|
||||||
|
import importlib.util
|
||||||
import logging.config
|
import logging.config
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
|
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if __name__ == '__main__': # been start will not run this
|
if __name__ == '__main__': # been start will not run this
|
||||||
sys.path.append('/bin/libs')
|
sys.path.append('/bin/libs')
|
||||||
sys.path.append('/bin')
|
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.crash import write_info_to_cache
|
||||||
from Difficult_Rocket.utils import tools
|
from Difficult_Rocket.utils import tools
|
||||||
from Difficult_Rocket.utils.translate import tr
|
from Difficult_Rocket.utils.translate import tr
|
||||||
@ -58,6 +63,9 @@ class Game:
|
|||||||
# version check
|
# version check
|
||||||
self.log_env()
|
self.log_env()
|
||||||
self.python_version_check()
|
self.python_version_check()
|
||||||
|
self.loaded_mods = []
|
||||||
|
# self.client = client.Client
|
||||||
|
# self.server = server.Server
|
||||||
self.setup()
|
self.setup()
|
||||||
|
|
||||||
def log_env(self) -> None:
|
def log_env(self) -> None:
|
||||||
@ -66,8 +74,67 @@ class Game:
|
|||||||
text = cache_steam.getvalue()
|
text = cache_steam.getvalue()
|
||||||
self.logger.info(text)
|
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:
|
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')
|
self.server = server.Server(net_mode='local')
|
||||||
|
|
||||||
def python_version_check(self) -> None: # best 3.8+ and write at 3.8.10
|
def python_version_check(self) -> None: # best 3.8+ and write at 3.8.10
|
||||||
|
@ -4,68 +4,3 @@
|
|||||||
# All rights reserved
|
# 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
|
# All rights reserved
|
||||||
# -------------------------------
|
# -------------------------------
|
||||||
|
|
||||||
"""
|
from Difficult_Rocket.api.screen import BaseScreen
|
||||||
writen by shenjackyuanjie
|
from Difficult_Rocket import DR_option, DR_runtime
|
||||||
mail: 3695888@qq.com
|
|
||||||
github: @shenjackyuanjie
|
|
||||||
gitee: @shenjackyuanjie
|
|
||||||
"""
|
|
||||||
|
@ -4,17 +4,3 @@
|
|||||||
# All rights reserved
|
# 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 functools
|
||||||
import inspect
|
import inspect
|
||||||
import threading
|
import threading
|
||||||
from Difficult_Rocket import crash, DR_option
|
from typing import Optional, Callable, Union, List
|
||||||
from typing import Optional, Callable
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This part of code come from MCDReforged(https://github.com/Fallen-Breath/MCDReforged)
|
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)
|
(have some changes)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'new_thread',
|
||||||
|
'FunctionThread'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
record_thread = False
|
||||||
|
record_destination: List[Callable[['FunctionThread'], None]] = []
|
||||||
|
|
||||||
|
|
||||||
def copy_signature(target: Callable, origin: Callable) -> Callable:
|
def copy_signature(target: Callable, origin: Callable) -> Callable:
|
||||||
"""
|
"""
|
||||||
@ -28,10 +36,13 @@ def copy_signature(target: Callable, origin: Callable) -> Callable:
|
|||||||
|
|
||||||
|
|
||||||
class FunctionThread(threading.Thread):
|
class FunctionThread(threading.Thread):
|
||||||
|
"""
|
||||||
|
A Thread subclass which is used in decorator :func:`new_thread` to wrap a synchronized function call
|
||||||
|
"""
|
||||||
__NONE = object()
|
__NONE = object()
|
||||||
|
|
||||||
def __init__(self, target, name, args, kwargs):
|
def __init__(self, target, name, args, kwargs, daemon):
|
||||||
super().__init__(target=target, args=args, kwargs=kwargs, name=name)
|
super().__init__(target=target, args=args, kwargs=kwargs, name=name, daemon=daemon)
|
||||||
self.__return_value = self.__NONE
|
self.__return_value = self.__NONE
|
||||||
self.__error = None
|
self.__error = None
|
||||||
|
|
||||||
@ -40,12 +51,33 @@ class FunctionThread(threading.Thread):
|
|||||||
self.__return_value = target(*args_, **kwargs_)
|
self.__return_value = target(*args_, **kwargs_)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.__error = e
|
self.__error = e
|
||||||
print(e)
|
|
||||||
raise e from None
|
raise e from None
|
||||||
|
|
||||||
self._target = wrapped_target
|
self._target = wrapped_target
|
||||||
|
|
||||||
def get_return_value(self, block: bool = False, timeout: Optional[float] = None):
|
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:
|
if block:
|
||||||
self.join(timeout)
|
self.join(timeout)
|
||||||
if self.__return_value is self.__NONE:
|
if self.__return_value is self.__NONE:
|
||||||
@ -54,30 +86,57 @@ class FunctionThread(threading.Thread):
|
|||||||
raise self.__error
|
raise self.__error
|
||||||
return self.__return_value
|
return self.__return_value
|
||||||
|
|
||||||
def join(self, timeout: Optional[float] = None) -> None:
|
|
||||||
super().join(timeout)
|
|
||||||
|
|
||||||
def start(self) -> None:
|
def new_thread(arg: Optional[Union[str, Callable]] = None,
|
||||||
super().start()
|
|
||||||
|
|
||||||
|
|
||||||
def new_thread(thread_name: Optional[str or Callable] = None,
|
|
||||||
daemon: bool = False,
|
daemon: bool = False,
|
||||||
log_thread: bool = True):
|
log_thread: bool = True):
|
||||||
"""
|
"""
|
||||||
Use a new thread to execute the decorated function
|
This is a one line solution to make your function executes in parallels.
|
||||||
The function return value will be set to the thread instance that executes this function
|
When decorated with this decorator, functions will be executed in a new daemon thread
|
||||||
The name of the thread can be specified in parameter
|
|
||||||
|
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):
|
def wrapper(func):
|
||||||
@functools.wraps(func) # to preserve the origin function information
|
@functools.wraps(func) # to preserve the origin function information
|
||||||
def wrap(*args, **kwargs):
|
def wrap(*args, **kwargs):
|
||||||
thread = FunctionThread(target=func, args=args, kwargs=kwargs, name=thread_name)
|
thread = FunctionThread(target=func, args=args, kwargs=kwargs, name=thread_name, daemon=daemon)
|
||||||
thread.daemon = daemon
|
if record_thread:
|
||||||
|
for destination in record_destination:
|
||||||
|
destination(thread)
|
||||||
thread.start()
|
thread.start()
|
||||||
if log_thread and DR_option.record_threads:
|
|
||||||
crash.all_thread.append(thread)
|
|
||||||
return thread
|
return thread
|
||||||
|
|
||||||
# bring the signature of the func to the wrap function
|
# 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
|
wrap.original = func # access this field to get the original function
|
||||||
return wrap
|
return wrap
|
||||||
|
|
||||||
# Directly use @on_new_thread without ending brackets case
|
# Directly use @new_thread without ending brackets case, e.g. @new_thread
|
||||||
if isinstance(thread_name, Callable):
|
if isinstance(arg, Callable):
|
||||||
this_is_a_function = thread_name
|
|
||||||
thread_name = None
|
thread_name = None
|
||||||
return wrapper(this_is_a_function)
|
return wrapper(arg)
|
||||||
# Use @on_new_thread with ending brackets case
|
# Use @new_thread with ending brackets case, e.g. @new_thread('A'), @new_thread()
|
||||||
return wrapper
|
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
|
self._config.is_final = True
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs) -> Union[dict, list, int, str]:
|
def __call__(self, *args, **kwargs) -> str:
|
||||||
return self.__str__()
|
return str(self)
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
return self.__copy__()
|
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]
|
Write-Output $args[0]
|
||||||
|
|
||||||
@ -23,6 +23,6 @@ if ($do -or "311" -in $args) {
|
|||||||
python3.11 setup.py build
|
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_level = "Log file record level : "
|
||||||
logger.logfile_fmt = "Log file record format : "
|
logger.logfile_fmt = "Log file record format : "
|
||||||
logger.logfile_datefmt = "Log file date 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]
|
[client]
|
||||||
setup.start = "Client start loading"
|
setup.start = "Client start loading"
|
||||||
|
@ -21,6 +21,16 @@ logger.logfile_level = "日志文件记录级别:"
|
|||||||
logger.logfile_fmt = "日志文件记录格式:"
|
logger.logfile_fmt = "日志文件记录格式:"
|
||||||
logger.logfile_datefmt = "日志文件日期格式:"
|
logger.logfile_datefmt = "日志文件日期格式:"
|
||||||
game_start.at = "游戏主线程开始于:"
|
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]
|
[client]
|
||||||
setup.start = "客户端加载开始"
|
setup.start = "客户端加载开始"
|
||||||
|
@ -7,8 +7,8 @@ fonts_folder = "libs/fonts"
|
|||||||
|
|
||||||
[window]
|
[window]
|
||||||
style = "None"
|
style = "None"
|
||||||
width = 1406
|
width = 2571
|
||||||
height = 900
|
height = 1510
|
||||||
visible = true
|
visible = true
|
||||||
gui_scale = 1
|
gui_scale = 1
|
||||||
caption = "Difficult Rocket v{DR_version}|DR_rs v{DR_Rust_get_version}"
|
caption = "Difficult Rocket v{DR_version}|DR_rs v{DR_Rust_get_version}"
|
||||||
@ -19,3 +19,5 @@ full_screen = false
|
|||||||
width = 1024
|
width = 1024
|
||||||
height = 768
|
height = 768
|
||||||
gui_scale = 1
|
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)
|
- [![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
|
- 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 加载来啦!
|
> 啊哈! 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
|
### Remove
|
||||||
|
|
||||||
- `game.config`
|
- `game.config`
|
||||||
- 已删除
|
- 已删除
|
||||||
- Removed
|
- 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
|
### Changes
|
||||||
|
|
||||||
@ -53,6 +92,14 @@
|
|||||||
- 现在游戏崩溃时会自动在 stdio 中输出崩溃日志 内容跟 crash report 中的基本相同
|
- 现在游戏崩溃时会自动在 stdio 中输出崩溃日志 内容跟 crash report 中的基本相同
|
||||||
- Now when the game crashes, it will automatically output the crash log in stdio
|
- 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
|
- 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
|
### Docs
|
||||||
|
|
||||||
@ -67,6 +114,13 @@
|
|||||||
### Mod Loader
|
### Mod Loader
|
||||||
|
|
||||||
- `ModInfo`
|
- `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
|
## 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
|
- 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`
|
## 20230405 DR `0.7.2.1`
|
||||||
|
|
||||||
### Changes
|
### 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 import _user32
|
||||||
from pyglet.libs.win32.constants import *
|
from pyglet.libs.win32.constants import *
|
||||||
from pyglet.libs.win32.types import *
|
from pyglet.libs.win32.types import *
|
||||||
from pyglet.libs.win32.context_managers import device_context
|
|
||||||
|
|
||||||
|
|
||||||
class Win32Display(Display):
|
class Win32Display(Display):
|
||||||
@ -31,13 +30,13 @@ class Win32Screen(Screen):
|
|||||||
self._handle = handle
|
self._handle = handle
|
||||||
|
|
||||||
def get_matching_configs(self, template):
|
def get_matching_configs(self, template):
|
||||||
with device_context(None) as hdc:
|
hdc = _user32.GetDC(0)
|
||||||
canvas = Win32Canvas(self.display, 0, hdc)
|
canvas = Win32Canvas(self.display, 0, hdc)
|
||||||
configs = template.match(canvas)
|
configs = template.match(canvas)
|
||||||
# XXX deprecate config's being screen-specific
|
# XXX deprecate config's being screen-specific
|
||||||
for config in configs:
|
for config in configs:
|
||||||
config.screen = self
|
config.screen = self
|
||||||
|
_user32.ReleaseDC(0, hdc)
|
||||||
return configs
|
return configs
|
||||||
|
|
||||||
def get_device_name(self):
|
def get_device_name(self):
|
||||||
|
@ -9,7 +9,6 @@ from pyglet.font import base
|
|||||||
from pyglet.font import win32query
|
from pyglet.font import win32query
|
||||||
import pyglet.image
|
import pyglet.image
|
||||||
from pyglet.libs.win32.constants import *
|
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.types import *
|
||||||
from pyglet.libs.win32 import _gdi32 as gdi32, _user32 as user32
|
from pyglet.libs.win32 import _gdi32 as gdi32, _user32 as user32
|
||||||
from pyglet.libs.win32 import _kernel32 as kernel32
|
from pyglet.libs.win32 import _kernel32 as kernel32
|
||||||
@ -196,13 +195,14 @@ class Win32Font(base.Font):
|
|||||||
self.hfont = gdi32.CreateFontIndirectW(byref(self.logfont))
|
self.hfont = gdi32.CreateFontIndirectW(byref(self.logfont))
|
||||||
|
|
||||||
# Create a dummy DC for coordinate mapping
|
# Create a dummy DC for coordinate mapping
|
||||||
with device_context(None) as dc:
|
dc = user32.GetDC(0)
|
||||||
metrics = TEXTMETRIC()
|
metrics = TEXTMETRIC()
|
||||||
gdi32.SelectObject(dc, self.hfont)
|
gdi32.SelectObject(dc, self.hfont)
|
||||||
gdi32.GetTextMetricsA(dc, byref(metrics))
|
gdi32.GetTextMetricsA(dc, byref(metrics))
|
||||||
self.ascent = metrics.tmAscent
|
self.ascent = metrics.tmAscent
|
||||||
self.descent = -metrics.tmDescent
|
self.descent = -metrics.tmDescent
|
||||||
self.max_glyph_width = metrics.tmMaxCharWidth
|
self.max_glyph_width = metrics.tmMaxCharWidth
|
||||||
|
user32.ReleaseDC(0, dc)
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
gdi32.DeleteObject(self.hfont)
|
gdi32.DeleteObject(self.hfont)
|
||||||
@ -210,22 +210,22 @@ class Win32Font(base.Font):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def get_logfont(name, size, bold, italic, dpi):
|
def get_logfont(name, size, bold, italic, dpi):
|
||||||
# Create a dummy DC for coordinate mapping
|
# Create a dummy DC for coordinate mapping
|
||||||
with device_context(None) as dc:
|
dc = user32.GetDC(0)
|
||||||
if dpi is None:
|
if dpi is None:
|
||||||
dpi = 96
|
dpi = 96
|
||||||
logpixelsy = dpi
|
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
|
|
||||||
|
|
||||||
|
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
|
return logfont
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -71,7 +71,6 @@ appropriate typeface name and create the font using CreateFont or
|
|||||||
CreateFontIndirect.
|
CreateFontIndirect.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from pyglet.libs.win32.context_managers import device_context
|
|
||||||
|
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
|
|
||||||
@ -386,33 +385,36 @@ def query(charset=DEFAULT_CHARSET):
|
|||||||
global FONTDB
|
global FONTDB
|
||||||
|
|
||||||
# 1. Get device context of the entire screen
|
# 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
|
# 2a. Call with empty font name to query all available fonts
|
||||||
# (or fonts for the specified charset)
|
# (or fonts for the specified charset)
|
||||||
#
|
#
|
||||||
# NOTES:
|
# NOTES:
|
||||||
#
|
#
|
||||||
# * there are fonts that don't support ANSI charset
|
# * there are fonts that don't support ANSI charset
|
||||||
# * for DEFAULT_CHARSET font is passed to callback function as
|
# * for DEFAULT_CHARSET font is passed to callback function as
|
||||||
# many times as charsets it supports
|
# many times as charsets it supports
|
||||||
|
|
||||||
# [ ] font name should be less than 32 symbols with terminating \0
|
# [ ] font name should be less than 32 symbols with terminating \0
|
||||||
# [ ] check double purpose - enumerate all available font names
|
# [ ] check double purpose - enumerate all available font names
|
||||||
# - enumerate all available charsets for a single font
|
# - enumerate all available charsets for a single font
|
||||||
# - other params?
|
# - other params?
|
||||||
|
|
||||||
logfont = LOGFONTW(0, 0, 0, 0, 0, 0, 0, 0, charset, 0, 0, 0, 0, '')
|
logfont = LOGFONTW(0, 0, 0, 0, 0, 0, 0, 0, charset, 0, 0, 0, 0, '')
|
||||||
FONTDB = [] # clear cached FONTDB for enum_font_names callback
|
FONTDB = [] # clear cached FONTDB for enum_font_names callback
|
||||||
res = gdi32.EnumFontFamiliesExW(
|
res = gdi32.EnumFontFamiliesExW(
|
||||||
hdc, # handle to device context
|
hdc, # handle to device context
|
||||||
ctypes.byref(logfont),
|
ctypes.byref(logfont),
|
||||||
enum_font_names, # pointer to callback function
|
enum_font_names, # pointer to callback function
|
||||||
0, # lParam - application-supplied data
|
0, # lParam - application-supplied data
|
||||||
0) # dwFlags - reserved = 0
|
0) # dwFlags - reserved = 0
|
||||||
# res here is the last value returned by callback function
|
# res here is the last value returned by callback function
|
||||||
|
|
||||||
|
# 3. Release DC
|
||||||
|
user32.ReleaseDC(None, hdc)
|
||||||
|
|
||||||
return FONTDB
|
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
|
@property
|
||||||
def dx(self) -> float: ...
|
def dx(self) -> float: ...
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dy(self) -> float: ...
|
def dy(self) -> float: ...
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def zoom(self) -> float: ...
|
def zoom(self) -> float: ...
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def position(self) -> Tuple[float, float]: ...
|
def position(self) -> Tuple[float, float]: ...
|
||||||
|
|
||||||
@dx.setter
|
@dx.setter
|
||||||
def dx(self, value: float) -> None: ...
|
def dx(self, value: float) -> None: ...
|
||||||
|
|
||||||
@dy.setter
|
@dy.setter
|
||||||
def dy(self, value: float) -> None: ...
|
def dy(self, value: float) -> None: ...
|
||||||
|
|
||||||
@zoom.setter
|
@zoom.setter
|
||||||
def zoom(self, value: float) -> None: ...
|
def zoom(self, value: float) -> None: ...
|
||||||
|
|
||||||
@ -93,3 +87,17 @@ if TYPE_CHECKING:
|
|||||||
def as_dict(self) -> Dict[str, SR1PartType_rs]: ...
|
def as_dict(self) -> Dict[str, SR1PartType_rs]: ...
|
||||||
|
|
||||||
def get_part_type(self, name: 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
|
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||||
# All rights reserved
|
# All rights reserved
|
||||||
# -------------------------------
|
# -------------------------------
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
import shutil
|
import shutil
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
from setuptools_rust import Binding, RustExtension
|
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'
|
package_path = 'Difficult_Rocket_rs'
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='Difficult_Rocket_rs',
|
name='Difficult_Rocket_rs',
|
||||||
version=DR_runtime.DR_Rust_version.__str__(),
|
version="0.2.7.0",
|
||||||
author='shenjackyuanjie',
|
author='shenjackyuanjie',
|
||||||
author_email='3695888@qq.com',
|
author_email='3695888@qq.com',
|
||||||
rust_extensions=[RustExtension(target="Difficult_Rocket_rs.Difficult_Rocket_rs",
|
rust_extensions=[RustExtension(target="Difficult_Rocket_rs.Difficult_Rocket_rs",
|
@ -17,7 +17,7 @@ mod types;
|
|||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn get_version_str() -> String { "0.2.6.1".to_string() }
|
fn get_version_str() -> String { "0.2.7.0".to_string() }
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn test_call(py_obj: &PyAny) -> PyResult<bool> {
|
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::CameraRs>()?;
|
||||||
m.add_class::<render::camera::CenterCameraRs>()?;
|
m.add_class::<render::camera::CenterCameraRs>()?;
|
||||||
m.add_class::<render::screen::PartFrame>()?;
|
m.add_class::<render::screen::PartFrame>()?;
|
||||||
|
m.add_class::<python::data::PySR1Ship>()?;
|
||||||
m.add_class::<python::data::PySR1PartList>()?;
|
m.add_class::<python::data::PySR1PartList>()?;
|
||||||
m.add_class::<python::data::PySR1PartType>()?;
|
m.add_class::<python::data::PySR1PartType>()?;
|
||||||
Ok(())
|
Ok(())
|
@ -15,11 +15,10 @@
|
|||||||
pub mod plugin_trait {
|
pub mod plugin_trait {
|
||||||
pub struct ModInfo {
|
pub struct ModInfo {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub version: String
|
pub version: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ModInfoTrait {
|
pub trait ModInfoTrait {
|
||||||
fn info() -> ModInfo;
|
fn info() -> ModInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -13,8 +13,8 @@ pub mod data {
|
|||||||
|
|
||||||
use crate::sr1_data::part_list::RawPartList;
|
use crate::sr1_data::part_list::RawPartList;
|
||||||
use crate::sr1_data::ship::RawShip;
|
use crate::sr1_data::ship::RawShip;
|
||||||
|
use crate::types::sr1::{SR1PartData, SR1PartListTrait};
|
||||||
use crate::types::sr1::{SR1PartList, SR1PartType, SR1Ship};
|
use crate::types::sr1::{SR1PartList, SR1PartType, SR1Ship};
|
||||||
use crate::types::sr1::{SR1PartListTrait, SR1ShipTrait};
|
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
#[pyo3(name = "SR1PartType_rs")]
|
#[pyo3(name = "SR1PartType_rs")]
|
||||||
@ -70,6 +70,12 @@ pub mod data {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pyclass]
|
||||||
|
#[pyo3(name = "SR1PartData_rs")]
|
||||||
|
pub struct PySR1PartData {
|
||||||
|
pub data: SR1PartData,
|
||||||
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
#[pyo3(name = "SR1Ship_rs")]
|
#[pyo3(name = "SR1Ship_rs")]
|
||||||
#[pyo3(text_signature = "(file_path = './configs/dock1.xml', part_list = './configs/PartList.xml', ship_name = 'NewShip')")]
|
#[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 {
|
impl PySR1Ship {
|
||||||
#[new]
|
#[new]
|
||||||
fn new(file_path: String, part_list: String, ship_name: String) -> Self {
|
fn new(file_path: String, part_list: String, ship_name: String) -> Self {
|
||||||
let raw_ship: RawShip = RawShip::from_file(file_path).unwrap();
|
let ship = SR1Ship::from_file(file_path, Some(ship_name)).unwrap();
|
||||||
let ship = raw_ship.to_sr_ship(Some(ship_name));
|
|
||||||
let part_list = SR1PartList::from_file(part_list).unwrap();
|
let part_list = SR1PartList::from_file(part_list).unwrap();
|
||||||
Self { ship, part_list }
|
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::prelude::*;
|
||||||
use pyo3::types::PyDict;
|
use pyo3::types::PyDict;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum BoolString {
|
||||||
|
Bool(bool),
|
||||||
|
String(String),
|
||||||
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
#[pyo3(name = "TranslateConfig_rs")]
|
#[pyo3(name = "TranslateConfig_rs")]
|
||||||
#[pyo3(text_signature = "(language, raise_error = False, replace_normal = False, add_error = False, is_result = False, keep_get = False)")]
|
#[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),
|
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]
|
#[pyclass]
|
@ -9,10 +9,9 @@
|
|||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use rapier2d_f64::prelude::*;
|
use rapier2d_f64::prelude::*;
|
||||||
|
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
#[pyo3(name = "simluation")]
|
#[pyo3(name = "simluation")]
|
||||||
pub fn simluation() -> PyResult<()> {
|
pub fn simluation() -> () {
|
||||||
let mut rigid_body_set = RigidBodySet::new();
|
let mut rigid_body_set = RigidBodySet::new();
|
||||||
let mut collider_set = ColliderSet::new();
|
let mut collider_set = ColliderSet::new();
|
||||||
|
|
||||||
@ -62,6 +61,4 @@ pub fn simluation() -> PyResult<()> {
|
|||||||
let ball_body = &rigid_body_set[ball_body_handle];
|
let ball_body = &rigid_body_set[ball_body_handle];
|
||||||
println!("Ball altitude: {} {}", ball_body.translation().x, ball_body.translation().y);
|
println!("Ball altitude: {} {}", ball_body.translation().x, ball_body.translation().y);
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
@ -8,7 +8,9 @@
|
|||||||
|
|
||||||
pub mod sr1 {
|
pub mod sr1 {
|
||||||
use std::collections::HashMap;
|
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::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::{AttachPoint, AttachPoints, Engine, Lander, Rcs, Shape as RawShape, Solar, Tank};
|
||||||
use crate::sr1_data::part_list::{RawPartList, RawPartType, SR1PartTypeEnum};
|
use crate::sr1_data::part_list::{RawPartList, RawPartType, SR1PartTypeEnum};
|
||||||
@ -179,31 +181,49 @@ pub mod sr1 {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SR1PartList {
|
pub struct SR1PartList {
|
||||||
pub types: Vec<SR1PartType>,
|
pub types: Vec<SR1PartType>,
|
||||||
pub cache: Option<HashMap<String, SR1PartType>>,
|
pub cache: HashMap<String, SR1PartType>,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SR1PartList {
|
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]
|
#[inline]
|
||||||
pub fn from_file(file_name: String) -> Option<SR1PartList> {
|
pub fn from_file(file_name: String) -> Option<SR1PartList> {
|
||||||
if let Some(raw_list) = RawPartList::from_file(file_name) {
|
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
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_hash_map(&mut self) -> HashMap<String, SR1PartType> {
|
pub fn get_part_type(self, type_name: String) -> Option<SR1PartType> {
|
||||||
if let Some(map) = &self.cache {
|
if let Some(part) = self.cache.get(&type_name) {
|
||||||
return map.clone();
|
return Some(part.clone());
|
||||||
}
|
}
|
||||||
let mut map = HashMap::new();
|
None
|
||||||
for part in self.types.iter() {
|
|
||||||
map.insert(part.id.clone(), part.clone());
|
|
||||||
}
|
|
||||||
self.cache = Some(map.clone());
|
|
||||||
map
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
pub trait SR1PartTypeData {
|
||||||
@ -226,17 +246,6 @@ pub mod sr1 {
|
|||||||
fn to_raw_ship(&self) -> RawShip;
|
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 {
|
impl SR1PartListTrait for SR1PartList {
|
||||||
fn to_sr_part_list(&self, name: Option<String>) -> SR1PartList {
|
fn to_sr_part_list(&self, name: Option<String>) -> SR1PartList {
|
||||||
return if let Some(name) = name {
|
return if let Some(name) = name {
|
||||||
@ -491,6 +500,32 @@ pub mod sr1 {
|
|||||||
pub explode: bool,
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum SR1PartDataAttr {
|
pub enum SR1PartDataAttr {
|
||||||
Tank {
|
Tank {
|
||||||
@ -532,6 +567,18 @@ pub mod sr1 {
|
|||||||
pub disconnected: Option<Vec<(Vec<SR1PartData>, Option<Vec<Connection>>)>>,
|
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 {
|
impl SR1ShipTrait for SR1Ship {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_sr_ship(&self, name: Option<String>) -> SR1Ship {
|
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)]
|
#[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_width = width / 2.0;
|
||||||
let d_height = height / 2.0;
|
let d_height = height / 2.0;
|
||||||
let mut edges: Vec<Edge> = vec![
|
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)),
|
||||||
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
|
edges = edges
|
||||||
.iter()
|
.iter()
|
||||||
.map(|edge| match edge {
|
.map(|edge| match edge {
|
||||||
Edge::OneTimeLine(line) => {
|
Edge::OneTimeLine(line) => {
|
||||||
let start = line.start.rotate(angle);
|
let start = line.start.rotate_radius(radius);
|
||||||
let end = line.end.rotate(angle);
|
let end = line.end.rotate_radius(radius);
|
||||||
Edge::OneTimeLine(OneTimeLine::point_new(&start, &end))
|
Edge::OneTimeLine(OneTimeLine::point_new(&start, &end))
|
||||||
}
|
}
|
||||||
Edge::CircularArc(arc) => {
|
Edge::CircularArc(arc) => {
|
||||||
let pos = arc.pos.rotate(angle);
|
let pos = arc.pos.rotate_radius(radius);
|
||||||
Edge::CircularArc(CircularArc {
|
Edge::CircularArc(CircularArc {
|
||||||
r: arc.r,
|
r: arc.r,
|
||||||
pos,
|
pos,
|
||||||
@ -722,6 +777,25 @@ pub mod math {
|
|||||||
bounds: edges,
|
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 {
|
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.text import Label
|
||||||
from pyglet.shapes import Line
|
from pyglet.shapes import Line
|
||||||
from pyglet.sprite import Sprite
|
from pyglet.sprite import Sprite
|
||||||
|
from pyglet.image import Texture
|
||||||
from pyglet.graphics import Batch, Group
|
from pyglet.graphics import Batch, Group
|
||||||
from pyglet.image import load as load_image
|
|
||||||
|
from . import DR_mod_runtime
|
||||||
|
|
||||||
# Difficult Rocket
|
# Difficult Rocket
|
||||||
from Difficult_Rocket import DR_option
|
from Difficult_Rocket import DR_option
|
||||||
@ -33,8 +35,8 @@ from Difficult_Rocket.api.types.SR1 import SR1Textures, SR1PartTexture, SR1PartD
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from Difficult_Rocket.client import ClientWindow
|
from Difficult_Rocket.client import ClientWindow
|
||||||
|
|
||||||
if DR_option.use_DR_rust:
|
if DR_mod_runtime.use_DR_rust:
|
||||||
from libs.Difficult_Rocket_rs import CenterCamera_rs, SR1PartList_rs
|
from .Difficult_Rocket_rs import CenterCamera_rs, SR1PartList_rs
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger('client')
|
logger = logging.getLogger('client')
|
||||||
@ -128,7 +130,7 @@ class SR1ShipRender(BaseScreen):
|
|||||||
load_end_time = time.time_ns()
|
load_end_time = time.time_ns()
|
||||||
logger.info(tr().client.sr1_render.setup.use_time().format(
|
logger.info(tr().client.sr1_render.setup.use_time().format(
|
||||||
(load_end_time - load_start_time) / 1000000000))
|
(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,
|
self.camera_rs = CenterCamera_rs(main_window,
|
||||||
min_zoom=(1 / 2) ** 10, max_zoom=10)
|
min_zoom=(1 / 2) ** 10, max_zoom=10)
|
||||||
self.rust_parts = None
|
self.rust_parts = None
|
||||||
@ -194,7 +196,7 @@ class SR1ShipRender(BaseScreen):
|
|||||||
self.part_data: Dict[int, SR1PartData] = {}
|
self.part_data: Dict[int, SR1PartData] = {}
|
||||||
self.parts_sprite: Dict[int, Sprite] = {}
|
self.parts_sprite: Dict[int, Sprite] = {}
|
||||||
self.camera_rs.zoom = 1.0
|
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.dx = 0
|
||||||
self.camera_rs.dy = 0
|
self.camera_rs.dy = 0
|
||||||
parts = self.xml_root.find('Parts')
|
parts = self.xml_root.find('Parts')
|
||||||
@ -213,13 +215,13 @@ class SR1ShipRender(BaseScreen):
|
|||||||
self.drawing = False
|
self.drawing = False
|
||||||
self.need_draw = False
|
self.need_draw = False
|
||||||
full_mass = 0
|
full_mass = 0
|
||||||
if DR_option.use_DR_rust:
|
if DR_mod_runtime.use_DR_rust:
|
||||||
for part in self.part_data:
|
for part in self.part_data:
|
||||||
full_mass += self.part_list_rs.get_part_type(self.part_data[part].p_type).mass * 500
|
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(
|
logger.info(tr().client.sr1_render.ship.load_time().format(
|
||||||
(time.perf_counter_ns() - start_time) / 1000000000))
|
(time.perf_counter_ns() - start_time) / 1000000000))
|
||||||
logger.info(tr().client.sr1_render.ship.info().format(
|
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
|
self.rendered = True
|
||||||
|
|
||||||
def get_ship_size(self) -> (int, int):
|
def get_ship_size(self) -> (int, int):
|
||||||
@ -359,6 +361,12 @@ class SR1ShipRender(BaseScreen):
|
|||||||
|
|
||||||
image_data = screenshot(self.window_pointer)
|
image_data = screenshot(self.window_pointer)
|
||||||
image_data.save('test.png')
|
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"):
|
def on_mouse_drag(self, x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int, window: "ClientWindow"):
|
||||||
if not self.focus:
|
if not self.focus:
|
Loading…
Reference in New Issue
Block a user