Merge branch 'main' into mod/five_point

This commit is contained in:
shenjack 2023-05-14 02:28:24 +08:00
commit 74c38ecfea
36 changed files with 921 additions and 577 deletions

83
.github/workflows/dr_rs.yml vendored Normal file
View File

@ -0,0 +1,83 @@
# 名称
name: Build DR rs
# 运行条件
on:
# 触发条件
push:
pull_request:
workflow_dispatch:
# 主任务
jobs:
build-dr-rs:
if: ${{!startsWith(github.event.ref, 'refs/tags/') && contains(github.event.head_commit.message, '[build rs skip]') == false }}
# 全都要!
strategy:
fail-fast: false
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
python-version: ["3.8", "3.10"] # 3.11 still not support by nuitka
runs-on: ${{ matrix.os }}
steps:
# Check-out repository
- name: Check out
uses: actions/checkout@v3
# 获取短 sha
- name: Get short commit sha
id: get_short_sha
shell: pwsh
run: |
# short_sha=$(echo ${GITHUB_SHA} | cut -c1-7)
# echo "short_sha=$short_sha" >> $GITHUB_OUTPUT
# echo $GITHUB_OUTPUT
$short_sha = Write-Output $env:GITHUB_SHA
$short_sha = $short_sha.substring(1,7)
Write-Output $short_sha
Write-Output "short_sha=$short_sha" >> $env:GITHUB_ENV
# 安装 Python
- name: Setup Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }} # 为了支持 win7 我还是得用 3.8
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
cache: 'pip'
cache-dependency-path: |
**/requirement-dev.txt
.github/workflows/requirement.txt
# 安装依赖
- name: Install env
run: |
pip install -r requirement-dev.txt
# 提取 DR 版本和其他信息
- name: Display Difficult-Rocket info
id: DR_info
shell: pwsh
run: |
$infos = python .github/workflows/get_info.py -github
Write-Output $infos >> $env:GITHUB_ENV
python .github/workflows/get_info.py
# 编译 dr_rs
- name: Build dr_rs
shell: pwsh
run: |
Set-Location mods/dr_game/Difficult_Rocket_rs/src
python setup.py build
python post_build.py
python setup.py clean
# Uploads artifact
- name: Upload Artifact
uses: actions/upload-artifact@v3
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

View File

@ -1,5 +1,5 @@
# 名称
name: Build
name: Build DR nuitka
# 运行条件
on:
@ -10,7 +10,7 @@ on:
# 主任务
jobs:
build:
build-nuitka:
if: ${{!startsWith(github.event.ref, 'refs/tags/') && contains(github.event.head_commit.message, '[build skip]') == false }}
# 全都要!
strategy:

View File

