mirror of
				https://github.com/unanmed/HumanBreak.git
				synced 2025-10-31 12:12:58 +08:00 
			
		
		
		
	插件热重载
This commit is contained in:
		
							parent
							
								
									c4e0b706f9
								
							
						
					
					
						commit
						8ec41d634a
					
				| @ -81,79 +81,56 @@ | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 热重载脚本编辑及插件编写 |      * 热重载脚本编辑 | ||||||
|      * @param {string} data |      * @param {string} data | ||||||
|      */ |      */ | ||||||
|     async function reloadScript(data) { |     async function reloadScript() { | ||||||
|         if (data === 'plugins') { |         // 脚本编辑略微麻烦点
 | ||||||
|             // 插件编写比较好办
 |         const before = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a; | ||||||
|             const before = plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1; |         // 这里不能用动态导入,因为动态导入会变成模块,变量就不是全局的了
 | ||||||
|             // 这里不能用动态导入,因为动态导入会变成模块,变量就不是全局的了
 |         const script = document.createElement('script'); | ||||||
|             const script = document.createElement('script'); |         script.src = `/project/functions.js?v=${Date.now()}`; | ||||||
|             script.src = `/project/plugins.js?v=${Date.now()}`; |         document.body.appendChild(script); | ||||||
|             document.body.appendChild(script); |         await new Promise(res => { | ||||||
|             await new Promise(res => { |             script.onload = () => res('success'); | ||||||
|                 script.onload = () => res('success'); |         }); | ||||||
|             }); |         const after = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a; | ||||||
|             const after = plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1; |         // 找到差异的函数
 | ||||||
|             // 找到差异的函数
 |         for (const mod in before) { | ||||||
|             for (const id in before) { |             const fns = before[mod]; | ||||||
|                 const fn = before[id]; |             for (const id in fns) { | ||||||
|                 if (typeof fn !== 'function') continue; |                 const fn = fns[id]; | ||||||
|                 if (fn.toString() !== after[id]?.toString()) { |                 if (typeof fn !== 'function' || id === 'hasSpecial') continue; | ||||||
|  |                 const now = after[mod][id]; | ||||||
|  |                 if (fn.toString() !== now.toString()) { | ||||||
|                     try { |                     try { | ||||||
|                         core.plugin[id] = after[id]; |                         if (mod === 'events') { | ||||||
|                         core.plugin[id].call(core.plugin); |                             core.events.eventdata[id] = now; | ||||||
|  |                         } else if (mod === 'enemys') { | ||||||
|  |                             core.enemys.enemydata[id] = now; | ||||||
|  |                         } else if (mod === 'actions') { | ||||||
|  |                             core.actions.actionsdata[id] = now; | ||||||
|  |                         } else if (mod === 'control') { | ||||||
|  |                             core.control.controldata[id] = now; | ||||||
|  |                         } else if (mod === 'ui') { | ||||||
|  |                             core.ui.uidata[id] = now; | ||||||
|  |                         } | ||||||
|                         core.updateStatusBar(true, true); |                         core.updateStatusBar(true, true); | ||||||
|                         console.log(`plugin hot reload: ${id}`); |                         console.log(`function hot reload: ${mod}.${id}`); | ||||||
|                     } catch (e) { |                     } catch (e) { | ||||||
|                         console.error(e); |                         console.error(e); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } else if (data === 'functions') { |  | ||||||
|             // 脚本编辑略微麻烦点
 |  | ||||||
|             const before = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a; |  | ||||||
|             // 这里不能用动态导入,因为动态导入会变成模块,变量就不是全局的了
 |  | ||||||
|             const script = document.createElement('script'); |  | ||||||
|             script.src = `/project/functions.js?v=${Date.now()}`; |  | ||||||
|             document.body.appendChild(script); |  | ||||||
|             await new Promise(res => { |  | ||||||
|                 script.onload = () => res('success'); |  | ||||||
|             }); |  | ||||||
|             const after = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a; |  | ||||||
|             // 找到差异的函数
 |  | ||||||
|             for (const mod in before) { |  | ||||||
|                 const fns = before[mod]; |  | ||||||
|                 for (const id in fns) { |  | ||||||
|                     const fn = fns[id]; |  | ||||||
|                     if (typeof fn !== 'function' || id === 'hasSpecial') |  | ||||||
|                         continue; |  | ||||||
|                     const now = after[mod][id]; |  | ||||||
|                     if (fn.toString() !== now.toString()) { |  | ||||||
|                         try { |  | ||||||
|                             if (mod === 'events') { |  | ||||||
|                                 core.events.eventdata[id] = now; |  | ||||||
|                             } else if (mod === 'enemys') { |  | ||||||
|                                 core.enemys.enemydata[id] = now; |  | ||||||
|                             } else if (mod === 'actions') { |  | ||||||
|                                 core.actions.actionsdata[id] = now; |  | ||||||
|                             } else if (mod === 'control') { |  | ||||||
|                                 core.control.controldata[id] = now; |  | ||||||
|                             } else if (mod === 'ui') { |  | ||||||
|                                 core.ui.uidata[id] = now; |  | ||||||
|                             } |  | ||||||
|                             core.updateStatusBar(true, true); |  | ||||||
|                             console.log(`function hot reload: ${mod}.${id}`); |  | ||||||
|                         } catch (e) { |  | ||||||
|                             console.error(e); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     async function reloadPlugin(data) { | ||||||
|  |         // 直接import就完事了
 | ||||||
|  |         await import(`/project/plugin/${data}.js?v=${Date.now()}`); | ||||||
|  |         console.log(`plugin hot reload: ${data}.js`); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * 属性热重载,包括全塔属性等 |      * 属性热重载,包括全塔属性等 | ||||||
|      * @param {string} data |      * @param {string} data | ||||||
| @ -239,6 +216,7 @@ | |||||||
|                     if (type === 'data') reloadData(file); |                     if (type === 'data') reloadData(file); | ||||||
|                     if (type === 'floor') reloadFloor(file); |                     if (type === 'floor') reloadFloor(file); | ||||||
|                     if (type === 'script') reloadScript(file); |                     if (type === 'script') reloadScript(file); | ||||||
|  |                     if (type === 'plugin') reloadPlugin(file); | ||||||
|                 }); |                 }); | ||||||
|             }, 1000); |             }, 1000); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -44,6 +44,7 @@ next(); | |||||||
| let repStart; | let repStart; | ||||||
| 
 | 
 | ||||||
| const listenedFloors = []; | const listenedFloors = []; | ||||||
|  | const listenedPlugins = []; | ||||||
| 
 | 
 | ||||||
| // ----- GET file
 | // ----- GET file
 | ||||||
| 
 | 
 | ||||||
| @ -172,6 +173,7 @@ async function writeFile(req, res) { | |||||||
|         const value = /&value=[^]+/.exec(data)[0].slice(7); |         const value = /&value=[^]+/.exec(data)[0].slice(7); | ||||||
|         await fs.writeFile(dir, value, { encoding: type }); |         await fs.writeFile(dir, value, { encoding: type }); | ||||||
|         testWatchFloor(name); |         testWatchFloor(name); | ||||||
|  |         testWatchPlugin(name); | ||||||
|         if (name.endsWith('project/events.js')) doDeclaration('events', value); |         if (name.endsWith('project/events.js')) doDeclaration('events', value); | ||||||
|         if (name.endsWith('project/items.js')) doDeclaration('items', value); |         if (name.endsWith('project/items.js')) doDeclaration('items', value); | ||||||
|         if (name.endsWith('project/maps.js')) doDeclaration('maps', value); |         if (name.endsWith('project/maps.js')) doDeclaration('maps', value); | ||||||
| @ -332,7 +334,7 @@ async function watch() { | |||||||
|         watchOneFloor(v.slice(15)); |         watchOneFloor(v.slice(15)); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     // 脚本编辑 及 插件 热重载
 |     // 脚本编辑 热重载
 | ||||||
|     const scripts = await extract('project/functions.js', 'project/plugins.js'); |     const scripts = await extract('project/functions.js', 'project/plugins.js'); | ||||||
|     scripts.forEach(v => { |     scripts.forEach(v => { | ||||||
|         const dir = path.resolve(__dirname, v); |         const dir = path.resolve(__dirname, v); | ||||||
| @ -343,6 +345,12 @@ async function watch() { | |||||||
|         }); |         }); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     // 插件热重载
 | ||||||
|  |     const plugins = await extract('project/plugin/*.js'); | ||||||
|  |     plugins.forEach(v => { | ||||||
|  |         watchOnePlugin(v.slice(15)); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     // 数据热重载
 |     // 数据热重载
 | ||||||
|     const datas = (await extract('project/*.js')).filter( |     const datas = (await extract('project/*.js')).filter( | ||||||
|         v => !v.endsWith('functions.js') && !v.endsWith('plugins.js') |         v => !v.endsWith('functions.js') && !v.endsWith('plugins.js') | ||||||
| @ -370,6 +378,19 @@ function testWatchFloor(url) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * 检测是否是楼层文件并进行监听 | ||||||
|  |  * @param {string} url 要测试的路径 | ||||||
|  |  */ | ||||||
|  | function testWatchPlugin(url) { | ||||||
|  |     if (/project(\/|\\)plugin(\/|\\).*\.js/.test(url)) { | ||||||
|  |         const f = url.slice(15); | ||||||
|  |         if (!listenedFloors.includes(f.slice(0, -3))) { | ||||||
|  |             watchOnePlugin(f); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * 监听一个楼层文件 |  * 监听一个楼层文件 | ||||||
|  * @param {string} file 要监听的文件 |  * @param {string} file 要监听的文件 | ||||||
| @ -386,6 +407,22 @@ function watchOneFloor(file) { | |||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * 监听一个楼层文件 | ||||||
|  |  * @param {string} file 要监听的文件 | ||||||
|  |  */ | ||||||
|  | function watchOnePlugin(file) { | ||||||
|  |     if (!/.*\.js/.test(file)) return; | ||||||
|  |     const f = file.slice(0, -3); | ||||||
|  |     listenedFloors.push(file.slice(0, -3)); | ||||||
|  |     fss.watchFile(`project/plugin/${file}`, { interval: 500 }, () => { | ||||||
|  |         const plugin = f; | ||||||
|  |         if (hotReloadData.includes(`@@plugin:${plugin}`)) return; | ||||||
|  |         hotReloadData += `@@plugin:${plugin}`; | ||||||
|  |         console.log(`plugin hot reload: ${plugin}`); | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * 修改部分文件后重新加载及热重载 |  * 修改部分文件后重新加载及热重载 | ||||||
|  * @param {http.IncomingMessage} req |  * @param {http.IncomingMessage} req | ||||||
| @ -411,189 +448,6 @@ function reload(req, res, hot = false) { | |||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ----- replay debugger
 |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 录像调试 |  | ||||||
|  * @param {http.IncomingMessage} req |  | ||||||
|  * @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res |  | ||||||
|  */ |  | ||||||
| function replay(req, res) { |  | ||||||
|     req.on('data', async chunk => { |  | ||||||
|         if (chunk.toString() === 'test' && !replayed) { |  | ||||||
|             replayed = true; |  | ||||||
|             try { |  | ||||||
|                 await fs.mkdir(path.resolve(__dirname, '_replay')); |  | ||||||
|                 await fs.mkdir(path.resolve(__dirname, '_replay/status')); |  | ||||||
|                 await fs.mkdir(path.resolve(__dirname, '_replay/save')); |  | ||||||
|             } catch {} |  | ||||||
| 
 |  | ||||||
|             try { |  | ||||||
|                 await fs.readFile( |  | ||||||
|                     path.resolve(__dirname, '_replay/.info'), |  | ||||||
|                     'utf-8' |  | ||||||
|                 ); |  | ||||||
|             } catch { |  | ||||||
|                 await fs.writeFile( |  | ||||||
|                     path.resolve(__dirname, '_replay/.info'), |  | ||||||
|                     `{
 |  | ||||||
|     "cnt": 0 |  | ||||||
| }`,
 |  | ||||||
|                     'utf-8' |  | ||||||
|                 ); |  | ||||||
|             } |  | ||||||
|             const data = fss.readFileSync( |  | ||||||
|                 path.resolve(__dirname, '_replay/.info'), |  | ||||||
|                 'utf-8' |  | ||||||
|             ); |  | ||||||
|             repStart = Number(JSON.parse(data).cnt); |  | ||||||
|             console.log(`服务器录像调试模块已开始服务`); |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     req.on('end', () => { |  | ||||||
|         res.end(); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 获取未占用的状态栏位 |  | ||||||
|  * @param {http.IncomingMessage} req |  | ||||||
|  * @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res |  | ||||||
|  */ |  | ||||||
| async function replayCnt() { |  | ||||||
|     const data = `{
 |  | ||||||
|     "cnt": ${++repStart} |  | ||||||
| }`;
 |  | ||||||
|     fss.writeFileSync(path.resolve(__dirname, '_replay/.info'), data, 'utf-8'); |  | ||||||
| 
 |  | ||||||
|     return repStart; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 写入 |  | ||||||
|  * @param {http.IncomingMessage} req |  | ||||||
|  * @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res |  | ||||||
|  */ |  | ||||||
| async function replayWrite(req, res) { |  | ||||||
|     const data = await getPostData(req); |  | ||||||
|     const n = await replayCnt(); |  | ||||||
| 
 |  | ||||||
|     if (isNaN(n)) res.end('@error'); |  | ||||||
| 
 |  | ||||||
|     await Promise.all([ |  | ||||||
|         fs.writeFile( |  | ||||||
|             path.resolve(__dirname, '_replay/.info'), |  | ||||||
|             `{
 |  | ||||||
|     "cnt": ${n + 1} |  | ||||||
| }`,
 |  | ||||||
|             'utf-8' |  | ||||||
|         ), |  | ||||||
|         fs.writeFile( |  | ||||||
|             path.resolve(__dirname, `_replay/status/${n}.rep`), |  | ||||||
|             data, |  | ||||||
|             'utf-8' |  | ||||||
|         ) |  | ||||||
|     ]); |  | ||||||
| 
 |  | ||||||
|     res.end(n.toString()); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 比对录像与本地数据 |  | ||||||
|  * @param {http.IncomingMessage} req |  | ||||||
|  * @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res |  | ||||||
|  */ |  | ||||||
| async function replayCheck(req, res) { |  | ||||||
|     const ans = await getPostData(req); |  | ||||||
|     const [n, data] = ans.split('@-|-@'); |  | ||||||
| 
 |  | ||||||
|     const local = ( |  | ||||||
|         await fs.readFile( |  | ||||||
|             path.resolve(__dirname, `_replay/status/${n}.rep`), |  | ||||||
|             'utf-8' |  | ||||||
|         ) |  | ||||||
|     ) |  | ||||||
|         .split('@---@') |  | ||||||
|         .map(v => JSON.parse(v)); |  | ||||||
|     const rep = data.split('@---@').map(v => JSON.parse(v)); |  | ||||||
| 
 |  | ||||||
|     if (local.length !== rep.length) return res.end('false'); |  | ||||||
| 
 |  | ||||||
|     const check = (a, b) => { |  | ||||||
|         if (a === b) return true; |  | ||||||
|         if (typeof a !== typeof b) return false; |  | ||||||
|         if (typeof a === 'object' && a !== null) { |  | ||||||
|             for (const j in a) { |  | ||||||
|                 if (j === 'statistics' || j === 'timeout') continue; // 忽略统计信息
 |  | ||||||
|                 const aa = a[j]; |  | ||||||
|                 const bb = b[j]; |  | ||||||
|                 if (!check(aa, bb)) { |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
|         if ( |  | ||||||
|             typeof a === 'boolean' || |  | ||||||
|             typeof a === 'number' || |  | ||||||
|             typeof a === 'string' || |  | ||||||
|             typeof a === 'symbol' || |  | ||||||
|             typeof a === 'undefined' || |  | ||||||
|             typeof a === 'bigint' || |  | ||||||
|             a === null |  | ||||||
|         ) { |  | ||||||
|             return a === b; |  | ||||||
|         } |  | ||||||
|         return true; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     for (let i = 0; i < local.length; i++) { |  | ||||||
|         const a = local[i]; |  | ||||||
|         const b = rep[i]; |  | ||||||
|         if (!check(a, b)) return res.end('false'); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     res.end('true'); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 获取本地属性 |  | ||||||
|  * @param {http.IncomingMessage} req |  | ||||||
|  * @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res |  | ||||||
|  */ |  | ||||||
| async function replayGet(req, res, dir) { |  | ||||||
|     const ans = Number(await getPostData(req)); |  | ||||||
| 
 |  | ||||||
|     const data = await fs.readFile( |  | ||||||
|         path.resolve(__dirname, `_replay/${dir}/${ans}.rep`) |  | ||||||
|     ); |  | ||||||
|     res.end(data); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 录像回放存档 |  | ||||||
|  * @param {http.IncomingMessage} req |  | ||||||
|  * @param {http.ServerResponse<http.IncomingMessage> & {req: http.IncomingMessage;}} res |  | ||||||
|  */ |  | ||||||
| async function replaySave(req, res) { |  | ||||||
|     const data = await getPostData(req); |  | ||||||
|     const [cnt, save] = data.split('@-|-@'); |  | ||||||
| 
 |  | ||||||
|     if (isNaN(Number(cnt))) { |  | ||||||
|         console.log('Invalid input of save cnt'); |  | ||||||
|         res.end('@error: 不合法的录像存档信息'); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     await fs.writeFile( |  | ||||||
|         path.resolve(__dirname, `_replay/save/${cnt}.rep`), |  | ||||||
|         save, |  | ||||||
|         'utf-8' |  | ||||||
|     ); |  | ||||||
| 
 |  | ||||||
|     res.end('success'); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ----- declaration
 | // ----- declaration
 | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user