Update #22 - Singleplayer and shared worlds, shader fixes

This commit is contained in:
lax1dude
2023-12-12 01:22:54 -08:00
parent e3d6256493
commit 3f00d402fa
2350 changed files with 45241 additions and 7798 deletions

View File

@ -0,0 +1,246 @@
package net.lax1dude.eaglercraft.v1_8.sp.internal;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.teavm.jso.JSBody;
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.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Uint8Array;
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.TeaVMUtils;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
/**
* 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
* 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 final LinkedList<IPCPacketData> messageQueue = new LinkedList();
@JSBody(params = {}, script = "return (typeof window.eaglercraftXClientScriptElement !== \"undefined\") ? window.eaglercraftXClientScriptElement : null;")
private static native JSObject loadIntegratedServerSourceOverride();
@JSBody(params = {}, script = "return (typeof window.eaglercraftXClientScriptURL === \"string\") ? window.eaglercraftXClientScriptURL : null;")
private static native String loadIntegratedServerSourceOverrideURL();
@JSBody(params = {}, script = "try{throw new Error();}catch(ex){return ex.stack;}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;"
+ "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);
private static String integratedServerSource = null;
private static String integratedServerSourceOriginalURL = null;
private static boolean serverSourceLoaded = false;
private static Worker workerObj = null;
@JSFunctor
private static interface WorkerBinaryPacketHandler extends JSObject {
public void onMessage(String channel, ArrayBuffer buf);
}
@JSBody(params = { "w", "wb" }, script = "w.onmessage = 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 });")
private static native void sendWorkerPacket(Worker w, String channel, ArrayBuffer arr);
@JSBody(params = { "w", "workerArgs" }, script = "w.postMessage({ msg : workerArgs });")
private static native void sendWorkerStartPacket(Worker w, String workerArgs);
private static class WorkerBinaryPacketHandlerImpl implements WorkerBinaryPacketHandler {
public void onMessage(String channel, ArrayBuffer buf) {
if(channel == null) {
logger.error("Recieved IPC packet with null channel");
return;
}
if(buf == null) {
logger.error("Recieved IPC packet with null buffer");
return;
}
synchronized(messageQueue) {
messageQueue.add(new IPCPacketData(channel, TeaVMUtils.wrapUnsignedByteArray(Uint8Array.create(buf))));
}
}
}
@JSBody(params = { "blobObj" }, script = "return URL.createObjectURL(blobObj);")
private static native String createWorkerScriptURL(JSObject blobObj);
@JSBody(params = { "cscText", "tail" }, script = "return new Blob([cscText, tail], { type: \"text/javascript;charset=utf8\" });")
private static native JSObject createBlobObj(ArrayBuffer buf, String tail);
private static final String workerBootstrapCode = "\n\nmain([\"_worker_process_\"]);";
private static JSObject loadIntegratedServerSource() {
String str = loadIntegratedServerSourceOverrideURL();
if(str != null) {
ArrayBuffer buf = PlatformRuntime.downloadRemoteURI(str);
if(buf != null) {
integratedServerSourceOriginalURL = str;
logger.info("Using integrated server at: {}", str);
return createBlobObj(buf, workerBootstrapCode);
}else {
logger.error("Failed to load integrated server: {}", str);
}
}
JSObject el = loadIntegratedServerSourceOverride();
if(el != null) {
String url = loadIntegratedServerSourceURL(el);
if(url == null) {
el = loadIntegratedServerSourceInline(el, workerBootstrapCode);
if(el != null) {
integratedServerSourceOriginalURL = "inline script tag";
logger.info("Loading integrated server from inline script tag");
return el;
}
}else {
ArrayBuffer buf = PlatformRuntime.downloadRemoteURI(url);
if(buf != null) {
integratedServerSourceOriginalURL = url;
logger.info("Using integrated server from script tag src: {}", url);
return createBlobObj(buf, workerBootstrapCode);
}else {
logger.error("Failed to load integrated server from script tag src: {}", url);
}
}
}
str = TeaVMUtils.tryResolveClassesSource();
if(str != null) {
ArrayBuffer buf = PlatformRuntime.downloadRemoteURI(str);
if(buf != null) {
integratedServerSourceOriginalURL = str;
logger.info("Using integrated server from script src: {}", str);
return createBlobObj(buf, workerBootstrapCode);
}else {
logger.error("Failed to load integrated server from script src: {}", str);
}
}
logger.info("Could not resolve the location of client's classes.js!");
logger.info("Make sure client's classes.js is linked/embedded in a dedicated <script> tag");
logger.info("Define \"window.eaglercraftXClientScriptElement\" or \"window.eaglercraftXClientScriptURL\" to force");
return null;
}
private static String createIntegratedServerWorkerURL() {
JSObject blobObj = loadIntegratedServerSource();
if(blobObj == null) {
return null;
}
return createWorkerScriptURL(blobObj);
}
public static String getLoadedWorkerURLTeaVM() {
return (serverSourceLoaded && workerObj != null) ? integratedServerSource : null;
}
public static String getLoadedWorkerSourceURLTeaVM() {
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);
}
});
registerPacketHandler(workerObj, new WorkerBinaryPacketHandlerImpl());
sendWorkerStartPacket(workerObj, PlatformRuntime.getClientConfigAdapter().dumpConfig().toString());
}
public static void sendPacket(IPCPacketData packet) {
ArrayBuffer arb = ArrayBuffer.create(packet.contents.length);
Uint8Array ar = Uint8Array.create(arb);
ar.set(packet.contents);
sendPacketTeaVM(packet.channel, TeaVMUtils.unwrapUnsignedByteArray(packet.contents).getBuffer());
}
public static void sendPacketTeaVM(String channel, ArrayBuffer packet) {
if(workerObj != null) {
sendWorkerPacket(workerObj, channel, packet);
}
}
public static List<IPCPacketData> recieveAllPacket() {
synchronized(messageQueue) {
if(messageQueue.size() == 0) {
return null;
}else {
List<IPCPacketData> ret = new ArrayList<>(messageQueue);
messageQueue.clear();
return ret;
}
}
}
public static boolean canKillWorker() {
return true;
}
public static void killWorker() {
if(workerObj != null) {
workerObj.terminate();
workerObj = null;
}
}
public static boolean isRunningSingleThreadMode() {
return false;
}
public static void showCrashReportOverlay(String report, int x, int y, int w, int h) {
ClientMain.showIntegratedServerCrashReportOverlay(report, x, y, w, h);
}
public static void hideCrashReportOverlay() {
ClientMain.hideIntegratedServerCrashReportOverlay();
}
}

View File

@ -0,0 +1,106 @@
package net.lax1dude.eaglercraft.v1_8.sp.server.internal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import net.lax1dude.eaglercraft.v1_8.sp.server.classes.EaglerServerBootstrap;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSFunctor;
import org.teavm.jso.JSObject;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Uint8Array;
import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem;
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;
/**
* 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
* 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 Logger logger = LogManager.getLogger("ServerPlatformSingleplayer");
private static final LinkedList<IPCPacketData> messageQueue = new LinkedList();
@JSFunctor
private static interface WorkerBinaryPacketHandler extends JSObject {
public void onMessage(String channel, ArrayBuffer buf);
}
private static class WorkerBinaryPacketHandlerImpl implements WorkerBinaryPacketHandler {
public void onMessage(String channel, ArrayBuffer buf) {
if(channel == null) {
logger.error("Recieved IPC packet with null channel");
return;
}
if(buf == null) {
logger.error("Recieved IPC packet with null buffer");
return;
}
synchronized(messageQueue) {
messageQueue.add(new IPCPacketData(channel, TeaVMUtils.wrapUnsignedByteArray(Uint8Array.create(buf))));
}
}
}
@JSBody(params = { "wb" }, script = "onmessage = function(o) { wb(o.data.ch, o.data.dat); };")
private static native void registerPacketHandler(WorkerBinaryPacketHandler wb);
public static void register() {
registerPacketHandler(new WorkerBinaryPacketHandlerImpl());
}
public static void initializeContext() {
PlatformFilesystem.initialize();
EaglerServerBootstrap.staticInit();
}
@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) {
ArrayBuffer arb = ArrayBuffer.create(packet.contents.length);
Uint8Array ar = Uint8Array.create(arb);
ar.set(packet.contents);
sendPacketTeaVM(packet.channel, arb);
}
public static List<IPCPacketData> recieveAllPacket() {
synchronized(messageQueue) {
if(messageQueue.size() == 0) {
return null;
}else {
List<IPCPacketData> ret = new ArrayList(messageQueue);
messageQueue.clear();
return ret;
}
}
}
public static IClientConfigAdapter getClientConfigAdapter() {
return TeaVMClientConfigAdapter.instance;
}
}

View File

@ -0,0 +1,98 @@
package net.lax1dude.eaglercraft.v1_8.sp.server.internal.teavm;
import java.io.PrintStream;
import org.json.JSONObject;
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 net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMClientConfigAdapter;
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) 2023-2024 lax1dude, ayunami2000. 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 {
__println(systemOut, false, "WorkerMain: [INFO] eaglercraftx worker thread is starting...");
String startArgs = getStartArgs();
__println(systemOut, false, "WorkerMain: [INFO] reading configuration");
if(startArgs == null) {
throw new NullPointerException("startup arguments is null!");
}
((TeaVMClientConfigAdapter)TeaVMClientConfigAdapter.instance).loadJSON(new JSONObject(startArgs));
__println(systemOut, false, "WorkerMain: [INFO] initializing server runtime");
EaglerIntegratedServerWorker.enableLoggingRedirector(true);
ServerPlatformSingleplayer.initializeContext();
__println(systemOut, false, "WorkerMain: [INFO] starting worker thread");
PlatformRuntime.setThreadName("IntegratedServer");
EaglerIntegratedServerWorker.serverMain();
}catch(Throwable t) {
System.setOut(systemOut);
System.setErr(systemErr);
__println(systemErr, true, "WorkerMain: [ERROR] uncaught exception thrown!");
EaglerIntegratedServerWorker.sendLogMessagePacket(EagRuntime.getStackTrace(t), true);
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 {
__println(systemErr, true, "WorkerMain: [ERROR] eaglercraftx worker thread has exited");
}
}
private static void __println(PrintStream stream, boolean err, String msg) {
stream.println(msg);
try {
EaglerIntegratedServerWorker.sendLogMessagePacket(msg, err);
}catch(Throwable t) {
}
}
@JSFunctor
private static interface WorkerArgumentsPacketHandler extends JSObject {
public void onMessage(String msg);
}
@JSBody(params = { "wb" }, script = "onmessage = function(o) { wb(o.data.msg); };")
private static native void setOnMessage(WorkerArgumentsPacketHandler cb);
@Async
private static native String getStartArgs();
private static void getStartArgs(final AsyncCallback<String> cb) {
setOnMessage(new WorkerArgumentsPacketHandler() {
@Override
public void onMessage(String msg) {
ServerPlatformSingleplayer.register();
cb.complete(msg);
}
});
}
}