feat(debug): 支持网易 Minecraft 调试启动
新增 debug 子命令,自动准备开发世界、注册内置调试 MOD,并将项目行为包和资源包链接到网易运行目录,方便启动游戏后直接进入调试世界。 调试 MOD 资源随仓库一起嵌入,避免依赖本机绝对路径;Windows junction 写入剥离 verbatim 前缀后的 DOS 路径,保证 Minecraft 能正确读取链接包。
This commit is contained in:
21
debug_mod/DEBUG_ENV_SCRIPT/Config.py
Normal file
21
debug_mod/DEBUG_ENV_SCRIPT/Config.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from json import loads
|
||||
_DEBUG_INFO = "{#debug_options}"
|
||||
_TARGET_MOD_DIRS = "{#target_mod_dirs}"
|
||||
|
||||
try:
|
||||
DEBUG_CONFIG = loads(_DEBUG_INFO) if not isinstance(_DEBUG_INFO, dict) else _DEBUG_INFO
|
||||
except:
|
||||
DEBUG_CONFIG = {}
|
||||
|
||||
try:
|
||||
TARGET_MOD_DIRS = loads(_TARGET_MOD_DIRS) if not isinstance(_TARGET_MOD_DIRS, list) else _TARGET_MOD_DIRS
|
||||
except:
|
||||
TARGET_MOD_DIRS = []
|
||||
|
||||
def GET_DEBUG_IPC_PORT():
|
||||
import os
|
||||
port = os.getenv("MCDEV_DEBUG_IPC_PORT")
|
||||
if port is None:
|
||||
return None
|
||||
return int(port)
|
||||
50
debug_mod/DEBUG_ENV_SCRIPT/Game.py
Normal file
50
debug_mod/DEBUG_ENV_SCRIPT/Game.py
Normal file
@@ -0,0 +1,50 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from common.utils import xupdate
|
||||
import mod.client.extraClientApi as clientApi
|
||||
from .Config import TARGET_MOD_DIRS
|
||||
|
||||
def _RELOAD_MOD():
|
||||
state = False
|
||||
for rootModDir in TARGET_MOD_DIRS:
|
||||
try:
|
||||
if xupdate.updata_all(rootModDir):
|
||||
state = True
|
||||
except Exception:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return state
|
||||
|
||||
def INIT_RELOAD_TIME():
|
||||
return xupdate.set_load_time()
|
||||
|
||||
def SEND_CLIENT_MSG(msg):
|
||||
import gui
|
||||
print(msg)
|
||||
gui.set_left_corner_notify_msg(msg)
|
||||
|
||||
def RELOAD_MOD():
|
||||
import gui
|
||||
msg = "[Dev] Scripts reloaded successfully."
|
||||
if not _RELOAD_MOD():
|
||||
msg = "[Dev] No script updates found."
|
||||
SEND_CLIENT_MSG(msg)
|
||||
|
||||
def RELOAD_ONCE_MODULE(moduleName):
|
||||
return xupdate.update(moduleName)
|
||||
|
||||
def RELOAD_ADDON():
|
||||
import gui
|
||||
import clientlevel
|
||||
clientlevel.refresh_addons()
|
||||
SEND_CLIENT_MSG("[Dev] Add-ons reloaded successfully.")
|
||||
|
||||
def RELOAD_WORLD():
|
||||
import clientlevel
|
||||
clientlevel.restart_local_game()
|
||||
|
||||
def RELOAD_SHADERS():
|
||||
import gui
|
||||
if clientApi.ReloadAllShaders():
|
||||
SEND_CLIENT_MSG("[Dev] Shaders reloaded successfully.")
|
||||
return
|
||||
SEND_CLIENT_MSG("[Dev] No shader updates found.")
|
||||
181
debug_mod/DEBUG_ENV_SCRIPT/IPCSystem.py
Normal file
181
debug_mod/DEBUG_ENV_SCRIPT/IPCSystem.py
Normal file
@@ -0,0 +1,181 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import mod.server.extraServerApi as serverApi
|
||||
import mod.client.extraClientApi as clientApi
|
||||
from .Config import GET_DEBUG_IPC_PORT
|
||||
import socket
|
||||
import threading
|
||||
import json
|
||||
|
||||
def U16_BE(b):
|
||||
# type: (bytearray | str) -> int
|
||||
if isinstance(b, bytearray):
|
||||
return (b[0] << 8) | b[1]
|
||||
return (ord(b[0]) << 8) | ord(b[1])
|
||||
|
||||
def U32_BE(b):
|
||||
# type: (bytearray | str) -> int
|
||||
if isinstance(b, bytearray):
|
||||
return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]
|
||||
return (ord(b[0]) << 24) | (ord(b[1]) << 16) | (ord(b[2]) << 8) | ord(b[3])
|
||||
|
||||
class IPCSystem:
|
||||
def __init__(self, port=None):
|
||||
# type: (int | None) -> None
|
||||
self.port = port
|
||||
self.sock = None
|
||||
self.mLock = threading.Lock()
|
||||
self.handers = {}
|
||||
|
||||
def registerHandler(self, typeID, handler):
|
||||
# type: (int, callable) -> None
|
||||
self.handers[typeID] = handler
|
||||
|
||||
def updateHandlers(self, handlers):
|
||||
# type: (dict[int, callable]) -> None
|
||||
self.handers.update(handlers)
|
||||
|
||||
def start(self):
|
||||
if self.sock or not self.port:
|
||||
return
|
||||
threading.Thread(target=self._threadListenLoop).start()
|
||||
|
||||
def close(self):
|
||||
sock = None
|
||||
with self.mLock:
|
||||
sock = self.sock
|
||||
self.sock = None
|
||||
if sock:
|
||||
sock.shutdown(socket.SHUT_RDWR)
|
||||
sock.close()
|
||||
|
||||
def _threadListenLoop(self):
|
||||
with self.mLock:
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock = self.sock
|
||||
sock.connect(("localhost", self.port))
|
||||
sock.settimeout(0.05)
|
||||
print("[IPCSystem] 已连接到调试服务器,端口:" + str(self.port))
|
||||
# [2B TypeID][4B DataLength][Data]
|
||||
def _recvAll(sock, length):
|
||||
# type: (socket.socket, int) -> bytearray
|
||||
buf = bytearray()
|
||||
while len(buf) < length:
|
||||
more = sock.recv(length - len(buf))
|
||||
if not more:
|
||||
raise EOFError("Socket closed before receiving all data")
|
||||
buf.extend(more)
|
||||
return buf
|
||||
while 1:
|
||||
try:
|
||||
header = _recvAll(sock, 6)
|
||||
typeID = U16_BE(header[0:2])
|
||||
dataLength = U32_BE(header[2:6])
|
||||
data = _recvAll(sock, dataLength)
|
||||
except socket.timeout:
|
||||
continue
|
||||
except EOFError:
|
||||
break
|
||||
except socket.error:
|
||||
break
|
||||
except Exception:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
break
|
||||
if typeID in self.handers:
|
||||
try:
|
||||
self.handers[typeID](data)
|
||||
except Exception:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
else:
|
||||
print("[IPCSystem] 未知的TypeID数据包:" + str(typeID))
|
||||
with self.mLock:
|
||||
self.sock = None
|
||||
print("[IPCSystem] 连接已关闭")
|
||||
|
||||
_CL_GAME_COMP = None
|
||||
_SR_GAME_COMP = None
|
||||
|
||||
def AUTO_RELOAD(_=None):
|
||||
from .Game import RELOAD_MOD
|
||||
if _CL_GAME_COMP:
|
||||
_CL_GAME_COMP.AddTimer(0, lambda: RELOAD_MOD())
|
||||
return
|
||||
|
||||
def FAST_RELOAD(data):
|
||||
from .Game import RELOAD_ONCE_MODULE
|
||||
pathList = json.loads(str(data))
|
||||
def _FAST_RELOAD():
|
||||
for path in pathList:
|
||||
if RELOAD_ONCE_MODULE(path):
|
||||
print("[FAST_RELOAD] Reloaded module successfully: \"" + path + "\"")
|
||||
if _CL_GAME_COMP:
|
||||
_CL_GAME_COMP.AddTimer(0, _FAST_RELOAD)
|
||||
return
|
||||
|
||||
def EXEC_CLIENT_CODE(data):
|
||||
code = compile(str(data), "<string>", "exec")
|
||||
def _EXEC_CODE():
|
||||
print("[CLIENT_CODE] Executed successfully: " + str(eval(code)))
|
||||
_CL_GAME_COMP.AddTimer(0, _EXEC_CODE)
|
||||
|
||||
def EXEC_SERVER_CODE(data):
|
||||
code = compile(str(data), "<string>", "exec")
|
||||
def _EXEC_CODE():
|
||||
print("[SERVER_CODE] Executed successfully: " + str(eval(code)))
|
||||
_SR_GAME_COMP.AddTimer(0, _EXEC_CODE)
|
||||
|
||||
def RELOAD_GAME(_=None):
|
||||
def _RELOAD_GAME():
|
||||
from .Game import RELOAD_WORLD
|
||||
print("[RELOAD_GAME] Reloading the game...")
|
||||
RELOAD_WORLD()
|
||||
_CL_GAME_COMP.AddTimer(0, _RELOAD_GAME)
|
||||
|
||||
def RELOAD_SHADERS(_=None):
|
||||
def _RELOAD_SHADERS():
|
||||
from .Game import RELOAD_SHADERS
|
||||
RELOAD_SHADERS()
|
||||
_CL_GAME_COMP.AddTimer(0, _RELOAD_SHADERS)
|
||||
|
||||
def RELOAD_ONCE_SHADERS(fileName):
|
||||
def _RELOAD_ONCE_SHADERS():
|
||||
if clientApi.ReloadOneShader(str(fileName)):
|
||||
print("[RELOAD_ONCE_SHADERS] Reloaded shaders successfully.")
|
||||
return
|
||||
print("[RELOAD_ONCE_SHADERS] Failed to reload shaders.")
|
||||
_CL_GAME_COMP.AddTimer(0, _RELOAD_ONCE_SHADERS)
|
||||
|
||||
def RELOAD_ADDON_AND_GAME(_=None):
|
||||
def _RELOAD_ADDON_AND_GAME():
|
||||
from .Game import RELOAD_WORLD, RELOAD_ADDON
|
||||
print("[RELOAD_ADDON_AND_GAME] Reloading the addon and the game...")
|
||||
RELOAD_ADDON()
|
||||
RELOAD_WORLD()
|
||||
_CL_GAME_COMP.AddTimer(0, _RELOAD_ADDON_AND_GAME)
|
||||
|
||||
_IPCSYSTEM = IPCSystem(GET_DEBUG_IPC_PORT())
|
||||
_IPCSYSTEM.updateHandlers(
|
||||
{
|
||||
1: AUTO_RELOAD,
|
||||
2: FAST_RELOAD,
|
||||
3: EXEC_CLIENT_CODE,
|
||||
4: EXEC_SERVER_CODE,
|
||||
5: RELOAD_GAME,
|
||||
6: RELOAD_SHADERS,
|
||||
7: RELOAD_ONCE_SHADERS,
|
||||
8: RELOAD_ADDON_AND_GAME,
|
||||
}
|
||||
)
|
||||
|
||||
def ON_CLIENT_INIT():
|
||||
global _CL_GAME_COMP
|
||||
_CL_GAME_COMP = clientApi.GetEngineCompFactory().CreateGame(clientApi.GetLevelId())
|
||||
_IPCSYSTEM.start()
|
||||
|
||||
def ON_CLIENT_EXIT():
|
||||
_IPCSYSTEM.close()
|
||||
|
||||
def ON_SERVER_INIT():
|
||||
global _SR_GAME_COMP
|
||||
_SR_GAME_COMP = serverApi.GetEngineCompFactory().CreateGame(serverApi.GetLevelId())
|
||||
60
debug_mod/DEBUG_ENV_SCRIPT/QuModLibs/IN.py
Normal file
60
debug_mod/DEBUG_ENV_SCRIPT/QuModLibs/IN.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .Util import SystemSide
|
||||
lambda: "By Zero123"
|
||||
|
||||
IsServerUser = False
|
||||
ModDirName = SystemSide.__module__.split(".")[0]
|
||||
QuModLibsPath = SystemSide.__module__[:SystemSide.__module__.rfind(".")]
|
||||
|
||||
class RuntimeService:
|
||||
_serverSystemList = []
|
||||
_clientSystemList = []
|
||||
_serverStarting = False
|
||||
_clientStarting = False
|
||||
# LOADER SYSTEM
|
||||
_serverLoadBefore = [] # type: list[function]
|
||||
_clientLoadBefore = [] # type: list[function]
|
||||
_serverLoadFinish = [] # type: list[function]
|
||||
_clientLoadFinish = [] # type: list[function]
|
||||
# THREAD ID
|
||||
_serverThreadID = None
|
||||
_clientThreadID = None
|
||||
_envPlayerId = None
|
||||
|
||||
def getUnderlineModDirName():
|
||||
# type: () -> str
|
||||
""" 获取下划线MOD目录名称 返回结果与preset内置变量__LQuModName__一致 (仅支持ascii字符串) """
|
||||
newStr = [] # type: list[int]
|
||||
for i, _charStr in enumerate(ModDirName):
|
||||
_char = ord(_charStr)
|
||||
if (_char >= 65 and _char <= 90):
|
||||
# 大写内容 进行处理
|
||||
if i > 0:
|
||||
newStr.append(ord("_"))
|
||||
newStr.append(_char + (97 - 65))
|
||||
continue
|
||||
# 常规小写内容 直接追加
|
||||
newStr.append(_char)
|
||||
return "".join((chr(x) for x in newStr))
|
||||
|
||||
def GET_THREAD_ID():
|
||||
""" 获取当前线程ID """
|
||||
from threading import current_thread
|
||||
return current_thread().ident
|
||||
|
||||
def IS_SERVER_THREAD():
|
||||
""" 检查是不是服务端线程 """
|
||||
return RuntimeService._serverThreadID != None and GET_THREAD_ID() == RuntimeService._serverThreadID
|
||||
|
||||
def IS_CLIENT_THREAD():
|
||||
""" 检查是不是客户端线程 """
|
||||
return RuntimeService._clientThreadID != None and GET_THREAD_ID() == RuntimeService._clientThreadID
|
||||
|
||||
def GET_THREAD_TYPE():
|
||||
""" 获取线程类型 -1.主线程 0.服务端线程 1.客户端线程 """
|
||||
tid = GET_THREAD_ID()
|
||||
if tid == RuntimeService._serverThreadID:
|
||||
return 0
|
||||
elif tid == RuntimeService._clientThreadID:
|
||||
return 1
|
||||
return -1
|
||||
17
debug_mod/DEBUG_ENV_SCRIPT/QuModLibs/Information.py
Normal file
17
debug_mod/DEBUG_ENV_SCRIPT/QuModLibs/Information.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
Version = "1.4.1" # 版本信息
|
||||
ApiVersion = 4 # API版本
|
||||
Author = "Zero123" # 创作者
|
||||
ContactInformation = "QQ:913702423" # 联系方式
|
||||
Other = """
|
||||
# QuModLibs By Zero123(网易:游趣开发组) 别名:一灵 | h2v-wither123... BilBil-UID:456549011
|
||||
# 开源协议: BSD(3)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
"""
|
||||
164
debug_mod/DEBUG_ENV_SCRIPT/QuModLibs/QuMod.py
Normal file
164
debug_mod/DEBUG_ENV_SCRIPT/QuModLibs/QuMod.py
Normal file
@@ -0,0 +1,164 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from mod.common.mod import Mod
|
||||
import mod.server.extraServerApi as serverApi
|
||||
import mod.client.extraClientApi as clientApi
|
||||
from .Util import TRY_EXEC_FUN
|
||||
from . import IN
|
||||
|
||||
class _TempData:
|
||||
""" modMain临时数据储存 包含了初始化注册的一些信息 """
|
||||
_nativePyServer = []
|
||||
_nativePyClient = []
|
||||
_serverInitCall = []
|
||||
_clientInitCall = []
|
||||
_threadAnalysis = False
|
||||
|
||||
@Mod.Binding(name = "QuMod_"+IN.ModDirName, version = "1.0.0")
|
||||
class QMain(object):
|
||||
""" QuMod MAIN入口逻辑 """
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@Mod.InitServer()
|
||||
def serverInit(self):
|
||||
# 服务端初始化
|
||||
IN.IsServerUser = True
|
||||
self._regNativePyServer()
|
||||
self._loadServerInitFuncs()
|
||||
if _TempData._threadAnalysis:
|
||||
from threading import current_thread
|
||||
IN.RuntimeService._serverThreadID = current_thread().ident
|
||||
if IN.RuntimeService._serverSystemList or IN.RuntimeService._serverLoadBefore:
|
||||
from .Systems.Loader.Server import LoaderSystem
|
||||
LoaderSystem.getSystem() # 初始化服务端加载器
|
||||
|
||||
@Mod.InitClient()
|
||||
def clientInit(self):
|
||||
IN.RuntimeService._envPlayerId = clientApi.GetLocalPlayerId()
|
||||
self._regNativePyClient()
|
||||
self._loadClientInitFuncs()
|
||||
if _TempData._threadAnalysis:
|
||||
from threading import current_thread
|
||||
IN.RuntimeService._clientThreadID = current_thread().ident
|
||||
if IN.RuntimeService._clientSystemList or IN.RuntimeService._clientLoadBefore:
|
||||
from .Systems.Loader.Client import LoaderSystem
|
||||
LoaderSystem.getSystem() # 初始化客户端加载器
|
||||
|
||||
def _regNativePyClient(self):
|
||||
""" 加载原版Python客户端系统注册 """
|
||||
for args in _TempData._nativePyClient:
|
||||
clientApi.RegisterSystem(*args)
|
||||
_TempData._nativePyClient = []
|
||||
|
||||
def _regNativePyServer(self):
|
||||
""" 加载原版Python服务端系统注册 """
|
||||
for args in _TempData._nativePyServer:
|
||||
serverApi.RegisterSystem(*args)
|
||||
_TempData._nativePyServer = []
|
||||
|
||||
def _loadServerInitFuncs(self):
|
||||
""" 加载服务端初始化函数 """
|
||||
for funObj in _TempData._serverInitCall:
|
||||
TRY_EXEC_FUN(funObj)
|
||||
_TempData._serverInitCall = []
|
||||
|
||||
def _loadClientInitFuncs(self):
|
||||
""" 加载客户端初始化函数 """
|
||||
for funObj in _TempData._clientInitCall:
|
||||
TRY_EXEC_FUN(funObj)
|
||||
_TempData._clientInitCall = []
|
||||
|
||||
@Mod.DestroyServer()
|
||||
def serverDestroy(self):
|
||||
pass
|
||||
|
||||
@Mod.DestroyClient()
|
||||
def clientDestroy(self):
|
||||
pass
|
||||
|
||||
class EasyMod:
|
||||
""" 简易Mod构造器 """
|
||||
def __init__(self, modDirName=None):
|
||||
# type: (str | None) -> None
|
||||
self._modDirName = modDirName if modDirName else IN.ModDirName
|
||||
""" Mod目录名 """
|
||||
|
||||
def regServer(self, relPath="", systemName=None):
|
||||
# type: (str, str | None) -> EasyMod
|
||||
""" 注册服务端(相对目录) """
|
||||
REG_SERVER_MODULE("{}.{}".format(self._modDirName, relPath), systemName)
|
||||
return self
|
||||
|
||||
def Server(self, relPath="", systemName=None):
|
||||
# type: (str, str | None) -> EasyMod
|
||||
""" 便捷式服务端注册 """
|
||||
return self.regServer(relPath, systemName)
|
||||
|
||||
def Client(self, relPath="", systemName=None):
|
||||
# type: (str, str | None) -> EasyMod
|
||||
""" 便捷式客户端注册 """
|
||||
return self.regClient(relPath, systemName)
|
||||
|
||||
def regClient(self, relPath="", systemName=None):
|
||||
# type: (str, str | None) -> EasyMod
|
||||
""" 注册客户端(相对目录) """
|
||||
REG_CLIENT_MODULE("{}.{}".format(self._modDirName, relPath), systemName)
|
||||
return self
|
||||
|
||||
def addServerInitCallFunc(self, callFunc=lambda: None):
|
||||
# type: (object) -> EasyMod
|
||||
""" 添加服务端初始化调用方法 """
|
||||
REG_SERVER_INIT_CALL(callFunc)
|
||||
return self
|
||||
|
||||
def addClientInitCallFunc(self, callFunc=lambda: None):
|
||||
# type: (object) -> EasyMod
|
||||
""" 添加客户端初始化调用方法 """
|
||||
REG_CLIENT_INIT_CALL(callFunc)
|
||||
return self
|
||||
|
||||
def START_THREAD_ANALYSIS():
|
||||
""" 启用线程分析 """
|
||||
_TempData._threadAnalysis = True
|
||||
|
||||
def STOP_THREAD_ANALYSIS():
|
||||
""" 禁用线程分析 """
|
||||
_TempData._threadAnalysis = False
|
||||
|
||||
def REG_SERVER_MODULE(absPath, systemName=None, _index=-1):
|
||||
# type: (str, str | None, int) -> None
|
||||
""" 注册服务端模块 (绝对路径) """
|
||||
if _index < 0:
|
||||
return IN.RuntimeService._serverSystemList.append((absPath, systemName))
|
||||
return IN.RuntimeService._serverSystemList.insert(_index, (absPath, systemName))
|
||||
|
||||
def REG_CLIENT_MODULE(absPath, systemName=None, _index=-1):
|
||||
# type: (str, str | None, int) -> None
|
||||
""" 注册客户端模块 (绝对路径) """
|
||||
if _index < 0:
|
||||
return IN.RuntimeService._clientSystemList.append((absPath, systemName))
|
||||
return IN.RuntimeService._clientSystemList.insert(_index, (absPath, systemName))
|
||||
|
||||
def REG_SERVER_INIT_CALL(func=lambda: None):
|
||||
# type: (function) -> function
|
||||
""" 注册服务端初始化调用函数 """
|
||||
_TempData._serverInitCall.append(func)
|
||||
return func
|
||||
|
||||
def REG_CLIENT_INIT_CALL(func=lambda: None):
|
||||
# type: (function) -> function
|
||||
""" 注册客户端初始化调用函数 """
|
||||
_TempData._clientInitCall.append(func)
|
||||
return func
|
||||
|
||||
def PRE_SERVER_LOADER_HOOK(func=lambda: None):
|
||||
# type: (function) -> function
|
||||
""" 注册服务端加载器处理前的前置逻辑 (此时依然可以注册文件 该功能用于前置关联的校验处理) """
|
||||
IN.RuntimeService._serverLoadBefore.append(func)
|
||||
return func
|
||||
|
||||
def PRE_CLIENT_LOADER_HOOK(func=lambda: None):
|
||||
# type: (function) -> function
|
||||
""" 注册客户端加载器处理前的前置逻辑 (此时依然可以注册文件 该功能用于前置关联的校验处理) """
|
||||
IN.RuntimeService._clientLoadBefore.append(func)
|
||||
return func
|
||||
185
debug_mod/DEBUG_ENV_SCRIPT/QuModLibs/Systems/Loader/Client.py
Normal file
185
debug_mod/DEBUG_ENV_SCRIPT/QuModLibs/Systems/Loader/Client.py
Normal file
@@ -0,0 +1,185 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import mod.client.extraClientApi as clientApi
|
||||
from ...Util import errorPrint, TRY_EXEC_FUN, getObjectPathName
|
||||
from ...IN import RuntimeService
|
||||
from .SharedRes import (
|
||||
CallObjData,
|
||||
EasyListener,
|
||||
SERVER_CALL_EVENT,
|
||||
CLIENT_CALL_EVENT,
|
||||
NAMESPACE,
|
||||
SYSTEMNAME
|
||||
)
|
||||
lambda: "By Zero123"
|
||||
ClientSystem = clientApi.GetClientSystemCls()
|
||||
engineSpaceName, engineSystemName = clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName()
|
||||
|
||||
def clientImportModule(filePath):
|
||||
""" 客户端文件导入 """
|
||||
return clientApi.ImportModule(filePath)
|
||||
|
||||
class LoaderSystem(ClientSystem, EasyListener):
|
||||
""" QuMod加载器系统
|
||||
加载器承担了系统文件的加载以及事件监听 系统通信
|
||||
"""
|
||||
@staticmethod
|
||||
def getSystem():
|
||||
# type: () -> LoaderSystem
|
||||
""" 获取加载器系统 如果未注册将会自动注册并返回 """
|
||||
system = clientApi.GetSystem(NAMESPACE, SYSTEMNAME)
|
||||
if system:
|
||||
return system
|
||||
return clientApi.RegisterSystem(NAMESPACE, SYSTEMNAME, LoaderSystem.__module__ + "." + LoaderSystem.__name__)
|
||||
|
||||
_REG_CALL_FUNCS = {}
|
||||
_REG_STATIC_LISTEN_FUNCS = {}
|
||||
_DY_IMP_CACHE = {}
|
||||
|
||||
@staticmethod
|
||||
def dyImportModule(modulePath):
|
||||
if not modulePath in LoaderSystem._DY_IMP_CACHE:
|
||||
LoaderSystem._DY_IMP_CACHE[modulePath] = clientImportModule(modulePath)
|
||||
return LoaderSystem._DY_IMP_CACHE[modulePath]
|
||||
|
||||
@staticmethod
|
||||
def REG_DESTROY_CALL_FUNC(func=lambda: None):
|
||||
""" 适用于静态函数的注册销毁时回调 """
|
||||
keyName = getObjectPathName(func)
|
||||
if not keyName in LoaderSystem._REG_CALL_FUNCS:
|
||||
# callFunc = lambda: LoaderSystem._REG_CALL_FUNCS[keyName]()
|
||||
LoaderSystem.getSystem().addDestroyCall(func)
|
||||
LoaderSystem._REG_CALL_FUNCS[keyName] = func
|
||||
|
||||
@staticmethod
|
||||
def REG_STATIC_LISTEN_FUNC(eventName="", funcObj=lambda: None):
|
||||
""" 注册静态监听函数 """
|
||||
keyName = getObjectPathName(funcObj)
|
||||
if not keyName in LoaderSystem._REG_STATIC_LISTEN_FUNCS:
|
||||
# callFunc = lambda *args: LoaderSystem._REG_STATIC_LISTEN_FUNCS[keyName](*args)
|
||||
LoaderSystem.getSystem().nativeStaticListen(eventName, funcObj)
|
||||
LoaderSystem._REG_STATIC_LISTEN_FUNCS[keyName] = funcObj
|
||||
|
||||
def __init__(self, namespace, systemName):
|
||||
ClientSystem.__init__(self, namespace, systemName)
|
||||
EasyListener.__init__(self)
|
||||
RuntimeService._clientStarting = True
|
||||
self.namespace = namespace
|
||||
self.systemName = systemName
|
||||
self._systemList = RuntimeService._clientSystemList
|
||||
self._initState = False
|
||||
self._regInitState = False
|
||||
self._waitTime = 0.0
|
||||
self._callQueue = [] # type: list[CallObjData]
|
||||
self._onDestroyCall = []
|
||||
self._onDestroyCall_LAST = []
|
||||
""" 后置销毁触发 通常是内部使用确保在用户业务之后执行 """
|
||||
self._initSystemListen()
|
||||
self.systemInit()
|
||||
|
||||
def _initSystemListen(self):
|
||||
self.ListenForEvent(NAMESPACE, SYSTEMNAME, SERVER_CALL_EVENT, self, self._systemCallListener)
|
||||
|
||||
def _easyListenForEvent(self, eventName="", parent=None, func=lambda: None):
|
||||
return self.ListenForEvent(engineSpaceName, engineSystemName, eventName, parent, func)
|
||||
|
||||
def _easyUnListenForEvent(self, eventName="", parent=None, func=lambda: None):
|
||||
return self.UnListenForEvent(engineSpaceName, engineSystemName, eventName, parent, func)
|
||||
|
||||
def sendCall(self, apiName="", args=tuple(), kwargs=dict()):
|
||||
""" 向服务器端请求调用 """
|
||||
sendData = self._packageCallArgs(apiName, args, kwargs)
|
||||
self.NotifyToServer(CLIENT_CALL_EVENT, sendData)
|
||||
|
||||
def addDestroyCall(self, funObj, doubleCheck=True):
|
||||
""" 添加销毁触发 """
|
||||
if doubleCheck and funObj in self._onDestroyCall:
|
||||
return
|
||||
self._onDestroyCall.append(funObj)
|
||||
|
||||
def removeDestroyCall(self, funObj):
|
||||
""" 移除销毁触发 """
|
||||
if funObj in self._onDestroyCall:
|
||||
self._onDestroyCall.remove(funObj)
|
||||
|
||||
def Destroy(self):
|
||||
# 用户级destroy执行
|
||||
for obj in self._onDestroyCall:
|
||||
TRY_EXEC_FUN(obj)
|
||||
self._onDestroyCall = []
|
||||
# 高权限destroy执行
|
||||
for obj in self._onDestroyCall_LAST:
|
||||
TRY_EXEC_FUN(obj)
|
||||
self._onDestroyCall_LAST = []
|
||||
RuntimeService._clientStarting = False
|
||||
|
||||
def getSystemList(self):
|
||||
# type: () -> list[tuple[str, str | None]]
|
||||
return self._systemList
|
||||
|
||||
def removeCallObjByUid(self, _uid = ""):
|
||||
""" 尝试移除特定uid的callObj 如果存在 """
|
||||
for i, obj in enumerate(self._callQueue):
|
||||
if obj._uid == _uid:
|
||||
del self._callQueue[i]
|
||||
break
|
||||
|
||||
def proxyRegister(self, funcObj):
|
||||
""" 代理注册 """
|
||||
from functools import wraps
|
||||
@wraps(funcObj)
|
||||
def newFun(*args, **kwargs):
|
||||
callObj = CallObjData(funcObj, args, kwargs)
|
||||
self._callQueue.append(callObj)
|
||||
return callObj
|
||||
return newFun
|
||||
|
||||
def unsafeUpdate(self, callObjData):
|
||||
# type: (CallObjData) -> bool
|
||||
""" 不安全的强制刷新 """
|
||||
if callObjData in self._callQueue:
|
||||
self._callQueue.remove(callObjData)
|
||||
callObjData.callObj(*callObjData.args, **callObjData.kwargs)
|
||||
return True
|
||||
return False
|
||||
|
||||
def Update(self):
|
||||
self.regSystemInit()
|
||||
if self._callQueue:
|
||||
for obj in self._callQueue:
|
||||
try:
|
||||
obj.callObj(*obj.args, **obj.kwargs)
|
||||
except Exception as e:
|
||||
errorPrint("{} call执行异常 {}".format(obj.callObj, e))
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
self._callQueue = []
|
||||
return ClientSystem.Update(self)
|
||||
|
||||
def systemInit(self):
|
||||
self._initState = True
|
||||
|
||||
def regSystemInit(self):
|
||||
""" 系统信息注册初始化 """
|
||||
if self._regInitState:
|
||||
return
|
||||
# 加载Before事件
|
||||
for funcObj in RuntimeService._clientLoadBefore:
|
||||
TRY_EXEC_FUN(funcObj)
|
||||
# 因历史原因systemName已废弃 此处仅兼容旧版项目
|
||||
for path, _ in self._systemList:
|
||||
sysObj = None
|
||||
try:
|
||||
sysObj = clientImportModule(path)
|
||||
if sysObj == None:
|
||||
errorPrint("[客户端] 系统文件加载失败(API异常): {}".format(path))
|
||||
continue
|
||||
except Exception as e:
|
||||
errorPrint("[客户端] 系统文件错误: {} ({})".format(path, e))
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
continue
|
||||
# if not systemName: systemName = uuid4().hex
|
||||
self._regInitState = True
|
||||
# 加载Finish事件
|
||||
for funcObj in RuntimeService._clientLoadFinish:
|
||||
TRY_EXEC_FUN(funcObj)
|
||||
200
debug_mod/DEBUG_ENV_SCRIPT/QuModLibs/Systems/Loader/Server.py
Normal file
200
debug_mod/DEBUG_ENV_SCRIPT/QuModLibs/Systems/Loader/Server.py
Normal file
@@ -0,0 +1,200 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import mod.server.extraServerApi as serverApi
|
||||
from ...Util import errorPrint, TRY_EXEC_FUN, getObjectPathName
|
||||
from ...IN import RuntimeService
|
||||
from .SharedRes import (
|
||||
CallObjData,
|
||||
EasyListener,
|
||||
SERVER_CALL_EVENT,
|
||||
CLIENT_CALL_EVENT,
|
||||
NAMESPACE,
|
||||
SYSTEMNAME
|
||||
)
|
||||
lambda: "By Zero123"
|
||||
ServerSystem = serverApi.GetServerSystemCls()
|
||||
engineSpaceName, engineSystemName = serverApi.GetEngineNamespace(), serverApi.GetEngineSystemName()
|
||||
|
||||
def serverImportModule(filePath):
|
||||
""" 服务端文件导入 """
|
||||
return serverApi.ImportModule(filePath)
|
||||
|
||||
class LoaderSystem(ServerSystem, EasyListener):
|
||||
""" QuMod加载器系统
|
||||
加载器承担了系统文件的加载以及事件监听 系统通信
|
||||
"""
|
||||
@staticmethod
|
||||
def getSystem():
|
||||
# type: () -> LoaderSystem
|
||||
""" 获取加载器系统 如果未注册将会自动注册并返回 """
|
||||
system = serverApi.GetSystem(NAMESPACE, SYSTEMNAME)
|
||||
if system:
|
||||
return system
|
||||
return serverApi.RegisterSystem(NAMESPACE, SYSTEMNAME, LoaderSystem.__module__ + "." + LoaderSystem.__name__)
|
||||
|
||||
_REG_CALL_FUNCS = {}
|
||||
_REG_STATIC_LISTEN_FUNCS = {}
|
||||
_DY_IMP_CACHE = {}
|
||||
|
||||
@staticmethod
|
||||
def dyImportModule(modulePath):
|
||||
if not modulePath in LoaderSystem._DY_IMP_CACHE:
|
||||
LoaderSystem._DY_IMP_CACHE[modulePath] = serverImportModule(modulePath)
|
||||
return LoaderSystem._DY_IMP_CACHE[modulePath]
|
||||
|
||||
@staticmethod
|
||||
def REG_DESTROY_CALL_FUNC(func=lambda: None):
|
||||
""" 适用于静态函数的注册销毁时回调 """
|
||||
keyName = getObjectPathName(func)
|
||||
if not keyName in LoaderSystem._REG_CALL_FUNCS:
|
||||
# callFunc = lambda: LoaderSystem._REG_CALL_FUNCS[keyName]()
|
||||
LoaderSystem.getSystem().addDestroyCall(func)
|
||||
LoaderSystem._REG_CALL_FUNCS[keyName] = func
|
||||
|
||||
@staticmethod
|
||||
def REG_STATIC_LISTEN_FUNC(eventName="", funcObj=lambda: None):
|
||||
""" 注册静态监听函数 """
|
||||
keyName = getObjectPathName(funcObj)
|
||||
if not keyName in LoaderSystem._REG_STATIC_LISTEN_FUNCS:
|
||||
# callFunc = lambda *args: LoaderSystem._REG_STATIC_LISTEN_FUNCS[keyName](*args)
|
||||
LoaderSystem.getSystem().nativeStaticListen(eventName, funcObj)
|
||||
LoaderSystem._REG_STATIC_LISTEN_FUNCS[keyName] = funcObj
|
||||
|
||||
def __init__(self, namespace, systemName):
|
||||
ServerSystem.__init__(self, namespace, systemName)
|
||||
EasyListener.__init__(self)
|
||||
RuntimeService._serverStarting = True
|
||||
self.namespace = namespace
|
||||
self.systemName = systemName
|
||||
self.rpcPlayerId = None
|
||||
self._systemList = RuntimeService._serverSystemList
|
||||
self._initState = False
|
||||
self._regInitState = False
|
||||
self._waitTime = 0.0
|
||||
self._onDestroyCall = []
|
||||
self._onDestroyCall_LAST = []
|
||||
""" 后置销毁触发 通常是内部使用确保在用户业务之后执行 """
|
||||
self._initSystemListen()
|
||||
self.systemInit()
|
||||
|
||||
def _systemCallListenerHook(self, args={}):
|
||||
target = "__id__"
|
||||
if target in args:
|
||||
self.rpcPlayerId = args[target]
|
||||
return
|
||||
self.rpcPlayerId = None
|
||||
|
||||
def _initSystemListen(self):
|
||||
self.ListenForEvent(NAMESPACE, SYSTEMNAME, CLIENT_CALL_EVENT, self, self._systemCallListener)
|
||||
|
||||
def _easyListenForEvent(self, eventName="", parent=None, func=lambda: None):
|
||||
return self.ListenForEvent(engineSpaceName, engineSystemName, eventName, parent, func)
|
||||
|
||||
def _easyUnListenForEvent(self, eventName="", parent=None, func=lambda: None):
|
||||
return self.UnListenForEvent(engineSpaceName, engineSystemName, eventName, parent, func)
|
||||
|
||||
def sendCall(self, playerId="", apiName="", args=tuple(), kwargs=dict()):
|
||||
""" 向指定玩家客户端请求调用 当playerId声明为*时代表全体玩家 """
|
||||
sendData = self._packageCallArgs(apiName, args, kwargs)
|
||||
if playerId == "*":
|
||||
self.BroadcastToAllClient(SERVER_CALL_EVENT, sendData)
|
||||
return
|
||||
self.NotifyToClient(playerId, SERVER_CALL_EVENT, sendData)
|
||||
|
||||
def sendMultiClientsCall(self, playerListId=[], apiName="", args=tuple(), kwargs=dict()):
|
||||
""" 批量向多个玩家客户端发包相同的调用数据 """
|
||||
sendData = self._packageCallArgs(apiName, args, kwargs)
|
||||
self.NotifyToMultiClients(playerListId, SERVER_CALL_EVENT, sendData)
|
||||
|
||||
def addDestroyCall(self, funObj, doubleCheck=True):
|
||||
""" 添加销毁触发 """
|
||||
if doubleCheck and funObj in self._onDestroyCall:
|
||||
return
|
||||
self._onDestroyCall.append(funObj)
|
||||
|
||||
def removeDestroyCall(self, funObj):
|
||||
""" 移除销毁触发 """
|
||||
if funObj in self._onDestroyCall:
|
||||
self._onDestroyCall.remove(funObj)
|
||||
|
||||
def Destroy(self):
|
||||
# 用户级destroy执行
|
||||
for obj in self._onDestroyCall:
|
||||
TRY_EXEC_FUN(obj)
|
||||
self._onDestroyCall = []
|
||||
# 高权限destroy执行
|
||||
for obj in self._onDestroyCall_LAST:
|
||||
TRY_EXEC_FUN(obj)
|
||||
self._onDestroyCall_LAST = []
|
||||
RuntimeService._serverStarting = False
|
||||
|
||||
def getSystemList(self):
|
||||
# type: () -> list[tuple[str, str | None]]
|
||||
return self._systemList
|
||||
|
||||
def removeCallObjByUid(self, _uid = ""):
|
||||
""" 尝试移除特定uid的callObj 如果存在 """
|
||||
for i, obj in enumerate(self._callQueue):
|
||||
if obj._uid == _uid:
|
||||
del self._callQueue[i]
|
||||
break
|
||||
|
||||
def proxyRegister(self, funcObj):
|
||||
""" 代理注册 """
|
||||
from functools import wraps
|
||||
@wraps(funcObj)
|
||||
def newFun(*args, **kwargs):
|
||||
callObj = CallObjData(funcObj, args, kwargs)
|
||||
self._callQueue.append(callObj)
|
||||
return callObj
|
||||
return newFun
|
||||
|
||||
def unsafeUpdate(self, callObjData):
|
||||
# type: (CallObjData) -> bool
|
||||
""" 不安全的强制刷新 """
|
||||
if callObjData in self._callQueue:
|
||||
self._callQueue.remove(callObjData)
|
||||
callObjData.callObj(*callObjData.args, **callObjData.kwargs)
|
||||
return True
|
||||
return False
|
||||
|
||||
def Update(self):
|
||||
self.regSystemInit()
|
||||
if self._callQueue:
|
||||
for obj in self._callQueue:
|
||||
try:
|
||||
obj.callObj(*obj.args, **obj.kwargs)
|
||||
except Exception as e:
|
||||
errorPrint("{} call执行异常 {}".format(obj.callObj, e))
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
self._callQueue = []
|
||||
return ServerSystem.Update(self)
|
||||
|
||||
def systemInit(self):
|
||||
self._initState = True
|
||||
|
||||
def regSystemInit(self):
|
||||
""" 系统信息注册初始化 """
|
||||
if self._regInitState:
|
||||
return
|
||||
# 加载Before事件
|
||||
for funcObj in RuntimeService._serverLoadBefore:
|
||||
TRY_EXEC_FUN(funcObj)
|
||||
# 因历史原因systemName已废弃 此处仅兼容旧版项目
|
||||
for path, _ in self._systemList:
|
||||
sysObj = None
|
||||
try:
|
||||
sysObj = serverImportModule(path)
|
||||
if sysObj == None:
|
||||
errorPrint("[服务端] 系统文件加载失败(API异常): {}".format(path))
|
||||
continue
|
||||
except Exception as e:
|
||||
errorPrint("[服务端] 系统文件错误: {} ({})".format(path, e))
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
continue
|
||||
# if not systemName: systemName = uuid4().hex
|
||||
self._regInitState = True
|
||||
# 加载Finish事件
|
||||
for funcObj in RuntimeService._serverLoadFinish:
|
||||
TRY_EXEC_FUN(funcObj)
|
||||
175
debug_mod/DEBUG_ENV_SCRIPT/QuModLibs/Systems/Loader/SharedRes.py
Normal file
175
debug_mod/DEBUG_ENV_SCRIPT/QuModLibs/Systems/Loader/SharedRes.py
Normal file
@@ -0,0 +1,175 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from uuid import uuid4
|
||||
from ...IN import ModDirName
|
||||
from ...Util import QStruct
|
||||
|
||||
class CallObjData:
|
||||
def __init__(self, callObj, args = tuple(), kwargs = {}):
|
||||
self.callObj = callObj
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self._uid = None
|
||||
|
||||
class EmptyContext:
|
||||
pass
|
||||
|
||||
NAMESPACE = "Qu_" + ModDirName
|
||||
SYSTEMNAME = "{}_QLoader_system".format(ModDirName)
|
||||
SERVER_CALL_EVENT = "{}_QServer".format(ModDirName)
|
||||
CLIENT_CALL_EVENT = "{}_QClient".format(ModDirName)
|
||||
|
||||
class EasyListener:
|
||||
def __init__(self):
|
||||
self._callQueue = [] # type: list[CallObjData]
|
||||
self._emptyContext = EmptyContext()
|
||||
self._QCustomAPI = {} # type: dict[str, function]
|
||||
|
||||
def regCustomApi(self, apiName="", func=lambda: None):
|
||||
""" 注册自定义API """
|
||||
self._QCustomAPI[apiName] = func
|
||||
|
||||
def removeCustomApi(self, apiName=""):
|
||||
""" 删除指定API如果存在 """
|
||||
if apiName in self._QCustomAPI:
|
||||
del self._QCustomAPI[apiName]
|
||||
|
||||
def getCustomApi(self, apiName=""):
|
||||
""" 获取自定义API如果存在 """
|
||||
return self._QCustomAPI.get(apiName)
|
||||
|
||||
def localCall(self, apiName="", *args, **kwargs):
|
||||
""" 本地调用 请确保API函数存在注册 否则抛出异常 """
|
||||
return self._QCustomAPI[apiName](*args, **kwargs)
|
||||
|
||||
def _systemCallListener(self, args={}):
|
||||
""" 系统call机制监听器(接收消息处理) """
|
||||
api = args["api"]
|
||||
ag = EasyListener._unPackRefArgs(args["args"])
|
||||
kwargs = EasyListener._unPackRefDictArgs(args["kw"])
|
||||
self._systemCallListenerHook(args)
|
||||
return self.localCall(api, *ag, **kwargs)
|
||||
|
||||
def _systemCallListenerHook(self, _={}):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _unPackRefArgs(data):
|
||||
# type: (list) -> list
|
||||
""" Ref解包Args数据 """
|
||||
for i, v in enumerate(data):
|
||||
if QStruct.isSignData(v):
|
||||
data[i] = QStruct.loadSignData(v).onNetUnPack()
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def _unPackRefDictArgs(data):
|
||||
# type: (dict) -> dict
|
||||
""" Ref解包Dict Args数据 """
|
||||
for k, v in data.items():
|
||||
if QStruct.isSignData(v):
|
||||
data[k] = QStruct.loadSignData(v).onNetUnPack()
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def _packArgs(data):
|
||||
# type: (tuple | list) -> list
|
||||
""" 打包Args数据 """
|
||||
newDataList = []
|
||||
for v in data:
|
||||
if isinstance(v, QStruct):
|
||||
newDataList.append(v.signDumps())
|
||||
continue
|
||||
newDataList.append(v)
|
||||
return newDataList
|
||||
|
||||
@staticmethod
|
||||
def _packDictArgs(data):
|
||||
# type: (dict) -> dict
|
||||
""" 打包Dict数据 keyName=xxx """
|
||||
newDict = {}
|
||||
for k, v in data.items():
|
||||
if isinstance(v, QStruct):
|
||||
newDict[k] = v.signDumps()
|
||||
continue
|
||||
newDict[k] = v
|
||||
return newDict
|
||||
|
||||
def _packageCallArgs(self, apiName="", args=tuple(), kwargs=dict()):
|
||||
""" 打包API参数(发送消息处理) """
|
||||
return {"api":apiName,"args":EasyListener._packArgs(args),"kw":EasyListener._packDictArgs(kwargs)}
|
||||
|
||||
def mallocRandomMetName(self):
|
||||
""" 动态分配随机方法名 """
|
||||
randomName = ""
|
||||
while not randomName or hasattr(self, randomName):
|
||||
randomName = "Q_{}".format(uuid4().hex)
|
||||
return randomName
|
||||
|
||||
def _allocMethodWithOUTFunction(self, callFunc=lambda *_: None):
|
||||
""" 基于外部函数分配一个映射的内部方法(介于网易Listen必须依赖内部方法 故有了该方法) """
|
||||
newFuncName = self.mallocRandomMetName()
|
||||
newFunc = lambda *args, **kwargs: callFunc(*args, **kwargs)
|
||||
newFunc.__name__ = newFuncName
|
||||
setattr(self, newFuncName, newFunc)
|
||||
return newFunc
|
||||
|
||||
def _delMethod(self, methodFunc=lambda *_: None):
|
||||
""" 对照与_allocMethodWithOUTFunction的反向删除 """
|
||||
try:
|
||||
delattr(self, methodFunc.__name__)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def nativeStaticListen(self, eventName="", callFunc=lambda *_: None):
|
||||
""" 原生静态监听注册 不支持运行时注销 """
|
||||
def _reg():
|
||||
self._easyListenForEvent(eventName, self, self._allocMethodWithOUTFunction(callFunc))
|
||||
self._callQueue.append(CallObjData(_reg))
|
||||
|
||||
def nativeListen(self, eventName="", parent=None, callFunc=lambda *_: None, updateNow=False):
|
||||
# type: (str, object, object, bool) -> CallObjData | None
|
||||
""" 原生动态监听 当updateNow声明为False时将会添加到系统队列安全的等待注册 """
|
||||
if not parent:
|
||||
parent = self._emptyContext
|
||||
newFuncName = "QListen{}_{}".format(id(parent), callFunc.__name__)
|
||||
newFunc = lambda *args: callFunc(*args)
|
||||
newFunc.__name__ = newFuncName
|
||||
if hasattr(self, newFuncName):
|
||||
print("[Error] 请勿在单个可执行对象上监听重复的事件")
|
||||
return None
|
||||
def _reg():
|
||||
setattr(self, newFuncName, newFunc)
|
||||
self._easyListenForEvent(eventName, self, newFunc)
|
||||
waitCallObj = CallObjData(_reg)
|
||||
waitCallObj._uid = newFuncName
|
||||
self._callQueue.append(waitCallObj)
|
||||
if updateNow:
|
||||
self.unsafeUpdate(waitCallObj)
|
||||
return waitCallObj
|
||||
|
||||
def unNativeListen(self, eventName="", parent=None, callFunc=lambda *_: None):
|
||||
# type: (str, object, object) -> None
|
||||
""" 取消特定方法的原生动态监听 """
|
||||
if not parent:
|
||||
parent = self._emptyContext
|
||||
newFuncName = "QListen{}_{}".format(id(parent), callFunc.__name__)
|
||||
if hasattr(self, newFuncName):
|
||||
# 已注册完毕的监听处理
|
||||
self._easyUnListenForEvent(eventName, self, getattr(self, newFuncName))
|
||||
delattr(self, newFuncName)
|
||||
return
|
||||
# 在队列中等待注册的监听处理
|
||||
self.removeCallObjByUid(newFuncName)
|
||||
|
||||
def unsafeUpdate(self, callObjData):
|
||||
# type: (CallObjData) -> bool
|
||||
pass
|
||||
|
||||
def _easyListenForEvent(self, eventName="", parent=None, func=lambda: None):
|
||||
pass
|
||||
|
||||
def _easyUnListenForEvent(self, eventName="", parent=None, func=lambda: None):
|
||||
pass
|
||||
|
||||
def removeCallObjByUid(self, _uid = ""):
|
||||
pass
|
||||
89
debug_mod/DEBUG_ENV_SCRIPT/QuModLibs/Util.py
Normal file
89
debug_mod/DEBUG_ENV_SCRIPT/QuModLibs/Util.py
Normal file
@@ -0,0 +1,89 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from functools import wraps
|
||||
from time import time
|
||||
import pickle as _pickle
|
||||
|
||||
class SystemSide(object):
|
||||
def __init__(self, Path, SystemName = None):
|
||||
self.SystemName = SystemName # 绑定系统
|
||||
self.Path = Path
|
||||
|
||||
ModDirName = SystemSide.__module__.split(".")[0]
|
||||
|
||||
def errorPrint(charPtr):
|
||||
""" 异常输出 """
|
||||
print("[Error] "+str(charPtr))
|
||||
|
||||
def TRY_EXEC_FUN(funObj, *args, **kwargs):
|
||||
try:
|
||||
return funObj(*args, **kwargs)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
print("TRY_EXEC发生异常: {}".format(e))
|
||||
traceback.print_exc()
|
||||
|
||||
def getObjectPathName(_callObj = lambda: None):
|
||||
# type: (object) -> str
|
||||
""" 获取可执行对象的目录名 """
|
||||
return ".".join((_callObj.__module__, _callObj.__name__))
|
||||
|
||||
class QStruct:
|
||||
""" 结构体 用于通用数据模型约定(即不涉及任何API) 应定义在Server/Client以外的通用文件 同理Struct也不应该持有任何涉及端侧API的内容 """
|
||||
_SIGN_FORMAT = "_QSTRUCT[{}]"
|
||||
def dumps(self):
|
||||
""" 序列化对象 """
|
||||
return _pickle.dumps(self)
|
||||
|
||||
def signDumps(self):
|
||||
""" 带有特征签名的序列化 """
|
||||
data = self.dumps()
|
||||
return [QStruct._SIGN_FORMAT.format(hex(hash(data))), data]
|
||||
|
||||
@staticmethod
|
||||
def isSignData(data):
|
||||
""" 校验数据 """
|
||||
if not isinstance(data, list) or len(data) != 2:
|
||||
return False
|
||||
signKey = data[0]
|
||||
if isinstance(signKey, str):
|
||||
dataObj = data[1]
|
||||
return signKey == QStruct._SIGN_FORMAT.format(hex(hash(dataObj)))
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def loads(data):
|
||||
# type: (str) -> QStruct
|
||||
""" 反序列化加载对象 """
|
||||
return _pickle.loads(data)
|
||||
|
||||
@staticmethod
|
||||
def loadSignData(data):
|
||||
# type: (list) -> QStruct
|
||||
""" 反序列化加载Sign对象表(不会校验) """
|
||||
return _pickle.loads(data[1])
|
||||
|
||||
def onNetUnPack(self):
|
||||
return self
|
||||
|
||||
class QRefStruct(QStruct):
|
||||
""" 万能引用 """
|
||||
def __init__(self, refObject):
|
||||
self.ref = refObject
|
||||
|
||||
def onNetUnPack(self):
|
||||
return self.ref
|
||||
|
||||
class QListStruct(QStruct, list):
|
||||
""" List容器结构 """
|
||||
def onNetUnPack(self):
|
||||
return list(self)
|
||||
|
||||
class QDictStruct(QStruct, dict):
|
||||
""" Dict容器结构 """
|
||||
def onNetUnPack(self):
|
||||
return dict(self)
|
||||
|
||||
class QTupleStruct(QStruct, tuple):
|
||||
""" Tuple容器结构 """
|
||||
def onNetUnPack(self):
|
||||
return tuple(self)
|
||||
0
debug_mod/DEBUG_ENV_SCRIPT/QuModLibs/__init__.py
Normal file
0
debug_mod/DEBUG_ENV_SCRIPT/QuModLibs/__init__.py
Normal file
0
debug_mod/DEBUG_ENV_SCRIPT/__init__.py
Normal file
0
debug_mod/DEBUG_ENV_SCRIPT/__init__.py
Normal file
128
debug_mod/DEBUG_ENV_SCRIPT/modMain.py
Normal file
128
debug_mod/DEBUG_ENV_SCRIPT/modMain.py
Normal file
@@ -0,0 +1,128 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .QuModLibs.QuMod import *
|
||||
from .Game import (
|
||||
RELOAD_MOD,
|
||||
INIT_RELOAD_TIME,
|
||||
RELOAD_ADDON,
|
||||
RELOAD_WORLD,
|
||||
RELOAD_SHADERS,
|
||||
)
|
||||
from .Config import DEBUG_CONFIG
|
||||
import threading
|
||||
import sys
|
||||
|
||||
lambda: "By Zero123"
|
||||
|
||||
REF = 0
|
||||
|
||||
|
||||
class STD_OUT_WRAPPER(object):
|
||||
def __init__(self, baseIO):
|
||||
self.baseIO = baseIO
|
||||
self.writeLock = threading.Lock()
|
||||
self._buffer = []
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.baseIO, name)
|
||||
|
||||
def write(self, data):
|
||||
with self.writeLock:
|
||||
parts = data.splitlines(True)
|
||||
for part in parts:
|
||||
if part.endswith("\n"):
|
||||
if self._buffer:
|
||||
line = "".join(self._buffer) + part
|
||||
self._buffer = []
|
||||
else:
|
||||
line = part
|
||||
self.baseIO.write("[Python] " + line)
|
||||
else:
|
||||
self._buffer.append(part)
|
||||
|
||||
def close(self):
|
||||
return self.baseIO.close()
|
||||
|
||||
def writelines(self, lines):
|
||||
for line in lines:
|
||||
self.write(line)
|
||||
|
||||
def fileno(self):
|
||||
return self.baseIO.fileno()
|
||||
|
||||
|
||||
stdout = sys.stdout
|
||||
stderr = sys.stderr
|
||||
|
||||
|
||||
def REST_STDOUT():
|
||||
sys.stdout = stdout
|
||||
sys.stderr = stderr
|
||||
|
||||
|
||||
sys.stdout = STD_OUT_WRAPPER(sys.stdout)
|
||||
sys.stderr = STD_OUT_WRAPPER(sys.stderr)
|
||||
|
||||
|
||||
@PRE_SERVER_LOADER_HOOK
|
||||
def SERVER_INIT():
|
||||
global REF
|
||||
REF += 1
|
||||
|
||||
def _DESTROY():
|
||||
global REF
|
||||
REF -= 1
|
||||
if REF != 0:
|
||||
return
|
||||
REST_STDOUT()
|
||||
|
||||
from .QuModLibs.Systems.Loader.Server import LoaderSystem
|
||||
|
||||
LoaderSystem.REG_DESTROY_CALL_FUNC(_DESTROY)
|
||||
from . import IPCSystem
|
||||
IPCSystem.ON_SERVER_INIT()
|
||||
|
||||
|
||||
def CLOnKeyPressInGame(args={}):
|
||||
if args["isDown"] != "0":
|
||||
return
|
||||
if args["screenName"] != "hud_screen" and not DEBUG_CONFIG.get(
|
||||
"reload_key_global", False
|
||||
):
|
||||
return
|
||||
key = args["key"]
|
||||
if key == str(DEBUG_CONFIG.get("reload_key", "82")):
|
||||
RELOAD_MOD()
|
||||
elif key == str(DEBUG_CONFIG.get("reload_world_key", "")):
|
||||
RELOAD_WORLD()
|
||||
elif key == str(DEBUG_CONFIG.get("reload_addon_key", "")):
|
||||
RELOAD_ADDON()
|
||||
elif key == str(DEBUG_CONFIG.get("reload_shaders_key", "")):
|
||||
RELOAD_SHADERS()
|
||||
|
||||
|
||||
@PRE_CLIENT_LOADER_HOOK
|
||||
def CLIENT_INIT():
|
||||
global REF
|
||||
REF += 1
|
||||
from . import IPCSystem
|
||||
|
||||
def _DESTROY():
|
||||
global REF
|
||||
REF -= 1
|
||||
IPCSystem.ON_CLIENT_EXIT()
|
||||
if REF != 0:
|
||||
return
|
||||
REST_STDOUT()
|
||||
|
||||
from .QuModLibs.Systems.Loader.Client import LoaderSystem
|
||||
|
||||
LoaderSystem.REG_DESTROY_CALL_FUNC(_DESTROY)
|
||||
|
||||
LoaderSystem.getSystem().nativeStaticListen("OnKeyPressInGame", CLOnKeyPressInGame)
|
||||
IPCSystem.ON_CLIENT_INIT()
|
||||
|
||||
|
||||
try:
|
||||
INIT_RELOAD_TIME()
|
||||
except:
|
||||
pass
|
||||
0
debug_mod/entities/__init__.py
Normal file
0
debug_mod/entities/__init__.py
Normal file
1
debug_mod/manifest.json
Normal file
1
debug_mod/manifest.json
Normal file
@@ -0,0 +1 @@
|
||||
{"format_version":1,"header":{"description":"","name":"","uuid":"c329b06f-7492-4457-9af3-b19ff29999f9","version":[1,0,0],"min_engine_version":[1,18,0]},"modules":[{"description":"","type":"data","uuid":"96b5e924-153e-460d-8e57-10463173510b","version":[1,0,0]}],"dependencies":[{"uuid":"48199e63-9544-494a-90ab-2864e8f37f39","version":[1,0,0]}]}
|
||||
Reference in New Issue
Block a user