Update #0 - First Release

This commit is contained in:
LAX1DUDE
2022-12-25 01:12:28 -08:00
commit e7179fad45
2154 changed files with 256324 additions and 0 deletions

View File

@ -0,0 +1,163 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import org.teavm.jso.webgl.WebGLBuffer;
import org.teavm.jso.webgl.WebGLFramebuffer;
import org.teavm.jso.webgl.WebGLProgram;
import org.teavm.jso.webgl.WebGLRenderbuffer;
import org.teavm.jso.webgl.WebGLShader;
import org.teavm.jso.webgl.WebGLTexture;
import org.teavm.jso.webgl.WebGLUniformLocation;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGLQuery;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGLVertexArray;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
class OpenGLObjects {
static class BufferGL implements IBufferGL {
final WebGLBuffer ptr;
BufferGL(WebGLBuffer ptr) {
this.ptr = ptr;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteBuffers(this);
}
}
static class BufferArrayGL implements IBufferArrayGL {
final WebGLVertexArray ptr;
BufferArrayGL(WebGLVertexArray ptr) {
this.ptr = ptr;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteVertexArrays(this);
}
}
static class TextureGL implements ITextureGL {
final WebGLTexture ptr;
TextureGL(WebGLTexture ptr) {
this.ptr = ptr;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteTextures(this);
}
}
static class ProgramGL implements IProgramGL {
final WebGLProgram ptr;
ProgramGL(WebGLProgram ptr) {
this.ptr = ptr;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteProgram(this);
}
}
static class UniformGL implements IUniformGL {
final WebGLUniformLocation ptr;
UniformGL(WebGLUniformLocation ptr) {
this.ptr = ptr;
}
@Override
public void free() {
}
}
static class ShaderGL implements IShaderGL {
final WebGLShader ptr;
ShaderGL(WebGLShader ptr) {
this.ptr = ptr;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteShader(this);
}
}
static class FramebufferGL implements IFramebufferGL {
final WebGLFramebuffer ptr;
FramebufferGL(WebGLFramebuffer ptr) {
this.ptr = ptr;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteFramebuffer(this);
}
}
static class RenderbufferGL implements IRenderbufferGL {
final WebGLRenderbuffer ptr;
RenderbufferGL(WebGLRenderbuffer ptr) {
this.ptr = ptr;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteRenderbuffer(this);
}
}
static class QueryGL implements IQueryGL {
final WebGLQuery ptr;
QueryGL(WebGLQuery ptr) {
this.ptr = ptr;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteQueries(this);
}
}
}

View File

@ -0,0 +1,235 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.teavm.interop.Async;
import org.teavm.interop.AsyncCallback;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSFunctor;
import org.teavm.jso.JSObject;
import org.teavm.jso.browser.Storage;
import org.teavm.jso.browser.TimerHandler;
import org.teavm.jso.browser.Window;
import org.teavm.jso.canvas.CanvasRenderingContext2D;
import org.teavm.jso.dom.events.Event;
import org.teavm.jso.dom.events.EventListener;
import org.teavm.jso.dom.html.HTMLCanvasElement;
import org.teavm.jso.dom.html.HTMLInputElement;
import org.teavm.jso.dom.xml.Document;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Int8Array;
import net.lax1dude.eaglercraft.v1_8.Base64;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.MainClass;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class PlatformApplication {
public static void openLink(String url) {
Window.current().open(url, "_blank");
}
public static void setClipboard(String text) {
try {
setClipboard0(text);
}catch(Throwable t) {
PlatformRuntime.logger.error("Exception setting clipboard data");
}
}
public static String getClipboard() {
try {
return getClipboard0();
}catch(Throwable t) {
PlatformRuntime.logger.error("Exception getting clipboard data");
return "";
}
}
@JSFunctor
private static interface StupidFunctionResolveString extends JSObject {
void resolveStr(String s);
}
@Async
private static native String getClipboard0();
private static void getClipboard0(final AsyncCallback<String> cb) {
final long start = System.currentTimeMillis();
getClipboard1(new StupidFunctionResolveString() {
@Override
public void resolveStr(String s) {
if(System.currentTimeMillis() - start > 500l) {
PlatformInput.unpressCTRL = true;
}
cb.complete(s);
}
});
}
@JSBody(params = { "cb" }, script = "if(!window.navigator.clipboard) cb(\"\"); else window.navigator.clipboard.readText().then(function(s) { cb(s); }, function(s) { cb(\"\"); });")
private static native void getClipboard1(StupidFunctionResolveString cb);
@JSBody(params = { "str" }, script = "if(window.navigator.clipboard) window.navigator.clipboard.writeText(str);")
private static native void setClipboard0(String str);
public static void setLocalStorage(String name, byte[] data) {
try {
Storage s = Window.current().getLocalStorage();
if(s != null) {
s.setItem("_eaglercraftX." + name, Base64.encodeBase64String(data));
}
}catch(Throwable t) {
}
}
public static byte[] getLocalStorage(String name) {
try {
Storage s = Window.current().getLocalStorage();
if(s != null) {
String str = s.getItem("_eaglercraftX." + name);
if(str != null) {
return Base64.decodeBase64(str);
}else {
return null;
}
}else {
return null;
}
}catch(Throwable t) {
return null;
}
}
private static final DateFormat dateFormatSS = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss");
public static String saveScreenshot() {
String name = "screenshot_" + dateFormatSS.format(new Date()).toString() + ".png";
int w = PlatformRuntime.canvas.getWidth();
int h = PlatformRuntime.canvas.getHeight();
HTMLCanvasElement copyCanvas = (HTMLCanvasElement) Window.current().getDocument().createElement("canvas");
copyCanvas.setWidth(w);
copyCanvas.setHeight(h);
CanvasRenderingContext2D ctx = (CanvasRenderingContext2D) copyCanvas.getContext("2d");
ctx.drawImage(PlatformRuntime.canvas, 0, 0);
saveScreenshot(name, copyCanvas);
return name;
}
@JSBody(params = { "name", "cvs" }, script = "var a=document.createElement(\"a\");a.href=cvs.toDataURL(\"image/png\");a.download=name;a.click();")
private static native void saveScreenshot(String name, HTMLCanvasElement cvs);
public static void showPopup(final String msg) {
Window.setTimeout(new TimerHandler() {
@Override
public void onTimer() {
Window.alert(msg);
}
}, 1);
}
@JSFunctor
private static interface FileChooserCallback extends JSObject {
void accept(String name, ArrayBuffer buffer);
}
private static class FileChooserCallbackImpl implements FileChooserCallback {
private static final FileChooserCallbackImpl instance = new FileChooserCallbackImpl();
@Override
public void accept(String name, ArrayBuffer buffer) {
fileChooserHasResult = true;
if(name == null) {
fileChooserResultObject = null;
}else {
Int8Array typedArray = Int8Array.create(buffer);
byte[] bytes = new byte[typedArray.getByteLength()];
for(int i = 0; i < bytes.length; ++i) {
bytes[i] = typedArray.get(i);
}
fileChooserResultObject = new FileChooserResult(name, bytes);
}
}
}
private static volatile boolean fileChooserHasResult = false;
private static volatile FileChooserResult fileChooserResultObject = null;
@JSBody(params = { "inputElement", "callback" }, script =
"if(inputElement.files.length > 0) {"
+ "const value = inputElement.files[0];"
+ "value.arrayBuffer().then(function(arr){ callback(value.name, arr); })"
+ ".catch(function(){ callback(null, null); });"
+ "} else callback(null, null);")
private static native void getFileChooserResult(HTMLInputElement inputElement, FileChooserCallback callback);
@JSBody(params = { "inputElement", "value" }, script = "inputElement.accept = value;")
private static native void setAcceptSelection(HTMLInputElement inputElement, String value);
@JSBody(params = { "inputElement", "enable" }, script = "inputElement.multiple = enable;")
private static native void setMultipleSelection(HTMLInputElement inputElement, boolean enable);
public static void displayFileChooser(String mime, String ext) {
final HTMLInputElement inputElement = (HTMLInputElement) Window.current().getDocument().createElement("input");
inputElement.setType("file");
if(mime == null) {
setAcceptSelection(inputElement, "." + ext);
}else {
setAcceptSelection(inputElement, mime);
}
setMultipleSelection(inputElement, false);
inputElement.addEventListener("change", new EventListener<Event>() {
@Override
public void handleEvent(Event evt) {
getFileChooserResult(inputElement, FileChooserCallbackImpl.instance);
}
});
inputElement.click();
}
public static boolean fileChooserHasResult() {
return fileChooserHasResult;
}
public static FileChooserResult getFileChooserResult() {
fileChooserHasResult = false;
FileChooserResult res = fileChooserResultObject;
fileChooserResultObject = null;
return res;
}
@JSBody(params = { "doc", "str" }, script = "doc.write(str);")
private static native void documentWrite(Document doc, String str);
public static void openCreditsPopup(String text) {
Window currentWin = Window.current();
int w = (int)(530 * currentWin.getDevicePixelRatio());
int h = (int)(450 * currentWin.getDevicePixelRatio());
int x = (currentWin.getScreen().getWidth() - w) / 2;
int y = (currentWin.getScreen().getHeight() - h) / 2;
Window newWin = Window.current().open("", "_blank", "top=" + y + ",left=" + x + ",width=" + w + ",height=" + h + ",menubar=0,status=0,titlebar=0,toolbar=0");
newWin.focus();
documentWrite(newWin.getDocument(), "<html><head><title>EaglercraftX 1.8 Credits</title></head><body><pre>" + text + "</pre></body></html>");
}
}

View File

@ -0,0 +1,143 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
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.canvas.CanvasRenderingContext2D;
import org.teavm.jso.dom.events.Event;
import org.teavm.jso.dom.events.EventListener;
import org.teavm.jso.dom.html.HTMLCanvasElement;
import org.teavm.jso.dom.html.HTMLImageElement;
import org.teavm.jso.dom.xml.Document;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.DataView;
import org.teavm.jso.typedarrays.Uint8Array;
import org.teavm.jso.typedarrays.Uint8ClampedArray;
import net.lax1dude.eaglercraft.v1_8.EaglerInputStream;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.MainClass;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils;
import net.lax1dude.eaglercraft.v1_8.opengl.ImageData;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class PlatformAssets {
private static final byte[] MISSING_FILE = new byte[0];
static final Map<String,byte[]> assets = new HashMap();
public static final byte[] getResourceBytes(String path) {
if(path.startsWith("/")) {
path = path.substring(1);
}
byte[] data = assets.get(path);
if(data == null && path.startsWith("assets/minecraft/lang/") && !path.endsWith(".mcmeta")) {
ArrayBuffer file = PlatformRuntime.downloadRemoteURI(
MainClass.configLocalesFolder + "/" + path.substring(22));
if(file != null && file.getByteLength() > 0) {
data = TeaVMUtils.arrayBufferToBytes(file);
assets.put(path, data);
return data;
}else {
assets.put(path, MISSING_FILE);
return null;
}
}else {
return data == MISSING_FILE ? null : data;
}
}
public static final ImageData loadImageFile(InputStream data) {
byte[] b = EaglerInputStream.inputStreamToBytesQuiet(data);
if(b != null) {
return loadImageFile(b);
}else {
return null;
}
}
private static HTMLCanvasElement imageLoadCanvas = null;
private static CanvasRenderingContext2D imageLoadContext = null;
public static ImageData loadImageFile(byte[] data) {
Uint8Array buf = Uint8Array.create(data.length);
buf.set(data);
return loadImageFile(buf.getBuffer());
}
@JSBody(params = { }, script = "return { willReadFrequently: true };")
public static native JSObject youEagler();
@Async
private static native ImageData loadImageFile(ArrayBuffer data);
private static void loadImageFile(ArrayBuffer data, final AsyncCallback<ImageData> ret) {
final Document doc = Window.current().getDocument();
final HTMLImageElement toLoad = (HTMLImageElement) doc.createElement("img");
toLoad.addEventListener("load", new EventListener<Event>() {
@Override
public void handleEvent(Event evt) {
if(imageLoadCanvas == null) {
imageLoadCanvas = (HTMLCanvasElement) doc.createElement("canvas");
}
if(imageLoadCanvas.getWidth() < toLoad.getWidth()) {
imageLoadCanvas.setWidth(toLoad.getWidth());
}
if(imageLoadCanvas.getHeight() < toLoad.getHeight()) {
imageLoadCanvas.setHeight(toLoad.getHeight());
}
if(imageLoadContext == null) {
imageLoadContext = (CanvasRenderingContext2D) imageLoadCanvas.getContext("2d", youEagler());
}
imageLoadContext.clearRect(0, 0, toLoad.getWidth(), toLoad.getHeight());
imageLoadContext.drawImage(toLoad, 0, 0, toLoad.getWidth(), toLoad.getHeight());
org.teavm.jso.canvas.ImageData pxlsDat = imageLoadContext.getImageData(0, 0, toLoad.getWidth(), toLoad.getHeight());
Uint8ClampedArray pxls = pxlsDat.getData();
int totalPixels = pxlsDat.getWidth() * pxlsDat.getHeight();
TeaVMUtils.freeDataURL(toLoad.getSrc());
if(pxls.getByteLength() < totalPixels << 2) {
ret.complete(null);
return;
}
DataView view = DataView.create(pxls.getBuffer());
int[] pixels = new int[totalPixels];
for(int i = 0; i < pixels.length; ++i) {
pixels[i] = view.getUint32(i << 2, true);
}
ret.complete(new ImageData(pxlsDat.getWidth(), pxlsDat.getHeight(), pixels, true));
}
});
toLoad.addEventListener("error", new EventListener<Event>() {
@Override
public void handleEvent(Event evt) {
TeaVMUtils.freeDataURL(toLoad.getSrc());
ret.complete(null);
}
});
String src = TeaVMUtils.getDataURL(data, "image/png");
if(src != null) {
toLoad.setSrc(src);
}else {
ret.complete(null);
}
}
}

View File

@ -0,0 +1,337 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.SoundCategory;
import org.teavm.interop.Async;
import org.teavm.interop.AsyncCallback;
import org.teavm.jso.JSObject;
import org.teavm.jso.dom.events.EventListener;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Uint8Array;
import org.teavm.jso.webaudio.AudioBuffer;
import org.teavm.jso.webaudio.AudioBufferSourceNode;
import org.teavm.jso.webaudio.AudioContext;
import org.teavm.jso.webaudio.AudioListener;
import org.teavm.jso.webaudio.DecodeErrorCallback;
import org.teavm.jso.webaudio.DecodeSuccessCallback;
import org.teavm.jso.webaudio.GainNode;
import org.teavm.jso.webaudio.MediaEvent;
import org.teavm.jso.webaudio.MediaStream;
import org.teavm.jso.webaudio.MediaStreamAudioDestinationNode;
import org.teavm.jso.webaudio.PannerNode;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.minecraft.util.MathHelper;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class PlatformAudio {
static final Logger logger = LogManager.getLogger("BrowserAudio");
private static AudioContext audioctx = null;
private static MediaStreamAudioDestinationNode recDest = null;
private static final Map<String, BrowserAudioResource> soundCache = new HashMap();
private static long cacheFreeTimer = 0l;
protected static class BrowserAudioResource implements IAudioResource {
protected AudioBuffer buffer;
protected long cacheHit = 0l;
protected BrowserAudioResource(AudioBuffer buffer) {
this.buffer = buffer;
}
}
protected static class BrowserAudioHandle implements IAudioHandle, EventListener<MediaEvent> {
protected final BrowserAudioResource resource;
protected AudioBufferSourceNode source;
protected final PannerNode panner;
protected final GainNode gain;
protected float pitch;
protected boolean isPaused = false;
protected boolean isEnded = false;
public BrowserAudioHandle(BrowserAudioResource resource, AudioBufferSourceNode source, PannerNode panner,
GainNode gain, float pitch) {
this.resource = resource;
this.source = source;
this.panner = panner;
this.gain = gain;
this.pitch = pitch;
source.setOnEnded(this);
}
@Override
public void pause(boolean setPaused) {
if(setPaused) {
if(!isPaused) {
isPaused = true;
source.getPlaybackRate().setValue(0.0f);
}
}else {
if(isPaused) {
isPaused = false;
source.getPlaybackRate().setValue(pitch);
}
}
}
@Override
public void restart() {
if(isEnded) {
isEnded = false;
AudioBufferSourceNode src = audioctx.createBufferSource();
resource.cacheHit = System.currentTimeMillis();
src.setBuffer(resource.buffer);
src.getPlaybackRate().setValue(pitch);
source.disconnect();
src.connect(panner == null ? gain : panner);
source = src;
source.start();
}else {
source.getPlaybackRate().setValue(pitch);
source.start(0.0);
}
}
@Override
public void move(float x, float y, float z) {
if(panner != null) {
panner.setPosition(x, y, z);
}
}
@Override
public void pitch(float f) {
pitch = f;
if(!isPaused) {
source.getPlaybackRate().setValue(pitch);
}
}
@Override
public void gain(float f) {
if(panner != null) {
float v1 = f * 16.0f;
if(v1 < 16.0f) v1 = 16.0f;
panner.setMaxDistance(v1);
}
float v2 = f;
if(v2 > 1.0f) v2 = 1.0f;
gain.getGain().setValue(v2);
}
@Override
public void end() {
if(!isEnded) {
isEnded = true;
source.stop();
}
}
@Override
public boolean shouldFree() {
return isEnded;
}
@Override
public void handleEvent(MediaEvent evt) {
isEnded = true;
}
}
static void initialize() {
try {
audioctx = AudioContext.create();
recDest = audioctx.createMediaStreamDestination();
}catch(Throwable t) {
throw new PlatformRuntime.RuntimeInitializationFailureException("Could not initialize audio context!", t);
}
PlatformInput.clearEvenBuffers();
}
private static GainNode micGain;
public static void setMicVol(float vol) {
if (micGain == null) return;
micGain.getGain().setValue(vol);
}
protected static void initRecDest() {
AudioBufferSourceNode absn = audioctx.createBufferSource();
AudioBuffer ab = audioctx.createBuffer(1, 1, 48000);
ab.copyToChannel(new float[] { 0 }, 0);
absn.setBuffer(ab);
absn.setLoop(true);
absn.start();
absn.connect(recDest);
MediaStream mic = PlatformRuntime.getMic();
if (mic != null) {
micGain = audioctx.createGain();
micGain.getGain().setValue(Minecraft.getMinecraft().gameSettings.getSoundLevel(SoundCategory.VOICE));
audioctx.createMediaStreamSource(mic).connect(micGain);
micGain.connect(recDest);
}
}
protected static MediaStream getRecStream() {
return recDest.getStream();
}
public static IAudioResource loadAudioData(String filename, boolean holdInCache) {
BrowserAudioResource buffer;
synchronized(soundCache) {
buffer = soundCache.get(filename);
}
if(buffer == null) {
byte[] file = PlatformAssets.getResourceBytes(filename);
if(file == null) return null;
Uint8Array buf = Uint8Array.create(file.length);
buf.set(file);
buffer = new BrowserAudioResource(decodeAudioAsync(buf.getBuffer(), filename));
if(holdInCache) {
synchronized(soundCache) {
soundCache.put(filename, buffer);
}
}
}
if(buffer.buffer != null) {
buffer.cacheHit = System.currentTimeMillis();
return buffer;
}else {
return null;
}
}
@Async
public static native AudioBuffer decodeAudioAsync(ArrayBuffer buffer, String errorFileName);
private static void decodeAudioAsync(ArrayBuffer buffer, final String errorFileName, final AsyncCallback<AudioBuffer> cb) {
audioctx.decodeAudioData(buffer, new DecodeSuccessCallback() {
@Override
public void onSuccess(AudioBuffer decodedData) {
cb.complete(decodedData);
}
}, new DecodeErrorCallback() {
@Override
public void onError(JSObject error) {
logger.error("Could not load audio: {}", errorFileName);
cb.complete(null);
}
});
}
public static void clearAudioCache() {
long millis = System.currentTimeMillis();
if(millis - cacheFreeTimer > 30000l) {
cacheFreeTimer = millis;
synchronized(soundCache) {
Iterator<BrowserAudioResource> itr = soundCache.values().iterator();
while(itr.hasNext()) {
if(millis - itr.next().cacheHit > 600000l) { // 10 minutes
itr.remove();
}
}
}
}
}
public static boolean available() {
return true; // this is not used
}
public static IAudioHandle beginPlayback(IAudioResource track, float x, float y, float z,
float volume, float pitch) {
BrowserAudioResource internalTrack = (BrowserAudioResource) track;
internalTrack.cacheHit = System.currentTimeMillis();
AudioBufferSourceNode src = audioctx.createBufferSource();
src.setBuffer(internalTrack.buffer);
src.getPlaybackRate().setValue(pitch);
PannerNode panner = audioctx.createPanner();
panner.setPosition(x, y, z);
float v1 = volume * 16.0f;
if(v1 < 16.0f) v1 = 16.0f;
panner.setMaxDistance(v1);
panner.setRolloffFactor(1.0f);
panner.setDistanceModel("linear");
panner.setPanningModel("HRTF");
panner.setConeInnerAngle(360.0f);
panner.setConeOuterAngle(0.0f);
panner.setConeOuterGain(0.0f);
panner.setOrientation(0.0f, 1.0f, 0.0f);
GainNode gain = audioctx.createGain();
float v2 = volume;
if(v2 > 1.0f) v2 = 1.0f;
gain.getGain().setValue(v2);
src.connect(panner);
panner.connect(gain);
gain.connect(audioctx.getDestination());
gain.connect(recDest);
src.start();
return new BrowserAudioHandle(internalTrack, src, panner, gain, pitch);
}
public static IAudioHandle beginPlaybackStatic(IAudioResource track, float volume, float pitch) {
BrowserAudioResource internalTrack = (BrowserAudioResource) track;
internalTrack.cacheHit = System.currentTimeMillis();
AudioBufferSourceNode src = audioctx.createBufferSource();
src.setBuffer(internalTrack.buffer);
src.getPlaybackRate().setValue(pitch);
GainNode gain = audioctx.createGain();
float v2 = volume;
if(v2 > 1.0f) v2 = 1.0f;
gain.getGain().setValue(v2);
src.connect(gain);
gain.connect(audioctx.getDestination());
gain.connect(recDest);
src.start();
return new BrowserAudioHandle(internalTrack, src, null, gain, pitch);
}
public static void setListener(float x, float y, float z, float pitchDegrees, float yawDegrees) {
float var2 = MathHelper.cos(-yawDegrees * 0.017453292F);
float var3 = MathHelper.sin(-yawDegrees * 0.017453292F);
float var4 = -MathHelper.cos(pitchDegrees * 0.017453292F);
float var5 = MathHelper.sin(pitchDegrees * 0.017453292F);
AudioListener l = audioctx.getListener();
l.setPosition(x, y, z);
l.setOrientation(-var3 * var4, -var5, -var2 * var4, 0.0f, 1.0f, 0.0f);
}
}

