Compare commits

...

205 Commits

Author SHA1 Message Date
5cb642ea3c
az 2023-08-21 01:24:25 +08:00
2e9e3afc39
改进 options 修复了无限循环的问题 2023-08-21 00:43:00 +08:00
2e509575cd
Options! 2023-08-20 21:42:46 +08:00
33d5788826
better widget 2023-08-20 21:42:38 +08:00
6dbffb322d
improve widget 2023-08-20 21:42:25 +08:00
bff9649ca9
继续干活 2023-08-20 02:01:15 +08:00
5f335fa9b5
button gogogo 2023-08-20 01:52:22 +08:00
4b639c37e0
do not use nightly 2023-08-19 20:22:04 +08:00
cc5dec9122
update builder 2023-08-19 20:15:35 +08:00
05ac9871da
sync pyglet 2023-08-19 20:15:25 +08:00
dd5dca1099
nope, use lto=false 2023-08-17 20:47:55 +08:00
804dbfc992
use lto=yes on github? 2023-08-17 20:31:49 +08:00
8e494faa63
improve camera 2023-08-16 16:25:47 +08:00
42eef6743d
weirdddd 2023-08-15 23:30:34 +08:00
6fefb4460b
weird 2023-08-15 23:30:25 +08:00
8dfa5b230a
sync pyglet 2023-08-15 23:29:49 +08:00
726ba4ac17
add some magic from OpenGL 2023-08-13 01:01:15 +08:00
ede6f9ff6e
add back Mat4 2023-08-13 00:21:22 +08:00
d6f0caf2ae
remove zoomx and zoomy 2023-08-12 23:34:17 +08:00
d398ed1af9
opengl based render! 2023-08-12 23:32:43 +08:00
e888ddfdad
add readme info 2023-08-12 20:20:18 +08:00
82111be30b
add feature for dr rs 2023-08-12 02:15:53 +08:00
39379c1621
Merge pull request #52 from shenjackyuanjie/dr_game/rust_render
Dr game/rust render
2023-08-12 01:55:12 +08:00
3595f98dc1
Merge pull request #51 from shenjackyuanjie/feature/group_camera
Feature/group camera
2023-08-12 01:54:22 +08:00
aa8af9ebef
DR sdk 0.8.7.0 2023-08-12 01:49:01 +08:00
692483320b
fetch up with DR 2023-08-12 01:20:24 +08:00
09e386e0fe
CenterGroupCamera! 2023-08-12 00:49:46 +08:00
55e83b708f
go for group cam 2023-08-12 00:28:31 +08:00
7278368b4c
sync pyglet 2023-08-09 12:29:18 +08:00
a2f12c2e77
fix little issue DR game 0.3.2.1 2023-08-09 11:55:26 +08:00
47d2629610
DR rs 0.2.21.0 2023-08-08 21:34:20 +08:00
e658fa3188
remove useless 2023-08-08 01:33:12 +08:00
dd7038272c
Merge pull request #50 from shenjackyuanjie/dr_game/rust_render
Dr game/rust render
2023-08-06 01:24:50 +08:00
f1172c30be
update pyglet 2023-08-03 20:28:58 +08:00
6c68da7edf
some aaaded 2023-07-31 16:42:13 +08:00
951784cf64
啊? 2023-07-28 14:34:31 +08:00
6887660ee1
sync pyglet 2023-07-28 14:29:50 +08:00
3df29bee64
sr1 ship improve 2023-07-27 01:13:20 +08:00
b70705fb67
remove glViewPort 2023-07-26 17:22:57 +08:00
ce0fe298e6
some test 2023-07-25 19:54:51 +08:00
0c5647fee2
some imporvement 2023-07-24 21:34:46 +08:00
253e3b62fb
some changes 2023-07-24 12:22:59 +08:00
640cc27e34
Merge branch 'dr_game/gen_img' 2023-07-24 10:30:19 +08:00
c14786e216
for encoding error 2023-07-24 10:30:10 +08:00
364924a7b7
fixed #47 fixed #48 fixed #49
create #50 #51(
2023-07-24 10:07:17 +08:00
893872c012
fix #47 and fix #48 2023-07-24 09:56:52 +08:00
2ad60e398c
do stuf 2023-07-24 09:46:19 +08:00
24f17f6d4f
#47 fixed 2023-07-24 09:46:11 +08:00
e997633cc5
add raise for SR1Ship 2023-07-24 09:45:40 +08:00
7fed4032e0
adapt for rust render 2023-07-23 11:03:22 +08:00
ad83def45e
Merge branch 'dr_game/rust_render' into dr_game/gen_img 2023-07-23 03:13:00 +08:00
cc5a07904d
Merge branch 'dr_game/gen_img' into dr_game/rust_render 2023-07-23 02:34:36 +08:00
3629bcf163
update~ 2023-07-23 02:25:39 +08:00
5cf40fcede
update denpendecy 2023-07-23 02:25:24 +08:00
81b770e5fc
add style in readme 2023-07-22 18:24:47 +08:00
ae9700e96d
update dr rs 2023-07-21 23:10:57 +08:00
204115d7c7
Merge branch 'main' into dr_game/rust_render
# Conflicts:
#	mods/dr_game/sr1_ship.py
2023-07-21 21:38:26 +08:00
cd0d9071e2
fix #46 2023-07-21 20:44:44 +08:00
7a1b10becc
statch 2023-07-21 20:43:45 +08:00
f10d09ddba
format data 2023-07-21 20:22:23 +08:00
ef73925b0a
enable render 2023-07-21 20:09:24 +08:00
5406e7f5c7
dr rs compat 2023-07-21 18:33:15 +08:00
505d68c67b
update dr rs 2023-07-21 18:32:59 +08:00
d22a5389e7
ready for rust render 2023-07-21 16:00:03 +08:00
42dfd60ce9
sync pyglet update 2023-07-21 13:10:34 +08:00
95644b1f3a
update dr game and dr rs 2023-07-21 13:10:21 +08:00
e411f25e8c
0.8.6.1 2023-07-21 08:31:09 +08:00
6865578237
bump version of dr rs 2023-07-21 08:29:17 +08:00
b02f0f3852
Merge pull request #45 from shenjackyuanjie/dr_game/dr_rs_requirement
update dr_rs dependecy
2023-07-21 08:07:11 +08:00
b2af672f96
update dr_rs dependecy 2023-07-21 08:06:24 +08:00
17180e29b3
move fonts 2023-07-17 20:00:03 +08:00
f5a5eb1758
build for release 2023-07-17 19:29:33 +08:00
12a69f9076
Merge pull request #41 from shenjackyuanjie/feature/assets
Feature/assets
2023-07-16 02:27:27 +08:00
2da10160fe
fix CI 2023-07-16 02:24:23 +08:00
366e02d992
update dependecy 2023-07-16 02:13:03 +08:00
5bdf086190
update: 0.8.6.0 2023-07-16 02:00:29 +08:00
eb12243e55
some update 2023-07-16 00:59:24 +08:00
8e9589e378
Merge pull request #43 from shenjackyuanjie/bugfix/crash-timezone
fix time zone in crash.py
2023-07-16 00:33:33 +08:00
04ab2e3965
fix build 2023-07-16 00:32:28 +08:00
f3c2540dae
clean up textures 2023-07-16 00:21:31 +08:00
2155ad7045
fix time zone in crash.py 2023-07-16 00:20:11 +08:00
d57da4c7ff
go for it! 2023-07-16 00:06:17 +08:00
ea6bc528b1
rua 2023-07-15 23:37:14 +08:00
df52f87bba
add custom part 2023-07-15 23:36:41 +08:00
a9b6422e89
update build version 2023-07-15 21:55:46 +08:00
112f83b79b
add "pwsh" 2023-07-14 00:31:24 +08:00
d9ef5d4070
update dependecy for mdbook building 2023-07-14 00:22:50 +08:00
1883d5d2df
add more 2023-07-14 00:21:32 +08:00
50461e3a46
test more! 2023-07-14 00:20:10 +08:00
259440290a
some more? 2023-07-13 23:34:06 +08:00
728ae0c41c
gogogo 2023-07-13 23:29:26 +08:00
7a8db98787 change some stuf 2023-07-13 23:04:20 +08:00
98106ccd08 sync pyglet 2023-07-13 19:38:36 +08:00
10b89a1919 build! go!~ 2023-07-12 16:59:46 +08:00
226f8aacbe logs 2023-07-12 16:48:18 +08:00
9d367b2256 update sr1 ship 2023-07-12 16:46:32 +08:00
70f2a30b88 sync pyglet 2023-07-12 16:11:06 +08:00
5e6fee7353
add space 2023-07-12 12:03:18 +08:00
d844e0ce0a add mod list display 2023-07-12 12:01:21 +08:00
565d568e44
Merge pull request #39 from erduotong/main
update DR&DR-start.py
2023-07-11 18:57:02 +08:00
DESKTOP-UL157US\13568
7792abc243 update DR&DR-start.py 2023-07-11 18:54:26 +08:00
DESKTOP-UL157US\13568
b3385d1840 update DR&DR-start.py 2023-07-11 18:34:43 +08:00
75d5494b6f remove console 2023-07-11 18:19:51 +08:00
cd04d9b296
Merge pull request #38 from erduotong/main
Update DR-start.py
2023-07-11 18:18:53 +08:00
erduotong
86d298109e
Update DR-start.py 2023-07-11 18:13:40 +08:00
ca8f5a984a remove replace back console 2023-07-11 00:17:57 +08:00
83b45f85fb dr rs 0.2.15.2 2023-07-11 00:16:38 +08:00
ddf10cccf1 docs bump more! 2023-07-08 11:45:18 +08:00
720b48d669 HUGE THANKS FOR REMINDING 2023-07-07 20:01:44 +08:00
539afd73b9 replace static option to self attr 2023-07-07 20:01:35 +08:00
086bd035e7 sync pyglet update (typing 2023-07-07 17:13:42 +08:00
a46b46ead6 sync pyglet update (shape) 2023-07-06 18:36:42 +08:00
f87ae43a10 sync pyglet update 2023-07-05 19:44:30 +08:00
bb83fbd117 sync pyglet 2023-07-02 21:18:37 +08:00
9b72f437a5 use store view and update some dr game 2023-07-02 20:16:21 +08:00
9107621fb8 add clear box line list 2023-07-02 15:58:49 +08:00
61b45fac7f add group for sr1 render 2023-07-02 15:54:50 +08:00
5958a14d47 more! 2023-07-02 01:48:31 +08:00
c1bdda5f03 0.2.15.1 2023-07-02 01:47:28 +08:00
11c2e79141 0.3.1.1 dr rs 2023-07-01 23:13:25 +08:00
c572fbd72a some fix 2023-07-01 21:57:54 +08:00
dcd727e690 bump DR to 0.8.5.1 and DR game 0.3.1.0 2023-07-01 20:50:00 +08:00
8ee315beb7 bump build file version 2023-07-01 19:24:19 +08:00
26d22f9e82 add more info to nuitka build 2023-07-01 00:51:26 +08:00
f112b654c4 upload more! 2023-07-01 00:34:54 +08:00
a190fd0b31 add rename 2023-07-01 00:25:41 +08:00
e0d8c0b0d7 add change lok 2023-06-30 23:56:31 +08:00
6f84e24fbb update require and cargo.lock 2023-06-30 23:50:49 +08:00
6ad0d78b5a use pyglet.TextEntry 2023-06-30 22:59:40 +08:00
0baf74492a remove useless 2023-06-27 01:06:09 +08:00
1e342ce0a8 sync pyget 2023-06-27 01:05:51 +08:00
8d3e14fc39
Merge pull request #36 from shenjackyuanjie/feature/reload_mod
Feature/reload mod
2023-06-26 10:47:09 +08:00
3e54946eeb update requirement and version DR 0.8.5.0 2023-06-26 10:46:43 +08:00
8c91198e87 dr sdk 0.8.4.1 2023-06-26 10:30:27 +08:00
94b8869506 reload ! 2023-06-26 10:14:28 +08:00
55d21b6b02 dr game 0.2.1.0 2023-06-26 01:39:16 +08:00
c4a1807033 add api dr rs 0.2.15.0 2023-06-26 01:28:53 +08:00
bad02add7c add activated for dock1 2023-06-26 01:15:29 +08:00
11e52083a6 fix bug and improve output 2023-06-26 01:03:31 +08:00
c20f4cd85f DR rs 0.2.14.0 2023-06-26 00:37:09 +08:00
0a264c603d update dr rs 2023-06-25 21:21:18 +08:00
5c299f4571 0213 2023-06-25 19:32:00 +08:00
6efdaaa5bc add compat for DR rs 2023-06-25 19:31:48 +08:00
c447374629 add ignore to generated file 2023-06-25 19:25:11 +08:00
482e607d0f docs update 2023-06-25 15:49:34 +08:00
c51b86ba9b
Merge pull request #35 from shenjackyuanjie/feature/py_camera
Feature/py camera
2023-06-25 15:45:53 +08:00
45cfa76d24 upadte dr game 2023-06-25 15:42:49 +08:00
67a9dddb30 add python camera 2023-06-25 15:00:37 +08:00
bbdc5d98ac add some xml test 2023-06-24 21:16:32 +08:00
85c2e4b21c rua 2023-06-23 01:12:33 +08:00
43966ec573 sync pyglet update 2023-06-23 00:21:59 +08:00
f1abeb430e bump version of pyglet 2023-06-22 20:16:24 +08:00
1b29360701 sync last commit before release 2023-06-22 20:13:59 +08:00
c55eaffa5d ready to sync pyglet 2023-06-22 20:13:26 +08:00
8b490aac17 update docs 2023-06-22 14:52:10 +08:00
ab8ff84a5e compat en-us 2023-06-22 14:16:47 +08:00
150e2a03ce 0840 2023-06-22 13:54:55 +08:00
b8170f5d63 0840 not 0831 2023-06-22 13:52:19 +08:00
9a18346763 docs 2023-06-22 13:50:21 +08:00
b2ac6fdda5 args 2023-06-22 13:50:17 +08:00
5f3642fd5c lang docs 2023-06-22 13:37:28 +08:00
c735b9b336 docs 2023-06-22 13:35:41 +08:00
996fb72b54 就,天天重构,淦! 2023-06-22 13:34:14 +08:00
226fcbfce8
Merge pull request #34 from shenjackyuanjie/dr_game/phy_simluation (first part)
first part for phy simluation
2023-06-22 02:11:47 +08:00
420e643e1f cargo fmt 2023-06-22 02:10:53 +08:00
97e0a569bb on resize 2023-06-22 02:05:12 +08:00
c3e40a9566 rua 2023-06-22 01:48:17 +08:00
09f97c8066 add client part 2023-06-22 01:44:53 +08:00
53fbb9c98c add rust part 2023-06-22 01:44:46 +08:00
77e143bf77 remove drgame lang file from main 2023-06-22 01:41:11 +08:00
29dc40b81d update DR rs cargo 2023-06-20 21:53:03 +08:00
0fd7bcf0f5 fix issue on command 2023-06-18 15:42:35 +08:00
3784a32184 fix issue 2023-06-18 14:33:20 +08:00
d483326263 fix debug not show 2023-06-18 02:06:58 +08:00
192e679c6d docs with changes 2023-06-18 02:06:49 +08:00
d41570b583 more wider 2023-06-18 01:45:00 +08:00
1fbab4b33b change a to 100->255 2023-06-18 01:29:42 +08:00
30d30e56c5 docs 2023-06-18 01:26:26 +08:00
22995a01af remove old white box 2023-06-18 01:26:03 +08:00
cf11afe18c add color box in render 2023-06-18 01:20:27 +08:00
f31a693eae add get_part_box 2023-06-18 01:20:17 +08:00
0673ba6981 Merge branch 'main' into dr_game/phy_simluation 2023-06-18 00:18:27 +08:00
44c07a64d9 fix #33 2023-06-17 18:31:06 +08:00
5183dbfb25 fix cpython issue 2023-06-17 16:20:49 +08:00
b1fc9495fe ignore pypy in some requirement 2023-06-17 16:17:07 +08:00
14206ecf14 litte dr_game iml ment 2023-06-17 16:16:55 +08:00
c9c3ff4561 fix msvc 2023-06-17 14:42:13 +08:00
fbb7dc7ae9 bump 0830 2023-06-17 14:42:07 +08:00
9bedadea15 remove docs 2023-06-17 14:36:58 +08:00
bb14fa4634 remove log(use less 2023-06-17 14:36:43 +08:00
9cd2756b30 copyright not copy-right 2023-06-17 14:34:19 +08:00
6d1b3925e4 fix disable ccache 2023-06-17 14:16:01 +08:00
76e58676de remove useless logger rework nuitka add cmd 2023-06-17 14:07:03 +08:00
5b50c9a9e2 fix #31 2023-06-17 01:30:43 +08:00
36e0895e86 add double format for caption 2023-06-17 01:28:10 +08:00
316d3d5f15 compat DR status rework 2023-06-17 01:16:30 +08:00
833ac81c08 add docs 2023-06-17 01:11:32 +08:00
05c39fefd2 sync pyglet 2023-06-17 01:09:43 +08:00
c79d98f89e
Merge pull request #30 from shenjackyuanjie/feature/rebase_dr_runtime
Feature/rebase dr runtime
2023-06-17 01:01:42 +08:00
45531c431b add event type for on_close 2023-06-17 01:00:49 +08:00
021ad2d5de add compat for workflow 2023-06-17 00:54:29 +08:00
893912fae3 replace runtime import [build skip] [build rs skip] 2023-06-17 00:51:05 +08:00
645e7ae80d remove use less sys.path.append [build skip] [build rs skip] 2023-06-17 00:48:20 +08:00
4128823b3f move DR_runtime [build skip] [build rs skip] 2023-06-17 00:47:00 +08:00
0d5c016dad init adding 2023-06-15 00:38:22 +08:00
353 changed files with 4634 additions and 2708 deletions

View File

@ -81,6 +81,8 @@ jobs:
python setup.py build
python post_build.py
python setup.py clean
cd ..
Remove-Item -Recurse -Force src
# Uploads artifact
- name: Upload Artifact
@ -88,4 +90,4 @@ jobs:
with:
name: DR_rs${{env.DR_version}}-${{runner.os}}${{matrix.python-version}}-Build.${{github.run_number}}+${{env.short_sha}}
path: |
mods/dr_game/Difficult_Rocket_rs/lib
mods/dr_game

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# -------------------------------
# Difficult Rocket
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
@ -109,7 +108,7 @@ def main():
return 0
dsm.clear_dsm()
dsm.upload_docs('docs/md5.txt')
dsm.fl.session.logout('FileStation')
dsm.fl.logout()
if __name__ == '__main__':

View File

@ -10,32 +10,24 @@ import rtoml
sys.path.append(os.path.abspath(os.curdir))
from Difficult_Rocket import DR_runtime
from Difficult_Rocket import DR_status
args = ['-env', '-github-dev']
# print(sys.argv)
if sys.argv == [__file__]: # 没有输入参数,直接输出默认信息并输出
print(sys.version)
from Difficult_Rocket.utils import tools
# 重置窗口信息
config_file = tools.load_file('./configs/main.toml')
config_file = tools.load_file('./config/main.toml')
config_file['window']['width'] = 1024
config_file['window']['height'] = 768
rtoml.dump(config_file, open('./configs/main.toml', 'w'))
rtoml.dump(config_file, open('./config/main.toml', 'w'))
elif os.path.abspath(os.curdir) in sys.path and '-env' in sys.argv:
with open('./.github/workflows/env.ps1', encoding='utf-8', mode='w') as env_file:
print(f'$env:DR_version = "{DR_runtime.DR_version}"', file=env_file)
print(f'$env:DR_language = "{DR_runtime.language}"', file=env_file)
print(f'$env:DR_long_version = "{DR_runtime.DR_long_version}"', file=env_file)
print(f'$env:Build_version = "{DR_runtime.Build_version}"', file=env_file)
print(f'$env:DR_version = "{DR_status.DR_version}"', file=env_file)
print(f'$env:Build_version = "{DR_status.Build_version}"', file=env_file)
elif os.path.abspath(os.curdir) in sys.path and '-github' in sys.argv:
print(f'DR_version={DR_runtime.DR_version}')
print(f'DR_language={DR_runtime.language}')
print(f'DR_long_version={DR_runtime.DR_long_version}')
print(f'Build_version={DR_runtime.Build_version}')
print(f'DR_version={DR_status.DR_version}')
print(f'Build_version={DR_status.Build_version}')

View File

@ -7,7 +7,6 @@ on:
push:
paths:
- "Difficult_Rocket/**" # 本体修改
- "configs/**" # 配置修改
- "libs/pyglet/**" # pyglet 修改
- ".github/workflows/**" # workflow 修改
- "nuitka_build.py" # 构建脚本修改
@ -15,7 +14,6 @@ on:
pull_request:
paths:
- "Difficult_Rocket/**" # 本体修改
- "configs/**" # 配置修改
- "libs/pyglet/**" # pyglet 修改
- ".github/workflows/**" # workflow 修改
- "nuitka_build.py" # 构建脚本修改

View File

@ -49,12 +49,12 @@ jobs:
run: |
# 设置变量
$urls = @(
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.3/mdbook-i18n',
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.3/mdbook-theme',
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.3/mdbook-cmdrun',
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.3/mdbook-pagetoc',
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.3/mdbook-footnote',
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.3/mdbook-external-links',
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.4/mdbook-i18n',
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.4/mdbook-theme',
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.4/mdbook-cmdrun',
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.4/mdbook-pagetoc',
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.4/mdbook-footnote',
'https://github.com/shenjackyuanjie/Minecraft_Science_Tree/releases/download/0.0.4/mdbook-external-links',
'https://github.com/plantuml/plantuml/releases/download/v1.2023.4/plantuml-1.2023.4.jar'
)
# 下载文件

7
.gitignore vendored
View File

@ -166,3 +166,10 @@ other things/
*cmake-build-debug
.pdm.toml
writer-test.xml
test-xml-rs.xml
test-file.xml
test-save.xml
index.html

View File

@ -2,9 +2,5 @@
"rust-analyzer.linkedProjects": [
"mods/dr_game/Difficult_Rocket_rs/src/Cargo.toml",
"libs/pyglet_rs/src/Cargo.toml",
],
"python.analysis.extraPaths": [
"./libs"
],
"python.analysis.typeCheckingMode": "basic"
]
}

