From 346047cf2462f740e1ca70f63e0bbd41daf88b6c Mon Sep 17 00:00:00 2001 From: lax1dude Date: Sat, 14 Dec 2024 20:54:34 -0800 Subject: [PATCH] Update #45 - Fixed various issues with the client --- client_version | 2 +- .../client/gui/GuiMultiplayer.edit.java | 5 +- .../assets/minecraft/lang/en_US.edit.lang | 2 +- .../v1_8/internal/PlatformInput.java | 23 +++- .../v1_8/internal/PlatformRuntime.java | 4 + .../internal/buffer/EaglerLWJGLAllocator.java | 88 +++++++++----- .../lax1dude/eaglercraft/v1_8/Display.java | 36 ------ .../lax1dude/eaglercraft/v1_8/EagRuntime.java | 4 + .../eaglercraft/v1_8/EaglerInputStream.java | 10 ++ .../eaglercraft/v1_8/EaglercraftVersion.java | 8 +- .../minecraft/EaglerFolderResourcePack.java | 2 +- .../v1_8/opengl/EaglercraftGPU.java | 2 + .../v1_8/opengl/FixedFunctionPipeline.java | 4 +- .../eaglercraft/v1_8/opengl/StreamBuffer.java | 104 ++++++++++++---- .../v1_8/sp/SingleplayerServerController.java | 2 +- .../v1_8/sp/lan/LANClientPeer.java | 63 ++++++++-- .../v1_8/sp/lan/LANServerController.java | 5 +- .../sp/server/export/WorldConverterEPK.java | 9 +- .../sp/server/export/WorldConverterMCA.java | 3 +- sources/resources/EPKVersionIdentifier.txt | 2 +- .../wasm_gc_teavm/CompileLoaderWASM.bat | 2 +- .../wasm_gc_teavm/CompileLoaderWASM.sh | 2 +- .../buildtools/MakeWASMClientBundle.jar | Bin 11090 -> 9424 bytes .../wasm_gc_teavm/javascript/epw_meta.txt | 6 +- .../wasm_gc_teavm/javascript/loader.js | 25 +++- .../boot_menu/teavm/ClientBootFactory.java | 2 +- .../v1_8/internal/PlatformInput.java | 45 +++++-- .../v1_8/internal/PlatformRuntime.java | 8 ++ .../v1_8/internal/PlatformVoiceClient.java | 1 + .../v1_8/internal/PlatformWebRTC.java | 113 ++++++++++++++++-- .../buffer/EaglerArrayBufferAllocator.java | 26 +++- sources/wasm-gc-teavm-loader/js/library.js | 43 ++++--- sources/wasm-gc-teavm-loader/js/pre.js | 22 +++- .../v1_8/internal/PlatformRuntime.java | 3 + .../buffer/WASMGCBufferAllocator.java | 52 +++++--- .../buffer/WASMGCDirectArrayConverter.java | 1 - .../wasm-gc-teavm/js/eagruntime_entrypoint.js | 1 + sources/wasm-gc-teavm/js/platformInput.js | 42 +++++-- sources/wasm-gc-teavm/js/platformRuntime.js | 7 ++ 39 files changed, 571 insertions(+), 208 deletions(-) diff --git a/client_version b/client_version index 15a5b3c..9f9fcca 100644 --- a/client_version +++ b/client_version @@ -1 +1 @@ -u44 \ No newline at end of file +u45 \ No newline at end of file diff --git a/patches/minecraft/net/minecraft/client/gui/GuiMultiplayer.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiMultiplayer.edit.java index 215cb26..f713d4b 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiMultiplayer.edit.java +++ b/patches/minecraft/net/minecraft/client/gui/GuiMultiplayer.edit.java @@ -74,13 +74,12 @@ > DELETE 1 @ 1 : 10 -> INSERT 3 : 8 @ 3 +> INSERT 3 : 7 @ 3 + if (lanServerList == null) { + lanServerList = new LANServerList(); -+ } else { -+ lanServerList.forceRefresh(); + } ++ lanServerList.forceRefresh(); > INSERT 12 : 17 @ 12 diff --git a/patches/resources/assets/minecraft/lang/en_US.edit.lang b/patches/resources/assets/minecraft/lang/en_US.edit.lang index a848f84..a630d9a 100644 --- a/patches/resources/assets/minecraft/lang/en_US.edit.lang +++ b/patches/resources/assets/minecraft/lang/en_US.edit.lang @@ -426,7 +426,7 @@ + eaglercraft.singleplayer.demo.create.join=Join Shared World + eaglercraft.singleplayer.demo.create.join.tooltip=Join someone else's world and play multiplayer + -+ eaglercraft.createWorld.seedNote=Note: Vanilla seeds now work! ++ eaglercraft.createWorld.seedNote=Note: Vanilla seeds work + + eaglercraft.singleplayer.oldseedwarning.title=Old World Detected! + eaglercraft.singleplayer.oldseedwarning.msg1=Please use EaglercraftX u32 or older to "Re-Create" this world diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java index 184ee3c..cb24e80 100644 --- a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java @@ -14,8 +14,6 @@ import org.lwjgl.glfw.GLFWGamepadState; import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.system.MemoryStack; -import net.lax1dude.eaglercraft.v1_8.Display; - /** * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * @@ -302,7 +300,7 @@ public class PlatformInput { update(0); } - private static final long[] syncTimer = new long[1]; + private static long syncTimer = 0l; public static void update(int limitFps) { glfwPollEvents(); @@ -311,10 +309,23 @@ public class PlatformInput { glfwVSyncState = vsync; } glfwSwapBuffers(win); - if(limitFps > 0 && !vsync) { - Display.sync(limitFps, syncTimer); + if(!vsync && limitFps > 0 && limitFps <= 1000) { + long frameNanos = (1000000000l / limitFps); + if(syncTimer == 0l) { + syncTimer = System.nanoTime() + frameNanos; + }else { + long nanos = System.nanoTime(); + int remaining = (int)((syncTimer - nanos) / 1000000l); + if(remaining > 0) { + PlatformRuntime.sleep(remaining); + nanos = System.nanoTime(); + } + if((syncTimer += frameNanos) < nanos) { + syncTimer = nanos; + } + } }else { - syncTimer[0] = 0l; + syncTimer = 0l; } } diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java index b510311..676d40d 100644 --- a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java @@ -647,4 +647,8 @@ public class PlatformRuntime { // nope } + public static boolean immediateContinueSupported() { + return false; + } + } diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLAllocator.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLAllocator.java index e1b7cad..b3aa2df 100644 --- a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLAllocator.java +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLAllocator.java @@ -33,48 +33,74 @@ public class EaglerLWJGLAllocator { return allocCount; } + private static final ByteBuffer ZERO_LENGTH_BYTE_BUFFER = new EaglerLWJGLByteBuffer(0l, 0, true); + public static ByteBuffer allocByteBuffer(int len) { - long ret = JEmalloc.nje_malloc(len); - if(ret == 0l) { - throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + if(len != 0) { + long ret = JEmalloc.nje_malloc(len); + if(ret == 0l) { + throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + } + if(enableAllocCount) ++allocCount; + return new EaglerLWJGLByteBuffer(ret, len, true); + }else { + return ZERO_LENGTH_BYTE_BUFFER; } - if(enableAllocCount) ++allocCount; - return new EaglerLWJGLByteBuffer(ret, len, true); } + private static final ShortBuffer ZERO_LENGTH_SHORT_BUFFER = new EaglerLWJGLShortBuffer(0l, 0, true); + public static ShortBuffer allocShortBuffer(int len) { - long ret = JEmalloc.nje_malloc(len << 1); - if(ret == 0l) { - throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + if(len != 0) { + long ret = JEmalloc.nje_malloc(len << 1); + if(ret == 0l) { + throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + } + if(enableAllocCount) ++allocCount; + return new EaglerLWJGLShortBuffer(ret, len, true); + }else { + return ZERO_LENGTH_SHORT_BUFFER; } - if(enableAllocCount) ++allocCount; - return new EaglerLWJGLShortBuffer(ret, len, true); } + private static final IntBuffer ZERO_LENGTH_INT_BUFFER = new EaglerLWJGLIntBuffer(0l, 0, true); + public static IntBuffer allocIntBuffer(int len) { - long ret = JEmalloc.nje_malloc(len << 2); - if(ret == 0l) { - throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + if(len != 0) { + long ret = JEmalloc.nje_malloc(len << 2); + if(ret == 0l) { + throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + } + if(enableAllocCount) ++allocCount; + return new EaglerLWJGLIntBuffer(ret, len, true); + }else { + return ZERO_LENGTH_INT_BUFFER; } - if(enableAllocCount) ++allocCount; - return new EaglerLWJGLIntBuffer(ret, len, true); } + private static final FloatBuffer ZERO_LENGTH_FLOAT_BUFFER = new EaglerLWJGLFloatBuffer(0l, 0, true); + public static FloatBuffer allocFloatBuffer(int len) { - long ret = JEmalloc.nje_malloc(len << 2); - if(ret == 0l) { - throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + if(len != 0) { + long ret = JEmalloc.nje_malloc(len << 2); + if(ret == 0l) { + throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + } + if(enableAllocCount) ++allocCount; + return new EaglerLWJGLFloatBuffer(ret, len, true); + }else { + return ZERO_LENGTH_FLOAT_BUFFER; } - if(enableAllocCount) ++allocCount; - return new EaglerLWJGLFloatBuffer(ret, len, true); } public static void freeByteBuffer(ByteBuffer buffer) { if(buffer instanceof EaglerLWJGLByteBuffer) { EaglerLWJGLByteBuffer buf = (EaglerLWJGLByteBuffer)buffer; if(buf.original) { - JEmalloc.nje_free(buf.address); - if(enableAllocCount) --allocCount; + if(buf.address != 0l) { + JEmalloc.nje_free(buf.address); + if(enableAllocCount) --allocCount; + } }else { throwNotOriginal(buffer); } @@ -96,8 +122,10 @@ public class EaglerLWJGLAllocator { if(buffer instanceof EaglerLWJGLShortBuffer) { EaglerLWJGLShortBuffer buf = (EaglerLWJGLShortBuffer)buffer; if(buf.original) { - JEmalloc.nje_free(buf.address); - if(enableAllocCount) --allocCount; + if(buf.address != 0l) { + JEmalloc.nje_free(buf.address); + if(enableAllocCount) --allocCount; + } }else { throwNotOriginal(buffer); } @@ -119,8 +147,10 @@ public class EaglerLWJGLAllocator { if(buffer instanceof EaglerLWJGLIntBuffer) { EaglerLWJGLIntBuffer buf = (EaglerLWJGLIntBuffer)buffer; if(buf.original) { - JEmalloc.nje_free(buf.address); - if(enableAllocCount) --allocCount; + if(buf.address != 0l) { + JEmalloc.nje_free(buf.address); + if(enableAllocCount) --allocCount; + } }else { throwNotOriginal(buffer); } @@ -142,8 +172,10 @@ public class EaglerLWJGLAllocator { if(buffer instanceof EaglerLWJGLFloatBuffer) { EaglerLWJGLFloatBuffer buf = (EaglerLWJGLFloatBuffer)buffer; if(buf.original) { - JEmalloc.nje_free(buf.address); - if(enableAllocCount) --allocCount; + if(buf.address != 0l) { + JEmalloc.nje_free(buf.address); + if(enableAllocCount) --allocCount; + } }else { throwNotOriginal(buffer); } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/Display.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/Display.java index 5f09109..cd4c9e6 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/Display.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/Display.java @@ -19,7 +19,6 @@ import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; */ public class Display { - private static long lastSwap = 0l; private static long lastDPIUpdate = -250l; private static float cacheDPI = 1.0f; @@ -79,41 +78,6 @@ public class Display { PlatformInput.update(limitFramerate); } - private static final long[] defaultSyncPtr = new long[1]; - - public static void sync(int limitFramerate) { - sync(limitFramerate, defaultSyncPtr); - } - - public static boolean sync(int limitFramerate, long[] timerPtr) { - boolean limitFPS = limitFramerate > 0 && limitFramerate < 1000; - boolean blocked = false; - - if(limitFPS) { - if(timerPtr[0] == 0l) { - timerPtr[0] = EagRuntime.steadyTimeMillis(); - }else { - long millis = EagRuntime.steadyTimeMillis(); - long frameMillis = (1000l / limitFramerate); - long frameTime = millis - timerPtr[0]; - if(frameTime > 2000l || frameTime < 0l) { - frameTime = frameMillis; - timerPtr[0] = millis; - }else { - timerPtr[0] += frameMillis; - } - if(frameTime >= 0l && frameTime < frameMillis) { - EagUtils.sleep(frameMillis - frameTime); - blocked = true; - } - } - }else { - timerPtr[0] = 0l; - } - - return blocked; - } - public static boolean contextLost() { return PlatformInput.contextLost(); } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/EagRuntime.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/EagRuntime.java index c4f049c..1c028ce 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/EagRuntime.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/EagRuntime.java @@ -375,4 +375,8 @@ public class EagRuntime { PlatformRuntime.immediateContinue(); } + public static boolean immediateContinueSupported() { + return PlatformRuntime.immediateContinueSupported(); + } + } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglerInputStream.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglerInputStream.java index bf1466c..b336837 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglerInputStream.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglerInputStream.java @@ -142,6 +142,16 @@ public class EaglerInputStream extends InputStream { } } + public static byte[] inputStreamToBytesNoClose(InputStream is) throws IOException { + EaglerOutputStream os = new EaglerOutputStream(1024); + byte[] buf = new byte[1024]; + int i; + while ((i = is.read(buf)) != -1) { + os.write(buf, 0, i); + } + return os.toByteArray(); + } + public byte[] getAsArray() { if (pos == 0 && count == buf.length) { return buf; diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java index 1011c6d..49e23be 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java +++ b/sources/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 = "u44"; + public static final String projectForkVersion = "u45"; 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 = "u44"; + public static final String projectOriginVersion = "u45"; public static final String projectOriginURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8"; // rest in peace // EPK Version Identifier - public static final String EPKVersionIdentifier = "u44"; // Set to null to disable EPK version check + public static final String EPKVersionIdentifier = "u45"; // 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 = 44; + public static final int updateBundlePackageVersionInt = 45; public static final String updateLatestLocalStorageKey = "latestUpdate_" + updateBundlePackageName; diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFolderResourcePack.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFolderResourcePack.java index 4a71340..aecdb25 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFolderResourcePack.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFolderResourcePack.java @@ -211,7 +211,7 @@ public class EaglerFolderResourcePack extends AbstractResourcePack { i += j; } }else { - buffer = EaglerInputStream.inputStreamToBytes(ziss); + buffer = EaglerInputStream.inputStreamToBytesNoClose(ziss); } (new VFile2(prefix, folderName, fn.substring(prefixLen))).setAllBytes(buffer); totalSize += buffer.length; diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java index 7265587..bcd3878 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java @@ -930,6 +930,8 @@ public class EaglercraftGPU { InstancedParticleRenderer.destroy(); EffectPipelineFXAA.destroy(); TextureCopyUtil.destroy(); + FixedFunctionPipeline.flushCache(); + StreamBuffer.destroyPool(); emulatedVAOs = false; emulatedVAOState = null; glesVers = -1; diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionPipeline.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionPipeline.java index 8b65c2e..0271cfc 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionPipeline.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionPipeline.java @@ -76,8 +76,8 @@ public class FixedFunctionPipeline { StreamBufferInstance sb = self.streamBuffer.getBuffer(buffer.remaining()); self.currentVertexArray = sb; - EaglercraftGPU.bindGLBufferArray(sb.vertexArray); - EaglercraftGPU.bindGLArrayBuffer(sb.vertexBuffer); + EaglercraftGPU.bindGLBufferArray(sb.getVertexArray()); + EaglercraftGPU.bindGLArrayBuffer(sb.getVertexBuffer()); _wglBufferSubData(GL_ARRAY_BUFFER, 0, buffer); diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/StreamBuffer.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/StreamBuffer.java index 252830e..01468af 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/StreamBuffer.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/StreamBuffer.java @@ -7,7 +7,7 @@ import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; /** - * Copyright (c) 2023 lax1dude. All Rights Reserved. + * Copyright (c) 2023-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 @@ -23,10 +23,48 @@ import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; */ public class StreamBuffer { + public static final int poolSize = 16; + public final int initialSize; public final int initialCount; public final int maxCount; + protected static final PoolInstance[] pool = new PoolInstance[poolSize]; + protected static int poolBufferID = 0; + + static { + for(int i = 0; i < poolSize; ++i) { + pool[i] = new PoolInstance(); + } + } + + protected static class PoolInstance { + + protected IBufferGL vertexBuffer = null; + protected int vertexBufferSize = 0; + + } + + private static PoolInstance fillPoolInstance() { + PoolInstance ret = pool[poolBufferID++]; + if(poolBufferID > poolSize - 1) { + poolBufferID = 0; + } + return ret; + } + + private static void resizeInstance(PoolInstance instance, int requiredMemory) { + if(instance.vertexBuffer == null) { + instance.vertexBuffer = _wglGenBuffers(); + } + if(instance.vertexBufferSize < requiredMemory) { + int newSize = (requiredMemory & 0xFFFFF000) + 0x2000; + EaglercraftGPU.bindGLArrayBuffer(instance.vertexBuffer); + _wglBufferData(GL_ARRAY_BUFFER, newSize, GL_STREAM_DRAW); + instance.vertexBufferSize = newSize; + } + } + protected StreamBufferInstance[] buffers; protected int currentBufferId = 0; @@ -36,9 +74,8 @@ public class StreamBuffer { public static class StreamBufferInstance { + protected PoolInstance poolInstance = null; protected IBufferArrayGL vertexArray = null; - protected IBufferGL vertexBuffer = null; - protected int vertexBufferSize = 0; public boolean bindQuad16 = false; public boolean bindQuad32 = false; @@ -48,7 +85,7 @@ public class StreamBuffer { } public IBufferGL getVertexBuffer() { - return vertexBuffer; + return poolInstance.vertexBuffer; } } @@ -58,9 +95,14 @@ public class StreamBuffer { } public StreamBuffer(int initialSize, int initialCount, int maxCount, IStreamBufferInitializer initializer) { + if(maxCount > poolSize) { + maxCount = poolSize; + } this.buffers = new StreamBufferInstance[initialCount]; for(int i = 0; i < this.buffers.length; ++i) { - this.buffers[i] = new StreamBufferInstance(); + StreamBufferInstance j = new StreamBufferInstance(); + j.poolInstance = fillPoolInstance(); + this.buffers[i] = j; } this.initialSize = initialSize; this.initialCount = initialCount; @@ -70,18 +112,10 @@ public class StreamBuffer { public StreamBufferInstance getBuffer(int requiredMemory) { StreamBufferInstance next = buffers[(currentBufferId++) % buffers.length]; - if(next.vertexBuffer == null) { - next.vertexBuffer = _wglGenBuffers(); - } + resizeInstance(next.poolInstance, requiredMemory); if(next.vertexArray == null) { next.vertexArray = EaglercraftGPU.createGLBufferArray(); - initializer.initialize(next.vertexArray, next.vertexBuffer); - } - if(next.vertexBufferSize < requiredMemory) { - int newSize = (requiredMemory & 0xFFFFF000) + 0x2000; - EaglercraftGPU.bindGLArrayBuffer(next.vertexBuffer); - _wglBufferData(GL_ARRAY_BUFFER, newSize, GL_STREAM_DRAW); - next.vertexBufferSize = newSize; + initializer.initialize(next.vertexArray, next.poolInstance.vertexBuffer); } return next; } @@ -102,12 +136,10 @@ public class StreamBuffer { if(buffers[i].vertexArray != null) { EaglercraftGPU.destroyGLBufferArray(buffers[i].vertexArray); } - if(buffers[i].vertexBuffer != null) { - _wglDeleteBuffers(buffers[i].vertexBuffer); - } } } buffers = newArray; + refill(); } overflowCounter = 0; }else if(overflowCounter > 15) { @@ -125,25 +157,51 @@ public class StreamBuffer { } } buffers = newArray; + refill(); } overflowCounter = 0; } currentBufferId = 0; } + private void refill() { + for(int i = 0; i < buffers.length; ++i) { + PoolInstance j = fillPoolInstance(); + StreamBufferInstance k = buffers[i]; + if(j != k.poolInstance) { + PoolInstance l = k.poolInstance; + k.poolInstance = j; + if(k.vertexArray != null) { + if(j.vertexBuffer == null) { + resizeInstance(j, l.vertexBufferSize); + } + initializer.initialize(k.vertexArray, j.vertexBuffer); + } + } + } + } + public void destroy() { for(int i = 0; i < buffers.length; ++i) { StreamBufferInstance next = buffers[i]; if(next.vertexArray != null) { EaglercraftGPU.destroyGLBufferArray(next.vertexArray); } - if(next.vertexBuffer != null) { - _wglDeleteBuffers(next.vertexBuffer); - } } buffers = new StreamBufferInstance[initialCount]; - for(int i = 0; i < buffers.length; ++i) { - buffers[i] = new StreamBufferInstance(); + for(int i = 0; i < initialCount; ++i) { + StreamBufferInstance j = new StreamBufferInstance(); + j.poolInstance = fillPoolInstance(); + buffers[i] = j; + } + } + + public static void destroyPool() { + for(int i = 0; i < pool.length; ++i) { + if(pool[i].vertexBuffer != null) { + _wglDeleteBuffers(pool[i].vertexBuffer); + pool[i].vertexBuffer = null; + } } } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/SingleplayerServerController.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/SingleplayerServerController.java index 04b7a39..c2a3a91 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/SingleplayerServerController.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/SingleplayerServerController.java @@ -153,7 +153,7 @@ public class SingleplayerServerController implements ISaveFormat { } public static boolean isChannelNameAllowed(String ch) { - return !IPC_CHANNEL.equals(ch) && !PLAYER_CHANNEL.equals(ch); + return !ch.startsWith("~!"); } public static void openPlayerChannel(String ch) { diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientPeer.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientPeer.java index 356133e..0ba839a 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientPeer.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientPeer.java @@ -1,5 +1,6 @@ package net.lax1dude.eaglercraft.v1_8.sp.lan; +import java.util.LinkedList; import java.util.List; import net.lax1dude.eaglercraft.v1_8.EagRuntime; @@ -42,6 +43,8 @@ class LANClientPeer { protected long startTime; protected String localICECandidate = null; + protected boolean localChannel = false; + protected List packetPreBuffer = null; protected LANClientPeer(String clientId) { this.clientId = clientId; @@ -75,7 +78,19 @@ class LANClientPeer { protected void handleSuccess() { if(state == SENT_ICE_CANDIDATE) { - state = RECEIVED_SUCCESS; + if(localChannel) { + SingleplayerServerController.openPlayerChannel(clientId); + PlatformWebRTC.serverLANPeerMapIPC(clientId, clientId); + if(packetPreBuffer != null) { + for(byte[] b : packetPreBuffer) { + ClientPlatformSingleplayer.sendPacket(new IPCPacketData(clientId, b)); + } + packetPreBuffer = null; + } + state = CONNECTED; + }else { + state = RECEIVED_SUCCESS; + } }else if(state != CONNECTED) { logger.error("Relay [{}] unexpected IPacket05ClientSuccess for '{}'", LANServerController.lanRelaySocket.getURI(), clientId); } @@ -113,13 +128,7 @@ class LANClientPeer { localICECandidate = ((LANPeerEvent.LANPeerICECandidateEvent)evt).candidates; continue read_loop; } - } - case RECEIVED_ICE_CANDIDATE: { - if(evt instanceof LANPeerEvent.LANPeerICECandidateEvent) { - LANServerController.lanRelaySocket.writePacket(new RelayPacket03ICECandidate(clientId, ((LANPeerEvent.LANPeerICECandidateEvent)evt).candidates)); - state = SENT_ICE_CANDIDATE; - continue read_loop; - } + break; } case RECEIVED_DESCRIPTION: { if(evt instanceof LANPeerEvent.LANPeerDescriptionEvent) { @@ -127,15 +136,50 @@ class LANClientPeer { state = SENT_DESCRIPTION; continue read_loop; } + break; + } + case RECEIVED_ICE_CANDIDATE: { + if(evt instanceof LANPeerEvent.LANPeerICECandidateEvent) { + LANServerController.lanRelaySocket.writePacket(new RelayPacket03ICECandidate(clientId, ((LANPeerEvent.LANPeerICECandidateEvent)evt).candidates)); + state = SENT_ICE_CANDIDATE; + continue read_loop; + }else if(evt instanceof LANPeerEvent.LANPeerDataChannelEvent) { + localChannel = true; + continue read_loop; + }else if(evt instanceof LANPeerEvent.LANPeerPacketEvent) { + if(packetPreBuffer == null) packetPreBuffer = new LinkedList<>(); + packetPreBuffer.add(((LANPeerEvent.LANPeerPacketEvent)evt).payload); + continue read_loop; + } + break; + } + case SENT_ICE_CANDIDATE: { + if(evt instanceof LANPeerEvent.LANPeerDataChannelEvent) { + localChannel = true; + continue read_loop; + }else if(evt instanceof LANPeerEvent.LANPeerPacketEvent) { + if(packetPreBuffer == null) packetPreBuffer = new LinkedList<>(); + packetPreBuffer.add(((LANPeerEvent.LANPeerPacketEvent)evt).payload); + continue read_loop; + } + break; } - case SENT_ICE_CANDIDATE: case RECEIVED_SUCCESS: { if(evt instanceof LANPeerEvent.LANPeerDataChannelEvent) { SingleplayerServerController.openPlayerChannel(clientId); PlatformWebRTC.serverLANPeerMapIPC(clientId, clientId); + if(packetPreBuffer != null) { + for(byte[] b : packetPreBuffer) { + ClientPlatformSingleplayer.sendPacket(new IPCPacketData(clientId, b)); + } + packetPreBuffer = null; + } state = CONNECTED; continue read_loop; + }else if(evt instanceof LANPeerEvent.LANPeerICECandidateEvent) { + continue read_loop; } + break; } case CONNECTED: { if(evt instanceof LANPeerEvent.LANPeerPacketEvent) { @@ -144,6 +188,7 @@ class LANClientPeer { ClientPlatformSingleplayer.sendPacket(new IPCPacketData(clientId, ((LANPeerEvent.LANPeerPacketEvent)evt).payload)); continue read_loop; } + break; } default: { break; diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANServerController.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANServerController.java index 4b2a5e1..d920db7 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANServerController.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANServerController.java @@ -12,6 +12,7 @@ import net.lax1dude.eaglercraft.v1_8.EagUtils; import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServerSocket; import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.*; @@ -142,7 +143,9 @@ public class LANServerController { while((pkt = lanRelaySocket.readPacket()) != null) { if(pkt instanceof RelayPacket02NewClient) { RelayPacket02NewClient ipkt = (RelayPacket02NewClient) pkt; - if(clients.containsKey(ipkt.clientId)) { + if(!SingleplayerServerController.isChannelNameAllowed(ipkt.clientId)) { + logger.error("Relay [{}] relay tried to open disallowed channel name: '{}'", lanRelaySocket.getURI(), ipkt.clientId); + }else if(clients.containsKey(ipkt.clientId)) { logger.error("Relay [{}] relay provided duplicate client '{}'", lanRelaySocket.getURI(), ipkt.clientId); }else { clients.put(ipkt.clientId, new LANClientPeer(ipkt.clientId)); diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterEPK.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterEPK.java index 524de85..ed4b749 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterEPK.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterEPK.java @@ -103,11 +103,10 @@ public class WorldConverterEPK { public static byte[] exportWorld(String worldName) { String realWorldName = worldName; String worldOwner = "UNKNOWN"; - String splitter = new String(new char[] { (char)253, (char)233, (char)233 }); - if(worldName.contains(splitter)) { - int i = worldName.lastIndexOf(splitter); - worldOwner = worldName.substring(i + 3); - realWorldName = worldName.substring(0, i); + int j = worldName.lastIndexOf(new String(new char[] { (char)253, (char)233, (char)233 })); + if(j != -1) { + worldOwner = worldName.substring(j + 3); + realWorldName = worldName.substring(0, j); } VFile2 worldDir = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(realWorldName, false).getWorldDirectory(); logger.info("Exporting world directory \"{}\" as EPK", worldDir.getPath()); diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterMCA.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterMCA.java index 7e5bfa5..13a7c14 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterMCA.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterMCA.java @@ -70,7 +70,6 @@ public class WorldConverterMCA { ZipEntry f = null; int lastProgUpdate = 0; int prog = 0; - byte[] bb = new byte[16384]; while ((f = zis.getNextEntry()) != null) { if (f.getName().contains("__MACOSX/")) continue; if (f.isDirectory()) continue; @@ -85,7 +84,7 @@ public class WorldConverterMCA { j += k; } }else { - b = EaglerInputStream.inputStreamToBytes(zis); + b = EaglerInputStream.inputStreamToBytesNoClose(zis); } String fileName = f.getName().substring(folderPrefixOffset); if (fileName.equals("level.dat") || fileName.equals("level.dat_old")) { diff --git a/sources/resources/EPKVersionIdentifier.txt b/sources/resources/EPKVersionIdentifier.txt index 15a5b3c..9f9fcca 100644 --- a/sources/resources/EPKVersionIdentifier.txt +++ b/sources/resources/EPKVersionIdentifier.txt @@ -1 +1 @@ -u44 \ No newline at end of file +u45 \ No newline at end of file diff --git a/sources/setup/workspace_template/wasm_gc_teavm/CompileLoaderWASM.bat b/sources/setup/workspace_template/wasm_gc_teavm/CompileLoaderWASM.bat index 86c08b2..f77b11c 100644 --- a/sources/setup/workspace_template/wasm_gc_teavm/CompileLoaderWASM.bat +++ b/sources/setup/workspace_template/wasm_gc_teavm/CompileLoaderWASM.bat @@ -5,5 +5,5 @@ call emcc -c -O3 ../src/wasm-gc-teavm-loader/c/main.c -o bin/emscripten/main.o call emcc -c -O3 ../src/wasm-gc-teavm-loader/c/xz/xz_crc32.c -o bin/emscripten/xz_crc32.o call emcc -c -O3 ../src/wasm-gc-teavm-loader/c/xz/xz_dec_lzma2.c -o bin/emscripten/xz_dec_lzma2.o call emcc -c -O3 ../src/wasm-gc-teavm-loader/c/xz/xz_dec_stream.c -o bin/emscripten/xz_dec_stream.o -call emcc -O3 -sMALLOC=dlmalloc -sALLOW_MEMORY_GROWTH -sINITIAL_HEAP=16777216 -sMAXIMUM_MEMORY=67108864 --pre-js ../src/wasm-gc-teavm-loader/js/pre.js --js-library ../src/wasm-gc-teavm-loader/js/library.js bin/emscripten/main.o bin/emscripten/xz_crc32.o bin/emscripten/xz_dec_lzma2.o bin/emscripten/xz_dec_stream.o -o javascript/loader.js +call emcc -O3 -sMALLOC=dlmalloc -sALLOW_MEMORY_GROWTH -sINITIAL_HEAP=16777216 -sMAXIMUM_MEMORY=67108864 --closure 1 --closure-args=--isolation_mode=IIFE --closure-args=--emit_use_strict --pre-js ../src/wasm-gc-teavm-loader/js/pre.js --js-library ../src/wasm-gc-teavm-loader/js/library.js bin/emscripten/main.o bin/emscripten/xz_crc32.o bin/emscripten/xz_dec_lzma2.o bin/emscripten/xz_dec_stream.o -o javascript/loader.js pause \ No newline at end of file diff --git a/sources/setup/workspace_template/wasm_gc_teavm/CompileLoaderWASM.sh b/sources/setup/workspace_template/wasm_gc_teavm/CompileLoaderWASM.sh index aa7b022..e528864 100644 --- a/sources/setup/workspace_template/wasm_gc_teavm/CompileLoaderWASM.sh +++ b/sources/setup/workspace_template/wasm_gc_teavm/CompileLoaderWASM.sh @@ -4,4 +4,4 @@ emcc -c -O3 ../src/wasm-gc-teavm-loader/c/main.c -o bin/emscripten/main.o emcc -c -O3 ../src/wasm-gc-teavm-loader/c/xz/xz_crc32.c -o bin/emscripten/xz_crc32.o emcc -c -O3 ../src/wasm-gc-teavm-loader/c/xz/xz_dec_lzma2.c -o bin/emscripten/xz_dec_lzma2.o emcc -c -O3 ../src/wasm-gc-teavm-loader/c/xz/xz_dec_stream.c -o bin/emscripten/xz_dec_stream.o -emcc -O3 -sMALLOC=dlmalloc -sALLOW_MEMORY_GROWTH -sINITIAL_HEAP=16777216 -sMAXIMUM_MEMORY=67108864 --pre-js ../src/wasm-gc-teavm-loader/js/pre.js --js-library ../src/wasm-gc-teavm-loader/js/library.js bin/emscripten/main.o bin/emscripten/xz_crc32.o bin/emscripten/xz_dec_lzma2.o bin/emscripten/xz_dec_stream.o -o javascript/loader.js +emcc -O3 -sMALLOC=dlmalloc -sALLOW_MEMORY_GROWTH -sINITIAL_HEAP=16777216 -sMAXIMUM_MEMORY=67108864 --closure 1 --closure-args=--isolation_mode=IIFE --closure-args=--emit_use_strict --pre-js ../src/wasm-gc-teavm-loader/js/pre.js --js-library ../src/wasm-gc-teavm-loader/js/library.js bin/emscripten/main.o bin/emscripten/xz_crc32.o bin/emscripten/xz_dec_lzma2.o bin/emscripten/xz_dec_stream.o -o javascript/loader.js diff --git a/sources/setup/workspace_template/wasm_gc_teavm/buildtools/MakeWASMClientBundle.jar b/sources/setup/workspace_template/wasm_gc_teavm/buildtools/MakeWASMClientBundle.jar index 6a1c494bfd57fd374c2b07526af848e78e2101ac..48aa8bdfd52366d6b93334c2836a1d625e0992e8 100644 GIT binary patch delta 8786 zcmZ9yWl$VU)3!~}1r~Qr0>LG?1`TeDySuv+*x>H2!JWk&La@bU(Zww|1P>A*Z>~Sz zbJy2@rmLrVrfRy*shK)@PJ31!OH~mGnGgX003c+PB_v}RBPIVEoE^aG${+$YLXpHE zB{C>iNHJVeqTAM{A3hp_Y*EacBQH+E8{Y7~p1a*^`wBXV_EfT-Brd1>fb^^e{XhWWgaDVC7>N5JuJMcIv5tfY@9_ge%p6Dh-n!U1mGff>jka3B z2j-hT`eV1>(h)x|#x20B0V448nk>)P^4a9#-v-$eZcoY86Jz81G$&V719qTHPyZtn)Wq&piMaWg!XCpBcg%No}3SWPMf!louPVf z0+I;rko5wWi8CBV}Ix{>R2t}0V2JeV9|bYaqI zVZ(CpxDaBTB@R3DciF0A$ZTTrfwRoCTKfBF4{90h%}D~;Xm7JJ*9(LzW<)AHx&wm# zK3-w|pfp#3lVbBgCYbhn#R?KVvRR@DXR7t~H8VfHxP)BGi)P21W-Bo>^i5dKc#wc& z#`mfA^WN4Cdk<87SiV&?j;(lrLtnj>W)8GA(1kz$$#$>8tt?pD0ob|yvkXOe*bJzorTSOww-&0Vk)&P9<7w83a<&nQrWf)Z ziJVPo+fR^`^5G-*fRl=7d7KX|Kl*2hmF>Uqe-qtVAsLU>?Zf%Ie+Yerp0a~!`PuOg zh=N?Wd6=pdCd3YGR5!FWx!dH+wt<5zEEv?03gG3IFzxl~rCcjHUE9m8DP1O^5m!rj zwZ?E}>4WeM6m+LnZzSNi+DgZhmg`&7UBs#c+g*U&b{aG2)--(KLK>+JEeO9W1WrLTeDZ0E2&g7hE?#Ogl zX1+v@^Y=$abNMC} zkMhOXn*e9gLsZPGfmAeCd{-)R>G3UGW-nQsMO6HAD)^omN(-N@Xf+$Ab6EhSL`npF zQPSLJDR{!ta3YP?Ua$0#G`K%VLW>lbpvMvr*AfG+m+nJcao_O2FEhl(CHTFAE;op~(kiP#Eca4aqqxes4 z3Yh;t#pxmZk17B2ol_N1RKW&|unTa@2FtO6_Zj2#R7Cz_Biow%j}PPa!k8+K3<+4# z1n-j19b}^&$>gU8@G~O#Q1k(_>WnA@>g~aE4VvD&j;vS)D;RGlM)a3vuNzwk5s{y# zI`vrvu}#1i$@kPf<}aQzliA!h)027Gg0lRdp6-4HBB<~%;se7_oWS}WqZbQ~GHXW77d>gKI1@lI5*{0w+NZSO|C z>+evTCy?sSMf=)@);bab!Vob~{3DjWn`tq^bxAi#3y8w?!L1GasHj2%=Ttjq2VeW- zF{hg(hF#GIQ%Y5Gbo&8uALPyv_id;;+|ks7!w7cwga<^yYIYn)8=A+M;ZCF|HDNGh zy6HesOvlgIPz6y&=zFMNlhf#+ zZhwnz{|6;&Fh!f~&Rm4*k1agw^RQ)H)I*g|`tF>+q)-@&!ZrrhHX*INch3bJZ7aXX-*|M5Fgub$$pNM@F!qXUp=r^jw?~N>u6d|tvA(2M zo~DWIMvkdzDEtiuu?2enj+1@$xvdM1eNj>dj~g>5h}D8g0*i=~DYKx$S^&+emD8d(N z`A(bKU}fkras@(bVSv-<#$-;08@-qKYH!@DIsuulK27t&fa?82%y zEM?43UeC=lmDtwy*C}CyqxNhFuNvKuc6T(#tI4&P@h^RCcjo(s11EdiXysov&|+>% zcZc)S^#QfDuzEBAtvgG?IgD%CynQxX4uB^EzQ@ykc+rntHX(OBw%r%V1vqmF>HeB? zZ8N!v7+6VdI%LyJVJT4ztWr}a0riJ>L|Hg7o?%6Np7>&}fkPUv4WKt5G>m)e9tZH^3A@FX5cyGEfq7B~ z#t34~nxI7RTtf7#3YoYhzy=1@)k#l6AB!bD4cUE7-d{@$iv#HlBM$iau2K^)dnC;wzDH$I0-}gTk(VX4a!|=VpQw5g`QK=ha`%>nH5K8V$tsD z)VAWH1<#VVi_PM@{?Qk78GgQlzWJjM4m6K+KC~?vp=xh;N)5KIz zV}-+GbtrBGQ|sH$-#jzAZ~(TH0Xtt9zofnTOxinDZXEuy-3$NoOhX8bcrGAWsyv{*YjCiV?T& zYLb0)cg54vO65QETD!tGPQG-HQSISCso&+@B+TdNg;Wfo%?W*+V zZ!lvq2kXu@q-4UCEUj})VZ4&IB_O!dq7HZX98#n8o?=D% z9Ir%ytAj2xoXC)pk}9=xNFhQ}EoO9RB%L5(F_)^2!IE5x)JKV?_x1?D8*@&pL9dvq z#LOJb$>rwfg8SMT-$Y2RVE?VE99KV~WUsXR$%)6X2#=@9<%s|tJdrSQ`|lh)85q;@ z&$1ZCh9ZkN$H?_{yGv<=nH(%VhO6zIxZWONKzPEKq9z0)Xnl^0qaO$nco(pA)?Nnbq!e~&C00{Bfx5A1%1H;V z?pmyzj|>WoSiJ%7{%DP-XW|iD=>-vw5)cV7`59>ln#_)2C@<1>edMWc73q=xnbfyp zD>Rj6EA#wriMUx(Pu7Eb8JQ>6+M{6;_qNT$5m&g!9ii!1o4L$AdVH6OeX~~p{vm6U zPEP$V$JK!q`{eSH z45k*tLQ9$agF7EvK7EM_#g-J+0Ln?oESabEH>=pb#i7()V2fyoW8)Hkvm%tEZVHGzBjnIZr_%ltd{@%WLBW}HN5kh0d>R_-G zquKowQGc>XXic4tU7Kxqc%mmD6Cx1d0;tBqa4J zNClk%Q1zaKRPwb%`?g5tCMv>1jvq0M#7Sa=AqiktD{(2ggU(CcPwg-q!c|EUhPpJC zZ&V`;!Bq7ee`v$o%2~~tei#?emU-&oXK>%xjaZoxalNE*)UKbpoL$PM8Z_5WozAeE z=!;r475s$At=!oBS&h9Fr7)VVif6B=A?(U|`0(#;KSazEF)^R1$DJO{Uyf2yZh`EZ ziXVc!)CHqRYA1!x7tJt)=?9M4V#!D$4YnA!kAk+;ir#^q> z`8{+M=@q9&);LFG+SN*=`VzJ72a(rVD+&;PA#^0EbA=}0< zEC%TK6fd076OOptG$B1Ls0X=48i(kdI+#s( zBJ)BD{*DM)LEU<; zJymH$#}7XgTkes&f$*$9`>y5m^p{^VQmP-l_s=pqS?GGKfe#EaCz9ip5R$mvR!Szq zz7>xNdnb^k2b{h4rzVpHMX^4V%CY+MjTcoA8Oj{4G<+>kJJNf?`DOl@m#$bUTx!Nc z3V>`7GeC9Pv&m-ygEq%9>u9E{7*m2@s77RFV6l>qAaX1Ow9glUwMy~0tqof>p!OY zMLP8*n@4q>N&lNNvU=q4q+a~7!(YU|3B!7;@-S4$C5a@zV@dl65J=<&b$5}Cq8nA{ z#69^l^_gDu1t)igK>+*9jyNCq?fmc)^;N`n>OMLKD36gN_-IYIyNOrIRMML`G|77>^{)B#@=?l%lsSsppVz z60_8lsR{fhMY8l}J~WFEx7$Y-Dz#D$@xXGnqUEVm5v-^*mH&uXFW%xhR+B<=%x@1Cq4$AuUGa2tpA65_NO{=Q{^V0UOfKFg<5W!usWC6)#qhT% zjQ`|)&G;)03TO!(jsE>)-us&oPKrci5j|K}??+X?vL98*TO#t^acWO9ALsqxeg}50 zKW^;|z{l+6_4StG>1cj|$Jh}{57KAASZs;&pb@yxhcEJIAd7)2v29ygcYxwEEpR!u8eV|y9BGZgd<)lJW`EJeP z6|%5)=xpdZSs(FH9ODx|r)RmaMWi@RLh(gZkx>_a*6!wIe9$(TWC80nP+1_P8e2_k z2fg|dnfCx)4-uT^r8>2GK~`;z4kA4n!!JlG!zL>DfwP0I_h<1vnNJhr23#*?_&f^N zjgh}gI`q-zj5ywG9ZpL=*x3l;k#XJoY8&?x`clkUjfT^dh9<&LdG-tEe82)3q{2{M zDcF6<)84#z1}M0X)0VaADWs6o0;$SY7_7+jB)}X;jld5e+6?rkW%T7c(UdNcP4m`D z^(JG@DvAr=CJn$(LPR~>pQX6E8i3jmQ%{Uww(uYH0BDj8s~@y#Tw+qOsI_8t73CaG zh)5SMj)(m3?YDR16Fjdu7N%mDenH-c`Pb^l8^@cU)tws;0wG}I*j++CHBbBsM!M!x z=#C0&44B~UBdYn1oe*r<94G!zBuzdFzwzEHHCWGy3vCL6jF4z1WC$T*=DSfv0Xcd-WN%OYv4SsD+Q^?|exUqx zORZ3)^1-#ZWv@6QXX#oO%46SlLuBX4ARJcG0Ix?sK0=Da?C6KX#!7)j-)W2jL-pQy zsq=9J< z0z-4Dc||t!Al5^<+zb6z2TB%WwORVsKD(t~mTSZG4=SHWFrt#ZH6#et^2C)^xMB^P z4V&BVMaLcE=$aP^yw%VzT+WE<>?S=Ee&(dI*XtQ^t?>pVo-t`Dc8_`RPespu0cGnS z>_P{uLOODA<5@g}Va3ynd*g4auhMGv!QGV^YjDa;zXI|6jF6oAjH;Wis!gpPeVfY9 zHRe-~OjI{}R2~+MM>Z!v0e*_sRjG?}0A8sgL0~*cybP!;CvFAYr!RDmpr+awd#p^EPw$bZO{i&(TUvGPk?YQ-stxRW8`l!~*JdRWHJxp~s-cuuNDON_N zhZCMobV^aJ5^W$ADGTt`0*->UK}yGzEZe>|5Px+Ao8Yf|`NQ_|3Ww2%2|g-XAeH01 z_uIaQlwS4+?9a&|CoRzUhjYXP5WaW%?>k3Lh9R{48Wj*V4}b(PR4vdltd+6Qt_7ajAaOA%pO&NY z4Hy4_YJAQNI{6bW#L0Q-?`3`RBNv&5^HO_xz4ZH5Xs;IX1hO|Se$fnh)mskv@I21YUR5hc zY_M5PTpp(`1!=C{Q%^efpU_pO!bFP|Ze-Zf$_Jn+3ZQpZ&o>JKmn*d(=0?~d?reLa8ZxcjLOijlk@)v9G*CE22j z(WfdhaU@DI4nI8S=8cwIZx)EY@bZ7j*QjToEVW}Gf>WN{!dO@ zQf_a&8xXFT7=kD~(N}?Z!RRiKOVt zZ}0jxW0z@Rmsivu{8i(MQ)Q4rH=@2Fk8ok$HBY6)71hs`ehSK*6T7GxPp^*|k7)bv zKV}OSS(-(;glX57u?aIrE3y0wiyF25yCfj&PUeZQ$a)a9daNR5QRdXWXD) zgn6Dz<&W@)vX0T&uWw1ez|A{|#CXnz5`o_ipj=sQ5(t_c#2p!k#9jfP{%lGX3 zlu#n5rz5xZj)Da?gsWiEl?lSh{^Mhuu_4&OF!VHzWG55)cF%v0?IGO_&6z_qiXuL7 zhklO$?)-(EpW@pf8#K|i=8AjWnmo)TH)6NF{0eTC6SAh<>>6oD7i6O!Dy)2Z88EMX z#UeJU~-44Hs zv*IXd1&(-RP=0b58&u3#@f~)@Fm7Jh{+Ka;6;X!bq95Wr;ZD8T<~S^zQF95|NA0nB zWP7#mBmp;{jr_@wy$aq(`C{uw@fNHnIy{mRhVvnzdCv4%V|<`O9a}W@+Eq6vWTwpS zK{6-tW0^f=^3o2A9ryQ)wj!ObF)q!c#9p*>twhc=Q>J!JGoF#Do?r+DS^S*0 zt34}oyCx_9h-O<%S^CKjX@4TOO8k3P%+H!i-S5%#W5TvgUp@~0Ir{LJ$fFz-_vspw zXRvyfwI8mTwx%}KGG3*{CAJoLSG;}gnqAeMN_F$E z5{gnxWQ^RyA5x_G*^78E!DQJE^cY1delWYRNu*g$#hNp zye;;KbuFE+WkbqrEq}pmP5IO*5r(=J+cNf>J>TgMTD}E8tn)W;)p4f$<{ZT{xHpWw zWv1vYyv8mz5XH#RrTFdY+7bSrPS2`979;DvoFMbQVD|Pc2_1O1UG9qnxNGhe*~kf; z9b_#gCDsm>T!#0RI}ni=Rs4i~OkeTrRhQsrtT~n`3w2g!FFGGz^o(`q-l%>qaMtji zuTLMT((sE9+T(z^Q0cV{Cd;-9m(NorC_Lp`2yX1!7Un1LdS>pic8$34ZJfdtz^;ZW zMhPZr?R(y<=Wyi!N;F$={T|-xg>M1205&Wz0p~bqq~i9g2mTApkBiE4x+pTAhk;}VuCNCKk!J805Ki+0QyVtePK}~XA6l4>F&nDZqv z9u`~EaM{fqv<0RCvv$iEaZSiWC3{JUwi6>ashX1G;lB|UXV6oHwWpHQ|{G3-XLRypIoH8L!HelMtf zTTI}!Re_@5>1U|kn5Xf`-?mHgyYvA=&!&a6qJ0R_ZasyA?oO2wbgoX5L! zsiS6Tshi({J+n1$L?sOJza1ug-_R_~pcF8%?e&6%@|7YXmw^Nllv~Lf!b`3>!dI+VUgJK4A=TXz!7*)dhj`)b(%wI z_!IFvDzDw)QFw@KGnx3?&*gp1WUs+yx9Bo+VtzZ{Oql~CaH*C^bg>*h%1Giv236z& z+tZJ%CWf!%25_pBe%Bfo%#v2%f8<#k4N6PxCFO4Y_`6Bj3P(ieoAY@wNi_!7u^8={ z;4beZnA*QpwC~4)bp6f1l;19cz>S0wG2i)_lyU1f7_u^ha{$W$i{aVK!**{t+ zmaCW+y|CiHl35jhS#8Ic-ySGERc1uLey=46*E;(hS+jB9@5g3&|3=_Gb}C!mq`Mmk zHz7j8Ku+3<_o=?7Mcr#cZ1gupwf6WzGQ+K!-Gc+fZvt{zlh$TgG?*3iCn$u5kcSf3 zSdq@Tm7C!%k#pg30A=9YbG#{7^N;v}uSBR<{Pn#g-u*IClty$IKvXl+Pzm$t-E$+_ z#cjIKMf3gq&!1u+mKTP&S6(UpBafE#c+vQj2nZSS|B=Txh=d6LU;jpG5YuPuf7D;^ z|M~$^XPErn{TKRQV5##zh!O!I2{CntNg;I-5r_4E*ZVK&fA{*2W&WR;qW(*gPfbEW PP5s1zi+WD;-_-vDmD|V^ delta 10432 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 toThrow};var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("fs");var nodePath=require("path");scriptDirectory=__dirname+"/";readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);var ret=fs.readFileSync(filename);return ret};readAsync=(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);return new Promise((resolve,reject)=>{fs.readFile(filename,binary?undefined:"utf8",(err,data)=>{if(err)reject(err);else resolve(binary?data.buffer:data)})})};if(!Module["thisProgram"]&&process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);if(typeof module!="undefined"){module["exports"]=Module}quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=url=>{if(isFileURI(url)){return new Promise((resolve,reject)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){resolve(xhr.response);return}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)})}return fetch(url,{credentials:"same-origin"}).then(response=>{if(response.ok){return response.arrayBuffer()}return Promise.reject(new Error(response.status+" : "+response.url))})}}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];var wasmBinary=Module["wasmBinary"];var wasmMemory;var ABORT=false;var EXITSTATUS;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b)}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);throw e}var dataURIPrefix="data:application/octet-stream;base64,";var isDataURI=filename=>filename.startsWith(dataURIPrefix);var isFileURI=filename=>filename.startsWith("file://");function findWasmBinary(){var f="loader.wasm";if(!isDataURI(f)){return locateFile(f)}return f}var wasmBinaryFile;function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}function getBinaryPromise(binaryFile){if(!wasmBinary){return readAsync(binaryFile).then(response=>new Uint8Array(response),()=>getBinarySync(binaryFile))}return Promise.resolve().then(()=>getBinarySync(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary,imports)).then(receiver,reason=>{err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}return instantiateArrayBuffer(binaryFile,imports,callback)}function getWasmImports(){return{a:wasmImports}}function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["o"];updateMemoryViews();addOnInit(wasmExports["p"]);removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);return false}}wasmBinaryFile??=findWasmBinary();instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult);return{}}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var noExitRuntime=Module["noExitRuntime"]||true;var __emscripten_memcpy_js=(dest,src,num)=>HEAPU8.copyWithin(dest,src,src+num);var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder:undefined;var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead=NaN)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";function _dbgErr(msg){console.error("LoaderMain: [ERROR] "+UTF8ToString(msg))}function _dbgLog(msg){console.log("LoaderMain: [INFO] "+UTF8ToString(msg))}var getHeapMax=()=>67108864;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};function _getEPWLength(){return epwFile.byteLength}function _getJSPISupported(){return typeof WebAssembly.Suspending!=="undefined"}function _initEPWStringResult(off,len){const id=results.length;results.push(UTF8Decoder.decode(new Uint8Array(epwFile,off,len)));return id}function _initResult(bufLen){const id=results.length;results.push(new Uint8Array(bufLen));return id}function _memcpyFromEPW(dest,off,len){HEAPU8.set(new Uint8Array(epwFile,off,len),dest)}function _memcpyFromEPWToResult(bufId,dest,off,len){results[bufId].set(new Uint8Array(epwFile,off,len),dest)}function _memcpyToResult(bufId,src,off,len){results[bufId].set(new Uint8Array(HEAPU8.buffer,src,len),off)}function _resultFailed(msg){const parentElement=createCrashParentElement();const messageContainer=document.createElement("div");messageContainer.setAttribute("style","z-index:100;position:absolute;top:10%;left:10%;right:10%;bottom:10%;background-color:white;border:2px solid #cccccc;overflow-x:hidden;overflow-y:scroll;");messageContainer.classList.add("_eaglercraftX_loader_failed_container");const failMsg=UTF8ToString(msg);console.error("LoaderMain: [FAILED] "+failMsg);const failureMsgElement=document.createElement("h2");failureMsgElement.style.color="#AA0000";failureMsgElement.style.padding="25px";failureMsgElement.style.fontFamily="sans-serif";failureMsgElement.style["marginBlock"]="0px";failureMsgElement.appendChild(document.createTextNode(failMsg));messageContainer.appendChild(failureMsgElement);const failureMsgElement2=document.createElement("h4");failureMsgElement2.style.color="#AA0000";failureMsgElement2.style.padding="25px";failureMsgElement2.style.fontFamily="sans-serif";failureMsgElement2.style["marginBlock"]="0px";failureMsgElement2.appendChild(document.createTextNode("Try again later"));messageContainer.appendChild(failureMsgElement2);parentElement.appendChild(messageContainer)}function _resultJSPIUnsupported(result){const idx=result>>2;const crashImgData=results[HEAP32[idx]];const crashImgMIME=results[HEAP32[idx+1]];const crashImg=crashImgData?URL.createObjectURL(new Blob([crashImgData],{type:crashImgMIME||"image/png"})):null;const markupData=results[HEAP32[idx+2]];const markup=markupData?UTF8Decoder.decode(markupData):"