View File

@ -0,0 +1,35 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class PlatformBufferFunctions {
public static void put(ByteBuffer newBuffer, ByteBuffer flip) {
int len = flip.remaining();
for(int i = 0; i < len; ++i) {
newBuffer.put(flip.get());
}
}
public static void put(IntBuffer intBuffer, int index, int[] data) {
int p = intBuffer.position();
intBuffer.position(index);
intBuffer.put(data);
intBuffer.position(p);
}
}

View File

@ -0,0 +1,530 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.util.LinkedList;
import java.util.List;
import org.teavm.jso.JSBody;
import org.teavm.jso.browser.TimerHandler;
import org.teavm.jso.browser.Window;
import org.teavm.jso.dom.events.EventListener;
import org.teavm.jso.dom.events.KeyboardEvent;
import org.teavm.jso.dom.events.MouseEvent;
import org.teavm.jso.dom.events.WheelEvent;
import org.teavm.jso.dom.html.HTMLCanvasElement;
import org.teavm.jso.webgl.WebGLFramebuffer;
import org.teavm.jso.webgl.WebGLRenderbuffer;
import net.lax1dude.eaglercraft.v1_8.EagUtils;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.EarlyLoadScreen;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGL2RenderingContext;
import static net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGL2RenderingContext.*;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class PlatformInput {
private static Window win = null;
private static HTMLCanvasElement canvas = null;
static WebGL2RenderingContext context = null;
static WebGLFramebuffer mainFramebuffer = null;
static WebGLRenderbuffer mainColorRenderbuffer = null;
static WebGLRenderbuffer mainDepthRenderbuffer = null;
private static int framebufferWidth = -1;
private static int framebufferHeight = -1;
private static EventListener contextmenu = null;
private static EventListener mousedown = null;
private static EventListener mouseup = null;
private static EventListener mousemove = null;
private static EventListener mouseenter = null;
private static EventListener mouseleave = null;
private static EventListener keydown = null;
private static EventListener keyup = null;
private static EventListener keypress = null;
private static EventListener wheel = null;
private static EventListener pointerlock = null;
private static List<MouseEvent> mouseEvents = new LinkedList();
private static List<KeyboardEvent> keyEvents = new LinkedList();
private static int mouseX = 0;
private static int mouseY = 0;
private static double mouseDX = 0.0D;
private static double mouseDY = 0.0D;
private static double mouseDWheel = 0.0D;
private static int width = 0;
private static int height = 0;
private static boolean enableRepeatEvents = true;
private static boolean isWindowFocused = true;
private static boolean isMouseOverWindow = true;
static boolean unpressCTRL = false;
private static int windowWidth = -1;
private static int windowHeight = -1;
private static int lastWasResizedWindowWidth = -2;
private static int lastWasResizedWindowHeight = -2;
private static MouseEvent currentEvent = null;
private static KeyboardEvent currentEventK = null;
private static boolean[] buttonStates = new boolean[8];
private static boolean[] keyStates = new boolean[256];
private static int functionKeyModifier = KeyboardConstants.KEY_F;
private static long mouseUngrabTimer = 0l;
private static long mouseGrabTimer = 0l;
private static int mouseUngrabTimeout = -1;
private static boolean pointerLockFlag = false;
@JSBody(params = { }, script = "window.onbeforeunload = () => {return false;};")
private static native void onBeforeCloseRegister();
static void initHooks(Window window, HTMLCanvasElement canvaz) {
win = window;
canvas = canvaz;
win.addEventListener("contextmenu", contextmenu = new EventListener<MouseEvent>() {
@Override
public void handleEvent(MouseEvent evt) {
evt.preventDefault();
evt.stopPropagation();
}
});
canvas.addEventListener("mousedown", mousedown = new EventListener<MouseEvent>() {
@Override
public void handleEvent(MouseEvent evt) {
evt.preventDefault();
evt.stopPropagation();
int b = evt.getButton();
buttonStates[b == 1 ? 2 : (b == 2 ? 1 : b)] = true;
mouseEvents.add(evt);
}
});
canvas.addEventListener("mouseup", mouseup = new EventListener<MouseEvent>() {
@Override
public void handleEvent(MouseEvent evt) {
evt.preventDefault();
evt.stopPropagation();
int b = evt.getButton();
buttonStates[b == 1 ? 2 : (b == 2 ? 1 : b)] = false;
mouseEvents.add(evt);
}
});
canvas.addEventListener("mousemove", mousemove = new EventListener<MouseEvent>() {
@Override
public void handleEvent(MouseEvent evt) {
evt.preventDefault();
evt.stopPropagation();
mouseX = (int)(getOffsetX(evt) * win.getDevicePixelRatio());
mouseY = (int)((canvas.getClientHeight() - getOffsetY(evt)) * win.getDevicePixelRatio());
mouseDX += evt.getMovementX();
mouseDY += -evt.getMovementY();
if(hasBeenActive()) {
mouseEvents.add(evt);
}
}
});
canvas.addEventListener("mouseenter", mouseenter = new EventListener<MouseEvent>() {
@Override
public void handleEvent(MouseEvent evt) {
isMouseOverWindow = true;
}
});
canvas.addEventListener("mouseleave", mouseleave = new EventListener<MouseEvent>() {
@Override
public void handleEvent(MouseEvent evt) {
isMouseOverWindow = false;
}
});
win.addEventListener("keydown", keydown = new EventListener<KeyboardEvent>() {
@Override
public void handleEvent(KeyboardEvent evt) {
int w = getWhich(evt);
if (w == 122) return; // F11
evt.preventDefault();
evt.stopPropagation();
if(!enableRepeatEvents && evt.isRepeat()) return;
w = processFunctionKeys(w);
keyStates[KeyboardConstants.getEaglerKeyFromBrowser(w, evt.getLocation())] = true;
keyEvents.add(evt);
}
});
win.addEventListener("keyup", keyup = new EventListener<KeyboardEvent>() {
@Override
public void handleEvent(KeyboardEvent evt) {
int w = getWhich(evt);
if (w == 122) return; // F11
evt.preventDefault();
evt.stopPropagation();
if(!enableRepeatEvents && evt.isRepeat()) return;
w = processFunctionKeys(w);
keyStates[KeyboardConstants.getEaglerKeyFromBrowser(w, evt.getLocation())] = false;
keyEvents.add(evt);
}
});
win.addEventListener("keypress", keypress = new EventListener<KeyboardEvent>() {
@Override
public void handleEvent(KeyboardEvent evt) {
evt.preventDefault();
evt.stopPropagation();
if(enableRepeatEvents && evt.isRepeat()) keyEvents.add(evt);
}
});
canvas.addEventListener("wheel", wheel = new EventListener<WheelEvent>() {
@Override
public void handleEvent(WheelEvent evt) {
evt.preventDefault();
evt.stopPropagation();
mouseEvents.add(evt);
mouseDWheel += evt.getDeltaY();
}
});
win.addEventListener("blur", new EventListener<WheelEvent>() {
@Override
public void handleEvent(WheelEvent evt) {
isWindowFocused = false;
for(int i = 0; i < buttonStates.length; ++i) {
buttonStates[i] = false;
}
for(int i = 0; i < keyStates.length; ++i) {
keyStates[i] = false;
}
}
});
win.addEventListener("focus", new EventListener<WheelEvent>() {
@Override
public void handleEvent(WheelEvent evt) {
isWindowFocused = true;
}
});
win.getDocument().addEventListener("pointerlockchange", pointerlock = new EventListener<WheelEvent>() {
@Override
public void handleEvent(WheelEvent evt) {
Window.setTimeout(new TimerHandler() {
@Override
public void onTimer() {
boolean grab = isPointerLocked();
if(!grab) {
if(pointerLockFlag) {
mouseUngrabTimer = System.currentTimeMillis();
}
}
pointerLockFlag = grab;
}
}, 60);
mouseDX = 0.0D;
mouseDY = 0.0D;
}
});
onBeforeCloseRegister();
}
@JSBody(params = { }, script = "if(window.navigator.userActivation){return window.navigator.userActivation.hasBeenActive;}else{return false;}")
public static native boolean hasBeenActive();
@JSBody(params = { "m" }, script = "return m.offsetX;")
private static native int getOffsetX(MouseEvent m);
@JSBody(params = { "m" }, script = "return m.offsetY;")
private static native int getOffsetY(MouseEvent m);
@JSBody(params = { "e" }, script = "return e.which;")
private static native int getWhich(KeyboardEvent e);
public static int getWindowWidth() {
return windowWidth;
}
public static int getWindowHeight() {
return windowHeight;
}
public static boolean getWindowFocused() {
return isWindowFocused || isPointerLocked();
}
public static boolean isCloseRequested() {
return false;
}
public static void update() {
double r = win.getDevicePixelRatio();
int w = PlatformRuntime.parent.getClientWidth();
int h = PlatformRuntime.parent.getClientHeight();
int w2 = windowWidth = (int)(w * r);
int h2 = windowHeight = (int)(h * r);
if(canvas.getWidth() != w2) {
canvas.setWidth(w2);
}
if(canvas.getHeight() != h2) {
canvas.setHeight(h2);
}
flipBuffer();
EagUtils.sleep(1l);
}
static void initFramebuffer(WebGL2RenderingContext ctx, WebGLFramebuffer fbo, int sw, int sh) {
context = ctx;
mainFramebuffer = fbo;
framebufferWidth = windowWidth = sw;
framebufferHeight = windowHeight = sh;
ctx.bindFramebuffer(FRAMEBUFFER, fbo);
mainColorRenderbuffer = ctx.createRenderbuffer();
mainDepthRenderbuffer = ctx.createRenderbuffer();
ctx.bindRenderbuffer(RENDERBUFFER, mainColorRenderbuffer);
ctx.renderbufferStorage(RENDERBUFFER, RGBA8, sw, sh);
ctx.framebufferRenderbuffer(FRAMEBUFFER, COLOR_ATTACHMENT0, RENDERBUFFER, mainColorRenderbuffer);
ctx.bindRenderbuffer(RENDERBUFFER, mainDepthRenderbuffer);
ctx.renderbufferStorage(RENDERBUFFER, DEPTH_COMPONENT32F, sw, sh);
ctx.framebufferRenderbuffer(FRAMEBUFFER, DEPTH_ATTACHMENT, RENDERBUFFER, mainDepthRenderbuffer);
ctx.drawBuffers(new int[] { COLOR_ATTACHMENT0 });
}
private static void flipBuffer() {
context.bindFramebuffer(READ_FRAMEBUFFER, mainFramebuffer);
context.bindFramebuffer(DRAW_FRAMEBUFFER, null);
context.blitFramebuffer(0, 0, framebufferWidth, framebufferHeight, 0, 0, windowWidth, windowHeight, COLOR_BUFFER_BIT, NEAREST);
context.bindFramebuffer(FRAMEBUFFER, mainFramebuffer);
if(windowWidth != framebufferWidth || windowHeight != framebufferHeight) {
framebufferWidth = windowWidth;
framebufferHeight = windowHeight;
context.bindRenderbuffer(RENDERBUFFER, mainColorRenderbuffer);
context.renderbufferStorage(RENDERBUFFER, RGBA8, framebufferWidth, framebufferHeight);
context.bindRenderbuffer(RENDERBUFFER, mainDepthRenderbuffer);
context.renderbufferStorage(RENDERBUFFER, DEPTH_COMPONENT32F, framebufferWidth, framebufferHeight);
}
}
public static boolean wasResized() {
if(windowWidth != lastWasResizedWindowWidth || windowHeight != lastWasResizedWindowHeight) {
lastWasResizedWindowWidth = windowWidth;
lastWasResizedWindowHeight = windowHeight;
return true;
}else {
return false;
}
}
public static boolean keyboardNext() {
if(unpressCTRL) { //un-press ctrl after copy/paste permission
keyEvents.clear();
currentEventK = null;
keyStates[29] = false;
keyStates[157] = false;
keyStates[28] = false;
keyStates[219] = false;
keyStates[220] = false;
unpressCTRL = false;
return false;
}
currentEventK = null;
return !keyEvents.isEmpty() && (currentEventK = keyEvents.remove(0)) != null;
}
public static boolean keyboardGetEventKeyState() {
return currentEventK == null? false : !currentEventK.getType().equals("keyup");
}
public static int keyboardGetEventKey() {
int w = processFunctionKeys(getWhich(currentEventK));
return currentEventK == null ? -1 : KeyboardConstants.getEaglerKeyFromBrowser(w, currentEventK.getLocation());
}
public static char keyboardGetEventCharacter() {
if(currentEventK == null) return '\0';
String s = currentEventK.getKey();
return currentEventK == null ? ' ' : (char) (s.length() > 1 ? '\0' : s.charAt(0));
}
public static boolean keyboardIsKeyDown(int key) {
if(unpressCTRL) { //un-press ctrl after copy/paste permission
keyStates[28] = false;
keyStates[29] = false;
keyStates[157] = false;
keyStates[219] = false;
keyStates[220] = false;
}
return key < 0 || key >= keyStates.length ? false : keyStates[key];
}
public static boolean keyboardIsRepeatEvent() {
return currentEventK == null ? false : currentEventK.isRepeat();
}
public static void keyboardEnableRepeatEvents(boolean b) {
enableRepeatEvents = b;
}
public static boolean mouseNext() {
currentEvent = null;
return !mouseEvents.isEmpty() && (currentEvent = mouseEvents.remove(0)) != null;
}
public static boolean mouseGetEventButtonState() {
return currentEvent == null ? false : currentEvent.getType().equals(MouseEvent.MOUSEDOWN);
}
public static int mouseGetEventButton() {
if(currentEvent == null || currentEvent.getType().equals(MouseEvent.MOUSEMOVE)) return -1;
int b = currentEvent.getButton();
return b == 1 ? 2 : (b == 2 ? 1 : b);
}
public static int mouseGetEventX() {
return currentEvent == null ? -1 : (int)(currentEvent.getClientX() * win.getDevicePixelRatio());
}
public static int mouseGetEventY() {
return currentEvent == null ? -1 : (int)((canvas.getClientHeight() - currentEvent.getClientY()) * win.getDevicePixelRatio());
}
public static int mouseGetEventDWheel() {
return ("wheel".equals(currentEvent.getType())) ? (((WheelEvent)currentEvent).getDeltaY() == 0.0D ? 0 : (((WheelEvent)currentEvent).getDeltaY() > 0.0D ? -1 : 1)) : 0;
}
public static int mouseGetX() {
return mouseX;
}
public static int mouseGetY() {
return mouseY;
}
public static boolean mouseIsButtonDown(int i) {
return buttonStates[i];
}
public static int mouseGetDWheel() {
int ret = (int)mouseDWheel;
mouseDWheel = 0.0D;
return ret;
}
public static void mouseSetGrabbed(boolean grab) {
long t = System.currentTimeMillis();
pointerLockFlag = grab;
mouseGrabTimer = t;
if(grab) {
canvas.requestPointerLock();
if(mouseUngrabTimeout != -1) Window.clearTimeout(mouseUngrabTimeout);
mouseUngrabTimeout = -1;
if(t - mouseUngrabTimer < 3000l) {
mouseUngrabTimeout = Window.setTimeout(new TimerHandler() {
@Override
public void onTimer() {
canvas.requestPointerLock();
}
}, 3100 - (int)(t - mouseUngrabTimer));
}
}else {
if(mouseUngrabTimeout != -1) Window.clearTimeout(mouseUngrabTimeout);
mouseUngrabTimeout = -1;
Window.current().getDocument().exitPointerLock();
}
mouseDX = 0.0D;
mouseDY = 0.0D;
}
public static boolean isMouseGrabbed() {
return pointerLockFlag;
}
@JSBody(params = { }, script = "return document.pointerLockElement != null;")
public static native boolean isPointerLocked();
public static int mouseGetDX() {
int ret = (int)mouseDX;
mouseDX = 0.0D;
return ret;
}
public static int mouseGetDY() {
int ret = (int)mouseDY;
mouseDY = 0.0D;
return ret;
}
public static void mouseSetCursorPosition(int x, int y) {
// obsolete
}
public static boolean mouseIsInsideWindow() {
return isMouseOverWindow;
}
public static boolean contextLost() {
return PlatformRuntime.webgl.isContextLost();
}
private static int processFunctionKeys(int key) {
if(keyboardIsKeyDown(functionKeyModifier)) {
if(key >= 49 && key <= 57) {
key = key - 49 + 112;
}
}
return key;
}
public static void setFunctionKeyModifier(int key) {
functionKeyModifier = key;
}
public static void removeEventHandlers() {
win.removeEventListener("contextmenu", contextmenu);
canvas.removeEventListener("mousedown", mousedown);
canvas.removeEventListener("mouseup", mouseup);
canvas.removeEventListener("mousemove", mousemove);
canvas.removeEventListener("mouseenter", mouseenter);
canvas.removeEventListener("mouseleave", mouseleave);
win.removeEventListener("keydown", keydown);
win.removeEventListener("keyup", keyup);
win.removeEventListener("keypress", keypress);
canvas.removeEventListener("wheel", wheel);
win.getDocument().removeEventListener("pointerlockchange", pointerlock);
if(mouseUngrabTimeout != -1) {
Window.clearTimeout(mouseUngrabTimeout);
mouseUngrabTimeout = -1;
}
}
public static void pressAnyKeyScreen() {
if(mouseEvents.isEmpty() && keyEvents.isEmpty() && !hasBeenActive()) {
EarlyLoadScreen.paintEnable();
while(mouseEvents.isEmpty() && keyEvents.isEmpty()) {
EagUtils.sleep(100l);
}
}
}
public static void clearEvenBuffers() {
mouseEvents.clear();
keyEvents.clear();
}
}

View File

@ -0,0 +1,196 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.util.LinkedList;
import org.teavm.interop.Async;
import org.teavm.interop.AsyncCallback;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
import org.teavm.jso.dom.events.Event;
import org.teavm.jso.dom.events.EventListener;
import org.teavm.jso.dom.events.MessageEvent;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Uint8Array;
import org.teavm.jso.websocket.WebSocket;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMServerQuery;
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 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class PlatformNetworking {
private static WebSocket sock = null;
private static boolean sockIsConnecting = false;
private static boolean sockIsConnected = false;
private static boolean sockIsAlive = false;
private static boolean sockIsFailed = false;
private static LinkedList<byte[]> readPackets = new LinkedList();
private static String currentSockURI = null;
private static EnumServerRateLimit serverRateLimit = null;
private static final Logger logger = LogManager.getLogger("PlatformNetworking");
public static EnumEaglerConnectionState playConnectionState() {
return !sockIsConnected ? (sockIsFailed ? EnumEaglerConnectionState.FAILED : EnumEaglerConnectionState.CLOSED)
: (sockIsConnecting ? EnumEaglerConnectionState.CONNECTING : EnumEaglerConnectionState.CONNECTED);
}
public static void startPlayConnection(String destination) {
sockIsFailed = !connectWebSocket(destination).booleanValue();
}
@JSBody(params = { "obj" }, script = "return typeof obj === \"string\";")
private static native boolean isString(JSObject obj);
@Async
public static native Boolean connectWebSocket(String sockURI);
private static void connectWebSocket(String sockURI, final AsyncCallback<Boolean> cb) {
sockIsConnecting = true;
sockIsConnected = false;
sockIsAlive = false;
currentSockURI = sockURI;
try {
sock = WebSocket.create(sockURI);
} catch(Throwable t) {
sockIsFailed = true;
sockIsConnecting = false;
sockIsAlive = false;
cb.complete(Boolean.FALSE);
return;
}
final WebSocket oldSock = sock;
sock.setBinaryType("arraybuffer");
TeaVMUtils.addEventListener(sock, "open", new EventListener<Event>() {
@Override
public void handleEvent(Event evt) {
if (oldSock != sock) return;
sockIsConnecting = false;
sockIsAlive = false;
sockIsConnected = true;
synchronized(readPackets) {
readPackets.clear();
}
cb.complete(Boolean.TRUE);
}
});
TeaVMUtils.addEventListener(sock, "close", new EventListener<Event>() {
@Override
public void handleEvent(Event evt) {
if (oldSock != sock) return;
sock = null;
boolean b = sockIsConnecting;
sockIsConnecting = false;
sockIsConnected = false;
sockIsAlive = false;
if(b) cb.complete(Boolean.FALSE);
}
});
TeaVMUtils.addEventListener(sock, "message", new EventListener<MessageEvent>() {
@Override
public void handleEvent(MessageEvent evt) {
if (oldSock != sock) return;
sockIsAlive = true;
if(isString(evt.getData())) {
String str = evt.getDataAsString();
if(str.equalsIgnoreCase("BLOCKED")) {
logger.error("Reached full IP ratelimit!");
serverRateLimit = EnumServerRateLimit.BLOCKED;
}else if(str.equalsIgnoreCase("LOCKED")) {
logger.error("Reached full IP ratelimit lockout!");
serverRateLimit = EnumServerRateLimit.LOCKED_OUT;
}
}else {
Uint8Array a = Uint8Array.create(evt.getDataAsArray());
byte[] b = new byte[a.getByteLength()];
for(int i = 0; i < b.length; ++i) {
b[i] = (byte) a.get(i);
}
synchronized(readPackets) {
readPackets.add(b);
}
}
}
});
TeaVMUtils.addEventListener(sock, "error", new EventListener<Event>() {
@Override
public void handleEvent(Event evt) {
if (oldSock != sock) return;
if(sockIsConnecting) {
sockIsFailed = true;
sockIsConnecting = false;
sockIsAlive = false;
cb.complete(Boolean.FALSE);
}
}
});
}
public static void playDisconnect() {
if(sock != null) sock.close();
sockIsConnecting = false;
}
public static byte[] readPlayPacket() {
synchronized(readPackets) {
if(!readPackets.isEmpty()) {
return readPackets.remove(0);
}else {
return null;
}
}
}
public static int countAvailableReadData() {
int total = 0;
synchronized(readPackets) {
for(int i = 0, l = readPackets.size(); i < l; ++i) {
total += readPackets.get(i).length;
}
}
return total;
}
@JSBody(params = { "sock", "buffer" }, script = "sock.send(buffer);")
private static native void nativeBinarySend(WebSocket sock, ArrayBuffer buffer);
public static void writePlayPacket(byte[] pkt) {
if(sock != null && !sockIsConnecting) {
Uint8Array arr = Uint8Array.create(pkt.length);
arr.set(pkt);
nativeBinarySend(sock, arr.getBuffer());
}
}
public static IServerQuery sendServerQuery(String uri, String accept) {
try {
return new TeaVMServerQuery(uri, accept);
}catch(Throwable t) {
logger.error("Could not send query to \"{}\"!", uri);
logger.error(t);
return null;
}
}
public static EnumServerRateLimit getRateLimit() {
return serverRateLimit == null ? EnumServerRateLimit.OK : serverRateLimit;
}
public static String getCurrentURI() {
return currentSockURI;
}
}