View File

@ -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
View File

@ -1,111 +1,70 @@
"""
writen by shenjackyuanjie
email: 3695888@qq.com
"""
# -------------------------------
# Difficult Rocket
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
# All rights reserved
# -------------------------------
import os
import sys
import time
import cProfile
import traceback
import threading
from io import StringIO
# TODO 默认位置配置文件
# TODO 可自定义工作路径
from pathlib import Path
hi = """Difficult Rocket is writen by shenjackyuanjie
email: 3695888@qq.com or shyj3695888@163.com
QQ: 3695888"""
error_format = {
'TestError': '游戏正在调试中,某处引发了一个 TestError不是bug造成的原因',
'AssertionError': '游戏的某处检查未通过情报告issue',
'error.unknown': '游戏报错了现在输出报错信息请报告issue',
'error.happen': '游戏出现了一个报错!正在处理'
}
def print_path() -> None:
print(f'{__file__=}')
print(f'{sys.path=}')
print(f'{sys.path[0]=}')
print(f'{sys.argv[0]=}')
print(f'{os.curdir=}')
print(f'{os.getcwd()=}')
print(f'{os.path.abspath(os.curdir)=}')
print(f'{os.path.abspath(__file__)=}')
print(f'{os.path.realpath(__file__)=}')
print(f'{os.path.split(os.path.split(os.path.realpath(__file__))[0])=}')
# 输出一遍大部分文件位置相关信息 以后可能会加到logs里
print(f'{Path.cwd()=}')
print(f'{Path(__file__).absolute()=}')
def modify_path() -> None:
os.chdir(Path(__file__).parent) # 将运行路径切换到文件位置 防止bug
sys.path.append('./Difficult_Rocket') # 添加local path
sys.path.append('./libs') # 添加 libs path
def start(start_time_ns: int) -> None:
from Difficult_Rocket import crash, DR_status
from Difficult_Rocket.runtime import DR_runtime
from Difficult_Rocket.exception import TestError
DR_runtime.start_time_ns = start_time_ns
try:
from Difficult_Rocket import main
main_game = main.Game()
main_game.start()
if DR_status.crash_report_test:
raise TestError('debug crash test')
except:
trace = traceback.format_exc()
crash.create_crash_report(trace)
print(trace)
crash.write_info_to_cache(sys.stdout)
print(crash.all_thread)
print(crash.all_process)
for a_thread in threading.enumerate():
print(a_thread)
if a_thread.is_alive() and a_thread != threading.current_thread() and a_thread != threading.main_thread():
a_thread.join(2) # wait for 2 sec
import pyglet
pyglet.app.exit() # make sure that pyglet has stopped
def main() -> int:
print(hi) # hi
print(hi, f"\n{time.ctime()}") # hi
# 记录启动信息
start_time_ns = time.time_ns()
start_time_perf_ns = time.perf_counter_ns()
print_path()
file_path = os.path.split(os.path.realpath(__file__))[0]
os.chdir(file_path) # 将运行路径切换到文件位置 防止bug
sys.path.append(f'{file_path}/Difficult_Rocket') # 添加local path
sys.path.append(f'{file_path}/libs') # 添加 libs path
from Difficult_Rocket.exception import TestError
from Difficult_Rocket import crash
from Difficult_Rocket import DR_status
try:
from libs import pyglet # 导入pyglet
pyglet.resource.path = ['/textures/']
pyglet.resource.reindex()
from Difficult_Rocket import main, DR_runtime
DR_runtime.start_time_ns = start_time_ns
# from pyglet.gl import glClearColor # 调整背景颜色
# glClearColor(0.5, 0.5, 0.5, 0)
game = main.Game() # 实例化一个游戏
print(time.perf_counter_ns() - start_time_perf_ns, (time.perf_counter_ns() - start_time_perf_ns) / (10 ** 9), 'start') # 输出一下启动用时
cprofile = False # 是否使用cprofile
if cprofile:
cProfile.run('game.start()', sort='calls') # 使用 cprofile 启动
else:
game.start() # 直接启动
if DR_status.crash_report_test:
raise TestError('debugging') # debug 嘛试试crash
except Exception as exp: # 出毛病了
# 解析错误信息
print(error_format['error.happen'])
error = traceback.format_exc()
name = type(exp).__name__
if name in error_format:
print(error_format[name])
else:
print(error_format['error.unknown'])
print(error)
# 输出 crash 信息
crash.create_crash_report(error)
cache_steam = StringIO()
crash.write_info_to_cache(cache_steam)
text = cache_steam.getvalue()
print(text)
else:
crash.record_thread = False
print(crash.all_thread)
print(crash.all_process)
# join all thread
for thread in threading.enumerate():
print(thread)
if thread.name == 'MainThread' or thread == threading.main_thread() or thread == threading.current_thread():
continue
if thread.daemon:
continue
thread.join()
# stop pyglet
import pyglet
pyglet.app.exit()
print("Difficult_Rocket 已关闭")
modify_path()
start(start_time_ns)
return 0

