diff --git a/src/teavm/java/net/lax1dude/eaglercraft/Client.java b/src/teavm/java/net/lax1dude/eaglercraft/Client.java index a1ad9f6..0cb7497 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/Client.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/Client.java @@ -2,12 +2,12 @@ package net.lax1dude.eaglercraft; import static org.teavm.jso.webgl.WebGLRenderingContext.*; -import java.io.PrintWriter; -import java.io.StringWriter; - import org.json.JSONException; import org.json.JSONObject; import org.teavm.jso.JSBody; +import org.teavm.jso.JSExceptions; +import org.teavm.jso.JSFunctor; +import org.teavm.jso.JSObject; import org.teavm.jso.browser.Window; import org.teavm.jso.core.JSError; import org.teavm.jso.dom.html.HTMLCanvasElement; @@ -70,23 +70,23 @@ public class Client { } String serverWorkerURI = conf.optString("serverWorkerURI", null); EaglerAdapterImpl2.setWorldDatabaseName(conf.optString("worldsFolder", "MAIN")); + + registerErrorHandler(); try { + EaglerAdapterImpl2.initializeContext(rootElement, assetsURI, serverWorkerURI); + + ServerList.loadDefaultServers(conf); + AssetRepository.loadOverrides(conf); + LocalStorageManager.loadStorage(); + + run0(); + }catch(Throwable t) { - StringWriter s = new StringWriter(); - t.printStackTrace(new PrintWriter(s)); - showCrashScreen(s.toString()); + showCrashScreen(t.toString() + "\n\n" + getStackTrace(t)); return; } - - registerErrorHandler(); - ServerList.loadDefaultServers(conf); - AssetRepository.loadOverrides(conf); - LocalStorageManager.loadStorage(); - - run0(); - } private static void oldMain() { @@ -100,23 +100,37 @@ public class Client { crashScreenOptsDump += "\"" + sh + "\""; } crashScreenOptsDump += " ]"; + + registerErrorHandler(); + try { + EaglerAdapterImpl2.initializeContext(rootElement = Window.current().getDocument().getElementById(e[0]), e[1], "worker_bootstrap.js"); + + LocalStorageManager.loadStorage(); + if(e.length > 2 && e[2].length() > 0) { + ServerList.loadDefaultServers(e[2]); + } + if(e.length > 3) { + EaglerAdapterImpl2.setServerToJoinOnLaunch(e[3]); + } + + run0(); + }catch(Throwable t) { - StringWriter s = new StringWriter(); - t.printStackTrace(new PrintWriter(s)); - showCrashScreen(s.toString()); + showCrashScreen(t.toString() + "\n\n" + getStackTrace(t)); return; } - registerErrorHandler(); - LocalStorageManager.loadStorage(); - if(e.length > 2 && e[2].length() > 0) { - ServerList.loadDefaultServers(e[2]); + } + + private static String getStackTrace(Throwable t) { + JSObject obj = JSExceptions.getJSException(t); + if(obj != null) { + JSError err = (JSError)obj; + return err.getStack() == null ? "[no stack trace]" : err.getStack(); + }else { + return "[no stack trace]"; } - if(e.length > 3) { - EaglerAdapterImpl2.setServerToJoinOnLaunch(e[3]); - } - run0(); } private static void run0() { @@ -131,29 +145,51 @@ public class Client { @JSBody(params = { }, script = "return window.minecraftOpts;") public static native String[] getOpts(); + + public static void registerErrorHandler() { + setWindowErrorHandler(new WindowErrorHandler() { - @JSBody(params = { }, script = "window.minecraftError = null; window.onerror = function(message, file, line, column, errorObj) { if(errorObj) { window.minecraftError = errorObj; window.minecraftErrorL = \"\"+line+\":\"+column; javaMethods.get(\"net.lax1dude.eaglercraft.Client.handleNativeError()V\").invoke(); } else { alert(\"a native browser exception was thrown but your browser does not support fith argument in onerror\"); } };") - public static native void registerErrorHandler(); - - @JSBody(params = { }, script = "return window.minecraftError;") - public static native JSError getWindowError(); - - @JSBody(params = { }, script = "return window.minecraftErrorL;") - public static native String getWindowErrorL(); - - public static void handleNativeError() { - JSError e = getWindowError(); - StringBuilder str = new StringBuilder(); - str.append("Native Browser Exception\n"); - str.append("----------------------------------\n"); - str.append(" Line: ").append(getWindowErrorL()).append('\n'); - str.append(" Type: ").append(e.getName()).append('\n'); - str.append(" Message: ").append(e.getMessage()).append('\n'); - str.append("----------------------------------\n\n"); - str.append(e.getStack()).append('\n'); - showCrashScreen(str.toString()); + @Override + public void call(String message, String file, int line, int col, JSError error) { + StringBuilder str = new StringBuilder(); + + str.append("Native Browser Exception\n"); + str.append("----------------------------------\n"); + str.append(" Line: ").append((file == null ? "unknown" : file) + ":" + line + ":" + col).append('\n'); + str.append(" Type: ").append(error == null ? "generic" : error.getName()).append('\n'); + + if(error != null) { + str.append(" Desc: ").append(error.getMessage() == null ? "null" : error.getMessage()).append('\n'); + } + + if(message != null) { + if(error == null || error.getMessage() == null || !message.endsWith(error.getMessage())) { + str.append(" Desc: ").append(message).append('\n'); + } + } + + str.append("----------------------------------\n\n"); + str.append(error.getStack() == null ? "No stack trace is available" : error.getStack()).append('\n'); + + showCrashScreen(str.toString()); + } + + }); } + @JSFunctor + private static interface WindowErrorHandler extends JSObject { + void call(String message, String file, int line, int col, JSError error); + } + + @JSBody(params = { "handler" }, script = "window.addEventListener(\"error\", function(e) { handler(" + + "(typeof e.message === \"string\") ? e.message : null," + + "(typeof e.filename === \"string\") ? e.filename : null," + + "(typeof e.lineno === \"number\") ? e.lineno : 0," + + "(typeof e.colno === \"number\") ? e.colno : 0," + + "(typeof e.error === \"undefined\") ? null : e.error); });") + public static native void setWindowErrorHandler(WindowErrorHandler handler); + private static boolean isCrashed = false; private static void showCrashScreen(String t) { @@ -166,7 +202,7 @@ public class Client { str.append('\n').append('\n'); str.append("eaglercraft.version = \"").append(ConfigConstants.version).append("\"\n"); str.append("eaglercraft.minecraft = \"1.5.2\"\n"); - str.append("eaglercraft.brand = \"eagtek\"\n"); + str.append("eaglercraft.brand = \"lax1dude\"\n"); str.append("eaglercraft.username = \"").append(EaglerProfile.username).append("\"\n"); str.append('\n'); str.append(addWebGLToCrash()); @@ -193,8 +229,6 @@ public class Client { addDebugScreen(str, "colorDepth"); addDebugScreen(str, "pixelDepth"); str.append('\n'); - addDebug(str, "currentContext"); - str.append('\n'); addDebugLocation(str, "href"); str.append("\n----- Begin Minecraft Config -----\n"); str.append(LocalStorageManager.dumpConfiguration()); @@ -221,14 +255,14 @@ public class Client { private static String addWebGLToCrash() { StringBuilder ret = new StringBuilder(); - HTMLCanvasElement cvs = (HTMLCanvasElement) Window.current().getDocument().createElement("canvas"); - - cvs.setWidth(64); - cvs.setHeight(64); - WebGLRenderingContext ctx = EaglerAdapterImpl2.webgl; if(ctx == null) { + HTMLCanvasElement cvs = (HTMLCanvasElement) Window.current().getDocument().createElement("canvas"); + + cvs.setWidth(64); + cvs.setHeight(64); + ctx = (WebGLRenderingContext)cvs.getContext("webgl"); } @@ -236,8 +270,14 @@ public class Client { if(EaglerAdapterImpl2.webgl != null) { ret.append("webgl.version = ").append(ctx.getParameterString(VERSION)).append('\n'); } - ret.append("webgl.renderer = ").append(ctx.getParameterString(RENDERER)).append('\n'); - ret.append("webgl.vendor = ").append(ctx.getParameterString(VENDOR)).append('\n'); + if(ctx.getExtension("WEBGL_debug_renderer_info") != null) { + ret.append("webgl.renderer = ").append(ctx.getParameterString(/* UNMASKED_RENDERER_WEBGL */ 0x9246)).append('\n'); + ret.append("webgl.vendor = ").append(ctx.getParameterString(/* UNMASKED_VENDOR_WEBGL */ 0x9245)).append('\n'); + }else { + ret.append("webgl.renderer = ").append("" + ctx.getParameterString(RENDERER) + " [masked]").append('\n'); + ret.append("webgl.vendor = ").append("" + ctx.getParameterString(VENDOR) + " [masked]").append('\n'); + } + ret.append("\nwebgl.anisotropicGlitch = ").append(DetectAnisotropicGlitch.hasGlitch()).append('\n'); }else { ret.append("Failed to query GPU info!\n"); } @@ -292,7 +332,15 @@ public class Client { WebGLRenderingContext ctx = (WebGLRenderingContext)cvs.getContext("webgl"); if(ctx != null) { - String r = ctx.getParameterString(RENDERER); + String r; + if(ctx.getExtension("WEBGL_debug_renderer_info") != null) { + r = ctx.getParameterString(/* UNMASKED_RENDERER_WEBGL */ 0x9246); + }else { + r = ctx.getParameterString(RENDERER); + if(r != null) { + r += " [masked]"; + } + } if(r != null) { webGLRenderer = r; } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/adapter/DetectAnisotropicGlitch.java b/src/teavm/java/net/lax1dude/eaglercraft/adapter/DetectAnisotropicGlitch.java index 592e7ed..ddd5869 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/adapter/DetectAnisotropicGlitch.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/adapter/DetectAnisotropicGlitch.java @@ -14,8 +14,19 @@ import net.lax1dude.eaglercraft.adapter.teavm.WebGLVertexArray; import org.teavm.jso.webgl.*; public class DetectAnisotropicGlitch { + + private static boolean known = false; + private static boolean detected = false; - public static boolean detectGlitch() { + public static boolean hasGlitch() { + if(!known) { + detected = detect(); + known = true; + } + return detected; + } + + public static boolean detect() { HTMLCanvasElement cvs = (HTMLCanvasElement) Window.current().getDocument().createElement("canvas"); cvs.setWidth(400); diff --git a/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java b/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java index 5eeba9f..e0473af 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/adapter/EaglerAdapterImpl2.java @@ -1032,7 +1032,7 @@ public class EaglerAdapterImpl2 { return getNavString("platform").toLowerCase().contains("win"); } public static final boolean glNeedsAnisotropicFix() { - return anisotropicFilteringSupported && DetectAnisotropicGlitch.detectGlitch(); + return anisotropicFilteringSupported && DetectAnisotropicGlitch.hasGlitch(); } private static HTMLCanvasElement imageLoadCanvas = null; @@ -1076,13 +1076,15 @@ public class EaglerAdapterImpl2 { Uint8ClampedArray pxls = pxlsDat.getData(); int totalPixels = pxlsDat.getWidth() * pxlsDat.getHeight(); freeDataURL(toLoad.getSrc()); - if(pxls.getByteLength() < totalPixels * 4) { + if(pxls.getByteLength() < totalPixels << 2) { ret.complete(null); return; } + DataView dv = DataView.create(pxls.getBuffer()); int[] pixels = new int[totalPixels]; - for(int i = 0; i < pixels.length; ++i) { - pixels[i] = (pxls.get(i * 4) << 16) | (pxls.get(i * 4 + 1) << 8) | pxls.get(i * 4 + 2) | (pxls.get(i * 4 + 3) << 24); + for(int i = 0, j; i < pixels.length; ++i) { + j = dv.getUint32(i << 2, false); + pixels[i] = (j >> 8) | ((j & 0xFF) << 24); } ret.complete(new EaglerImage(pixels, pxlsDat.getWidth(), pxlsDat.getHeight(), true)); }