refactor(debug-mod): 使用原生 System 替换 QuMod

删除未使用的大型 QuMod 加载框架,改为直接注册客户端和服务端 System,并显式管理按键监听、输出包装与双端销毁。

IPC 网络线程仅负责收包,所有 ModAPI 回调由游戏线程 Update 派发,同时补充断线重连、分帧读取和幂等关闭。
This commit is contained in:
2026-07-21 20:52:10 +08:00
parent 3afc122404
commit 94b1b9a1f5
15 changed files with 386 additions and 1057 deletions

View File

@@ -4,6 +4,7 @@ import mod.client.extraClientApi as clientApi
from .Config import GET_DEBUG_IPC_PORT
import socket
import threading
import time
import json
def U16_BE(b):
@@ -24,74 +25,162 @@ class IPCSystem:
self.port = port
self.sock = None
self.mLock = threading.Lock()
self.handers = {}
self.handlers = {}
self.pendingLock = threading.Lock()
self.pendingClient = []
self.pendingServer = []
self.running = False
self.generation = 0
self.thread = None
self.nextStartTime = 0.0
def registerHandler(self, typeID, handler):
# type: (int, callable) -> None
self.handers[typeID] = handler
self.handlers[typeID] = handler
def updateHandlers(self, handlers):
# type: (dict[int, callable]) -> None
self.handers.update(handlers)
self.handlers.update(handlers)
def start(self):
if self.sock or not self.port:
return
threading.Thread(target=self._threadListenLoop).start()
now = time.time()
with self.mLock:
if self.running or not self.port or now < self.nextStartTime:
return
self.running = True
self.nextStartTime = now + 1.0
self.generation += 1
generation = self.generation
thread = threading.Thread(
target=self._threadListenLoop,
args=(generation,),
)
thread.daemon = True
self.thread = thread
thread.start()
def close(self):
sock = None
with self.mLock:
self.running = False
self.generation += 1
self.nextStartTime = 0.0
sock = self.sock
self.sock = None
thread = self.thread
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
sock.shutdown(socket.SHUT_RDWR)
except socket.error:
break
pass
try:
sock.close()
except socket.error:
pass
if thread:
thread.join(1.0)
with self.pendingLock:
self.pendingClient = []
self.pendingServer = []
def updateClient(self):
self.start()
with self.pendingLock:
pending = self.pendingClient
self.pendingClient = []
self._runPending(pending)
def updateServer(self):
with self.pendingLock:
pending = self.pendingServer
self.pendingServer = []
self._runPending(pending)
@staticmethod
def _runPending(pending):
for handler, data in pending:
try:
handler(data)
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))
def _threadListenLoop(self, generation):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
with self.mLock:
self.sock = None
print("[IPCSystem] 连接已关闭")
if not self.running or generation != self.generation:
sock.close()
return
self.sock = sock
try:
sock.settimeout(2.0)
sock.connect(("localhost", self.port))
sock.settimeout(0.2)
print("[IPCSystem] 已连接到调试服务器,端口:" + str(self.port))
while self._isCurrent(generation, sock):
header = self._recvAll(generation, sock, 6)
typeID = U16_BE(header[0:2])
dataLength = U32_BE(header[2:6])
data = self._recvAll(generation, sock, dataLength)
self._queueHandler(generation, typeID, data)
except EOFError:
pass
except socket.error:
if self._isCurrent(generation, sock):
import traceback
traceback.print_exc()
except Exception:
import traceback
traceback.print_exc()
finally:
try:
sock.close()
except socket.error:
pass
with self.mLock:
if self.sock is sock:
self.sock = None
if self.thread is threading.currentThread():
self.thread = None
if generation == self.generation:
self.running = False
print("[IPCSystem] 连接已关闭")
def _isCurrent(self, generation, sock):
with self.mLock:
return (
self.running
and generation == self.generation
and self.sock is sock
)
def _queueHandler(self, generation, typeID, data):
handler = self.handlers.get(typeID)
if handler is None:
print("[IPCSystem] 未知的TypeID数据包" + str(typeID))
return
with self.mLock:
if not self.running or generation != self.generation:
return
with self.pendingLock:
if typeID == 4:
self.pendingServer.append((handler, data))
else:
self.pendingClient.append((handler, data))
def _recvAll(self, generation, sock, length):
buf = bytearray()
while len(buf) < length:
try:
more = sock.recv(length - len(buf))
except socket.timeout:
if not self._isCurrent(generation, sock):
raise EOFError("IPC connection stopped")
continue
if not more:
raise EOFError("Socket closed before receiving all data")
buf.extend(more)
return buf
_CL_GAME_COMP = None
_SR_GAME_COMP = None
@@ -168,14 +257,25 @@ _IPCSYSTEM.updateHandlers(
}
)
def UPDATE_CLIENT():
_IPCSYSTEM.updateClient()
def UPDATE_SERVER():
_IPCSYSTEM.updateServer()
def ON_CLIENT_INIT():
global _CL_GAME_COMP
_CL_GAME_COMP = clientApi.GetEngineCompFactory().CreateGame(clientApi.GetLevelId())
_IPCSYSTEM.start()
def ON_CLIENT_EXIT():
global _CL_GAME_COMP
_IPCSYSTEM.close()
_CL_GAME_COMP = None
def ON_SERVER_INIT():
global _SR_GAME_COMP
_SR_GAME_COMP = serverApi.GetEngineCompFactory().CreateGame(serverApi.GetLevelId())
_SR_GAME_COMP = serverApi.GetEngineCompFactory().CreateGame(serverApi.GetLevelId())
def ON_SERVER_EXIT():
global _SR_GAME_COMP
_SR_GAME_COMP = None