From e2453773916243fa722a81f00c6d10d0d4927867 Mon Sep 17 00:00:00 2001 From: liuxiaobo <1224730913@qq.com> Date: Tue, 8 Jul 2025 23:29:43 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B6=88=E6=81=AF=E5=AF=BC=E5=87=BA=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=EF=BC=8C=E6=94=B6=E5=8F=91=E6=B6=88=E6=81=AF=E7=AE=80?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/scripts/constant.ts | 4 + assets/scripts/login/loginPanel.ts | 42 ++++----- assets/scripts/network/pbExport.ts | 80 +++++++++++++++++ assets/scripts/network/pbHelper.ts | 15 ++-- assets/scripts/network/wsclient.ts | 32 ++++--- assets/scripts/network/wsconfig.ts | 2 +- tools/pb_exports.js | 138 +++++++++++++++++++++++++++++ 7 files changed, 271 insertions(+), 42 deletions(-) create mode 100644 assets/scripts/network/pbExport.ts create mode 100644 tools/pb_exports.js diff --git a/assets/scripts/constant.ts b/assets/scripts/constant.ts index da25fd9..de46d58 100644 --- a/assets/scripts/constant.ts +++ b/assets/scripts/constant.ts @@ -4,3 +4,7 @@ export class PrefabNames { return this.login } } + +export enum EventType { + Ws = 'ws-message', +} diff --git a/assets/scripts/login/loginPanel.ts b/assets/scripts/login/loginPanel.ts index 27dee44..b1f58d4 100644 --- a/assets/scripts/login/loginPanel.ts +++ b/assets/scripts/login/loginPanel.ts @@ -2,11 +2,8 @@ import { _decorator, Button, Component, EditBox, Label, Node } from 'cc' import { WsClient } from '../network/wsclient' import { WsConfig } from '../network/wsconfig' import { PbHelper } from '../network/pbHelper' -import pb from '../pb/login.js' -import pb1 from '../pb/client.js' -import pb2 from '../pb/msgId.js' -import pb3 from '../pb/service.js' -// import * as pb from '../pb/login.d' +import { ReqUserLogin, ServiceTypeId, MsgId, RspUserLogin } from '../network/pbExport' +import { EventType } from '../constant' const { ccclass, property } = _decorator @ccclass('loginPanel') @@ -24,11 +21,11 @@ export class loginPanel extends Component { start() { this.loginButton.node.on(Button.EventType.CLICK, this.onLoginButtonClick, this) // 监听websocket消息事件 - window.addEventListener('ws-message', this.onMessage.bind(this)) + window.addEventListener(EventType.Ws, this.onMessage.bind(this)) } protected onDestroy(): void { - window.removeEventListener('ws-message', this.onMessage.bind(this)) + window.removeEventListener(EventType.Ws, this.onMessage.bind(this)) } // 登录按钮点击处理函数 @@ -46,7 +43,7 @@ export class loginPanel extends Component { const ws = WsClient.Instance() console.log(typeof ws) if (!ws.IsConnected) { - await ws.ConnectAsync(WsConfig.Address[0].Address[0]) + await ws.ConnectAsync(WsConfig.Address[1].Address[0]) if (!ws.IsConnected) { this.statusLable.string = '连接网络失败' return @@ -54,33 +51,28 @@ export class loginPanel extends Component { } console.log('登陆帐号:', username, '密码:', pwd) - console.log('ReqUserLogin:', pb.ReqUserLogin) - const req = new pb.ReqUserLogin() + const req = new ReqUserLogin() req.username = username req.password = pwd req.version = '20250601123030' - // const req = pb.ReqUserLogin.create({ username: username, password: pwd, ip: '', deviceId: '', version: '' }) - const buffer = pb.ReqUserLogin.encode(req).finish() - const clinetMsg = new pb1.ClientMsg() - clinetMsg.msgId = pb2.MsgId.ReqUserLoginId - clinetMsg.data = buffer - clinetMsg.serviceTid = pb3.ServiceTypeId.STI_Login - const buffer1 = pb1.ClientMsg.encode(clinetMsg).finish() - ws.Send(buffer1) + const buffer = ReqUserLogin.encode(req).finish() + ws.Send(ServiceTypeId.STI_Login, MsgId.ReqUserLoginId, buffer) } private onMessage(event: CustomEvent) { try { - const message = JSON.parse(event.detail) // 解析消息内容 - if (message.type === 'loginSuccess') { - this.statusLable.string = '登录成功' - this.node.active = false - } else if (message.type === 'loginError') { - this.statusLable.string = '登录失败:' + message.reason + const [msgid, buffer] = PbHelper.DecodeClient(event.detail) + switch (msgid) { + case MsgId.RspUserLoginId: + const rsp = PbHelper.Decode(RspUserLogin, buffer) + console.log('收到返回消息:', rsp) + default: + // console.log('收到非json消息:', event.detail) } } catch (error) { - console.log('收到非json消息:', event.detail) + // console.log('解析错误失败:', error) + console.log(PbHelper.Uint8ToHex(event.detail)) } } diff --git a/assets/scripts/network/pbExport.ts b/assets/scripts/network/pbExport.ts new file mode 100644 index 0000000..d28b428 --- /dev/null +++ b/assets/scripts/network/pbExport.ts @@ -0,0 +1,80 @@ +// 由 pb-import-export-generator 自动生成,请勿手动修改 + +// 导入语句 +import chat from '../pb/chat.js' +import client from '../pb/client.js' +import code from '../pb/code.js' +import colorgame from '../pb/colorgame.js' +import common from '../pb/common.js' +import login from '../pb/login.js' +import match from '../pb/match.js' +import msgId from '../pb/msgId.js' +import service from '../pb/service.js' +import user from '../pb/user.js' + +// 解构赋值 +const { ChatType, ReqChat, ChatUser, GameUser, ServiceTypeId } = chat +const { ClientMsg } = client +const { ErrCode } = code +const { ColorPrizeArea, ColorPrizeAreaRange, ColorRoomConfig, ColorType, ColorBetArea, ColorGameStatus, ColorRoomWaitStart, ColorRoomStart, ColorRoomBetting, ColorRoomEndBet, ColorRoomOpenThreeDice, ColorRoomSettle, NtfColorRoomInfo, NtfColorGameStart, NtfColorBetting, ReqColorBetting, RspColorBetting, ColorBetAreaInfo, NtfColorBetAreaInfo, ColorPrizeType, ColorBetAreaMul, NtfColorEndBetting, NtfColorOpenThreeDice, NtfColorSettle, ColorUser, NtfColorBigUser, NtfColorTrend, ColorBigUser, NtfColorMaintain } = colorgame +const { NtfKickOutUser, ReqEnterRoom, RspEnterRoom, ReqLeaveRoom, RspLeaveRoom, NtfPayoutFail } = common +const { ReqUserLogin, RspUserLogin, NtfUserOnline, NtfUserInService, ReqUserLogout, RspUserLogout, NtfUserOffline } = login +const { ReqMatchRoom, RspMatchRoom, NotifyUserEnterRoom } = match +const { MsgId } = msgId + +// 导出语句 +export { + ChatType, + ReqChat, + ChatUser, + GameUser, + ServiceTypeId, + ClientMsg, + ErrCode, + ColorPrizeArea, + ColorPrizeAreaRange, + ColorRoomConfig, + ColorType, + ColorBetArea, + ColorGameStatus, + ColorRoomWaitStart, + ColorRoomStart, + ColorRoomBetting, + ColorRoomEndBet, + ColorRoomOpenThreeDice, + ColorRoomSettle, + NtfColorRoomInfo, + NtfColorGameStart, + NtfColorBetting, + ReqColorBetting, + RspColorBetting, + ColorBetAreaInfo, + NtfColorBetAreaInfo, + ColorPrizeType, + ColorBetAreaMul, + NtfColorEndBetting, + NtfColorOpenThreeDice, + NtfColorSettle, + ColorUser, + NtfColorBigUser, + NtfColorTrend, + ColorBigUser, + NtfColorMaintain, + NtfKickOutUser, + ReqEnterRoom, + RspEnterRoom, + ReqLeaveRoom, + RspLeaveRoom, + NtfPayoutFail, + ReqUserLogin, + RspUserLogin, + NtfUserOnline, + NtfUserInService, + ReqUserLogout, + RspUserLogout, + NtfUserOffline, + ReqMatchRoom, + RspMatchRoom, + NotifyUserEnterRoom, + MsgId +}; diff --git a/assets/scripts/network/pbHelper.ts b/assets/scripts/network/pbHelper.ts index 4e4de2c..fcb4251 100644 --- a/assets/scripts/network/pbHelper.ts +++ b/assets/scripts/network/pbHelper.ts @@ -1,8 +1,4 @@ -/** - * - * pb helper - */ -// import { Message } from 'protobufjs' +import { ClientMsg } from '../network/pbExport' interface ProtoEncodable { create(data: T): unknown @@ -16,9 +12,18 @@ export class PbHelper { const message = protoClass.create(data) return protoClass.encode(message).finish() } + static DecodeClient(buffer: Uint8Array): [number, Uint8Array] { + const cMsg = ClientMsg.decode(buffer) + return [cMsg.msgId, cMsg.data] + } static Decode(protoClass: ProtoEncodable, buffer: Uint8Array): T { return protoClass.decode(buffer) } + static Uint8ToHex(buffer: Uint8Array): string { + return Array.from(buffer) + .map((byte) => byte.toString(16).padStart(2, '0')) + .join(' ') + } } // // 假设有一个生成的 Protobuf 类 diff --git a/assets/scripts/network/wsclient.ts b/assets/scripts/network/wsclient.ts index 9ec5abb..8e7dad2 100644 --- a/assets/scripts/network/wsclient.ts +++ b/assets/scripts/network/wsclient.ts @@ -1,3 +1,7 @@ +import { ClientMsg } from '../network/pbExport' +import { EventType } from '../constant' +import { PbHelper } from '../network/pbHelper' + export class WsClient { private socket: WebSocket | null = null private url: string = 'ws://echo.websocket.org' // 测试服务器 @@ -81,19 +85,18 @@ export class WsClient { } // 发送消息到服务器 - Send(message: Uint8Array) { + Send(sid: number, msgId: number, msg: Uint8Array) { if (!this.socket || this.socket.readyState !== WebSocket.OPEN) { // 如果连接不存在或未打开 console.error('websocket未连接,无法发送消息') return false } - // var msg: string = '' - // if (typeof message == 'string') { - // msg = message - // } else { - // msg = JSON.stringify(message) - // } - this.socket.send(message) + const clinetMsg = new ClientMsg() + clinetMsg.msgId = msgId + clinetMsg.data = msg + clinetMsg.serviceTid = sid + const buffer = ClientMsg.encode(clinetMsg).finish() + this.socket.send(buffer) return true } @@ -104,9 +107,16 @@ export class WsClient { } // 收到消息回调 - private onMessage(event: MessageEvent) { - console.log('收到消息:', event.data) - window.dispatchEvent(new CustomEvent('ws-message', { detail: event.data })) + private async onMessage(event: MessageEvent) { + try { + const buffer = await event.data.arrayBuffer() + const uint8Array = new Uint8Array(buffer) + // console.log('收到消息:', uint8Array) + console.log('收到消息:', PbHelper.Uint8ToHex(uint8Array)) + window.dispatchEvent(new CustomEvent(EventType.Ws, { detail: uint8Array })) + } catch (error) { + console.error('解析网络消息失败:', error) + } } // 连接关闭回调 diff --git a/assets/scripts/network/wsconfig.ts b/assets/scripts/network/wsconfig.ts index e72033b..3694427 100644 --- a/assets/scripts/network/wsconfig.ts +++ b/assets/scripts/network/wsconfig.ts @@ -6,7 +6,7 @@ class AddressInfo { // 网关配置 export class WsConfig { public static Address: Array = [ - { Name: 'super', Address: ['ws://127.0.0.1:5100', 'ws://127.0.0.1:5101'] }, + { Name: 'dev', Address: ['ws://127.0.0.1:5100', 'ws://127.0.0.1:5101'] }, { Name: 'test', Address: ['ws://114.132.124.145:5100', 'ws://114.132.124.145:5101'] }, ] } diff --git a/tools/pb_exports.js b/tools/pb_exports.js new file mode 100644 index 0000000..c37a419 --- /dev/null +++ b/tools/pb_exports.js @@ -0,0 +1,138 @@ +const fs = require('fs') +const path = require('path') + +/** + * PB 模块导入导出代码生成器 + * @param {string} pbDir - PB 文件所在的目录路径 + * @param {string} outputFile - 生成的代码输出文件路径 + * @param {string} [relativePath='../pb'] - 导入语句中的相对路径 + */ +function generatePbImportsAndExports(pbDir, outputFile, relativePath = '../pb') { + try { + // 读取 PB 目录下的所有文件 + const files = fs.readdirSync(pbDir) + + // 过滤出 .js 文件并去除扩展名 + const jsFiles = files.filter((file) => file.endsWith('.js')) + + if (jsFiles.length === 0) { + console.warn(`警告: 在目录 ${pbDir} 中没有找到任何 .js 文件`) + return + } + + // 生成导入语句 + const importStatements = jsFiles + .map((file) => { + const moduleName = file.replace('.js', '') + return `import ${moduleName} from '${relativePath}/${moduleName}.js'` + }) + .join('\n') + + // 为每个文件生成导出内容 + const moduleExports = {} + const allExports = new Set() + const exportSources = {} // 记录每个导出项的来源 + + jsFiles.forEach((file) => { + const filePath = path.join(pbDir, file) + const code = fs.readFileSync(filePath, 'utf-8') + const moduleName = file.replace('.js', '') + + // 提取 $root.X 形式的导出 + const rootExports = extractRootExports(code) + + // 提取 enum 定义 + const enumExports = extractEnumExports(code) + + // 合并所有导出 + const exports = [...rootExports, ...enumExports] + + if (exports.length > 0) { + moduleExports[moduleName] = exports + + // 记录导出项的来源 + exports.forEach((exp) => { + if (!exportSources[exp]) { + exportSources[exp] = moduleName + allExports.add(exp) + } + }) + } + }) + + if (allExports.size === 0) { + console.warn(`警告: 没有找到任何可导出的类或枚举`) + return + } + + // 生成解构赋值语句(只包含首次出现的导出项) + const destructureStatements = Object.entries(moduleExports) + .map(([moduleName, exports]) => { + // 过滤出该模块独有的导出项 + const uniqueExports = exports.filter((exp) => exportSources[exp] === moduleName) + if (uniqueExports.length > 0) { + return `const { ${uniqueExports.join(', ')} } = ${moduleName}` + } + return '' + }) + .filter(Boolean) // 移除空字符串 + .join('\n') + + // 生成导出语句(所有唯一导出项) + const exportList = Array.from(allExports) + + // 完整的输出代码 + const outputCode = + `// 由 pb-import-export-generator 自动生成,请勿手动修改\n\n` + + `// 导入语句\n${importStatements}\n\n` + + `// 解构赋值\n${destructureStatements}\n\n` + + `// 导出语句\nexport {\n ${exportList.join(',\n ')}\n};\n` + + // 确保输出目录存在 + const outputDir = path.dirname(outputFile) + if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }) + } + + // 写入输出文件 + fs.writeFileSync(outputFile, outputCode) + + console.log(`成功生成 PB 导入导出代码到 ${outputFile}`) + console.log(`共生成 ${jsFiles.length} 个导入语句和 ${exportList.length} 个导出变量`) + } catch (error) { + console.error('生成 PB 导入导出代码时出错:', error.message) + } +} + +/** + * 提取 $root.X 形式的导出 + */ +function extractRootExports(code) { + const exports = [] + const rootExportRegex = /\$root\.([A-Za-z][A-Za-z0-9_]*)\s*=/g + + let match + while ((match = rootExportRegex.exec(code)) !== null) { + exports.push(match[1]) + } + + return exports +} + +/** + * 提取 enum 定义 + */ +function extractEnumExports(code) { + const exports = [] + const enumRegex = /@enum\s*{.*?}\s*\n\s*\*\s*@exports\s+([A-Za-z][A-Za-z0-9_]*)/g + + let match + while ((match = enumRegex.exec(code)) !== null) { + exports.push(match[1]) + } + + return exports +} + +// 示例用法 +generatePbImportsAndExports('../assets/scripts/pb', '../assets/scripts/network/pbExport.ts')