@yangrunchi/a_6 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1021 @@
1
+ // -- 作者: 杨润池
2
+ // -- 日期: 2025年10月23日
3
+ // -- 版本: 2.6
4
+ // -- 描述: 从 NetMsgCfg.lua 生成 funcData 类(区分请求和响应,只添加新方法),并生成管理器,自动提交SVN
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const { execSync } = require('child_process');
9
+ const os = require('os');
10
+
11
+ // ==================== 配置 ====================
12
+
13
+ const bPath = path.join(__dirname, '../../../dev/GUILayout/ssrgame/net', 'NetMsgCfg.lua');
14
+ const ssrgameRoot = path.join(__dirname, '../../../dev/GUILayout/ssrgame');
15
+ const funcDataDir = path.join(ssrgameRoot, 'funcData');
16
+ const mgrDir = path.join(ssrgameRoot, 'mgr');
17
+
18
+ // 标记定义(统一管理,避免硬编码)
19
+ const MARKERS = {
20
+ requestStart: '--------------------------------自动生成 ↓↓↓ 请求方法 ↓↓↓ start---------------------------------------',
21
+ requestEnd: '--------------------------------自动生成 ↓↓↓ 请求方法 ↓↓↓ end---------------------------------------',
22
+ responseStart: '--------------------------------自动生成 ↓↓↓ 响应方法 ↓↓↓ start---------------------------------------',
23
+ responseEnd: '--------------------------------自动生成 ↓↓↓ 响应方法 ↓↓↓ end---------------------------------------'
24
+ };
25
+
26
+ // 读取排除模块配置
27
+ const excludeModulesPath = path.join(__dirname, 'A4_exclude_modules.json');
28
+ let excludeModules = new Set(['Global', 'sync', 'ssrNetMsgCfg', 'test']);
29
+
30
+ try {
31
+ if (fs.existsSync(excludeModulesPath)) {
32
+ const excludeConfig = JSON.parse(fs.readFileSync(excludeModulesPath, 'utf-8'));
33
+ excludeModules = new Set(excludeConfig.excludeModules || []);
34
+ console.log('✅ 已加载排除模块配置:', Array.from(excludeModules));
35
+ } else {
36
+ console.log('⚠️ 未找到排除模块配置文件,使用默认排除列表');
37
+ }
38
+ } catch (err) {
39
+ console.log('⚠️ 读取排除模块配置失败,使用默认排除列表');
40
+ }
41
+
42
+ // 读取数据类模板
43
+ const dataTemplatePath = path.join(__dirname, 'A4_template_3.txt');
44
+ let dataTemplate = '';
45
+
46
+ try {
47
+ dataTemplate = fs.readFileSync(dataTemplatePath, 'utf-8');
48
+ console.log('✅ 已加载数据类模板: A4_template_3.txt');
49
+ } catch (err) {
50
+ console.error(`❌ 数据类模板不存在: ${dataTemplatePath}`);
51
+ process.exit(1);
52
+ }
53
+
54
+ // 读取管理器模板
55
+ const managerTemplatePath = path.join(__dirname, 'A4_template_4.txt');
56
+ let managerTemplate = '';
57
+
58
+ try {
59
+ managerTemplate = fs.readFileSync(managerTemplatePath, 'utf-8');
60
+ console.log('✅ 已加载管理器模板: A4_template_4.txt');
61
+ } catch (err) {
62
+ console.error(`❌ 管理器模板不存在: ${managerTemplatePath}`);
63
+ process.exit(1);
64
+ }
65
+
66
+ // 读取配置
67
+ let config = { author: "杨润池" };
68
+ try {
69
+ const configPath = path.join(__dirname, '../AutoCode/A1_996_config.json');
70
+ if (fs.existsSync(configPath)) {
71
+ const configContent = fs.readFileSync(configPath, 'utf-8');
72
+ config = JSON.parse(configContent);
73
+ console.log('✅ 已加载配置, 作者:', config.author);
74
+ }
75
+ } catch (err) {
76
+ console.log('⚠️ 未找到配置文件,使用默认配置');
77
+ }
78
+
79
+ console.log('🚀 开始生成 funcData 类...');
80
+ console.log('📁 协议文件:', bPath);
81
+ console.log('📁 数据类输出目录:', funcDataDir);
82
+ console.log('📁 管理器输出目录:', mgrDir);
83
+
84
+ // ==================== 辅助函数 ====================
85
+ function getDateTime() {
86
+ const d = new Date();
87
+ const year = d.getFullYear();
88
+ const month = String(d.getMonth() + 1).padStart(2, '0');
89
+ const day = String(d.getDate()).padStart(2, '0');
90
+ const hours = String(d.getHours()).padStart(2, '0');
91
+ const minutes = String(d.getMinutes()).padStart(2, '0');
92
+ const seconds = String(d.getSeconds()).padStart(2, '0');
93
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
94
+ }
95
+
96
+ function getShortIP() {
97
+ try {
98
+ const interfaces = os.networkInterfaces();
99
+ for (const name of Object.keys(interfaces)) {
100
+ for (const iface of interfaces[name]) {
101
+ if (iface.family === 'IPv4' && !iface.internal) {
102
+ const parts = iface.address.split('.');
103
+ if (parts.length === 4) {
104
+ return `${parts[2]}.${parts[3]}`;
105
+ }
106
+ return iface.address;
107
+ }
108
+ }
109
+ }
110
+ } catch (err) { }
111
+ return 'local';
112
+ }
113
+
114
+ function getCommitMessage(action, fileName, responseCount, requestCount) {
115
+ const ip = getShortIP();
116
+ const author = config.author;
117
+ const respInfo = responseCount ? `响应+${responseCount}` : '';
118
+ const reqInfo = requestCount ? `请求+${requestCount}` : '';
119
+ const methodInfo = [respInfo, reqInfo].filter(s => s).join(',');
120
+ return `[Auto] ${action} ${fileName}${methodInfo ? ` (${methodInfo})` : ''} ${author}@${ip}`;
121
+ }
122
+
123
+ function commitToSvn(filePath, action, responseCount = 0, requestCount = 0) {
124
+ if (!fs.existsSync(filePath)) {
125
+ return false;
126
+ }
127
+
128
+ try {
129
+ const fileName = path.basename(filePath);
130
+
131
+ let isVersioned = false;
132
+ try {
133
+ execSync(`svn info "${filePath}"`, { stdio: 'ignore' });
134
+ isVersioned = true;
135
+ } catch (err) {
136
+ isVersioned = false;
137
+ }
138
+
139
+ if (!isVersioned) {
140
+ console.log(` 📝 添加新文件: ${fileName}`);
141
+ execSync(`svn add "${filePath}"`, { stdio: 'ignore' });
142
+ }
143
+
144
+ const statusOutput = execSync(`svn status "${filePath}"`, { encoding: 'utf8' });
145
+ const needCommit = statusOutput.trim().length > 0 &&
146
+ (statusOutput.trim().startsWith('M') || statusOutput.trim().startsWith('A'));
147
+
148
+ if (needCommit) {
149
+ const commitMsg = getCommitMessage(action, fileName, responseCount, requestCount);
150
+ console.log(` 📤 提交: ${fileName} - ${commitMsg}`);
151
+ execSync(`svn commit "${filePath}" -m "${commitMsg}"`, { stdio: 'ignore' });
152
+ return true;
153
+ }
154
+ return false;
155
+ } catch (error) {
156
+ console.log(` ⚠️ 提交失败: ${error.message}`);
157
+ return false;
158
+ }
159
+ }
160
+
161
+ // 判断方法类型
162
+ function isResponseMethod(methodName) {
163
+ if (methodName.includes('Request')) return false;
164
+ if (methodName.includes('Response')) return true;
165
+ if (methodName.startsWith('Sync')) return true;
166
+ if (methodName.startsWith('On')) return true;
167
+ return false;
168
+ }
169
+
170
+ function isRequestMethod(methodName) {
171
+ if (methodName.includes('Request')) return true;
172
+ if (methodName.startsWith('Send')) return true;
173
+ if (methodName.startsWith('Req')) return true;
174
+ return false;
175
+ }
176
+
177
+ // 解析协议,提取模块的响应和请求方法
178
+ function parseModulesWithProtocols(content) {
179
+ const modules = new Map();
180
+ const processedKeys = new Set(); // 去重
181
+
182
+ const modulePattern = /ssrNetMsgCfg\.(\w+)\s*=\s*"(\w+)"/g;
183
+ let match;
184
+
185
+ while ((match = modulePattern.exec(content)) !== null) {
186
+ const moduleName = match[1];
187
+ if (!excludeModules.has(moduleName)) {
188
+ if (!modules.has(moduleName)) {
189
+ modules.set(moduleName, { responseMethods: [], requestMethods: [] });
190
+ }
191
+ }
192
+ }
193
+
194
+ const methodPattern = /ssrNetMsgCfg\.(\w+)_(\w+)\s*=\s*(\d+)(?:\s*--\s*(.+))?/g;
195
+ while ((match = methodPattern.exec(content)) !== null) {
196
+ const moduleName = match[1];
197
+ const methodName = match[2];
198
+ const protocolId = match[3];
199
+ const comment = match[4] || '';
200
+ const fullKey = `${moduleName}_${methodName}`;
201
+
202
+ // 去重检查
203
+ if (processedKeys.has(fullKey)) {
204
+ console.log(` ⚠️ 重复协议定义: ${fullKey},跳过`);
205
+ continue;
206
+ }
207
+ processedKeys.add(fullKey);
208
+
209
+ if (!excludeModules.has(moduleName)) {
210
+ if (!modules.has(moduleName)) {
211
+ modules.set(moduleName, { responseMethods: [], requestMethods: [] });
212
+ }
213
+
214
+ const module = modules.get(moduleName);
215
+ const methodInfo = {
216
+ name: methodName,
217
+ protocolId: parseInt(protocolId),
218
+ fullKey: fullKey,
219
+ comment: comment.trim()
220
+ };
221
+
222
+ if (isResponseMethod(methodName)) {
223
+ module.responseMethods.push(methodInfo);
224
+ } else if (isRequestMethod(methodName)) {
225
+ module.requestMethods.push(methodInfo);
226
+ } else {
227
+ console.log(` ⚠️ 无法识别方法类型: ${moduleName}_${methodName}`);
228
+ }
229
+ }
230
+ }
231
+
232
+ // 排序
233
+ for (const [_, module] of modules) {
234
+ module.responseMethods.sort((a, b) => a.protocolId - b.protocolId);
235
+ module.requestMethods.sort((a, b) => a.protocolId - b.protocolId);
236
+ }
237
+
238
+ return modules;
239
+ }
240
+
241
+ // 生成响应方法代码
242
+ function generateResponseMethods(methods, moduleName) {
243
+ if (!methods || methods.length === 0) return '';
244
+
245
+ const methodLines = [];
246
+ for (const method of methods) {
247
+ const methodName = method.name;
248
+ const comment = method.comment || `${methodName} 协议响应处理`;
249
+
250
+ methodLines.push(`--- ${comment}
251
+ function ${moduleName}Data:${methodName}(arg1, arg2, arg3, data)
252
+ -- TODO: 处理 ${methodName} 协议
253
+ -- ssrPrintNet("${moduleName}Data_${methodName}", arg1, arg2, arg3,data)
254
+ end`);
255
+ }
256
+ return methodLines.join('\n\n');
257
+ }
258
+
259
+ // 生成请求方法代码
260
+ function generateRequestMethods(methods, moduleName) {
261
+ if (!methods || methods.length === 0) return '';
262
+
263
+ const methodLines = [];
264
+ for (const method of methods) {
265
+ const methodName = method.name;
266
+ const comment = method.comment || `${methodName} 协议请求发送`;
267
+
268
+ methodLines.push(`--- ${comment}
269
+ function ${moduleName}Data:${methodName}(arg1, arg2, arg3, data)
270
+ ssrMessage:sendmsg(ssrNetMsgCfg.${moduleName}_${methodName}, arg1, arg2, arg3, data)
271
+ end`);
272
+ }
273
+ return methodLines.join('\n\n');
274
+ }
275
+
276
+ // 生成模块类(新文件用)
277
+ function generateModuleClass(moduleName, moduleData, author, template) {
278
+ const responseMethods = generateResponseMethods(moduleData.responseMethods, moduleName);
279
+ const requestMethods = generateRequestMethods(moduleData.requestMethods, moduleName);
280
+
281
+ return template
282
+ .replace(/\{AUTHOR\}/g, author)
283
+ .replace(/\{DATETIME\}/g, getDateTime())
284
+ .replace(/\{CLASSNAME\}/g, moduleName + "Data")
285
+ .replace(/\{MODULE_NAME\}/g, moduleName)
286
+ .replace(/\{RESPONSE_METHODS\}/g, responseMethods || ' -- 暂无响应方法')
287
+ .replace(/\{REQUEST_METHODS\}/g, requestMethods || ' -- 暂无请求方法');
288
+ }
289
+
290
+ // ==================== 修复后的合并函数 ====================
291
+
292
+ // 验证文件结构是否有效
293
+ function isFileStructureValid(content) {
294
+ // 检查所有标记是否都存在
295
+ for (const [key, marker] of Object.entries(MARKERS)) {
296
+ if (content.indexOf(marker) === -1) {
297
+ return { valid: false, reason: `缺少标记: ${key}` };
298
+ }
299
+ }
300
+
301
+ // 检查标记顺序是否正确
302
+ const requestStartIdx = content.indexOf(MARKERS.requestStart);
303
+ const requestEndIdx = content.indexOf(MARKERS.requestEnd);
304
+ const responseStartIdx = content.indexOf(MARKERS.responseStart);
305
+ const responseEndIdx = content.indexOf(MARKERS.responseEnd);
306
+
307
+ if (requestStartIdx > requestEndIdx) {
308
+ return { valid: false, reason: '请求开始标记在结束标记之后' };
309
+ }
310
+ if (responseStartIdx > responseEndIdx) {
311
+ return { valid: false, reason: '响应开始标记在结束标记之后' };
312
+ }
313
+ if (requestEndIdx > responseStartIdx) {
314
+ return { valid: false, reason: '请求区域应在响应区域之前' };
315
+ }
316
+
317
+ return { valid: true };
318
+ }
319
+
320
+ // 精确提取指定区域的方法(推荐使用)
321
+ function extractMethodsFromSection(section, className) {
322
+ const methods = new Map();
323
+ if (!section || section.trim().length === 0) return methods;
324
+
325
+ // 匹配从可选注释行到 end 的完整方法块
326
+ const methodPattern = new RegExp(
327
+ `(?:---[^\\n]*\\n)?` + // 可选的注释行
328
+ `function\\s+${className}:(\\w+)\\s*\\([^)]*\\)[\\n\\r]+` +
329
+ `([\\s\\S]*?)` +
330
+ `[\\n\\r]+end(?=[\\n\\r]+|$)`,
331
+ 'gm'
332
+ );
333
+
334
+ let match;
335
+ while ((match = methodPattern.exec(section)) !== null) {
336
+ const methodName = match[1];
337
+ const fullMethod = match[0].trim();
338
+
339
+ if (!methods.has(methodName)) {
340
+ methods.set(methodName, fullMethod);
341
+ }
342
+ }
343
+
344
+ console.log(` 📋 提取到 ${methods.size} 个方法: ${Array.from(methods.keys()).join(', ') || '无'}`);
345
+ return methods;
346
+ }
347
+
348
+ // 获取标记区域的边界(修复版)
349
+ function getMarkerBoundaries(content) {
350
+ const boundaries = {};
351
+
352
+ boundaries.requestStartIdx = content.indexOf(MARKERS.requestStart);
353
+ boundaries.requestEndIdx = content.indexOf(MARKERS.requestEnd);
354
+ boundaries.responseStartIdx = content.indexOf(MARKERS.responseStart);
355
+ boundaries.responseEndIdx = content.indexOf(MARKERS.responseEnd);
356
+
357
+ // 验证所有标记都存在
358
+ for (const [key, idx] of Object.entries(boundaries)) {
359
+ if (idx === -1) {
360
+ console.log(` ⚠️ 未找到标记: ${key}`);
361
+ return null;
362
+ }
363
+ }
364
+
365
+ boundaries.requestStartLength = MARKERS.requestStart.length;
366
+ boundaries.requestEndLength = MARKERS.requestEnd.length;
367
+ boundaries.responseStartLength = MARKERS.responseStart.length;
368
+ boundaries.responseEndLength = MARKERS.responseEnd.length;
369
+
370
+ // 调试:打印边界位置
371
+ console.log(` 📍 标记边界: 请求区域(${boundaries.requestStartIdx}-${boundaries.requestEndIdx}), 响应区域(${boundaries.responseStartIdx}-${boundaries.responseEndIdx})`);
372
+
373
+ return boundaries;
374
+ }
375
+
376
+ // 智能合并:添加新方法、删除已移除的方法、自动移除占位符
377
+ function mergeWithExisting(existingContent, newResponseContent, newRequestContent, moduleName) {
378
+ if (!existingContent) return null;
379
+
380
+ // 检查文件结构
381
+ const structureCheck = isFileStructureValid(existingContent);
382
+ if (!structureCheck.valid) {
383
+ console.log(` ⚠️ 文件结构无效: ${structureCheck.reason},将重新生成`);
384
+ return null;
385
+ }
386
+
387
+ const className = moduleName + 'Data';
388
+ const boundaries = getMarkerBoundaries(existingContent);
389
+
390
+ if (!boundaries) {
391
+ console.log(` ⚠️ 未找到完整的标记区域,将重新生成`);
392
+ return null;
393
+ }
394
+
395
+ // 提取现有内容
396
+ let existingRequestSection = '';
397
+ let existingResponseSection = '';
398
+ let middleSection = '';
399
+
400
+ try {
401
+ existingRequestSection = existingContent.substring(
402
+ boundaries.requestStartIdx + boundaries.requestStartLength,
403
+ boundaries.requestEndIdx
404
+ );
405
+
406
+ middleSection = existingContent.substring(
407
+ boundaries.requestEndIdx + boundaries.requestEndLength,
408
+ boundaries.responseStartIdx
409
+ );
410
+
411
+ existingResponseSection = existingContent.substring(
412
+ boundaries.responseStartIdx + boundaries.responseStartLength,
413
+ boundaries.responseEndIdx
414
+ );
415
+
416
+ console.log(` 📏 请求区域长度: ${existingRequestSection.length}, 响应区域长度: ${existingResponseSection.length}`);
417
+
418
+ } catch (err) {
419
+ console.log(` ⚠️ 提取区域失败: ${err.message}`);
420
+ return null;
421
+ }
422
+
423
+ // 提取现有方法
424
+ console.log(` 🔍 解析现有响应方法...`);
425
+ const existingResponses = extractMethodsFromSection(existingResponseSection, className);
426
+ console.log(` 🔍 解析现有请求方法...`);
427
+ const existingRequests = extractMethodsFromSection(existingRequestSection, className);
428
+
429
+ // 解析新方法(从生成的代码中提取)
430
+ console.log(` 🔍 解析新响应方法...`);
431
+ const newResponses = extractMethodsFromSection(newResponseContent, className);
432
+ console.log(` 🔍 解析新请求方法...`);
433
+ const newRequests = extractMethodsFromSection(newRequestContent, className);
434
+
435
+ console.log(` 📋 协议中的响应方法: ${Array.from(newResponses.keys()).join(', ') || '无'}`);
436
+ console.log(` 📋 协议中的请求方法: ${Array.from(newRequests.keys()).join(', ') || '无'}`);
437
+
438
+ // 找出需要添加和删除的方法
439
+ const toAddResponses = [];
440
+ const toAddRequests = [];
441
+ const toRemoveResponses = [];
442
+ const toRemoveRequests = [];
443
+
444
+ // 检查需要删除的响应方法(存在于文件但不在协议中)
445
+ for (const [name] of existingResponses) {
446
+ if (!newResponses.has(name)) {
447
+ toRemoveResponses.push(name);
448
+ console.log(` ❌ 删除响应方法: ${name} (协议中已移除)`);
449
+ }
450
+ }
451
+
452
+ // 检查需要删除的请求方法
453
+ for (const [name] of existingRequests) {
454
+ if (!newRequests.has(name)) {
455
+ toRemoveRequests.push(name);
456
+ console.log(` ❌ 删除请求方法: ${name} (协议中已移除)`);
457
+ }
458
+ }
459
+
460
+ // 添加新响应方法
461
+ for (const [name, code] of newResponses) {
462
+ if (!existingResponses.has(name)) {
463
+ toAddResponses.push({ name, code });
464
+ console.log(` ➕ 新增响应方法: ${name}`);
465
+ } else {
466
+ console.log(` ⏭️ 响应方法已存在,跳过: ${name}`);
467
+ }
468
+ }
469
+
470
+ // 添加新请求方法
471
+ for (const [name, code] of newRequests) {
472
+ if (!existingRequests.has(name)) {
473
+ toAddRequests.push({ name, code });
474
+ console.log(` ➕ 新增请求方法: ${name}`);
475
+ } else {
476
+ console.log(` ⏭️ 请求方法已存在,跳过: ${name}`);
477
+ }
478
+ }
479
+
480
+ // 标记是否有变化
481
+ let hasChanges = false;
482
+ let newRequestSection = existingRequestSection;
483
+ let newResponseSection = existingResponseSection;
484
+
485
+ // 删除需要移除的请求方法
486
+ for (const name of toRemoveRequests) {
487
+ const methodRegex = new RegExp(
488
+ `(?:---[^\\r\\n]*[\\r\\n]+)?` +
489
+ `function\\s+${className}:${name}\\s*\\([^)]*\\)[\\r\\n]+` +
490
+ `[\\s\\S]*?[\\r\\n]+end(?=[\\r\\n]+|$)`,
491
+ 'gm'
492
+ );
493
+ newRequestSection = newRequestSection.replace(methodRegex, '');
494
+ hasChanges = true;
495
+ console.log(` 🗑️ 已删除请求方法: ${name}`);
496
+ }
497
+
498
+ // 删除需要移除的响应方法
499
+ for (const name of toRemoveResponses) {
500
+ const methodRegex = new RegExp(
501
+ `(?:---[^\\r\\n]*[\\r\\n]+)?` +
502
+ `function\\s+${className}:${name}\\s*\\([^)]*\\)[\\r\\n]+` +
503
+ `[\\s\\S]*?[\\r\\n]+end(?=[\\r\\n]+|$)`,
504
+ 'gm'
505
+ );
506
+ newResponseSection = newResponseSection.replace(methodRegex, '');
507
+ hasChanges = true;
508
+ console.log(` 🗑️ 已删除响应方法: ${name}`);
509
+ }
510
+
511
+ // 清理删除后可能留下的多余空行
512
+ newRequestSection = newRequestSection.replace(/\n{3,}/g, '\n\n');
513
+ newResponseSection = newResponseSection.replace(/\n{3,}/g, '\n\n');
514
+
515
+ // 【关键修复】只有真正要添加请求方法时,才移除占位符
516
+ if (toAddRequests.length > 0 && existingRequests.size === 0) {
517
+ const placeholderPattern = /^[\s]*--\s*暂无请求方法[\s]*\n?/gm;
518
+ if (placeholderPattern.test(newRequestSection)) {
519
+ newRequestSection = newRequestSection.replace(placeholderPattern, '');
520
+ hasChanges = true;
521
+ console.log(` 🗑️ 移除占位符: -- 暂无请求方法`);
522
+ }
523
+ }
524
+
525
+ // 【关键修复】只有真正要添加响应方法时,才移除占位符
526
+ if (toAddResponses.length > 0 && existingResponses.size === 0) {
527
+ const placeholderPattern = /^[\s]*--\s*暂无响应方法[\s]*\n?/gm;
528
+ if (placeholderPattern.test(newResponseSection)) {
529
+ newResponseSection = newResponseSection.replace(placeholderPattern, '');
530
+ hasChanges = true;
531
+ console.log(` 🗑️ 移除占位符: -- 暂无响应方法`);
532
+ }
533
+ }
534
+
535
+ // 添加新方法到请求区域
536
+ if (toAddRequests.length > 0) {
537
+ if (newRequestSection.trim().length > 0 && !newRequestSection.endsWith('\n')) {
538
+ newRequestSection += '\n';
539
+ }
540
+ if (newRequestSection.trim().length > 0 && !newRequestSection.endsWith('\n\n')) {
541
+ newRequestSection += '\n';
542
+ }
543
+ newRequestSection += toAddRequests.map(m => m.code).join('\n\n');
544
+ if (!newRequestSection.endsWith('\n')) {
545
+ newRequestSection += '\n';
546
+ }
547
+ hasChanges = true;
548
+ }
549
+
550
+ // 添加新方法到响应区域
551
+ if (toAddResponses.length > 0) {
552
+ if (newResponseSection.trim().length > 0 && !newResponseSection.endsWith('\n')) {
553
+ newResponseSection += '\n';
554
+ }
555
+ if (newResponseSection.trim().length > 0 && !newResponseSection.endsWith('\n\n')) {
556
+ newResponseSection += '\n';
557
+ }
558
+ newResponseSection += toAddResponses.map(m => m.code).join('\n\n');
559
+ if (!newResponseSection.endsWith('\n')) {
560
+ newResponseSection += '\n';
561
+ }
562
+ hasChanges = true;
563
+ }
564
+
565
+ // 清理多余空行(保留最多一个空行)
566
+ newRequestSection = newRequestSection.replace(/\n\s*\n/g, '\n'); // 多个空行变一个
567
+ newRequestSection = newRequestSection.replace(/^\s*\n/, ''); // 删除开头的空行
568
+ newRequestSection = newRequestSection.replace(/\n\s*$/, '\n'); // 确保末尾只有一个换行
569
+
570
+ newResponseSection = newResponseSection.replace(/\n\s*\n/g, '\n');
571
+ newResponseSection = newResponseSection.replace(/^\s*\n/, '');
572
+ newResponseSection = newResponseSection.replace(/\n\s*$/, '\n\n'); // 末尾加两个换行(一个空行)
573
+
574
+ // 确保区域不为空(添加占位符)
575
+ if (!newRequestSection.trim()) {
576
+ newRequestSection = ' -- 暂无请求方法\n';
577
+ hasChanges = true;
578
+ console.log(` 📝 添加占位符: -- 暂无请求方法`);
579
+ }
580
+
581
+ if (!newResponseSection.trim()) {
582
+ newResponseSection = ' -- 暂无响应方法\n';
583
+ hasChanges = true;
584
+ console.log(` 📝 添加占位符: -- 暂无响应方法`);
585
+ }
586
+
587
+ // 如果没有变化,返回null
588
+ if (!hasChanges) {
589
+ console.log(` ⏭️ 没有新方法需要添加,也没有旧方法需要删除`);
590
+ return null;
591
+ }
592
+
593
+ // 重新组装文件
594
+ const headerPart = existingContent.substring(0, boundaries.requestStartIdx);
595
+ const afterResponsePart = existingContent.substring(boundaries.responseEndIdx + boundaries.responseEndLength);
596
+
597
+ const newContent = headerPart +
598
+ MARKERS.requestStart + '\n' +
599
+ newRequestSection +
600
+ MARKERS.requestEnd +
601
+ middleSection +
602
+ MARKERS.responseStart + '\n' +
603
+ newResponseSection +
604
+ MARKERS.responseEnd +
605
+ afterResponsePart;
606
+
607
+ // 输出变更统计
608
+ console.log(` 📊 变更统计: 响应(+${toAddResponses.length}/-${toRemoveResponses.length}) 请求(+${toAddRequests.length}/-${toRemoveRequests.length})`);
609
+
610
+ return {
611
+ content: newContent,
612
+ newResponseCount: toAddResponses.length,
613
+ newRequestCount: toAddRequests.length,
614
+ removedResponseCount: toRemoveResponses.length,
615
+ removedRequestCount: toRemoveRequests.length
616
+ };
617
+ }
618
+
619
+ // 深度修复文件(处理各种异常情况)
620
+ function deepFixFile(filePath, className) {
621
+ console.log(` 🔧 执行深度修复: ${path.basename(filePath)}`);
622
+
623
+ let content = fs.readFileSync(filePath, 'utf8');
624
+ let fixed = false;
625
+ const originalContent = content;
626
+
627
+ // 1. 移除重复的标记区域,只保留第一次出现的
628
+ for (const [key, marker] of Object.entries(MARKERS)) {
629
+ const regex = new RegExp(marker.replace(/[-]/g, '\\-'), 'g');
630
+ let firstOccurrence = true;
631
+ content = content.replace(regex, (match) => {
632
+ if (firstOccurrence) {
633
+ firstOccurrence = false;
634
+ return match;
635
+ }
636
+ fixed = true;
637
+ console.log(` 🗑️ 移除重复标记: ${key}`);
638
+ return '';
639
+ });
640
+ }
641
+
642
+ // 2. 确保标记顺序正确(请求在前,响应在后)
643
+ const requestStartIdx = content.indexOf(MARKERS.requestStart);
644
+ const responseStartIdx = content.indexOf(MARKERS.responseStart);
645
+
646
+ if (requestStartIdx > responseStartIdx && requestStartIdx !== -1 && responseStartIdx !== -1) {
647
+ console.log(` ⚠️ 标记顺序错误,正在修正...`);
648
+ fixed = true;
649
+
650
+ const beforeFirst = content.substring(0, Math.min(requestStartIdx, responseStartIdx));
651
+ const requestSection = content.substring(requestStartIdx, content.indexOf(MARKERS.requestEnd) + MARKERS.requestEnd.length);
652
+ const responseSection = content.substring(responseStartIdx, content.indexOf(MARKERS.responseEnd) + MARKERS.responseEnd.length);
653
+ const afterLast = content.substring(Math.max(
654
+ content.indexOf(MARKERS.requestEnd) + MARKERS.requestEnd.length,
655
+ content.indexOf(MARKERS.responseEnd) + MARKERS.responseEnd.length
656
+ ));
657
+
658
+ content = beforeFirst + requestSection + '\n' + responseSection + afterLast;
659
+ }
660
+
661
+ // 3. 清理响应区域中的请求方法(移到请求区域)
662
+ const responseStart = content.indexOf(MARKERS.responseStart);
663
+ const responseEnd = content.indexOf(MARKERS.responseEnd);
664
+
665
+ if (responseStart !== -1 && responseEnd !== -1 && responseStart < responseEnd) {
666
+ const beforeResponse = content.substring(0, responseStart + MARKERS.responseStart.length);
667
+ let responseSection = content.substring(responseStart + MARKERS.responseStart.length, responseEnd);
668
+ const afterResponse = content.substring(responseEnd);
669
+
670
+ const requestMethodRegex = /(---[^\n]*\nfunction\s+\w+Data:Request\w+\([\s\S]*?\nend)/g;
671
+ const movedMethods = [];
672
+
673
+ responseSection = responseSection.replace(requestMethodRegex, (match) => {
674
+ movedMethods.push(match);
675
+ fixed = true;
676
+ return '';
677
+ });
678
+
679
+ if (movedMethods.length > 0) {
680
+ console.log(` 📦 将 ${movedMethods.length} 个请求方法移回请求区域`);
681
+
682
+ const requestStart = content.indexOf(MARKERS.requestStart);
683
+ const requestEndIdx = content.indexOf(MARKERS.requestEnd);
684
+
685
+ if (requestStart !== -1 && requestEndIdx !== -1) {
686
+ const beforeRequest = content.substring(0, requestEndIdx);
687
+ const afterRequest = content.substring(requestEndIdx);
688
+ const newRequestSection = '\n' + movedMethods.join('\n\n') + '\n';
689
+ content = beforeRequest + newRequestSection + afterRequest;
690
+ }
691
+ }
692
+
693
+ content = beforeResponse + '\n' + responseSection + '\n' + afterResponse;
694
+ }
695
+
696
+ // 4. 清理请求区域中的响应方法(移到响应区域)
697
+ const requestStart = content.indexOf(MARKERS.requestStart);
698
+ const requestEndIdx = content.indexOf(MARKERS.requestEnd);
699
+
700
+ if (requestStart !== -1 && requestEndIdx !== -1 && requestStart < requestEndIdx) {
701
+ const beforeRequest = content.substring(0, requestStart + MARKERS.requestStart.length);
702
+ let requestSection = content.substring(requestStart + MARKERS.requestStart.length, requestEndIdx);
703
+ const afterRequest = content.substring(requestEndIdx);
704
+
705
+ const responseMethodRegex = /(---[^\n]*\nfunction\s+\w+Data:Response\w+\([\s\S]*?\nend)/g;
706
+ const movedMethods = [];
707
+
708
+ requestSection = requestSection.replace(responseMethodRegex, (match) => {
709
+ movedMethods.push(match);
710
+ fixed = true;
711
+ return '';
712
+ });
713
+
714
+ if (movedMethods.length > 0) {
715
+ console.log(` 📦 将 ${movedMethods.length} 个响应方法移回响应区域`);
716
+
717
+ const responseStartIdx = content.indexOf(MARKERS.responseStart);
718
+ const responseEndIdx = content.indexOf(MARKERS.responseEnd);
719
+
720
+ if (responseStartIdx !== -1 && responseEndIdx !== -1) {
721
+ const beforeResponse = content.substring(0, responseEndIdx);
722
+ const afterResponse = content.substring(responseEndIdx);
723
+ const newResponseSection = '\n' + movedMethods.join('\n\n') + '\n';
724
+ content = beforeResponse + newResponseSection + afterResponse;
725
+ }
726
+ }
727
+
728
+ content = beforeRequest + '\n' + requestSection + '\n' + afterRequest;
729
+ }
730
+
731
+ // 5. 去重:每个区域内的重复方法
732
+ const classNamePattern = className || path.basename(filePath, '.lua');
733
+
734
+ for (const markerType of ['request', 'response']) {
735
+ const startMarker = MARKERS[markerType + 'Start'];
736
+ const endMarker = MARKERS[markerType + 'End'];
737
+ const startIdx = content.indexOf(startMarker);
738
+ const endIdx = content.indexOf(endMarker);
739
+
740
+ if (startIdx !== -1 && endIdx !== -1 && startIdx < endIdx) {
741
+ const before = content.substring(0, startIdx + startMarker.length);
742
+ const section = content.substring(startIdx + startMarker.length, endIdx);
743
+ const after = content.substring(endIdx);
744
+
745
+ const methods = extractMethodsFromSection(section, classNamePattern);
746
+ const uniqueMethods = Array.from(methods.values()).join('\n\n');
747
+
748
+ if (uniqueMethods !== section.trim()) {
749
+ content = before + '\n' + uniqueMethods + '\n' + after;
750
+ fixed = true;
751
+ console.log(` 🗑️ 已清理 ${markerType} 区域的重复方法`);
752
+ }
753
+ }
754
+ }
755
+
756
+ if (fixed) {
757
+ const backupPath = filePath + '.backup_' + Date.now();
758
+ fs.writeFileSync(backupPath, originalContent, 'utf8');
759
+ fs.writeFileSync(filePath, content, 'utf8');
760
+ console.log(` ✅ 深度修复完成,备份文件: ${path.basename(backupPath)}`);
761
+ return true;
762
+ }
763
+
764
+ console.log(` ✅ 文件结构正常,无需修复`);
765
+ return false;
766
+ }
767
+
768
+ // 验证并修复已存在的文件
769
+ function validateAndFixFile(filePath) {
770
+ if (!fs.existsSync(filePath)) return false;
771
+
772
+ const content = fs.readFileSync(filePath, 'utf8');
773
+ const className = path.basename(filePath, '.lua');
774
+
775
+ let hasIssue = false;
776
+
777
+ // 检查是否有重复的标记区域
778
+ for (const [key, marker] of Object.entries(MARKERS)) {
779
+ const regex = new RegExp(marker.replace(/[-]/g, '\\-'), 'g');
780
+ const count = (content.match(regex) || []).length;
781
+ if (count !== 1) {
782
+ console.log(` ⚠️ 检测到标记重复: ${key} (出现 ${count} 次)`);
783
+ hasIssue = true;
784
+ }
785
+ }
786
+
787
+ // 检查标记是否都存在
788
+ for (const [key, marker] of Object.entries(MARKERS)) {
789
+ if (content.indexOf(marker) === -1) {
790
+ console.log(` ⚠️ 检测到缺少标记: ${key}`);
791
+ hasIssue = true;
792
+ }
793
+ }
794
+
795
+ // 检查响应区域中是否有请求方法
796
+ const responseStartIdx = content.indexOf(MARKERS.responseStart);
797
+ const responseEndIdx = content.indexOf(MARKERS.responseEnd);
798
+ if (responseStartIdx !== -1 && responseEndIdx !== -1 && responseStartIdx < responseEndIdx) {
799
+ const responseSection = content.substring(responseStartIdx, responseEndIdx);
800
+ if (responseSection.match(/function\s+\w+Data:Request\w+\(/)) {
801
+ console.log(` ⚠️ 检测到响应区域包含请求方法`);
802
+ hasIssue = true;
803
+ }
804
+ }
805
+
806
+ // 检查请求区域中是否有响应方法
807
+ const requestStartIdx = content.indexOf(MARKERS.requestStart);
808
+ const requestEndIdx = content.indexOf(MARKERS.requestEnd);
809
+ if (requestStartIdx !== -1 && requestEndIdx !== -1 && requestStartIdx < requestEndIdx) {
810
+ const requestSection = content.substring(requestStartIdx, requestEndIdx);
811
+ if (requestSection.match(/function\s+\w+Data:Response\w+\(/)) {
812
+ console.log(` ⚠️ 检测到请求区域包含响应方法`);
813
+ hasIssue = true;
814
+ }
815
+ }
816
+
817
+ if (hasIssue) {
818
+ console.log(` 🔧 检测到文件异常,执行深度修复...`);
819
+ return deepFixFile(filePath, className);
820
+ }
821
+
822
+ return false;
823
+ }
824
+
825
+ // 生成管理器类
826
+ function generateManagerClass(modules, author, template) {
827
+ const dateTime = getDateTime();
828
+ const moduleNames = Array.from(modules.keys())
829
+ .filter(name => modules.get(name).responseMethods.length > 0 || modules.get(name).requestMethods.length > 0)
830
+ .sort();
831
+
832
+ let requireLines = '';
833
+ if (moduleNames.length > 0) {
834
+ requireLines = moduleNames.map(name => {
835
+ return ` ---@type ${name}Data\n ssrFuncDataMgr.${name}Data = SL:Require("GUILayout/ssrgame/funcData/${name}Data", true)`;
836
+ }).join('\n');
837
+ } else {
838
+ requireLines = ' -- 暂无模块';
839
+ }
840
+
841
+ return template
842
+ .replace(/\{AUTHOR\}/g, author)
843
+ .replace(/\{DATETIME\}/g, dateTime)
844
+ .replace(/\{REQUIRE_LINES\}/g, requireLines);
845
+ }
846
+
847
+ function hasManagerContentChanged(managerPath, newContent) {
848
+ if (!fs.existsSync(managerPath)) {
849
+ return true;
850
+ }
851
+ try {
852
+ const oldContent = fs.readFileSync(managerPath, 'utf-8');
853
+ const normalize = (c) => c.split('\n').filter(l => !l.includes('-- 日期:')).join('\n');
854
+ return normalize(oldContent) !== normalize(newContent);
855
+ } catch (err) {
856
+ return true;
857
+ }
858
+ }
859
+
860
+ // ==================== 主流程 ====================
861
+ function main() {
862
+ if (!fs.existsSync(bPath)) {
863
+ console.error(`❌ NetMsgCfg.lua 不存在: ${bPath}`);
864
+ console.log(' 请先运行 A4_sync-netmsg.js 同步协议');
865
+ process.exit(1);
866
+ }
867
+
868
+ console.log('\n=== 解析协议阶段 ===');
869
+ const content = fs.readFileSync(bPath, 'utf8');
870
+ const modules = parseModulesWithProtocols(content);
871
+
872
+ console.log(`📋 解析到 ${modules.size} 个模块`);
873
+
874
+ let totalResponseMethods = 0;
875
+ let totalRequestMethods = 0;
876
+ for (const [name, module] of modules) {
877
+ totalResponseMethods += module.responseMethods.length;
878
+ totalRequestMethods += module.requestMethods.length;
879
+ if (module.responseMethods.length > 0 || module.requestMethods.length > 0) {
880
+ console.log(` 📦 ${name}: 响应${module.responseMethods.length}个, 请求${module.requestMethods.length}个`);
881
+ }
882
+ }
883
+ console.log(`📡 共 ${totalResponseMethods} 个响应方法, ${totalRequestMethods} 个请求方法`);
884
+
885
+ // 创建目录
886
+ if (!fs.existsSync(funcDataDir)) {
887
+ fs.mkdirSync(funcDataDir, { recursive: true });
888
+ console.log(`📁 创建目录: ${funcDataDir}`);
889
+ }
890
+ if (!fs.existsSync(mgrDir)) {
891
+ fs.mkdirSync(mgrDir, { recursive: true });
892
+ console.log(`📁 创建目录: ${mgrDir}`);
893
+ }
894
+
895
+ // 验证现有文件(修复之前可能存在的问题)
896
+ console.log('\n=== 验证现有文件阶段 ===');
897
+ if (fs.existsSync(funcDataDir)) {
898
+ const existingFiles = fs.readdirSync(funcDataDir).filter(f => f.endsWith('Data.lua'));
899
+ for (const file of existingFiles) {
900
+ const filePath = path.join(funcDataDir, file);
901
+ if (validateAndFixFile(filePath)) {
902
+ console.log(` ✅ 已修复: ${file}`);
903
+ }
904
+ }
905
+ }
906
+
907
+ // 生成数据类
908
+ console.log('\n=== 生成 funcData 阶段 ===');
909
+ let generatedCount = 0, updatedCount = 0, skippedCount = 0;
910
+ let committedFiles = [];
911
+
912
+ for (const [moduleName, moduleData] of modules) {
913
+ if (moduleData.responseMethods.length === 0 && moduleData.requestMethods.length === 0) {
914
+ skippedCount++;
915
+ continue;
916
+ }
917
+
918
+ const fileName = `${moduleName}Data.lua`;
919
+ const filePath = path.join(funcDataDir, fileName);
920
+ const newResponseContent = generateResponseMethods(moduleData.responseMethods, moduleName);
921
+ const newRequestContent = generateRequestMethods(moduleData.requestMethods, moduleName);
922
+
923
+ if (fs.existsSync(filePath)) {
924
+ console.log(`\n📄 处理: ${fileName}`);
925
+
926
+ // 再次验证并修复当前文件
927
+ validateAndFixFile(filePath);
928
+
929
+ const existingContent = fs.readFileSync(filePath, 'utf8');
930
+ const mergeResult = mergeWithExisting(existingContent, newResponseContent, newRequestContent, moduleName);
931
+
932
+ if (mergeResult) {
933
+ // 备份原文件
934
+ const backupPath = filePath + '.backup';
935
+ fs.writeFileSync(backupPath, existingContent, 'utf8');
936
+
937
+ try {
938
+ fs.writeFileSync(filePath, mergeResult.content, 'utf8');
939
+ console.log(` ✅ 更新完成 (新增响应${mergeResult.newResponseCount}个, 请求${mergeResult.newRequestCount}个)`);
940
+ updatedCount++;
941
+
942
+ // 删除备份
943
+ fs.unlinkSync(backupPath);
944
+
945
+ if (commitToSvn(filePath, '更新', mergeResult.newResponseCount, mergeResult.newRequestCount)) {
946
+ committedFiles.push(fileName);
947
+ }
948
+ } catch (err) {
949
+ console.log(` ❌ 写入失败,正在恢复备份...`);
950
+ if (fs.existsSync(backupPath)) {
951
+ fs.writeFileSync(filePath, fs.readFileSync(backupPath, 'utf8'), 'utf8');
952
+ fs.unlinkSync(backupPath);
953
+ }
954
+ throw err;
955
+ }
956
+ } else {
957
+ console.log(` ⏭️ 无需更新`);
958
+ skippedCount++;
959
+ }
960
+ } else {
961
+ const fullContent = generateModuleClass(moduleName, moduleData, config.author, dataTemplate);
962
+ fs.writeFileSync(filePath, fullContent, 'utf8');
963
+ console.log(`✅ 生成: ${fileName} (响应${moduleData.responseMethods.length}个, 请求${moduleData.requestMethods.length}个)`);
964
+ generatedCount++;
965
+ if (commitToSvn(filePath, '创建', moduleData.responseMethods.length, moduleData.requestMethods.length)) {
966
+ committedFiles.push(fileName);
967
+ }
968
+ }
969
+ }
970
+
971
+ console.log(`\n📊 数据类统计: 新增 ${generatedCount}, 更新 ${updatedCount}, 跳过 ${skippedCount}`);
972
+ if (committedFiles.length > 0) {
973
+ console.log(`📤 已提交: ${committedFiles.join(', ')}`);
974
+ }
975
+
976
+ // 生成管理器
977
+ console.log('\n=== 生成管理器阶段 ===');
978
+ const managerPath = path.join(mgrDir, 'FuncDataMgr.lua');
979
+ const managerContent = generateManagerClass(modules, config.author, managerTemplate);
980
+ const hasModules = Array.from(modules.keys()).some(name =>
981
+ modules.get(name).responseMethods.length > 0 || modules.get(name).requestMethods.length > 0
982
+ );
983
+
984
+ if (hasModules) {
985
+ const needCommit = !fs.existsSync(managerPath) || hasManagerContentChanged(managerPath, managerContent);
986
+
987
+ if (needCommit) {
988
+ fs.writeFileSync(managerPath, managerContent, 'utf8');
989
+ const action = !fs.existsSync(managerPath) ? '创建' : '更新';
990
+ const registeredCount = Array.from(modules.keys()).filter(name =>
991
+ modules.get(name).responseMethods.length > 0 || modules.get(name).requestMethods.length > 0
992
+ ).length;
993
+ console.log(`✅ ${action}管理器: ${managerPath}`);
994
+ console.log(`📋 管理器注册了 ${registeredCount} 个模块`);
995
+
996
+ if (commitToSvn(managerPath, action, registeredCount)) {
997
+ console.log(`📤 已提交: FuncDataMgr.lua`);
998
+ }
999
+ } else {
1000
+ console.log(`✨ 管理器内容无变化,跳过提交`);
1001
+ }
1002
+ } else {
1003
+ console.log(`⚠️ 没有可注册的模块,跳过生成管理器`);
1004
+ }
1005
+
1006
+ console.log('\n🎉 所有操作完成!');
1007
+ }
1008
+
1009
+ if (require.main === module) {
1010
+ main();
1011
+ }
1012
+
1013
+ module.exports = {
1014
+ parseModulesWithProtocols,
1015
+ generateModuleClass,
1016
+ mergeWithExisting,
1017
+ generateManagerClass,
1018
+ commitToSvn,
1019
+ validateAndFixFile,
1020
+ deepFixFile
1021
+ };