Failed to load error screen

";const parentElement=createCrashParentElement();const img=document.createElement("img");img.setAttribute("style","z-index:100;position:absolute;top:10px;left:calc(50% - 151px);");img.src=crashImg;parentElement.appendChild(img);const iframeContainer=document.createElement("div");iframeContainer.setAttribute("style","z-index:100;position:absolute;top:135px;left:10%;right:10%;bottom:50px;background-color:white;border:2px solid #cccccc;");iframeContainer.classList.add("_eaglercraftX_jspi_unsupported_container");const iframe=document.createElement("iframe");iframe.classList.add("_eaglercraftX_jspi_unsupported_frame");iframe.setAttribute("style","border:none;width:100%;height:100%;");iframe.srcdoc=markup;iframeContainer.appendChild(iframe);parentElement.appendChild(iframeContainer)}function _resultSuccess(result){const idx=result>>2;const eagRuntimeJSURL=URL.createObjectURL(new Blob([results[HEAP32[idx]]],{type:"text/javascript;charset=utf-8"}));const classesWASMURL=URL.createObjectURL(new Blob([results[HEAP32[idx+1]]],{type:"application/wasm"}));const classesDeobfTEADBGURL=URL.createObjectURL(new Blob([results[HEAP32[idx+2]]],{type:"application/octet-stream"}));const classesDeobfWASMURL=URL.createObjectURL(new Blob([results[HEAP32[idx+3]]],{type:"application/wasm"}));const pressAnyKey=URL.createObjectURL(new Blob([results[HEAP32[idx+4]]],{type:results[HEAP32[idx+5]]}));const crashImg=URL.createObjectURL(new Blob([results[HEAP32[idx+6]]],{type:results[HEAP32[idx+7]]}));const faviconImg=URL.createObjectURL(new Blob([results[HEAP32[idx+8]]],{type:results[HEAP32[idx+9]]}));const numEPKs=HEAP32[idx+10];const epkFiles=new Array(numEPKs);for(var i=0,j;inoExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var handleException=e=>{if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)};var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var wasmImports={k:__emscripten_memcpy_js,c:_dbgErr,b:_dbgLog,j:_emscripten_resize_heap,n:_getEPWLength,i:_getJSPISupported,f:_initEPWStringResult,e:_initResult,d:_memcpyFromEPW,g:_memcpyFromEPWToResult,l:_memcpyToResult,a:_resultFailed,h:_resultJSPIUnsupported,m:_resultSuccess};var wasmExports=createWasm();var ___wasm_call_ctors=()=>(___wasm_call_ctors=wasmExports["p"])();var _main=Module["_main"]=(a0,a1)=>(_main=Module["_main"]=wasmExports["q"])(a0,a1);var __emscripten_stack_alloc=a0=>(__emscripten_stack_alloc=wasmExports["s"])(a0);var calledRun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function callMain(args=[]){var entryFunction=_main;args.unshift(thisProgram);var argc=args.length;var argv=stackAlloc((argc+1)*4);var argv_ptr=argv;args.forEach(arg=>{HEAPU32[argv_ptr>>2]=stringToUTF8OnStack(arg);argv_ptr+=4});HEAPU32[argv_ptr>>2]=0;try{var ret=entryFunction(argc,argv);exitJS(ret,true);return ret}catch(e){return handleException(e)}}function run(args=arguments_){if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();Module["onRuntimeInitialized"]?.();if(shouldRunNow)callMain(args);postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}var shouldRunNow=true;if(Module["noInitialRun"])shouldRunNow=false;run(); +(function(){'use strict';var e;e||=typeof Module != 'undefined' ? Module : {};var aa="object"==typeof window,g="undefined"!=typeof WorkerGlobalScope,k="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node&&"renderer"!=process.type,ba=window.__eaglercraftXLoaderContextPre.loaderWASMURL;e.locateFile=function(a){return"loader.wasm"===a?ba:a}; +var r=window.__eaglercraftXLoaderContextPre.rootElement,ca=window.__eaglercraftXLoaderContextPre.eaglercraftXOpts,t=window.__eaglercraftXLoaderContextPre.theEPWFileBuffer,da=window.__eaglercraftXLoaderContextPre.splashURL;delete window.__eaglercraftXLoaderContextPre;var v=[null]; +function w(){for(var a=null,b;b=r.lastChild;)a||=b,r.removeChild(b);b=document.createElement("div");b.classList.add("_eaglercraftX_wrapper_element");b.setAttribute("style","position:relative;width:100%;height:100%;overflow:hidden;");a&&(a.classList.add("_eaglercraftX_early_splash_element"),a.style.position="absolute",a.style.top="0px",a.style.left="0px",a.style.right="0px",a.style.bottom="0px",a.style.zIndex="2",b.appendChild(a));r.appendChild(b);return b}function x(){e=y=z=A=B=null} +var C=Object.assign({},e),D=[],E="./this.program",F=(a,b)=>{throw b;},G="",H,J; +if(k){var fs=require("fs"),ea=require("path");G=__dirname+"/";J=a=>{a=L(a)?new URL(a):ea.normalize(a);return fs.readFileSync(a)};H=a=>{a=L(a)?new URL(a):ea.normalize(a);return new Promise((b,d)=>{fs.readFile(a,void 0,(c,f)=>{c?d(c):b(f.buffer)})})};!e.thisProgram&&1{process.exitCode=a;throw b;}}else if(aa||g)g?G=self.location.href:"undefined"!=typeof document&& +document.currentScript&&(G=document.currentScript.src),G=G.startsWith("blob:")?"":G.substr(0,G.replace(/[?#].*/,"").lastIndexOf("/")+1),g&&(J=a=>{var b=new XMLHttpRequest;b.open("GET",a,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),H=a=>L(a)?new Promise((b,d)=>{var c=new XMLHttpRequest;c.open("GET",a,!0);c.responseType="arraybuffer";c.onload=()=>{200==c.status||0==c.status&&c.response?b(c.response):d(c.status)};c.onerror=d;c.send(null)}):fetch(a,{credentials:"same-origin"}).then(b=> +b.ok?b.arrayBuffer():Promise.reject(Error(b.status+" : "+b.url)));e.print||console.log.bind(console);var M=e.printErr||console.error.bind(console);Object.assign(e,C);C=null;e.arguments&&(D=e.arguments);e.thisProgram&&(E=e.thisProgram);var N=e.wasmBinary,y,O=!1,z,A,B; +function fa(){var a=y.buffer;e.HEAP8=new Int8Array(a);e.HEAP16=new Int16Array(a);e.HEAPU8=z=new Uint8Array(a);e.HEAPU16=new Uint16Array(a);e.HEAP32=A=new Int32Array(a);e.HEAPU32=B=new Uint32Array(a);e.HEAPF32=new Float32Array(a);e.HEAPF64=new Float64Array(a)}var ha=[],ia=[],ja=[],ka=[];function la(){var a=e.preRun.shift();ha.unshift(a)}var P=0,Q=null,R=null,ma=a=>a.startsWith("data:application/octet-stream;base64,"),L=a=>a.startsWith("file://"),S; +function na(a){if(a==S&&N)return new Uint8Array(N);if(J)return J(a);throw"both async and sync fetching of the wasm failed";}function oa(a){return N?Promise.resolve().then(()=>na(a)):H(a).then(b=>new Uint8Array(b),()=>na(a))}function pa(a,b,d){return oa(a).then(c=>WebAssembly.instantiate(c,b)).then(d,c=>{M(`failed to asynchronously prepare wasm: ${c}`);e.onAbort?.(c);c="Aborted("+c+")";M(c);O=!0;throw new WebAssembly.RuntimeError(c+". Build with -sASSERTIONS for more info.");})} +function qa(a,b){var d=S;N||"function"!=typeof WebAssembly.instantiateStreaming||ma(d)||L(d)||k||"function"!=typeof fetch?pa(d,a,b):fetch(d,{credentials:"same-origin"}).then(c=>WebAssembly.instantiateStreaming(c,a).then(b,function(f){M(`wasm streaming compile failed: ${f}`);M("falling back to ArrayBuffer instantiation");return pa(d,a,b)}))}class ra{name="ExitStatus";constructor(a){this.message=`Program terminated with exit(${a})`;this.status=a}} +var T=a=>{for(;0{for(var b=z,d=a+NaN,c=a;b[c]&&!(c>=d);)++c;if(16f?d+=String.fromCharCode(f):(f-=65536,d+=String.fromCharCode(55296|f>>10,56320| +f&1023))}}else d+=String.fromCharCode(f)}return d},ta=a=>{sa||(e.onExit?.(a),O=!0);F(a,new ra(a))},ua={k:(a,b,d)=>z.copyWithin(a,b,b+d),c:function(a){console.error("LoaderMain: [ERROR] "+(a?W(a):""))},b:function(a){console.log("LoaderMain: [INFO] "+(a?W(a):""))},j:a=>{var b=z.length;a>>>=0;if(67108864=d;d*=2){var c=b*(1+.2/d);c=Math.min(c,a+100663296);a:{c=(Math.min(67108864,65536*Math.ceil(Math.max(a,c)/65536))-y.buffer.byteLength+65535)/65536|0;try{y.grow(c);fa();var f= +1;break a}catch(u){}f=void 0}if(f)return!0}return!1},n:function(){return t.byteLength},i:function(){return"undefined"!==typeof WebAssembly.Suspending},f:function(a,b){const d=v.length;v.push(U.decode(new Uint8Array(t,a,b)));return d},e:function(a){const b=v.length;v.push(new Uint8Array(a));return b},d:function(a,b,d){z.set(new Uint8Array(t,b,d),a)},g:function(a,b,d,c){v[a].set(new Uint8Array(t,d,c),b)},l:function(a,b,d,c){v[a].set(new Uint8Array(z.buffer,b,c),d)},a:function(a){t=v=null;setTimeout(x, +20);const b=w(),d=document.createElement("div");d.setAttribute("style","z-index:100;position:absolute;top:10%;left:10%;right:10%;bottom:10%;background-color:white;border:2px solid #cccccc;overflow-x:hidden;overflow-y:scroll;");d.classList.add("_eaglercraftX_loader_failed_container");a=a?W(a):"";console.error("LoaderMain: [FAILED] "+a);const c=document.createElement("h2");c.style.color="#AA0000";c.style.padding="25px";c.style.fontFamily="sans-serif";c.style.marginBlock="0px";c.appendChild(document.createTextNode(a)); +d.appendChild(c);a=document.createElement("h4");a.style.color="#AA0000";a.style.padding="25px";a.style.fontFamily="sans-serif";a.style.marginBlock="0px";a.appendChild(document.createTextNode("Try again later"));d.appendChild(a);b.appendChild(d)},h:function(a){var b=a>>2;a=v[A[b]];var d=v[A[b+1]];a=a?URL.createObjectURL(new Blob([a],{type:d||"image/png"})):null;b=(b=v[A[b+2]])?U.decode(b):"

