Update #44 - WebAssembly GC support, fix more WebRTC bugs

This commit is contained in:
lax1dude
2024-12-03 23:38:28 -08:00
parent 919014b4df
commit 70b52bbf7a
216 changed files with 34358 additions and 91 deletions

View File

@ -0,0 +1,143 @@
package net.lax1dude.eaglercraft.v1_8.sp.internal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import org.teavm.interop.Import;
import org.teavm.jso.core.JSString;
import org.teavm.jso.typedarrays.Uint8Array;
import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCDirectArrayConverter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.BetterJSStringConverter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WASMGCClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.wasm_gc_teavm.JS_IPCPacketData;
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.wasm_gc_teavm.SingleThreadWorker;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class ClientPlatformSingleplayer {
private static final Logger logger = LogManager.getLogger("ClientPlatformSingleplayer");
private static boolean isSingleThreadMode = false;
private static final LinkedList<IPCPacketData> singleThreadMessageQueue = new LinkedList<>();
public static void startIntegratedServer(boolean singleThreadMode) {
singleThreadMode |= ((WASMGCClientConfigAdapter)PlatformRuntime.getClientConfigAdapter()).isSingleThreadModeTeaVM();
if(singleThreadMode) {
if(!isSingleThreadMode) {
SingleThreadWorker.singleThreadStartup(singleThreadMessageQueue::add);
isSingleThreadMode = true;
}
}else {
if(!startIntegratedServer0()) {
logger.error("Failed to start integrated server!");
logger.error("Falling back to single thread mode...");
startIntegratedServer(true);
}else {
logger.info("Integrated server started");
}
}
}
@Import(module = "clientPlatformSingleplayer", name = "startIntegratedServer")
private static native boolean startIntegratedServer0();
public static void sendPacket(IPCPacketData packet) {
if(isSingleThreadMode) {
SingleThreadWorker.sendPacketToWorker(packet);
}else {
ByteBuffer buf = WASMGCDirectArrayConverter.byteArrayToBuffer(packet.contents);
try {
sendPacket0(BetterJSStringConverter.stringToJS(packet.channel), WASMGCBufferAllocator.getUnsignedByteBufferView(buf));
}finally {
PlatformRuntime.freeByteBuffer(buf);
}
}
}
@Import(module = "clientPlatformSingleplayer", name = "sendPacket")
private static native void sendPacket0(JSString channel, Uint8Array arr);
public static List<IPCPacketData> recieveAllPacket() {
if(isSingleThreadMode) {
if(singleThreadMessageQueue.size() == 0) {
return null;
}else {
List<IPCPacketData> ret = new ArrayList<>(singleThreadMessageQueue);
singleThreadMessageQueue.clear();
return ret;
}
}else {
int cnt = getAvailablePackets();
if(cnt == 0) {
return null;
}
IPCPacketData[] ret = new IPCPacketData[cnt];
for(int i = 0; i < cnt; ++i) {
ret[i] = getNextPacket().internalize();
}
return Arrays.asList(ret);
}
}
@Import(module = "clientPlatformSingleplayer", name = "getAvailablePackets")
private static native int getAvailablePackets();
@Import(module = "clientPlatformSingleplayer", name = "getNextPacket")
private static native JS_IPCPacketData getNextPacket();
public static boolean canKillWorker() {
return !isSingleThreadMode;
}
@Import(module = "clientPlatformSingleplayer", name = "killWorker")
public static native void killWorker();
public static boolean isRunningSingleThreadMode() {
return isSingleThreadMode;
}
public static boolean isSingleThreadModeSupported() {
return true;
}
public static void updateSingleThreadMode() {
if(isSingleThreadMode) {
SingleThreadWorker.singleThreadUpdate();
}
}
public static void showCrashReportOverlay(String report, int x, int y, int w, int h) {
showCrashReportOverlay0(BetterJSStringConverter.stringToJS(report), x, y, w, h);
}
@Import(module = "clientPlatformSingleplayer", name = "showCrashReportOverlay")
private static native void showCrashReportOverlay0(JSString report, int x, int y, int w, int h);
@Import(module = "clientPlatformSingleplayer", name = "hideCrashReportOverlay")
public static native void hideCrashReportOverlay();
}

