Update #51 - Protocol and FPS improvements, better workspace

This commit is contained in:
lax1dude
2025-05-18 15:01:06 -07:00
parent 71c61e33fd
commit 325a6826bf
1191 changed files with 9266 additions and 187695 deletions

View File

@ -56,6 +56,7 @@ class OpenGLObjects {
private static int hashGen = 0;
final WebGLVertexArray ptr;
final int hash;
int enabled;
VertexArrayGL(WebGLVertexArray ptr) {
this.ptr = ptr;
@ -71,6 +72,21 @@ class OpenGLObjects {
PlatformOpenGL._wglDeleteVertexArrays(this);
}
@Override
public int getBits() {
return enabled;
}
@Override
public void setBit(int bit) {
enabled |= bit;
}
@Override
public void unsetBit(int bit) {
enabled &= ~bit;
}
}
static class TextureGL implements ITextureGL {

View File

@ -242,7 +242,7 @@ public class PlatformAudio {
}
}
PlatformInput.clearEvenBuffers();
PlatformInput.clearEventBuffers();
}

View File

@ -1694,7 +1694,7 @@ public class PlatformInput {
PlatformRuntime.logger.info("Waiting for user to select option on mobile press any key screen");
}
public static void clearEvenBuffers() {
public static void clearEventBuffers() {
mouseEvents.clear();
keyEvents.clear();
touchEvents.clear();

View File

@ -19,6 +19,7 @@ package net.lax1dude.eaglercraft.v1_8.internal;
import java.util.ArrayList;
import java.util.List;
import org.teavm.jso.webgl.WebGLShader;
import org.teavm.jso.webgl.WebGLUniformLocation;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
@ -295,7 +296,17 @@ public class PlatformOpenGL {
}
public static IShaderGL _wglCreateShader(int type) {
return new OpenGLObjects.ShaderGL(ctx.createShader(type));
WebGLShader shader = ctx.createShader(type);
if(shader == null) {
// Workaround for chrome not handling lost context correctly in shaderSource
// "Failed to execute 'shaderSource' on 'WebGL2RenderingContext': parameter 1 is not of type 'WebGLShader'."
if (PlatformInput.contextLost()) {
throw new ContextLostError();
} else {
throw new Error("createShader returned null and the context is not lost");
}
}
return new OpenGLObjects.ShaderGL(shader);
}
public static IFramebufferGL _wglCreateFramebuffer() {

View File

@ -410,6 +410,11 @@ public class PlatformRuntime {
webgl = (WebGL2RenderingContext) webgl_;
webglExperimental = experimental;
if (webgl.isContextLost()) {
throw new ContextLostError();
}
PlatformOpenGL.setCurrentContext(glesVer, webgl);
logger.info("OpenGL Version: {}", PlatformOpenGL._wglGetString(0x1F02));
@ -1195,6 +1200,9 @@ public class PlatformRuntime {
@JSBody(params = {}, script = "delete __isEaglerX188Running;")
private static native void clearRunningFlag();
@JSBody(params = { "webgl" }, script = "var loseCtx = webgl.getExtension(\"WEBGL_lose_context\"); if (loseCtx) loseCtx.loseContext();")
private static native void loseWebGLContext(WebGL2RenderingContext webgl);
static void enterBootMenu(boolean manual) {
if(!getClientConfigAdapter().isAllowBootMenu()) {
throw new IllegalStateException("Boot menu is disabled");
@ -1207,8 +1215,9 @@ public class PlatformRuntime {
removeEventHandlers();
if(webgl != null) {
EarlyLoadScreen.destroy();
PlatformInput.clearEvenBuffers();
PlatformInput.clearEventBuffers();
WebGLBackBuffer.destroy();
loseWebGLContext(webgl);
}
if(canvas != null) {
canvas.delete();

View File

@ -146,7 +146,9 @@ public class PlatformVoiceClient {
public final EaglercraftUUID peerId;
public final JSObject peerConnection;
public MediaStream rawStream;
private MediaStreamAudioSourceNode audioNode = null;
private AnalyserNode analyser = null;
private GainNode gain = null;
private PannerNode panner = null;
@ -372,50 +374,96 @@ public class PlatformVoiceClient {
}
}
public static void makePeerGlobal(EaglercraftUUID peerId) {
VoicePeer peer = peerList.get(peerId);
if (peer != null) {
makePeerGlobalInternal(peer);
}
}
public static void makePeerProximity(EaglercraftUUID peerId) {
VoicePeer peer = peerList.get(peerId);
if (peer != null) {
makePeerProximityInternal(peer);
}
}
private static void makePeerGlobalInternal(VoicePeer peer) {
if (peer.audioNode == null) {
return;
}
if (peer.panner != null) {
if (peer.gain != null) {
peer.panner.disconnect(peer.gain);
}
peer.audioNode.disconnect(peer.panner);
peer.panner = null;
} else if (peer.gain != null) {
peer.audioNode.disconnect(peer.gain);
}
GainNode gain = PlatformAudio.audioctx.createGain();
gain.getGain().setValue(VoiceClientController.getVoiceListenVolume());
peer.audioNode.connect(gain);
gain.connect(PlatformAudio.audioctx.getDestination());
if(PlatformAudio.gameRecGain != null) {
gain.connect(PlatformAudio.gameRecGain);
}
VoiceClientController.getVoiceListening().add(peer.peerId);
peer.gain = gain;
peer.recNode = gain;
}
private static void makePeerProximityInternal(VoicePeer peer) {
if (peer.audioNode == null) {
return;
}
if (peer.panner != null) {
if (peer.gain != null) {
peer.panner.disconnect(peer.gain);
}
peer.audioNode.disconnect(peer.panner);
peer.panner = null;
} else if (peer.gain != null) {
peer.audioNode.disconnect(peer.gain);
}
PannerNode panner = PlatformAudio.audioctx.createPanner();
panner.setRolloffFactor(1f);
panner.setDistanceModel("linear");
panner.setPanningModel("HRTF");
panner.setConeInnerAngle(360f);
panner.setConeOuterAngle(0f);
panner.setConeOuterGain(0f);
panner.setOrientation(0f, 1f, 0f);
panner.setPosition(0, 0, 0);
float vol = VoiceClientController.getVoiceListenVolume();
panner.setMaxDistance(vol * 2 * VoiceClientController.getVoiceProximity() + 0.1f);
GainNode gain = PlatformAudio.audioctx.createGain();
gain.getGain().setValue(vol);
peer.audioNode.connect(gain);
gain.connect(panner);
panner.connect(PlatformAudio.audioctx.getDestination());
if(PlatformAudio.gameRecGain != null) {
panner.connect(PlatformAudio.gameRecGain);
}
VoiceClientController.getVoiceListening().add(peer.peerId);
peer.panner = panner;
peer.gain = gain;
peer.recNode = panner;
}
private static void handlePeerTrack(VoicePeer peer, MediaStream audioStream) {
if (VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.NONE) return;
MediaStreamAudioSourceNode audioNode = PlatformAudio.audioctx.createMediaStreamSource(audioStream);
peer.audioNode = audioNode;
AnalyserNode analyser = PlatformAudio.audioctx.createAnalyser();
analyser.setSmoothingTimeConstant(0f);
analyser.setFftSize(32);
audioNode.connect(analyser);
peer.analyser = analyser;
if (VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.GLOBAL) {
GainNode gain = PlatformAudio.audioctx.createGain();
gain.getGain().setValue(VoiceClientController.getVoiceListenVolume());
audioNode.connect(gain);
gain.connect(PlatformAudio.audioctx.getDestination());
if(PlatformAudio.gameRecGain != null) {
gain.connect(PlatformAudio.gameRecGain);
}
VoiceClientController.getVoiceListening().add(peer.peerId);
peer.analyser = analyser;
peer.gain = gain;
peer.recNode = gain;
makePeerGlobalInternal(peer);
} else if (VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.PROXIMITY) {
PannerNode panner = PlatformAudio.audioctx.createPanner();
panner.setRolloffFactor(1f);
panner.setDistanceModel("linear");
panner.setPanningModel("HRTF");
panner.setConeInnerAngle(360f);
panner.setConeOuterAngle(0f);
panner.setConeOuterGain(0f);
panner.setOrientation(0f, 1f, 0f);
panner.setPosition(0, 0, 0);
float vol = VoiceClientController.getVoiceListenVolume();
panner.setMaxDistance(vol * 2 * VoiceClientController.getVoiceProximity() + 0.1f);
GainNode gain = PlatformAudio.audioctx.createGain();
gain.getGain().setValue(vol);
audioNode.connect(gain);
gain.connect(panner);
panner.connect(PlatformAudio.audioctx.getDestination());
if(PlatformAudio.gameRecGain != null) {
panner.connect(PlatformAudio.gameRecGain);
}
VoiceClientController.getVoiceListening().add(peer.peerId);
peer.analyser = analyser;
peer.panner = panner;
peer.gain = gain;
peer.recNode = panner;
makePeerProximityInternal(peer);
}
if (VoiceClientController.getVoiceMuted().contains(peer.peerId)) mutePeer(peer.peerId, true);
}

View File

@ -40,6 +40,7 @@ import org.teavm.jso.webgl.WebGLRenderingContext;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion;
import net.lax1dude.eaglercraft.v1_8.boot_menu.teavm.BootMenuEntryPoint;
import net.lax1dude.eaglercraft.v1_8.internal.ContextLostError;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformIncompatibleException;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput;
@ -192,6 +193,13 @@ public class ClientMain {
try {
EagRuntime.create();
}catch(ContextLostError ex) {
systemErr.println("ClientMain: [ERROR] webgl context lost during initialization!");
try {
showContextLostScreen(EagRuntime.getStackTrace(ex));
}catch(Throwable t) {
}
return;
}catch(PlatformIncompatibleException ex) {
systemErr.println("ClientMain: [ERROR] this browser is incompatible with eaglercraftx!");
systemErr.println("ClientMain: [ERROR] Reason: " + ex.getMessage());
@ -221,6 +229,12 @@ public class ClientMain {
try {
Main.appMain();
}catch(ContextLostError ex) {
systemErr.println("ClientMain: [ERROR] webgl context lost!");
try {
showContextLostScreen(EagRuntime.getStackTrace(ex));
}catch(Throwable t) {
}
}catch(Throwable t) {
systemErr.println("ClientMain: [ERROR] unhandled exception caused main thread to exit");
EagRuntime.debugPrintStackTraceToSTDERR(t);
@ -619,6 +633,52 @@ public class ClientMain {
}
}
public static void showContextLostScreen(String t) {
if(!isCrashed) {
isCrashed = true;
HTMLDocument doc = Window.current().getDocument();
HTMLElement el;
if(PlatformRuntime.parent != null) {
el = PlatformRuntime.parent;
}else {
if(configRootElement == null) {
configRootElement = doc.getElementById(configRootElementId);
}
el = configRootElement;
}
if(el == null) {
Window.alert("WebGL context lost!");
System.err.println("WebGL context lost: " + t);
return;
}
String s = el.getAttribute("style");
el.setAttribute("style", (s == null ? "" : s) + "position:relative;");
HTMLElement img = doc.createElement("img");
HTMLElement div = doc.createElement("div");
img.setAttribute("style", "z-index:100;position:absolute;top:10px;left:calc(50% - 151px);");
img.setAttribute("src", crashImageWrapper());
div.setAttribute("style", "z-index:100;position:absolute;top:135px;left:10%;right:10%;bottom:50px;background-color:white;border:1px solid #cccccc;overflow-x:hidden;overflow-y:scroll;font:18px sans-serif;padding:40px;");
div.getClassList().add("_eaglercraftX_context_lost_element");
el.appendChild(img);
el.appendChild(div);
div.setInnerHTML("<h2><svg style=\"vertical-align:middle;margin:0px 16px 8px 8px;\" xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\"><path stroke=\"#000000\" stroke-width=\"3\" stroke-linecap=\"square\" d=\"M1.5 8.5v34h45v-28m-3-3h-10v-3m-3-3h-10m15 6h-18v-3m-3-3h-10\"/><path stroke=\"#000000\" stroke-width=\"2\" stroke-linecap=\"square\" d=\"M12 21h0m0 4h0m4 0h0m0-4h0m-2 2h0m20-2h0m0 4h0m4 0h0m0-4h0m-2 2h0\"/><path stroke=\"#000000\" stroke-width=\"2\" stroke-linecap=\"square\" d=\"M20 30h0 m2 2h0 m2 2h0 m2 2h0 m2 -2h0 m2 -2h0 m2 -2h0\"/></svg> + WebGL context lost!</h2>"
+ "<div style=\"margin-left:40px;\">"
+ "<p style=\"font-size:1.2em;\">Your browser has forcibly released all of the resources "
+ "allocated by the game's 3D rendering context. EaglercraftX cannot continue, please refresh "
+ "the page to restart the game.</p>"
+ "<p style=\"font-size:1.2em;\">This is not a bug, it is usually caused by the browser "
+ "deciding it no longer has sufficient resources to continue rendering this page. If it "
+ "happens again, try closing your other browser tabs and windows.</p>"
+ "<p style=\"overflow-wrap:break-word;white-space:pre-wrap;font:0.75em monospace;margin-top:1.5em;\" id=\"_eaglercraftX_contextLostTrace\"></p>"
+ "</div>");
div.querySelector("#_eaglercraftX_contextLostTrace").appendChild(doc.createTextNode(t));
}
}
public static HTMLElement integratedServerCrashPanel = null;
public static boolean integratedServerCrashPanelShowing = false;

View File

@ -248,7 +248,7 @@ public class WebGLBackBuffer {
}
GlStateManager.eagPopStateForGLES2BlitHack();
}else {
EaglercraftGPU.clearCurrentBinding(EaglercraftGPU.CLEAR_BINDING_TEXTURE0 | EaglercraftGPU.CLEAR_BINDING_ACTIVE_TEXTURE | EaglercraftGPU.CLEAR_BINDING_SHADER_PROGRAM | EaglercraftGPU.CLEAR_BINDING_BUFFER_ARRAY);
EaglercraftGPU.clearCurrentBinding(EaglercraftGPU.CLEAR_BINDING_TEXTURE0 | EaglercraftGPU.CLEAR_BINDING_ACTIVE_TEXTURE | EaglercraftGPU.CLEAR_BINDING_SHADER_PROGRAM | EaglercraftGPU.CLEAR_BINDING_VERTEX_ARRAY);
}
ctx.bindFramebuffer(_GL_FRAMEBUFFER, framebuffer);