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

@ -990,7 +990,7 @@ public class PlatformInput {
functionKeyModifier = key;
}
public static void clearEvenBuffers() {
public static void clearEventBuffers() {
mouseEvents.clear();
keyEvents.clear();
//touchEvents.clear();

View File

@ -39,6 +39,7 @@ import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.BetterJSStringConverter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.MemoryViews;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WebGLBackBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WebGLQuery;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WebGLVertexArray;
@ -164,24 +165,43 @@ public class PlatformOpenGL {
public static native void _wglReadBuffer(int glEnum);
public static void _wglReadPixels(int x, int y, int width, int height, int format, int type, ByteBuffer buffer) {
_wglReadPixelsN(x, y, width, height, format, type, WASMGCBufferAllocator.getUnsignedByteBufferView(buffer));
if (glesVers == 200) {
_wglReadPixelsN(x, y, width, height, format, type, WASMGCBufferAllocator.getUnsignedByteBufferView(buffer));
} else {
_wglReadPixelsN(x, y, width, height, format, type, MemoryViews.u8, WASMGCBufferAllocator.getByteBufferViewIndex(buffer));
}
}
public static void _wglReadPixels_u16(int x, int y, int width, int height, int format, int type, ByteBuffer buffer) {
_wglReadPixelsN(x, y, width, height, format, type, WASMGCBufferAllocator.getUnsignedShortBufferView(buffer));
if (glesVers == 200) {
_wglReadPixelsN(x, y, width, height, format, type, WASMGCBufferAllocator.getUnsignedShortBufferView(buffer));
} else {
_wglReadPixelsN(x, y, width, height, format, type, MemoryViews.u16, WASMGCBufferAllocator.getShortBufferViewIndex(buffer));
}
}
public static void _wglReadPixels(int x, int y, int width, int height, int format, int type, IntBuffer buffer) {
_wglReadPixelsN(x, y, width, height, format, type, WASMGCBufferAllocator.getIntBufferView(buffer));
if (glesVers == 200) {
_wglReadPixelsN(x, y, width, height, format, type, WASMGCBufferAllocator.getIntBufferView(buffer));
} else {
_wglReadPixelsN(x, y, width, height, format, type, MemoryViews.i32, WASMGCBufferAllocator.getIntBufferViewIndex(buffer));
}
}
public static void _wglReadPixels(int x, int y, int width, int height, int format, int type, FloatBuffer buffer) {
_wglReadPixelsN(x, y, width, height, format, type, WASMGCBufferAllocator.getFloatBufferView(buffer));
if (glesVers == 200) {
_wglReadPixelsN(x, y, width, height, format, type, WASMGCBufferAllocator.getFloatBufferView(buffer));
} else {
_wglReadPixelsN(x, y, width, height, format, type, MemoryViews.f32, WASMGCBufferAllocator.getFloatBufferViewIndex(buffer));
}
}
@Import(module = "platformOpenGL", name = "glReadPixels")
static native void _wglReadPixelsN(int x, int y, int width, int height, int format, int type, ArrayBufferView array);
@Import(module = "platformOpenGL", name = "glReadPixels0")
static native void _wglReadPixelsN(int x, int y, int width, int height, int format, int type, ArrayBufferView array, int offset);
@Import(module = "platformOpenGL", name = "glPolygonOffset")
public static native void _wglPolygonOffset(float f1, float f2);
@ -217,7 +237,17 @@ public class PlatformOpenGL {
static native WebGLProgram _wglCreateProgramN();
public static IShaderGL _wglCreateShader(int type) {
return new OpenGLObjects.ShaderGL(_wglCreateShaderN(type));
WebGLShader shader = _wglCreateShaderN(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);
}
@Import(module = "platformOpenGL", name = "glCreateShader")
@ -311,35 +341,65 @@ public class PlatformOpenGL {
public static native void _wglBufferData(int target, int size, int usage);
public static void _wglBufferData(int target, ByteBuffer buffer, int usage) {
_wglBufferDataN(target, WASMGCBufferAllocator.getUnsignedByteBufferView(buffer), usage);
if (glesVers == 200) {
_wglBufferDataN(target, WASMGCBufferAllocator.getUnsignedByteBufferView(buffer), usage);
} else {
_wglBufferDataN(target, MemoryViews.u8, usage, WASMGCBufferAllocator.getByteBufferViewIndex(buffer), buffer.remaining());
}
}
public static void _wglBufferData(int target, IntBuffer buffer, int usage) {
_wglBufferDataN(target, WASMGCBufferAllocator.getIntBufferView(buffer), usage);
if (glesVers == 200) {
_wglBufferDataN(target, WASMGCBufferAllocator.getIntBufferView(buffer), usage);
} else {
_wglBufferDataN(target, MemoryViews.i32, usage, WASMGCBufferAllocator.getIntBufferViewIndex(buffer), buffer.remaining());
}
}
public static void _wglBufferData(int target, FloatBuffer buffer, int usage) {
_wglBufferDataN(target, WASMGCBufferAllocator.getFloatBufferView(buffer), usage);
if (glesVers == 200) {
_wglBufferDataN(target, WASMGCBufferAllocator.getFloatBufferView(buffer), usage);
} else {
_wglBufferDataN(target, MemoryViews.f32, usage, WASMGCBufferAllocator.getFloatBufferViewIndex(buffer), buffer.remaining());
}
}
@Import(module = "platformOpenGL", name = "glBufferData")
static native void _wglBufferDataN(int target, ArrayBufferView typedArray, int usage);
@Import(module = "platformOpenGL", name = "glBufferData0")
static native void _wglBufferDataN(int target, ArrayBufferView typedArray, int usage, int srcOffset, int length);
public static void _wglBufferSubData(int target, int dstOffset, ByteBuffer buffer) {
_wglBufferSubDataN(target, dstOffset, WASMGCBufferAllocator.getUnsignedByteBufferView(buffer));
if (glesVers == 200) {
_wglBufferSubDataN(target, dstOffset, WASMGCBufferAllocator.getUnsignedByteBufferView(buffer));
} else {
_wglBufferSubDataN(target, dstOffset, MemoryViews.u8, WASMGCBufferAllocator.getByteBufferViewIndex(buffer), buffer.remaining());
}
}
public static void _wglBufferSubData(int target, int dstOffset, IntBuffer buffer) {
_wglBufferSubDataN(target, dstOffset, WASMGCBufferAllocator.getIntBufferView(buffer));
if (glesVers == 200) {
_wglBufferSubDataN(target, dstOffset, WASMGCBufferAllocator.getIntBufferView(buffer));
} else {
_wglBufferSubDataN(target, dstOffset, MemoryViews.i32, WASMGCBufferAllocator.getIntBufferViewIndex(buffer), buffer.remaining());
}
}
public static void _wglBufferSubData(int target, int dstOffset, FloatBuffer buffer) {
_wglBufferSubDataN(target, dstOffset, WASMGCBufferAllocator.getFloatBufferView(buffer));
if (glesVers == 200) {
_wglBufferSubDataN(target, dstOffset, WASMGCBufferAllocator.getFloatBufferView(buffer));
} else {
_wglBufferSubDataN(target, dstOffset, MemoryViews.f32, WASMGCBufferAllocator.getFloatBufferViewIndex(buffer), buffer.remaining());
}
}
@Import(module = "platformOpenGL", name = "glBufferSubData")
static native void _wglBufferSubDataN(int target, int dstOffset, ArrayBufferView typedArray);
@Import(module = "platformOpenGL", name = "glBufferSubData0")
static native void _wglBufferSubDataN(int target, int dstOffset, ArrayBufferView typedArray, int srcOffset, int length);
public static void _wglBindVertexArray(IVertexArrayGL objId) {
_wglBindVertexArrayN(objId != null ? ((OpenGLObjects.VertexArrayGL)objId).ptr : null);
}
@ -378,76 +438,137 @@ public class PlatformOpenGL {
public static void _wglTexImage3D(int target, int level, int internalFormat, int width, int height, int depth,
int border, int format, int type, ByteBuffer data) {
_wglTexImage3DN(target, level, internalFormat, width, height, depth, border, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedByteBufferView(data) : null);
if (data == null) {
_wglTexImage3DN(target, level, internalFormat, width, height, depth, border, format, type, null);
} else {
_wglTexImage3DN(target, level, internalFormat, width, height, depth, border, format, type,
MemoryViews.u8, WASMGCBufferAllocator.getByteBufferViewIndex(data));
}
}
@Import(module = "platformOpenGL", name = "glTexImage3D")
static native void _wglTexImage3DN(int target, int level, int internalFormat, int width, int height, int depth,
int border, int format, int type, ArrayBufferView typedArray);
@Import(module = "platformOpenGL", name = "glTexImage3D0")
static native void _wglTexImage3DN(int target, int level, int internalFormat, int width, int height, int depth,
int border, int format, int type, ArrayBufferView typedArray, int offset);
public static void _wglTexImage2D(int target, int level, int internalFormat, int width, int height, int border,
int format, int type, ByteBuffer data) {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedByteBufferView(data) : null);
if (data == null || glesVers == 200) {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedByteBufferView(data) : null);
} else {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
MemoryViews.u8, WASMGCBufferAllocator.getByteBufferViewIndex(data));
}
}
public static void _wglTexImage2Du16(int target, int level, int internalFormat, int width, int height, int border,
int format, int type, ByteBuffer data) {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedShortBufferView(data) : null);
if (data == null || glesVers == 200) {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedShortBufferView(data) : null);
} else {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
MemoryViews.u16, WASMGCBufferAllocator.getShortBufferViewIndex(data));
}
}
public static void _wglTexImage2Df32(int target, int level, int internalFormat, int width, int height, int border,
int format, int type, ByteBuffer data) {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
data != null ? WASMGCBufferAllocator.getFloatBufferView(data) : null);
if (data == null || glesVers == 200) {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
data != null ? WASMGCBufferAllocator.getFloatBufferView(data) : null);
} else {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
MemoryViews.f32, WASMGCBufferAllocator.getFloatBufferViewIndex(data));
}
}
public static void _wglTexImage2D(int target, int level, int internalFormat, int width,
int height, int border, int format, int type, IntBuffer data) {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedByteBufferView(data) : null);
if (data == null || glesVers == 200) {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedByteBufferView(data) : null);
} else {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
MemoryViews.u8, WASMGCBufferAllocator.getByteBufferViewIndex(data));
}
}
public static void _wglTexImage2Df32(int target, int level, int internalFormat, int width,
int height, int border, int format, int type, FloatBuffer data) {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
data != null ? WASMGCBufferAllocator.getFloatBufferView(data) : null);
if (data == null || glesVers == 200) {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
data != null ? WASMGCBufferAllocator.getFloatBufferView(data) : null);
} else {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
MemoryViews.f32, WASMGCBufferAllocator.getFloatBufferViewIndex(data));
}
}
@Import(module = "platformOpenGL", name = "glTexImage2D")
static native void _wglTexImage2DN(int target, int level, int internalFormat, int width, int height, int border,
int format, int type, ArrayBufferView typedArray);
@Import(module = "platformOpenGL", name = "glTexImage2D0")
static native void _wglTexImage2DN(int target, int level, int internalFormat, int width, int height, int border,
int format, int type, ArrayBufferView typedArray, int offset);
public static void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset,
int width, int height, int format, int type, ByteBuffer data) {
_wglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedByteBufferView(data) : null);
if (data == null || glesVers == 200) {
_wglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedByteBufferView(data) : null);
} else {
_wglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
MemoryViews.u8, WASMGCBufferAllocator.getByteBufferViewIndex(data));
}
}
public static void _wglTexSubImage2Du16(int target, int level, int xoffset, int yoffset,
int width, int height, int format, int type, ByteBuffer data) {
_wglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedShortBufferView(data) : null);
if (data == null || glesVers == 200) {
_wglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedShortBufferView(data) : null);
} else {
_wglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
MemoryViews.u16, WASMGCBufferAllocator.getShortBufferViewIndex(data));
}
}
public static void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset,
int width, int height, int format, int type, IntBuffer data) {
_wglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedByteBufferView(data) : null);
if (data == null || glesVers == 200) {
_wglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedByteBufferView(data) : null);
} else {
_wglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
MemoryViews.u8, WASMGCBufferAllocator.getByteBufferViewIndex(data));
}
}
public static void _wglTexSubImage2Df32(int target, int level, int xoffset, int yoffset,
int width, int height, int format, int type, FloatBuffer data) {
_wglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
data != null ? WASMGCBufferAllocator.getFloatBufferView(data) : null);
if (data == null || glesVers == 200) {
_wglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
data != null ? WASMGCBufferAllocator.getFloatBufferView(data) : null);
} else {
_wglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
MemoryViews.f32, WASMGCBufferAllocator.getFloatBufferViewIndex(data));
}
}
@Import(module = "platformOpenGL", name = "glTexSubImage2D")
static native void _wglTexSubImage2D(int target, int level, int offsetx, int offsety, int width, int height,
int format, int type, ArrayBufferView typedArray);
@Import(module = "platformOpenGL", name = "glTexSubImage2D0")
static native void _wglTexSubImage2D(int target, int level, int offsetx, int offsety, int width, int height,
int format, int type, ArrayBufferView typedArray, int offset);
@Import(module = "platformOpenGL", name = "glCopyTexSubImage2D")
public static native void _wglCopyTexSubImage2D(int target, int level, int xoffset, int yoffset,
int x, int y, int width, int height);