View File

@ -0,0 +1,474 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.EaglerArrayBufferAllocator;
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.teavm.WebGL2RenderingContext;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class PlatformOpenGL {
static WebGL2RenderingContext ctx = null;
static boolean hasDebugRenderInfoExt = false;
static void setCurrentContext(WebGL2RenderingContext context) {
ctx = context;
hasDebugRenderInfoExt = ctx.getExtension("WEBGL_debug_renderer_info") != null;
_wglClearColor(1.0f, 1.0f, 1.0f, 1.0f);
}
public static final void _wglEnable(int glEnum) {
ctx.enable(glEnum);
}
public static final void _wglDisable(int glEnum) {
ctx.disable(glEnum);
}
public static final void _wglClearColor(float r, float g, float b, float a) {
ctx.clearColor(r, g, b, a);
}
public static final void _wglClearDepth(float f) {
ctx.clearDepth(f);
}
public static final void _wglClear(int bits) {
ctx.clear(bits);
}
public static final void _wglDepthFunc(int glEnum) {
ctx.depthFunc(glEnum);
}
public static final void _wglDepthMask(boolean mask) {
ctx.depthMask(mask);
}
public static final void _wglCullFace(int glEnum) {
ctx.cullFace(glEnum);
}
public static final void _wglViewport(int x, int y, int w, int h) {
ctx.viewport(x, y, w, h);
}
public static final void _wglBlendFunc(int src, int dst) {
ctx.blendFunc(src, dst);
}
public static final void _wglBlendFuncSeparate(int srcColor, int dstColor,
int srcAlpha, int dstAlpha) {
ctx.blendFuncSeparate(srcColor, dstColor, srcAlpha, dstAlpha);
}
public static final void _wglBlendEquation(int glEnum) {
ctx.blendEquation(glEnum);
}
public static final void _wglColorMask(boolean r, boolean g, boolean b, boolean a) {
ctx.colorMask(r, g, b, a);
}
public static final void _wglDrawBuffers(int buffer) {
ctx.drawBuffers(new int[] { buffer });
}
public static final void _wglDrawBuffers(int[] buffers) {
ctx.drawBuffers(buffers);
}
public static final void _wglReadBuffer(int buffer) {
ctx.readBuffer(buffer);
}
public static final void _wglPolygonOffset(float f1, float f2) {
ctx.polygonOffset(f1, f2);
}
public static final void _wglLineWidth(float width) {
ctx.lineWidth(width);
}
public static final IBufferGL _wglGenBuffers() {
return new OpenGLObjects.BufferGL(ctx.createBuffer());
}
public static final ITextureGL _wglGenTextures() {
return new OpenGLObjects.TextureGL(ctx.createTexture());
}
public static final IBufferArrayGL _wglGenVertexArrays() {
return new OpenGLObjects.BufferArrayGL(ctx.createVertexArray());
}
public static final IProgramGL _wglCreateProgram() {
return new OpenGLObjects.ProgramGL(ctx.createProgram());
}
public static final IShaderGL _wglCreateShader(int type) {
return new OpenGLObjects.ShaderGL(ctx.createShader(type));
}
public static final IFramebufferGL _wglCreateFramebuffer() {
return new OpenGLObjects.FramebufferGL(ctx.createFramebuffer());
}
public static final IRenderbufferGL _wglCreateRenderbuffer() {
return new OpenGLObjects.RenderbufferGL(ctx.createRenderbuffer());
}
public static final IQueryGL _wglGenQueries() {
return new OpenGLObjects.QueryGL(ctx.createQuery());
}
public static final void _wglDeleteBuffers(IBufferGL obj) {
ctx.deleteBuffer(obj == null ? null : ((OpenGLObjects.BufferGL)obj).ptr);
}
public static final void _wglDeleteTextures(ITextureGL obj) {
ctx.deleteTexture(obj == null ? null : ((OpenGLObjects.TextureGL)obj).ptr);
}
public static final void _wglDeleteVertexArrays(IBufferArrayGL obj) {
ctx.deleteVertexArray(obj == null ? null : ((OpenGLObjects.BufferArrayGL)obj).ptr);
}
public static final void _wglDeleteProgram(IProgramGL obj) {
ctx.deleteProgram(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr);
}
public static final void _wglDeleteShader(IShaderGL obj) {
ctx.deleteShader(obj == null ? null : ((OpenGLObjects.ShaderGL)obj).ptr);
}
public static final void _wglDeleteFramebuffer(IFramebufferGL obj) {
ctx.deleteFramebuffer(obj == null ? null : ((OpenGLObjects.FramebufferGL)obj).ptr);
}
public static final void _wglDeleteRenderbuffer(IRenderbufferGL obj) {
ctx.deleteRenderbuffer(obj == null ? null : ((OpenGLObjects.RenderbufferGL)obj).ptr);
}
public static final void _wglDeleteQueries(IQueryGL obj) {
ctx.deleteQuery(obj == null ? null : ((OpenGLObjects.QueryGL)obj).ptr);
}
public static final void _wglBindBuffer(int target, IBufferGL obj) {
ctx.bindBuffer(target, obj == null ? null : ((OpenGLObjects.BufferGL)obj).ptr);
}
public static final void _wglBufferData(int target, ByteBuffer data, int usage) {
ctx.bufferData(target, data == null ? null : EaglerArrayBufferAllocator.getDataView(data), usage);
}
public static final void _wglBufferData(int target, IntBuffer data, int usage) {
ctx.bufferData(target, data == null ? null : EaglerArrayBufferAllocator.getDataView(data), usage);
}
public static final void _wglBufferData(int target, FloatBuffer data, int usage) {
ctx.bufferData(target, data == null ? null : EaglerArrayBufferAllocator.getDataView(data), usage);
}
public static final void _wglBufferData(int target, int size, int usage) {
ctx.bufferData(target, size, usage);
}
public static final void _wglBufferSubData(int target, int offset, ByteBuffer data) {
ctx.bufferSubData(target, offset, data == null ? null : EaglerArrayBufferAllocator.getDataView(data));
}
public static final void _wglBufferSubData(int target, int offset, IntBuffer data) {
ctx.bufferSubData(target, offset, data == null ? null : EaglerArrayBufferAllocator.getDataView(data));
}
public static final void _wglBufferSubData(int target, int offset, FloatBuffer data) {
ctx.bufferSubData(target, offset, data == null ? null : EaglerArrayBufferAllocator.getDataView(data));
}
public static final void _wglBindVertexArray(IBufferArrayGL obj) {
ctx.bindVertexArray(obj == null ? null : ((OpenGLObjects.BufferArrayGL)obj).ptr);
}
public static final void _wglEnableVertexAttribArray(int index) {
ctx.enableVertexAttribArray(index);
}
public static final void _wglDisableVertexAttribArray(int index) {
ctx.disableVertexAttribArray(index);
}
public static final void _wglVertexAttribPointer(int index, int size, int type,
boolean normalized, int stride, int offset) {
ctx.vertexAttribPointer(index, size, type, normalized, stride, offset);
}
public static final void _wglVertexAttribDivisor(int index, int divisor) {
ctx.vertexAttribDivisor(index, divisor);
}
public static final void _wglActiveTexture(int texture) {
ctx.activeTexture(texture);
}
public static final void _wglBindTexture(int target, ITextureGL obj) {
ctx.bindTexture(target, obj == null ? null : ((OpenGLObjects.TextureGL)obj).ptr);
}
public static final void _wglTexParameterf(int target, int param, float value) {
ctx.texParameterf(target, param, value);
}
public static final void _wglTexParameteri(int target, int param, int value) {
ctx.texParameteri(target, param, value);
}
public static final void _wglTexImage2D(int target, int level, int internalFormat, int width,
int height, int border, int format, int type, ByteBuffer data) {
ctx.texImage2D(target, level, internalFormat, width, height, border, format, type,
data == null ? null : EaglerArrayBufferAllocator.getDataViewStupid(data));
}
public static final void _wglTexImage2D(int target, int level, int internalFormat, int width,
int height, int border, int format, int type, IntBuffer data) {
ctx.texImage2D(target, level, internalFormat, width, height, border, format, type,
data == null ? null : EaglerArrayBufferAllocator.getDataViewStupid(data));
}
public static final void _wglTexImage2D(int target, int level, int internalFormat, int width,
int height, int border, int format, int type, FloatBuffer data) {
ctx.texImage2D(target, level, internalFormat, width, height, border, format, type,
data == null ? null : EaglerArrayBufferAllocator.getDataViewStupid(data));
}
public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset,
int width, int height, int format, int type, ByteBuffer data) {
ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
data == null ? null : EaglerArrayBufferAllocator.getDataViewStupid(data));
}
public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset,
int width, int height, int format, int type, IntBuffer data) {
ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
data == null ? null : EaglerArrayBufferAllocator.getDataViewStupid(data));
}
public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset,
int width, int height, int format, int type, FloatBuffer data) {
ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
data == null ? null : EaglerArrayBufferAllocator.getDataViewStupid(data));
}
public static final void _wglCopyTexSubImage2D(int target, int level, int xoffset, int yoffset,
int x, int y, int width, int height) {
ctx.copyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
}
public static final void _wglPixelStorei(int pname, int value) {
ctx.pixelStorei(pname, value);
}
public static final void _wglGenerateMipmap(int target) {
ctx.generateMipmap(target);
}
public static final void _wglShaderSource(IShaderGL obj, String source) {
ctx.shaderSource(obj == null ? null : ((OpenGLObjects.ShaderGL)obj).ptr, source);
}
public static final void _wglCompileShader(IShaderGL obj) {
ctx.compileShader(obj == null ? null : ((OpenGLObjects.ShaderGL)obj).ptr);
}
public static final int _wglGetShaderi(IShaderGL obj, int param) {
return ctx.getShaderParameteri(obj == null ? null : ((OpenGLObjects.ShaderGL)obj).ptr, param);
}
public static final String _wglGetShaderInfoLog(IShaderGL obj) {
return ctx.getShaderInfoLog(obj == null ? null : ((OpenGLObjects.ShaderGL)obj).ptr);
}
public static final void _wglUseProgram(IProgramGL obj) {
ctx.useProgram(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr);
}
public static final void _wglAttachShader(IProgramGL obj, IShaderGL shader) {
ctx.attachShader(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr,
shader == null ? null : ((OpenGLObjects.ShaderGL)shader).ptr);
}
public static final void _wglDetachShader(IProgramGL obj, IShaderGL shader) {
ctx.detachShader(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr,
shader == null ? null : ((OpenGLObjects.ShaderGL)shader).ptr);
}
public static final void _wglLinkProgram(IProgramGL obj) {
ctx.linkProgram(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr);
}
public static final int _wglGetProgrami(IProgramGL obj, int param) {
return ctx.getProgramParameteri(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr, param);
}
public static final String _wglGetProgramInfoLog(IProgramGL obj) {
return ctx.getProgramInfoLog(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr);
}
public static final void _wglBindAttribLocation(IProgramGL obj, int index, String name) {
ctx.bindAttribLocation(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr, index, name);
}
public static final int _wglGetAttribLocation(IProgramGL obj, String name) {
return ctx.getAttribLocation(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr, name);
}
public static final void _wglDrawArrays(int mode, int first, int count) {
ctx.drawArrays(mode, first, count);
}
public static final void _wglDrawArraysInstanced(int mode, int first, int count, int instanced) {
ctx.drawArraysInstanced(mode, first, count, instanced);
}
public static final void _wglDrawElements(int mode, int count, int type, int offset) {
ctx.drawElements(mode, count, type, offset);
}
public static final void _wglDrawElementsInstanced(int mode, int count, int type, int offset, int instanced) {
ctx.drawElementsInstanced(mode, count, type, offset, instanced);
}
public static final IUniformGL _wglGetUniformLocation(IProgramGL obj, String name) {
return new OpenGLObjects.UniformGL(ctx.getUniformLocation(obj == null ? null : ((OpenGLObjects.ProgramGL)obj).ptr, name));
}
public static final void _wglUniform1f(IUniformGL obj, float x) {
ctx.uniform1f(obj == null ? null : ((OpenGLObjects.UniformGL)obj).ptr, x);
}
public static final void _wglUniform2f(IUniformGL obj, float x, float y) {
ctx.uniform2f(obj == null ? null : ((OpenGLObjects.UniformGL)obj).ptr, x, y);
}
public static final void _wglUniform3f(IUniformGL obj, float x, float y, float z) {
ctx.uniform3f(obj == null ? null : ((OpenGLObjects.UniformGL)obj).ptr, x, y, z);
}
public static final void _wglUniform4f(IUniformGL obj, float x, float y, float z, float w) {
ctx.uniform4f(obj == null ? null : ((OpenGLObjects.UniformGL)obj).ptr, x, y, z, w);
}
public static final void _wglUniform1i(IUniformGL obj, int x) {
ctx.uniform1i(obj == null ? null : ((OpenGLObjects.UniformGL)obj).ptr, x);
}
public static final void _wglUniform2i(IUniformGL obj, int x, int y) {
ctx.uniform2i(obj == null ? null : ((OpenGLObjects.UniformGL)obj).ptr, x, y);
}
public static final void _wglUniform3i(IUniformGL obj, int x, int y, int z) {
ctx.uniform3i(obj == null ? null : ((OpenGLObjects.UniformGL)obj).ptr, x, y, z);
}
public static final void _wglUniform4i(IUniformGL obj, int x, int y, int z, int w) {
ctx.uniform4i(obj == null ? null : ((OpenGLObjects.UniformGL)obj).ptr, x, y, z, w);
}
public static final void _wglUniformMatrix2fv(IUniformGL obj, boolean transpose, FloatBuffer mat) {
ctx.uniformMatrix2fv(obj == null ? null : ((OpenGLObjects.UniformGL)obj).ptr, transpose,
mat == null ? null : EaglerArrayBufferAllocator.getFloatArrayStupid(mat));
}
public static final void _wglUniformMatrix3fv(IUniformGL obj, boolean transpose, FloatBuffer mat) {
ctx.uniformMatrix3fv(obj == null ? null : ((OpenGLObjects.UniformGL)obj).ptr, transpose,
mat == null ? null : EaglerArrayBufferAllocator.getFloatArrayStupid(mat));
}
public static final void _wglUniformMatrix4fv(IUniformGL obj, boolean transpose, FloatBuffer mat) {
ctx.uniformMatrix4fv(obj == null ? null : ((OpenGLObjects.UniformGL)obj).ptr, transpose,
mat == null ? null : EaglerArrayBufferAllocator.getFloatArrayStupid(mat));
}
public static final void _wglBindFramebuffer(int target, IFramebufferGL framebuffer) {
ctx.bindFramebuffer(target, framebuffer == null ? PlatformRuntime.mainFramebuffer
: ((OpenGLObjects.FramebufferGL) framebuffer).ptr);
}
public static final int _wglCheckFramebufferStatus(int target) {
return ctx.checkFramebufferStatus(target);
}
public static final void _wglFramebufferTexture2D(int target, int attachment, int texTarget,
ITextureGL texture, int level) {
ctx.framebufferTexture2D(target, attachment, texTarget,
texture == null ? null : ((OpenGLObjects.TextureGL)texture).ptr, level);
}
public static final void _wglBlitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1,
int dstX0, int dstY0, int dstX1, int dstY1, int bits, int filter) {
ctx.blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, bits, filter);
}
public static final void _wglBindRenderbuffer(int target, IRenderbufferGL renderbuffer) {
ctx.bindRenderbuffer(target,
renderbuffer == null ? null : ((OpenGLObjects.RenderbufferGL)renderbuffer).ptr);
}
public static final void _wglRenderbufferStorage(int target, int internalformat,
int width, int height) {
ctx.renderbufferStorage(target, internalformat, width, height);
}
public static final void _wglFramebufferRenderbuffer(int target, int attachment,
int renderbufferTarget, IRenderbufferGL renderbuffer) {
ctx.framebufferRenderbuffer(target, attachment, renderbufferTarget,
((OpenGLObjects.RenderbufferGL)renderbuffer).ptr);
}
public static final String _wglGetString(int param) {
if(hasDebugRenderInfoExt) {
String s;
switch(param) {
case 0x1f00: // VENDOR
s = ctx.getParameterString(0x9245); // UNMASKED_VENDOR_WEBGL
if(s == null) {
s = ctx.getParameterString(0x1f00); // VENDOR
}
return s;
case 0x1f01: // RENDERER
s = ctx.getParameterString(0x9246); // UNMASKED_RENDERER_WEBGL
if(s == null) {
s = ctx.getParameterString(0x1f01); // RENDERER
}
return s;
default:
return ctx.getParameterString(param);
}
}else {
return ctx.getParameterString(param);
}
}
public static final int _wglGetInteger(int param) {
return ctx.getParameteri(param);
}
public static final int _wglGetError() {
return ctx.getError();
}
}

View File