Failed to load error screen

";t=v=null;setTimeout(x,20);d=w();var c=document.createElement("img");c.setAttribute("style", +"z-index:100;position:absolute;top:10px;left:calc(50% - 151px);");c.src=a;d.appendChild(c);a=document.createElement("div");a.setAttribute("style","z-index:100;position:absolute;top:135px;left:10%;right:10%;bottom:50px;background-color:white;border:2px solid #cccccc;");a.classList.add("_eaglercraftX_jspi_unsupported_container");c=document.createElement("iframe");c.classList.add("_eaglercraftX_jspi_unsupported_frame");c.setAttribute("style","border:none;width:100%;height:100%;");c.srcdoc=b;a.appendChild(c); +d.appendChild(a)},m:function(a){a>>=2;const b=URL.createObjectURL(new Blob([v[A[a]]],{type:"text/javascript;charset=utf-8"})),d=URL.createObjectURL(new Blob([v[A[a+1]]],{type:"application/wasm"})),c=URL.createObjectURL(new Blob([v[A[a+2]]],{type:"application/octet-stream"})),f=URL.createObjectURL(new Blob([v[A[a+3]]],{type:"application/wasm"})),u=URL.createObjectURL(new Blob([v[A[a+4]]],{type:v[A[a+5]]})),m=URL.createObjectURL(new Blob([v[A[a+6]]],{type:v[A[a+7]]})),V=URL.createObjectURL(new Blob([v[A[a+ +8]]],{type:v[A[a+9]]})),I=A[a+10],q=Array(I);for(var h=0,l;h(va=e._main=X.q)(a,b),Y=a=>(Y=X.s)(a),Z;R=function wa(){Z||xa();Z||(R=wa)}; +function ya(a=[]){var b=va;a.unshift(E);var d=a.length,c=Y(4*(d+1)),f=c;a.forEach(m=>{for(var V=B,I=f>>2,q=0,h=0;h=l?q++:2047>=l?q+=2:55296<=l&&57343>=l?(q+=4,++h):q+=3}var p=q+1;h=q=Y(p);l=z;if(0=n){var Aa=m.charCodeAt(++K);n=65536+((n&1023)<<10)|Aa&1023}if(127>=n){if(h>=p)break;l[h++]=n}else{if(2047>=n){if(h+1>=p)break;l[h++]=192|n>>6}else{if(65535>=n){if(h+2>=p)break;l[h++]= +224|n>>12}else{if(h+3>=p)break;l[h++]=240|n>>18;l[h++]=128|n>>12&63}l[h++]=128|n>>6&63}l[h++]=128|n&63}}l[h]=0}V[I]=q;f+=4});B[f>>2]=0;try{var u=b(d,c);ta(u)}catch(m){m instanceof ra||"unwind"==m||F(1,m)}} +function xa(){var a=D;function b(){if(!Z&&(Z=!0,e.calledRun=!0,!O)){T(ia);T(ja);e.onRuntimeInitialized?.();za&&ya(a);if(e.postRun)for("function"==typeof e.postRun&&(e.postRun=[e.postRun]);e.postRun.length;){var d=e.postRun.shift();ka.unshift(d)}T(ka)}}if(!(0{setTimeout(()=>e.setStatus(""),1);b()},1)):b())}} +if(e.preInit)for("function"==typeof e.preInit&&(e.preInit=[e.preInit]);0 1000) { + syncTimer = 0.0; PlatformRuntime.swapDelayTeaVM(); }else { - if(!Display.sync(fpsLimit, syncTimer)) { + double frameMillis = (1000.0 / fpsLimit); + if(syncTimer == 0.0) { + syncTimer = PlatformRuntime.steadyTimeMillisTeaVM() + frameMillis; PlatformRuntime.swapDelayTeaVM(); + }else { + double millis = PlatformRuntime.steadyTimeMillisTeaVM(); + int remaining = (int)(syncTimer - millis); + if(remaining > 0) { + if(!PlatformRuntime.useDelayOnSwap && PlatformRuntime.immediateContinueSupport) { + PlatformRuntime.immediateContinue(); // cannot stack setTimeouts, or it will throttle + millis = PlatformRuntime.steadyTimeMillisTeaVM(); + remaining = (int)(syncTimer - millis); + if(remaining > 0) { + PlatformRuntime.sleep((int)remaining); + } + }else { + PlatformRuntime.sleep((int)remaining); + } + }else { + PlatformRuntime.swapDelayTeaVM(); + } + millis = PlatformRuntime.steadyTimeMillisTeaVM(); + if((syncTimer += frameMillis) < millis) { + syncTimer = millis; + } } } } }else { - syncTimer[0] = 0l; - EagUtils.sleep(50); + syncTimer = 0.0; + PlatformRuntime.sleep(50); } } @@ -1579,7 +1600,7 @@ public class PlatformInput { EarlyLoadScreen.paintEnable(PlatformOpenGL.checkVAOCapable(), allowBootMenu); while(mouseEvents.isEmpty() && keyEvents.isEmpty() && touchEvents.isEmpty()) { - EagUtils.sleep(100); + PlatformRuntime.sleep(100); } } } @@ -1864,7 +1885,7 @@ public class PlatformInput { if(touchKeyboardField != null) { touchKeyboardField.blur(); touchKeyboardField.setValue(""); - EagUtils.sleep(10); + PlatformRuntime.sleep(10); if(touchKeyboardForm != null) { touchKeyboardForm.removeChild(touchKeyboardField); }else { @@ -2135,7 +2156,7 @@ public class PlatformInput { touchKeyboardField.blur(); touchKeyboardField.setValue(""); if(sync) { - EagUtils.sleep(10); + PlatformRuntime.sleep(10); if(touchKeyboardForm != null) { touchKeyboardForm.removeChild(touchKeyboardField); }else { diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java index 9d97459..cf33cc9 100644 --- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java +++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java @@ -766,6 +766,10 @@ public class PlatformRuntime { } } + public static boolean immediateContinueSupported() { + return immediateContinueSupport; + } + @Async private static native void immediateContinueTeaVM0(); @@ -1101,6 +1105,10 @@ public class PlatformRuntime { @JSBody(params = { "steadyTimeFunc" }, script = "return steadyTimeFunc();") private static native double steadyTimeMillis0(JSObject steadyTimeFunc); + public static double steadyTimeMillisTeaVM() { + return steadyTimeMillis0(steadyTimeFunc); + } + public static long steadyTimeMillis() { return (long)steadyTimeMillis0(steadyTimeFunc); } diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java index 6838e51..499bbd5 100644 --- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java +++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java @@ -111,6 +111,7 @@ public class PlatformVoiceClient { if(!PlatformWebRTC.hasCheckedSupport) PlatformWebRTC.supported(); switch(PlatformWebRTC.supportedImpl) { case PlatformWebRTC.WEBRTC_SUPPORT_CORE: + case PlatformWebRTC.WEBRTC_SUPPORT_CORE_NON_PROMISING: case PlatformWebRTC.WEBRTC_SUPPORT_WEBKIT: addCoreIceCandidate(peerConnection, str); break; diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebRTC.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebRTC.java index 1a434d8..6ebf4a1 100644 --- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebRTC.java +++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebRTC.java @@ -49,8 +49,18 @@ public class PlatformWebRTC { static final int WEBRTC_SUPPORT_CORE = 1; static final int WEBRTC_SUPPORT_WEBKIT = 2; static final int WEBRTC_SUPPORT_MOZ = 3; + static final int WEBRTC_SUPPORT_CORE_NON_PROMISING = 4; - @JSBody(script = "return (typeof RTCPeerConnection !== \"undefined\") ? 1 : ((typeof webkitRTCPeerConnection !== \"undefined\") ? 2 : ((typeof mozRTCPeerConnection !== \"undefined\") ? 3 : 0));") + @JSBody(script = "var checkPromising = function() { try {" + + "return (typeof (new RTCPeerConnection({iceServers:[{urls:\"stun:127.69.0.1:6969\"}]})).createOffer() === \"object\") ? 1 : 4;" + + "} catch(ex) {" + + "return (ex.name === \"TypeError\") ? 4 : 1;" + + "}};" + + "return (typeof RTCPeerConnection !== \"undefined\")" + + " ? checkPromising()" + + " : ((typeof webkitRTCPeerConnection !== \"undefined\") ? 2" + + " : ((typeof mozRTCPeerConnection !== \"undefined\") ? 3" + + " : 0));") private static native int checkSupportedImpl(); static boolean hasCheckedSupport = false; @@ -69,6 +79,8 @@ public class PlatformWebRTC { logger.info("Using webkit- prefix for RTCPeerConnection"); }else if(supportedImpl == WEBRTC_SUPPORT_MOZ) { logger.info("Using moz- prefix for RTCPeerConnection"); + }else if(supportedImpl == WEBRTC_SUPPORT_CORE_NON_PROMISING) { + logger.info("Using non-promising RTCPeerConnection"); } if(supportedImpl != WEBRTC_SUPPORT_NONE) { belowChrome71Fix = isChromeBelow71(); @@ -160,6 +172,7 @@ public class PlatformWebRTC { if(!hasCheckedSupport) supported(); switch(supportedImpl) { case WEBRTC_SUPPORT_CORE: + case WEBRTC_SUPPORT_CORE_NON_PROMISING: return createCoreRTCPeerConnection(iceServers); case WEBRTC_SUPPORT_WEBKIT: return createWebkitRTCPeerConnection(iceServers); @@ -191,11 +204,49 @@ public class PlatformWebRTC { @JSBody(params = { "item" }, script = "return item.channel;") static native JSObject getChannel(JSObject item); + @JSBody(params = { "peerConnection", "h1", "h2" }, script = "peerConnection.createOffer().then(h1).catch(h2);") + private static native void createOfferPromising(JSObject peerConnection, DescHandler h1, ErrorHandler h2); + @JSBody(params = { "peerConnection", "h1", "h2" }, script = "peerConnection.createOffer(h1, h2);") - static native void createOffer(JSObject peerConnection, DescHandler h1, ErrorHandler h2); + private static native void createOfferLegacy(JSObject peerConnection, DescHandler h1, ErrorHandler h2); + + static void createOffer(JSObject peerConnection, DescHandler h1, ErrorHandler h2) { + if(!hasCheckedSupport) supported(); + switch(supportedImpl) { + case WEBRTC_SUPPORT_CORE: + createOfferPromising(peerConnection, h1, h2); + break; + case WEBRTC_SUPPORT_WEBKIT: + case WEBRTC_SUPPORT_MOZ: + case WEBRTC_SUPPORT_CORE_NON_PROMISING: + createOfferLegacy(peerConnection, h1, h2); + break; + default: + throw new UnsupportedOperationException(); + } + } + + @JSBody(params = { "peerConnection", "desc", "h1", "h2" }, script = "peerConnection.setLocalDescription(desc).then(h1).catch(h2);") + private static native void setLocalDescriptionPromising(JSObject peerConnection, JSObject desc, EmptyHandler h1, ErrorHandler h2); @JSBody(params = { "peerConnection", "desc", "h1", "h2" }, script = "peerConnection.setLocalDescription(desc, h1, h2);") - static native void setLocalDescription(JSObject peerConnection, JSObject desc, EmptyHandler h1, ErrorHandler h2); + private static native void setLocalDescriptionLegacy(JSObject peerConnection, JSObject desc, EmptyHandler h1, ErrorHandler h2); + + static void setLocalDescription(JSObject peerConnection, JSObject desc, EmptyHandler h1, ErrorHandler h2) { + if(!hasCheckedSupport) supported(); + switch(supportedImpl) { + case WEBRTC_SUPPORT_CORE: + setLocalDescriptionPromising(peerConnection, desc, h1, h2); + break; + case WEBRTC_SUPPORT_WEBKIT: + case WEBRTC_SUPPORT_MOZ: + case WEBRTC_SUPPORT_CORE_NON_PROMISING: + setLocalDescriptionLegacy(peerConnection, desc, h1, h2); + break; + default: + throw new UnsupportedOperationException(); + } + } @JSBody(params = { "peerConnection", "str" }, script = "var candidateList = JSON.parse(str); for (var i = 0; i < candidateList.length; ++i) { peerConnection.addIceCandidate(new RTCIceCandidate(candidateList[i])); }; return null;") private static native void addCoreIceCandidates(JSObject peerConnection, String str); @@ -219,6 +270,7 @@ public class PlatformWebRTC { if(!hasCheckedSupport) supported(); switch(supportedImpl) { case WEBRTC_SUPPORT_CORE: + case WEBRTC_SUPPORT_CORE_NON_PROMISING: case WEBRTC_SUPPORT_WEBKIT: addCoreIceCandidates(peerConnection, str); break; @@ -246,6 +298,7 @@ public class PlatformWebRTC { } switch(supportedImpl) { case WEBRTC_SUPPORT_CORE: + case WEBRTC_SUPPORT_CORE_NON_PROMISING: if(useSessionDescConstructor) { setCoreRemoteDescriptionLegacy(peerConnection, str); }else { @@ -267,14 +320,20 @@ public class PlatformWebRTC { } } + @JSBody(params = { "peerConnection", "str", "h1", "h2" }, script = "try { peerConnection.setRemoteDescription(str).then(h1).catch(h2); return true; } catch(ex) { if(ex.name === \"TypeError\") return false; else throw ex; }") + private static native boolean setCoreRemoteDescription2Promising(JSObject peerConnection, JSObject str, EmptyHandler h1, ErrorHandler h2); + @JSBody(params = { "peerConnection", "str", "h1", "h2" }, script = "try { peerConnection.setRemoteDescription(str, h1, h2); return true; } catch(ex) { if(ex.name === \"TypeError\") return false; else throw ex; }") - private static native boolean setCoreRemoteDescription2(JSObject peerConnection, JSObject str, EmptyHandler h1, ErrorHandler h2); + private static native boolean setCoreRemoteDescription2Legacy(JSObject peerConnection, JSObject str, EmptyHandler h1, ErrorHandler h2); + + @JSBody(params = { "peerConnection", "str", "h1", "h2" }, script = "peerConnection.setRemoteDescription(new RTCSessionDescription(str)).then(h1).catch(h2);") + private static native void setCoreRemoteDescription2PromisingLegacy(JSObject peerConnection, JSObject str, EmptyHandler h1, ErrorHandler h2); @JSBody(params = { "peerConnection", "str", "h1", "h2" }, script = "peerConnection.setRemoteDescription(new RTCSessionDescription(str), h1, h2);") - private static native void setCoreRemoteDescription2Legacy(JSObject peerConnection, JSObject str, EmptyHandler h1, ErrorHandler h2); + private static native void setCoreRemoteDescription2LegacyLegacy(JSObject peerConnection, JSObject str, EmptyHandler h1, ErrorHandler h2); @JSBody(params = { "peerConnection", "str", "h1", "h2" }, script = "peerConnection.setRemoteDescription(new mozRTCSessionDescription(str), h1, h2);") - private static native void setMozRemoteDescription2Legacy(JSObject peerConnection, JSObject str, EmptyHandler h1, ErrorHandler h2); + private static native void setMozRemoteDescription2LegacyLegacy(JSObject peerConnection, JSObject str, EmptyHandler h1, ErrorHandler h2); static void setRemoteDescription2(JSObject peerConnection, JSObject str, EmptyHandler h1, ErrorHandler h2) { if(!hasCheckedSupport) supported(); @@ -284,20 +343,31 @@ public class PlatformWebRTC { switch(supportedImpl) { case WEBRTC_SUPPORT_CORE: if(useSessionDescConstructor) { - setCoreRemoteDescription2Legacy(peerConnection, str, h1, h2); + setCoreRemoteDescription2PromisingLegacy(peerConnection, str, h1, h2); }else { - if(!setCoreRemoteDescription2(peerConnection, str, h1, h2)) { + if(!setCoreRemoteDescription2Promising(peerConnection, str, h1, h2)) { useSessionDescConstructor = true; logger.info("Note: Caught suspicious exception, using legacy RTCSessionDescription method"); - setCoreRemoteDescription2Legacy(peerConnection, str, h1, h2); + setCoreRemoteDescription2PromisingLegacy(peerConnection, str, h1, h2); } } break; case WEBRTC_SUPPORT_WEBKIT: - setCoreRemoteDescription2Legacy(peerConnection, str, h1, h2); + setCoreRemoteDescription2LegacyLegacy(peerConnection, str, h1, h2); break; case WEBRTC_SUPPORT_MOZ: - setMozRemoteDescription2Legacy(peerConnection, str, h1, h2); + setMozRemoteDescription2LegacyLegacy(peerConnection, str, h1, h2); + break; + case WEBRTC_SUPPORT_CORE_NON_PROMISING: + if(useSessionDescConstructor) { + setCoreRemoteDescription2LegacyLegacy(peerConnection, str, h1, h2); + }else { + if(!setCoreRemoteDescription2Legacy(peerConnection, str, h1, h2)) { + useSessionDescConstructor = true; + logger.info("Note: Caught suspicious exception, using legacy RTCSessionDescription method"); + setCoreRemoteDescription2LegacyLegacy(peerConnection, str, h1, h2); + } + } break; default: throw new UnsupportedOperationException(); @@ -312,8 +382,27 @@ public class PlatformWebRTC { + "}") private static native void removeExtmapAllowMixed(JSObject objIn); + @JSBody(params = { "peerConnection", "h1", "h2" }, script = "peerConnection.createAnswer().then(h1).catch(h2);") + private static native void createAnswerPromising(JSObject peerConnection, DescHandler h1, ErrorHandler h2); + @JSBody(params = { "peerConnection", "h1", "h2" }, script = "peerConnection.createAnswer(h1, h2);") - static native void createAnswer(JSObject peerConnection, DescHandler h1, ErrorHandler h2); + private static native void createAnswerLegacy(JSObject peerConnection, DescHandler h1, ErrorHandler h2); + + static void createAnswer(JSObject peerConnection, DescHandler h1, ErrorHandler h2) { + if(!hasCheckedSupport) supported(); + switch(supportedImpl) { + case WEBRTC_SUPPORT_CORE: + createAnswerPromising(peerConnection, h1, h2); + break; + case WEBRTC_SUPPORT_WEBKIT: + case WEBRTC_SUPPORT_MOZ: + case WEBRTC_SUPPORT_CORE_NON_PROMISING: + createAnswerLegacy(peerConnection, h1, h2); + break; + default: + throw new UnsupportedOperationException(); + } + } @JSBody(params = { "sock", "buffer" }, script = "sock.send(buffer);") static native void nativeBinarySend(WebSocket sock, ArrayBuffer buffer); diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayBufferAllocator.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayBufferAllocator.java index 755046a..8f5d0f4 100644 --- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayBufferAllocator.java +++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayBufferAllocator.java @@ -8,7 +8,7 @@ import org.teavm.jso.typedarrays.Uint16Array; import org.teavm.jso.typedarrays.Uint8Array; /** - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. + * Copyright (c) 2022-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 @@ -30,8 +30,14 @@ public class EaglerArrayBufferAllocator { } } + private static final ByteBuffer ZERO_LENGTH_BYTE_BUFFER = new EaglerArrayByteBuffer(Int8Array.create(0)); + public static ByteBuffer allocateByteBuffer(int size) { - return new EaglerArrayByteBuffer(Int8Array.create(size)); + if(size != 0) { + return new EaglerArrayByteBuffer(Int8Array.create(size)); + }else { + return ZERO_LENGTH_BYTE_BUFFER; + } } public static ByteBuffer wrapByteBufferTeaVM(DataView dv) { @@ -42,16 +48,28 @@ public class EaglerArrayBufferAllocator { return new EaglerArrayByteBuffer(typedArray); } + private static final IntBuffer ZERO_LENGTH_INT_BUFFER = new EaglerArrayIntBuffer(Int32Array.create(0)); + public static IntBuffer allocateIntBuffer(int size) { - return new EaglerArrayIntBuffer(Int32Array.create(size)); + if(size != 0) { + return new EaglerArrayIntBuffer(Int32Array.create(size)); + }else { + return ZERO_LENGTH_INT_BUFFER; + } } public static IntBuffer wrapIntBufferTeaVM(Int32Array typedArray) { return new EaglerArrayIntBuffer(typedArray); } + private static final FloatBuffer ZERO_LENGTH_FLOAT_BUFFER = new EaglerArrayFloatBuffer(Float32Array.create(0)); + public static FloatBuffer allocateFloatBuffer(int size) { - return new EaglerArrayFloatBuffer(Float32Array.create(size)); + if(size != 0) { + return new EaglerArrayFloatBuffer(Float32Array.create(size)); + }else { + return ZERO_LENGTH_FLOAT_BUFFER; + } } public static FloatBuffer wrapFloatBufferTeaVM(Float32Array typedArray) { diff --git a/sources/wasm-gc-teavm-loader/js/library.js b/sources/wasm-gc-teavm-loader/js/library.js index e00cc69..370c2cf 100644 --- a/sources/wasm-gc-teavm-loader/js/library.js +++ b/sources/wasm-gc-teavm-loader/js/library.js @@ -41,6 +41,11 @@ addToLibrary({ return id; }, resultFailed: function(msg) { + results = null; + epwFile = null; + + setTimeout(unsetHeapViews, 20); + const parentElement = createCrashParentElement(); const messageContainer = document.createElement("div"); @@ -85,40 +90,43 @@ addToLibrary({ for(var i = 0, j; i < numEPKs; ++i) { j = idx + 11 + i * 3; epkFiles[i] = { - data: results[HEAP32[j]], - name: results[HEAP32[j + 1]], - path: results[HEAP32[j + 2]] + "data": results[HEAP32[j]], + "name": results[HEAP32[j + 1]], + "path": results[HEAP32[j + 2]] }; } - results.length = 0; + results = null; + epwFile = null; - window.__eaglercraftXLoaderContext = { - getEaglercraftXOpts: function() { + setTimeout(unsetHeapViews, 20); + + window["__eaglercraftXLoaderContext"] = { + "getEaglercraftXOpts": function() { return optsObj; }, - getEagRuntimeJSURL: function() { + "getEagRuntimeJSURL": function() { return eagRuntimeJSURL; }, - getClassesWASMURL: function() { + "getClassesWASMURL": function() { return classesWASMURL; }, - getClassesDeobfWASMURL: function() { + "getClassesDeobfWASMURL": function() { return classesDeobfWASMURL; }, - getClassesTEADBGURL: function() { + "getClassesTEADBGURL": function() { return classesDeobfTEADBGURL; }, - getEPKFiles: function() { + "getEPKFiles": function() { return epkFiles; }, - getRootElement: function() { + "getRootElement": function() { return rootElement; }, - getMainArgs: function() { + "getMainArgs": function() { return []; }, - getImageURL: function(idx) { + "getImageURL": function(idx) { switch(idx) { case 0: return splashURL; @@ -132,7 +140,7 @@ addToLibrary({ return null; } }, - runMain: function(fn) { + "runMain": function(fn) { setTimeout(fn, 10); } }; @@ -152,6 +160,11 @@ addToLibrary({ const markupData = results[HEAP32[idx + 2]]; const markup = markupData ? UTF8Decoder.decode(markupData) : "

