Compare commits
205 Commits
44b183db0e
...
5cb642ea3c
Author | SHA1 | Date | |
---|---|---|---|
5cb642ea3c | |||
2e9e3afc39 | |||
2e509575cd | |||
33d5788826 | |||
6dbffb322d | |||
bff9649ca9 | |||
5f335fa9b5 | |||
4b639c37e0 | |||
cc5dec9122 | |||
05ac9871da | |||
dd5dca1099 | |||
804dbfc992 | |||
8e494faa63 | |||
42eef6743d | |||
6fefb4460b | |||
8dfa5b230a | |||
726ba4ac17 | |||
ede6f9ff6e | |||
d6f0caf2ae | |||
d398ed1af9 | |||
e888ddfdad | |||
82111be30b | |||
39379c1621 | |||
3595f98dc1 | |||
aa8af9ebef | |||
692483320b | |||
09e386e0fe | |||
55e83b708f | |||
7278368b4c | |||
a2f12c2e77 | |||
47d2629610 | |||
e658fa3188 | |||
dd7038272c | |||
f1172c30be | |||
6c68da7edf | |||
951784cf64 | |||
6887660ee1 | |||
3df29bee64 | |||
b70705fb67 | |||
ce0fe298e6 | |||
0c5647fee2 | |||
253e3b62fb | |||
640cc27e34 | |||
c14786e216 | |||
364924a7b7 | |||
893872c012 | |||
2ad60e398c | |||
24f17f6d4f | |||
e997633cc5 | |||
7fed4032e0 | |||
ad83def45e | |||
cc5a07904d | |||
3629bcf163 | |||
5cf40fcede | |||
81b770e5fc | |||
ae9700e96d | |||
204115d7c7 | |||
cd0d9071e2 | |||
7a1b10becc | |||
f10d09ddba | |||
ef73925b0a | |||
5406e7f5c7 | |||
505d68c67b | |||
d22a5389e7 | |||
42dfd60ce9 | |||
95644b1f3a | |||
e411f25e8c | |||
6865578237 | |||
b02f0f3852 | |||
b2af672f96 | |||
17180e29b3 | |||
f5a5eb1758 | |||
12a69f9076 | |||
2da10160fe | |||
366e02d992 | |||
5bdf086190 | |||
eb12243e55 | |||
8e9589e378 | |||
04ab2e3965 | |||
f3c2540dae | |||
2155ad7045 | |||
d57da4c7ff | |||
ea6bc528b1 | |||
df52f87bba | |||
a9b6422e89 | |||
112f83b79b | |||
d9ef5d4070 | |||
1883d5d2df | |||
50461e3a46 | |||
259440290a | |||
728ae0c41c | |||
7a8db98787 | |||
98106ccd08 | |||
10b89a1919 | |||
226f8aacbe | |||
9d367b2256 | |||
70f2a30b88 | |||
5e6fee7353 | |||
d844e0ce0a | |||
565d568e44 | |||
|
7792abc243 | ||
|
b3385d1840 | ||
75d5494b6f | |||
cd04d9b296 | |||
|
86d298109e | ||
ca8f5a984a | |||
83b45f85fb | |||
ddf10cccf1 | |||
720b48d669 | |||
539afd73b9 | |||
086bd035e7 | |||
a46b46ead6 | |||
f87ae43a10 | |||
bb83fbd117 | |||
9b72f437a5 | |||
9107621fb8 | |||
61b45fac7f | |||
5958a14d47 | |||
c1bdda5f03 | |||
11c2e79141 | |||
c572fbd72a | |||
dcd727e690 | |||
8ee315beb7 | |||
26d22f9e82 | |||
f112b654c4 | |||
a190fd0b31 | |||
e0d8c0b0d7 | |||
6f84e24fbb | |||
6ad0d78b5a | |||
0baf74492a | |||
1e342ce0a8 | |||
8d3e14fc39 | |||
3e54946eeb | |||
8c91198e87 | |||
94b8869506 | |||
55d21b6b02 | |||
c4a1807033 | |||
bad02add7c | |||
11e52083a6 | |||
c20f4cd85f | |||
0a264c603d | |||
5c299f4571 | |||
6efdaaa5bc | |||
c447374629 | |||
482e607d0f | |||
c51b86ba9b | |||
45cfa76d24 | |||
67a9dddb30 | |||
bbdc5d98ac | |||
85c2e4b21c | |||
43966ec573 | |||
f1abeb430e | |||
1b29360701 | |||
c55eaffa5d | |||
8b490aac17 | |||
ab8ff84a5e | |||
150e2a03ce | |||
b8170f5d63 | |||
9a18346763 | |||
b2ac6fdda5 | |||
5f3642fd5c | |||
c735b9b336 | |||
996fb72b54 | |||
226fcbfce8 | |||
420e643e1f | |||
97e0a569bb | |||
c3e40a9566 | |||
09f97c8066 | |||
53fbb9c98c | |||
77e143bf77 | |||
29dc40b81d | |||
0fd7bcf0f5 | |||
3784a32184 | |||
d483326263 | |||
192e679c6d | |||
d41570b583 | |||
1fbab4b33b | |||
30d30e56c5 | |||
22995a01af | |||
cf11afe18c | |||
f31a693eae | |||
0673ba6981 | |||
44c07a64d9 | |||
5183dbfb25 | |||
b1fc9495fe | |||
14206ecf14 | |||
c9c3ff4561 | |||
fbb7dc7ae9 | |||
9bedadea15 | |||
bb14fa4634 | |||
9cd2756b30 | |||
6d1b3925e4 | |||
76e58676de | |||
5b50c9a9e2 | |||
36e0895e86 | |||
316d3d5f15 | |||
833ac81c08 | |||
05c39fefd2 | |||
c79d98f89e | |||
45531c431b | |||
021ad2d5de | |||
893912fae3 | |||
645e7ae80d | |||
4128823b3f | |||
0d5c016dad |
4
.github/workflows/dr_rs.yml
vendored
@ -81,6 +81,8 @@ jobs:
|
||||
python setup.py build
|
||||
python post_build.py
|
||||
python setup.py clean
|
||||
cd ..
|
||||
Remove-Item -Recurse -Force src
|
||||
|
||||
# Uploads artifact
|
||||
- name: Upload Artifact
|
||||
@ -88,4 +90,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/Difficult_Rocket_rs/lib
|
||||
mods/dr_game
|
||||
|
3
.github/workflows/dsm.py
vendored
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
@ -109,7 +108,7 @@ def main():
|
||||
return 0
|
||||
dsm.clear_dsm()
|
||||
dsm.upload_docs('docs/md5.txt')
|
||||
dsm.fl.session.logout('FileStation')
|
||||
dsm.fl.logout()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
22
.github/workflows/get_info.py
vendored
@ -10,32 +10,24 @@ import rtoml
|
||||
|
||||
sys.path.append(os.path.abspath(os.curdir))
|
||||
|
||||
from Difficult_Rocket import DR_runtime
|
||||
from Difficult_Rocket import DR_status
|
||||
|
||||
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('./configs/main.toml')
|
||||
config_file = tools.load_file('./config/main.toml')
|
||||
config_file['window']['width'] = 1024
|
||||
config_file['window']['height'] = 768
|
||||
rtoml.dump(config_file, open('./configs/main.toml', 'w'))
|
||||
rtoml.dump(config_file, open('./config/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_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)
|
||||
|
||||
print(f'$env:DR_version = "{DR_status.DR_version}"', file=env_file)
|
||||
print(f'$env:Build_version = "{DR_status.Build_version}"', file=env_file)
|
||||
elif os.path.abspath(os.curdir) in sys.path and '-github' in sys.argv:
|
||||
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}')
|
||||
print(f'DR_version={DR_status.DR_version}')
|
||||
print(f'Build_version={DR_status.Build_version}')
|
||||
|
2
.github/workflows/nuitka.yml
vendored
@ -7,7 +7,6 @@ on:
|
||||
push:
|
||||
paths:
|
||||
- "Difficult_Rocket/**" # 本体修改
|
||||
- "configs/**" # 配置修改
|
||||
- "libs/pyglet/**" # pyglet 修改
|
||||
- ".github/workflows/**" # workflow 修改
|
||||
- "nuitka_build.py" # 构建脚本修改
|
||||
@ -15,7 +14,6 @@ on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "Difficult_Rocket/**" # 本体修改
|
||||
- "configs/**" # 配置修改
|
||||
- "libs/pyglet/**" # pyglet 修改
|
||||
- ".github/workflows/**" # workflow 修改
|
||||
- "nuitka_build.py" # 构建脚本修改
|
||||
|
12
.github/workflows/page.yml
vendored
@ -49,12 +49,12 @@ jobs:
|
||||
run: |
|
||||
# 设置变量
|
||||
$urls = @(
|
||||
'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/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/plantuml/plantuml/releases/download/v1.2023.4/plantuml-1.2023.4.jar'
|
||||
)
|
||||
# 下载文件
|
||||
|
7
.gitignore
vendored
@ -166,3 +166,10 @@ 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
@ -2,9 +2,5 @@
|
||||
"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
@ -1,47 +0,0 @@
|
||||
# -------------------------------
|
||||
# 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
@ -1,111 +1,70 @@
|
||||
"""
|
||||
writen by shenjackyuanjie
|
||||
email: 3695888@qq.com
|
||||
"""
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import cProfile
|
||||
import traceback
|
||||
import threading
|
||||
|
||||
from io import StringIO
|
||||
|
||||
# TODO 默认位置配置文件
|
||||
# TODO 可自定义工作路径
|
||||
from pathlib import Path
|
||||
|
||||
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'{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里
|
||||
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
|
||||
|
||||
|
||||
def main() -> int:
|
||||
print(hi) # hi!
|
||||
print(hi, f"\n{time.ctime()}") # hi!
|
||||
# 记录启动信息
|
||||
start_time_ns = time.time_ns()
|
||||
start_time_perf_ns = time.perf_counter_ns()
|
||||
print_path()
|
||||
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 已关闭")
|
||||
modify_path()
|
||||
start(start_time_ns)
|
||||
return 0
|
||||
|
||||
|
||||
|
@ -4,20 +4,34 @@
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
import sys
|
||||
import importlib
|
||||
import traceback
|
||||
import contextlib
|
||||
import importlib.util
|
||||
import time
|
||||
import logging.config
|
||||
from pathlib import Path
|
||||
from typing import Optional, List, Tuple
|
||||
|
||||
from Difficult_Rocket.api.types import Options, Version
|
||||
|
||||
game_version = Version("0.8.2.0") # 游戏版本
|
||||
build_version = Version("2.1.0.0") # 编译文件版本(与游戏本体无关)
|
||||
sdk_version = Version("0.8.7.0") # SDK 版本
|
||||
build_version = Version("2.1.3.0") # 编译文件版本(与游戏本体无关)
|
||||
Api_version = Version("0.1.1.0") # API 版本
|
||||
__version__ = game_version
|
||||
__version__ = sdk_version
|
||||
|
||||
|
||||
__all__ = [
|
||||
# __init__
|
||||
'DR_status',
|
||||
# folder
|
||||
'api',
|
||||
'client',
|
||||
'server',
|
||||
'command',
|
||||
'crash',
|
||||
'exception',
|
||||
'mod',
|
||||
'utils',
|
||||
# file
|
||||
'main',
|
||||
'runtime',
|
||||
]
|
||||
|
||||
|
||||
class _DR_status(Options):
|
||||
@ -43,7 +57,7 @@ class _DR_status(Options):
|
||||
crash_report_test: bool = False
|
||||
|
||||
# game version status
|
||||
DR_version: Version = game_version # DR SDK 版本
|
||||
DR_version: Version = sdk_version # DR SDK 版本
|
||||
Build_version: Version = build_version # DR 构建 版本
|
||||
API_version: Version = Api_version # DR SDK API 版本
|
||||
|
||||
@ -58,64 +72,23 @@ 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()
|
||||
DR_runtime = _DR_runtime()
|
||||
|
||||
|
||||
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()
|
||||
|
||||
|
||||
if DR_status.playing:
|
||||
from Difficult_Rocket.utils.thread import new_thread
|
||||
|
@ -12,8 +12,6 @@ gitee: @shenjackyuanjie
|
||||
"""
|
||||
|
||||
|
||||
# from Difficult_Rocket.api import screen, mod, exception
|
||||
|
||||
__all__ = [
|
||||
'exception',
|
||||
# 错误类定义
|
||||
@ -23,6 +21,4 @@ __all__ = [
|
||||
# 类型定义
|
||||
'mod',
|
||||
# mod api
|
||||
'log'
|
||||
# log api
|
||||
]
|
||||
|
17
Difficult_Rocket/api/camera.py
Normal file
@ -0,0 +1,17 @@
|
||||
# -------------------------------
|
||||
# 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'
|
||||
]
|
9
Difficult_Rocket/api/gui/__init__.py
Normal file
@ -0,0 +1,9 @@
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
__all__ = [
|
||||
'widget'
|
||||
]
|
11
Difficult_Rocket/api/gui/widget/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
from Difficult_Rocket.gui.widget.button import PressTextButton
|
||||
|
||||
__all__ = [
|
||||
'PressTextButton'
|
||||
]
|
@ -1,16 +0,0 @@
|
||||
# -------------------------------
|
||||
# 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,6 +112,7 @@ 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,12 +12,15 @@ from Difficult_Rocket.utils.options import (Options,
|
||||
OptionNotFound,
|
||||
get_type_hints_)
|
||||
|
||||
from libs.MCDR.version import Version
|
||||
from libs.MCDR.version import (Version,
|
||||
VersionRequirement,
|
||||
VersionParsingError)
|
||||
|
||||
__all__ = [
|
||||
# main class
|
||||
'Options',
|
||||
'Version',
|
||||
'VersionRequirement',
|
||||
|
||||
# data class
|
||||
'FontData',
|
||||
@ -27,6 +30,7 @@ __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
|
||||
from typing import Callable, Dict, List, TYPE_CHECKING, Type
|
||||
|
||||
# third function
|
||||
import rtoml
|
||||
@ -23,23 +23,28 @@ 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.api.types import Options
|
||||
from Difficult_Rocket.command import line
|
||||
from Difficult_Rocket.api.types import Options
|
||||
from Difficult_Rocket.utils.translate import tr
|
||||
from Difficult_Rocket import DR_runtime
|
||||
from Difficult_Rocket.runtime 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
|
||||
from Difficult_Rocket.client.screen import DRScreen, DRDEBUGScreen
|
||||
|
||||
|
||||
logger = logging.getLogger('client')
|
||||
|
||||
|
||||
class ClientOption(Options):
|
||||
@ -51,20 +56,24 @@ class ClientOption(Options):
|
||||
resizeable: bool = True
|
||||
visible: bool = True
|
||||
gui_scale: float = 1.0
|
||||
caption: str = "Difficult Rocket v{DR_version}|DR_rs v{DR_Rust_get_version}"
|
||||
caption: str = "Difficult Rocket v{DR_version}"
|
||||
|
||||
def load_file(self) -> None:
|
||||
file: dict = tools.load_file('./configs/main.toml')
|
||||
file: dict = tools.load_file('./config/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_runtime.format(file['window']['caption'])
|
||||
self.caption = DR_status.format(file['window']['caption'])
|
||||
self.caption = DR_runtime.format(self.caption)
|
||||
|
||||
|
||||
class Client:
|
||||
"""
|
||||
客户端
|
||||
"""
|
||||
def __init__(self, game: "Game", net_mode='local'):
|
||||
start_time = time.time_ns()
|
||||
# logging
|
||||
@ -89,6 +98,9 @@ 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 写一下服务端启动相关,还是需要服务端啊
|
||||
@ -98,6 +110,11 @@ 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)
|
||||
@ -105,13 +122,48 @@ 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':
|
||||
pyglet.font.add_file(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')
|
||||
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)
|
||||
@ -130,6 +182,14 @@ 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():
|
||||
@ -166,16 +226,16 @@ class ClientWindow(Window):
|
||||
self.net_mode = net_mode
|
||||
self.run_input = False
|
||||
self.command_list: List[str] = []
|
||||
# configs
|
||||
self.main_config = tools.load_file('./configs/main.toml')
|
||||
self.game_config = tools.load_file('./configs/game.config')
|
||||
# config
|
||||
self.main_config = tools.load_file('./config/main.toml')
|
||||
self.game_config = tools.load_file('./config/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.part_batch = pyglet.graphics.Batch()
|
||||
self.label_batch = pyglet.graphics.Batch()
|
||||
self.main_batch = Batch()
|
||||
self.main_group = Group(0)
|
||||
# frame
|
||||
self.frame = pyglet.gui.Frame(self, order=20)
|
||||
self.M_frame = pyglet.gui.MovableFrame(self, modifier=key.LCTRL)
|
||||
@ -183,12 +243,11 @@ class ClientWindow(Window):
|
||||
# setup
|
||||
self.setup()
|
||||
# 命令显示
|
||||
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 = TextEntry(x=50, y=30, width=300,
|
||||
batch=self.main_batch, text='', group=Group(1000, parent=self.main_group)) # 实例化
|
||||
self.input_box.push_handlers(self)
|
||||
self.input_box.set_handler('on_commit', self.on_input)
|
||||
self.set_handlers(self.input_box)
|
||||
self.push_handlers(self.input_box)
|
||||
self.input_box.enabled = True
|
||||
# 设置刷新率
|
||||
# pyglet.clock.schedule_interval(self.draw_update, float(self.SPF))
|
||||
@ -202,12 +261,10 @@ class ClientWindow(Window):
|
||||
self.count = 0
|
||||
|
||||
def setup(self):
|
||||
self.set_icon(pyglet.image.load('./textures/icon.png'))
|
||||
self.set_icon(pyglet.image.load('assets/textures/icon.png'))
|
||||
self.load_fonts()
|
||||
# TODO 读取配置文件,加载不同的屏幕,解耦
|
||||
self.screen_list['DR_debug'] = DRDEBUGScreen(self)
|
||||
self.screen_list['DR_main'] = DRScreen(self)
|
||||
self.game.dispatch_event('on_client_start', game=self.game, client=self)
|
||||
self.game.dispatch_mod_event('on_client_start', game=self.game, client=self)
|
||||
|
||||
def load_fonts(self) -> None:
|
||||
fonts_folder_path = self.main_config['runtime']['fonts_folder']
|
||||
@ -216,33 +273,39 @@ class ClientWindow(Window):
|
||||
pyglet_load_fonts_folder(fonts_folder_path)
|
||||
|
||||
def start_game(self) -> None:
|
||||
self.set_icon(pyglet.image.load('./textures/icon.png'))
|
||||
self.set_icon(pyglet.image.load('assets/textures/icon.png'))
|
||||
try:
|
||||
pyglet.app.event_loop.run(1 / self.main_config['runtime']['fps'])
|
||||
# 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))
|
||||
except KeyboardInterrupt:
|
||||
print("==========client stop. KeyboardInterrupt info==========")
|
||||
self.logger.warning("==========client stop. KeyboardInterrupt info==========")
|
||||
traceback.print_exc()
|
||||
print("==========client stop. KeyboardInterrupt info end==========")
|
||||
self.dispatch_event("on_close")
|
||||
self.logger.warning("==========client stop. KeyboardInterrupt info end==========")
|
||||
self.dispatch_event("on_close", 'input')
|
||||
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('./configs/main.toml')
|
||||
config_file: dict = tools.load_file('./config/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('./configs/main.toml', 'w'))
|
||||
rtoml.dump(config_file, open('./config/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
|
||||
"""
|
||||
@ -254,11 +317,13 @@ class ClientWindow(Window):
|
||||
self.fps_log.update_tick(now_FPS, decimal_tick)
|
||||
|
||||
@_call_screen_after
|
||||
def on_draw(self, *dt):
|
||||
while command := self.game.console.get_command():
|
||||
# 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:
|
||||
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()
|
||||
|
||||
@ -281,10 +346,9 @@ class ClientWindow(Window):
|
||||
# self.set_location(*self.get_location())
|
||||
print('on hide!')
|
||||
|
||||
@_call_screen_after
|
||||
@_call_screen_before
|
||||
def draw_batch(self):
|
||||
self.part_batch.draw()
|
||||
self.label_batch.draw()
|
||||
self.main_batch.draw()
|
||||
|
||||
"""
|
||||
command line event
|
||||
@ -295,15 +359,18 @@ 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):
|
||||
print(command.find('/'))
|
||||
self.logger.info(tr().window.command.text().format(command))
|
||||
command.text = command.text.rstrip('\n').rstrip(' ').strip('/')
|
||||
self.logger.info(tr().window.command.text().format(f"|{command.text}|"))
|
||||
if command.find('stop'):
|
||||
# self.dispatch_event('on_exit')
|
||||
print("command stop!")
|
||||
pyglet.app.platform_event_loop.stop()
|
||||
self.dispatch_event('on_close', 'command') # source = command
|
||||
self.logger.info("command stop!")
|
||||
# HUGE THANKS to Discord @nokiyasos for this fix!
|
||||
pyglet.app.exit()
|
||||
elif command.find('fps'):
|
||||
if command.find('log'):
|
||||
self.logger.debug(self.fps_log.fps_list)
|
||||
@ -322,10 +389,19 @@ class ClientWindow(Window):
|
||||
tr._language = lang
|
||||
self.logger.info(tr().language_set_to())
|
||||
except LanguageNotFound:
|
||||
self.logger.info(tr().language_available().format(os.listdir('./configs/lang')))
|
||||
self.logger.info(tr().language_available().format(os.listdir('./config/lang')))
|
||||
self.save_info()
|
||||
|
||||
# self.command_tree.parse(command.plain_command)
|
||||
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())
|
||||
|
||||
@_call_screen_after
|
||||
def on_message(self, message: line.CommandText):
|
||||
@ -348,7 +424,7 @@ class ClientWindow(Window):
|
||||
...
|
||||
|
||||
@_call_screen_after
|
||||
def on_mouse_motion(self, x, y, dx, dy) -> None:
|
||||
def on_mouse_motion(self, x, y, dx, dy):
|
||||
...
|
||||
|
||||
@_call_screen_after
|
||||
@ -388,7 +464,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')
|
||||
self.dispatch_event('on_close', 'window')
|
||||
if symbol == key.SLASH:
|
||||
self.input_box._set_focus(True)
|
||||
self.logger.debug(
|
||||
@ -424,7 +500,7 @@ class ClientWindow(Window):
|
||||
|
||||
@_call_screen_before
|
||||
def on_close(self, source: str = 'window') -> None:
|
||||
self.game.dispatch_event('on_close', game=self.game, client=self, source=source)
|
||||
self.game.dispatch_mod_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
|
||||
|
@ -1,302 +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, 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,24 +7,17 @@
|
||||
import typing
|
||||
|
||||
from pyglet.text import Label
|
||||
from pyglet.graphics import Batch, Group
|
||||
from pyglet.clock import get_frequency
|
||||
from pyglet.graphics import Batch, Group
|
||||
|
||||
# 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)
|
||||
@ -55,4 +48,3 @@ class DRDEBUGScreen(BaseScreen):
|
||||
|
||||
def on_draw(self, *dt, window: "ClientWindow"):
|
||||
self.main_batch.draw()
|
||||
# print(self.window_pointer.try_if_runs)
|
||||
|
@ -44,10 +44,13 @@ class CommandText:
|
||||
i += 1
|
||||
|
||||
def find(self, text: str) -> bool:
|
||||
find = self.text.find(text)
|
||||
if find != -1:
|
||||
self.text = self.text[find + len(text):]
|
||||
return True
|
||||
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
|
||||
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.gmtime(time.time()))
|
||||
date_time = time.strftime('%Y-%m-%d %H-%M-%S', time.localtime())
|
||||
filename = f'crash-{date_time}.md'
|
||||
cache_stream = io.StringIO()
|
||||
try:
|
||||
@ -92,16 +92,17 @@ 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.gmtime(time.time()))))
|
||||
cache_stream.write(Head_message.format(now_time=time.strftime('%Y/%m/%d %H:%M:%S', time.localtime())))
|
||||
# 崩溃信息
|
||||
cache_stream.write(crash_info)
|
||||
|
||||
|
||||
def write_info_to_cache(cache_stream):
|
||||
# 运行状态信息
|
||||
from Difficult_Rocket import DR_status, DR_runtime
|
||||
from Difficult_Rocket import DR_status
|
||||
from Difficult_Rocket.runtime import DR_runtime
|
||||
cache_stream.write(Run_message)
|
||||
cache_stream.write(markdown_line_handler(f'DR Version: {Difficult_Rocket.game_version}', level=1))
|
||||
cache_stream.write(markdown_line_handler(f'DR Version: {Difficult_Rocket.sdk_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()}")
|
||||
|
125
Difficult_Rocket/gui/widget/button.py
Normal file
@ -0,0 +1,125 @@
|
||||
# -------------------------------
|
||||
# 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,33 +11,24 @@ 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 TYPE_CHECKING, List, Optional, Dict, TypeVar
|
||||
from typing import List, Optional, Dict
|
||||
|
||||
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, DR_runtime
|
||||
from Difficult_Rocket import client, server, DR_status
|
||||
|
||||
|
||||
class Console(Options):
|
||||
@ -68,6 +59,9 @@ 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'
|
||||
@ -78,75 +72,21 @@ class Game(Options):
|
||||
console_class: Console = Console
|
||||
|
||||
main_config: Dict
|
||||
logging_config: Dict
|
||||
logger: logging.Logger
|
||||
|
||||
mod_module: List["ModInfo"] = []
|
||||
mod_manager: ModManager
|
||||
|
||||
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 dispatch_mod_event(self, event_name: str, *args, **kwargs) -> None:
|
||||
self.mod_manager.dispatch_event(event_name, *args, **kwargs)
|
||||
|
||||
def init_mods(self) -> None:
|
||||
"""验证/加载 mod"""
|
||||
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
|
||||
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)
|
||||
|
||||
def init_console(self) -> None:
|
||||
self.console = self.console_class()
|
||||
@ -166,32 +106,15 @@ 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:
|
||||
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
|
||||
self.logger.info(f'\n{self.as_markdown()}')
|
||||
|
||||
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()
|
||||
@ -199,9 +122,6 @@ 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,38 +4,203 @@
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
import sys
|
||||
import time
|
||||
import logging
|
||||
import traceback
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Optional
|
||||
from typing import List, Dict, Optional, TypeVar
|
||||
|
||||
from Difficult_Rocket.api.screen import BaseScreen
|
||||
from Difficult_Rocket.api.types import Options, Version
|
||||
from Difficult_Rocket.mod.api import ModInfo
|
||||
# from Difficult_Rocket import DR_status, DR_runtime
|
||||
from Difficult_Rocket.utils.translate import tr
|
||||
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))
|
||||
|
||||
|
||||
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 find_mods(self) -> List[Path]:
|
||||
def get_mod_module(self, mod_name: str) -> Optional[ModInfo]:
|
||||
"""
|
||||
查找mods文件夹下的所有mod
|
||||
获取指定 mod 的模块
|
||||
:param mod_name: mod 名
|
||||
:return:
|
||||
"""
|
||||
mods = []
|
||||
for path in self.mods_path:
|
||||
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 init(self) -> None:
|
||||
self.logger = logging.getLogger('client')
|
||||
self.logger.name = 'mod_manager'
|
||||
self.logger.info('Mod Manager init')
|
||||
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:
|
||||
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))
|
||||
|
||||
|
79
Difficult_Rocket/runtime.py
Normal file
@ -0,0 +1,79 @@
|
||||
# -------------------------------
|
||||
# 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('configs/main.toml')
|
||||
self.config = tools.load_file('config/main.toml')
|
||||
# self.dev = Dev
|
||||
# self.net_mode = net_mode
|
||||
self.logger.info(tr().server.setup.use_time().format(time.time() - start_time))
|
||||
|
@ -4,3 +4,10 @@
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
__all__ = [
|
||||
'camera',
|
||||
'options',
|
||||
'thread',
|
||||
'tools',
|
||||
'translate'
|
||||
]
|
||||
|
263
Difficult_Rocket/utils/camera.py
Normal file
@ -0,0 +1,263 @@
|
||||
# -------------------------------
|
||||
# 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))
|
@ -1,34 +0,0 @@
|
||||
# -------------------------------
|
||||
# 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,19 +4,21 @@
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
import warnings
|
||||
import shutil
|
||||
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',
|
||||
'Fonts',
|
||||
'FontData',
|
||||
'OptionsError',
|
||||
'OptionNotFound',
|
||||
'OptionNameNotDefined']
|
||||
__all__ = [
|
||||
'get_type_hints_',
|
||||
'Options',
|
||||
'OptionsError',
|
||||
'OptionNotFound',
|
||||
'OptionNameNotDefined',
|
||||
'Fonts',
|
||||
'FontData'
|
||||
]
|
||||
|
||||
|
||||
def get_type_hints_(cls: Type):
|
||||
@ -174,15 +176,12 @@ class Options:
|
||||
self.cached_options = self.option()
|
||||
return self.cached_options
|
||||
|
||||
def option_with_len(self, longest: Optional[int] = None) -> Tuple[List[Tuple[str, Union[Any, Type], Type]], int, int, int]:
|
||||
def option_with_len(self) -> Tuple[List[Tuple[str, Any, Type]], int, int, int]:
|
||||
"""
|
||||
返回一个可以用于打印的 option 列表
|
||||
:return:
|
||||
"""
|
||||
if longest is None:
|
||||
options = self.flush_option()
|
||||
else:
|
||||
options = self.str_option(longest)
|
||||
options = self.flush_option()
|
||||
max_len_key = 1
|
||||
max_len_value = 1
|
||||
max_len_value_t = 1
|
||||
@ -192,19 +191,61 @@ 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
|
||||
option_list.append([key, value, value_t])
|
||||
return [option_list, max_len_key, max_len_value, max_len_value_t] # noqa
|
||||
|
||||
def as_markdown(self, longest: Optional[int] = None) -> str:
|
||||
"""
|
||||
返回一个 markdown 格式的 option 字符串
|
||||
:param longest: 最长的输出长度
|
||||
:return: markdown 格式的 option 字符串
|
||||
"""
|
||||
value = self.option_with_len(longest)
|
||||
value = self.option_with_len()
|
||||
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,22 +16,21 @@ import sys
|
||||
import time
|
||||
import math
|
||||
import json
|
||||
import rtoml
|
||||
import logging
|
||||
import configparser
|
||||
|
||||
from typing import Union
|
||||
from pathlib import Path
|
||||
from typing import Union, Optional
|
||||
from xml.etree import ElementTree
|
||||
|
||||
import rtoml
|
||||
|
||||
from defusedxml.ElementTree import parse
|
||||
|
||||
from Difficult_Rocket.exception.unsupport import NoMoreJson5
|
||||
|
||||
# logger
|
||||
tools_logger = logging.getLogger('part-tools')
|
||||
tools_logger = logging.getLogger('tools')
|
||||
"""
|
||||
file configs
|
||||
file config
|
||||
"""
|
||||
|
||||
file_error = {FileNotFoundError: 'no {filetype} file was founded!:\n file name: {filename}\n file_type: {filetype}\n stack: {stack}',
|
||||
@ -39,10 +38,12 @@ 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: str,
|
||||
stack: Union[str, list, dict, None] = None,
|
||||
raise_error: bool = True,
|
||||
encoding: str = 'utf-8') -> Union[dict, ElementTree.ElementTree]:
|
||||
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)
|
||||
f_type = file_name[file_name.rfind('.') + 1:] # 从最后一个.到末尾 (截取文件格式)
|
||||
get_file = NotImplementedError('解析失败,请检查文件类型/文件内容/文件是否存在!')
|
||||
try:
|
||||
@ -98,7 +99,7 @@ def save_dict_file(file_name: str,
|
||||
|
||||
|
||||
# main config
|
||||
main_config_file = load_file('./configs/main.toml')
|
||||
main_config_file = load_file('./config/main.toml')
|
||||
|
||||
|
||||
def get_At(name, in_xml, need_type=str):
|
||||
|
@ -14,11 +14,13 @@ 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_runtime, DR_status
|
||||
from Difficult_Rocket import DR_status
|
||||
from Difficult_Rocket.utils import tools
|
||||
from Difficult_Rocket.runtime import DR_runtime
|
||||
from Difficult_Rocket.exception.language import (LanguageNotFound,
|
||||
TranslateKeyNotFound)
|
||||
|
||||
@ -138,12 +140,12 @@ class Translates:
|
||||
|
||||
def __str__(self):
|
||||
if not any(not x[0] for x in self._get_list):
|
||||
return self._value
|
||||
return str(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 self._value
|
||||
return str(self._value)
|
||||
|
||||
|
||||
class Tr:
|
||||
@ -152,15 +154,20 @@ class Tr:
|
||||
GOOD
|
||||
"""
|
||||
|
||||
def __init__(self, language: str = None, config: Optional[TranslateConfig] = None):
|
||||
def __init__(self,
|
||||
language: str = None,
|
||||
config: Optional[TranslateConfig] = None,
|
||||
lang_path: Optional[Path] = None):
|
||||
"""
|
||||
诶嘿,我抄的MCDR
|
||||
:param language: Tr 所使用的的语言
|
||||
:param config: 配置
|
||||
:param lang_path: 语言文件夹路径
|
||||
"""
|
||||
self.language_name = language if language is not None else DR_runtime.language
|
||||
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.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.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())
|
||||
|
||||
@ -184,11 +191,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'./configs/lang/{lang}.toml'):
|
||||
print(f"lang: {os.path.exists(f'./configs/lang/{lang}.toml')} language = {lang} {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=}")
|
||||
raise LanguageNotFound(lang)
|
||||
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: 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_cache = Translates(value=self.translates, config=self.default_config.copy())
|
||||
self.language_name = lang
|
||||
DR_runtime.language = self.language_name
|
||||
|
54
README.md
@ -1,3 +1,5 @@
|
||||
<div style="text-align: center;">
|
||||
|
||||
# Difficult Rocket
|
||||
|
||||
中文 | [English](./docs/README-en.md)
|
||||
@ -9,22 +11,28 @@
|
||||
|
||||
## 请注意 这个仓库未来只会发布 `DR SDK` 的更新 `DR game` 的更新会在 [这里](https://github.com/shenjackyuanjie/DR-game) 发布
|
||||
|
||||
![demo](docs/src/demo.png)
|
||||
|
||||
<a href="https://996.icu"><img src="https://img.shields.io/badge/link-996.icu-red.svg" alt="996.icu" /></a>
|
||||
[![Generic badge](https://img.shields.io/badge/SemVer-2.0.0-blue.svg)](https://Semver.org/)
|
||||
[![Generic badge](https://img.shields.io/badge/编写于_Python_版本-3.8.10-blue.svg)](https://Python.org)
|
||||
[![Generic badge](https://img.shields.io/badge/编写于_Pyglet_版本-2.0.7-blue.svg)](https://pyglet.org)
|
||||
[![Generic badge](https://img.shields.io/badge/编写于_Pyglet_版本-2.0.8-blue.svg)](https://pyglet.org)
|
||||
[![Generic badge](https://img.shields.io/badge/Python-_3.8_|_3.9_|_3.10_|_3.11_-blue.svg)](https://Python.org)
|
||||
|
||||
## 版本
|
||||
|
||||
[关于版本号的说明](./docs/src/version.md)
|
||||
|
||||
[![Generic badge](https://img.shields.io/badge/Release-0.8.2.0-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
|
||||
[![Generic badge](https://img.shields.io/badge/Pre_Release-0.8.2.0-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
|
||||
[![Generic badge](https://img.shields.io/badge/Devloping-0.8.3-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
|
||||
[![Generic badge](https://img.shields.io/badge/Release-0.8.5.1-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
|
||||
[![Generic badge](https://img.shields.io/badge/Pre_Release-0.8.6.0-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
|
||||
[![Generic badge](https://img.shields.io/badge/Devloping-0.8.6-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
|
||||
|
||||
[![language badge](https://stats.deeptrain.net/repo/shenjackyuanjie/Difficult-Rocket?theme=dark)](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游戏(简称:火箭模拟器)
|
||||
@ -42,27 +50,27 @@
|
||||
## 环境需求 (测试过的 / 开发平台)
|
||||
|
||||
- `开发平台 1 - Windows 10 x64 22H2`
|
||||
- Python `3.8.10`
|
||||
- Python `3.8.10` / `3.10.11`
|
||||
- pillow `9.5.0`
|
||||
- psutil `5.9.5`
|
||||
- rtoml `0.9.0`
|
||||
- tomlkit `0.11.7`
|
||||
- tomlkit `0.11.8`
|
||||
- defusedxml `0.7.1`
|
||||
- objprint `0.2.2`
|
||||
- viztracer `0.15.6`
|
||||
- vizplugins `0.1.3`
|
||||
- nuitka `1.5.6`
|
||||
- nuitka `1.6.6`
|
||||
- ordered-set `4.1.0`
|
||||
- imageio `2.27.0`
|
||||
- imageio `2.31.0`
|
||||
- wheel `0.40.0`
|
||||
- setuptools `67.6.1`
|
||||
- setuptools-rust `1.5.2`
|
||||
- setuptools `67.8.0`
|
||||
- setuptools-rust `1.6.0`
|
||||
- `AMD R5 5600X`
|
||||
- `AMD RX 550 4G`
|
||||
|
||||
## 需要的 Python 模块
|
||||
|
||||
- `pyglet` (已经内置 V2.0.5 路径:`./libs/pyglet`)
|
||||
- `pyglet` (已经内置 V2.0.8 路径:`./libs/pyglet`)
|
||||
- `xmltodict` (已经内置 V0.12.0 路径:`./libs/xmltodict`)
|
||||
- `pyperclip` (已经内置 V1.8.2 路径: `./libs/pyperclip`)
|
||||
|
||||
@ -73,28 +81,30 @@
|
||||
# DR contributing
|
||||
|
||||
# for images
|
||||
pillow >= 9.5.0
|
||||
# not for pypy >= 3.10
|
||||
pillow >= 10.0.0; (platform_python_implementation == "PyPy" and python_version < "3.10") or platform_python_implementation == "CPython"
|
||||
|
||||
# for sys info
|
||||
psutil >= 5.9.5
|
||||
|
||||
# for files
|
||||
rtoml >= 0.9.0
|
||||
tomlkit >= 0.11.7
|
||||
tomlkit >= 0.11.8
|
||||
defusedxml >= 0.7.1
|
||||
|
||||
# for debug
|
||||
objprint >= 0.2.2
|
||||
viztracer >= 0.15.6
|
||||
vizplugins >= 0.1.3
|
||||
viztracer >= 0.15.6; platform_python_implementation != "PyPy"
|
||||
vizplugins >= 0.1.3; platform_python_implementation != "PyPy"
|
||||
|
||||
# for compile
|
||||
nuitka >= 1.5.6
|
||||
nuitka >= 1.7.5
|
||||
ordered-set >= 4.1.0
|
||||
imageio >= 2.27.0
|
||||
imageio >= 2.31.0; (platform_python_implementation == "PyPy" and python_version < "3.10") or platform_python_implementation == "CPython"
|
||||
wheel >= 0.40.0
|
||||
setuptools >= 67.6.1
|
||||
setuptools-rust >= 1.5.2
|
||||
setuptools >= 67.8.0
|
||||
setuptools-rust >= 1.6.0
|
||||
|
||||
```
|
||||
|
||||
## 感谢
|
||||
@ -104,8 +114,8 @@ setuptools-rust >= 1.5.2
|
||||
- `tomlkit` / `rtoml` : toml 解析器
|
||||
- `xmltodict`: xml 与 dict 转换器
|
||||
- `pyperclip`: 剪贴板!
|
||||
- `rapier2d`: 物理模拟引擎
|
||||
- `pyo3`: Rust Python 扩展
|
||||
- [rapier2d](https://rapier.rs/) : 物理模拟引擎
|
||||
- [pyo3](https://pyo3.rs/main): Rust Python 扩展
|
||||
|
||||
- 主要贡献者
|
||||
- [@Rayawa](https://github.com/Rayawa) : 文档矫正 & 翻译部分 lang
|
||||
@ -113,6 +123,8 @@ setuptools-rust >= 1.5.2
|
||||
- [@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="0" 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="1" 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,15 +20,26 @@ 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.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"
|
||||
|
||||
[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: {}"
|
||||
|
||||
[client]
|
||||
setup.start = "Client start loading"
|
||||
@ -54,8 +65,6 @@ 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: {}"
|
||||
@ -63,6 +72,9 @@ 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"
|
||||
@ -71,17 +83,6 @@ os.pid_is = "Server PID: {} PPID: {}"
|
||||
|
||||
[game]
|
||||
input = "console"
|
||||
command = "in game commands"
|
||||
window = "window"
|
||||
command = "in game commands"
|
||||
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,16 +20,26 @@ logger.logfile_level = "日志文件记录级别:"
|
||||
logger.logfile_fmt = "日志文件记录格式:"
|
||||
logger.logfile_datefmt = "日志文件日期格式:"
|
||||
game_start.at = "游戏主线程开始于:"
|
||||
mod.find.start = "正在校验 Mod: {}"
|
||||
mod.find.faild.no_spec = "importlib 无法找到 spec"
|
||||
mod.find.faild.no_mod_folder = "没有找到 Mod 文件夹"
|
||||
mod.find.done = "所有 Mod 校验完成"
|
||||
mod.load.start = "正在加载 Mod: {}"
|
||||
mod.load.info = "mod id: {} 版本号: {}"
|
||||
mod.load.faild.info = "Mod 加载失败: {} 错误信息: {}"
|
||||
mod.load.faild.no_mod_class = "没有找到 Mod 类"
|
||||
mod.load.done = "所有 Mod 加载完成"
|
||||
mod.event.error = "Mod 事件 {} 发生错误 {} Mod: {}"
|
||||
|
||||
[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 重载成功: {}"
|
||||
|
||||
[client]
|
||||
setup.start = "客户端加载开始"
|
||||
@ -55,8 +65,6 @@ 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 = "在字体列表中找到以下字体库: {}"
|
||||
@ -64,6 +72,9 @@ fonts.load = "正在加载字体: {}"
|
||||
game.stop_get = "从{}传入关闭指令,关闭游戏中"
|
||||
game.stop = "游戏正在关闭,保存数据中···"
|
||||
game.end = "游戏已经关闭"
|
||||
command.text = "输入命令: {}"
|
||||
message.text = "输入信息: {}"
|
||||
command.mods.reload.no_mod_id = "没有指定 mod id"
|
||||
|
||||
[server]
|
||||
setup.start = "服务端开始加载"
|
||||
@ -72,18 +83,6 @@ os.pid_is = "服务端 PID: {} PPID: {}"
|
||||
|
||||
[game]
|
||||
input = "控制台"
|
||||
command = "游戏内命令行"
|
||||
window = "窗口"
|
||||
command = "游戏内命令行"
|
||||
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 = "飞船渲染完成"
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 121 B After Width: | Height: | Size: 121 B |
Before Width: | Height: | Size: 133 B After Width: | Height: | Size: 133 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 450 B |
Before Width: | Height: | Size: 517 B After Width: | Height: | Size: 517 B |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 140 B After Width: | Height: | Size: 140 B |
Before Width: | Height: | Size: 932 B After Width: | Height: | Size: 932 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 775 B After Width: | Height: | Size: 775 B |
Before Width: | Height: | Size: 540 B After Width: | Height: | Size: 540 B |