@ -0,0 +1,560 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.function.Consumer;
import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion;
import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile;
import org.teavm.interop.Async;
import org.teavm.interop.AsyncCallback;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSExceptions;
import org.teavm.jso.JSFunctor;
import org.teavm.jso.JSObject;
import org.teavm.jso.ajax.XMLHttpRequest;
import org.teavm.jso.browser.Window;
import org.teavm.jso.canvas.CanvasRenderingContext2D;
import org.teavm.jso.core.JSError;
import org.teavm.jso.dom.css.CSSStyleDeclaration;
import org.teavm.jso.dom.events.Event;
import org.teavm.jso.dom.events.EventListener;
import org.teavm.jso.dom.html.HTMLAnchorElement;
import org.teavm.jso.dom.html.HTMLCanvasElement;
import org.teavm.jso.dom.html.HTMLDocument;
import org.teavm.jso.dom.html.HTMLElement;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.webaudio.MediaStream;
import org.teavm.jso.webgl.WebGLFramebuffer;
import com.jcraft.jzlib.DeflaterOutputStream;
import com.jcraft.jzlib.GZIPInputStream;
import com.jcraft.jzlib.GZIPOutputStream;
import com.jcraft.jzlib.InflaterInputStream;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.EaglerArrayBufferAllocator;
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.teavm.EPKLoader;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.EarlyLoadScreen;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.MainClass;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.MainClass.EPKFileEntry;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.WebGL2RenderingContext;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class PlatformRuntime {
static final Logger logger = LogManager.getLogger("BrowserRuntime");
public static Window win = null;
public static HTMLDocument doc = null;
public static HTMLElement parent = null;
public static HTMLCanvasElement canvas = null;
public static WebGL2RenderingContext webgl = null;
static WebGLFramebuffer mainFramebuffer = null;
public static void create() {
win = Window.current();
doc = win.getDocument();
logger.info("Creating main game canvas");
parent = doc.getElementById(MainClass.configRootElement);
if(parent == null) {
throw new RuntimeInitializationFailureException("Root element \"" + MainClass.configRootElement + "\" was not found in this document!");
}
CSSStyleDeclaration style = parent.getStyle();
style.setProperty("overflowX", "hidden");
style.setProperty("overflowY", "hidden");
canvas = (HTMLCanvasElement) doc.createElement("canvas");
style = canvas.getStyle();
style.setProperty("width", "100%");
style.setProperty("height", "100%");
style.setProperty("image-rendering", "pixelated");
double r = win.getDevicePixelRatio();
int iw = parent.getClientWidth();
int ih = parent.getClientHeight();
int sw = (int)(r * iw);
int sh = (int)(r * ih);
canvas.setWidth(sw);
canvas.setHeight(sh);
parent.appendChild(canvas);
try {
PlatformInput.initHooks(win, canvas);
}catch(Throwable t) {
throw new RuntimeInitializationFailureException("Exception while registering window event handlers", t);
}
try {
doc.exitPointerLock();
}catch(Throwable t) {
throw new PlatformIncompatibleException("Mouse cursor lock is not available on this device!");
}
logger.info("Creating WebGL context");
JSObject webgl_ = canvas.getContext("webgl2", youEagler());
if(webgl_ == null) {
throw new PlatformIncompatibleException("WebGL 2.0 is not supported on this device!");
}
webgl = (WebGL2RenderingContext) webgl_;
PlatformOpenGL.setCurrentContext(webgl);
mainFramebuffer = webgl.createFramebuffer();
PlatformInput.initFramebuffer(webgl, mainFramebuffer, sw, sh);
EarlyLoadScreen.paintScreen();
EPKFileEntry[] epkFiles = MainClass.configEPKFiles;
for(int i = 0; i < epkFiles.length; ++i) {
String url = epkFiles[i].url;
String logURL = url.startsWith("data:") ? "<data: " + url.length() + " chars>" : url;
logger.info("Downloading: {}", logURL);
ArrayBuffer epkFileData = downloadRemoteURI(url);
if(epkFileData == null) {
throw new RuntimeInitializationFailureException("Could not download EPK file \"" + url + "\"");
}
logger.info("Decompressing: {}", logURL);
try {
EPKLoader.loadEPK(epkFileData, epkFiles[i].path, PlatformAssets.assets);
}catch(Throwable t) {
throw new RuntimeInitializationFailureException("Could not extract EPK file \"" + url + "\"", t);
}
}
logger.info("Loaded {} resources from EPKs", PlatformAssets.assets.size());
byte[] finalLoadScreen = PlatformAssets.getResourceBytes("/assets/eagler/eagtek.png");
logger.info("Initializing sound engine...");
PlatformInput.pressAnyKeyScreen();
PlatformAudio.initialize();
if(finalLoadScreen != null) {
EarlyLoadScreen.paintFinal(finalLoadScreen);
}
logger.info("Platform initialization complete");
}
@JSBody(params = { }, script = "return {antialias: false, depth: false, powerPreference: \"high-performance\", desynchronized: true, preserveDrawingBuffer: false, premultipliedAlpha: false, alpha: false};")
public static native JSObject youEagler();
public static class RuntimeInitializationFailureException extends IllegalStateException {
public RuntimeInitializationFailureException(String message, Throwable cause) {
super(message, cause);
}
public RuntimeInitializationFailureException(String s) {
super(s);
}
}
public static class PlatformIncompatibleException extends IllegalStateException {
public PlatformIncompatibleException(String s) {
super(s);
}
}
public static void destroy() {
logger.fatal("Game tried to destroy the context! Browser runtime can't do that");
}
public static EnumPlatformType getPlatformType() {
return EnumPlatformType.JAVASCRIPT;
}
public static EnumPlatformAgent getPlatformAgent() {
return EnumPlatformAgent.getFromUA(getUserAgentString());
}
@JSBody(params = { }, script = "return window.navigator.userAgent;")
public static native String getUserAgentString();
public static EnumPlatformOS getPlatformOS() {
return EnumPlatformOS.getFromUA(getUserAgentString());
}
public static void requestANGLE(EnumPlatformANGLE plaf) {
}
public static EnumPlatformANGLE getPlatformANGLE() {
return EnumPlatformANGLE.fromGLRendererString(getGLRenderer());
}
public static String getGLVersion() {
return PlatformOpenGL._wglGetString(RealOpenGLEnums.GL_VERSION);
}
public static String getGLRenderer() {
return PlatformOpenGL._wglGetString(RealOpenGLEnums.GL_RENDERER);
}
public static ByteBuffer allocateByteBuffer(int length) {
return EaglerArrayBufferAllocator.allocateByteBuffer(length);
}
public static IntBuffer allocateIntBuffer(int length) {
return EaglerArrayBufferAllocator.allocateIntBuffer(length);
}
public static FloatBuffer allocateFloatBuffer(int length) {
return EaglerArrayBufferAllocator.allocateFloatBuffer(length);
}
public static void freeByteBuffer(ByteBuffer byteBuffer) {
}
public static void freeIntBuffer(IntBuffer intBuffer) {
}
public static void freeFloatBuffer(FloatBuffer floatBuffer) {
}
public static void downloadRemoteURI(String assetPackageURI, final Consumer<ArrayBuffer> cb) {
downloadRemoteURI(assetPackageURI, new AsyncCallback<ArrayBuffer>() {
@Override
public void complete(ArrayBuffer result) {
cb.accept(result);
}
@Override
public void error(Throwable e) {
e.printStackTrace();
cb.accept(null);
}
});
}
@Async
public static native ArrayBuffer downloadRemoteURI(String assetPackageURI);
private static void downloadRemoteURI(String assetPackageURI, final AsyncCallback<ArrayBuffer> cb) {
final XMLHttpRequest request = XMLHttpRequest.create();
request.setResponseType("arraybuffer");
request.open("GET", assetPackageURI, true);
TeaVMUtils.addEventListener(request, "load", new EventListener<Event>() {
@Override
public void handleEvent(Event evt) {
int stat = request.getStatus();
if(stat == 0 || (stat >= 200 && stat < 400)) {
cb.complete((ArrayBuffer)request.getResponse());
}else {
cb.complete(null);
}
}
});
TeaVMUtils.addEventListener(request, "error", new EventListener<Event>() {
@Override
public void handleEvent(Event evt) {
cb.complete(null);
}
});
request.send();
}
public static boolean isDebugRuntime() {
return false;
}
public static void writeCrashReport(String crashDump) {
MainClass.showCrashScreen(crashDump);
}
public static void removeEventHandlers() {
try {
PlatformInput.removeEventHandlers();
}catch(Throwable t) {
}
}
public static void getStackTrace(Throwable t, Consumer<String> ret) {
JSObject o = JSExceptions.getJSException(t);
if(o != null) {
try {
JSError err = o.cast();
String stack = err.getStack();
if(stack != null) {
String[] stackElements = stack.split("[\\r\\n]+");
if(stackElements.length > 0) {
for(int i = 0; i < stackElements.length; ++i) {
String str = stackElements[i].trim();
if(str.startsWith("at ")) {
str = str.substring(3).trim();
}
ret.accept(str);
}
return;
}
}
}catch(Throwable tt) {
ret.accept("[ error: " + t.toString() + " ]");
}
}
getFallbackStackTrace(t, ret);
}
private static void getFallbackStackTrace(Throwable t, Consumer<String> ret) {
StackTraceElement[] el = t.getStackTrace();
if(el.length > 0) {
for(int i = 0; i < el.length; ++i) {
ret.accept(el[i].toString());
}
}else {
ret.accept("[no stack trace]");
}
}
@JSBody(params = { "o" }, script = "console.error(o);")
private static native void printNativeExceptionToConsole(JSObject o);
public static boolean printJSExceptionIfBrowser(Throwable t) {
if(t != null) {
JSObject o = JSExceptions.getJSException(t);
if(o != null) {
printNativeExceptionToConsole(o);
return true;
}
}
return false;
}
public static void exit() {
logger.fatal("Game is attempting to exit!");
}
public static void setThreadName(String string) {
// no teavm support
}
public static long maxMemory() {
return 1073741824l;
}
public static long totalMemory() {
return 1073741824l;
}
public static long freeMemory() {
return 1073741824l;
}
public static String getCallingClass(int backTrace) {
return null;
}
public static OutputStream newDeflaterOutputStream(OutputStream os) throws IOException {
return new DeflaterOutputStream(os);
}
public static OutputStream newGZIPOutputStream(OutputStream os) throws IOException {
return new GZIPOutputStream(os);
}
public static InputStream newInflaterInputStream(InputStream is) throws IOException {
return new InflaterInputStream(is);
}
public static InputStream newGZIPInputStream(InputStream is) throws IOException {
return new GZIPInputStream(is);
}
@JSBody(params = { }, script = "return window.location.protocol && window.location.protocol.toLowerCase().startsWith(\"https\");")
public static native boolean requireSSL();
public static IClientConfigAdapter getClientConfigAdapter() {
return TeaVMClientConfigAdapter.instance;
}
private static boolean canRec = false;
private static boolean recording = false;
private static JSObject mediaRec = null;
private static HTMLCanvasElement recCanvas = null;
private static CanvasRenderingContext2D recCtx = null;
private static MediaStream recStream = null;
public static boolean isRec() {
return recording && canRec;
}
@JSBody(params = { "canvas", "audio" }, script = "const stream = canvas.captureStream(); stream.addTrack(audio.getTracks()[0]); return stream;")
private static native MediaStream captureStreamAndAddAudio(HTMLCanvasElement canvas, MediaStream audio);
@JSBody(params = { "stream" }, script = "const rec = new MediaRecorder(stream, { mimeType: MediaRecorder.isTypeSupported(\"video/webm;codecs=vp9,opus\") ? \"video/webm;codecs=vp9,opus\" : \"video/webm\" }); rec.start(); return rec;")
private static native JSObject createMediaRecorder(MediaStream stream);
@JSBody(params = { "rec" }, script = "rec.stop();")
private static native void stopRec(JSObject rec);
@JSBody(params = { }, script = "return \"MediaRecorder\" in window;")
private static native boolean canRec();
@JSFunctor
private static interface RecUrlHandler extends JSObject {
void onUrl(String url);
}
@JSBody(params = { "e", "duration", "cb" }, script = "if (\"ysFixWebmDuration\" in window) { ysFixWebmDuration(e.data, duration, function(b) { cb(URL.createObjectURL(b)); }); } else { cb(URL.createObjectURL(e.data)); }")
private static native void getRecUrl(Event e, int duration, RecUrlHandler cb);
public static boolean recSupported() {
return true;
}
public static String getRecText() {
if (recording && !canRec) {
return "recording.unsupported";
}
return recording ? "recording.stop" : "recording.start";
}
private static void recFrame() {
if (mediaRec != null) {
int w = PlatformRuntime.canvas.getWidth();
int h = PlatformRuntime.canvas.getHeight();
if (recCanvas.getWidth() != w || recCanvas.getHeight() != h) {
recCanvas.setWidth(w);
recCanvas.setHeight(h);
}
recCtx.drawImage(canvas, 0, 0);
}
}
private static void onRecFrame() {
if (recording) {
recFrame();
long t = System.currentTimeMillis();
Window.requestAnimationFrame(timestamp -> {
long d = (1000 / 30) - (System.currentTimeMillis() - t);
if (d <= 0) {
onRecFrame();
} else {
Window.setTimeout(PlatformRuntime::onRecFrame, d);
}
});
}
}
@JSFunctor
private static interface MediaHandler extends JSObject {
void onMedia(MediaStream stream);
}
@JSBody(params = { "cb" }, script = "if (\"navigator\" in window && \"mediaDevices\" in window.navigator && \"getUserMedia\" in window.navigator.mediaDevices) { try { window.navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(function(stream) { cb(stream); }).catch(function(err) { console.error(err); cb(null); }); } catch(e) { console.error(\"getUserMedia Error!\"); cb(null); } } else { console.error(\"No getUserMedia!\"); cb(null); }")
private static native void getMic0(MediaHandler cb);
@Async
private static native MediaStream getMic1();
private static void getMic1(AsyncCallback<MediaStream> cb) {
getMic0(cb::complete);
}
private static boolean canMic = true;
private static MediaStream mic = null;
protected static MediaStream getMic() {
if (canMic) {
if (mic == null) {
mic = getMic1();
if (mic == null) {
canMic = false;
return null;
}
return mic;
}
return mic;
}
return null;
}
private static final SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd hh-mm-ss");
private static final Date dateInstance = new Date();
public static void toggleRec() {
if (recording && !canRec) {
return;
}
recording = !recording;
if (recording) {
if (!canRec) {
canRec = canRec();
if (!canRec) {
return;
}
}
if (recCanvas == null) {
recCanvas = (HTMLCanvasElement) Window.current().getDocument().createElement("canvas");
recCtx = (CanvasRenderingContext2D) recCanvas.getContext("2d");
PlatformAudio.initRecDest();
recStream = captureStreamAndAddAudio(recCanvas, PlatformAudio.getRecStream());
}
mediaRec = createMediaRecorder(recStream);
long startTime = System.currentTimeMillis();
TeaVMUtils.addEventListener(mediaRec, "dataavailable", new EventListener<Event>() {
@Override
public void handleEvent(Event evt) {
getRecUrl(evt, (int) (System.currentTimeMillis() - startTime), url -> {
HTMLAnchorElement a = (HTMLAnchorElement) doc.createElement("a");
dateInstance.setTime(startTime);
a.setDownload(EaglercraftVersion.mainMenuStringB + " - " + EaglerProfile.getName() + " - " + fmt.format(dateInstance) + ".webm");
a.setHref(url);
a.click();
});
}
});
onRecFrame();
} else {
stopRec(mediaRec);
mediaRec = null;
}
}
}

View File

@ -0,0 +1,148 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.DataView;
import org.teavm.jso.typedarrays.Float32Array;
import org.teavm.jso.typedarrays.Uint8Array;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class EaglerArrayBufferAllocator {
public static class WrongBufferClassType extends RuntimeException {
public WrongBufferClassType(String msg) {
super(msg);
}
}
public static ByteBuffer allocateByteBuffer(int size) {
return new EaglerArrayByteBuffer(DataView.create(ArrayBuffer.create(size)));
}
public static IntBuffer allocateIntBuffer(int size) {
return new EaglerArrayIntBuffer(DataView.create(ArrayBuffer.create(size << 2)));
}
public static FloatBuffer allocateFloatBuffer(int size) {
return new EaglerArrayFloatBuffer(DataView.create(ArrayBuffer.create(size << 2)));
}
public static DataView getDataView(ByteBuffer buffer) {
if(buffer instanceof EaglerArrayByteBuffer) {
EaglerArrayByteBuffer b = (EaglerArrayByteBuffer)buffer;
DataView d = b.dataView;
int p = b.position;
int l = b.limit;
if(p == 0 && l == b.capacity) {
return d;
}else {
int i = d.getByteOffset();
return DataView.create(d.getBuffer(), i + p, l - p);
}
}else {
throw notEagler(buffer);
}
}
public static Uint8Array getDataViewStupid(ByteBuffer buffer) {
if(buffer instanceof EaglerArrayByteBuffer) {
EaglerArrayByteBuffer b = (EaglerArrayByteBuffer)buffer;
DataView d = b.dataView;
int p = b.position;
int l = b.limit;
int i = d.getByteOffset();
return Uint8Array.create(d.getBuffer(), i + p, l - p);
}else {
throw notEagler(buffer);
}
}
public static DataView getDataView(IntBuffer buffer) {
if(buffer instanceof EaglerArrayIntBuffer) {
EaglerArrayIntBuffer b = (EaglerArrayIntBuffer)buffer;
DataView d = b.dataView;
int p = b.position;
int l = b.limit;
if(p == 0 && l == b.capacity) {
return d;
}else {
int i = d.getByteOffset();
return DataView.create(d.getBuffer(), i + (p << 2), (l - p) << 2);
}
}else {
throw notEagler(buffer);
}
}
public static Uint8Array getDataViewStupid(IntBuffer buffer) {
if(buffer instanceof EaglerArrayIntBuffer) {
EaglerArrayIntBuffer b = (EaglerArrayIntBuffer)buffer;
DataView d = b.dataView;
int p = b.position;
int l = b.limit;
int i = d.getByteOffset();
return Uint8Array.create(d.getBuffer(), i + (p << 2), (l - p) << 2);
}else {
throw notEagler(buffer);
}
}
public static DataView getDataView(FloatBuffer buffer) {
if(buffer instanceof EaglerArrayFloatBuffer) {
EaglerArrayFloatBuffer b = (EaglerArrayFloatBuffer)buffer;
DataView d = b.dataView;
int p = b.position;
int l = b.limit;
if(p == 0 && l == b.capacity) {
return d;
}else {
int i = d.getByteOffset();
return DataView.create(d.getBuffer(), i + (p << 2), (l - p) << 2);
}
}else {
throw notEagler(buffer);
}
}
public static Uint8Array getDataViewStupid(FloatBuffer buffer) {
if(buffer instanceof EaglerArrayFloatBuffer) {
EaglerArrayFloatBuffer b = (EaglerArrayFloatBuffer)buffer;
DataView d = b.dataView;
int p = b.position;
int l = b.limit;
int i = d.getByteOffset();
return Uint8Array.create(d.getBuffer(), i + (p << 2), (l - p) << 2);
}else {
throw notEagler(buffer);
}
}
public static Float32Array getFloatArrayStupid(FloatBuffer buffer) {
if(buffer instanceof EaglerArrayFloatBuffer) {
EaglerArrayFloatBuffer b = (EaglerArrayFloatBuffer)buffer;
DataView d = b.dataView;
int p = b.position;
int l = b.limit;
int i = d.getByteOffset();
return Float32Array.create(d.getBuffer(), i + p, l - p);
}else {
throw notEagler(buffer);
}
}
private static WrongBufferClassType notEagler(Object clazz) {
return new WrongBufferClassType("Tried to pass a " + clazz.getClass().getSimpleName() + " which is not a native eagler buffer");
}
}

View File

