mirror of
https://github.com/Eaglercraft-Archive/Eaglercraftx-1.8.8-src.git
synced 2025-06-28 10:58:15 -05:00
Update #37 - Touch support without userscript, many other feats
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.sp.internal;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@ -9,15 +10,19 @@ import org.teavm.jso.JSFunctor;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.dom.events.ErrorEvent;
|
||||
import org.teavm.jso.dom.events.EventListener;
|
||||
import org.teavm.jso.dom.html.HTMLScriptElement;
|
||||
import org.teavm.jso.typedarrays.ArrayBuffer;
|
||||
import org.teavm.jso.workers.Worker;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.teavm.ClientMain;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMBlobURLManager;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMClientConfigAdapter;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils;
|
||||
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.teavm.SingleThreadWorker;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2023-2024 lax1dude. All Rights Reserved.
|
||||
@ -38,28 +43,34 @@ public class ClientPlatformSingleplayer {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger("ClientPlatformSingleplayer");
|
||||
|
||||
private static final LinkedList<IPCPacketData> messageQueue = new LinkedList();
|
||||
private static final LinkedList<IPCPacketData> messageQueue = new LinkedList<>();
|
||||
|
||||
@JSBody(params = {}, script = "return (typeof window.eaglercraftXClientScriptElement !== \"undefined\") ? window.eaglercraftXClientScriptElement : null;")
|
||||
@JSBody(params = {}, script = "return (typeof eaglercraftXClientScriptElement !== \"undefined\") ? eaglercraftXClientScriptElement : null;")
|
||||
private static native JSObject loadIntegratedServerSourceOverride();
|
||||
|
||||
@JSBody(params = {}, script = "return (typeof window.eaglercraftXClientScriptURL === \"string\") ? window.eaglercraftXClientScriptURL : null;")
|
||||
@JSBody(params = {}, script = "return (typeof eaglercraftXClientScriptURL === \"string\") ? eaglercraftXClientScriptURL : null;")
|
||||
private static native String loadIntegratedServerSourceOverrideURL();
|
||||
|
||||
@JSBody(params = {}, script = "try{throw new Error();}catch(ex){return ex.stack;}return null;")
|
||||
@JSBody(params = {}, script = "try{throw new Error();}catch(ex){return ex.stack||null;}return null;")
|
||||
private static native String loadIntegratedServerSourceStack();
|
||||
|
||||
@JSBody(params = { "csc" }, script = "if(typeof csc.src === \"string\" && csc.src.length > 0) return csc.src; else return null;")
|
||||
private static native String loadIntegratedServerSourceURL(JSObject scriptTag);
|
||||
|
||||
@JSBody(params = { "csc", "tail" }, script = "const cscText = csc.text;"
|
||||
@JSBody(params = { "csc", "tail" }, script = "var cscText = csc.text;"
|
||||
+ "if(typeof cscText === \"string\" && cscText.length > 0) return new Blob([cscText, tail], { type: \"text/javascript;charset=utf8\" });"
|
||||
+ "else return null;")
|
||||
private static native JSObject loadIntegratedServerSourceInline(JSObject scriptTag, String tail);
|
||||
|
||||
@JSBody(params = { "csc" }, script = "var cscText = csc.text;"
|
||||
+ "if(typeof cscText === \"string\" && cscText.length > 0) return cscText;"
|
||||
+ "else return null;")
|
||||
private static native String loadIntegratedServerSourceInlineStr(JSObject scriptTag);
|
||||
|
||||
private static String integratedServerSource = null;
|
||||
private static String integratedServerSourceOriginalURL = null;
|
||||
private static boolean serverSourceLoaded = false;
|
||||
private static boolean isSingleThreadMode = false;
|
||||
|
||||
private static Worker workerObj = null;
|
||||
|
||||
@ -68,7 +79,7 @@ public class ClientPlatformSingleplayer {
|
||||
public void onMessage(String channel, ArrayBuffer buf);
|
||||
}
|
||||
|
||||
@JSBody(params = { "w", "wb" }, script = "w.onmessage = function(o) { wb(o.data.ch, o.data.dat); };")
|
||||
@JSBody(params = { "w", "wb" }, script = "w.addEventListener(\"message\", function(o) { wb(o.data.ch, o.data.dat); });")
|
||||
private static native void registerPacketHandler(Worker w, WorkerBinaryPacketHandler wb);
|
||||
|
||||
@JSBody(params = { "w", "ch", "dat" }, script = "w.postMessage({ ch: ch, dat : dat });")
|
||||
@ -108,13 +119,13 @@ public class ClientPlatformSingleplayer {
|
||||
private static JSObject loadIntegratedServerSource() {
|
||||
String str = loadIntegratedServerSourceOverrideURL();
|
||||
if(str != null) {
|
||||
ArrayBuffer buf = PlatformRuntime.downloadRemoteURI(str);
|
||||
ArrayBuffer buf = PlatformRuntime.downloadRemoteURI(str, true);
|
||||
if(buf != null) {
|
||||
integratedServerSourceOriginalURL = str;
|
||||
logger.info("Using integrated server at: {}", str);
|
||||
logger.info("Using integrated server at: {}", truncateURL(str));
|
||||
return createBlobObj(buf, workerBootstrapCode);
|
||||
}else {
|
||||
logger.error("Failed to load integrated server: {}", str);
|
||||
logger.error("Failed to load integrated server: {}", truncateURL(str));
|
||||
}
|
||||
}
|
||||
JSObject el = loadIntegratedServerSourceOverride();
|
||||
@ -128,25 +139,34 @@ public class ClientPlatformSingleplayer {
|
||||
return el;
|
||||
}
|
||||
}else {
|
||||
ArrayBuffer buf = PlatformRuntime.downloadRemoteURI(url);
|
||||
ArrayBuffer buf = PlatformRuntime.downloadRemoteURI(url, true);
|
||||
if(buf != null) {
|
||||
integratedServerSourceOriginalURL = url;
|
||||
logger.info("Using integrated server from script tag src: {}", url);
|
||||
logger.info("Using integrated server from script tag src: {}", truncateURL(url));
|
||||
return createBlobObj(buf, workerBootstrapCode);
|
||||
}else {
|
||||
logger.error("Failed to load integrated server from script tag src: {}", url);
|
||||
logger.error("Failed to load integrated server from script tag src: {}", truncateURL(url));
|
||||
}
|
||||
}
|
||||
}
|
||||
str = TeaVMUtils.tryResolveClassesSource();
|
||||
if(str != null) {
|
||||
ArrayBuffer buf = PlatformRuntime.downloadRemoteURI(str);
|
||||
ArrayBuffer buf = PlatformRuntime.downloadRemoteURI(str, true);
|
||||
if(buf != null) {
|
||||
integratedServerSourceOriginalURL = str;
|
||||
logger.info("Using integrated server from script src: {}", str);
|
||||
logger.info("Using integrated server from script src: {}", truncateURL(str));
|
||||
return createBlobObj(buf, workerBootstrapCode);
|
||||
}else {
|
||||
logger.error("Failed to load integrated server from script src: {}", str);
|
||||
logger.error("Failed to load integrated server from script src: {}", truncateURL(str));
|
||||
}
|
||||
}
|
||||
HTMLScriptElement sc = TeaVMUtils.tryResolveClassesSourceInline();
|
||||
if(sc != null) {
|
||||
el = loadIntegratedServerSourceInline(sc, workerBootstrapCode);
|
||||
if(el != null) {
|
||||
integratedServerSourceOriginalURL = "inline script tag (client guess)";
|
||||
logger.info("Loading integrated server from (likely) inline script tag");
|
||||
return el;
|
||||
}
|
||||
}
|
||||
logger.info("Could not resolve the location of client's classes.js!");
|
||||
@ -155,12 +175,57 @@ public class ClientPlatformSingleplayer {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String truncateURL(String url) {
|
||||
if(url == null) return null;
|
||||
if(url.length() > 256) {
|
||||
url = url.substring(0, 254) + "...";
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
private static String createIntegratedServerWorkerURL() {
|
||||
JSObject blobObj = loadIntegratedServerSource();
|
||||
if(blobObj == null) {
|
||||
return null;
|
||||
}
|
||||
return createWorkerScriptURL(blobObj);
|
||||
return TeaVMBlobURLManager.registerNewURLBlob(blobObj).toExternalForm();
|
||||
}
|
||||
|
||||
public static byte[] getIntegratedServerSourceTeaVM() {
|
||||
String str = loadIntegratedServerSourceOverrideURL();
|
||||
if(str != null) {
|
||||
ArrayBuffer buf = PlatformRuntime.downloadRemoteURI(str, true);
|
||||
if(buf != null) {
|
||||
return TeaVMUtils.wrapByteArrayBuffer(buf);
|
||||
}
|
||||
}
|
||||
JSObject el = loadIntegratedServerSourceOverride();
|
||||
if(el != null) {
|
||||
String url = loadIntegratedServerSourceURL(el);
|
||||
if(url == null) {
|
||||
str = loadIntegratedServerSourceInlineStr(el);
|
||||
if(str != null) {
|
||||
return str.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
}else {
|
||||
ArrayBuffer buf = PlatformRuntime.downloadRemoteURI(url, true);
|
||||
if(buf != null) {
|
||||
return TeaVMUtils.wrapByteArrayBuffer(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
str = TeaVMUtils.tryResolveClassesSource();
|
||||
if(str != null) {
|
||||
ArrayBuffer buf = PlatformRuntime.downloadRemoteURI(str, true);
|
||||
if(buf != null) {
|
||||
return TeaVMUtils.wrapByteArrayBuffer(buf);
|
||||
}
|
||||
}
|
||||
HTMLScriptElement sc = TeaVMUtils.tryResolveClassesSourceInline();
|
||||
if(sc != null) {
|
||||
return sc.getText().getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getLoadedWorkerURLTeaVM() {
|
||||
@ -171,36 +236,58 @@ public class ClientPlatformSingleplayer {
|
||||
return (serverSourceLoaded && workerObj != null) ? integratedServerSourceOriginalURL : null;
|
||||
}
|
||||
|
||||
public static void startIntegratedServer() {
|
||||
if(!serverSourceLoaded) {
|
||||
integratedServerSource = createIntegratedServerWorkerURL();
|
||||
serverSourceLoaded = true;
|
||||
}
|
||||
|
||||
if(integratedServerSource == null) {
|
||||
throw new RuntimeException("Could not resolve the location of client's classes.js! Make sure client's classes.js is linked/embedded in a dedicated <script> tag. Define \"window.eaglercraftXClientScriptElement\" or \"window.eaglercraftXClientScriptURL\" to force");
|
||||
}
|
||||
|
||||
workerObj = Worker.create(integratedServerSource);
|
||||
workerObj.onError(new EventListener<ErrorEvent>() {
|
||||
@Override
|
||||
public void handleEvent(ErrorEvent evt) {
|
||||
logger.error("Worker Error: {}", evt.getError());
|
||||
PlatformRuntime.printNativeExceptionToConsoleTeaVM(evt);
|
||||
public static void startIntegratedServer(boolean singleThreadMode) {
|
||||
singleThreadMode |= ((TeaVMClientConfigAdapter)PlatformRuntime.getClientConfigAdapter()).isSingleThreadModeTeaVM();
|
||||
if(singleThreadMode) {
|
||||
if(!isSingleThreadMode) {
|
||||
SingleThreadWorker.singleThreadStartup((pkt) -> {
|
||||
synchronized(messageQueue) {
|
||||
messageQueue.add(pkt);
|
||||
}
|
||||
});
|
||||
isSingleThreadMode = true;
|
||||
}
|
||||
});
|
||||
registerPacketHandler(workerObj, new WorkerBinaryPacketHandlerImpl());
|
||||
sendWorkerStartPacket(workerObj, PlatformRuntime.getClientConfigAdapter().getIntegratedServerOpts().toString());
|
||||
|
||||
}else {
|
||||
if(!serverSourceLoaded) {
|
||||
integratedServerSource = createIntegratedServerWorkerURL();
|
||||
serverSourceLoaded = true;
|
||||
}
|
||||
|
||||
if(integratedServerSource == null) {
|
||||
logger.error("Could not resolve the location of client's classes.js! Make sure client's classes.js is linked/embedded in a dedicated <script> tag. Define \"window.eaglercraftXClientScriptElement\" or \"window.eaglercraftXClientScriptURL\" to force");
|
||||
logger.error("Falling back to single thread mode...");
|
||||
startIntegratedServer(true);
|
||||
return;
|
||||
}
|
||||
|
||||
workerObj = Worker.create(integratedServerSource);
|
||||
workerObj.addEventListener("error", new EventListener<ErrorEvent>() {
|
||||
@Override
|
||||
public void handleEvent(ErrorEvent evt) {
|
||||
logger.error("Worker Error: {}", evt.getError());
|
||||
PlatformRuntime.printNativeExceptionToConsoleTeaVM(evt);
|
||||
}
|
||||
});
|
||||
registerPacketHandler(workerObj, new WorkerBinaryPacketHandlerImpl());
|
||||
sendWorkerStartPacket(workerObj, PlatformRuntime.getClientConfigAdapter().getIntegratedServerOpts().toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendPacket(IPCPacketData packet) {
|
||||
sendPacketTeaVM(packet.channel, TeaVMUtils.unwrapArrayBuffer(packet.contents));
|
||||
if(isSingleThreadMode) {
|
||||
SingleThreadWorker.sendPacketToWorker(packet);
|
||||
}else {
|
||||
sendPacketTeaVM(packet.channel, TeaVMUtils.unwrapArrayBuffer(packet.contents));
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendPacketTeaVM(String channel, ArrayBuffer packet) {
|
||||
if(workerObj != null) {
|
||||
sendWorkerPacket(workerObj, channel, packet);
|
||||
if(isSingleThreadMode) {
|
||||
SingleThreadWorker.sendPacketToWorker(new IPCPacketData(channel, TeaVMUtils.wrapByteArrayBuffer(packet)));
|
||||
}else {
|
||||
if(workerObj != null) {
|
||||
sendWorkerPacket(workerObj, channel, packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,7 +304,7 @@ public class ClientPlatformSingleplayer {
|
||||
}
|
||||
|
||||
public static boolean canKillWorker() {
|
||||
return true;
|
||||
return !isSingleThreadMode;
|
||||
}
|
||||
|
||||
public static void killWorker() {
|
||||
@ -228,7 +315,17 @@ public class ClientPlatformSingleplayer {
|
||||
}
|
||||
|
||||
public static boolean isRunningSingleThreadMode() {
|
||||
return false;
|
||||
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) {
|
||||
|
@ -3,17 +3,36 @@ package net.lax1dude.eaglercraft.v1_8.sp.server.internal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.teavm.interop.Async;
|
||||
import org.teavm.interop.AsyncCallback;
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSFunctor;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.browser.Window;
|
||||
import org.teavm.jso.core.JSString;
|
||||
import org.teavm.jso.dom.events.EventListener;
|
||||
import org.teavm.jso.dom.events.MessageEvent;
|
||||
import org.teavm.jso.typedarrays.ArrayBuffer;
|
||||
|
||||
import com.google.common.collect.Collections2;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.EagUtils;
|
||||
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.PlatformFilesystem;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.teavm.ES6ShimStatus;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.teavm.EnumES6ShimStatus;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.teavm.EnumES6Shims;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.teavm.MessageChannel;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMBlobURLManager;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMClientConfigAdapter;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
|
||||
|
||||
@ -36,7 +55,17 @@ public class ServerPlatformSingleplayer {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger("ServerPlatformSingleplayer");
|
||||
|
||||
private static final LinkedList<IPCPacketData> messageQueue = new LinkedList();
|
||||
private static final LinkedList<IPCPacketData> messageQueue = new LinkedList<>();
|
||||
|
||||
private static boolean immediateContinueSupport = false;
|
||||
private static MessageChannel immediateContinueChannel = null;
|
||||
private static Runnable currentContinueHack = null;
|
||||
private static final Object immediateContLock = new Object();
|
||||
private static final JSString emptyJSString = JSString.valueOf("");
|
||||
private static boolean singleThreadMode = false;
|
||||
private static Consumer<IPCPacketData> singleThreadCB = null;
|
||||
|
||||
private static IEaglerFilesystem filesystem = null;
|
||||
|
||||
@JSFunctor
|
||||
private static interface WorkerBinaryPacketHandler extends JSObject {
|
||||
@ -63,7 +92,7 @@ public class ServerPlatformSingleplayer {
|
||||
|
||||
}
|
||||
|
||||
@JSBody(params = { "wb" }, script = "onmessage = function(o) { wb(o.data.ch, o.data.dat); };")
|
||||
@JSBody(params = { "wb" }, script = "__eaglerXOnMessage = function(o) { wb(o.data.ch, o.data.dat); };")
|
||||
private static native void registerPacketHandler(WorkerBinaryPacketHandler wb);
|
||||
|
||||
public static void register() {
|
||||
@ -71,14 +100,66 @@ public class ServerPlatformSingleplayer {
|
||||
}
|
||||
|
||||
public static void initializeContext() {
|
||||
PlatformFilesystem.initialize(getClientConfigAdapter().getWorldsDB());
|
||||
singleThreadMode = false;
|
||||
singleThreadCB = null;
|
||||
ES6ShimStatus shimStatus = ES6ShimStatus.getRuntimeStatus();
|
||||
if(shimStatus != null) {
|
||||
EnumES6ShimStatus stat = shimStatus.getStatus();
|
||||
switch(stat) {
|
||||
case STATUS_ERROR:
|
||||
case STATUS_DISABLED_ERRORS:
|
||||
logger.error("ES6 Shim Status: {}", stat.statusDesc);
|
||||
break;
|
||||
case STATUS_ENABLED_ERRORS:
|
||||
logger.error("ES6 Shim Status: {}", stat.statusDesc);
|
||||
dumpShims(shimStatus.getShims());
|
||||
break;
|
||||
case STATUS_DISABLED:
|
||||
case STATUS_NOT_PRESENT:
|
||||
logger.info("ES6 Shim Status: {}", stat.statusDesc);
|
||||
break;
|
||||
case STATUS_ENABLED:
|
||||
logger.info("ES6 Shim Status: {}", stat.statusDesc);
|
||||
dumpShims(shimStatus.getShims());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TeaVMBlobURLManager.initialize();
|
||||
|
||||
checkImmediateContinueSupport();
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
private static void dumpShims(Set<EnumES6Shims> shims) {
|
||||
if(!shims.isEmpty()) {
|
||||
logger.info("(Enabled {} shims: {})", shims.size(), String.join(", ", Collections2.transform(shims, (shim) -> shim.shimDesc)));
|
||||
}
|
||||
}
|
||||
|
||||
@JSBody(params = { "ch", "dat" }, script = "postMessage({ ch: ch, dat : dat });")
|
||||
public static native void sendPacketTeaVM(String channel, ArrayBuffer arr);
|
||||
|
||||
public static void sendPacket(IPCPacketData packet) {
|
||||
sendPacketTeaVM(packet.channel, TeaVMUtils.unwrapArrayBuffer(packet.contents));
|
||||
if(singleThreadMode) {
|
||||
singleThreadCB.accept(packet);
|
||||
}else {
|
||||
sendPacketTeaVM(packet.channel, TeaVMUtils.unwrapArrayBuffer(packet.contents));
|
||||
}
|
||||
}
|
||||
|
||||
public static List<IPCPacketData> recieveAllPacket() {
|
||||
@ -86,7 +167,7 @@ public class ServerPlatformSingleplayer {
|
||||
if(messageQueue.size() == 0) {
|
||||
return null;
|
||||
}else {
|
||||
List<IPCPacketData> ret = new ArrayList(messageQueue);
|
||||
List<IPCPacketData> ret = new ArrayList<>(messageQueue);
|
||||
messageQueue.clear();
|
||||
return ret;
|
||||
}
|
||||
@ -96,4 +177,118 @@ public class ServerPlatformSingleplayer {
|
||||
public static IClientConfigAdapter getClientConfigAdapter() {
|
||||
return TeaVMClientConfigAdapter.instance;
|
||||
}
|
||||
|
||||
private static void checkImmediateContinueSupport() {
|
||||
try {
|
||||
immediateContinueSupport = false;
|
||||
if(!MessageChannel.supported()) {
|
||||
logger.error("Fast immediate continue will be disabled for server context due to MessageChannel being unsupported");
|
||||
return;
|
||||
}
|
||||
immediateContinueChannel = MessageChannel.create();
|
||||
immediateContinueChannel.getPort1().addEventListener("message", new EventListener<MessageEvent>() {
|
||||
@Override
|
||||
public void handleEvent(MessageEvent evt) {
|
||||
Runnable toRun;
|
||||
synchronized(immediateContLock) {
|
||||
toRun = currentContinueHack;
|
||||
currentContinueHack = null;
|
||||
}
|
||||
if(toRun != null) {
|
||||
toRun.run();
|
||||
}
|
||||
}
|
||||
});
|
||||
immediateContinueChannel.getPort1().start();
|
||||
immediateContinueChannel.getPort2().start();
|
||||
final boolean[] checkMe = new boolean[1];
|
||||
checkMe[0] = false;
|
||||
currentContinueHack = () -> {
|
||||
checkMe[0] = true;
|
||||
};
|
||||
immediateContinueChannel.getPort2().postMessage(emptyJSString);
|
||||
if(checkMe[0]) {
|
||||
currentContinueHack = null;
|
||||
if(immediateContinueChannel != null) {
|
||||
safeShutdownChannel(immediateContinueChannel);
|
||||
}
|
||||
immediateContinueChannel = null;
|
||||
logger.error("Fast immediate continue will be disabled for server context due to actually continuing immediately");
|
||||
return;
|
||||
}
|
||||
EagUtils.sleep(10l);
|
||||
currentContinueHack = null;
|
||||
if(!checkMe[0]) {
|
||||
if(immediateContinueChannel != null) {
|
||||
safeShutdownChannel(immediateContinueChannel);
|
||||
}
|
||||
immediateContinueChannel = null;
|
||||
logger.error("Fast immediate continue will be disabled for server context due to startup check failing");
|
||||
}else {
|
||||
immediateContinueSupport = true;
|
||||
}
|
||||
}catch(Throwable t) {
|
||||
logger.error("Fast immediate continue will be disabled for server context due to exceptions");
|
||||
immediateContinueSupport = false;
|
||||
if(immediateContinueChannel != null) {
|
||||
safeShutdownChannel(immediateContinueChannel);
|
||||
}
|
||||
immediateContinueChannel = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void safeShutdownChannel(MessageChannel chan) {
|
||||
try {
|
||||
chan.getPort1().close();
|
||||
}catch(Throwable tt) {
|
||||
}
|
||||
try {
|
||||
chan.getPort2().close();
|
||||
}catch(Throwable tt) {
|
||||
}
|
||||
}
|
||||
|
||||
public static void immediateContinue() {
|
||||
if(singleThreadMode) {
|
||||
PlatformRuntime.immediateContinue();
|
||||
}else {
|
||||
if(immediateContinueSupport) {
|
||||
immediateContinueTeaVM();
|
||||
}else {
|
||||
EagUtils.sleep(0l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Async
|
||||
private static native void immediateContinueTeaVM();
|
||||
|
||||
private static void immediateContinueTeaVM(final AsyncCallback<Void> cb) {
|
||||
synchronized(immediateContLock) {
|
||||
if(currentContinueHack != null) {
|
||||
cb.error(new IllegalStateException("Worker thread is already waiting for an immediate continue callback!"));
|
||||
return;
|
||||
}
|
||||
currentContinueHack = () -> {
|
||||
cb.complete(null);
|
||||
};
|
||||
try {
|
||||
immediateContinueChannel.getPort2().postMessage(emptyJSString);
|
||||
}catch(Throwable t) {
|
||||
logger.error("Caught error posting immediate continue, using setTimeout instead");
|
||||
Window.setTimeout(() -> cb.complete(null), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isSingleThreadMode() {
|
||||
return singleThreadMode;
|
||||
}
|
||||
|
||||
public static void recievePacketSingleThreadTeaVM(IPCPacketData pkt) {
|
||||
synchronized(messageQueue) {
|
||||
messageQueue.add(pkt);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
package net.lax1dude.eaglercraft.v1_8.sp.server.internal.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();
|
||||
}
|
||||
|
||||
}
|
@ -77,7 +77,7 @@ public class WorkerMain {
|
||||
public void onMessage(String msg);
|
||||
}
|
||||
|
||||
@JSBody(params = { "wb" }, script = "onmessage = function(o) { wb(o.data.msg); };")
|
||||
@JSBody(params = { "wb" }, script = "__eaglerXOnMessage = function(o) { wb(o.data.msg); }; addEventListener(\"message\", function(evt) { __eaglerXOnMessage(evt); });")
|
||||
private static native void setOnMessage(WorkerArgumentsPacketHandler cb);
|
||||
|
||||
@Async
|
||||
|
Reference in New Issue
Block a user