Merge branch 'main' into mod/five_point

This commit is contained in:
shenjack 2023-05-14 21:01:21 +08:00
commit f2701d2594
13 changed files with 370 additions and 233 deletions

View File

@ -5,6 +5,7 @@
# ------------------------------- # -------------------------------
import sys import sys
import time
import importlib import importlib
import traceback import traceback
import contextlib import contextlib

View File

@ -210,8 +210,6 @@ class ClientWindow(Window):
def start_game(self) -> None: def start_game(self) -> None:
self.set_icon(pyglet.image.load('./textures/icon.png')) self.set_icon(pyglet.image.load('./textures/icon.png'))
self.run_input = True
self.read_input()
try: try:
pyglet.app.event_loop.run(1 / self.main_config['runtime']['fps']) pyglet.app.event_loop.run(1 / self.main_config['runtime']['fps'])
except KeyboardInterrupt: except KeyboardInterrupt:
@ -221,22 +219,6 @@ class ClientWindow(Window):
self.dispatch_event("on_close") self.dispatch_event("on_close")
sys.exit(0) sys.exit(0)
@new_thread('window read_input', daemon=True)
def read_input(self):
self.logger.debug('read_input start')
while self.run_input:
try:
get = input(">")
except (EOFError, KeyboardInterrupt):
self.run_input = False
break
if get in ('', ' ', '\n', '\r'):
continue
if get == 'stop':
self.run_input = False
self.command_list.append(get)
self.logger.debug('read_input end')
@new_thread('window save_info') @new_thread('window save_info')
def save_info(self): def save_info(self):
self.logger.info(tr().client.config.save.start()) self.logger.info(tr().client.config.save.start())
@ -266,10 +248,8 @@ class ClientWindow(Window):
@_call_screen_after @_call_screen_after
def on_draw(self, *dt): def on_draw(self, *dt):
if self.command_list: while command := self.game.console.get_command():
for command in self.command_list: self.on_command(line.CommandText(command))
self.on_command(line.CommandText(command))
self.command_list.pop(0)
pyglet.gl.glClearColor(0.1, 0, 0, 0.0) pyglet.gl.glClearColor(0.1, 0, 0, 0.0)
self.clear() self.clear()
self.draw_update(float(self.SPF)) self.draw_update(float(self.SPF))
@ -310,25 +290,26 @@ class ClientWindow(Window):
@_call_screen_after @_call_screen_after
def on_command(self, command: line.CommandText): def on_command(self, command: line.CommandText):
print(command.re_match('/')) print(command.find('/'))
self.logger.info(tr().window.command.text().format(command)) self.logger.info(tr().window.command.text().format(command))
if command.re_match('stop'): if command.find('stop'):
# self.dispatch_event('on_exit') # self.dispatch_event('on_exit')
print("command stop!")
pyglet.app.platform_event_loop.stop() pyglet.app.platform_event_loop.stop()
self.dispatch_event('on_close', 'command') # source = command self.dispatch_event('on_close', 'command') # source = command
elif command.re_match('fps'): elif command.find('fps'):
if command.re_match('log'): if command.find('log'):
self.logger.debug(self.fps_log.fps_list) self.logger.debug(self.fps_log.fps_list)
elif command.re_match('max'): elif command.find('max'):
self.logger.info(self.fps_log.max_fps) self.logger.info(self.fps_log.max_fps)
self.command.push_line(self.fps_log.max_fps, block_line=True) self.command.push_line(self.fps_log.max_fps, block_line=True)
elif command.re_match('min'): elif command.find('min'):
self.logger.info(self.fps_log.min_fps) self.logger.info(self.fps_log.min_fps)
self.command.push_line(self.fps_log.min_fps, block_line=True) self.command.push_line(self.fps_log.min_fps, block_line=True)
elif command.re_match('default'): elif command.find('default'):
self.set_size(int(self.main_config['window_default']['width']), self.set_size(int(self.main_config['window_default']['width']),
int(self.main_config['window_default']['height'])) int(self.main_config['window_default']['height']))
elif command.re_match('lang'): elif command.find('lang'):
try: try:
lang = command.text[5:] lang = command.text[5:]
tr._language = lang tr._language = lang
@ -439,10 +420,13 @@ class ClientWindow(Window):
self.game.dispatch_event('on_close', game=self.game, client=self, source=source) self.game.dispatch_event('on_close', game=self.game, client=self, source=source)
self.logger.info(tr().window.game.stop_get().format(tr().game[source]())) self.logger.info(tr().window.game.stop_get().format(tr().game[source]()))
self.logger.info(tr().window.game.stop()) self.logger.info(tr().window.game.stop())
self.fps_log.check_list = False # self.fps_log.check_list = False
DR_runtime.running = False DR_runtime.running = False
if self.run_input: if self.run_input:
self.run_input = False self.run_input = False
self.save_info() self.save_info()
super().on_close() super().on_close()
self.logger.info(tr().window.game.end()) self.logger.info(tr().window.game.end())
ClientWindow.register_event_type("on_command")

