UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

168 lines (121 loc) 4.47 kB
import { noop } from "../../../core/function/noop.js"; import { Asset } from "../Asset.js"; import { CrossOriginConfig } from "../CORS/CrossOriginConfig.js"; import { CrossOriginKind } from "../CORS/CrossOriginKind.js"; import { AssetLoader } from "./AssetLoader.js"; /** * * @param {Response} input * @param {function} progress * @return {Response} */ function observeResponseProgress(input, progress) { let response = input // Workaround: Checking if response.body === undefined for Alipay browser if (typeof ReadableStream === 'undefined' || response.body === undefined || response.body.getReader === undefined) { return response; } // Nginx needs X-File-Size check // https://serverfault.com/questions/482875/why-does-nginx-remove-content-length-header-for-chunked-content const contentLength = response.headers.get('Content-Length') || response.headers.get('X-File-Size'); const total = contentLength ? parseInt(contentLength) : 0; let loaded = 0; // periodically read data into the new stream tracking while download progress const stream_prototype = { type: "bytes", start(controller) { /** * @type {ReadableStreamDefaultReader<Uint8Array>} */ const reader = response.body.getReader(); pump(); function pump() { reader.read().then(({ done, value }) => { if (done) { // no more data, we're done controller.close(); return; } loaded += value.byteLength; progress(loaded, total); controller.enqueue(value); pump(); }); } } }; /** * @type {ReadableStream} */ let stream; try { stream = new ReadableStream(stream_prototype); response = new Response(stream); } catch (e) { /* Workaround for Safari bug: "TypeError: ReadableByteStreamController is not implemented" By not wrapping the response we lose the ability to track progress, but that's not a critical issue in most cases */ console.warn(e); } return response; } export class ArrayBufferLoader extends AssetLoader { /** * * @param {"auto"|"low"|"high"} fetch_priority */ constructor({ fetch_priority = "auto" } = {}) { super(); /** * * @type {"auto"|"low"|"high"} * @private */ this.__fetch_priority = fetch_priority; } async load( scope, path, success, failure = console.error, progress = noop ) { const coc = this.assetManager !== null ? this.assetManager.crossOriginConfig : CrossOriginConfig.default; const headers = new Headers(); const request = new Request(path, { credentials: coc.kind === CrossOriginKind.UseCredentials ? 'include' : 'same-origin', headers: headers }); if (typeof request.priority !== "undefined") { // priority API is present request.priority = this.__fetch_priority; } let response = await fetch(request); const response_status = response.status; if (response_status !== 200 && response_status !== 0) { throw Error(`fetch for "${response.url}" responded with ${response_status}: ${response.statusText}`); } // Some browsers return HTTP Status 0 when using non-http protocol // e.g. 'file://' or 'data://'. Handle as success. if (response_status === 0) { console.warn('HTTP Status 0 received.'); } try { response = observeResponseProgress(response, progress); } catch (e) { console.warn('Failed to wrap response'); } const arrayBuffer = await response.arrayBuffer(); const asset = new Asset( function () { return arrayBuffer; }, arrayBuffer.byteSize ); success(asset); return asset; } }