diff --git a/gulp/preloader/preloader.js b/gulp/preloader/preloader.js index b23e5000..553cd9c1 100644 --- a/gulp/preloader/preloader.js +++ b/gulp/preloader/preloader.js @@ -5,7 +5,32 @@ // Catch load errors function errorHandler(event, source, lineno, colno, error) { - console.error("👀 Init Error:", event, source, lineno, colno, error); + if (("" + event).indexOf("Script error.") >= 0) { + console.warn("Thirdparty script error:", event); + return; + } + + if (("" + event).indexOf("NS_ERROR_FAILURE") >= 0) { + console.warn("Firefox NS_ERROR_FAILURE error:", event); + return; + } + + if (("" + event).indexOf("Cannot read property 'postMessage' of null") >= 0) { + console.warn("Safari can not read post message error:", event); + return; + } + + if (("" + event).indexOf("Possible side-effect in debug-evaluate") >= 0) { + console.warn("Chrome debug-evaluate error:", event); + return; + } + + if (("" + source).indexOf("shapez.io") < 0) { + console.warn("Thirdparty error:", event); + return; + } + + console.error("👀 App Error:", event, source, lineno, colno, error); var element = document.createElement("div"); element.id = "ll_preload_error"; @@ -38,9 +63,11 @@ } document.documentElement.appendChild(element); + + window.APP_ERROR_OCCURED = true; } - window.addEventListener("error", errorHandler); + window.onerror = errorHandler; function expectJsParsed() { if (!callbackDone) { @@ -81,10 +108,10 @@ xhr.responseType = "arraybuffer"; xhr.onprogress = function (ev) { if (ev.lengthComputable) { - console.log(ev.total); progressHandler(ev.loaded / ev.total); } else { - progressHandler(Math.min(1, ev.loaded / 1250000)); + // Hardcoded length + progressHandler(Math.min(1, ev.loaded / 2349009)); } }; diff --git a/src/js/core/background_resources_loader.js b/src/js/core/background_resources_loader.js index f7e0654a..e93c73d4 100644 --- a/src/js/core/background_resources_loader.js +++ b/src/js/core/background_resources_loader.js @@ -33,6 +33,12 @@ const INGAME_ASSETS = { const LOADER_TIMEOUT_PER_RESOURCE = 180000; +// Cloudflare does not send content-length headers with brotli compression, +// so store the actual (compressed) file sizes so we can show a progress bar. +const HARDCODED_FILE_SIZES = { + "async-resources.css": 2216145, +}; + export class BackgroundResourcesLoader { /** * @@ -135,25 +141,24 @@ export class BackgroundResourcesLoader { let progress = 0; this.resourceStateChangedSignal.dispatch({ progress }); let promises = []; + for (let i = 0; i < promiseFunctions.length; i++) { let lastIndividualProgress = 0; const progressHandler = individualProgress => { const delta = clamp(individualProgress) - lastIndividualProgress; - lastIndividualProgress = individualProgress; + lastIndividualProgress = clamp(individualProgress); progress += delta / originalAmount; this.resourceStateChangedSignal.dispatch({ progress }); }; promises.push( - promiseFunctions - .shift()(progressHandler) - .then(() => { - progressHandler(1); - }) + promiseFunctions[i](progressHandler).then(() => { + progressHandler(1); + }) ); } await Promise.all(promises); - logger.log("⏰ Preloaded assets in", Math.round((performance.now() - start) / 1000.0), "ms"); + logger.log("⏰ Preloaded assets in", Math.round(performance.now() - start), "ms"); } /** @@ -189,23 +194,32 @@ export class BackgroundResourcesLoader { const xhr = new XMLHttpRequest(); let notifiedNotComputable = false; - xhr.open("GET", src, true); + const fullUrl = cachebust(src); + xhr.open("GET", fullUrl, true); xhr.responseType = "arraybuffer"; xhr.onprogress = function (ev) { if (ev.lengthComputable) { progressHandler(ev.loaded / ev.total); } else { - if (!notifiedNotComputable) { - notifiedNotComputable = true; - console.warn("Progress not computable:", src, ev); - progressHandler(0); + if (window.location.search.includes("alwaysLogFileSize")) { + console.warn("Progress:", src, ev.loaded); + } + + if (HARDCODED_FILE_SIZES[src]) { + progressHandler(clamp(ev.loaded / HARDCODED_FILE_SIZES[src])); + } else { + if (!notifiedNotComputable) { + notifiedNotComputable = true; + console.warn("Progress not computable:", src, ev.loaded); + progressHandler(0); + } } } }; xhr.onloadend = function () { if (!xhr.status.toString().match(/^2/)) { - reject(src + ": " + xhr.status + " " + xhr.statusText); + reject(fullUrl + ": " + xhr.status + " " + xhr.statusText); } else { if (!notifiedNotComputable) { progressHandler(1); @@ -226,7 +240,7 @@ export class BackgroundResourcesLoader { } internalPreloadCss(src, progressHandler) { - return this.preloadWithProgress(cachebust(src), progressHandler).then(blobSrc => { + return this.preloadWithProgress(src, progressHandler).then(blobSrc => { var styleElement = document.createElement("link"); styleElement.href = blobSrc; styleElement.rel = "stylesheet"; diff --git a/src/js/core/error_handler.js b/src/js/core/error_handler.js deleted file mode 100644 index 61cd9251..00000000 --- a/src/js/core/error_handler.js +++ /dev/null @@ -1,16 +0,0 @@ -export let APPLICATION_ERROR_OCCURED = false; - -/** - * - * @param {Event|string} message - * @param {string} source - * @param {number} lineno - * @param {number} colno - * @param {Error} source - */ -function catchErrors(message, source, lineno, colno, error) { - APPLICATION_ERROR_OCCURED = true; - console.error(message, source, lineno, colno, error); -} - -window.addEventListener("error", catchErrors); diff --git a/src/js/core/loader.js b/src/js/core/loader.js index b134aca9..29afa123 100644 --- a/src/js/core/loader.js +++ b/src/js/core/loader.js @@ -80,15 +80,13 @@ class LoaderImpl { * @returns {Promise} */ internalPreloadImage(key, progressHandler) { - const url = cachebust("res/" + key); - const image = new Image(); - return this.app.backgroundResourceLoader - .preloadWithProgress(url, progress => { + .preloadWithProgress("res/" + key, progress => { progressHandler(progress); }) .then(url => { return new Promise((resolve, reject) => { + const image = new Image(); image.addEventListener("load", () => resolve(image)); image.addEventListener("error", err => reject("Failed to load sprite " + key + ": " + err) diff --git a/src/js/core/state_manager.js b/src/js/core/state_manager.js index 77e934da..70b4b7f4 100644 --- a/src/js/core/state_manager.js +++ b/src/js/core/state_manager.js @@ -4,7 +4,6 @@ import { Application } from "../application"; import { GameState } from "./game_state"; import { createLogger } from "./logging"; -import { APPLICATION_ERROR_OCCURED } from "./error_handler"; import { waitNextFrame, removeAllChildren } from "./utils"; import { MOD_SIGNALS } from "../mods/mod_signals"; @@ -56,7 +55,7 @@ export class StateManager { * @param {string} key State Key */ moveToState(key, payload = {}) { - if (APPLICATION_ERROR_OCCURED) { + if (window.APP_ERROR_OCCURED) { console.warn("Skipping state transition because of application crash"); return; } diff --git a/src/js/globals.d.ts b/src/js/globals.d.ts index fcf1dbd5..f500035f 100644 --- a/src/js/globals.d.ts +++ b/src/js/globals.d.ts @@ -100,6 +100,8 @@ declare interface Window { shapez: any; + APP_ERROR_OCCURED?: boolean; + webkitRequestAnimationFrame(); assert(condition: boolean, failureMessage: string); diff --git a/src/js/main.js b/src/js/main.js index 634ad4d2..31c7dc2e 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -1,6 +1,5 @@ import "./core/polyfills"; import "./core/assert"; -import "./core/error_handler"; import "./mods/modloader"; diff --git a/src/js/states/ingame.js b/src/js/states/ingame.js index 21e29803..26433bbd 100644 --- a/src/js/states/ingame.js +++ b/src/js/states/ingame.js @@ -1,4 +1,3 @@ -import { APPLICATION_ERROR_OCCURED } from "../core/error_handler"; import { GameState } from "../core/game_state"; import { logSection, createLogger } from "../core/logging"; import { waitNextFrame } from "../core/utils"; @@ -419,7 +418,7 @@ export class InGameState extends GameState { * @param {number} dt */ onRender(dt) { - if (APPLICATION_ERROR_OCCURED) { + if (window.APP_ERROR_OCCURED) { // Application somehow crashed, do not do anything return; } @@ -465,7 +464,7 @@ export class InGameState extends GameState { return Promise.resolve(); } - if (APPLICATION_ERROR_OCCURED) { + if (window.APP_ERROR_OCCURED) { logger.warn("skipping save because application crashed"); return Promise.resolve(); }