Files
emod-cli/debug_mod/DEBUG_ENV_SCRIPT/IPCSystem.py
Blank038 011b59c948 feat(debug): 支持网易 Minecraft 调试启动
新增 debug 子命令,自动准备开发世界、注册内置调试 MOD,并将项目行为包和资源包链接到网易运行目录,方便启动游戏后直接进入调试世界。

调试 MOD 资源随仓库一起嵌入,避免依赖本机绝对路径;Windows junction 写入剥离 verbatim 前缀后的 DOS 路径,保证 Minecraft 能正确读取链接包。
2026-05-16 00:59:51 +08:00

181 lines
5.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- 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())