diff --git a/.gitignore b/.gitignore index 9fe15ab..833f8af 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,5 @@ dist.rar index.cjs !public/swap/*.h5save _bundle -out \ No newline at end of file +out +dist-resource \ No newline at end of file diff --git a/public/project/animates/animates.h5data b/public/project/animates/animates.h5data deleted file mode 100644 index 2b79e84..0000000 Binary files a/public/project/animates/animates.h5data and /dev/null differ diff --git a/public/project/autotiles/autotiles.h5data b/public/project/autotiles/autotiles.h5data deleted file mode 100644 index 81477a6..0000000 Binary files a/public/project/autotiles/autotiles.h5data and /dev/null differ diff --git a/public/project/images/images.h5data b/public/project/images/images.h5data deleted file mode 100644 index 6411605..0000000 Binary files a/public/project/images/images.h5data and /dev/null differ diff --git a/public/project/materials/materials.h5data b/public/project/materials/materials.h5data deleted file mode 100644 index e50574c..0000000 Binary files a/public/project/materials/materials.h5data and /dev/null differ diff --git a/public/project/sounds/sounds.h5data b/public/project/sounds/sounds.h5data deleted file mode 100644 index 1dcb995..0000000 Binary files a/public/project/sounds/sounds.h5data and /dev/null differ diff --git a/public/project/tilesets/tilesets.h5data b/public/project/tilesets/tilesets.h5data deleted file mode 100644 index 7a622c5..0000000 Binary files a/public/project/tilesets/tilesets.h5data and /dev/null differ diff --git a/script/lines.ts b/script/lines.ts index 17ea672..3dcf1d0 100644 --- a/script/lines.ts +++ b/script/lines.ts @@ -1,11 +1,13 @@ import fs from 'fs-extra'; import { extname, resolve } from 'path'; +import { formatSize } from './utils.js'; (async function () { - const dir = process.argv[2] || './src'; + const dir = process.argv.slice(2) || ['./src']; let totalLines = 0; let totalFiles = 0; - const list: Record = {}; + let totalSize = 0; + const list: Record = {}; const ignoreDir = [ 'node_modules', 'floors', @@ -36,32 +38,38 @@ import { extname, resolve } from 'path'; const file = await fs.readFile(resolve(dir, one), 'utf-8'); const lines = file.split('\n').length; const ext = extname(one); - list[ext] ??= [0, 0]; + list[ext] ??= [0, 0, 0]; list[ext][0]++; list[ext][1] += lines; + list[ext][2] += stat.size; totalLines += lines; totalFiles++; + totalSize += stat.size; } } else { await check(resolve(dir, one)); } } }; - await check(dir); + await Promise.all(dir.map(v => check(v))); const sorted = Object.entries(list).sort((a, b) => { return a[1][1] - b[1][1]; }); - for (const [ext, [file, lines]] of sorted) { + for (const [ext, [file, lines, size]] of sorted) { console.log( `${ext.slice(1).padEnd(7, ' ')}files: ${file .toString() - .padEnd(6, ' ')}lines: ${lines}` + .padEnd(6, ' ')}lines: ${lines + .toString() + .padEnd(9, ' ')}size: ${formatSize(size)}` ); } console.log( `\x1b[33mtotal files: ${totalFiles .toString() - .padEnd(6, ' ')}lines: ${totalLines}\x1b[0m` + .padEnd(6, ' ')}lines: ${totalLines + .toString() + .padEnd(9, ' ')}size: ${formatSize(totalSize)}\x1b[0m` ); })(); diff --git a/script/resource.ts b/script/resource.ts index 5925074..ec9b635 100644 --- a/script/resource.ts +++ b/script/resource.ts @@ -3,8 +3,8 @@ import { uniqueSymbol } from './utils.js'; import { extname, resolve } from 'path'; type ResorceType = - | 'bgm' - | 'sound' + | 'bgms' + | 'sounds' | 'autotiles' | 'images' | 'materials' @@ -12,9 +12,8 @@ type ResorceType = | 'animates' | 'fonts'; -let resorceIndex = 0; const compress: ResorceType[] = [ - 'sound', + 'sounds', 'animates', 'autotiles', 'images', @@ -24,13 +23,16 @@ const compress: ResorceType[] = [ const SYMBOL = uniqueSymbol(); const MAX_SIZE = 100 * (1 << 20); +const baseDir = './dist'; let totalSize = 0; +type Stats = fs.Stats & { name?: string }; + export async function splitResorce(compress: boolean = false) { const folder = await fs.stat('./dist'); totalSize = folder.size; - if (totalSize < MAX_SIZE) return; + // if (totalSize < MAX_SIZE) return; await fs.ensureDir('./dist-resource'); await doSplit(compress); @@ -38,16 +40,112 @@ export async function splitResorce(compress: boolean = false) { async function sortDir(dir: string, ext?: string[]) { const path = await fs.readdir(dir); - const stats: fs.Stats[] = []; + const stats: Stats[] = []; for await (const one of path) { if (ext && !ext.includes(extname(one))) continue; const stat = await fs.stat(resolve(dir, one)); if (!stat.isFile()) continue; - stats.push(stat); + const status: Stats = { + ...stat, + name: one + }; + stats.push(status); } return stats.sort((a, b) => b.size - a.size); } -async function doSplit(compress: boolean) {} +async function calResourceSize() { + return ( + await Promise.all( + [ + 'animates', + 'autotiles', + 'bgms', + 'fonts', + 'images', + 'materials', + 'sounds', + 'tilesets' + ].map(v => fs.stat(resolve('./dist/project/', v))) + ) + ).reduce((pre, cur) => pre + cur.size, 0); +} + +async function doSplit(compress: boolean) { + let size = await calResourceSize(); + await fs.emptyDir('./dist-resource'); + const priority: ResorceType[] = [ + 'materials', + 'tilesets', + 'autotiles', + 'animates', + 'images', + 'sounds', + 'fonts', + 'bgms' + ]; + const dirInfo: Record = Object.fromEntries( + await Promise.all( + priority.map(async v => [ + v, + await sortDir(resolve(baseDir, 'project', v)) + ]) + ) + ); + + let currSize = 0; + const split = async (index: number): Promise => { + size -= currSize; + currSize = 0; + + const cut: string[] = []; + + (() => { + const mapped: ResorceType[] = []; + while (1) { + const toCut = priority.find( + v => dirInfo[v].length > 0 && !mapped.includes(v) + ); + if (!toCut) return; + + mapped.push(toCut); + + let pass = 0; + while (1) { + const stats = dirInfo[toCut]; + const stat = stats[pass]; + if (!stat) { + break; + } + if (currSize + stat.size >= MAX_SIZE) { + pass++; + continue; + } + cut.push(`${toCut}/${stat.name}`); + stats.splice(pass, 1); + currSize += stat.size; + } + } + })(); + + const dir = `./dist-resource/${index}`; + await fs.ensureDir(dir); + await Promise.all(priority.map(v => fs.ensureDir(resolve(dir, v)))); + + await Promise.all( + cut.map(v => + fs.move( + resolve('./dist/project', v), + resolve(dir, `${v}-${SYMBOL}`) + ) + ) + ); + + if (Object.values(dirInfo).every(v => v.length === 0)) return; + else return split(index + 1); + }; + + await split(0); +} diff --git a/script/utils.ts b/script/utils.ts index 2517d98..f70e6eb 100644 --- a/script/utils.ts +++ b/script/utils.ts @@ -1,3 +1,13 @@ export function uniqueSymbol() { return Math.ceil(Math.random() * 0xefffffff + 0x10000000).toString(16); } + +export function formatSize(size: number) { + return size < 1 << 10 + ? `${size.toFixed(2)}B` + : size < 1 << 20 + ? `${(size / (1 << 10)).toFixed(2)}KB` + : size < 1 << 30 + ? `${(size / (1 << 20)).toFixed(2)}MB` + : `${(size / (1 << 30)).toFixed(2)}GB`; +} diff --git a/src/core/common/disposable.ts b/src/core/common/disposable.ts new file mode 100644 index 0000000..2187a8f --- /dev/null +++ b/src/core/common/disposable.ts @@ -0,0 +1,36 @@ +import { EmitableEvent, EventEmitter } from './eventEmitter'; + +interface DisposableEvent extends EmitableEvent { + active: (value: T) => void; + dispose: (value: T) => void; +} + +export class Disposable extends EventEmitter> { + protected _data: T; + set data(value: T | null) { + if (value !== null) this._data = value; + } + get data(): T | null { + if (!this.activated) { + return null; + } + return this._data; + } + + protected activated: boolean = false; + + constructor(data: T) { + super(); + this._data = data; + } + + active() { + this.activated = true; + this.emit('active', this._data); + } + + dispose() { + this.activated = false; + this.emit('dispose', this._data); + } +} diff --git a/src/core/common/eventEmitter.ts b/src/core/common/eventEmitter.ts new file mode 100644 index 0000000..f52db37 --- /dev/null +++ b/src/core/common/eventEmitter.ts @@ -0,0 +1,73 @@ +export interface EmitableEvent { + [event: string]: (...params: any) => any; +} + +interface Listener any> { + fn: T; + once?: boolean; +} + +interface ListenerOptions { + once: boolean; +} + +export class EventEmitter { + private events: { + [P in keyof T]?: Listener[]; + } = {}; + + /** + * 监听某个事件 + * @param event 要监听的事件类型 + * @param fn 触发事件时执行的函数 + * @param options 监听选项 + */ + on( + event: K, + fn: T[K], + options?: Partial + ) { + this.events[event] ??= []; + this.events[event]?.push({ + fn, + once: options?.once + }); + } + + /** + * 取消监听某个事件 + * @param event 要取消监听的事件类型 + * @param fn 要取消监听的函数 + */ + off(event: K, fn: T[K]) { + const index = this.events[event]?.findIndex(v => v.fn === fn); + if (index === -1 || index === void 0) return; + this.events[event]?.splice(index, 1); + } + + /** + * 监听事件,并只触发一次 + * @param event 要监听的事件 + * @param fn 监听函数 + */ + once(event: K, fn: T[K]) { + this.on(event, fn, { once: true }); + } + + /** + * 触发某个事件 + * @param event 要触发的事件类型 + * @param params 传入的参数 + */ + emit(event: K, ...params: Parameters) { + const events = (this.events[event] ??= []); + for (let i = 0; i < events.length; i++) { + const e = events[i]; + e.fn(...(params as any)); + if (e.once) { + events.splice(i, 1); + i--; + } + } + } +} diff --git a/src/data/settings.json b/src/data/settings.json index f6bdaba..f8749fe 100644 --- a/src/data/settings.json +++ b/src/data/settings.json @@ -76,7 +76,7 @@ "text": "优化加载", "desc": [ "开启后游戏将对加载进行优化,缩短进入游戏时的加载时长,而在游戏中对资源进行部分性按需加载,从而对加载进行优化。", - "该设置不会影响你的正常游戏。", + "该设置不会影响你的正常游戏,但如果网络环境较差,可能会导致楼层转换时间明显变长。", "
", "
", "注:修改后刷新页面起效。" diff --git a/src/ui/settings.vue b/src/ui/settings.vue index f566dba..239e05e 100644 --- a/src/ui/settings.vue +++ b/src/ui/settings.vue @@ -130,7 +130,8 @@ const settings: Record> = { useFixed, autoLocate, antiAliasing, - fullscreen + fullscreen, + betterLoad: ref(false) }; const ignore: (keyof Settings)[] = ['fullscreen'];