mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-01-18 20:09:27 +08:00
重写服务器
This commit is contained in:
parent
e4c18c0766
commit
0edbf05e9e
1
.gitignore
vendored
1
.gitignore
vendored
@ -33,3 +33,4 @@ dist.rar
|
|||||||
*.h5route
|
*.h5route
|
||||||
index.cjs
|
index.cjs
|
||||||
!public/swap/*.h5save
|
!public/swap/*.h5save
|
||||||
|
_bundle
|
@ -30,10 +30,9 @@
|
|||||||
1. 首先请确保你安装了`node.js`与`pnpm`
|
1. 首先请确保你安装了`node.js`与`pnpm`
|
||||||
2. 将项目拉到你的设备上
|
2. 将项目拉到你的设备上
|
||||||
3. 运行`pnpm i`以安装所有依赖包
|
3. 运行`pnpm i`以安装所有依赖包
|
||||||
4. 在`public`目录运行`node server.cjs`以启动样板热重载服务
|
4. 在根目录运行`pnpm run dev`以启动`vite`服务和样板的`http`服务与热重载服务
|
||||||
5. 在根目录运行`pnpm run dev`以启动`vite`服务
|
5. 打开`vite`提供的网址即可进入游戏
|
||||||
6. 打开`vite`提供的网址即可进入游戏
|
6. 打开样板服务提供的网址即可进入编辑器
|
||||||
7. 打开样板热重载服务提供的网址即可进入编辑器
|
|
||||||
|
|
||||||
## 构建说明
|
## 构建说明
|
||||||
|
|
||||||
|
38
package.json
38
package.json
@ -4,7 +4,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "ts-node-esm script/dev.ts",
|
||||||
"build": "vue-tsc && vite build && ts-node-esm script/build.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-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",
|
"build-local": "vue-tsc && vite build --base=/ && ts-node-esm script/build.ts 1",
|
||||||
@ -16,41 +16,45 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons-vue": "^6.1.0",
|
"@ant-design/icons-vue": "^6.1.0",
|
||||||
"ant-design-vue": "^3.2.17",
|
"ant-design-vue": "^3.2.20",
|
||||||
"axios": "^1.3.5",
|
"axios": "^1.4.0",
|
||||||
"chart.js": "^4.2.1",
|
"chart.js": "^4.3.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"lz-string": "^1.5.0",
|
"lz-string": "^1.5.0",
|
||||||
"mutate-animate": "^1.1.1",
|
"mutate-animate": "^1.1.1",
|
||||||
"three": "^0.149.0",
|
"three": "^0.149.0",
|
||||||
"vue": "^3.2.47"
|
"vue": "^3.3.1",
|
||||||
|
"ws": "^8.13.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "^7.21.0",
|
"@babel/cli": "^7.21.5",
|
||||||
"@babel/core": "^7.21.4",
|
"@babel/core": "^7.21.8",
|
||||||
"@babel/preset-env": "^7.21.4",
|
"@babel/preset-env": "^7.21.5",
|
||||||
"@rollup/plugin-babel": "^6.0.3",
|
"@rollup/plugin-babel": "^6.0.3",
|
||||||
|
"@rollup/plugin-node-resolve": "^15.0.2",
|
||||||
"@rollup/plugin-terser": "^0.4.1",
|
"@rollup/plugin-terser": "^0.4.1",
|
||||||
"@rollup/plugin-typescript": "^11.1.0",
|
"@rollup/plugin-typescript": "^11.1.0",
|
||||||
"@types/babel__core": "^7.20.0",
|
"@types/babel__core": "^7.20.0",
|
||||||
"@types/fontmin": "^0.9.0",
|
"@types/fontmin": "^0.9.0",
|
||||||
"@types/fs-extra": "^9.0.13",
|
"@types/fs-extra": "^9.0.13",
|
||||||
"@types/lodash": "^4.14.194",
|
"@types/lodash-es": "^4.17.7",
|
||||||
"@types/node": "^18.15.11",
|
"@types/node": "^18.16.7",
|
||||||
"@vitejs/plugin-legacy": "^4.0.2",
|
"@types/ws": "^8.5.4",
|
||||||
"@vitejs/plugin-vue": "^4.1.0",
|
"@vitejs/plugin-legacy": "^4.0.3",
|
||||||
|
"@vitejs/plugin-vue": "^4.2.2",
|
||||||
"@vitejs/plugin-vue-jsx": "^3.0.1",
|
"@vitejs/plugin-vue-jsx": "^3.0.1",
|
||||||
|
"chokidar": "^3.5.3",
|
||||||
"compressing": "^1.9.0",
|
"compressing": "^1.9.0",
|
||||||
"fontmin": "^0.9.9",
|
"fontmin": "^0.9.9",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
"less": "^4.1.3",
|
"less": "^4.1.3",
|
||||||
"rollup": "^3.20.2",
|
"rollup": "^3.21.6",
|
||||||
"terser": "^5.16.9",
|
"terser": "^5.17.3",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
"unplugin-vue-components": "^0.22.12",
|
"unplugin-vue-components": "^0.22.12",
|
||||||
"vite": "^4.2.1",
|
"vite": "^4.3.5",
|
||||||
"vue-tsc": "^1.2.0"
|
"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) {
|
if (main.pluginUseCompress) {
|
||||||
await main.loadScript(`project/plugin.min.js?v=${main.version}`);
|
await main.loadScript(`project/plugin.min.js?v=${main.version}`);
|
||||||
} else {
|
} else {
|
||||||
|
if (main.mode === 'play') {
|
||||||
await main.loadScript(`src/plugin/game/index.js`, true);
|
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 { getCriticalDamage, getDefDamage } from '../plugin/ui/book';
|
||||||
import Chart, { ChartConfiguration } from 'chart.js/auto';
|
import Chart, { ChartConfiguration } from 'chart.js/auto';
|
||||||
import { has, setCanvasSize } from '../plugin/utils';
|
import { has, setCanvasSize } from '../plugin/utils';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash-es';
|
||||||
import { isMobile } from '../plugin/use';
|
import { isMobile } from '../plugin/use';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { cloneDeep, debounce } from 'lodash';
|
import { cloneDeep, debounce } from 'lodash-es';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { getDamageColor } from '../utils';
|
import { getDamageColor } from '../utils';
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
/// <reference path="../types/core.d.ts" />
|
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import { MessageApi } from 'ant-design-vue/lib/message';
|
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 { Animation, sleep, TimingFn } from 'mutate-animate';
|
||||||
import { ComputedRef, ref } from 'vue';
|
import { ComputedRef, ref } from 'vue';
|
||||||
import { EVENT_KEY_CODE_MAP } from './keyCodes';
|
import { EVENT_KEY_CODE_MAP } from './keyCodes';
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash-es';
|
||||||
import { sleep } from 'mutate-animate';
|
import { sleep } from 'mutate-animate';
|
||||||
import { onMounted, onUnmounted, ref } from 'vue';
|
import { onMounted, onUnmounted, ref } from 'vue';
|
||||||
import EnemyOne from '../components/enemyOne.vue';
|
import EnemyOne from '../components/enemyOne.vue';
|
||||||
|
@ -92,7 +92,7 @@ import {
|
|||||||
RightOutlined,
|
RightOutlined,
|
||||||
DoubleRightOutlined
|
DoubleRightOutlined
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash-es';
|
||||||
import { downloadCanvasImage, keycode, tip } from '../plugin/utils';
|
import { downloadCanvasImage, keycode, tip } from '../plugin/utils';
|
||||||
import { sleep } from 'mutate-animate';
|
import { sleep } from 'mutate-animate';
|
||||||
import { KeyCode } from '../plugin/keyCodes';
|
import { KeyCode } from '../plugin/keyCodes';
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"src/**/*.d.ts",
|
"src/**/*.d.ts",
|
||||||
"src/**/*.tsx",
|
"src/**/*.tsx",
|
||||||
"src/**/*.vue",
|
"src/**/*.vue",
|
||||||
"mota.config.ts"
|
"mota.config.ts",
|
||||||
|
"script/**/*.ts"
|
||||||
],
|
],
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
}
|
}
|
||||||
|
@ -47,14 +47,19 @@ export default defineConfig({
|
|||||||
'/makeDir': FSHOST,
|
'/makeDir': FSHOST,
|
||||||
'/moveFile': FSHOST,
|
'/moveFile': FSHOST,
|
||||||
'/deleteFile': FSHOST,
|
'/deleteFile': FSHOST,
|
||||||
'/reload': FSHOST,
|
|
||||||
'/hotReload': FSHOST,
|
|
||||||
'^/all/.*': {
|
'^/all/.*': {
|
||||||
target: FSHOST,
|
target: FSHOST,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite(path) {
|
rewrite(path) {
|
||||||
return path.replace(/^\/all/, '');
|
return path.replace(/^\/all/, '');
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
'^/forceTem/.*': {
|
||||||
|
target: FSHOST,
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite(path) {
|
||||||
|
return path.replace(/^\/forceTem/, '');
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
Loading…
Reference in New Issue
Block a user