partial vsync implementation, immediate continue hack
This commit is contained in:
parent
511e95a067
commit
d862a08a4a
|
@ -59,15 +59,10 @@ public class EAGMinecraftServer extends MinecraftServer {
|
|||
this.tick();
|
||||
lastTick = SysUtil.steadyTimeMillis();
|
||||
} else {
|
||||
boolean mustYield = false;
|
||||
while (delta >= 50L) {
|
||||
if(mustYield) {
|
||||
SysUtil.sleep(1); // allow some async
|
||||
}
|
||||
if (delta >= 50l) {
|
||||
delta -= 50L;
|
||||
lastTick = SysUtil.steadyTimeMillis();
|
||||
lastTick += 50l;
|
||||
this.tick();
|
||||
mustYield = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -749,6 +749,8 @@ public class IntegratedServer {
|
|||
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket01StopServer.ID));
|
||||
currentProcess = null;
|
||||
}
|
||||
}else {
|
||||
SysUtil.sleep(50);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -767,7 +769,7 @@ public class IntegratedServer {
|
|||
|
||||
mainLoop();
|
||||
|
||||
SysUtil.sleep(1); // allow some async to occur
|
||||
SysUtil.immediateContinue();
|
||||
}
|
||||
|
||||
// yee
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSClass;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.JSProperty;
|
||||
import org.teavm.jso.workers.MessagePort;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
@JSClass
|
||||
public class MessageChannel implements JSObject {
|
||||
|
||||
@JSBody(params = { }, script = "return (typeof MessageChannel !== \"undefined\");")
|
||||
public static native boolean supported();
|
||||
|
||||
@JSProperty
|
||||
public native MessagePort getPort1();
|
||||
|
||||
@JSProperty
|
||||
public native MessagePort getPort2();
|
||||
|
||||
}
|
|
@ -4,6 +4,10 @@ import org.teavm.interop.Async;
|
|||
import org.teavm.interop.AsyncCallback;
|
||||
import org.teavm.jso.JSBody;
|
||||
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.platform.Platform;
|
||||
import org.teavm.platform.PlatformRunnable;
|
||||
|
||||
|
@ -45,4 +49,110 @@ public class SysUtil {
|
|||
}
|
||||
}
|
||||
|
||||
private static boolean hasCheckedImmediateContinue = 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("");
|
||||
|
||||
public static void immediateContinue() {
|
||||
if(!hasCheckedImmediateContinue) {
|
||||
hasCheckedImmediateContinue = true;
|
||||
checkImmediateContinueSupport();
|
||||
}
|
||||
if(immediateContinueChannel != null) {
|
||||
immediateContinueTeaVM();
|
||||
}else {
|
||||
sleep(0);
|
||||
}
|
||||
}
|
||||
|
||||
@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) {
|
||||
System.err.println("Caught error posting immediate continue, using setTimeout instead");
|
||||
Window.setTimeout(() -> cb.complete(null), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkImmediateContinueSupport() {
|
||||
try {
|
||||
immediateContinueChannel = null;
|
||||
if(!MessageChannel.supported()) {
|
||||
System.err.println("Fast immediate continue will be disabled for server context due to MessageChannel being unsupported");
|
||||
return;
|
||||
}
|
||||
immediateContinueChannel = new MessageChannel();
|
||||
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;
|
||||
System.err.println("Fast immediate continue will be disabled for server context due to actually continuing immediately");
|
||||
return;
|
||||
}
|
||||
sleep(10);
|
||||
currentContinueHack = null;
|
||||
if(!checkMe[0]) {
|
||||
if(immediateContinueChannel != null) {
|
||||
safeShutdownChannel(immediateContinueChannel);
|
||||
}
|
||||
immediateContinueChannel = null;
|
||||
System.err.println("Fast immediate continue will be disabled for server context due to startup check failing");
|
||||
}
|
||||
}catch(Throwable t) {
|
||||
System.err.println("Fast immediate continue will be disabled for server context due to exceptions");
|
||||
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) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -81,8 +81,6 @@ import net.lax1dude.eaglercraft.RelayQuery.VersionMismatch;
|
|||
import net.lax1dude.eaglercraft.RelayWorldsQuery;
|
||||
import net.lax1dude.eaglercraft.ServerQuery;
|
||||
import net.lax1dude.eaglercraft.Voice;
|
||||
import net.lax1dude.eaglercraft.adapter.EaglerAdapterImpl2.ProgramGL;
|
||||
import net.lax1dude.eaglercraft.adapter.EaglerAdapterImpl2.RateLimit;
|
||||
import net.lax1dude.eaglercraft.adapter.lwjgl.GameWindowListener;
|
||||
import net.lax1dude.eaglercraft.sp.relay.pkt.IPacket;
|
||||
import net.lax1dude.eaglercraft.sp.relay.pkt.IPacket07LocalWorlds.LocalWorld;
|
||||
|
@ -971,11 +969,15 @@ public class EaglerAdapterImpl2 {
|
|||
public static final boolean shouldShutdown() {
|
||||
return Display.isCloseRequested();
|
||||
}
|
||||
public static final void updateDisplay() {
|
||||
Display.update();
|
||||
}
|
||||
public static final void setVSyncEnabled(boolean p1) {
|
||||
Display.setVSyncEnabled(p1);
|
||||
public static final void updateDisplay(int fpsLimit, boolean vsync) {
|
||||
if(vsync) {
|
||||
Display.setVSyncEnabled(true);
|
||||
Display.update();
|
||||
}else {
|
||||
Display.setVSyncEnabled(false);
|
||||
Display.sync(fpsLimit);
|
||||
Display.update();
|
||||
}
|
||||
}
|
||||
public static final void enableRepeatEvents(boolean b) {
|
||||
Keyboard.enableRepeatEvents(b);
|
||||
|
@ -1005,17 +1007,14 @@ public class EaglerAdapterImpl2 {
|
|||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
public static final void syncDisplay(int performanceToFps) {
|
||||
Display.sync(performanceToFps);
|
||||
}
|
||||
|
||||
private static final Set<String> rateLimitedAddresses = new HashSet();
|
||||
private static final Set<String> blockedAddresses = new HashSet();
|
||||
private static final Set<String> rateLimitedAddresses = new HashSet<>();
|
||||
private static final Set<String> blockedAddresses = new HashSet<>();
|
||||
|
||||
private static WebSocketClient clientSocket = null;
|
||||
private static final Object socketSync = new Object();
|
||||
|
||||
private static LinkedList<byte[]> readPackets = new LinkedList();
|
||||
private static LinkedList<byte[]> readPackets = new LinkedList<>();
|
||||
|
||||
private static class EaglerSocketClient extends WebSocketClient {
|
||||
|
||||
|
@ -1465,7 +1464,7 @@ public class EaglerAdapterImpl2 {
|
|||
public static final float getVoiceSpeakVolume() {
|
||||
return volumeSpeak;
|
||||
}
|
||||
private static final Set<String> emptySet = new HashSet();
|
||||
private static final Set<String> emptySet = new HashSet<>();
|
||||
public static final Set<String> getVoiceListening() {
|
||||
return emptySet;
|
||||
}
|
||||
|
@ -1478,7 +1477,7 @@ public class EaglerAdapterImpl2 {
|
|||
public static final Set<String> getVoiceMuted() {
|
||||
return emptySet;
|
||||
}
|
||||
private static final List<String> emptyList = new ArrayList();
|
||||
private static final List<String> emptyList = new ArrayList<>();
|
||||
public static final List<String> getVoiceRecent() {
|
||||
return emptyList;
|
||||
}
|
||||
|
@ -1546,8 +1545,8 @@ public class EaglerAdapterImpl2 {
|
|||
|
||||
private static class ServerQueryImpl extends WebSocketClient implements ServerQuery {
|
||||
|
||||
private final LinkedList<QueryResponse> queryResponses = new LinkedList();
|
||||
private final LinkedList<byte[]> queryResponsesBytes = new LinkedList();
|
||||
private final LinkedList<QueryResponse> queryResponses = new LinkedList<>();
|
||||
private final LinkedList<byte[]> queryResponsesBytes = new LinkedList<>();
|
||||
private final String type;
|
||||
private boolean open;
|
||||
private boolean alive;
|
||||
|
|
|
@ -99,7 +99,8 @@ public class EarlyLoadScreen {
|
|||
_wglDrawArrays(_wGL_TRIANGLES, 0, 6);
|
||||
_wglDisableVertexAttribArray(0);
|
||||
_wglFlush();
|
||||
updateDisplay();
|
||||
updateDisplay(0, false);
|
||||
sleep(20);
|
||||
|
||||
_wglUseProgram(null);
|
||||
_wglBindBuffer(_wGL_ARRAY_BUFFER, null);
|
||||
|
@ -156,7 +157,8 @@ public class EarlyLoadScreen {
|
|||
_wglDrawArrays(_wGL_TRIANGLES, 0, 6);
|
||||
_wglDisableVertexAttribArray(0);
|
||||
_wglFlush();
|
||||
updateDisplay();
|
||||
updateDisplay(0, false);
|
||||
sleep(20);
|
||||
|
||||
_wglUseProgram(null);
|
||||
_wglBindBuffer(_wGL_ARRAY_BUFFER, null);
|
||||
|
|
|
@ -1613,4 +1613,33 @@ public class EaglerAdapterGL30 extends EaglerAdapterImpl2 {
|
|||
return ret;
|
||||
}
|
||||
|
||||
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] = steadyTimeMillis();
|
||||
}else {
|
||||
long millis = 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) {
|
||||
sleep((int)(frameMillis - frameTime));
|
||||
blocked = true;
|
||||
}
|
||||
}
|
||||
}else {
|
||||
timerPtr[0] = 0l;
|
||||
}
|
||||
|
||||
return blocked;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -243,6 +243,7 @@ public class Minecraft implements Runnable {
|
|||
|
||||
public boolean lanState = false;
|
||||
public boolean yeeState = false;
|
||||
public boolean checkGLErrors = false;
|
||||
|
||||
public Minecraft() {
|
||||
this.tempDisplayHeight = 480;
|
||||
|
@ -404,7 +405,7 @@ public class Minecraft implements Runnable {
|
|||
EaglerAdapter.glPopMatrix();
|
||||
|
||||
EaglerAdapter.glFlush();
|
||||
EaglerAdapter.updateDisplay();
|
||||
updateDisplay();
|
||||
|
||||
long t = t1 + 17 + 17*i - EaglerAdapter.steadyTimeMillis();
|
||||
if(t > 0) {
|
||||
|
@ -437,7 +438,7 @@ public class Minecraft implements Runnable {
|
|||
EaglerAdapter.glPopMatrix();
|
||||
|
||||
EaglerAdapter.glFlush();
|
||||
EaglerAdapter.updateDisplay();
|
||||
updateDisplay();
|
||||
|
||||
long t = t1 + 17 + 17*i - EaglerAdapter.steadyTimeMillis();
|
||||
if(t > 0) {
|
||||
|
@ -468,7 +469,7 @@ public class Minecraft implements Runnable {
|
|||
EaglerAdapter.glPopMatrix();
|
||||
|
||||
EaglerAdapter.glFlush();
|
||||
EaglerAdapter.updateDisplay();
|
||||
updateDisplay();
|
||||
|
||||
long t = t1 + 17 + 17*i - EaglerAdapter.steadyTimeMillis();
|
||||
if(t > 0) {
|
||||
|
@ -478,7 +479,7 @@ public class Minecraft implements Runnable {
|
|||
|
||||
EaglerAdapter.glClear(EaglerAdapter.GL_COLOR_BUFFER_BIT | EaglerAdapter.GL_DEPTH_BUFFER_BIT);
|
||||
EaglerAdapter.glFlush();
|
||||
EaglerAdapter.updateDisplay();
|
||||
updateDisplay();
|
||||
|
||||
EaglerAdapter.sleep(100);
|
||||
|
||||
|
@ -521,7 +522,7 @@ public class Minecraft implements Runnable {
|
|||
EaglerAdapter.glEnable(EaglerAdapter.GL_ALPHA_TEST);
|
||||
EaglerAdapter.glAlphaFunc(EaglerAdapter.GL_GREATER, 0.1F);
|
||||
EaglerAdapter.glFlush();
|
||||
EaglerAdapter.updateDisplay();
|
||||
updateDisplay();
|
||||
EaglerAdapter.optimize();
|
||||
}
|
||||
|
||||
|
@ -603,6 +604,7 @@ public class Minecraft implements Runnable {
|
|||
* string.
|
||||
*/
|
||||
public void checkGLError(String par1Str) {
|
||||
if(!checkGLErrors) return;
|
||||
int var2;
|
||||
|
||||
|
||||
|
@ -712,7 +714,7 @@ public class Minecraft implements Runnable {
|
|||
EaglerAdapter.glEnable(EaglerAdapter.GL_TEXTURE_2D);
|
||||
|
||||
if (!EaglerAdapter.isKeyDown(65)) {
|
||||
EaglerAdapter.updateDisplay();
|
||||
updateDisplay();
|
||||
}
|
||||
|
||||
if (this.thePlayer != null && this.thePlayer.isEntityInsideOpaqueBlock()) {
|
||||
|
@ -781,11 +783,6 @@ public class Minecraft implements Runnable {
|
|||
chunkGeometryUpdates = 0;
|
||||
secondTimer = EaglerAdapter.steadyTimeMillis();
|
||||
}
|
||||
this.mcProfiler.startSection("syncDisplay");
|
||||
|
||||
if (this.func_90020_K() > 0) {
|
||||
EaglerAdapter.syncDisplay(EntityRenderer.performanceToFps(this.func_90020_K()));
|
||||
}
|
||||
|
||||
if(isGonnaTakeDatScreenShot) {
|
||||
isGonnaTakeDatScreenShot = false;
|
||||
|
@ -1104,6 +1101,11 @@ public class Minecraft implements Runnable {
|
|||
this.voiceOverlay.setResolution(var4, var5);
|
||||
}
|
||||
|
||||
public void updateDisplay() {
|
||||
int i = this.func_90020_K();
|
||||
EaglerAdapter.updateDisplay(i > 0 ? EntityRenderer.performanceToFps(i) : 0, false);
|
||||
}
|
||||
|
||||
private boolean wasPaused = false;
|
||||
|
||||
/**
|
||||
|
|
|
@ -101,13 +101,13 @@ public class GuiConnecting extends GuiScreen {
|
|||
private void showDisconnectScreen(String e) {
|
||||
RateLimit l = EaglerAdapter.getRateLimitStatus();
|
||||
if(l == RateLimit.NOW_LOCKED) {
|
||||
this.mc.displayGuiScreen(new GuiDisconnected(this.field_98098_c, "disconnect.ipNowLocked", "disconnect.endOfStream", null));
|
||||
this.mc.displayGuiScreen(new GuiDisconnected(this.field_98098_c, "disconnect.ipNowLocked", "disconnect.endOfStream"));
|
||||
}else if(l == RateLimit.LOCKED) {
|
||||
this.mc.displayGuiScreen(new GuiDisconnected(this.field_98098_c, "disconnect.ipLocked", "disconnect.endOfStream", null));
|
||||
this.mc.displayGuiScreen(new GuiDisconnected(this.field_98098_c, "disconnect.ipLocked", "disconnect.endOfStream"));
|
||||
}else if(l == RateLimit.BLOCKED) {
|
||||
this.mc.displayGuiScreen(new GuiDisconnected(this.field_98098_c, "disconnect.ipBlocked", "disconnect.endOfStream", null));
|
||||
this.mc.displayGuiScreen(new GuiDisconnected(this.field_98098_c, "disconnect.ipBlocked", "disconnect.endOfStream"));
|
||||
}else if(l == RateLimit.FAILED_POSSIBLY_LOCKED) {
|
||||
this.mc.displayGuiScreen(new GuiDisconnected(this.field_98098_c, "disconnect.ipFailedPossiblyLocked", "disconnect.endOfStream", null));
|
||||
this.mc.displayGuiScreen(new GuiDisconnected(this.field_98098_c, "disconnect.ipFailedPossiblyLocked", "disconnect.endOfStream"));
|
||||
}else {
|
||||
this.mc.displayGuiScreen(new GuiDisconnected(this.field_98098_c, "connect.failed", "disconnect.genericReason", "could not connect to "+uri, e));
|
||||
}
|
||||
|
|
|
@ -137,7 +137,7 @@ public class LoadingScreenRenderer implements IProgressUpdate {
|
|||
|
||||
this.mc.fontRenderer.drawStringWithShadow(this.currentlyDisplayedText, (var5 - this.mc.fontRenderer.getStringWidth(this.currentlyDisplayedText)) / 2, var6 / 2 - 4 - 16, 16777215);
|
||||
this.mc.fontRenderer.drawStringWithShadow(this.field_73727_a, (var5 - this.mc.fontRenderer.getStringWidth(this.field_73727_a)) / 2, var6 / 2 - 4 + 8, 16777215);
|
||||
EaglerAdapter.updateDisplay();
|
||||
this.mc.updateDisplay();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,20 +119,20 @@ public class NetClientHandler extends NetHandler {
|
|||
RateLimit r = EaglerAdapter.getRateLimitStatus();
|
||||
if(r != null) {
|
||||
if(r == RateLimit.NOW_LOCKED) {
|
||||
this.mc.displayGuiScreen(new GuiDisconnected(backToMenu(), "disconnect.ratelimit.ipNowLocked", "disconnect.endOfStream", null));
|
||||
this.mc.displayGuiScreen(new GuiDisconnected(backToMenu(), "disconnect.ratelimit.ipNowLocked", "disconnect.endOfStream"));
|
||||
}else if(r == RateLimit.LOCKED) {
|
||||
this.mc.displayGuiScreen(new GuiDisconnected(backToMenu(), "disconnect.ratelimit.ipLocked", "disconnect.endOfStream", null));
|
||||
this.mc.displayGuiScreen(new GuiDisconnected(backToMenu(), "disconnect.ratelimit.ipLocked", "disconnect.endOfStream"));
|
||||
}else if(r == RateLimit.BLOCKED) {
|
||||
this.mc.displayGuiScreen(new GuiDisconnected(backToMenu(), "disconnect.ratelimit.ipBlocked", "disconnect.endOfStream", null));
|
||||
this.mc.displayGuiScreen(new GuiDisconnected(backToMenu(), "disconnect.ratelimit.ipBlocked", "disconnect.endOfStream"));
|
||||
}else if(r == RateLimit.FAILED_POSSIBLY_LOCKED) {
|
||||
this.mc.displayGuiScreen(new GuiDisconnected(backToMenu(), "disconnect.ratelimit.ipFailedPossiblyLocked", "disconnect.endOfStream", null));
|
||||
this.mc.displayGuiScreen(new GuiDisconnected(backToMenu(), "disconnect.ratelimit.ipFailedPossiblyLocked", "disconnect.endOfStream"));
|
||||
}else {
|
||||
this.mc.displayGuiScreen(new GuiDisconnected(backToMenu(), "disconnect.disconnected", "RateLimit." + r.name(), null));
|
||||
this.mc.displayGuiScreen(new GuiDisconnected(backToMenu(), "disconnect.disconnected", "RateLimit." + r.name()));
|
||||
}
|
||||
}else {
|
||||
if(!(this.mc.currentScreen instanceof GuiDisconnected) && !(this.mc.currentScreen instanceof GuiScreenSingleplayerException) &&
|
||||
!(this.mc.currentScreen instanceof GuiScreenSingleplayerLoading)) {
|
||||
this.mc.stopServerAndDisplayGuiScreen(new GuiDisconnected(backToMenu(), "disconnect.disconnected", "disconnect.endOfStream", null));
|
||||
this.mc.stopServerAndDisplayGuiScreen(new GuiDisconnected(backToMenu(), "disconnect.disconnected", "disconnect.endOfStream"));
|
||||
}
|
||||
}
|
||||
this.disconnected = true;
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.teavm.interop.AsyncCallback;
|
|||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSFunctor;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.JSProperty;
|
||||
import org.teavm.jso.ajax.ReadyStateChangeHandler;
|
||||
import org.teavm.jso.ajax.XMLHttpRequest;
|
||||
import org.teavm.jso.browser.Storage;
|
||||
|
@ -39,6 +40,7 @@ import org.teavm.jso.browser.TimerHandler;
|
|||
import org.teavm.jso.browser.Window;
|
||||
import org.teavm.jso.canvas.CanvasRenderingContext2D;
|
||||
import org.teavm.jso.canvas.ImageData;
|
||||
import org.teavm.jso.core.JSString;
|
||||
import org.teavm.jso.dom.css.CSSStyleDeclaration;
|
||||
import org.teavm.jso.dom.events.ErrorEvent;
|
||||
import org.teavm.jso.dom.events.Event;
|
||||
|
@ -108,10 +110,12 @@ import net.lax1dude.eaglercraft.Voice;
|
|||
import net.lax1dude.eaglercraft.adapter.teavm.EaglercraftLANClient;
|
||||
import net.lax1dude.eaglercraft.adapter.teavm.EaglercraftLANServer;
|
||||
import net.lax1dude.eaglercraft.adapter.teavm.EaglercraftVoiceClient;
|
||||
import net.lax1dude.eaglercraft.adapter.teavm.MessageChannel;
|
||||
import net.lax1dude.eaglercraft.adapter.teavm.SelfDefence;
|
||||
import net.lax1dude.eaglercraft.adapter.teavm.WebGL2RenderingContext;
|
||||
import net.lax1dude.eaglercraft.adapter.teavm.WebGLQuery;
|
||||
import net.lax1dude.eaglercraft.adapter.teavm.WebGLVertexArray;
|
||||
import net.lax1dude.eaglercraft.glemu.EaglerAdapterGL30;
|
||||
import net.lax1dude.eaglercraft.sp.relay.pkt.IPacket;
|
||||
import net.lax1dude.eaglercraft.sp.relay.pkt.IPacket00Handshake;
|
||||
import net.lax1dude.eaglercraft.sp.relay.pkt.IPacket07LocalWorlds;
|
||||
|
@ -243,6 +247,13 @@ public class EaglerAdapterImpl2 {
|
|||
private static String[] identifier = new String[0];
|
||||
private static String integratedServerScript = "worker_bootstrap.js";
|
||||
private static boolean anisotropicFilteringSupported = false;
|
||||
private static boolean vsyncSupport = false;
|
||||
private static int vsyncTimeout = -1;
|
||||
private static boolean useDelayOnSwap = false;
|
||||
private static MessageChannel immediateContinueChannel = null;
|
||||
private static Runnable currentMsgChannelContinueHack = null;
|
||||
|
||||
private static final EagsFileChooser fileChooser = initFileChooser();
|
||||
|
||||
public static final String[] getIdentifier() {
|
||||
return identifier;
|
||||
|
@ -395,6 +406,18 @@ public class EaglerAdapterImpl2 {
|
|||
});
|
||||
onBeforeCloseRegister();
|
||||
|
||||
checkImmediateContinueSupport();
|
||||
|
||||
vsyncTimeout = -1;
|
||||
vsyncSupport = false;
|
||||
|
||||
try {
|
||||
asyncRequestAnimationFrame();
|
||||
vsyncSupport = true;
|
||||
}catch(Throwable t) {
|
||||
System.err.println("VSync is not supported on this browser!");
|
||||
}
|
||||
|
||||
initFileChooser();
|
||||
|
||||
EarlyLoadScreen.paintScreen();
|
||||
|
@ -456,33 +479,54 @@ public class EaglerAdapterImpl2 {
|
|||
@JSBody(params = { }, script = "return window.startVoiceClient();")
|
||||
private static native EaglercraftVoiceClient startVoiceClient();
|
||||
|
||||
private static interface EagsFileChooser extends JSObject {
|
||||
|
||||
@JSProperty
|
||||
HTMLElement getInputElement();
|
||||
|
||||
void openFileChooser(String ext, String mime);
|
||||
|
||||
@JSProperty
|
||||
ArrayBuffer getGetFileChooserResult();
|
||||
|
||||
@JSProperty
|
||||
void setGetFileChooserResult(ArrayBuffer val);
|
||||
|
||||
@JSProperty
|
||||
String getGetFileChooserResultName();
|
||||
|
||||
@JSProperty
|
||||
void setGetFileChooserResultName(String str);
|
||||
|
||||
}
|
||||
|
||||
@JSBody(params = { }, script =
|
||||
"window.eagsFileChooser = {\r\n" +
|
||||
"var ret = {\r\n" +
|
||||
"inputElement: null,\r\n" +
|
||||
"openFileChooser: function(ext, mime){\r\n" +
|
||||
"var el = window.eagsFileChooser.inputElement = document.createElement(\"input\");\r\n" +
|
||||
"var el = ret.inputElement = document.createElement(\"input\");\r\n" +
|
||||
"el.type = \"file\";\r\n" +
|
||||
"el.multiple = false;\r\n" +
|
||||
"el.addEventListener(\"change\", function(evt){\r\n" +
|
||||
"var f = window.eagsFileChooser.inputElement.files;\r\n" +
|
||||
"var f = ret.inputElement.files;\r\n" +
|
||||
"if(f.length == 0){\r\n" +
|
||||
"window.eagsFileChooser.getFileChooserResult = null;\r\n" +
|
||||
"ret.getFileChooserResult = null;\r\n" +
|
||||
"}else{\r\n" +
|
||||
"(async function(){\r\n" +
|
||||
"window.eagsFileChooser.getFileChooserResult = await f[0].arrayBuffer();\r\n" +
|
||||
"window.eagsFileChooser.getFileChooserResultName = f[0].name;\r\n" +
|
||||
"})();\r\n" +
|
||||
"f[0].arrayBuffer().then(function(res) {\r\n" +
|
||||
"ret.getFileChooserResult = res;\r\n" +
|
||||
"ret.getFileChooserResultName = f[0].name;\r\n" +
|
||||
"});\r\n" +
|
||||
"}\r\n" +
|
||||
"});\r\n" +
|
||||
"window.eagsFileChooser.getFileChooserResult = null;\r\n" +
|
||||
"window.eagsFileChooser.getFileChooserResultName = null;\r\n" +
|
||||
"ret.getFileChooserResult = null;\r\n" +
|
||||
"ret.getFileChooserResultName = null;\r\n" +
|
||||
"el.accept = \".\" + ext;\r\n" +
|
||||
"el.click();\r\n" +
|
||||
"},\r\n" +
|
||||
"getFileChooserResult: null,\r\n" +
|
||||
"getFileChooserResultName: null\r\n" +
|
||||
"};")
|
||||
private static native void initFileChooser();
|
||||
"}; return ret;")
|
||||
private static native EagsFileChooser initFileChooser();
|
||||
|
||||
public static final void destroyContext() {
|
||||
|
||||
|
@ -1695,7 +1739,13 @@ public class EaglerAdapterImpl2 {
|
|||
public static final boolean shouldShutdown() {
|
||||
return false;
|
||||
}
|
||||
public static final void updateDisplay() {
|
||||
public static final boolean isVSyncSupported() {
|
||||
return vsyncSupport;
|
||||
}
|
||||
@JSBody(params = { "doc" }, script = "return (typeof doc.visibilityState !== \"string\") || (doc.visibilityState === \"visible\");")
|
||||
private static native boolean getVisibilityState(JSObject doc);
|
||||
private static final long[] syncTimer = new long[1];
|
||||
public static final void updateDisplay(int fpsLimit, boolean vsync) {
|
||||
double r = win.getDevicePixelRatio();
|
||||
int w = parent.getClientWidth();
|
||||
int h = parent.getClientHeight();
|
||||
|
@ -1713,7 +1763,172 @@ public class EaglerAdapterImpl2 {
|
|||
webgl.blitFramebuffer(0, 0, backBufferWidth, backBufferHeight, 0, 0, w2, h2, COLOR_BUFFER_BIT, NEAREST);
|
||||
webgl.bindFramebuffer(FRAMEBUFFER, backBuffer.obj);
|
||||
resizeBackBuffer(w2, h2);
|
||||
sleep(1);
|
||||
|
||||
if(getVisibilityState(win.getDocument())) {
|
||||
if(vsyncSupport && vsync) {
|
||||
syncTimer[0] = 0l;
|
||||
asyncRequestAnimationFrame();
|
||||
}else {
|
||||
if(fpsLimit <= 0) {
|
||||
syncTimer[0] = 0l;
|
||||
swapDelayTeaVM();
|
||||
}else {
|
||||
if(!EaglerAdapterGL30.sync(fpsLimit, syncTimer)) {
|
||||
swapDelayTeaVM();
|
||||
}
|
||||
}
|
||||
}
|
||||
}else {
|
||||
syncTimer[0] = 0l;
|
||||
sleep(50);
|
||||
}
|
||||
}
|
||||
@Async
|
||||
private static native void asyncRequestAnimationFrame();
|
||||
private static void asyncRequestAnimationFrame(AsyncCallback<Void> cb) {
|
||||
if(vsyncTimeout != -1) {
|
||||
cb.error(new IllegalStateException("Already waiting for vsync!"));
|
||||
return;
|
||||
}
|
||||
final boolean[] hasTimedOut = new boolean[] { false };
|
||||
final int[] timeout = new int[] { -1 };
|
||||
Window.requestAnimationFrame((d) -> {
|
||||
if(!hasTimedOut[0]) {
|
||||
hasTimedOut[0] = true;
|
||||
if(vsyncTimeout != -1) {
|
||||
if(vsyncTimeout == timeout[0]) {
|
||||
try {
|
||||
Window.clearTimeout(vsyncTimeout);
|
||||
}catch(Throwable t) {
|
||||
}
|
||||
vsyncTimeout = -1;
|
||||
}
|
||||
cb.complete(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
vsyncTimeout = timeout[0] = Window.setTimeout(() -> {
|
||||
if(!hasTimedOut[0]) {
|
||||
hasTimedOut[0] = true;
|
||||
if(vsyncTimeout != -1) {
|
||||
vsyncTimeout = -1;
|
||||
cb.complete(null);
|
||||
}
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
private static final void swapDelayTeaVM() {
|
||||
if(!useDelayOnSwap && immediateContinueChannel != null) {
|
||||
immediateContinueTeaVM0();
|
||||
}else {
|
||||
sleep(0);
|
||||
}
|
||||
}
|
||||
public static final void immediateContinue() {
|
||||
if(immediateContinueChannel != null) {
|
||||
immediateContinueTeaVM0();
|
||||
}else {
|
||||
sleep(0);
|
||||
}
|
||||
}
|
||||
private static final JSString emptyJSString = JSString.valueOf("");
|
||||
@Async
|
||||
private static native void immediateContinueTeaVM0();
|
||||
private static void immediateContinueTeaVM0(final AsyncCallback<Void> cb) {
|
||||
if(currentMsgChannelContinueHack != null) {
|
||||
cb.error(new IllegalStateException("Main thread is already waiting for an immediate continue callback!"));
|
||||
return;
|
||||
}
|
||||
currentMsgChannelContinueHack = () -> {
|
||||
cb.complete(null);
|
||||
};
|
||||
try {
|
||||
immediateContinueChannel.getPort2().postMessage(emptyJSString);
|
||||
}catch(Throwable t) {
|
||||
currentMsgChannelContinueHack = null;
|
||||
System.err.println("Caught error posting immediate continue, using setTimeout instead");
|
||||
Window.setTimeout(() -> cb.complete(null), 0);
|
||||
}
|
||||
}
|
||||
private static final int IMMEDIATE_CONT_SUPPORTED = 0;
|
||||
private static final int IMMEDIATE_CONT_FAILED_NOT_ASYNC = 1;
|
||||
private static final int IMMEDIATE_CONT_FAILED_NOT_CONT = 2;
|
||||
private static final int IMMEDIATE_CONT_FAILED_EXCEPTIONS = 3;
|
||||
private static void checkImmediateContinueSupport() {
|
||||
immediateContinueChannel = null;
|
||||
int stat = checkImmediateContinueSupport0();
|
||||
if(stat == IMMEDIATE_CONT_SUPPORTED) {
|
||||
return;
|
||||
}else if(stat == IMMEDIATE_CONT_FAILED_NOT_ASYNC) {
|
||||
System.err.println("MessageChannel fast immediate continue hack is incompatible with this browser due to actually continuing immediately!");
|
||||
}else if(stat == IMMEDIATE_CONT_FAILED_NOT_CONT) {
|
||||
System.err.println("MessageChannel fast immediate continue hack is incompatible with this browser due to startup check failing!");
|
||||
}else if(stat == IMMEDIATE_CONT_FAILED_EXCEPTIONS) {
|
||||
System.err.println("MessageChannel fast immediate continue hack is incompatible with this browser due to exceptions!");
|
||||
}
|
||||
immediateContinueChannel = null;
|
||||
}
|
||||
private static int checkImmediateContinueSupport0() {
|
||||
try {
|
||||
if(!MessageChannel.supported()) {
|
||||
return IMMEDIATE_CONT_SUPPORTED;
|
||||
}
|
||||
immediateContinueChannel = new MessageChannel();
|
||||
immediateContinueChannel.getPort1().addEventListener("message", new EventListener<MessageEvent>() {
|
||||
@Override
|
||||
public void handleEvent(MessageEvent evt) {
|
||||
Runnable toRun = currentMsgChannelContinueHack;
|
||||
currentMsgChannelContinueHack = null;
|
||||
if(toRun != null) {
|
||||
toRun.run();
|
||||
}
|
||||
}
|
||||
});
|
||||
immediateContinueChannel.getPort1().start();
|
||||
immediateContinueChannel.getPort2().start();
|
||||
final boolean[] checkMe = new boolean[1];
|
||||
checkMe[0] = false;
|
||||
currentMsgChannelContinueHack = () -> {
|
||||
checkMe[0] = true;
|
||||
};
|
||||
immediateContinueChannel.getPort2().postMessage(emptyJSString);
|
||||
if(checkMe[0]) {
|
||||
currentMsgChannelContinueHack = null;
|
||||
if(immediateContinueChannel != null) {
|
||||
safeShutdownChannel(immediateContinueChannel);
|
||||
}
|
||||
immediateContinueChannel = null;
|
||||
return IMMEDIATE_CONT_FAILED_NOT_ASYNC;
|
||||
}
|
||||
sleep(10);
|
||||
currentMsgChannelContinueHack = null;
|
||||
if(!checkMe[0]) {
|
||||
if(immediateContinueChannel != null) {
|
||||
safeShutdownChannel(immediateContinueChannel);
|
||||
}
|
||||
immediateContinueChannel = null;
|
||||
return IMMEDIATE_CONT_FAILED_NOT_CONT;
|
||||
}else {
|
||||
return IMMEDIATE_CONT_SUPPORTED;
|
||||
}
|
||||
}catch(Throwable t) {
|
||||
currentMsgChannelContinueHack = null;
|
||||
if(immediateContinueChannel != null) {
|
||||
safeShutdownChannel(immediateContinueChannel);
|
||||
}
|
||||
immediateContinueChannel = null;
|
||||
return IMMEDIATE_CONT_FAILED_EXCEPTIONS;
|
||||
}
|
||||
}
|
||||
private static void safeShutdownChannel(MessageChannel chan) {
|
||||
try {
|
||||
chan.getPort1().close();
|
||||
}catch(Throwable tt) {
|
||||
}
|
||||
try {
|
||||
chan.getPort2().close();
|
||||
}catch(Throwable tt) {
|
||||
}
|
||||
}
|
||||
public static final void setupBackBuffer() {
|
||||
backBuffer = _wglCreateFramebuffer();
|
||||
|
@ -1739,9 +1954,6 @@ public class EaglerAdapterImpl2 {
|
|||
}
|
||||
public static final float getContentScaling() {
|
||||
return (float)win.getDevicePixelRatio();
|
||||
}
|
||||
public static final void setVSyncEnabled(boolean p1) {
|
||||
|
||||
}
|
||||
public static final void enableRepeatEvents(boolean b) {
|
||||
enableRepeatEvents = b;
|
||||
|
@ -1776,9 +1988,6 @@ public class EaglerAdapterImpl2 {
|
|||
}
|
||||
public static final void setDisplaySize(int x, int y) {
|
||||
|
||||
}
|
||||
public static final void syncDisplay(int performanceToFps) {
|
||||
|
||||
}
|
||||
|
||||
private static final DateFormat dateFormatSS = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss");
|
||||
|
@ -2001,11 +2210,13 @@ public class EaglerAdapterImpl2 {
|
|||
@JSBody(params = { }, script = "window.onbeforeunload = function(){javaMethods.get('net.lax1dude.eaglercraft.adapter.EaglerAdapterImpl2.onWindowUnload()V').invoke();return false;};")
|
||||
private static native void onBeforeCloseRegister();
|
||||
|
||||
@JSBody(params = { "ext", "mime" }, script = "window.eagsFileChooser.openFileChooser(ext, mime);")
|
||||
public static native void openFileChooser(String ext, String mime);
|
||||
public static final void openFileChooser(String ext, String mime) {
|
||||
fileChooser.openFileChooser(ext, mime);
|
||||
}
|
||||
|
||||
@JSBody(params = { }, script = "return window.eagsFileChooser.getFileChooserResult != null;")
|
||||
public static final native boolean getFileChooserResultAvailable();
|
||||
public static final boolean getFileChooserResultAvailable() {
|
||||
return fileChooser.getGetFileChooserResult() != null;
|
||||
}
|
||||
|
||||
public static final byte[] getFileChooserResult() {
|
||||
ArrayBuffer b = getFileChooserResult0();
|
||||
|
@ -2022,11 +2233,15 @@ public class EaglerAdapterImpl2 {
|
|||
getFileChooserResult0();
|
||||
}
|
||||
|
||||
@JSBody(params = { }, script = "var ret = window.eagsFileChooser.getFileChooserResult; window.eagsFileChooser.getFileChooserResult = null; return ret;")
|
||||
private static native ArrayBuffer getFileChooserResult0();
|
||||
private static final ArrayBuffer getFileChooserResult0() {
|
||||
ArrayBuffer ret = fileChooser.getGetFileChooserResult();
|
||||
fileChooser.setGetFileChooserResultName(null);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@JSBody(params = { }, script = "var ret = window.eagsFileChooser.getFileChooserResultName; window.eagsFileChooser.getFileChooserResultName = null; return ret;")
|
||||
public static native String getFileChooserResultName();
|
||||
public static final String getFileChooserResultName() {
|
||||
return fileChooser.getGetFileChooserResultName();
|
||||
}
|
||||
|
||||
public static final void setListenerPos(float x, float y, float z, float vx, float vy, float vz, float pitch, float yaw) {
|
||||
float var2 = MathHelper.cos(-yaw * 0.017453292F);
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package net.lax1dude.eaglercraft.adapter.teavm;
|
||||
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSClass;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.JSProperty;
|
||||
import org.teavm.jso.workers.MessagePort;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
@JSClass
|
||||
public class MessageChannel implements JSObject {
|
||||
|
||||
@JSBody(params = { }, script = "return (typeof MessageChannel !== \"undefined\");")
|
||||
public static native boolean supported();
|
||||
|
||||
@JSProperty
|
||||
public native MessagePort getPort1();
|
||||
|
||||
@JSProperty
|
||||
public native MessagePort getPort2();
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user