View File

@ -0,0 +1,146 @@
package net.lax1dude.eaglercraft.v1_8.sp.server.internal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
import org.teavm.interop.Import;
import org.teavm.jso.JSFunctor;
import org.teavm.jso.JSObject;
import org.teavm.jso.core.JSString;
import org.teavm.jso.typedarrays.Uint8Array;
import net.lax1dude.eaglercraft.v1_8.Filesystem;
import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCDirectArrayConverter;
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.BetterJSStringConverter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WASMGCClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerIntegratedServerWorker;
import net.lax1dude.eaglercraft.v1_8.sp.server.IWASMCrashCallback;
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.wasm_gc_teavm.JS_IPCPacketData;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class ServerPlatformSingleplayer {
private static final List<IPCPacketData> messageQueue = new LinkedList<>();
private static boolean singleThreadMode = false;
private static Consumer<IPCPacketData> singleThreadCB = null;
private static IEaglerFilesystem filesystem = null;
public static void initializeContext() {
singleThreadMode = false;
singleThreadCB = null;
filesystem = Filesystem.getHandleFor(getClientConfigAdapter().getWorldsDB());
VFile2.setPrimaryFilesystem(filesystem);
}
public static IEaglerFilesystem getWorldsDatabase() {
return filesystem;
}
public static void initializeContextSingleThread(Consumer<IPCPacketData> packetSendCallback) {
singleThreadMode = true;
singleThreadCB = packetSendCallback;
filesystem = Filesystem.getHandleFor(getClientConfigAdapter().getWorldsDB());
}
public static void sendPacket(IPCPacketData packet) {
if(singleThreadMode) {
singleThreadCB.accept(packet);
}else {
ByteBuffer buf = WASMGCDirectArrayConverter.byteArrayToBuffer(packet.contents);
try {
sendPacket0(BetterJSStringConverter.stringToJS(packet.channel), WASMGCBufferAllocator.getUnsignedByteBufferView(buf));
}finally {
PlatformRuntime.freeByteBuffer(buf);
}
}
}
@Import(module = "serverPlatformSingleplayer", name = "sendPacket")
private static native void sendPacket0(JSString channel, Uint8Array arr);
public static List<IPCPacketData> recieveAllPacket() {
if(singleThreadMode) {
if(messageQueue.size() == 0) {
return null;
}else {
List<IPCPacketData> ret = new ArrayList<>(messageQueue);
messageQueue.clear();
return ret;
}
}else {
int cnt = getAvailablePackets();
if(cnt == 0) {
return null;
}
IPCPacketData[] ret = new IPCPacketData[cnt];
for(int i = 0; i < cnt; ++i) {
ret[i] = getNextPacket().internalize();
}
return Arrays.asList(ret);
}
}
@Import(module = "serverPlatformSingleplayer", name = "getAvailablePackets")
private static native int getAvailablePackets();
@Import(module = "serverPlatformSingleplayer", name = "getNextPacket")
private static native JS_IPCPacketData getNextPacket();
@Import(module = "platformRuntime", name = "immediateContinue")
public static native void immediateContinue();
public static IClientConfigAdapter getClientConfigAdapter() {
return WASMGCClientConfigAdapter.instance;
}
public static boolean isSingleThreadMode() {
return singleThreadMode;
}
public static void recievePacketSingleThreadTeaVM(IPCPacketData pkt) {
messageQueue.add(pkt);
}
public static void setCrashCallbackWASM(IWASMCrashCallback callback) {
setCrashCallbackWASM0().call(callback != null ? callback::callback : null);
}
@JSFunctor
private static interface JSWASMCrashCallback extends JSObject {
void callback(String crashReport, boolean terminated);
}
private static interface JSWASMCrashCallbackInterface extends JSObject {
void call(JSWASMCrashCallback callback);
}
@Import(module = "serverPlatformSingleplayer", name = "setCrashCallback")
private static native JSWASMCrashCallbackInterface setCrashCallbackWASM0();
}