Failed to load error screen

"; + results = null; + epwFile = null; + + setTimeout(unsetHeapViews, 20); + const parentElement = createCrashParentElement(); const img = document.createElement("img"); diff --git a/sources/wasm-gc-teavm-loader/js/pre.js b/sources/wasm-gc-teavm-loader/js/pre.js index 33e4915..37bb7ad 100644 --- a/sources/wasm-gc-teavm-loader/js/pre.js +++ b/sources/wasm-gc-teavm-loader/js/pre.js @@ -14,20 +14,24 @@ * */ +var loaderWASMPath = window["__eaglercraftXLoaderContextPre"]["loaderWASMURL"]; + Module["locateFile"] = function(path) { if(path === "loader.wasm") { - return window.__eaglercraftXLoaderContextPre.loaderWASMURL; + return loaderWASMPath; }else { return path; } }; -const rootElement = window.__eaglercraftXLoaderContextPre.rootElement; -const optsObj = window.__eaglercraftXLoaderContextPre.eaglercraftXOpts; -const epwFile = window.__eaglercraftXLoaderContextPre.theEPWFileBuffer; -const splashURL = window.__eaglercraftXLoaderContextPre.splashURL; +var rootElement = window["__eaglercraftXLoaderContextPre"]["rootElement"]; +var optsObj = window["__eaglercraftXLoaderContextPre"]["eaglercraftXOpts"]; +var epwFile = window["__eaglercraftXLoaderContextPre"]["theEPWFileBuffer"]; +var splashURL = window["__eaglercraftXLoaderContextPre"]["splashURL"]; -const results = [ null ]; +delete window["__eaglercraftXLoaderContextPre"]; + +var results = [ null ]; function createCrashParentElement() { var oldSplash = null; @@ -59,3 +63,9 @@ function createCrashParentElement() { return parentElement; } + +function unsetHeapViews() { + HEAP = HEAP8 = HEAPU8 = HEAP16 = HEAPU16 = HEAP32 = HEAPU32 = HEAPF32 = HEAPF64 = null; + wasmMemory = null; + Module = null; +} diff --git a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java index 923b564..324c085 100644 --- a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java +++ b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java @@ -464,6 +464,9 @@ public class PlatformRuntime { @Import(module = "platformRuntime", name = "immediateContinue") private static native void immediateContinue0(); + @Import(module = "platformRuntime", name = "immediateContinueSupported") + public static native boolean immediateContinueSupported(); + public static void postCreate() { } diff --git a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/WASMGCBufferAllocator.java b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/WASMGCBufferAllocator.java index 1226217..9e11fa8 100644 --- a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/WASMGCBufferAllocator.java +++ b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/WASMGCBufferAllocator.java @@ -32,7 +32,7 @@ public class WASMGCBufferAllocator { public static Address malloc(int size) { if(size == 0) { - throw new IllegalArgumentException("Tried to allocate 0 bytes!"); + return Address.fromInt(0); } Address addr; if(enableBufferOverflowCheck) { @@ -55,11 +55,11 @@ public class WASMGCBufferAllocator { } public static Address calloc(int size) { + if(size == 0) { + return Address.fromInt(0); + } Address addr; if(enableBufferOverflowCheck) { - if(size == 0) { - throw new OutOfMemoryError("Tried to allocate 0 bytes!"); - } addr = DirectMalloc.calloc(size + 12); if(addr.toInt() == 0) { throw new OutOfMemoryError("DirectMalloc returned null pointer!"); @@ -87,33 +87,55 @@ public class WASMGCBufferAllocator { if(tag != ptr.add(size + 8).getInt()) { throw new RuntimeException("Detected a buffer write overflow"); } - DirectMalloc.free(ptr); - }else { - DirectMalloc.free(ptr); } + DirectMalloc.free(ptr); } } + private static final ByteBuffer ZERO_LENGTH_BYTE_BUFFER = new DirectMallocByteBuffer(Address.fromInt(0), 0, true); + public static ByteBuffer allocateByteBuffer(int size) { - return new DirectMallocByteBuffer(malloc(size), size, true); + if(size != 0) { + return new DirectMallocByteBuffer(malloc(size), size, true); + }else { + return ZERO_LENGTH_BYTE_BUFFER; + } } + private static final ShortBuffer ZERO_LENGTH_SHORT_BUFFER = new DirectMallocShortBuffer(Address.fromInt(0), 0, true); + public static ShortBuffer allocateShortBuffer(int size) { - return new DirectMallocShortBuffer(malloc(size << 1), size, true); + if(size != 0) { + return new DirectMallocShortBuffer(malloc(size << 1), size, true); + }else { + return ZERO_LENGTH_SHORT_BUFFER; + } } + private static final IntBuffer ZERO_LENGTH_INT_BUFFER = new DirectMallocIntBuffer(Address.fromInt(0), 0, true); + public static IntBuffer allocateIntBuffer(int size) { - return new DirectMallocIntBuffer(malloc(size << 2), size, true); + if(size != 0) { + return new DirectMallocIntBuffer(malloc(size << 2), size, true); + }else { + return ZERO_LENGTH_INT_BUFFER; + } } + private static final FloatBuffer ZERO_LENGTH_FLOAT_BUFFER = new DirectMallocFloatBuffer(Address.fromInt(0), 0, true); + public static FloatBuffer allocateFloatBuffer(int size) { - return new DirectMallocFloatBuffer(malloc(size << 2), size, true); + if(size != 0) { + return new DirectMallocFloatBuffer(malloc(size << 2), size, true); + }else { + return ZERO_LENGTH_FLOAT_BUFFER; + } } public static void freeByteBuffer(ByteBuffer buffer) { DirectMallocByteBuffer buf = (DirectMallocByteBuffer)buffer; if(buf.original) { - WASMGCBufferAllocator.free(buf.address); + free(buf.address); }else { throwNotOriginal(buf); } @@ -122,7 +144,7 @@ public class WASMGCBufferAllocator { public static void freeShortBuffer(ShortBuffer buffer) { DirectMallocShortBuffer buf = (DirectMallocShortBuffer)buffer; if(buf.original) { - WASMGCBufferAllocator.free(buf.address); + free(buf.address); }else { throwNotOriginal(buf); } @@ -131,7 +153,7 @@ public class WASMGCBufferAllocator { public static void freeIntBuffer(IntBuffer buffer) { DirectMallocIntBuffer buf = (DirectMallocIntBuffer)buffer; if(buf.original) { - WASMGCBufferAllocator.free(buf.address); + free(buf.address); }else { throwNotOriginal(buf); } @@ -140,7 +162,7 @@ public class WASMGCBufferAllocator { public static void freeFloatBuffer(FloatBuffer buffer) { DirectMallocFloatBuffer buf = (DirectMallocFloatBuffer)buffer; if(buf.original) { - WASMGCBufferAllocator.free(buf.address); + free(buf.address); }else { throwNotOriginal(buf); } diff --git a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/WASMGCDirectArrayConverter.java b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/WASMGCDirectArrayConverter.java index 69a5be2..e1a4e2c 100644 --- a/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/WASMGCDirectArrayConverter.java +++ b/sources/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/WASMGCDirectArrayConverter.java @@ -1,7 +1,6 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; import org.teavm.interop.Address; -import org.teavm.interop.DirectMalloc; import org.teavm.jso.typedarrays.Float32Array; import org.teavm.jso.typedarrays.Int16Array; import org.teavm.jso.typedarrays.Int32Array; diff --git a/sources/wasm-gc-teavm/js/eagruntime_entrypoint.js b/sources/wasm-gc-teavm/js/eagruntime_entrypoint.js index f6c3c1f..555b6be 100644 --- a/sources/wasm-gc-teavm/js/eagruntime_entrypoint.js +++ b/sources/wasm-gc-teavm/js/eagruntime_entrypoint.js @@ -28,6 +28,7 @@ async function entryPoint() { crashURL = self.__eaglercraftXLoaderContext.getImageURL(2); faviconURL = self.__eaglercraftXLoaderContext.getImageURL(3); const args = self.__eaglercraftXLoaderContext.getMainArgs(); + delete self.__eaglercraftXLoaderContext; const isWorker = args[0] === "_worker_process_"; if(!isWorker) { diff --git a/sources/wasm-gc-teavm/js/platformInput.js b/sources/wasm-gc-teavm/js/platformInput.js index 2abb124..21235f2 100644 --- a/sources/wasm-gc-teavm/js/platformInput.js +++ b/sources/wasm-gc-teavm/js/platformInput.js @@ -561,21 +561,39 @@ async function initPlatformInput(inputImports) { * @return {Promise} */ function syncDelay(fpsLimit) { - if(fpsLimit > 0 && fpsLimit < 1000) { + if(fpsLimit > 0 && fpsLimit <= 1000) { + const frameMillis = (1000 / fpsLimit); if(manualSyncTimer === 0) { - manualSyncTimer = performance.now(); + manualSyncTimer = performance.now() + frameMillis; }else { - const millis = performance.now(); - const frameMillis = (1000 / fpsLimit); - var frameTime = millis - manualSyncTimer; - if(frameTime > 2000 || frameTime < 0) { - frameTime = frameMillis; + var millis = performance.now(); + var remaining = (manualSyncTimer - millis) | 0; + if(remaining > 0) { + if(!runtimeOpts.useDelayOnSwap && allowImmediateContinue) { + return immediateContinueImpl().then(function() { + var millis0 = performance.now(); + var remaining0 = (manualSyncTimer - millis0) | 0; + if(remaining0 > 0) { + return sleepPromise(remaining0).then(function() { + var millis1 = performance.now(); + if((manualSyncTimer += frameMillis) < millis1) { + manualSyncTimer = millis1; + } + }); + }else if((manualSyncTimer += frameMillis) < millis0) { + manualSyncTimer = millis0; + } + }); + }else { + return sleepPromise(remaining).then(function() { + var millis0 = performance.now(); + if((manualSyncTimer += frameMillis) < millis0) { + manualSyncTimer = millis0; + } + }); + } + }else if((manualSyncTimer += frameMillis) < millis) { manualSyncTimer = millis; - }else { - manualSyncTimer += frameMillis; - } - if(frameTime >= 0 && frameTime < frameMillis) { - return sleepPromise(frameMillis - frameTime); } } }else { diff --git a/sources/wasm-gc-teavm/js/platformRuntime.js b/sources/wasm-gc-teavm/js/platformRuntime.js index 2bbe912..00c3b1f 100644 --- a/sources/wasm-gc-teavm/js/platformRuntime.js +++ b/sources/wasm-gc-teavm/js/platformRuntime.js @@ -199,6 +199,13 @@ function swapDelayImpl() { eagruntimeImpl.platformRuntime["immediateContinue"] = new WebAssembly.Suspending(immediateContinueImpl); +/** + * @return {boolean} + */ +eagruntimeImpl.platformRuntime["immediateContinueSupported"] = function() { + return allowImmediateContinue; +}; + /** * @param {number} id * @param {string} str