View File

@ -4,20 +4,34 @@
# All rights reserved
# -------------------------------
import sys
import importlib
import traceback
import contextlib
import importlib.util
import time
import logging.config
from pathlib import Path
from typing import Optional, List, Tuple
from Difficult_Rocket.api.types import Options, Version
game_version = Version("0.8.2.0") # 游戏版本
build_version = Version("2.1.0.0") # 编译文件版本(与游戏本体无关)
sdk_version = Version("0.8.7.0") # SDK 版本
build_version = Version("2.1.3.0") # 编译文件版本(与游戏本体无关)
Api_version = Version("0.1.1.0") # API 版本
__version__ = game_version
__version__ = sdk_version
__all__ = [
# __init__
'DR_status',
# folder
'api',
'client',
'server',
'command',
'crash',
'exception',
'mod',
'utils',
# file
'main',
'runtime',
]
class _DR_status(Options):
@ -43,7 +57,7 @@ class _DR_status(Options):
crash_report_test: bool = False
# game version status
DR_version: Version = game_version # DR SDK 版本
DR_version: Version = sdk_version # DR SDK 版本
Build_version: Version = build_version # DR 构建 版本
API_version: Version = Api_version # DR SDK API 版本
@ -58,64 +72,23 @@ class _DR_status(Options):
return round(12 * self.gui_scale)
class _DR_runtime(Options):
"""
DR 的运行时配置 / 状态
"""
name = 'DR Runtime'
language: str = 'zh-CN'
mod_path: str = './mods'
DR_Mod_List: List[Tuple[str, Version]] = [] # DR Mod 列表 (name, version)
# run status
start_time_ns: Optional[int] = None
client_setup_cause_ns: Optional[int] = None
server_setup_cause_ns: Optional[int] = None
def load_file(self) -> bool:
with contextlib.suppress(FileNotFoundError):
with open('./configs/main.toml', 'r', encoding='utf-8') as f:
import rtoml
config_file = rtoml.load(f)
self.language = config_file['runtime']['language']
self.mod_path = config_file['game']['mods']['path']
return True
return False
def find_mods(self) -> List[str]:
mods = []
mod_path = Path(self.mod_path)
if not mod_path.exists():
mod_path.mkdir()
return []
paths = mod_path.iterdir()
sys.path.append(self.mod_path)
for mod_path in paths:
try:
if mod_path.is_dir() and mod_path.name != '__pycache__': # 处理文件夹 mod
if importlib.util.find_spec(mod_path.name) is not None:
mods.append(mod_path.name)
else:
print(f'can not import mod {mod_path} because importlib can not find spec')
elif mod_path.suffix in ('.pyz', '.zip'): # 处理压缩包 mod
if importlib.util.find_spec(mod_path.name) is not None:
mods.append(mod_path.name)
elif mod_path.suffix == '.pyd': # pyd 扩展 mod
if importlib.util.find_spec(mod_path.name) is not None:
mods.append(mod_path.name)
elif mod_path.suffix == '.py': # 处理单文件 mod
print(f'importing mod {mod_path=} {mod_path.stem}')
if importlib.util.find_spec(mod_path.stem) is not None:
mods.append(mod_path.stem)
except ImportError:
print(f'ImportError when loading mod {mod_path}')
traceback.print_exc()
return mods
DR_status = _DR_status()
DR_runtime = _DR_runtime()
def load_logging():
with open('./config/logger.toml') as f:
import rtoml
logger_config = rtoml.load(f)
log_path = logger_config['handlers']['file']['filename']
log_path = f"logs/{log_path.format(time.strftime('%Y-%m-%d %H-%M-%S', time.gmtime(time.time_ns() / 1000_000_000)))}"
if not Path('logs/').is_dir():
Path('logs/').mkdir()
logger_config['handlers']['file']['filename'] = log_path
logging.config.dictConfig(logger_config)
load_logging()
if DR_status.playing:
from Difficult_Rocket.utils.thread import new_thread
@ -127,4 +100,4 @@ if DR_status.playing:
@new_thread('think')
def think(some_thing_to_think):
gotcha = think_it(some_thing_to_think)
return gotcha
return gotcha

View File

@ -12,8 +12,6 @@ gitee: @shenjackyuanjie
"""
# from Difficult_Rocket.api import screen, mod, exception
__all__ = [
'exception',
# 错误类定义
@ -23,6 +21,4 @@ __all__ = [
# 类型定义
'mod',
# mod api
'log'
# log api
]

View 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'
]

View File

@ -0,0 +1,9 @@
# -------------------------------
# Difficult Rocket
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
# All rights reserved
# -------------------------------
__all__ = [
'widget'
]

View 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'
]

View File

@ -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',
]

View File

@ -112,6 +112,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
# def on_draw(self, dt: float, window: ClientWindow): # TODO: wait for pyglet 2.1
def on_draw(self, window: ClientWindow):
"""The window contents must be redrawn.

View File

