UNPKG

unity-find-fault

Version:

A tool to find fault in unity project.

160 lines 6.99 kB
import fg from "fast-glob"; import fs from "fs-extra"; import path from "path"; import { Project } from "ts-morph"; import { readGB2312, writeGB2312 } from "./vendor.js"; import { toolchain } from "../toolchain.js"; export class ProtocolStriper { async removeUseless() { const project = new Project({ tsConfigFilePath: path.join(toolchain.opts.projectRoot, 'TsScripts/tsconfig.json') }); const outputFile = `tmp/${toolchain.opts.projectName}.protocolUsed.txt`; await this.findUsed(project, outputFile); const outContent = await fs.readFile(outputFile, 'utf-8'); const lines = outContent.split(/\r?\n/); const usedItfs = []; for (const line of lines) { if (!line) continue; usedItfs.push(line); } await this.markItfDefinitions(usedItfs); } async findUsed(project, outFile) { if (!toolchain.opts.svrRoot) { console.error('no svr root'); process.exit(1); } // Protocol.d.ts const usedItfs = []; const protocolFile = path.join(toolchain.opts.projectRoot, 'TsScripts/System/protocol/Protocol.d.ts'); const gameConfigSrc = project.getSourceFile(protocolFile); const module = gameConfigSrc.getModuleOrThrow('Protocol'); const interfaces = module.getInterfaces(); for (const itf of interfaces) { let used = false; if (this.isNodeReferedOutside(itf)) { used = true; } else { // 检查字段是否被使用 const properties = itf.getProperties(); for (const property of properties) { // if (property.getName() == 'm_iSkillPriority' && itf.getName() == 'SkillConfigM') { // console.log('this comes!'); // } if (this.isNodeReferedOutside(property)) { used = true; break; } } } if (used) { usedItfs.push(itf.getName()); } } // ProtocolUtil.ts // 部分协议没有显式使用Request const protocolUtilFile = path.join(toolchain.opts.projectRoot, 'TsScripts/System/protocol/ProtocolUtil.ts'); const protoclUtilContent = await fs.readFile(protocolUtilFile, 'utf-8'); const mchs = protoclUtilContent.matchAll(/(?<=SendMsgUtil\.get).+(?=\(\))/g); for (const mch of mchs) { const request = mch[0]; if (!usedItfs.includes(request)) { usedItfs.push(request); } } // 部分协议response也只是监听了msgid,并没有显式使用到msgbody await this.findByAddNetListener(usedItfs); await fs.writeFile(outFile, usedItfs.join('\n') + '\n', 'utf-8'); } async findByAddNetListener(out) { const tss = await fg('TsScripts/**/*.ts', { cwd: toolchain.opts.projectRoot }); const usedMsgids = []; for (const ts of tss) { const tsFile = path.join(toolchain.opts.projectRoot, ts); const content = await fs.readFile(tsFile, 'utf-8'); const mchs = content.matchAll(/(?<=addNetListener\(Macros.).+?(?=,)/g); for (const mch of mchs) { usedMsgids.push(mch[0]); } } // 获取对应的response const csXml = path.join(toolchain.opts.svrRoot, 'CS.xml'); const csContent = await fs.readFile(csXml, 'utf-8'); const mchs = csContent.matchAll(/<entry\s+name=\s*"\w+"\s+type\s*=\s*"(\w+)"\s+value\s*=\s*"(\w+)"/g); for (const mch of mchs) { const rspntf = mch[1]; const msgid = mch[2]; if (usedMsgids.includes(msgid) && !out.includes(rspntf)) { out.push(rspntf); } } } isNodeReferedOutside(node) { const excludes = ['DecodeBase.ts', 'DecodeUtil.ts', 'EncodeUtil.ts', 'SendMsgUtil.ts']; const refSymbols = node.findReferences(); let used = false; for (const refSymbol of refSymbols) { for (const ref of refSymbol.getReferences()) { if (ref.isDefinition()) continue; // console.log(`${refSymbol.getDefinition().getName()} refered in ${ref.getSourceFile().getFilePath()}`); const bn = ref.getSourceFile().getBaseName(); if (!excludes.includes(ref.getSourceFile().getBaseName())) { used = true; break; } } if (used) break; } return used; } async markItfDefinitions(usedItfs) { const xmlFiles = ['Common.xml', 'CS.xml', 'ProtoType.xml']; for (const x of xmlFiles) { const xmlFile = path.join(toolchain.opts.svrRoot, x); const xmlContent = await readGB2312(xmlFile); const lines = xmlContent.split(/\r?\n/); let modified = false; for (let i = 0, len = lines.length; i < len; i++) { let line = lines[i]; const originalLine = line; const mch = line.match(/^\s*<struct\s+name\s*=\s*"(\w+)".* (?:convertOption\s*=\s*"(\w+)")?/); if (mch != null) { // struct定义开始 const theItf = mch[1]; // 只标记一级字段 // if (!theItf.endsWith('Notify') && !theItf.endsWith('Response') && !theItf.endsWith('Request')) continue; const convertOption = mch[2]?.split(','); if (usedItfs.includes(theItf)) { // 添加标记 if (!convertOption?.includes('clientUsed')) { if (line.includes('convertOption')) { line = originalLine.replace(/convertOption\s*=\s*"/, `convertOption="clientUsed,`); } else { line = originalLine.replace('>', ' convertOption="clientUsed">'); } lines[i] = line; modified = true; } } else { // 去除标记 if (convertOption?.includes('clientUsed')) { line = originalLine.replace('clientUsed', '').replace('convertOption=""', ''); lines[i] = line; modified = true; } } } } if (modified) await writeGB2312(xmlFile, lines.join('\n')); } } } //# sourceMappingURL=ProtocolStriper.js.map