diff --git a/package.json b/package.json index c39a248..000963d 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "ant-design-vue": "^3.2.20", "axios": "^1.4.0", "chart.js": "^4.3.0", + "jszip": "^3.10.1", "lodash-es": "^4.17.21", "lz-string": "^1.5.0", "mutate-animate": "^1.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eb81504..0781907 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,6 +13,9 @@ dependencies: chart.js: specifier: ^4.3.0 version: 4.3.0 + jszip: + specifier: ^3.10.1 + version: 3.10.1 lodash-es: specifier: ^4.17.21 version: 4.17.21 @@ -2605,7 +2608,6 @@ packages: /core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - dev: true /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} @@ -3184,6 +3186,10 @@ packages: dev: true optional: true + /immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + dev: false + /imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -3212,7 +3218,6 @@ packages: /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - dev: true /ip@2.0.0: resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==} @@ -3367,7 +3372,6 @@ packages: /isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - dev: true /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -3409,6 +3413,15 @@ packages: graceful-fs: 4.2.11 dev: true + /jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + dev: false + /kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -3448,6 +3461,12 @@ packages: - supports-color dev: true + /lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + dependencies: + immediate: 3.0.6 + dev: false + /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true @@ -3892,6 +3911,10 @@ packages: aggregate-error: 3.1.0 dev: true + /pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + dev: false + /pako@2.1.0: resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} dev: true @@ -3956,7 +3979,6 @@ packages: /process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - dev: true /promise-inflight@1.0.1: resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} @@ -4050,7 +4072,6 @@ packages: safe-buffer: 5.1.2 string_decoder: 1.1.1 util-deprecate: 1.0.2 - dev: true /readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} @@ -4204,7 +4225,6 @@ packages: /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: true /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -4253,6 +4273,10 @@ packages: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} dev: true + /setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + dev: false + /shallow-equal@1.2.1: resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==} dev: false @@ -4368,7 +4392,6 @@ packages: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: safe-buffer: 5.1.2 - dev: true /string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -4680,7 +4703,6 @@ packages: /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - dev: true /uuid@2.0.3: resolution: {integrity: sha512-FULf7fayPdpASncVy4DLh3xydlXEJJpvIELjYjNeQWYUZ9pclcpvCZSr2gkmN2FrrGcI7G/cJsIEwk5/8vfXpg==} diff --git a/script/resource.ts b/script/resource.ts index 7952181..b01eef9 100644 --- a/script/resource.ts +++ b/script/resource.ts @@ -175,7 +175,7 @@ async function rewriteMain(sourceIndex: Record) { .replace(/this\.USE_RESOURCE\s*\=\s*false/, 'this.USE_RESOURCE = true') .replace( /this\.RESOURCE_URL\s*\=\s*'.*'/, - "this.RESOURCE_URL = '/games/HumanBreakRes/'" + "this.RESOURCE_URL = '/games/HumanBreakRes'" ) .replace( /this\.RESOURCE_SYMBOL\s*\=\s*'.*'/, diff --git a/src/core/loader/resource.ts b/src/core/loader/resource.ts index 5768fa6..a448e92 100644 --- a/src/core/loader/resource.ts +++ b/src/core/loader/resource.ts @@ -1,12 +1,16 @@ import axios, { AxiosResponse } from 'axios'; import { Disposable } from '../common/disposable'; import { ensureArray } from '../../plugin/game/utils'; +import { has } from '../../plugin/utils'; +import JSZip from 'jszip'; +import { EmitableEvent, EventEmitter } from '../common/eventEmitter'; interface ResourceData { image: HTMLImageElement; arraybuffer: ArrayBuffer; text: string; json: any; + zip: ZippedResource; } type ResourceType = keyof ResourceData; @@ -25,20 +29,43 @@ export class Resource< super(resource); this.data = this.resolveUrl(resource); this.format = type; + this.on('active', this.load); } + /** + * 解析资源url + * @param resource 资源字符串 + * @returns 解析出的资源url + */ protected resolveUrl(resource: string) { const resolve = resource.split('.'); const type = resolve[0]; - const name = resolve.slice(1).join('.'); - return resource; + const name = resolve.slice(1, -1).join('.'); + const ext = '.' + resolve.at(-1); + + if (!main.USE_RESOURCE) { + return `/games/${core.data.firstData.name}/project/${type}/${name}${ext}`; + } + + const base = main.RESOURCE_URL; + const indexes = main.RESOURCE_INDEX; + const symbol = main.RESOURCE_SYMBOL; + + if (has(indexes[`${type}.*`])) { + const i = indexes[`${type}.*`]; + return `${base}${i}/${type}/${name}-${symbol}${ext}`; + } else { + const i = indexes[`${type}.${name}${ext}`]; + const index = has(i) ? i : 0; + return `${base}${index}/${type}/${name}-${symbol}${ext}`; + } } /** * 加载资源 */ - protected async load() { + protected load() { const data = this.data; if (!data) { throw new Error(`Unexpected null of url in loading resource.`); @@ -53,11 +80,23 @@ export class Resource< res('@imageLoaded'); }); }); - } else { + } else if ( + this.format === 'json' || + this.format === 'text' || + this.format === 'arraybuffer' + ) { this.request = axios .get(data, { responseType: this.format }) .then(v => { - this.resource = v; + this.resource = v.data; + this.loaded = true; + return v; + }); + } else if (this.format === 'zip') { + this.request = axios + .get(data, { responseType: 'arraybuffer' }) + .then(v => { + this.resource = new ZippedResource(v.data); this.loaded = true; return v; }); @@ -68,15 +107,35 @@ export class Resource< * 获取资源,如果还没加载会等待加载完毕再获取 */ async getData(): Promise { + if (!this.activated) return null; if (this.loaded) return this.resource ?? null; else { + if (!this.request) this.load(); await this.request; return this.resource ?? null; } } } -class ReosurceStore extends Map { +interface ZippedEvent extends EmitableEvent { + ready: (data: JSZip) => void; +} + +export class ZippedResource extends EventEmitter { + zip: Promise; + data?: JSZip; + + constructor(buffer: ArrayBuffer) { + super(); + this.zip = JSZip.loadAsync(buffer).then(v => { + this.emit('ready', v); + this.data = v; + return v; + }); + } +} + +class ResourceStore extends Map { active(key: string[] | string) { const keys = ensureArray(key); keys.forEach(v => this.get(v)?.active()); @@ -115,10 +174,10 @@ class ReosurceStore extends Map { declare global { interface Window { /** 游戏资源 */ - gameResource: ReosurceStore; + gameResource: ResourceStore; } /** 游戏资源 */ - const gameResource: ReosurceStore; + const gameResource: ResourceStore; } -window.gameResource = new ReosurceStore(); +window.gameResource = new ResourceStore(); diff --git a/src/types/core.d.ts b/src/types/core.d.ts index 57ba984..76d0139 100644 --- a/src/types/core.d.ts +++ b/src/types/core.d.ts @@ -1256,6 +1256,11 @@ interface Main extends MainData { */ readonly __VERSION_CODE__: number; + readonly RESOURCE_INDEX: Record; + readonly RESOURCE_URL: string; + readonly RESOURCE_SYMBOL: string; + readonly USE_RESOURCE: boolean; + /** * 初始化游戏 * @param mode 初始化游戏的模式,游玩还是编辑器