@ -12,12 +12,15 @@ from Difficult_Rocket.utils.options import (Options,
OptionNotFound,
get_type_hints_)
from libs.MCDR.version import Version
from libs.MCDR.version import (Version,
VersionRequirement,
VersionParsingError)
__all__ = [
# main class
'Options',
'Version',
'VersionRequirement',
# data class
'FontData',
@ -27,6 +30,7 @@ __all__ = [
'OptionsError',
'OptionNameNotDefined',
'OptionNotFound',
'VersionParsingError',
# other
'get_type_hints_',

View File

@ -15,7 +15,7 @@ import traceback
from pathlib import Path
from decimal import Decimal
from typing import Callable, Dict, List, TYPE_CHECKING
from typing import Callable, Dict, List, TYPE_CHECKING, Type
# third function
import rtoml
@ -23,23 +23,28 @@ import pyglet
# from pyglet import gl
# from pyglet.gl import glClearColor
# from pyglet.libs.win32 import _user32
from pyglet.graphics import Group, Batch
from pyglet.window import Window
from pyglet.window import key, mouse
from pyglet.gui.widgets import TextEntry
# Difficult_Rocket function
if TYPE_CHECKING:
from Difficult_Rocket.main import Game
from Difficult_Rocket import DR_status
from Difficult_Rocket.utils import tools
from Difficult_Rocket.api.types import Options
from Difficult_Rocket.command import line
from Difficult_Rocket.api.types import Options
from Difficult_Rocket.utils.translate import tr
from Difficult_Rocket import DR_runtime
from Difficult_Rocket.runtime import DR_runtime
from Difficult_Rocket.api.screen import BaseScreen
from Difficult_Rocket.utils.thread import new_thread
from Difficult_Rocket.client.screen import DRDEBUGScreen
from Difficult_Rocket.client.fps.fps_log import FpsLogger
from Difficult_Rocket.client.guis.widgets import InputBox
from Difficult_Rocket.exception.language import LanguageNotFound
from Difficult_Rocket.client.screen import DRScreen, DRDEBUGScreen
logger = logging.getLogger('client')
class ClientOption(Options):
@ -51,20 +56,24 @@ class ClientOption(Options):
resizeable: bool = True
visible: bool = True
gui_scale: float = 1.0
caption: str = "Difficult Rocket v{DR_version}|DR_rs v{DR_Rust_get_version}"
caption: str = "Difficult Rocket v{DR_version}"
def load_file(self) -> None:
file: dict = tools.load_file('./configs/main.toml')
file: dict = tools.load_file('./config/main.toml')
self.fps = int(file['runtime']['fps'])
self.width = int(file['window']['width'])
self.height = int(file['window']['height'])
self.fullscreen = tools.format_bool(file['window']['full_screen'])
self.resizeable = tools.format_bool(file['window']['resizable'])
self.gui_scale = float(file['window']['gui_scale'])
self.caption = DR_runtime.format(file['window']['caption'])
self.caption = DR_status.format(file['window']['caption'])
self.caption = DR_runtime.format(self.caption)
class Client:
"""
客户端
"""
def __init__(self, game: "Game", net_mode='local'):
start_time = time.time_ns()
# logging
@ -89,6 +98,9 @@ class Client:
self.logger.debug(tr().client.setup.use_time_ns().format(self.use_time))
def start(self):
"""
启动客户端
"""
DR_runtime.running = True
self.window.start_game() # 游戏启动
# TODO 写一下服务端启动相关,还是需要服务端啊
@ -98,6 +110,11 @@ class Client:
def pyglet_load_fonts_folder(folder) -> None:
"""
递归加载字体文件夹
:param folder:
:return:
"""
font_path = Path(folder)
if not font_path.exists():
font_path.mkdir(parents=True)
@ -105,13 +122,48 @@ def pyglet_load_fonts_folder(folder) -> None:
file_folder_list = os.listdir(folder)
for obj in file_folder_list:
if os.path.isfile(os.path.join(folder, obj)):
if obj[-4:] == '.ttf':
pyglet.font.add_file(os.path.join(folder, obj))
if obj[-4:] == '.ttf' or obj[-4:] == '.otf':
logger.debug(f'loading font {os.path.join(folder, obj)}')
try:
pyglet.font.add_file(os.path.join(folder, obj))
except Exception:
logger.error(traceback.format_exc())
logger.error(f'loading font {os.path.join(folder, obj)} failed')
else:
logger.info(f'loading font folder {os.path.join(folder, obj)}')
pyglet_load_fonts_folder(os.path.join(folder, obj))
def _call_back(call_back: Callable) -> Callable:
"""
>>> def call_back():
>>> pass
>>> @_call_back(call_back)
>>> def on_draw(self):
>>> pass
用于在调用窗口函数后调用指定函数 的装饰器
:param call_back: 需要调用的函数
:return: 包装后的函数
"""
def wrapper(func):
@functools.wraps(func)
def warp(self: "ClientWindow", *args, **kwargs):
result = func(self, *args, **kwargs)
call_back(self)
return result
return warp
return wrapper
def _call_screen_after(func: Callable) -> Callable:
"""
>>> @_call_screen_after
>>> def on_draw(self):
>>> pass
用于在调用窗口函数后调用子窗口函数 的装饰器
:param func: 需要包装的函数
:return: 包装后的函数
"""
@functools.wraps(func)
def warped(self: "ClientWindow", *args, **kwargs):
result = func(self, *args, **kwargs)
@ -130,6 +182,14 @@ def _call_screen_after(func: Callable) -> Callable:
def _call_screen_before(func: Callable) -> Callable:
"""
>>> @_call_screen_before
>>> def on_draw(self):
>>> pass
用于在调用窗口函数前调用子窗口函数 的装饰器
:param func: 需要包装的函数
:return: 包装后的函数
"""
@functools.wraps(func)
def warped(self: "ClientWindow", *args, **kwargs):
for title, a_screen in self.screen_list.items():
@ -166,16 +226,16 @@ class ClientWindow(Window):
self.net_mode = net_mode
self.run_input = False
self.command_list: List[str] = []
# configs
self.main_config = tools.load_file('./configs/main.toml')
self.game_config = tools.load_file('./configs/game.config')
# config
self.main_config = tools.load_file('./config/main.toml')
self.game_config = tools.load_file('./config/game.config')
# FPS
self.FPS = Decimal(int(self.main_config['runtime']['fps']))
self.SPF = Decimal('1') / self.FPS
self.fps_log = FpsLogger(stable_fps=int(self.FPS))
# batch
self.part_batch = pyglet.graphics.Batch()
self.label_batch = pyglet.graphics.Batch()
self.main_batch = Batch()
self.main_group = Group(0)
# frame
self.frame = pyglet.gui.Frame(self, order=20)
self.M_frame = pyglet.gui.MovableFrame(self, modifier=key.LCTRL)
@ -183,12 +243,11 @@ class ClientWindow(Window):
# setup
self.setup()
# 命令显示
self.command_group = pyglet.graphics.Group(0)
self.input_box = InputBox(x=50, y=30, width=300,
batch=self.label_batch, text='') # 实例化
self.input_box = TextEntry(x=50, y=30, width=300,
batch=self.main_batch, text='', group=Group(1000, parent=self.main_group)) # 实例化
self.input_box.push_handlers(self)
self.input_box.set_handler('on_commit', self.on_input)
self.set_handlers(self.input_box)
self.push_handlers(self.input_box)
self.input_box.enabled = True
# 设置刷新率
# pyglet.clock.schedule_interval(self.draw_update, float(self.SPF))
@ -202,12 +261,10 @@ class ClientWindow(Window):
self.count = 0
def setup(self):
self.set_icon(pyglet.image.load('./textures/icon.png'))
self.set_icon(pyglet.image.load('assets/textures/icon.png'))
self.load_fonts()
# TODO 读取配置文件,加载不同的屏幕,解耦
self.screen_list['DR_debug'] = DRDEBUGScreen(self)
self.screen_list['DR_main'] = DRScreen(self)
self.game.dispatch_event('on_client_start', game=self.game, client=self)
self.game.dispatch_mod_event('on_client_start', game=self.game, client=self)
def load_fonts(self) -> None:
fonts_folder_path = self.main_config['runtime']['fonts_folder']
@ -216,33 +273,39 @@ class ClientWindow(Window):
pyglet_load_fonts_folder(fonts_folder_path)
def start_game(self) -> None:
self.set_icon(pyglet.image.load('./textures/icon.png'))
self.set_icon(pyglet.image.load('assets/textures/icon.png'))
try:
pyglet.app.event_loop.run(1 / self.main_config['runtime']['fps'])
# pyglet.clock.schedule_interval(self.on_draw, float(self.SPF))
# pyglet.app.run()
# TODO: wait for pyglet 2.1
pyglet.app.run(float(self.SPF))
except KeyboardInterrupt:
print("==========client stop. KeyboardInterrupt info==========")
self.logger.warning("==========client stop. KeyboardInterrupt info==========")
traceback.print_exc()
print("==========client stop. KeyboardInterrupt info end==========")
self.dispatch_event("on_close")
self.logger.warning("==========client stop. KeyboardInterrupt info end==========")
self.dispatch_event("on_close", 'input')
sys.exit(0)
@new_thread('window save_info')
def save_info(self):
self.logger.info(tr().client.config.save.start())
config_file: dict = tools.load_file('./configs/main.toml')
config_file: dict = tools.load_file('./config/main.toml')
config_file['window']['width'] = self.width
config_file['window']['height'] = self.height
config_file['runtime']['language'] = DR_runtime.language
rtoml.dump(config_file, open('./configs/main.toml', 'w'))
rtoml.dump(config_file, open('./config/main.toml', 'w'))
self.logger.info(tr().client.config.save.done())
"""
client api
"""
def add_sub_screen(self, title: str, sub_screen: type(BaseScreen)):
def add_sub_screen(self, title: str, sub_screen: Type[BaseScreen]):
self.screen_list[title] = sub_screen(self)
def remove_sub_screen(self, title: str):
self.screen_list.pop(title)
"""
draws and some event
"""
@ -254,11 +317,13 @@ class ClientWindow(Window):
self.fps_log.update_tick(now_FPS, decimal_tick)
@_call_screen_after
def on_draw(self, *dt):
while command := self.game.console.get_command():
# def on_draw(self, dt: float): # TODO: wait for pyglet 2.1
def on_draw(self):
while (command := self.game.console.get_command()) is not None:
self.on_command(line.CommandText(command))
pyglet.gl.glClearColor(0.1, 0, 0, 0.0)
self.clear()
# self.draw_update(dt) # TODO: wait for pyglet 2.1
self.draw_update(float(self.SPF))
self.draw_batch()
@ -281,10 +346,9 @@ class ClientWindow(Window):
# self.set_location(*self.get_location())
print('on hide!')
@_call_screen_after
@_call_screen_before
def draw_batch(self):
self.part_batch.draw()
self.label_batch.draw()
self.main_batch.draw()
"""
command line event
@ -295,15 +359,18 @@ class ClientWindow(Window):
self.on_command(command_text)
self.input_box.value = ''
def new_command(self):
self.game.console.new_command()
@_call_back(new_command)
@_call_screen_after
def on_command(self, command: line.CommandText):
print(command.find('/'))
self.logger.info(tr().window.command.text().format(command))
command.text = command.text.rstrip('\n').rstrip(' ').strip('/')
self.logger.info(tr().window.command.text().format(f"|{command.text}|"))
if command.find('stop'):
# self.dispatch_event('on_exit')
print("command stop!")
pyglet.app.platform_event_loop.stop()
self.dispatch_event('on_close', 'command') # source = command
self.logger.info("command stop!")
# HUGE THANKS to Discord @nokiyasos for this fix!
pyglet.app.exit()
elif command.find('fps'):
if command.find('log'):
self.logger.debug(self.fps_log.fps_list)
@ -322,10 +389,19 @@ class ClientWindow(Window):
tr._language = lang
self.logger.info(tr().language_set_to())
except LanguageNotFound:
self.logger.info(tr().language_available().format(os.listdir('./configs/lang')))
self.logger.info(tr().language_available().format(os.listdir('./config/lang')))
self.save_info()
# self.command_tree.parse(command.plain_command)
elif command.find('mods'):
if command.find('list'):
self.logger.info(tr().mod.list())
for mod in self.game.mod_manager.loaded_mod_modules.values():
self.logger.info(f"mod: {mod.name} id: {mod.mod_id} version: {mod.version}")
elif command.find('reload'):
if not len(command.text) == 0:
print(f"reload mod: |{command.text}|")
self.game.mod_manager.reload_mod(command.text, game=self.game)
else:
logger.info(tr().window.command.mods.reload.no_mod_id())
@_call_screen_after
def on_message(self, message: line.CommandText):
@ -348,7 +424,7 @@ class ClientWindow(Window):
...
@_call_screen_after
def on_mouse_motion(self, x, y, dx, dy) -> None:
def on_mouse_motion(self, x, y, dx, dy):
...
@_call_screen_after
@ -388,7 +464,7 @@ class ClientWindow(Window):
if symbol == key.ESCAPE and not (modifiers & ~(key.MOD_NUMLOCK |
key.MOD_CAPSLOCK |
key.MOD_SCROLLLOCK)):
self.dispatch_event('on_close')
self.dispatch_event('on_close', 'window')
if symbol == key.SLASH:
self.input_box._set_focus(True)
self.logger.debug(
@ -424,7 +500,7 @@ class ClientWindow(Window):
@_call_screen_before
def on_close(self, source: str = 'window') -> None:
self.game.dispatch_event('on_close', game=self.game, client=self, source=source)
self.game.dispatch_mod_event('on_close', game=self.game, client=self, source=source)
self.logger.info(tr().window.game.stop_get().format(tr().game[source]()))
self.logger.info(tr().window.game.stop())
# self.fps_log.check_list = False

View File

@ -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

View File

@ -7,24 +7,17 @@
import typing
from pyglet.text import Label
from pyglet.graphics import Batch, Group
from pyglet.clock import get_frequency
from pyglet.graphics import Batch, Group
# Difficult Rocket function
from Difficult_Rocket.api.types import Fonts
# from Difficult_Rocket.utils import translate
from Difficult_Rocket.api.screen import BaseScreen
# from Difficult_Rocket.command.tree import CommandTree
if typing.TYPE_CHECKING:
from Difficult_Rocket.client import ClientWindow
class DRScreen(BaseScreen):
def __init__(self, main_window: "ClientWindow"):
super().__init__(main_window)
class DRDEBUGScreen(BaseScreen):
def __init__(self, main_window: "ClientWindow"):
super().__init__(main_window)
@ -55,4 +48,3 @@ class DRDEBUGScreen(BaseScreen):
def on_draw(self, *dt, window: "ClientWindow"):
self.main_batch.draw()
# print(self.window_pointer.try_if_runs)

View File

@ -44,10 +44,13 @@ class CommandText:
i += 1
def find(self, text: str) -> bool:
find = self.text.find(text)
if find != -1:
self.text = self.text[find + len(text):]
return True
startswith = self.text.startswith(text)
if startswith:
find = self.text.find(text)
if find != -1:
if not len(text) == len(self.text):
self.text = self.text[find + len(text):] if not self.text[find+len(text)] == ' ' else self.text[find + len(text) + 1:]
return True
return False
def re_find(self, text: str) -> Union[str, bool]:

View File

@ -77,7 +77,7 @@ def create_crash_report(info: Optional[str] = None) -> None:
crash_info = crash_info_handler(info)
if 'crash_report' not in os.listdir('./'):
os.mkdir('./crash_report')
date_time = time.strftime('%Y-%m-%d %H-%M-%S', time.gmtime(time.time()))
date_time = time.strftime('%Y-%m-%d %H-%M-%S', time.localtime())
filename = f'crash-{date_time}.md'
cache_stream = io.StringIO()
try:
@ -92,16 +92,17 @@ def create_crash_report(info: Optional[str] = None) -> None:
def write_cache(cache_stream, crash_info):
# 开头信息
cache_stream.write(Head_message.format(now_time=time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(time.time()))))
cache_stream.write(Head_message.format(now_time=time.strftime('%Y/%m/%d %H:%M:%S', time.localtime())))
# 崩溃信息
cache_stream.write(crash_info)
def write_info_to_cache(cache_stream):
# 运行状态信息
from Difficult_Rocket import DR_status, DR_runtime
from Difficult_Rocket import DR_status
from Difficult_Rocket.runtime import DR_runtime
cache_stream.write(Run_message)
cache_stream.write(markdown_line_handler(f'DR Version: {Difficult_Rocket.game_version}', level=1))
cache_stream.write(markdown_line_handler(f'DR Version: {Difficult_Rocket.sdk_version}', level=1))
cache_stream.write(markdown_line_handler(f'DR language: {DR_runtime.language}', level=1))
cache_stream.write(markdown_line_handler(f'Running Dir: {Path(os.curdir).resolve()}', level=1))
cache_stream.write(f"\n{DR_runtime.as_markdown()}")

View 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')

View File

@ -11,33 +11,24 @@ github: @shenjackyuanjie
gitee: @shenjackyuanjie
"""
import sys
import time
import logging
import traceback
import importlib
import importlib.util
import logging.config
import multiprocessing
from io import StringIO
from pathlib import Path
from typing import TYPE_CHECKING, List, Optional, Dict, TypeVar
from typing import List, Optional, Dict
if __name__ == '__main__': # been start will not run this
sys.path.append('/bin/libs')
sys.path.append('/bin')
if TYPE_CHECKING:
from Difficult_Rocket.api.mod import ModInfo
else:
ModInfo = TypeVar('ModInfo')
from Difficult_Rocket.utils import tools
from Difficult_Rocket.api.types import Options
from Difficult_Rocket.utils.translate import tr
from Difficult_Rocket.runtime import DR_runtime
from Difficult_Rocket.mod.loader import ModManager
from Difficult_Rocket.utils.thread import new_thread
from Difficult_Rocket.crash import write_info_to_cache
from Difficult_Rocket import client, server, DR_status, DR_runtime
from Difficult_Rocket import client, server, DR_status
class Console(Options):
@ -68,6 +59,9 @@ class Console(Options):
def get_command(self) -> Optional[str]:
return self.caches.pop(0) if self.caches else None
def new_command(self) -> None:
return None
class Game(Options):
name = 'MainGame'
@ -78,75 +72,21 @@ class Game(Options):
console_class: Console = Console
main_config: Dict
logging_config: Dict
logger: logging.Logger
mod_module: List["ModInfo"] = []
mod_manager: ModManager
def init_logger(self) -> None:
log_path = self.logging_config['handlers']['file']['filename']
log_path = f"logs/{log_path.format(time.strftime('%Y-%m-%d %H-%M-%S' , time.gmtime(DR_runtime.start_time_ns / 1000_000_000)))}"
mkdir = False
if not Path('logs/').is_dir():
Path('logs/').mkdir()
mkdir = True
self.logging_config['handlers']['file']['filename'] = log_path
logging.config.dictConfig(self.logging_config)
self.logger = logging.getLogger('main')
if mkdir:
self.logger.info(tr().main.logger.mkdir())
def dispatch_mod_event(self, event_name: str, *args, **kwargs) -> None:
self.mod_manager.dispatch_event(event_name, *args, **kwargs)
def init_mods(self) -> None:
"""验证/加载 mod"""
self.mod_module = []
mods = []
mod_path = Path(DR_runtime.mod_path)
if not mod_path.exists():
self.logger.info(tr().main.mod.find.faild.no_mod_folder())
return
# 寻找有效 mod
paths = mod_path.iterdir()
sys.path.append(DR_runtime.mod_path)
for mod_path in paths:
try:
if mod_path.name == '__pycache__':
continue
self.logger.info(tr().main.mod.find.start().format(mod_path))
if mod_path.is_dir():
if importlib.util.find_spec(mod_path.name) is not None:
mods.append(mod_path.name)
else:
self.logger.warning(tr().main.mod.load.faild.info().format(mod_path.name, tr().main.mod.find.faild.no_spec()))
elif mod_path.suffix in ('.pyz', '.zip', '.pyd', '.py'):
if importlib.util.find_spec(mod_path.name) is not None:
mods.append(mod_path.name)
except ImportError as e:
self.logger.warning(tr().main.mod.find.faild().format(mod_path, e))
self.logger.info(tr().main.mod.find.done())
# 加载有效 mod
module = []
for mod in mods:
try:
self.logger.info(tr().main.mod.load.start().format(mod))
mod_module = importlib.import_module(mod)
if not hasattr(mod_module, "mod_class"):
self.logger.warning(tr().main.mod.load.faild.info().format(mod, tr().main.mod.load.faild.no_mod_class()))
del mod_module # 释放内存
continue
mod_class: type(ModInfo) = mod_module.mod_class
mod_class = mod_class()
module.append(mod_class)
self.logger.info(tr().main.mod.load.info().format(mod_class.mod_id, mod_class.version))
except ImportError as e:
self.logger.warning(tr().main.mod.load.faild.info().format(mod, e))
self.logger.info(tr().main.mod.load.done())
self.mod_module = module
mod_list = []
for mod in module:
mod_list.append((mod.mod_id, mod.version))
# 调用 on_load
self.dispatch_event('on_load', game=self)
DR_runtime.DR_Mod_List = mod_list
from Difficult_Rocket.mod import loader as mod_loader
mod_loader.logger = logging.getLogger('mod_manager')
self.mod_manager = ModManager()
mod_class = self.mod_manager.load_mods()
self.mod_manager.init_mods(mod_class)
self.dispatch_mod_event('on_load', game=self)
def init_console(self) -> None:
self.console = self.console_class()
@ -166,32 +106,15 @@ class Game(Options):
else:
self.client.start()
def dispatch_event(self, event_name: str, *args, **kwargs) -> None:
"""向 mod 分发事件"""
for mod in self.mod_module:
if hasattr(mod, event_name):
try:
getattr(mod, event_name)(*args, **kwargs)
except Exception:
error = traceback.format_exc()
self.logger.error(tr().main.mod.event.error().format(event_name, error, mod.mod_id))
def log_env(self) -> None:
cache_steam = StringIO()
write_info_to_cache(cache_steam)
text = cache_steam.getvalue()
self.logger.info(text)
self.flush_option()
config_cache = self.logging_config.copy()
self.logging_config = {"logging_config": "too long to show"}
self.logger.info(f"\n{self.as_markdown()}")
self.logging_config = config_cache
self.logger.info(f'\n{self.as_markdown()}')
def setup(self) -> None:
self.client = client.Client(game=self, net_mode='local')
self.server = server.Server(net_mode='local')
def init(self, **kwargs) -> bool:
self.logger = logging.getLogger('main')
self.load_file()
self.setup()
self.log_env()
@ -199,9 +122,6 @@ class Game(Options):
def load_file(self) -> bool:
"""加载文件"""
self.logging_config = tools.load_file('configs/logger.toml')
self.init_logger()
self.init_mods()
self.init_console()
return True

View File

@ -4,38 +4,203 @@
# All rights reserved
# -------------------------------
import sys
import time
import logging
import traceback
import importlib
from pathlib import Path
from typing import List, Dict, Optional
from typing import List, Dict, Optional, TypeVar
from Difficult_Rocket.api.screen import BaseScreen
from Difficult_Rocket.api.types import Options, Version
from Difficult_Rocket.mod.api import ModInfo
# from Difficult_Rocket import DR_status, DR_runtime
from Difficult_Rocket.utils.translate import tr
from Difficult_Rocket.api.types import Options, Version
Game = TypeVar('Game')
logger = logging.getLogger('mod_manager')
ONE_FILE_SUFFIX = ('.py', '.pyc', '.pyd')
PACKAGE_SUFFIX = ('.pyz', '.zip', '.dr_mod')
def _add_path_to_sys(paths: List[Path]):
for path in paths:
if str(path) not in sys.path:
sys.path.append(str(path))
class ModManager(Options):
name = 'Mod Manager'
logger: logging.Logger
mods_path: List[Path] = [Path('./mods')]
find_mod_paths: Dict[str, Path] = {}
loaded_mod_modules: Dict[str, ModInfo] = {}
def find_mods(self) -> List[Path]:
def get_mod_module(self, mod_name: str) -> Optional[ModInfo]:
"""
查找mods文件夹下的所有mod
获取指定 mod 的模块
:param mod_name: mod
:return:
"""
mods = []
for path in self.mods_path:
for mod in self.loaded_mod_modules.values():
if mod.name == mod_name:
return mod
return None
def dispatch_event(self, event_name: str, *args, **kwargs):
"""
分发事件
:param event_name: 事件名
:param args: 事件参数
:param kwargs: 事件参数
:return:
"""
for mod in self.loaded_mod_modules.values():
if hasattr(mod, event_name):
try:
getattr(mod, event_name)(*args, **kwargs)
except Exception as e:
logger.error(tr().mod.event.error().format(mod, event_name, e, traceback.format_exc()))
def load_mod(self, mod_path: Path) -> Optional[type(ModInfo)]:
"""
加载指定路径下的 mod
:param mod_path: mod 的路径
:return:
"""
if not mod_path.exists():
logger.error(tr().mod.load.faild.not_exist().format(mod_path))
return None
_add_path_to_sys([mod_path.parent])
try:
if mod_path.name == '__pycache__':
# 忽略 __pycache__ 文件夹 (Python 编译文件)
return None
logger.info(tr().mod.load.loading().format(mod_path))
if mod_path.is_dir() or mod_path.suffix in PACKAGE_SUFFIX or mod_path.suffix in ONE_FILE_SUFFIX:
# 文件夹 mod
loading_mod = importlib.import_module(mod_path.name)
if not hasattr(loading_mod, 'mod_class') or not issubclass(loading_mod.mod_class, ModInfo):
logger.warning(tr().mod.load.faild.no_mod_class().format(mod_path))
return None
mod_class: type(ModInfo) = loading_mod.mod_class # 获取 mod 类
if mod_class.mod_id not in self.find_mod_paths:
self.find_mod_paths[mod_class.mod_id] = mod_path
return mod_class
except ImportError:
logger.warning(tr().mod.load.faild.error().format(mod_path, traceback.format_exc()))
return None
def find_mods_in_path(self, extra_mods_path: Optional[List[Path]] = None) -> List[Path]:
"""
查找所有 mod 路径
:return: 找到的 mod 的路径 (未校验)
"""
find_path = self.mods_path + (extra_mods_path if extra_mods_path is not None else [])
mods_path = []
start_time = time.time()
for path in find_path:
if not path.exists():
path.mkdir(parents=True)
continue
for mod in path.iterdir():
...
if mod.name == '__pycache__':
# 忽略 __pycache__ 文件夹 (Python 编译文件)
continue
if mod.is_dir() or mod.suffix in PACKAGE_SUFFIX or mod.suffix in ONE_FILE_SUFFIX:
# 文件夹 mod
mods_path.append(mod)
logger.info(tr().mod.finded().format(len(mods_path), time.time() - start_time))
return mods_path
def init(self) -> None:
self.logger = logging.getLogger('client')
self.logger.name = 'mod_manager'
self.logger.info('Mod Manager init')
def load_mods(self,
extra_path: Optional[List[Path]] = None,
extra_mod_path: Optional[List[Path]] = None) -> List[type(ModInfo)]:
"""
加载所有 mod (可提供额外的 mod 路径)
:param extra_path: 额外的 mod 路径
:param extra_mod_path: 额外的找到的 mod 路径
:return:
"""
find_path = self.mods_path + (extra_path if extra_path is not None else [])
_add_path_to_sys(find_path)
mods = []
start_time = time.time()
logger.info(tr().mod.load.start().format(find_path))
for path in find_path:
if not path.exists():
path.mkdir(parents=True)
continue
for mod in path.iterdir():
if (cache := self.load_mod(mod)) is not None:
mods.append(cache)
if extra_mod_path is not None:
for path in extra_mod_path:
if (cache := self.load_mod(path)) is not None:
mods.append(cache)
logger.info(tr().mod.load.use_time().format(time.time() - start_time))
return mods
def init_mods(self, mods: List[type(ModInfo)]):
"""
加载 mod
:param mods: 要加载的 mod ModInfo
:return:
"""
start_time = time.time()
for mod_class in mods:
try:
init_mod = mod_class()
self.loaded_mod_modules[init_mod.mod_id] = init_mod
logger.info(tr().mod.init.success().format(init_mod, init_mod.version))
except Exception as e:
logger.error(tr().mod.init.faild().format(mod_class, e, traceback.format_exc()))
continue
logger.info(tr().mod.init.use_time().format(time.time() - start_time))
def unload_mod(self, mod_id: str, game: Game) -> Optional[ModInfo]:
"""
卸载 mod
:param mod_id: 要卸载的 mod id
:param game: 游戏实例
:return: 卸载的 mod ModInfo
"""
if not (mod_class := self.loaded_mod_modules.get(mod_id)) and (mod_class := self.get_mod_module(mod_id)) is None:
logger.warning(tr().mod.unload.faild.not_find().format(mod_id))
return None
try:
mod_class.on_unload(game=game)
self.loaded_mod_modules.pop(mod_class.mod_id)
logger.info(tr().mod.unload.success().format(mod_id))
return mod_class
except Exception as e:
logger.error(tr().mod.unload.faild.error().format(mod_id, e, traceback.format_exc()))
return None
def reload_mod(self, mod_id: str, game: Game):
"""
重载 mod
:param mod_id:
:param game:
:return:
"""
unload = self.unload_mod(mod_id, game)
if unload is None:
return
mod_class: Optional[ModInfo] = None
if unload.mod_id not in self.find_mod_paths:
logger.warning(tr().mod.reload.faild.not_find().format(unload.mod_id))
paths = self.find_mods_in_path()
for path in paths:
mod_class = self.load_mod(path)
if mod_class is not None and mod_class.mod_id == unload.mod_id:
self.init_mods([mod_class])
break
else:
mod_class = self.load_mod(self.find_mod_paths[unload.mod_id])
if mod_class is not None:
self.init_mods([mod_class])
if mod_id in self.loaded_mod_modules and mod_class is not None:
self.loaded_mod_modules[mod_id].on_load(game=game, old_self=mod_class)
logger.info(tr().mod.reload.success().format(mod_id))

View 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()

View File

@ -36,7 +36,7 @@ class Server:
# os.set
self.process_name = 'server process'
# config
self.config = tools.load_file('configs/main.toml')
self.config = tools.load_file('config/main.toml')
# self.dev = Dev
# self.net_mode = net_mode
self.logger.info(tr().server.setup.use_time().format(time.time() - start_time))

View File

@ -4,3 +4,10 @@
# All rights reserved
# -------------------------------
__all__ = [
'camera',
'options',
'thread',
'tools',
'translate'
]

View 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))