@ -0,0 +1,439 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.DataView;
import org.teavm.jso.typedarrays.Uint8Array;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class EaglerArrayByteBuffer implements ByteBuffer {
final DataView dataView;
final int capacity;
int position;
int limit;
int mark;
static final DataView ZERO_LENGTH_BUFFER = DataView.create(ArrayBuffer.create(0));
EaglerArrayByteBuffer(DataView dataView) {
this.dataView = dataView;
this.capacity = dataView.getByteLength();
this.position = 0;
this.limit = this.capacity;
this.mark = -1;
}
EaglerArrayByteBuffer(DataView dataView, int position, int limit, int mark) {
this.dataView = dataView;
this.capacity = dataView.getByteLength();
this.position = position;
this.limit = limit;
this.mark = mark;
}
@Override
public int capacity() {
return capacity;
}
@Override
public int position() {
return position;
}
@Override
public int limit() {
return limit;
}
@Override
public int remaining() {
return limit - position;
}
@Override
public boolean hasRemaining() {
return limit > position;
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public boolean hasArray() {
return false;
}
@Override
public Object array() {
throw new UnsupportedOperationException();
}
@Override
public boolean isDirect() {
return true;
}
@Override
public ByteBuffer slice() {
int o = dataView.getByteOffset();
return new EaglerArrayByteBuffer(DataView.create(dataView.getBuffer(), o + position, limit - position));
}
@Override
public ByteBuffer duplicate() {
return new EaglerArrayByteBuffer(dataView, position, limit, mark);
}
@Override
public ByteBuffer asReadOnlyBuffer() {
return new EaglerArrayByteBuffer(dataView, position, limit, mark);
}
@Override
public byte get() {
if(position >= limit) throw new ArrayIndexOutOfBoundsException(position);
return dataView.getInt8(position++);
}
@Override
public ByteBuffer put(byte b) {
if(position >= limit) throw new ArrayIndexOutOfBoundsException(position);
dataView.setInt8(position++, b);
return this;
}
@Override
public byte get(int index) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
return dataView.getInt8(index);
}
@Override
public ByteBuffer put(int index, byte b) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
dataView.setInt8(index, b);
return this;
}
@Override
public ByteBuffer get(byte[] dst, int offset, int length) {
if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1);
for(int i = 0; i < length; ++i) {
dst[offset + i] = dataView.getInt8(position + i);
}
position += length;
return this;
}
@Override
public ByteBuffer get(byte[] dst) {
if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1);
for(int i = 0; i < dst.length; ++i) {
dst[position + i] = dataView.getInt8(position + i);
}
position += dst.length;
return this;
}
@Override
public ByteBuffer put(ByteBuffer src) {
if(src instanceof EaglerArrayByteBuffer) {
EaglerArrayByteBuffer c = (EaglerArrayByteBuffer)src;
int l = c.limit - c.position;
if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1);
int o = c.dataView.getByteOffset();
Uint8Array.create(dataView.getBuffer()).set(
Uint8Array.create(c.dataView.getBuffer(), o + c.position, c.limit - c.position),
dataView.getByteOffset() + position);
position += l;
c.position += l;
}else {
int l = src.remaining();
if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1);
for(int i = 0; i < l; ++i) {
dataView.setInt8(position + l, src.get());
}
position += l;
}
return this;
}
@Override
public ByteBuffer put(byte[] src, int offset, int length) {
if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1);
for(int i = 0; i < length; ++i) {
dataView.setInt8(position + i, src[offset + i]);
}
position += length;
return this;
}
@Override
public ByteBuffer put(byte[] src) {
if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1);
dataView.set(src, position);
position += src.length;
return this;
}
@Override
public int arrayOffset() {
return position;
}
@Override
public ByteBuffer compact() {
if(limit > capacity) throw new ArrayIndexOutOfBoundsException(limit);
if(position > limit) throw new ArrayIndexOutOfBoundsException(position);
if(position == limit) {
return new EaglerArrayByteBuffer(ZERO_LENGTH_BUFFER);
}
int o = dataView.getByteOffset();
Uint8Array dst = Uint8Array.create(ArrayBuffer.create(limit - position));
dst.set(Uint8Array.create(dataView.getBuffer(), o + position, limit - position));
return new EaglerArrayByteBuffer(DataView.create(dst.getBuffer()));
}
@Override
public char getChar() {
if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position);
char c = (char)dataView.getUint16(position, true);
position += 2;
return c;
}
@Override
public ByteBuffer putChar(char value) {
if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position);
dataView.setUint16(position, (short)value, true);
position += 2;
return this;
}
@Override
public char getChar(int index) {
if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index);
return (char)dataView.getUint16(index, true);
}
@Override
public ByteBuffer putChar(int index, char value) {
if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index);
dataView.setUint16(index, value, true);
return this;
}
@Override
public short getShort() {
if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position);
short s = dataView.getInt16(position, true);
position += 2;
return s;
}
@Override
public ByteBuffer putShort(short value) {
if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position);
dataView.setInt16(position, value, true);
position += 2;
return this;
}
@Override
public short getShort(int index) {
if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index);
return dataView.getInt16(index, true);
}
@Override
public ByteBuffer putShort(int index, short value) {
if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index);
dataView.setInt16(index, value, true);
return this;
}
@Override
public ShortBuffer asShortBuffer() {
return new EaglerArrayShortBuffer(dataView);
}
@Override
public int getInt() {
if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position);
int i = dataView.getInt32(position, true);
position += 4;
return i;
}
@Override
public ByteBuffer putInt(int value) {
if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position);
dataView.setInt32(position, value, true);
position += 4;
return this;
}
@Override
public int getInt(int index) {
if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index);
return dataView.getInt32(index, true);
}
@Override
public ByteBuffer putInt(int index, int value) {
if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index);
dataView.setInt32(index, value, true);
return this;
}
@Override
public IntBuffer asIntBuffer() {
return new EaglerArrayIntBuffer(dataView);
}
@Override
public long getLong() {
if(position + 8 > limit) throw new ArrayIndexOutOfBoundsException(position);
long l = dataView.getUint32(position) | ((long) dataView.getUint8(position + 4) << 32)
| ((long) dataView.getUint8(position + 5) << 40) | ((long) dataView.getUint8(position + 6) << 48)
| ((long) dataView.getUint8(position + 7) << 56);
position += 8;
return l;
}
@Override
public ByteBuffer putLong(long value) {
if(position + 8 > limit) throw new ArrayIndexOutOfBoundsException(position);
dataView.setUint32(position, (int) (value & 0xFFFFFFFF), true);
dataView.setUint8(position + 4, (short) ((value >> 32) & 0xFF));
dataView.setUint8(position + 5, (short) ((value >> 40) & 0xFF));
dataView.setUint8(position + 6, (short) ((value >> 48) & 0xFF));
dataView.setUint8(position + 7, (short) ((value >> 56) & 0xFF));
position += 8;
return this;
}
@Override
public long getLong(int index) {
if(index + 8 > limit) throw new ArrayIndexOutOfBoundsException(index);
return dataView.getUint32(index, true) | ((long) dataView.getUint8(index + 4) << 32)
| ((long) dataView.getUint8(index + 5) << 40) | ((long) dataView.getUint8(index + 6) << 48)
| ((long) dataView.getUint8(index + 7) << 56);
}
@Override
public ByteBuffer putLong(int index, long value) {
if(index + 8 > limit) throw new ArrayIndexOutOfBoundsException(index);
dataView.setUint32(index, (int) (value & 0xFFFFFFFF), true);
dataView.setUint8(index + 4, (short) ((value >> 32) & 0xFF));
dataView.setUint8(index + 5, (short) ((value >> 40) & 0xFF));
dataView.setUint8(index + 6, (short) ((value >> 48) & 0xFF));
dataView.setUint8(index + 7, (short) ((value >> 56) & 0xFF));
return this;
}
@Override
public float getFloat() {
if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position);
float f = dataView.getFloat32(position, true);
position += 4;
return f;
}
@Override
public ByteBuffer putFloat(float value) {
if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position);
dataView.setFloat32(position, value, true);
position += 4;
return this;
}
@Override
public float getFloat(int index) {
if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index);
return dataView.getFloat32(index, true);
}
@Override
public ByteBuffer putFloat(int index, float value) {
if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index);
dataView.setFloat32(index, value, true);
return this;
}
@Override
public FloatBuffer asFloatBuffer() {
return new EaglerArrayFloatBuffer(dataView);
}
@Override
public ByteBuffer mark() {
mark = position;
return this;
}
@Override
public ByteBuffer reset() {
int m = mark;
if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m);
position = m;
return this;
}
@Override
public ByteBuffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
@Override
public ByteBuffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
@Override
public ByteBuffer rewind() {
position = 0;
mark = -1;
return this;
}
@Override
public ByteBuffer limit(int newLimit) {
if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit);
limit = newLimit;
return this;
}
@Override
public ByteBuffer position(int newPosition) {
if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition);
position = newPosition;
return this;
}
}

View File

@ -0,0 +1,287 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.DataView;
import org.teavm.jso.typedarrays.Uint8Array;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class EaglerArrayFloatBuffer implements FloatBuffer {
final DataView dataView;
final int capacity;
int position;
int limit;
int mark;
private static final int SHIFT = 2;
EaglerArrayFloatBuffer(DataView dataView) {
this.dataView = dataView;
this.capacity = dataView.getByteLength() >> SHIFT;
this.position = 0;
this.limit = this.capacity;
this.mark = -1;
}
EaglerArrayFloatBuffer(DataView dataView, int position, int limit, int mark) {
this.dataView = dataView;
this.capacity = dataView.getByteLength() >> SHIFT;
this.position = position;
this.limit = limit;
this.mark = mark;
}
@Override
public int capacity() {
return capacity;
}
@Override
public int position() {
return position;
}
@Override
public int limit() {
return limit;
}
@Override
public int remaining() {
return limit - position;
}
@Override
public boolean hasRemaining() {
return position < limit;
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public boolean hasArray() {
return false;
}
@Override
public Object array() {
throw new UnsupportedOperationException();
}
@Override
public int arrayOffset() {
return position;
}
@Override
public FloatBuffer slice() {
int o = dataView.getByteOffset();
return new EaglerArrayFloatBuffer(DataView.create(dataView.getBuffer(), o + (position << SHIFT), (limit - position) << SHIFT));
}
@Override
public FloatBuffer duplicate() {
return new EaglerArrayFloatBuffer(dataView, position, limit, mark);
}
@Override
public FloatBuffer asReadOnlyBuffer() {
return new EaglerArrayFloatBuffer(dataView, position, limit, mark);
}
@Override
public float get() {
if(position >= limit) throw new ArrayIndexOutOfBoundsException(position);
return dataView.getFloat32((position++) << SHIFT, true);
}
@Override
public FloatBuffer put(float b) {
if(position >= limit) throw new ArrayIndexOutOfBoundsException(position);
dataView.setFloat32((position++) << SHIFT, b, true);
return this;
}
@Override
public float get(int index) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
return dataView.getFloat32(index << SHIFT, true);
}
@Override
public FloatBuffer put(int index, float b) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
dataView.setFloat32(index << SHIFT, b, true);
return this;
}
@Override
public float getElement(int index) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
return dataView.getFloat32(index << SHIFT, true);
}
@Override
public void putElement(int index, float value) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
dataView.setFloat32(index << SHIFT, value, true);
}
@Override
public FloatBuffer get(float[] dst, int offset, int length) {
if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1);
for(int i = 0; i < length; ++i) {
dst[offset + i] = dataView.getFloat32((position + i) << SHIFT, true);
}
position += length;
return this;
}
@Override
public FloatBuffer get(float[] dst) {
if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1);
for(int i = 0; i < dst.length; ++i) {
dst[i] = dataView.getFloat32((position + i) << SHIFT, true);
}
position += dst.length;
return this;
}
@Override
public FloatBuffer put(FloatBuffer src) {
if(src instanceof EaglerArrayFloatBuffer) {
EaglerArrayFloatBuffer c = (EaglerArrayFloatBuffer)src;
int l = c.limit - c.position;
if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1);
int o = c.dataView.getByteOffset();
Uint8Array.create(dataView.getBuffer()).set(
Uint8Array.create(c.dataView.getBuffer(), o + (c.position << SHIFT), (c.limit - c.position) << SHIFT),
dataView.getByteOffset() + (position << SHIFT));
position += l;
c.position += l;
}else {
int l = src.remaining();
if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1);
for(int i = 0; i < l; ++i) {
dataView.setFloat32((position + l) << SHIFT, src.get(), true);
}
position += l;
}
return this;
}
@Override
public FloatBuffer put(float[] src, int offset, int length) {
if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1);
for(int i = 0; i < length; ++i) {
dataView.setFloat32((position + i) << SHIFT, src[offset + i], true);
}
position += length;
return this;
}
@Override
public FloatBuffer put(float[] src) {
if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1);
for(int i = 0; i < src.length; ++i) {
dataView.setFloat32((position + i) << SHIFT, src[i], true);
}
position += src.length;
return this;
}
@Override
public int getArrayOffset() {
return position;
}
@Override
public FloatBuffer compact() {
if(limit > capacity) throw new ArrayIndexOutOfBoundsException(limit);
if(position > limit) throw new ArrayIndexOutOfBoundsException(position);
if(position == limit) {
return new EaglerArrayFloatBuffer(EaglerArrayByteBuffer.ZERO_LENGTH_BUFFER);
}
int o = dataView.getByteOffset();
Uint8Array dst = Uint8Array.create(ArrayBuffer.create((limit - position) << SHIFT));
dst.set(Uint8Array.create(dataView.getBuffer(), o + (position << SHIFT), (limit - position) << SHIFT));
return new EaglerArrayFloatBuffer(DataView.create(dst.getBuffer()));
}
@Override
public boolean isDirect() {
return true;
}
@Override
public FloatBuffer mark() {
mark = position;
return this;
}
@Override
public FloatBuffer reset() {
int m = mark;
if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m);
position = m;
return this;
}
@Override
public FloatBuffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
@Override
public FloatBuffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
@Override
public FloatBuffer rewind() {
position = 0;
mark = -1;
return this;
}
@Override
public FloatBuffer limit(int newLimit) {
if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit);
limit = newLimit;
return this;
}
@Override
public FloatBuffer position(int newPosition) {
if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition);
position = newPosition;
return this;
}
}

View File

@ -0,0 +1,287 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.DataView;
import org.teavm.jso.typedarrays.Uint8Array;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class EaglerArrayIntBuffer implements IntBuffer {
final DataView dataView;
final int capacity;
int position;
int limit;
int mark;
private static final int SHIFT = 2;
EaglerArrayIntBuffer(DataView dataView) {
this.dataView = dataView;
this.capacity = dataView.getByteLength() >> SHIFT;
this.position = 0;
this.limit = this.capacity;
this.mark = -1;
}
EaglerArrayIntBuffer(DataView dataView, int position, int limit, int mark) {
this.dataView = dataView;
this.capacity = dataView.getByteLength() >> SHIFT;
this.position = position;
this.limit = limit;
this.mark = mark;
}
@Override
public int capacity() {
return capacity;
}
@Override
public int position() {
return position;
}
@Override
public int limit() {
return limit;
}
@Override
public int remaining() {
return limit - position;
}
@Override
public boolean hasRemaining() {
return position < limit;
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public boolean hasArray() {
return false;
}
@Override
public Object array() {
throw new UnsupportedOperationException();
}
@Override
public int arrayOffset() {
return position;
}
@Override
public IntBuffer slice() {
int o = dataView.getByteOffset();
return new EaglerArrayIntBuffer(DataView.create(dataView.getBuffer(), o + (position << SHIFT), (limit - position) << SHIFT));
}
@Override
public IntBuffer duplicate() {
return new EaglerArrayIntBuffer(dataView, position, limit, mark);
}
@Override
public IntBuffer asReadOnlyBuffer() {
return new EaglerArrayIntBuffer(dataView, position, limit, mark);
}
@Override
public int get() {
if(position >= limit) throw new ArrayIndexOutOfBoundsException(position);
return dataView.getInt32((position++) << SHIFT, true);
}
@Override
public IntBuffer put(int b) {
if(position >= limit) throw new ArrayIndexOutOfBoundsException(position);
dataView.setInt32((position++) << SHIFT, b, true);
return this;
}
@Override
public int get(int index) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
return dataView.getInt32(index << SHIFT, true);
}
@Override
public IntBuffer put(int index, int b) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
dataView.setInt32(index << SHIFT, b, true);
return this;
}
@Override
public int getElement(int index) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
return dataView.getInt32(index << SHIFT, true);
}
@Override
public void putElement(int index, int value) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
dataView.setInt32(index << SHIFT, value, true);
}
@Override
public IntBuffer get(int[] dst, int offset, int length) {
if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1);
for(int i = 0; i < length; ++i) {
dst[offset + i] = dataView.getInt32((position + i) << SHIFT, true);
}
position += length;
return this;
}
@Override
public IntBuffer get(int[] dst) {
if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1);
for(int i = 0; i < dst.length; ++i) {
dst[i] = dataView.getInt32((position + i) << SHIFT, true);
}
position += dst.length;
return this;
}
@Override
public IntBuffer put(IntBuffer src) {
if(src instanceof EaglerArrayIntBuffer) {
EaglerArrayIntBuffer c = (EaglerArrayIntBuffer)src;
int l = c.limit - c.position;
if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1);
int o = c.dataView.getByteOffset();
Uint8Array.create(dataView.getBuffer()).set(
Uint8Array.create(c.dataView.getBuffer(), o + (c.position << SHIFT), (c.limit - c.position) << SHIFT),
dataView.getByteOffset() + (position << SHIFT));
position += l;
c.position += l;
}else {
int l = src.remaining();
if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1);
for(int i = 0; i < l; ++i) {
dataView.setInt32((position + l) << SHIFT, src.get(), true);
}
position += l;
}
return this;
}
@Override
public IntBuffer put(int[] src, int offset, int length) {
if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1);
for(int i = 0; i < length; ++i) {
dataView.setInt32((position + i) << SHIFT, src[offset + i], true);
}
position += length;
return this;
}
@Override
public IntBuffer put(int[] src) {
if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1);
for(int i = 0; i < src.length; ++i) {
dataView.setInt32((position + i) << SHIFT, src[i], true);
}
position += src.length;
return this;
}
@Override
public int getArrayOffset() {
return position;
}
@Override
public IntBuffer compact() {
if(limit > capacity) throw new ArrayIndexOutOfBoundsException(limit);
if(position > limit) throw new ArrayIndexOutOfBoundsException(position);
if(position == limit) {
return new EaglerArrayIntBuffer(EaglerArrayByteBuffer.ZERO_LENGTH_BUFFER);
}
int o = dataView.getByteOffset();
Uint8Array dst = Uint8Array.create(ArrayBuffer.create((limit - position) << SHIFT));
dst.set(Uint8Array.create(dataView.getBuffer(), o + (position << SHIFT), (limit - position) << SHIFT));
return new EaglerArrayIntBuffer(DataView.create(dst.getBuffer()));
}
@Override
public boolean isDirect() {
return true;
}
@Override
public IntBuffer mark() {
mark = position;
return this;
}
@Override
public IntBuffer reset() {
int m = mark;
if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m);
position = m;
return this;
}
@Override
public IntBuffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
@Override
public IntBuffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
@Override
public IntBuffer rewind() {
position = 0;
mark = -1;
return this;
}
@Override
public IntBuffer limit(int newLimit) {
if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit);
limit = newLimit;
return this;
}
@Override
public IntBuffer position(int newPosition) {
if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition);
position = newPosition;
return this;
}
}

View File

