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 setup.py build
|
||||||
python post_build.py
|
python post_build.py
|
||||||
python setup.py clean
|
python setup.py clean
|
||||||
|
cd ..
|
||||||
|
Remove-Item -Recurse -Force src
|
||||||
|
|
||||||
# Uploads artifact
|
# Uploads artifact
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
@ -88,4 +90,4 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: DR_rs${{env.DR_version}}-${{runner.os}}${{matrix.python-version}}-Build.${{github.run_number}}+${{env.short_sha}}
|
name: DR_rs${{env.DR_version}}-${{runner.os}}${{matrix.python-version}}-Build.${{github.run_number}}+${{env.short_sha}}
|
||||||
path: |
|
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
|
# Difficult Rocket
|
||||||
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||||
@ -109,7 +108,7 @@ def main():
|
|||||||
return 0
|
return 0
|
||||||
dsm.clear_dsm()
|
dsm.clear_dsm()
|
||||||
dsm.upload_docs('docs/md5.txt')
|
dsm.upload_docs('docs/md5.txt')
|
||||||
dsm.fl.session.logout('FileStation')
|
dsm.fl.logout()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
22
.github/workflows/get_info.py
vendored
@ -10,32 +10,24 @@ import rtoml
|
|||||||
|
|
||||||
sys.path.append(os.path.abspath(os.curdir))
|
sys.path.append(os.path.abspath(os.curdir))
|
||||||
|
|
||||||
from Difficult_Rocket import DR_runtime
|
from Difficult_Rocket import DR_status
|
||||||
|
|
||||||
args = ['-env', '-github-dev']
|
args = ['-env', '-github-dev']
|
||||||
|
|
||||||
# print(sys.argv)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if sys.argv == [__file__]: # 没有输入参数,直接输出默认信息并输出
|
if sys.argv == [__file__]: # 没有输入参数,直接输出默认信息并输出
|
||||||
print(sys.version)
|
print(sys.version)
|
||||||
from Difficult_Rocket.utils import tools
|
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']['width'] = 1024
|
||||||
config_file['window']['height'] = 768
|
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:
|
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:
|
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_version = "{DR_status.DR_version}"', file=env_file)
|
||||||
print(f'$env:DR_language = "{DR_runtime.language}"', file=env_file)
|
print(f'$env:Build_version = "{DR_status.Build_version}"', file=env_file)
|
||||||
print(f'$env:DR_long_version = "{DR_runtime.DR_long_version}"', file=env_file)
|
|
||||||
print(f'$env:Build_version = "{DR_runtime.Build_version}"', file=env_file)
|
|
||||||
|
|
||||||
elif os.path.abspath(os.curdir) in sys.path and '-github' in sys.argv:
|
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_version={DR_status.DR_version}')
|
||||||
print(f'DR_language={DR_runtime.language}')
|
print(f'Build_version={DR_status.Build_version}')
|
||||||
print(f'DR_long_version={DR_runtime.DR_long_version}')
|
|
||||||
print(f'Build_version={DR_runtime.Build_version}')
|
|
||||||
|
2
.github/workflows/nuitka.yml
vendored
@ -7,7 +7,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- "Difficult_Rocket/**" # 本体修改
|
- "Difficult_Rocket/**" # 本体修改
|
||||||
- "configs/**" # 配置修改
|
|
||||||
- "libs/pyglet/**" # pyglet 修改
|
- "libs/pyglet/**" # pyglet 修改
|
||||||
- ".github/workflows/**" # workflow 修改
|
- ".github/workflows/**" # workflow 修改
|
||||||
- "nuitka_build.py" # 构建脚本修改
|
- "nuitka_build.py" # 构建脚本修改
|
||||||
@ -15,7 +14,6 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- "Difficult_Rocket/**" # 本体修改
|
- "Difficult_Rocket/**" # 本体修改
|
||||||
- "configs/**" # 配置修改
|
|
||||||
- "libs/pyglet/**" # pyglet 修改
|
- "libs/pyglet/**" # pyglet 修改
|
||||||
- ".github/workflows/**" # workflow 修改
|
- ".github/workflows/**" # workflow 修改
|
||||||
- "nuitka_build.py" # 构建脚本修改
|
- "nuitka_build.py" # 构建脚本修改
|
||||||
|
12
.github/workflows/page.yml
vendored
@ -49,12 +49,12 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
# 设置变量
|
# 设置变量
|
||||||
$urls = @(
|
$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.4/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.4/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.4/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.4/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.4/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-external-links',
|
||||||
'https://github.com/plantuml/plantuml/releases/download/v1.2023.4/plantuml-1.2023.4.jar'
|
'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
|
*cmake-build-debug
|
||||||
.pdm.toml
|
.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": [
|
"rust-analyzer.linkedProjects": [
|
||||||
"mods/dr_game/Difficult_Rocket_rs/src/Cargo.toml",
|
"mods/dr_game/Difficult_Rocket_rs/src/Cargo.toml",
|
||||||
"libs/pyglet_rs/src/Cargo.toml",
|
"libs/pyglet_rs/src/Cargo.toml",
|
||||||
],
|
]
|
||||||
"python.analysis.extraPaths": [
|
|
||||||
"./libs"
|
|
||||||
],
|
|
||||||
"python.analysis.typeCheckingMode": "basic"
|
|
||||||
}
|
}
|
||||||
|
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
|
# Difficult Rocket
|
||||||
email: 3695888@qq.com
|
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
|
||||||
"""
|
# All rights reserved
|
||||||
|
# -------------------------------
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import cProfile
|
|
||||||
import traceback
|
import traceback
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from io import StringIO
|
from pathlib import Path
|
||||||
|
|
||||||
# TODO 默认位置配置文件
|
|
||||||
# TODO 可自定义工作路径
|
|
||||||
|
|
||||||
hi = """Difficult Rocket is writen by shenjackyuanjie
|
hi = """Difficult Rocket is writen by shenjackyuanjie
|
||||||
email: 3695888@qq.com or shyj3695888@163.com
|
email: 3695888@qq.com or shyj3695888@163.com
|
||||||
QQ: 3695888"""
|
QQ: 3695888"""
|
||||||
|
|
||||||
error_format = {
|
|
||||||
'TestError': '游戏正在调试中,某处引发了一个 TestError,不是bug造成的原因',
|
|
||||||
'AssertionError': '游戏的某处检查未通过,情报告issue',
|
|
||||||
'error.unknown': '游戏报错了,现在输出报错信息,请报告issue',
|
|
||||||
'error.happen': '游戏出现了一个报错!正在处理'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def print_path() -> None:
|
def print_path() -> None:
|
||||||
print(f'{__file__=}')
|
print(f'{__file__=}')
|
||||||
print(f'{sys.path=}')
|
print(f'{sys.path=}')
|
||||||
print(f'{sys.path[0]=}')
|
print(f'{sys.path[0]=}')
|
||||||
print(f'{sys.argv[0]=}')
|
print(f'{sys.argv[0]=}')
|
||||||
print(f'{os.curdir=}')
|
print(f'{Path.cwd()=}')
|
||||||
print(f'{os.getcwd()=}')
|
print(f'{Path(__file__).absolute()=}')
|
||||||
print(f'{os.path.abspath(os.curdir)=}')
|
|
||||||
print(f'{os.path.abspath(__file__)=}')
|
|
||||||
print(f'{os.path.realpath(__file__)=}')
|
def modify_path() -> None:
|
||||||
print(f'{os.path.split(os.path.split(os.path.realpath(__file__))[0])=}')
|
os.chdir(Path(__file__).parent) # 将运行路径切换到文件位置 防止bug
|
||||||
# 输出一遍大部分文件位置相关信息 以后可能会加到logs里
|
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:
|
def main() -> int:
|
||||||
print(hi) # hi!
|
print(hi, f"\n{time.ctime()}") # hi!
|
||||||
|
# 记录启动信息
|
||||||
start_time_ns = time.time_ns()
|
start_time_ns = time.time_ns()
|
||||||
start_time_perf_ns = time.perf_counter_ns()
|
|
||||||
print_path()
|
print_path()
|
||||||
file_path = os.path.split(os.path.realpath(__file__))[0]
|
modify_path()
|
||||||
os.chdir(file_path) # 将运行路径切换到文件位置 防止bug
|
start(start_time_ns)
|
||||||
sys.path.append(f'{file_path}/Difficult_Rocket') # 添加local path
|
|
||||||
sys.path.append(f'{file_path}/libs') # 添加 libs path
|
|
||||||
|
|
||||||
from Difficult_Rocket.exception import TestError
|
|
||||||
from Difficult_Rocket import crash
|
|
||||||
from Difficult_Rocket import DR_status
|
|
||||||
try:
|
|
||||||
from libs import pyglet # 导入pyglet
|
|
||||||
pyglet.resource.path = ['/textures/']
|
|
||||||
pyglet.resource.reindex()
|
|
||||||
|
|
||||||
from Difficult_Rocket import main, DR_runtime
|
|
||||||
DR_runtime.start_time_ns = start_time_ns
|
|
||||||
|
|
||||||
# from pyglet.gl import glClearColor # 调整背景颜色
|
|
||||||
# glClearColor(0.5, 0.5, 0.5, 0)
|
|
||||||
|
|
||||||
game = main.Game() # 实例化一个游戏
|
|
||||||
print(time.perf_counter_ns() - start_time_perf_ns, (time.perf_counter_ns() - start_time_perf_ns) / (10 ** 9), 'start') # 输出一下启动用时
|
|
||||||
|
|
||||||
cprofile = False # 是否使用cprofile
|
|
||||||
if cprofile:
|
|
||||||
cProfile.run('game.start()', sort='calls') # 使用 cprofile 启动
|
|
||||||
else:
|
|
||||||
game.start() # 直接启动
|
|
||||||
if DR_status.crash_report_test:
|
|
||||||
raise TestError('debugging') # debug 嘛,试试crash
|
|
||||||
except Exception as exp: # 出毛病了
|
|
||||||
# 解析错误信息
|
|
||||||
print(error_format['error.happen'])
|
|
||||||
error = traceback.format_exc()
|
|
||||||
name = type(exp).__name__
|
|
||||||
if name in error_format:
|
|
||||||
print(error_format[name])
|
|
||||||
else:
|
|
||||||
print(error_format['error.unknown'])
|
|
||||||
print(error)
|
|
||||||
# 输出 crash 信息
|
|
||||||
crash.create_crash_report(error)
|
|
||||||
cache_steam = StringIO()
|
|
||||||
crash.write_info_to_cache(cache_steam)
|
|
||||||
text = cache_steam.getvalue()
|
|
||||||
print(text)
|
|
||||||
else:
|
|
||||||
crash.record_thread = False
|
|
||||||
print(crash.all_thread)
|
|
||||||
print(crash.all_process)
|
|
||||||
# join all thread
|
|
||||||
for thread in threading.enumerate():
|
|
||||||
print(thread)
|
|
||||||
if thread.name == 'MainThread' or thread == threading.main_thread() or thread == threading.current_thread():
|
|
||||||
continue
|
|
||||||
if thread.daemon:
|
|
||||||
continue
|
|
||||||
thread.join()
|
|
||||||
# stop pyglet
|
|
||||||
import pyglet
|
|
||||||
pyglet.app.exit()
|
|
||||||
print("Difficult_Rocket 已关闭")
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,20 +4,34 @@
|
|||||||
# All rights reserved
|
# All rights reserved
|
||||||
# -------------------------------
|
# -------------------------------
|
||||||
|
|
||||||
import sys
|
import time
|
||||||
import importlib
|
import logging.config
|
||||||
import traceback
|
|
||||||
import contextlib
|
|
||||||
import importlib.util
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, List, Tuple
|
|
||||||
|
|
||||||
from Difficult_Rocket.api.types import Options, Version
|
from Difficult_Rocket.api.types import Options, Version
|
||||||
|
|
||||||
game_version = Version("0.8.2.0") # 游戏版本
|
sdk_version = Version("0.8.7.0") # SDK 版本
|
||||||
build_version = Version("2.1.0.0") # 编译文件版本(与游戏本体无关)
|
build_version = Version("2.1.3.0") # 编译文件版本(与游戏本体无关)
|
||||||
Api_version = Version("0.1.1.0") # API 版本
|
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):
|
class _DR_status(Options):
|
||||||
@ -43,7 +57,7 @@ class _DR_status(Options):
|
|||||||
crash_report_test: bool = False
|
crash_report_test: bool = False
|
||||||
|
|
||||||
# game version status
|
# game version status
|
||||||
DR_version: Version = game_version # DR SDK 版本
|
DR_version: Version = sdk_version # DR SDK 版本
|
||||||
Build_version: Version = build_version # DR 构建 版本
|
Build_version: Version = build_version # DR 构建 版本
|
||||||
API_version: Version = Api_version # DR SDK API 版本
|
API_version: Version = Api_version # DR SDK API 版本
|
||||||
|
|
||||||
@ -58,64 +72,23 @@ class _DR_status(Options):
|
|||||||
return round(12 * self.gui_scale)
|
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_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:
|
if DR_status.playing:
|
||||||
from Difficult_Rocket.utils.thread import new_thread
|
from Difficult_Rocket.utils.thread import new_thread
|
||||||
@ -127,4 +100,4 @@ if DR_status.playing:
|
|||||||
@new_thread('think')
|
@new_thread('think')
|
||||||
def think(some_thing_to_think):
|
def think(some_thing_to_think):
|
||||||
gotcha = think_it(some_thing_to_think)
|
gotcha = think_it(some_thing_to_think)
|
||||||
return gotcha
|
return gotcha
|
@ -12,8 +12,6 @@ gitee: @shenjackyuanjie
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# from Difficult_Rocket.api import screen, mod, exception
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'exception',
|
'exception',
|
||||||
# 错误类定义
|
# 错误类定义
|
||||||
@ -23,6 +21,4 @@ __all__ = [
|
|||||||
# 类型定义
|
# 类型定义
|
||||||
'mod',
|
'mod',
|
||||||
# mod api
|
# 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:
|
:event:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# def on_draw(self, dt: float, window: ClientWindow): # TODO: wait for pyglet 2.1
|
||||||
def on_draw(self, window: ClientWindow):
|
def on_draw(self, window: ClientWindow):
|
||||||
"""The window contents must be redrawn.
|
"""The window contents must be redrawn.
|
||||||
|
|
||||||
|
@ -12,12 +12,15 @@ from Difficult_Rocket.utils.options import (Options,
|
|||||||
OptionNotFound,
|
OptionNotFound,
|
||||||
get_type_hints_)
|
get_type_hints_)
|
||||||
|
|
||||||
from libs.MCDR.version import Version
|
from libs.MCDR.version import (Version,
|
||||||
|
VersionRequirement,
|
||||||
|
VersionParsingError)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
# main class
|
# main class
|
||||||
'Options',
|
'Options',
|
||||||
'Version',
|
'Version',
|
||||||
|
'VersionRequirement',
|
||||||
|
|
||||||
# data class
|
# data class
|
||||||
'FontData',
|
'FontData',
|
||||||
@ -27,6 +30,7 @@ __all__ = [
|
|||||||
'OptionsError',
|
'OptionsError',
|
||||||
'OptionNameNotDefined',
|
'OptionNameNotDefined',
|
||||||
'OptionNotFound',
|
'OptionNotFound',
|
||||||
|
'VersionParsingError',
|
||||||
|
|
||||||
# other
|
# other
|
||||||
'get_type_hints_',
|
'get_type_hints_',
|
||||||
|
@ -15,7 +15,7 @@ import traceback
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import Callable, Dict, List, TYPE_CHECKING
|
from typing import Callable, Dict, List, TYPE_CHECKING, Type
|
||||||
|
|
||||||
# third function
|
# third function
|
||||||
import rtoml
|
import rtoml
|
||||||
@ -23,23 +23,28 @@ import pyglet
|
|||||||
# from pyglet import gl
|
# from pyglet import gl
|
||||||
# from pyglet.gl import glClearColor
|
# from pyglet.gl import glClearColor
|
||||||
# from pyglet.libs.win32 import _user32
|
# from pyglet.libs.win32 import _user32
|
||||||
|
from pyglet.graphics import Group, Batch
|
||||||
from pyglet.window import Window
|
from pyglet.window import Window
|
||||||
from pyglet.window import key, mouse
|
from pyglet.window import key, mouse
|
||||||
|
from pyglet.gui.widgets import TextEntry
|
||||||
|
|
||||||
# Difficult_Rocket function
|
# Difficult_Rocket function
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from Difficult_Rocket.main import Game
|
from Difficult_Rocket.main import Game
|
||||||
|
from Difficult_Rocket import DR_status
|
||||||
from Difficult_Rocket.utils import tools
|
from Difficult_Rocket.utils import tools
|
||||||
from Difficult_Rocket.api.types import Options
|
|
||||||
from Difficult_Rocket.command import line
|
from Difficult_Rocket.command import line
|
||||||
|
from Difficult_Rocket.api.types import Options
|
||||||
from Difficult_Rocket.utils.translate import tr
|
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.api.screen import BaseScreen
|
||||||
from Difficult_Rocket.utils.thread import new_thread
|
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.fps.fps_log import FpsLogger
|
||||||
from Difficult_Rocket.client.guis.widgets import InputBox
|
|
||||||
from Difficult_Rocket.exception.language import LanguageNotFound
|
from Difficult_Rocket.exception.language import LanguageNotFound
|
||||||
from Difficult_Rocket.client.screen import DRScreen, DRDEBUGScreen
|
|
||||||
|
|
||||||
|
logger = logging.getLogger('client')
|
||||||
|
|
||||||
|
|
||||||
class ClientOption(Options):
|
class ClientOption(Options):
|
||||||
@ -51,20 +56,24 @@ class ClientOption(Options):
|
|||||||
resizeable: bool = True
|
resizeable: bool = True
|
||||||
visible: bool = True
|
visible: bool = True
|
||||||
gui_scale: float = 1.0
|
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:
|
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.fps = int(file['runtime']['fps'])
|
||||||
self.width = int(file['window']['width'])
|
self.width = int(file['window']['width'])
|
||||||
self.height = int(file['window']['height'])
|
self.height = int(file['window']['height'])
|
||||||
self.fullscreen = tools.format_bool(file['window']['full_screen'])
|
self.fullscreen = tools.format_bool(file['window']['full_screen'])
|
||||||
self.resizeable = tools.format_bool(file['window']['resizable'])
|
self.resizeable = tools.format_bool(file['window']['resizable'])
|
||||||
self.gui_scale = float(file['window']['gui_scale'])
|
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:
|
class Client:
|
||||||
|
"""
|
||||||
|
客户端
|
||||||
|
"""
|
||||||
def __init__(self, game: "Game", net_mode='local'):
|
def __init__(self, game: "Game", net_mode='local'):
|
||||||
start_time = time.time_ns()
|
start_time = time.time_ns()
|
||||||
# logging
|
# logging
|
||||||
@ -89,6 +98,9 @@ class Client:
|
|||||||
self.logger.debug(tr().client.setup.use_time_ns().format(self.use_time))
|
self.logger.debug(tr().client.setup.use_time_ns().format(self.use_time))
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
|
"""
|
||||||
|
启动客户端
|
||||||
|
"""
|
||||||
DR_runtime.running = True
|
DR_runtime.running = True
|
||||||
self.window.start_game() # 游戏启动
|
self.window.start_game() # 游戏启动
|
||||||
# TODO 写一下服务端启动相关,还是需要服务端啊
|
# TODO 写一下服务端启动相关,还是需要服务端啊
|
||||||
@ -98,6 +110,11 @@ class Client:
|
|||||||
|
|
||||||
|
|
||||||
def pyglet_load_fonts_folder(folder) -> None:
|
def pyglet_load_fonts_folder(folder) -> None:
|
||||||
|
"""
|
||||||
|
递归加载字体文件夹
|
||||||
|
:param folder:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
font_path = Path(folder)
|
font_path = Path(folder)
|
||||||
if not font_path.exists():
|
if not font_path.exists():
|
||||||
font_path.mkdir(parents=True)
|
font_path.mkdir(parents=True)
|
||||||
@ -105,13 +122,48 @@ def pyglet_load_fonts_folder(folder) -> None:
|
|||||||
file_folder_list = os.listdir(folder)
|
file_folder_list = os.listdir(folder)
|
||||||
for obj in file_folder_list:
|
for obj in file_folder_list:
|
||||||
if os.path.isfile(os.path.join(folder, obj)):
|
if os.path.isfile(os.path.join(folder, obj)):
|
||||||
if obj[-4:] == '.ttf':
|
if obj[-4:] == '.ttf' or obj[-4:] == '.otf':
|
||||||
pyglet.font.add_file(os.path.join(folder, obj))
|
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:
|
else:
|
||||||
|
logger.info(f'loading font folder {os.path.join(folder, obj)}')
|
||||||
pyglet_load_fonts_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:
|
def _call_screen_after(func: Callable) -> Callable:
|
||||||
|
"""
|
||||||
|
>>> @_call_screen_after
|
||||||
|
>>> def on_draw(self):
|
||||||
|
>>> pass
|
||||||
|
用于在调用窗口函数后调用子窗口函数 的装饰器
|
||||||
|
:param func: 需要包装的函数
|
||||||
|
:return: 包装后的函数
|
||||||
|
"""
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def warped(self: "ClientWindow", *args, **kwargs):
|
def warped(self: "ClientWindow", *args, **kwargs):
|
||||||
result = func(self, *args, **kwargs)
|
result = func(self, *args, **kwargs)
|
||||||
@ -130,6 +182,14 @@ def _call_screen_after(func: Callable) -> Callable:
|
|||||||
|
|
||||||
|
|
||||||
def _call_screen_before(func: Callable) -> Callable:
|
def _call_screen_before(func: Callable) -> Callable:
|
||||||
|
"""
|
||||||
|
>>> @_call_screen_before
|
||||||
|
>>> def on_draw(self):
|
||||||
|
>>> pass
|
||||||
|
用于在调用窗口函数前调用子窗口函数 的装饰器
|
||||||
|
:param func: 需要包装的函数
|
||||||
|
:return: 包装后的函数
|
||||||
|
"""
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def warped(self: "ClientWindow", *args, **kwargs):
|
def warped(self: "ClientWindow", *args, **kwargs):
|
||||||
for title, a_screen in self.screen_list.items():
|
for title, a_screen in self.screen_list.items():
|
||||||
@ -166,16 +226,16 @@ class ClientWindow(Window):
|
|||||||
self.net_mode = net_mode
|
self.net_mode = net_mode
|
||||||
self.run_input = False
|
self.run_input = False
|
||||||
self.command_list: List[str] = []
|
self.command_list: List[str] = []
|
||||||
# configs
|
# config
|
||||||
self.main_config = tools.load_file('./configs/main.toml')
|
self.main_config = tools.load_file('./config/main.toml')
|
||||||
self.game_config = tools.load_file('./configs/game.config')
|
self.game_config = tools.load_file('./config/game.config')
|
||||||
# FPS
|
# FPS
|
||||||
self.FPS = Decimal(int(self.main_config['runtime']['fps']))
|
self.FPS = Decimal(int(self.main_config['runtime']['fps']))
|
||||||
self.SPF = Decimal('1') / self.FPS
|
self.SPF = Decimal('1') / self.FPS
|
||||||
self.fps_log = FpsLogger(stable_fps=int(self.FPS))
|
self.fps_log = FpsLogger(stable_fps=int(self.FPS))
|
||||||
# batch
|
# batch
|
||||||
self.part_batch = pyglet.graphics.Batch()
|
self.main_batch = Batch()
|
||||||
self.label_batch = pyglet.graphics.Batch()
|
self.main_group = Group(0)
|
||||||
# frame
|
# frame
|
||||||
self.frame = pyglet.gui.Frame(self, order=20)
|
self.frame = pyglet.gui.Frame(self, order=20)
|
||||||
self.M_frame = pyglet.gui.MovableFrame(self, modifier=key.LCTRL)
|
self.M_frame = pyglet.gui.MovableFrame(self, modifier=key.LCTRL)
|
||||||
@ -183,12 +243,11 @@ class ClientWindow(Window):
|
|||||||
# setup
|
# setup
|
||||||
self.setup()
|
self.setup()
|
||||||
# 命令显示
|
# 命令显示
|
||||||
self.command_group = pyglet.graphics.Group(0)
|
self.input_box = TextEntry(x=50, y=30, width=300,
|
||||||
self.input_box = InputBox(x=50, y=30, width=300,
|
batch=self.main_batch, text='', group=Group(1000, parent=self.main_group)) # 实例化
|
||||||
batch=self.label_batch, text='') # 实例化
|
|
||||||
self.input_box.push_handlers(self)
|
self.input_box.push_handlers(self)
|
||||||
self.input_box.set_handler('on_commit', self.on_input)
|
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
|
self.input_box.enabled = True
|
||||||
# 设置刷新率
|
# 设置刷新率
|
||||||
# pyglet.clock.schedule_interval(self.draw_update, float(self.SPF))
|
# pyglet.clock.schedule_interval(self.draw_update, float(self.SPF))
|
||||||
@ -202,12 +261,10 @@ class ClientWindow(Window):
|
|||||||
self.count = 0
|
self.count = 0
|
||||||
|
|
||||||
def setup(self):
|
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()
|
self.load_fonts()
|
||||||
# TODO 读取配置文件,加载不同的屏幕,解耦
|
|
||||||
self.screen_list['DR_debug'] = DRDEBUGScreen(self)
|
self.screen_list['DR_debug'] = DRDEBUGScreen(self)
|
||||||
self.screen_list['DR_main'] = DRScreen(self)
|
self.game.dispatch_mod_event('on_client_start', game=self.game, client=self)
|
||||||
self.game.dispatch_event('on_client_start', game=self.game, client=self)
|
|
||||||
|
|
||||||
def load_fonts(self) -> None:
|
def load_fonts(self) -> None:
|
||||||
fonts_folder_path = self.main_config['runtime']['fonts_folder']
|
fonts_folder_path = self.main_config['runtime']['fonts_folder']
|
||||||
@ -216,33 +273,39 @@ class ClientWindow(Window):
|
|||||||
pyglet_load_fonts_folder(fonts_folder_path)
|
pyglet_load_fonts_folder(fonts_folder_path)
|
||||||
|
|
||||||
def start_game(self) -> None:
|
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:
|
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:
|
except KeyboardInterrupt:
|
||||||
print("==========client stop. KeyboardInterrupt info==========")
|
self.logger.warning("==========client stop. KeyboardInterrupt info==========")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
print("==========client stop. KeyboardInterrupt info end==========")
|
self.logger.warning("==========client stop. KeyboardInterrupt info end==========")
|
||||||
self.dispatch_event("on_close")
|
self.dispatch_event("on_close", 'input')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
@new_thread('window save_info')
|
@new_thread('window save_info')
|
||||||
def save_info(self):
|
def save_info(self):
|
||||||
self.logger.info(tr().client.config.save.start())
|
self.logger.info(tr().client.config.save.start())
|
||||||
config_file: 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']['width'] = self.width
|
||||||
config_file['window']['height'] = self.height
|
config_file['window']['height'] = self.height
|
||||||
config_file['runtime']['language'] = DR_runtime.language
|
config_file['runtime']['language'] = DR_runtime.language
|
||||||
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())
|
self.logger.info(tr().client.config.save.done())
|
||||||
|
|
||||||
"""
|
"""
|
||||||
client api
|
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)
|
self.screen_list[title] = sub_screen(self)
|
||||||
|
|
||||||
|
def remove_sub_screen(self, title: str):
|
||||||
|
self.screen_list.pop(title)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
draws and some event
|
draws and some event
|
||||||
"""
|
"""
|
||||||
@ -254,11 +317,13 @@ class ClientWindow(Window):
|
|||||||
self.fps_log.update_tick(now_FPS, decimal_tick)
|
self.fps_log.update_tick(now_FPS, decimal_tick)
|
||||||
|
|
||||||
@_call_screen_after
|
@_call_screen_after
|
||||||
def on_draw(self, *dt):
|
# def on_draw(self, dt: float): # TODO: wait for pyglet 2.1
|
||||||
while command := self.game.console.get_command():
|
def on_draw(self):
|
||||||
|
while (command := self.game.console.get_command()) is not None:
|
||||||
self.on_command(line.CommandText(command))
|
self.on_command(line.CommandText(command))
|
||||||
pyglet.gl.glClearColor(0.1, 0, 0, 0.0)
|
pyglet.gl.glClearColor(0.1, 0, 0, 0.0)
|
||||||
self.clear()
|
self.clear()
|
||||||
|
# self.draw_update(dt) # TODO: wait for pyglet 2.1
|
||||||
self.draw_update(float(self.SPF))
|
self.draw_update(float(self.SPF))
|
||||||
self.draw_batch()
|
self.draw_batch()
|
||||||
|
|
||||||
@ -281,10 +346,9 @@ class ClientWindow(Window):
|
|||||||
# self.set_location(*self.get_location())
|
# self.set_location(*self.get_location())
|
||||||
print('on hide!')
|
print('on hide!')
|
||||||
|
|
||||||
@_call_screen_after
|
@_call_screen_before
|
||||||
def draw_batch(self):
|
def draw_batch(self):
|
||||||
self.part_batch.draw()
|
self.main_batch.draw()
|
||||||
self.label_batch.draw()
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
command line event
|
command line event
|
||||||
@ -295,15 +359,18 @@ class ClientWindow(Window):
|
|||||||
self.on_command(command_text)
|
self.on_command(command_text)
|
||||||
self.input_box.value = ''
|
self.input_box.value = ''
|
||||||
|
|
||||||
|
def new_command(self):
|
||||||
|
self.game.console.new_command()
|
||||||
|
|
||||||
|
@_call_back(new_command)
|
||||||
@_call_screen_after
|
@_call_screen_after
|
||||||
def on_command(self, command: line.CommandText):
|
def on_command(self, command: line.CommandText):
|
||||||
print(command.find('/'))
|
command.text = command.text.rstrip('\n').rstrip(' ').strip('/')
|
||||||
self.logger.info(tr().window.command.text().format(command))
|
self.logger.info(tr().window.command.text().format(f"|{command.text}|"))
|
||||||
if command.find('stop'):
|
if command.find('stop'):
|
||||||
# self.dispatch_event('on_exit')
|
self.logger.info("command stop!")
|
||||||
print("command stop!")
|
# HUGE THANKS to Discord @nokiyasos for this fix!
|
||||||
pyglet.app.platform_event_loop.stop()
|
pyglet.app.exit()
|
||||||
self.dispatch_event('on_close', 'command') # source = command
|
|
||||||
elif command.find('fps'):
|
elif command.find('fps'):
|
||||||
if command.find('log'):
|
if command.find('log'):
|
||||||
self.logger.debug(self.fps_log.fps_list)
|
self.logger.debug(self.fps_log.fps_list)
|
||||||
@ -322,10 +389,19 @@ class ClientWindow(Window):
|
|||||||
tr._language = lang
|
tr._language = lang
|
||||||
self.logger.info(tr().language_set_to())
|
self.logger.info(tr().language_set_to())
|
||||||
except LanguageNotFound:
|
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.save_info()
|
||||||
|
elif command.find('mods'):
|
||||||
# self.command_tree.parse(command.plain_command)
|
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
|
@_call_screen_after
|
||||||
def on_message(self, message: line.CommandText):
|
def on_message(self, message: line.CommandText):
|
||||||
@ -348,7 +424,7 @@ class ClientWindow(Window):
|
|||||||
...
|
...
|
||||||
|
|
||||||
@_call_screen_after
|
@_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
|
@_call_screen_after
|
||||||
@ -388,7 +464,7 @@ class ClientWindow(Window):
|
|||||||
if symbol == key.ESCAPE and not (modifiers & ~(key.MOD_NUMLOCK |
|
if symbol == key.ESCAPE and not (modifiers & ~(key.MOD_NUMLOCK |
|
||||||
key.MOD_CAPSLOCK |
|
key.MOD_CAPSLOCK |
|
||||||
key.MOD_SCROLLLOCK)):
|
key.MOD_SCROLLLOCK)):
|
||||||
self.dispatch_event('on_close')
|
self.dispatch_event('on_close', 'window')
|
||||||
if symbol == key.SLASH:
|
if symbol == key.SLASH:
|
||||||
self.input_box._set_focus(True)
|
self.input_box._set_focus(True)
|
||||||
self.logger.debug(
|
self.logger.debug(
|
||||||
@ -424,7 +500,7 @@ class ClientWindow(Window):
|
|||||||
|
|
||||||
@_call_screen_before
|
@_call_screen_before
|
||||||
def on_close(self, source: str = 'window') -> None:
|
def on_close(self, source: str = 'window') -> None:
|
||||||
self.game.dispatch_event('on_close', game=self.game, client=self, source=source)
|
self.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_get().format(tr().game[source]()))
|
||||||
self.logger.info(tr().window.game.stop())
|
self.logger.info(tr().window.game.stop())
|
||||||
# self.fps_log.check_list = False
|
# self.fps_log.check_list = False
|
||||||
|
@ -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
|
import typing
|
||||||
|
|
||||||
from pyglet.text import Label
|
from pyglet.text import Label
|
||||||
from pyglet.graphics import Batch, Group
|
|
||||||
from pyglet.clock import get_frequency
|
from pyglet.clock import get_frequency
|
||||||
|
from pyglet.graphics import Batch, Group
|
||||||
|
|
||||||
# Difficult Rocket function
|
# Difficult Rocket function
|
||||||
from Difficult_Rocket.api.types import Fonts
|
from Difficult_Rocket.api.types import Fonts
|
||||||
# from Difficult_Rocket.utils import translate
|
|
||||||
from Difficult_Rocket.api.screen import BaseScreen
|
from Difficult_Rocket.api.screen import BaseScreen
|
||||||
# from Difficult_Rocket.command.tree import CommandTree
|
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from Difficult_Rocket.client import ClientWindow
|
from Difficult_Rocket.client import ClientWindow
|
||||||
|
|
||||||
|
|
||||||
class DRScreen(BaseScreen):
|
|
||||||
def __init__(self, main_window: "ClientWindow"):
|
|
||||||
super().__init__(main_window)
|
|
||||||
|
|
||||||
|
|
||||||
class DRDEBUGScreen(BaseScreen):
|
class DRDEBUGScreen(BaseScreen):
|
||||||
def __init__(self, main_window: "ClientWindow"):
|
def __init__(self, main_window: "ClientWindow"):
|
||||||
super().__init__(main_window)
|
super().__init__(main_window)
|
||||||
@ -55,4 +48,3 @@ class DRDEBUGScreen(BaseScreen):
|
|||||||
|
|
||||||
def on_draw(self, *dt, window: "ClientWindow"):
|
def on_draw(self, *dt, window: "ClientWindow"):
|
||||||
self.main_batch.draw()
|
self.main_batch.draw()
|
||||||
# print(self.window_pointer.try_if_runs)
|
|
||||||
|
@ -44,10 +44,13 @@ class CommandText:
|
|||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
def find(self, text: str) -> bool:
|
def find(self, text: str) -> bool:
|
||||||
find = self.text.find(text)
|
startswith = self.text.startswith(text)
|
||||||
if find != -1:
|
if startswith:
|
||||||
self.text = self.text[find + len(text):]
|
find = self.text.find(text)
|
||||||
return True
|
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
|
return False
|
||||||
|
|
||||||
def re_find(self, text: str) -> Union[str, bool]:
|
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)
|
crash_info = crash_info_handler(info)
|
||||||
if 'crash_report' not in os.listdir('./'):
|
if 'crash_report' not in os.listdir('./'):
|
||||||
os.mkdir('./crash_report')
|
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'
|
filename = f'crash-{date_time}.md'
|
||||||
cache_stream = io.StringIO()
|
cache_stream = io.StringIO()
|
||||||
try:
|
try:
|
||||||
@ -92,16 +92,17 @@ def create_crash_report(info: Optional[str] = None) -> None:
|
|||||||
|
|
||||||
def write_cache(cache_stream, crash_info):
|
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)
|
cache_stream.write(crash_info)
|
||||||
|
|
||||||
|
|
||||||
def write_info_to_cache(cache_stream):
|
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(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'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(markdown_line_handler(f'Running Dir: {Path(os.curdir).resolve()}', level=1))
|
||||||
cache_stream.write(f"\n{DR_runtime.as_markdown()}")
|
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
|
gitee: @shenjackyuanjie
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
import importlib
|
|
||||||
import importlib.util
|
|
||||||
import logging.config
|
import logging.config
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
|
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from pathlib import Path
|
from 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.utils import tools
|
||||||
from Difficult_Rocket.api.types import Options
|
from Difficult_Rocket.api.types import Options
|
||||||
from Difficult_Rocket.utils.translate import tr
|
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.utils.thread import new_thread
|
||||||
from Difficult_Rocket.crash import write_info_to_cache
|
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):
|
class Console(Options):
|
||||||
@ -68,6 +59,9 @@ class Console(Options):
|
|||||||
def get_command(self) -> Optional[str]:
|
def get_command(self) -> Optional[str]:
|
||||||
return self.caches.pop(0) if self.caches else None
|
return self.caches.pop(0) if self.caches else None
|
||||||
|
|
||||||
|
def new_command(self) -> None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class Game(Options):
|
class Game(Options):
|
||||||
name = 'MainGame'
|
name = 'MainGame'
|
||||||
@ -78,75 +72,21 @@ class Game(Options):
|
|||||||
console_class: Console = Console
|
console_class: Console = Console
|
||||||
|
|
||||||
main_config: Dict
|
main_config: Dict
|
||||||
logging_config: Dict
|
|
||||||
logger: logging.Logger
|
logger: logging.Logger
|
||||||
|
|
||||||
mod_module: List["ModInfo"] = []
|
mod_manager: ModManager
|
||||||
|
|
||||||
def init_logger(self) -> None:
|
def dispatch_mod_event(self, event_name: str, *args, **kwargs) -> None:
|
||||||
log_path = self.logging_config['handlers']['file']['filename']
|
self.mod_manager.dispatch_event(event_name, *args, **kwargs)
|
||||||
log_path = f"logs/{log_path.format(time.strftime('%Y-%m-%d %H-%M-%S' , time.gmtime(DR_runtime.start_time_ns / 1000_000_000)))}"
|
|
||||||
mkdir = False
|
|
||||||
if not Path('logs/').is_dir():
|
|
||||||
Path('logs/').mkdir()
|
|
||||||
mkdir = True
|
|
||||||
self.logging_config['handlers']['file']['filename'] = log_path
|
|
||||||
logging.config.dictConfig(self.logging_config)
|
|
||||||
self.logger = logging.getLogger('main')
|
|
||||||
if mkdir:
|
|
||||||
self.logger.info(tr().main.logger.mkdir())
|
|
||||||
|
|
||||||
def init_mods(self) -> None:
|
def init_mods(self) -> None:
|
||||||
"""验证/加载 mod"""
|
"""验证/加载 mod"""
|
||||||
self.mod_module = []
|
from Difficult_Rocket.mod import loader as mod_loader
|
||||||
mods = []
|
mod_loader.logger = logging.getLogger('mod_manager')
|
||||||
mod_path = Path(DR_runtime.mod_path)
|
self.mod_manager = ModManager()
|
||||||
if not mod_path.exists():
|
mod_class = self.mod_manager.load_mods()
|
||||||
self.logger.info(tr().main.mod.find.faild.no_mod_folder())
|
self.mod_manager.init_mods(mod_class)
|
||||||
return
|
self.dispatch_mod_event('on_load', game=self)
|
||||||
# 寻找有效 mod
|
|
||||||
paths = mod_path.iterdir()
|
|
||||||
sys.path.append(DR_runtime.mod_path)
|
|
||||||
for mod_path in paths:
|
|
||||||
try:
|
|
||||||
if mod_path.name == '__pycache__':
|
|
||||||
continue
|
|
||||||
self.logger.info(tr().main.mod.find.start().format(mod_path))
|
|
||||||
if mod_path.is_dir():
|
|
||||||
if importlib.util.find_spec(mod_path.name) is not None:
|
|
||||||
mods.append(mod_path.name)
|
|
||||||
else:
|
|
||||||
self.logger.warning(tr().main.mod.load.faild.info().format(mod_path.name, tr().main.mod.find.faild.no_spec()))
|
|
||||||
elif mod_path.suffix in ('.pyz', '.zip', '.pyd', '.py'):
|
|
||||||
if importlib.util.find_spec(mod_path.name) is not None:
|
|
||||||
mods.append(mod_path.name)
|
|
||||||
except ImportError as e:
|
|
||||||
self.logger.warning(tr().main.mod.find.faild().format(mod_path, e))
|
|
||||||
self.logger.info(tr().main.mod.find.done())
|
|
||||||
# 加载有效 mod
|
|
||||||
module = []
|
|
||||||
for mod in mods:
|
|
||||||
try:
|
|
||||||
self.logger.info(tr().main.mod.load.start().format(mod))
|
|
||||||
mod_module = importlib.import_module(mod)
|
|
||||||
if not hasattr(mod_module, "mod_class"):
|
|
||||||
self.logger.warning(tr().main.mod.load.faild.info().format(mod, tr().main.mod.load.faild.no_mod_class()))
|
|
||||||
del mod_module # 释放内存
|
|
||||||
continue
|
|
||||||
mod_class: type(ModInfo) = mod_module.mod_class
|
|
||||||
mod_class = mod_class()
|
|
||||||
module.append(mod_class)
|
|
||||||
self.logger.info(tr().main.mod.load.info().format(mod_class.mod_id, mod_class.version))
|
|
||||||
except ImportError as e:
|
|
||||||
self.logger.warning(tr().main.mod.load.faild.info().format(mod, e))
|
|
||||||
self.logger.info(tr().main.mod.load.done())
|
|
||||||
self.mod_module = module
|
|
||||||
mod_list = []
|
|
||||||
for mod in module:
|
|
||||||
mod_list.append((mod.mod_id, mod.version))
|
|
||||||
# 调用 on_load
|
|
||||||
self.dispatch_event('on_load', game=self)
|
|
||||||
DR_runtime.DR_Mod_List = mod_list
|
|
||||||
|
|
||||||
def init_console(self) -> None:
|
def init_console(self) -> None:
|
||||||
self.console = self.console_class()
|
self.console = self.console_class()
|
||||||
@ -166,32 +106,15 @@ class Game(Options):
|
|||||||
else:
|
else:
|
||||||
self.client.start()
|
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:
|
def log_env(self) -> None:
|
||||||
cache_steam = StringIO()
|
self.logger.info(f'\n{self.as_markdown()}')
|
||||||
write_info_to_cache(cache_steam)
|
|
||||||
text = cache_steam.getvalue()
|
|
||||||
self.logger.info(text)
|
|
||||||
self.flush_option()
|
|
||||||
config_cache = self.logging_config.copy()
|
|
||||||
self.logging_config = {"logging_config": "too long to show"}
|
|
||||||
self.logger.info(f"\n{self.as_markdown()}")
|
|
||||||
self.logging_config = config_cache
|
|
||||||
|
|
||||||
def setup(self) -> None:
|
def setup(self) -> None:
|
||||||
self.client = client.Client(game=self, net_mode='local')
|
self.client = client.Client(game=self, net_mode='local')
|
||||||
self.server = server.Server(net_mode='local')
|
self.server = server.Server(net_mode='local')
|
||||||
|
|
||||||
def init(self, **kwargs) -> bool:
|
def init(self, **kwargs) -> bool:
|
||||||
|
self.logger = logging.getLogger('main')
|
||||||
self.load_file()
|
self.load_file()
|
||||||
self.setup()
|
self.setup()
|
||||||
self.log_env()
|
self.log_env()
|
||||||
@ -199,9 +122,6 @@ class Game(Options):
|
|||||||
|
|
||||||
def load_file(self) -> bool:
|
def load_file(self) -> bool:
|
||||||
"""加载文件"""
|
"""加载文件"""
|
||||||
self.logging_config = tools.load_file('configs/logger.toml')
|
|
||||||
self.init_logger()
|
|
||||||
self.init_mods()
|
self.init_mods()
|
||||||
self.init_console()
|
self.init_console()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -4,38 +4,203 @@
|
|||||||
# All rights reserved
|
# All rights reserved
|
||||||
# -------------------------------
|
# -------------------------------
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
import logging
|
import logging
|
||||||
|
import traceback
|
||||||
|
import importlib
|
||||||
from pathlib import Path
|
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.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):
|
class ModManager(Options):
|
||||||
name = 'Mod Manager'
|
name = 'Mod Manager'
|
||||||
logger: logging.Logger
|
|
||||||
|
|
||||||
mods_path: List[Path] = [Path('./mods')]
|
mods_path: List[Path] = [Path('./mods')]
|
||||||
|
find_mod_paths: Dict[str, Path] = {}
|
||||||
loaded_mod_modules: Dict[str, ModInfo] = {}
|
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:
|
:return:
|
||||||
"""
|
"""
|
||||||
mods = []
|
for mod in self.loaded_mod_modules.values():
|
||||||
for path in self.mods_path:
|
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():
|
if not path.exists():
|
||||||
path.mkdir(parents=True)
|
path.mkdir(parents=True)
|
||||||
continue
|
continue
|
||||||
for mod in path.iterdir():
|
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:
|
def load_mods(self,
|
||||||
self.logger = logging.getLogger('client')
|
extra_path: Optional[List[Path]] = None,
|
||||||
self.logger.name = 'mod_manager'
|
extra_mod_path: Optional[List[Path]] = None) -> List[type(ModInfo)]:
|
||||||
self.logger.info('Mod Manager init')
|
"""
|
||||||
|
加载所有 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
|
# os.set
|
||||||
self.process_name = 'server process'
|
self.process_name = 'server process'
|
||||||
# config
|
# config
|
||||||
self.config = tools.load_file('configs/main.toml')
|
self.config = tools.load_file('config/main.toml')
|
||||||
# self.dev = Dev
|
# self.dev = Dev
|
||||||
# self.net_mode = net_mode
|
# self.net_mode = net_mode
|
||||||
self.logger.info(tr().server.setup.use_time().format(time.time() - start_time))
|
self.logger.info(tr().server.setup.use_time().format(time.time() - start_time))
|
||||||
|
@ -4,3 +4,10 @@
|
|||||||
# All rights reserved
|
# 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
|
# All rights reserved
|
||||||
# -------------------------------
|
# -------------------------------
|
||||||
|
|
||||||
import warnings
|
import shutil
|
||||||
import traceback
|
import traceback
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import get_type_hints, Type, List, Union, Dict, Any, Callable, Tuple, Optional, TYPE_CHECKING, Iterable
|
from typing import get_type_hints, Type, List, Union, Dict, Any, Callable, Tuple, Optional, TYPE_CHECKING, Iterable
|
||||||
|
|
||||||
__all__ = ['get_type_hints_',
|
__all__ = [
|
||||||
'Options',
|
'get_type_hints_',
|
||||||
'Fonts',
|
'Options',
|
||||||
'FontData',
|
'OptionsError',
|
||||||
'OptionsError',
|
'OptionNotFound',
|
||||||
'OptionNotFound',
|
'OptionNameNotDefined',
|
||||||
'OptionNameNotDefined']
|
'Fonts',
|
||||||
|
'FontData'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def get_type_hints_(cls: Type):
|
def get_type_hints_(cls: Type):
|
||||||
@ -174,15 +176,12 @@ class Options:
|
|||||||
self.cached_options = self.option()
|
self.cached_options = self.option()
|
||||||
return self.cached_options
|
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 列表
|
返回一个可以用于打印的 option 列表
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if longest is None:
|
options = self.flush_option()
|
||||||
options = self.flush_option()
|
|
||||||
else:
|
|
||||||
options = self.str_option(longest)
|
|
||||||
max_len_key = 1
|
max_len_key = 1
|
||||||
max_len_value = 1
|
max_len_value = 1
|
||||||
max_len_value_t = 1
|
max_len_value_t = 1
|
||||||
@ -192,19 +191,61 @@ class Options:
|
|||||||
max_len_key = max(max_len_key, len(key))
|
max_len_key = max(max_len_key, len(key))
|
||||||
max_len_value = max(max_len_value, len(str(value)))
|
max_len_value = max(max_len_value, len(str(value)))
|
||||||
max_len_value_t = max(max_len_value_t, len(str(value_t)))
|
max_len_value_t = max(max_len_value_t, len(str(value_t)))
|
||||||
option_list.append((key, value, value_t))
|
option_list.append([key, value, value_t])
|
||||||
return option_list, max_len_key, max_len_value, max_len_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:
|
def as_markdown(self, longest: Optional[int] = None) -> str:
|
||||||
"""
|
"""
|
||||||
返回一个 markdown 格式的 option 字符串
|
返回一个 markdown 格式的 option 字符串
|
||||||
|
:param longest: 最长的输出长度
|
||||||
:return: markdown 格式的 option 字符串
|
:return: markdown 格式的 option 字符串
|
||||||
"""
|
"""
|
||||||
value = self.option_with_len(longest)
|
value = self.option_with_len()
|
||||||
cache = StringIO()
|
cache = StringIO()
|
||||||
option_len = max(value[1], len('Option'))
|
option_len = max(value[1], len('Option'))
|
||||||
value_len = max(value[2], len('Value'))
|
value_len = max(value[2], len('Value'))
|
||||||
value_type_len = max(value[3], len('Value Type'))
|
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(
|
cache.write(
|
||||||
f"| Option{' ' * (option_len - 3)}| Value{' ' * (value_len - 2)}| Value Type{' ' * (value_type_len - 7)}|\n")
|
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')
|
cache.write(f'|:{"-" * (option_len + 3)}|:{"-" * (value_len + 3)}|:{"-" * (value_type_len + 3)}|\n')
|
||||||
|
@ -16,22 +16,21 @@ import sys
|
|||||||
import time
|
import time
|
||||||
import math
|
import math
|
||||||
import json
|
import json
|
||||||
|
import rtoml
|
||||||
import logging
|
import logging
|
||||||
import configparser
|
import configparser
|
||||||
|
|
||||||
from typing import Union
|
from pathlib import Path
|
||||||
|
from typing import Union, Optional
|
||||||
from xml.etree import ElementTree
|
from xml.etree import ElementTree
|
||||||
|
|
||||||
import rtoml
|
|
||||||
|
|
||||||
from defusedxml.ElementTree import parse
|
from defusedxml.ElementTree import parse
|
||||||
|
|
||||||
from Difficult_Rocket.exception.unsupport import NoMoreJson5
|
from Difficult_Rocket.exception.unsupport import NoMoreJson5
|
||||||
|
|
||||||
# logger
|
# 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}',
|
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}'}
|
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,
|
def load_file(file_name: Union[str, Path],
|
||||||
stack: Union[str, list, dict, None] = None,
|
stack: Optional[Union[str, list, dict]] = None,
|
||||||
raise_error: bool = True,
|
raise_error: Optional[bool] = True,
|
||||||
encoding: str = 'utf-8') -> Union[dict, ElementTree.ElementTree]:
|
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:] # 从最后一个.到末尾 (截取文件格式)
|
f_type = file_name[file_name.rfind('.') + 1:] # 从最后一个.到末尾 (截取文件格式)
|
||||||
get_file = NotImplementedError('解析失败,请检查文件类型/文件内容/文件是否存在!')
|
get_file = NotImplementedError('解析失败,请检查文件类型/文件内容/文件是否存在!')
|
||||||
try:
|
try:
|
||||||
@ -98,7 +99,7 @@ def save_dict_file(file_name: str,
|
|||||||
|
|
||||||
|
|
||||||
# main config
|
# 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):
|
def get_At(name, in_xml, need_type=str):
|
||||||
|
@ -14,11 +14,13 @@ gitee: @shenjackyuanjie
|
|||||||
import os
|
import os
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Union, Tuple, Any, List, Dict, Hashable, Optional
|
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.utils import tools
|
||||||
|
from Difficult_Rocket.runtime import DR_runtime
|
||||||
from Difficult_Rocket.exception.language import (LanguageNotFound,
|
from Difficult_Rocket.exception.language import (LanguageNotFound,
|
||||||
TranslateKeyNotFound)
|
TranslateKeyNotFound)
|
||||||
|
|
||||||
@ -138,12 +140,12 @@ class Translates:
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if not any(not x[0] for x in self._get_list):
|
if not any(not x[0] for x in self._get_list):
|
||||||
return self._value
|
return str(self._value)
|
||||||
if self._config.crack_normal:
|
if self._config.crack_normal:
|
||||||
return f'{".".join(f"{gets[1]}({gets[0]})" for gets in self._get_list)}'
|
return f'{".".join(f"{gets[1]}({gets[0]})" for gets in self._get_list)}'
|
||||||
elif self._config.insert_crack:
|
elif self._config.insert_crack:
|
||||||
return f'{self._value}.{".".join(gets[1] for gets in self._get_list if not gets[0])}'
|
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:
|
class Tr:
|
||||||
@ -152,15 +154,20 @@ class Tr:
|
|||||||
GOOD
|
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
|
诶嘿,我抄的MCDR
|
||||||
:param language: Tr 所使用的的语言
|
:param language: Tr 所使用的的语言
|
||||||
:param config: 配置
|
:param config: 配置
|
||||||
|
:param lang_path: 语言文件夹路径
|
||||||
"""
|
"""
|
||||||
self.language_name = language if language is not None else DR_runtime.language
|
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.language_path = lang_path if lang_path is not None else Path('assets/lang')
|
||||||
self.default_translate: Dict = tools.load_file(f'configs/lang/{DR_status.default_language}.toml')
|
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.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())
|
self.translates_cache = Translates(value=self.translates, config=self.default_config.copy())
|
||||||
|
|
||||||
@ -184,11 +191,11 @@ class Tr:
|
|||||||
if lang == ' ' or lang == '':
|
if lang == ' ' or lang == '':
|
||||||
raise LanguageNotFound('Can not be empty')
|
raise LanguageNotFound('Can not be empty')
|
||||||
lang = lang or self.language_name
|
lang = lang or self.language_name
|
||||||
if not os.path.exists(f'./configs/lang/{lang}.toml'):
|
if not os.path.exists(f'{self.language_path}/{lang}.toml'):
|
||||||
print(f"lang: {os.path.exists(f'./configs/lang/{lang}.toml')} language = {lang} {self.language_name=}")
|
print(f"lang: {os.path.exists(f'{self.language_path}/{lang}.toml')} language = {lang} {self.language_name=}")
|
||||||
raise LanguageNotFound(lang)
|
raise LanguageNotFound(lang)
|
||||||
self.translates: Dict[str, Union[str, Dict]] = tools.load_file(f'configs/lang/{lang}.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'configs/lang/{DR_runtime.default_language}.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.translates_cache = Translates(value=self.translates, config=self.default_config.copy())
|
||||||
self.language_name = lang
|
self.language_name = lang
|
||||||
DR_runtime.language = self.language_name
|
DR_runtime.language = self.language_name
|
||||||
|
54
README.md
@ -1,3 +1,5 @@
|
|||||||
|
<div style="text-align: center;">
|
||||||
|
|
||||||
# Difficult Rocket
|
# Difficult Rocket
|
||||||
|
|
||||||
中文 | [English](./docs/README-en.md)
|
中文 | [English](./docs/README-en.md)
|
||||||
@ -9,22 +11,28 @@
|
|||||||
|
|
||||||
## 请注意 这个仓库未来只会发布 `DR SDK` 的更新 `DR game` 的更新会在 [这里](https://github.com/shenjackyuanjie/DR-game) 发布
|
## 请注意 这个仓库未来只会发布 `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>
|
<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/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/编写于_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)
|
[![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)
|
[关于版本号的说明](./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/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.2.0-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.3-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)
|
[![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)
|
## English README please look [here](./docs/README-en.md)
|
||||||
|
|
||||||
> 这是一个用Python制作的类Simple Rocket游戏(简称:火箭模拟器)
|
> 这是一个用Python制作的类Simple Rocket游戏(简称:火箭模拟器)
|
||||||
@ -42,27 +50,27 @@
|
|||||||
## 环境需求 (测试过的 / 开发平台)
|
## 环境需求 (测试过的 / 开发平台)
|
||||||
|
|
||||||
- `开发平台 1 - Windows 10 x64 22H2`
|
- `开发平台 1 - Windows 10 x64 22H2`
|
||||||
- Python `3.8.10`
|
- Python `3.8.10` / `3.10.11`
|
||||||
- pillow `9.5.0`
|
- pillow `9.5.0`
|
||||||
- psutil `5.9.5`
|
- psutil `5.9.5`
|
||||||
- rtoml `0.9.0`
|
- rtoml `0.9.0`
|
||||||
- tomlkit `0.11.7`
|
- tomlkit `0.11.8`
|
||||||
- defusedxml `0.7.1`
|
- defusedxml `0.7.1`
|
||||||
- objprint `0.2.2`
|
- objprint `0.2.2`
|
||||||
- viztracer `0.15.6`
|
- viztracer `0.15.6`
|
||||||
- vizplugins `0.1.3`
|
- vizplugins `0.1.3`
|
||||||
- nuitka `1.5.6`
|
- nuitka `1.6.6`
|
||||||
- ordered-set `4.1.0`
|
- ordered-set `4.1.0`
|
||||||
- imageio `2.27.0`
|
- imageio `2.31.0`
|
||||||
- wheel `0.40.0`
|
- wheel `0.40.0`
|
||||||
- setuptools `67.6.1`
|
- setuptools `67.8.0`
|
||||||
- setuptools-rust `1.5.2`
|
- setuptools-rust `1.6.0`
|
||||||
- `AMD R5 5600X`
|
- `AMD R5 5600X`
|
||||||
- `AMD RX 550 4G`
|
- `AMD RX 550 4G`
|
||||||
|
|
||||||
## 需要的 Python 模块
|
## 需要的 Python 模块
|
||||||
|
|
||||||
- `pyglet` (已经内置 V2.0.5 路径:`./libs/pyglet`)
|
- `pyglet` (已经内置 V2.0.8 路径:`./libs/pyglet`)
|
||||||
- `xmltodict` (已经内置 V0.12.0 路径:`./libs/xmltodict`)
|
- `xmltodict` (已经内置 V0.12.0 路径:`./libs/xmltodict`)
|
||||||
- `pyperclip` (已经内置 V1.8.2 路径: `./libs/pyperclip`)
|
- `pyperclip` (已经内置 V1.8.2 路径: `./libs/pyperclip`)
|
||||||
|
|
||||||
@ -73,28 +81,30 @@
|
|||||||
# DR contributing
|
# DR contributing
|
||||||
|
|
||||||
# for images
|
# 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
|
# for sys info
|
||||||
psutil >= 5.9.5
|
psutil >= 5.9.5
|
||||||
|
|
||||||
# for files
|
# for files
|
||||||
rtoml >= 0.9.0
|
rtoml >= 0.9.0
|
||||||
tomlkit >= 0.11.7
|
tomlkit >= 0.11.8
|
||||||
defusedxml >= 0.7.1
|
defusedxml >= 0.7.1
|
||||||
|
|
||||||
# for debug
|
# for debug
|
||||||
objprint >= 0.2.2
|
objprint >= 0.2.2
|
||||||
viztracer >= 0.15.6
|
viztracer >= 0.15.6; platform_python_implementation != "PyPy"
|
||||||
vizplugins >= 0.1.3
|
vizplugins >= 0.1.3; platform_python_implementation != "PyPy"
|
||||||
|
|
||||||
# for compile
|
# for compile
|
||||||
nuitka >= 1.5.6
|
nuitka >= 1.7.5
|
||||||
ordered-set >= 4.1.0
|
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
|
wheel >= 0.40.0
|
||||||
setuptools >= 67.6.1
|
setuptools >= 67.8.0
|
||||||
setuptools-rust >= 1.5.2
|
setuptools-rust >= 1.6.0
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 感谢
|
## 感谢
|
||||||
@ -104,8 +114,8 @@ setuptools-rust >= 1.5.2
|
|||||||
- `tomlkit` / `rtoml` : toml 解析器
|
- `tomlkit` / `rtoml` : toml 解析器
|
||||||
- `xmltodict`: xml 与 dict 转换器
|
- `xmltodict`: xml 与 dict 转换器
|
||||||
- `pyperclip`: 剪贴板!
|
- `pyperclip`: 剪贴板!
|
||||||
- `rapier2d`: 物理模拟引擎
|
- [rapier2d](https://rapier.rs/) : 物理模拟引擎
|
||||||
- `pyo3`: Rust Python 扩展
|
- [pyo3](https://pyo3.rs/main): Rust Python 扩展
|
||||||
|
|
||||||
- 主要贡献者
|
- 主要贡献者
|
||||||
- [@Rayawa](https://github.com/Rayawa) : 文档矫正 & 翻译部分 lang
|
- [@Rayawa](https://github.com/Rayawa) : 文档矫正 & 翻译部分 lang
|
||||||
@ -113,6 +123,8 @@ setuptools-rust >= 1.5.2
|
|||||||
- [@Billchyi](https://github.com/Billchyi) : 文档矫正
|
- [@Billchyi](https://github.com/Billchyi) : 文档矫正
|
||||||
- [@MSDNicrosoft](https://github.com/MSDNicrosoft) : 优化代码
|
- [@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">
|
<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"/>
|
<Engine fuel="0.000000"/>
|
||||||
</Part>
|
</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">
|
<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"/>
|
<Engine fuel="0.000000"/>
|
||||||
</Part>
|
</Part>
|
@ -20,15 +20,26 @@ logger.logfile_level = "Log file record level : "
|
|||||||
logger.logfile_fmt = "Log file record format : "
|
logger.logfile_fmt = "Log file record format : "
|
||||||
logger.logfile_datefmt = "Log file date format : "
|
logger.logfile_datefmt = "Log file date format : "
|
||||||
game_start.at = "Game MainThread start at: {}"
|
game_start.at = "Game MainThread start at: {}"
|
||||||
mod.find.start = "Checking Mod: {}"
|
|
||||||
mod.find.faild.no_spec = "importlib can't find spec"
|
[mod]
|
||||||
mod.find.faild.no_mod_folder = "Can't find mod folder"
|
list = "Mod list: "
|
||||||
mod.find.done = "All Mod checked"
|
find.finded = "Mod founded: {}"
|
||||||
mod.load.start = "Loading Mod: {}"
|
load.start = "Loading Mod in path {}"
|
||||||
mod.load.info = "mod id: {} version: {}"
|
load.use_time = "Mod loading has used: {} second"
|
||||||
mod.load.faild.info = "Mod load failed: {} error info: {}"
|
load.done = "All Mod loaded"
|
||||||
mod.load.faild.no_mod_class = "Can't find Mod class"
|
load.loading = "Loading Mod: {}"
|
||||||
mod.load.done = "All Mod loaded"
|
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]
|
[client]
|
||||||
setup.start = "Client start loading"
|
setup.start = "Client start loading"
|
||||||
@ -54,8 +65,6 @@ text.input = "input text \"{}\""
|
|||||||
text.new_line = "new line"
|
text.new_line = "new line"
|
||||||
text.motion = "text move {}"
|
text.motion = "text move {}"
|
||||||
text.motion_select = "text select {}"
|
text.motion_select = "text select {}"
|
||||||
command.text = "input command: {}"
|
|
||||||
message.text = "input message: {}"
|
|
||||||
libs.local = "using local pyglet, version: {}"
|
libs.local = "using local pyglet, version: {}"
|
||||||
libs.outer = "using global pyglet, version: {}\n(may cause bug)"
|
libs.outer = "using global pyglet, version: {}\n(may cause bug)"
|
||||||
fonts.found = "found fonts in font lib: {}"
|
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_get = "Received closing commands from {}, game closing"
|
||||||
game.stop = "game closing, saving data……"
|
game.stop = "game closing, saving data……"
|
||||||
game.end = "game closed"
|
game.end = "game closed"
|
||||||
|
command.text = "input command: {}"
|
||||||
|
message.text = "input message: {}"
|
||||||
|
command.mods.reload.no_mod_id = "no mod id specified"
|
||||||
|
|
||||||
[server]
|
[server]
|
||||||
setup.start = "Server start loading"
|
setup.start = "Server start loading"
|
||||||
@ -71,17 +83,6 @@ os.pid_is = "Server PID: {} PPID: {}"
|
|||||||
|
|
||||||
[game]
|
[game]
|
||||||
input = "console"
|
input = "console"
|
||||||
command = "in game commands"
|
|
||||||
window = "window"
|
window = "window"
|
||||||
|
command = "in game commands"
|
||||||
require_DR_rs = "require DR_rs module"
|
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_fmt = "日志文件记录格式:"
|
||||||
logger.logfile_datefmt = "日志文件日期格式:"
|
logger.logfile_datefmt = "日志文件日期格式:"
|
||||||
game_start.at = "游戏主线程开始于:"
|
game_start.at = "游戏主线程开始于:"
|
||||||
mod.find.start = "正在校验 Mod: {}"
|
|
||||||
mod.find.faild.no_spec = "importlib 无法找到 spec"
|
[mod]
|
||||||
mod.find.faild.no_mod_folder = "没有找到 Mod 文件夹"
|
list = "Mod 列表: "
|
||||||
mod.find.done = "所有 Mod 校验完成"
|
find.finded = "找到 Mod: {}"
|
||||||
mod.load.start = "正在加载 Mod: {}"
|
load.start = "开始加载路径 {} 下的 Mod"
|
||||||
mod.load.info = "mod id: {} 版本号: {}"
|
load.use_time = "Mod 加载消耗时间: {} 秒"
|
||||||
mod.load.faild.info = "Mod 加载失败: {} 错误信息: {}"
|
load.done = "所有 Mod 加载完成"
|
||||||
mod.load.faild.no_mod_class = "没有找到 Mod 类"
|
load.loading = "正在加载 Mod: {}"
|
||||||
mod.load.done = "所有 Mod 加载完成"
|
load.faild.error = "Mod 加载失败: {} 错误信息: {}"
|
||||||
mod.event.error = "Mod 事件 {} 发生错误 {} 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]
|
[client]
|
||||||
setup.start = "客户端加载开始"
|
setup.start = "客户端加载开始"
|
||||||
@ -55,8 +65,6 @@ text.input = "输入字符 \"{}\""
|
|||||||
text.new_line = "换行"
|
text.new_line = "换行"
|
||||||
text.motion = "光标移动 {}"
|
text.motion = "光标移动 {}"
|
||||||
text.motion_select = "光标选择 {}"
|
text.motion_select = "光标选择 {}"
|
||||||
command.text = "输入命令: {}"
|
|
||||||
message.text = "输入信息: {}"
|
|
||||||
libs.local = "正在使用本地 pyglet 库 版本为: {}"
|
libs.local = "正在使用本地 pyglet 库 版本为: {}"
|
||||||
libs.outer = "正在使用全局 pyglet 库 版本为: {}\n(可能会造成bug,因为本地库版本为2.0dev9)"
|
libs.outer = "正在使用全局 pyglet 库 版本为: {}\n(可能会造成bug,因为本地库版本为2.0dev9)"
|
||||||
fonts.found = "在字体列表中找到以下字体库: {}"
|
fonts.found = "在字体列表中找到以下字体库: {}"
|
||||||
@ -64,6 +72,9 @@ fonts.load = "正在加载字体: {}"
|
|||||||
game.stop_get = "从{}传入关闭指令,关闭游戏中"
|
game.stop_get = "从{}传入关闭指令,关闭游戏中"
|
||||||
game.stop = "游戏正在关闭,保存数据中···"
|
game.stop = "游戏正在关闭,保存数据中···"
|
||||||
game.end = "游戏已经关闭"
|
game.end = "游戏已经关闭"
|
||||||
|
command.text = "输入命令: {}"
|
||||||
|
message.text = "输入信息: {}"
|
||||||
|
command.mods.reload.no_mod_id = "没有指定 mod id"
|
||||||
|
|
||||||
[server]
|
[server]
|
||||||
setup.start = "服务端开始加载"
|
setup.start = "服务端开始加载"
|
||||||
@ -72,18 +83,6 @@ os.pid_is = "服务端 PID: {} PPID: {}"
|
|||||||
|
|
||||||
[game]
|
[game]
|
||||||
input = "控制台"
|
input = "控制台"
|
||||||
command = "游戏内命令行"
|
|
||||||
window = "窗口"
|
window = "窗口"
|
||||||
|
command = "游戏内命令行"
|
||||||
require_DR_rs = "需要 DR_rs 模块"
|
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 |