@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.
- package/A5_/346/270/270/346/210/217/345/256/242/346/210/267/347/253/257/346/250/241/345/235/227/345/274/200/345/217/221/350/247/204/350/214/203.md +275 -0
- package/AutoCfg/A6_generate-tablemgr.js +696 -0
- package/AutoCfg/A6_template_tablemgr.txt +37 -0
- package/AutoCfg/csvcfg.js +417 -0
- package/AutoCode/A1_996_config.json +5 -0
- package/AutoCode/A2_generate-panel.js +141 -0
- package/AutoCode/A3_watcher.js +132 -0
- package/AutoCode/A4_template_1.txt +31 -0
- package/AutoCode/A4_template_2.txt +30 -0
- package/AutoNetMsg/A4_exclude_modules.json +9 -0
- package/AutoNetMsg/A4_template_3.txt +30 -0
- package/AutoNetMsg/A4_template_4.txt +25 -0
- package/AutoNetMsg/A5_generate-funcdata.js +1021 -0
- package/AutoNetMsg/A5_sync-netmsg.js +98 -0
- package/SVNUpdate/svn_996.js +20 -0
- package/Tools/install-keys.js +117 -0
- package/Tools/project-keys.json +24 -0
- package/Tools/svn-utils.js +126 -0
- package/Tools/uninstall-keys.js +65 -0
- package/bat_manager/index.html +1248 -0
- package/bat_manager/local_scripts.json +42 -0
- package/bat_manager/run-server.bat +4 -0
- package/bat_manager/server.js +498 -0
- package/bat_manager/users.json +7 -0
- package/cli.js +29 -0
- package/index.js +9 -0
- package/package.json +47 -0
- package/run-autoCode.bat +5 -0
- package/run-client.bat +14 -0
- package/run-csvcfg.bat +4 -0
- package/run-svn.bat +4 -0
- package/run-sync.bat +4 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
-- 作者: {AUTHOR}
|
|
2
|
+
-- 日期: {DATETIME}
|
|
3
|
+
-- 版本: 1.0
|
|
4
|
+
-- 描述: ssrTableMgr 统一注册
|
|
5
|
+
-- 功能: 管理所有游戏配置表的加载和查询,提供通过ID快速获取配置的能力
|
|
6
|
+
|
|
7
|
+
-- Auto-generated by gen_TableMgr.go. Do not edit manually.
|
|
8
|
+
---@class ssrTableMgr
|
|
9
|
+
local ssrTableMgr = {}
|
|
10
|
+
|
|
11
|
+
ssrTableMgr.__cname = "ssrTableMgr"
|
|
12
|
+
ssrTableMgr.NAME = ssrTableMgr.__cname
|
|
13
|
+
|
|
14
|
+
-- 初始化函数:加载所有配置表
|
|
15
|
+
-- 注意:此函数在游戏启动时调用,会加载 csvcfg 和 game_config 两个目录下的所有配置
|
|
16
|
+
function ssrTableMgr:ctor()
|
|
17
|
+
self._indexCache = {}
|
|
18
|
+
--------------------------------自动注册配置 csvcfg ↓↓↓ start---------------------------------------
|
|
19
|
+
-- csvcfg 目录:策划配置的CSV文件转换而来的Lua配置表
|
|
20
|
+
-- 包含活动、道具、副本、任务等游戏核心数据
|
|
21
|
+
{REQUIRE_LINES_1}
|
|
22
|
+
--------------------------------自动注册配置 csvcfg ↓↓↓ end---------------------------------------
|
|
23
|
+
|
|
24
|
+
--------------------------------自动注册配置 game_config ↓↓↓ start---------------------------------------
|
|
25
|
+
-- game_config 目录:程序配置的Lua配置文件
|
|
26
|
+
-- 包含客户端表现相关的配置,如UI样式、音效、特效等
|
|
27
|
+
{REQUIRE_LINES_2}
|
|
28
|
+
--------------------------------自动注册配置 game_config ↓↓↓ end---------------------------------------
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
--------------------------------自定义代码 ↓↓↓ start--------------------------------------------
|
|
32
|
+
-- 在此处添加自定义代码,重新生成时会保留
|
|
33
|
+
--------------------------------自定义代码 ↓↓↓ end-----------------------------------------------
|
|
34
|
+
|
|
35
|
+
ssrTableMgr:ctor()
|
|
36
|
+
|
|
37
|
+
return ssrTableMgr
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
// -- 作者: 杨润池
|
|
2
|
+
// -- 日期: 2025年10月25日
|
|
3
|
+
// -- 版本: 3.0
|
|
4
|
+
// -- 描述: 功能模块表 前后端打表工具 - 支持 XLSX 转 Lua,智能提交优化,自动生成 ssrTableMgr
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const svnUtils = require('../Tools/svn-utils');
|
|
9
|
+
const iconv = require('iconv-lite');
|
|
10
|
+
const { execSync } = require('child_process');
|
|
11
|
+
|
|
12
|
+
// ==================== 路径配置 ====================
|
|
13
|
+
const projectRoot = path.join(__dirname, '../../../../'); // 996M2_Client 目录
|
|
14
|
+
const moduleTableRoot = path.join(__dirname, '../../../../ModuleTable'); // ModuleTable 目录
|
|
15
|
+
|
|
16
|
+
// 源文件目录(xlsx 文件)
|
|
17
|
+
const tPath = path.join(moduleTableRoot, 'table');
|
|
18
|
+
|
|
19
|
+
// 工具目录(Python 脚本)
|
|
20
|
+
const toolPath = path.join(moduleTableRoot, 'tool');
|
|
21
|
+
|
|
22
|
+
// 服务端输出目录
|
|
23
|
+
const sPath = path.join(__dirname, '../../../../MirServer/Mir200/Envir/QuestDiary/cfgcsv');
|
|
24
|
+
|
|
25
|
+
// 客户端输出目录
|
|
26
|
+
const cPath = path.join(__dirname, '../../../dev/GUILayout/ssrgame/csvcfg');
|
|
27
|
+
|
|
28
|
+
// 打印路径信息
|
|
29
|
+
console.log('========================================');
|
|
30
|
+
console.log('功能模块表转换工具 v3.0');
|
|
31
|
+
console.log('========================================');
|
|
32
|
+
console.log('工作目录:', __dirname);
|
|
33
|
+
console.log('表格目录(xlsx):', tPath);
|
|
34
|
+
console.log('工具目录:', toolPath);
|
|
35
|
+
console.log('服务器配置目录:', sPath);
|
|
36
|
+
console.log('客户端配置目录:', cPath);
|
|
37
|
+
|
|
38
|
+
// 检查目录是否存在
|
|
39
|
+
console.log('\n=== 检查目录 ===');
|
|
40
|
+
console.log('表格目录存在:', fs.existsSync(tPath));
|
|
41
|
+
console.log('工具目录存在:', fs.existsSync(toolPath));
|
|
42
|
+
console.log('服务器目录存在:', fs.existsSync(sPath));
|
|
43
|
+
console.log('客户端目录存在:', fs.existsSync(cPath));
|
|
44
|
+
|
|
45
|
+
// Python 脚本路径
|
|
46
|
+
const pythonScripts = {
|
|
47
|
+
server: path.join(toolPath, 'xlsx2server.py'),
|
|
48
|
+
client: path.join(toolPath, 'xlsx2client.py')
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// 存储文件状态用于比较
|
|
52
|
+
let fileStates = new Map();
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 执行 Python 脚本
|
|
56
|
+
* @param {string} scriptPath Python 脚本路径
|
|
57
|
+
* @param {string} outputDir 输出目录
|
|
58
|
+
* @param {string} name 脚本名称(用于日志)
|
|
59
|
+
* @returns {boolean} 是否成功
|
|
60
|
+
*/
|
|
61
|
+
function runPythonScript(scriptPath, outputDir, name) {
|
|
62
|
+
|
|
63
|
+
// 检查 openpyxl(提示安装,不是自动安装)
|
|
64
|
+
try {
|
|
65
|
+
execSync('python -c "import openpyxl"', { stdio: 'ignore' });
|
|
66
|
+
} catch (err) {
|
|
67
|
+
console.log(`
|
|
68
|
+
╔══════════════════════════════════════════════════════════════╗
|
|
69
|
+
║ ❌ openpyxl 未安装! ║
|
|
70
|
+
║ ║
|
|
71
|
+
║ 请运行以下命令安装: ║
|
|
72
|
+
║ ║
|
|
73
|
+
║ python -m pip install openpyxl ║
|
|
74
|
+
║ ║
|
|
75
|
+
╚══════════════════════════════════════════════════════════════╝
|
|
76
|
+
`);
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!fs.existsSync(scriptPath)) {
|
|
81
|
+
console.log(`❌ Python 脚本不存在: ${scriptPath}`);
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
console.log(`\n执行 ${name}...`);
|
|
86
|
+
console.log(`命令: python "${scriptPath}" "${outputDir}"`);
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const outputBuffer = execSync(`python "${scriptPath}" "${outputDir}"`, {
|
|
90
|
+
encoding: 'buffer',
|
|
91
|
+
cwd: toolPath,
|
|
92
|
+
timeout: 120000
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const decodedOutput = iconv.decode(outputBuffer, 'gbk');
|
|
96
|
+
console.log(`✅ ${name} 执行成功`);
|
|
97
|
+
if (decodedOutput.trim()) {
|
|
98
|
+
console.log('输出:', decodedOutput);
|
|
99
|
+
}
|
|
100
|
+
return true;
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.log(`❌ ${name} 执行失败: ${error.message}`);
|
|
103
|
+
if (error.stdout) {
|
|
104
|
+
const decodedStdout = iconv.decode(error.stdout, 'gbk');
|
|
105
|
+
console.log('stdout:', decodedStdout);
|
|
106
|
+
}
|
|
107
|
+
if (error.stderr) {
|
|
108
|
+
const decodedStderr = iconv.decode(error.stderr, 'gbk');
|
|
109
|
+
console.log('stderr:', decodedStderr);
|
|
110
|
+
}
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 清理临时目录
|
|
117
|
+
*/
|
|
118
|
+
function cleanTempDirectory() {
|
|
119
|
+
const targetDir = path.join(toolPath, 'dataserver');
|
|
120
|
+
if (fs.existsSync(targetDir)) {
|
|
121
|
+
console.log(`清理临时目录: ${targetDir}`);
|
|
122
|
+
try {
|
|
123
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
124
|
+
console.log('✅ 临时目录已清理');
|
|
125
|
+
} catch (err) {
|
|
126
|
+
console.log(`⚠️ 清理临时目录失败: ${err.message}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 生成 ssrTableMgr.lua
|
|
133
|
+
*/
|
|
134
|
+
function generateTableMgr() {
|
|
135
|
+
console.log('\n=== 生成 ssrTableMgr.lua ===');
|
|
136
|
+
try {
|
|
137
|
+
const tableMgrGenerator = require('./A6_generate-tablemgr');
|
|
138
|
+
tableMgrGenerator.main();
|
|
139
|
+
console.log('✅ ssrTableMgr.lua 生成完成');
|
|
140
|
+
return true;
|
|
141
|
+
} catch (genError) {
|
|
142
|
+
console.log(`⚠️ ssrTableMgr.lua 生成失败: ${genError.message}`);
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ==================== 文件状态管理 ====================
|
|
148
|
+
|
|
149
|
+
function recordFileStates() {
|
|
150
|
+
fileStates.clear();
|
|
151
|
+
const directories = [tPath, sPath, cPath];
|
|
152
|
+
|
|
153
|
+
for (const dir of directories) {
|
|
154
|
+
if (fs.existsSync(dir)) {
|
|
155
|
+
const files = getFilesRecursively(dir);
|
|
156
|
+
for (const file of files) {
|
|
157
|
+
try {
|
|
158
|
+
const stats = fs.statSync(file);
|
|
159
|
+
const content = fs.readFileSync(file);
|
|
160
|
+
const hash = simpleHash(content.toString());
|
|
161
|
+
fileStates.set(file, {
|
|
162
|
+
size: stats.size,
|
|
163
|
+
mtime: stats.mtime.getTime(),
|
|
164
|
+
hash: hash
|
|
165
|
+
});
|
|
166
|
+
} catch (error) {
|
|
167
|
+
// 忽略无法读取的文件
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
console.log(`📊 记录了 ${fileStates.size} 个文件的初始状态`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function checkContentChanges() {
|
|
176
|
+
let changedFiles = 0;
|
|
177
|
+
const directories = [tPath, sPath, cPath];
|
|
178
|
+
|
|
179
|
+
for (const dir of directories) {
|
|
180
|
+
if (fs.existsSync(dir)) {
|
|
181
|
+
const files = getFilesRecursively(dir);
|
|
182
|
+
for (const file of files) {
|
|
183
|
+
try {
|
|
184
|
+
if (!fileStates.has(file)) {
|
|
185
|
+
changedFiles++;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const oldState = fileStates.get(file);
|
|
190
|
+
const stats = fs.statSync(file);
|
|
191
|
+
const content = fs.readFileSync(file);
|
|
192
|
+
const newHash = simpleHash(content.toString());
|
|
193
|
+
|
|
194
|
+
if (stats.size !== oldState.size ||
|
|
195
|
+
stats.mtime.getTime() !== oldState.mtime ||
|
|
196
|
+
newHash !== oldState.hash) {
|
|
197
|
+
changedFiles++;
|
|
198
|
+
}
|
|
199
|
+
} catch (error) {
|
|
200
|
+
if (fileStates.has(file)) changedFiles++;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
console.log(`🔍 内容变化检查: ${changedFiles} 个文件有实质变化`);
|
|
207
|
+
return changedFiles > 0;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function getFilesRecursively(dir) {
|
|
211
|
+
let results = [];
|
|
212
|
+
if (!fs.existsSync(dir)) return results;
|
|
213
|
+
|
|
214
|
+
const list = fs.readdirSync(dir);
|
|
215
|
+
for (const file of list) {
|
|
216
|
+
const filePath = path.join(dir, file);
|
|
217
|
+
const stat = fs.statSync(filePath);
|
|
218
|
+
if (stat && stat.isDirectory()) {
|
|
219
|
+
results = results.concat(getFilesRecursively(filePath));
|
|
220
|
+
} else {
|
|
221
|
+
results.push(filePath);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return results;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function simpleHash(str) {
|
|
228
|
+
let hash = 0;
|
|
229
|
+
for (let i = 0; i < str.length; i++) {
|
|
230
|
+
const char = str.charCodeAt(i);
|
|
231
|
+
hash = ((hash << 5) - hash) + char;
|
|
232
|
+
hash = hash & hash;
|
|
233
|
+
}
|
|
234
|
+
return hash.toString();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function hasChanges(directory) {
|
|
238
|
+
if (!fs.existsSync(directory)) return false;
|
|
239
|
+
try {
|
|
240
|
+
const statusOutput = execSync('svn status', {
|
|
241
|
+
encoding: 'utf8',
|
|
242
|
+
cwd: directory,
|
|
243
|
+
stdio: ['pipe', 'pipe', 'ignore']
|
|
244
|
+
});
|
|
245
|
+
return statusOutput.trim().length > 0;
|
|
246
|
+
} catch (error) {
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function addNewFilesToSvn(directory) {
|
|
252
|
+
const newFiles = [];
|
|
253
|
+
if (!fs.existsSync(directory)) return newFiles;
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
const statusOutput = execSync('svn status', {
|
|
257
|
+
encoding: 'utf8',
|
|
258
|
+
cwd: directory
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
const lines = statusOutput.split('\n').filter(line => line.trim());
|
|
262
|
+
|
|
263
|
+
for (const line of lines) {
|
|
264
|
+
if (line.startsWith('?')) {
|
|
265
|
+
const file = line.substring(1).trim();
|
|
266
|
+
console.log(`添加新文件到 SVN: ${file}`);
|
|
267
|
+
try {
|
|
268
|
+
execSync(`svn add "${file}"`, {
|
|
269
|
+
stdio: 'ignore',
|
|
270
|
+
cwd: directory
|
|
271
|
+
});
|
|
272
|
+
newFiles.push(file);
|
|
273
|
+
} catch (addError) {
|
|
274
|
+
console.log(`添加文件失败: ${file}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
} catch (error) {
|
|
279
|
+
console.log(`检查SVN状态时出错: ${error.message}`);
|
|
280
|
+
}
|
|
281
|
+
return newFiles;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function markDeletedFiles(directory) {
|
|
285
|
+
const deletedFiles = [];
|
|
286
|
+
if (!fs.existsSync(directory)) return deletedFiles;
|
|
287
|
+
|
|
288
|
+
try {
|
|
289
|
+
const statusOutput = execSync('svn status', {
|
|
290
|
+
encoding: 'utf8',
|
|
291
|
+
cwd: directory
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
const lines = statusOutput.split('\n').filter(line => line.trim());
|
|
295
|
+
|
|
296
|
+
for (const line of lines) {
|
|
297
|
+
if (line.startsWith('!')) {
|
|
298
|
+
const file = line.substring(1).trim();
|
|
299
|
+
console.log(`标记删除文件: ${file}`);
|
|
300
|
+
try {
|
|
301
|
+
execSync(`svn delete "${file}"`, {
|
|
302
|
+
stdio: 'ignore',
|
|
303
|
+
cwd: directory
|
|
304
|
+
});
|
|
305
|
+
deletedFiles.push(file);
|
|
306
|
+
} catch (deleteError) {
|
|
307
|
+
console.log(`标记删除文件失败: ${file}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
} catch (error) {
|
|
312
|
+
console.log(`检查SVN删除状态时出错: ${error.message}`);
|
|
313
|
+
}
|
|
314
|
+
return deletedFiles;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// ==================== 主流程 ====================
|
|
318
|
+
|
|
319
|
+
function main() {
|
|
320
|
+
try {
|
|
321
|
+
console.log('\n开始执行自动化流程...');
|
|
322
|
+
|
|
323
|
+
// 1. 记录初始文件状态
|
|
324
|
+
recordFileStates();
|
|
325
|
+
|
|
326
|
+
// 2. 执行 SVN 更新
|
|
327
|
+
console.log('\n=== 执行 SVN 更新 ===');
|
|
328
|
+
const success0 = svnUtils.runCommand(tPath, '更新表格目录');
|
|
329
|
+
const success1 = svnUtils.runCommand(sPath, '更新服务器配置');
|
|
330
|
+
const success2 = svnUtils.runCommand(cPath, '更新客户端配置');
|
|
331
|
+
|
|
332
|
+
if (!success0 || !success1 || !success2) {
|
|
333
|
+
console.log('⚠️ SVN 更新过程中出现错误,但继续执行后续流程');
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// 3. 执行 Python 脚本转换(XLSX → Lua)
|
|
337
|
+
console.log('\n=== 执行 XLSX 转 Lua ===');
|
|
338
|
+
|
|
339
|
+
const serverSuccess = runPythonScript(pythonScripts.server, sPath, 'xlsx2server.py');
|
|
340
|
+
const clientSuccess = runPythonScript(pythonScripts.client, cPath, 'xlsx2client.py');
|
|
341
|
+
|
|
342
|
+
if (!serverSuccess || !clientSuccess) {
|
|
343
|
+
console.log('\n⚠️ Python 脚本执行失败,跳过后续流程');
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// 4. 清理临时目录
|
|
348
|
+
console.log('\n=== 清理临时目录 ===');
|
|
349
|
+
cleanTempDirectory();
|
|
350
|
+
|
|
351
|
+
// 5. 生成 ssrTableMgr.lua
|
|
352
|
+
generateTableMgr();
|
|
353
|
+
|
|
354
|
+
// 6. 执行 SVN 提交
|
|
355
|
+
console.log('\n=== 执行 SVN 提交 ===');
|
|
356
|
+
|
|
357
|
+
// 添加新增文件
|
|
358
|
+
console.log('检查并添加新增文件...');
|
|
359
|
+
const addedTFiles = addNewFilesToSvn(tPath);
|
|
360
|
+
const addedServerFiles = addNewFilesToSvn(sPath);
|
|
361
|
+
const addedClientFiles = addNewFilesToSvn(cPath);
|
|
362
|
+
|
|
363
|
+
console.log(`表格目录新增文件: ${addedTFiles.length} 个`);
|
|
364
|
+
console.log(`服务器目录新增文件: ${addedServerFiles.length} 个`);
|
|
365
|
+
console.log(`客户端目录新增文件: ${addedClientFiles.length} 个`);
|
|
366
|
+
|
|
367
|
+
// 标记删除文件
|
|
368
|
+
console.log('检查并标记删除的文件...');
|
|
369
|
+
const deletedTFiles = markDeletedFiles(tPath);
|
|
370
|
+
const deletedServerFiles = markDeletedFiles(sPath);
|
|
371
|
+
const deletedClientFiles = markDeletedFiles(cPath);
|
|
372
|
+
|
|
373
|
+
console.log(`表格目录删除文件: ${deletedTFiles.length} 个`);
|
|
374
|
+
console.log(`服务器目录删除文件: ${deletedServerFiles.length} 个`);
|
|
375
|
+
console.log(`客户端目录删除文件: ${deletedClientFiles.length} 个`);
|
|
376
|
+
|
|
377
|
+
// 检查内容变化
|
|
378
|
+
const hasContentChanges = checkContentChanges();
|
|
379
|
+
|
|
380
|
+
// 检查各目录是否有更改
|
|
381
|
+
const hasTChanges = hasChanges(tPath);
|
|
382
|
+
const hasSChanges = hasChanges(sPath);
|
|
383
|
+
const hasCChanges = hasChanges(cPath);
|
|
384
|
+
const hasAnyChanges = hasTChanges || hasSChanges || hasCChanges;
|
|
385
|
+
|
|
386
|
+
// 定义 shouldCommit
|
|
387
|
+
const shouldCommit = hasAnyChanges && hasContentChanges;
|
|
388
|
+
|
|
389
|
+
if (shouldCommit) {
|
|
390
|
+
console.log('📝 检测到实际内容变化,执行提交');
|
|
391
|
+
const successAll = svnUtils.runCommit([tPath, sPath, cPath], '自动同步功能模块表配置');
|
|
392
|
+
if (!successAll) {
|
|
393
|
+
console.log('⚠️ SVN 提交过程中出现错误');
|
|
394
|
+
}
|
|
395
|
+
} else if (hasAnyChanges && !hasContentChanges) {
|
|
396
|
+
console.log('ℹ️ 检测到SVN状态变化但内容无实质变化,跳过提交');
|
|
397
|
+
} else {
|
|
398
|
+
console.log('ℹ️ 没有检测到任何更改,跳过提交');
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
console.log('\n========================================');
|
|
402
|
+
console.log('🎉 所有操作完成!');
|
|
403
|
+
console.log('========================================');
|
|
404
|
+
|
|
405
|
+
} catch (error) {
|
|
406
|
+
console.log(`💥 程序执行过程中发生错误: ${error.message}`);
|
|
407
|
+
console.log('完整错误堆栈:', error.stack);
|
|
408
|
+
process.exit(1);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// 执行主函数
|
|
413
|
+
if (require.main === module) {
|
|
414
|
+
main();
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
module.exports = { main };
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// -- 作者: 杨润池
|
|
2
|
+
// -- 日期: 2026年04月21日
|
|
3
|
+
// -- 版本: 1.2
|
|
4
|
+
// -- 描述: 自动生成代码 (支持 UI/UISub/UIMain 前缀)
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
// 读取配置
|
|
10
|
+
const configPath = path.join(__dirname, 'A1_996_config.json');
|
|
11
|
+
let config = {};
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const configContent = fs.readFileSync(configPath, 'utf-8');
|
|
15
|
+
config = JSON.parse(configContent);
|
|
16
|
+
} catch (err) {
|
|
17
|
+
console.error(`[ERROR] Failed to read config: ${err.message}`);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 读取 MainUI 模板(默认模板)
|
|
22
|
+
const templatePath = path.join(__dirname, 'A4_template_1.txt');
|
|
23
|
+
let templateMain = '';
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
templateMain = fs.readFileSync(templatePath, 'utf-8');
|
|
27
|
+
} catch (err) {
|
|
28
|
+
console.error(`Template error (MainUI): ${err.message}`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 读取 SubUI 模板
|
|
33
|
+
const templatePath2 = path.join(__dirname, 'A4_template_2.txt');
|
|
34
|
+
let templateSub = '';
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
templateSub = fs.readFileSync(templatePath2, 'utf-8');
|
|
38
|
+
} catch (err) {
|
|
39
|
+
console.error(`Template error (SubUI): ${err.message}`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const guiExportDir = path.join(__dirname, '../../../dev/GUIExport');
|
|
44
|
+
const guiLayoutDir = path.join(__dirname, '../../../dev/GUILayout');
|
|
45
|
+
const author = config.author;
|
|
46
|
+
|
|
47
|
+
console.log(`[DEBUG] guiExportDir: ${guiExportDir}`);
|
|
48
|
+
console.log(`[DEBUG] guiLayoutDir: ${guiLayoutDir}`);
|
|
49
|
+
|
|
50
|
+
function getDateTime() {
|
|
51
|
+
const d = new Date();
|
|
52
|
+
const year = d.getFullYear();
|
|
53
|
+
const month = String(d.getMonth() + 1).padStart(2, '0');
|
|
54
|
+
const day = String(d.getDate()).padStart(2, '0');
|
|
55
|
+
const hours = String(d.getHours()).padStart(2, '0');
|
|
56
|
+
const minutes = String(d.getMinutes()).padStart(2, '0');
|
|
57
|
+
const seconds = String(d.getSeconds()).padStart(2, '0');
|
|
58
|
+
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function generate(uiFilePath) {
|
|
62
|
+
const uiFileName = path.basename(uiFilePath);
|
|
63
|
+
|
|
64
|
+
console.log(`[DEBUG] uiFileName: ${uiFileName}`);
|
|
65
|
+
|
|
66
|
+
// 修正: 检查文件名是否包含 UI、UISub、UIMain 前缀
|
|
67
|
+
const hasUIPrefix = uiFileName.startsWith('UI');
|
|
68
|
+
const hasUISubPrefix = uiFileName.startsWith('UISub');
|
|
69
|
+
const hasUIMainPrefix = uiFileName.startsWith('UIMain');
|
|
70
|
+
|
|
71
|
+
const isValidUI = hasUIPrefix || hasUISubPrefix || hasUIMainPrefix;
|
|
72
|
+
|
|
73
|
+
if (!isValidUI) {
|
|
74
|
+
console.log(`[SKIP] Not a valid UI file: ${uiFileName}`);
|
|
75
|
+
console.log(`[INFO] Expected file starting with: UI, UISub, or UIMain`);
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const relativePath = path.relative(guiExportDir, uiFilePath);
|
|
80
|
+
const relativeDir = path.dirname(relativePath);
|
|
81
|
+
const relativePathName = path.basename(relativePath);
|
|
82
|
+
|
|
83
|
+
// console.log(`[DEBUG] \n relativePath: ${relativePath}`, `\n relativeDir: ${relativeDir}`, `\n relativePathName: ${relativePathName}`);
|
|
84
|
+
|
|
85
|
+
// 提取基础名称(去掉前缀和.lua后缀)
|
|
86
|
+
let baseName = uiFileName.replace(/\.lua$/, ''); // 只去掉.lua后缀,保留完整文件名
|
|
87
|
+
let className = baseName + 'Panel'; // 直接使用完整文件名 + Panel
|
|
88
|
+
const panelFileName = `${className}.lua`;
|
|
89
|
+
|
|
90
|
+
// console.log(`[DEBUG] \n baseName: ${baseName}`, `\n className: ${className}`, `\n panelFileName: ${panelFileName}`);
|
|
91
|
+
|
|
92
|
+
const outputPath = path.join(guiLayoutDir, relativeDir, panelFileName);
|
|
93
|
+
console.log(`[DEBUG] outputPath: ${outputPath}`);
|
|
94
|
+
|
|
95
|
+
// 检查文件是否已存在
|
|
96
|
+
if (fs.existsSync(outputPath)) {
|
|
97
|
+
console.log(`[SKIP] Already exists: ${path.join(relativeDir, panelFileName)}`);
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 创建目录
|
|
102
|
+
const dir = path.dirname(outputPath);
|
|
103
|
+
if (!fs.existsSync(dir)) {
|
|
104
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
105
|
+
console.log(`[MKDIR] ${path.relative(guiLayoutDir, dir)}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 判断模板类型
|
|
109
|
+
let selectedTemplate = templateMain;
|
|
110
|
+
let templateType = "MainUI";
|
|
111
|
+
|
|
112
|
+
if (hasUISubPrefix) {
|
|
113
|
+
selectedTemplate = templateSub;
|
|
114
|
+
templateType = "SubUI";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log(`[INFO] Using ${templateType} template (prefix: ${hasUISubPrefix ? 'UISub' : (hasUIMainPrefix ? 'UIMain' : 'UI')})`);
|
|
118
|
+
|
|
119
|
+
// 替换模板变量
|
|
120
|
+
const content = selectedTemplate
|
|
121
|
+
.replace(/\{AUTHOR\}/g, author)
|
|
122
|
+
.replace(/\{DATETIME\}/g, getDateTime())
|
|
123
|
+
.replace(/\{CLASSNAME\}/g, className);
|
|
124
|
+
|
|
125
|
+
fs.writeFileSync(outputPath, content, 'utf-8');
|
|
126
|
+
console.log(`[GENERATE] ✅ Created: ${path.join(relativeDir, panelFileName)}`);
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 执行生成
|
|
131
|
+
const uiFilePath = process.argv[2];
|
|
132
|
+
if (uiFilePath && fs.existsSync(uiFilePath)) {
|
|
133
|
+
console.log(`\n${'='.repeat(60)}`);
|
|
134
|
+
console.log(`[INPUT] 📄 ${path.basename(uiFilePath)}`);
|
|
135
|
+
console.log(`${'='.repeat(60)}`);
|
|
136
|
+
generate(uiFilePath);
|
|
137
|
+
console.log(`${'='.repeat(60)}\n`);
|
|
138
|
+
} else {
|
|
139
|
+
console.log(`[ERROR] ❌ File not found: ${uiFilePath}`);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|