/* * Copyright (c) 2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ const eagruntimeImpl = { WASMGCBufferAllocator: {}, platformApplication: {}, platformAssets: {}, platformAudio: {}, platformFilesystem: {}, platformInput: {}, platformNetworking: {}, platformOpenGL: {}, platformRuntime: {}, platformScreenRecord: {}, platformVoiceClient: {}, platformWebRTC: {}, platformWebView: {}, clientPlatformSingleplayer: {}, serverPlatformSingleplayer: {} }; /** @type {WebAssembly.Module} */ var classesWASMModule = null; /** @type {WebAssembly.Module} */ var classesDeobfWASMModule = null; /** @type {Int8Array} */ var classesTEADBG = null; /** @type {function(Array):Array|null} */ var deobfuscatorFunc = null; /** @type {Array} */ var epkFileList = null; /** @type {string|null} */ var splashURL = null; /** @type {string|null} */ var pressAnyKeyURL = null; /** @type {string|null} */ var crashURL = null; /** @type {string|null} */ var faviconURL = null; /** @type {Object} */ var eaglercraftXOpts = null; /** @type {string|null} */ var eagRuntimeJSURL = null; /** @type {HTMLElement} */ var rootElement = null; /** @type {HTMLElement} */ var parentElement = null; /** @type {HTMLCanvasElement} */ var canvasElement = null; /** @type {WebGL2RenderingContext} */ var webglContext = null; /** @type {boolean} */ var webglExperimental = false; /** @type {number} */ var webglGLESVer = 0; /** @type {AudioContext} */ var audioContext = null; /** @type {WebAssembly.Memory} */ var heapMemory = null; /** @type {ArrayBuffer} */ var heapArrayBuffer = null; /** @type {Uint8Array} */ var heapU8Array = null; /** @type {Int8Array} */ var heapI8Array = null; /** @type {Uint16Array} */ var heapU16Array = null; /** @type {Int16Array} */ var heapI16Array = null; /** @type {Int32Array} */ var heapI32Array = null; /** @type {Uint32Array} */ var heapU32Array = null; /** @type {Float32Array} */ var heapF32Array = null; /** @type {boolean} */ var isLikelyMobileBrowser = false; /** @type {function(string, !ArrayBuffer)|null} */ var serverLANPeerPassIPCFunc = null; /** @type {function(string, !ArrayBuffer)|null} */ var sendIPCPacketFunc = null; /** @type {boolean} */ var isCrashed = false; /** @type {Array} */ const crashReportStrings = []; /** @type {function()|null} */ var removeEventHandlers = null; const runtimeOpts = { localStorageNamespace: "_eaglercraftX", openDebugConsoleOnLaunch: false, fixDebugConsoleUnloadListener: false, forceWebViewSupport: false, enableWebViewCSP: true, forceWebGL1: false, forceWebGL2: false, allowExperimentalWebGL1: true, useWebGLExt: true, useDelayOnSwap: false }; function setupRuntimeOpts() { if(typeof eaglercraftXOpts["localStorageNamespace"] === "string") runtimeOpts.localStorageNamespace = eaglercraftXOpts["localStorageNamespace"]; if(typeof eaglercraftXOpts["openDebugConsoleOnLaunch"] === "boolean") runtimeOpts.openDebugConsoleOnLaunch = eaglercraftXOpts["openDebugConsoleOnLaunch"]; if(typeof eaglercraftXOpts["fixDebugConsoleUnloadListener"] === "boolean") runtimeOpts.fixDebugConsoleUnloadListener = eaglercraftXOpts["fixDebugConsoleUnloadListener"]; if(typeof eaglercraftXOpts["forceWebViewSupport"] === "boolean") runtimeOpts.forceWebViewSupport = eaglercraftXOpts["forceWebViewSupport"]; if(typeof eaglercraftXOpts["enableWebViewCSP"] === "boolean") runtimeOpts.enableWebViewCSP = eaglercraftXOpts["enableWebViewCSP"]; if(typeof eaglercraftXOpts["forceWebGL1"] === "boolean") runtimeOpts.forceWebGL1 = eaglercraftXOpts["forceWebGL1"]; if(typeof eaglercraftXOpts["forceWebGL2"] === "boolean") runtimeOpts.forceWebGL2 = eaglercraftXOpts["forceWebGL2"]; if(typeof eaglercraftXOpts["allowExperimentalWebGL1"] === "boolean") runtimeOpts.allowExperimentalWebGL1 = eaglercraftXOpts["allowExperimentalWebGL1"]; if(typeof eaglercraftXOpts["useWebGLExt"] === "boolean") runtimeOpts.useWebGLExt = eaglercraftXOpts["useWebGLExt"]; if(typeof eaglercraftXOpts["useDelayOnSwap"] === "boolean") runtimeOpts.useDelayOnSwap = eaglercraftXOpts["useDelayOnSwap"]; } /** * @return {!Promise} */ async function initializeContext() { setupRuntimeOpts(); currentRedirectorFunc = addLogMessageImpl; window.__curEaglerX188UnloadListenerCB = function() { //TODO: Autosave somehow? }; if(window.__isEaglerX188UnloadListenerSet !== "yes") { window.onbeforeunload = function(evt) { if(window.__curEaglerX188UnloadListenerCB) { window.__curEaglerX188UnloadListenerCB(); } return false; }; window.__isEaglerX188UnloadListenerSet = "yes"; } eagInfo("Initializing EagRuntime JS context..."); await initializePlatfRuntime(); initializePlatfApplication(eagruntimeImpl.platformApplication); initializePlatfScreenRecord(eagruntimeImpl.platformScreenRecord); initializePlatfVoiceClient(eagruntimeImpl.platformVoiceClient); initializePlatfWebRTC(eagruntimeImpl.platformWebRTC); initializePlatfWebView(eagruntimeImpl.platformWebView); initializeClientPlatfSP(eagruntimeImpl.clientPlatformSingleplayer); initializeNoServerPlatfSP(eagruntimeImpl.serverPlatformSingleplayer); rootElement.classList.add("_eaglercraftX_root_element"); rootElement.style.overflow = "hidden"; /** @type {HTMLElement} */ var oldSplash = null; var node; while(node = rootElement.lastChild) { if(!oldSplash) { oldSplash = /** @type {HTMLElement} */ (node); } rootElement.removeChild(node); } parentElement = /** @type {HTMLElement} */ (document.createElement("div")); parentElement.classList.add("_eaglercraftX_wrapper_element"); parentElement.style.position = "relative"; parentElement.style.width = "100%"; parentElement.style.height = "100%"; parentElement.style.overflow = "hidden"; parentElement.style.backgroundColor = "black"; rootElement.appendChild(parentElement); if(oldSplash) { oldSplash.style.position = "absolute"; oldSplash.style.top = "0px"; oldSplash.style.left = "0px"; oldSplash.style.right = "0px"; oldSplash.style.bottom = "0px"; oldSplash.style.zIndex = "2"; oldSplash.classList.add("_eaglercraftX_early_splash_element"); parentElement.appendChild(oldSplash); } await promiseTimeout(10); const d = window.devicePixelRatio; const iw = parentElement.clientWidth; const ih = parentElement.clientHeight; const sw = (d * iw) | 0; const sh = (d * ih) | 0; const canvasW = sw; const canvasH = sh; eagInfo("Initializing audio context"); if(typeof document.exitPointerLock === "function") { var ua = navigator.userAgent; if(ua !== null) { ua = ua.toLowerCase(); isLikelyMobileBrowser = ua.indexOf("mobi") !== -1 || ua.indexOf("tablet") !== -1; }else { isLikelyMobileBrowser = false; } }else { isLikelyMobileBrowser = true; } var audioCtx = null; const createAudioContext = function() { try { audioCtx = new AudioContext(); }catch(ex) { eagStackTrace(ERROR, "Could not initialize audio context", ex); } }; if(isLikelyMobileBrowser || !navigator.userActivation || !navigator.userActivation.hasBeenActive) { const pressAnyKeyImage = /** @type {HTMLElement} */ (document.createElement("div")); pressAnyKeyImage.classList.add("_eaglercraftX_press_any_key_image"); pressAnyKeyImage.style.position = "absolute"; pressAnyKeyImage.style.top = "0px"; pressAnyKeyImage.style.left = "0px"; pressAnyKeyImage.style.right = "0px"; pressAnyKeyImage.style.bottom = "0px"; pressAnyKeyImage.style.width = "100%"; pressAnyKeyImage.style.height = "100%"; pressAnyKeyImage.style.zIndex = "3"; pressAnyKeyImage.style.touchAction = "pan-x pan-y"; pressAnyKeyImage.style.background = "center / contain no-repeat url(\"" + pressAnyKeyURL + "\"), left / 1000000% 100% no-repeat url(\"" + pressAnyKeyURL + "\") white"; pressAnyKeyImage.style.setProperty("image-rendering", "pixelated"); parentElement.appendChild(pressAnyKeyImage); await new Promise(function(resolve, reject) { var resolved = false; var mobilePressAnyKeyScreen; var createAudioContextHandler = function() { if(!resolved) { resolved = true; if(isLikelyMobileBrowser) { parentElement.removeChild(mobilePressAnyKeyScreen); }else { window.removeEventListener("keydown", /** @type {function(Event)} */ (createAudioContextHandler)); parentElement.removeEventListener("mousedown", /** @type {function(Event)} */ (createAudioContextHandler)); parentElement.removeEventListener("touchstart", /** @type {function(Event)} */ (createAudioContextHandler)); } try { createAudioContext(); }catch(ex) { reject(ex); return; } resolve(); } }; if(isLikelyMobileBrowser) { mobilePressAnyKeyScreen = /** @type {HTMLElement} */ (document.createElement("div")); mobilePressAnyKeyScreen.classList.add("_eaglercraftX_mobile_press_any_key"); mobilePressAnyKeyScreen.setAttribute("style", "position:absolute;background-color:white;font-family:sans-serif;top:10%;left:10%;right:10%;bottom:10%;border:5px double black;padding:calc(5px + 7vh) 15px;text-align:center;font-size:20px;user-select:none;z-index:10;"); mobilePressAnyKeyScreen.innerHTML = "

Mobile Browser Detected

" + "

Warning: EaglercraftX WASM-GC requires a lot of memory and may not be stable on most mobile devices!

" + "

" /*+ (allowBootMenu ? "

" : "")*/ + "

(Tablets and phones with large screens work best)

"; mobilePressAnyKeyScreen.querySelector("._eaglercraftX_mobile_launch_client").addEventListener("click", /** @type {function(Event)} */ (createAudioContextHandler)); parentElement.appendChild(mobilePressAnyKeyScreen); }else { window.addEventListener("keydown", /** @type {function(Event)} */ (createAudioContextHandler)); parentElement.addEventListener("mousedown", /** @type {function(Event)} */ (createAudioContextHandler)); parentElement.addEventListener("touchstart", /** @type {function(Event)} */ (createAudioContextHandler)); } }); parentElement.removeChild(pressAnyKeyImage); }else { createAudioContext(); } if(audioCtx) { setCurrentAudioContext(audioCtx, eagruntimeImpl.platformAudio); }else { setNoAudioContext(eagruntimeImpl.platformAudio); } eagInfo("Creating main canvas"); canvasElement = /** @type {HTMLCanvasElement} */ (document.createElement("canvas")); canvasElement.classList.add("_eaglercraftX_canvas_element"); canvasElement.style.width = "100%"; canvasElement.style.height = "100%"; canvasElement.style.zIndex = "1"; canvasElement.style.touchAction = "pan-x pan-y"; canvasElement.style.setProperty("-webkit-touch-callout", "none"); canvasElement.style.setProperty("-webkit-tap-highlight-color", "rgba(255, 255, 255, 0)"); canvasElement.style.setProperty("image-rendering", "pixelated"); canvasElement.width = canvasW; canvasElement.height = canvasH; parentElement.appendChild(canvasElement); await initPlatformInput(eagruntimeImpl.platformInput); eagInfo("Creating WebGL context"); parentElement.addEventListener("webglcontextcreationerror", function(evt) { eagError("[WebGL Error]: {}", evt.statusMessage); }); /** @type {Object} */ const contextCreationHints = { "antialias": false, "depth": false, "powerPreference": "high-performance", "desynchronized": true, "preserveDrawingBuffer": false, "premultipliedAlpha": false, "alpha": false }; /** @type {number} */ var glesVer; /** @type {boolean} */ var experimental = false; /** @type {WebGL2RenderingContext|null} */ var webgl_; if(runtimeOpts.forceWebGL2) { eagInfo("Note: Forcing WebGL 2.0 context"); glesVer = 300; webgl_ = /** @type {WebGL2RenderingContext} */ (canvasElement.getContext("webgl2", contextCreationHints)); if(!webgl_) { showIncompatibleScreen("WebGL 2.0 is not supported on this device!"); return false; } }else { if(runtimeOpts.forceWebGL1) { eagInfo("Note: Forcing WebGL 1.0 context"); glesVer = 200; webgl_ = /** @type {WebGL2RenderingContext} */ (canvasElement.getContext("webgl", contextCreationHints)); if(!webgl_) { if(runtimeOpts.allowExperimentalWebGL1) { experimental = true; webgl_ = /** @type {WebGL2RenderingContext} */ (canvasElement.getContext("experimental-webgl", contextCreationHints)); if(!webgl_) { showIncompatibleScreen("WebGL is not supported on this device!"); return false; } }else { showIncompatibleScreen("WebGL is not supported on this device!"); return false; } } }else { glesVer = 300; webgl_ = /** @type {WebGL2RenderingContext} */ (canvasElement.getContext("webgl2", contextCreationHints)); if(!webgl_) { glesVer = 200; webgl_ = /** @type {WebGL2RenderingContext} */ (canvasElement.getContext("webgl", contextCreationHints)); if(!webgl_) { if(runtimeOpts.allowExperimentalWebGL1) { experimental = true; webgl_ = /** @type {WebGL2RenderingContext} */ (canvasElement.getContext("experimental-webgl", contextCreationHints)); if(!webgl_) { showIncompatibleScreen("WebGL is not supported on this device!"); return false; } }else { showIncompatibleScreen("WebGL is not supported on this device!"); return false; } } } } } if(experimental) { alert("WARNING: Detected \"experimental\" WebGL 1.0 support, certain graphics API features may be missing, and therefore EaglercraftX may malfunction and crash!"); } webglGLESVer = glesVer; webglContext = webgl_; webglExperimental = experimental; setCurrentGLContext(webgl_, glesVer, runtimeOpts.useWebGLExt, eagruntimeImpl.platformOpenGL); eagInfo("OpenGL Version: {}", eagruntimeImpl.platformOpenGL["glGetString"](0x1F02)); eagInfo("OpenGL Renderer: {}", eagruntimeImpl.platformOpenGL["glGetString"](0x1F01)); /** @type {Array} */ const exts = eagruntimeImpl.platformOpenGL["dumpActiveExtensions"](); if(exts.length === 0) { eagInfo("Unlocked the following OpenGL ES extensions: (NONE)"); }else { exts.sort(); eagInfo("Unlocked the following OpenGL ES extensions:"); for(var i = 0; i < exts.length; ++i) { eagInfo(" - {}", exts[i]); } } eagruntimeImpl.platformOpenGL["glClearColor"](0.0, 0.0, 0.0, 1.0); eagruntimeImpl.platformOpenGL["glClear"](0x4000); await promiseTimeout(20); eagInfo("EagRuntime JS context initialization complete"); return true; } async function initializeContextWorker() { setupRuntimeOpts(); /** * @param {string} txt * @param {boolean} err */ currentRedirectorFunc = function(txt, err) { postMessage({ "ch": "~!LOGGER", "txt": txt, "err": err }); }; eagInfo("Initializing EagRuntime worker JS context..."); await initializePlatfRuntime(); initializeNoPlatfApplication(eagruntimeImpl.platformApplication); setNoAudioContext(eagruntimeImpl.platformAudio); initNoPlatformInput(eagruntimeImpl.platformInput); setNoGLContext(eagruntimeImpl.platformOpenGL); initializeNoPlatfScreenRecord(eagruntimeImpl.platformScreenRecord); initializeNoPlatfVoiceClient(eagruntimeImpl.platformVoiceClient); initializeNoPlatfWebRTC(eagruntimeImpl.platformWebRTC); initializeNoPlatfWebView(eagruntimeImpl.platformWebView); initializeNoClientPlatfSP(eagruntimeImpl.clientPlatformSingleplayer); initializeServerPlatfSP(eagruntimeImpl.serverPlatformSingleplayer); eagInfo("EagRuntime worker JS context initialization complete"); } /** * @param {WebAssembly.Memory} mem */ function handleMemoryResized(mem) { heapMemory = mem; heapArrayBuffer = mem.buffer; eagInfo("WebAssembly direct memory resized to {} MiB", ((heapArrayBuffer.byteLength / 1024.0 / 10.24) | 0) * 0.01); heapU8Array = new Uint8Array(heapArrayBuffer); heapI8Array = new Int8Array(heapArrayBuffer); heapU16Array = new Uint16Array(heapArrayBuffer); heapI16Array = new Int16Array(heapArrayBuffer); heapU32Array = new Uint32Array(heapArrayBuffer); heapI32Array = new Int32Array(heapArrayBuffer); heapF32Array = new Float32Array(heapArrayBuffer); } const EVENT_TYPE_INPUT = 0; const EVENT_TYPE_RUNTIME = 1; const EVENT_TYPE_VOICE = 2; const EVENT_TYPE_WEBVIEW = 3; const mainEventQueue = new EaglerLinkedQueue(); /** * @param {number} eventType * @param {number} eventId * @param {*} eventObj */ function pushEvent(eventType, eventId, eventObj) { mainEventQueue.push({ "eventType": ((eventType << 5) | eventId), "eventObj": eventObj, "_next": null }); } let exceptionFrameRegex2 = /.+:wasm-function\[[0-9]+]:0x([0-9a-f]+).*/; /** * @param {string|null} stack * @return {Array} */ function deobfuscateStack(stack) { if(!stack) return null; /** @type {!Array} */ const stackFrames = []; for(let line of stack.split("\n")) { if(deobfuscatorFunc) { const match = exceptionFrameRegex2.exec(line); if(match !== null && match.length >= 2) { const val = parseInt(match[1], 16); if(!isNaN(val)) { try { /** @type {Array} */ const resultList = deobfuscatorFunc([val]); if(resultList.length > 0) { for(let obj of resultList) { stackFrames.push("" + obj["className"] + "." + obj["method"] + "(" + obj["file"] + ":" + obj["line"] + ")"); } continue; } }catch(ex) { } } } } line = line.trim(); if(line.startsWith("at ")) { line = line.substring(3); } stackFrames.push(line); } return stackFrames; } function displayUncaughtCrashReport(error) { const stack = error ? deobfuscateStack(error.stack) : null; const crashContent = "Native Browser Exception\n" + "----------------------------------\n" + " Line: " + ((error && (typeof error.fileName === "string")) ? error.fileName : "unknown") + ":" + ((error && (typeof error.lineNumber === "number")) ? error.lineNumber : "unknown") + ":" + ((error && (typeof error.columnNumber === "number")) ? error.columnNumber : "unknown") + "\n Type: " + ((error && (typeof error.name === "string")) ? error.name : "unknown") + "\n Desc: " + ((error && (typeof error.message === "string")) ? error.message : "null") + "\n----------------------------------\n\n" + "Deobfuscated stack trace:\n at " + (stack ? stack.join("\n at ") : "null") + "\n\nThis exception was not handled by the WASM binary\n"; if(typeof window !== "undefined") { displayCrashReport(crashContent, true); }else if(sendIntegratedServerCrash) { eagError("\n{}", crashContent); try { sendIntegratedServerCrash(crashContent, true); }catch(ex) { console.log(ex); } }else { eagError("\n{}", crashContent); } } /** * @param {string} crashReport * @param {boolean} enablePrint */ function displayCrashReport(crashReport, enablePrint) { eagError("Game crashed!"); var strBefore = "Game Crashed! I have fallen and I can't get up!\n\n" + crashReport + "\n\n"; var strAfter = "eaglercraft.version = \"" + crashReportStrings[0] + "\"\neaglercraft.minecraft = \"" + crashReportStrings[2] + "\"\neaglercraft.brand = \"" + crashReportStrings[1] + "\"\n\n" + addWebGLToCrash() + "\nwindow.eaglercraftXOpts = " + JSON.stringify(eaglercraftXOpts) + "\n\ncurrentTime = " + (new Date()).toLocaleString() + "\n\n" + addDebugNav("userAgent") + addDebugNav("vendor") + addDebugNav("language") + addDebugNav("hardwareConcurrency") + addDebugNav("deviceMemory") + addDebugNav("platform") + addDebugNav("product") + addDebugNavPlugins() + "\n" + addDebug("localStorage") + addDebug("sessionStorage") + addDebug("indexedDB") + "\n" + "rootElement.clientWidth = " + (parentElement ? parentElement.clientWidth : "undefined") + "\nrootElement.clientHeight = " + (parentElement ? parentElement.clientHeight : "undefined") + "\n" + addDebug("innerWidth") + addDebug("innerHeight") + addDebug("outerWidth") + addDebug("outerHeight") + addDebug("devicePixelRatio") + addDebugScreen("availWidth") + addDebugScreen("availHeight") + addDebugScreen("colorDepth") + addDebugScreen("pixelDepth") + "\n" + addDebugLocation("href") + "\n"; var strFinal = strBefore + strAfter; const additionalInfo = []; try { if((typeof eaglercraftXOpts === "object") && (typeof eaglercraftXOpts["hooks"] === "object") && (typeof eaglercraftXOpts["hooks"]["crashReportShow"] === "function")) { eaglercraftXOpts["hooks"]["crashReportShow"](strFinal, function(str) { additionalInfo.push(str); }); } }catch(ex) { eagStackTrace(ERROR, "Uncaught exception invoking crash report hook", ex); } if(!isCrashed) { isCrashed = true; if(additionalInfo.length > 0) { strFinal = strBefore + "Got the following messages from the crash report hook registered in eaglercraftXOpts:\n\n"; for(var i = 0; i < additionalInfo.length; ++i) { strFinal += "----------[ CRASH HOOK ]----------\n" + additionalInfo[i] + "\n----------------------------------\n\n"; } strFinal += strAfter; } var parentEl = parentElement || rootElement; if(!parentEl) { alert("Root element not found, crash report was printed to console"); eagError("\n{}", strFinal); return; } if(enablePrint) { eagError("\n{}", strFinal); } const img = document.createElement("img"); const div = document.createElement("div"); img.setAttribute("style", "z-index:100;position:absolute;top:10px;left:calc(50% - 151px);"); img.src = crashURL; div.setAttribute("style", "z-index:100;position:absolute;top:135px;left:10%;right:10%;bottom:50px;background-color:white;border:1px solid #cccccc;overflow-x:hidden;overflow-y:scroll;overflow-wrap:break-word;white-space:pre-wrap;font: 14px monospace;padding:10px;"); div.classList.add("_eaglercraftX_crash_element"); parentEl.appendChild(img); parentEl.appendChild(div); div.appendChild(document.createTextNode(strFinal)); if(removeEventHandlers) removeEventHandlers(); window.__curEaglerX188UnloadListenerCB = null; }else { eagError(""); eagError("An additional crash report was supressed:"); var s = crashReport.split(/[\r\n]+/); for(var i = 0; i < s.length; ++i) { eagError(" {}", s[i]); } if(additionalInfo.length > 0) { for(var i = 0; i < additionalInfo.length; ++i) { var str2 = additionalInfo[i]; if(str2) { eagError(""); eagError(" ----------[ CRASH HOOK ]----------"); s = str2.split(/[\r\n]+/); for(var i = 0; i < s.length; ++i) { eagError(" {}", s[i]); } eagError(" ----------------------------------"); } } } } } /** * @param {string} msg */ function showIncompatibleScreen(msg) { if(!isCrashed) { isCrashed = true; var parentEl = parentElement || rootElement; eagError("Compatibility error: {}", msg); if(!parentEl) { alert("Compatibility error: " + msg); return; } const img = document.createElement("img"); const div = document.createElement("div"); img.setAttribute("style", "z-index:100;position:absolute;top:10px;left:calc(50% - 151px);"); img.src = crashURL; div.setAttribute("style", "z-index:100;position:absolute;top:135px;left:10%;right:10%;bottom:50px;background-color:white;border:1px solid #cccccc;overflow-x:hidden;overflow-y:scroll;font:18px sans-serif;padding:40px;"); div.classList.add("_eaglercraftX_incompatible_element"); parentEl.appendChild(img); parentEl.appendChild(div); div.innerHTML = "

+ This device is incompatible with Eaglercraft :(

" + "
" + "

Issue:

" + "

" + "

" + "

Current Date: " + (new Date()).toLocaleString() + "

" + "


Things you can try:

" + "
    " + "
  1. Just try using Eaglercraft on a different device, it isn't a bug it's common sense
  2. " + "
  3. If this screen just appeared randomly, try restarting your browser or device
  4. " + "
  5. If you are not using Chrome/Edge, try installing the latest Google Chrome
  6. " + "
  7. If your browser is out of date, please update it to the latest version
  8. " + "
" + "
"; div.querySelector("#_eaglercraftX_crashReason").appendChild(document.createTextNode(msg)); div.querySelector("#_eaglercraftX_crashUserAgent").appendChild(document.createTextNode(getStringNav("userAgent"))); if(removeEventHandlers) removeEventHandlers(); window.__curEaglerX188UnloadListenerCB = null; var webGLRenderer = "No GL_RENDERER string could be queried"; try { const cvs = /** @type {HTMLCanvasElement} */ (document.createElement("canvas")); cvs.width = 64; cvs.height = 64; const ctx = /** @type {WebGLRenderingContext} */ (cvs.getContext("webgl")); if(ctx) { /** @type {string|null} */ var r; if(ctx.getExtension("WEBGL_debug_renderer_info")) { r = /** @type {string|null} */ (ctx.getParameter(/* UNMASKED_RENDERER_WEBGL */ 0x9246)); }else { r = /** @type {string|null} */ (ctx.getParameter(WebGLRenderingContext.RENDERER)); if(r) { r += " [masked]"; } } if(r) { webGLRenderer = r; } } }catch(tt) { } div.querySelector("#_eaglercraftX_crashWebGL").appendChild(document.createTextNode(webGLRenderer)); } } /** @type {string|null} */ var webGLCrashStringCache = null; /** * @return {string} */ function addWebGLToCrash() { if(webGLCrashStringCache) { return webGLCrashStringCache; } try { /** @type {WebGL2RenderingContext} */ var ctx = webglContext; var experimental = webglExperimental; if(!ctx) { experimental = false; var cvs = document.createElement("canvas"); cvs.width = 64; cvs.height = 64; ctx = /** @type {WebGL2RenderingContext} */ (cvs.getContext("webgl2")); if(!ctx) { ctx = /** @type {WebGL2RenderingContext} */ (cvs.getContext("webgl")); if(!ctx) { experimental = true; ctx = /** @type {WebGL2RenderingContext} */ (cvs.getContext("experimental-webgl")); } } } if(ctx) { var ret = ""; if(webglGLESVer > 0) { ret += "webgl.version = " + ctx.getParameter(/* VERSION */ 0x1F02) + "\n"; } if(ctx.getExtension("WEBGL_debug_renderer_info")) { ret += "webgl.renderer = " + ctx.getParameter(/* UNMASKED_RENDERER_WEBGL */ 0x9246) + "\nwebgl.vendor = " + ctx.getParameter(/* UNMASKED_VENDOR_WEBGL */ 0x9245) + "\n"; }else { ret += "webgl.renderer = " + ctx.getParameter(/* RENDERER */ 0x1F01) + " [masked]\nwebgl.vendor = " + ctx.getParameter(/* VENDOR */ 0x1F00) + " [masked]\n"; } if(webglGLESVer > 0) { ret += "\nwebgl.version.id = " + webglGLESVer + "\nwebgl.experimental = " + experimental; if(webglGLESVer === 200) { ret += "\nwebgl.ext.ANGLE_instanced_arrays = " + !!ctx.getExtension("ANGLE_instanced_arrays") + "\nwebgl.ext.EXT_color_buffer_half_float = " + !!ctx.getExtension("EXT_color_buffer_half_float") + "\nwebgl.ext.EXT_shader_texture_lod = " + !!ctx.getExtension("EXT_shader_texture_lod") + "\nwebgl.ext.OES_fbo_render_mipmap = " + !!ctx.getExtension("OES_fbo_render_mipmap") + "\nwebgl.ext.OES_texture_float = " + !!ctx.getExtension("OES_texture_float") + "\nwebgl.ext.OES_texture_half_float = " + !!ctx.getExtension("OES_texture_half_float") + "\nwebgl.ext.OES_texture_half_float_linear = " + !!ctx.getExtension("OES_texture_half_float_linear"); }else if(webglGLESVer >= 300) { ret += "\nwebgl.ext.EXT_color_buffer_float = " + !!ctx.getExtension("EXT_color_buffer_float") + "\nwebgl.ext.EXT_color_buffer_half_float = " + !!ctx.getExtension("EXT_color_buffer_half_float") + "\nwebgl.ext.OES_texture_float_linear = " + !!ctx.getExtension("OES_texture_float_linear"); } ret += "\nwebgl.ext.EXT_texture_filter_anisotropic = " + !!ctx.getExtension("EXT_texture_filter_anisotropic") + "\n"; }else { ret += "webgl.ext.ANGLE_instanced_arrays = " + !!ctx.getExtension("ANGLE_instanced_arrays") + "\nwebgl.ext.EXT_color_buffer_float = " + !!ctx.getExtension("EXT_color_buffer_float") + "\nwebgl.ext.EXT_color_buffer_half_float = " + !!ctx.getExtension("EXT_color_buffer_half_float") + "\nwebgl.ext.EXT_shader_texture_lod = " + !!ctx.getExtension("EXT_shader_texture_lod") + "\nwebgl.ext.OES_fbo_render_mipmap = " + !!ctx.getExtension("OES_fbo_render_mipmap") + "\nwebgl.ext.OES_texture_float = " + !!ctx.getExtension("OES_texture_float") + "\nwebgl.ext.OES_texture_float_linear = " + !!ctx.getExtension("OES_texture_float_linear") + "\nwebgl.ext.OES_texture_half_float = " + !!ctx.getExtension("OES_texture_half_float") + "\nwebgl.ext.OES_texture_half_float_linear = " + !!ctx.getExtension("OES_texture_half_float_linear") + "\nwebgl.ext.EXT_texture_filter_anisotropic = " + !!ctx.getExtension("EXT_texture_filter_anisotropic") + "\n"; } return webGLCrashStringCache = ret; }else { return webGLCrashStringCache = "Failed to query GPU info!\n"; } }catch(ex) { return webGLCrashStringCache = "ERROR: could not query webgl info - " + ex + "\n"; } } /** * @param {string} k * @return {string} */ function addDebugNav(k) { var val; try { val = window.navigator[k]; } catch(e) { val = ""; } return "window.navigator." + k + " = " + val + "\n"; } /** * @param {string} k * @return {string} */ function getStringNav(k) { try { return window.navigator[k]; } catch(e) { return ""; } } /** * @return {string} */ function addDebugNavPlugins() { var val; try { var retObj = new Array(); if(typeof navigator.plugins === "object") { var len = navigator.plugins.length; if(len > 0) { for(var idx = 0; idx < len; ++idx) { var thePlugin = navigator.plugins[idx]; retObj.push({ "name": thePlugin.name, "filename": thePlugin.filename, "desc": thePlugin.description }); } } } val = JSON.stringify(retObj); } catch(e) { val = ""; } return "window.navigator.plugins = " + val + "\n"; } /** * @param {string} k * @return {string} */ function addDebugScreen(k) { var val; try { val = window.screen[k]; } catch(e) { val = ""; } return "window.screen." + k + " = " + val + "\n"; } /** * @param {string} k * @return {string} */ function addDebugLocation(k) { var val; try { val = window.location[k]; } catch(e) { val = ""; } return "window.location." + k + " = " + val + "\n"; } /** * @param {string} k * @return {string} */ function addDebug(k) { var val; try { val = window[k]; } catch(e) { val = ""; } return "window." + k + " = " + val + "\n"; }