mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-01-18 11:59:26 +08:00
重写服务器
This commit is contained in:
parent
e4c18c0766
commit
0edbf05e9e
3
.gitignore
vendored
3
.gitignore
vendored
@ -32,4 +32,5 @@ dist.rar
|
||||
*.h5save
|
||||
*.h5route
|
||||
index.cjs
|
||||
!public/swap/*.h5save
|
||||
!public/swap/*.h5save
|
||||
_bundle
|
@ -30,10 +30,9 @@
|
||||
1. 首先请确保你安装了`node.js`与`pnpm`
|
||||
2. 将项目拉到你的设备上
|
||||
3. 运行`pnpm i`以安装所有依赖包
|
||||
4. 在`public`目录运行`node server.cjs`以启动样板热重载服务
|
||||
5. 在根目录运行`pnpm run dev`以启动`vite`服务
|
||||
6. 打开`vite`提供的网址即可进入游戏
|
||||
7. 打开样板热重载服务提供的网址即可进入编辑器
|
||||
4. 在根目录运行`pnpm run dev`以启动`vite`服务和样板的`http`服务与热重载服务
|
||||
5. 打开`vite`提供的网址即可进入游戏
|
||||
6. 打开样板服务提供的网址即可进入编辑器
|
||||
|
||||
## 构建说明
|
||||
|
||||
|
38
package.json
38
package.json
@ -4,7 +4,7 @@
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev": "ts-node-esm script/dev.ts",
|
||||
"build": "vue-tsc && vite build && ts-node-esm script/build.ts",
|
||||
"build-gh": "vue-tsc && vite build --base=/HumanBreak/ && ts-node-esm script/build.ts 1",
|
||||
"build-local": "vue-tsc && vite build --base=/ && ts-node-esm script/build.ts 1",
|
||||
@ -16,41 +16,45 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons-vue": "^6.1.0",
|
||||
"ant-design-vue": "^3.2.17",
|
||||
"axios": "^1.3.5",
|
||||
"chart.js": "^4.2.1",
|
||||
"lodash": "^4.17.21",
|
||||
"ant-design-vue": "^3.2.20",
|
||||
"axios": "^1.4.0",
|
||||
"chart.js": "^4.3.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lz-string": "^1.5.0",
|
||||
"mutate-animate": "^1.1.1",
|
||||
"three": "^0.149.0",
|
||||
"vue": "^3.2.47"
|
||||
"vue": "^3.3.1",
|
||||
"ws": "^8.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.21.0",
|
||||
"@babel/core": "^7.21.4",
|
||||
"@babel/preset-env": "^7.21.4",
|
||||
"@babel/cli": "^7.21.5",
|
||||
"@babel/core": "^7.21.8",
|
||||
"@babel/preset-env": "^7.21.5",
|
||||
"@rollup/plugin-babel": "^6.0.3",
|
||||
"@rollup/plugin-node-resolve": "^15.0.2",
|
||||
"@rollup/plugin-terser": "^0.4.1",
|
||||
"@rollup/plugin-typescript": "^11.1.0",
|
||||
"@types/babel__core": "^7.20.0",
|
||||
"@types/fontmin": "^0.9.0",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/lodash": "^4.14.194",
|
||||
"@types/node": "^18.15.11",
|
||||
"@vitejs/plugin-legacy": "^4.0.2",
|
||||
"@vitejs/plugin-vue": "^4.1.0",
|
||||
"@types/lodash-es": "^4.17.7",
|
||||
"@types/node": "^18.16.7",
|
||||
"@types/ws": "^8.5.4",
|
||||
"@vitejs/plugin-legacy": "^4.0.3",
|
||||
"@vitejs/plugin-vue": "^4.2.2",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.1",
|
||||
"chokidar": "^3.5.3",
|
||||
"compressing": "^1.9.0",
|
||||
"fontmin": "^0.9.9",
|
||||
"form-data": "^4.0.0",
|
||||
"fs-extra": "^10.1.0",
|
||||
"less": "^4.1.3",
|
||||
"rollup": "^3.20.2",
|
||||
"terser": "^5.16.9",
|
||||
"rollup": "^3.21.6",
|
||||
"terser": "^5.17.3",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.9.5",
|
||||
"unplugin-vue-components": "^0.22.12",
|
||||
"vite": "^4.2.1",
|
||||
"vue-tsc": "^1.2.0"
|
||||
"vite": "^4.3.5",
|
||||
"vue-tsc": "^1.6.4"
|
||||
}
|
||||
}
|
2697
pnpm-lock.yaml
2697
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -343,7 +343,11 @@ core.prototype._loadPlugin = async function () {
|
||||
if (main.pluginUseCompress) {
|
||||
await main.loadScript(`project/plugin.min.js?v=${main.version}`);
|
||||
} else {
|
||||
await main.loadScript(`src/plugin/game/index.js`, true);
|
||||
if (main.mode === 'play') {
|
||||
await main.loadScript(`src/plugin/game/index.js`, true);
|
||||
} else {
|
||||
await main.loadScript(`src/plugin/game/index.esm.js`, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
553
script/dev.ts
Normal file
553
script/dev.ts
Normal file
@ -0,0 +1,553 @@
|
||||
import { createServer } from 'vite';
|
||||
import {
|
||||
IncomingMessage,
|
||||
Server,
|
||||
ServerResponse,
|
||||
createServer as http
|
||||
} from 'http';
|
||||
import { isNil, some } from 'lodash-es';
|
||||
import config from '../mota.config.js';
|
||||
import fs from 'fs-extra';
|
||||
import { resolve, basename } from 'path';
|
||||
import * as rollup from 'rollup';
|
||||
import typescript from '@rollup/plugin-typescript';
|
||||
import nodeResolve from '@rollup/plugin-node-resolve';
|
||||
import EventEmitter from 'events';
|
||||
import { WebSocket, WebSocketServer } from 'ws';
|
||||
import chokidar from 'chokidar';
|
||||
|
||||
const base = './public';
|
||||
|
||||
type Request = IncomingMessage;
|
||||
type Response = ServerResponse<IncomingMessage> & {
|
||||
req: IncomingMessage;
|
||||
};
|
||||
|
||||
interface RollupInfo {
|
||||
dir: string;
|
||||
file: string;
|
||||
bundled: RefValue<boolean>;
|
||||
watcher: rollup.RollupWatcher;
|
||||
}
|
||||
const rollupMap = new Map<string, RollupInfo>();
|
||||
let bundleIndex = 0;
|
||||
let ws: WebSocket;
|
||||
|
||||
class RefValue<T> extends EventEmitter {
|
||||
private _value: T;
|
||||
public get value(): T {
|
||||
return this._value;
|
||||
}
|
||||
public set value(v: T) {
|
||||
this._value = v;
|
||||
this.emit('valueChange', v);
|
||||
}
|
||||
|
||||
constructor(value: T) {
|
||||
super();
|
||||
this._value = value;
|
||||
}
|
||||
|
||||
waitValueTo(value: T) {
|
||||
return new Promise(res => {
|
||||
if (this._value === value) {
|
||||
res(value);
|
||||
return;
|
||||
}
|
||||
const fn = (v: T) => {
|
||||
if (v === value) {
|
||||
this.off('valueChange', fn);
|
||||
res(v);
|
||||
}
|
||||
};
|
||||
this.on('valueChange', fn);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function resolvePath(path: string) {
|
||||
return resolve(base, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求文件
|
||||
*/
|
||||
async function getFile(req: Request, res: Response, path: string) {
|
||||
try {
|
||||
const data = await fs.readFile(resolvePath(path));
|
||||
if (path.endsWith('.js'))
|
||||
res.writeHead(200, { 'Content-type': 'text/javascript' });
|
||||
if (path.endsWith('.css'))
|
||||
res.writeHead(200, { 'Content-type': 'text/css' });
|
||||
if (path.endsWith('.html'))
|
||||
res.writeHead(200, { 'Content-type': 'text/html' });
|
||||
return res.end(data), true;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 高层塔优化及动画加载
|
||||
* @param suffix 后缀名
|
||||
* @param dir 文件夹路径
|
||||
* @param join 分隔符
|
||||
*/
|
||||
async function getAll(
|
||||
req: Request,
|
||||
res: Response,
|
||||
ids: string[],
|
||||
suffix: string,
|
||||
dir: string,
|
||||
join: string
|
||||
) {
|
||||
let data: Record<string, Buffer> = {};
|
||||
const tasks = ids.map(v => {
|
||||
return new Promise(res => {
|
||||
const d = resolvePath(`${dir}${v}${suffix}`);
|
||||
try {
|
||||
fs.readFile(d).then(vv => {
|
||||
data[v] = vv;
|
||||
res(`${v} pack success.`);
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
await Promise.all(tasks);
|
||||
const result = ids.map(v => data[v]);
|
||||
return res.end(result.join(join)), true;
|
||||
}
|
||||
|
||||
async function getEsmFile(
|
||||
req: Request,
|
||||
res: Response,
|
||||
dir: string
|
||||
): Promise<void> {
|
||||
const path = resolvePath(dir.replace('.esm', ''));
|
||||
|
||||
const watcher = rollupMap.get(path);
|
||||
|
||||
const file = (bundleIndex++).toString();
|
||||
|
||||
if (!watcher) {
|
||||
await fs.ensureDir('_bundle');
|
||||
// 配置rollup监听器
|
||||
const w = rollup.watch({
|
||||
input: path,
|
||||
output: {
|
||||
file: `_bundle/${file}.js`,
|
||||
sourcemap: true,
|
||||
format: 'es'
|
||||
},
|
||||
cache: true,
|
||||
watch: {
|
||||
exclude: '**/node_modules/**',
|
||||
buildDelay: 200
|
||||
},
|
||||
plugins: [typescript({ sourceMap: true }), nodeResolve()],
|
||||
onwarn() {}
|
||||
});
|
||||
|
||||
const info: RollupInfo = {
|
||||
watcher: w,
|
||||
file: `_bundle/${file}.js`,
|
||||
dir,
|
||||
bundled: new RefValue(false)
|
||||
};
|
||||
w.on('event', e => {
|
||||
if (e.code === 'BUNDLE_END') {
|
||||
info.bundled.value = true;
|
||||
console.log(`${path} bundle end`);
|
||||
}
|
||||
|
||||
if (e.code === 'BUNDLE_START') {
|
||||
info.bundled.value = false;
|
||||
}
|
||||
});
|
||||
w.on('change', id => {
|
||||
console.log(`${id} changed. Refresh Page.`);
|
||||
ws.send(JSON.stringify({ type: 'reload' }));
|
||||
});
|
||||
rollupMap.set(path, info);
|
||||
|
||||
// 配置完毕,直接重新获取即可(
|
||||
return getEsmFile(req, res, dir);
|
||||
} else {
|
||||
try {
|
||||
await watcher.bundled.waitValueTo(true);
|
||||
const content = await fs.readFile(watcher.file, 'utf-8');
|
||||
res.writeHead(200, { 'Content-type': 'text/javascript' });
|
||||
res.end(content);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取POST数据
|
||||
*/
|
||||
async function getPostData(req: Request) {
|
||||
let data = '';
|
||||
await new Promise(res => {
|
||||
req.on('data', chunk => {
|
||||
data += chunk.toString();
|
||||
});
|
||||
req.on('end', res);
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
async function readDir(req: Request, res: Response) {
|
||||
const data = await getPostData(req);
|
||||
const dir = resolvePath(data.toString().slice(5));
|
||||
try {
|
||||
const info = await fs.readdir(dir);
|
||||
res.end(JSON.stringify(info));
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
res.end(`Error: Read dir ${dir} fail. Does the dir exists?`);
|
||||
}
|
||||
}
|
||||
|
||||
async function mkdir(req: Request, res: Response) {
|
||||
const data = await getPostData(req);
|
||||
const dir = resolvePath(data.toString().slice(5));
|
||||
try {
|
||||
await fs.ensureDir(dir);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
res.end();
|
||||
}
|
||||
|
||||
async function readFile(req: Request, res: Response) {
|
||||
const data = (await getPostData(req)).toString();
|
||||
const dir = resolvePath(data.split('&name=')[1]);
|
||||
try {
|
||||
const type = /^type=(utf8|base64)/.exec(data)?.[0].slice(5) ?? 'utf8';
|
||||
const info = await fs.readFile(dir, { encoding: type });
|
||||
res.end(info);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function writeFile(req: Request, res: Response) {
|
||||
const data = (await getPostData(req)).toString();
|
||||
const name = data.split('&name=')[1].split('&value=')[0];
|
||||
const dir = resolvePath(name);
|
||||
try {
|
||||
const type = /^type=(utf8|base64)/.exec(data)?.[0].slice(5) ?? 'utf8';
|
||||
const value = /&value=.+/.exec(data)?.[0].slice(7) ?? '';
|
||||
await fs.writeFile(dir, value, { encoding: type });
|
||||
if (name.endsWith('project/events.js')) doDeclaration('events', value);
|
||||
if (name.endsWith('project/items.js')) doDeclaration('items', value);
|
||||
if (name.endsWith('project/maps.js')) doDeclaration('maps', value);
|
||||
if (name.endsWith('project/data.js')) doDeclaration('data', value);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
res.end(
|
||||
`error: Write file ${dir} fail. Does the parent folder exists?`
|
||||
);
|
||||
}
|
||||
res.end();
|
||||
}
|
||||
|
||||
async function rm(req: Request, res: Response) {
|
||||
const data = (await getPostData(req)).toString();
|
||||
const dir = resolvePath(data.slice(5));
|
||||
try {
|
||||
await fs.remove(dir);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
res.end(`error: Remove file ${dir} fail. Does this file exists?`);
|
||||
}
|
||||
res.end();
|
||||
}
|
||||
|
||||
async function moveFile(req: Request, res: Response) {
|
||||
const data = (await getPostData(req)).toString();
|
||||
const info = data.split('&dest=');
|
||||
const src = resolvePath(info[0].slice(4));
|
||||
const dest = resolvePath(info[1]);
|
||||
try {
|
||||
await fs.move(src, dest);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
res.end();
|
||||
}
|
||||
|
||||
async function writeMultiFiles(req: Request, res: Response) {
|
||||
const data = (await getPostData(req)).toString();
|
||||
const names =
|
||||
/name=.+&value=/.exec(data)?.[0].slice(5, -7).split(';') ?? [];
|
||||
const value = /&value=.+/.exec(data)?.[0].slice(7).split(';') ?? [];
|
||||
|
||||
const tasks = names.map((v, i) => {
|
||||
try {
|
||||
return new Promise(res => {
|
||||
fs.writeFile(
|
||||
resolvePath(v),
|
||||
value[i],
|
||||
'base64' // 多文件是base64写入的
|
||||
).then(v => {
|
||||
res(`write ${v} success.`);
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
res.end(`error: Write multi files fail.`);
|
||||
}
|
||||
});
|
||||
await Promise.all(tasks).catch(e => console.log(e));
|
||||
res.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* 声明某种类型
|
||||
* @param {string} type 类型
|
||||
* @param {string} data 信息
|
||||
*/
|
||||
async function doDeclaration(type: string, data: string) {
|
||||
const buf = Buffer.from(data, 'base64');
|
||||
data = buf.toString('utf-8');
|
||||
if (type === 'events') {
|
||||
// 事件
|
||||
const eventData = JSON.parse(data.split('\n').slice(1).join(''));
|
||||
|
||||
let eventDec = 'type EventDeclaration = \n';
|
||||
for (const id in eventData.commonEvent) {
|
||||
eventDec += ` | '${id}'\n`;
|
||||
}
|
||||
await fs.writeFile('../src/source/events.d.ts', eventDec, 'utf-8');
|
||||
} else if (type === 'items') {
|
||||
// 道具
|
||||
const itemData = JSON.parse(data.split('\n').slice(1).join(''));
|
||||
|
||||
let itemDec = 'interface ItemDeclaration {\n';
|
||||
for (const id in itemData) {
|
||||
itemDec += ` ${id}: '${itemData[id].cls}';\n`;
|
||||
}
|
||||
itemDec += '}';
|
||||
await fs.writeFile('../src/source/items.d.ts', itemDec, 'utf-8');
|
||||
} else if (type === 'maps') {
|
||||
// 映射
|
||||
const d = JSON.parse(data.split('\n').slice(1).join(''));
|
||||
|
||||
let id2num = 'interface IdToNumber {\n';
|
||||
let num2id = 'interface NumberToId {\n';
|
||||
let id2cls = 'interface IdToCls {\n';
|
||||
for (const num in d) {
|
||||
const { id, cls } = d[num];
|
||||
id2num += ` ${id}: ${num};\n`;
|
||||
num2id += ` ${num}: '${id}';\n`;
|
||||
id2cls += ` ${id}: '${cls}';\n`;
|
||||
}
|
||||
id2cls += '}';
|
||||
id2num += '}';
|
||||
num2id += '}';
|
||||
await fs.writeFile('../src/source/cls.d.ts', id2cls, 'utf-8');
|
||||
await fs.writeFile(
|
||||
'../src/source/maps.d.ts',
|
||||
`${id2num}\n${num2id}`,
|
||||
'utf-8'
|
||||
);
|
||||
} else if (type === 'data') {
|
||||
// 全塔属性的注册信息
|
||||
const d = JSON.parse(data.split('\n').slice(1).join('')).main;
|
||||
|
||||
let floorId = 'type FloorIds =\n';
|
||||
let imgs = 'type ImageIds =\n';
|
||||
let anis = 'type AnimationIds =\n';
|
||||
let sounds = 'type SoundIds =\n';
|
||||
let names = 'interface NameMap {\n';
|
||||
let bgms = 'type BgmIds =\n';
|
||||
let fonts = 'type FontIds =\n';
|
||||
|
||||
floorId += d.floorIds.map((v: any) => ` | '${v}'\n`).join('');
|
||||
imgs += d.images.map((v: any) => ` | '${v}'\n`).join('');
|
||||
anis += d.animates.map((v: any) => ` | '${v}'\n`).join('');
|
||||
sounds += d.sounds.map((v: any) => ` | '${v}'\n`).join('');
|
||||
bgms += d.bgms.map((v: any) => ` | '${v}'\n`).join('');
|
||||
fonts += d.fonts.map((v: any) => ` | '${v}'\n`).join('');
|
||||
for (const name in d.nameMap) {
|
||||
names += ` '${name}': '${d.nameMap[name]}';\n`;
|
||||
}
|
||||
names += '}';
|
||||
|
||||
await fs.writeFile(
|
||||
'../src/source/data.d.ts',
|
||||
`
|
||||
${floorId}
|
||||
${d.images.length > 0 ? imgs : 'type ImageIds = never\n'}
|
||||
${d.animates.length > 0 ? anis : 'type AnimationIds = never\n'}
|
||||
${d.sounds.length > 0 ? sounds : 'type SoundIds = never\n'}
|
||||
${d.bgms.length > 0 ? bgms : 'type BgmIds = never\n'}
|
||||
${d.fonts.length > 0 ? fonts : 'type FontIds = never\n'}
|
||||
${names}
|
||||
`,
|
||||
'utf-8'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function startHttpServer(port: number = 3000) {
|
||||
const server = http();
|
||||
|
||||
const tryNext = () => {
|
||||
server.listen(port++, '127.0.0.1');
|
||||
};
|
||||
server.on('error', () => {
|
||||
tryNext();
|
||||
});
|
||||
server.on('listening', () => {
|
||||
console.log(`编辑器地址:http://127.0.0.1:${port - 1}/editor.html`);
|
||||
setupHttp(server);
|
||||
});
|
||||
tryNext();
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
function setupHttp(server: Server) {
|
||||
server.on('request', async (req, res) => {
|
||||
const p = req.url
|
||||
?.replace(`/games/${config.name}`, '')
|
||||
.replace('/all/', '/') // 样板中特殊处理的all文件
|
||||
.replace('/forceTem/', '/') // 强制用样板的http服务获取文件
|
||||
.replace('/src/', '../src/'); // src在上一级目录
|
||||
if (isNil(p)) return;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
const dir = resolvePath(
|
||||
p === '/' ? 'index.html' : p.slice(1)
|
||||
).split('?v=')[0];
|
||||
|
||||
if (/.*\.esm\..*/.test(p)) {
|
||||
// xxx.esm.xxx,说明是需要打包的es模块化文件,需要rollup打包后传输
|
||||
return getEsmFile(req, res, p);
|
||||
}
|
||||
|
||||
if (p.startsWith('/__all_floors__.js')) {
|
||||
const all = p.split('&id=')[1].split(',');
|
||||
res.writeHead(200, { 'Content-type': 'text/javascript' });
|
||||
return getAll(req, res, all, '.js', 'project/floors/', '\n');
|
||||
}
|
||||
|
||||
if (p.startsWith('/__all_animates__')) {
|
||||
const all = p.split('&id=')[1].split(',');
|
||||
const split = '@@@~~~###~~~@@@';
|
||||
const dir = 'project/animates/';
|
||||
return getAll(req, res, all, '.animate', dir, split);
|
||||
}
|
||||
|
||||
if (await getFile(req, res, dir)) return;
|
||||
}
|
||||
|
||||
if (req.method === 'POST') {
|
||||
if (p === '/listFile') return readDir(req, res);
|
||||
if (p === '/makeDir') return mkdir(req, res);
|
||||
if (p === '/readFile') return readFile(req, res);
|
||||
if (p === '/writeFile') return writeFile(req, res);
|
||||
if (p === '/deleteFile') return rm(req, res);
|
||||
if (p === '/moveFile') return moveFile(req, res);
|
||||
if (p === '/writeMultiFiles') return writeMultiFiles(req, res);
|
||||
}
|
||||
|
||||
res.statusCode = 404;
|
||||
res.end();
|
||||
});
|
||||
}
|
||||
|
||||
function watchProject() {
|
||||
const watcher = chokidar.watch('public/', {
|
||||
persistent: true,
|
||||
ignored: [
|
||||
'**/node_modules/**',
|
||||
'**/.git/**',
|
||||
'**/thirdparty/**',
|
||||
'**/_docs/**',
|
||||
'**/_save/**',
|
||||
/\.min\./,
|
||||
/(^|[\/\\])\../,
|
||||
/(^|[\/\\])[^a-zA-Z:\._0-9\/\\]/
|
||||
]
|
||||
});
|
||||
watcher.on('change', async path => {
|
||||
// 楼层热重载
|
||||
if (/project(\/|\\)floors(\/|\\).*\.js$/.test(path)) {
|
||||
const floor = basename(path).slice(0, -3);
|
||||
ws.send(JSON.stringify({ type: 'floorHotReload', floor }));
|
||||
console.log(`Floor hot reload: ${floor}.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 脚本编辑热重载
|
||||
if (/project(\/|\\)functions\.js$/.test(path)) {
|
||||
ws.send(JSON.stringify({ type: 'functionsHotReload' }));
|
||||
console.log(`Functions hot reload.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 数据热重载
|
||||
if (/project(\/|\\).*\.js/.test(path)) {
|
||||
const data = basename(path);
|
||||
ws.send(JSON.stringify({ type: 'dataHotReload', data }));
|
||||
console.log(`Data hot reload: ${data}.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// css热重载
|
||||
if (/.*\.css$/.test(path)) {
|
||||
ws.send(JSON.stringify({ type: 'cssHotReload', path }));
|
||||
console.log(`Css hot reload: ${path}.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 剩余内容全部reload
|
||||
ws.send(JSON.stringify({ type: 'reload' }));
|
||||
});
|
||||
}
|
||||
|
||||
function setupSocket(socket: WebSocket) {
|
||||
ws = socket;
|
||||
socket.send(JSON.stringify({ type: 'connected' }));
|
||||
watchProject();
|
||||
}
|
||||
|
||||
async function startWsServer(port: number = 8080) {
|
||||
const tryNext = () => {
|
||||
return new Promise<WebSocketServer>(res => {
|
||||
const server = new WebSocketServer({
|
||||
port: port++
|
||||
});
|
||||
|
||||
server.on('error', async () => {
|
||||
res(await tryNext());
|
||||
});
|
||||
|
||||
server.on('connection', socket => {
|
||||
setupSocket(socket);
|
||||
res(server);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
(async function () {
|
||||
// 1. 启动vite服务
|
||||
const vite = await createServer();
|
||||
await vite.listen(5173);
|
||||
console.log(`游戏地址:http://localhost:5173/games/${config.name}/`);
|
||||
|
||||
// 2. 启动样板http服务
|
||||
await startHttpServer();
|
||||
|
||||
// 3. 启动样板ws热重载服务
|
||||
await startWsServer();
|
||||
})();
|
@ -91,7 +91,7 @@ import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { getCriticalDamage, getDefDamage } from '../plugin/ui/book';
|
||||
import Chart, { ChartConfiguration } from 'chart.js/auto';
|
||||
import { has, setCanvasSize } from '../plugin/utils';
|
||||
import { debounce } from 'lodash';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { isMobile } from '../plugin/use';
|
||||
|
||||
const props = defineProps<{
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { cloneDeep, debounce } from 'lodash';
|
||||
import { cloneDeep, debounce } from 'lodash-es';
|
||||
import { ref } from 'vue';
|
||||
import { getDamageColor } from '../utils';
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
/// <reference path="../types/core.d.ts" />
|
||||
import { message } from 'ant-design-vue';
|
||||
import { MessageApi } from 'ant-design-vue/lib/message';
|
||||
import { isNil } from 'lodash';
|
||||
import { isNil } from 'lodash-es';
|
||||
import { Animation, sleep, TimingFn } from 'mutate-animate';
|
||||
import { ComputedRef, ref } from 'vue';
|
||||
import { EVENT_KEY_CODE_MAP } from './keyCodes';
|
||||
|
@ -38,7 +38,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="tsx">
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { sleep } from 'mutate-animate';
|
||||
import { onMounted, onUnmounted, ref } from 'vue';
|
||||
import EnemyOne from '../components/enemyOne.vue';
|
||||
|
@ -92,7 +92,7 @@ import {
|
||||
RightOutlined,
|
||||
DoubleRightOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import { debounce } from 'lodash';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { downloadCanvasImage, keycode, tip } from '../plugin/utils';
|
||||
import { sleep } from 'mutate-animate';
|
||||
import { KeyCode } from '../plugin/keyCodes';
|
||||
|
@ -19,7 +19,8 @@
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"mota.config.ts"
|
||||
"mota.config.ts",
|
||||
"script/**/*.ts"
|
||||
],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
|
@ -47,14 +47,19 @@ export default defineConfig({
|
||||
'/makeDir': FSHOST,
|
||||
'/moveFile': FSHOST,
|
||||
'/deleteFile': FSHOST,
|
||||
'/reload': FSHOST,
|
||||
'/hotReload': FSHOST,
|
||||
'^/all/.*': {
|
||||
target: FSHOST,
|
||||
changeOrigin: true,
|
||||
rewrite(path) {
|
||||
return path.replace(/^\/all/, '');
|
||||
},
|
||||
},
|
||||
'^/forceTem/.*': {
|
||||
target: FSHOST,
|
||||
changeOrigin: true,
|
||||
rewrite(path) {
|
||||
return path.replace(/^\/forceTem/, '');
|
||||
},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
Loading…
Reference in New Issue
Block a user