@ -0,0 +1,287 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.DataView;
import org.teavm.jso.typedarrays.Uint8Array;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class EaglerArrayShortBuffer implements ShortBuffer {
final DataView dataView;
final int capacity;
int position;
int limit;
int mark;
private static final int SHIFT = 1;
EaglerArrayShortBuffer(DataView dataView) {
this.dataView = dataView;
this.capacity = dataView.getByteLength() >> SHIFT;
this.position = 0;
this.limit = this.capacity;
this.mark = -1;
}
EaglerArrayShortBuffer(DataView dataView, int position, int limit, int mark) {
this.dataView = dataView;
this.capacity = dataView.getByteLength() >> SHIFT;
this.position = position;
this.limit = limit;
this.mark = mark;
}
@Override
public int capacity() {
return capacity;
}
@Override
public int position() {
return position;
}
@Override
public int limit() {
return limit;
}
@Override
public int remaining() {
return limit - position;
}
@Override
public boolean hasRemaining() {
return position < limit;
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public boolean hasArray() {
return false;
}
@Override
public Object array() {
throw new UnsupportedOperationException();
}
@Override
public int arrayOffset() {
return position;
}
@Override
public ShortBuffer slice() {
int o = dataView.getByteOffset();
return new EaglerArrayShortBuffer(DataView.create(dataView.getBuffer(), o + (position << SHIFT), (limit - position) << SHIFT));
}
@Override
public ShortBuffer duplicate() {
return new EaglerArrayShortBuffer(dataView, position, limit, mark);
}
@Override
public ShortBuffer asReadOnlyBuffer() {
return new EaglerArrayShortBuffer(dataView, position, limit, mark);
}
@Override
public short get() {
if(position >= limit) throw new ArrayIndexOutOfBoundsException(position);
return dataView.getInt16((position++) << SHIFT, true);
}
@Override
public ShortBuffer put(short b) {
if(position >= limit) throw new ArrayIndexOutOfBoundsException(position);
dataView.setInt16((position++) << SHIFT, b, true);
return this;
}
@Override
public short get(int index) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
return dataView.getInt16(index << SHIFT, true);
}
@Override
public ShortBuffer put(int index, short b) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
dataView.setInt16(index << SHIFT, b, true);
return this;
}
@Override
public short getElement(int index) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
return dataView.getInt16(index << SHIFT, true);
}
@Override
public void putElement(int index, short value) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
dataView.setInt16(index << SHIFT, value, true);
}
@Override
public ShortBuffer get(short[] dst, int offset, int length) {
if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1);
for(int i = 0; i < length; ++i) {
dst[offset + i] = dataView.getInt16((position + i) << SHIFT, true);
}
position += length;
return this;
}
@Override
public ShortBuffer get(short[] dst) {
if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1);
for(int i = 0; i < dst.length; ++i) {
dst[i] = dataView.getInt16((position + i) << SHIFT, true);
}
position += dst.length;
return this;
}
@Override
public ShortBuffer put(ShortBuffer src) {
if(src instanceof EaglerArrayShortBuffer) {
EaglerArrayShortBuffer c = (EaglerArrayShortBuffer)src;
int l = c.limit - c.position;
if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1);
int o = c.dataView.getByteOffset();
Uint8Array.create(dataView.getBuffer()).set(
Uint8Array.create(c.dataView.getBuffer(), o + (c.position << SHIFT), (c.limit - c.position) << SHIFT),
dataView.getByteOffset() + (position << SHIFT));
position += l;
c.position += l;
}else {
int l = src.remaining();
if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1);
for(int i = 0; i < l; ++i) {
dataView.setInt16((position + l) << SHIFT, src.get(), true);
}
position += l;
}
return this;
}
@Override
public ShortBuffer put(short[] src, int offset, int length) {
if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1);
for(int i = 0; i < length; ++i) {
dataView.setInt16((position + i) << SHIFT, src[offset + i], true);
}
position += length;
return this;
}
@Override
public ShortBuffer put(short[] src) {
if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1);
for(int i = 0; i < src.length; ++i) {
dataView.setInt16((position + i) << SHIFT, src[i], true);
}
position += src.length;
return this;
}
@Override
public int getArrayOffset() {
return position;
}
@Override
public ShortBuffer compact() {
if(limit > capacity) throw new ArrayIndexOutOfBoundsException(limit);
if(position > limit) throw new ArrayIndexOutOfBoundsException(position);
if(position == limit) {
return new EaglerArrayShortBuffer(EaglerArrayByteBuffer.ZERO_LENGTH_BUFFER);
}
int o = dataView.getByteOffset();
Uint8Array dst = Uint8Array.create(ArrayBuffer.create((limit - position) << SHIFT));
dst.set(Uint8Array.create(dataView.getBuffer(), o + (position << SHIFT), (limit - position) << SHIFT));
return new EaglerArrayShortBuffer(DataView.create(dst.getBuffer()));
}
@Override
public boolean isDirect() {
return true;
}
@Override
public ShortBuffer mark() {
mark = position;
return this;
}
@Override
public ShortBuffer reset() {
int m = mark;
if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m);
position = m;
return this;
}
@Override
public ShortBuffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
@Override
public ShortBuffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
@Override
public ShortBuffer rewind() {
position = 0;
mark = -1;
return this;
}
@Override
public ShortBuffer limit(int newLimit) {
if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit);
limit = newLimit;
return this;
}
@Override
public ShortBuffer position(int newPosition) {
if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition);
position = newPosition;
return this;
}
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.lax1dude.eaglercraft.v1_8.internal.indexeddb;
import org.teavm.jso.JSFunctor;
import org.teavm.jso.JSObject;
@JSFunctor
public interface EventHandler extends JSObject {
void handleEvent();
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.lax1dude.eaglercraft.v1_8.internal.indexeddb;
import org.teavm.jso.JSProperty;
public interface IDBCountRequest extends IDBRequest {
@JSProperty
int getResult();
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.lax1dude.eaglercraft.v1_8.internal.indexeddb;
import org.teavm.jso.JSMethod;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
public interface IDBCursor extends JSObject {
String DIRECTION_NEXT = "next";
String DIRECTION_NEXT_UNIQUE = "nextunique";
String DIRECTION_PREVIOUS = "prev";
String DIRECTION_PREVIOUS_UNIQUE = "prevunique";
@JSProperty
IDBCursorSource getSource();
@JSProperty
String getDirection();
@JSProperty
JSObject getKey();
@JSProperty
JSObject getValue();
@JSProperty
JSObject getPrimaryKey();
IDBRequest update(JSObject value);
void advance(int count);
@JSMethod("continue")
void doContinue();
IDBRequest delete();
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.lax1dude.eaglercraft.v1_8.internal.indexeddb;
import org.teavm.jso.JSProperty;
public interface IDBCursorRequest extends IDBRequest {
@JSProperty
IDBCursor getResult();
}

View File

@ -0,0 +1,21 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.lax1dude.eaglercraft.v1_8.internal.indexeddb;
import org.teavm.jso.JSObject;
public interface IDBCursorSource extends JSObject {
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.lax1dude.eaglercraft.v1_8.internal.indexeddb;
import org.teavm.jso.JSProperty;
import org.teavm.jso.dom.events.EventTarget;
public interface IDBDatabase extends EventTarget {
String TRANSACTION_READONLY = "readonly";
String TRANSACTION_READWRITE = "readwrite";
String TRANSACTION_VERSIONCHANGE = "versionchange";
@JSProperty
String getName();
@JSProperty
int getVersion();
@JSProperty
String[] getObjectStoreNames();
IDBObjectStore createObjectStore(String name, IDBObjectStoreParameters optionalParameters);
IDBObjectStore createObjectStore(String name);
void deleteObjectStore(String name);
IDBTransaction transaction(String storeName, String transactionMode);
IDBTransaction transaction(String storeName);
IDBTransaction transaction(String[] storeNames, String transactionMode);
IDBTransaction transaction(String[] storeNames);
void close();
@JSProperty("onabort")
void setOnAbort(EventHandler handler);
@JSProperty("onerror")
void setOnError(EventHandler handler);
@JSProperty("onversionchange")
void setOnVersionChange(EventHandler handler);
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.lax1dude.eaglercraft.v1_8.internal.indexeddb;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
public abstract class IDBError implements JSObject {
@JSProperty
public abstract String getName();
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.lax1dude.eaglercraft.v1_8.internal.indexeddb;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
public abstract class IDBFactory implements JSObject {
public static boolean isSupported() {
return !getInstanceImpl().isUndefined();
}
@JSBody(script = "return typeof this === 'undefined';")
private native boolean isUndefined();
public static IDBFactory getInstance() {
IDBFactory factory = getInstanceImpl();
if (!factory.isUndefined()) {
throw new IllegalStateException("IndexedDB is not supported in this browser");
}
return factory;
}
@JSBody(script = "return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || "
+ "window.msIndexedDB;")
static native IDBFactory getInstanceImpl();
public abstract IDBOpenDBRequest open(String name, int version);
public abstract IDBOpenDBRequest deleteDatabase(String name);
public abstract int cmp(JSObject a, JSObject b);
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.lax1dude.eaglercraft.v1_8.internal.indexeddb;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
public interface IDBGetRequest extends IDBRequest {
@JSProperty
JSObject getResult();
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.lax1dude.eaglercraft.v1_8.internal.indexeddb;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
import org.teavm.jso.core.JSString;
public abstract class IDBIndex implements JSObject, IDBCursorSource {
@JSProperty
public abstract String getName();
@JSProperty("keyPath")
abstract JSObject getKeyPathImpl();
public final String[] getKeyPath() {
JSObject result = getKeyPathImpl();
if (JSString.isInstance(result)) {
return new String[] { result.<JSString>cast().stringValue() };
} else {
return unwrapStringArray(result);
}
}
@JSBody(params = { "obj" }, script = "return this;")
private native String[] unwrapStringArray(JSObject obj);
@JSProperty
public abstract boolean isMultiEntry();
@JSProperty
public abstract boolean isUnique();
public abstract IDBCursorRequest openCursor();
public abstract IDBCursorRequest openCursor(IDBKeyRange range);
public abstract IDBCursorRequest openKeyCursor();
public abstract IDBGetRequest get(JSObject key);
public abstract IDBGetRequest getKey(JSObject key);
public abstract IDBCountRequest count(JSObject key);
public abstract IDBCountRequest count();
}

View File

@ -0,0 +1,59 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.lax1dude.eaglercraft.v1_8.internal.indexeddb;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
public abstract class IDBKeyRange implements JSObject {
@JSProperty
public abstract JSObject getLower();
@JSProperty
public abstract JSObject getUpper();
@JSProperty
public abstract boolean isLowerOpen();
@JSProperty
public abstract boolean isUpperOpen();
@JSBody(params = "value", script = "return IDBKeyRange.only(value);")
public static native IDBKeyRange only(JSObject value);
@JSBody(params = { "lower", "open" }, script = "return IDBKeyRange.lowerBound(lower, open);")
public static native IDBKeyRange lowerBound(JSObject lower, boolean open);
public static IDBKeyRange lowerBound(JSObject lower) {
return lowerBound(lower, false);
}
@JSBody(params = { "upper", "open" }, script = "return IDBKeyRange.upperBound(upper, open);")
public static native IDBKeyRange upperBound(JSObject upper, boolean open);
public static IDBKeyRange upperBound(JSObject upper) {
return upperBound(upper, false);
}
@JSBody(params = { "lower", "upper", "lowerOpen", "upperOpen" },
script = "return IDBKeyRange.bound(lower, upper, lowerOpen, upperOpen);")
public static native IDBKeyRange bound(JSObject lower, JSObject upper, boolean lowerOpen, boolean upperOpen);
public static IDBKeyRange bound(JSObject lower, JSObject upper) {
return bound(lower, upper, false, false);
}
}

View File

@ -0,0 +1,77 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.lax1dude.eaglercraft.v1_8.internal.indexeddb;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
import org.teavm.jso.core.JSString;
public abstract class IDBObjectStore implements JSObject, IDBCursorSource {
@JSProperty
public abstract String getName();
@JSProperty("keyPath")
abstract JSObject getKeyPathImpl();
public final String[] getKeyPath() {
JSObject result = getKeyPathImpl();
if (JSString.isInstance(result)) {
return new String[] { result.<JSString>cast().stringValue() };
} else {
return unwrapStringArray(result);
}
}
@JSBody(params = { "obj" }, script = "return this;")
private native String[] unwrapStringArray(JSObject obj);
@JSProperty
public abstract String[] getIndexNames();
@JSProperty
public abstract boolean isAutoIncrement();
public abstract IDBRequest put(JSObject value, JSObject key);
public abstract IDBRequest put(JSObject value);
public abstract IDBRequest add(JSObject value, JSObject key);
public abstract IDBRequest add(JSObject value);
public abstract IDBRequest delete(JSObject key);
public abstract IDBGetRequest get(JSObject key);
public abstract IDBRequest clear();
public abstract IDBCursorRequest openCursor();
public abstract IDBCursorRequest openCursor(IDBKeyRange range);
public abstract IDBIndex createIndex(String name, String key);
public abstract IDBIndex createIndex(String name, String[] keys);
public abstract IDBIndex index(String name);
public abstract void deleteIndex(String name);
public abstract IDBCountRequest count();
public abstract IDBCountRequest count(JSObject key);
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.lax1dude.eaglercraft.v1_8.internal.indexeddb;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
public abstract class IDBObjectStoreParameters implements JSObject {
@JSBody(script = "return {};")
public static native IDBObjectStoreParameters create();
public final IDBObjectStoreParameters keyPath(String... keys) {
setKeyPath(keys);
return this;
}
public final IDBObjectStoreParameters autoIncrement(boolean autoIncrement) {
setAutoIncrement(autoIncrement);
return this;
}
@JSProperty
abstract void setKeyPath(String[] keys);
@JSProperty
abstract void setAutoIncrement(boolean autoIncrement);
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.lax1dude.eaglercraft.v1_8.internal.indexeddb;
import org.teavm.jso.JSProperty;
import org.teavm.jso.dom.events.EventListener;
public interface IDBOpenDBRequest extends IDBRequest {
@JSProperty
IDBDatabase getResult();
@JSProperty
void setOnBlocked(EventHandler handler);
@JSProperty("onupgradeneeded")
void setOnUpgradeNeeded(EventListener<IDBVersionChangeEvent> listener);
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.lax1dude.eaglercraft.v1_8.internal.indexeddb;
import org.teavm.jso.JSProperty;
import org.teavm.jso.dom.events.EventTarget;
public interface IDBRequest extends EventTarget {
String STATE_PENDING = "pending";
String STATE_DONE = "done";
@JSProperty
IDBError getError();
@JSProperty
IDBRequestSource getSource();
@JSProperty
IDBTransaction getTransaction();
@JSProperty
String getReadyState();
@JSProperty("onerror")
void setOnError(EventHandler handler);
@JSProperty("onsuccess")
void setOnSuccess(EventHandler handler);
}

View File

@ -0,0 +1,21 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.lax1dude.eaglercraft.v1_8.internal.indexeddb;
import org.teavm.jso.JSObject;
public interface IDBRequestSource extends JSObject {
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.lax1dude.eaglercraft.v1_8.internal.indexeddb;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
import org.teavm.jso.dom.events.EventTarget;
public interface IDBTransaction extends JSObject, EventTarget {
@JSProperty
String getMode();
@JSProperty
IDBDatabase getDb();
@JSProperty
IDBError getError();
IDBObjectStore objectStore(String name);
void abort();
@JSProperty("onabort")
void setOnAbort(EventHandler handler);
@JSProperty("oncomplete")
void setOnComplete(EventHandler handler);
@JSProperty("onerror")
void setOnError(EventHandler handler);
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.lax1dude.eaglercraft.v1_8.internal.indexeddb;
import org.teavm.jso.JSProperty;
import org.teavm.jso.dom.events.Event;
public interface IDBVersionChangeEvent extends Event {
@JSProperty
int getOldVersion();
@JSProperty
int getNewVersion();
}

View File

@ -0,0 +1,102 @@
package net.lax1dude.eaglercraft.v1_8.internal.teavm;
import java.io.InputStream;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Uint8Array;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class ArrayBufferInputStream extends InputStream {
private int position;
private int limit;
private final ArrayBuffer buffer;
private final Uint8Array typed;
public ArrayBufferInputStream(ArrayBuffer bufferIn) {
this(bufferIn, 0, bufferIn.getByteLength());
}
public ArrayBufferInputStream(ArrayBuffer bufferIn, int off, int len) {
if(off + len > bufferIn.getByteLength()) {
throw new IllegalArgumentException("offset " + off + " and length " + len + " are out of bounds for a "
+ bufferIn.getByteLength() + " long arraybuffer");
}
buffer = bufferIn;
typed = Uint8Array.create(bufferIn);
position = off;
limit = off + len;
}
@Override
public int read() {
if(position >= limit) {
return -1;
}
return typed.get(position++);
}
@Override
public int read(byte b[], int off, int len) {
if(off + len > b.length) {
throw new ArrayIndexOutOfBoundsException("offset " + off + " and length " + len
+ " are out of bounds for a " + b.length + " array");
}
int avail = limit - position;
if(len > avail) {
len = avail;
}
if(len <= 0) {
return -1;
}
for(int i = 0; i < len; ++i) {
b[off + i] = (byte)typed.get(position + i);
}
position += len;
return len;
}
public long skip(long n) {
int avail = limit - position;
if(n > avail) {
n = avail;
}
position += (int)n;
return n;
}
@Override
public int available() {
return limit - position;
}
public int getPosition() {
return position;
}
public int getLimit() {
return limit;
}
public ArrayBuffer getBuffer() {
return buffer;
}
}

View File

@ -0,0 +1,189 @@
package net.lax1dude.eaglercraft.v1_8.internal.teavm;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Uint8Array;
import com.jcraft.jzlib.CRC32;
import com.jcraft.jzlib.GZIPInputStream;
import com.jcraft.jzlib.InflaterInputStream;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class EPKLoader {
public static final void loadEPK(ArrayBuffer epkFile, Map<String, byte[]> loadedFiles) throws IOException {
loadEPK(epkFile, "", loadedFiles);
}
public static final void loadEPK(ArrayBuffer epkFile, String path, Map<String, byte[]> loadedFiles) throws IOException {
int byteLength = epkFile.getByteLength();
int l = byteLength - 16;
if(l < 1) {
throw new IOException("EPK file is incomplete");
}
ArrayBufferInputStream is = new ArrayBufferInputStream(epkFile, 0, byteLength - 8);
byte[] header = new byte[8];
is.read(header);
String type = readASCII(header);
if(!"EAGPKG$$".equals(type)) {
throw new IOException("Invalid EPK file type '" + type + "'");
}
Uint8Array readEndCode = Uint8Array.create(epkFile, byteLength - 8, 8);
byte[] endCode = new byte[] { (byte)':', (byte)':', (byte)':', (byte)'Y',
(byte)'E', (byte)'E', (byte)':', (byte)'>' };
for(int i = 0; i < 8; ++i) {
if(readEndCode.get(i) != endCode[i]) {
throw new IOException("EPK file is missing EOF code (:::YEE:>)");
}
}
String vers = readASCII(is);
if(!vers.startsWith("ver2.")) {
throw new IOException("Unknown or invalid EPK version: " + vers);
}
is.skip(is.read()); // skip filename
is.skip(loadShort(is)); // skip comment
is.skip(8); // skip millis date
int numFiles = loadInt(is);
char compressionType = (char)is.read();
InputStream zis;
switch(compressionType) {
case 'G':
zis = new GZIPInputStream(is);
break;
case 'Z':
zis = new InflaterInputStream(is);
break;
case '0':
zis = is;
break;
default:
throw new IOException("Invalid or unsupported EPK compression: " + compressionType);
}
int blockFile = ('F' << 24) | ('I' << 16) | ('L' << 8) | 'E';
int blockEnd = ('E' << 24) | ('N' << 16) | ('D' << 8) | '$';
int blockHead = ('H' << 24) | ('E' << 16) | ('A' << 8) | 'D';
if(path.length() > 0 && !path.endsWith("/")) {
path = path + "/";
}
CRC32 crc32 = new CRC32();
int blockType;
for(int i = 0; i < numFiles; ++i) {
blockType = loadInt(zis);
if(blockType == blockEnd) {
throw new IOException("Unexpected END when there are still " + (numFiles - i) + " files remaining");
}
String name = readASCII(zis);
int len = loadInt(zis);
if(i == 0) {
if(blockType == blockHead) {
byte[] readType = new byte[len];
zis.read(readType);
if(!"file-type".equals(name) || !"epk/resources".equals(readASCII(readType))) {
throw new IOException("EPK is not of file-type 'epk/resources'!");
}
if(zis.read() != '>') {
throw new IOException("Object '" + name + "' is incomplete");
}
continue;
}else {
throw new IOException("File '" + name + "' did not have a file-type block as the first entry in the file");
}
}
if(blockType == blockFile) {
if(len < 5) {
throw new IOException("File '" + name + "' is incomplete");
}
int expectedCRC = loadInt(zis);
byte[] load = new byte[len - 5];
zis.read(load);
if(len > 5) {
crc32.reset();
crc32.update(load, 0, load.length);
if(expectedCRC != (int)crc32.getValue()) {
throw new IOException("File '" + name + "' has an invalid checksum");
}
}
if(zis.read() != ':') {
throw new IOException("File '" + name + "' is incomplete");
}
loadedFiles.put(path + name, load);
}else {
zis.skip(len);
}
if(zis.read() != '>') {
throw new IOException("Object '" + name + "' is incomplete");
}
}
if(loadInt(zis) != blockEnd) {
throw new IOException("EPK missing END$ object");
}
zis.close();
}
private static final int loadShort(InputStream is) throws IOException {
return (is.read() << 8) | is.read();
}
private static final int loadInt(InputStream is) throws IOException {
return (is.read() << 24) | (is.read() << 16) | (is.read() << 8) | is.read();
}
private static final String readASCII(byte[] bytesIn) throws IOException {
char[] charIn = new char[bytesIn.length];
for(int i = 0; i < bytesIn.length; ++i) {
charIn[i] = (char)((int)bytesIn[i] & 0xFF);
}
return new String(charIn);
}
private static final String readASCII(InputStream bytesIn) throws IOException {
int len = bytesIn.read();
char[] charIn = new char[len];
for(int i = 0; i < len; ++i) {
charIn[i] = (char)(bytesIn.read() & 0xFF);
}
return new String(charIn);
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,64 @@
package net.lax1dude.eaglercraft.v1_8.internal.teavm;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class TeaVMClientConfigAdapter implements IClientConfigAdapter {
public static final IClientConfigAdapter instance = new TeaVMClientConfigAdapter();
private String defaultLocale = "en_US";
private boolean hideDownDefaultServers = false;
private List<DefaultServer> defaultServers = new ArrayList();
private String serverToJoin = null;
void loadJSON(JSONObject eaglercraftOpts) {
defaultLocale = eaglercraftOpts.optString("lang", "en_US");
serverToJoin = eaglercraftOpts.optString("joinServer", null);
JSONArray serversArray = eaglercraftOpts.optJSONArray("servers");
if(serversArray != null) {
for(int i = 0, l = serversArray.length(); i < l; ++i) {
JSONObject serverEntry = serversArray.getJSONObject(i);
String serverAddr = serverEntry.optString("addr", null);
if(serverAddr != null) {
String serverName = serverEntry.optString("name", "Default Server #" + i);
defaultServers.add(new DefaultServer(serverName, serverAddr));
}
}
}
}
@Override
public String getDefaultLocale() {
return defaultLocale;
}
@Override
public List<DefaultServer> getDefaultServerList() {
return defaultServers;
}
@Override
public String getServerToJoin() {
return serverToJoin;
}
}

View File

@ -0,0 +1,209 @@
package net.lax1dude.eaglercraft.v1_8.internal.teavm;
import java.util.LinkedList;
import java.util.List;
import org.json.JSONObject;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
import org.teavm.jso.dom.events.Event;
import org.teavm.jso.dom.events.EventListener;
import org.teavm.jso.dom.events.MessageEvent;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Int8Array;
import org.teavm.jso.websocket.WebSocket;
import net.lax1dude.eaglercraft.v1_8.internal.EnumServerRateLimit;
import net.lax1dude.eaglercraft.v1_8.internal.IServerQuery;
import net.lax1dude.eaglercraft.v1_8.internal.QueryResponse;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class TeaVMServerQuery implements IServerQuery {
public static final Logger logger = LogManager.getLogger("WebSocketQuery");
private final List<QueryResponse> queryResponses = new LinkedList();
private final List<byte[]> queryResponsesBytes = new LinkedList();
protected final String uri;
protected final String accept;
protected final WebSocket sock;
protected boolean open = true;
protected boolean alive = false;
protected long pingStart = -1l;
protected long pingTimer = -1l;
private EnumServerRateLimit rateLimit = EnumServerRateLimit.OK;
public TeaVMServerQuery(String uri, String accept) {
this.uri = uri;
this.accept = accept;
this.sock = WebSocket.create(uri);
initHandlers();
}
@JSBody(params = { "obj" }, script = "return typeof obj === \"string\";")
private static native boolean isString(JSObject obj);
protected void initHandlers() {
sock.setBinaryType("arraybuffer");
TeaVMUtils.addEventListener(sock, "open", new EventListener<Event>() {
@Override
public void handleEvent(Event evt) {
sock.send("Accept: " + accept);
}
});
TeaVMUtils.addEventListener(sock, "close", new EventListener<Event>() {
@Override
public void handleEvent(Event evt) {
open = false;
}
});
TeaVMUtils.addEventListener(sock, "message", new EventListener<MessageEvent>() {
@Override
public void handleEvent(MessageEvent evt) {
alive = true;
if(pingTimer == -1) {
pingTimer = System.currentTimeMillis() - pingStart;
if(pingTimer < 1) {
pingTimer = 1;
}
}
if(isString(evt.getData())) {
String str = evt.getDataAsString();
if(str.equalsIgnoreCase("BLOCKED")) {
logger.error("Reached full IP ratelimit for {}!", uri);
rateLimit = EnumServerRateLimit.BLOCKED;
return;
}
if(str.equalsIgnoreCase("LOCKED")) {
logger.error("Reached full IP ratelimit lockout for {}!", uri);
rateLimit = EnumServerRateLimit.LOCKED_OUT;
return;
}
try {
JSONObject obj = new JSONObject(str);
if("blocked".equalsIgnoreCase(obj.optString("type", null))) {
logger.error("Reached query ratelimit for {}!", uri);
rateLimit = EnumServerRateLimit.BLOCKED;
}else if("locked".equalsIgnoreCase(obj.optString("type", null))) {
logger.error("Reached query ratelimit lockout for {}!", uri);
rateLimit = EnumServerRateLimit.LOCKED_OUT;
}else {
QueryResponse response = new QueryResponse(obj, pingTimer);
synchronized(queryResponses) {
queryResponses.add(response);
}
}
}catch(Throwable t) {
logger.error("Exception thrown parsing websocket query response from \"" + uri + "\"!");
logger.error(t);
}
}else {
synchronized(queryResponsesBytes) {
Int8Array packetBytes = Int8Array.create(evt.getDataAsArray());
byte[] data = new byte[packetBytes.getLength()];
for(int i = 0; i < data.length; ++i) {
data[i] = packetBytes.get(i);
}
queryResponsesBytes.add(data);
}
}
}
});
TeaVMUtils.addEventListener(sock, "error", new EventListener<Event>() {
@Override
public void handleEvent(Event evt) {
sock.close();
open = false;
}
});
}
@Override
public void send(String str) {
if(open) {
sock.send(str);
}
}
@JSBody(params = { "sock", "buffer" }, script = "sock.send(buffer);")
private static native void nativeBinarySend(WebSocket sock, ArrayBuffer buffer);
@Override
public void send(byte[] bytes) {
if(open) {
Int8Array arr = Int8Array.create(bytes.length);
arr.set(bytes, 0);
nativeBinarySend(sock, arr.getBuffer());
}
}
@Override
public int responsesAvailable() {
synchronized(queryResponses) {
return queryResponses.size();
}
}
@Override
public QueryResponse getResponse() {
synchronized(queryResponses) {
if(queryResponses.size() > 0) {
return queryResponses.remove(0);
}else {
return null;
}
}
}
@Override
public int binaryResponsesAvailable() {
synchronized(queryResponsesBytes) {
return queryResponsesBytes.size();
}
}
@Override
public byte[] getBinaryResponse() {
synchronized(queryResponsesBytes) {
if(queryResponsesBytes.size() > 0) {
return queryResponsesBytes.remove(0);
}else {
return null;
}
}
}
@Override
public QueryReadyState readyState() {
return open ? (alive ? QueryReadyState.OPEN : QueryReadyState.CONNECTING)
: (alive ? QueryReadyState.CLOSED : QueryReadyState.FAILED);
}
@Override
public void close() {
if(open) {
open = false;
sock.close();
}
}
@Override
public EnumServerRateLimit getRateLimit() {
return rateLimit;
}
}

View File

@ -0,0 +1,46 @@
package net.lax1dude.eaglercraft.v1_8.internal.teavm;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Int8Array;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class TeaVMUtils {
@JSBody(params = { "url" }, script = "URL.revokeObjectURL(url);")
public static native void freeDataURL(String url);
@JSBody(params = { "buf", "mime" }, script = "return URL.createObjectURL(new Blob([buf], {type: mime}));")
public static native String getDataURL(ArrayBuffer buf, String mime);
@JSBody(params = { "obj", "name", "handler" }, script = "obj.addEventListener(name, handler);")
public static native void addEventListener(JSObject obj, String name, JSObject handler);
public static final byte[] arrayBufferToBytes(ArrayBuffer buf) {
if(buf == null) {
return null;
}
Int8Array arr = Int8Array.create(buf);
byte[] ret = new byte[arr.getByteLength()];
for(int i = 0; i < ret.length; ++i) {
ret[i] = arr.get(i);
}
return ret;
}
}

View File

@ -0,0 +1,65 @@
package net.lax1dude.eaglercraft.v1_8.internal.teavm;
import org.teavm.jso.webgl.WebGLRenderingContext;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public interface WebGL2RenderingContext extends WebGLRenderingContext {
int TEXTURE_MAX_LEVEL = 0x0000813D;
int TEXTURE_MAX_ANISOTROPY_EXT = 0x000084FE;
int UNSIGNED_INT_24_8 = 0x000084FA;
int ANY_SAMPLES_PASSED = 0x00008D6A;
int QUERY_RESULT = 0x00008866;
int QUERY_RESULT_AVAILABLE = 0x00008867;
int DEPTH24_STENCIL8 = 0x000088F0;
int DEPTH_COMPONENT32F = 0x00008CAC;
int READ_FRAMEBUFFER = 0x00008CA8;
int DRAW_FRAMEBUFFER = 0x00008CA9;
int RGB8 = 0x00008051;
int RGBA8 = 0x00008058;
int R8 = 0x00008229;
int RED = 0x00001903;
WebGLQuery createQuery();
void beginQuery(int p1, WebGLQuery obj);
void endQuery(int p1);
void deleteQuery(WebGLQuery obj);
int getQueryParameter(WebGLQuery obj, int p2);
WebGLVertexArray createVertexArray();
void deleteVertexArray(WebGLVertexArray obj);
void bindVertexArray(WebGLVertexArray obj);
void renderbufferStorageMultisample(int p1, int p2, int p3, int p4, int p5);
void blitFramebuffer(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, int p9, int p10);
void drawBuffers(int[] p1);
void readBuffer(int p1);
void vertexAttribDivisor(int p1, int p2);
void drawArraysInstanced(int p1, int p2, int p3, int p4);
void drawElementsInstanced(int p1, int p2, int p3, int p4, int p5);
}

View File

@ -0,0 +1,19 @@
package net.lax1dude.eaglercraft.v1_8.internal.teavm;
import org.teavm.jso.JSObject;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public interface WebGLQuery extends JSObject {
}

View File

@ -0,0 +1,19 @@
package net.lax1dude.eaglercraft.v1_8.internal.teavm;
import org.teavm.jso.JSObject;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public interface WebGLVertexArray extends JSObject {
}

View File

@ -0,0 +1,32 @@
package net.lax1dude.eaglercraft.v1_8.internal.vfs;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class BooleanResult {
public static final BooleanResult TRUE = new BooleanResult(true);
public static final BooleanResult FALSE = new BooleanResult(false);
public final boolean bool;
private BooleanResult(boolean b) {
bool = b;
}
public static BooleanResult _new(boolean b) {
return b ? TRUE : FALSE;
}
}

View File

@ -0,0 +1,58 @@
package net.lax1dude.eaglercraft.v1_8.internal.vfs;
import com.google.common.collect.Sets;
import net.minecraft.client.resources.AbstractResourcePack;
import java.io.InputStream;
import java.util.List;
import java.util.Set;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class FolderResourcePack extends AbstractResourcePack {
private final String prefix;
public FolderResourcePack(String resourcePackFileIn, String prefix) {
super(resourcePackFileIn);
this.prefix = prefix;
}
protected InputStream getInputStreamByName(String name) {
return SYS.VFS.getFile(prefix + this.resourcePackFile + "/" + name).getInputStream();
}
protected boolean hasResourceName(String name) {
return SYS.VFS.fileExists(prefix + this.resourcePackFile + "/" + name);
}
public Set<String> getResourceDomains() {
Set<String> set = Sets.<String>newHashSet();
String pfx = prefix + this.resourcePackFile + "/assets/";
List<String> files = SYS.VFS.listFiles(pfx);
for (String file : files) {
String s = file.substring(pfx.length());
int ind = s.indexOf('/');
if (ind != -1) s = s.substring(0, ind);
if (!s.equals(s.toLowerCase())) {
this.logNameNotLowercase(s);
} else {
set.add(s);
}
}
return set;
}
}

View File

@ -0,0 +1,192 @@
package net.lax1dude.eaglercraft.v1_8.internal.vfs;
import net.lax1dude.eaglercraft.v1_8.crypto.SHA1Digest;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.teavm.ArrayBufferInputStream;
import net.lax1dude.eaglercraft.v1_8.internal.vfs.VirtualFilesystem.VFSHandle;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class SYS {
public static final VirtualFilesystem VFS;
static {
VFSHandle vh = VirtualFilesystem.openVFS("_net_lax1dude_eaglercraft_v1_8_VirtualFilesystem_");
if(vh.vfs == null) {
System.err.println("Could not init filesystem!");
}
VFS = vh.vfs;
List<String> srp = getResourcePackNames(true);
for (String name : srp) {
if (System.currentTimeMillis() - Long.parseLong(name.substring(name.lastIndexOf('_') + 1)) >= 604800000L) {
deleteResourcePack(name, true);
}
}
}
public static final void loadRemoteResourcePack(String url, String hash, Consumer<String> cb, Consumer<Runnable> ast, Runnable loading) {
if (!hash.matches("^[a-f0-9]{40}$")) {
cb.accept(null);
return;
}
List<String> srpPre = getResourcePackNames(true);
String alreadyHere = srpPre.stream().filter(s -> s.startsWith(hash + "_")).findFirst().orElse(null);
if (alreadyHere != null) {
cb.accept(alreadyHere);
return;
}
PlatformRuntime.downloadRemoteURI(url, ab -> {
ast.accept(() -> {
if (ab == null) {
cb.accept(null);
return;
}
List<String> srp = getResourcePackNames(true);
// delete old server resource packs - todo: test
if (srp.size() > 5) {
srp.sort(Comparator.comparingLong(val -> Long.parseLong(val.substring(val.lastIndexOf('_') + 1))));
for (int i = 0; i < srp.size() - 5; i++) {
deleteResourcePack(srp.get(i), true);
}
}
String packName = hash + "_" + System.currentTimeMillis();
loading.run();
boolean success = loadResourcePack(packName + ".zip", new ArrayBufferInputStream(ab), hash);
if (success) {
cb.accept(packName);
return;
}
cb.accept(null);
});
});
}
public static final boolean loadResourcePack(String name, InputStream is, String hash) {
BufferedInputStream bis = new BufferedInputStream(is);
bis.mark(Integer.MAX_VALUE);
if (hash != null) {
try {
SHA1Digest digest = new SHA1Digest();
byte[] buffer = new byte[16000];
int read = 0;
while ((read = bis.read(buffer)) > 0) {
digest.update(buffer, 0, read);
}
byte[] sha1sum = new byte[20];
digest.doFinal(sha1sum, 0);
bis.reset();
if (!hash.equals((new BigInteger(1, sha1sum)).toString(16))) {
return false;
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
String packName = name.substring(0, name.lastIndexOf('.')).replace('/', '_');
try {
int prefixLen = Integer.MAX_VALUE;
ZipInputStream ziss = new ZipInputStream(bis);
ZipEntry zipEntryy;
while ((zipEntryy = ziss.getNextEntry()) != null) {
String zn;
if (!zipEntryy.isDirectory() && ((zn = zipEntryy.getName()).equals("pack.mcmeta") || zn.endsWith("/pack.mcmeta"))) {
int currPrefixLen = zn.length() - 11;
if (prefixLen > currPrefixLen) {
prefixLen = currPrefixLen;
}
}
}
if (prefixLen == Integer.MAX_VALUE) {
prefixLen = 0;
}
bis.reset();
ZipInputStream zis = new ZipInputStream(bis);
byte[] bb = new byte[16000];
ZipEntry zipEntry;
while ((zipEntry = zis.getNextEntry()) != null) {
if (zipEntry.isDirectory()) continue;
if (zipEntry.getName().length() <= prefixLen) continue;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len;
while ((len = zis.read(bb)) != -1) {
baos.write(bb, 0, len);
}
baos.close();
SYS.VFS.getFile((hash == null ? "resourcepacks/" : "srp/") + packName + "/" + zipEntry.getName().substring(prefixLen)).setAllBytes(baos.toByteArray());
}
zis.closeEntry();
zis.close();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
public static final List<String> getResourcePackNames() {
return getResourcePackNames(false);
}
private static final List<String> getResourcePackNames(boolean srp) {
List<String> res = new ArrayList<>();
List<String> resourcePackFiles = SYS.VFS.listFiles(srp ? "srp/" : "resourcepacks/");
for (String path : resourcePackFiles) {
String trimmed = path.substring(srp ? 4 : 14);
trimmed = trimmed.substring(0, trimmed.indexOf('/'));
boolean hasIt = false;
for (String alreadyHas : res) {
if (trimmed.equals(alreadyHas)) {
hasIt = true;
break;
}
}
if (hasIt) continue;
res.add(trimmed);
}
return res;
}
public static final void deleteResourcePack(String packName) {
deleteResourcePack(packName, false);
}
private static final void deleteResourcePack(String packName, boolean srp) {
SYS.VFS.deleteFiles((srp ? "srp/" : "resourcepacks/") + packName);
}
}

View File

@ -0,0 +1,31 @@
package net.lax1dude.eaglercraft.v1_8.internal.vfs;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public interface VFSIterator {
public static class BreakLoop extends RuntimeException {
public BreakLoop() {
super("iterator loop break request");
}
}
public default void end() {
throw new BreakLoop();
}
public void next(VIteratorFile entry);
}

View File

@ -0,0 +1,241 @@
package net.lax1dude.eaglercraft.v1_8.internal.vfs;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class VFile {
public static final String pathSeperator = "/";
public static final String[] altPathSeperator = new String[] { "\\" };
public static String normalizePath(String p) {
for(int i = 0; i < altPathSeperator.length; ++i) {
p = p.replace(altPathSeperator[i], pathSeperator);
}
if(p.startsWith(pathSeperator)) {
p = p.substring(1);
}
if(p.endsWith(pathSeperator)) {
p = p.substring(0, p.length() - pathSeperator.length());
}
return p;
}
public static String[] splitPath(String p) {
String[] pth = normalizePath(p).split(pathSeperator);
for(int i = 0; i < pth.length; ++i) {
pth[i] = pth[i].trim();
}
return pth;
}
protected String path;
public static String createPath(Object... p) {
ArrayList<String> r = new ArrayList();
for(int i = 0; i < p.length; ++i) {
if(p[i] == null) {
continue;
}
String gg = p[i].toString();
if(gg == null) {
continue;
}
String[] parts = splitPath(gg);
for(int j = 0; j < parts.length; ++j) {
if(parts[j] == null || parts[j].equals(".")) {
continue;
}else if(parts[j].equals("..") && r.size() > 0) {
int k = r.size() - 1;
if(!r.get(k).equals("..")) {
r.remove(k);
}else {
r.add("..");
}
}else {
r.add(parts[j]);
}
}
}
if(r.size() > 0) {
StringBuilder s = new StringBuilder();
for(int i = 0; i < r.size(); ++i) {
if(i > 0) {
s.append(pathSeperator);
}
s.append(r.get(i));
}
return s.toString();
}else {
return null;
}
}
public VFile(Object... p) {
this.path = createPath(p);
}
public InputStream getInputStream() {
return isRelative() ? null : SYS.VFS.getFile(path).getInputStream();
}
public OutputStream getOutputStream() {
return isRelative() ? null : SYS.VFS.getFile(path).getOutputStream();
}
public String toString() {
return path;
}
public boolean isRelative() {
return path == null || path.contains("..");
}
public boolean canRead() {
return !isRelative() && SYS.VFS.fileExists(path);
}
public String getPath() {
return path.equals("unnamed") ? null : path;
}
public String getName() {
if(path == null) {
return null;
}
int i = path.indexOf(pathSeperator);
return i == -1 ? path : path.substring(i + 1);
}
public boolean canWrite() {
return !isRelative();
}
public String getParent() {
if(path == null) {
return null;
}
int i = path.indexOf(pathSeperator);
return i == -1 ? ".." : path.substring(0, i);
}
public int hashCode() {
return path == null ? 0 : path.hashCode();
}
public boolean equals(Object o) {
return path != null && o != null && (o instanceof VFile) && path.equals(((VFile)o).path);
}
public boolean exists() {
return !isRelative() && SYS.VFS.fileExists(path);
}
public boolean delete() {
return !isRelative() && SYS.VFS.deleteFile(path);
}
public boolean renameTo(String p, boolean copy) {
if(!isRelative() && SYS.VFS.renameFile(path, p, copy)) {
path = p;
return true;
}
return false;
}
public int length() {
return isRelative() ? -1 : SYS.VFS.getFile(path).getSize();
}
public void getBytes(int fileOffset, byte[] array, int offset, int length) {
if(isRelative()) {
throw new ArrayIndexOutOfBoundsException("File is relative");
}
SYS.VFS.getFile(path).getBytes(fileOffset, array, offset, length);
}
public void setCacheEnabled() {
if(isRelative()) {
throw new RuntimeException("File is relative");
}
SYS.VFS.getFile(path).setCacheEnabled();
}
public byte[] getAllBytes() {
if(isRelative()) {
return null;
}
return SYS.VFS.getFile(path).getAllBytes();
}
public String getAllChars() {
if(isRelative()) {
return null;
}
return SYS.VFS.getFile(path).getAllChars();
}
public String[] getAllLines() {
if(isRelative()) {
return null;
}
return SYS.VFS.getFile(path).getAllLines();
}
public byte[] getAllBytes(boolean copy) {
if(isRelative()) {
return null;
}
return SYS.VFS.getFile(path).getAllBytes(copy);
}
public boolean setAllChars(String bytes) {
if(isRelative()) {
return false;
}
return SYS.VFS.getFile(path).setAllChars(bytes);
}
public boolean setAllBytes(byte[] bytes) {
if(isRelative()) {
return false;
}
return SYS.VFS.getFile(path).setAllBytes(bytes);
}
public boolean setAllBytes(byte[] bytes, boolean copy) {
if(isRelative()) {
return false;
}
return SYS.VFS.getFile(path).setAllBytes(bytes, copy);
}
public List<String> list() {
if(isRelative()) {
return Arrays.asList(path);
}
return SYS.VFS.listFiles(path);
}
public int deleteAll() {
return isRelative() ? 0 : SYS.VFS.deleteFiles(path);
}
}

View File

@ -0,0 +1,303 @@
package net.lax1dude.eaglercraft.v1_8.internal.vfs;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import org.teavm.interop.Async;
import org.teavm.interop.AsyncCallback;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
import org.teavm.jso.dom.events.Event;
import org.teavm.jso.dom.events.EventListener;
import net.lax1dude.eaglercraft.v1_8.internal.indexeddb.IDBCursor;
import net.lax1dude.eaglercraft.v1_8.internal.indexeddb.IDBRequest;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Uint8Array;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
/**
* Do not use an instance of this class outside of the VFSIterator.next() method
*/
public class VIteratorFile extends VFile {
static final VIteratorFile instance = new VIteratorFile();
private VIteratorFile() {
super("");
this.idx = -1;
this.cur = null;
this.vfs = null;
}
private static class VirtualIteratorOutputStream extends ByteArrayOutputStream {
private final VIteratorFile itr;
protected VirtualIteratorOutputStream(VIteratorFile itr) {
this.itr = itr;
}
public void close() throws IOException {
if(!itr.setAllBytes(super.toByteArray(), false)) {
throw new IOException("Could not close stream and write to \"" + itr.path + "\" on VFS \"" + itr.vfs.database + "\" (the file was probably deleted)");
}
}
}
private int idx;
private IDBCursor cur;
private VirtualFilesystem vfs;
private boolean wasDeleted;
@JSBody(params = { "k" }, script = "return ((typeof k) === \"string\") ? k : (((typeof k) === \"undefined\") ? null : (((typeof k[0]) === \"string\") ? k[0] : null));")
private static native String readKey(JSObject k);
static VIteratorFile create(int idx, VirtualFilesystem vfs, IDBCursor cur) {
String k = readKey(cur.getKey());
if(k == null) {
return null;
}
instance.update(idx, k, vfs, cur);
return instance;
}
public VFile makeVFile() {
return new VFile(path);
}
private void update(int idx, String path, VirtualFilesystem vfs, IDBCursor cur) {
this.idx = idx;
this.path = path;
this.vfs = vfs;
this.cur = cur;
this.wasDeleted = false;
}
public InputStream getInputStream() {
return !wasDeleted ? new ByteArrayInputStream(getAllBytes()) : null;
}
public OutputStream getOutputStream() {
return !wasDeleted ? new VirtualIteratorOutputStream(this) : null;
}
public String toString() {
return path;
}
public boolean isRelative() {
return false;
}
public boolean canRead() {
return !wasDeleted;
}
public String getPath() {
return path;
}
public String getName() {
if(path == null) {
return null;
}
int i = path.indexOf(pathSeperator);
return i == -1 ? path : path.substring(i + 1);
}
public boolean canWrite() {
return !wasDeleted;
}
public String getParent() {
if(path == null) {
return null;
}
int i = path.indexOf(pathSeperator);
return i == -1 ? ".." : path.substring(0, i);
}
public int hashCode() {
return path == null ? 0 : path.hashCode();
}
public boolean equals(Object o) {
return path != null && o != null && (o instanceof VFile) && path.equals(((VFile)o).path);
}
public boolean exists() {
return !wasDeleted;
}
public boolean delete() {
return wasDeleted = AsyncHandlers.awaitRequest(cur.delete()).bool;
}
public boolean renameTo(String p) {
byte[] data = getAllBytes();
String op = path;
path = p;
if(!setAllBytes(data)) {
path = op;
return false;
}
path = op;
if(!delete()) {
return false;
}
path = p;
return true;
}
public int length() {
JSObject obj = cur.getValue();
if(obj == null) {
throw new RuntimeException("Value of entry is missing");
}
ArrayBuffer arr = readRow(obj);
if(arr == null) {
throw new RuntimeException("Value of the fucking value of the entry is missing");
}
return arr.getByteLength();
}
public void getBytes(int fileOffset, byte[] array, int offset, int length) {
JSObject obj = cur.getValue();
if(obj == null) {
throw new ArrayIndexOutOfBoundsException("Value of entry is missing");
}
ArrayBuffer arr = readRow(obj);
if(arr == null) {
throw new ArrayIndexOutOfBoundsException("Value of the fucking value of the entry is missing");
}
Uint8Array a = Uint8Array.create(arr);
if(a.getLength() < fileOffset + length) {
throw new ArrayIndexOutOfBoundsException("file '" + path + "' size was "+a.getLength()+" but user tried to read index "+(fileOffset + length - 1));
}
for(int i = 0; i < length; ++i) {
array[i + offset] = (byte)a.get(i + fileOffset);
}
}
public void setCacheEnabled() {
// no
}
@JSBody(params = { "obj" }, script = "return (typeof obj === 'undefined') ? null : ((typeof obj.data === 'undefined') ? null : obj.data);")
private static native ArrayBuffer readRow(JSObject obj);
public byte[] getAllBytes() {
JSObject obj = cur.getValue();
if(obj == null) {
return null;
}
ArrayBuffer arr = readRow(obj);
if(arr == null) {
return null;
}
Uint8Array a = Uint8Array.create(arr);
int ii = a.getByteLength();
byte[] array = new byte[ii];
for(int i = 0; i < ii; ++i) {
array[i] = (byte)a.get(i);
}
return array;
}
public String getAllChars() {
return VirtualFilesystem.utf8(getAllBytes());
}
public String[] getAllLines() {
return VirtualFilesystem.lines(VirtualFilesystem.utf8(getAllBytes()));
}
public byte[] getAllBytes(boolean copy) {
return getAllBytes();
}
public boolean setAllChars(String bytes) {
return setAllBytes(VirtualFilesystem.utf8(bytes));
}
public List<String> list() {
throw new RuntimeException("Cannot perform list all in VFS callback");
}
public int deleteAll() {
throw new RuntimeException("Cannot perform delete all in VFS callback");
}
@JSBody(params = { "pat", "dat" }, script = "return { path: pat, data: dat };")
private static native JSObject writeRow(String name, ArrayBuffer data);
public boolean setAllBytes(byte[] bytes) {
ArrayBuffer a = ArrayBuffer.create(bytes.length);
Uint8Array ar = Uint8Array.create(a);
ar.set(bytes);
JSObject obj = writeRow(path, a);
BooleanResult r = AsyncHandlers.awaitRequest(cur.update(obj));
return r.bool;
}
public boolean setAllBytes(byte[] bytes, boolean copy) {
return setAllBytes(bytes);
}
public static class AsyncHandlers {
@Async
public static native BooleanResult awaitRequest(IDBRequest r);
private static void awaitRequest(IDBRequest r, final AsyncCallback<BooleanResult> cb) {
r.addEventListener("success", new EventListener<Event>() {
@Override
public void handleEvent(Event evt) {
cb.complete(BooleanResult._new(true));
}
});
r.addEventListener("error", new EventListener<Event>() {
@Override
public void handleEvent(Event evt) {
cb.complete(BooleanResult._new(false));
}
});
}
}
}

View File

@ -0,0 +1,708 @@
package net.lax1dude.eaglercraft.v1_8.internal.vfs;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import net.lax1dude.eaglercraft.v1_8.EaglerInputStream;
import org.teavm.interop.Async;
import org.teavm.interop.AsyncCallback;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
import org.teavm.jso.dom.events.EventListener;
import net.lax1dude.eaglercraft.v1_8.internal.indexeddb.EventHandler;
import net.lax1dude.eaglercraft.v1_8.internal.indexeddb.IDBCountRequest;
import net.lax1dude.eaglercraft.v1_8.internal.indexeddb.IDBCursor;
import net.lax1dude.eaglercraft.v1_8.internal.indexeddb.IDBCursorRequest;
import net.lax1dude.eaglercraft.v1_8.internal.indexeddb.IDBDatabase;
import net.lax1dude.eaglercraft.v1_8.internal.indexeddb.IDBFactory;
import net.lax1dude.eaglercraft.v1_8.internal.indexeddb.IDBGetRequest;
import net.lax1dude.eaglercraft.v1_8.internal.indexeddb.IDBObjectStoreParameters;
import net.lax1dude.eaglercraft.v1_8.internal.indexeddb.IDBOpenDBRequest;
import net.lax1dude.eaglercraft.v1_8.internal.indexeddb.IDBRequest;
import net.lax1dude.eaglercraft.v1_8.internal.indexeddb.IDBTransaction;
import net.lax1dude.eaglercraft.v1_8.internal.indexeddb.IDBVersionChangeEvent;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Uint8Array;
/**
* Copyright (c) 2022 LAX1DUDE. All Rights Reserved.
*
* WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES
* NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED
* TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE
* SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR.
*
* NOT FOR COMMERCIAL OR MALICIOUS USE
*
* (please read the 'LICENSE' file this repo's root directory for more info)
*
*/
public class VirtualFilesystem {
protected static class VirtualOutputStream extends ByteArrayOutputStream {
private final VFSFile file;
protected VirtualOutputStream(VFSFile file) {
this.file = file;
}
public void close() throws IOException {
if(!file.setAllBytes(super.toByteArray(), false)) {
throw new IOException("Could not close stream and write to \"" + file.filePath + "\" on VFS \"" + file.virtualFilesystem.database + "\" (the file was probably deleted)");
}
}
}
public static class VFSFile {
public final VirtualFilesystem virtualFilesystem;
protected boolean cacheEnabled;
protected String filePath;
protected int fileSize = -1;
protected boolean hasBeenDeleted = false;
protected boolean hasBeenAccessed = false;
protected boolean exists = false;
protected byte[] cache = null;
protected long cacheHit;
protected VFSFile(VirtualFilesystem vfs, String filePath, boolean cacheEnabled) {
this.virtualFilesystem = vfs;
this.filePath = filePath;
this.cacheHit = System.currentTimeMillis();
if(cacheEnabled) {
setCacheEnabled();
}
}
public boolean equals(Object o) {
return (o instanceof VFSFile) && ((VFSFile)o).filePath.equals(filePath);
}
public int hashCode() {
return filePath.hashCode();
}
public String getPath() {
return filePath;
}
public int getSize() {
cacheHit = System.currentTimeMillis();
if(fileSize < 0) {
if(cacheEnabled) {
byte[] b = getAllBytes(false);
if(b != null) {
fileSize = b.length;
}
}else {
ArrayBuffer dat = AsyncHandlers.readWholeFile(virtualFilesystem.indexeddb, filePath);
if(dat != null) {
fileSize = dat.getByteLength();
}
}
}
return fileSize;
}
public InputStream getInputStream() {
byte[] dat = getAllBytes(false);
if(dat == null) {
return null;
}
return new EaglerInputStream(dat);
}
public OutputStream getOutputStream() {
return new VirtualOutputStream(this);
}
public void getBytes(int fileOffset, byte[] array, int offset, int length) {
if(hasBeenDeleted) {
throw new ArrayIndexOutOfBoundsException("file '" + filePath + "' has been deleted");
}else if(hasBeenAccessed && !exists) {
throw new ArrayIndexOutOfBoundsException("file '" + filePath + "' does not exist");
}
cacheHit = System.currentTimeMillis();
if(cacheEnabled && cache != null) {
System.arraycopy(cache, fileOffset, array, offset, length);
}else {
ArrayBuffer aa = AsyncHandlers.readWholeFile(virtualFilesystem.indexeddb, filePath);
hasBeenAccessed = true;
if(aa != null) {
exists = true;
}else {
exists = false;
throw new ArrayIndexOutOfBoundsException("file '" + filePath + "' does not exist");
}
Uint8Array a = Uint8Array.create(aa);
this.fileSize = a.getByteLength();
if(cacheEnabled) {
cache = new byte[fileSize];
for(int i = 0; i < fileSize; ++i) {
cache[i] = (byte)a.get(i);
}
}
if(a.getLength() < fileOffset + length) {
throw new ArrayIndexOutOfBoundsException("file '" + filePath + "' size was "+a.getLength()+" but user tried to read index "+(fileOffset + length - 1));
}
for(int i = 0; i < length; ++i) {
array[i + offset] = (byte)a.get(i + fileOffset);
}
}
}
public void setCacheEnabled() {
if(!cacheEnabled && !hasBeenDeleted && !(hasBeenAccessed && !exists)) {
cacheHit = System.currentTimeMillis();
cache = getAllBytes(false);
cacheEnabled = true;
}
}
public byte[] getAllBytes() {
return getAllBytes(false);
}
public String getAllChars() {
return utf8(getAllBytes(false));
}
public String[] getAllLines() {
return lines(getAllChars());
}
public byte[] getAllBytes(boolean copy) {
if(hasBeenDeleted || (hasBeenAccessed && !exists)) {
return null;
}
cacheHit = System.currentTimeMillis();
if(cacheEnabled && cache != null) {
byte[] b = cache;
if(copy) {
b = new byte[cache.length];
System.arraycopy(cache, 0, b, 0, cache.length);
}
return b;
}else {
hasBeenAccessed = true;
ArrayBuffer b = AsyncHandlers.readWholeFile(virtualFilesystem.indexeddb, filePath);
if(b != null) {
exists = true;
}else {
exists = false;
return null;
}
Uint8Array a = Uint8Array.create(b);
this.fileSize = a.getByteLength();
byte[] array = new byte[fileSize];
for(int i = 0; i < a.getByteLength(); ++i) {
array[i] = (byte)a.get(i);
}
if(cacheEnabled) {
if(copy) {
cache = new byte[fileSize];
System.arraycopy(b, 0, cache, 0, cache.length);
}else {
cache = array;
}
}
return array;
}
}
public boolean setAllChars(String bytes) {
return setAllBytes(utf8(bytes), true);
}
public boolean setAllBytes(byte[] bytes) {
return setAllBytes(bytes, true);
}
public boolean setAllBytes(byte[] bytes, boolean copy) {
if(hasBeenDeleted || bytes == null) {
return false;
}
cacheHit = System.currentTimeMillis();
this.fileSize = bytes.length;
if(cacheEnabled) {
byte[] copz = bytes;
if(copy) {
copz = new byte[bytes.length];
System.arraycopy(bytes, 0, copz, 0, bytes.length);
}
cache = copz;
return sync();
}else {
ArrayBuffer a = ArrayBuffer.create(bytes.length);
Uint8Array ar = Uint8Array.create(a);
ar.set(bytes);
boolean s = AsyncHandlers.writeWholeFile(virtualFilesystem.indexeddb, filePath, a).bool;
hasBeenAccessed = true;
exists = exists || s;
return s;
}
}
public boolean sync() {
if(cacheEnabled && cache != null && !hasBeenDeleted) {
cacheHit = System.currentTimeMillis();
ArrayBuffer a = ArrayBuffer.create(cache.length);
Uint8Array ar = Uint8Array.create(a);
ar.set(cache);
boolean tryWrite = AsyncHandlers.writeWholeFile(virtualFilesystem.indexeddb, filePath, a).bool;
hasBeenAccessed = true;
exists = exists || tryWrite;
return tryWrite;
}
return false;
}
public boolean delete() {
if(!hasBeenDeleted && !(hasBeenAccessed && !exists)) {
cacheHit = System.currentTimeMillis();
if(!AsyncHandlers.deleteFile(virtualFilesystem.indexeddb, filePath).bool) {
hasBeenAccessed = true;
return false;
}
virtualFilesystem.fileMap.remove(filePath);
hasBeenDeleted = true;
hasBeenAccessed = true;
exists = false;
return true;
}
return false;
}
public boolean rename(String newName, boolean copy) {
if(!hasBeenDeleted && !(hasBeenAccessed && !exists)) {
cacheHit = System.currentTimeMillis();
ArrayBuffer arr = AsyncHandlers.readWholeFile(virtualFilesystem.indexeddb, filePath);
hasBeenAccessed = true;
if(arr != null) {
exists = true;
if(!AsyncHandlers.writeWholeFile(virtualFilesystem.indexeddb, newName, arr).bool) {
return false;
}
if(!copy && !AsyncHandlers.deleteFile(virtualFilesystem.indexeddb, filePath).bool) {
return false;
}
}else {
exists = false;
}
if(!copy) {
virtualFilesystem.fileMap.remove(filePath);
filePath = newName;
virtualFilesystem.fileMap.put(newName, this);
}
return true;
}
return false;
}
public boolean exists() {
if(hasBeenDeleted) {
return false;
}
cacheHit = System.currentTimeMillis();
if(hasBeenAccessed) {
return exists;
}
exists = AsyncHandlers.fileExists(virtualFilesystem.indexeddb, filePath).bool;
hasBeenAccessed = true;
return exists;
}
}
private final HashMap<String, VFSFile> fileMap = new HashMap();
public final String database;
private final IDBDatabase indexeddb;
public static class VFSHandle {
public final boolean failedInit;
public final boolean failedLocked;
public final String failedError;
public final VirtualFilesystem vfs;
public VFSHandle(boolean init, boolean locked, String error, VirtualFilesystem db) {
failedInit = init;
failedLocked = locked;
failedError = error;
vfs = db;
}
public String toString() {
if(failedInit) {
return "IDBFactory threw an exception, IndexedDB is most likely not supported in this browser." + (failedError == null ? "" : "\n\n" + failedError);
}
if(failedLocked) {
return "The filesystem requested is already in use on a different tab.";
}
if(failedError != null) {
return "The IDBFactory.open() request failed, reason: " + failedError;
}
return "Virtual Filesystem Object: " + vfs.database;
}
}
public static VFSHandle openVFS(String db) {
DatabaseOpen evt = AsyncHandlers.openDB(db);
if(evt.failedInit) {
return new VFSHandle(true, false, evt.failedError, null);
}
if(evt.failedLocked) {
return new VFSHandle(false, true, null, null);
}
if(evt.failedError != null) {
return new VFSHandle(false, false, evt.failedError, null);
}
return new VFSHandle(false, false, null, new VirtualFilesystem(db, evt.database));
}
private VirtualFilesystem(String db, IDBDatabase idb) {
database = db;
indexeddb = idb;
}
public void close() {
indexeddb.close();
}
public VFSFile getFile(String path) {
return getFile(path, false);
}
public VFSFile getFile(String path, boolean cache) {
VFSFile f = fileMap.get(path);
if(f == null) {
fileMap.put(path, f = new VFSFile(this, path, cache));
}else {
if(cache) {
f.setCacheEnabled();
}
}
return f;
}
public boolean renameFile(String oldName, String newName, boolean copy) {
return getFile(oldName).rename(newName, copy);
}
public boolean deleteFile(String path) {
return getFile(path).delete();
}
public boolean fileExists(String path) {
return getFile(path).exists();
}
public List<String> listFiles(String prefix) {
final ArrayList<String> list = new ArrayList();
AsyncHandlers.iterateFiles(indexeddb, this, prefix, false, (v) -> {
list.add(v.getPath());
});
return list;
}
public int deleteFiles(String prefix) {
return AsyncHandlers.deleteFiles(indexeddb, prefix);
}
public int iterateFiles(String prefix, boolean rw, VFSIterator itr) {
return AsyncHandlers.iterateFiles(indexeddb, this, prefix, rw, itr);
}
public int renameFiles(String oldPrefix, String newPrefix, boolean copy) {
List<String> filesToCopy = listFiles(oldPrefix);
int i = 0;
for(String str : filesToCopy) {
String f = VFile.createPath(newPrefix, str.substring(oldPrefix.length()));
if(!renameFile(str, f, copy)) {
System.err.println("Could not " + (copy ? "copy" : "rename") + " file \"" + str + "\" to \"" + f + "\" for some reason");
}else {
++i;
}
}
return i;
}
public void flushCache(long age) {
long curr = System.currentTimeMillis();
Iterator<VFSFile> files = fileMap.values().iterator();
while(files.hasNext()) {
if(curr - files.next().cacheHit > age) {
files.remove();
}
}
}
protected static class DatabaseOpen {
protected final boolean failedInit;
protected final boolean failedLocked;
protected final String failedError;
protected final IDBDatabase database;
protected DatabaseOpen(boolean init, boolean locked, String error, IDBDatabase db) {
failedInit = init;
failedLocked = locked;
failedError = error;
database = db;
}
}
@JSBody(script = "return ((typeof indexedDB) !== 'undefined') ? indexedDB : null;")
protected static native IDBFactory createIDBFactory();
protected static class AsyncHandlers {
@Async
protected static native DatabaseOpen openDB(String name);
private static void openDB(String name, final AsyncCallback<DatabaseOpen> cb) {
IDBFactory i = createIDBFactory();
if(i == null) {
cb.complete(new DatabaseOpen(false, false, "window.indexedDB was null or undefined", null));
return;
}
final IDBOpenDBRequest f = i.open(name, 1);
f.setOnBlocked(new EventHandler() {
@Override
public void handleEvent() {
cb.complete(new DatabaseOpen(false, true, null, null));
}
});
f.setOnSuccess(new EventHandler() {
@Override
public void handleEvent() {
cb.complete(new DatabaseOpen(false, false, null, f.getResult()));
}
});
f.setOnError(new EventHandler() {
@Override
public void handleEvent() {
cb.complete(new DatabaseOpen(false, false, "open error", null));
}
});
f.setOnUpgradeNeeded(new EventListener<IDBVersionChangeEvent>() {
@Override
public void handleEvent(IDBVersionChangeEvent evt) {
f.getResult().createObjectStore("filesystem", IDBObjectStoreParameters.create().keyPath("path"));
}
});
}
@Async
protected static native BooleanResult deleteFile(IDBDatabase db, String name);
private static void deleteFile(IDBDatabase db, String name, final AsyncCallback<BooleanResult> cb) {
IDBTransaction tx = db.transaction("filesystem", "readwrite");
final IDBRequest r = tx.objectStore("filesystem").delete(makeTheFuckingKeyWork(name));
r.setOnSuccess(new EventHandler() {
@Override
public void handleEvent() {
cb.complete(BooleanResult._new(true));
}
});
r.setOnError(new EventHandler() {
@Override
public void handleEvent() {
cb.complete(BooleanResult._new(false));
}
});
}
@JSBody(params = { "obj" }, script = "return (typeof obj === 'undefined') ? null : ((typeof obj.data === 'undefined') ? null : obj.data);")
protected static native ArrayBuffer readRow(JSObject obj);
@JSBody(params = { "obj" }, script = "return [obj];")
private static native JSObject makeTheFuckingKeyWork(String k);
@Async
protected static native ArrayBuffer readWholeFile(IDBDatabase db, String name);
private static void readWholeFile(IDBDatabase db, String name, final AsyncCallback<ArrayBuffer> cb) {
IDBTransaction tx = db.transaction("filesystem", "readonly");
final IDBGetRequest r = tx.objectStore("filesystem").get(makeTheFuckingKeyWork(name));
r.setOnSuccess(new EventHandler() {
@Override
public void handleEvent() {
cb.complete(readRow(r.getResult()));
}
});
r.setOnError(new EventHandler() {
@Override
public void handleEvent() {
cb.complete(null);
}
});
}
@JSBody(params = { "k" }, script = "return ((typeof k) === \"string\") ? k : (((typeof k) === \"undefined\") ? null : (((typeof k[0]) === \"string\") ? k[0] : null));")
private static native String readKey(JSObject k);
@JSBody(params = { "k" }, script = "return ((typeof k) === \"undefined\") ? null : (((typeof k.path) === \"undefined\") ? null : (((typeof k.path) === \"string\") ? k[0] : null));")
private static native String readRowKey(JSObject r);
@Async
protected static native Integer iterateFiles(IDBDatabase db, final VirtualFilesystem vfs, final String prefix, boolean rw, final VFSIterator itr);
private static void iterateFiles(IDBDatabase db, final VirtualFilesystem vfs, final String prefix, boolean rw, final VFSIterator itr, final AsyncCallback<Integer> cb) {
IDBTransaction tx = db.transaction("filesystem", rw ? "readwrite" : "readonly");
final IDBCursorRequest r = tx.objectStore("filesystem").openCursor();
final int[] res = new int[1];
r.setOnSuccess(new EventHandler() {
@Override
public void handleEvent() {
IDBCursor c = r.getResult();
if(c == null || c.getKey() == null || c.getValue() == null) {
cb.complete(res[0]);
return;
}
String k = readKey(c.getKey());
if(k != null) {
if(k.startsWith(prefix)) {
int ci = res[0]++;
try {
itr.next(VIteratorFile.create(ci, vfs, c));
}catch(VFSIterator.BreakLoop ex) {
cb.complete(res[0]);
return;
}
}
}
c.doContinue();
}
});
r.setOnError(new EventHandler() {
@Override
public void handleEvent() {
cb.complete(res[0] > 0 ? res[0] : -1);
}
});
}
@Async
protected static native Integer deleteFiles(IDBDatabase db, final String prefix);
private static void deleteFiles(IDBDatabase db, final String prefix, final AsyncCallback<Integer> cb) {
IDBTransaction tx = db.transaction("filesystem", "readwrite");
final IDBCursorRequest r = tx.objectStore("filesystem").openCursor();
final int[] res = new int[1];
r.setOnSuccess(new EventHandler() {
@Override
public void handleEvent() {
IDBCursor c = r.getResult();
if(c == null || c.getKey() == null || c.getValue() == null) {
cb.complete(res[0]);
return;
}
String k = readKey(c.getKey());
if(k != null) {
if(k.startsWith(prefix)) {
c.delete();
++res[0];
}
}
c.doContinue();
}
});
r.setOnError(new EventHandler() {
@Override
public void handleEvent() {
cb.complete(res[0] > 0 ? res[0] : -1);
}
});
}
@Async
protected static native BooleanResult fileExists(IDBDatabase db, String name);
private static void fileExists(IDBDatabase db, String name, final AsyncCallback<BooleanResult> cb) {
IDBTransaction tx = db.transaction("filesystem", "readonly");
final IDBCountRequest r = tx.objectStore("filesystem").count(makeTheFuckingKeyWork(name));
r.setOnSuccess(new EventHandler() {
@Override
public void handleEvent() {
cb.complete(BooleanResult._new(r.getResult() > 0));
}
});
r.setOnError(new EventHandler() {
@Override
public void handleEvent() {
cb.complete(BooleanResult._new(false));
}
});
}
@JSBody(params = { "pat", "dat" }, script = "return { path: pat, data: dat };")
protected static native JSObject writeRow(String name, ArrayBuffer data);
@Async
protected static native BooleanResult writeWholeFile(IDBDatabase db, String name, ArrayBuffer data);
private static void writeWholeFile(IDBDatabase db, String name, ArrayBuffer data, final AsyncCallback<BooleanResult> cb) {
IDBTransaction tx = db.transaction("filesystem", "readwrite");
final IDBRequest r = tx.objectStore("filesystem").put(writeRow(name, data));
r.setOnSuccess(new EventHandler() {
@Override
public void handleEvent() {
cb.complete(BooleanResult._new(true));
}
});
r.setOnError(new EventHandler() {
@Override
public void handleEvent() {
cb.complete(BooleanResult._new(false));
}
});
}
}
public static byte[] utf8(String str) {
if(str == null) return null;
return str.getBytes(Charset.forName("UTF-8"));
}
public static String utf8(byte[] str) {
if(str == null) return null;
return new String(str, Charset.forName("UTF-8"));
}
public static String CRLFtoLF(String str) {
if(str == null) return null;
str = str.indexOf('\r') != -1 ? str.replace("\r", "") : str;
str = str.trim();
if(str.endsWith("\n")) {
str = str.substring(0, str.length() - 1);
}
if(str.startsWith("\n")) {
str = str.substring(1);
}
return str;
}
public static String[] lines(String str) {
if(str == null) return null;
return CRLFtoLF(str).split("\n");
}
}