@ -1,6 +1,6 @@
{
"rust-analyzer.linkedProjects": [
"libs/Difficult_Rocket_rs/src/Cargo.toml",
"mods/dr_game/Difficult_Rocket_rs/src/Cargo.toml",
"libs/pyglet_rs/src/Cargo.toml",
],
"python.analysis.extraPaths": [

View File

@ -5,7 +5,6 @@
# -------------------------------
import sys
import warnings
import importlib
import traceback
import contextlib
@ -14,7 +13,7 @@ from pathlib import Path
from typing import Optional, List, Tuple
from Difficult_Rocket.api.types import Options
from Difficult_Rocket.utils.new_thread import new_thread
from Difficult_Rocket.utils.thread import new_thread
from libs.MCDR.version import Version

View File

@ -4,7 +4,7 @@
# All rights reserved
# -------------------------------
from typing import List, TYPE_CHECKING
from typing import List, TYPE_CHECKING, TypeVar
from os import PathLike
# from pyglet.window import Window
@ -15,6 +15,8 @@ from Difficult_Rocket.command.api import CommandText
if TYPE_CHECKING:
from Difficult_Rocket.client import ClientWindow
else:
ClientWindow = TypeVar("ClientWindow")
class BaseScreen(EventDispatcher):
@ -22,28 +24,28 @@ class BaseScreen(EventDispatcher):
DR 页面API
"""
def __init__(self, main_window: "ClientWindow"):
def __init__(self, main_window: ClientWindow):
super().__init__()
self.focus = False
self.window_pointer = main_window
if TYPE_CHECKING:
def on_command(self, command: CommandText, window: "ClientWindow"):
def on_command(self, command: CommandText, window: ClientWindow):
"""
命令输入事件
"""
def on_message(self, message: CommandText, window: "ClientWindow"):
def on_message(self, message: CommandText, window: ClientWindow):
"""
消息输入事件
"""
def draw_update(self, tick: float, window: "ClientWindow"):
def draw_update(self, tick: float, window: ClientWindow):
"""
画面更新
"""
def draw_batch(self, window: "ClientWindow"):
def draw_batch(self, window: ClientWindow):
"""
画面绘制
"""
@ -51,7 +53,7 @@ class BaseScreen(EventDispatcher):
"""
Pyglet 定义的事件
"""
def on_activate(self, window: "ClientWindow"):
def on_activate(self, window: ClientWindow):
"""The window was activated.
This event can be triggered by clicking on the title bar, bringing
@ -62,7 +64,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_close(self, window: "ClientWindow"):
def on_close(self, window: ClientWindow):
"""The user attempted to close the window.
This event can be triggered by clicking on the "X" control box in
@ -75,7 +77,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_context_lost(self, window: "ClientWindow"):
def on_context_lost(self, window: ClientWindow):
"""The window's GL context was lost.
When the context is lost no more GL methods can be called until it
@ -87,7 +89,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_context_state_lost(self, window: "ClientWindow"):
def on_context_state_lost(self, window: ClientWindow):
"""The state of the window's GL context was lost.
pyglet may sometimes need to recreate the window's GL context if
@ -100,7 +102,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_deactivate(self, window: "ClientWindow"):
def on_deactivate(self, window: ClientWindow):
"""The window was deactivated.
This event can be triggered by clicking on another application
@ -110,7 +112,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_draw(self, window: "ClientWindow"):
def on_draw(self, window: ClientWindow):
"""The window contents must be redrawn.
The `EventLoop` will dispatch this event when the window
@ -130,7 +132,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_expose(self, window: "ClientWindow"):
def on_expose(self, window: ClientWindow):
"""A portion of the window needs to be redrawn.
This event is triggered when the window first appears, and any time
@ -145,7 +147,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_file_drop(self, x: int, y: int, paths: List[PathLike] , window: "ClientWindow"):
def on_file_drop(self, x: int, y: int, paths: List[PathLike] , window: ClientWindow):
"""File(s) were dropped into the window, will return the position of the cursor and
a list of paths to the files that were dropped.
@ -154,7 +156,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_hide(self, window: "ClientWindow"):
def on_hide(self, window: ClientWindow):
"""The window was hidden.
This event is triggered when a window is minimised
@ -163,7 +165,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_key_press(self, symbol: int, modifiers: int, window: "ClientWindow"):
def on_key_press(self, symbol: int, modifiers: int, window: ClientWindow):
"""A key on the keyboard was pressed (and held down).
Since pyglet 1.1 the default handler dispatches the :py:meth:`~pyglet.window.Window.on_close`
@ -178,7 +180,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_key_release(self, symbol: int, modifiers: int, window: "ClientWindow"):
def on_key_release(self, symbol: int, modifiers: int, window: ClientWindow):
"""A key on the keyboard was released.
:Parameters:
@ -190,7 +192,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_mouse_motion(self, x: int, y: int, dx: int, dy: int, window: "ClientWindow"):
def on_mouse_motion(self, x: int, y: int, dx: int, dy: int, window: ClientWindow):
"""The mouse was moved with no buttons held down.
:Parameters:
@ -206,7 +208,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_mouse_drag(self, x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int, window: "ClientWindow"):
def on_mouse_drag(self, x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int, window: ClientWindow):
"""The mouse was moved with one or more mouse buttons pressed.
This event will continue to be fired even if the mouse leaves
@ -230,7 +232,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_mouse_press(self, x: int, y: int, button: int, modifiers: int, window: "ClientWindow"):
def on_mouse_press(self, x: int, y: int, button: int, modifiers: int, window: ClientWindow):
"""A mouse button was pressed (and held down).
:Parameters:
@ -247,7 +249,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_mouse_release(self, x: int, y: int, button: int, modifiers: int, window: "ClientWindow"):
def on_mouse_release(self, x: int, y: int, button: int, modifiers: int, window: ClientWindow):
"""A mouse button was released.
:Parameters:
@ -264,7 +266,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_mouse_scroll(self, x: int, y: int, scroll_x: float, scroll_y: float, window: "ClientWindow"):
def on_mouse_scroll(self, x: int, y: int, scroll_x: float, scroll_y: float, window: ClientWindow):
"""The mouse wheel was scrolled.
Note that most mice have only a vertical scroll wheel, so
@ -285,7 +287,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_mouse_enter(self, x: int, y: int, window: "ClientWindow"):
def on_mouse_enter(self, x: int, y: int, window: ClientWindow):
"""The mouse was moved into the window.
This event will not be triggered if the mouse is currently being
@ -300,7 +302,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_mouse_leave(self, x: int, y: int, window: "ClientWindow"):
def on_mouse_leave(self, x: int, y: int, window: ClientWindow):
"""The mouse was moved outside of the window.
This event will not be triggered if the mouse is currently being
@ -316,7 +318,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_move(self, x: int, y: int, window: "ClientWindow"):
def on_move(self, x: int, y: int, window: ClientWindow):
"""The window was moved.
:Parameters:
@ -331,7 +333,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_refresh(self, dt, window: "ClientWindow"):
def on_refresh(self, dt, window: ClientWindow):
"""The window contents must be redrawn.
The `EventLoop` will dispatch this event when the window
@ -350,7 +352,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_resize(self, width: int, height: int, window: "ClientWindow"):
def on_resize(self, width: int, height: int, window: ClientWindow):
"""The window was resized.
The window will have the GL context when this event is dispatched;
@ -365,7 +367,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_show(self, window: "ClientWindow"):
def on_show(self, window: ClientWindow):
"""The window was shown.
This event is triggered when a window is restored after being
@ -374,7 +376,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_text(self, text: str, window: "ClientWindow"):
def on_text(self, text: str, window: ClientWindow):
"""The user input some text.
Typically this is called after :py:meth:`~pyglet.window.Window.on_key_press` and before
@ -393,7 +395,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_text_motion(self, motion: int, window: "ClientWindow"):
def on_text_motion(self, motion: int, window: ClientWindow):
"""The user moved the text input cursor.
Typically this is called after :py:meth:`~pyglet.window.Window.on_key_press` and before
@ -429,7 +431,7 @@ class BaseScreen(EventDispatcher):
:event:
"""
def on_text_motion_select(self, motion: int, window: "ClientWindow"):
def on_text_motion_select(self, motion: int, window: ClientWindow):
"""The user moved the text input cursor while extending the
selection.

View File

@ -10,10 +10,3 @@ mail: 3695888@qq.com
github: @shenjackyuanjie
gitee: @shenjackyuanjie
"""
# system function
from xml.etree import ElementTree as ET
# Difficult_Rocket function
# libs function

View File

@ -32,12 +32,11 @@ from Difficult_Rocket.utils import tools
from Difficult_Rocket.api.types import Options
from Difficult_Rocket.command import line, tree
from Difficult_Rocket.utils.translate import tr
from Difficult_Rocket import DR_runtime, DR_option
from Difficult_Rocket import DR_runtime
from Difficult_Rocket.api.screen import BaseScreen
from Difficult_Rocket.utils.new_thread import new_thread
from Difficult_Rocket.utils.thread import new_thread
from Difficult_Rocket.client.fps.fps_log import FpsLogger
from Difficult_Rocket.client.guis.widgets import InputBox
from Difficult_Rocket.exception.command import CommandError
from Difficult_Rocket.exception.language import LanguageNotFound
from Difficult_Rocket.client.screen import DRScreen, DRDEBUGScreen

View File

@ -27,7 +27,7 @@ from pyglet.graphics import Batch, Group
# from DR
from Difficult_Rocket.utils import translate
from Difficult_Rocket.command.api import CommandText
from Difficult_Rocket.utils.new_thread import new_thread
from Difficult_Rocket.utils.thread import new_thread
class CommandLineTextEntry(widgets.TextEntry):

View File

@ -19,7 +19,7 @@ import traceback
import threading
import multiprocessing
from pathlib import Path
from typing import Optional, Union, TextIO
from typing import Optional, Union
# import psutil
# for more system info
@ -73,18 +73,6 @@ def to_code(string: str):
return f'`{string}`'
def write_markdown_tablet(crash_file: TextIO, tablet: list) -> None:
a_len = max(tablet[1], 6)
b_len = max(tablet[2], 5)
c_len = max(tablet[3], 10)
crash_file.write(f'\n| Option{" " * (a_len - 4)} | Value{" " * (b_len - 3)} | Value Type{" " * (c_len - 8)} |\n')
crash_file.write(f'|:{"-" * (a_len + 3)}|:{"-" * (b_len + 3)}|:{"-" * (c_len + 3)}|\n')
for a, b, c in tablet[0]:
b, c = str(b), str(c)
crash_file.write(
f'| `{a}`{" " * (a_len - len(a))} | `{b}`{" " * (b_len - len(b))} | `{c}`{" " * (c_len - len(c))} |\n')
def create_crash_report(info: Optional[str] = None) -> None:
crash_info = crash_info_handler(info)
if 'crash_report' not in os.listdir('./'):
@ -116,8 +104,10 @@ def write_info_to_cache(cache_stream):
cache_stream.write(markdown_line_handler(f'DR Version: {Difficult_Rocket.game_version}', level=1))
cache_stream.write(markdown_line_handler(f'DR language: {DR_runtime.language}', level=1))
cache_stream.write(markdown_line_handler(f'Running Dir: {Path(os.curdir).resolve()}', level=1))
write_options(DR_runtime, cache_stream, DR_configs)
write_options(DR_option, cache_stream, Process_message)
cache_stream.write(f"\n{DR_runtime.as_markdown()}")
cache_stream.write(DR_configs)
cache_stream.write(f"\n{DR_option.as_markdown()}")
cache_stream.write(Process_message)
for process in all_process:
process: multiprocessing.Process
cache_stream.write(markdown_line_handler(f'{process.name}', code=True))
@ -148,13 +138,6 @@ def write_info_to_cache(cache_stream):
cache_stream.write(markdown_line_handler(f'version: {to_code(platform.version())}', level=1))
def write_options(arg0, cache_stream, arg2):
result = arg0.option_with_len()
write_markdown_tablet(crash_file=cache_stream, tablet=result)
# # DR 的游戏设置
cache_stream.write(arg2)
if __name__ == '__main__':
os.chdir('../../')
try:

View File

@ -86,3 +86,4 @@ class ModInfo(Options):
def on_unload(self, game: Game):
""" 卸载时调用 """
print(f'Mod {self.mod_id} unloaded')

View File

@ -1,155 +0,0 @@
# -------------------------------
# Difficult Rocket
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
# All rights reserved
# -------------------------------
import functools
import inspect
import threading
from typing import Optional, Callable, Union, List
"""
This part of code come from MCDReforged(https://github.com/Fallen-Breath/MCDReforged)
Very thanks to Fallen_Breath and other coder who helped MCDR worked better
GNU Lesser General Public License v3.0GNU LGPL v3)
(have some changes)
"""
__all__ = [
'new_thread',
'FunctionThread'
]
record_thread = False
record_destination: List[Callable[['FunctionThread'], None]] = []
def copy_signature(target: Callable, origin: Callable) -> Callable:
"""
Copy the function signature of origin into target
"""
# https://stackoverflow.com/questions/39926567/python-create-decorator-preserving-function-arguments
target.__signature__ = inspect.signature(origin)
return target
class FunctionThread(threading.Thread):
"""
A Thread subclass which is used in decorator :func:`new_thread` to wrap a synchronized function call
"""
__NONE = object()
def __init__(self, target, name, args, kwargs, daemon):
super().__init__(target=target, args=args, kwargs=kwargs, name=name, daemon=daemon)
self.__return_value = self.__NONE
self.__error = None
def wrapped_target(*args_, **kwargs_):
try:
self.__return_value = target(*args_, **kwargs_)
except Exception as e:
self.__error = e
raise e from None
self._target = wrapped_target
def get_return_value(self, block: bool = False, timeout: Optional[float] = None):
"""
Get the return value of the original function
If an exception has occurred during the original function call, the exception will be risen again here
Examples::
>>> import time
>>> @new_thread
... def do_something(text: str):
... time.sleep(1)
... return text
>>> do_something('task').get_return_value(block=True)
'task'
:param block: If it should join the thread before getting the return value to make sure the function invocation finishes
:param timeout: The maximum timeout for the thread join
:raise RuntimeError: If the thread is still alive when getting return value. Might be caused by ``block=False``
while the thread is still running, or thread join operation times out
:return: The return value of the original function
"""
if block:
self.join(timeout)
if self.__return_value is self.__NONE:
if self.is_alive():
raise RuntimeError('The thread is still running')
raise self.__error
return self.__return_value
def new_thread(arg: Optional[Union[str, Callable]] = None,
daemon: bool = False,
log_thread: bool = True):
"""
This is a one line solution to make your function executes in parallels.
When decorated with this decorator, functions will be executed in a new daemon thread
This decorator only changes the return value of the function to the created ``Thread`` object.
Beside the return value, it reserves all signatures of the decorated function,
so you can safely use the decorated function as if there's no decorating at all
It's also a simple compatible upgrade method for old MCDR 0.x plugins
The return value of the decorated function is changed to the ``Thread`` object that executes this function
The decorated function has 1 extra field:
* ``original`` field: The original undecorated function
Examples::
>>> import time
>>> @new_thread('My Plugin Thread')
... def do_something(text: str):
... time.sleep(1)
... print(threading.current_thread().name)
>>> callable(do_something.original)
True
>>> t = do_something('foo')
>>> isinstance(t, FunctionThread)
True
>>> t.join()
My Plugin Thread
:param arg: A :class:`str`, the name of the thread. It's recommend to specify the thread name, so when you
log something by ``server.logger``, a meaningful thread name will be displayed
instead of a plain and meaningless ``Thread-3``
:param daemon: If the thread should be a daemon thread
:param log_thread: If the thread should be logged to callback defined in record_destination
"""
def wrapper(func):
@functools.wraps(func) # to preserve the origin function information
def wrap(*args, **kwargs):
thread = FunctionThread(target=func, args=args, kwargs=kwargs, name=thread_name, daemon=daemon)
if record_thread:
for destination in record_destination:
destination(thread)
thread.start()
return thread
# bring the signature of the func to the wrap function
# so inspect.getfullargspec(func) works correctly
copy_signature(wrap, func)
wrap.original = func # access this field to get the original function
return wrap
# Directly use @new_thread without ending brackets case, e.g. @new_thread
if isinstance(arg, Callable):
thread_name = None
return wrapper(arg)
# Use @new_thread with ending brackets case, e.g. @new_thread('A'), @new_thread()
else:
thread_name = arg
return wrapper

View File

@ -5,6 +5,7 @@
# -------------------------------
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
@ -38,7 +39,16 @@ class OptionNotFound(OptionsError):
class Options:
"""
Difficult Rocket 的游戏配置的存储基类
一个用于存储选项 / 提供 API 定义 的类
用法:
存储配置: 继承 Options
在类里定义 option: typing
(可选 定义 name: str = 'Option Base' 用于在打印的时候显示名字)
提供 API 接口: 继承 Options
在类里定义 option: typing
定义 一些需要的方法
子类: 继承 新的 Options
实现定义的方法
"""
name = 'Option Base'
cached_options: Dict[str, Union[str, Any]] = {}
@ -50,7 +60,7 @@ class Options:
:param kwargs:
"""
if TYPE_CHECKING:
self.options: Dict[str, Union[Callable, object]] = {}
self._options: Dict[str, Union[Callable, object]] = {}
self.flush_option()
for option, value in kwargs.items():
if option not in self.cached_options:
@ -66,7 +76,7 @@ class Options:
self.flush_option()
if TYPE_CHECKING:
options: Dict[str, Union[Callable, object]] = {}
_options: Dict[str, Union[Callable, object]] = {}
def init(self, **kwargs) -> None:
""" 如果子类定义了这个函数,则会在 __init__ 之后调用这个函数 """
@ -89,9 +99,9 @@ class Options:
if values[ann] is None:
values[ann] = self.__annotations__[ann]
if not hasattr(self, 'options'):
self.options: Dict[str, Union[Callable, object]] = {}
for option, a_fun in self.options.items(): # 获取额外内容
if not hasattr(self, '_options'):
self._options: Dict[str, Union[Callable, object]] = {}
for option, a_fun in self._options.items(): # 获取额外内容
values[option] = a_fun
for option, a_fun in values.items(): # 检查是否为 property
@ -123,26 +133,56 @@ class Options:
self.cached_options = self.option()
return self.cached_options
def option_with_len(self) -> List[Union[List[Tuple[str, Any, Any]], int, Any]]:
def option_with_len(self) -> Tuple[List[Tuple[str, Union[Any, Type], Type]], int, int, int]:
"""
返回一个可以用于打印的 option 列表
:return:
"""
options = self.flush_option()
max_len_key = 1
max_len_value = 1
max_len_value_t = 1
option_list = []
for key, value in options.items():
value_t = value if isinstance(value, Type) else type(value)
value_t = value if isinstance(value, Type) else type(value) # 判定这个类型 是不是 基本类型
max_len_key = max(max_len_key, len(key))
max_len_value = max(max_len_value, len(str(value)))
max_len_value_t = max(max_len_value_t, len(str(value_t)))
option_list.append((key, value, value_t))
return [option_list, max_len_key, max_len_value, max_len_value_t]
return option_list, max_len_key, max_len_value, max_len_value_t
def as_markdown(self) -> str:
"""
返回一个 markdown 格式的 option 字符串
:return: markdown 格式的 option 字符串
"""
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'))
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')
for option, value, value_t in value[0]:
cache.write(f"| `{option}`{' '* (option_len - len(option))} "
f"| `{value}`{' '* (value_len - len(str(value)))} "
f"| `{value_t}`{' '* (value_type_len - len(str(value_t)))} |\n")
result = cache.getvalue()
cache.close()
return result
@classmethod
def add_option(cls, name: str, value: Union[Callable, object]) -> Dict:
if not hasattr(cls, 'options'):
cls.options: Dict[str, Union[Callable, object]] = {}
cls.options[name] = value
return cls.options
"""
向配置类中添加一个额外的配置
:param name: 配置的名字
:param value: 用于获取配置的函数或者类
:return: 配置类的所有配置
"""
if not hasattr(cls, '_options'):
cls._options: Dict[str, Union[Callable, object]] = {}
cls._options[name] = value
return cls._options
@staticmethod
def init_option(options_class: Type['Options'], init_value: Optional[dict] = None) -> 'Options':

View File

@ -11,20 +11,24 @@ github: @shenjackyuanjie
gitee: @shenjackyuanjie
"""
import functools
import inspect
import threading
from typing import Union
from threading import Lock
from typing import Optional, Callable, Union, List
from Difficult_Rocket import DR_option, crash
from Difficult_Rocket.exception.threading import LockTimeOutError
__all__ = [
'new_thread',
'FunctionThread',
'ThreadLock',
'record_thread',
'record_destination',
]
class Threads(threading.Thread):
def run(self):
if DR_option.record_thread:
crash.all_thread.append(self)
super().run()
record_thread = False
record_destination: List[Callable[['FunctionThread'], None]] = []
class ThreadLock:
@ -44,6 +48,143 @@ class ThreadLock:
self.lock.release()
"""
This part of code come from MCDReforged(https://github.com/Fallen-Breath/MCDReforged)
Very thanks to Fallen_Breath and other coder who helped MCDR worked better
GNU Lesser General Public License v3.0GNU LGPL v3)
(have some changes)
"""
def copy_signature(target: Callable, origin: Callable) -> Callable:
"""
Copy the function signature of origin into target
"""
# https://stackoverflow.com/questions/39926567/python-create-decorator-preserving-function-arguments
target.__signature__ = inspect.signature(origin)
return target
class FunctionThread(threading.Thread):
"""
A Thread subclass which is used in decorator :func:`new_thread` to wrap a synchronized function call
"""
__NONE = object()
def __init__(self, target, name, args, kwargs, daemon):
super().__init__(target=target, args=args, kwargs=kwargs, name=name, daemon=daemon)
self.__return_value = self.__NONE
self.__error = None
def wrapped_target(*args_, **kwargs_):
try:
self.__return_value = target(*args_, **kwargs_)
except Exception as e:
self.__error = e
raise e from None
self._target = wrapped_target
def get_return_value(self, block: bool = False, timeout: Optional[float] = None):
"""
Get the return value of the original function
If an exception has occurred during the original function call, the exception will be risen again here
Examples::
>>> import time
>>> @new_thread
... def do_something(text: str):
... time.sleep(1)
... return text
>>> do_something('task').get_return_value(block=True)
'task'
:param block: If it should join the thread before getting the return value to make sure the function invocation finishes
:param timeout: The maximum timeout for the thread join
:raise RuntimeError: If the thread is still alive when getting return value. Might be caused by ``block=False``
while the thread is still running, or thread join operation times out
:return: The return value of the original function
"""
if block:
self.join(timeout)
if self.__return_value is self.__NONE:
if self.is_alive():
raise RuntimeError('The thread is still running')
raise self.__error
return self.__return_value
def new_thread(arg: Optional[Union[str, Callable]] = None,
daemon: bool = False,
log_thread: bool = True):
"""
This is a one line solution to make your function executes in parallels.
When decorated with this decorator, functions will be executed in a new daemon thread
This decorator only changes the return value of the function to the created ``Thread`` object.
Beside the return value, it reserves all signatures of the decorated function,
so you can safely use the decorated function as if there's no decorating at all
It's also a simple compatible upgrade method for old MCDR 0.x plugins
The return value of the decorated function is changed to the ``Thread`` object that executes this function
The decorated function has 1 extra field:
* ``original`` field: The original undecorated function
Examples::
>>> import time
>>> @new_thread('My Plugin Thread')
... def do_something(text: str):
... time.sleep(1)
... print(threading.current_thread().name)
>>> callable(do_something.original)
True
>>> t = do_something('foo')
>>> isinstance(t, FunctionThread)
True
>>> t.join()
My Plugin Thread
:param arg: A :class:`str`, the name of the thread. It's recommend to specify the thread name, so when you
log something by ``server.logger``, a meaningful thread name will be displayed
instead of a plain and meaningless ``Thread-3``
:param daemon: If the thread should be a daemon thread
:param log_thread: If the thread should be logged to callback defined in record_destination
"""
def wrapper(func):
@functools.wraps(func) # to preserve the origin function information
def wrap(*args, **kwargs):
thread = FunctionThread(target=func, args=args, kwargs=kwargs, name=thread_name, daemon=daemon)
if record_thread:
for destination in record_destination:
destination(thread)
thread.start()
return thread
# bring the signature of the func to the wrap function
# so inspect.getfullargspec(func) works correctly
copy_signature(wrap, func)
wrap.original = func # access this field to get the original function
return wrap
# Directly use @new_thread without ending brackets case, e.g. @new_thread
if isinstance(arg, Callable):
thread_name = None
return wrapper(arg)
# Use @new_thread with ending brackets case, e.g. @new_thread('A'), @new_thread()
else:
thread_name = arg
return wrapper
if __name__ == "__main__":
from Difficult_Rocket.exception import TestError

View File

@ -7,8 +7,8 @@ fonts_folder = "libs/fonts"
[window]
style = "None"
width = 1305
height = 722
width = 1261
height = 935
visible = true
gui_scale = 1
caption = "Difficult Rocket v{DR_version}|DR_rs v{DR_Rust_get_version}"

View File

@ -70,6 +70,14 @@
- Completely removed the `DR_rust` part
- 现在 `client` 不会在 `setup()` 中调用 `DR_runtime``find_mods()` 方法
- Now `client` will not call the `find_mods()` method of `DR_runtime` in `setup()`
- `Difficult_Rocket.crash`
- Remove `write_options` method
- Remove `write_markdown_tablet` method
- Replace with `Option().as_markdown()`
- `Difficult_Rocket.utils.new_thread`
- Moved to `Diffiuclt_Rocket.utils.thread`
- `Difficult_Rocket.utils.thread`
- Remove `Threads`
### Changes
@ -100,6 +108,20 @@
- `Difficult_Rocket.api`
- 大重构,移除定义,改为引用
- Big refactoring, remove definition, change to reference
- `Difficult_Rocket.api.types.Options` ( `Difficult_Rocket.utils.options.Options` )
- `options` -> `_options`
- `option_with_len(self) ->`
- 修改 返回值 类型+类型注释
- Modify the return value type + type annotation
- `List[Union[List[Tuple[str, Any, Any]], int, Any]]:` -> `Tuple[List[Tuple[str, Union[Any, Type], Type]], int, int, int]`
### Add
- `Difficult_Rocket.api.types.Options` ( `Difficult_Rocket.utils.options.Options` )
- 添加 `as_markdown` 方法
- 用于方便的用人类可读的 Markdown 格式 直接输出一个已经实例化的 `Options` 类的所有字段
- Add `as_markdown` method
- Used to easily output all fields of an instantiated `Options` class in a human-readable Markdown format
### Docs

View File

@ -9,7 +9,7 @@ import sys
from typing import TYPE_CHECKING
#: The release version
version = '2.0.5'
version = '2.0.7'
__version__ = version
MIN_PYTHON_VERSION = 3, 8

View File

@ -3,6 +3,7 @@ from .base import Display, Screen, ScreenMode, Canvas
from pyglet.libs.win32 import _user32
from pyglet.libs.win32.constants import *
from pyglet.libs.win32.types import *
from pyglet.libs.win32.context_managers import device_context
class Win32Display(Display):
@ -30,13 +31,13 @@ class Win32Screen(Screen):
self._handle = handle
def get_matching_configs(self, template):
hdc = _user32.GetDC(0)
canvas = Win32Canvas(self.display, 0, hdc)
configs = template.match(canvas)
# XXX deprecate config's being screen-specific
for config in configs:
config.screen = self
_user32.ReleaseDC(0, hdc)
with device_context(None) as hdc:
canvas = Win32Canvas(self.display, 0, hdc)
configs = template.match(canvas)
# XXX deprecate config's being screen-specific
for config in configs:
config.screen = self
return configs
def get_device_name(self):

View File

@ -991,7 +991,7 @@ def unpack_rows(rows):
to being a sequence of bytes.
"""
for row in rows:
fmt = f'!{len(row)}'
fmt = f'!{len(row)}H'
yield bytearray(struct.pack(fmt, *row))
@ -1722,7 +1722,7 @@ class Reader:
"PLTE chunk is required before bKGD chunk.")
self.background = struct.unpack('B', data)
else:
self.background = struct.unpack(f"!{self.color_planes}",
self.background = struct.unpack(f"!{self.color_planes}H",
data)
except struct.error:
raise FormatError("bKGD chunk has incorrect length.")
@ -1744,7 +1744,7 @@ class Reader:
f"tRNS chunk is not valid with colour type {self.color_type}.")
try:
self.transparent = \
struct.unpack(f"!{self.color_planes}", data)
struct.unpack(f"!{self.color_planes}H", data)
except struct.error:
raise FormatError("tRNS chunk has incorrect length.")
@ -1977,7 +1977,7 @@ class Reader:
pixels = itertrns(pixels)
targetbitdepth = None
if self.sbit:
sbit = struct.unpack(f'{len(self.sbit)}', self.sbit)
sbit = struct.unpack(f'{len(self.sbit)}B', self.sbit)
targetbitdepth = max(sbit)
if targetbitdepth > info['bitdepth']:
raise Error(f'sBIT chunk {sbit!r} exceeds bitdepth {self.bitdepth}')

View File

@ -367,16 +367,16 @@ class TruetypeInfo:
# a fuckwit.
header = _read_cmap_format4Header(self._data, offset)
seg_count = header.seg_count_x2 // 2
array_size = struct.calcsize(f'>{seg_count}')
end_count = self._read_array(f'>{seg_count}',
array_size = struct.calcsize(f'>{seg_count}H')
end_count = self._read_array(f'>{seg_count}H',
offset + header.size)
start_count = self._read_array(f'>{seg_count}',
start_count = self._read_array(f'>{seg_count}H',
offset + header.size + array_size + 2)
id_delta = self._read_array(f'>{seg_count}',
id_delta = self._read_array(f'>{seg_count}H',
offset + header.size + array_size + 2 + array_size)
id_range_offset_address = \
offset + header.size + array_size + 2 + array_size + array_size
id_range_offset = self._read_array(f'>{seg_count}',
id_range_offset = self._read_array(f'>{seg_count}H',
id_range_offset_address)
character_map = {}
for i in range(0, seg_count):

View File

@ -9,6 +9,7 @@ from pyglet.font import base
from pyglet.font import win32query
import pyglet.image
from pyglet.libs.win32.constants import *
from pyglet.libs.win32.context_managers import device_context
from pyglet.libs.win32.types import *
from pyglet.libs.win32 import _gdi32 as gdi32, _user32 as user32
from pyglet.libs.win32 import _kernel32 as kernel32
@ -195,14 +196,13 @@ class Win32Font(base.Font):
self.hfont = gdi32.CreateFontIndirectW(byref(self.logfont))
# Create a dummy DC for coordinate mapping
dc = user32.GetDC(0)
metrics = TEXTMETRIC()
gdi32.SelectObject(dc, self.hfont)
gdi32.GetTextMetricsA(dc, byref(metrics))
self.ascent = metrics.tmAscent
self.descent = -metrics.tmDescent
self.max_glyph_width = metrics.tmMaxCharWidth
user32.ReleaseDC(0, dc)
with device_context(None) as dc:
metrics = TEXTMETRIC()
gdi32.SelectObject(dc, self.hfont)
gdi32.GetTextMetricsA(dc, byref(metrics))
self.ascent = metrics.tmAscent
self.descent = -metrics.tmDescent
self.max_glyph_width = metrics.tmMaxCharWidth
def __del__(self):
gdi32.DeleteObject(self.hfont)
@ -210,22 +210,22 @@ class Win32Font(base.Font):
@staticmethod
def get_logfont(name, size, bold, italic, dpi):
# Create a dummy DC for coordinate mapping
dc = user32.GetDC(0)
if dpi is None:
dpi = 96
logpixelsy = dpi
with device_context(None) as dc:
if dpi is None:
dpi = 96
logpixelsy = dpi
logfont = LOGFONTW()
# Conversion of point size to device pixels
logfont.lfHeight = int(-size * logpixelsy // 72)
if bold:
logfont.lfWeight = FW_BOLD
else:
logfont.lfWeight = FW_NORMAL
logfont.lfItalic = italic
logfont.lfFaceName = name
logfont.lfQuality = ANTIALIASED_QUALITY
logfont = LOGFONTW()
# Conversion of point size to device pixels
logfont.lfHeight = int(-size * logpixelsy // 72)
if bold:
logfont.lfWeight = FW_BOLD
else:
logfont.lfWeight = FW_NORMAL
logfont.lfItalic = italic
logfont.lfFaceName = name
logfont.lfQuality = ANTIALIASED_QUALITY
user32.ReleaseDC(0, dc)
return logfont
@classmethod

View File

@ -71,6 +71,7 @@ appropriate typeface name and create the font using CreateFont or
CreateFontIndirect.
"""
from pyglet.libs.win32.context_managers import device_context
DEBUG = False
@ -385,36 +386,33 @@ def query(charset=DEFAULT_CHARSET):
global FONTDB
# 1. Get device context of the entire screen
hdc = user32.GetDC(None)
with device_context(None) as hdc:
# 2. Call EnumFontFamiliesExA (ANSI version)
# 2. Call EnumFontFamiliesExA (ANSI version)
# 2a. Call with empty font name to query all available fonts
# (or fonts for the specified charset)
#
# NOTES:
#
# * there are fonts that don't support ANSI charset
# * for DEFAULT_CHARSET font is passed to callback function as
# many times as charsets it supports
# 2a. Call with empty font name to query all available fonts
# (or fonts for the specified charset)
#
# NOTES:
#
# * there are fonts that don't support ANSI charset
# * for DEFAULT_CHARSET font is passed to callback function as
# many times as charsets it supports
# [ ] font name should be less than 32 symbols with terminating \0
# [ ] check double purpose - enumerate all available font names
# - enumerate all available charsets for a single font
# - other params?
# [ ] font name should be less than 32 symbols with terminating \0
# [ ] check double purpose - enumerate all available font names
# - enumerate all available charsets for a single font
# - other params?
logfont = LOGFONTW(0, 0, 0, 0, 0, 0, 0, 0, charset, 0, 0, 0, 0, '')
FONTDB = [] # clear cached FONTDB for enum_font_names callback
res = gdi32.EnumFontFamiliesExW(
hdc, # handle to device context
ctypes.byref(logfont),
enum_font_names, # pointer to callback function
0, # lParam - application-supplied data
0) # dwFlags - reserved = 0
# res here is the last value returned by callback function
# 3. Release DC
user32.ReleaseDC(None, hdc)
logfont = LOGFONTW(0, 0, 0, 0, 0, 0, 0, 0, charset, 0, 0, 0, 0, '')
FONTDB = [] # clear cached FONTDB for enum_font_names callback
res = gdi32.EnumFontFamiliesExW(
hdc, # handle to device context
ctypes.byref(logfont),
enum_font_names, # pointer to callback function
0, # lParam - application-supplied data
0) # dwFlags - reserved = 0
# res here is the last value returned by callback function
return FONTDB

View File

@ -370,7 +370,7 @@ class TextEntry(WidgetBase):
"""
def __init__(self, text, x, y, width,
color=(255, 255, 255, 255), text_color=(0, 0, 0, 255), caret_color=(0, 0, 0),
color=(255, 255, 255, 255), text_color=(0, 0, 0, 255), caret_color=(0, 0, 0, 255),
batch=None, group=None):
"""Create a text entry widget.
@ -387,8 +387,9 @@ class TextEntry(WidgetBase):
The color of the outline box in RGBA format.
`text_color` : (int, int, int, int)
The color of the text in RGBA format.
`caret_color` : (int, int, int)
The color of the caret in RGB format.
`caret_color` : (int, int, int, int)
The color of the caret when it is visible in RGBA or RGB
format.
`batch` : `~pyglet.graphics.Batch`
Optional batch to add the text entry widget to.
`group` : `~pyglet.graphics.Group`
@ -438,6 +439,7 @@ class TextEntry(WidgetBase):
def _set_focus(self, value):
self._focus = value
self._caret.visible = value
self._caret.layout = self._layout
def update_groups(self, order):
self._outline.group = Group(order=order + 1, parent=self._user_group)

View File

@ -0,0 +1,57 @@
"""
Win32 resources as handy context managers.
These are intended to help keep loader code clean & stable at the price
of a tiny bit of execution speed. Performance-critical code should avoid
using these helpers in favor of direct win32 API calls.
Before::
def loader_function(arg):
context_handle = user32.GetResource(None)
result = calculation(arg)
# Easily forgotten!
user32.ReleaseResource(context_handle)
return result
After::
def loader_function(arg):
with resource_context() as context_handle:
result = calculation(arg)
return result
"""
from contextlib import contextmanager
from typing import Optional, Generator
from ctypes.wintypes import HANDLE
from ctypes import WinError
from pyglet.libs.win32 import _user32 as user32
@contextmanager
def device_context(window_handle: Optional[int] = None) -> Generator[HANDLE, None, None]:
"""
A Windows device context wrapped as a context manager.
Args:
window_handle: A specific window handle to use, if any.
Raises:
WinError: Raises if a device context cannot be acquired or released
Yields:
HANDLE: the managed drawing context handle to auto-close.
"""
if not (_dc := user32.GetDC(window_handle)):
raise WinError()
try:
yield _dc
finally:
if not user32.ReleaseDC(None, _dc):
raise WinError()

View File

@ -85,8 +85,10 @@ class Caret:
"""
from pyglet import gl
self._layout = layout
batch = batch or layout.batch
group = layout.foreground_decoration_group
self._custom_batch = True if batch else False
self._batch = batch or layout.batch
self._group = layout.foreground_decoration_group
# Handle both 3 and 4 byte colors
r, g, b, *a = color
@ -96,7 +98,7 @@ class Caret:
colors = r, g, b, self._visible_alpha, r, g, b, self._visible_alpha
self._list = group.program.vertex_list(2, gl.GL_LINES, batch, group, colors=('Bn', colors))
self._list = self._group.program.vertex_list(2, gl.GL_LINES, batch, self._group, colors=('Bn', colors))
self._ideal_x = None
self._ideal_line = None
self._next_attributes = {}
@ -105,6 +107,21 @@ class Caret:
layout.push_handlers(self)
@property
def layout(self):
return self._layout
@layout.setter
def layout(self, layout):
if self._layout == layout and self._group == layout.group:
return
from pyglet import gl
self._layout = layout
batch = self._batch if self._custom_batch else layout.batch
self._group = layout.foreground_decoration_group
self._batch.migrate(self._list, gl.GL_LINES, self._group, batch)
def delete(self):
"""Remove the caret from its batch.

View File

@ -958,6 +958,7 @@ class TextLayout:
def group(self, group):
self._user_group = group
self._initialize_groups()
self.group_cache.clear()
self._update()
@property

56
libs/utils/nuitka.py Normal file
View File

@ -0,0 +1,56 @@
# -------------------------------
# Difficult Rocket
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
# All rights reserved
# -------------------------------
# 用于使用 nuitka 构建 DR
import platform
import traceback
from pathlib import Path
from typing import List
from Difficult_Rocket.api.types import Options
from libs.MCDR.version import Version
class Status(Options):
name = 'Nuitka Build Status'
output_path: Path = Path("./build/nuitka")
src_file: Path = Path('DR.py')
# 以下为 nuitka 的参数
use_lto: bool = False
use_clang: bool = True
use_msvc: bool = True
standalone: bool = True
company_name: str = 'tool-shenjack-workshop'
product_name: str = 'Difficult-Rocket'
product_version: Version
file_version: Version
icon_path: Path = Path('textures/icon.png')
def init(self, **kwargs) -> None:
# 非 windows 平台不使用 msvc
if platform.system() != 'Windows':
self.use_msvc = False
def load_file(self) -> bool:
try:
from Difficult_Rocket import DR_runtime
self.product_version = DR_runtime.DR_version
self.file_version = DR_runtime.Build_version
return True
except ImportError:
traceback.print_exc()
return False
def gen_subprocess_cmd(self) -> List[str]:
# macos 和 非 macos icon 参数不同
if platform.system() == 'Darwin':
icon_cmd = f"--macos-app-icon={self.icon_path.absolute()}"
elif platform.system() == 'Windows':
icon_cmd = f"--windows-icon-from-ico={self.icon_path.absolute()}"
else:
icon_cmd = ""

View File

@ -343,9 +343,9 @@ dependencies = [
[[package]]
name = "pyo3"
version = "0.18.2"
version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfb848f80438f926a9ebddf0a539ed6065434fd7aae03a89312a9821f81b8501"
checksum = "e3b1ac5b3731ba34fdaa9785f8d74d17448cd18f30cf19e0c7e7b1fdb5272109"
dependencies = [
"cfg-if",
"indoc",
@ -360,9 +360,9 @@ dependencies = [
[[package]]
name = "pyo3-build-config"
version = "0.18.2"
version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98a42e7f42e917ce6664c832d5eee481ad514c98250c49e0b03b20593e2c7ed0"
checksum = "9cb946f5ac61bb61a5014924910d936ebd2b23b705f7a4a3c40b05c720b079a3"
dependencies = [
"once_cell",
"target-lexicon",
@ -370,9 +370,9 @@ dependencies = [
[[package]]
name = "pyo3-ffi"
version = "0.18.2"
version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0707f0ab26826fe4ccd59b69106e9df5e12d097457c7b8f9c0fd1d2743eec4d"
checksum = "fd4d7c5337821916ea2a1d21d1092e8443cf34879e53a0ac653fbb98f44ff65c"
dependencies = [
"libc",
"pyo3-build-config",
@ -380,9 +380,9 @@ dependencies = [
[[package]]
name = "pyo3-macros"
version = "0.18.2"
version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978d18e61465ecd389e1f235ff5a467146dc4e3c3968b90d274fe73a5dd4a438"
checksum = "a9d39c55dab3fc5a4b25bbd1ac10a2da452c4aca13bb450f22818a002e29648d"
dependencies = [
"proc-macro2",
"pyo3-macros-backend",
@ -392,9 +392,9 @@ dependencies = [
[[package]]
name = "pyo3-macros-backend"
version = "0.18.2"
version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e0e1128f85ce3fca66e435e08aa2089a2689c1c48ce97803e13f63124058462"
checksum = "97daff08a4c48320587b5224cc98d609e3c27b6d437315bd40b605c98eeb5918"
dependencies = [
"proc-macro2",
"quote",
@ -475,9 +475,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.159"
version = "1.0.163"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065"
checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
dependencies = [
"serde_derive",
]
@ -496,9 +496,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.159"
version = "1.0.163"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
dependencies = [
"proc-macro2",
"quote",
@ -695,6 +695,6 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "xml-rs"
version = "0.8.4"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
checksum = "dc95a04ea24f543cd9be5aab44f963fa35589c99e18415c38fb2b17e133bf8d2"

View File

@ -23,11 +23,11 @@ codegen-units = 1
#features = ["serialize"]
[dependencies.serde]
version = "1.0.157"
version = "1.0.163"
features = ["derive"]
[dependencies.xml-rs]
version = "0.8.4"
version = "0.8.10"
[dependencies.serde-xml-rs]
version = "0.6.0"
@ -37,5 +37,5 @@ version = "0.17.2"
features = ["simd-stable"]
[dependencies.pyo3]
version = "0.18.1"
version = "0.18.3"
features = ["extension-module"]

View File

@ -7,9 +7,10 @@ import os
import shutil
import warnings
import traceback
from pathlib import Path
package_path = 'Difficult_Rocket_rs'
lib_path = '../lib'
lib_path = Path('../lib').resolve()
build_path = 'build'
if not os.path.exists(lib_path):
@ -28,7 +29,6 @@ for build_dir in builds:
warnings.warn(f'package not found at {build_path}/{build_dir}')
continue
for file in os.listdir(os.path.join(build_path, build_dir, package_path)):
# file_name = os.path.join(lib_path, file.replace(package_path, f'{package_path}.{DR_runtime.DR_Rust_version}'))
file_name = os.path.join(lib_path, file)
shutil.rmtree(file_name, ignore_errors=True)
try:

View File

@ -16,6 +16,16 @@ mod types;
use pyo3::prelude::*;
// const MOD_PATH: String = String::from("mods");
enum LoadState {
init,
wait_start,
pre_start,
running,
clean,
}
#[pyfunction]
fn get_version_str() -> String { "0.2.7.0".to_string() }
@ -44,3 +54,7 @@ fn module_init(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_class::<python::data::PySR1PartType>()?;
Ok(())
}
// pub fn run() {}
// fn init() {}

View File

@ -28,7 +28,7 @@ pub mod data {
fn get_name(&self) -> String { self.data.name.clone() }
#[getter]
fn get_mass(&self) -> f64 { self.data.mass }
fn get_mass(&self) -> f64 { self.data.mass.to_owned() }
}
impl PySR1PartType {
@ -39,7 +39,7 @@ pub mod data {
#[pyo3(name = "SR1PartList_rs")]
#[pyo3(text_signature = "(file_path = './configs/PartList.xml', list_name = 'NewPartList')")]
pub struct PySR1PartList {
pub part_list: SR1PartList,
pub data: SR1PartList,
}
#[pymethods]
@ -47,16 +47,16 @@ pub mod data {
#[new]
fn new(file_path: String, list_name: String) -> Self {
let raw_part_list: RawPartList = RawPartList::from_file(file_path).unwrap();
let part_list = raw_part_list.to_sr_part_list(Some(list_name));
Self { part_list }
let data = raw_part_list.to_sr_part_list(Some(list_name));
Self { data }
}
fn as_dict(&self) -> HashMap<String, PySR1PartType> {
self.part_list.cache.iter().map(|(k, v)| (k.clone(), PySR1PartType::new(v.clone()))).collect()
self.data.get_cache().iter().map(|(k, v)| (k.clone(), PySR1PartType::new(v.clone()))).collect()
}
fn get_part_type(&mut self, name: String) -> Option<PySR1PartType> {
let part_type = self.part_list.cache.get(&name);
fn get_part_type(&self, name: String) -> Option<PySR1PartType> {
let part_type = self.data.get_part_type(name.clone());
if let Some(part_type) = part_type {
Some(PySR1PartType::new(part_type.clone()))
} else {
@ -94,16 +94,16 @@ pub mod data {
// 左下角,右上角
let mut max_box = get_max_box(&self.ship.parts, &self.part_list);
todo!();
img_pos
// img_pos
}
fn get_name(&self) -> String { self.ship.name.clone() }
fn get_description(&self) -> String { self.ship.description.clone() }
fn get_lift_off(&self) -> bool { self.ship.lift_off }
fn get_lift_off(&self) -> bool { self.ship.lift_off.to_owned() }
fn get_touch_ground(&self) -> bool { self.ship.touch_ground }
fn get_touch_ground(&self) -> bool { self.ship.touch_ground.to_owned() }
}
}
@ -111,12 +111,6 @@ pub mod translate {
use pyo3::prelude::*;
use pyo3::types::PyDict;
#[derive(Clone)]
pub enum BoolString {
Bool(bool),
String(String),
}
#[pyclass]
#[pyo3(name = "TranslateConfig_rs")]
#[pyo3(text_signature = "(language, raise_error = False, replace_normal = False, add_error = False, is_result = False, keep_get = False)")]

View File

@ -124,8 +124,8 @@ pub mod part_list {
#[inline]
pub fn to_damage(&self) -> crate::types::sr1::Damage {
crate::types::sr1::Damage {
disconnect: self.disconnect,
explode: self.explode,
disconnect: self.disconnect.to_owned(),
explode: self.explode.to_owned(),
explosion_power: self.explosion_power.unwrap_or(100),
explosion_size: self.explosion_size.unwrap_or(100),
}
@ -299,9 +299,9 @@ pub mod part_list {
description: self.description.clone(),
sprite: self.sprite.clone(),
p_type: self.r#type,
mass: self.mass,
width: self.width,
height: self.height,
mass: self.mass.to_owned(),
width: self.width.to_owned(),
height: self.height.to_owned(),
friction: self.friction.unwrap_or(0.0),
category: self.category.clone().unwrap_or("".to_string()),
ignore_editor_intersections: self.ignore_editor_intersections.unwrap_or(false),
@ -419,9 +419,8 @@ pub mod ship {
pub engine: Option<Engine>,
#[serde(rename = "Pod")]
pub pod: Option<Pod>,
#[serde(rename = "partType")]
pub part_type: SR1PartTypeEnum,
pub part_type_id: String,
pub id: i64,
pub x: f64,
pub y: f64,
@ -519,58 +518,28 @@ pub mod ship {
pub child_part: i64,
}
impl Part {
/// 根据 Part 的原始数据猜测 Part 的类型
/// jundroo 我日你先人
fn guess_part_type(&self) -> SR1PartTypeEnum { todo!() }
}
impl SR1PartDataTrait for Part {
fn to_sr_part_data(&self) -> SR1PartData {
let attr = match self.part_type {
SR1PartTypeEnum::tank => SR1PartDataAttr::Tank {
fuel: if let Some(tank) = &self.tank { tank.fuel } else { 0_f64 },
},
SR1PartTypeEnum::engine => SR1PartDataAttr::Engine {
fuel: if let Some(engine) = &self.engine { engine.fuel } else { 0_f64 },
},
SR1PartTypeEnum::solar => SR1PartDataAttr::Solar {
extension: self.extension.unwrap_or(0_f64),
},
SR1PartTypeEnum::parachute => SR1PartDataAttr::Parachute {
chute_x: self.chute_x.unwrap_or(0_f64),
chute_y: self.chute_y.unwrap_or(0_f64),
chute_angle: self.chute_angle.unwrap_or(0_f64),
chute_height: self.chute_height.unwrap_or(0_f64),
inflate: i8_to_bool(self.inflate.unwrap_or(0_i8)),
inflation: i8_to_bool(self.inflation.unwrap_or(0_i8)),
deployed: i8_to_bool(self.deployed.unwrap_or(0_i8)),
rope: i8_to_bool(self.rope.unwrap_or(0_i8)),
},
SR1PartTypeEnum::pod => {
let pod = self.pod.as_ref().unwrap(); // 一定是有的,别问我为什么
let mut steps = Vec::new();
for step in &pod.stages.steps {
let mut activates = Vec::new();
for active in &step.activates {
activates.push((active.id, i8_to_bool(active.moved)))
}
steps.push(activates)
}
SR1PartDataAttr::Pod {
name: pod.name.clone(),
throttle: pod.throttle,
current_stage: pod.stages.current_stage,
steps,
}
}
_ => SR1PartDataAttr::None,
};
let attr = SR1PartDataAttr::from_raw(&self, None, true);
let part_type = attr.get_part_type();
SR1PartData {
attr,
x: self.x,
y: self.y,
id: self.id,
angle: self.angle,
angle_v: self.angle_v,
x: self.x.to_owned(),
y: self.y.to_owned(),
id: self.id.to_owned(),
angle: self.angle.to_owned(),
angle_v: self.angle_v.to_owned(),
flip_x: i8_to_bool(self.flip_x.unwrap_or(0_i8)),
flip_y: i8_to_bool(self.flip_y.unwrap_or(0_i8)),
editor_angle: self.editor_angle,
part_type: self.part_type,
editor_angle: self.editor_angle.to_owned(),
part_type,
part_type_id: self.part_type_id.clone(),
active: i8_to_bool(self.activated.unwrap_or(0_i8)),
explode: i8_to_bool(self.exploded.unwrap_or(0_i8)),
}
@ -588,8 +557,6 @@ pub mod ship {
}
let disconnected = match &self.disconnected {
Some(disconnect) => {
// let mut disconnect_parts = Vec::new();
let mut disconnect_parts = Vec::new();
for disconnected_part in &disconnect.parts {
let mut parts_vec = Vec::new();
@ -611,8 +578,8 @@ pub mod ship {
description: "".to_string(),
parts,
connections,
lift_off: i8_to_bool(self.lift_off),
touch_ground: i8_to_bool(self.touch_ground),
lift_off: i8_to_bool(self.lift_off.to_owned()),
touch_ground: i8_to_bool(self.touch_ground.to_owned()),
disconnected,
}
}

View File

@ -7,8 +7,8 @@
*/
pub mod sr1 {
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::fs;
use super::math::{Edge, Shape};
use crate::sr1_data::part_list::Damage as RawDamage;
@ -70,6 +70,22 @@ pub mod sr1 {
}
}
#[inline]
pub fn option_i8_to_option_bool(i: Option<i8>) -> Option<bool> {
match i {
Some(i) => Some(i8_to_bool(i)),
None => None,
}
}
#[inline]
pub fn option_bool_to_option_i8(b: Option<bool>) -> Option<i8> {
match b {
Some(b) => Some(bool_to_i8(b)),
None => None,
}
}
#[derive(Debug, Copy, Clone)]
pub enum SR1PartTypeAttr {
Tank {
@ -122,10 +138,10 @@ pub mod sr1 {
impl Damage {
pub fn to_raw_damage(&self) -> RawDamage {
RawDamage {
disconnect: self.disconnect,
explode: self.explode,
explosion_power: Some(self.explosion_power),
explosion_size: Some(self.explosion_size),
disconnect: self.disconnect.to_owned(),
explode: self.explode.to_owned(),
explosion_power: Some(self.explosion_power.to_owned()),
explosion_size: Some(self.explosion_size.to_owned()),
}
}
}
@ -181,38 +197,49 @@ pub mod sr1 {
#[derive(Debug, Clone)]
pub struct SR1PartList {
pub types: Vec<SR1PartType>,
pub cache: HashMap<String, SR1PartType>,
pub cache: RefCell<Option<HashMap<String, SR1PartType>>>,
pub name: String,
}
impl SR1PartList {
#[inline]
pub fn new(name: String, types: Vec<SR1PartType>) -> SR1PartList {
let mut map = HashMap::new();
for part in types.iter() {
map.insert(part.id.clone(), part.clone());
SR1PartList {
types,
cache: RefCell::new(None),
name,
}
SR1PartList { types, cache: map, name }
}
#[inline]
pub fn from_file(file_name: String) -> Option<SR1PartList> {
if let Some(raw_list) = RawPartList::from_file(file_name) {
let sr_list = raw_list.to_sr_part_list(None);
let mut map = HashMap::new();
for part in sr_list.types.iter() {
map.insert(part.id.clone(), part.clone());
}
return Some(sr_list);
}
None
}
#[inline]
pub fn get_part_type(self, type_name: String) -> Option<SR1PartType> {
if let Some(part) = self.cache.get(&type_name) {
return Some(part.clone());
pub fn get_cache(&self) -> HashMap<String, SR1PartType> {
let mut cache = self.cache.borrow_mut();
if cache.is_none() {
let mut map = HashMap::new();
for part in self.types.iter() {
map.insert(part.id.to_owned(), part.to_owned());
}
*cache = Some(map);
self.cache.replace(cache.to_owned());
}
cache.to_owned().unwrap()
}
#[inline]
pub fn get_part_type(&self, type_name: String) -> Option<SR1PartType> {
let cache = self.get_cache();
match cache.get(&type_name) {
Some(part) => Some(part.to_owned()),
None => None,
}
None
}
pub fn part_types_new(part_types: Vec<SR1PartType>, name: Option<String>) -> Self {
@ -269,9 +296,9 @@ pub mod sr1 {
let tank: Option<Tank> = match &self.attr {
Some(attr) => match attr {
SR1PartTypeAttr::Tank { fuel, dry_mass, fuel_type } => Some(Tank {
fuel: *fuel,
dry_mass: *dry_mass,
fuel_type: Some(*fuel_type),
fuel: fuel.to_owned(),
dry_mass: dry_mass.to_owned(),
fuel_type: Some(fuel_type.to_owned()),
}),
_ => None,
},
@ -287,12 +314,12 @@ pub mod sr1 {
fuel_type,
throttle_exponential,
} => Some(Engine {
power: *power,
consumption: *consumption,
throttle_exponential: Some(*throttle_exponential),
size: *size,
turn: *turn,
fuel_type: Some(*fuel_type),
power: power.to_owned(),
consumption: consumption.to_owned(),
throttle_exponential: Some(throttle_exponential.to_owned()),
size: size.to_owned(),
turn: turn.to_owned(),
fuel_type: Some(fuel_type.to_owned()),
}),
_ => None,
},
@ -301,9 +328,9 @@ pub mod sr1 {
let rcs: Option<Rcs> = match &self.attr {
Some(attr) => match attr {
SR1PartTypeAttr::Rcs { power, consumption, size } => Some(Rcs {
power: *power,
consumption: *consumption,
size: *size,
power: power.to_owned(),
consumption: consumption.to_owned(),
size: size.to_owned(),
}),
_ => None,
},
@ -311,7 +338,9 @@ pub mod sr1 {
};
let solar: Option<Solar> = match &self.attr {
Some(attr) => match attr {
SR1PartTypeAttr::Solar { charge_rate } => Some(Solar { charge_rate: *charge_rate }),
SR1PartTypeAttr::Solar { charge_rate } => Some(Solar {
charge_rate: charge_rate.to_owned(),
}),
_ => None,
},
_ => None,
@ -326,12 +355,12 @@ pub mod sr1 {
length_speed,
width,
} => Some(Lander {
max_angle: *max_angle,
min_length: *min_length,
max_length: *max_length,
angle_speed: Some(*angle_speed),
length_speed: Some(*length_speed),
width: *width,
max_angle: max_angle.to_owned(),
min_length: min_length.to_owned(),
max_length: max_length.to_owned(),
angle_speed: Some(angle_speed.to_owned()),
length_speed: Some(length_speed.to_owned()),
width: width.to_owned(),
}),
_ => None,
},
@ -353,19 +382,19 @@ pub mod sr1 {
description: self.description.clone(),
sprite: self.sprite.clone(),
r#type: self.p_type.clone(),
mass: self.mass,
width: self.width,
height: self.height,
friction: Some(self.friction),
mass: self.mass.to_owned(),
width: self.width.to_owned(),
height: self.height.to_owned(),
friction: Some(self.friction.to_owned()),
category: Some(self.category.clone()),
ignore_editor_intersections: Some(self.ignore_editor_intersections),
disable_editor_rotation: Some(self.disable_editor_rotation),
can_explode: Some(self.can_explode),
cover_height: Some(self.cover_height),
sandbox_only: Some(self.sandbox_only),
drag: Some(self.drag),
hidden: Some(self.hidden),
buoyancy: Some(self.buoyancy),
ignore_editor_intersections: Some(self.ignore_editor_intersections.to_owned()),
disable_editor_rotation: Some(self.disable_editor_rotation.to_owned()),
can_explode: Some(self.can_explode.to_owned()),
cover_height: Some(self.cover_height.to_owned()),
sandbox_only: Some(self.sandbox_only.to_owned()),
drag: Some(self.drag.to_owned()),
hidden: Some(self.hidden.to_owned()),
buoyancy: Some(self.buoyancy.to_owned()),
damage: Some(self.damage.to_raw_damage()),
tank,
engine,
@ -384,94 +413,94 @@ pub mod sr1 {
#[inline]
fn to_raw_part_data(&self) -> RawPartData {
let tank = match &self.attr {
SR1PartDataAttr::Tank { fuel } => Some(RawTank { fuel: *fuel }),
_ => None,
let (tank, engine) = if let Some(fuel) = &self.attr.fuel {
match self.part_type {
SR1PartTypeEnum::tank => (Some(RawTank { fuel: fuel.to_owned() }), None),
SR1PartTypeEnum::engine => (None, Some(RawEngine { fuel: fuel.to_owned() })),
_ => (None, None),
}
} else {
(None, None)
};
let engine = match &self.attr {
SR1PartDataAttr::Engine { fuel } => Some(RawEngine { fuel: *fuel }),
_ => None,
};
let pod = match &self.attr {
SR1PartDataAttr::Pod {
name,
throttle,
current_stage,
steps,
} => Some({
// let pod = match &self.attr {
// SR1PartDataAttr::Pod {
// name,
// throttle,
// current_stage,
// steps,
// } => Some({
// let mut actives = Vec::new();
// for step in steps {
// let mut steps_ = Vec::new();
// for active in step {
// steps_.push(RawActivate {
// id: active.0,
// moved: bool_to_i8(active.1),
// });
// }
// actives.push(RawStep { activates: steps_ });
// }
// let stages = RawStaging {
// current_stage: *current_stage,
// steps: actives,
// };
// RawPod {
// name: name.clone(),
// throttle: *throttle,
// stages,
// }
// }),
// _ => None,
// };
let pod = match (&self.attr.name, &self.attr.throttle, &self.attr.current_stage, &self.attr.steps) {
(Some(name), Some(throttle), Some(current_stage), Some(steps)) => Some({
let mut actives = Vec::new();
for step in steps {
let mut steps_ = Vec::new();
for active in step {
steps_.push(RawActivate {
id: active.0,
moved: bool_to_i8(active.1),
id: active.0.to_owned(),
moved: bool_to_i8(active.1.to_owned()),
});
}
actives.push(RawStep { activates: steps_ });
}
let stages = RawStaging {
current_stage: *current_stage,
current_stage: current_stage.to_owned(),
steps: actives,
};
RawPod {
name: name.clone(),
throttle: *throttle,
throttle: throttle.to_owned(),
stages,
}
}),
_ => None,
};
let (chute_x, chute_y, chute_angle, chute_height, inflate, inflation, deployed, rope) = match &self.attr {
SR1PartDataAttr::Parachute {
chute_x,
chute_y,
chute_angle,
chute_height,
inflate,
inflation,
deployed,
rope,
} => (
Some(*chute_x),
Some(*chute_y),
Some(*chute_angle),
Some(*chute_height),
Some(bool_to_i8(*inflate)),
Some(bool_to_i8(*inflation)),
Some(bool_to_i8(*deployed)),
Some(bool_to_i8(*rope)),
),
_ => (None, None, None, None, None, None, None, None),
};
let extension = match &self.attr {
SR1PartDataAttr::Solar { extension } => Some(*extension),
_ => None,
};
RawPartData {
tank,
engine,
pod,
part_type: self.part_type,
id: self.id,
x: self.x,
y: self.y,
editor_angle: self.editor_angle,
angle: self.angle,
angle_v: self.angle_v,
flip_x: Some(bool_to_i8(self.flip_x)),
flip_y: Some(bool_to_i8(self.flip_y)),
chute_x,
chute_y,
chute_height,
extension,
inflate,
inflation,
exploded: Some(bool_to_i8(self.explode)),
rope,
chute_angle,
activated: Some(bool_to_i8(self.active)),
deployed,
part_type_id: self.part_type_id.clone(),
id: self.id.to_owned(),
x: self.x.to_owned(),
y: self.y.to_owned(),
editor_angle: self.editor_angle.to_owned(),
angle: self.angle.to_owned(),
angle_v: self.angle_v.to_owned(),
flip_x: Some(bool_to_i8(self.flip_x.to_owned())),
flip_y: Some(bool_to_i8(self.flip_y.to_owned())),
chute_x: self.attr.chute_x.to_owned(),
chute_y: self.attr.chute_y.to_owned(),
chute_height: self.attr.chute_height.to_owned(),
extension: self.attr.extension.to_owned(),
inflate: option_bool_to_option_i8(self.attr.inflate.to_owned()),
inflation: option_bool_to_option_i8(self.attr.inflation.to_owned()),
exploded: Some(bool_to_i8(self.explode.to_owned())),
rope: option_bool_to_option_i8(self.attr.rope.to_owned()),
chute_angle: self.attr.chute_angle.to_owned(),
activated: Some(bool_to_i8(self.active.to_owned())),
deployed: option_bool_to_option_i8(self.attr.deployed.to_owned()),
}
}
}
@ -488,6 +517,7 @@ pub mod sr1 {
pub angle_v: f64,
// 状态属性
pub part_type: SR1PartTypeEnum,
pub part_type_id: String,
pub editor_angle: i32,
pub flip_x: bool,
pub flip_y: bool,
@ -498,11 +528,11 @@ pub mod sr1 {
impl SR1PartData {
pub fn get_box(&self, part_type: &SR1PartType) -> (f64, f64, f64, f64) {
let width = part_type.width;
let height = part_type.height;
let radius = self.angle;
let width = part_type.width.to_owned();
let height = part_type.height.to_owned();
let radius = self.angle.to_owned();
let mut shape = Shape::new_width_height(width as f64, height as f64, Some(radius));
shape.move_xy(Some(self.x), Some(self.y));
shape.move_xy(Some(self.x.to_owned()), Some(self.y.to_owned()));
let mut pos_box = (0_f64, 0_f64, 0_f64, 0_f64);
match shape.bounds[0] {
Edge::OneTimeLine(line) => {
@ -523,33 +553,145 @@ pub mod sr1 {
}
#[derive(Debug, Clone)]
pub enum SR1PartDataAttr {
Tank {
fuel: f64,
},
Engine {
fuel: f64,
},
Pod {
name: String,
throttle: f64,
current_stage: u32,
steps: Vec<Vec<(i64, bool)>>,
},
Solar {
extension: f64,
},
Parachute {
chute_x: f64,
chute_y: f64,
chute_angle: f64,
chute_height: f64,
inflate: bool,
inflation: bool,
deployed: bool,
rope: bool,
},
None,
pub struct SR1PartDataAttr {
// Tank | Engine
pub fuel: Option<f64>,
// Pod
pub name: Option<String>,
pub throttle: Option<f64>,
pub current_stage: Option<u32>,
pub steps: Option<Vec<Vec<(i64, bool)>>>,
// Solar
pub extension: Option<f64>,
// Parachute
pub chute_x: Option<f64>,
pub chute_y: Option<f64>,
pub chute_height: Option<f64>,
pub chute_angle: Option<f64>,
pub inflate: Option<bool>,
pub inflation: Option<bool>,
pub deployed: Option<bool>,
pub rope: Option<bool>,
// part_type
pub part_type: Cell<Option<SR1PartTypeEnum>>,
}
impl SR1PartDataAttr {
pub fn guess_type(&self) -> SR1PartTypeEnum {
if let Some(part_type) = self.part_type.get() {
return part_type;
}
if self.fuel.is_some() {
self.part_type.set(Some(SR1PartTypeEnum::tank));
return self.part_type.get().unwrap();
}
if self.name.is_some() {
self.part_type.set(Some(SR1PartTypeEnum::pod));
return self.part_type.get().unwrap();
}
if self.extension.is_some() {
self.part_type.set(Some(SR1PartTypeEnum::solar));
return self.part_type.get().unwrap();
}
if self.chute_x.is_some() {
self.part_type.set(Some(SR1PartTypeEnum::parachute));
return self.part_type.get().unwrap();
}
SR1PartTypeEnum::strut // 默认为 Strut 开摆
}
pub fn get_part_type(&self) -> SR1PartTypeEnum {
if let Some(part_type) = self.part_type.get() {
return part_type;
}
self.guess_type()
}
pub fn new(
fuel: Option<f64>,
name: Option<String>,
throttle: Option<f64>,
current_stage: Option<u32>,
steps: Option<Vec<Vec<(i64, bool)>>>,
extension: Option<f64>,
chute_x: Option<f64>,
chute_y: Option<f64>,
chute_height: Option<f64>,
chute_angle: Option<f64>,
inflate: Option<bool>,
inflation: Option<bool>,
deployed: Option<bool>,
rope: Option<bool>,
part_type: Option<SR1PartTypeEnum>,
) -> Self {
SR1PartDataAttr {
fuel,
name,
throttle,
current_stage,
steps,
extension,
chute_x,
chute_y,
chute_height,
chute_angle,
inflate,
inflation,
deployed,
rope,
part_type: Cell::new(part_type),
}
}
pub fn from_raw(raw_data: &RawPartData, part_type: Option<SR1PartTypeEnum>, guess: bool) -> Self {
let fuel = if let Some(tank) = &raw_data.tank {
Some(tank.fuel.to_owned())
} else if let Some(engine) = &raw_data.engine {
Some(engine.fuel.to_owned())
} else {
None
};
let (name, throttle, current_stage, steps) = if let Some(pod) = &raw_data.pod {
(
Some(pod.name.to_owned()),
Some(pod.throttle.to_owned()),
Some(pod.stages.current_stage.to_owned()),
Some({
let mut steps = Vec::new();
for step in &pod.stages.steps {
let mut step_vec = Vec::new();
for act in &step.activates {
step_vec.push((act.id.to_owned(), i8_to_bool(act.moved.to_owned())));
}
steps.push(step_vec);
}
steps
}),
)
} else {
(None, None, None, None)
};
let results = SR1PartDataAttr {
fuel,
name,
throttle,
current_stage,
steps,
extension: raw_data.extension.to_owned(),
chute_x: raw_data.chute_x.to_owned(),
chute_y: raw_data.chute_y.to_owned(),
chute_height: raw_data.chute_height.to_owned(),
chute_angle: raw_data.chute_angle.to_owned(),
inflate: option_i8_to_option_bool(raw_data.inflate.to_owned()),
inflation: option_i8_to_option_bool(raw_data.inflation.to_owned()),
deployed: option_i8_to_option_bool(raw_data.deployed.to_owned()),
rope: option_i8_to_option_bool(raw_data.rope.to_owned()),
part_type: Cell::new(part_type),
};
if guess & results.part_type.get().is_none() {
results.guess_type();
}
results
}
}
#[derive(Debug, Clone)]
@ -619,8 +761,8 @@ pub mod sr1 {
parts: RawParts { parts },
connects: connections,
version: 1,
lift_off: bool_to_i8(self.lift_off),
touch_ground: bool_to_i8(self.touch_ground),
lift_off: bool_to_i8(self.lift_off.to_owned()),
touch_ground: bool_to_i8(self.touch_ground.to_owned()),
disconnected,
}
}
@ -658,8 +800,8 @@ pub mod math {
#[inline]
pub fn distance(&self, other: &Point2D) -> f64 {
let dx = (other.x - self.x).powf(2.0);
let dy = (other.y - self.y).powf(2.0);
let dx = (other.x.to_owned() - self.x.to_owned()).powf(2.0);
let dy = (other.y.to_owned() - self.y.to_owned()).powf(2.0);
(dx + dy).powf(0.5)
}

View File

@ -20,6 +20,7 @@ DR_rust_version = Version("0.2.7.0") # DR_mod 的 Rust 编写部分的兼容版
class _DR_mod_runtime(Options):
name = 'DR mod runtime'
use_DR_rust: bool = True
DR_rust_available: bool = False

View File

@ -10,13 +10,6 @@ if (-Not (Test-Path -Path "./.github/workflows/env.ps1"))
./.github/workflows/env.ps1
Set-Location libs
Set-Location Difficult_Rocket_rs
Set-Location src
python3.9 setup.py build
python3 post_build.py
Set-Location ../../..
$arg = @()
# 输出配置
$arg += @("--standalone")
@ -44,11 +37,11 @@ python3.9 -m nuitka $arg $args DR.py
$end_time = Get-Uptime
$out = $end_time.TotalMilliseconds - $start_time.TotalMilliseconds
Write-Output $end_time.TotalSeconds $start_time.TotalSeconds $out s
Write-Output $start_time $end_time
Write-Output "--clang --lto=no and ($args)"
Copy-Item .\libs\pyglet\ .\build\nuitka-mac\DR.dist -Recurse
# --include-data-dir=./libs/pyglet=./pyglet
# --run
# --disable-ccache

View File

@ -10,13 +10,6 @@ if (-Not (Test-Path -Path "./.github/workflows/env.ps1"))
./.github/workflows/env.ps1
Set-Location libs
Set-Location Difficult_Rocket_rs
Set-Location src
python3.8 setup.py build
python post_build.py
Set-Location ../../..
$arg = @()
# 输出配置
$arg += @("--standalone")