Compare commits
No commits in common. "5cb642ea3c75beb67b10146b7642b7edcc3417bd" and "44b183db0ed456e081b7ed5750dd47e619af4d45" have entirely different histories.
5cb642ea3c
...
44b183db0e
4
.github/workflows/dr_rs.yml
vendored
4
.github/workflows/dr_rs.yml
vendored
@ -81,8 +81,6 @@ jobs:
|
||||
python setup.py build
|
||||
python post_build.py
|
||||
python setup.py clean
|
||||
cd ..
|
||||
Remove-Item -Recurse -Force src
|
||||
|
||||
# Uploads artifact
|
||||
- name: Upload Artifact
|
||||
@ -90,4 +88,4 @@ jobs:
|
||||
with:
|
||||
name: DR_rs${{env.DR_version}}-${{runner.os}}${{matrix.python-version}}-Build.${{github.run_number}}+${{env.short_sha}}
|
||||
path: |
|
||||
mods/dr_game
|
||||
mods/dr_game/Difficult_Rocket_rs/lib
|
||||
|
3
.github/workflows/dsm.py
vendored
3
.github/workflows/dsm.py
vendored
@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
@ -108,7 +109,7 @@ def main():
|
||||
return 0
|
||||
dsm.clear_dsm()
|
||||
dsm.upload_docs('docs/md5.txt')
|
||||
dsm.fl.logout()
|
||||
dsm.fl.session.logout('FileStation')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
22
.github/workflows/get_info.py
vendored
22
.github/workflows/get_info.py
vendored
@ -10,24 +10,32 @@ import rtoml
|
||||
|
||||
sys.path.append(os.path.abspath(os.curdir))
|
||||
|
||||
from Difficult_Rocket import DR_status
|
||||
from Difficult_Rocket import DR_runtime
|
||||
|
||||
args = ['-env', '-github-dev']
|
||||
|
||||
# print(sys.argv)
|
||||
|
||||
|
||||
|
||||
if sys.argv == [__file__]: # 没有输入参数,直接输出默认信息并输出
|
||||
print(sys.version)
|
||||
from Difficult_Rocket.utils import tools
|
||||
# 重置窗口信息
|
||||
config_file = tools.load_file('./config/main.toml')
|
||||
config_file = tools.load_file('./configs/main.toml')
|
||||
config_file['window']['width'] = 1024
|
||||
config_file['window']['height'] = 768
|
||||
rtoml.dump(config_file, open('./config/main.toml', 'w'))
|
||||
rtoml.dump(config_file, open('./configs/main.toml', 'w'))
|
||||
|
||||
elif os.path.abspath(os.curdir) in sys.path and '-env' in sys.argv:
|
||||
with open('./.github/workflows/env.ps1', encoding='utf-8', mode='w') as env_file:
|
||||
print(f'$env:DR_version = "{DR_status.DR_version}"', file=env_file)
|
||||
print(f'$env:Build_version = "{DR_status.Build_version}"', file=env_file)
|
||||
print(f'$env:DR_version = "{DR_runtime.DR_version}"', file=env_file)
|
||||
print(f'$env:DR_language = "{DR_runtime.language}"', file=env_file)
|
||||
print(f'$env:DR_long_version = "{DR_runtime.DR_long_version}"', file=env_file)
|
||||
print(f'$env:Build_version = "{DR_runtime.Build_version}"', file=env_file)
|
||||
|
||||
elif os.path.abspath(os.curdir) in sys.path and '-github' in sys.argv:
|
||||
print(f'DR_version={DR_status.DR_version}')
|
||||
print(f'Build_version={DR_status.Build_version}')
|
||||
print(f'DR_version={DR_runtime.DR_version}')
|
||||
print(f'DR_language={DR_runtime.language}')
|
||||
print(f'DR_long_version={DR_runtime.DR_long_version}')
|
||||
print(f'Build_version={DR_runtime.Build_version}')
|
||||
|
2
.github/workflows/nuitka.yml
vendored
2
.github/workflows/nuitka.yml
vendored
@ -7,6 +7,7 @@ on:
|
||||
push:
|
||||
paths:
|
||||
- "Difficult_Rocket/**" # 本体修改
|
||||
- "configs/**" # 配置修改
|
||||
- "libs/pyglet/**" # pyglet 修改
|
||||
- ".github/workflows/**" # workflow 修改
|
||||
- "nuitka_build.py" # 构建脚本修改
|
||||
@ -14,6 +15,7 @@ on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "Difficult_Rocket/**" # 本体修改
|
||||
- "configs/**" # 配置修改
|
||||
- "libs/pyglet/**" # pyglet 修改
|
||||
- ".github/workflows/**" # workflow 修改
|
||||
- "nuitka_build.py" # 构建脚本修改
|
||||
|
12
.github/workflows/page.yml
vendored
12
.github/workflows/page.yml
vendored
@ -49,12 +49,12 @@ jobs:
|
||||
run: |
|
||||
# 设置变量
|
||||
$urls = @(
|
||||
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.4/mdbook-i18n',
|
||||
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.4/mdbook-theme',
|
||||
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.4/mdbook-cmdrun',
|
||||
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.4/mdbook-pagetoc',
|
||||
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.4/mdbook-footnote',
|
||||
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.4/mdbook-external-links',
|
||||
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.3/mdbook-i18n',
|
||||
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.3/mdbook-theme',
|
||||
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.3/mdbook-cmdrun',
|
||||
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.3/mdbook-pagetoc',
|
||||
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.3/mdbook-footnote',
|
||||
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.3/mdbook-external-links',
|
||||
'https://github.com/plantuml/plantuml/releases/download/v1.2023.4/plantuml-1.2023.4.jar'
|
||||
)
|
||||
# 下载文件
|
||||
|
7
.gitignore
vendored
7
.gitignore
vendored
@ -166,10 +166,3 @@ other things/
|
||||
|
||||
*cmake-build-debug
|
||||
.pdm.toml
|
||||
|
||||
writer-test.xml
|
||||
test-xml-rs.xml
|
||||
test-file.xml
|
||||
test-save.xml
|
||||
|
||||
index.html
|
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@ -2,5 +2,9 @@
|
||||
"rust-analyzer.linkedProjects": [
|
||||
"mods/dr_game/Difficult_Rocket_rs/src/Cargo.toml",
|
||||
"libs/pyglet_rs/src/Cargo.toml",
|
||||
]
|
||||
],
|
||||
"python.analysis.extraPaths": [
|
||||
"./libs"
|
||||
],
|
||||
"python.analysis.typeCheckingMode": "basic"
|
||||
}
|
||||
|
47
DR-start.py
Normal file
47
DR-start.py
Normal file
@ -0,0 +1,47 @@
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
hi = """Difficult Rocket is writen by shenjackyuanjie
|
||||
email: 3695888@qq.com or shyj3695888@163.com
|
||||
QQ: 3695888"""
|
||||
|
||||
errors = {
|
||||
'TestError': '游戏正在调试中,某处引发了一个 TestError,不是bug造成的原因',
|
||||
'AssertionError': '游戏的某处检查未通过,情报告issue',
|
||||
'error.unknown': '游戏报错了,现在输出报错信息,请报告issue',
|
||||
'error.happen': '游戏出现了一个报错!正在处理'
|
||||
}
|
||||
|
||||
|
||||
def print_path() -> None:
|
||||
print(f'{__file__=}')
|
||||
print(f'{sys.path=}')
|
||||
print(f'{sys.path[0]=}')
|
||||
print(f'{sys.argv[0]=}')
|
||||
print(f'{os.getcwd()=}')
|
||||
print(f'{os.path.abspath(__file__)=}')
|
||||
print(f'{os.path.realpath(__file__)=}')
|
||||
print(f'{os.path.split(os.path.split(os.path.realpath(__file__))[0])=}')
|
||||
# 输出一遍大部分文件位置相关信息 以后可能会加到logs里
|
||||
|
||||
|
||||
def modify_path() -> None:
|
||||
file_path = os.path.split(os.path.realpath(__file__))[0]
|
||||
os.chdir(file_path) # 将运行路径切换到文件位置 防止bug
|
||||
sys.path.append(f'{file_path}/Difficult_Rocket') # 添加local path
|
||||
sys.path.append(f'{file_path}/libs') # 添加 libs path
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(hi) # hi!
|
||||
# 记录启动信息
|
||||
start_time_ns = time.time_ns()
|
||||
start_time_perf_ns = time.perf_counter_ns()
|
||||
print_path()
|
||||
modify_path()
|
131
DR.py
131
DR.py
@ -1,70 +1,111 @@
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
"""
|
||||
writen by shenjackyuanjie
|
||||
email: 3695888@qq.com
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import cProfile
|
||||
import traceback
|
||||
import threading
|
||||
|
||||
from pathlib import Path
|
||||
from io import StringIO
|
||||
|
||||
# TODO 默认位置配置文件
|
||||
# TODO 可自定义工作路径
|
||||
|
||||
hi = """Difficult Rocket is writen by shenjackyuanjie
|
||||
email: 3695888@qq.com or shyj3695888@163.com
|
||||
QQ: 3695888"""
|
||||
|
||||
error_format = {
|
||||
'TestError': '游戏正在调试中,某处引发了一个 TestError,不是bug造成的原因',
|
||||
'AssertionError': '游戏的某处检查未通过,情报告issue',
|
||||
'error.unknown': '游戏报错了,现在输出报错信息,请报告issue',
|
||||
'error.happen': '游戏出现了一个报错!正在处理'
|
||||
}
|
||||
|
||||
|
||||
def print_path() -> None:
|
||||
print(f'{__file__=}')
|
||||
print(f'{sys.path=}')
|
||||
print(f'{sys.path[0]=}')
|
||||
print(f'{sys.argv[0]=}')
|
||||
print(f'{Path.cwd()=}')
|
||||
print(f'{Path(__file__).absolute()=}')
|
||||
|
||||
|
||||
def modify_path() -> None:
|
||||
os.chdir(Path(__file__).parent) # 将运行路径切换到文件位置 防止bug
|
||||
sys.path.append('./Difficult_Rocket') # 添加local path
|
||||
sys.path.append('./libs') # 添加 libs path
|
||||
|
||||
|
||||
def start(start_time_ns: int) -> None:
|
||||
from Difficult_Rocket import crash, DR_status
|
||||
from Difficult_Rocket.runtime import DR_runtime
|
||||
from Difficult_Rocket.exception import TestError
|
||||
DR_runtime.start_time_ns = start_time_ns
|
||||
try:
|
||||
from Difficult_Rocket import main
|
||||
main_game = main.Game()
|
||||
main_game.start()
|
||||
if DR_status.crash_report_test:
|
||||
raise TestError('debug crash test')
|
||||
except:
|
||||
trace = traceback.format_exc()
|
||||
crash.create_crash_report(trace)
|
||||
print(trace)
|
||||
crash.write_info_to_cache(sys.stdout)
|
||||
print(crash.all_thread)
|
||||
print(crash.all_process)
|
||||
for a_thread in threading.enumerate():
|
||||
print(a_thread)
|
||||
if a_thread.is_alive() and a_thread != threading.current_thread() and a_thread != threading.main_thread():
|
||||
a_thread.join(2) # wait for 2 sec
|
||||
import pyglet
|
||||
pyglet.app.exit() # make sure that pyglet has stopped
|
||||
print(f'{os.curdir=}')
|
||||
print(f'{os.getcwd()=}')
|
||||
print(f'{os.path.abspath(os.curdir)=}')
|
||||
print(f'{os.path.abspath(__file__)=}')
|
||||
print(f'{os.path.realpath(__file__)=}')
|
||||
print(f'{os.path.split(os.path.split(os.path.realpath(__file__))[0])=}')
|
||||
# 输出一遍大部分文件位置相关信息 以后可能会加到logs里
|
||||
|
||||
|
||||
def main() -> int:
|
||||
print(hi, f"\n{time.ctime()}") # hi!
|
||||
# 记录启动信息
|
||||
print(hi) # hi!
|
||||
start_time_ns = time.time_ns()
|
||||
start_time_perf_ns = time.perf_counter_ns()
|
||||
print_path()
|
||||
modify_path()
|
||||
start(start_time_ns)
|
||||
file_path = os.path.split(os.path.realpath(__file__))[0]
|
||||
os.chdir(file_path) # 将运行路径切换到文件位置 防止bug
|
||||
sys.path.append(f'{file_path}/Difficult_Rocket') # 添加local path
|
||||
sys.path.append(f'{file_path}/libs') # 添加 libs path
|
||||
|
||||
from Difficult_Rocket.exception import TestError
|
||||
from Difficult_Rocket import crash
|
||||
from Difficult_Rocket import DR_status
|
||||
try:
|
||||
from libs import pyglet # 导入pyglet
|
||||
pyglet.resource.path = ['/textures/']
|
||||
pyglet.resource.reindex()
|
||||
|
||||
from Difficult_Rocket import main, DR_runtime
|
||||
DR_runtime.start_time_ns = start_time_ns
|
||||
|
||||
# from pyglet.gl import glClearColor # 调整背景颜色
|
||||
# glClearColor(0.5, 0.5, 0.5, 0)
|
||||
|
||||
game = main.Game() # 实例化一个游戏
|
||||
print(time.perf_counter_ns() - start_time_perf_ns, (time.perf_counter_ns() - start_time_perf_ns) / (10 ** 9), 'start') # 输出一下启动用时
|
||||
|
||||
cprofile = False # 是否使用cprofile
|
||||
if cprofile:
|
||||
cProfile.run('game.start()', sort='calls') # 使用 cprofile 启动
|
||||
else:
|
||||
game.start() # 直接启动
|
||||
if DR_status.crash_report_test:
|
||||
raise TestError('debugging') # debug 嘛,试试crash
|
||||
except Exception as exp: # 出毛病了
|
||||
# 解析错误信息
|
||||
print(error_format['error.happen'])
|
||||
error = traceback.format_exc()
|
||||
name = type(exp).__name__
|
||||
if name in error_format:
|
||||
print(error_format[name])
|
||||
else:
|
||||
print(error_format['error.unknown'])
|
||||
print(error)
|
||||
# 输出 crash 信息
|
||||
crash.create_crash_report(error)
|
||||
cache_steam = StringIO()
|
||||
crash.write_info_to_cache(cache_steam)
|
||||
text = cache_steam.getvalue()
|
||||
print(text)
|
||||
else:
|
||||
crash.record_thread = False
|
||||
print(crash.all_thread)
|
||||
print(crash.all_process)
|
||||
# join all thread
|
||||
for thread in threading.enumerate():
|
||||
print(thread)
|
||||
if thread.name == 'MainThread' or thread == threading.main_thread() or thread == threading.current_thread():
|
||||
continue
|
||||
if thread.daemon:
|
||||
continue
|
||||
thread.join()
|
||||
# stop pyglet
|
||||
import pyglet
|
||||
pyglet.app.exit()
|
||||
print("Difficult_Rocket 已关闭")
|
||||
return 0
|
||||
|
||||
|
||||
|
@ -4,34 +4,20 @@
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
import time
|
||||
import logging.config
|
||||
import sys
|
||||
import importlib
|
||||
import traceback
|
||||
import contextlib
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
from typing import Optional, List, Tuple
|
||||
|
||||
from Difficult_Rocket.api.types import Options, Version
|
||||
|
||||
sdk_version = Version("0.8.7.0") # SDK 版本
|
||||
build_version = Version("2.1.3.0") # 编译文件版本(与游戏本体无关)
|
||||
game_version = Version("0.8.2.0") # 游戏版本
|
||||
build_version = Version("2.1.0.0") # 编译文件版本(与游戏本体无关)
|
||||
Api_version = Version("0.1.1.0") # API 版本
|
||||
__version__ = sdk_version
|
||||
|
||||
|
||||
__all__ = [
|
||||
# __init__
|
||||
'DR_status',
|
||||
# folder
|
||||
'api',
|
||||
'client',
|
||||
'server',
|
||||
'command',
|
||||
'crash',
|
||||
'exception',
|
||||
'mod',
|
||||
'utils',
|
||||
# file
|
||||
'main',
|
||||
'runtime',
|
||||
]
|
||||
__version__ = game_version
|
||||
|
||||
|
||||
class _DR_status(Options):
|
||||
@ -57,7 +43,7 @@ class _DR_status(Options):
|
||||
crash_report_test: bool = False
|
||||
|
||||
# game version status
|
||||
DR_version: Version = sdk_version # DR SDK 版本
|
||||
DR_version: Version = game_version # DR SDK 版本
|
||||
Build_version: Version = build_version # DR 构建 版本
|
||||
API_version: Version = Api_version # DR SDK API 版本
|
||||
|
||||
@ -72,23 +58,64 @@ class _DR_status(Options):
|
||||
return round(12 * self.gui_scale)
|
||||
|
||||
|
||||
class _DR_runtime(Options):
|
||||
"""
|
||||
DR 的运行时配置 / 状态
|
||||
"""
|
||||
name = 'DR Runtime'
|
||||
|
||||
language: str = 'zh-CN'
|
||||
mod_path: str = './mods'
|
||||
DR_Mod_List: List[Tuple[str, Version]] = [] # DR Mod 列表 (name, version)
|
||||
|
||||
# run status
|
||||
start_time_ns: Optional[int] = None
|
||||
client_setup_cause_ns: Optional[int] = None
|
||||
server_setup_cause_ns: Optional[int] = None
|
||||
|
||||
def load_file(self) -> bool:
|
||||
with contextlib.suppress(FileNotFoundError):
|
||||
with open('./configs/main.toml', 'r', encoding='utf-8') as f:
|
||||
import rtoml
|
||||
config_file = rtoml.load(f)
|
||||
self.language = config_file['runtime']['language']
|
||||
self.mod_path = config_file['game']['mods']['path']
|
||||
return True
|
||||
return False
|
||||
|
||||
def find_mods(self) -> List[str]:
|
||||
mods = []
|
||||
mod_path = Path(self.mod_path)
|
||||
if not mod_path.exists():
|
||||
mod_path.mkdir()
|
||||
return []
|
||||
paths = mod_path.iterdir()
|
||||
sys.path.append(self.mod_path)
|
||||
for mod_path in paths:
|
||||
try:
|
||||
if mod_path.is_dir() and mod_path.name != '__pycache__': # 处理文件夹 mod
|
||||
if importlib.util.find_spec(mod_path.name) is not None:
|
||||
mods.append(mod_path.name)
|
||||
else:
|
||||
print(f'can not import mod {mod_path} because importlib can not find spec')
|
||||
elif mod_path.suffix in ('.pyz', '.zip'): # 处理压缩包 mod
|
||||
if importlib.util.find_spec(mod_path.name) is not None:
|
||||
mods.append(mod_path.name)
|
||||
elif mod_path.suffix == '.pyd': # pyd 扩展 mod
|
||||
if importlib.util.find_spec(mod_path.name) is not None:
|
||||
mods.append(mod_path.name)
|
||||
elif mod_path.suffix == '.py': # 处理单文件 mod
|
||||
print(f'importing mod {mod_path=} {mod_path.stem}')
|
||||
if importlib.util.find_spec(mod_path.stem) is not None:
|
||||
mods.append(mod_path.stem)
|
||||
except ImportError:
|
||||
print(f'ImportError when loading mod {mod_path}')
|
||||
traceback.print_exc()
|
||||
return mods
|
||||
|
||||
|
||||
DR_status = _DR_status()
|
||||
|
||||
|
||||
def load_logging():
|
||||
with open('./config/logger.toml') as f:
|
||||
import rtoml
|
||||
logger_config = rtoml.load(f)
|
||||
log_path = logger_config['handlers']['file']['filename']
|
||||
log_path = f"logs/{log_path.format(time.strftime('%Y-%m-%d %H-%M-%S', time.gmtime(time.time_ns() / 1000_000_000)))}"
|
||||
if not Path('logs/').is_dir():
|
||||
Path('logs/').mkdir()
|
||||
logger_config['handlers']['file']['filename'] = log_path
|
||||
logging.config.dictConfig(logger_config)
|
||||
|
||||
|
||||
load_logging()
|
||||
|
||||
DR_runtime = _DR_runtime()
|
||||
|
||||
if DR_status.playing:
|
||||
from Difficult_Rocket.utils.thread import new_thread
|
||||
@ -100,4 +127,4 @@ if DR_status.playing:
|
||||
@new_thread('think')
|
||||
def think(some_thing_to_think):
|
||||
gotcha = think_it(some_thing_to_think)
|
||||
return gotcha
|
||||
return gotcha
|
||||
|
@ -12,6 +12,8 @@ gitee: @shenjackyuanjie
|
||||
"""
|
||||
|
||||
|
||||
# from Difficult_Rocket.api import screen, mod, exception
|
||||
|
||||
__all__ = [
|
||||
'exception',
|
||||
# 错误类定义
|
||||
@ -21,4 +23,6 @@ __all__ = [
|
||||
# 类型定义
|
||||
'mod',
|
||||
# mod api
|
||||
'log'
|
||||
# log api
|
||||
]
|
||||
|
@ -1,17 +0,0 @@
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
from Difficult_Rocket.utils.camera import (Camera,
|
||||
CenterCamera,
|
||||
GroupCamera,
|
||||
CenterGroupCamera)
|
||||
|
||||
__all__ = [
|
||||
'Camera',
|
||||
'CenterCamera',
|
||||
'GroupCamera',
|
||||
'CenterGroupCamera'
|
||||
]
|
@ -1,9 +0,0 @@
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
__all__ = [
|
||||
'widget'
|
||||
]
|
@ -1,11 +0,0 @@
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
from Difficult_Rocket.gui.widget.button import PressTextButton
|
||||
|
||||
__all__ = [
|
||||
'PressTextButton'
|
||||
]
|
16
Difficult_Rocket/api/log.py
Normal file
16
Difficult_Rocket/api/log.py
Normal file
@ -0,0 +1,16 @@
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
from Difficult_Rocket.utils.log import (get_named_client_logger,
|
||||
get_named_server_logger,
|
||||
get_named_main_logger)
|
||||
|
||||
|
||||
__all__ = [
|
||||
'get_named_client_logger',
|
||||
'get_named_server_logger',
|
||||
'get_named_main_logger',
|
||||
]
|
@ -112,7 +112,6 @@ class BaseScreen(EventDispatcher):
|
||||
:event:
|
||||
"""
|
||||
|
||||
# def on_draw(self, dt: float, window: ClientWindow): # TODO: wait for pyglet 2.1
|
||||
def on_draw(self, window: ClientWindow):
|
||||
"""The window contents must be redrawn.
|
||||
|
||||
|
@ -12,15 +12,12 @@ from Difficult_Rocket.utils.options import (Options,
|
||||
OptionNotFound,
|
||||
get_type_hints_)
|
||||
|
||||
from libs.MCDR.version import (Version,
|
||||
VersionRequirement,
|
||||
VersionParsingError)
|
||||
from libs.MCDR.version import Version
|
||||
|
||||
__all__ = [
|
||||
# main class
|
||||
'Options',
|
||||
'Version',
|
||||
'VersionRequirement',
|
||||
|
||||
# data class
|
||||
'FontData',
|
||||
@ -30,7 +27,6 @@ __all__ = [
|
||||
'OptionsError',
|
||||
'OptionNameNotDefined',
|
||||
'OptionNotFound',
|
||||
'VersionParsingError',
|
||||
|
||||
# other
|
||||
'get_type_hints_',
|
||||
|
@ -15,7 +15,7 @@ import traceback
|
||||
|
||||
from pathlib import Path
|
||||
from decimal import Decimal
|
||||
from typing import Callable, Dict, List, TYPE_CHECKING, Type
|
||||
from typing import Callable, Dict, List, TYPE_CHECKING
|
||||
|
||||
# third function
|
||||
import rtoml
|
||||
@ -23,28 +23,23 @@ import pyglet
|
||||
# from pyglet import gl
|
||||
# from pyglet.gl import glClearColor
|
||||
# from pyglet.libs.win32 import _user32
|
||||
from pyglet.graphics import Group, Batch
|
||||
from pyglet.window import Window
|
||||
from pyglet.window import key, mouse
|
||||
from pyglet.gui.widgets import TextEntry
|
||||
|
||||
# Difficult_Rocket function
|
||||
if TYPE_CHECKING:
|
||||
from Difficult_Rocket.main import Game
|
||||
from Difficult_Rocket import DR_status
|
||||
from Difficult_Rocket.utils import tools
|
||||
from Difficult_Rocket.command import line
|
||||
from Difficult_Rocket.api.types import Options
|
||||
from Difficult_Rocket.command import line
|
||||
from Difficult_Rocket.utils.translate import tr
|
||||
from Difficult_Rocket.runtime import DR_runtime
|
||||
from Difficult_Rocket import DR_runtime
|
||||
from Difficult_Rocket.api.screen import BaseScreen
|
||||
from Difficult_Rocket.utils.thread import new_thread
|
||||
from Difficult_Rocket.client.screen import DRDEBUGScreen
|
||||
from Difficult_Rocket.client.fps.fps_log import FpsLogger
|
||||
from Difficult_Rocket.client.guis.widgets import InputBox
|
||||
from Difficult_Rocket.exception.language import LanguageNotFound
|
||||
|
||||
|
||||
logger = logging.getLogger('client')
|
||||
from Difficult_Rocket.client.screen import DRScreen, DRDEBUGScreen
|
||||
|
||||
|
||||
class ClientOption(Options):
|
||||
@ -56,24 +51,20 @@ class ClientOption(Options):
|
||||
resizeable: bool = True
|
||||
visible: bool = True
|
||||
gui_scale: float = 1.0
|
||||
caption: str = "Difficult Rocket v{DR_version}"
|
||||
caption: str = "Difficult Rocket v{DR_version}|DR_rs v{DR_Rust_get_version}"
|
||||
|
||||
def load_file(self) -> None:
|
||||
file: dict = tools.load_file('./config/main.toml')
|
||||
file: dict = tools.load_file('./configs/main.toml')
|
||||
self.fps = int(file['runtime']['fps'])
|
||||
self.width = int(file['window']['width'])
|
||||
self.height = int(file['window']['height'])
|
||||
self.fullscreen = tools.format_bool(file['window']['full_screen'])
|
||||
self.resizeable = tools.format_bool(file['window']['resizable'])
|
||||
self.gui_scale = float(file['window']['gui_scale'])
|
||||
self.caption = DR_status.format(file['window']['caption'])
|
||||
self.caption = DR_runtime.format(self.caption)
|
||||
self.caption = DR_runtime.format(file['window']['caption'])
|
||||
|
||||
|
||||
class Client:
|
||||
"""
|
||||
客户端
|
||||
"""
|
||||
def __init__(self, game: "Game", net_mode='local'):
|
||||
start_time = time.time_ns()
|
||||
# logging
|
||||
@ -98,9 +89,6 @@ class Client:
|
||||
self.logger.debug(tr().client.setup.use_time_ns().format(self.use_time))
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
启动客户端
|
||||
"""
|
||||
DR_runtime.running = True
|
||||
self.window.start_game() # 游戏启动
|
||||
# TODO 写一下服务端启动相关,还是需要服务端啊
|
||||
@ -110,11 +98,6 @@ class Client:
|
||||
|
||||
|
||||
def pyglet_load_fonts_folder(folder) -> None:
|
||||
"""
|
||||
递归加载字体文件夹
|
||||
:param folder:
|
||||
:return:
|
||||
"""
|
||||
font_path = Path(folder)
|
||||
if not font_path.exists():
|
||||
font_path.mkdir(parents=True)
|
||||
@ -122,48 +105,13 @@ def pyglet_load_fonts_folder(folder) -> None:
|
||||
file_folder_list = os.listdir(folder)
|
||||
for obj in file_folder_list:
|
||||
if os.path.isfile(os.path.join(folder, obj)):
|
||||
if obj[-4:] == '.ttf' or obj[-4:] == '.otf':
|
||||
logger.debug(f'loading font {os.path.join(folder, obj)}')
|
||||
try:
|
||||
pyglet.font.add_file(os.path.join(folder, obj))
|
||||
except Exception:
|
||||
logger.error(traceback.format_exc())
|
||||
logger.error(f'loading font {os.path.join(folder, obj)} failed')
|
||||
if obj[-4:] == '.ttf':
|
||||
pyglet.font.add_file(os.path.join(folder, obj))
|
||||
else:
|
||||
logger.info(f'loading font folder {os.path.join(folder, obj)}')
|
||||
pyglet_load_fonts_folder(os.path.join(folder, obj))
|
||||
|
||||
|
||||
def _call_back(call_back: Callable) -> Callable:
|
||||
"""
|
||||
>>> def call_back():
|
||||
>>> pass
|
||||
>>> @_call_back(call_back)
|
||||
>>> def on_draw(self):
|
||||
>>> pass
|
||||
用于在调用窗口函数后调用指定函数 的装饰器
|
||||
:param call_back: 需要调用的函数
|
||||
:return: 包装后的函数
|
||||
"""
|
||||
def wrapper(func):
|
||||
@functools.wraps(func)
|
||||
def warp(self: "ClientWindow", *args, **kwargs):
|
||||
result = func(self, *args, **kwargs)
|
||||
call_back(self)
|
||||
return result
|
||||
return warp
|
||||
return wrapper
|
||||
|
||||
|
||||
def _call_screen_after(func: Callable) -> Callable:
|
||||
"""
|
||||
>>> @_call_screen_after
|
||||
>>> def on_draw(self):
|
||||
>>> pass
|
||||
用于在调用窗口函数后调用子窗口函数 的装饰器
|
||||
:param func: 需要包装的函数
|
||||
:return: 包装后的函数
|
||||
"""
|
||||
@functools.wraps(func)
|
||||
def warped(self: "ClientWindow", *args, **kwargs):
|
||||
result = func(self, *args, **kwargs)
|
||||
@ -182,14 +130,6 @@ def _call_screen_after(func: Callable) -> Callable:
|
||||
|
||||
|
||||
def _call_screen_before(func: Callable) -> Callable:
|
||||
"""
|
||||
>>> @_call_screen_before
|
||||
>>> def on_draw(self):
|
||||
>>> pass
|
||||
用于在调用窗口函数前调用子窗口函数 的装饰器
|
||||
:param func: 需要包装的函数
|
||||
:return: 包装后的函数
|
||||
"""
|
||||
@functools.wraps(func)
|
||||
def warped(self: "ClientWindow", *args, **kwargs):
|
||||
for title, a_screen in self.screen_list.items():
|
||||
@ -226,16 +166,16 @@ class ClientWindow(Window):
|
||||
self.net_mode = net_mode
|
||||
self.run_input = False
|
||||
self.command_list: List[str] = []
|
||||
# config
|
||||
self.main_config = tools.load_file('./config/main.toml')
|
||||
self.game_config = tools.load_file('./config/game.config')
|
||||
# configs
|
||||
self.main_config = tools.load_file('./configs/main.toml')
|
||||
self.game_config = tools.load_file('./configs/game.config')
|
||||
# FPS
|
||||
self.FPS = Decimal(int(self.main_config['runtime']['fps']))
|
||||
self.SPF = Decimal('1') / self.FPS
|
||||
self.fps_log = FpsLogger(stable_fps=int(self.FPS))
|
||||
# batch
|
||||
self.main_batch = Batch()
|
||||
self.main_group = Group(0)
|
||||
self.part_batch = pyglet.graphics.Batch()
|
||||
self.label_batch = pyglet.graphics.Batch()
|
||||
# frame
|
||||
self.frame = pyglet.gui.Frame(self, order=20)
|
||||
self.M_frame = pyglet.gui.MovableFrame(self, modifier=key.LCTRL)
|
||||
@ -243,11 +183,12 @@ class ClientWindow(Window):
|
||||
# setup
|
||||
self.setup()
|
||||
# 命令显示
|
||||
self.input_box = TextEntry(x=50, y=30, width=300,
|
||||
batch=self.main_batch, text='', group=Group(1000, parent=self.main_group)) # 实例化
|
||||
self.command_group = pyglet.graphics.Group(0)
|
||||
self.input_box = InputBox(x=50, y=30, width=300,
|
||||
batch=self.label_batch, text='') # 实例化
|
||||
self.input_box.push_handlers(self)
|
||||
self.input_box.set_handler('on_commit', self.on_input)
|
||||
self.push_handlers(self.input_box)
|
||||
self.set_handlers(self.input_box)
|
||||
self.input_box.enabled = True
|
||||
# 设置刷新率
|
||||
# pyglet.clock.schedule_interval(self.draw_update, float(self.SPF))
|
||||
@ -261,10 +202,12 @@ class ClientWindow(Window):
|
||||
self.count = 0
|
||||
|
||||
def setup(self):
|
||||
self.set_icon(pyglet.image.load('assets/textures/icon.png'))
|
||||
self.set_icon(pyglet.image.load('./textures/icon.png'))
|
||||
self.load_fonts()
|
||||
# TODO 读取配置文件,加载不同的屏幕,解耦
|
||||
self.screen_list['DR_debug'] = DRDEBUGScreen(self)
|
||||
self.game.dispatch_mod_event('on_client_start', game=self.game, client=self)
|
||||
self.screen_list['DR_main'] = DRScreen(self)
|
||||
self.game.dispatch_event('on_client_start', game=self.game, client=self)
|
||||
|
||||
def load_fonts(self) -> None:
|
||||
fonts_folder_path = self.main_config['runtime']['fonts_folder']
|
||||
@ -273,39 +216,33 @@ class ClientWindow(Window):
|
||||
pyglet_load_fonts_folder(fonts_folder_path)
|
||||
|
||||
def start_game(self) -> None:
|
||||
self.set_icon(pyglet.image.load('assets/textures/icon.png'))
|
||||
self.set_icon(pyglet.image.load('./textures/icon.png'))
|
||||
try:
|
||||
# pyglet.clock.schedule_interval(self.on_draw, float(self.SPF))
|
||||
# pyglet.app.run()
|
||||
# TODO: wait for pyglet 2.1
|
||||
pyglet.app.run(float(self.SPF))
|
||||
pyglet.app.event_loop.run(1 / self.main_config['runtime']['fps'])
|
||||
except KeyboardInterrupt:
|
||||
self.logger.warning("==========client stop. KeyboardInterrupt info==========")
|
||||
print("==========client stop. KeyboardInterrupt info==========")
|
||||
traceback.print_exc()
|
||||
self.logger.warning("==========client stop. KeyboardInterrupt info end==========")
|
||||
self.dispatch_event("on_close", 'input')
|
||||
print("==========client stop. KeyboardInterrupt info end==========")
|
||||
self.dispatch_event("on_close")
|
||||
sys.exit(0)
|
||||
|
||||
@new_thread('window save_info')
|
||||
def save_info(self):
|
||||
self.logger.info(tr().client.config.save.start())
|
||||
config_file: dict = tools.load_file('./config/main.toml')
|
||||
config_file: dict = tools.load_file('./configs/main.toml')
|
||||
config_file['window']['width'] = self.width
|
||||
config_file['window']['height'] = self.height
|
||||
config_file['runtime']['language'] = DR_runtime.language
|
||||
rtoml.dump(config_file, open('./config/main.toml', 'w'))
|
||||
rtoml.dump(config_file, open('./configs/main.toml', 'w'))
|
||||
self.logger.info(tr().client.config.save.done())
|
||||
|
||||
"""
|
||||
client api
|
||||
"""
|
||||
|
||||
def add_sub_screen(self, title: str, sub_screen: Type[BaseScreen]):
|
||||
def add_sub_screen(self, title: str, sub_screen: type(BaseScreen)):
|
||||
self.screen_list[title] = sub_screen(self)
|
||||
|
||||
def remove_sub_screen(self, title: str):
|
||||
self.screen_list.pop(title)
|
||||
|
||||
"""
|
||||
draws and some event
|
||||
"""
|
||||
@ -317,13 +254,11 @@ class ClientWindow(Window):
|
||||
self.fps_log.update_tick(now_FPS, decimal_tick)
|
||||
|
||||
@_call_screen_after
|
||||
# def on_draw(self, dt: float): # TODO: wait for pyglet 2.1
|
||||
def on_draw(self):
|
||||
while (command := self.game.console.get_command()) is not None:
|
||||
def on_draw(self, *dt):
|
||||
while command := self.game.console.get_command():
|
||||
self.on_command(line.CommandText(command))
|
||||
pyglet.gl.glClearColor(0.1, 0, 0, 0.0)
|
||||
self.clear()
|
||||
# self.draw_update(dt) # TODO: wait for pyglet 2.1
|
||||
self.draw_update(float(self.SPF))
|
||||
self.draw_batch()
|
||||
|
||||
@ -346,9 +281,10 @@ class ClientWindow(Window):
|
||||
# self.set_location(*self.get_location())
|
||||
print('on hide!')
|
||||
|
||||
@_call_screen_before
|
||||
@_call_screen_after
|
||||
def draw_batch(self):
|
||||
self.main_batch.draw()
|
||||
self.part_batch.draw()
|
||||
self.label_batch.draw()
|
||||
|
||||
"""
|
||||
command line event
|
||||
@ -359,18 +295,15 @@ class ClientWindow(Window):
|
||||
self.on_command(command_text)
|
||||
self.input_box.value = ''
|
||||
|
||||
def new_command(self):
|
||||
self.game.console.new_command()
|
||||
|
||||
@_call_back(new_command)
|
||||
@_call_screen_after
|
||||
def on_command(self, command: line.CommandText):
|
||||
command.text = command.text.rstrip('\n').rstrip(' ').strip('/')
|
||||
self.logger.info(tr().window.command.text().format(f"|{command.text}|"))
|
||||
print(command.find('/'))
|
||||
self.logger.info(tr().window.command.text().format(command))
|
||||
if command.find('stop'):
|
||||
self.logger.info("command stop!")
|
||||
# HUGE THANKS to Discord @nokiyasos for this fix!
|
||||
pyglet.app.exit()
|
||||
# self.dispatch_event('on_exit')
|
||||
print("command stop!")
|
||||
pyglet.app.platform_event_loop.stop()
|
||||
self.dispatch_event('on_close', 'command') # source = command
|
||||
elif command.find('fps'):
|
||||
if command.find('log'):
|
||||
self.logger.debug(self.fps_log.fps_list)
|
||||
@ -389,19 +322,10 @@ class ClientWindow(Window):
|
||||
tr._language = lang
|
||||
self.logger.info(tr().language_set_to())
|
||||
except LanguageNotFound:
|
||||
self.logger.info(tr().language_available().format(os.listdir('./config/lang')))
|
||||
self.logger.info(tr().language_available().format(os.listdir('./configs/lang')))
|
||||
self.save_info()
|
||||
elif command.find('mods'):
|
||||
if command.find('list'):
|
||||
self.logger.info(tr().mod.list())
|
||||
for mod in self.game.mod_manager.loaded_mod_modules.values():
|
||||
self.logger.info(f"mod: {mod.name} id: {mod.mod_id} version: {mod.version}")
|
||||
elif command.find('reload'):
|
||||
if not len(command.text) == 0:
|
||||
print(f"reload mod: |{command.text}|")
|
||||
self.game.mod_manager.reload_mod(command.text, game=self.game)
|
||||
else:
|
||||
logger.info(tr().window.command.mods.reload.no_mod_id())
|
||||
|
||||
# self.command_tree.parse(command.plain_command)
|
||||
|
||||
@_call_screen_after
|
||||
def on_message(self, message: line.CommandText):
|
||||
@ -424,7 +348,7 @@ class ClientWindow(Window):
|
||||
...
|
||||
|
||||
@_call_screen_after
|
||||
def on_mouse_motion(self, x, y, dx, dy):
|
||||
def on_mouse_motion(self, x, y, dx, dy) -> None:
|
||||
...
|
||||
|
||||
@_call_screen_after
|
||||
@ -464,7 +388,7 @@ class ClientWindow(Window):
|
||||
if symbol == key.ESCAPE and not (modifiers & ~(key.MOD_NUMLOCK |
|
||||
key.MOD_CAPSLOCK |
|
||||
key.MOD_SCROLLLOCK)):
|
||||
self.dispatch_event('on_close', 'window')
|
||||
self.dispatch_event('on_close')
|
||||
if symbol == key.SLASH:
|
||||
self.input_box._set_focus(True)
|
||||
self.logger.debug(
|
||||
@ -500,7 +424,7 @@ class ClientWindow(Window):
|
||||
|
||||
@_call_screen_before
|
||||
def on_close(self, source: str = 'window') -> None:
|
||||
self.game.dispatch_mod_event('on_close', game=self.game, client=self, source=source)
|
||||
self.game.dispatch_event('on_close', game=self.game, client=self, source=source)
|
||||
self.logger.info(tr().window.game.stop_get().format(tr().game[source]()))
|
||||
self.logger.info(tr().window.game.stop())
|
||||
# self.fps_log.check_list = False
|
||||
|
302
Difficult_Rocket/client/guis/widgets.py
Normal file
302
Difficult_Rocket/client/guis/widgets.py
Normal file
@ -0,0 +1,302 @@
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
"""
|
||||
writen by shenjackyuanjie
|
||||
mail: 3695888@qq.com
|
||||
github: @shenjackyuanjie
|
||||
gitee: @shenjackyuanjie
|
||||
"""
|
||||
|
||||
from typing import Optional, Union, Tuple
|
||||
|
||||
# from libs import pyglet
|
||||
# from pyglet import font
|
||||
from pyglet.text import Label, HTMLLabel
|
||||
# from pyglet.window import key
|
||||
from pyglet.gui import widgets
|
||||
# from pyglet.sprite import Sprite
|
||||
from pyglet.shapes import Rectangle
|
||||
# from pyglet.image import AbstractImage
|
||||
from pyglet.graphics import Batch, Group
|
||||
from pyglet.text.caret import Caret
|
||||
from pyglet.text.document import UnformattedDocument
|
||||
from pyglet.text.layout import IncrementalTextLayout
|
||||
|
||||
# from libs import pyperclip
|
||||
# from libs.pyperclip import paste
|
||||
|
||||
from Difficult_Rocket.api.types import FontData, Fonts
|
||||
# from Difficult_Rocket.client.guis.format import html
|
||||
from Difficult_Rocket import DR_status
|
||||
|
||||
__all__ = ['InputBox']
|
||||
|
||||
|
||||
class TextButton(widgets.WidgetBase):
|
||||
"""
|
||||
自带字符的按钮,就不用单独做材质了
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
x: int, y: int, width: int, height: int,
|
||||
text: str,
|
||||
font: str = Fonts.鸿蒙简体, font_size: int = 13):
|
||||
super().__init__(x, y, width, height)
|
||||
self.text = text
|
||||
self.text_label = Label(
|
||||
font_name=font, font_size=font_size)
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self.text
|
||||
|
||||
def _update_position(self):
|
||||
self.text_label.position = self._x, self._y
|
||||
...
|
||||
|
||||
|
||||
if not DR_status.InputBox_use_TextEntry:
|
||||
class InputBox(widgets.TextEntry):
|
||||
""" 自定义的输入框 """
|
||||
|
||||
def __init__(self, x: int, y: int, width: int,
|
||||
text: str,
|
||||
side_width: int = 2,
|
||||
font_data: Optional[FontData] = None,
|
||||
color: Optional[Tuple[int, int, int, int]] = (255, 255, 255, 255),
|
||||
text_color: Optional[Tuple[int, int, int, int]] = (0, 0, 0, 255),
|
||||
caret_color: Optional[Tuple[int, int, int, int]] = (0, 0, 0),
|
||||
batch: Optional[Batch] = None, group: Optional[Group] = None):
|
||||
if font_data is None:
|
||||
font_data = FontData()
|
||||
self._doc = UnformattedDocument(text)
|
||||
self._doc.set_style(0, len(self._doc.text), {**font_data.dict(), 'text_color': text_color})
|
||||
font = self._doc.get_font()
|
||||
height = font.ascent - font.descent
|
||||
|
||||
self._user_group = group
|
||||
bg_group = Group(order=0, parent=group)
|
||||
fg_group = Group(order=1, parent=group)
|
||||
|
||||
# Rectangular outline with 2-pixel pad:
|
||||
self._pad = p = side_width
|
||||
self._outline = Rectangle(x-p, y-p, width+p+p, height+p+p, color[:3], batch, bg_group)
|
||||
self._outline.opacity = color[3]
|
||||
|
||||
# Text and Caret:
|
||||
self._layout = IncrementalTextLayout(self._doc, width, height, multiline=False, batch=batch, group=fg_group)
|
||||
self._layout.x = x
|
||||
self._layout.y = y
|
||||
self._caret = Caret(self._layout, color=caret_color)
|
||||
self._caret.visible = False
|
||||
|
||||
self._focus = False
|
||||
super().__init__()
|
||||
|
||||
# InputBox.register_event_type('on_commit')
|
||||
|
||||
|
||||
# class InputBox(widgets.WidgetBase):
|
||||
# """
|
||||
# input box
|
||||
# """
|
||||
#
|
||||
# def __init__(self,
|
||||
# x: int, y: int, width: int, height: int,
|
||||
# message: str = '',
|
||||
# font_name: str = translate.微软等宽,
|
||||
# font_size: int = 15,
|
||||
# font_bold: bool = False,
|
||||
# font_italic: bool = False,
|
||||
# font_stretch: bool = False,
|
||||
# font_dpi: int = 100,
|
||||
# text_color: [int, int, int] = (187, 187, 187, 255),
|
||||
# out_line_color: [int, int, int] = (37, 116, 176),
|
||||
# cursor_color: [int, int, int] = (187, 187, 187),
|
||||
# select_color: [int, int, int] = (63, 115, 255),
|
||||
# out_line: int = 2,
|
||||
# batch: Batch = None,
|
||||
# group: Group = None):
|
||||
# if batch is None:
|
||||
# batch = Batch()
|
||||
# if group is None:
|
||||
# group = Group()
|
||||
# super().__init__(x, y, width, height)
|
||||
# self.enabled = False
|
||||
# self._text = message
|
||||
# self._cursor_poi = 0
|
||||
# self.font = font.load(name=font_name, size=font_size,
|
||||
# bold=font_bold, italic=font_italic, stretch=font_stretch,
|
||||
# dpi=font_dpi)
|
||||
# self.font_height = self.font.ascent - self.font.descent
|
||||
# self.out_bound = out_line
|
||||
# if DR_status.InputBox_use_TextEntry:
|
||||
# # 基于IncrementalTextLayout的处理系统
|
||||
# self._doc = FormattedDocument(message)
|
||||
# # self._doc.set_style()
|
||||
# self._layout = IncrementalTextLayout(self._doc, self.font, width, height,
|
||||
# batch=batch, group=group)
|
||||
# self._input_box = widgets.TextEntry(x, y, width, height,
|
||||
# self._doc, self._layout,
|
||||
# batch=batch, group=group)
|
||||
# else:
|
||||
# # 基于Label的处理系统
|
||||
# self._input_box = Label(x=x + out_line, y=y + out_line,
|
||||
# width=width, height=self.font_height + self.out_bound * 2,
|
||||
# color=text_color,
|
||||
# font_name=font_name, font_size=font_size,
|
||||
# batch=batch, group=group,
|
||||
# text=message)
|
||||
# self._HTML_box = HTMLLabel(x=x + out_line, y=y + out_line + 30,
|
||||
# width=width, height=self.font_height + self.out_bound * 2,
|
||||
# batch=batch, group=group,
|
||||
# text=message)
|
||||
# self._out_box = Rectangle(x=x - out_line, y=y - out_line,
|
||||
# color=out_line_color,
|
||||
# width=width + (out_line * 2), height=height + (out_line * 2),
|
||||
# batch=batch, group=group)
|
||||
# self._光标 = Rectangle(x=x + out_line, y=y + out_line,
|
||||
# color=cursor_color,
|
||||
# width=1, height=self.font_height,
|
||||
# batch=batch, group=group)
|
||||
# self._选择框 = Rectangle(x=x, y=y, width=0, height=self.font_height,
|
||||
# color=select_color)
|
||||
# self._选择的字 = Label(x=x, y=y, width=0, height=self.font_height,
|
||||
# color=text_color,
|
||||
# font_name=font_name, font_size=font_size,
|
||||
# batch=batch, group=group,
|
||||
# text='')
|
||||
#
|
||||
# """
|
||||
# 输入框的属性
|
||||
# """
|
||||
#
|
||||
# # 本身属性
|
||||
# @property
|
||||
# def text(self) -> str: # 输入框的文本
|
||||
# return self._text
|
||||
#
|
||||
# @text.setter
|
||||
# def text(self, value) -> None:
|
||||
# assert type(value) is str, 'Input Box\'s text must be string!'
|
||||
# self._text = value
|
||||
# self._input_box.text = value
|
||||
# self._HTML_box.text = html.decode_text2HTML(value, show_style=True)
|
||||
#
|
||||
# @property
|
||||
# def cursor_poi(self) -> int: # 光标位置
|
||||
# return self._cursor_poi
|
||||
#
|
||||
# @cursor_poi.setter
|
||||
# def cursor_poi(self, value) -> None:
|
||||
# assert type(value) is int, 'Input Box\'s cursor poi must be int!'
|
||||
# self._cursor_poi = value
|
||||
# self._光标.x = self.x + self.out_bound + self._input_box.content_width
|
||||
#
|
||||
# # 渲染时属性
|
||||
# @property
|
||||
# def opacity(self) -> int: # 透明度
|
||||
# return self._input_box.opacity
|
||||
#
|
||||
# @opacity.setter
|
||||
# def opacity(self, value: int) -> None:
|
||||
# assert type(value) is int, 'Input Box\'s opacity must be int!'
|
||||
# self._input_box.opacity = value
|
||||
# self._out_box.opacity = value
|
||||
# self._选择的字.opacity = value
|
||||
# self._选择框.opacity = value
|
||||
# self._光标.opacity = value
|
||||
#
|
||||
# @property
|
||||
# def visible(self) -> bool: # 是否可见
|
||||
# return self._input_box.visible
|
||||
#
|
||||
# @visible.setter
|
||||
# def visible(self, value: bool) -> None:
|
||||
# assert type(value) is bool, 'Input Box\'s visible must be bool!'
|
||||
# self._input_box.visible = value
|
||||
# self._out_box.visible = value
|
||||
# self._选择的字.visible = value
|
||||
# self._选择框.visible = value
|
||||
# self._光标.visible = value
|
||||
#
|
||||
# @property
|
||||
# def value(self) -> str:
|
||||
# return self._text
|
||||
#
|
||||
# """
|
||||
# 事件调用
|
||||
# """
|
||||
#
|
||||
# def _update_position(self):
|
||||
# self._input_box.position = self._x + self.out_bound, self._y + self.out_bound
|
||||
# self._out_box.position = self._x - self.out_bound, self._y - self.out_bound
|
||||
# self._光标.position = self._x + self.out_bound, self._y + self.out_bound
|
||||
#
|
||||
# # 输入东西
|
||||
# def on_text(self, text: str):
|
||||
# if self.enabled:
|
||||
# if text in ('\r', '\n'):
|
||||
# if self.text:
|
||||
# self.dispatch_event('on_commit', self.text)
|
||||
# else:
|
||||
# self.text = f'{self.text[:self.cursor_poi]}{text}{self.text[self.cursor_poi:]}'
|
||||
# self.cursor_poi += len(text)
|
||||
#
|
||||
# # 移动光标
|
||||
# def on_text_motion(self, motion):
|
||||
# if self.enabled:
|
||||
# # 根据按键处理
|
||||
# # 单格移动光标(上下左右)
|
||||
# if motion in (key.MOTION_UP, key.MOTION_LEFT): # 往上一个移动
|
||||
# self.cursor_poi = max(0, self._cursor_poi - 1)
|
||||
# elif motion in (key.MOTION_DOWN, key.MOTION_RIGHT): # 往下一个移动
|
||||
# self.cursor_poi = min(len(self.text), self._cursor_poi + 1)
|
||||
# # 大前后移动(开头或结尾)
|
||||
# elif motion in (
|
||||
# key.MOTION_BEGINNING_OF_LINE, key.MOTION_BEGINNING_OF_FILE, key.MOTION_PREVIOUS_PAGE): # 开头
|
||||
# self.cursor_poi = 0
|
||||
# elif motion in (key.MOTION_END_OF_LINE, key.MOTION_END_OF_FILE, key.MOTION_NEXT_PAGE): # 结尾
|
||||
# self.cursor_poi = len(self.text)
|
||||
# # 删除操作
|
||||
# elif motion == key.MOTION_BACKSPACE:
|
||||
# if self.text: # 如果有文字
|
||||
# self.text = f'{self.text[:self.cursor_poi - 1]}{self.text[self.cursor_poi:]}'
|
||||
# self.cursor_poi = max(0, self._cursor_poi - 1)
|
||||
# elif motion == key.MOTION_DELETE:
|
||||
# if self.text and self.cursor_poi != len(self.text) - 1: # 如果有文字,并且光标不在最后
|
||||
# self.text = f'{self.text[:self.cursor_poi]}{self.text[self.cursor_poi + 1:]}'
|
||||
# # 剪贴板操作
|
||||
# elif motion == key.MOTION_COPY:
|
||||
# pass
|
||||
# elif motion == key.MOTION_PASTE:
|
||||
# paste_text = paste()
|
||||
# self.text = f'{self.text[:self.cursor_poi]}{paste_text}{self.text[self.cursor_poi:]}'
|
||||
# self.cursor_poi += len(paste_text)
|
||||
#
|
||||
# def on_text_motion_select(self, motion):
|
||||
# pass
|
||||
#
|
||||
# def on_mouse_press(self, x, y, buttons, modifiers):
|
||||
# if self._check_hit(x, y) and self._input_box.visible:
|
||||
# self.enabled = True
|
||||
# else:
|
||||
# self.enabled = False
|
||||
#
|
||||
# def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
|
||||
# pass
|
||||
#
|
||||
# def on_mouse_release(self, x, y, buttons, modifiers):
|
||||
# pass
|
||||
#
|
||||
# def on_commit(self, text: str):
|
||||
# pass
|
||||
|
||||
else:
|
||||
InputBox = widgets.TextEntry
|
||||
# class InputBox(widgets.TextEntry):
|
||||
# pass
|
@ -7,17 +7,24 @@
|
||||
import typing
|
||||
|
||||
from pyglet.text import Label
|
||||
from pyglet.clock import get_frequency
|
||||
from pyglet.graphics import Batch, Group
|
||||
from pyglet.clock import get_frequency
|
||||
|
||||
# Difficult Rocket function
|
||||
from Difficult_Rocket.api.types import Fonts
|
||||
# from Difficult_Rocket.utils import translate
|
||||
from Difficult_Rocket.api.screen import BaseScreen
|
||||
# from Difficult_Rocket.command.tree import CommandTree
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from Difficult_Rocket.client import ClientWindow
|
||||
|
||||
|
||||
class DRScreen(BaseScreen):
|
||||
def __init__(self, main_window: "ClientWindow"):
|
||||
super().__init__(main_window)
|
||||
|
||||
|
||||
class DRDEBUGScreen(BaseScreen):
|
||||
def __init__(self, main_window: "ClientWindow"):
|
||||
super().__init__(main_window)
|
||||
@ -48,3 +55,4 @@ class DRDEBUGScreen(BaseScreen):
|
||||
|
||||
def on_draw(self, *dt, window: "ClientWindow"):
|
||||
self.main_batch.draw()
|
||||
# print(self.window_pointer.try_if_runs)
|
||||
|
@ -44,13 +44,10 @@ class CommandText:
|
||||
i += 1
|
||||
|
||||
def find(self, text: str) -> bool:
|
||||
startswith = self.text.startswith(text)
|
||||
if startswith:
|
||||
find = self.text.find(text)
|
||||
if find != -1:
|
||||
if not len(text) == len(self.text):
|
||||
self.text = self.text[find + len(text):] if not self.text[find+len(text)] == ' ' else self.text[find + len(text) + 1:]
|
||||
return True
|
||||
find = self.text.find(text)
|
||||
if find != -1:
|
||||
self.text = self.text[find + len(text):]
|
||||
return True
|
||||
return False
|
||||
|
||||
def re_find(self, text: str) -> Union[str, bool]:
|
||||
|
@ -77,7 +77,7 @@ def create_crash_report(info: Optional[str] = None) -> None:
|
||||
crash_info = crash_info_handler(info)
|
||||
if 'crash_report' not in os.listdir('./'):
|
||||
os.mkdir('./crash_report')
|
||||
date_time = time.strftime('%Y-%m-%d %H-%M-%S', time.localtime())
|
||||
date_time = time.strftime('%Y-%m-%d %H-%M-%S', time.gmtime(time.time()))
|
||||
filename = f'crash-{date_time}.md'
|
||||
cache_stream = io.StringIO()
|
||||
try:
|
||||
@ -92,17 +92,16 @@ def create_crash_report(info: Optional[str] = None) -> None:
|
||||
|
||||
def write_cache(cache_stream, crash_info):
|
||||
# 开头信息
|
||||
cache_stream.write(Head_message.format(now_time=time.strftime('%Y/%m/%d %H:%M:%S', time.localtime())))
|
||||
cache_stream.write(Head_message.format(now_time=time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(time.time()))))
|
||||
# 崩溃信息
|
||||
cache_stream.write(crash_info)
|
||||
|
||||
|
||||
def write_info_to_cache(cache_stream):
|
||||
# 运行状态信息
|
||||
from Difficult_Rocket import DR_status
|
||||
from Difficult_Rocket.runtime import DR_runtime
|
||||
from Difficult_Rocket import DR_status, DR_runtime
|
||||
cache_stream.write(Run_message)
|
||||
cache_stream.write(markdown_line_handler(f'DR Version: {Difficult_Rocket.sdk_version}', level=1))
|
||||
cache_stream.write(markdown_line_handler(f'DR Version: {Difficult_Rocket.game_version}', level=1))
|
||||
cache_stream.write(markdown_line_handler(f'DR language: {DR_runtime.language}', level=1))
|
||||
cache_stream.write(markdown_line_handler(f'Running Dir: {Path(os.curdir).resolve()}', level=1))
|
||||
cache_stream.write(f"\n{DR_runtime.as_markdown()}")
|
||||
|
@ -1,125 +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
|
||||
"""
|
||||
|
||||
from typing import Optional, Union, Tuple
|
||||
|
||||
# from libs import pyglet
|
||||
# from pyglet import font
|
||||
from pyglet.text import Label
|
||||
from pyglet.window import mouse
|
||||
from pyglet.gui import widgets
|
||||
# from pyglet.sprite import Sprite
|
||||
from pyglet.shapes import Rectangle
|
||||
# from pyglet.image import AbstractImage
|
||||
from pyglet.graphics import Batch, Group
|
||||
|
||||
from Difficult_Rocket.api.types import Fonts
|
||||
|
||||
# from Difficult_Rocket import DR_status
|
||||
|
||||
|
||||
RGBA = Tuple[int, int, int, int]
|
||||
|
||||
|
||||
class BaseTheme:
|
||||
"""
|
||||
用于定义主题的类
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
main_color: RGBA = (39, 73, 114, 256),
|
||||
secondary_color: RGBA = (66, 150, 250, 256),
|
||||
main_font: str = Fonts.鸿蒙简体,
|
||||
):
|
||||
self.main_color = main_color
|
||||
self.secondary_color = secondary_color
|
||||
self.main_font = main_font
|
||||
|
||||
|
||||
class PressTextButton(widgets.WidgetBase):
|
||||
"""
|
||||
自带 字符 + 材质 的按钮,就不用单独做材质了
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
x: int,
|
||||
y: int,
|
||||
width: int,
|
||||
height: int,
|
||||
text: str,
|
||||
font: str = Fonts.鸿蒙简体,
|
||||
font_size: int = 13,
|
||||
batch: Optional[Batch] = None,
|
||||
group: Optional[Group] = None,
|
||||
):
|
||||
super().__init__(x, y, width, height)
|
||||
self.back_ground_batch = batch or Batch()
|
||||
self.front_batch = batch or Batch()
|
||||
if group:
|
||||
self.front_group = Group(order=10, parent=group)
|
||||
self.back_ground_group = Group(order=5, parent=group)
|
||||
else:
|
||||
self.front_group = Group(order=5)
|
||||
self.back_ground_group = Group(order=10)
|
||||
|
||||
self.pressed = False
|
||||
|
||||
self.untouched_color = (39, 73, 114, 255)
|
||||
self.touched_color = (66, 150, 250, 255)
|
||||
self.hit_color = (15, 135, 250, 255)
|
||||
# from ImGui
|
||||
|
||||
self.text = text
|
||||
self.text_label = Label(font_name=font, font_size=font_size,
|
||||
batch=self.front_batch, group=self.front_group,
|
||||
x=self._x, y=self._y, text=self.text)
|
||||
self.back_rec = Rectangle(x=self._x, y=self._y, width=self._width, height=self._height,
|
||||
color=self.untouched_color, # ImGui color
|
||||
batch=self.back_ground_batch, group=self.back_ground_group)
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self.text
|
||||
|
||||
def __contains__(self, item):
|
||||
return item in self.back_rec
|
||||
|
||||
def on_mouse_motion(self, x, y, dx, dy):
|
||||
if (x, y) in self.back_rec:
|
||||
self.back_rec.color = self.touched_color
|
||||
else:
|
||||
self.pressed = False
|
||||
self.back_rec.color = self.untouched_color
|
||||
|
||||
def on_mouse_press(self, x, y, buttons, modifiers) -> bool:
|
||||
if (x, y) in self and buttons == mouse.LEFT:
|
||||
self.back_rec.color = self.hit_color
|
||||
self.dispatch_event('on_press', x, y)
|
||||
self.pressed = True
|
||||
return True
|
||||
return False
|
||||
|
||||
def on_mouse_release(self, x, y, buttons, modifiers):
|
||||
if self.pressed and (x, y) in self:
|
||||
self.back_rec.color = self.touched_color
|
||||
self.pressed = False
|
||||
|
||||
def _update_position(self):
|
||||
self.text_label.position = self._x, self._y
|
||||
self.back_rec.position = self._x, self._y
|
||||
self.back_rec.width = self._width
|
||||
self.back_rec.height = self._height
|
||||
...
|
||||
|
||||
|
||||
PressTextButton.register_event_type('on_press')
|
@ -11,24 +11,33 @@ github: @shenjackyuanjie
|
||||
gitee: @shenjackyuanjie
|
||||
"""
|
||||
|
||||
import sys
|
||||
import time
|
||||
import logging
|
||||
import traceback
|
||||
import importlib
|
||||
import importlib.util
|
||||
import logging.config
|
||||
import multiprocessing
|
||||
|
||||
from io import StringIO
|
||||
from pathlib import Path
|
||||
from typing import List, Optional, Dict
|
||||
from typing import TYPE_CHECKING, List, Optional, Dict, TypeVar
|
||||
|
||||
if __name__ == '__main__': # been start will not run this
|
||||
sys.path.append('/bin/libs')
|
||||
sys.path.append('/bin')
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from Difficult_Rocket.api.mod import ModInfo
|
||||
else:
|
||||
ModInfo = TypeVar('ModInfo')
|
||||
from Difficult_Rocket.utils import tools
|
||||
from Difficult_Rocket.api.types import Options
|
||||
from Difficult_Rocket.utils.translate import tr
|
||||
from Difficult_Rocket.runtime import DR_runtime
|
||||
from Difficult_Rocket.mod.loader import ModManager
|
||||
from Difficult_Rocket.utils.thread import new_thread
|
||||
from Difficult_Rocket.crash import write_info_to_cache
|
||||
from Difficult_Rocket import client, server, DR_status
|
||||
from Difficult_Rocket import client, server, DR_status, DR_runtime
|
||||
|
||||
|
||||
class Console(Options):
|
||||
@ -59,9 +68,6 @@ class Console(Options):
|
||||
def get_command(self) -> Optional[str]:
|
||||
return self.caches.pop(0) if self.caches else None
|
||||
|
||||
def new_command(self) -> None:
|
||||
return None
|
||||
|
||||
|
||||
class Game(Options):
|
||||
name = 'MainGame'
|
||||
@ -72,21 +78,75 @@ class Game(Options):
|
||||
console_class: Console = Console
|
||||
|
||||
main_config: Dict
|
||||
logging_config: Dict
|
||||
logger: logging.Logger
|
||||
|
||||
mod_manager: ModManager
|
||||
mod_module: List["ModInfo"] = []
|
||||
|
||||
def dispatch_mod_event(self, event_name: str, *args, **kwargs) -> None:
|
||||
self.mod_manager.dispatch_event(event_name, *args, **kwargs)
|
||||
def init_logger(self) -> None:
|
||||
log_path = self.logging_config['handlers']['file']['filename']
|
||||
log_path = f"logs/{log_path.format(time.strftime('%Y-%m-%d %H-%M-%S' , time.gmtime(DR_runtime.start_time_ns / 1000_000_000)))}"
|
||||
mkdir = False
|
||||
if not Path('logs/').is_dir():
|
||||
Path('logs/').mkdir()
|
||||
mkdir = True
|
||||
self.logging_config['handlers']['file']['filename'] = log_path
|
||||
logging.config.dictConfig(self.logging_config)
|
||||
self.logger = logging.getLogger('main')
|
||||
if mkdir:
|
||||
self.logger.info(tr().main.logger.mkdir())
|
||||
|
||||
def init_mods(self) -> None:
|
||||
"""验证/加载 mod"""
|
||||
from Difficult_Rocket.mod import loader as mod_loader
|
||||
mod_loader.logger = logging.getLogger('mod_manager')
|
||||
self.mod_manager = ModManager()
|
||||
mod_class = self.mod_manager.load_mods()
|
||||
self.mod_manager.init_mods(mod_class)
|
||||
self.dispatch_mod_event('on_load', game=self)
|
||||
self.mod_module = []
|
||||
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.info().format(mod, e))
|
||||
self.logger.info(tr().main.mod.load.done())
|
||||
self.mod_module = 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 init_console(self) -> None:
|
||||
self.console = self.console_class()
|
||||
@ -106,15 +166,32 @@ class Game(Options):
|
||||
else:
|
||||
self.client.start()
|
||||
|
||||
def dispatch_event(self, event_name: str, *args, **kwargs) -> None:
|
||||
"""向 mod 分发事件"""
|
||||
for mod in self.mod_module:
|
||||
if hasattr(mod, event_name):
|
||||
try:
|
||||
getattr(mod, event_name)(*args, **kwargs)
|
||||
except Exception:
|
||||
error = traceback.format_exc()
|
||||
self.logger.error(tr().main.mod.event.error().format(event_name, error, mod.mod_id))
|
||||
|
||||
def log_env(self) -> None:
|
||||
self.logger.info(f'\n{self.as_markdown()}')
|
||||
cache_steam = StringIO()
|
||||
write_info_to_cache(cache_steam)
|
||||
text = cache_steam.getvalue()
|
||||
self.logger.info(text)
|
||||
self.flush_option()
|
||||
config_cache = self.logging_config.copy()
|
||||
self.logging_config = {"logging_config": "too long to show"}
|
||||
self.logger.info(f"\n{self.as_markdown()}")
|
||||
self.logging_config = config_cache
|
||||
|
||||
def setup(self) -> None:
|
||||
self.client = client.Client(game=self, net_mode='local')
|
||||
self.server = server.Server(net_mode='local')
|
||||
|
||||
def init(self, **kwargs) -> bool:
|
||||
self.logger = logging.getLogger('main')
|
||||
self.load_file()
|
||||
self.setup()
|
||||
self.log_env()
|
||||
@ -122,6 +199,9 @@ class Game(Options):
|
||||
|
||||
def load_file(self) -> bool:
|
||||
"""加载文件"""
|
||||
self.logging_config = tools.load_file('configs/logger.toml')
|
||||
self.init_logger()
|
||||
self.init_mods()
|
||||
self.init_console()
|
||||
return True
|
||||
|
||||
|
@ -4,203 +4,38 @@
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
import sys
|
||||
import time
|
||||
import logging
|
||||
import traceback
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Optional, TypeVar
|
||||
from typing import List, Dict, Optional
|
||||
|
||||
from Difficult_Rocket.mod.api import ModInfo
|
||||
from Difficult_Rocket.utils.translate import tr
|
||||
from Difficult_Rocket.api.screen import BaseScreen
|
||||
from Difficult_Rocket.api.types import Options, Version
|
||||
|
||||
Game = TypeVar('Game')
|
||||
|
||||
logger = logging.getLogger('mod_manager')
|
||||
ONE_FILE_SUFFIX = ('.py', '.pyc', '.pyd')
|
||||
PACKAGE_SUFFIX = ('.pyz', '.zip', '.dr_mod')
|
||||
|
||||
|
||||
def _add_path_to_sys(paths: List[Path]):
|
||||
for path in paths:
|
||||
if str(path) not in sys.path:
|
||||
sys.path.append(str(path))
|
||||
from Difficult_Rocket.mod.api import ModInfo
|
||||
# from Difficult_Rocket import DR_status, DR_runtime
|
||||
|
||||
|
||||
class ModManager(Options):
|
||||
name = 'Mod Manager'
|
||||
logger: logging.Logger
|
||||
|
||||
mods_path: List[Path] = [Path('./mods')]
|
||||
find_mod_paths: Dict[str, Path] = {}
|
||||
loaded_mod_modules: Dict[str, ModInfo] = {}
|
||||
|
||||
def get_mod_module(self, mod_name: str) -> Optional[ModInfo]:
|
||||
def find_mods(self) -> List[Path]:
|
||||
"""
|
||||
获取指定 mod 的模块
|
||||
:param mod_name: mod 名
|
||||
查找mods文件夹下的所有mod
|
||||
:return:
|
||||
"""
|
||||
for mod in self.loaded_mod_modules.values():
|
||||
if mod.name == mod_name:
|
||||
return mod
|
||||
return None
|
||||
|
||||
def dispatch_event(self, event_name: str, *args, **kwargs):
|
||||
"""
|
||||
分发事件
|
||||
:param event_name: 事件名
|
||||
:param args: 事件参数
|
||||
:param kwargs: 事件参数
|
||||
:return:
|
||||
"""
|
||||
for mod in self.loaded_mod_modules.values():
|
||||
if hasattr(mod, event_name):
|
||||
try:
|
||||
getattr(mod, event_name)(*args, **kwargs)
|
||||
except Exception as e:
|
||||
logger.error(tr().mod.event.error().format(mod, event_name, e, traceback.format_exc()))
|
||||
|
||||
def load_mod(self, mod_path: Path) -> Optional[type(ModInfo)]:
|
||||
"""
|
||||
加载指定路径下的 mod
|
||||
:param mod_path: mod 的路径
|
||||
:return:
|
||||
"""
|
||||
if not mod_path.exists():
|
||||
logger.error(tr().mod.load.faild.not_exist().format(mod_path))
|
||||
return None
|
||||
_add_path_to_sys([mod_path.parent])
|
||||
try:
|
||||
if mod_path.name == '__pycache__':
|
||||
# 忽略 __pycache__ 文件夹 (Python 编译文件)
|
||||
return None
|
||||
logger.info(tr().mod.load.loading().format(mod_path))
|
||||
if mod_path.is_dir() or mod_path.suffix in PACKAGE_SUFFIX or mod_path.suffix in ONE_FILE_SUFFIX:
|
||||
# 文件夹 mod
|
||||
loading_mod = importlib.import_module(mod_path.name)
|
||||
if not hasattr(loading_mod, 'mod_class') or not issubclass(loading_mod.mod_class, ModInfo):
|
||||
logger.warning(tr().mod.load.faild.no_mod_class().format(mod_path))
|
||||
return None
|
||||
mod_class: type(ModInfo) = loading_mod.mod_class # 获取 mod 类
|
||||
if mod_class.mod_id not in self.find_mod_paths:
|
||||
self.find_mod_paths[mod_class.mod_id] = mod_path
|
||||
return mod_class
|
||||
except ImportError:
|
||||
logger.warning(tr().mod.load.faild.error().format(mod_path, traceback.format_exc()))
|
||||
return None
|
||||
|
||||
def find_mods_in_path(self, extra_mods_path: Optional[List[Path]] = None) -> List[Path]:
|
||||
"""
|
||||
查找所有 mod 路径
|
||||
:return: 找到的 mod 的路径 (未校验)
|
||||
"""
|
||||
find_path = self.mods_path + (extra_mods_path if extra_mods_path is not None else [])
|
||||
mods_path = []
|
||||
start_time = time.time()
|
||||
for path in find_path:
|
||||
if not path.exists():
|
||||
path.mkdir(parents=True)
|
||||
continue
|
||||
for mod in path.iterdir():
|
||||
if mod.name == '__pycache__':
|
||||
# 忽略 __pycache__ 文件夹 (Python 编译文件)
|
||||
continue
|
||||
if mod.is_dir() or mod.suffix in PACKAGE_SUFFIX or mod.suffix in ONE_FILE_SUFFIX:
|
||||
# 文件夹 mod
|
||||
mods_path.append(mod)
|
||||
logger.info(tr().mod.finded().format(len(mods_path), time.time() - start_time))
|
||||
return mods_path
|
||||
|
||||
def load_mods(self,
|
||||
extra_path: Optional[List[Path]] = None,
|
||||
extra_mod_path: Optional[List[Path]] = None) -> List[type(ModInfo)]:
|
||||
"""
|
||||
加载所有 mod (可提供额外的 mod 路径)
|
||||
:param extra_path: 额外的 mod 路径
|
||||
:param extra_mod_path: 额外的找到的 mod 路径
|
||||
:return:
|
||||
"""
|
||||
find_path = self.mods_path + (extra_path if extra_path is not None else [])
|
||||
_add_path_to_sys(find_path)
|
||||
mods = []
|
||||
start_time = time.time()
|
||||
logger.info(tr().mod.load.start().format(find_path))
|
||||
for path in find_path:
|
||||
for path in self.mods_path:
|
||||
if not path.exists():
|
||||
path.mkdir(parents=True)
|
||||
continue
|
||||
for mod in path.iterdir():
|
||||
if (cache := self.load_mod(mod)) is not None:
|
||||
mods.append(cache)
|
||||
if extra_mod_path is not None:
|
||||
for path in extra_mod_path:
|
||||
if (cache := self.load_mod(path)) is not None:
|
||||
mods.append(cache)
|
||||
logger.info(tr().mod.load.use_time().format(time.time() - start_time))
|
||||
return mods
|
||||
...
|
||||
|
||||
def init_mods(self, mods: List[type(ModInfo)]):
|
||||
"""
|
||||
加载 mod
|
||||
:param mods: 要加载的 mod 的 ModInfo 类
|
||||
:return:
|
||||
"""
|
||||
start_time = time.time()
|
||||
for mod_class in mods:
|
||||
try:
|
||||
init_mod = mod_class()
|
||||
self.loaded_mod_modules[init_mod.mod_id] = init_mod
|
||||
logger.info(tr().mod.init.success().format(init_mod, init_mod.version))
|
||||
except Exception as e:
|
||||
logger.error(tr().mod.init.faild().format(mod_class, e, traceback.format_exc()))
|
||||
continue
|
||||
logger.info(tr().mod.init.use_time().format(time.time() - start_time))
|
||||
|
||||
def unload_mod(self, mod_id: str, game: Game) -> Optional[ModInfo]:
|
||||
"""
|
||||
卸载 mod
|
||||
:param mod_id: 要卸载的 mod id
|
||||
:param game: 游戏实例
|
||||
:return: 卸载的 mod 的 ModInfo 类
|
||||
"""
|
||||
if not (mod_class := self.loaded_mod_modules.get(mod_id)) and (mod_class := self.get_mod_module(mod_id)) is None:
|
||||
logger.warning(tr().mod.unload.faild.not_find().format(mod_id))
|
||||
return None
|
||||
try:
|
||||
mod_class.on_unload(game=game)
|
||||
self.loaded_mod_modules.pop(mod_class.mod_id)
|
||||
logger.info(tr().mod.unload.success().format(mod_id))
|
||||
return mod_class
|
||||
except Exception as e:
|
||||
logger.error(tr().mod.unload.faild.error().format(mod_id, e, traceback.format_exc()))
|
||||
return None
|
||||
|
||||
def reload_mod(self, mod_id: str, game: Game):
|
||||
"""
|
||||
重载 mod
|
||||
:param mod_id:
|
||||
:param game:
|
||||
:return:
|
||||
"""
|
||||
unload = self.unload_mod(mod_id, game)
|
||||
if unload is None:
|
||||
return
|
||||
mod_class: Optional[ModInfo] = None
|
||||
if unload.mod_id not in self.find_mod_paths:
|
||||
logger.warning(tr().mod.reload.faild.not_find().format(unload.mod_id))
|
||||
paths = self.find_mods_in_path()
|
||||
for path in paths:
|
||||
mod_class = self.load_mod(path)
|
||||
if mod_class is not None and mod_class.mod_id == unload.mod_id:
|
||||
self.init_mods([mod_class])
|
||||
break
|
||||
else:
|
||||
mod_class = self.load_mod(self.find_mod_paths[unload.mod_id])
|
||||
if mod_class is not None:
|
||||
self.init_mods([mod_class])
|
||||
if mod_id in self.loaded_mod_modules and mod_class is not None:
|
||||
self.loaded_mod_modules[mod_id].on_load(game=game, old_self=mod_class)
|
||||
logger.info(tr().mod.reload.success().format(mod_id))
|
||||
def init(self) -> None:
|
||||
self.logger = logging.getLogger('client')
|
||||
self.logger.name = 'mod_manager'
|
||||
self.logger.info('Mod Manager init')
|
||||
|
||||
|
@ -1,79 +0,0 @@
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
import sys
|
||||
import importlib
|
||||
import traceback
|
||||
import contextlib
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
from typing import Optional, List, Tuple
|
||||
|
||||
from Difficult_Rocket.api.types import Options, Version
|
||||
|
||||
|
||||
__all__ = [
|
||||
'DR_runtime'
|
||||
]
|
||||
|
||||
|
||||
class _DR_runtime(Options):
|
||||
"""
|
||||
DR 的运行时配置 / 状态
|
||||
"""
|
||||
name = 'DR Runtime'
|
||||
|
||||
language: str = 'zh-CN'
|
||||
mod_path: str = './mods'
|
||||
DR_Mod_List: List[Tuple[str, Version]] = [] # DR Mod 列表 (name, version)
|
||||
|
||||
# run status
|
||||
start_time_ns: Optional[int] = None
|
||||
client_setup_cause_ns: Optional[int] = None
|
||||
server_setup_cause_ns: Optional[int] = None
|
||||
|
||||
def load_file(self) -> bool:
|
||||
with contextlib.suppress(FileNotFoundError):
|
||||
with open('./config/main.toml', 'r', encoding='utf-8') as f:
|
||||
import rtoml
|
||||
config_file = rtoml.load(f)
|
||||
self.language = config_file['runtime']['language']
|
||||
self.mod_path = config_file['game']['mods']['path']
|
||||
return True
|
||||
return False
|
||||
|
||||
def find_mods(self) -> List[str]:
|
||||
mods = []
|
||||
mod_path = Path(self.mod_path)
|
||||
if not mod_path.exists():
|
||||
mod_path.mkdir()
|
||||
return []
|
||||
paths = mod_path.iterdir()
|
||||
sys.path.append(self.mod_path)
|
||||
for mod_path in paths:
|
||||
try:
|
||||
if mod_path.is_dir() and mod_path.name != '__pycache__': # 处理文件夹 mod
|
||||
if importlib.util.find_spec(mod_path.name) is not None:
|
||||
mods.append(mod_path.name)
|
||||
else:
|
||||
print(f'can not import mod {mod_path} because importlib can not find spec')
|
||||
elif mod_path.suffix in ('.pyz', '.zip'): # 处理压缩包 mod
|
||||
if importlib.util.find_spec(mod_path.name) is not None:
|
||||
mods.append(mod_path.name)
|
||||
elif mod_path.suffix == '.pyd': # pyd 扩展 mod
|
||||
if importlib.util.find_spec(mod_path.name) is not None:
|
||||
mods.append(mod_path.name)
|
||||
elif mod_path.suffix == '.py': # 处理单文件 mod
|
||||
print(f'importing mod {mod_path=} {mod_path.stem}')
|
||||
if importlib.util.find_spec(mod_path.stem) is not None:
|
||||
mods.append(mod_path.stem)
|
||||
except ImportError:
|
||||
print(f'ImportError when loading mod {mod_path}')
|
||||
traceback.print_exc()
|
||||
return mods
|
||||
|
||||
|
||||
DR_runtime = _DR_runtime()
|
@ -36,7 +36,7 @@ class Server:
|
||||
# os.set
|
||||
self.process_name = 'server process'
|
||||
# config
|
||||
self.config = tools.load_file('config/main.toml')
|
||||
self.config = tools.load_file('configs/main.toml')
|
||||
# self.dev = Dev
|
||||
# self.net_mode = net_mode
|
||||
self.logger.info(tr().server.setup.use_time().format(time.time() - start_time))
|
||||
|
@ -4,10 +4,3 @@
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
__all__ = [
|
||||
'camera',
|
||||
'options',
|
||||
'thread',
|
||||
'tools',
|
||||
'translate'
|
||||
]
|
||||
|
@ -1,263 +0,0 @@
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
# Huge thanks to pyglet developers
|
||||
|
||||
from typing import Tuple, Optional
|
||||
|
||||
from pyglet.gl import gl_compat, gl
|
||||
from pyglet.math import Mat4, Vec3
|
||||
from pyglet.graphics import Group
|
||||
|
||||
|
||||
class Camera:
|
||||
"""
|
||||
|
||||
>>> from pyglet.window import Window
|
||||
>>> window = Window()
|
||||
|
||||
>>> camera = Camera(window)
|
||||
>>> @window.event
|
||||
|
||||
>>> def on_draw():
|
||||
>>> camera.begin()
|
||||
>>> window.clear()
|
||||
>>> camera.end()
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
window,
|
||||
zoom: Optional[float] = 1.0,
|
||||
dx: Optional[float] = 1.0,
|
||||
dy: Optional[float] = 1.0,
|
||||
min_zoom: Optional[float] = 1.0,
|
||||
max_zoom: Optional[float] = 1.0) -> None:
|
||||
self.window = window
|
||||
self.dx = dx
|
||||
self.dy = dy
|
||||
self.zoom = zoom
|
||||
self.min_zoom = min_zoom
|
||||
self.max_zoom = max_zoom
|
||||
self._stored_view = window.view
|
||||
|
||||
@property
|
||||
def position(self) -> Tuple[float, float]:
|
||||
return self.dx, self.dy
|
||||
|
||||
@position.setter
|
||||
def position(self, value: Tuple[float, float]):
|
||||
self.dx, self.dy = value
|
||||
|
||||
@property
|
||||
def zoom_level(self) -> float:
|
||||
return self.zoom
|
||||
|
||||
@zoom_level.setter
|
||||
def zoom_level(self, value: float) -> None:
|
||||
self.zoom = min(max(value, self.min_zoom), self.max_zoom)
|
||||
|
||||
def begin(self) -> None:
|
||||
view = self.window.view
|
||||
self._stored_view = view
|
||||
x = self.window.width / self.zoom + (self.dx / self.zoom)
|
||||
y = self.window.height / self.zoom + (self.dy / self.zoom)
|
||||
|
||||
view_matrix = view.translate((x * self.zoom, y * self.zoom, 0))
|
||||
view_matrix = view_matrix.scale((self.zoom, self.zoom, 1))
|
||||
|
||||
self.window.view = view_matrix
|
||||
|
||||
def end(self) -> None:
|
||||
self.window.view = self._stored_view
|
||||
|
||||
def reset(self):
|
||||
self.zoom = 1
|
||||
self.dx = 0
|
||||
self.dy = 0
|
||||
|
||||
def __enter__(self):
|
||||
self.begin()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.end()
|
||||
|
||||
|
||||
class CenterCamera(Camera):
|
||||
"""
|
||||
A camera that centers the view in the center of the window
|
||||
|
||||
>>> from pyglet.window import Window
|
||||
>>> window = Window()
|
||||
|
||||
>>> camera = CenterCamera(window)
|
||||
>>> @window.event
|
||||
|
||||
>>> def on_draw():
|
||||
>>> camera.begin()
|
||||
>>> window.clear()
|
||||
>>> camera.end()
|
||||
"""
|
||||
|
||||
def begin(self) -> None:
|
||||
view = self.window.view
|
||||
self._stored_view = view
|
||||
x = self.window.width / 2.0 / self.zoom + (self.dx / self.zoom)
|
||||
y = self.window.height / 2.0 / self.zoom + (self.dy / self.zoom)
|
||||
|
||||
view_matrix = view.translate((x * self.zoom, y * self.zoom, 0))
|
||||
view_matrix = view_matrix.scale((self.zoom, self.zoom, 1))
|
||||
|
||||
self.window.view = view_matrix
|
||||
|
||||
def end(self) -> None:
|
||||
self.window.view = self._stored_view
|
||||
|
||||
|
||||
class GroupCamera(Group):
|
||||
"""
|
||||
A camera by group
|
||||
can be used by just added to your widget
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
window,
|
||||
order: int = 0,
|
||||
parent: Optional[Group] = None,
|
||||
view_x: Optional[int] = 0,
|
||||
view_y: Optional[int] = 0,
|
||||
zoom: Optional[float] = 1.0,
|
||||
min_zoom: Optional[float] = 1.0,
|
||||
max_zoom: Optional[float] = 1.0):
|
||||
super().__init__(order=order, parent=parent)
|
||||
self._window = window
|
||||
self._previous_view = None
|
||||
|
||||
self._view_x = view_x
|
||||
self._view_y = view_y
|
||||
self._zoom = zoom
|
||||
self.min_zoom = min_zoom
|
||||
self.max_zoom = max_zoom
|
||||
|
||||
@property
|
||||
def view_x(self) -> int:
|
||||
return self._view_x
|
||||
|
||||
@view_x.setter
|
||||
def view_x(self, value: int):
|
||||
self._view_x = value
|
||||
|
||||
@property
|
||||
def view_y(self) -> int:
|
||||
return self._view_y
|
||||
|
||||
@view_y.setter
|
||||
def view_y(self, value: int):
|
||||
self._view_y = value
|
||||
|
||||
@property
|
||||
def zoom(self) -> float:
|
||||
return min(max(self._zoom, self.min_zoom), self.max_zoom)
|
||||
|
||||
@zoom.setter
|
||||
def zoom(self, value: float):
|
||||
self._zoom = value
|
||||
|
||||
def reset(self):
|
||||
self._view_x = 0
|
||||
self._view_y = 0
|
||||
self.zoom = 1
|
||||
|
||||
def set_state(self):
|
||||
self._previous_view = self._window.view
|
||||
|
||||
view = Mat4.from_translation(Vec3(self._view_x, self._view_y, 0))
|
||||
if self._zoom == 1.0:
|
||||
self._window.view = view
|
||||
else:
|
||||
view = view.scale(Vec3(self._zoom, self._zoom, 1))
|
||||
self._window.view = view
|
||||
|
||||
def unset_state(self):
|
||||
self._window.view = self._previous_view
|
||||
|
||||
|
||||
class CenterGroupCamera(GroupCamera):
|
||||
"""
|
||||
A camera by group
|
||||
can be used by just added to your widget
|
||||
"""
|
||||
|
||||
def set_state(self):
|
||||
self._previous_view = self._window.view
|
||||
x = (self._window.width / 2) / self._zoom + (self._view_x / self._zoom)
|
||||
y = (self._window.height / 2) / self._zoom + (self._view_y / self._zoom)
|
||||
|
||||
view = Mat4.from_translation(Vec3(x * self._zoom, y * self._zoom, 0))
|
||||
# 不懂就问 为啥这里 * zoom 下面还 * zoom
|
||||
if self._zoom == 1.0:
|
||||
self._window.view = view
|
||||
else:
|
||||
view = view.scale(Vec3(self._zoom, self._zoom, 1))
|
||||
self._window.view = view
|
||||
|
||||
def unset_state(self):
|
||||
self._window.view = self._previous_view
|
||||
|
||||
|
||||
class CenterGroupFrame(Group):
|
||||
"""
|
||||
A camera by group
|
||||
can be used by just added to your widget
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
window,
|
||||
order: int = 0,
|
||||
parent: Optional[Group] = None,
|
||||
dx: Optional[int] = 0,
|
||||
dy: Optional[int] = 0,
|
||||
width: Optional[int] = 0,
|
||||
height: Optional[int] = 0,
|
||||
zoom: Optional[float] = 1.0,
|
||||
min_zoom: Optional[float] = 1.0,
|
||||
max_zoom: Optional[float] = 1.0):
|
||||
super().__init__(order=order, parent=parent)
|
||||
self.window = window
|
||||
self.dx = dx
|
||||
self.dy = dy
|
||||
self._width = width
|
||||
self._height = height
|
||||
self._zoom = zoom
|
||||
self.min_zoom = min_zoom
|
||||
self.max_zoom = max_zoom
|
||||
|
||||
@property
|
||||
def zoom(self) -> float:
|
||||
return self._zoom
|
||||
|
||||
@zoom.setter
|
||||
def zoom(self, value: float):
|
||||
self._zoom = min(max(value, self.min_zoom), self.max_zoom)
|
||||
|
||||
def set_state(self):
|
||||
self._previous_view = self.window.view
|
||||
|
||||
gl.glScissor(int(self.dx), int(self.dy), int(self._width), int(self._height))
|
||||
gl.glViewport(int(self.dx), int(self.dy), int(self.window.width), int(self.window.height))
|
||||
gl.glEnable(gl.GL_SCISSOR_TEST)
|
||||
|
||||
x = (self.window.width / 2) / self._zoom + (self.dx / self._zoom)
|
||||
y = (self.window.height / 2) / self._zoom + (self.dy / self._zoom)
|
||||
|
||||
view = Mat4.from_translation(Vec3(x * self._zoom, y * self._zoom, 0))
|
||||
view.scale(Vec3(self._zoom, self._zoom, 1))
|
||||
self.window.view = view
|
||||
|
||||
def unset_state(self):
|
||||
self.window.view = self._previous_view
|
||||
gl.glDisable(gl.GL_SCISSOR_TEST)
|
||||
gl.glViewport(0, 0, int(self.window.width), int(self.window.height))
|
34
Difficult_Rocket/utils/log.py
Normal file
34
Difficult_Rocket/utils/log.py
Normal file
@ -0,0 +1,34 @@
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
import logging
|
||||
|
||||
from typing import Callable
|
||||
|
||||
|
||||
__all__ = [
|
||||
'get_named_client_logger',
|
||||
'get_named_server_logger',
|
||||
'get_named_main_logger',
|
||||
]
|
||||
|
||||
|
||||
def _gen_get_named_logger(from_name: str) -> Callable[[str], logging.Logger]:
|
||||
|
||||
def get_named_logger(name: str) -> logging.Logger:
|
||||
logger = logging.getLogger(from_name)
|
||||
logger.name = f'{from_name}.{name}'
|
||||
return logger
|
||||
|
||||
return get_named_logger
|
||||
|
||||
|
||||
get_named_client_logger = _gen_get_named_logger('client')
|
||||
# 用于获取一个基于 client 配置的 logger
|
||||
get_named_server_logger = _gen_get_named_logger('server')
|
||||
# 用于获取一个基于 server 配置的 logger
|
||||
get_named_main_logger = _gen_get_named_logger('main')
|
||||
# 用于获取一个基于 main 配置的 logger
|
@ -4,21 +4,19 @@
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
import shutil
|
||||
import warnings
|
||||
import traceback
|
||||
from io import StringIO
|
||||
from dataclasses import dataclass
|
||||
from typing import get_type_hints, Type, List, Union, Dict, Any, Callable, Tuple, Optional, TYPE_CHECKING, Iterable
|
||||
|
||||
__all__ = [
|
||||
'get_type_hints_',
|
||||
'Options',
|
||||
'OptionsError',
|
||||
'OptionNotFound',
|
||||
'OptionNameNotDefined',
|
||||
'Fonts',
|
||||
'FontData'
|
||||
]
|
||||
__all__ = ['get_type_hints_',
|
||||
'Options',
|
||||
'Fonts',
|
||||
'FontData',
|
||||
'OptionsError',
|
||||
'OptionNotFound',
|
||||
'OptionNameNotDefined']
|
||||
|
||||
|
||||
def get_type_hints_(cls: Type):
|
||||
@ -176,12 +174,15 @@ class Options:
|
||||
self.cached_options = self.option()
|
||||
return self.cached_options
|
||||
|
||||
def option_with_len(self) -> Tuple[List[Tuple[str, Any, Type]], int, int, int]:
|
||||
def option_with_len(self, longest: Optional[int] = None) -> Tuple[List[Tuple[str, Union[Any, Type], Type]], int, int, int]:
|
||||
"""
|
||||
返回一个可以用于打印的 option 列表
|
||||
:return:
|
||||
"""
|
||||
options = self.flush_option()
|
||||
if longest is None:
|
||||
options = self.flush_option()
|
||||
else:
|
||||
options = self.str_option(longest)
|
||||
max_len_key = 1
|
||||
max_len_value = 1
|
||||
max_len_value_t = 1
|
||||
@ -191,61 +192,19 @@ class Options:
|
||||
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] # noqa
|
||||
option_list.append((key, value, value_t))
|
||||
return option_list, max_len_key, max_len_value, max_len_value_t
|
||||
|
||||
def as_markdown(self, longest: Optional[int] = None) -> str:
|
||||
"""
|
||||
返回一个 markdown 格式的 option 字符串
|
||||
:param longest: 最长的输出长度
|
||||
:return: markdown 格式的 option 字符串
|
||||
"""
|
||||
value = self.option_with_len()
|
||||
value = self.option_with_len(longest)
|
||||
cache = StringIO()
|
||||
option_len = max(value[1], len('Option'))
|
||||
value_len = max(value[2], len('Value'))
|
||||
value_type_len = max(value[3], len('Value Type'))
|
||||
|
||||
# | Option | Value | Value Type |
|
||||
shortest = len('Option" "Value" "Value Type')
|
||||
|
||||
if longest is not None:
|
||||
console_width = max(longest, shortest)
|
||||
else:
|
||||
console_width = shutil.get_terminal_size(fallback=(100, 80)).columns
|
||||
console_width = max(console_width, shortest)
|
||||
|
||||
# 为每一栏 预分配 1/3 或者 需要的宽度 (如果不需要 1/3)
|
||||
option_len = min(option_len, console_width // 3)
|
||||
value_len = min(value_len, console_width // 3)
|
||||
value_type_len = min(value_type_len, console_width // 3)
|
||||
|
||||
# 先指定每一个列的输出最窄宽度, 然后去尝试增加宽度
|
||||
# 循环分配新空间之前 首先检查是否已经不需要多分配 (and 后面)
|
||||
while option_len + value_len + value_type_len + 16 < console_width\
|
||||
and (option_len < value[1]
|
||||
or value_len < value[2]
|
||||
or value_type_len < value[3]):
|
||||
# 每一个部分的逻辑都是
|
||||
# 如果现在的输出长度小于原始长度
|
||||
# 并且长度 + 1 之后的总长度依然在允许范围内
|
||||
# 那么就 + 1
|
||||
if option_len < value[1] and option_len + value_len + value_type_len + 15 < console_width:
|
||||
option_len += 1
|
||||
if value_len < value[2] and option_len + value_len + value_type_len + 15 < console_width:
|
||||
value_len += 1
|
||||
if value_type_len < value[3] and option_len + value_len + value_type_len + 15 < console_width:
|
||||
value_type_len += 1
|
||||
# 实际上 对于列表(可变对象) for 出来的这个值是一个引用
|
||||
# 所以可以直接修改 string
|
||||
for v in value[0]:
|
||||
if len(str(v[0])) > option_len:
|
||||
v[0] = f'{str(v[0])[:value_len - 3]}...'
|
||||
if len(str(v[1])) > value_len:
|
||||
v[1] = f'{str(v[1])[:value_len - 3]}...'
|
||||
if len(str(v[2])) > value_type_len:
|
||||
v[2] = f'{str(v[2])[:value_len - 3]}..'
|
||||
|
||||
cache.write(
|
||||
f"| Option{' ' * (option_len - 3)}| Value{' ' * (value_len - 2)}| Value Type{' ' * (value_type_len - 7)}|\n")
|
||||
cache.write(f'|:{"-" * (option_len + 3)}|:{"-" * (value_len + 3)}|:{"-" * (value_type_len + 3)}|\n')
|
||||
|
@ -16,21 +16,22 @@ import sys
|
||||
import time
|
||||
import math
|
||||
import json
|
||||
import rtoml
|
||||
import logging
|
||||
import configparser
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Union, Optional
|
||||
from typing import Union
|
||||
from xml.etree import ElementTree
|
||||
|
||||
import rtoml
|
||||
|
||||
from defusedxml.ElementTree import parse
|
||||
|
||||
from Difficult_Rocket.exception.unsupport import NoMoreJson5
|
||||
|
||||
# logger
|
||||
tools_logger = logging.getLogger('tools')
|
||||
tools_logger = logging.getLogger('part-tools')
|
||||
"""
|
||||
file config
|
||||
file configs
|
||||
"""
|
||||
|
||||
file_error = {FileNotFoundError: 'no {filetype} file was founded!:\n file name: {filename}\n file_type: {filetype}\n stack: {stack}',
|
||||
@ -38,12 +39,10 @@ file_error = {FileNotFoundError: 'no {filetype} file was founded!:\n file name:
|
||||
Exception: 'get some {error_type} when read {filetype} file {filename}! \n file type: {} \n file name: {} \n stack: {stack}'}
|
||||
|
||||
|
||||
def load_file(file_name: Union[str, Path],
|
||||
stack: Optional[Union[str, list, dict]] = None,
|
||||
raise_error: Optional[bool] = True,
|
||||
encoding: Optional[str] = 'utf-8') -> Union[dict, ElementTree.ElementTree]:
|
||||
if isinstance(file_name, Path):
|
||||
file_name = str(file_name)
|
||||
def load_file(file_name: str,
|
||||
stack: Union[str, list, dict, None] = None,
|
||||
raise_error: bool = True,
|
||||
encoding: str = 'utf-8') -> Union[dict, ElementTree.ElementTree]:
|
||||
f_type = file_name[file_name.rfind('.') + 1:] # 从最后一个.到末尾 (截取文件格式)
|
||||
get_file = NotImplementedError('解析失败,请检查文件类型/文件内容/文件是否存在!')
|
||||
try:
|
||||
@ -99,7 +98,7 @@ def save_dict_file(file_name: str,
|
||||
|
||||
|
||||
# main config
|
||||
main_config_file = load_file('./config/main.toml')
|
||||
main_config_file = load_file('./configs/main.toml')
|
||||
|
||||
|
||||
def get_At(name, in_xml, need_type=str):
|
||||
|
@ -14,13 +14,11 @@ gitee: @shenjackyuanjie
|
||||
import os
|
||||
import inspect
|
||||
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass
|
||||
from typing import Union, Tuple, Any, List, Dict, Hashable, Optional
|
||||
|
||||
from Difficult_Rocket import DR_status
|
||||
from Difficult_Rocket import DR_runtime, DR_status
|
||||
from Difficult_Rocket.utils import tools
|
||||
from Difficult_Rocket.runtime import DR_runtime
|
||||
from Difficult_Rocket.exception.language import (LanguageNotFound,
|
||||
TranslateKeyNotFound)
|
||||
|
||||
@ -140,12 +138,12 @@ class Translates:
|
||||
|
||||
def __str__(self):
|
||||
if not any(not x[0] for x in self._get_list):
|
||||
return str(self._value)
|
||||
return self._value
|
||||
if self._config.crack_normal:
|
||||
return f'{".".join(f"{gets[1]}({gets[0]})" for gets in self._get_list)}'
|
||||
elif self._config.insert_crack:
|
||||
return f'{self._value}.{".".join(gets[1] for gets in self._get_list if not gets[0])}'
|
||||
return str(self._value)
|
||||
return self._value
|
||||
|
||||
|
||||
class Tr:
|
||||
@ -154,20 +152,15 @@ class Tr:
|
||||
GOOD
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
language: str = None,
|
||||
config: Optional[TranslateConfig] = None,
|
||||
lang_path: Optional[Path] = None):
|
||||
def __init__(self, language: str = None, config: Optional[TranslateConfig] = None):
|
||||
"""
|
||||
诶嘿,我抄的MCDR
|
||||
:param language: Tr 所使用的的语言
|
||||
:param config: 配置
|
||||
:param lang_path: 语言文件夹路径
|
||||
"""
|
||||
self.language_name = language if language is not None else DR_runtime.language
|
||||
self.language_path = lang_path if lang_path is not None else Path('assets/lang')
|
||||
self.translates: Dict[str, Union[str, Dict]] = tools.load_file(self.language_path / f'{self.language_name}.toml')
|
||||
self.default_translate: Dict = tools.load_file(f'{self.language_path}/{DR_status.default_language}.toml')
|
||||
self.translates: Dict[str, Union[str, Dict]] = tools.load_file(f'configs/lang/{self.language_name}.toml')
|
||||
self.default_translate: Dict = tools.load_file(f'configs/lang/{DR_status.default_language}.toml')
|
||||
self.default_config = config.set('source', self) if config is not None else TranslateConfig(source=self)
|
||||
self.translates_cache = Translates(value=self.translates, config=self.default_config.copy())
|
||||
|
||||
@ -191,11 +184,11 @@ class Tr:
|
||||
if lang == ' ' or lang == '':
|
||||
raise LanguageNotFound('Can not be empty')
|
||||
lang = lang or self.language_name
|
||||
if not os.path.exists(f'{self.language_path}/{lang}.toml'):
|
||||
print(f"lang: {os.path.exists(f'{self.language_path}/{lang}.toml')} language = {lang} {self.language_name=}")
|
||||
if not os.path.exists(f'./configs/lang/{lang}.toml'):
|
||||
print(f"lang: {os.path.exists(f'./configs/lang/{lang}.toml')} language = {lang} {self.language_name=}")
|
||||
raise LanguageNotFound(lang)
|
||||
self.translates: Dict[str, Union[str, Dict]] = tools.load_file(f'{self.language_path}/{lang}.toml')
|
||||
self.default_translate: Dict = tools.load_file(f'{self.language_path}/{DR_runtime.default_language}.toml')
|
||||
self.translates: Dict[str, Union[str, Dict]] = tools.load_file(f'configs/lang/{lang}.toml')
|
||||
self.default_translate: Dict = tools.load_file(f'configs/lang/{DR_runtime.default_language}.toml')
|
||||
self.translates_cache = Translates(value=self.translates, config=self.default_config.copy())
|
||||
self.language_name = lang
|
||||
DR_runtime.language = self.language_name
|
||||
|
54
README.md
54
README.md
@ -1,5 +1,3 @@
|
||||
<div style="text-align: center;">
|
||||
|
||||
# Difficult Rocket
|
||||
|
||||
中文 | [English](./docs/README-en.md)
|
||||
@ -11,28 +9,22 @@
|
||||
|
||||
## 请注意 这个仓库未来只会发布 `DR SDK` 的更新 `DR game` 的更新会在 [这里](https://github.com/shenjackyuanjie/DR-game) 发布
|
||||
|
||||

|
||||
|
||||
<a href="https://996.icu"><img src="https://img.shields.io/badge/link-996.icu-red.svg" alt="996.icu" /></a>
|
||||
[](https://Semver.org/)
|
||||
[](https://Python.org)
|
||||
[](https://pyglet.org)
|
||||
[](https://pyglet.org)
|
||||
[](https://Python.org)
|
||||
|
||||
## 版本
|
||||
|
||||
[关于版本号的说明](./docs/src/version.md)
|
||||
|
||||
[](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
|
||||
[](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
|
||||
[](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
|
||||
[](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
|
||||
[](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
|
||||
[](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
|
||||
|
||||
[](https://stats.deeptrain.net/repo/shenjackyuanjie/Difficult-Rocket?theme=dark)
|
||||
|
||||
[DR sdk 最新 Action 构建](https://nightly.link/shenjackyuanjie/Difficult-Rocket/workflows/nuitka/main)
|
||||
|
||||
[DR rs 最新 Action 构建](https://nightly.link/shenjackyuanjie/Difficult-Rocket/workflows/dr_rs/main)
|
||||
|
||||
## English README please look [here](./docs/README-en.md)
|
||||
|
||||
> 这是一个用Python制作的类Simple Rocket游戏(简称:火箭模拟器)
|
||||
@ -50,27 +42,27 @@
|
||||
## 环境需求 (测试过的 / 开发平台)
|
||||
|
||||
- `开发平台 1 - Windows 10 x64 22H2`
|
||||
- Python `3.8.10` / `3.10.11`
|
||||
- Python `3.8.10`
|
||||
- pillow `9.5.0`
|
||||
- psutil `5.9.5`
|
||||
- rtoml `0.9.0`
|
||||
- tomlkit `0.11.8`
|
||||
- tomlkit `0.11.7`
|
||||
- defusedxml `0.7.1`
|
||||
- objprint `0.2.2`
|
||||
- viztracer `0.15.6`
|
||||
- vizplugins `0.1.3`
|
||||
- nuitka `1.6.6`
|
||||
- nuitka `1.5.6`
|
||||
- ordered-set `4.1.0`
|
||||
- imageio `2.31.0`
|
||||
- imageio `2.27.0`
|
||||
- wheel `0.40.0`
|
||||
- setuptools `67.8.0`
|
||||
- setuptools-rust `1.6.0`
|
||||
- setuptools `67.6.1`
|
||||
- setuptools-rust `1.5.2`
|
||||
- `AMD R5 5600X`
|
||||
- `AMD RX 550 4G`
|
||||
|
||||
## 需要的 Python 模块
|
||||
|
||||
- `pyglet` (已经内置 V2.0.8 路径:`./libs/pyglet`)
|
||||
- `pyglet` (已经内置 V2.0.5 路径:`./libs/pyglet`)
|
||||
- `xmltodict` (已经内置 V0.12.0 路径:`./libs/xmltodict`)
|
||||
- `pyperclip` (已经内置 V1.8.2 路径: `./libs/pyperclip`)
|
||||
|
||||
@ -81,30 +73,28 @@
|
||||
# DR contributing
|
||||
|
||||
# for images
|
||||
# not for pypy >= 3.10
|
||||
pillow >= 10.0.0; (platform_python_implementation == "PyPy" and python_version < "3.10") or platform_python_implementation == "CPython"
|
||||
pillow >= 9.5.0
|
||||
|
||||
# for sys info
|
||||
psutil >= 5.9.5
|
||||
|
||||
# for files
|
||||
rtoml >= 0.9.0
|
||||
tomlkit >= 0.11.8
|
||||
tomlkit >= 0.11.7
|
||||
defusedxml >= 0.7.1
|
||||
|
||||
# for debug
|
||||
objprint >= 0.2.2
|
||||
viztracer >= 0.15.6; platform_python_implementation != "PyPy"
|
||||
vizplugins >= 0.1.3; platform_python_implementation != "PyPy"
|
||||
viztracer >= 0.15.6
|
||||
vizplugins >= 0.1.3
|
||||
|
||||
# for compile
|
||||
nuitka >= 1.7.5
|
||||
nuitka >= 1.5.6
|
||||
ordered-set >= 4.1.0
|
||||
imageio >= 2.31.0; (platform_python_implementation == "PyPy" and python_version < "3.10") or platform_python_implementation == "CPython"
|
||||
imageio >= 2.27.0
|
||||
wheel >= 0.40.0
|
||||
setuptools >= 67.8.0
|
||||
setuptools-rust >= 1.6.0
|
||||
|
||||
setuptools >= 67.6.1
|
||||
setuptools-rust >= 1.5.2
|
||||
```
|
||||
|
||||
## 感谢
|
||||
@ -114,8 +104,8 @@ setuptools-rust >= 1.6.0
|
||||
- `tomlkit` / `rtoml` : toml 解析器
|
||||
- `xmltodict`: xml 与 dict 转换器
|
||||
- `pyperclip`: 剪贴板!
|
||||
- [rapier2d](https://rapier.rs/) : 物理模拟引擎
|
||||
- [pyo3](https://pyo3.rs/main): Rust Python 扩展
|
||||
- `rapier2d`: 物理模拟引擎
|
||||
- `pyo3`: Rust Python 扩展
|
||||
|
||||
- 主要贡献者
|
||||
- [@Rayawa](https://github.com/Rayawa) : 文档矫正 & 翻译部分 lang
|
||||
@ -123,8 +113,6 @@ setuptools-rust >= 1.6.0
|
||||
- [@Billchyi](https://github.com/Billchyi) : 文档矫正
|
||||
- [@MSDNicrosoft](https://github.com/MSDNicrosoft) : 优化代码
|
||||
|
||||
</div>
|
||||
|
||||
## 相关链接
|
||||
|
||||
## 关于分享协议
|
||||
|
@ -180,7 +180,7 @@
|
||||
<Part partType="engine-2" id="112" x="-13.000000" y="-5.500000" angle="0.000000" angleV="0.000000" editorAngle="0" activated="0" exploded="0" flippedX="0" flippedY="0">
|
||||
<Engine fuel="0.000000"/>
|
||||
</Part>
|
||||
<Part partType="port-1" id="167" x="-13.000000" y="-7.500000" angle="4.712389" angleV="0.000000" editorAngle="3" activated="1" exploded="0" flippedX="1" flippedY="0"/>
|
||||
<Part partType="port-1" id="167" x="-13.000000" y="-7.500000" angle="4.712389" angleV="0.000000" editorAngle="3" activated="0" exploded="0" flippedX="1" flippedY="0"/>
|
||||
<Part partType="engine-2" id="113" x="-19.000000" y="-5.500000" angle="0.000000" angleV="0.000000" editorAngle="0" activated="0" exploded="0" flippedX="0" flippedY="0">
|
||||
<Engine fuel="0.000000"/>
|
||||
</Part>
|
@ -20,26 +20,15 @@ logger.logfile_level = "Log file record level : "
|
||||
logger.logfile_fmt = "Log file record format : "
|
||||
logger.logfile_datefmt = "Log file date format : "
|
||||
game_start.at = "Game MainThread start at: {}"
|
||||
|
||||
[mod]
|
||||
list = "Mod list: "
|
||||
find.finded = "Mod founded: {}"
|
||||
load.start = "Loading Mod in path {}"
|
||||
load.use_time = "Mod loading has used: {} second"
|
||||
load.done = "All Mod loaded"
|
||||
load.loading = "Loading Mod: {}"
|
||||
load.faild.error = "Mod loading faild: {} error: {}"
|
||||
load.faild.not_exist = "Mod loading faild: {} mod path not exist"
|
||||
load.faild.no_mod_class = "Mod loading faild: {} no Mod class"
|
||||
init.success = "mod id: {} version: {}"
|
||||
init.faild = "Mod init faild: {} error: {}\nstack: {}"
|
||||
init.use_time = "Mod init has used: {} second"
|
||||
event.error = "Mod event {} error {} Mod: {}\nstack: {}"
|
||||
unload.not_find = "Mod unload faild: {} no Mod found"
|
||||
unload.faild = "Mod unload faild: {} error: {}\nstack: {}"
|
||||
unload.success = "Mod unload success: {}"
|
||||
reload.faild.not_find = "Mod reload faild: {} no Mod found, trying to find mod again"
|
||||
reload.success = "Mod reload success: {}"
|
||||
mod.find.start = "Checking Mod: {}"
|
||||
mod.find.faild.no_spec = "importlib can't find spec"
|
||||
mod.find.faild.no_mod_folder = "Can't find mod folder"
|
||||
mod.find.done = "All Mod checked"
|
||||
mod.load.start = "Loading Mod: {}"
|
||||
mod.load.info = "mod id: {} version: {}"
|
||||
mod.load.faild.info = "Mod load failed: {} error info: {}"
|
||||
mod.load.faild.no_mod_class = "Can't find Mod class"
|
||||
mod.load.done = "All Mod loaded"
|
||||
|
||||
[client]
|
||||
setup.start = "Client start loading"
|
||||
@ -65,6 +54,8 @@ text.input = "input text \"{}\""
|
||||
text.new_line = "new line"
|
||||
text.motion = "text move {}"
|
||||
text.motion_select = "text select {}"
|
||||
command.text = "input command: {}"
|
||||
message.text = "input message: {}"
|
||||
libs.local = "using local pyglet, version: {}"
|
||||
libs.outer = "using global pyglet, version: {}\n(may cause bug)"
|
||||
fonts.found = "found fonts in font lib: {}"
|
||||
@ -72,9 +63,6 @@ fonts.load = "loading fonts: {}"
|
||||
game.stop_get = "Received closing commands from {}, game closing"
|
||||
game.stop = "game closing, saving data……"
|
||||
game.end = "game closed"
|
||||
command.text = "input command: {}"
|
||||
message.text = "input message: {}"
|
||||
command.mods.reload.no_mod_id = "no mod id specified"
|
||||
|
||||
[server]
|
||||
setup.start = "Server start loading"
|
||||
@ -83,6 +71,17 @@ os.pid_is = "Server PID: {} PPID: {}"
|
||||
|
||||
[game]
|
||||
input = "console"
|
||||
window = "window"
|
||||
command = "in game commands"
|
||||
window = "window"
|
||||
require_DR_rs = "require DR_rs module"
|
||||
|
||||
[client.sr1_render]
|
||||
setup.start = "SR1 Renderer start loading"
|
||||
setup.use_time = "SR1 Renderer loading has used: {} second"
|
||||
xml.loading = "Loading XML file: {}"
|
||||
xml.load_done = "XML file loaded"
|
||||
xml.load_time = "XML file loading has used: {} second"
|
||||
ship.load = "Loading ship: {}"
|
||||
ship.load_time = "Ship loading has used: {} second"
|
||||
ship.info = "Ship info:\n- Parts: {}\n- Weight: {}"
|
||||
ship.render.done = "Ship render done"
|
@ -20,26 +20,16 @@ logger.logfile_level = "日志文件记录级别:"
|
||||
logger.logfile_fmt = "日志文件记录格式:"
|
||||
logger.logfile_datefmt = "日志文件日期格式:"
|
||||
game_start.at = "游戏主线程开始于:"
|
||||
|
||||
[mod]
|
||||
list = "Mod 列表: "
|
||||
find.finded = "找到 Mod: {}"
|
||||
load.start = "开始加载路径 {} 下的 Mod"
|
||||
load.use_time = "Mod 加载消耗时间: {} 秒"
|
||||
load.done = "所有 Mod 加载完成"
|
||||
load.loading = "正在加载 Mod: {}"
|
||||
load.faild.error = "Mod 加载失败: {} 错误信息: {}"
|
||||
load.faild.not_exist = "Mod 加载失败: {} mod 路径不存在"
|
||||
load.faild.no_mod_class = "Mod 加载失败: {} 没有找到 Mod 类"
|
||||
init.success = "mod id: {} 版本号: {}"
|
||||
init.faild = "Mod 初始化失败: {} 错误信息: {}\n堆栈信息: {}"
|
||||
init.use_time = "Mod 初始化消耗时间: {} 秒"
|
||||
event.error = "Mod 事件 {} 发生错误 {} Mod: {}\n堆栈信息: {}"
|
||||
unload.faild.not_find = "Mod 卸载失败: {} 没有找到 Mod"
|
||||
unload.faild.error = "Mod 卸载失败: {} 错误信息: {}\n堆栈信息: {}"
|
||||
unload.success = "Mod 卸载成功: {}"
|
||||
reload.faild.not_find = "Mod 重载失败: {} 没有找到 Mod 原始路径,正在尝试重新查找 mod"
|
||||
reload.success = "Mod 重载成功: {}"
|
||||
mod.find.start = "正在校验 Mod: {}"
|
||||
mod.find.faild.no_spec = "importlib 无法找到 spec"
|
||||
mod.find.faild.no_mod_folder = "没有找到 Mod 文件夹"
|
||||
mod.find.done = "所有 Mod 校验完成"
|
||||
mod.load.start = "正在加载 Mod: {}"
|
||||
mod.load.info = "mod id: {} 版本号: {}"
|
||||
mod.load.faild.info = "Mod 加载失败: {} 错误信息: {}"
|
||||
mod.load.faild.no_mod_class = "没有找到 Mod 类"
|
||||
mod.load.done = "所有 Mod 加载完成"
|
||||
mod.event.error = "Mod 事件 {} 发生错误 {} Mod: {}"
|
||||
|
||||
[client]
|
||||
setup.start = "客户端加载开始"
|
||||
@ -65,6 +55,8 @@ text.input = "输入字符 \"{}\""
|
||||
text.new_line = "换行"
|
||||
text.motion = "光标移动 {}"
|
||||
text.motion_select = "光标选择 {}"
|
||||
command.text = "输入命令: {}"
|
||||
message.text = "输入信息: {}"
|
||||
libs.local = "正在使用本地 pyglet 库 版本为: {}"
|
||||
libs.outer = "正在使用全局 pyglet 库 版本为: {}\n(可能会造成bug,因为本地库版本为2.0dev9)"
|
||||
fonts.found = "在字体列表中找到以下字体库: {}"
|
||||
@ -72,9 +64,6 @@ fonts.load = "正在加载字体: {}"
|
||||
game.stop_get = "从{}传入关闭指令,关闭游戏中"
|
||||
game.stop = "游戏正在关闭,保存数据中···"
|
||||
game.end = "游戏已经关闭"
|
||||
command.text = "输入命令: {}"
|
||||
message.text = "输入信息: {}"
|
||||
command.mods.reload.no_mod_id = "没有指定 mod id"
|
||||
|
||||
[server]
|
||||
setup.start = "服务端开始加载"
|
||||
@ -83,6 +72,18 @@ os.pid_is = "服务端 PID: {} PPID: {}"
|
||||
|
||||
[game]
|
||||
input = "控制台"
|
||||
window = "窗口"
|
||||
command = "游戏内命令行"
|
||||
window = "窗口"
|
||||
require_DR_rs = "需要 DR_rs 模块"
|
||||
|
||||
[client.sr1_render]
|
||||
setup.start = "SR1 渲染器开始载入"
|
||||
setup.use_time = "SR1 渲染器载入消耗时间: {} 秒"
|
||||
xml.loading = "正在加载XML文件: {}"
|
||||
xml.load_done = "XML 文件加载完成"
|
||||
xml.load_time = "XML 文件加载消耗时间: {} 秒"
|
||||
ship.load = "正在加载飞船: {}"
|
||||
ship.load_time = "飞船加载消耗时间: {} 秒"
|
||||
#ship.info = "飞船信息:\n- 部件数量: {}\n- 部件重量: {}\n- 文件大小: {}"
|
||||
ship.info = "飞船信息:\n- 部件数量: {}\n- 部件重量: {}"
|
||||
ship.render.done = "飞船渲染完成"
|
@ -22,7 +22,7 @@ datefmt = "%Y-%m-%d %H:%M:%S"
|
||||
[handlers.console]
|
||||
class = "logging.StreamHandler"
|
||||
formatter = "file"
|
||||
level = "INFO"
|
||||
level = "DEBUG"
|
||||
|
||||
[handlers.file]
|
||||
class = "logging.FileHandler"
|
@ -3,12 +3,12 @@ fps = 60
|
||||
language = "zh-CN"
|
||||
date_fmt = "%Y-%m-%d %H-%M-%S"
|
||||
write_py_v = "3.8.10"
|
||||
fonts_folder = "assets/fonts"
|
||||
fonts_folder = "libs/fonts"
|
||||
|
||||
[window]
|
||||
style = "None"
|
||||
width = 1112
|
||||
height = 793
|
||||
width = 1021
|
||||
height = 1078
|
||||
visible = true
|
||||
gui_scale = 1
|
||||
caption = "Difficult Rocket v{DR_version}"
|
2
docs/.gitignore
vendored
2
docs/.gitignore
vendored
@ -3,5 +3,3 @@ index.html
|
||||
|
||||
book
|
||||
#theme
|
||||
|
||||
README-en.html
|
@ -9,28 +9,22 @@
|
||||
|
||||
## Notice: This repo will only publish `DR SDK` updates, `DR game` updates will be published [here](https://github.com/shenjackyuanjie/DR-game)
|
||||
|
||||

|
||||
|
||||
<a href="https://996.icu"><img src="https://img.shields.io/badge/link-996.icu-red.svg" alt="996.icu" /></a>
|
||||
[](https://Semver.org/)
|
||||
[](https://Python.org)
|
||||
[](https://pyglet.org)
|
||||
[](https://pyglet.org)
|
||||
[](https://Python.org)
|
||||
|
||||
## Version
|
||||
|
||||
[About Versions](src/version.md)
|
||||
|
||||
[](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
|
||||
[](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
|
||||
[](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
|
||||
[](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
|
||||
[](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
|
||||
[](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
|
||||
|
||||
[](https://stats.deeptrain.net/repo/shenjackyuanjie/Difficult-Rocket?theme=dark)
|
||||
|
||||
[DR sdk latest Action build](https://nightly.link/shenjackyuanjie/Difficult-Rocket/workflows/nuitka/main)
|
||||
|
||||
[DR rs latest Action build](https://nightly.link/shenjackyuanjie/Difficult-Rocket/workflows/dr_rs/main)
|
||||
|
||||
## 中文README请移步 [这里](../README.md)
|
||||
|
||||
> Difficult-rocket is a Simple Rocket liked game build with Python (in short: rocket simulator)
|
||||
@ -48,7 +42,7 @@
|
||||
## Environment (been tested / developed on)
|
||||
|
||||
- `Develop platform 1 - Windows 10 x64 22H2`
|
||||
- Python `3.8.10` / `3.10.11`
|
||||
- Python `3.8.10`
|
||||
- pillow `9.5.0`
|
||||
- psutil `5.9.5`
|
||||
- rtoml `0.9.0`
|
||||
@ -57,7 +51,7 @@
|
||||
- objprint `0.2.2`
|
||||
- viztracer `0.15.6`
|
||||
- vizplugins `0.1.3`
|
||||
- nuitka `1.6.6`
|
||||
- nuitka `1.6.1`
|
||||
- ordered-set `4.1.0`
|
||||
- imageio `2.31.0`
|
||||
- wheel `0.40.0`
|
||||
@ -68,7 +62,7 @@
|
||||
|
||||
## Required python modules
|
||||
|
||||
- `pyglet` (pre-installed V2.0.8 path:`./libs/pyglet`)
|
||||
- `pyglet` (pre-installed V2.0.5 path:`./libs/pyglet`)
|
||||
- `xmltodict` (pre-installed V0.12.0 path:`./libs/xmltodict`)
|
||||
- `pyperclip` (pre-installed V1.8.2 path: `./libs/pyperclip`)
|
||||
|
||||
@ -79,30 +73,28 @@
|
||||
# DR contributing
|
||||
|
||||
# for images
|
||||
# not for pypy >= 3.10
|
||||
pillow >= 10.0.0; (platform_python_implementation == "PyPy" and python_version < "3.10") or platform_python_implementation == "CPython"
|
||||
pillow >= 9.5.0
|
||||
|
||||
# for sys info
|
||||
psutil >= 5.9.5
|
||||
|
||||
# for files
|
||||
rtoml >= 0.9.0
|
||||
tomlkit >= 0.11.8
|
||||
tomlkit >= 0.11.7
|
||||
defusedxml >= 0.7.1
|
||||
|
||||
# for debug
|
||||
objprint >= 0.2.2
|
||||
viztracer >= 0.15.6; platform_python_implementation != "PyPy"
|
||||
vizplugins >= 0.1.3; platform_python_implementation != "PyPy"
|
||||
viztracer >= 0.15.6
|
||||
vizplugins >= 0.1.3
|
||||
|
||||
# for compile
|
||||
nuitka >= 1.7.5
|
||||
nuitka >= 1.5.6
|
||||
ordered-set >= 4.1.0
|
||||
imageio >= 2.31.0; (platform_python_implementation == "PyPy" and python_version < "3.10") or platform_python_implementation == "CPython"
|
||||
imageio >= 2.27.0
|
||||
wheel >= 0.40.0
|
||||
setuptools >= 67.8.0
|
||||
setuptools-rust >= 1.6.0
|
||||
|
||||
setuptools >= 67.6.1
|
||||
setuptools-rust >= 1.5.2
|
||||
```
|
||||
|
||||
## thanks to
|
||||
@ -112,8 +104,8 @@ setuptools-rust >= 1.6.0
|
||||
- `tomlkit` / `rtoml` toml parser
|
||||
- `xmltodict`: translate data between xml and dict
|
||||
- `pyperclip`: paste board!
|
||||
- [rapier2d](https://rapier.rs/) : Phy simulate engine
|
||||
- [pyo3](https://pyo3.rs/main) : Rust Python Binding
|
||||
- `rapier2d`: Phy simulate engine
|
||||
- `pyo3`: Rust Python Binding
|
||||
|
||||
- Main contributors
|
||||
- [@Rayawa](https://github.com/Rayawa) : Check mistake in docs & some translates
|
||||
|
@ -41,9 +41,6 @@ page-break = true # insert page-break after each chapter
|
||||
[rust]
|
||||
edition = "2021" # the default edition for code blocks
|
||||
|
||||
# 从本地运行一些东西拿到 markdown 里来
|
||||
[preprocessor.cmdrun]
|
||||
|
||||
### 扩展部分
|
||||
|
||||
# 检查文档内链接
|
||||
@ -58,10 +55,12 @@ edition = "2021" # the default edition for code blocks
|
||||
|
||||
# 添加右侧目录
|
||||
[preprocessor.theme]
|
||||
turn-off = true
|
||||
pagetoc = true
|
||||
pagetoc-width = "13%"
|
||||
sidebar-width = "300px"
|
||||
|
||||
# 从本地运行一些东西拿到 markdown 里来
|
||||
[preprocessor.cmdrun]
|
||||
|
||||
# 在新页面中打开链接
|
||||
[preprocessor.external-links]
|
||||
|
@ -25,7 +25,6 @@
|
||||
- [部件可成环](./plan_features/multi-connect.md)
|
||||
- [多指令舱控制](./plan_features/multi-control.md)
|
||||
- [轨道规划](./plan_features/orbit-plan.md)
|
||||
- [自定义部件收集](./plan_features/custom-part.md)
|
||||
|
||||
- [开发文档](./howto/readme.md)
|
||||
- [client](./howto/client.md)
|
||||
|
@ -2,38 +2,7 @@
|
||||
# DR 构建 更新日志
|
||||
|
||||
- 最新构建版本号
|
||||
- build_version: 2.1.3.0
|
||||
|
||||
## 20230715 build 2.1.3.0
|
||||
|
||||
### 修改
|
||||
|
||||
- 添加 `email` `win32con` `smtplib` `win32evtlog` `win32evtlogutil` `win32api` 到 `--no-follow-import`
|
||||
- Add `email`, `win32con`, `smtplib`, `win32evtlog`, `win32evtlogutil`, `win32api` to `--no-follow-import`
|
||||
- `include_data_dir` 移除 `libs/fonts` `textures`
|
||||
- 改为 `assets` 和 `config`
|
||||
|
||||
## 20230708 build 2.1.2.0
|
||||
|
||||
### 修改
|
||||
|
||||
- 同步了 `lib-not-dr` 的修改
|
||||
- `NuitkaCompiler`
|
||||
- 添加了 `run_after_build` 选项
|
||||
- `--run`
|
||||
- 添加了 `compat_nuitka_version` 选项
|
||||
- 目前是 `1.7.1`
|
||||
|
||||
## 20230630 build 2.1.1.0
|
||||
|
||||
### 修改
|
||||
|
||||
- 现在本地构建会根据系统名称修改输出目录
|
||||
- 例如
|
||||
- 在 `Windows` 上构建会输出到 `build\nuitka-windows`
|
||||
- Now the local build will modify the output directory according to the system name
|
||||
- For example
|
||||
- Build on `Windows` will output to `build\nuitka-windows`
|
||||
- build_version: 2.1.0.0
|
||||
|
||||
## 20230611 build 2.1.0.0
|
||||
|
||||
@ -44,20 +13,13 @@
|
||||
- 在 `Windows` 上 `--no-follow-import` `pyglet.libs.x11`
|
||||
- 大幅提升编译效率
|
||||
- 缩短编译时间
|
||||
- Now `pyglet` compatibility modules for other platforms will be automatically excluded on each platform
|
||||
- For example
|
||||
- `--no-follow-import` `pyglet.libs.x11` on `Windows`
|
||||
- Greatly improve compilation efficiency
|
||||
- Shorten compilation time
|
||||
|
||||
## 20230609 build 2.0.0.0
|
||||
|
||||
### Breaking Change
|
||||
|
||||
- 将构建脚本生成方式改为通过 `libs/utils/nuitka.py` 生成
|
||||
- Change the way the build script is generated to generate it through `libs/utils/nuitka.py`
|
||||
- GitHub Actions 也使用相同脚本构建
|
||||
- GitHub Actions also uses the same script to build
|
||||
|
||||
## 202306 build 1.3.0.0
|
||||
|
||||
|
@ -2,251 +2,8 @@
|
||||
# DR game/DR rs 更新日志
|
||||
|
||||
- 最新版本号
|
||||
- DR game: 0.3.3.0
|
||||
- DR rs: 0.2.21.0
|
||||
|
||||
## 20230812 DR game 0.3.3.0
|
||||
|
||||
### Changes
|
||||
|
||||
- 将 `sr1_ship` 渲染器使用的 Camera 改成 `CenterGroupCamera`
|
||||
- 删除了之前的 Camera 相关代码
|
||||
- 将用于渲染到材质的代码部分改为使用 `glScissor` 和 `glViewport`
|
||||
- 优化了一点性能 ( 毕竟是OpenGL )
|
||||
|
||||
## 20230809 DR game 0.3.2.1
|
||||
|
||||
### Fix
|
||||
|
||||
- 因为把部件加载图片的数据源改成从 `SR1PartType_rs` 里取
|
||||
- 所以修改了 `SR1Textures` 的加载逻辑
|
||||
- 可以自动忽略文件名最后的 `.png`
|
||||
|
||||
## 20230808 DR rs 0.2.21.0
|
||||
|
||||
### Add
|
||||
|
||||
- 在 `__init__.py` 里添加了
|
||||
- `sprite`
|
||||
- `type`
|
||||
- 的导出 (实际上就是 typing)
|
||||
- `SR1PartType_rs`
|
||||
- 添加了 `type` getter
|
||||
|
||||
摸鱼真开心
|
||||
|
||||
## 20230724 DR rs 0.2.20.2
|
||||
|
||||
### Fix
|
||||
|
||||
- [#49](https://github.com/shenjackyuanjie/Difficult-Rocket/issues/49)
|
||||
- missing field `touchingGround`
|
||||
- SR1 says: `touchingGround` field is NOT Required
|
||||
- make them happy
|
||||
- SR1 说: `touchingGround` 字段也是可选的
|
||||
- 让他们开心
|
||||
- 我就看看我能发多少个 issue
|
||||
|
||||
## 20230724 DR rs 0.2.20.1
|
||||
|
||||
### Fix
|
||||
|
||||
- [#48](https://github.com/shenjackyuanjie/Difficult-Rocket/issues/48)
|
||||
- `missing field version`
|
||||
- SR1 says: `version` field is NOT Required
|
||||
- make them happy
|
||||
- SR1 说: `version` 字段也是可选的
|
||||
- 让他们开心
|
||||
- 我谢谢您啊 Jundroo
|
||||
- 我就看看我能发多少个 issue
|
||||
|
||||
## 20230724 DR rs 0.2.20.0
|
||||
|
||||
### Fix
|
||||
|
||||
- [#47](https://github.com/shenjackyuanjie/Difficult-Rocket/issues/47)
|
||||
- `editorAngle field is Option<i32>`
|
||||
- SR1 says: `editorAngle` field is Optional
|
||||
- make them happy
|
||||
- SR1 说: `editorAngle` 字段是可选的
|
||||
- 让他们开心
|
||||
|
||||
## 20230721 DR rs 0.2.19.0
|
||||
|
||||
### Add
|
||||
|
||||
- `PySR1Ship`
|
||||
- `as_list`
|
||||
|
||||
## 20230721 DR game 0.3.2.0
|
||||
|
||||
### BUG Fix
|
||||
|
||||
- [#46](https://github.com/shenjackyuanjie/Difficult-Rocket/issues/46)
|
||||
- 渲染偏移 bug
|
||||
|
||||
## 20230721 DR rs 0.2.18.0
|
||||
|
||||
### Add
|
||||
|
||||
- 导出了 export
|
||||
- `PySR1PartData`
|
||||
- `PySaveStatus`
|
||||
- `map_ptype_textures(part_type: str) -> str`
|
||||
|
||||
## 20230721 DR rs 0.2.17.0
|
||||
|
||||
### BreakingChanges
|
||||
|
||||
- `SR1Ship_rs`
|
||||
- `__init__`
|
||||
- 将 `part_list` 参数从传入 `PartList.xml` 路径 改为直接传入 `SR1PartList_rs` 实例
|
||||
- Change the `part_list` parameter from passing in the `PartList.xml` path to directly passing in the `SR1PartList_rs` instance
|
||||
|
||||
## 20230721 DR rs 0.2.16.0
|
||||
|
||||
### Dependencies
|
||||
|
||||
in [#45](https://github.com/shenjackyuanjie/Difficult-Rocket/pull/45)
|
||||
|
||||
- `pyo3`
|
||||
- `0.19.0` -> `0.19.1`
|
||||
- `xml-rs`
|
||||
- `0.8.14` -> `0.8.16`
|
||||
- `serde`
|
||||
- `1.0.164` -> `1.0.173`
|
||||
|
||||
## DR game 0.3.1.2 / 0.3.1.3
|
||||
|
||||
- 加回了显示 delta 的那根线
|
||||
- Add back the line that displays delta
|
||||
|
||||
## DR rs 0.2.15.2
|
||||
|
||||
### Add
|
||||
|
||||
- `SR1PartData_rs`
|
||||
- `get_id -> IdType`
|
||||
- `get_x -> f64`
|
||||
- `get_y -> f64`
|
||||
- `get_activate -> bool`
|
||||
- `get_angle_v -> f64`
|
||||
- `get_explode -> bool`
|
||||
|
||||
## DR rs 0.2.15.1
|
||||
|
||||
### Changes
|
||||
|
||||
- `types.rs` & `python.rs` 利用可用的 Clippy 改进了代码
|
||||
- `types.rs` & `python.rs` improved the code with available Clippy
|
||||
|
||||
## DR game 0.3.1.1
|
||||
|
||||
### Fix
|
||||
|
||||
- `sr_tr` 加载语言文件的路径并没有跟随目录名称改变
|
||||
- `sr_tr` does not follow the directory name change when loading the language file path
|
||||
|
||||
## DR game 0.3.1.0
|
||||
|
||||
- 使用 `Difficult_Rocket.api.camera.Camera` 类
|
||||
- Use `Difficult_Rocket.api.camera.Camera` class
|
||||
|
||||
## DR game 0.3.0.0
|
||||
|
||||
有一些修改(忘记记录了
|
||||
|
||||
## DR rs 0.3.0.0
|
||||
|
||||
### 修改
|
||||
|
||||
- 适配了 `DR sdk` 的关于 mods 的修改
|
||||
- Adapted the modification of mods about `DR sdk`
|
||||
- `RustConsole`
|
||||
- 现在输入体验更好了
|
||||
- Now the input experience is better
|
||||
|
||||
## DR rs 0.2.15.0
|
||||
|
||||
### 修改
|
||||
|
||||
- 现在支持新的 mods reload 和 unload 了
|
||||
- 适配了 `DR sdk` 的关于 mods 的修改
|
||||
- Now support new mods reload and unload
|
||||
- Adapted the modification of mods about `DR sdk`
|
||||
|
||||
### 添加
|
||||
|
||||
- `IdType = i64`
|
||||
- 统一的 id 类型
|
||||
- Unified id type
|
||||
- `PySR1Ship`
|
||||
- `get_connection -> Vec<(i32, i32, IdType, IdType)>`
|
||||
- 获取飞船的连接信息
|
||||
- Get the connection information of the ship
|
||||
|
||||
## DR rs 0.2.14.0
|
||||
|
||||
### 删除
|
||||
|
||||
- 删除了多个 xml 测试读取函数
|
||||
- Remove multiple xml test read functions
|
||||
|
||||
### 改进
|
||||
|
||||
- 改进了 xml writer 的 `SR1Ship` 写入
|
||||
- Improve the xml writer's `SR1Ship` write
|
||||
|
||||
## DR game 0.2.1.0
|
||||
|
||||
### 修改
|
||||
|
||||
- 将 `sr1_ship` 中的 `Camera_rs` 改为 `Difficult_Rocket.utils.camera.Camera`
|
||||
- Change `Camera_rs` in `sr1_ship` to `Difficult_Rocket.utils.camera.Camera`
|
||||
- 添加了部件的连接线(都是彩色哒)
|
||||
- Add the connection line of the part (all are colored)
|
||||
|
||||
## DR rs 0.2.13.0
|
||||
|
||||
### 删除
|
||||
|
||||
- 删除了 `render.rs`
|
||||
- 没必要拿 rust 写这玩意(
|
||||
- 用 `DR game` 的 `camera` 代替
|
||||
- Delete `render.rs`
|
||||
- No need to write this thing with rust (
|
||||
- Use `DR game`'s `camera` instead
|
||||
|
||||
### 添加
|
||||
|
||||
- 添加了 基于 `quick-xml::writer::Writer` 的 xml `SR1Ship` 写入
|
||||
- 折磨啊啊啊啊啊啊啊啊
|
||||
- Add xml `SR1Ship` write based on `quick-xml::writer::Writer`
|
||||
- Torture ah ah ah ah ah ah ah ah
|
||||
|
||||
## DR rs 0.2.12.0
|
||||
|
||||
### 添加
|
||||
|
||||
- 添加了 xml 的读取测试
|
||||
- Add xml read test
|
||||
|
||||
## DR game 0.1.2.0
|
||||
|
||||
### 修改
|
||||
|
||||
- 现在渲染飞船的时候不会再显示那个白色框了
|
||||
- 改为一个彩色的框
|
||||
- Now, the ship will not be displayed in the white box
|
||||
- Change to a colored box
|
||||
|
||||
## DR rs 0.2.11.0
|
||||
|
||||
### 添加
|
||||
|
||||
- `Python::data::PySR1Ship`
|
||||
- `get_part_box(&self, part_id: i64) -> Option<(f64, f64), (f64, f64)>`
|
||||
- 用于获取对应 id 的实际碰撞箱
|
||||
- DR game: 0.2.0.0
|
||||
- DR rs: 0.2.10.1
|
||||
|
||||
## DR game 0.2.0.0
|
||||
|
||||
|
@ -2,197 +2,10 @@
|
||||
# DR SDK 更新日志
|
||||
|
||||
- 最新版本号
|
||||
- DR sdk: 0.8.7.0
|
||||
- DR api: 0.1.1.0
|
||||
|
||||
## DR sdk 0.8.7.0
|
||||
|
||||
### Add
|
||||
|
||||
- 添加了 `Difficult_Rocket.utils.camera.GroupCamera`
|
||||
- 和 `Difficult_Rocket.utils.camera.CenterGroupCamera`
|
||||
- 实际上就是使用 `pyglet.graphics.Group` 来实现的 `Camera`
|
||||
- 具有相同的功能
|
||||
- 顺便同样在 `api.camera` 里添加了导出
|
||||
- 这次我一定不会再忘记导出了
|
||||
- Added `Difficult_Rocket.utils.camera.GroupCamera`
|
||||
- And `Difficult_Rocket.utils.camera.CenterGroupCamera`
|
||||
- Actually, it is implemented `Camera` using `pyglet.graphics.Group`
|
||||
- Has the same function
|
||||
- By the way, the export was also added in `api.camera`
|
||||
- This time I will never forget to export it again
|
||||
- 为所有 `xxCamera` 添加了
|
||||
- `reset` 方法
|
||||
- 用于一键重置缩放+平移
|
||||
- Added `reset` method for all `xxCamera`
|
||||
- Used to reset zoom + translation with one click
|
||||
|
||||
## DR sdk 0.8.6.1
|
||||
|
||||
### Fix
|
||||
|
||||
- 现在 `DR.py` 启动之后如果崩溃会在控制台输出完整的 Crash report 了
|
||||
- Now, after `DR.py` starts, if it crashes, the complete Crash report will be output to the console
|
||||
|
||||
## DR sdk 0.8.6.0
|
||||
|
||||
重构了一下项目结构
|
||||
|
||||
Refactored the project structure
|
||||
|
||||
### Fix
|
||||
|
||||
- issue [#42](https://github.com/shenjackyuanjie/Difficult-Rocket/issues/42)
|
||||
- Crash report 的时区不正确
|
||||
- Crash report time zone is incorrect
|
||||
|
||||
### Change
|
||||
|
||||
- 将大部分资源文件移动到 `assets/`
|
||||
- `libs/fonts` -> `assets/fonts`
|
||||
- `configs/lang` -> `assets/lang`
|
||||
- `configs/xxx.xml` -> `assets/builtin/xxx.xml`
|
||||
- `textures` -> `assets/textures`
|
||||
- Move all the resources to `assets/`
|
||||
|
||||
## DR sdk 0.8.5.2
|
||||
|
||||
### Fix
|
||||
|
||||
- `crash` 引用了已经重命名的 `Difficult_Rocket.game_version` (`Difficult_Rocket.sdk_version`)
|
||||
- `Difficult_Rocket.api.types`
|
||||
- `VersionRequirement`
|
||||
- `VersionParsingError`
|
||||
|
||||
### Change
|
||||
|
||||
- 修改了 `DR.py`
|
||||
- 实际上是 `DR-start.py` 的改名
|
||||
- Changed `DR.py`
|
||||
- Actually renamed `DR-start.py`
|
||||
|
||||
### Clean
|
||||
|
||||
- 删除了 `DR-start.py`
|
||||
- 实际上是改成了 `DR.py`
|
||||
- Removed `DR-start.py`
|
||||
- Actually changed to `DR.py`
|
||||
- 删除了 `libs/utils/dsm.py`
|
||||
- Deleted `libs/utils/dsm.py`
|
||||
|
||||
## DR sdk 0.8.5.1
|
||||
|
||||
### API
|
||||
|
||||
- 将 `Camera` 和 `CenterCamera` 添加到 `Difficult_Rocket.api.camera`
|
||||
- Add `Camera` and `CenterCamera` to `Difficult_Rocket.api.camera`
|
||||
|
||||
## DR sdk 0.8.5.0
|
||||
|
||||
### Changes
|
||||
|
||||
- `Difficult_Rocket.__init__`
|
||||
- 重命名 / Rename
|
||||
- `game_version` -> `sdk_version`
|
||||
- `ModManager`
|
||||
- `get_mod_module(mod_name: str) -> Optional[ModInfo]`
|
||||
- 通过 mod 名称获取 mod 的信息
|
||||
- Get mod information by mod name
|
||||
- `load_mod(mod_path: Path) -> Optional[ModInfo]`
|
||||
- 加载指定路径的 mod
|
||||
- Load the mod at the specified path
|
||||
- `find_mods_in_path(extra_mods_path: Optional[List[Path]]) -> List[Path]`
|
||||
- 在指定的路径中查找 mod 并返回 mod 的路径
|
||||
- Find mods in the specified path and return the path of the mod
|
||||
- `unload_mod(mod_id: str, game: Game) -> Optional[ModInfo]`
|
||||
- 卸载指定的 mod 并返回 mod 的信息
|
||||
- Unload the specified mod and return the mod information
|
||||
- `reload_mod(mod_id: str, game: Game)`
|
||||
- 重新加载指定的 mod
|
||||
- Reload the specified mod
|
||||
|
||||
### Remove
|
||||
|
||||
- `DR.client.guis.widget.InputBox`
|
||||
- 没用了 `TextEntry` 已经好了 可以直接用了
|
||||
- Use Less, the `TextEntry` is ok to use
|
||||
|
||||
### Command
|
||||
|
||||
- `mods`
|
||||
- `list`
|
||||
- 列出所有已加载的 mod
|
||||
- List all loaded mods
|
||||
- `reload <mod_id>`
|
||||
- 重新加载指定的 mod
|
||||
- Reload the specified mod
|
||||
|
||||
## DR sdk 0.8.4.1
|
||||
|
||||
### Add
|
||||
|
||||
- `utils.camera`
|
||||
- `Camera`
|
||||
- 一个 2D 摄影机,可以用于高效变换渲染坐标
|
||||
- `CenterCamera`
|
||||
- 一个中心对器的 2D 摄影机,可以用于高效变换渲染坐标
|
||||
|
||||
## DR sdk 0.8.4.0
|
||||
|
||||
### Fix
|
||||
|
||||
- issue #33 (<https://github.com/shenjackyuanjie/Difficult-Rocket/issues/33>)
|
||||
- 修复了实际上并不会加载 `.otf` 格式的字体文件的问题
|
||||
|
||||
### language
|
||||
|
||||
- 去除了 `dr_game` 相关的键值
|
||||
- 现在这些键值已经在 `dr_game` 自己的语言文件中了
|
||||
- Removed the key values related to `dr_game`
|
||||
- Now these key values are in the language file of `dr_game` itself
|
||||
- 将 mod 加载部分的键值独立出来
|
||||
- 这样调用起来更方便一些
|
||||
- Separate the key values of the mod loading part
|
||||
- This makes it easier to call
|
||||
|
||||
### 修改
|
||||
|
||||
- `utils.translate`
|
||||
- `Tr`
|
||||
- 现在支持自定义语言文件的路径了
|
||||
- `lang_path`
|
||||
- 用于给 mod 加载自己的语言文件
|
||||
- 现在 `logging` 的配置转移到 `__init__` 中了
|
||||
- 保证调用 `logging` 的时候可以正常使用
|
||||
- Now the configuration of `logging` is moved to `__init__`
|
||||
- Ensure that `logging` can be used normally when called
|
||||
- 现在输入命令之后不会输出一个 `True`/`False` 了
|
||||
- (实际上是用来检测命令是不是用 `/` 开头的)
|
||||
- Now, the command will not output a `True`/`False`
|
||||
- (Actually used to detect whether the command starts with `/`)
|
||||
- `CommandText`
|
||||
- `find`
|
||||
- 现在会先用 `str.startswith` 检测是否以要求的字符串开头
|
||||
- 实际上就是丐版 `re.match`
|
||||
- 并且会在匹配上之后 如果匹配内容后面第一个字符是空格 则会截取掉空格
|
||||
- Now, it will first use `str.startswith` to detect whether it starts with the required string
|
||||
- Actually a poor version of `re.match`
|
||||
- And after matching, if the first character after the matching content is a space, the space will be intercepted
|
||||
- DR sdk: 0.8.2.0
|
||||
|
||||
## DR sdk 0.8.3.0
|
||||
|
||||
### 删除
|
||||
|
||||
- `pyglet_rs`
|
||||
- 事实证明这玩意没啥用
|
||||
- Removed pyglet_rs
|
||||
|
||||
### Fix
|
||||
|
||||
- issue #31 (https://github.com/shenjackyuanjie/Difficult-Rocket/issues/31)
|
||||
- 窗口标题不正确 (实际上是因为只使用 `DR_runtime` 进行格式化)
|
||||
- Window title is incorrect
|
||||
|
||||
### 添加
|
||||
|
||||
- `DR_status`
|
||||
@ -207,6 +20,13 @@ Refactored the project structure
|
||||
- Mod loader
|
||||
- 添加了对支持版本号的 warnings
|
||||
- Added warnings for supporting version numbers
|
||||
- API
|
||||
- `Difficult_Rocket.api.log`
|
||||
- `get_named_client_logger`
|
||||
- `get_named_server_logger`
|
||||
- `get_named_main_logger`
|
||||
- 分别用于获取 基于 对应名称配置的 logger
|
||||
- Get the logger for the corresponding name configuration
|
||||
|
||||
### 移动
|
||||
|
||||
|
@ -18,5 +18,3 @@
|
||||
- `python -m pip install -r requre`
|
||||
3. 使用 `build_rs.ps1` 尝试编译一次 `DR_rs`
|
||||
- `./build_rs.ps1` 38/39/310/311
|
||||
|
||||
<!-- cmdrun pwsh test.ps1 -->
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 408 KiB |
@ -23,7 +23,6 @@
|
||||
| 部件平移 | part-move | planing |
|
||||
| 存档额外信息 | extra-save-info | planing |
|
||||
| 自定义部件分组开关 | custom-stage-on-off | planing |
|
||||
| 各种奇怪部件 | custom-part | planing |
|
||||
|
||||
# 文件范例
|
||||
|
||||
|
@ -1,33 +0,0 @@
|
||||
# 各种自定义部件 custom-part.md `custom part`
|
||||
|
||||
## 2023 07 15
|
||||
|
||||
## shenjack 和 qq 群的各位
|
||||
|
||||
- 北风百草
|
||||
- 小阳阳
|
||||
|
||||
## 状态
|
||||
|
||||
- 计划中
|
||||
|
||||
## 描述
|
||||
|
||||
- 各种奇怪自定义部件的收集
|
||||
|
||||
## 列表
|
||||
|
||||
### `核推`
|
||||
|
||||
- 消耗工质
|
||||
- 产生大量热量
|
||||
- 需要散热
|
||||
|
||||
### `装饰性外壳`
|
||||
|
||||
- 没有碰撞箱
|
||||
- 或者最外层有碰撞箱
|
||||
|
||||
### `部件 Pro`
|
||||
|
||||
- 超级大
|
@ -19,5 +19,5 @@ pyglet 坐标轴原点是左上角 0, 0
|
||||
我check一下微调器的代码,原理应该一样,有个函数我看看怎么实现
|
||||
# 素材提供
|
||||
|
||||
[背景候选1号](../../assets/textures/runtime/background.png)
|
||||
[背景候选1号](../../textures/runtime/background.png)
|
||||
来自 @底层萌新 QQ:1744251171
|
||||
|
@ -1,8 +0,0 @@
|
||||
|
||||
# ls 之后将每一行输出包裹在 ` 里面
|
||||
echo "<pre>"
|
||||
Get-ChildItem . | ForEach-Object {
|
||||
echo $_
|
||||
}
|
||||
|
||||
echo "</pre>"
|
25
docs/theme/index.hbs
vendored
25
docs/theme/index.hbs
vendored
@ -110,34 +110,12 @@
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
|
||||
<!-- Track and set sidebar scroll position -->
|
||||
<script>
|
||||
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
|
||||
sidebarScrollbox.addEventListener('click', function(e) {
|
||||
if (e.target.tagName === 'A') {
|
||||
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
|
||||
}
|
||||
}, { passive: true });
|
||||
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
|
||||
sessionStorage.removeItem('sidebar-scroll');
|
||||
if (sidebarScrollTop) {
|
||||
// preserve sidebar scroll position when navigating via links within sidebar
|
||||
sidebarScrollbox.scrollTop = sidebarScrollTop;
|
||||
} else {
|
||||
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
|
||||
var activeSection = document.querySelector('#sidebar .active');
|
||||
if (activeSection) {
|
||||
activeSection.scrollIntoView({ block: 'center' });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
{{> header}}
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div id="menu-bar" class="menu-bar sticky bordered">
|
||||
<div class="left-buttons">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
@ -205,6 +183,7 @@
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<!-- Page table of contents -->
|
||||
<div class="sidetoc"><nav class="pagetoc"></nav></div>
|
||||
|
||||
{{{ content }}}
|
||||
|
@ -7,11 +7,11 @@ Thanks a lot to Fallen_Breath and MCDR contributors
|
||||
GNU Lesser General Public License v3.0 (GNU LGPL v3)
|
||||
"""
|
||||
|
||||
import re
|
||||
from typing import List, Callable, Tuple, Optional, Union
|
||||
"""
|
||||
Plugin Version
|
||||
"""
|
||||
import re
|
||||
from typing import List, Callable, Tuple, Optional, Union
|
||||
|
||||
|
||||
# beta.3 -> (beta, 3), random -> (random, None)
|
||||
@ -217,4 +217,4 @@ class VersionRequirement:
|
||||
|
||||
|
||||
class VersionParsingError(ValueError):
|
||||
pass
|
||||
pass
|
@ -9,7 +9,7 @@ import sys
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
#: The release version
|
||||
version = '2.0.9'
|
||||
version = '2.0.7'
|
||||
__version__ = version
|
||||
|
||||
MIN_PYTHON_VERSION = 3, 8
|
||||
@ -336,7 +336,6 @@ if TYPE_CHECKING:
|
||||
from . import app
|
||||
from . import canvas
|
||||
from . import clock
|
||||
from . import customtypes
|
||||
from . import event
|
||||
from . import font
|
||||
from . import gl
|
||||
@ -357,7 +356,6 @@ else:
|
||||
app = _ModuleProxy('app')
|
||||
canvas = _ModuleProxy('canvas')
|
||||
clock = _ModuleProxy('clock')
|
||||
customtypes = _ModuleProxy('customtypes')
|
||||
event = _ModuleProxy('event')
|
||||
font = _ModuleProxy('font')
|
||||
gl = _ModuleProxy('gl')
|
||||
|
@ -122,24 +122,12 @@ class EventLoop(event.EventDispatcher):
|
||||
def run(self, interval=1/60):
|
||||
"""Begin processing events, scheduled functions and window updates.
|
||||
|
||||
:Parameters:
|
||||
`interval` : float or None [default: 1/60]
|
||||
Windows redraw interval, in seconds (framerate).
|
||||
If `interval == 0`, windows will redraw at maximum rate.
|
||||
If `interval is None`, Pyglet will not call its redraw function.
|
||||
The user must schedule (or call on demand) a custom redraw
|
||||
function for each window, allowing a custom framerate per window.
|
||||
(see example in documentation)
|
||||
|
||||
This method returns when :py:attr:`has_exit` is set to True.
|
||||
|
||||
Developers are discouraged from overriding this method, as the
|
||||
implementation is platform-specific.
|
||||
"""
|
||||
if interval is None:
|
||||
# User application will manage a custom _redraw_windows() method
|
||||
pass
|
||||
elif interval == 0:
|
||||
if not interval:
|
||||
self.clock.schedule(self._redraw_windows)
|
||||
else:
|
||||
self.clock.schedule_interval(self._redraw_windows, interval)
|
||||
|
@ -78,12 +78,6 @@ class CocoaScreen(Screen):
|
||||
self.height = mode.height
|
||||
|
||||
def restore_mode(self):
|
||||
match_attrs = ['width', 'height', 'depth', 'rate']
|
||||
current_mode = self.get_mode()
|
||||
if all(getattr(current_mode, attr) == getattr(self._default_mode, attr) for
|
||||
attr in match_attrs):
|
||||
# Already in default mode
|
||||
return
|
||||
quartz.CGDisplaySetDisplayMode(self._cg_display_id, self._default_mode.cgmode, None)
|
||||
quartz.CGDisplayRelease(self._cg_display_id)
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user