reload !
This commit is contained in:
parent
55d21b6b02
commit
94b8869506
@ -355,7 +355,8 @@ class ClientWindow(Window):
|
||||
@_call_screen_after
|
||||
def on_command(self, command: line.CommandText):
|
||||
command.text = command.text.rstrip('\n')
|
||||
self.logger.info(tr().window.command.text().format(command))
|
||||
command.text = command.text.rstrip(' ')
|
||||
self.logger.info(tr().window.command.text().format(f"|{command.text}|"))
|
||||
command.find('/')
|
||||
if command.find('stop'):
|
||||
# self.dispatch_event('on_exit')
|
||||
@ -383,8 +384,15 @@ class ClientWindow(Window):
|
||||
self.logger.info(tr().language_available().format(os.listdir('./configs/lang')))
|
||||
self.save_info()
|
||||
elif command.find('mods'):
|
||||
for mod in self.game.mod_manager.loaded_mod_modules.values():
|
||||
self.logger.info(f"mod: {mod.name} id: {mod.mod_id} version: {mod.version}")
|
||||
if command.find('list'):
|
||||
for mod in self.game.mod_manager.loaded_mod_modules.values():
|
||||
self.logger.info(f"mod: {mod.name} id: {mod.mod_id} version: {mod.version}")
|
||||
elif command.find('reload'):
|
||||
if not len(command.text) == 0:
|
||||
print(f"reload mod: |{command.text}|")
|
||||
self.game.mod_manager.reload_mod(command.text, game=self.game)
|
||||
else:
|
||||
logger.info(tr().window.command.mods.reload.no_mod_id())
|
||||
|
||||
@_call_screen_after
|
||||
def on_message(self, message: line.CommandText):
|
||||
|
@ -10,29 +10,49 @@ import logging
|
||||
import traceback
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Optional
|
||||
from typing import List, Dict, Optional, TypeVar
|
||||
|
||||
from Difficult_Rocket.mod.api import ModInfo
|
||||
from Difficult_Rocket.utils.translate import tr
|
||||
from Difficult_Rocket.api.types import Options, Version
|
||||
|
||||
Game = TypeVar('Game')
|
||||
|
||||
logger = logging.getLogger('mod_manager')
|
||||
ONE_FILE_SUFFIX = ('.py', '.pyc', '.pyd')
|
||||
PACKAGE_SUFFIX = ('.pyz', '.zip', '.dr_mod')
|
||||
|
||||
|
||||
def _add_path_to_sys(paths: List[Path]):
|
||||
for path in paths:
|
||||
if str(path) not in sys.path:
|
||||
sys.path.append(str(path))
|
||||
|
||||
|
||||
class ModManager(Options):
|
||||
name = 'Mod Manager'
|
||||
|
||||
mods_path: List[Path] = [Path('./mods')]
|
||||
find_mod_paths: Dict[str, Path] = {}
|
||||
loaded_mod_modules: Dict[str, ModInfo] = {}
|
||||
|
||||
def get_mod_module(self, mod_name: str) -> Optional[ModInfo]:
|
||||
"""
|
||||
获取指定 mod 的模块
|
||||
:param mod_name: mod 名
|
||||
:return:
|
||||
"""
|
||||
for mod in self.loaded_mod_modules.values():
|
||||
if mod.name == mod_name:
|
||||
return mod
|
||||
return None
|
||||
|
||||
def dispatch_event(self, event_name: str, *args, **kwargs):
|
||||
"""
|
||||
分发事件
|
||||
:param event_name:
|
||||
:param args:
|
||||
:param kwargs:
|
||||
:param event_name: 事件名
|
||||
:param args: 事件参数
|
||||
:param kwargs: 事件参数
|
||||
:return:
|
||||
"""
|
||||
for mod in self.loaded_mod_modules.values():
|
||||
@ -42,14 +62,68 @@ class ModManager(Options):
|
||||
except Exception as e:
|
||||
logger.error(tr().mod.event.error().format(mod, event_name, e, traceback.format_exc()))
|
||||
|
||||
def load_mods(self, extra_path: Optional[List[Path]] = None) -> List[type(ModInfo)]:
|
||||
def load_mod(self, mod_path: Path) -> Optional[type(ModInfo)]:
|
||||
"""
|
||||
加载指定路径下的 mod
|
||||
:param mod_path: mod 的路径
|
||||
:return:
|
||||
"""
|
||||
if not mod_path.exists():
|
||||
logger.error(tr().mod.load.faild.not_exist().format(mod_path))
|
||||
return None
|
||||
_add_path_to_sys([mod_path.parent])
|
||||
try:
|
||||
if mod_path.name == '__pycache__':
|
||||
# 忽略 __pycache__ 文件夹 (Python 编译文件)
|
||||
return None
|
||||
logger.info(tr().mod.load.loading().format(mod_path))
|
||||
if mod_path.is_dir() or mod_path.suffix in PACKAGE_SUFFIX or mod_path.suffix in ONE_FILE_SUFFIX:
|
||||
# 文件夹 mod
|
||||
loading_mod = importlib.import_module(mod_path.name)
|
||||
if not hasattr(loading_mod, 'mod_class') or not issubclass(loading_mod.mod_class, ModInfo):
|
||||
logger.warning(tr().mod.load.faild.no_mod_class().format(mod_path))
|
||||
return None
|
||||
mod_class: type(ModInfo) = loading_mod.mod_class # 获取 mod 类
|
||||
if mod_class.mod_id not in self.find_mod_paths:
|
||||
self.find_mod_paths[mod_class.mod_id] = mod_path
|
||||
return mod_class
|
||||
except ImportError:
|
||||
logger.warning(tr().mod.load.faild.error().format(mod_path, traceback.format_exc()))
|
||||
return None
|
||||
|
||||
def find_mods_in_path(self, extra_mods_path: Optional[List[Path]] = None) -> List[Path]:
|
||||
"""
|
||||
查找所有 mod 路径
|
||||
:return: 找到的 mod 的路径 (未校验)
|
||||
"""
|
||||
find_path = self.mods_path + (extra_mods_path if extra_mods_path is not None else [])
|
||||
mods_path = []
|
||||
start_time = time.time()
|
||||
for path in find_path:
|
||||
if not path.exists():
|
||||
path.mkdir(parents=True)
|
||||
continue
|
||||
for mod in path.iterdir():
|
||||
if mod.name == '__pycache__':
|
||||
# 忽略 __pycache__ 文件夹 (Python 编译文件)
|
||||
continue
|
||||
if mod.is_dir() or mod.suffix in PACKAGE_SUFFIX or mod.suffix in ONE_FILE_SUFFIX:
|
||||
# 文件夹 mod
|
||||
mods_path.append(mod)
|
||||
logger.info(tr().mod.finded().format(len(mods_path), time.time() - start_time))
|
||||
return mods_path
|
||||
|
||||
def load_mods(self,
|
||||
extra_path: Optional[List[Path]] = None,
|
||||
extra_mod_path: Optional[List[Path]] = None) -> List[type(ModInfo)]:
|
||||
"""
|
||||
加载所有 mod (可提供额外的 mod 路径)
|
||||
:param extra_path: 额外的 mod 路径
|
||||
:param extra_mod_path: 额外的找到的 mod 路径
|
||||
:return:
|
||||
"""
|
||||
find_path = self.mods_path + (extra_path if extra_path is not None else [])
|
||||
sys.path += [str(path_) for path_ in find_path]
|
||||
_add_path_to_sys(find_path)
|
||||
mods = []
|
||||
start_time = time.time()
|
||||
logger.info(tr().mod.load.start().format(find_path))
|
||||
@ -58,37 +132,75 @@ class ModManager(Options):
|
||||
path.mkdir(parents=True)
|
||||
continue
|
||||
for mod in path.iterdir():
|
||||
try:
|
||||
if mod.name == '__pycache__':
|
||||
# 忽略 __pycache__ 文件夹 (Python 编译文件)
|
||||
continue
|
||||
logger.info(tr().mod.load.loading().format(mod))
|
||||
if mod.is_dir() or mod.suffix in PACKAGE_SUFFIX or mod.suffix in ONE_FILE_SUFFIX:
|
||||
# 文件夹 mod
|
||||
loading_mod = importlib.import_module(mod.name)
|
||||
if not hasattr(loading_mod, 'mod_class'):
|
||||
logger.warning(tr().mod.load.faild.no_mod_class().format(mod))
|
||||
continue
|
||||
mod_class: type(ModInfo) = loading_mod.mod_class # 获取 mod 类
|
||||
mods.append(mod_class)
|
||||
except ImportError:
|
||||
logger.warning(tr().mod.load.faild.error().format(mod))
|
||||
if (cache := self.load_mod(mod)) is not None:
|
||||
mods.append(cache)
|
||||
if extra_mod_path is not None:
|
||||
for path in extra_mod_path:
|
||||
if (cache := self.load_mod(path)) is not None:
|
||||
mods.append(cache)
|
||||
logger.info(tr().mod.load.use_time().format(time.time() - start_time))
|
||||
return mods
|
||||
|
||||
def init_mods(self, mods: List[type(ModInfo)]):
|
||||
"""
|
||||
加载 mod
|
||||
:param mods:
|
||||
:param mods: 要加载的 mod 的 ModInfo 类
|
||||
:return:
|
||||
"""
|
||||
start_time = time.time()
|
||||
for mod_class in mods:
|
||||
try:
|
||||
init_mod = mod_class()
|
||||
self.loaded_mod_modules[init_mod.name] = init_mod
|
||||
self.loaded_mod_modules[init_mod.mod_id] = init_mod
|
||||
logger.info(tr().mod.init.success().format(init_mod, init_mod.version))
|
||||
except Exception as e:
|
||||
logger.error(tr().mod.init.faild().format(mod_class, e, traceback.format_exc()))
|
||||
continue
|
||||
logger.info(tr().mod.init.use_time().format(time.time() - start_time))
|
||||
|
||||
def unload_mod(self, mod_id: str, game: Game) -> Optional[ModInfo]:
|
||||
"""
|
||||
卸载 mod
|
||||
:param mod_id: 要卸载的 mod id
|
||||
:param game: 游戏实例
|
||||
:return: 卸载的 mod 的 ModInfo 类
|
||||
"""
|
||||
if not (mod_class := self.loaded_mod_modules.get(mod_id)) and (mod_class := self.get_mod_module(mod_id)) is None:
|
||||
logger.warning(tr().mod.unload.faild.not_find().format(mod_id))
|
||||
return None
|
||||
try:
|
||||
mod_class.on_unload(game=game)
|
||||
self.loaded_mod_modules.pop(mod_class.mod_id)
|
||||
logger.info(tr().mod.unload.success().format(mod_id))
|
||||
return mod_class
|
||||
except Exception as e:
|
||||
logger.error(tr().mod.unload.faild.error().format(mod_id, e, traceback.format_exc()))
|
||||
return None
|
||||
|
||||
def reload_mod(self, mod_id: str, game: Game):
|
||||
"""
|
||||
重载 mod
|
||||
:param mod_id:
|
||||
:param game:
|
||||
:return:
|
||||
"""
|
||||
unload = self.unload_mod(mod_id, game)
|
||||
if unload is None:
|
||||
return
|
||||
mod_class: Optional[ModInfo] = None
|
||||
if unload.mod_id not in self.find_mod_paths:
|
||||
logger.warning(tr().mod.reload.faild.not_find().format(unload.mod_id))
|
||||
paths = self.find_mods_in_path()
|
||||
for path in paths:
|
||||
mod_class = self.load_mod(path)
|
||||
if mod_class is not None and mod_class.mod_id == unload.mod_id:
|
||||
self.init_mods([mod_class])
|
||||
break
|
||||
else:
|
||||
mod_class = self.load_mod(self.find_mod_paths[unload.mod_id])
|
||||
if mod_class is not None:
|
||||
self.init_mods([mod_class])
|
||||
if mod_id in self.loaded_mod_modules and mod_class is not None:
|
||||
self.loaded_mod_modules[mod_id].on_load(game=game, old_self=mod_class)
|
||||
logger.info(tr().mod.reload.success().format(mod_id))
|
||||
|
||||
|
@ -22,16 +22,23 @@ logger.logfile_datefmt = "Log file date format : "
|
||||
game_start.at = "Game MainThread start at: {}"
|
||||
|
||||
[mod]
|
||||
find.finded = "Mod founded: {}"
|
||||
load.start = "Loading Mod in path {}"
|
||||
load.use_time = "Mod loading has used: {} second"
|
||||
load.done = "All Mod loaded"
|
||||
load.loading = "Loading Mod: {}"
|
||||
load.faild.error = "Mod loading faild: {} error: {}"
|
||||
load.faild.not_exist = "Mod loading faild: {} mod path not exist"
|
||||
load.faild.no_mod_class = "Mod loading faild: {} no Mod class"
|
||||
init.success = "mod id: {} version: {}"
|
||||
init.faild = "Mod init faild: {} error: {}\nstack: {}"
|
||||
init.use_time = "Mod init has used: {} second"
|
||||
event.error = "Mod event {} error {} Mod: {}\nstack: {}"
|
||||
unload.not_find = "Mod unload faild: {} no Mod found"
|
||||
unload.faild = "Mod unload faild: {} error: {}\nstack: {}"
|
||||
unload.success = "Mod unload success: {}"
|
||||
reload.faild.not_find = "Mod reload faild: {} no Mod found, trying to find mod again"
|
||||
reload.success = "Mod reload success: {}"
|
||||
|
||||
[client]
|
||||
setup.start = "Client start loading"
|
||||
@ -57,8 +64,6 @@ text.input = "input text \"{}\""
|
||||
text.new_line = "new line"
|
||||
text.motion = "text move {}"
|
||||
text.motion_select = "text select {}"
|
||||
command.text = "input command: {}"
|
||||
message.text = "input message: {}"
|
||||
libs.local = "using local pyglet, version: {}"
|
||||
libs.outer = "using global pyglet, version: {}\n(may cause bug)"
|
||||
fonts.found = "found fonts in font lib: {}"
|
||||
@ -66,6 +71,9 @@ fonts.load = "loading fonts: {}"
|
||||
game.stop_get = "Received closing commands from {}, game closing"
|
||||
game.stop = "game closing, saving data……"
|
||||
game.end = "game closed"
|
||||
command.text = "input command: {}"
|
||||
message.text = "input message: {}"
|
||||
command.mods.reload.no_mod_id = "no mod id specified"
|
||||
|
||||
[server]
|
||||
setup.start = "Server start loading"
|
||||
|
@ -22,16 +22,23 @@ logger.logfile_datefmt = "日志文件日期格式:"
|
||||
game_start.at = "游戏主线程开始于:"
|
||||
|
||||
[mod]
|
||||
find.finded = "找到 Mod: {}"
|
||||
load.start = "开始加载路径 {} 下的 Mod"
|
||||
load.use_time = "Mod 加载消耗时间: {} 秒"
|
||||
load.done = "所有 Mod 加载完成"
|
||||
load.loading = "正在加载 Mod: {}"
|
||||
load.faild.error = "Mod 加载失败: {} 错误信息: {}"
|
||||
load.faild.not_exist = "Mod 加载失败: {} mod 路径不存在"
|
||||
load.faild.no_mod_class = "Mod 加载失败: {} 没有找到 Mod 类"
|
||||
init.success = "mod id: {} 版本号: {}"
|
||||
init.faild = "Mod 初始化失败: {} 错误信息: {}\n堆栈信息: {}"
|
||||
init.use_time = "Mod 初始化消耗时间: {} 秒"
|
||||
event.error = "Mod 事件 {} 发生错误 {} Mod: {}\n堆栈信息: {}"
|
||||
unload.faild.not_find = "Mod 卸载失败: {} 没有找到 Mod"
|
||||
unload.faild.error = "Mod 卸载失败: {} 错误信息: {}\n堆栈信息: {}"
|
||||
unload.success = "Mod 卸载成功: {}"
|
||||
reload.faild.not_find = "Mod 重载失败: {} 没有找到 Mod 原始路径,正在尝试重新查找 mod"
|
||||
reload.success = "Mod 重载成功: {}"
|
||||
|
||||
[client]
|
||||
setup.start = "客户端加载开始"
|
||||
@ -57,8 +64,6 @@ text.input = "输入字符 \"{}\""
|
||||
text.new_line = "换行"
|
||||
text.motion = "光标移动 {}"
|
||||
text.motion_select = "光标选择 {}"
|
||||
command.text = "输入命令: {}"
|
||||
message.text = "输入信息: {}"
|
||||
libs.local = "正在使用本地 pyglet 库 版本为: {}"
|
||||
libs.outer = "正在使用全局 pyglet 库 版本为: {}\n(可能会造成bug,因为本地库版本为2.0dev9)"
|
||||
fonts.found = "在字体列表中找到以下字体库: {}"
|
||||
@ -66,6 +71,9 @@ fonts.load = "正在加载字体: {}"
|
||||
game.stop_get = "从{}传入关闭指令,关闭游戏中"
|
||||
game.stop = "游戏正在关闭,保存数据中···"
|
||||
game.end = "游戏已经关闭"
|
||||
command.text = "输入命令: {}"
|
||||
message.text = "输入信息: {}"
|
||||
command.mods.reload.no_mod_id = "没有指定 mod id"
|
||||
|
||||
[server]
|
||||
setup.start = "服务端开始加载"
|
||||
|
@ -286,6 +286,8 @@ pub mod console {
|
||||
let mut input = String::new();
|
||||
let _ = std_in.read_line(&mut input);
|
||||
if !input.is_empty() {
|
||||
// 预处理
|
||||
input = input.trim().to_string();
|
||||
keyboard_input_sender.send(input).unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +77,8 @@ class DR_mod(ModInfo):
|
||||
game.console_class = RustConsole # 替换掉原来的 console 类
|
||||
|
||||
if old_self:
|
||||
game.client.window.add_sub_screen("SR1_ship", old_self.screen)
|
||||
from .sr1_ship import SR1ShipRender
|
||||
game.client.window.add_sub_screen("SR1_ship", SR1ShipRender)
|
||||
else:
|
||||
self.config.flush_option()
|
||||
logger.info("on_load")
|
||||
@ -86,11 +87,11 @@ class DR_mod(ModInfo):
|
||||
|
||||
def on_client_start(self, game: Game, client: ClientWindow):
|
||||
from .sr1_ship import SR1ShipRender
|
||||
self.screen = SR1ShipRender
|
||||
client.add_sub_screen("SR1_ship", SR1ShipRender)
|
||||
logger.info('on_client_start added sub screen')
|
||||
|
||||
def on_unload(self, game: Game):
|
||||
game.client.window.screen_list.pop("SR1_ship")
|
||||
if DR_mod_runtime.DR_rust_available:
|
||||
game.console.stop()
|
||||
game.console_class = Console
|
||||
|
Loading…
Reference in New Issue
Block a user