View File

@ -0,0 +1,37 @@
package net.lax1dude.eaglercraft.v1_8.sp.server.internal.wasm_gc_teavm;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
import org.teavm.jso.typedarrays.Uint8Array;
import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCDirectArrayConverter;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public interface JS_IPCPacketData extends JSObject {
@JSProperty
String getCh();
@JSProperty
Uint8Array getData();
default IPCPacketData internalize() {
return new IPCPacketData(getCh(), WASMGCDirectArrayConverter.externU8ArrayToByteArray(getData()));
}
}

View File

@ -0,0 +1,44 @@
package net.lax1dude.eaglercraft.v1_8.sp.server.internal.wasm_gc_teavm;
import java.util.function.Consumer;
import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerIntegratedServerWorker;
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.ServerPlatformSingleplayer;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class SingleThreadWorker {
private static final Logger logger = LogManager.getLogger("SingleThreadWorker");
public static void singleThreadStartup(Consumer<IPCPacketData> packetSendCallback) {
logger.info("Starting single-thread mode worker...");
ServerPlatformSingleplayer.initializeContextSingleThread(packetSendCallback);
EaglerIntegratedServerWorker.singleThreadMain();
}
public static void sendPacketToWorker(IPCPacketData pkt) {
ServerPlatformSingleplayer.recievePacketSingleThreadTeaVM(pkt);
}
public static void singleThreadUpdate() {
EaglerIntegratedServerWorker.singleThreadUpdate();
}
}

View File

@ -0,0 +1,61 @@
package net.lax1dude.eaglercraft.v1_8.sp.server.internal.wasm_gc_teavm;
import java.io.PrintStream;
import org.teavm.interop.Import;
import org.teavm.jso.JSObject;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WASMGCClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.sp.ipc.IPCPacket15Crashed;
import net.lax1dude.eaglercraft.v1_8.sp.ipc.IPCPacketFFProcessKeepAlive;
import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerIntegratedServerWorker;
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.ServerPlatformSingleplayer;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class WorkerMain {
public static void _main() {
PrintStream systemOut = System.out;
PrintStream systemErr = System.err;
try {
systemOut.println("WorkerMain: [INFO] eaglercraftx worker thread is starting...");
JSObject startArgs = getEaglerXOpts();
systemOut.println("WorkerMain: [INFO] reading configuration");
((WASMGCClientConfigAdapter)WASMGCClientConfigAdapter.instance).loadNative(startArgs);
systemOut.println("WorkerMain: [INFO] initializing server runtime");
ServerPlatformSingleplayer.initializeContext();
systemOut.println("WorkerMain: [INFO] starting worker thread");
PlatformRuntime.setThreadName("IntegratedServer");
EaglerIntegratedServerWorker.serverMain();
}catch(Throwable t) {
System.setOut(systemOut);
System.setErr(systemErr);
systemErr.println("WorkerMain: [ERROR] uncaught exception thrown!");
EagRuntime.debugPrintStackTraceToSTDERR(t);
EaglerIntegratedServerWorker.sendIPCPacket(new IPCPacket15Crashed("UNCAUGHT EXCEPTION CAUGHT IN WORKER PROCESS!\n\n" + EagRuntime.getStackTrace(t)));
EaglerIntegratedServerWorker.sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacketFFProcessKeepAlive.EXITED));
}finally {
systemErr.println("WorkerMain: [ERROR] eaglercraftx worker thread has exited");
}
}
@Import(module = "platformRuntime", name = "getEaglercraftXOpts")
private static native JSObject getEaglerXOpts();
}