diff --git a/desktopRuntime/resources/assets/minecraft/lang/en_US.lang b/desktopRuntime/resources/assets/minecraft/lang/en_US.lang index 622cf50..5dce2ca 100755 --- a/desktopRuntime/resources/assets/minecraft/lang/en_US.lang +++ b/desktopRuntime/resources/assets/minecraft/lang/en_US.lang @@ -592,7 +592,7 @@ eaglercraft.singleplayer.demo.create.create.tooltip=Play the Minecraft 1.8 demo 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/src/game/java/net/minecraft/client/gui/GuiMultiplayer.java b/src/game/java/net/minecraft/client/gui/GuiMultiplayer.java index 20b6cbc..d5aeab1 100755 --- a/src/game/java/net/minecraft/client/gui/GuiMultiplayer.java +++ b/src/game/java/net/minecraft/client/gui/GuiMultiplayer.java @@ -100,9 +100,8 @@ public class GuiMultiplayer extends GuiScreen implements GuiYesNoCallback { this.serverListSelector.func_148195_a(this.savedServerList); if (lanServerList == null) { lanServerList = new LANServerList(); - } else { - lanServerList.forceRefresh(); } + lanServerList.forceRefresh(); } else { this.serverListSelector.setDimensions(this.width, this.height, 32, this.height - 64); } diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java index 97dafa6..ad26371 100755 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java +++ b/src/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/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java index 4cd715c..7fbabe9 100755 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java +++ b/src/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/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLAllocator.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLAllocator.java index 1a403d2..7952d25 100755 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLAllocator.java +++ b/src/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/src/main/java/net/lax1dude/eaglercraft/v1_8/Display.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/Display.java index 910a457..b5b85d0 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/Display.java +++ b/src/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/src/main/java/net/lax1dude/eaglercraft/v1_8/EagRuntime.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/EagRuntime.java index 8748e36..c272160 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/EagRuntime.java +++ b/src/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/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglerInputStream.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglerInputStream.java index 05c9580..2ef6a0b 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglerInputStream.java +++ b/src/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/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java index 94d0575..2f547d3 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java @@ -10,7 +10,7 @@ public class EaglercraftVersion { /// Customize these to fit your fork: public static final String projectForkName = "EaglercraftX"; - public static final String projectForkVersion = "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/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFolderResourcePack.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFolderResourcePack.java index 57840ee..d075449 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFolderResourcePack.java +++ b/src/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/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java index 6554930..c44464f 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java +++ b/src/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/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionPipeline.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionPipeline.java index 0885ff2..c23631b 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/FixedFunctionPipeline.java +++ b/src/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/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/StreamBuffer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/StreamBuffer.java index f92d5f3..3f926dd 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/StreamBuffer.java +++ b/src/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/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/SingleplayerServerController.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/SingleplayerServerController.java index a7781c9..ae15610 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/SingleplayerServerController.java +++ b/src/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/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientPeer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientPeer.java index 11add57..6b955ca 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientPeer.java +++ b/src/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/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANServerController.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANServerController.java index 5f09065..e03f5bd 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANServerController.java +++ b/src/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/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterEPK.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterEPK.java index 65f26e3..c1ab282 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterEPK.java +++ b/src/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/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterMCA.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterMCA.java index eff2004..398b4b4 100755 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterMCA.java +++ b/src/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/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/ClientBootFactory.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/ClientBootFactory.java index b81aa09..6811d08 100755 --- a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/ClientBootFactory.java +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/ClientBootFactory.java @@ -382,7 +382,7 @@ public class ClientBootFactory { System.arraycopy(classesServerJS, 0, concatClassesServerJS, appendToClassesServerJS.length, classesServerJS.length); TeaVMBlobURLHandle classesServerJSURL = TeaVMBlobURLManager.registerNewURLByte(concatClassesServerJS, "text/javascript"); toCleanOnException.add(classesServerJSURL); - launchOpts.put("serverWorkerURI", classesServerJSURL); + launchOpts.put("serverWorkerURI", classesServerJSURL.toExternalForm()); doUpdateMessage(cb, "Resolving assets.epk (" + clientData.epkFiles.get(0).dataUUID + ")"); TeaVMBlobURLHandle assetsEPKURL = loadingCache.get(new UUIDStringPair(clientData.epkFiles.get(0).dataUUID, "application/octet-stream")); if(assetsEPKURL == null) { diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java index d89461b..f33c441 100755 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java @@ -36,8 +36,6 @@ import org.teavm.jso.gamepad.Gamepad; import org.teavm.jso.gamepad.GamepadButton; import org.teavm.jso.gamepad.GamepadEvent; -import net.lax1dude.eaglercraft.v1_8.Display; -import net.lax1dude.eaglercraft.v1_8.EagUtils; import net.lax1dude.eaglercraft.v1_8.internal.teavm.ClientMain; import net.lax1dude.eaglercraft.v1_8.internal.teavm.EarlyLoadScreen; import net.lax1dude.eaglercraft.v1_8.internal.teavm.InputEvent; @@ -889,7 +887,7 @@ public class PlatformInput { update(0); } - private static final long[] syncTimer = new long[1]; + private static double syncTimer = 0.0; public static void update(int fpsLimit) { double r = getDevicePixelRatio(win); @@ -943,21 +941,44 @@ public class PlatformInput { PlatformScreenRecord.captureFrameHook(); if(getVisibilityState(win.getDocument())) { if(vsyncSupport && vsync) { - syncTimer[0] = 0l; + syncTimer = 0.0; asyncRequestAnimationFrame(); }else { - if(fpsLimit <= 0) { - syncTimer[0] = 0l; + if(fpsLimit <= 0 || fpsLimit > 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/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java index 2b115aa..628ee0b 100755 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java +++ b/src/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/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java index ac62fc9..068217c 100755 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java +++ b/src/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/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebRTC.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebRTC.java index 7d061e0..bf9548a 100755 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebRTC.java +++ b/src/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/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayBufferAllocator.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayBufferAllocator.java index 1147a40..907e941 100755 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayBufferAllocator.java +++ b/src/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/src/wasm-gc-teavm-loader/js/library.js b/src/wasm-gc-teavm-loader/js/library.js index 9df0e50..b026609 100755 --- a/src/wasm-gc-teavm-loader/js/library.js +++ b/src/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/src/wasm-gc-teavm-loader/js/pre.js b/src/wasm-gc-teavm-loader/js/pre.js index 5aa2e5a..82fcae4 100755 --- a/src/wasm-gc-teavm-loader/js/pre.js +++ b/src/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/src/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java b/src/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java index fd2fa03..0afae53 100755 --- a/src/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java +++ b/src/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/src/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/WASMGCBufferAllocator.java b/src/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/WASMGCBufferAllocator.java index 8df8ed0..7ebe8d6 100755 --- a/src/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/WASMGCBufferAllocator.java +++ b/src/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/src/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/WASMGCDirectArrayConverter.java b/src/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/WASMGCDirectArrayConverter.java index 0013904..d397ec2 100755 --- a/src/wasm-gc-teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/WASMGCDirectArrayConverter.java +++ b/src/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/src/wasm-gc-teavm/js/eagruntime_entrypoint.js b/src/wasm-gc-teavm/js/eagruntime_entrypoint.js index 6593fff..9bbf32d 100755 --- a/src/wasm-gc-teavm/js/eagruntime_entrypoint.js +++ b/src/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/src/wasm-gc-teavm/js/platformInput.js b/src/wasm-gc-teavm/js/platformInput.js index 186cc68..0573086 100755 --- a/src/wasm-gc-teavm/js/platformInput.js +++ b/src/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/src/wasm-gc-teavm/js/platformRuntime.js b/src/wasm-gc-teavm/js/platformRuntime.js index 69d49cc..076525d 100755 --- a/src/wasm-gc-teavm/js/platformRuntime.js +++ b/src/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 diff --git a/wasm_gc_teavm/CompileLoaderWASM.bat b/wasm_gc_teavm/CompileLoaderWASM.bat index 86c08b2..f77b11c 100755 --- a/wasm_gc_teavm/CompileLoaderWASM.bat +++ b/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/wasm_gc_teavm/CompileLoaderWASM.sh b/wasm_gc_teavm/CompileLoaderWASM.sh index aa7b022..e528864 100755 --- a/wasm_gc_teavm/CompileLoaderWASM.sh +++ b/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/wasm_gc_teavm/buildtools/MakeWASMClientBundle.jar b/wasm_gc_teavm/buildtools/MakeWASMClientBundle.jar index 6a1c494..48aa8bd 100755 Binary files a/wasm_gc_teavm/buildtools/MakeWASMClientBundle.jar and b/wasm_gc_teavm/buildtools/MakeWASMClientBundle.jar differ diff --git a/wasm_gc_teavm/javascript/epw_meta.txt b/wasm_gc_teavm/javascript/epw_meta.txt index bbf4cd0..34daf11 100755 --- a/wasm_gc_teavm/javascript/epw_meta.txt +++ b/wasm_gc_teavm/javascript/epw_meta.txt @@ -1,8 +1,8 @@ -client-version-integer=44 +client-version-integer=45 client-package-name=net.lax1dude.eaglercraft.v1_8.client client-origin-name=EaglercraftX -client-origin-version=u44 +client-origin-version=u45 client-origin-vendor=lax1dude client-fork-name=EaglercraftX -client-fork-version=u44 +client-fork-version=u45 client-fork-vendor=lax1dude diff --git a/wasm_gc_teavm/javascript/loader.js b/wasm_gc_teavm/javascript/loader.js index 312dc73..505a07b 100755 --- a/wasm_gc_teavm/javascript/loader.js +++ b/wasm_gc_teavm/javascript/loader.js @@ -1 +1,24 @@ -var Module=typeof Module!="undefined"?Module:{};var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof WorkerGlobalScope!="undefined";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string"&&process.type!="renderer";if(ENVIRONMENT_IS_NODE){}Module["locateFile"]=function(path){if(path==="loader.wasm"){return window.__eaglercraftXLoaderContextPre.loaderWASMURL}else{return path}};const rootElement=window.__eaglercraftXLoaderContextPre.rootElement;const optsObj=window.__eaglercraftXLoaderContextPre.eaglercraftXOpts;const epwFile=window.__eaglercraftXLoaderContextPre.theEPWFileBuffer;const splashURL=window.__eaglercraftXLoaderContextPre.splashURL;const results=[null];function createCrashParentElement(){var oldSplash=null;var node;while(node=rootElement.lastChild){if(!oldSplash){oldSplash=node}rootElement.removeChild(node)}const parentElement=document.createElement("div");parentElement.classList.add("_eaglercraftX_wrapper_element");parentElement.setAttribute("style","position:relative;width:100%;height:100%;overflow:hidden;");if(oldSplash){oldSplash.classList.add("_eaglercraftX_early_splash_element");oldSplash.style.position="absolute";oldSplash.style.top="0px";oldSplash.style.left="0px";oldSplash.style.right="0px";oldSplash.style.bottom="0px";oldSplash.style.zIndex="2";parentElement.appendChild(oldSplash)}rootElement.appendChild(parentElement);return parentElement}var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw 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