View File

@ -398,6 +398,13 @@ public class PlatformRuntime {
@Import(module = "platformRuntime", name = "writeCrashReport")
private static native void writeCrashReport0(JSString crashDump);
public static void showContextLostScreen(String crashDump) {
showContextLostScreen0(BetterJSStringConverter.stringToJS(crashDump));
}
@Import(module = "platformRuntime", name = "showContextLostScreen")
private static native void showContextLostScreen0(JSString crashDump);
public static void getStackTrace(Throwable t, Consumer<String> ret) {
StackTraceElement[] el = t.getStackTrace();
if(el.length > 0) {

View File

@ -145,6 +145,8 @@ public class PlatformVoiceClient {
private final EaglercraftUUID peerId;
private final JSVoicePeerHandle jsHandle;
private MediaStreamAudioSourceNode audioNode = null;
private MediaStream rawStream = null;
private AnalyserNode analyser = null;
private GainNode gain = null;
@ -175,50 +177,77 @@ public class PlatformVoiceClient {
jsHandle.addRemoteICECandidate(BetterJSStringConverter.stringToJS(candidate));
}
private void makeGlobal() {
if (audioNode == null) {
return;
}
if (panner != null) {
if (gain != null) {
panner.disconnect(gain);
}
audioNode.disconnect(panner);
panner = null;
} else if (gain != null) {
audioNode.disconnect(gain);
}
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(peerId);
recNode = gain;
}
private void makeProximity() {
if (audioNode == null) {
return;
}
if (panner != null) {
if (gain != null) {
panner.disconnect(gain);
}
audioNode.disconnect(panner);
panner = null;
} else if (gain != null) {
audioNode.disconnect(gain);
}
panner = PlatformAudio.audioctx.createPanner();
panner.setRolloffFactor(1f);
PlatformAudio.setDistanceModelLinearFast(panner);
PlatformAudio.setPanningModelHRTFFast(panner);
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);
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(peerId);
recNode = panner;
}
private void handleEventOpened(MediaStream stream) {
rawStream = stream;
MediaStreamAudioSourceNode audioNode = PlatformAudio.audioctx.createMediaStreamSource(stream);
AnalyserNode analyser = PlatformAudio.audioctx.createAnalyser();
audioNode = PlatformAudio.audioctx.createMediaStreamSource(stream);
analyser = PlatformAudio.audioctx.createAnalyser();
analyser.setSmoothingTimeConstant(0f);
analyser.setFftSize(32);
audioNode.connect(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(peerId);
this.analyser = analyser;
this.gain = gain;
this.recNode = gain;
makeGlobal();
} else if (VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.PROXIMITY) {
PannerNode panner = PlatformAudio.audioctx.createPanner();
panner.setRolloffFactor(1f);
PlatformAudio.setDistanceModelLinearFast(panner);
PlatformAudio.setPanningModelHRTFFast(panner);
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(peerId);
this.analyser = analyser;
this.panner = panner;
this.gain = gain;
this.recNode = panner;
makeProximity();
}
if (VoiceClientController.getVoiceMuted().contains(peerId)) mute(true);
}
@ -440,6 +469,20 @@ public class PlatformVoiceClient {
}
}
public static void makePeerGlobal(EaglercraftUUID peerId) {
VoicePeer peer = peerList.get(peerId);
if (peer != null) {
peer.makeGlobal();
}
}
public static void makePeerProximity(EaglercraftUUID peerId) {
VoicePeer peer = peerList.get(peerId);
if (peer != null) {
peer.makeProximity();
}
}
static void addRecordingDest(AudioNode destNode) {
for(VoicePeer peer : peerList.values()) {
if(peer.recNode != null) {

View File

@ -17,7 +17,6 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.teavm.interop.Address;
import org.teavm.interop.DirectMalloc;
public class DirectMallocByteBuffer extends ByteBuffer {
@ -138,7 +137,7 @@ public class DirectMallocByteBuffer extends ByteBuffer {
DirectMallocByteBuffer c = (DirectMallocByteBuffer)src;
int l = c.limit - c.position;
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
DirectMalloc.memcpy(address.add(position), c.address.add(c.position), l);
Address.moveMemoryBlock(c.address.add(c.position), address.add(position), l);
position += l;
c.position += l;
}else {

View File

@ -17,7 +17,6 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.teavm.interop.Address;
import org.teavm.interop.DirectMalloc;
public class DirectMallocFloatBuffer extends FloatBuffer {
@ -147,7 +146,7 @@ public class DirectMallocFloatBuffer extends FloatBuffer {
DirectMallocFloatBuffer c = (DirectMallocFloatBuffer)src;
int l = c.limit - c.position;
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
DirectMalloc.memcpy(address.add(position << SHIFT), c.address.add(c.position << SHIFT), l << SHIFT);
Address.moveMemoryBlock(c.address.add(c.position << SHIFT), address.add(position << SHIFT), l << SHIFT);
position += l;
c.position += l;
}else {

View File

@ -17,7 +17,6 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.teavm.interop.Address;
import org.teavm.interop.DirectMalloc;
public class DirectMallocIntBuffer extends IntBuffer {
@ -147,7 +146,7 @@ public class DirectMallocIntBuffer extends IntBuffer {
DirectMallocIntBuffer c = (DirectMallocIntBuffer)src;
int l = c.limit - c.position;
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
DirectMalloc.memcpy(address.add(position << SHIFT), c.address.add(c.position << SHIFT), l << SHIFT);
Address.moveMemoryBlock(c.address.add(c.position << SHIFT), address.add(position << SHIFT), l << SHIFT);
position += l;
c.position += l;
}else {

View File

@ -17,7 +17,6 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.teavm.interop.Address;
import org.teavm.interop.DirectMalloc;
public class DirectMallocShortBuffer extends ShortBuffer {
@ -147,7 +146,7 @@ public class DirectMallocShortBuffer extends ShortBuffer {
DirectMallocShortBuffer c = (DirectMallocShortBuffer)src;
int l = c.limit - c.position;
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
DirectMalloc.memcpy(address.add(position << SHIFT), c.address.add(c.position << SHIFT), l << SHIFT);
Address.moveMemoryBlock(c.address.add(c.position << SHIFT), address.add(position << SHIFT), l << SHIFT);
position += l;
c.position += l;
}else {

View File

@ -17,7 +17,7 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.teavm.interop.Address;
import org.teavm.interop.DirectMalloc;
import org.teavm.runtime.heap.Heap;
public class MemoryStack {
@ -32,9 +32,9 @@ public class MemoryStack {
private static Address stackTopPointer;
static {
stackBase = DirectMalloc.malloc(STACK_SIZE + 16);
stackBase = Heap.alloc(STACK_SIZE + 16);
if(stackBase.toInt() == 0) {
throw new IllegalStateException("Could not allocate MemoryStack of size " + STACK_SIZE);
throw new Error("Could not allocate MemoryStack of size " + STACK_SIZE);
}
stackMax = stackBase.add(STACK_SIZE);
stackBottomPointer = stackBase;
@ -99,36 +99,4 @@ public class MemoryStack {
return new DirectMallocFloatBuffer(malloc(length << 2), length, false);
}
public static Address calloc(int length) {
if(length > MALLOC_THRESHOLD || (stackMax.toInt() - stackTopPointer.toInt() - length) < RESERVE_SIZE) {
if(stackTopPointer.toInt() + 8 > stackMax.toInt()) {
throw new StackOverflowError();
}
Address malloced = WASMGCBufferAllocator.calloc(length);
Address cleanup = stackBottomPointer.add(4).getAddress();
stackTopPointer.putAddress(malloced);
stackTopPointer.add(4).putAddress(cleanup);
stackBottomPointer.add(4).putAddress(stackTopPointer);
stackTopPointer = stackTopPointer.add(8);
return malloced;
}else {
DirectMalloc.zmemset(stackTopPointer, length);
Address ret = stackTopPointer;
stackTopPointer = stackTopPointer.add((length + 3) & 0xFFFFFFFC);
return ret;
}
}
public static ByteBuffer callocByteBuffer(int length) {
return new DirectMallocByteBuffer(calloc(length), length, false);
}
public static IntBuffer callocIntBuffer(int length) {
return new DirectMallocIntBuffer(calloc(length << 2), length, false);
}
public static FloatBuffer callocFloatBuffer(int length) {
return new DirectMallocFloatBuffer(calloc(length << 2), length, false);
}
}

View File

@ -17,7 +17,6 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.teavm.interop.Address;
import org.teavm.interop.DirectMalloc;
import org.teavm.interop.Import;
import org.teavm.jso.typedarrays.Float32Array;
import org.teavm.jso.typedarrays.Int16Array;
@ -26,6 +25,7 @@ import org.teavm.jso.typedarrays.Int8Array;
import org.teavm.jso.typedarrays.Uint16Array;
import org.teavm.jso.typedarrays.Uint8Array;
import org.teavm.jso.typedarrays.Uint8ClampedArray;
import org.teavm.runtime.heap.Heap;
public class WASMGCBufferAllocator {
@ -37,9 +37,9 @@ public class WASMGCBufferAllocator {
}
Address addr;
if(enableBufferOverflowCheck) {
addr = DirectMalloc.malloc(size + 12);
addr = Heap.alloc(size + 12);
if(addr.toInt() == 0) {
throw new OutOfMemoryError("DirectMalloc returned null pointer!");
throw new OutOfMemoryError("Heap returned null pointer!");
}
int tag = (int)(Math.random() * 2147483647.0);
addr.putInt(size);
@ -47,33 +47,9 @@ public class WASMGCBufferAllocator {
addr.add(size + 8).putInt(tag);
addr = addr.add(8);
}else {
addr = DirectMalloc.malloc(size);
addr = Heap.alloc(size);
if(addr.toInt() == 0) {
throw new OutOfMemoryError("DirectMalloc returned null pointer!");
}
}
return addr;
}
public static Address calloc(int size) {
if(size == 0) {
return Address.fromInt(0);
}
Address addr;
if(enableBufferOverflowCheck) {
addr = DirectMalloc.calloc(size + 12);
if(addr.toInt() == 0) {
throw new OutOfMemoryError("DirectMalloc returned null pointer!");
}
int tag = (int)(Math.random() * 2147483647.0);
addr.putInt(size);
addr.add(4).putInt(tag);
addr.add(size + 8).putInt(tag);
addr = addr.add(8);
}else {
addr = DirectMalloc.calloc(size);
if(addr.toInt() == 0) {
throw new OutOfMemoryError("DirectMalloc returned null pointer!");
throw new OutOfMemoryError("Heap returned null pointer!");
}
}
return addr;
@ -86,10 +62,10 @@ public class WASMGCBufferAllocator {
int size = ptr.getInt();
int tag = ptr.add(4).getInt();
if(tag != ptr.add(size + 8).getInt()) {
throw new RuntimeException("Detected a buffer write overflow");
throw new Error("Detected a buffer write overflow");
}
}
DirectMalloc.free(ptr);
Heap.release(ptr);
}
}
@ -223,6 +199,21 @@ public class WASMGCBufferAllocator {
@Import(module = "WASMGCBufferAllocator", name = "getUnsignedClampedByteBufferView")
public static native Uint8ClampedArray getUnsignedClampedByteBufferView0(Address addr, int length);
public static int getByteBufferViewIndex(ByteBuffer buffer) {
DirectMallocByteBuffer buf = (DirectMallocByteBuffer)buffer;
return buf.address.toInt() + buf.position();
}
public static int getByteBufferViewIndex(ShortBuffer buffer) {
DirectMallocShortBuffer buf = (DirectMallocShortBuffer)buffer;
return buf.address.add(buf.position() << 1).toInt();
}
public static int getByteBufferViewIndex(IntBuffer buffer) {
DirectMallocIntBuffer buf = (DirectMallocIntBuffer)buffer;
return buf.address.add(buf.position() << 2).toInt();
}
public static Int16Array getShortBufferView(ShortBuffer buffer) {
DirectMallocShortBuffer buf = (DirectMallocShortBuffer)buffer;
return getShortBufferView0(buf.address.add(buf.position() << 1), buf.remaining());
@ -249,6 +240,16 @@ public class WASMGCBufferAllocator {
@Import(module = "WASMGCBufferAllocator", name = "getUnsignedShortBufferView")
public static native Uint16Array getUnsignedShortBufferView0(Address addr, int length);
public static int getShortBufferViewIndex(ByteBuffer buffer) {
DirectMallocByteBuffer buf = (DirectMallocByteBuffer)buffer;
return buf.address.add(buf.position()).toInt() >> 1;
}
public static int getShortBufferViewIndex(ShortBuffer buffer) {
DirectMallocShortBuffer buf = (DirectMallocShortBuffer)buffer;
return (buf.address.toInt() >> 1) + buf.position();
}
public static Int32Array getIntBufferView(IntBuffer buffer) {
DirectMallocIntBuffer buf = (DirectMallocIntBuffer)buffer;
return getIntBufferView0(buf.address.add(buf.position() << 2), buf.remaining());
@ -262,6 +263,16 @@ public class WASMGCBufferAllocator {
@Import(module = "WASMGCBufferAllocator", name = "getIntBufferView")
public static native Int32Array getIntBufferView0(Address addr, int length);
public static int getIntBufferViewIndex(ByteBuffer buffer) {
DirectMallocByteBuffer buf = (DirectMallocByteBuffer)buffer;
return buf.address.add(buf.position()).toInt() >> 2;
}
public static int getIntBufferViewIndex(IntBuffer buffer) {
DirectMallocIntBuffer buf = (DirectMallocIntBuffer)buffer;
return (buf.address.toInt() >> 2) + buf.position();
}
public static Float32Array getFloatBufferView(FloatBuffer buffer) {
DirectMallocFloatBuffer buf = (DirectMallocFloatBuffer)buffer;
return getFloatBufferView0(buf.address.add(buf.position() << 2), buf.remaining());
@ -275,6 +286,16 @@ public class WASMGCBufferAllocator {
@Import(module = "WASMGCBufferAllocator", name = "getFloatBufferView")
public static native Float32Array getFloatBufferView0(Address addr, int length);
public static int getFloatBufferViewIndex(ByteBuffer buffer) {
DirectMallocByteBuffer buf = (DirectMallocByteBuffer)buffer;
return buf.address.add(buf.position()).toInt() >> 2;
}
public static int getFloatBufferViewIndex(FloatBuffer buffer) {
DirectMallocFloatBuffer buf = (DirectMallocFloatBuffer)buffer;
return (buf.address.toInt() >> 2) + buf.position();
}
private static void throwNotOriginal(Object clazz) {
throw notOriginal(clazz);
}

View File

@ -16,67 +16,37 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import org.teavm.interop.Address;
import org.teavm.interop.Import;
import org.teavm.interop.Unmanaged;
import org.teavm.jso.core.JSArray;
import org.teavm.jso.core.JSString;
import org.teavm.jso.impl.JS;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.MemoryStack;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator;
/**
* Note: this is left over from when TeaVM JSO converted
* strings using a horrifying concatenation loop, eagler
* had a "better" implementation back then
*/
public class BetterJSStringConverter {
private static final TextDecoder textDecoder = new TextDecoder("utf-16");
@Unmanaged
public static JSString stringToJS(String input) {
if(input == null) return null;
int len = input.length();
MemoryStack.push();
Address tmpAddr = MemoryStack.malloc(len << 1);
for(int i = 0; i < len; ++i) {
tmpAddr.add(i << 1).putChar(input.charAt(i));
}
JSString ret = textDecoder.decode(WASMGCBufferAllocator.getUnsignedByteBufferView0(tmpAddr, len << 1));
MemoryStack.pop();
return ret;
return (JSString) JS.wrap(input);
}
@Unmanaged
@SuppressWarnings("unchecked")
public static JSArray<JSString> stringArrayToJS(String[] input) {
if(input == null) return null;
int len = input.length;
JSArray<JSString> ret = new JSArray<>(len);
for(int i = 0; i < len; ++i) {
ret.set(i, stringToJS(input[i]));
}
return ret;
return (JSArray<JSString>) JS.wrap(input);
}
@Unmanaged
public static String stringFromJS(JSString input) {
if(input == null) return null;
int len = input.getLength();
char[] chars = new char[len];
for(int i = 0; i < len; ++i) {
chars[i] = charCodeAt(input, i);
}
return new String(chars);
return JS.unwrapString(input);
}
@Import(module = "teavmJso", name = "charAt")
private static native char charCodeAt(JSString str, int idx);
@Unmanaged
public static String[] stringArrayFromJS(JSArray<JSString> input) {
if(input == null) return null;
int len = input.getLength();
String[] ret = new String[len];
for(int i = 0; i < len; ++i) {
ret[i] = stringFromJS(input.get(i));
}
return ret;
return JS.unwrapStringArray(input);
}
}

View File

@ -23,6 +23,7 @@ import org.teavm.jso.JSObject;
import org.teavm.jso.browser.Window;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.ContextLostError;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.opts.JSEaglercraftXOptsRoot;
import net.minecraft.client.main.Main;
@ -68,6 +69,10 @@ public class ClientMain {
try {
EagRuntime.create();
}catch(ContextLostError t) {
systemErr.println("ClientMain: [ERROR] webgl context lost during initialization!");
PlatformRuntime.showContextLostScreen(EagRuntime.getStackTrace(t));
return;
}catch(Throwable t) {
systemErr.println("ClientMain: [ERROR] eaglercraftx's runtime could not be initialized!");
EagRuntime.debugPrintStackTraceToSTDERR(t);
@ -80,6 +85,9 @@ public class ClientMain {
try {
Main.appMain();
}catch(ContextLostError t) {
systemErr.println("ClientMain: [ERROR] webgl context lost!");
PlatformRuntime.showContextLostScreen(EagRuntime.getStackTrace(t));
}catch(Throwable t) {
systemErr.println("ClientMain: [ERROR] unhandled exception caused main thread to exit");
EagRuntime.debugPrintStackTraceToSTDERR(t);

View File

@ -22,6 +22,7 @@ public class MainClass {
public static void main(String[] args) {
WASMGCCrashReportStrings.setCrashReportStrings();
MemoryViews.setupCallback();
if(args.length == 1) {
if("_worker_process_".equalsIgnoreCase(args[0])) {
workerMain();

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2025 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import org.teavm.interop.Import;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSFunctor;
import org.teavm.jso.JSObject;
import org.teavm.jso.typedarrays.Float32Array;
import org.teavm.jso.typedarrays.Int16Array;
import org.teavm.jso.typedarrays.Int32Array;
import org.teavm.jso.typedarrays.Int8Array;
import org.teavm.jso.typedarrays.Uint16Array;
import org.teavm.jso.typedarrays.Uint32Array;
import org.teavm.jso.typedarrays.Uint8Array;
public class MemoryViews {
public static Int8Array i8 = null;
public static Uint8Array u8 = null;
public static Int16Array i16 = null;
public static Uint16Array u16 = null;
public static Int32Array i32 = null;
public static Uint32Array u32 = null;
public static Float32Array f32 = null;
static void setupCallback() {
setHeapViewCallback(jso(MemoryViews::updateHeapViews));
}
@JSBody(params = { "obj" }, script = "return obj;")
private static native JSObject jso(IHeapViewUpdate obj);
@Import(module = "WASMGCBufferAllocator", name = "setHeapViewCallback")
private static native void setHeapViewCallback(JSObject callback);
@JSFunctor
private interface IHeapViewUpdate extends JSObject {
void call(Int8Array i8, Uint8Array u8, Int16Array i16, Uint16Array u16, Int32Array i32,
Uint32Array u32, Float32Array f32);
}
private static void updateHeapViews(Int8Array i8, Uint8Array u8, Int16Array i16, Uint16Array u16, Int32Array i32,
Uint32Array u32, Float32Array f32) {
MemoryViews.i8 = i8;
MemoryViews.u8 = u8;
MemoryViews.i16 = i16;
MemoryViews.u16 = u16;
MemoryViews.i32 = i32;
MemoryViews.u32 = u32;
MemoryViews.f32 = f32;
}
}

View File

@ -1,32 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import org.teavm.jso.JSClass;
import org.teavm.jso.JSObject;
import org.teavm.jso.core.JSString;
import org.teavm.jso.typedarrays.Uint8Array;
@JSClass
public class TextDecoder implements JSObject {
public TextDecoder(String encoding) {
}
public native JSString decode(Uint8Array buffer);
}

View File

@ -41,7 +41,7 @@ public class WASMGCWebSocketClient implements IWebSocketClient {
void closeSocket();
void sendStringFrame(JSString str);
void sendStringFrame(String str);
void sendBinaryFrame(Uint8Array arr);
@ -225,7 +225,7 @@ public class WASMGCWebSocketClient implements IWebSocketClient {
@Override
public void send(String str) {
handle.sendStringFrame(BetterJSStringConverter.stringToJS(str));
handle.sendStringFrame(str);
}
@Override

View File

@ -57,8 +57,9 @@ public class WebGLBackBuffer {
private static final int _GL_RENDERBUFFER = 0x8D41;
private static final int _GL_COLOR_ATTACHMENT0 = 0x8CE0;
private static final int _GL_DEPTH_ATTACHMENT = 0x8D00;
private static final int _GL_DEPTH_COMPONENT16 = 0x81A5;
private static final int _GL_DEPTH_STENCIL_ATTACHMENT = 0x821A;
private static final int _GL_DEPTH_COMPONENT32F = 0x8CAC;
private static final int _GL_DEPTH_STENCIL = 0x84F9;
private static final int _GL_READ_FRAMEBUFFER = 0x8CA8;
private static final int _GL_DRAW_FRAMEBUFFER = 0x8CA9;
@ -92,8 +93,8 @@ public class WebGLBackBuffer {
_wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, sw, sh, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null);
_wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gles2ColorTexture, 0);
_wglBindRenderbuffer(_GL_RENDERBUFFER, gles2DepthRenderbuffer);
_wglRenderbufferStorage(_GL_RENDERBUFFER, _GL_DEPTH_COMPONENT16, sw, sh);
_wglFramebufferRenderbuffer(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, _GL_RENDERBUFFER, gles2DepthRenderbuffer);
_wglRenderbufferStorage(_GL_RENDERBUFFER, _GL_DEPTH_STENCIL, sw, sh);
_wglFramebufferRenderbuffer(_GL_FRAMEBUFFER, _GL_DEPTH_STENCIL_ATTACHMENT, _GL_RENDERBUFFER, gles2DepthRenderbuffer);
MemoryStack.push();
try {
@ -238,7 +239,7 @@ public class WebGLBackBuffer {
_wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, windowWidth, windowHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null);
_wglBindRenderbuffer(_GL_RENDERBUFFER, gles2DepthRenderbuffer);
_wglRenderbufferStorage(_GL_RENDERBUFFER, _GL_DEPTH_COMPONENT16, windowWidth, windowHeight);
_wglRenderbufferStorage(_GL_RENDERBUFFER, _GL_DEPTH_STENCIL, windowWidth, windowHeight);
}
if(isEmulatedVAOPhase) {
@ -248,7 +249,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);
}
_wglBindFramebuffer(_GL_FRAMEBUFFER, eagFramebuffer);