View File

@ -43,7 +43,14 @@ class CommandText:
break break
i += 1 i += 1
def find(self, text: str) -> Union[str, bool]: def find(self, text: str) -> bool:
find = self.text.find(text)
if find != -1:
self.text = self.text[find + len(text):]
return True
return False
def re_find(self, text: str) -> Union[str, bool]:
return finding.group() if (finding := re.match(text, self.text)) else False return finding.group() if (finding := re.match(text, self.text)) else False
def re_match(self, text: str) -> bool: def re_match(self, text: str) -> bool:

View File

@ -11,10 +11,10 @@ github: @shenjackyuanjie
gitee: @shenjackyuanjie gitee: @shenjackyuanjie
""" """
import os
import sys import sys
import time import time
import logging import logging
import traceback
import importlib import importlib
import importlib.util import importlib.util
import logging.config import logging.config
@ -22,59 +22,83 @@ import multiprocessing
from io import StringIO from io import StringIO
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING from typing import TYPE_CHECKING, List, Optional, Dict, TypeVar
if __name__ == '__main__': # been start will not run this if __name__ == '__main__': # been start will not run this
sys.path.append('/bin/libs') sys.path.append('/bin/libs')
sys.path.append('/bin') sys.path.append('/bin')
from Difficult_Rocket import client, server, DR_option, DR_runtime
if TYPE_CHECKING: if TYPE_CHECKING:
from Difficult_Rocket.api.mod import ModInfo from Difficult_Rocket.api.mod import ModInfo
from Difficult_Rocket.crash import write_info_to_cache else:
ModInfo = TypeVar('ModInfo')
from Difficult_Rocket.utils import tools from Difficult_Rocket.utils import tools
from Difficult_Rocket.api.types import Options
from Difficult_Rocket.utils.translate import tr from Difficult_Rocket.utils.translate import tr
from Difficult_Rocket.utils.thread import new_thread
from Difficult_Rocket.crash import write_info_to_cache
from Difficult_Rocket import client, server, DR_option, DR_runtime
class Game: class Console(Options):
def __init__(self): name = 'python stdin console'
# basic config
self.on_python_v_info = sys.version_info running: bool = False
self.on_python_v = sys.version.split(' ')[0]
self.start_time = time.strftime('%Y-%m-%d %H-%M-%S', time.gmtime(time.time())) @new_thread('python console', daemon=True, log_thread=True)
# lang_config def main(self):
self.language = tools.load_file('configs/main.toml', 'runtime')['language'] while self.running:
DR_option.language = self.language try:
# logging config get_str = input('>>>')
log_config = tools.load_file('configs/logger.toml') except (EOFError, KeyboardInterrupt):
file_name = log_config['handlers']['file']['filename'] get_str = 'stop'
del log_config['handlers']['file']['datefmt'] self.caches.append(get_str)
log_config['handlers']['file']['filename'] = f'logs/{file_name.format(self.start_time)}' if get_str == 'stop':
try: self.running = False
logging.config.dictConfig(log_config) break
self.logger = logging.getLogger('main')
except ValueError: # it should be no 'logs/' folder def start(self):
os.mkdir('logs') self.running = True
logging.config.dictConfig(log_config) self.caches: List[str] = []
self.logger = logging.getLogger('main') self.main()
def stop(self):
self.running = False
def get_command(self) -> str:
return self.caches.pop(0)
class Game(Options):
name = 'MainGame'
client: client.Client
server: server.Server
console: Console
console_class: Console = Console
main_config: Dict
logging_config: Dict
logger: logging.Logger
mod_module: List["ModInfo"]
def init_logger(self) -> None:
log_path = self.logging_config['handlers']['file']['filename']
log_path = 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/").exists():
log_path.mkdir(parents=True)
mkdir = True
self.logging_config['handlers']['file']['filename'] = str(log_path.absolute())
logging.config.dictConfig(self.logging_config)
self.logger = logging.getLogger('main')
if mkdir:
self.logger.info(tr().main.logger.mkdir()) self.logger.info(tr().main.logger.mkdir())
self.logger.info(tr().language_set_to())
self.logger.info(tr().main.logger.created())
# version check
self.log_env()
self.python_version_check()
self.loaded_mods = []
# self.client = client.Client
# self.server = server.Server
self.setup()
def log_env(self) -> None:
cache_steam = StringIO()
write_info_to_cache(cache_steam)
text = cache_steam.getvalue()
self.logger.info(text)
def load_mods(self) -> None: def init_mods(self) -> None:
"""验证/加载 mod"""
print(self)
mods = [] mods = []
mod_path = Path(DR_runtime.mod_path) mod_path = Path(DR_runtime.mod_path)
if not mod_path.exists(): if not mod_path.exists():
@ -114,9 +138,9 @@ class Game:
module.append(mod_class) module.append(mod_class)
self.logger.info(tr().main.mod.load.info().format(mod_class.mod_id, mod_class.version)) self.logger.info(tr().main.mod.load.info().format(mod_class.mod_id, mod_class.version))
except ImportError as e: except ImportError as e:
self.logger.warning(tr().main.mod.load.faild().format(mod, e)) self.logger.warning(tr().main.mod.load.faild.info().format(mod, e))
self.logger.info(tr().main.mod.load.done()) self.logger.info(tr().main.mod.load.done())
self.loaded_mods = module self.mod_module = module
mod_list = [] mod_list = []
for mod in module: for mod in module:
mod_list.append((mod.mod_id, mod.version)) mod_list.append((mod.mod_id, mod.version))
@ -124,30 +148,11 @@ class Game:
self.dispatch_event('on_load', game=self) self.dispatch_event('on_load', game=self)
DR_runtime.DR_Mod_List = mod_list DR_runtime.DR_Mod_List = mod_list
def dispatch_event(self, event_name: str, *args, **kwargs) -> None: def init_console(self) -> None:
for mod in self.loaded_mods: self.console = self.console_class()
if hasattr(mod, event_name): self.console.start()
try:
getattr(mod, event_name)(*args, **kwargs)
except Exception as e:
self.logger.error(tr().main.mod.event.error().format(event_name, e, mod.mod_id))
def setup(self) -> None: def start(self):
self.load_mods()
self.client = client.Client(game=self, net_mode='local')
self.server = server.Server(net_mode='local')
def python_version_check(self) -> None: # best 3.8+ and write at 3.8.10
self.logger.info(f"{tr().main.version.now_on()} {self.on_python_v}")
if self.on_python_v_info[0] == 2:
self.logger.critical(tr().main.version.need3p())
raise SystemError(tr().main.version.need3p())
elif self.on_python_v_info[1] < 8:
warning = tools.name_handler(tr.main.version.best38p())
self.logger.warning(warning)
# @new_thread('main')
def _start(self):
self.server.run() self.server.run()
if DR_option.use_multiprocess: if DR_option.use_multiprocess:
try: try:
@ -161,5 +166,42 @@ class Game:
else: else:
self.client.start() self.client.start()
def start(self) -> None: def dispatch_event(self, event_name: str, *args, **kwargs) -> None:
self._start() """向 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
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.load_file()
self.setup()
self.log_env()
return True
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

@ -66,9 +66,11 @@ class Options:
if option not in self.cached_options: if option not in self.cached_options:
raise OptionNameNotDefined(f"option: {option} with value: {value} is not defined") raise OptionNameNotDefined(f"option: {option} with value: {value} is not defined")
setattr(self, option, value) setattr(self, option, value)
run_load_file = True
if hasattr(self, 'init'): if hasattr(self, 'init'):
self.init(**kwargs) run_load_file = self.init(**kwargs) # 默认 False/None
if hasattr(self, 'load_file'): run_load_file = not run_load_file
if hasattr(self, 'load_file') and run_load_file:
try: try:
self.load_file() self.load_file()
except Exception: except Exception:
@ -78,8 +80,10 @@ class Options:
if TYPE_CHECKING: if TYPE_CHECKING:
_options: Dict[str, Union[Callable, object]] = {} _options: Dict[str, Union[Callable, object]] = {}
def init(self, **kwargs) -> None: def init(self, **kwargs) -> bool:
""" 如果子类定义了这个函数,则会在 __init__ 之后调用这个函数 """ """ 如果子类定义了这个函数,则会在 __init__ 之后调用这个函数
返回值为 True 则不会调用 load_file 函数
"""
def load_file(self) -> bool: def load_file(self) -> bool:
"""如果子类定义了这个函数,则会在 __init__ 和 init 之后再调用这个函数 """如果子类定义了这个函数,则会在 __init__ 和 init 之后再调用这个函数

View File

@ -27,7 +27,6 @@ level = "DEBUG"
[handlers.file] [handlers.file]
class = "logging.FileHandler" class = "logging.FileHandler"
filename = "{} DR.log" filename = "{} DR.log"
datefmt = "%Y-%m-%d %H:%M:%S"
encoding = "utf-8" encoding = "utf-8"
formatter = "file" formatter = "file"
level = "DEBUG" level = "DEBUG"

View File

@ -59,6 +59,9 @@
- `get_box(&self, part_type: &SR1PartType) -> (f64, f64, f64, f64)` - `get_box(&self, part_type: &SR1PartType) -> (f64, f64, f64, f64)`
- `types::SR1Ship` - `types::SR1Ship`
- `from_file` - `from_file`
- 添加了 `Console_rs`
- 用于使用 Rust 多线程读取 stdin
- Use Rust mutithread to read stdin
### Remove ### Remove
@ -114,6 +117,13 @@
- 修改 返回值 类型+类型注释 - 修改 返回值 类型+类型注释
- Modify the return value type + type annotation - 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]` - `List[Union[List[Tuple[str, Any, Any]], int, Any]]:` -> `Tuple[List[Tuple[str, Union[Any, Type], Type]], int, int, int]`
- `Difficult_Roocket.main.Game`
- 使用 `Options` 完全重构
- 分离 `init mods` `init console` `init logger` `load_file`
- Completely refactored using `Options`
- Separate `init mods` `init console` `init logger` `load_file`
- `Difficult_Rocket.command.api.CommandText`
- `find` -> `re_find`
### Add ### Add
@ -122,6 +132,10 @@
- 用于方便的用人类可读的 Markdown 格式 直接输出一个已经实例化的 `Options` 类的所有字段 - 用于方便的用人类可读的 Markdown 格式 直接输出一个已经实例化的 `Options` 类的所有字段
- Add `as_markdown` method - Add `as_markdown` method
- Used to easily output all fields of an instantiated `Options` class in a human-readable Markdown format - Used to easily output all fields of an instantiated `Options` class in a human-readable Markdown format
- `Difficult_Rocket.command.api.CommandText`
- 添加基于 `str.find``find(text: str) -> bool` 方法
- Add method `find(text: str)` based on `str.find`
### Docs ### Docs

View File

@ -101,3 +101,9 @@ if TYPE_CHECKING:
@property @property
def img_pos(self) -> Tuple[int, int, int, int]: ... def img_pos(self) -> Tuple[int, int, int, int]: ...
""" -x -y +x +y 左下右上 """ """ -x -y +x +y 左下右上 """
class Console_rs:
def __init__(self) -> None: ...
def start(self) -> None: ...
def stop(self) -> bool: ...
def get_command(self) -> Optional[str]: ...

View File

@ -18,16 +18,17 @@ use pyo3::prelude::*;
// const MOD_PATH: String = String::from("mods"); // const MOD_PATH: String = String::from("mods");
#[allow(unused)]
enum LoadState { enum LoadState {
init, Init,
wait_start, WaitStart,
pre_start, PreStart,
running, Running,
clean, Clean,
} }
#[pyfunction] #[pyfunction]
fn get_version_str() -> String { "0.2.7.0".to_string() } fn get_version_str() -> String { "0.2.8.0".to_string() }
#[pyfunction] #[pyfunction]
fn test_call(py_obj: &PyAny) -> PyResult<bool> { fn test_call(py_obj: &PyAny) -> PyResult<bool> {
@ -52,6 +53,7 @@ fn module_init(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_class::<python::data::PySR1Ship>()?; m.add_class::<python::data::PySR1Ship>()?;
m.add_class::<python::data::PySR1PartList>()?; m.add_class::<python::data::PySR1PartList>()?;
m.add_class::<python::data::PySR1PartType>()?; m.add_class::<python::data::PySR1PartType>()?;
m.add_class::<python::console::PyConsole>()?;
Ok(()) Ok(())
} }

View File

@ -12,7 +12,6 @@ pub mod data {
use pyo3::prelude::*; use pyo3::prelude::*;
use crate::sr1_data::part_list::RawPartList; use crate::sr1_data::part_list::RawPartList;
use crate::sr1_data::ship::RawShip;
use crate::types::sr1::{get_max_box, SR1PartData, SR1PartListTrait}; use crate::types::sr1::{get_max_box, SR1PartData, SR1PartListTrait};
use crate::types::sr1::{SR1PartList, SR1PartType, SR1Ship}; use crate::types::sr1::{SR1PartList, SR1PartType, SR1Ship};
@ -167,3 +166,67 @@ pub mod translate {
} }
} }
} }
pub mod console {
use std::println;
use pyo3::prelude::*;
#[pyclass]
#[pyo3(name = "Console_rs")]
pub struct PyConsole {
/// 向子线程发送结束信号
pub stop_sender: Option<std::sync::mpsc::Sender<()>>,
pub keyboard_input_receiver: Option<std::sync::mpsc::Receiver<String>>,
}
#[pymethods]
impl PyConsole {
#[new]
fn new() -> Self {
Self {
stop_sender: None,
keyboard_input_receiver: None,
}
}
fn start(&mut self) {
let (stop_sender, stop_receiver) = std::sync::mpsc::channel();
let (keyboard_input_sender, keyboard_input_receiver) = std::sync::mpsc::channel();
std::thread::spawn(move || {
let std_in = std::io::stdin();
loop {
if let Ok(()) = stop_receiver.try_recv() {
break;
}
let mut input = String::new();
let _ = std_in.read_line(&mut input);
if !input.is_empty() {
keyboard_input_sender.send(input).unwrap();
}
print!(">>");
}
});
self.stop_sender = Some(stop_sender);
self.keyboard_input_receiver = Some(keyboard_input_receiver);
}
fn stop(&self) -> bool {
if let Some(sender) = &self.stop_sender {
sender.send(()).unwrap();
return true;
}
false
}
fn get_command(&self) -> Option<String> {
// 获取输入
if let Some(receiver) = &self.keyboard_input_receiver {
if let Ok(string) = receiver.try_recv() {
return Some(string);
}
}
None
}
}
}

View File

@ -138,10 +138,10 @@ pub mod sr1 {
impl Damage { impl Damage {
pub fn to_raw_damage(&self) -> RawDamage { pub fn to_raw_damage(&self) -> RawDamage {
RawDamage { RawDamage {
disconnect: self.disconnect.to_owned(), disconnect: self.disconnect,
explode: self.explode.to_owned(), explode: self.explode,
explosion_power: Some(self.explosion_power.to_owned()), explosion_power: Some(self.explosion_power),
explosion_size: Some(self.explosion_size.to_owned()), explosion_size: Some(self.explosion_size),
} }
} }
} }
@ -225,10 +225,10 @@ pub mod sr1 {
if cache.is_none() { if cache.is_none() {
let mut map = HashMap::new(); let mut map = HashMap::new();
for part in self.types.iter() { for part in self.types.iter() {
map.insert(part.id.to_owned(), part.to_owned()); map.insert(part.id.clone(), part.clone());
} }
*cache = Some(map); *cache = Some(map.clone());
self.cache.replace(cache.to_owned()); self.cache.replace(cache.clone());
} }
cache.to_owned().unwrap() cache.to_owned().unwrap()
} }
@ -237,7 +237,7 @@ pub mod sr1 {
pub fn get_part_type(&self, type_name: String) -> Option<SR1PartType> { pub fn get_part_type(&self, type_name: String) -> Option<SR1PartType> {
let cache = self.get_cache(); let cache = self.get_cache();
match cache.get(&type_name) { match cache.get(&type_name) {
Some(part) => Some(part.to_owned()), Some(part) => Some(part.clone()),
None => None, None => None,
} }
} }
@ -294,18 +294,18 @@ pub mod sr1 {
fn to_raw_part_type(&self) -> RawPartType { fn to_raw_part_type(&self) -> RawPartType {
let tank: Option<Tank> = match &self.attr { let tank: Option<Tank> = match &self.attr {
Some(attr) => match attr { Some(attr) => match attr.to_owned() {
SR1PartTypeAttr::Tank { fuel, dry_mass, fuel_type } => Some(Tank { SR1PartTypeAttr::Tank { fuel, dry_mass, fuel_type } => Some(Tank {
fuel: fuel.to_owned(), fuel,
dry_mass: dry_mass.to_owned(), dry_mass,
fuel_type: Some(fuel_type.to_owned()), fuel_type: Some(fuel_type),
}), }),
_ => None, _ => None,
}, },
_ => None, _ => None,
}; };
let engine: Option<Engine> = match &self.attr { let engine: Option<Engine> = match &self.attr {
Some(attr) => match attr { Some(attr) => match attr.to_owned() {
SR1PartTypeAttr::Engine { SR1PartTypeAttr::Engine {
power, power,
consumption, consumption,
@ -314,39 +314,33 @@ pub mod sr1 {
fuel_type, fuel_type,
throttle_exponential, throttle_exponential,
} => Some(Engine { } => Some(Engine {
power: power.to_owned(), power,
consumption: consumption.to_owned(), consumption,
throttle_exponential: Some(throttle_exponential.to_owned()), throttle_exponential: Some(throttle_exponential),
size: size.to_owned(), size,
turn: turn.to_owned(), turn,
fuel_type: Some(fuel_type.to_owned()), fuel_type: Some(fuel_type),
}), }),
_ => None, _ => None,
}, },
_ => None, _ => None,
}; };
let rcs: Option<Rcs> = match &self.attr { let rcs: Option<Rcs> = match &self.attr {
Some(attr) => match attr { Some(attr) => match attr.to_owned() {
SR1PartTypeAttr::Rcs { power, consumption, size } => Some(Rcs { SR1PartTypeAttr::Rcs { power, consumption, size } => Some(Rcs { power, consumption, size }),
power: power.to_owned(),
consumption: consumption.to_owned(),
size: size.to_owned(),
}),
_ => None, _ => None,
}, },
_ => None, _ => None,
}; };
let solar: Option<Solar> = match &self.attr { let solar: Option<Solar> = match &self.attr {
Some(attr) => match attr { Some(attr) => match attr.to_owned() {
SR1PartTypeAttr::Solar { charge_rate } => Some(Solar { SR1PartTypeAttr::Solar { charge_rate } => Some(Solar { charge_rate }),
charge_rate: charge_rate.to_owned(),
}),
_ => None, _ => None,
}, },
_ => None, _ => None,
}; };
let lander: Option<Lander> = match &self.attr { let lander: Option<Lander> = match &self.attr {
Some(attr) => match attr { Some(attr) => match attr.to_owned() {
SR1PartTypeAttr::Lander { SR1PartTypeAttr::Lander {
max_angle, max_angle,
min_length, min_length,
@ -355,12 +349,12 @@ pub mod sr1 {
length_speed, length_speed,
width, width,
} => Some(Lander { } => Some(Lander {
max_angle: max_angle.to_owned(), max_angle,
min_length: min_length.to_owned(), min_length,
max_length: max_length.to_owned(), max_length,
angle_speed: Some(angle_speed.to_owned()), angle_speed: Some(angle_speed),
length_speed: Some(length_speed.to_owned()), length_speed: Some(length_speed),
width: width.to_owned(), width,
}), }),
_ => None, _ => None,
}, },
@ -382,19 +376,19 @@ pub mod sr1 {
description: self.description.clone(), description: self.description.clone(),
sprite: self.sprite.clone(), sprite: self.sprite.clone(),
r#type: self.p_type.clone(), r#type: self.p_type.clone(),
mass: self.mass.to_owned(), mass: self.mass,
width: self.width.to_owned(), width: self.width,
height: self.height.to_owned(), height: self.height,
friction: Some(self.friction.to_owned()), friction: Some(self.friction),
category: Some(self.category.clone()), category: Some(self.category.clone()),
ignore_editor_intersections: Some(self.ignore_editor_intersections.to_owned()), ignore_editor_intersections: Some(self.ignore_editor_intersections),
disable_editor_rotation: Some(self.disable_editor_rotation.to_owned()), disable_editor_rotation: Some(self.disable_editor_rotation),
can_explode: Some(self.can_explode.to_owned()), can_explode: Some(self.can_explode),
cover_height: Some(self.cover_height.to_owned()), cover_height: Some(self.cover_height),
sandbox_only: Some(self.sandbox_only.to_owned()), sandbox_only: Some(self.sandbox_only),
drag: Some(self.drag.to_owned()), drag: Some(self.drag),
hidden: Some(self.hidden.to_owned()), hidden: Some(self.hidden),
buoyancy: Some(self.buoyancy.to_owned()), buoyancy: Some(self.buoyancy),
damage: Some(self.damage.to_raw_damage()), damage: Some(self.damage.to_raw_damage()),
tank, tank,
engine, engine,
@ -422,36 +416,6 @@ pub mod sr1 {
} else { } else {
(None, None) (None, None)
}; };
// 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) { 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({ (Some(name), Some(throttle), Some(current_stage), Some(steps)) => Some({
let mut actives = Vec::new(); let mut actives = Vec::new();
@ -482,25 +446,25 @@ pub mod sr1 {
engine, engine,
pod, pod,
part_type_id: self.part_type_id.clone(), part_type_id: self.part_type_id.clone(),
id: self.id.to_owned(), id: self.id,
x: self.x.to_owned(), x: self.x,
y: self.y.to_owned(), y: self.y,
editor_angle: self.editor_angle.to_owned(), editor_angle: self.editor_angle,
angle: self.angle.to_owned(), angle: self.angle,
angle_v: self.angle_v.to_owned(), angle_v: self.angle_v,
flip_x: Some(bool_to_i8(self.flip_x.to_owned())), flip_x: Some(bool_to_i8(self.flip_x)),
flip_y: Some(bool_to_i8(self.flip_y.to_owned())), flip_y: Some(bool_to_i8(self.flip_y)),
chute_x: self.attr.chute_x.to_owned(), chute_x: self.attr.chute_x,
chute_y: self.attr.chute_y.to_owned(), chute_y: self.attr.chute_y,
chute_height: self.attr.chute_height.to_owned(), chute_height: self.attr.chute_height,
extension: self.attr.extension.to_owned(), extension: self.attr.extension,
inflate: option_bool_to_option_i8(self.attr.inflate.to_owned()), inflate: option_bool_to_option_i8(self.attr.inflate),
inflation: option_bool_to_option_i8(self.attr.inflation.to_owned()), inflation: option_bool_to_option_i8(self.attr.inflation),
exploded: Some(bool_to_i8(self.explode.to_owned())), exploded: Some(bool_to_i8(self.explode)),
rope: option_bool_to_option_i8(self.attr.rope.to_owned()), rope: option_bool_to_option_i8(self.attr.rope),
chute_angle: self.attr.chute_angle.to_owned(), chute_angle: self.attr.chute_angle,
activated: Some(bool_to_i8(self.active.to_owned())), activated: Some(bool_to_i8(self.active)),
deployed: option_bool_to_option_i8(self.attr.deployed.to_owned()), deployed: option_bool_to_option_i8(self.attr.deployed),
} }
} }
} }
@ -528,11 +492,11 @@ pub mod sr1 {
impl SR1PartData { impl SR1PartData {
pub fn get_box(&self, part_type: &SR1PartType) -> (f64, f64, f64, f64) { pub fn get_box(&self, part_type: &SR1PartType) -> (f64, f64, f64, f64) {
let width = part_type.width.to_owned(); let width = part_type.width;
let height = part_type.height.to_owned(); let height = part_type.height;
let radius = self.angle.to_owned(); let radius = self.angle;
let mut shape = Shape::new_width_height(width as f64, height as f64, Some(radius)); let mut shape = Shape::new_width_height(width as f64, height as f64, Some(radius));
shape.move_xy(Some(self.x.to_owned()), Some(self.y.to_owned())); shape.move_xy(Some(self.x), Some(self.y));
let mut pos_box = (0_f64, 0_f64, 0_f64, 0_f64); let mut pos_box = (0_f64, 0_f64, 0_f64, 0_f64);
match shape.bounds[0] { match shape.bounds[0] {
Edge::OneTimeLine(line) => { Edge::OneTimeLine(line) => {
@ -652,15 +616,15 @@ pub mod sr1 {
}; };
let (name, throttle, current_stage, steps) = if let Some(pod) = &raw_data.pod { let (name, throttle, current_stage, steps) = if let Some(pod) = &raw_data.pod {
( (
Some(pod.name.to_owned()), Some(pod.name.clone()),
Some(pod.throttle.to_owned()), Some(pod.throttle),
Some(pod.stages.current_stage.to_owned()), Some(pod.stages.current_stage),
Some({ Some({
let mut steps = Vec::new(); let mut steps = Vec::new();
for step in &pod.stages.steps { for step in &pod.stages.steps {
let mut step_vec = Vec::new(); let mut step_vec = Vec::new();
for act in &step.activates { for act in &step.activates {
step_vec.push((act.id.to_owned(), i8_to_bool(act.moved.to_owned()))); step_vec.push((act.id, i8_to_bool(act.moved)));
} }
steps.push(step_vec); steps.push(step_vec);
} }
@ -676,15 +640,15 @@ pub mod sr1 {
throttle, throttle,
current_stage, current_stage,
steps, steps,
extension: raw_data.extension.to_owned(), extension: raw_data.extension,
chute_x: raw_data.chute_x.to_owned(), chute_x: raw_data.chute_x,
chute_y: raw_data.chute_y.to_owned(), chute_y: raw_data.chute_y,
chute_height: raw_data.chute_height.to_owned(), chute_height: raw_data.chute_height,
chute_angle: raw_data.chute_angle.to_owned(), chute_angle: raw_data.chute_angle,
inflate: option_i8_to_option_bool(raw_data.inflate.to_owned()), inflate: option_i8_to_option_bool(raw_data.inflate),
inflation: option_i8_to_option_bool(raw_data.inflation.to_owned()), inflation: option_i8_to_option_bool(raw_data.inflation),
deployed: option_i8_to_option_bool(raw_data.deployed.to_owned()), deployed: option_i8_to_option_bool(raw_data.deployed),
rope: option_i8_to_option_bool(raw_data.rope.to_owned()), rope: option_i8_to_option_bool(raw_data.rope),
part_type: Cell::new(part_type), part_type: Cell::new(part_type),
}; };
if guess & results.part_type.get().is_none() { if guess & results.part_type.get().is_none() {
@ -715,6 +679,30 @@ pub mod sr1 {
let ship: RawShip = RawShip::from_file(file_name).unwrap(); let ship: RawShip = RawShip::from_file(file_name).unwrap();
Some(ship.to_sr_ship(ship_name)) Some(ship.to_sr_ship(ship_name))
} }
pub fn parse_part_list_to_part(&mut self, part_list: SR1PartList) {
// parse parts
for part in self.parts.iter_mut() {
let part_type_id = part.part_type_id.clone();
if let Some(part_type) = part_list.get_part_type(part_type_id) {
part.part_type = part_type.p_type;
} else {
part.part_type = SR1PartTypeEnum::strut;
}
}
for disconnects in self.disconnected.iter_mut() {
for (parts, _) in disconnects.iter_mut() {
for part in parts.iter_mut() {
let part_type_id = part.part_type_id.clone();
if let Some(part_type) = part_list.get_part_type(part_type_id) {
part.part_type = part_type.p_type;
} else {
part.part_type = SR1PartTypeEnum::strut;
}
}
}
}
}
} }
impl SR1ShipTrait for SR1Ship { impl SR1ShipTrait for SR1Ship {
@ -761,8 +749,8 @@ pub mod sr1 {
parts: RawParts { parts }, parts: RawParts { parts },
connects: connections, connects: connections,
version: 1, version: 1,
lift_off: bool_to_i8(self.lift_off.to_owned()), lift_off: bool_to_i8(self.lift_off),
touch_ground: bool_to_i8(self.touch_ground.to_owned()), touch_ground: bool_to_i8(self.touch_ground),
disconnected, disconnected,
} }
} }
@ -800,8 +788,8 @@ pub mod math {
#[inline] #[inline]
pub fn distance(&self, other: &Point2D) -> f64 { pub fn distance(&self, other: &Point2D) -> f64 {
let dx = (other.x.to_owned() - self.x.to_owned()).powf(2.0); let dx = (other.x - self.x).powf(2.0);
let dy = (other.y.to_owned() - self.y.to_owned()).powf(2.0); let dy = (other.y - self.y).powf(2.0);
(dx + dy).powf(0.5) (dx + dy).powf(0.5)
} }

View File

@ -9,14 +9,13 @@ import traceback
from typing import Optional from typing import Optional
from libs.MCDR.version import Version from libs.MCDR.version import Version
from Difficult_Rocket.main import Game from Difficult_Rocket.main import Game, Console
from Difficult_Rocket.api.mod import ModInfo from Difficult_Rocket.api.mod import ModInfo
from Difficult_Rocket.api.types import Options from Difficult_Rocket.api.types import Options
from Difficult_Rocket.client import ClientWindow from Difficult_Rocket.client import ClientWindow
DR_rust_version = Version("0.2.7.0") # DR_mod 的 Rust 编写部分的兼容版本 DR_rust_version = Version("0.2.8.0") # DR_mod 的 Rust 编写部分的兼容版本
class _DR_mod_runtime(Options): class _DR_mod_runtime(Options):
@ -68,6 +67,10 @@ class DR_mod(ModInfo):
def on_load(self, game: Game, old_self: Optional["DR_mod"] = None) -> bool: def on_load(self, game: Game, old_self: Optional["DR_mod"] = None) -> bool:
if not DR_mod_runtime.DR_rust_available: if not DR_mod_runtime.DR_rust_available:
return False return False
from .console import RustConsole
game.console_class = RustConsole # 替换掉原来的 console 类
if old_self: if old_self:
game.client.window.add_sub_screen("SR1_ship", old_self.screen) game.client.window.add_sub_screen("SR1_ship", old_self.screen)
else: else:

24
mods/dr_game/console.py Normal file
View File

@ -0,0 +1,24 @@
from . import DR_mod_runtime
from Difficult_Rocket.main import Console
if DR_mod_runtime.use_DR_rust:
from .Difficult_Rocket_rs import Console_rs
class RustConsole(Console):
name = 'Rust stdin Console'
running: bool = False
console: Console_rs
def start(self):
self.console.start()
def stop(self):
return self.console.stop()
def init(self, **kwargs) -> None:
self.console = Console_rs()
def get_command(self) -> str:
return self.console.get_command()