设计亮点:使用 paths 统一管理路径,避免直接写 ../ 等相对路径,提升可维护性。
新增文件 paths.js
// src/paths.js const path = require('path'); const { common } = require('protobufjs'); // 工程根目录:src/ 的上级目录(your-project/) const projectRoot = path.resolve(__dirname, '../'); module.exports = { root: projectRoot, src: path.resolve(projectRoot, 'src'), common: path.resolve(projectRoot, 'src/common'), data: path.resolve(projectRoot, 'src/data'), html: path.resolve(projectRoot, 'src/html'), lib: path.resolve(projectRoot, 'src/lib'), logic: path.resolve(projectRoot, 'src/logic'), logicHot: path.resolve(projectRoot, 'src/logicHot'), manager: path.resolve(projectRoot, 'src/manager'), };
这段代码是 Node.js 服务器的命令处理器模块,主要用于游戏 / 服务端场景中,将客户端 / 外部发送的命令(CMD)映射到对应的业务逻辑处理函数,并实现了定时热修复(HotFix)机制,核心目标是:
命令 - 逻辑解耦:通过映射表关联命令常量与业务处理函数,便于扩展和维护;
动态热更新:定时检测热修复目录的文件,无需重启服务即可替换业务逻辑,快速修复线上问题;
模块化拆分:将业务逻辑拆分为多个独立模块(登录、道具、战斗等),降低耦合。
commandManager.js
const path = require('path'); const paths = require('../paths'); const fs = require('fs').promises; const gCmd = require(path.join(paths.common, 'constant')).gCmd const log2file = require(path.join(paths.common, 'log2file')) const logicObject={ ["loginLogic"]:require(path.join(paths.logic, 'loginLogic')), ["itemLogic"]:require(path.join(paths.logic, 'itemLogic')), ["battleLogic"]:require(path.join(paths.logic, 'battleLogic')), ["accountLogic"]:require(path.join(paths.logic, 'accountLogic')), ["mailLogic"]:require(path.join(paths.logic, 'mailLogic')), ["rewardLogic"]:require(path.join(paths.logic, 'rewardLogic')), } const commandHandlers = { [gCmd.CMD_SERVER_REGISTER_LOGIN]: (client, data, obj, clientObject) => logicObject["loginLogic"].loginHandler1(client, data, obj, clientObject), [gCmd.CMD_SERVER_USE_ITEM]: (client, data, obj) => logicObject["itemLogic"].itemHandler1(client, data, obj), [gCmd.CMD_SERVER_BATTLE_BEGIN]: (client, data, obj) => logicObject["battleLogic"].battleHandler1(client, data, obj), [gCmd.CMD_SERVER_BATTLE_USE_ITEM]: (client, data, obj) => logicObject["battleLogic"].battleHandler2(client, data, obj), [gCmd.CMD_SERVER_BATTLE_DROP_ITEM]: (client, data, obj) => logicObject["battleLogic"].battleHandler3(client, data, obj), [gCmd.CMD_SERVER_BATTLE_ADD_ITEM]: (client, data, obj) => logicObject["battleLogic"].battleHandler4(client, data, obj), [gCmd.CMD_SERVER_BIND_ACCOUNT]: (client, data, obj) => logicObject["accountLogic"].accountHandler1(client, data, obj), [gCmd.CMD_SERVER_UNBIND_ACCOUNT]: (client, data, obj) => logicObject["accountLogic"].accountHandler2(client, data, obj), // [gCmd.CMD_SERVER_BATTLE_UPDATE_ENDTIME]: (client, data, obj) => logicObject["battleLogic"].battleHandler5(client, data, obj), [gCmd.CMD_SERVER_BATTLE_SET_RESULT]: (client, data, obj) => logicObject["battleLogic"].battleHandler6(client, data, obj), [gCmd.CMD_SERVER_BATTLE_RECEIVE_REWARD]: (client, data, obj) => logicObject["battleLogic"].battleHandler7(client, data, obj), [gCmd.CMD_SERVER_BATTLE_RECEIVE_REWARD_2]: (client, data, obj) => logicObject["battleLogic"].battleHandler8(client, data, obj), [gCmd.CMD_SERVER_BATTLE_DROP_GOLD]: (client, data, obj) => logicObject["battleLogic"].battleHandler9(client, data, obj), [gCmd.CMD_SERVER_MAIL_READ]: (client, data, obj) => logicObject["mailLogic"].mailHandler1(client, data, obj), [gCmd.CMD_SERVER_MAIL_RECEIVE]: (client, data, obj) => logicObject["mailLogic"].mailHandler2(client, data, obj), [gCmd.CMD_SERVER_MAIL_DELETE]: (client, data, obj) => logicObject["mailLogic"].mailHandler3(client, data, obj), [gCmd.CMD_SERVER_MODIFY_NICKNAME]: (client, data, obj) => logicObject["accountLogic"].accountHandler3(client, data, obj), [gCmd.CMD_SERVER_MODIFY_SIGNATURE]: (client, data, obj) => logicObject["accountLogic"].accountHandler4(client, data, obj), [gCmd.CMD_SERVER_BASE_SETTING]: (client, data, obj) => logicObject["accountLogic"].accountHandler5(client, data, obj), [gCmd.CMD_SERVER_REDEMPTION_CODE]: (client, data, obj) => logicObject["rewardLogic"].rewardHandler1(client, data, obj), }; async function checkFileExists(filePath) { try { await fs.access(filePath); return true; } catch { return false; } } let isChecking = false; async function checkHotFix() { if (isChecking) { log2file.logger.warn('上一次热修复检测未完成,跳过本次'); return; } isChecking = true; try { for(let key in logicObject){ const fileInfo='../logicHot/'+key const filePath = path.join(__dirname, fileInfo+".js"); let isExist=await checkFileExists(filePath) if(isExist){ logicObject[key] = require(path.join(paths.logicHot, key)) log2file.logger.info('checkHotFix filePath=', filePath); } } } finally { isChecking = false; // 无论成功失败,重置标志位 } } // 启动定时器 setInterval(checkHotFix, 60 * 1000); module.exports = { commandHandlers: commandHandlers, };
核心作用:定时检测 logicHot 目录下的热修复文件,若存在则动态替换 logicObject 中的对应模块,实现不重启服务的热更新;
设计亮点:使用异步文件检查(fs.promises),避免阻塞 Node.js 事件循环。