mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 13:41:03 +00:00 
			
		
		
		
	 f90b24a805
			
		
	
	
		f90b24a805
		
			
		
	
	
	
	
		
			
			We were using `Content-Length` from the server when `Content-Encoding`
was not set (i.e. response was not compressed).
Sadly, in CORS requests accessing headers is restricted, and while
`Content-Length` is enabled by default, `Content-Encoding` is not.
This results in the impossibility of knowing if the content was
compressed, unless the server explicitly enabled the encoding header
via `Access-Control-Expose-Headers`.
To keep maximum compatibility we must disable `body_size` completely.
(cherry picked from commit 737ed0f66e)
		
	
			
		
			
				
	
	
		
			133 lines
		
	
	
	
		
			3.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			133 lines
		
	
	
	
		
			3.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| const Preloader = /** @constructor */ function () { // eslint-disable-line no-unused-vars
 | |
| 	function getTrackedResponse(response, load_status) {
 | |
| 		function onloadprogress(reader, controller) {
 | |
| 			return reader.read().then(function (result) {
 | |
| 				if (load_status.done) {
 | |
| 					return Promise.resolve();
 | |
| 				}
 | |
| 				if (result.value) {
 | |
| 					controller.enqueue(result.value);
 | |
| 					load_status.loaded += result.value.length;
 | |
| 				}
 | |
| 				if (!result.done) {
 | |
| 					return onloadprogress(reader, controller);
 | |
| 				}
 | |
| 				load_status.done = true;
 | |
| 				return Promise.resolve();
 | |
| 			});
 | |
| 		}
 | |
| 		const reader = response.body.getReader();
 | |
| 		return new Response(new ReadableStream({
 | |
| 			start: function (controller) {
 | |
| 				onloadprogress(reader, controller).then(function () {
 | |
| 					controller.close();
 | |
| 				});
 | |
| 			},
 | |
| 		}), { headers: response.headers });
 | |
| 	}
 | |
| 
 | |
| 	function loadFetch(file, tracker, fileSize, raw) {
 | |
| 		tracker[file] = {
 | |
| 			total: fileSize || 0,
 | |
| 			loaded: 0,
 | |
| 			done: false,
 | |
| 		};
 | |
| 		return fetch(file).then(function (response) {
 | |
| 			if (!response.ok) {
 | |
| 				return Promise.reject(new Error(`Failed loading file '${file}'`));
 | |
| 			}
 | |
| 			const tr = getTrackedResponse(response, tracker[file]);
 | |
| 			if (raw) {
 | |
| 				return Promise.resolve(tr);
 | |
| 			}
 | |
| 			return tr.arrayBuffer();
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	function retry(func, attempts = 1) {
 | |
| 		function onerror(err) {
 | |
| 			if (attempts <= 1) {
 | |
| 				return Promise.reject(err);
 | |
| 			}
 | |
| 			return new Promise(function (resolve, reject) {
 | |
| 				setTimeout(function () {
 | |
| 					retry(func, attempts - 1).then(resolve).catch(reject);
 | |
| 				}, 1000);
 | |
| 			});
 | |
| 		}
 | |
| 		return func().catch(onerror);
 | |
| 	}
 | |
| 
 | |
| 	const DOWNLOAD_ATTEMPTS_MAX = 4;
 | |
| 	const loadingFiles = {};
 | |
| 	const lastProgress = { loaded: 0, total: 0 };
 | |
| 	let progressFunc = null;
 | |
| 
 | |
| 	const animateProgress = function () {
 | |
| 		let loaded = 0;
 | |
| 		let total = 0;
 | |
| 		let totalIsValid = true;
 | |
| 		let progressIsFinal = true;
 | |
| 
 | |
| 		Object.keys(loadingFiles).forEach(function (file) {
 | |
| 			const stat = loadingFiles[file];
 | |
| 			if (!stat.done) {
 | |
| 				progressIsFinal = false;
 | |
| 			}
 | |
| 			if (!totalIsValid || stat.total === 0) {
 | |
| 				totalIsValid = false;
 | |
| 				total = 0;
 | |
| 			} else {
 | |
| 				total += stat.total;
 | |
| 			}
 | |
| 			loaded += stat.loaded;
 | |
| 		});
 | |
| 		if (loaded !== lastProgress.loaded || total !== lastProgress.total) {
 | |
| 			lastProgress.loaded = loaded;
 | |
| 			lastProgress.total = total;
 | |
| 			if (typeof progressFunc === 'function') {
 | |
| 				progressFunc(loaded, total);
 | |
| 			}
 | |
| 		}
 | |
| 		if (!progressIsFinal) {
 | |
| 			requestAnimationFrame(animateProgress);
 | |
| 		}
 | |
| 	};
 | |
| 
 | |
| 	this.animateProgress = animateProgress;
 | |
| 
 | |
| 	this.setProgressFunc = function (callback) {
 | |
| 		progressFunc = callback;
 | |
| 	};
 | |
| 
 | |
| 	this.loadPromise = function (file, fileSize, raw = false) {
 | |
| 		return retry(loadFetch.bind(null, file, loadingFiles, fileSize, raw), DOWNLOAD_ATTEMPTS_MAX);
 | |
| 	};
 | |
| 
 | |
| 	this.preloadedFiles = [];
 | |
| 	this.preload = function (pathOrBuffer, destPath, fileSize) {
 | |
| 		let buffer = null;
 | |
| 		if (typeof pathOrBuffer === 'string') {
 | |
| 			const me = this;
 | |
| 			return this.loadPromise(pathOrBuffer, fileSize).then(function (buf) {
 | |
| 				me.preloadedFiles.push({
 | |
| 					path: destPath || pathOrBuffer,
 | |
| 					buffer: buf,
 | |
| 				});
 | |
| 				return Promise.resolve();
 | |
| 			});
 | |
| 		} else if (pathOrBuffer instanceof ArrayBuffer) {
 | |
| 			buffer = new Uint8Array(pathOrBuffer);
 | |
| 		} else if (ArrayBuffer.isView(pathOrBuffer)) {
 | |
| 			buffer = new Uint8Array(pathOrBuffer.buffer);
 | |
| 		}
 | |
| 		if (buffer) {
 | |
| 			this.preloadedFiles.push({
 | |
| 				path: destPath,
 | |
| 				buffer: pathOrBuffer,
 | |
| 			});
 | |
| 			return Promise.resolve();
 | |
| 		}
 | |
| 		return Promise.reject(new Error('Invalid object for preloading'));
 | |
| 	};
 | |
| };
 |