eaglercraft-1.8/sources/wasm-gc-teavm/js/eagruntime_main.js

971 lines
33 KiB
JavaScript

/*
* 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<number>):Array<Object>|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<string>} */
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<boolean>}
*/
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 = "<h3 style=\"margin-block-start:0px;margin-block-end:0px;margin:20px 5px;\">Mobile Browser Detected</h3>"
+ "<p style=\"margin-block-start:0px;margin-block-end:0px;margin:20px 5px;\">Warning: EaglercraftX WASM-GC requires a lot of memory and may not be stable on most mobile devices!</p>"
+ "<p style=\"margin-block-start:0px;margin-block-end:0px;margin:20px 2px;\"><button style=\"font: 24px sans-serif;font-weight:bold;\" class=\"_eaglercraftX_mobile_launch_client\">Launch EaglercraftX</button></p>"
/*+ (allowBootMenu ? "<p style=\"margin-block-start:0px;margin-block-end:0px;margin:20px 2px;\"><button style=\"font: 24px sans-serif;\" class=\"_eaglercraftX_mobile_enter_boot_menu\">Enter Boot Menu</button></p>" : "")*/
+ "<p style=\"margin-block-start:0px;margin-block-end:0px;margin:25px 5px;\">(Tablets and phones with large screens work best)</p>";
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<string>} */
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<string>}
*/
function deobfuscateStack(stack) {
if(!stack) return null;
/** @type {!Array<string>} */
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<Object>} */
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 = "<h2><svg style=\"vertical-align:middle;margin:0px 16px 8px 8px;\" xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\"><path stroke=\"#000000\" stroke-width=\"3\" stroke-linecap=\"square\" d=\"M1.5 8.5v34h45v-28m-3-3h-10v-3m-3-3h-10m15 6h-18v-3m-3-3h-10\"/><path stroke=\"#000000\" stroke-width=\"2\" stroke-linecap=\"square\" d=\"M12 21h0m0 4h0m4 0h0m0-4h0m-2 2h0m20-2h0m0 4h0m4 0h0m0-4h0m-2 2h0\"/><path stroke=\"#000000\" stroke-width=\"2\" stroke-linecap=\"square\" d=\"M20 30h0 m2 2h0 m2 2h0 m2 2h0 m2 -2h0 m2 -2h0 m2 -2h0\"/></svg>+ This device is incompatible with Eaglercraft&ensp;:(</h2>"
+ "<div style=\"margin-left:40px;\">"
+ "<p style=\"font-size:1.2em;\"><b style=\"font-size:1.1em;\">Issue:</b> <span style=\"color:#BB0000;\" id=\"_eaglercraftX_crashReason\"></span><br /></p>"
+ "<p style=\"margin-left:10px;font:0.9em monospace;\" id=\"_eaglercraftX_crashUserAgent\"></p>"
+ "<p style=\"margin-left:10px;font:0.9em monospace;\" id=\"_eaglercraftX_crashWebGL\"></p>"
+ "<p style=\"margin-left:10px;font:0.9em monospace;\">Current Date: " + (new Date()).toLocaleString() + "</p>"
+ "<p><br /><span style=\"font-size:1.1em;border-bottom:1px dashed #AAAAAA;padding-bottom:5px;\">Things you can try:</span></p>"
+ "<ol>"
+ "<li><span style=\"font-weight:bold;\">Just try using Eaglercraft on a different device</span>, it isn't a bug it's common sense</li>"
+ "<li style=\"margin-top:7px;\">If this screen just appeared randomly, try restarting your browser or device</li>"
+ "<li style=\"margin-top:7px;\">If you are not using Chrome/Edge, try installing the latest Google Chrome</li>"
+ "<li style=\"margin-top:7px;\">If your browser is out of date, please update it to the latest version</li>"
+ "</ol>"
+ "</div>";
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 = "<error>";
}
return "window.navigator." + k + " = " + val + "\n";
}
/**
* @param {string} k
* @return {string}
*/
function getStringNav(k) {
try {
return window.navigator[k];
} catch(e) {
return "<error>";
}
}
/**
* @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 = "<error>";
}
return "window.navigator.plugins = " + val + "\n";
}
/**
* @param {string} k
* @return {string}
*/
function addDebugScreen(k) {
var val;
try {
val = window.screen[k];
} catch(e) {
val = "<error>";
}
return "window.screen." + k + " = " + val + "\n";
}
/**
* @param {string} k
* @return {string}
*/
function addDebugLocation(k) {
var val;
try {
val = window.location[k];
} catch(e) {
val = "<error>";
}
return "window.location." + k + " = " + val + "\n";
}
/**
* @param {string} k
* @return {string}
*/
function addDebug(k) {
var val;
try {
val = window[k];
} catch(e) {
val = "<error>";
}
return "window." + k + " = " + val + "\n";
}