View File

@ -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

View File

@ -4,19 +4,21 @@
# All rights reserved
# -------------------------------
import warnings
import shutil
import traceback
from io import StringIO
from dataclasses import dataclass
from typing import get_type_hints, Type, List, Union, Dict, Any, Callable, Tuple, Optional, TYPE_CHECKING, Iterable
__all__ = ['get_type_hints_',
'Options',
'Fonts',
'FontData',
'OptionsError',
'OptionNotFound',
'OptionNameNotDefined']
__all__ = [
'get_type_hints_',
'Options',
'OptionsError',
'OptionNotFound',
'OptionNameNotDefined',
'Fonts',
'FontData'
]
def get_type_hints_(cls: Type):
@ -174,15 +176,12 @@ class Options:
self.cached_options = self.option()
return self.cached_options
def option_with_len(self, longest: Optional[int] = None) -> Tuple[List[Tuple[str, Union[Any, Type], Type]], int, int, int]:
def option_with_len(self) -> Tuple[List[Tuple[str, Any, Type]], int, int, int]:
"""
返回一个可以用于打印的 option 列表
:return:
"""
if longest is None:
options = self.flush_option()
else:
options = self.str_option(longest)
options = self.flush_option()
max_len_key = 1
max_len_value = 1
max_len_value_t = 1
@ -192,19 +191,61 @@ class Options:
max_len_key = max(max_len_key, len(key))
max_len_value = max(max_len_value, len(str(value)))
max_len_value_t = max(max_len_value_t, len(str(value_t)))
option_list.append((key, value, value_t))
return option_list, max_len_key, max_len_value, max_len_value_t
option_list.append([key, value, value_t])
return [option_list, max_len_key, max_len_value, max_len_value_t] # noqa
def as_markdown(self, longest: Optional[int] = None) -> str:
"""
返回一个 markdown 格式的 option 字符串
:param longest: 最长的输出长度
:return: markdown 格式的 option 字符串
"""
value = self.option_with_len(longest)
value = self.option_with_len()
cache = StringIO()
option_len = max(value[1], len('Option'))
value_len = max(value[2], len('Value'))
value_type_len = max(value[3], len('Value Type'))
# | Option | Value | Value Type |
shortest = len('Option" "Value" "Value Type')
if longest is not None:
console_width = max(longest, shortest)
else:
console_width = shutil.get_terminal_size(fallback=(100, 80)).columns
console_width = max(console_width, shortest)
# 为每一栏 预分配 1/3 或者 需要的宽度 (如果不需要 1/3)
option_len = min(option_len, console_width // 3)
value_len = min(value_len, console_width // 3)
value_type_len = min(value_type_len, console_width // 3)
# 先指定每一个列的输出最窄宽度, 然后去尝试增加宽度
# 循环分配新空间之前 首先检查是否已经不需要多分配 (and 后面)
while option_len + value_len + value_type_len + 16 < console_width\
and (option_len < value[1]
or value_len < value[2]
or value_type_len < value[3]):
# 每一个部分的逻辑都是
# 如果现在的输出长度小于原始长度
# 并且长度 + 1 之后的总长度依然在允许范围内
# 那么就 + 1
if option_len < value[1] and option_len + value_len + value_type_len + 15 < console_width:
option_len += 1
if value_len < value[2] and option_len + value_len + value_type_len + 15 < console_width:
value_len += 1
if value_type_len < value[3] and option_len + value_len + value_type_len + 15 < console_width:
value_type_len += 1
# 实际上 对于列表(可变对象) for 出来的这个值是一个引用
# 所以可以直接修改 string
for v in value[0]:
if len(str(v[0])) > option_len:
v[0] = f'{str(v[0])[:value_len - 3]}...'
if len(str(v[1])) > value_len:
v[1] = f'{str(v[1])[:value_len - 3]}...'
if len(str(v[2])) > value_type_len:
v[2] = f'{str(v[2])[:value_len - 3]}..'
cache.write(
f"| Option{' ' * (option_len - 3)}| Value{' ' * (value_len - 2)}| Value Type{' ' * (value_type_len - 7)}|\n")
cache.write(f'|:{"-" * (option_len + 3)}|:{"-" * (value_len + 3)}|:{"-" * (value_type_len + 3)}|\n')

View File

@ -16,22 +16,21 @@ import sys
import time
import math
import json
import rtoml
import logging
import configparser
from typing import Union
from pathlib import Path
from typing import Union, Optional
from xml.etree import ElementTree
import rtoml
from defusedxml.ElementTree import parse
from Difficult_Rocket.exception.unsupport import NoMoreJson5
# logger
tools_logger = logging.getLogger('part-tools')
tools_logger = logging.getLogger('tools')
"""
file configs
file config
"""
file_error = {FileNotFoundError: 'no {filetype} file was founded!:\n file name: {filename}\n file_type: {filetype}\n stack: {stack}',
@ -39,10 +38,12 @@ file_error = {FileNotFoundError: 'no {filetype} file was founded!:\n file name:
Exception: 'get some {error_type} when read {filetype} file {filename}! \n file type: {} \n file name: {} \n stack: {stack}'}
def load_file(file_name: str,
stack: Union[str, list, dict, None] = None,
raise_error: bool = True,
encoding: str = 'utf-8') -> Union[dict, ElementTree.ElementTree]:
def load_file(file_name: Union[str, Path],
stack: Optional[Union[str, list, dict]] = None,
raise_error: Optional[bool] = True,
encoding: Optional[str] = 'utf-8') -> Union[dict, ElementTree.ElementTree]:
if isinstance(file_name, Path):
file_name = str(file_name)
f_type = file_name[file_name.rfind('.') + 1:] # 从最后一个.到末尾 (截取文件格式)
get_file = NotImplementedError('解析失败,请检查文件类型/文件内容/文件是否存在!')
try:
@ -98,7 +99,7 @@ def save_dict_file(file_name: str,
# main config
main_config_file = load_file('./configs/main.toml')
main_config_file = load_file('./config/main.toml')
def get_At(name, in_xml, need_type=str):

View File

@ -14,11 +14,13 @@ gitee: @shenjackyuanjie
import os
import inspect
from pathlib import Path
from dataclasses import dataclass
from typing import Union, Tuple, Any, List, Dict, Hashable, Optional
from Difficult_Rocket import DR_runtime, DR_status
from Difficult_Rocket import DR_status
from Difficult_Rocket.utils import tools
from Difficult_Rocket.runtime import DR_runtime
from Difficult_Rocket.exception.language import (LanguageNotFound,
TranslateKeyNotFound)
@ -138,12 +140,12 @@ class Translates:
def __str__(self):
if not any(not x[0] for x in self._get_list):
return self._value
return str(self._value)
if self._config.crack_normal:
return f'{".".join(f"{gets[1]}({gets[0]})" for gets in self._get_list)}'
elif self._config.insert_crack:
return f'{self._value}.{".".join(gets[1] for gets in self._get_list if not gets[0])}'
return self._value
return str(self._value)
class Tr:
@ -152,15 +154,20 @@ class Tr:
GOOD
"""
def __init__(self, language: str = None, config: Optional[TranslateConfig] = None):
def __init__(self,
language: str = None,
config: Optional[TranslateConfig] = None,
lang_path: Optional[Path] = None):
"""
诶嘿我抄的MCDR
:param language: Tr 所使用的的语言
:param config: 配置
:param lang_path: 语言文件夹路径
"""
self.language_name = language if language is not None else DR_runtime.language
self.translates: Dict[str, Union[str, Dict]] = tools.load_file(f'configs/lang/{self.language_name}.toml')
self.default_translate: Dict = tools.load_file(f'configs/lang/{DR_status.default_language}.toml')
self.language_path = lang_path if lang_path is not None else Path('assets/lang')
self.translates: Dict[str, Union[str, Dict]] = tools.load_file(self.language_path / f'{self.language_name}.toml')
self.default_translate: Dict = tools.load_file(f'{self.language_path}/{DR_status.default_language}.toml')
self.default_config = config.set('source', self) if config is not None else TranslateConfig(source=self)
self.translates_cache = Translates(value=self.translates, config=self.default_config.copy())
@ -184,11 +191,11 @@ class Tr:
if lang == ' ' or lang == '':
raise LanguageNotFound('Can not be empty')
lang = lang or self.language_name
if not os.path.exists(f'./configs/lang/{lang}.toml'):
print(f"lang: {os.path.exists(f'./configs/lang/{lang}.toml')} language = {lang} {self.language_name=}")
if not os.path.exists(f'{self.language_path}/{lang}.toml'):
print(f"lang: {os.path.exists(f'{self.language_path}/{lang}.toml')} language = {lang} {self.language_name=}")
raise LanguageNotFound(lang)
self.translates: Dict[str, Union[str, Dict]] = tools.load_file(f'configs/lang/{lang}.toml')
self.default_translate: Dict = tools.load_file(f'configs/lang/{DR_runtime.default_language}.toml')
self.translates: Dict[str, Union[str, Dict]] = tools.load_file(f'{self.language_path}/{lang}.toml')
self.default_translate: Dict = tools.load_file(f'{self.language_path}/{DR_runtime.default_language}.toml')
self.translates_cache = Translates(value=self.translates, config=self.default_config.copy())
self.language_name = lang
DR_runtime.language = self.language_name

View File

@ -1,3 +1,5 @@
<div style="text-align: center;">
# Difficult Rocket
中文 | [English](./docs/README-en.md)
@ -9,22 +11,28 @@
## 请注意 这个仓库未来只会发布 `DR SDK` 的更新 `DR game` 的更新会在 [这里](https://github.com/shenjackyuanjie/DR-game) 发布
![demo](docs/src/demo.png)
<a href="https://996.icu"><img src="https://img.shields.io/badge/link-996.icu-red.svg" alt="996.icu" /></a>
[![Generic badge](https://img.shields.io/badge/SemVer-2.0.0-blue.svg)](https://Semver.org/)
[![Generic badge](https://img.shields.io/badge/编写于_Python_版本-3.8.10-blue.svg)](https://Python.org)
[![Generic badge](https://img.shields.io/badge/编写于_Pyglet_版本-2.0.7-blue.svg)](https://pyglet.org)
[![Generic badge](https://img.shields.io/badge/编写于_Pyglet_版本-2.0.8-blue.svg)](https://pyglet.org)
[![Generic badge](https://img.shields.io/badge/Python-_3.8_|_3.9_|_3.10_|_3.11_-blue.svg)](https://Python.org)
## 版本
[关于版本号的说明](./docs/src/version.md)
[![Generic badge](https://img.shields.io/badge/Release-0.8.2.0-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
[![Generic badge](https://img.shields.io/badge/Pre_Release-0.8.2.0-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
[![Generic badge](https://img.shields.io/badge/Devloping-0.8.3-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
[![Generic badge](https://img.shields.io/badge/Release-0.8.5.1-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
[![Generic badge](https://img.shields.io/badge/Pre_Release-0.8.6.0-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
[![Generic badge](https://img.shields.io/badge/Devloping-0.8.6-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
[![language badge](https://stats.deeptrain.net/repo/shenjackyuanjie/Difficult-Rocket?theme=dark)](https://stats.deeptrain.net/repo/shenjackyuanjie/Difficult-Rocket?theme=dark)
[DR sdk 最新 Action 构建](https://nightly.link/shenjackyuanjie/Difficult-Rocket/workflows/nuitka/main)
[DR rs 最新 Action 构建](https://nightly.link/shenjackyuanjie/Difficult-Rocket/workflows/dr_rs/main)
## English README please look [here](./docs/README-en.md)
> 这是一个用Python制作的类Simple Rocket游戏(简称:火箭模拟器)
@ -42,27 +50,27 @@
## 环境需求 (测试过的 / 开发平台)
- `开发平台 1 - Windows 10 x64 22H2`
- Python `3.8.10`
- Python `3.8.10` / `3.10.11`
- pillow `9.5.0`
- psutil `5.9.5`
- rtoml `0.9.0`
- tomlkit `0.11.7`
- tomlkit `0.11.8`
- defusedxml `0.7.1`
- objprint `0.2.2`
- viztracer `0.15.6`
- vizplugins `0.1.3`
- nuitka `1.5.6`
- nuitka `1.6.6`
- ordered-set `4.1.0`
- imageio `2.27.0`
- imageio `2.31.0`
- wheel `0.40.0`
- setuptools `67.6.1`
- setuptools-rust `1.5.2`
- setuptools `67.8.0`
- setuptools-rust `1.6.0`
- `AMD R5 5600X`
- `AMD RX 550 4G`
## 需要的 Python 模块
- `pyglet` (已经内置 V2.0.5 路径:`./libs/pyglet`)
- `pyglet` (已经内置 V2.0.8 路径:`./libs/pyglet`)
- `xmltodict` (已经内置 V0.12.0 路径:`./libs/xmltodict`)
- `pyperclip` (已经内置 V1.8.2 路径: `./libs/pyperclip`)
@ -73,28 +81,30 @@
# DR contributing
# for images
pillow >= 9.5.0
# not for pypy >= 3.10
pillow >= 10.0.0; (platform_python_implementation == "PyPy" and python_version < "3.10") or platform_python_implementation == "CPython"
# for sys info
psutil >= 5.9.5
# for files
rtoml >= 0.9.0
tomlkit >= 0.11.7
tomlkit >= 0.11.8
defusedxml >= 0.7.1
# for debug
objprint >= 0.2.2
viztracer >= 0.15.6
vizplugins >= 0.1.3
viztracer >= 0.15.6; platform_python_implementation != "PyPy"
vizplugins >= 0.1.3; platform_python_implementation != "PyPy"
# for compile
nuitka >= 1.5.6
nuitka >= 1.7.5
ordered-set >= 4.1.0
imageio >= 2.27.0
imageio >= 2.31.0; (platform_python_implementation == "PyPy" and python_version < "3.10") or platform_python_implementation == "CPython"
wheel >= 0.40.0
setuptools >= 67.6.1
setuptools-rust >= 1.5.2
setuptools >= 67.8.0
setuptools-rust >= 1.6.0
```
## 感谢
@ -104,8 +114,8 @@ setuptools-rust >= 1.5.2
- `tomlkit` / `rtoml` : toml 解析器
- `xmltodict`: xml 与 dict 转换器
- `pyperclip`: 剪贴板!
- `rapier2d`: 物理模拟引擎
- `pyo3`: Rust Python 扩展
- [rapier2d](https://rapier.rs/) : 物理模拟引擎
- [pyo3](https://pyo3.rs/main): Rust Python 扩展
- 主要贡献者
- [@Rayawa](https://github.com/Rayawa) : 文档矫正 & 翻译部分 lang
@ -113,6 +123,8 @@ setuptools-rust >= 1.5.2
- [@Billchyi](https://github.com/Billchyi) : 文档矫正
- [@MSDNicrosoft](https://github.com/MSDNicrosoft) : 优化代码
</div>
## 相关链接
## 关于分享协议

View File

@ -180,7 +180,7 @@
<Part partType="engine-2" id="112" x="-13.000000" y="-5.500000" angle="0.000000" angleV="0.000000" editorAngle="0" activated="0" exploded="0" flippedX="0" flippedY="0">
<Engine fuel="0.000000"/>
</Part>
<Part partType="port-1" id="167" x="-13.000000" y="-7.500000" angle="4.712389" angleV="0.000000" editorAngle="3" activated="0" exploded="0" flippedX="1" flippedY="0"/>
<Part partType="port-1" id="167" x="-13.000000" y="-7.500000" angle="4.712389" angleV="0.000000" editorAngle="3" activated="1" exploded="0" flippedX="1" flippedY="0"/>
<Part partType="engine-2" id="113" x="-19.000000" y="-5.500000" angle="0.000000" angleV="0.000000" editorAngle="0" activated="0" exploded="0" flippedX="0" flippedY="0">
<Engine fuel="0.000000"/>
</Part>

View File

@ -20,15 +20,26 @@ logger.logfile_level = "Log file record level : "
logger.logfile_fmt = "Log file record format : "
logger.logfile_datefmt = "Log file date format : "
game_start.at = "Game MainThread start at: {}"
mod.find.start = "Checking Mod: {}"
mod.find.faild.no_spec = "importlib can't find spec"
mod.find.faild.no_mod_folder = "Can't find mod folder"
mod.find.done = "All Mod checked"
mod.load.start = "Loading Mod: {}"
mod.load.info = "mod id: {} version: {}"
mod.load.faild.info = "Mod load failed: {} error info: {}"
mod.load.faild.no_mod_class = "Can't find Mod class"
mod.load.done = "All Mod loaded"
[mod]
list = "Mod list: "
find.finded = "Mod founded: {}"
load.start = "Loading Mod in path {}"
load.use_time = "Mod loading has used: {} second"
load.done = "All Mod loaded"
load.loading = "Loading Mod: {}"
load.faild.error = "Mod loading faild: {} error: {}"
load.faild.not_exist = "Mod loading faild: {} mod path not exist"
load.faild.no_mod_class = "Mod loading faild: {} no Mod class"
init.success = "mod id: {} version: {}"
init.faild = "Mod init faild: {} error: {}\nstack: {}"
init.use_time = "Mod init has used: {} second"
event.error = "Mod event {} error {} Mod: {}\nstack: {}"
unload.not_find = "Mod unload faild: {} no Mod found"
unload.faild = "Mod unload faild: {} error: {}\nstack: {}"
unload.success = "Mod unload success: {}"
reload.faild.not_find = "Mod reload faild: {} no Mod found, trying to find mod again"
reload.success = "Mod reload success: {}"
[client]
setup.start = "Client start loading"
@ -54,8 +65,6 @@ text.input = "input text \"{}\""
text.new_line = "new line"
text.motion = "text move {}"
text.motion_select = "text select {}"
command.text = "input command: {}"
message.text = "input message: {}"
libs.local = "using local pyglet, version: {}"
libs.outer = "using global pyglet, version: {}\n(may cause bug)"
fonts.found = "found fonts in font lib: {}"
@ -63,6 +72,9 @@ fonts.load = "loading fonts: {}"
game.stop_get = "Received closing commands from {}, game closing"
game.stop = "game closing, saving data……"
game.end = "game closed"
command.text = "input command: {}"
message.text = "input message: {}"
command.mods.reload.no_mod_id = "no mod id specified"
[server]
setup.start = "Server start loading"
@ -71,17 +83,6 @@ os.pid_is = "Server PID: {} PPID: {}"
[game]
input = "console"
command = "in game commands"
window = "window"
command = "in game commands"
require_DR_rs = "require DR_rs module"
[client.sr1_render]
setup.start = "SR1 Renderer start loading"
setup.use_time = "SR1 Renderer loading has used: {} second"
xml.loading = "Loading XML file: {}"
xml.load_done = "XML file loaded"
xml.load_time = "XML file loading has used: {} second"
ship.load = "Loading ship: {}"
ship.load_time = "Ship loading has used: {} second"
ship.info = "Ship info:\n- Parts: {}\n- Weight: {}"
ship.render.done = "Ship render done"

View File

@ -20,16 +20,26 @@ logger.logfile_level = "日志文件记录级别:"
logger.logfile_fmt = "日志文件记录格式:"
logger.logfile_datefmt = "日志文件日期格式:"
game_start.at = "游戏主线程开始于:"
mod.find.start = "正在校验 Mod: {}"
mod.find.faild.no_spec = "importlib 无法找到 spec"
mod.find.faild.no_mod_folder = "没有找到 Mod 文件夹"
mod.find.done = "所有 Mod 校验完成"
mod.load.start = "正在加载 Mod: {}"
mod.load.info = "mod id: {} 版本号: {}"
mod.load.faild.info = "Mod 加载失败: {} 错误信息: {}"
mod.load.faild.no_mod_class = "没有找到 Mod 类"
mod.load.done = "所有 Mod 加载完成"
mod.event.error = "Mod 事件 {} 发生错误 {} Mod: {}"
[mod]
list = "Mod 列表: "
find.finded = "找到 Mod: {}"
load.start = "开始加载路径 {} 下的 Mod"
load.use_time = "Mod 加载消耗时间: {} 秒"
load.done = "所有 Mod 加载完成"
load.loading = "正在加载 Mod: {}"
load.faild.error = "Mod 加载失败: {} 错误信息: {}"
load.faild.not_exist = "Mod 加载失败: {} mod 路径不存在"
load.faild.no_mod_class = "Mod 加载失败: {} 没有找到 Mod 类"
init.success = "mod id: {} 版本号: {}"
init.faild = "Mod 初始化失败: {} 错误信息: {}\n堆栈信息: {}"
init.use_time = "Mod 初始化消耗时间: {} 秒"
event.error = "Mod 事件 {} 发生错误 {} Mod: {}\n堆栈信息: {}"
unload.faild.not_find = "Mod 卸载失败: {} 没有找到 Mod"
unload.faild.error = "Mod 卸载失败: {} 错误信息: {}\n堆栈信息: {}"
unload.success = "Mod 卸载成功: {}"
reload.faild.not_find = "Mod 重载失败: {} 没有找到 Mod 原始路径,正在尝试重新查找 mod"
reload.success = "Mod 重载成功: {}"
[client]
setup.start = "客户端加载开始"
@ -55,8 +65,6 @@ text.input = "输入字符 \"{}\""
text.new_line = "换行"
text.motion = "光标移动 {}"
text.motion_select = "光标选择 {}"
command.text = "输入命令: {}"
message.text = "输入信息: {}"
libs.local = "正在使用本地 pyglet 库 版本为: {}"
libs.outer = "正在使用全局 pyglet 库 版本为: {}\n(可能会造成bug因为本地库版本为2.0dev9)"
fonts.found = "在字体列表中找到以下字体库: {}"
@ -64,6 +72,9 @@ fonts.load = "正在加载字体: {}"
game.stop_get = "从{}传入关闭指令,关闭游戏中"
game.stop = "游戏正在关闭,保存数据中···"
game.end = "游戏已经关闭"
command.text = "输入命令: {}"
message.text = "输入信息: {}"
command.mods.reload.no_mod_id = "没有指定 mod id"
[server]
setup.start = "服务端开始加载"
@ -72,18 +83,6 @@ os.pid_is = "服务端 PID: {} PPID: {}"
[game]
input = "控制台"
command = "游戏内命令行"
window = "窗口"
command = "游戏内命令行"
require_DR_rs = "需要 DR_rs 模块"
[client.sr1_render]
setup.start = "SR1 渲染器开始载入"
setup.use_time = "SR1 渲染器载入消耗时间: {} 秒"
xml.loading = "正在加载XML文件: {}"
xml.load_done = "XML 文件加载完成"
xml.load_time = "XML 文件加载消耗时间: {} 秒"
ship.load = "正在加载飞船: {}"
ship.load_time = "飞船加载消耗时间: {} 秒"
#ship.info = "飞船信息:\n- 部件数量: {}\n- 部件重量: {}\n- 文件大小: {}"
ship.info = "飞船信息:\n- 部件数量: {}\n- 部件重量: {}"
ship.render.done = "飞船渲染完成"

View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 121 B

After

Width:  |  Height:  |  Size: 121 B

View File

Before

Width:  |  Height:  |  Size: 133 B

After

Width:  |  Height:  |  Size: 133 B

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 450 B

View File

Before

Width:  |  Height:  |  Size: 517 B

After

Width:  |  Height:  |  Size: 517 B

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 140 B

After

Width:  |  Height:  |  Size: 140 B

View File

Before

Width:  |  Height:  |  Size: 932 B

After

Width:  |  Height:  |  Size: 932 B

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 775 B

After

Width:  |  Height:  |  Size: 775 B

View File

Before

Width:  |  Height:  |  Size: 540 B

After

Width:  |  Height:  |  Size: 540 B

Some files were not shown because too many files have changed in this diff Show More