diff --git a/desktopRuntime/resources/EPKVersionIdentifier.txt b/desktopRuntime/resources/EPKVersionIdentifier.txt
index 15a5b3c..50356da 100755
--- a/desktopRuntime/resources/EPKVersionIdentifier.txt
+++ b/desktopRuntime/resources/EPKVersionIdentifier.txt
@@ -1 +1 @@
-u44
\ No newline at end of file
+u46
\ No newline at end of file
diff --git a/desktopRuntime/resources/assets/eagler/glsl/core.fsh b/desktopRuntime/resources/assets/eagler/glsl/core.fsh
index 9f5f8fa..43c0e00 100755
--- a/desktopRuntime/resources/assets/eagler/glsl/core.fsh
+++ b/desktopRuntime/resources/assets/eagler/glsl/core.fsh
@@ -162,7 +162,7 @@ void main() {
 
 #ifdef COMPILE_ENABLE_MC_LIGHTING
 #ifdef COMPILE_NORMAL_ATTRIB
-	vec3 normal = normalize(v_normal3f);
+	vec3 normal = v_normal3f;
 #else
 	vec3 normal = u_uniformNormal3f;
 #endif
diff --git a/desktopRuntime/resources/assets/eagler/glsl/deferred/deferred_core_gbuffer.fsh b/desktopRuntime/resources/assets/eagler/glsl/deferred/deferred_core_gbuffer.fsh
index 13dcf19..3e14fe3 100755
--- a/desktopRuntime/resources/assets/eagler/glsl/deferred/deferred_core_gbuffer.fsh
+++ b/desktopRuntime/resources/assets/eagler/glsl/deferred/deferred_core_gbuffer.fsh
@@ -104,7 +104,7 @@ void main() {
 
 	vec3 normal;
 #ifdef COMPILE_NORMAL_ATTRIB
-	normal = normalize(v_normal3f);
+	normal = v_normal3f;
 #else
 	normal = u_uniformNormal3f;
 #endif
diff --git a/desktopRuntime/resources/assets/eagler/glsl/deferred/forward_core.fsh b/desktopRuntime/resources/assets/eagler/glsl/deferred/forward_core.fsh
index a2bd56f..c754909 100755
--- a/desktopRuntime/resources/assets/eagler/glsl/deferred/forward_core.fsh
+++ b/desktopRuntime/resources/assets/eagler/glsl/deferred/forward_core.fsh
@@ -206,8 +206,8 @@ void main() {
 #endif
 
 #ifdef COMPILE_NORMAL_ATTRIB
-	normalVector3f = normalize(v_normal3f);
-	block1f = v_block1f;
+	normalVector3f = v_normal3f;
+	block1f = round(v_block1f);
 #else
 	normalVector3f = u_uniformNormal3f;
 	block1f = u_blockConstant1f;
diff --git a/desktopRuntime/resources/assets/eagler/glsl/deferred/realistic_water_render.fsh b/desktopRuntime/resources/assets/eagler/glsl/deferred/realistic_water_render.fsh
index ae371ad..900d686 100755
--- a/desktopRuntime/resources/assets/eagler/glsl/deferred/realistic_water_render.fsh
+++ b/desktopRuntime/resources/assets/eagler/glsl/deferred/realistic_water_render.fsh
@@ -199,7 +199,7 @@ void main() {
 #endif
 
 #ifdef COMPILE_NORMAL_ATTRIB
-	normalVector3f = normalize(v_normal3f);
+	normalVector3f = v_normal3f;
 	block1f = round(v_block1f);
 #else
 	normalVector3f = u_uniformNormal3f;
diff --git a/desktopRuntime/resources/assets/eagler/glsl/dynamiclights/core_dynamiclights.fsh b/desktopRuntime/resources/assets/eagler/glsl/dynamiclights/core_dynamiclights.fsh
index 114eb3c..44c2066 100755
--- a/desktopRuntime/resources/assets/eagler/glsl/dynamiclights/core_dynamiclights.fsh
+++ b/desktopRuntime/resources/assets/eagler/glsl/dynamiclights/core_dynamiclights.fsh
@@ -146,7 +146,7 @@ void main() {
 #endif
 
 #ifdef COMPILE_NORMAL_ATTRIB
-	vec3 normal = normalize(v_normal3f);
+	vec3 normal = v_normal3f;
 #else
 	vec3 normal = u_uniformNormal3f;
 #endif
diff --git a/src/game/java/net/minecraft/client/gui/GuiIngame.java b/src/game/java/net/minecraft/client/gui/GuiIngame.java
index e848d6c..8922315 100755
--- a/src/game/java/net/minecraft/client/gui/GuiIngame.java
+++ b/src/game/java/net/minecraft/client/gui/GuiIngame.java
@@ -1102,12 +1102,16 @@ public class GuiIngame extends Gui {
 					&& touchVPosX < interactButtonX + interactButtonW && touchVPosY < interactButtonY + interactButtonH;
 			float f = MathHelper.clamp_float(mc.gameSettings.touchControlOpacity, 0.0f, 1.0f);
 			if (f > 0.0f) {
+				if (f < 1.0f)
+					GlStateManager.enableBlend();
 				GlStateManager.color(1.0f, 1.0f, 1.0f, f);
 				drawTexturedModalRect(xx, yy, 0, hover ? 216 : 236, 118, 20);
 				GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
 				drawCenteredString(mc.fontRendererObj, I18n.format("touch.interact.entity"),
 						parScaledResolution.getScaledWidth() / 2, yy + 6,
 						(hover ? 16777120 : 14737632) | ((int) (f * 255.0f) << 24));
+				if (f < 1.0f)
+					GlStateManager.disableBlend();
 			}
 		} else {
 			interactButtonX = -1;
diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java
index d07dfb6..09beda5 100755
--- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java
+++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java
@@ -96,4 +96,8 @@ public class ServerPlatformSingleplayer {
 		
 	}
 
+	public static boolean isTabAboutToCloseWASM() {
+		return false;
+	}
+
 }
diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java
index 2f547d3..6434d40 100755
--- a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java
+++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java
@@ -10,7 +10,7 @@ public class EaglercraftVersion {
 	/// Customize these to fit your fork:
 	
 	public static final String projectForkName = "EaglercraftX";
-	public static final String projectForkVersion = "u45";
+	public static final String projectForkVersion = "u46";
 	public static final String projectForkVendor = "lax1dude";
 	
 	public static final String projectForkURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8";
@@ -20,20 +20,20 @@ public class EaglercraftVersion {
 	public static final String projectOriginName = "EaglercraftX";
 	public static final String projectOriginAuthor = "lax1dude";
 	public static final String projectOriginRevision = "1.8";
-	public static final String projectOriginVersion = "u45";
+	public static final String projectOriginVersion = "u46";
 	
 	public static final String projectOriginURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8"; // rest in peace
 	
 	// EPK Version Identifier
 	
-	public static final String EPKVersionIdentifier = "u45"; // Set to null to disable EPK version check
+	public static final String EPKVersionIdentifier = "u46"; // Set to null to disable EPK version check
 	
 	// Updating configuration
 	
 	public static final boolean enableUpdateService = true;
 
 	public static final String updateBundlePackageName = "net.lax1dude.eaglercraft.v1_8.client";
-	public static final int updateBundlePackageVersionInt = 45;
+	public static final int updateBundlePackageVersionInt = 46;
 
 	public static final String updateLatestLocalStorageKey = "latestUpdate_" + updateBundlePackageName;
 
diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java
index 7ff353f..0d09532 100755
--- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java
+++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java
@@ -86,6 +86,11 @@ public class EaglerIntegratedServerWorker {
 				}
 			}
 		}
+		if(ServerPlatformSingleplayer.isTabAboutToCloseWASM() && !isServerStopped()) {
+			logger.info("Autosaving worlds because the tab is about to close!");
+			currentProcess.getConfigurationManager().saveAllPlayerData();
+			currentProcess.saveAllWorlds(false);
+		}
 	}
 
 	public static void tick() {
diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java
index f33c441..a943fc4 100755
--- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java
+++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java
@@ -99,6 +99,7 @@ public class PlatformInput {
 	private static EventListener<?> focus = null;
 	private static EventListener<?> blur = null;
 	private static EventListener<?> pointerlock = null;
+	private static EventListener<?> pointerlockerr = null;
 	private static EventListener<?> fullscreen = null;
 
 	private static Map<String,LegacyKeycodeTranslator.LegacyKeycode> keyCodeTranslatorMap = null;
@@ -224,6 +225,7 @@ public class PlatformInput {
 	private static long mouseGrabTimer = 0l;
 	private static int mouseUngrabTimeout = -1;
 	private static boolean pointerLockFlag = false;
+	private static boolean pointerLockWaiting = false;
 
 	private static final int FULLSCREEN_NONE = 0;
 	private static final int FULLSCREEN_CORE = 1;
@@ -643,7 +645,7 @@ public class PlatformInput {
 					Window.setTimeout(new TimerHandler() {
 						@Override
 						public void onTimer() {
-							boolean grab = isPointerLocked();
+							boolean grab = isPointerLockedImpl();
 							if(!grab) {
 								if(pointerLockFlag) {
 									mouseUngrabTimer = PlatformRuntime.steadyTimeMillis();
@@ -654,6 +656,13 @@ public class PlatformInput {
 					}, 60);
 					mouseDX = 0.0D;
 					mouseDY = 0.0D;
+					pointerLockWaiting = false;
+				}
+			});
+			win.getDocument().addEventListener(pointerLockSupported == POINTER_LOCK_MOZ ? "mozpointerlockerror" : "pointerlockerror", pointerlockerr = new EventListener<Event>() {
+				@Override
+				public void handleEvent(Event evt) {
+					pointerLockWaiting = false;
 				}
 			});
 			if(pointerLockSupported == POINTER_LOCK_MOZ) {
@@ -693,9 +702,9 @@ public class PlatformInput {
 					}
 				});
 			}
-			if(pointerLockSupported == FULLSCREEN_WEBKIT) {
+			if(fullscreenSupported == FULLSCREEN_WEBKIT) {
 				PlatformRuntime.logger.info("Using webkit- vendor prefix for fullscreen");
-			}else if(pointerLockSupported == FULLSCREEN_MOZ) {
+			}else if(fullscreenSupported == FULLSCREEN_MOZ) {
 				PlatformRuntime.logger.info("Using moz- vendor prefix for fullscreen");
 			}
 		}else {
@@ -1322,6 +1331,7 @@ public class PlatformInput {
 		pointerLockFlag = grab;
 		mouseGrabTimer = t;
 		if(grab) {
+			pointerLockWaiting = true;
 			callRequestPointerLock(canvas);
 			if(mouseUngrabTimeout != -1) Window.clearTimeout(mouseUngrabTimeout);
 			mouseUngrabTimeout = -1;
@@ -1336,7 +1346,9 @@ public class PlatformInput {
 		}else {
 			if(mouseUngrabTimeout != -1) Window.clearTimeout(mouseUngrabTimeout);
 			mouseUngrabTimeout = -1;
-			callExitPointerLock(win.getDocument());
+			if(!pointerLockWaiting) {
+				callExitPointerLock(win.getDocument());
+			}
 		}
 		mouseDX = 0.0D;
 		mouseDY = 0.0D;
@@ -1408,6 +1420,11 @@ public class PlatformInput {
 	}
 
 	public static boolean isPointerLocked() {
+		if(pointerLockWaiting) return true; // workaround for chrome bug
+		return isPointerLockedImpl();
+	}
+
+	private static boolean isPointerLockedImpl() {
 		switch(pointerLockSupported) {
 		case POINTER_LOCK_CORE:
 			return isPointerLocked0(win.getDocument(), canvas);
@@ -1534,6 +1551,10 @@ public class PlatformInput {
 			win.getDocument().removeEventListener("pointerlockchange", pointerlock);
 			pointerlock = null;
 		}
+		if(pointerlockerr != null) {
+			win.getDocument().removeEventListener("pointerlockerror", pointerlockerr);
+			pointerlockerr = null;
+		}
 		if(fullscreen != null) {
 			TeaVMUtils.removeEventListener(fullscreenQuery, "change", fullscreen);
 			fullscreen = null;
diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java
index 796d114..df6b40e 100755
--- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java
+++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java
@@ -296,4 +296,8 @@ public class ServerPlatformSingleplayer {
 		
 	}
 
+	public static boolean isTabAboutToCloseWASM() {
+		return false;
+	}
+
 }
diff --git a/src/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java b/src/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java
index 4ddc702..2cd9699 100755
--- a/src/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java
+++ b/src/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/sp/server/internal/ServerPlatformSingleplayer.java
@@ -143,4 +143,7 @@ public class ServerPlatformSingleplayer {
 	@Import(module = "serverPlatformSingleplayer", name = "setCrashCallback")
 	private static native JSWASMCrashCallbackInterface setCrashCallbackWASM0();
 
+	@Import(module = "serverPlatformSingleplayer", name = "isTabAboutToClose")
+	public static native boolean isTabAboutToCloseWASM();
+
 }
diff --git a/src/wasm-gc-teavm/js/clientPlatformSingleplayer.js b/src/wasm-gc-teavm/js/clientPlatformSingleplayer.js
index 7e30cf2..d4efcfb 100755
--- a/src/wasm-gc-teavm/js/clientPlatformSingleplayer.js
+++ b/src/wasm-gc-teavm/js/clientPlatformSingleplayer.js
@@ -196,6 +196,13 @@ function initializeClientPlatfSP(spImports) {
 		}
 	};
 
+	window.__curEaglerX188UnloadListenerCB = function() {
+		if(workerObj) {
+			workerObj.postMessage({
+				"ch": "~!WASM_AUTOSAVE"
+			});
+		}
+	};
 }
 
 function initializeNoClientPlatfSP(spImports) {
diff --git a/src/wasm-gc-teavm/js/eagruntime_main.js b/src/wasm-gc-teavm/js/eagruntime_main.js
index 9bdce71..cc4b7f2 100755
--- a/src/wasm-gc-teavm/js/eagruntime_main.js
+++ b/src/wasm-gc-teavm/js/eagruntime_main.js
@@ -133,9 +133,6 @@ async function initializeContext() {
 	
 	currentRedirectorFunc = addLogMessageImpl;
 	
-	window.__curEaglerX188UnloadListenerCB = function() {
-		//TODO: Autosave somehow?
-	};
 	if(window.__isEaglerX188UnloadListenerSet !== "yes") {
 		window.onbeforeunload = function(evt) {
 			if(window.__curEaglerX188UnloadListenerCB) {
diff --git a/src/wasm-gc-teavm/js/platformInput.js b/src/wasm-gc-teavm/js/platformInput.js
index 0573086..abc45e7 100755
--- a/src/wasm-gc-teavm/js/platformInput.js
+++ b/src/wasm-gc-teavm/js/platformInput.js
@@ -80,6 +80,7 @@ async function initPlatformInput(inputImports) {
 
 	var pointerLockSupported = false;
 	var pointerLockFlag = false;
+	var pointerLockWaiting = false;
 	var mouseUngrabTimer = 0;
 	var mouseGrabTimer = 0;
 	var mouseUngrabTimeout = -1;
@@ -116,6 +117,7 @@ async function initPlatformInput(inputImports) {
 		focus: null,
 		blur: null,
 		pointerlock: null,
+		pointerlockerr: null,
 		fullscreenChange: null
 	};
 
@@ -412,6 +414,10 @@ async function initPlatformInput(inputImports) {
 				}
 				pointerLockFlag = grab;
 			}, 60);
+			pointerLockWaiting = false;
+		}));
+		document.addEventListener("pointerlockerror", /** @type {function(Event)} */ (currentEventListeners.pointerlockerr = function(evt) {
+			pointerLockWaiting = false;
 		}));
 	}else {
 		eagError("Pointer lock is not supported on this browser");
@@ -646,6 +652,7 @@ async function initPlatformInput(inputImports) {
 		const t = performance.now() | 0;
 		mouseGrabTimer = t;
 		if(grab) {
+			pointerLockWaiting = true;
 			try {
 				canvasElement.requestPointerLock();
 			}catch(ex) {
@@ -663,9 +670,11 @@ async function initPlatformInput(inputImports) {
 		}else {
 			if(mouseUngrabTimeout !== -1) window.clearTimeout(mouseUngrabTimeout);
 			mouseUngrabTimeout = -1;
-			try {
-				document.exitPointerLock();
-			}catch(ex) {
+			if(!pointerLockWaiting) {
+				try {
+					document.exitPointerLock();
+				}catch(ex) {
+				}
 			}
 		}
 	};
@@ -690,7 +699,7 @@ async function initPlatformInput(inputImports) {
 	 * @return {boolean}
 	 */
 	inputImports["isPointerLocked"] = function() {
-		return pointerLockSupported && document.pointerLockElement === canvasElement;
+		return pointerLockSupported && (pointerLockWaiting || document.pointerLockElement === canvasElement);
 	};
 
 	/**
@@ -1124,6 +1133,10 @@ async function initPlatformInput(inputImports) {
 				document.removeEventListener("pointerlockchange", /** @type {function(Event)} */ (currentEventListeners.pointerlock));
 				currentEventListeners.pointerlock = null;
 			}
+			if(currentEventListeners.pointerlockerr) {
+				document.removeEventListener("pointerlockerror", /** @type {function(Event)} */ (currentEventListeners.pointerlockerr));
+				currentEventListeners.pointerlockerr = null;
+			}
 			if(currentEventListeners.fullscreenChange) {
 				fullscreenQuery.removeEventListener("change", /** @type {function(Event)} */ (currentEventListeners.fullscreenChange));
 				currentEventListeners.fullscreenChange = null;
diff --git a/src/wasm-gc-teavm/js/serverPlatformSingleplayer.js b/src/wasm-gc-teavm/js/serverPlatformSingleplayer.js
index b928900..6f7fb95 100755
--- a/src/wasm-gc-teavm/js/serverPlatformSingleplayer.js
+++ b/src/wasm-gc-teavm/js/serverPlatformSingleplayer.js
@@ -19,6 +19,9 @@ const serverPlatfSPName = "serverPlatformSingleplayer";
 /** @type {function(string, boolean)|null} */
 var sendIntegratedServerCrash = null;
 
+/** @type {boolean} */
+var isTabClosingFlag = false;
+
 function initializeServerPlatfSP(spImports) {
 
 	const serverMessageQueue = new EaglerLinkedQueue();
@@ -35,6 +38,11 @@ function initializeServerPlatfSP(spImports) {
 			return;
 		}
 		
+		if(channel === "~!WASM_AUTOSAVE") {
+			isTabClosingFlag = true;
+			return;
+		}
+		
 		if(!buf) {
 			eagError("Recieved IPC packet with null buffer");
 			return;
@@ -72,6 +80,14 @@ function initializeServerPlatfSP(spImports) {
 		};
 	};
 
+	/**
+	 * @return {boolean}
+	 */
+	spImports["isTabAboutToClose"] = function() {
+		const ret = isTabClosingFlag;
+		isTabClosingFlag = false;
+		return ret;
+	};
 }
 
 function initializeNoServerPlatfSP(spImports) {
@@ -79,4 +95,5 @@ function initializeNoServerPlatfSP(spImports) {
 	setUnsupportedFunc(spImports, serverPlatfSPName, "getAvailablePackets");
 	setUnsupportedFunc(spImports, serverPlatfSPName, "getNextPacket");
 	setUnsupportedFunc(spImports, serverPlatfSPName, "setCrashCallback");
+	setUnsupportedFunc(spImports, serverPlatfSPName, "isTabAboutToClose");
 }
diff --git a/wasm_gc_teavm/javascript/epw_meta.txt b/wasm_gc_teavm/javascript/epw_meta.txt
index 34daf11..9af6abc 100755
--- a/wasm_gc_teavm/javascript/epw_meta.txt
+++ b/wasm_gc_teavm/javascript/epw_meta.txt
@@ -1,8 +1,8 @@
-client-version-integer=45
+client-version-integer=46
 client-package-name=net.lax1dude.eaglercraft.v1_8.client
 client-origin-name=EaglercraftX
-client-origin-version=u45
+client-origin-version=u46
 client-origin-vendor=lax1dude
 client-fork-name=EaglercraftX
-client-fork-version=u45
+client-fork-version=u46
 client-fork-vendor=lax1dude