mirror of
https://github.com/Eaglercraft-Archive/Eaglercraftx-1.8.8-src.git
synced 2025-06-27 18:38:14 -05:00
Update #44 - WebAssembly GC support, fix more WebRTC bugs
This commit is contained in:
970
sources/wasm-gc-teavm/js/eagruntime_main.js
Normal file
970
sources/wasm-gc-teavm/js/eagruntime_main.js
Normal file
@ -0,0 +1,970 @@
|
||||
/*
|
||||
* 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 :(</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";
|
||||
}
|
Reference in New Issue
Block a user