Update #37 - Touch support without userscript, many other feats

This commit is contained in:
lax1dude
2024-09-21 20:17:42 -07:00
parent 173727c8c4
commit ec1ab8ece3
683 changed files with 62074 additions and 8996 deletions

View File

@ -25,6 +25,10 @@ class OpenGLObjects {
this.ptr = ptr;
}
public int hashCode() {
return ptr;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteBuffers(this);
@ -40,6 +44,10 @@ class OpenGLObjects {
this.ptr = ptr;
}
public int hashCode() {
return ptr;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteVertexArrays(this);
@ -55,6 +63,10 @@ class OpenGLObjects {
this.ptr = ptr;
}
public int hashCode() {
return ptr;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteTextures(this);
@ -70,6 +82,10 @@ class OpenGLObjects {
this.ptr = ptr;
}
public int hashCode() {
return ptr;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteProgram(this);
@ -85,6 +101,10 @@ class OpenGLObjects {
this.ptr = ptr;
}
public int hashCode() {
return ptr;
}
@Override
public void free() {
}
@ -99,6 +119,10 @@ class OpenGLObjects {
this.ptr = ptr;
}
public int hashCode() {
return ptr;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteShader(this);
@ -114,6 +138,10 @@ class OpenGLObjects {
this.ptr = ptr;
}
public int hashCode() {
return ptr;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteFramebuffer(this);
@ -129,6 +157,10 @@ class OpenGLObjects {
this.ptr = ptr;
}
public int hashCode() {
return ptr;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteRenderbuffer(this);
@ -144,6 +176,10 @@ class OpenGLObjects {
this.ptr = ptr;
}
public int hashCode() {
return ptr;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteQueries(this);

View File

@ -7,6 +7,7 @@ import java.awt.Desktop;
import java.awt.EventQueue;
import java.awt.HeadlessException;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.Dialog.ModalExclusionType;
import java.awt.Dialog.ModalityType;
import java.io.File;
@ -14,16 +15,23 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.imageio.ImageIO;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileFilter;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.MainMenuCreditsDialog;
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-2023 lax1dude, ayunami2000. All Rights Reserved.
@ -49,10 +57,23 @@ public class PlatformApplication {
}
public static void openLink(String url) {
URI safeURL;
try {
Desktop.getDesktop().browse(new URI(url));
safeURL = new URI(url);
String proto = safeURL.getScheme();
if(!proto.equalsIgnoreCase("http") && !proto.equalsIgnoreCase("https")) {
throw new IllegalArgumentException("Suspicious protocol: " + proto);
}
}catch(URISyntaxException | IllegalArgumentException ex) {
PlatformRuntime.logger.error("Refusing to open invalid URL: {}", url);
PlatformRuntime.logger.error(ex);
return;
}
try {
Desktop.getDesktop().browse(safeURL);
} catch (Throwable var5) {
EagRuntime.debugPrintStackTrace(var5);
PlatformRuntime.logger.error("Failed to browse to URL: {}", safeURL.toString());
PlatformRuntime.logger.error(var5);
}
}
@ -98,9 +119,40 @@ public class PlatformApplication {
return null;
}
}
private static final DateFormat dateFormatSS = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss");
private static final File screeshotsDir = new File("screenshots");
public static String saveScreenshot() {
return "nothing";
if(!screeshotsDir.isDirectory() && !screeshotsDir.mkdirs()) {
PlatformRuntime.logger.error("Failed to create screenshots directory: {}", screeshotsDir.getAbsolutePath());
return "nothing";
}
String name = "screenshot_" + dateFormatSS.format(new Date()).toString() + ".png";
int w = PlatformInput.getWindowWidth();
int h = PlatformInput.getWindowHeight();
ByteBuffer screenshotBuffer = PlatformRuntime.allocateByteBuffer(w * h * 4);
PlatformOpenGL._wglReadPixels(0, 0, w, h, RealOpenGLEnums.GL_RGBA, RealOpenGLEnums.GL_UNSIGNED_BYTE, screenshotBuffer);
BufferedImage bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
int i;
for(int y = 0; y < h; ++y) {
for(int x = 0; x < w; ++x) {
i = (x + (h - y - 1) * w) << 2;
bufferedImage.setRGB(x, y,
((screenshotBuffer.get(i) & 0xFF) << 16) | ((screenshotBuffer.get(i + 1) & 0xFF) << 8)
| (screenshotBuffer.get(i + 2) & 0xFF) | 0xFF000000);
}
}
PlatformRuntime.freeByteBuffer(screenshotBuffer);
File screenshotFile = new File(screeshotsDir, name);
try {
ImageIO.write(bufferedImage, "PNG", screenshotFile);
}catch(IOException ex) {
PlatformRuntime.logger.error("Failed to write screenshot: {}", screenshotFile.getAbsolutePath());
return "nothing";
}
PlatformRuntime.logger.info("Saved screenshot to: {}", screenshotFile.getAbsolutePath());
return name;
}
public static void showPopup(String msg) {
@ -127,6 +179,7 @@ public class PlatformApplication {
public static void displayFileChooser(final String mime, final String ext) {
if(!fileChooserOpen) {
fileChooserOpen = true;
clearFileChooserResult();
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {

View File

@ -43,7 +43,11 @@ public class PlatformAssets {
}
}
public static final byte[] getResourceBytes(String path) {
public static boolean getResourceExists(String path) {
return (new File("resources", path)).isFile();
}
public static byte[] getResourceBytes(String path) {
File loadFile = new File("resources", path);
byte[] ret = new byte[(int) loadFile.length()];
try(FileInputStream is = new FileInputStream(loadFile)) {
@ -56,8 +60,12 @@ public class PlatformAssets {
return null;
}
}
public static final ImageData loadImageFile(InputStream data) {
public static ImageData loadImageFile(InputStream data) {
return loadImageFile(data, "image/png");
}
public static ImageData loadImageFile(InputStream data, String mime) {
try {
BufferedImage img = ImageIO.read(data);
if(img == null) {
@ -81,9 +89,13 @@ public class PlatformAssets {
return null;
}
}
public static final ImageData loadImageFile(byte[] data) {
return loadImageFile(new EaglerInputStream(data));
public static ImageData loadImageFile(byte[] data) {
return loadImageFile(new EaglerInputStream(data), "image/png");
}
public static ImageData loadImageFile(byte[] data, String mime) {
return loadImageFile(new EaglerInputStream(data), mime);
}
}

View File

@ -46,7 +46,7 @@ public class PlatformAudio {
protected PaulscodeAudioHandle(String sourceName) {
this.sourceName = sourceName;
this.stall = System.currentTimeMillis();
this.stall = PlatformRuntime.steadyTimeMillis();
}
@Override
@ -64,7 +64,7 @@ public class PlatformAudio {
@Override
public void restart() {
this.stall = System.currentTimeMillis();
this.stall = PlatformRuntime.steadyTimeMillis();
sndSystem.rewind(sourceName);
sndSystem.play(sourceName);
}
@ -91,7 +91,7 @@ public class PlatformAudio {
@Override
public boolean shouldFree() {
return !sndSystem.playing(sourceName) && System.currentTimeMillis() - this.stall > 250l; //TODO: I hate this hack
return !sndSystem.playing(sourceName) && PlatformRuntime.steadyTimeMillis() - this.stall > 250l; //TODO: I hate this hack
}
}
@ -125,7 +125,7 @@ public class PlatformAudio {
private static SoundSystem sndSystem = null;
static void platformInitialize() {
logger.info("Eaglercraft still uses Paul Lamb's SoundSystem but with LWJGL3");
logger.info("Eaglercraft uses Paul Lamb's SoundSystem (with LWJGL3)");
logger.info(" \"Author: Paul Lamb, www.paulscode.com\"");
try {
SoundSystemConfig.addLibrary(LibraryLWJGLOpenAL.class);

View File

@ -2,7 +2,6 @@ package net.lax1dude.eaglercraft.v1_8.internal;
import java.io.File;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.DebugFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.JDBCFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.JDBCFilesystemConverter;
@ -28,104 +27,50 @@ public class PlatformFilesystem {
public static final Logger logger = LogManager.getLogger("PlatformFilesystem");
@Deprecated
public static final File debugFilesystemRoot = (new File("filesystem/sp")).getAbsoluteFile();
private static IFilesystemProvider provider = null;
public static final File filesystemsRoot = (new File("filesystem")).getAbsoluteFile();
public static String jdbcUri = null;
public static String jdbcDriver = null;
private static final boolean isLegacyFolder = checkLegacy();
public static void initialize() {
if(provider == null) {
if(jdbcUri != null && jdbcDriver != null) {
provider = JDBCFilesystem.initialize(jdbcUri, jdbcDriver);
private static boolean checkLegacy() {
if(!debugFilesystemRoot.isDirectory()) return false;
String[] str = debugFilesystemRoot.list();
return str != null && str.length > 0;
}
public static IEaglerFilesystem initializePersist(String dbName) {
String jdbcUri = System.getProperty("eagler.jdbc." + dbName + ".uri");
String jdbcDriver = System.getProperty("eagler.jdbc." + dbName + ".driver");
if(jdbcUri != null && jdbcDriver != null) {
try {
IEaglerFilesystem provider = JDBCFilesystem.initialize(dbName, jdbcUri, jdbcDriver);
if(((JDBCFilesystem)provider).isNewFilesystem() && debugFilesystemRoot.isDirectory() && debugFilesystemRoot.list().length > 0) {
JDBCFilesystemConverter.convertFilesystem("Converting filesystem, please wait...", debugFilesystemRoot, provider, true);
}
return provider;
}catch(Throwable t) {
logger.error("Could not open jdbc-based filesystem: {}", dbName);
logger.error(t);
return null;
}
}else {
File f;
if(isLegacyFolder && (dbName.equals("worlds") || dbName.equals("resourcePacks"))) {
f = debugFilesystemRoot;
logger.info("Note: filesystem \"{}\" will be stored in the legacy \"sp\" folder because it exists and is not empty", dbName);
}else {
provider = DebugFilesystem.initialize(debugFilesystemRoot);
f = new File(filesystemsRoot, dbName);
}
try {
return DebugFilesystem.initialize(dbName, f);
}catch(Throwable t) {
logger.error("Could not open folder-based filesystem: {}", dbName);
logger.error(t);
return null;
}
}
}
public static void setUseJDBC(String uri) {
jdbcUri = uri;
}
public static void setJDBCDriverClass(String driver) {
jdbcDriver = driver;
}
public static interface IFilesystemProvider {
boolean eaglerDelete(String pathName);
ByteBuffer eaglerRead(String pathName);
void eaglerWrite(String pathName, ByteBuffer data);
boolean eaglerExists(String pathName);
boolean eaglerMove(String pathNameOld, String pathNameNew);
int eaglerCopy(String pathNameOld, String pathNameNew);
int eaglerSize(String pathName);
void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive);
}
private static void throwNotInitialized() {
throw new UnsupportedOperationException("Filesystem has not been initialized!");
}
public static boolean eaglerDelete(String pathName) {
if(provider == null) throwNotInitialized();
return provider.eaglerDelete(pathName);
}
public static ByteBuffer eaglerRead(String pathName) {
if(provider == null) throwNotInitialized();
return provider.eaglerRead(pathName);
}
public static void eaglerWrite(String pathName, ByteBuffer data) {
if(provider == null) throwNotInitialized();
provider.eaglerWrite(pathName, data);
}
public static boolean eaglerExists(String pathName) {
if(provider == null) throwNotInitialized();
return provider.eaglerExists(pathName);
}
public static boolean eaglerMove(String pathNameOld, String pathNameNew) {
if(provider == null) throwNotInitialized();
return provider.eaglerMove(pathNameOld, pathNameNew);
}
public static int eaglerCopy(String pathNameOld, String pathNameNew) {
if(provider == null) throwNotInitialized();
return provider.eaglerCopy(pathNameOld, pathNameNew);
}
public static int eaglerSize(String pathName) {
if(provider == null) throwNotInitialized();
return provider.eaglerSize(pathName);
}
public static void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive) {
if(provider == null) throwNotInitialized();
provider.eaglerIterate(pathName, itr, recursive);
}
public static void platformShutdown() {
if(provider != null) {
if(provider instanceof JDBCFilesystem) {
((JDBCFilesystem)provider).shutdown();
}
provider = null;
}
}
}

View File

@ -2,15 +2,20 @@ package net.lax1dude.eaglercraft.v1_8.internal;
import static org.lwjgl.glfw.GLFW.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import org.lwjgl.PointerBuffer;
import org.lwjgl.glfw.GLFWGamepadState;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.system.MemoryStack;
/**
* Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved.
* Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -34,6 +39,7 @@ public class PlatformInput {
private static boolean windowFocused = true;
private static boolean windowResized = true;
private static boolean windowResized2 = true;
private static boolean windowCursorEntered = true;
private static boolean windowMouseGrabbed = false;
@ -46,7 +52,7 @@ public class PlatformInput {
private static int windowWidth = 640;
private static int windowHeight = 480;
private static final List<KeyboardEvent> keyboardEventList = new LinkedList();
private static final List<KeyboardEvent> keyboardEventList = new LinkedList<>();
private static KeyboardEvent currentKeyboardEvent = null;
private static final char[] keyboardReleaseEventChars = new char[256];
@ -56,7 +62,7 @@ public class PlatformInput {
public static boolean lockKeys = false;
private static final List<Character> keyboardCharList = new LinkedList();
private static final List<Character> keyboardCharList = new LinkedList<>();
private static boolean vsync = true;
private static boolean glfwVSyncState = false;
@ -75,8 +81,8 @@ public class PlatformInput {
}
}
private static final List<MouseEvent> mouseEventList = new LinkedList();
private static final List<MouseEvent> mouseEventList = new LinkedList<>();
private static MouseEvent currentMouseEvent = null;
private static class MouseEvent {
@ -97,6 +103,44 @@ public class PlatformInput {
}
private static final List<Gamepad> gamepadList = new ArrayList<>();
private static int selectedGamepad = -1;
private static String selectedGamepadName = null;
private static String selectedGamepadUUID = null;
private static final boolean[] gamepadButtonStates = new boolean[24];
private static final float[] gamepadAxisStates = new float[4];
private static class Gamepad {
protected final int gamepadId;
protected final String gamepadName;
protected final String gamepadUUID;
protected Gamepad(int gamepadId, String gamepadName, String gamepadUUID) {
this.gamepadId = gamepadId;
this.gamepadName = gamepadName;
this.gamepadUUID = gamepadUUID;
}
@Override
public int hashCode() {
return Objects.hash(gamepadId, gamepadName, gamepadUUID);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Gamepad other = (Gamepad) obj;
return gamepadId == other.gamepadId && Objects.equals(gamepadName, other.gamepadName)
&& Objects.equals(gamepadUUID, other.gamepadUUID);
}
}
static void initHooks(long glfwWindow) {
win = glfwWindow;
@ -121,13 +165,20 @@ public class PlatformInput {
windowWidth = v1[0];
windowHeight = v2[0];
windowResized = true;
windowResized2 = true;
glfwSetFramebufferSizeCallback(glfwWindow, (window, width, height) -> {
windowWidth = width;
windowHeight = height;
windowResized = true;
if(windowWidth != width || windowHeight != height) {
windowWidth = width;
windowHeight = height;
windowResized = true;
windowResized2 = true;
}
});
windowFocused = true;
glfwSetWindowFocusCallback(glfwWindow, (window, focused) -> {
windowFocused = focused;
});
@ -199,6 +250,15 @@ public class PlatformInput {
if(!fullscreen && startupFullscreen) {
toggleFullscreen();
}
gamepadEnumerateDevices();
glfwSetJoystickCallback((jid, event) -> {
if(event == GLFW_DISCONNECTED && jid == selectedGamepad) {
selectedGamepad = -1;
}
gamepadEnumerateDevices();
});
}
public static int getWindowWidth() {
@ -209,6 +269,22 @@ public class PlatformInput {
return windowHeight;
}
public static int getVisualViewportX() {
return 0;
}
public static int getVisualViewportY() {
return 0;
}
public static int getVisualViewportW() {
return windowWidth;
}
public static int getVisualViewportH() {
return windowHeight;
}
public static boolean getWindowFocused() {
return windowFocused;
}
@ -240,6 +316,12 @@ public class PlatformInput {
return b;
}
public static boolean wasVisualViewportResized() {
boolean b = windowResized2;
windowResized2 = false;
return b;
}
public static boolean keyboardNext() {
if(keyboardEventList.size() > 0) {
currentKeyboardEvent = keyboardEventList.remove(0);
@ -274,6 +356,33 @@ public class PlatformInput {
}
}
public static void keyboardFireEvent(EnumFireKeyboardEvent eventType, int eagKey, char keyChar) {
switch(eventType) {
case KEY_DOWN:
keyboardCharList.add(keyChar);
keyboardEventList.add(new KeyboardEvent(eagKey, true, false));
break;
case KEY_UP:
if(eagKey >= 0 && eagKey < keyboardReleaseEventChars.length) {
keyboardReleaseEventChars[eagKey] = keyChar;
}
keyboardEventList.add(new KeyboardEvent(eagKey, false, false));
break;
case KEY_REPEAT:
keyboardCharList.add(keyChar);
keyboardEventList.add(new KeyboardEvent(eagKey, true, true));
break;
default:
throw new UnsupportedOperationException();
}
if(keyboardEventList.size() > 64) {
keyboardEventList.remove(0);
}
if(keyboardCharList.size() > 64) {
keyboardCharList.remove(0);
}
}
public static boolean keyboardGetEventKeyState() {
return currentKeyboardEvent.pressed;
}
@ -315,6 +424,44 @@ public class PlatformInput {
}
}
public static void mouseFireMoveEvent(EnumFireMouseEvent eventType, int posX, int posY) {
if(eventType == EnumFireMouseEvent.MOUSE_MOVE) {
mouseEventList.add(new MouseEvent(-1, false, posX, posY, 0.0f));
if(mouseEventList.size() > 64) {
mouseEventList.remove(0);
}
}else {
throw new UnsupportedOperationException();
}
}
public static void mouseFireButtonEvent(EnumFireMouseEvent eventType, int posX, int posY, int button) {
switch(eventType) {
case MOUSE_DOWN:
mouseEventList.add(new MouseEvent(button, true, posX, posY, 0.0f));
break;
case MOUSE_UP:
mouseEventList.add(new MouseEvent(button, false, posX, posY, 0.0f));
break;
default:
throw new UnsupportedOperationException();
}
if(mouseEventList.size() > 64) {
mouseEventList.remove(0);
}
}
public static void mouseFireWheelEvent(EnumFireMouseEvent eventType, int posX, int posY, float wheel) {
if(eventType == EnumFireMouseEvent.MOUSE_WHEEL) {
mouseEventList.add(new MouseEvent(-1, false, posX, posY, wheel));
if(mouseEventList.size() > 64) {
mouseEventList.remove(0);
}
}else {
throw new UnsupportedOperationException();
}
}
public static boolean mouseGetEventButtonState() {
return currentMouseEvent.pressed;
}
@ -366,6 +513,10 @@ public class PlatformInput {
}
}
public static boolean mouseGrabSupported() {
return true;
}
public static boolean isPointerLocked() {
return windowMouseGrabbed;
}
@ -404,6 +555,10 @@ public class PlatformInput {
functionKeyModifier = KeyboardConstants.getGLFWKeyFromEagler(key);
}
public static boolean supportsFullscreen() {
return true;
}
private static boolean fullscreen = false;
private static boolean startupFullscreen = false;
private static int[] lastPos = new int[4];
@ -483,4 +638,247 @@ public class PlatformInput {
break;
}
}
public static boolean touchNext() {
return false;
}
public static EnumTouchEvent touchGetEventType() {
return null;
}
public static int touchGetEventTouchPointCount() {
return 0;
}
public static int touchGetEventTouchX(int pointId) {
return 0;
}
public static int touchGetEventTouchY(int pointId) {
return 0;
}
public static float touchGetEventTouchRadiusX(int pointId) {
return 0.0f;
}
public static float touchGetEventTouchRadiusY(int pointId) {
return 0.0f;
}
public static float touchGetEventTouchRadiusMixed(int pointId) {
return touchGetEventTouchRadiusX(pointId) * 0.5f + touchGetEventTouchRadiusY(pointId) * 0.5f;
}
public static float touchGetEventTouchForce(int pointId) {
return 0.0f;
}
public static int touchGetEventTouchPointUID(int pointId) {
return 0;
}
public static int touchPointCount() {
return 0;
}
public static int touchPointX(int pointId) {
return 0;
}
public static int touchPointY(int pointId) {
return 0;
}
public static float touchRadiusX(int pointId) {
return 0.0f;
}
public static float touchRadiusY(int pointId) {
return 0.0f;
}
public static float touchRadiusMixed(int pointId) {
return touchRadiusX(pointId) * 0.5f + touchRadiusY(pointId) * 0.5f;
}
public static float touchForce(int pointId) {
return 0.0f;
}
public static int touchPointUID(int pointId) {
return 0;
}
public static void touchSetOpenKeyboardZone(int x, int y, int w, int h) {
}
public static void touchCloseDeviceKeyboard() {
}
public static boolean touchIsDeviceKeyboardOpenMAYBE() {
return false;
}
public static String touchGetPastedString() {
return null;
}
private static void gamepadEnumerateDevices() {
if(selectedGamepad != -1 && !glfwJoystickIsGamepad(selectedGamepad)) {
selectedGamepad = -1;
}
List<Gamepad> oldList = null;
if(!gamepadList.isEmpty()) {
oldList = new ArrayList<>(gamepadList);
gamepadList.clear();
}
for(int i = GLFW_JOYSTICK_1; i <= GLFW_JOYSTICK_16; ++i) {
if(glfwJoystickIsGamepad(i)) {
gamepadList.add(new Gamepad(i, gamepadMakeName(i), glfwGetJoystickGUID(i)));
}
}
vigg: if(selectedGamepad != -1) {
for(int i = 0, l = gamepadList.size(); i < l; ++i) {
Gamepad gp = gamepadList.get(i);
if(gp.gamepadId == selectedGamepad && gp.gamepadUUID.equals(selectedGamepadUUID)) {
break vigg;
}
}
selectedGamepad = -1;
}
if(oldList == null) {
if(!gamepadList.isEmpty()) {
for(int i = 0, l = gamepadList.size(); i < l; ++i) {
PlatformRuntime.logger.info("Found controller: {}", gamepadList.get(i).gamepadName);
}
}
}else {
if(gamepadList.isEmpty()) {
for(int i = 0, l = oldList.size(); i < l; ++i) {
PlatformRuntime.logger.info("Lost controller: {}", oldList.get(i).gamepadName);
}
}else {
Set<String> oldGamepadUUIDs = new HashSet<>();
for(int i = 0, l = oldList.size(); i < l; ++i) {
oldGamepadUUIDs.add(oldList.get(i).gamepadUUID);
}
Set<String> newGamepadUUIDs = new HashSet<>();
for(int i = 0, l = gamepadList.size(); i < l; ++i) {
newGamepadUUIDs.add(gamepadList.get(i).gamepadUUID);
}
for(int i = 0, l = oldList.size(); i < l; ++i) {
Gamepad g = oldList.get(i);
if(!newGamepadUUIDs.contains(g.gamepadUUID)) {
PlatformRuntime.logger.info("Lost controller: {}", g.gamepadName);
}
}
for(int i = 0, l = gamepadList.size(); i < l; ++i) {
Gamepad g = gamepadList.get(i);
if(!oldGamepadUUIDs.contains(g.gamepadUUID)) {
PlatformRuntime.logger.info("Found controller: {}", g.gamepadName);
}
}
}
}
}
private static String gamepadMakeName(int glfwId) {
String s = glfwGetGamepadName(glfwId);
if(s.endsWith(" (GLFW)")) {
s = s.substring(0, s.length() - 7);
}
return glfwGetJoystickName(glfwId) + " (" + s + ")";
}
public static int gamepadGetValidDeviceCount() {
return gamepadList.size();
}
public static String gamepadGetDeviceName(int deviceId) {
if(deviceId >= 0 && deviceId < gamepadList.size()) {
return gamepadList.get(deviceId).gamepadName;
}else {
return "Unknown";
}
}
public static void gamepadSetSelectedDevice(int deviceId) {
gamepadReset();
if(deviceId >= 0 && deviceId < gamepadList.size()) {
selectedGamepad = gamepadList.get(deviceId).gamepadId;
if(!glfwJoystickIsGamepad(selectedGamepad)) {
selectedGamepad = -1;
}
}else {
selectedGamepad = -1;
}
}
private static void gamepadReset() {
for(int i = 0; i < gamepadButtonStates.length; ++i) {
gamepadButtonStates[i] = false;
}
for(int i = 0; i < gamepadAxisStates.length; ++i) {
gamepadAxisStates[i] = 0.0f;
}
}
public static void gamepadUpdate() {
gamepadReset();
if(selectedGamepad != -1) {
if(!glfwJoystickIsGamepad(selectedGamepad)) {
selectedGamepad = -1;
return;
}
try(MemoryStack ms = MemoryStack.stackPush()) {
GLFWGamepadState state = GLFWGamepadState.calloc(ms);
glfwGetGamepadState(selectedGamepad, state);
java.nio.FloatBuffer axes = state.axes();
axes.get(gamepadAxisStates);
java.nio.ByteBuffer buttons = state.buttons();
for(int i = 0, l = buttons.remaining(); i < l && i < gamepadButtonStates.length; ++i) {
boolean v = buttons.get() != (byte)0;
int j = GamepadConstants.getEaglerButtonFromGLFW(i);
if(j != -1) {
gamepadButtonStates[j] = v;
}
}
gamepadButtonStates[GamepadConstants.GAMEPAD_LEFT_TRIGGER] = axes.get() > 0.4f;
gamepadButtonStates[GamepadConstants.GAMEPAD_RIGHT_TRIGGER] = axes.get() > 0.4f;
}
}
}
public static boolean gamepadIsValid() {
return selectedGamepad != -1;
}
public static String gamepadGetName() {
return selectedGamepad != -1 ? selectedGamepadName : "Unknown";
}
public static boolean gamepadGetButtonState(int button) {
return selectedGamepad != -1 && button >= 0 && button < gamepadButtonStates.length ? gamepadButtonStates[button] : false;
}
public static float gamepadGetAxis(int axis) {
return selectedGamepad != -1 && axis >= 0 && axis < gamepadAxisStates.length ? gamepadAxisStates[axis] : 0.0f;
}
public static float getDPI() {
float[] dpiX = new float[1];
float[] dpiY = new float[1];
glfwGetWindowContentScale(win, dpiX, dpiY);
float ret = dpiX[0] * 0.5f + dpiY[0] * 0.5f;
if(ret <= 0.0f) {
ret = 1.0f;
}
return ret;
}
}

View File

@ -2,17 +2,13 @@ package net.lax1dude.eaglercraft.v1_8.internal;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.java_websocket.enums.ReadyState;
import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.DesktopWebSocketClient;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
/**
* Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved.
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -28,124 +24,22 @@ import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
*/
public class PlatformNetworking {
static final Logger networkLogger = LogManager.getLogger("PlatformNetworking");
private static final Logger logger = LogManager.getLogger("PlatformNetworking");
private static WebSocketPlayClient wsPlayClient = null;
static EnumEaglerConnectionState playConnectState = EnumEaglerConnectionState.CLOSED;
static EnumServerRateLimit serverRateLimit = null;
static String currentURI = null;
public static EnumEaglerConnectionState playConnectionState() {
return ((wsPlayClient == null || wsPlayClient.isClosed()) && playConnectState == EnumEaglerConnectionState.CONNECTING) ? EnumEaglerConnectionState.FAILED :
((wsPlayClient != null && wsPlayClient.getReadyState() == ReadyState.NOT_YET_CONNECTED) ? EnumEaglerConnectionState.CONNECTING :
(((wsPlayClient == null || wsPlayClient.isClosed()) && playConnectState != EnumEaglerConnectionState.FAILED) ? EnumEaglerConnectionState.CLOSED : playConnectState));
}
public static void startPlayConnection(String destination) {
if(!playConnectionState().isClosed()) {
networkLogger.warn("Tried connecting to a server while already connected to a different server!");
playDisconnect();
}
currentURI = destination;
synchronized(playPackets) {
playPackets.clear();
}
playConnectState = EnumEaglerConnectionState.CONNECTING;
networkLogger.info("Connecting to server: {}", destination);
URI u;
public static IWebSocketClient openWebSocket(String socketURI) {
try {
u = new URI(destination);
}catch(URISyntaxException ex) {
networkLogger.error("Invalid server URI: {}", destination);
playConnectState = EnumEaglerConnectionState.FAILED;
return;
}
wsPlayClient = new WebSocketPlayClient(u);
wsPlayClient.connect();
}
public static void playDisconnect() {
if(!playConnectionState().isClosed() && wsPlayClient != null) {
try {
wsPlayClient.closeBlocking();
} catch (InterruptedException e) {
// :(
}
playConnectState = EnumEaglerConnectionState.CLOSED;
}
}
private static final List<byte[]> playPackets = new LinkedList();
public static byte[] readPlayPacket() {
synchronized(playPackets) {
return playPackets.size() > 0 ? playPackets.remove(0) : null;
}
}
public static List<byte[]> readAllPacket() {
synchronized(playPackets) {
if(!playPackets.isEmpty()) {
List<byte[]> ret = new ArrayList<>(playPackets);
playPackets.clear();
return ret;
}else {
return null;
}
}
}
public static int countAvailableReadData() {
int total = 0;
synchronized(playPackets) {
for(int i = 0, l = playPackets.size(); i < l; ++i) {
total += playPackets.get(i).length;
}
}
return total;
}
static void recievedPlayPacket(byte[] arg0) {
synchronized(playPackets) {
playPackets.add(arg0);
}
}
public static void writePlayPacket(byte[] pkt) {
if(wsPlayClient == null || wsPlayClient.isClosed()) {
networkLogger.error("Tried to send {} byte play packet while the socket was closed!", pkt.length);
}else {
wsPlayClient.send(pkt);
}
}
public static IServerQuery sendServerQuery(String uri, String accept) {
URI u;
try {
u = new URI(uri);
}catch(URISyntaxException ex) {
networkLogger.error("Invalid server URI: {}", uri);
playConnectState = EnumEaglerConnectionState.FAILED;
URI uri = new URI(socketURI);
return new DesktopWebSocketClient(uri);
}catch(Throwable t) {
logger.error("Could not open WebSocket to \"{}\"!", socketURI);
logger.error(t);
return null;
}
return new WebSocketServerQuery(accept, u);
}
public static EnumServerRateLimit getRateLimit() {
return serverRateLimit == null ? EnumServerRateLimit.OK : serverRateLimit;
}
public static String getCurrentURI() {
return currentURI;
public static IWebSocketClient openWebSocketUnsafe(String socketURI) throws URISyntaxException {
URI uri = new URI(socketURI);
return new DesktopWebSocketClient(uri);
}
}

View File

@ -6,6 +6,13 @@ import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer;
import static org.lwjgl.opengles.GLES30.*;
import static org.lwjgl.opengles.ANGLEInstancedArrays.*;
import static org.lwjgl.opengles.EXTInstancedArrays.*;
import static org.lwjgl.opengles.EXTTextureStorage.*;
import static org.lwjgl.opengles.OESVertexArrayObject.*;
import java.util.ArrayList;
import java.util.List;
import org.lwjgl.opengles.GLESCapabilities;
@ -26,10 +33,103 @@ import org.lwjgl.opengles.GLESCapabilities;
*/
public class PlatformOpenGL {
private static int glesVers = -1;
private static boolean hasANGLEInstancedArrays = false;
private static boolean hasEXTColorBufferFloat = false;
private static boolean hasEXTColorBufferHalfFloat = false;
private static boolean hasEXTGPUShader5 = false;
private static boolean hasEXTInstancedArrays = false;
private static boolean hasEXTShaderTextureLOD = false;
private static boolean hasEXTTextureStorage = false;
private static boolean hasOESFBORenderMipmap = false;
private static boolean hasOESGPUShader5 = false;
private static boolean hasOESVertexArrayObject = false;
private static boolean hasOESTextureFloat = false;
private static boolean hasOESTextureFloatLinear = false;
private static boolean hasOESTextureHalfFloat = false;
private static boolean hasOESTextureHalfFloatLinear = false;
private static boolean hasEXTTextureFilterAnisotropic = false;
private static boolean hasFBO16FSupport = false;
private static boolean hasFBO32FSupport = false;
private static boolean hasLinearHDR16FSupport = false;
private static boolean hasLinearHDR32FSupport = false;
static void setCurrentContext(GLESCapabilities caps) {
private static final int VAO_IMPL_NONE = -1;
private static final int VAO_IMPL_CORE = 0;
private static final int VAO_IMPL_OES = 1;
private static int vertexArrayImpl = VAO_IMPL_NONE;
private static final int INSTANCE_IMPL_NONE = -1;
private static final int INSTANCE_IMPL_CORE = 0;
private static final int INSTANCE_IMPL_ANGLE = 1;
private static final int INSTANCE_IMPL_EXT = 2;
private static int instancingImpl = INSTANCE_IMPL_NONE;
private static final int TEX_STORAGE_IMPL_NONE = -1;
private static final int TEX_STORAGE_IMPL_CORE = 0;
private static final int TEX_STORAGE_IMPL_EXT = 1;
private static int texStorageImpl = TEX_STORAGE_IMPL_NONE;
static void setCurrentContext(int glesVersIn, GLESCapabilities caps) {
glesVers = glesVersIn;
hasANGLEInstancedArrays = glesVersIn == 200 && caps.GL_ANGLE_instanced_arrays;
hasOESTextureFloat = glesVersIn == 200 && caps.GL_OES_texture_float;
hasOESTextureFloatLinear = glesVersIn >= 300 && caps.GL_OES_texture_float_linear;
hasOESTextureHalfFloat = glesVersIn == 200 && caps.GL_OES_texture_half_float;
hasOESTextureHalfFloatLinear = glesVersIn == 200 && caps.GL_OES_texture_half_float_linear;
hasEXTColorBufferFloat = (glesVersIn == 310 || glesVersIn == 300) && caps.GL_EXT_color_buffer_float;
hasEXTColorBufferHalfFloat = !hasEXTColorBufferFloat
&& (glesVersIn == 310 || glesVersIn == 300 || glesVersIn == 200) && caps.GL_EXT_color_buffer_half_float;
hasEXTInstancedArrays = !hasANGLEInstancedArrays && glesVersIn == 200 && caps.GL_EXT_instanced_arrays;
hasEXTShaderTextureLOD = glesVersIn == 200 && caps.GL_EXT_shader_texture_lod;
hasEXTTextureStorage = glesVersIn == 200 && caps.GL_EXT_texture_storage;
hasOESGPUShader5 = glesVersIn == 310 && caps.GL_OES_gpu_shader5;
hasEXTGPUShader5 = !hasOESGPUShader5 && glesVersIn == 310 && caps.GL_EXT_gpu_shader5;
hasOESFBORenderMipmap = glesVersIn == 200 && caps.GL_OES_fbo_render_mipmap;
hasOESVertexArrayObject = glesVersIn == 200 && caps.GL_OES_vertex_array_object;
hasLinearHDR32FSupport = caps.GL_OES_texture_float_linear;
hasEXTTextureFilterAnisotropic = caps.GL_EXT_texture_filter_anisotropic;
hasFBO16FSupport = glesVersIn >= 320 || ((glesVersIn >= 300 || hasOESTextureFloat) && (hasEXTColorBufferFloat || hasEXTColorBufferHalfFloat));
hasFBO32FSupport = glesVersIn >= 320 || ((glesVersIn >= 300 || hasOESTextureHalfFloat) && hasEXTColorBufferFloat);
hasLinearHDR16FSupport = glesVersIn >= 300 || hasOESTextureHalfFloatLinear;
hasLinearHDR32FSupport = glesVersIn >= 300 && hasOESTextureFloatLinear;
if(glesVersIn >= 300) {
vertexArrayImpl = VAO_IMPL_CORE;
instancingImpl = INSTANCE_IMPL_CORE;
texStorageImpl = TEX_STORAGE_IMPL_CORE;
}else if(glesVersIn == 200) {
vertexArrayImpl = hasOESVertexArrayObject ? VAO_IMPL_OES : VAO_IMPL_NONE;
instancingImpl = hasANGLEInstancedArrays ? INSTANCE_IMPL_ANGLE : (hasEXTInstancedArrays ? INSTANCE_IMPL_EXT : INSTANCE_IMPL_NONE);
texStorageImpl = hasEXTTextureStorage ? TEX_STORAGE_IMPL_EXT : TEX_STORAGE_IMPL_NONE;
}else {
vertexArrayImpl = VAO_IMPL_NONE;
instancingImpl = INSTANCE_IMPL_NONE;
texStorageImpl = TEX_STORAGE_IMPL_NONE;
}
}
public static final List<String> dumpActiveExtensions() {
List<String> exts = new ArrayList<>();
if(hasANGLEInstancedArrays) exts.add("ANGLE_instanced_arrays");
if(hasEXTColorBufferFloat) exts.add("EXT_color_buffer_float");
if(hasEXTColorBufferHalfFloat) exts.add("EXT_color_buffer_half_float");
if(hasEXTGPUShader5) exts.add("EXT_gpu_shader5");
if(hasEXTInstancedArrays) exts.add("EXT_instanced_arrays");
if(hasEXTTextureStorage) exts.add("EXT_texture_storage");
if(hasOESFBORenderMipmap) exts.add("OES_fbo_render_mipmap");
if(hasOESGPUShader5) exts.add("OES_gpu_shader5");
if(hasOESVertexArrayObject) exts.add("OES_vertex_array_object");
if(hasOESTextureFloat) exts.add("OES_texture_float");
if(hasOESTextureFloatLinear) exts.add("OES_texture_float_linear");
if(hasOESTextureHalfFloat) exts.add("OES_texture_half_float");
if(hasOESTextureHalfFloatLinear) exts.add("OES_texture_half_float_linear");
if(hasEXTTextureFilterAnisotropic) exts.add("EXT_texture_filter_anisotropic");
return exts;
}
public static final void _wglEnable(int glEnum) {
@ -89,17 +189,45 @@ public class PlatformOpenGL {
}
public static final void _wglDrawBuffers(int buffer) {
glDrawBuffers(buffer);
if(glesVers == 200) {
if(buffer != 0x8CE0) { // GL_COLOR_ATTACHMENT0
throw new UnsupportedOperationException();
}
}else {
glDrawBuffers(buffer);
}
}
public static final void _wglDrawBuffers(int[] buffers) {
glDrawBuffers(buffers);
if(glesVers == 200) {
if(buffers.length != 1 || buffers[0] != 0x8CE0) { // GL_COLOR_ATTACHMENT0
throw new UnsupportedOperationException();
}
}else {
glDrawBuffers(buffers);
}
}
public static final void _wglReadBuffer(int buffer) {
glReadBuffer(buffer);
}
public static final void _wglReadPixels(int x, int y, int width, int height, int format, int type, ByteBuffer data) {
nglReadPixels(x, y, width, height, format, type, EaglerLWJGLAllocator.getAddress(data));
}
public static final void _wglReadPixels_u16(int x, int y, int width, int height, int format, int type, ByteBuffer data) {
nglReadPixels(x, y, width, height, format, type, EaglerLWJGLAllocator.getAddress(data));
}
public static final void _wglReadPixels(int x, int y, int width, int height, int format, int type, IntBuffer data) {
nglReadPixels(x, y, width, height, format, type, EaglerLWJGLAllocator.getAddress(data));
}
public static final void _wglReadPixels(int x, int y, int width, int height, int format, int type, FloatBuffer data) {
nglReadPixels(x, y, width, height, format, type, EaglerLWJGLAllocator.getAddress(data));
}
public static final void _wglPolygonOffset(float f1, float f2) {
glPolygonOffset(f1, f2);
}
@ -117,7 +245,14 @@ public class PlatformOpenGL {
}
public static final IBufferArrayGL _wglGenVertexArrays() {
return new OpenGLObjects.BufferArrayGL(glGenVertexArrays());
switch(vertexArrayImpl) {
case VAO_IMPL_CORE:
return new OpenGLObjects.BufferArrayGL(glGenVertexArrays());
case VAO_IMPL_OES:
return new OpenGLObjects.BufferArrayGL(glGenVertexArraysOES());
default:
throw new UnsupportedOperationException();
}
}
public static final IProgramGL _wglCreateProgram() {
@ -149,7 +284,17 @@ public class PlatformOpenGL {
}
public static final void _wglDeleteVertexArrays(IBufferArrayGL obj) {
glDeleteVertexArrays(((OpenGLObjects.BufferArrayGL) obj).ptr);
int ptr = ((OpenGLObjects.BufferArrayGL) obj).ptr;
switch(vertexArrayImpl) {
case VAO_IMPL_CORE:
glDeleteVertexArrays(ptr);
break;
case VAO_IMPL_OES:
glDeleteVertexArraysOES(ptr);
break;
default:
throw new UnsupportedOperationException();
}
}
public static final void _wglDeleteProgram(IProgramGL obj) {
@ -211,7 +356,17 @@ public class PlatformOpenGL {
}
public static final void _wglBindVertexArray(IBufferArrayGL obj) {
glBindVertexArray(obj == null ? 0 : ((OpenGLObjects.BufferArrayGL) obj).ptr);
int ptr = obj == null ? 0 : ((OpenGLObjects.BufferArrayGL) obj).ptr;
switch(vertexArrayImpl) {
case VAO_IMPL_CORE:
glBindVertexArray(ptr);
break;
case VAO_IMPL_OES:
glBindVertexArrayOES(ptr);
break;
default:
throw new UnsupportedOperationException();
}
}
public static final void _wglEnableVertexAttribArray(int index) {
@ -228,7 +383,19 @@ public class PlatformOpenGL {
}
public static final void _wglVertexAttribDivisor(int index, int divisor) {
glVertexAttribDivisor(index, divisor);
switch(instancingImpl) {
case INSTANCE_IMPL_CORE:
glVertexAttribDivisor(index, divisor);
break;
case INSTANCE_IMPL_ANGLE:
glVertexAttribDivisorANGLE(index, divisor);
break;
case INSTANCE_IMPL_EXT:
glVertexAttribDivisorEXT(index, divisor);
break;
default:
throw new UnsupportedOperationException();
}
}
public static final void _wglActiveTexture(int texture) {
@ -313,7 +480,16 @@ public class PlatformOpenGL {
}
public static final void _wglTexStorage2D(int target, int levels, int internalFormat, int w, int h) {
glTexStorage2D(target, levels, internalFormat, w, h);
switch(texStorageImpl) {
case TEX_STORAGE_IMPL_CORE:
glTexStorage2D(target, levels, internalFormat, w, h);
break;
case TEX_STORAGE_IMPL_EXT:
glTexStorage2DEXT(target, levels, internalFormat, w, h);
break;
default:
throw new UnsupportedOperationException();
}
}
public static final void _wglPixelStorei(int pname, int value) {
@ -377,7 +553,19 @@ public class PlatformOpenGL {
}
public static final void _wglDrawArraysInstanced(int mode, int first, int count, int instanced) {
glDrawArraysInstanced(mode, first, count, instanced);
switch(instancingImpl) {
case INSTANCE_IMPL_CORE:
glDrawArraysInstanced(mode, first, count, instanced);
break;
case INSTANCE_IMPL_ANGLE:
glDrawArraysInstancedANGLE(mode, first, count, instanced);
break;
case INSTANCE_IMPL_EXT:
glDrawArraysInstancedEXT(mode, first, count, instanced);
break;
default:
throw new UnsupportedOperationException();
}
}
public static final void _wglDrawElements(int mode, int count, int type, int offset) {
@ -385,7 +573,19 @@ public class PlatformOpenGL {
}
public static final void _wglDrawElementsInstanced(int mode, int count, int type, int offset, int instanced) {
glDrawElementsInstanced(mode, count, type, offset, instanced);
switch(instancingImpl) {
case INSTANCE_IMPL_CORE:
glDrawElementsInstanced(mode, count, type, offset, instanced);
break;
case INSTANCE_IMPL_ANGLE:
glDrawElementsInstancedANGLE(mode, count, type, offset, instanced);
break;
case INSTANCE_IMPL_EXT:
glDrawElementsInstancedEXT(mode, count, type, offset, instanced);
break;
default:
throw new UnsupportedOperationException();
}
}
public static final IUniformGL _wglGetUniformLocation(IProgramGL obj, String name) {
@ -533,11 +733,79 @@ public class PlatformOpenGL {
return glGetError();
}
public static final boolean checkHDRFramebufferSupport(int bits) {
return true;
public static final int checkOpenGLESVersion() {
return glesVers;
}
public static final boolean checkEXTGPUShader5Capable() {
return hasEXTGPUShader5;
}
public static final boolean checkOESGPUShader5Capable() {
return hasOESGPUShader5;
}
public static final boolean checkFBORenderMipmapCapable() {
return hasOESFBORenderMipmap;
}
public static final boolean checkVAOCapable() {
return vertexArrayImpl != VAO_IMPL_NONE;
}
public static final boolean checkInstancingCapable() {
return instancingImpl != INSTANCE_IMPL_NONE;
}
public static final boolean checkTexStorageCapable() {
return texStorageImpl != TEX_STORAGE_IMPL_NONE;
}
public static final boolean checkTextureLODCapable() {
return glesVers >= 300 || hasEXTShaderTextureLOD;
}
public static final boolean checkNPOTCapable() {
return glesVers >= 300;
}
public static final boolean checkHDRFramebufferSupport(int bits) {
switch(bits) {
case 16:
return hasFBO16FSupport;
case 32:
return hasFBO32FSupport;
default:
return false;
}
}
public static final boolean checkLinearHDRFilteringSupport(int bits) {
switch(bits) {
case 16:
return hasLinearHDR16FSupport;
case 32:
return hasLinearHDR32FSupport;
default:
return false;
}
}
// legacy
public static final boolean checkLinearHDR32FSupport() {
return hasLinearHDR32FSupport;
}
public static final boolean checkAnisotropicFilteringSupport() {
return hasEXTTextureFilterAnisotropic;
}
public static final String[] getAllExtensions() {
return glGetString(GL_EXTENSIONS).split(" ");
}
public static final void enterVAOEmulationHook() {
}
}

View File

@ -13,7 +13,9 @@ import java.io.OutputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.function.Consumer;
import java.util.zip.DeflaterOutputStream;
@ -31,6 +33,7 @@ import org.lwjgl.opengles.GLDebugMessageKHRCallback;
import org.lwjgl.opengles.GLDebugMessageKHRCallbackI;
import org.lwjgl.opengles.GLES;
import org.lwjgl.opengles.GLES30;
import org.lwjgl.opengles.GLESCapabilities;
import org.lwjgl.opengles.KHRDebug;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
@ -38,16 +41,19 @@ import org.lwjgl.system.jemalloc.JEmalloc;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.EaglerLWJGLAllocator;
import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream;
import net.lax1dude.eaglercraft.v1_8.Filesystem;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
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.lwjgl.DesktopClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFolderResourcePack;
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.ServerPlatformSingleplayer;
/**
* Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved.
* Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -74,7 +80,22 @@ public class PlatformRuntime {
public static void create() {
logger.info("Starting Desktop Runtime...");
PlatformFilesystem.initialize();
ByteBuffer endiannessTestBytes = allocateByteBuffer(4);
try {
endiannessTestBytes.asIntBuffer().put(0x6969420);
if (((endiannessTestBytes.get(0) & 0xFF) | ((endiannessTestBytes.get(1) & 0xFF) << 8)
| ((endiannessTestBytes.get(2) & 0xFF) << 16) | ((endiannessTestBytes.get(3) & 0xFF) << 24)) != 0x6969420) {
throw new UnsupportedOperationException("Big endian CPU detected! (somehow)");
}else {
logger.info("Endianness: this CPU is little endian");
}
}finally {
freeByteBuffer(endiannessTestBytes);
}
IEaglerFilesystem resourcePackFilesystem = Filesystem.getHandleFor(getClientConfigAdapter().getResourcePacksDB());
VFile2.setPrimaryFilesystem(resourcePackFilesystem);
EaglerFolderResourcePack.setSupported(true);
if(requestedANGLEPlatform != EnumPlatformANGLE.DEFAULT) {
@ -91,23 +112,54 @@ public class PlatformRuntime {
glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_CENTER_CURSOR, GLFW_TRUE);
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
PointerBuffer buf = glfwGetMonitors();
GLFWVidMode mon = glfwGetVideoMode(buf.get(0));
int windowWidth = mon.width() - 200;
int windowHeight = mon.height() - 250;
String title = "Eaglercraft Desktop Runtime";
int winX = (mon.width() - windowWidth) / 2;
int winY = (mon.height() - windowHeight - 20) / 2;
windowHandle = glfwCreateWindow(windowWidth, windowHeight, "Eaglercraft Desktop Runtime", 0l, 0l);
int myGLVersion = -1;
if(requestedGLVersion >= 310 && windowHandle == 0) {
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
windowHandle = glfwCreateWindow(windowWidth, windowHeight, title, 0l, 0l);
if(windowHandle == 0l) {
logger.error("Failed to create OpenGL ES 3.1 context!");
}else {
myGLVersion = 310;
}
}
if(requestedGLVersion >= 300 && windowHandle == 0) {
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
windowHandle = glfwCreateWindow(windowWidth, windowHeight, title, 0l, 0l);
if(windowHandle == 0l) {
logger.error("Failed to create OpenGL ES 3.0 context!");
}else {
myGLVersion = 300;
}
}
if(requestedGLVersion >= 200 && windowHandle == 0) {
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
windowHandle = glfwCreateWindow(windowWidth, windowHeight, title, 0l, 0l);
if(windowHandle == 0l) {
logger.error("Failed to create OpenGL ES 2.0 context!");
}else {
myGLVersion = 200;
}
}
if(myGLVersion == -1) {
throw new RuntimeException("Could not create a supported OpenGL ES context!");
}
glfwSetWindowPos(windowHandle, winX, winY);
@ -171,13 +223,28 @@ public class PlatformRuntime {
EGL.createDisplayCapabilities(glfw_eglHandle, major[0], minor[0]);
glfwMakeContextCurrent(windowHandle);
PlatformOpenGL.setCurrentContext(GLES.createCapabilities());
GLESCapabilities caps = GLES.createCapabilities();
PlatformOpenGL.setCurrentContext(myGLVersion, caps);
logger.info("OpenGL Version: {}", (glVersion = GLES30.glGetString(GLES30.GL_VERSION)));
logger.info("OpenGL Renderer: {}", (glRenderer = GLES30.glGetString(GLES30.GL_RENDERER)));
rendererANGLEPlatform = EnumPlatformANGLE.fromGLRendererString(glRenderer);
int realGLVersion = (glVersion != null && probablyGLES2(glVersion)) ? 200
: (GLES30.glGetInteger(GLES30.GL_MAJOR_VERSION) * 100
+ GLES30.glGetInteger(GLES30.GL_MINOR_VERSION) * 10);
if(realGLVersion != myGLVersion) {
logger.warn("Unexpected GLES verison resolved for requested {} context: {}", myGLVersion, realGLVersion);
if(myGLVersion == 200) {
logger.warn("Note: try adding the \"d3d9\" option if you are on windows trying to get GLES 2.0");
}
if(realGLVersion != 320 && realGLVersion != 310 && realGLVersion != 300 && realGLVersion != 200) {
throw new RuntimeException("Unsupported OpenGL ES version detected: " + realGLVersion);
}
myGLVersion = realGLVersion;
PlatformOpenGL.setCurrentContext(myGLVersion, caps);
}
if(requestedANGLEPlatform != EnumPlatformANGLE.DEFAULT
&& rendererANGLEPlatform != requestedANGLEPlatform) {
logger.warn("Incorrect ANGLE Platform: {}", rendererANGLEPlatform.name);
@ -187,6 +254,18 @@ public class PlatformRuntime {
logger.info("ANGLE Platform: {}", rendererANGLEPlatform.name);
}
List<String> exts = PlatformOpenGL.dumpActiveExtensions();
if(exts.isEmpty()) {
logger.info("Unlocked the following OpenGL ES extensions: (NONE)");
}else {
Collections.sort(exts);
logger.info("Unlocked the following OpenGL ES extensions:");
for(int i = 0, l = exts.size(); i < l; ++i) {
logger.info(" - " + exts.get(i));
}
}
glfwSwapInterval(0);
KHRDebug.glDebugMessageCallbackKHR(new GLDebugMessageKHRCallbackI() {
@ -245,13 +324,20 @@ public class PlatformRuntime {
public static void destroy() {
PlatformAudio.platformShutdown();
PlatformFilesystem.platformShutdown();
Filesystem.closeAllHandles();
ServerPlatformSingleplayer.platformShutdown();
GLES.destroy();
EGL.destroy();
glfwDestroyWindow(windowHandle);
glfwTerminate();
}
private static boolean probablyGLES2(String glVersion) {
if(glVersion == null) return false;
glVersion = glVersion.toLowerCase();
return glVersion.contains("opengl es 2.0") || glVersion.contains("ES 2.0");
}
public static EnumPlatformType getPlatformType() {
return EnumPlatformType.DESKTOP;
}
@ -274,11 +360,16 @@ public class PlatformRuntime {
}
private static EnumPlatformANGLE requestedANGLEPlatform = EnumPlatformANGLE.DEFAULT;
private static int requestedGLVersion = 300;
public static void requestANGLE(EnumPlatformANGLE plaf) {
requestedANGLEPlatform = plaf;
}
public static void requestGL(int i) {
requestedGLVersion = i;
}
public static EnumPlatformANGLE getPlatformANGLE() {
return rendererANGLEPlatform;
}
@ -499,18 +590,6 @@ public class PlatformRuntime {
return DesktopClientConfigAdapter.instance;
}
public static String getRecText() {
return "recording.unsupported";
}
public static boolean recSupported() {
return false;
}
public static void toggleRec() {
//
}
private static final Random seedProvider = new Random();
public static long randomSeed() {
@ -530,4 +609,25 @@ public class PlatformRuntime {
public static long getWindowHandle() {
return windowHandle;
}
public static long steadyTimeMillis() {
return System.nanoTime() / 1000000l;
}
public static long nanoTime() {
return System.nanoTime();
}
public static void postCreate() {
}
public static void setDisplayBootMenuNextRefresh(boolean en) {
}
public static void immediateContinue() {
// nope
}
}

View File

@ -1,10 +1,9 @@
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;
import net.lax1dude.eaglercraft.v1_8.recording.EnumScreenRecordingCodec;
/**
* Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved.
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -18,17 +17,43 @@ import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class PlatformBufferFunctions {
public class PlatformScreenRecord {
public static void put(ByteBuffer newBuffer, ByteBuffer flip) {
newBuffer.put(flip);
public static boolean isSupported() {
return false;
}
public static void put(IntBuffer intBuffer, int index, int[] data) {
int p = intBuffer.position();
intBuffer.position(index);
intBuffer.put(data);
intBuffer.position(p);
public static boolean isCodecSupported(EnumScreenRecordingCodec codec) {
return false;
}
public static void setGameVolume(float volume) {
}
public static void setMicrophoneVolume(float volume) {
}
public static void startRecording(ScreenRecordParameters params) {
}
public static void endRecording() {
}
public static boolean isRecording() {
return false;
}
public static boolean isMicVolumeLocked() {
return false;
}
public static boolean isVSyncLocked() {
return false;
}
}

View File

@ -2,6 +2,7 @@ package net.lax1dude.eaglercraft.v1_8.internal;
import net.lax1dude.eaglercraft.v1_8.update.UpdateCertificate;
import net.lax1dude.eaglercraft.v1_8.update.UpdateProgressStruct;
import net.lax1dude.eaglercraft.v1_8.update.UpdateResultObj;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
@ -46,6 +47,15 @@ public class PlatformUpdateSvc {
return dummyStruct;
}
public static UpdateResultObj getUpdateResult() {
return null;
}
public static void installSignedClient(UpdateCertificate clientCert, byte[] clientPayload, boolean setDefault,
boolean setTimeout) {
}
public static void quine(String filename, byte[] cert, byte[] data, String date) {
}

View File

@ -4,20 +4,22 @@ import dev.onvoid.webrtc.*;
import dev.onvoid.webrtc.internal.NativeLoader;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EagUtils;
import net.lax1dude.eaglercraft.v1_8.EaglerInputStream;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.sp.lan.LANPeerEvent;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayLoggerImpl;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayQuery;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayQueryImpl;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayQueryRateLimitDummy;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServerRateLimitTracker;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServerSocket;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServerSocketImpl;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServerSocketRateLimitDummy;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayWorldsQuery;
import net.lax1dude.eaglercraft.v1_8.sp.relay.pkt.*;
import net.lax1dude.eaglercraft.v1_8.update.UpdateService;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayWorldsQueryImpl;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayWorldsQueryRateLimitDummy;
import org.apache.commons.lang3.SystemUtils;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONWriter;
@ -25,10 +27,7 @@ import org.json.JSONWriter;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import java.io.DataInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.file.Paths;
import java.util.*;
@ -52,10 +51,25 @@ public class PlatformWebRTC {
private static final Logger logger = LogManager.getLogger("PlatformWebRTC");
private static final RelayLoggerImpl loggerImpl = new RelayLoggerImpl(LogManager.getLogger("RelayPacket"));
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
private static final Object lock3 = new Object();
private static final Object lock4 = new Object();
private static final List<ScheduledRunnable> scheduledRunnables = new LinkedList<>();
private static class ScheduledRunnable {
private final long runAt;
private final Runnable runnable;
private ScheduledRunnable(long runAt, Runnable runnable) {
this.runAt = runAt;
this.runnable = runnable;
}
}
public static PeerConnectionFactory pcFactory;
@ -72,7 +86,8 @@ public class PlatformWebRTC {
Runtime.getRuntime().addShutdownHook(new Thread(() -> pcFactory.dispose()));
supported = true;
} catch (Exception e) {
EagRuntime.debugPrintStackTrace(e);
logger.error("Failed to load WebRTC native library!");
logger.error(e);
supported = false;
}
}
@ -81,6 +96,46 @@ public class PlatformWebRTC {
private static final Map<String, RTCDataChannel> fuckTeaVM = new HashMap<>();
private static final Comparator<ScheduledRunnable> sortTasks = (r1, r2) -> {
return (int)(r1.runAt - r2.runAt);
};
public static void runScheduledTasks() {
List<ScheduledRunnable> toRun = null;
synchronized(scheduledRunnables) {
if(scheduledRunnables.isEmpty()) return;
long millis = PlatformRuntime.steadyTimeMillis();
Iterator<ScheduledRunnable> itr = scheduledRunnables.iterator();
while(itr.hasNext()) {
ScheduledRunnable r = itr.next();
if(r.runAt < millis) {
itr.remove();
if(toRun == null) {
toRun = new ArrayList<>(1);
}
toRun.add(r);
}
}
}
if(toRun != null) {
Collections.sort(toRun, sortTasks);
for(int i = 0, l = toRun.size(); i < l; ++i) {
try {
toRun.get(i).runnable.run();
}catch(Throwable t) {
logger.error("Caught exception running scheduled WebRTC task!");
logger.error(t);
}
}
}
}
static void scheduleTask(long runAfter, Runnable runnable) {
synchronized(scheduledRunnables) {
scheduledRunnables.add(new ScheduledRunnable(PlatformRuntime.steadyTimeMillis() + runAfter, runnable));
}
}
public static class LANClient {
public static final byte READYSTATE_INIT_FAILED = -2;
public static final byte READYSTATE_FAILED = -1;
@ -123,15 +178,14 @@ public class PlatformWebRTC {
synchronized (lock1) {
if (iceCandidate.sdp != null && !iceCandidate.sdp.isEmpty()) {
if (iceCandidates.isEmpty()) {
new Thread(() -> {
EagUtils.sleep(3000);
scheduleTask(3000l, () -> {
synchronized (lock1) {
if (peerConnection != null && peerConnection.getConnectionState() != RTCPeerConnectionState.DISCONNECTED) {
clientICECandidate = JSONWriter.valueToString(iceCandidates);
iceCandidates.clear();
}
}
}).start();
});
}
Map<String, String> m = new HashMap<>();
m.put("sdpMLineIndex", "" + iceCandidate.sdpMLineIndex);
@ -203,7 +257,7 @@ public class PlatformWebRTC {
@Override
public void onStateChange() {
if (dataChannel != null && dataChannel.getState() == RTCDataChannelState.OPEN) {
new Thread(() -> {
scheduleTask(-1l, () -> {
while (true) {
synchronized (lock1) {
if (iceCandidates.isEmpty()) {
@ -216,7 +270,7 @@ public class PlatformWebRTC {
clientDataChannelClosed = false;
clientDataChannelOpen = true;
}
}).start();
});
}
}
@ -486,8 +540,7 @@ public class PlatformWebRTC {
synchronized (lock3) {
if (iceCandidate.sdp != null && !iceCandidate.sdp.isEmpty()) {
if (iceCandidates.isEmpty()) {
new Thread(() -> {
EagUtils.sleep(3000);
scheduleTask(3000l, () -> {
synchronized (lock3) {
if (peerConnection[0] != null && peerConnection[0].getConnectionState() != RTCPeerConnectionState.DISCONNECTED) {
LANPeerEvent.LANPeerICECandidateEvent e = new LANPeerEvent.LANPeerICECandidateEvent(peerId, JSONWriter.valueToString(iceCandidates));
@ -497,7 +550,7 @@ public class PlatformWebRTC {
iceCandidates.clear();
}
}
}).start();
});
}
Map<String, String> m = new HashMap<>();
m.put("sdpMLineIndex", "" + iceCandidate.sdpMLineIndex);
@ -509,7 +562,7 @@ public class PlatformWebRTC {
@Override
public void onDataChannel(RTCDataChannel dataChannel) {
new Thread(() -> {
scheduleTask(-1l, () -> {
while (true) {
synchronized (lock3) {
if (iceCandidates.isEmpty()) {
@ -547,7 +600,7 @@ public class PlatformWebRTC {
}
}
});
}).start();
});
}
@Override
@ -625,795 +678,27 @@ public class PlatformWebRTC {
return peerList.size();
}
}
private static final Map<String,Long> relayQueryLimited = new HashMap<>();
private static final Map<String,Long> relayQueryBlocked = new HashMap<>();
private static class RelayQueryImpl implements RelayQuery {
private final WebSocketClient sock;
private final String uri;
private boolean open;
private boolean failed;
private boolean hasRecievedAnyData = false;
private int vers = -1;
private String comment = "<no comment>";
private String brand = "<no brand>";
private long connectionOpenedAt;
private long connectionPingStart = -1;
private long connectionPingTimer = -1;
private RateLimit rateLimitStatus = RateLimit.NONE;
private VersionMismatch versError = VersionMismatch.UNKNOWN;
private RelayQueryImpl(String uri) {
this.uri = uri;
WebSocketClient s;
try {
connectionOpenedAt = System.currentTimeMillis();
s = new WebSocketClient(new URI(uri)) {
@Override
public void onOpen(ServerHandshake serverHandshake) {
try {
connectionPingStart = System.currentTimeMillis();
sock.send(IPacket.writePacket(new IPacket00Handshake(0x03, RelayManager.preferredRelayVersion, "")));
} catch (IOException e) {
logger.error(e.toString());
sock.close();
failed = true;
}
}
@Override
public void onMessage(String s) {
//
}
@Override
public void onMessage(ByteBuffer bb) {
if(bb != null) {
hasRecievedAnyData = true;
byte[] arr = new byte[bb.remaining()];
bb.get(arr);
if(arr.length == 2 && arr[0] == (byte)0xFC) {
long millis = System.currentTimeMillis();
if(arr[1] == (byte)0x00 || arr[1] == (byte)0x01) {
rateLimitStatus = RateLimit.BLOCKED;
relayQueryLimited.put(RelayQueryImpl.this.uri, millis);
}else if(arr[1] == (byte)0x02) {
rateLimitStatus = RateLimit.NOW_LOCKED;
relayQueryLimited.put(RelayQueryImpl.this.uri, millis);
relayQueryBlocked.put(RelayQueryImpl.this.uri, millis);
}else {
rateLimitStatus = RateLimit.LOCKED;
relayQueryBlocked.put(RelayQueryImpl.this.uri, millis);
}
failed = true;
open = false;
sock.close();
}else {
if(open) {
try {
IPacket pkt = IPacket.readPacket(new DataInputStream(new EaglerInputStream(arr)));
if(pkt instanceof IPacket69Pong) {
IPacket69Pong ipkt = (IPacket69Pong)pkt;
versError = VersionMismatch.COMPATIBLE;
if(connectionPingTimer == -1) {
connectionPingTimer = System.currentTimeMillis() - connectionPingStart;
}
vers = ipkt.protcolVersion;
comment = ipkt.comment;
brand = ipkt.brand;
open = false;
failed = false;
sock.close();
}else if(pkt instanceof IPacket70SpecialUpdate) {
IPacket70SpecialUpdate ipkt = (IPacket70SpecialUpdate)pkt;
if(ipkt.operation == IPacket70SpecialUpdate.OPERATION_UPDATE_CERTIFICATE) {
UpdateService.addCertificateToSet(ipkt.updatePacket);
}
}else if(pkt instanceof IPacketFFErrorCode) {
IPacketFFErrorCode ipkt = (IPacketFFErrorCode)pkt;
if(ipkt.code == IPacketFFErrorCode.TYPE_PROTOCOL_VERSION) {
String s1 = ipkt.desc.toLowerCase();
if(s1.contains("outdated client") || s1.contains("client outdated")) {
versError = VersionMismatch.CLIENT_OUTDATED;
}else if(s1.contains("outdated server") || s1.contains("server outdated") ||
s1.contains("outdated relay") || s1.contains("server relay")) {
versError = VersionMismatch.RELAY_OUTDATED;
}else {
versError = VersionMismatch.UNKNOWN;
}
}
logger.error("{}\": Recieved query error code {}: {}", uri, ipkt.code, ipkt.desc);
open = false;
failed = true;
sock.close();
}else {
throw new IOException("Unexpected packet '" + pkt.getClass().getSimpleName() + "'");
}
} catch (IOException e) {
logger.error("Relay Query Error: {}", e.toString());
EagRuntime.debugPrintStackTrace(e);
open = false;
failed = true;
sock.close();
}
}
}
}
}
@Override
public void onClose(int i, String s, boolean b) {
open = false;
if(!hasRecievedAnyData) {
failed = true;
Long l = relayQueryBlocked.get(uri);
if(l != null) {
if(System.currentTimeMillis() - l.longValue() < 400000l) {
rateLimitStatus = RateLimit.LOCKED;
return;
}
}
l = relayQueryLimited.get(uri);
if(l != null) {
if(System.currentTimeMillis() - l.longValue() < 900000l) {
rateLimitStatus = RateLimit.BLOCKED;
return;
}
}
}
}
@Override
public void onError(Exception e) {
EagRuntime.debugPrintStackTrace(e);
}
};
s.connect();
open = true;
failed = false;
}catch(Throwable t) {
connectionOpenedAt = 0l;
sock = null;
open = false;
failed = true;
return;
}
sock = s;
}
@Override
public boolean isQueryOpen() {
return open;
}
@Override
public boolean isQueryFailed() {
return failed;
}
@Override
public RateLimit isQueryRateLimit() {
return rateLimitStatus;
}
@Override
public void close() {
if(sock != null && open) {
sock.close();
}
open = false;
}
@Override
public int getVersion() {
return vers;
}
@Override
public String getComment() {
return comment;
}
@Override
public String getBrand() {
return brand;
}
@Override
public long getPing() {
return connectionPingTimer < 1 ? 1 : connectionPingTimer;
}
@Override
public VersionMismatch getCompatible() {
return versError;
}
}
private static class RelayQueryRatelimitDummy implements RelayQuery {
private final RateLimit type;
private RelayQueryRatelimitDummy(RateLimit type) {
this.type = type;
}
@Override
public boolean isQueryOpen() {
return false;
}
@Override
public boolean isQueryFailed() {
return true;
}
@Override
public RateLimit isQueryRateLimit() {
return type;
}
@Override
public void close() {
}
@Override
public int getVersion() {
return RelayManager.preferredRelayVersion;
}
@Override
public String getComment() {
return "this query was rate limited";
}
@Override
public String getBrand() {
return "lax1dude";
}
@Override
public long getPing() {
return 0l;
}
@Override
public VersionMismatch getCompatible() {
return VersionMismatch.COMPATIBLE;
}
}
public static RelayQuery openRelayQuery(String addr) {
long millis = System.currentTimeMillis();
Long l = relayQueryBlocked.get(addr);
if(l != null && millis - l.longValue() < 60000l) {
return new RelayQueryRatelimitDummy(RelayQuery.RateLimit.LOCKED);
RelayQuery.RateLimit limit = RelayServerRateLimitTracker.isLimited(addr);
if(limit == RelayQuery.RateLimit.LOCKED || limit == RelayQuery.RateLimit.BLOCKED) {
return new RelayQueryRateLimitDummy(limit);
}
l = relayQueryLimited.get(addr);
if(l != null && millis - l.longValue() < 10000l) {
return new RelayQueryRatelimitDummy(RelayQuery.RateLimit.BLOCKED);
}
return new RelayQueryImpl(addr);
}
private static class RelayWorldsQueryImpl implements RelayWorldsQuery {
private final WebSocketClient sock;
private final String uri;
private boolean open;
private boolean failed;
private boolean hasRecievedAnyData = false;
private RelayQuery.RateLimit rateLimitStatus = RelayQuery.RateLimit.NONE;
private RelayQuery.VersionMismatch versError = RelayQuery.VersionMismatch.UNKNOWN;
private List<IPacket07LocalWorlds.LocalWorld> worlds = null;
private RelayWorldsQueryImpl(String uri) {
this.uri = uri;
WebSocketClient s;
try {
s = new WebSocketClient(new URI(uri)) {
@Override
public void onOpen(ServerHandshake serverHandshake) {
try {
sock.send(IPacket.writePacket(new IPacket00Handshake(0x04, RelayManager.preferredRelayVersion, "")));
} catch (IOException e) {
logger.error(e.toString());
sock.close();
open = false;
failed = true;
}
}
@Override
public void onMessage(String s) {
//
}
@Override
public void onMessage(ByteBuffer bb) {
if(bb != null) {
hasRecievedAnyData = true;
byte[] arr = new byte[bb.remaining()];
bb.get(arr);
if(arr.length == 2 && arr[0] == (byte)0xFC) {
long millis = System.currentTimeMillis();
if(arr[1] == (byte)0x00 || arr[1] == (byte)0x01) {
rateLimitStatus = RelayQuery.RateLimit.BLOCKED;
relayQueryLimited.put(RelayWorldsQueryImpl.this.uri, millis);
}else if(arr[1] == (byte)0x02) {
rateLimitStatus = RelayQuery.RateLimit.NOW_LOCKED;
relayQueryLimited.put(RelayWorldsQueryImpl.this.uri, millis);
relayQueryBlocked.put(RelayWorldsQueryImpl.this.uri, millis);
}else {
rateLimitStatus = RelayQuery.RateLimit.LOCKED;
relayQueryBlocked.put(RelayWorldsQueryImpl.this.uri, millis);
}
open = false;
failed = true;
sock.close();
}else {
if(open) {
try {
IPacket pkt = IPacket.readPacket(new DataInputStream(new EaglerInputStream(arr)));
if(pkt instanceof IPacket07LocalWorlds) {
worlds = ((IPacket07LocalWorlds)pkt).worldsList;
sock.close();
open = false;
failed = false;
}else if(pkt instanceof IPacket70SpecialUpdate) {
IPacket70SpecialUpdate ipkt = (IPacket70SpecialUpdate)pkt;
if(ipkt.operation == IPacket70SpecialUpdate.OPERATION_UPDATE_CERTIFICATE) {
UpdateService.addCertificateToSet(ipkt.updatePacket);
}
}else if(pkt instanceof IPacketFFErrorCode) {
IPacketFFErrorCode ipkt = (IPacketFFErrorCode)pkt;
if(ipkt.code == IPacketFFErrorCode.TYPE_PROTOCOL_VERSION) {
String s1 = ipkt.desc.toLowerCase();
if(s1.contains("outdated client") || s1.contains("client outdated")) {
versError = RelayQuery.VersionMismatch.CLIENT_OUTDATED;
}else if(s1.contains("outdated server") || s1.contains("server outdated") ||
s1.contains("outdated relay") || s1.contains("server relay")) {
versError = RelayQuery.VersionMismatch.RELAY_OUTDATED;
}else {
versError = RelayQuery.VersionMismatch.UNKNOWN;
}
}
logger.error("{}: Recieved query error code {}: {}", uri, ipkt.code, ipkt.desc);
open = false;
failed = true;
sock.close();
}else {
throw new IOException("Unexpected packet '" + pkt.getClass().getSimpleName() + "'");
}
} catch (IOException e) {
logger.error("Relay World Query Error: {}", e.toString());
EagRuntime.debugPrintStackTrace(e);
open = false;
failed = true;
sock.close();
}
}
}
}
}
@Override
public void onClose(int i, String s, boolean b) {
open = false;
if(!hasRecievedAnyData) {
failed = true;
Long l = relayQueryBlocked.get(uri);
if(l != null) {
if(System.currentTimeMillis() - l.longValue() < 400000l) {
rateLimitStatus = RelayQuery.RateLimit.LOCKED;
return;
}
}
l = relayQueryLimited.get(uri);
if(l != null) {
if(System.currentTimeMillis() - l.longValue() < 900000l) {
rateLimitStatus = RelayQuery.RateLimit.BLOCKED;
return;
}
}
}
}
@Override
public void onError(Exception e) {
EagRuntime.debugPrintStackTrace(e);
}
};
s.connect();
open = true;
failed = false;
}catch(Throwable t) {
sock = null;
open = false;
failed = true;
return;
}
sock = s;
}
@Override
public boolean isQueryOpen() {
return open;
}
@Override
public boolean isQueryFailed() {
return failed;
}
@Override
public RelayQuery.RateLimit isQueryRateLimit() {
return rateLimitStatus;
}
@Override
public void close() {
if(open && sock != null) {
sock.close();
}
open = false;
}
@Override
public List<IPacket07LocalWorlds.LocalWorld> getWorlds() {
return worlds;
}
@Override
public RelayQuery.VersionMismatch getCompatible() {
return versError;
}
}
private static class RelayWorldsQueryRatelimitDummy implements RelayWorldsQuery {
private final RelayQuery.RateLimit rateLimit;
private RelayWorldsQueryRatelimitDummy(RelayQuery.RateLimit rateLimit) {
this.rateLimit = rateLimit;
}
@Override
public boolean isQueryOpen() {
return false;
}
@Override
public boolean isQueryFailed() {
return true;
}
@Override
public RelayQuery.RateLimit isQueryRateLimit() {
return rateLimit;
}
@Override
public void close() {
}
@Override
public List<IPacket07LocalWorlds.LocalWorld> getWorlds() {
return new ArrayList(0);
}
@Override
public RelayQuery.VersionMismatch getCompatible() {
return RelayQuery.VersionMismatch.COMPATIBLE;
}
}
public static RelayWorldsQuery openRelayWorldsQuery(String addr) {
long millis = System.currentTimeMillis();
Long l = relayQueryBlocked.get(addr);
if(l != null && millis - l.longValue() < 60000l) {
return new RelayWorldsQueryRatelimitDummy(RelayQuery.RateLimit.LOCKED);
RelayQuery.RateLimit limit = RelayServerRateLimitTracker.isLimited(addr);
if(limit == RelayQuery.RateLimit.LOCKED || limit == RelayQuery.RateLimit.BLOCKED) {
return new RelayWorldsQueryRateLimitDummy(limit);
}
l = relayQueryLimited.get(addr);
if(l != null && millis - l.longValue() < 10000l) {
return new RelayWorldsQueryRatelimitDummy(RelayQuery.RateLimit.BLOCKED);
}
return new RelayWorldsQueryImpl(addr);
}
private static class RelayServerSocketImpl implements RelayServerSocket {
private final WebSocketClient sock;
private final String uri;
private boolean open;
private boolean closed;
private boolean failed;
private boolean hasRecievedAnyData;
private final List<Throwable> exceptions = new LinkedList();
private final List<IPacket> packets = new LinkedList();
private RelayServerSocketImpl(String uri, int timeout) {
this.uri = uri;
WebSocketClient s;
try {
s = new WebSocketClient(new URI(uri)) {
@Override
public void onOpen(ServerHandshake serverHandshake) {
synchronized (lock4) {
open = true;
}
}
@Override
public void onMessage(String s) {
//
}
@Override
public void onMessage(ByteBuffer bb) {
if(bb != null) {
hasRecievedAnyData = true;
try {
byte[] arr = new byte[bb.remaining()];
bb.get(arr);
IPacket pkt = IPacket.readPacket(new DataInputStream(new EaglerInputStream(arr)));
if(pkt instanceof IPacket70SpecialUpdate) {
IPacket70SpecialUpdate ipkt = (IPacket70SpecialUpdate)pkt;
if(ipkt.operation == IPacket70SpecialUpdate.OPERATION_UPDATE_CERTIFICATE) {
UpdateService.addCertificateToSet(ipkt.updatePacket);
}
}else {
packets.add(pkt);
}
} catch (IOException e) {
exceptions.add(e);
logger.error("Relay Socket Error: {}", e.toString());
EagRuntime.debugPrintStackTrace(e);
synchronized (lock4) {
open = false;
failed = true;
}
closed = true;
synchronized (lock4) {
sock.close();
}
}
}
}
@Override
public void onClose(int i, String s, boolean b) {
if (!hasRecievedAnyData) {
failed = true;
}
synchronized (lock4) {
open = false;
closed = true;
}
}
@Override
public void onError(Exception e) {
EagRuntime.debugPrintStackTrace(e);
}
};
s.connect();
synchronized (lock4) {
open = false;
closed = false;
}
failed = false;
}catch(Throwable t) {
exceptions.add(t);
synchronized (lock4) {
sock = null;
open = false;
closed = true;
}
failed = true;
return;
}
synchronized (lock4) {
sock = s;
}
new Thread(() -> {
EagUtils.sleep(timeout);
synchronized (lock4) {
if (!open && !closed) {
closed = true;
sock.close();
}
}
}).start();
}
@Override
public boolean isOpen() {
return open;
}
@Override
public boolean isClosed() {
synchronized (lock4) {
return closed;
}
}
@Override
public void close() {
if(open && sock != null) {
synchronized (lock4) {
sock.close();
}
}
synchronized (lock4) {
open = false;
closed = true;
}
}
@Override
public boolean isFailed() {
return failed;
}
@Override
public Throwable getException() {
if(!exceptions.isEmpty()) {
return exceptions.remove(0);
}else {
return null;
}
}
@Override
public void writePacket(IPacket pkt) {
try {
sock.send(IPacket.writePacket(pkt));
} catch (Throwable e) {
logger.error("Relay connection error: {}", e.toString());
EagRuntime.debugPrintStackTrace(e);
exceptions.add(e);
failed = true;
synchronized (lock4) {
open = false;
closed = true;
sock.close();
}
}
}
@Override
public IPacket readPacket() {
if(!packets.isEmpty()) {
return packets.remove(0);
}else {
return null;
}
}
@Override
public IPacket nextPacket() {
if(!packets.isEmpty()) {
return packets.get(0);
}else {
return null;
}
}
@Override
public RelayQuery.RateLimit getRatelimitHistory() {
if(relayQueryBlocked.containsKey(uri)) {
return RelayQuery.RateLimit.LOCKED;
}
if(relayQueryLimited.containsKey(uri)) {
return RelayQuery.RateLimit.BLOCKED;
}
return RelayQuery.RateLimit.NONE;
}
@Override
public String getURI() {
return uri;
}
}
private static class RelayServerSocketRatelimitDummy implements RelayServerSocket {
private final RelayQuery.RateLimit limit;
private RelayServerSocketRatelimitDummy(RelayQuery.RateLimit limit) {
this.limit = limit;
}
@Override
public boolean isOpen() {
return false;
}
@Override
public boolean isClosed() {
return true;
}
@Override
public void close() {
}
@Override
public boolean isFailed() {
return true;
}
@Override
public Throwable getException() {
return null;
}
@Override
public void writePacket(IPacket pkt) {
}
@Override
public IPacket readPacket() {
return null;
}
@Override
public IPacket nextPacket() {
return null;
}
@Override
public RelayQuery.RateLimit getRatelimitHistory() {
return limit;
}
@Override
public String getURI() {
return "<disconnected>";
}
}
public static RelayServerSocket openRelayConnection(String addr, int timeout) {
long millis = System.currentTimeMillis();
Long l = relayQueryBlocked.get(addr);
if(l != null && millis - l.longValue() < 60000l) {
return new RelayServerSocketRatelimitDummy(RelayQuery.RateLimit.LOCKED);
RelayQuery.RateLimit limit = RelayServerRateLimitTracker.isLimited(addr);
if(limit == RelayQuery.RateLimit.LOCKED || limit == RelayQuery.RateLimit.BLOCKED) {
return new RelayServerSocketRateLimitDummy(limit);
}
l = relayQueryLimited.get(addr);
if(l != null && millis - l.longValue() < 10000l) {
return new RelayServerSocketRatelimitDummy(RelayQuery.RateLimit.BLOCKED);
}
return new RelayServerSocketImpl(addr, timeout);
}
@ -1453,7 +738,7 @@ public class PlatformWebRTC {
public static List<byte[]> clientLANReadAllPacket() {
synchronized(clientLANPacketBuffer) {
if(!clientLANPacketBuffer.isEmpty()) {
List<byte[]> ret = new ArrayList(clientLANPacketBuffer);
List<byte[]> ret = new ArrayList<>(clientLANPacketBuffer);
clientLANPacketBuffer.clear();
return ret;
}else {

View File

@ -0,0 +1,93 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.FallbackWebViewServer;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketWebViewMessageV4EAG;
import net.lax1dude.eaglercraft.v1_8.webview.WebViewOverlayController.IPacketSendCallback;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class PlatformWebView {
private static FallbackWebViewServer fallbackServer = null;
private static IPacketSendCallback packetCallback = null;
public static boolean supported() {
return false;
}
public static boolean isShowing() {
return false;
}
public static void beginShowing(WebViewOptions options, int x, int y, int w, int h) {
}
public static void resize(int x, int y, int w, int h) {
}
public static void endShowing() {
}
public static boolean fallbackSupported() {
return true;
}
public static void launchFallback(WebViewOptions options) {
fallbackServer = new FallbackWebViewServer(options);
fallbackServer.setPacketSendCallback(packetCallback);
fallbackServer.start();
}
public static boolean fallbackRunning() {
return fallbackServer != null && !fallbackServer.isDead();
}
public static String getFallbackURL() {
return fallbackServer != null ? fallbackServer.getURL() : null;
}
public static void endFallbackServer() {
if(fallbackServer != null && !fallbackServer.isDead()) {
fallbackServer.killServer();
}
}
public static void handleMessageFromServer(SPacketWebViewMessageV4EAG packet) {
if(fallbackServer != null && !fallbackServer.isDead()) {
fallbackServer.handleMessageFromServer(packet);
}
}
public static void setPacketSendCallback(IPacketSendCallback callback) {
packetCallback = callback;
if(fallbackServer != null) {
fallbackServer.setPacketSendCallback(callback);
}
}
public static void runTick() {
if(fallbackServer != null) {
fallbackServer.runTick();
if(fallbackServer.isDead()) {
fallbackServer = null;
}
}
}
}

View File

@ -1,173 +0,0 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.List;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.extensions.permessage_deflate.PerMessageDeflateExtension;
import org.java_websocket.handshake.ServerHandshake;
import org.json.JSONObject;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
/**
* Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
class WebSocketServerQuery extends WebSocketClient implements IServerQuery {
private static final Draft perMessageDeflateDraft = new Draft_6455(new PerMessageDeflateExtension());
public static final Logger logger = LogManager.getLogger("WebSocketQuery");
private final List<QueryResponse> queryResponses = new LinkedList();
private final List<byte[]> queryResponsesBytes = new LinkedList();
private final String type;
private boolean open = true;
private boolean alive = false;
private long pingStart = -1l;
private long pingTimer = -1l;
private EnumServerRateLimit rateLimit = EnumServerRateLimit.OK;
WebSocketServerQuery(String type, URI serverUri) {
super(serverUri, perMessageDeflateDraft);
this.type = type;
this.setConnectionLostTimeout(5);
this.setTcpNoDelay(true);
this.connect();
}
@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 EnumServerRateLimit getRateLimit() {
return rateLimit;
}
@Override
public void onClose(int arg0, String arg1, boolean arg2) {
open = false;
}
@Override
public void onError(Exception arg0) {
logger.error("Exception thrown by websocket query \"" + this.getURI().toString() + "\"!");
logger.error(arg0);
}
@Override
public void onMessage(String arg0) {
alive = true;
if(pingTimer == -1) {
pingTimer = System.currentTimeMillis() - pingStart;
if(pingTimer < 1) {
pingTimer = 1;
}
}
if(arg0.equalsIgnoreCase("BLOCKED")) {
logger.error("Reached full IP ratelimit for {}!", this.uri.toString());
rateLimit = EnumServerRateLimit.BLOCKED;
return;
}
if(arg0.equalsIgnoreCase("LOCKED")) {
logger.error("Reached full IP ratelimit lockout for {}!", this.uri.toString());
rateLimit = EnumServerRateLimit.LOCKED_OUT;
return;
}
try {
JSONObject obj = new JSONObject(arg0);
if("blocked".equalsIgnoreCase(obj.optString("type", null))) {
logger.error("Reached query ratelimit for {}!", this.uri.toString());
rateLimit = EnumServerRateLimit.BLOCKED;
}else if("locked".equalsIgnoreCase(obj.optString("type", null))) {
logger.error("Reached query ratelimit lockout for {}!", this.uri.toString());
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 \"" + this.getURI().toString() + "\"!");
logger.error(t);
}
}
@Override
public void onMessage(ByteBuffer arg0) {
alive = true;
if(pingTimer == -1) {
pingTimer = System.currentTimeMillis() - pingStart;
if(pingTimer < 1) {
pingTimer = 1;
}
}
synchronized(queryResponsesBytes) {
queryResponsesBytes.add(arg0.array());
}
}
@Override
public void onOpen(ServerHandshake arg0) {
pingStart = System.currentTimeMillis();
send("Accept: " + type);
}
}

View File

@ -1,7 +1,5 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.lwjgl.system.jemalloc.JEmalloc;
import net.lax1dude.unsafememcpy.UnsafeMemcpy;
import net.lax1dude.unsafememcpy.UnsafeUtils;
@ -29,7 +27,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
private int position;
private int limit;
private int mark;
EaglerLWJGLByteBuffer(long address, int capacity, boolean original) {
this(address, capacity, 0, capacity, -1, original);
}
@ -68,18 +66,13 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
return position < limit;
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public boolean hasArray() {
return false;
}
@Override
public Object array() {
public byte[] array() {
throw new UnsupportedOperationException();
}
@ -88,50 +81,40 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
return true;
}
@Override
public ByteBuffer slice() {
return new EaglerLWJGLByteBuffer(address + position, limit - position, false);
}
@Override
public ByteBuffer duplicate() {
return new EaglerLWJGLByteBuffer(address, capacity, position, limit, mark, false);
}
@Override
public ByteBuffer asReadOnlyBuffer() {
return new EaglerLWJGLByteBuffer(address, capacity, position, limit, mark, false);
}
@Override
public byte get() {
if(position >= limit) throw new ArrayIndexOutOfBoundsException(position);
if(position >= limit) throw Buffer.makeIOOBE(position);
return UnsafeUtils.getMemByte(address + position++);
}
@Override
public ByteBuffer put(byte b) {
if(position >= limit) throw new ArrayIndexOutOfBoundsException(position);
if(position >= limit) throw Buffer.makeIOOBE(position);
UnsafeUtils.setMemByte(address + position++, b);
return this;
}
@Override
public byte get(int index) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
return UnsafeUtils.getMemByte(address + index);
}
@Override
public ByteBuffer put(int index, byte b) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
UnsafeUtils.setMemByte(address + index, b);
return this;
}
@Override
public ByteBuffer get(byte[] dst, int offset, int length) {
if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1);
if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1);
UnsafeMemcpy.memcpy(dst, offset, address + position, length);
position += length;
return this;
@ -139,7 +122,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
@Override
public ByteBuffer get(byte[] dst) {
if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1);
if(position + dst.length > limit) throw Buffer.makeIOOBE(position + dst.length - 1);
UnsafeMemcpy.memcpy(dst, 0, address + position, dst.length);
position += dst.length;
return this;
@ -150,13 +133,13 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
if(src instanceof EaglerLWJGLByteBuffer) {
EaglerLWJGLByteBuffer c = (EaglerLWJGLByteBuffer)src;
int l = c.limit - c.position;
if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1);
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
UnsafeMemcpy.memcpy(address + position, c.address + c.position, l);
position += l;
c.position += l;
}else {
int l = src.remaining();
if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1);
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
for(int i = 0; i < l; ++i) {
UnsafeUtils.setMemByte(address + position + l, src.get());
}
@ -167,7 +150,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
@Override
public ByteBuffer put(byte[] src, int offset, int length) {
if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1);
if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1);
UnsafeMemcpy.memcpy(address + position, src, offset, length);
position += length;
return this;
@ -175,39 +158,15 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
@Override
public ByteBuffer put(byte[] src) {
if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1);
if(position + src.length > limit) throw Buffer.makeIOOBE(position + src.length - 1);
UnsafeMemcpy.memcpy(address + position, src, 0, src.length);
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 EaglerLWJGLByteBuffer(0l, 0, false);
}
int newLen = limit - position;
long newAlloc = JEmalloc.nje_malloc(newLen);
if(newAlloc == 0l) {
throw new OutOfMemoryError("Native je_malloc call returned null pointer!");
}
UnsafeMemcpy.memcpy(newAlloc, address + position, newLen);
return new EaglerLWJGLByteBuffer(newAlloc, newLen, true);
}
@Override
public char getChar() {
if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position);
if(position + 2 > limit) throw Buffer.makeIOOBE(position);
char c = UnsafeUtils.getMemChar(address + position);
position += 2;
return c;
@ -215,7 +174,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
@Override
public ByteBuffer putChar(char value) {
if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position);
if(position + 2 > limit) throw Buffer.makeIOOBE(position);
UnsafeUtils.setMemChar(address + position, value);
position += 2;
return this;
@ -223,20 +182,20 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
@Override
public char getChar(int index) {
if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index + 2 > limit) throw Buffer.makeIOOBE(index);
return UnsafeUtils.getMemChar(address + index);
}
@Override
public ByteBuffer putChar(int index, char value) {
if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index + 2 > limit) throw Buffer.makeIOOBE(index);
UnsafeUtils.setMemChar(address + index, value);
return this;
}
@Override
public short getShort() {
if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position);
if(position + 2 > limit) throw Buffer.makeIOOBE(position);
short s = UnsafeUtils.getMemShort(address + position);
position += 2;
return s;
@ -244,7 +203,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
@Override
public ByteBuffer putShort(short value) {
if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position);
if(position + 2 > limit) throw Buffer.makeIOOBE(position);
UnsafeUtils.setMemShort(address + position, value);
position += 2;
return this;
@ -252,13 +211,13 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
@Override
public short getShort(int index) {
if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index + 2 > limit) throw Buffer.makeIOOBE(index);
return UnsafeUtils.getMemShort(address + index);
}
@Override
public ByteBuffer putShort(int index, short value) {
if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index + 2 > limit) throw Buffer.makeIOOBE(index);
UnsafeUtils.setMemShort(address + index, value);
return this;
}
@ -270,7 +229,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
@Override
public int getInt() {
if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position);
if(position + 4 > limit) throw Buffer.makeIOOBE(position);
int i = UnsafeUtils.getMemInt(address + position);
position += 4;
return i;
@ -278,7 +237,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
@Override
public ByteBuffer putInt(int value) {
if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position);
if(position + 4 > limit) throw Buffer.makeIOOBE(position);
UnsafeUtils.setMemInt(address + position, value);
position += 4;
return this;
@ -286,13 +245,13 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
@Override
public int getInt(int index) {
if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index + 4 > limit) throw Buffer.makeIOOBE(index);
return UnsafeUtils.getMemInt(address + index);
}
@Override
public ByteBuffer putInt(int index, int value) {
if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index + 4 > limit) throw Buffer.makeIOOBE(index);
UnsafeUtils.setMemInt(address + index, value);
return this;
}
@ -304,7 +263,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
@Override
public long getLong() {
if(position + 8 > limit) throw new ArrayIndexOutOfBoundsException(position);
if(position + 8 > limit) throw Buffer.makeIOOBE(position);
long l = UnsafeUtils.getMemLong(address + position);
position += 8;
return l;
@ -312,7 +271,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
@Override
public ByteBuffer putLong(long value) {
if(position + 8 > limit) throw new ArrayIndexOutOfBoundsException(position);
if(position + 8 > limit) throw Buffer.makeIOOBE(position);
UnsafeUtils.setMemLong(address + position, value);
position += 8;
return this;
@ -320,20 +279,20 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
@Override
public long getLong(int index) {
if(index + 8 > limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index + 8 > limit) throw Buffer.makeIOOBE(index);
return UnsafeUtils.getMemLong(address + index);
}
@Override
public ByteBuffer putLong(int index, long value) {
if(index + 8 > limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index + 8 > limit) throw Buffer.makeIOOBE(index);
UnsafeUtils.setMemLong(address + index, value);
return this;
}
@Override
public float getFloat() {
if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position);
if(position + 4 > limit) throw Buffer.makeIOOBE(position);
float f = UnsafeUtils.getMemFloat(address + position);
position += 4;
return f;
@ -341,7 +300,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
@Override
public ByteBuffer putFloat(float value) {
if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position);
if(position + 4 > limit) throw Buffer.makeIOOBE(position);
UnsafeUtils.setMemFloat(address + position, value);
position += 4;
return this;
@ -349,13 +308,13 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
@Override
public float getFloat(int index) {
if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index + 4 > limit) throw Buffer.makeIOOBE(index);
return UnsafeUtils.getMemFloat(address + index);
}
@Override
public ByteBuffer putFloat(int index, float value) {
if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index + 4 > limit) throw Buffer.makeIOOBE(index);
UnsafeUtils.setMemFloat(address + index, value);
return this;
}
@ -374,7 +333,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
@Override
public ByteBuffer reset() {
int m = mark;
if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m);
if(m < 0) throw new IndexOutOfBoundsException("Invalid mark: " + m);
position = m;
return this;
}
@ -404,14 +363,14 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer {
@Override
public ByteBuffer limit(int newLimit) {
if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit);
if(newLimit < 0 || newLimit > capacity) throw Buffer.makeIOOBE(newLimit);
limit = newLimit;
return this;
}
@Override
public ByteBuffer position(int newPosition) {
if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition);
if(newPosition < 0 || newPosition > limit) throw Buffer.makeIOOBE(newPosition);
position = newPosition;
return this;
}

View File

@ -1,7 +1,5 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.lwjgl.system.jemalloc.JEmalloc;
import net.lax1dude.unsafememcpy.UnsafeMemcpy;
import net.lax1dude.unsafememcpy.UnsafeUtils;
@ -29,9 +27,9 @@ public class EaglerLWJGLFloatBuffer implements FloatBuffer {
private int position;
private int limit;
private int mark;
private static final int SHIFT = 2;
EaglerLWJGLFloatBuffer(long address, int capacity, boolean original) {
this(address, capacity, 0, capacity, -1, original);
}
@ -70,82 +68,62 @@ public class EaglerLWJGLFloatBuffer implements FloatBuffer {
return position < limit;
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public boolean hasArray() {
return false;
}
@Override
public Object array() {
public float[] array() {
throw new UnsupportedOperationException();
}
@Override
public int arrayOffset() {
return position;
}
@Override
public FloatBuffer slice() {
return new EaglerLWJGLFloatBuffer(address + (position << SHIFT), limit - position, false);
}
@Override
public FloatBuffer duplicate() {
return new EaglerLWJGLFloatBuffer(address, capacity, position, limit, mark, false);
}
@Override
public FloatBuffer asReadOnlyBuffer() {
return new EaglerLWJGLFloatBuffer(address, capacity, position, limit, mark, false);
}
@Override
public float get() {
if(position >= limit) throw new ArrayIndexOutOfBoundsException(position);
if(position >= limit) throw Buffer.makeIOOBE(position);
return UnsafeUtils.getMemFloat(address + ((position++) << SHIFT));
}
@Override
public FloatBuffer put(float b) {
if(position >= limit) throw new ArrayIndexOutOfBoundsException(position);
if(position >= limit) throw Buffer.makeIOOBE(position);
UnsafeUtils.setMemFloat(address + ((position++) << SHIFT), b);
return this;
}
@Override
public float get(int index) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
return UnsafeUtils.getMemFloat(address + (index << SHIFT));
}
@Override
public FloatBuffer put(int index, float b) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
UnsafeUtils.setMemFloat(address + (index << SHIFT), b);
return this;
}
@Override
public float getElement(int index) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
return UnsafeUtils.getMemFloat(address + (index << SHIFT));
}
@Override
public void putElement(int index, float value) {
if(position >= limit) throw new ArrayIndexOutOfBoundsException(position);
UnsafeUtils.setMemFloat(address + ((position++) << SHIFT), value);
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
UnsafeUtils.setMemFloat(address + (index << SHIFT), value);
}
@Override
public FloatBuffer get(float[] dst, int offset, int length) {
if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1);
if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1);
UnsafeMemcpy.memcpyAlignDst(dst, offset << SHIFT, address + (position << SHIFT), length);
position += length;
return this;
@ -153,7 +131,7 @@ public class EaglerLWJGLFloatBuffer implements FloatBuffer {
@Override
public FloatBuffer get(float[] dst) {
if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1);
if(position + dst.length > limit) throw Buffer.makeIOOBE(position + dst.length - 1);
UnsafeMemcpy.memcpyAlignDst(dst, 0, address + (position << SHIFT), dst.length);
position += dst.length;
return this;
@ -164,13 +142,13 @@ public class EaglerLWJGLFloatBuffer implements FloatBuffer {
if(src instanceof EaglerLWJGLFloatBuffer) {
EaglerLWJGLFloatBuffer c = (EaglerLWJGLFloatBuffer)src;
int l = c.limit - c.position;
if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1);
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
UnsafeMemcpy.memcpy(address + (position << SHIFT), c.address + (c.position << SHIFT), l << SHIFT);
position += l;
c.position += l;
}else {
int l = src.remaining();
if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1);
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
for(int i = 0; i < l; ++i) {
UnsafeUtils.setMemFloat(address + ((position + l) << SHIFT), src.get());
}
@ -181,7 +159,7 @@ public class EaglerLWJGLFloatBuffer implements FloatBuffer {
@Override
public FloatBuffer put(float[] src, int offset, int length) {
if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1);
if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1);
UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, offset << SHIFT, length);
position += length;
return this;
@ -189,36 +167,12 @@ public class EaglerLWJGLFloatBuffer implements FloatBuffer {
@Override
public FloatBuffer put(float[] src) {
if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1);
if(position + src.length > limit) throw Buffer.makeIOOBE(position + src.length - 1);
UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, 0, src.length);
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 EaglerLWJGLFloatBuffer(0l, 0, false);
}
int newLen = limit - position;
long newAlloc = JEmalloc.nje_malloc(newLen);
if(newAlloc == 0l) {
throw new OutOfMemoryError("Native je_malloc call returned null pointer!");
}
UnsafeMemcpy.memcpy(newAlloc, address + (position << SHIFT), newLen << SHIFT);
return new EaglerLWJGLFloatBuffer(newAlloc, newLen, true);
}
@Override
public boolean isDirect() {
return true;
@ -233,7 +187,7 @@ public class EaglerLWJGLFloatBuffer implements FloatBuffer {
@Override
public FloatBuffer reset() {
int m = mark;
if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m);
if(m < 0) throw new IndexOutOfBoundsException("Invalid mark: " + m);
position = m;
return this;
}
@ -263,14 +217,14 @@ public class EaglerLWJGLFloatBuffer implements FloatBuffer {
@Override
public FloatBuffer limit(int newLimit) {
if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit);
if(newLimit < 0 || newLimit > capacity) throw Buffer.makeIOOBE(newLimit);
limit = newLimit;
return this;
}
@Override
public FloatBuffer position(int newPosition) {
if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition);
if(newPosition < 0 || newPosition > limit) throw Buffer.makeIOOBE(newPosition);
position = newPosition;
return this;
}

View File

@ -1,7 +1,5 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.lwjgl.system.jemalloc.JEmalloc;
import net.lax1dude.unsafememcpy.UnsafeMemcpy;
import net.lax1dude.unsafememcpy.UnsafeUtils;
@ -21,7 +19,7 @@ import net.lax1dude.unsafememcpy.UnsafeUtils;
*
*/
public class EaglerLWJGLIntBuffer implements IntBuffer {
final long address;
final boolean original;
@ -29,9 +27,9 @@ public class EaglerLWJGLIntBuffer implements IntBuffer {
private int position;
private int limit;
private int mark;
private static final int SHIFT = 2;
EaglerLWJGLIntBuffer(long address, int capacity, boolean original) {
this(address, capacity, 0, capacity, -1, original);
}
@ -44,7 +42,7 @@ public class EaglerLWJGLIntBuffer implements IntBuffer {
this.mark = mark;
this.original = original;
}
@Override
public int capacity() {
return capacity;
@ -70,82 +68,62 @@ public class EaglerLWJGLIntBuffer implements IntBuffer {
return position < limit;
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public boolean hasArray() {
return false;
}
@Override
public Object array() {
public int[] array() {
throw new UnsupportedOperationException();
}
@Override
public int arrayOffset() {
return position;
}
@Override
public IntBuffer slice() {
return new EaglerLWJGLIntBuffer(address + (position << SHIFT), limit - position, false);
}
@Override
public IntBuffer duplicate() {
return new EaglerLWJGLIntBuffer(address, capacity, position, limit, mark, false);
}
@Override
public IntBuffer asReadOnlyBuffer() {
return new EaglerLWJGLIntBuffer(address, capacity, position, limit, mark, false);
}
@Override
public int get() {
if(position >= limit) throw new ArrayIndexOutOfBoundsException(position);
if(position >= limit) throw Buffer.makeIOOBE(position);
return UnsafeUtils.getMemInt(address + ((position++) << SHIFT));
}
@Override
public IntBuffer put(int b) {
if(position >= limit) throw new ArrayIndexOutOfBoundsException(position);
if(position >= limit) throw Buffer.makeIOOBE(position);
UnsafeUtils.setMemInt(address + ((position++) << SHIFT), b);
return this;
}
@Override
public int get(int index) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
return UnsafeUtils.getMemInt(address + (index << SHIFT));
}
@Override
public IntBuffer put(int index, int b) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
UnsafeUtils.setMemInt(address + (index << SHIFT), b);
return this;
}
@Override
public int getElement(int index) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
return UnsafeUtils.getMemInt(address + (index << SHIFT));
}
@Override
public void putElement(int index, int value) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
UnsafeUtils.setMemInt(address + (index << SHIFT), value);
}
@Override
public IntBuffer get(int[] dst, int offset, int length) {
if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1);
if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1);
UnsafeMemcpy.memcpyAlignDst(dst, offset << SHIFT, address + (position << SHIFT), length);
position += length;
return this;
@ -153,7 +131,7 @@ public class EaglerLWJGLIntBuffer implements IntBuffer {
@Override
public IntBuffer get(int[] dst) {
if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1);
if(position + dst.length > limit) throw Buffer.makeIOOBE(position + dst.length - 1);
UnsafeMemcpy.memcpyAlignDst(dst, 0, address + (position << SHIFT), dst.length);
position += dst.length;
return this;
@ -164,13 +142,13 @@ public class EaglerLWJGLIntBuffer implements IntBuffer {
if(src instanceof EaglerLWJGLIntBuffer) {
EaglerLWJGLIntBuffer c = (EaglerLWJGLIntBuffer)src;
int l = c.limit - c.position;
if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1);
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
UnsafeMemcpy.memcpy(address + (position << SHIFT), c.address + (c.position << SHIFT), l << SHIFT);
position += l;
c.position += l;
}else {
int l = src.remaining();
if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1);
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
for(int i = 0; i < l; ++i) {
UnsafeUtils.setMemInt(address + ((position + l) << SHIFT), src.get());
}
@ -181,7 +159,7 @@ public class EaglerLWJGLIntBuffer implements IntBuffer {
@Override
public IntBuffer put(int[] src, int offset, int length) {
if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1);
if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1);
UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, offset << SHIFT, length);
position += length;
return this;
@ -189,36 +167,12 @@ public class EaglerLWJGLIntBuffer implements IntBuffer {
@Override
public IntBuffer put(int[] src) {
if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1);
if(position + src.length > limit) throw Buffer.makeIOOBE(position + src.length - 1);
UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, 0, src.length);
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 EaglerLWJGLIntBuffer(0l, 0, false);
}
int newLen = limit - position;
long newAlloc = JEmalloc.nje_malloc(newLen);
if(newAlloc == 0l) {
throw new OutOfMemoryError("Native je_malloc call returned null pointer!");
}
UnsafeMemcpy.memcpy(newAlloc, address + (position << SHIFT), newLen << SHIFT);
return new EaglerLWJGLIntBuffer(newAlloc, newLen, true);
}
@Override
public boolean isDirect() {
return true;
@ -233,7 +187,7 @@ public class EaglerLWJGLIntBuffer implements IntBuffer {
@Override
public IntBuffer reset() {
int m = mark;
if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m);
if(m < 0) throw new IndexOutOfBoundsException("Invalid mark: " + m);
position = m;
return this;
}
@ -263,14 +217,14 @@ public class EaglerLWJGLIntBuffer implements IntBuffer {
@Override
public IntBuffer limit(int newLimit) {
if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit);
if(newLimit < 0 || newLimit > capacity) throw Buffer.makeIOOBE(newLimit);
limit = newLimit;
return this;
}
@Override
public IntBuffer position(int newPosition) {
if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition);
if(newPosition < 0 || newPosition > limit) throw Buffer.makeIOOBE(newPosition);
position = newPosition;
return this;
}

View File

@ -1,7 +1,5 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.lwjgl.system.jemalloc.JEmalloc;
import net.lax1dude.unsafememcpy.UnsafeMemcpy;
import net.lax1dude.unsafememcpy.UnsafeUtils;
@ -21,7 +19,7 @@ import net.lax1dude.unsafememcpy.UnsafeUtils;
*
*/
public class EaglerLWJGLShortBuffer implements ShortBuffer {
final long address;
final boolean original;
@ -29,9 +27,9 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer {
private int position;
private int limit;
private int mark;
private static final int SHIFT = 1;
EaglerLWJGLShortBuffer(long address, int capacity, boolean original) {
this(address, capacity, 0, capacity, -1, original);
}
@ -44,7 +42,7 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer {
this.mark = mark;
this.original = original;
}
@Override
public int capacity() {
return capacity;
@ -70,82 +68,62 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer {
return position < limit;
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public boolean hasArray() {
return false;
}
@Override
public Object array() {
public short[] array() {
throw new UnsupportedOperationException();
}
@Override
public int arrayOffset() {
return position;
}
@Override
public ShortBuffer slice() {
return new EaglerLWJGLShortBuffer(address + (position << SHIFT), limit - position, false);
}
@Override
public ShortBuffer duplicate() {
return new EaglerLWJGLShortBuffer(address, capacity, position, limit, mark, false);
}
@Override
public ShortBuffer asReadOnlyBuffer() {
return new EaglerLWJGLShortBuffer(address, capacity, position, limit, mark, false);
}
@Override
public short get() {
if(position >= limit) throw new ArrayIndexOutOfBoundsException(position);
if(position >= limit) throw Buffer.makeIOOBE(position);
return UnsafeUtils.getMemShort(address + ((position++) << SHIFT));
}
@Override
public ShortBuffer put(short b) {
if(position >= limit) throw new ArrayIndexOutOfBoundsException(position);
if(position >= limit) throw Buffer.makeIOOBE(position);
UnsafeUtils.setMemShort(address + ((position++) << SHIFT), b);
return this;
}
@Override
public short get(int index) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
return UnsafeUtils.getMemShort(address + (index << SHIFT));
}
@Override
public ShortBuffer put(int index, short b) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
UnsafeUtils.setMemShort(address + (index << SHIFT), b);
return this;
}
@Override
public short getElement(int index) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
return UnsafeUtils.getMemShort(address + (index << SHIFT));
}
@Override
public void putElement(int index, short value) {
if(index >= limit) throw new ArrayIndexOutOfBoundsException(index);
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
UnsafeUtils.setMemShort(address + (index << SHIFT), value);
}
@Override
public ShortBuffer get(short[] dst, int offset, int length) {
if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1);
if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1);
UnsafeMemcpy.memcpyAlignDst(dst, offset << SHIFT, address + (position << SHIFT), length);
position += length;
return this;
@ -153,7 +131,7 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer {
@Override
public ShortBuffer get(short[] dst) {
if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1);
if(position + dst.length > limit) throw Buffer.makeIOOBE(position + dst.length - 1);
UnsafeMemcpy.memcpyAlignDst(dst, 0, address + (position << SHIFT), dst.length);
position += dst.length;
return this;
@ -164,13 +142,13 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer {
if(src instanceof EaglerLWJGLShortBuffer) {
EaglerLWJGLShortBuffer c = (EaglerLWJGLShortBuffer)src;
int l = c.limit - c.position;
if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1);
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
UnsafeMemcpy.memcpy(address + (position << SHIFT), c.address + (c.position << SHIFT), l << SHIFT);
position += l;
c.position += l;
}else {
int l = src.remaining();
if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1);
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
for(int i = 0; i < l; ++i) {
UnsafeUtils.setMemInt(address + ((position + l) << SHIFT), src.get());
}
@ -181,7 +159,7 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer {
@Override
public ShortBuffer put(short[] src, int offset, int length) {
if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1);
if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1);
UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, offset << SHIFT, length);
position += length;
return this;
@ -189,36 +167,12 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer {
@Override
public ShortBuffer put(short[] src) {
if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1);
if(position + src.length > limit) throw Buffer.makeIOOBE(position + src.length - 1);
UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, 0, src.length);
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 EaglerLWJGLShortBuffer(0l, 0, false);
}
int newLen = limit - position;
long newAlloc = JEmalloc.nje_malloc(newLen);
if(newAlloc == 0l) {
throw new OutOfMemoryError("Native je_malloc call returned null pointer!");
}
UnsafeMemcpy.memcpy(newAlloc, address + (position << SHIFT), newLen << SHIFT);
return new EaglerLWJGLShortBuffer(newAlloc, newLen, true);
}
@Override
public boolean isDirect() {
return true;
@ -233,7 +187,7 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer {
@Override
public ShortBuffer reset() {
int m = mark;
if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m);
if(m < 0) throw new IndexOutOfBoundsException("Invalid mark: " + m);
position = m;
return this;
}
@ -263,14 +217,14 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer {
@Override
public ShortBuffer limit(int newLimit) {
if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit);
if(newLimit < 0 || newLimit > capacity) throw Buffer.makeIOOBE(newLimit);
limit = newLimit;
return this;
}
@Override
public ShortBuffer position(int newPosition) {
if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition);
if(newPosition < 0 || newPosition > limit) throw Buffer.makeIOOBE(newPosition);
position = newPosition;
return this;
}

View File

@ -5,6 +5,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator;
@ -27,21 +28,33 @@ import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFSIterator2.BreakLoop;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class DebugFilesystem implements PlatformFilesystem.IFilesystemProvider {
public class DebugFilesystem implements IEaglerFilesystem {
public static DebugFilesystem initialize(File filesystemRoot) {
public static DebugFilesystem initialize(String fsName, File filesystemRoot) {
if(!filesystemRoot.isDirectory() && !filesystemRoot.mkdirs()) {
throw new EaglerFileSystemException("Could not create directory for virtual filesystem: " + filesystemRoot.getAbsolutePath());
}
return new DebugFilesystem(filesystemRoot);
return new DebugFilesystem(fsName, filesystemRoot);
}
private final File filesystemRoot;
private final String fsName;
private DebugFilesystem(File root) {
private DebugFilesystem(String fsName, File root) {
this.fsName = fsName;
this.filesystemRoot = root;
}
@Override
public String getFilesystemName() {
return fsName;
}
@Override
public String getInternalDBName() {
return "desktopruntime:" + filesystemRoot.getAbsolutePath();
}
@Override
public boolean eaglerDelete(String pathName) {
File f = getJREFile(pathName);
@ -78,7 +91,7 @@ public class DebugFilesystem implements PlatformFilesystem.IFilesystemProvider {
return tmp;
}catch (IOException e) {
throw new EaglerFileSystemException("Failed to read: " + f.getAbsolutePath(), e);
}catch(ArrayIndexOutOfBoundsException ex) {
}catch(IndexOutOfBoundsException ex) {
throw new EaglerFileSystemException("ERROR: Expected " + fileSize + " bytes, buffer overflow reading: " + f.getAbsolutePath(), ex);
}finally {
if(buf != null) {
@ -221,4 +234,15 @@ public class DebugFilesystem implements PlatformFilesystem.IFilesystemProvider {
f.delete();
}
}
@Override
public boolean isRamdisk() {
return false;
}
@Override
public void closeHandle() {
}
}

View File

@ -31,7 +31,7 @@ public class DesktopClientConfigAdapter implements IClientConfigAdapter {
public static final IClientConfigAdapter instance = new DesktopClientConfigAdapter();
public final List<DefaultServer> defaultServers = new ArrayList();
public final List<DefaultServer> defaultServers = new ArrayList<>();
private final DesktopClientConfigAdapterHooks hooks = new DesktopClientConfigAdapterHooks();
@ -52,17 +52,17 @@ public class DesktopClientConfigAdapter implements IClientConfigAdapter {
@Override
public String getWorldsDB() {
return "desktop";
return "worlds";
}
@Override
public String getResourcePacksDB() {
return "desktop";
return "resourcePacks";
}
@Override
public JSONObject getIntegratedServerOpts() {
return new JSONObject("{\"container\":null,\"worldsDB\":\"desktop\"}");
return new JSONObject("{\"container\":null,\"worldsDB\":\"worlds\"}");
}
private final List<RelayEntry> relays = new ArrayList<>();
@ -148,6 +148,51 @@ public class DesktopClientConfigAdapter implements IClientConfigAdapter {
return true;
}
@Override
public boolean isEnableServerCookies() {
return true;
}
@Override
public boolean isAllowServerRedirects() {
return true;
}
@Override
public boolean isOpenDebugConsoleOnLaunch() {
return false;
}
@Override
public boolean isForceWebViewSupport() {
return false;
}
@Override
public boolean isEnableWebViewCSP() {
return true;
}
@Override
public boolean isAllowBootMenu() {
return false;
}
@Override
public boolean isForceProfanityFilter() {
return false;
}
@Override
public boolean isEaglerNoDelay() {
return false;
}
@Override
public boolean isRamdiskMode() {
return false;
}
@Override
public IClientConfigAdapterHooks getHooks() {
return hooks;
@ -170,5 +215,12 @@ public class DesktopClientConfigAdapter implements IClientConfigAdapter {
}
@Override
public void callScreenChangedHook(String screenName, int scaledWidth, int scaledHeight, int realWidth,
int realHeight, int scaleFactor) {
}
}
}

View File

@ -0,0 +1,109 @@
package net.lax1dude.eaglercraft.v1_8.internal.lwjgl;
import java.net.URI;
import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion;
import net.lax1dude.eaglercraft.v1_8.internal.AbstractWebSocketClient;
import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class DesktopWebSocketClient extends AbstractWebSocketClient {
static final Logger logger = LogManager.getLogger("DesktopWebSocketClient");
volatile EnumEaglerConnectionState playConnectState = EnumEaglerConnectionState.CONNECTING;
final Object connectOpenMutex = new Object();
final WebSocketClientImpl clientImpl;
final URI currentURI;
final String currentURIStr;
public DesktopWebSocketClient(URI currentURI) {
super(currentURI.toString());
this.currentURI = currentURI;
currentURIStr = currentURI.toString();
clientImpl = new WebSocketClientImpl(this, currentURI);
clientImpl.addHeader("Origin", "EAG_LWJGL_" + (EaglercraftVersion.projectForkName + "_"
+ EaglercraftVersion.projectOriginVersion).replaceAll("[^a-zA-Z0-9\\-_\\.]", "_"));
}
@Override
public EnumEaglerConnectionState getState() {
return playConnectState;
}
@Override
public boolean connectBlocking(int timeoutMS) {
synchronized(connectOpenMutex) {
try {
connectOpenMutex.wait(timeoutMS);
} catch (InterruptedException e) {
return false;
}
}
return playConnectState.isOpen();
}
@Override
public boolean isOpen() {
return playConnectState.isOpen();
}
@Override
public boolean isClosed() {
return playConnectState.isClosed();
}
@Override
public void close() {
if(!playConnectState.isClosed()) {
try {
clientImpl.closeBlocking();
} catch (InterruptedException e) {
}
playConnectState = EnumEaglerConnectionState.CLOSED;
}
}
@Override
public void send(String str) {
if(clientImpl.isClosed()) {
logger.error("[{}]: Client tried to send {} char packet while the socket was closed!", currentURIStr, str.length());
}else {
clientImpl.send(str);
}
}
@Override
public void send(byte[] bytes) {
if(clientImpl.isClosed()) {
logger.error("[{}]: Client tried to send {} byte packet while the socket was closed!", currentURIStr, bytes.length);
}else {
clientImpl.send(bytes);
}
}
public void handleString(String str) {
addRecievedFrame(new DesktopWebSocketFrameString(str));
}
public void handleBytes(byte[] array) {
addRecievedFrame(new DesktopWebSocketFrameBinary(array));
}
}

View File

@ -0,0 +1,64 @@
package net.lax1dude.eaglercraft.v1_8.internal.lwjgl;
import java.io.InputStream;
import net.lax1dude.eaglercraft.v1_8.EaglerInputStream;
import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class DesktopWebSocketFrameBinary implements IWebSocketFrame {
private final byte[] byteArray;
private final long timestamp;
public DesktopWebSocketFrameBinary(byte[] byteArray) {
this.byteArray = byteArray;
this.timestamp = PlatformRuntime.steadyTimeMillis();
}
@Override
public boolean isString() {
return false;
}
@Override
public String getString() {
return null;
}
@Override
public byte[] getByteArray() {
return byteArray;
}
@Override
public InputStream getInputStream() {
return new EaglerInputStream(byteArray);
}
@Override
public int getLength() {
return byteArray.length;
}
@Override
public long getTimestamp() {
return timestamp;
}
}

View File

@ -0,0 +1,63 @@
package net.lax1dude.eaglercraft.v1_8.internal.lwjgl;
import java.io.InputStream;
import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class DesktopWebSocketFrameString implements IWebSocketFrame {
private final String string;
private final long timestamp;
public DesktopWebSocketFrameString(String string) {
this.string = string;
this.timestamp = PlatformRuntime.steadyTimeMillis();
}
@Override
public boolean isString() {
return true;
}
@Override
public String getString() {
return string;
}
@Override
public byte[] getByteArray() {
return null;
}
@Override
public InputStream getInputStream() {
return null;
}
@Override
public int getLength() {
return string.length();
}
@Override
public long getTimestamp() {
return timestamp;
}
}

View File

@ -0,0 +1,41 @@
package net.lax1dude.eaglercraft.v1_8.internal.lwjgl;
import fi.iki.elonen.NanoHTTPD;
import fi.iki.elonen.NanoHTTPD.Response.Status;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
class FallbackWebViewHTTPD extends NanoHTTPD {
static final Logger logger = FallbackWebViewServer.logger;
private String index;
FallbackWebViewHTTPD(String hostname, int port, String index) {
super(hostname, port);
this.index = index;
}
@Override
public Response serve(IHTTPSession session) {
if("/RTWebViewClient".equals(session.getUri())) {
return newFixedLengthResponse(Status.OK, MIME_HTML, index);
}else {
return newFixedLengthResponse(Status.NOT_FOUND, MIME_HTML, "<!DOCTYPE html><html><head><title>Eaglercraft Desktop Runtime</title></head><body><h1>404 Not Found</h1></body></html>");
}
}
}

View File

@ -0,0 +1,299 @@
package net.lax1dude.eaglercraft.v1_8.internal.lwjgl;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONException;
import org.json.JSONObject;
import net.lax1dude.eaglercraft.v1_8.internal.WebViewOptions;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.webview.PermissionsCache;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
class FallbackWebViewProtocol {
static final Logger logger = FallbackWebViewServer.logger;
private static final Map<Integer,Class<? extends FallbackWebViewPacket>> packetIDToClass = new HashMap<>();
private static final Map<Class<? extends FallbackWebViewPacket>,Integer> packetClassToID = new HashMap<>();
private static final int CLIENT_TO_SERVER = 0;
private static final int SERVER_TO_CLIENT = 1;
static {
register(0x00, CLIENT_TO_SERVER, CPacketClientHandshake.class);
register(0x01, SERVER_TO_CLIENT, SPacketServerHandshake.class);
register(0x02, SERVER_TO_CLIENT, SPacketServerError.class);
register(0x03, CLIENT_TO_SERVER, CPacketWebViewChannelOpen.class);
register(0x04, CLIENT_TO_SERVER, CPacketWebViewChannelClose.class);
register(0x05, CLIENT_TO_SERVER, CPacketWebViewMessage.class);
register(0x06, SERVER_TO_CLIENT, SPacketWebViewMessage.class);
register(0x07, CLIENT_TO_SERVER, CPacketWebViewJSPermission.class);
}
private static void register(int id, int dir, Class<? extends FallbackWebViewPacket> packet) {
if(dir == CLIENT_TO_SERVER) {
packetIDToClass.put(id, packet);
}else if(dir == SERVER_TO_CLIENT) {
packetClassToID.put(packet, id);
}else {
throw new IllegalArgumentException();
}
}
static String writePacket(FallbackWebViewPacket packet) {
Class<? extends FallbackWebViewPacket> cls = packet.getClass();
Integer id = packetClassToID.get(cls);
if(id == null) {
throw new RuntimeException("Tried to send unknown packet to client: " + cls.getSimpleName());
}
JSONObject json = new JSONObject();
json.put("$", id);
packet.writePacket(json);
return json.toString();
}
static FallbackWebViewPacket readPacket(String data) {
try {
JSONObject json = new JSONObject(data);
int id = json.getInt("$");
Class<? extends FallbackWebViewPacket> cls = packetIDToClass.get(id);
if(cls == null) {
logger.error("Unknown packet ID {} recieved from webview controller", id);
return null;
}
FallbackWebViewPacket ret;
try {
ret = cls.newInstance();
}catch(Throwable t) {
throw new RuntimeException("Failed to call packet constructor for \"" + cls.getSimpleName() + "\"! (is it defined?)");
}
ret.readPacket(json);
return ret;
}catch(Throwable ex) {
logger.error("Failed to parse message from webview controller: \"{}\"", data);
logger.error(ex);
return null;
}
}
static interface FallbackWebViewPacket {
void readPacket(JSONObject json);
void writePacket(JSONObject json);
}
static class CPacketClientHandshake implements FallbackWebViewPacket {
public boolean cspSupport;
public CPacketClientHandshake() {
}
@Override
public void readPacket(JSONObject json) {
cspSupport = json.getBoolean("cspSupport");
}
@Override
public void writePacket(JSONObject json) {
throw new UnsupportedOperationException("Client only!");
}
}
static class CPacketWebViewChannelOpen implements FallbackWebViewPacket {
public String messageChannel;
public CPacketWebViewChannelOpen() {
}
public CPacketWebViewChannelOpen(String messageChannel) {
this.messageChannel = messageChannel;
}
@Override
public void readPacket(JSONObject json) {
messageChannel = json.getString("channel");
if(messageChannel.length() > 255) {
throw new JSONException("Channel name too long!");
}
}
@Override
public void writePacket(JSONObject json) {
throw new UnsupportedOperationException("Client only!");
}
}
static class CPacketWebViewChannelClose implements FallbackWebViewPacket {
public CPacketWebViewChannelClose() {
}
@Override
public void readPacket(JSONObject json) {
}
@Override
public void writePacket(JSONObject json) {
throw new UnsupportedOperationException("Client only!");
}
}
// for string messages, binary are sent as a binary frame
static class CPacketWebViewMessage implements FallbackWebViewPacket {
public String messageContent;
public CPacketWebViewMessage() {
}
public CPacketWebViewMessage(String messageContent) {
this.messageContent = messageContent;
}
@Override
public void readPacket(JSONObject json) {
messageContent = json.getString("msg");
}
@Override
public void writePacket(JSONObject json) {
throw new UnsupportedOperationException("Client only!");
}
}
static class SPacketServerHandshake implements FallbackWebViewPacket {
public WebViewOptions options;
public EnumWebViewJSPermission hasApprovedJS;
public SPacketServerHandshake() {
}
public SPacketServerHandshake(WebViewOptions options, EnumWebViewJSPermission hasApprovedJS) {
this.options = options;
this.hasApprovedJS = hasApprovedJS;
}
@Override
public void readPacket(JSONObject json) {
throw new UnsupportedOperationException("Server only!");
}
@Override
public void writePacket(JSONObject json) {
json.put("contentMode", options.contentMode.toString());
json.put("fallbackTitle", options.fallbackTitle);
json.put("scriptEnabled", options.scriptEnabled);
json.put("strictCSPEnable", options.strictCSPEnable);
json.put("serverMessageAPIEnabled", options.serverMessageAPIEnabled);
json.put("url", options.url);
json.put("blob", options.blob != null ? new String(options.blob, StandardCharsets.UTF_8) : null);
json.put("hasApprovedJS", hasApprovedJS.toString());
}
}
static class SPacketServerError implements FallbackWebViewPacket {
public String errorMessage;
public SPacketServerError() {
}
public SPacketServerError(String errorMessage) {
this.errorMessage = errorMessage;
}
@Override
public void readPacket(JSONObject json) {
throw new UnsupportedOperationException("Server only!");
}
@Override
public void writePacket(JSONObject json) {
json.put("msg", errorMessage);
}
}
static class SPacketWebViewMessage implements FallbackWebViewPacket {
public String message;
public SPacketWebViewMessage() {
}
public SPacketWebViewMessage(String message) {
this.message = message;
}
@Override
public void readPacket(JSONObject json) {
throw new UnsupportedOperationException("Server only!");
}
@Override
public void writePacket(JSONObject json) {
json.put("msg", message);
}
}
static enum EnumWebViewJSPermission {
NOT_SET, ALLOW, BLOCK;
static EnumWebViewJSPermission fromPermission(PermissionsCache.Permission perm) {
if(perm != null) {
return perm.choice ? ALLOW : BLOCK;
}else {
return NOT_SET;
}
}
}
static class CPacketWebViewJSPermission implements FallbackWebViewPacket {
public EnumWebViewJSPermission permission;
public CPacketWebViewJSPermission() {
}
@Override
public void readPacket(JSONObject json) {
permission = EnumWebViewJSPermission.valueOf(json.getString("perm"));
}
@Override
public void writePacket(JSONObject json) {
throw new UnsupportedOperationException("Client only!");
}
}
}

View File

@ -0,0 +1,190 @@
package net.lax1dude.eaglercraft.v1_8.internal.lwjgl;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.charset.StandardCharsets;
import org.json.JSONObject;
import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.WebViewOptions;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketWebViewMessageV4EAG;
import net.lax1dude.eaglercraft.v1_8.webview.WebViewOverlayController.IPacketSendCallback;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class FallbackWebViewServer {
static final Logger logger = LogManager.getLogger("FallbackWebViewServer");
public static final String LISTEN_ADDR = "127.69.69.69";
public static final File webViewClientHTML = new File("RTWebViewClient.html");
public final WebViewOptions options;
private FallbackWebViewWSD websocketServer;
private FallbackWebViewHTTPD httpServer;
private String currentURL;
private volatile boolean dead;
private IPacketSendCallback callback = null;
public FallbackWebViewServer(WebViewOptions options) {
this.options = options;
}
public void start() throws RuntimeException {
dead = false;
StringBuilder vigg = new StringBuilder();
try(BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(webViewClientHTML), StandardCharsets.UTF_8))) {
String line;
while((line = reader.readLine()) != null) {
vigg.append(line).append('\n');
}
}catch(IOException ex) {
logger.error("Failed to read \"{}\"!");
}
String indexHTML = vigg.toString();
Object mutex = new Object();
websocketServer = new FallbackWebViewWSD(LISTEN_ADDR, randomPort(), options);
websocketServer.setEaglerPacketSendCallback(callback);
synchronized(mutex) {
websocketServer.doStartup(mutex);
try {
mutex.wait(5000l);
} catch (InterruptedException e) {
}
}
if(!websocketServer.hasStarted) {
logger.error("Failed to start WebSocket in time!");
try {
websocketServer.stop(5000);
}catch(Throwable t) {
}
websocketServer = null;
throw new RuntimeException("Failed to start WebSocket server!");
}
InetSocketAddress addr = websocketServer.getAddress();
String wsAddr = "ws://" + addr.getHostString() + ":" + addr.getPort() + "/";
logger.info("Listening for WebSocket on {}", wsAddr);
indexHTML = indexHTML.replace("${client_websocket_uri}", wsAddr);
JSONObject optsExport = new JSONObject();
IClientConfigAdapter cfgAdapter = PlatformRuntime.getClientConfigAdapter();
optsExport.put("forceWebViewSupport", cfgAdapter.isForceWebViewSupport());
optsExport.put("enableWebViewCSP", cfgAdapter.isEnableWebViewCSP());
indexHTML = indexHTML.replace("{eaglercraftXOpts}", optsExport.toString());
httpServer = new FallbackWebViewHTTPD(LISTEN_ADDR, 0, indexHTML);
try {
httpServer.start(5000, true);
} catch (IOException e) {
logger.error("Failed to start NanoHTTPD!");
try {
websocketServer.stop(5000);
}catch(Throwable t) {
}
websocketServer = null;
httpServer = null;
throw new RuntimeException("Failed to start NanoHTTPD!", e);
}
int httpPort = httpServer.getListeningPort();
currentURL = "http://" + LISTEN_ADDR + ":" + httpPort + "/RTWebViewClient";
logger.info("Listening for HTTP on {}", currentURL);
}
private int randomPort() {
try(ServerSocket sockler = new ServerSocket(0)) {
return sockler.getLocalPort();
}catch(IOException ex) {
throw new RuntimeException("Failed to find random port to bind to!", ex);
}
}
public boolean isDead() {
return dead;
}
public String getURL() {
return !dead ? currentURL : null;
}
public void handleMessageFromServer(SPacketWebViewMessageV4EAG packet) {
if(packet.type == SPacketWebViewMessageV4EAG.TYPE_STRING) {
if(websocketServer != null) {
websocketServer.handleServerMessageStr(new String(packet.data, StandardCharsets.UTF_8));
}else {
logger.error("Recieved string message, but the webview server is not running!");
}
}else if(packet.type == SPacketWebViewMessageV4EAG.TYPE_BINARY) {
if(websocketServer != null) {
websocketServer.handleServerMessageBytes(packet.data);
}else {
logger.error("Recieved string message, but the webview server is not running!");
}
}else {
logger.error("Unknown server webview message type {}", packet.type);
}
}
public void setPacketSendCallback(IPacketSendCallback callback) {
this.callback = callback;
if(websocketServer != null) {
websocketServer.setEaglerPacketSendCallback(callback);
}
}
public void runTick() {
}
public void killServer() {
if(!dead) {
dead = true;
if(websocketServer != null) {
try {
websocketServer.stop(10000);
} catch (Throwable th) {
logger.error("Failed to stop WebSocket server, aborting");
logger.error(th);
}
websocketServer = null;
}
if(httpServer != null) {
try {
httpServer.stop();
} catch (Throwable th) {
logger.error("Failed to stop HTTP server, aborting");
logger.error(th);
}
httpServer = null;
}
}
}
}

View File

@ -0,0 +1,273 @@
package net.lax1dude.eaglercraft.v1_8.internal.lwjgl;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.WebViewOptions;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketWebViewMessageEnV4EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketWebViewMessageV4EAG;
import net.lax1dude.eaglercraft.v1_8.webview.PermissionsCache;
import net.lax1dude.eaglercraft.v1_8.webview.PermissionsCache.Permission;
import net.lax1dude.eaglercraft.v1_8.webview.WebViewOverlayController.IPacketSendCallback;
import static net.lax1dude.eaglercraft.v1_8.internal.lwjgl.FallbackWebViewProtocol.*;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
class FallbackWebViewWSD extends WebSocketServer {
static final Logger logger = FallbackWebViewServer.logger;
private Object onStartNotify;
volatile boolean hasStarted = false;
private final Object webSockMutex = new Object();
volatile WebSocket webSocket = null;
volatile boolean hasHandshake = false;
volatile String currentChannelName = null;
private IPacketSendCallback callback = null;
WebViewOptions options;
private boolean enableCSP;
private boolean cspSupport;
FallbackWebViewWSD(String address, int port, WebViewOptions options) {
super(new InetSocketAddress(address, port), 1);
this.setTcpNoDelay(true);
this.setReuseAddr(true);
this.setConnectionLostTimeout(30);
this.options = options;
this.enableCSP = PlatformRuntime.getClientConfigAdapter().isEnableWebViewCSP();
this.cspSupport = true;
}
public void doStartup(Object onStartNotify) {
this.onStartNotify = onStartNotify;
this.start();
}
private void handleOpen() {
hasHandshake = false;
currentChannelName = null;
}
private void handleClose() {
if(currentChannelName != null && callback != null) {
callback.sendPacket(new CPacketWebViewMessageEnV4EAG(false, null));
}
currentChannelName = null;
}
private int hashPermissionFlags() {
int i = (options.scriptEnabled ? 1 : 0);
i |= ((enableCSP && cspSupport && options.strictCSPEnable) ? 0 : 2);
i |= (options.serverMessageAPIEnabled ? 4 : 0);
return i;
}
private void handleMessage(String str) {
WebSocket ws = webSocket;
FallbackWebViewPacket _packet = readPacket(str);
if(_packet != null) {
if(!hasHandshake) {
if(_packet instanceof CPacketClientHandshake) {
hasHandshake = true;
Permission perm = PermissionsCache.getJavaScriptAllowed(options.permissionsOriginUUID, hashPermissionFlags());
ws.send(writePacket(new SPacketServerHandshake(options, EnumWebViewJSPermission.fromPermission(perm))));
}else {
terminate("Unknown or unexpected packet: " + _packet.getClass().getSimpleName());
}
}else {
if(_packet instanceof CPacketWebViewChannelOpen) {
CPacketWebViewChannelOpen packet = (CPacketWebViewChannelOpen)_packet;
if(currentChannelName == null) {
currentChannelName = packet.messageChannel;
logger.info("[{}]: opened WebView channel \"{}\"", ws.getRemoteSocketAddress(), packet.messageChannel);
safeCallbackSend(new CPacketWebViewMessageEnV4EAG(true, packet.messageChannel));
}else {
terminate("Tried to open multiple channels");
}
}else if(_packet instanceof CPacketWebViewMessage) {
CPacketWebViewMessage packet = (CPacketWebViewMessage)_packet;
if(currentChannelName != null) {
safeCallbackSend(new CPacketWebViewMessageV4EAG(packet.messageContent));
}else {
terminate("Tried to send message without opening channel");
}
}else if(_packet instanceof CPacketWebViewChannelClose) {
if(currentChannelName != null) {
currentChannelName = null;
safeCallbackSend(new CPacketWebViewMessageEnV4EAG(false, null));
}else {
terminate("Tried to close missing channel");
}
}else if(_packet instanceof CPacketWebViewJSPermission) {
CPacketWebViewJSPermission packet = (CPacketWebViewJSPermission)_packet;
switch(packet.permission) {
case NOT_SET:
PermissionsCache.clearJavaScriptAllowed(options.permissionsOriginUUID);
break;
case ALLOW:
PermissionsCache.setJavaScriptAllowed(options.permissionsOriginUUID, hashPermissionFlags(), true);
break;
case BLOCK:
PermissionsCache.setJavaScriptAllowed(options.permissionsOriginUUID, hashPermissionFlags(), false);
break;
default:
terminate("Unknown permission state selected!");
break;
}
}else {
terminate("Unknown or unexpected packet: " + _packet.getClass().getSimpleName());
}
}
}else {
terminate("Invalid packet recieved");
}
}
private void handleMessage(ByteBuffer buffer) {
if(currentChannelName != null) {
safeCallbackSend(new CPacketWebViewMessageV4EAG(buffer.array()));
}else {
terminate("Sent binary webview message while channel was closed");
}
}
private void terminate(String msg) {
if(webSocket != null) {
logger.error("[{}]: Terminating connection, reason: \"{}\"", webSocket.getRemoteSocketAddress(), msg);
webSocket.send(writePacket(new SPacketServerError(msg)));
webSocket.close();
}
}
private void safeCallbackSend(GameMessagePacket packet) {
if(callback != null) {
callback.sendPacket(packet);
}else {
logger.error("webview sent packet to server, but there's no callback registered to send packets!");
}
}
void handleServerMessageStr(String msg) {
if(webSocket != null) {
if(currentChannelName != null) {
webSocket.send(writePacket(new SPacketWebViewMessage(msg)));
}else {
logger.error("Recieved string message from server, but the channel is not open!");
}
}else {
logger.error("Recieved string message from server, but there is no active websocket!");
}
}
void handleServerMessageBytes(byte[] msg) {
if(webSocket != null) {
if(currentChannelName != null) {
webSocket.send(msg);
}else {
logger.error("Recieved binary message from server, but the channel is not open!");
}
}else {
logger.error("Recieved binary message from server, but there is no active websocket!");
}
}
@Override
public void onStart() {
hasStarted = true;
if(onStartNotify != null) {
synchronized(onStartNotify) {
onStartNotify.notifyAll();
}
onStartNotify = null;
}else {
logger.warn("No mutex to notify!");
}
}
@Override
public void onOpen(WebSocket arg0, ClientHandshake arg1) {
boolean result;
synchronized(webSockMutex) {
if(webSocket == null) {
webSocket = arg0;
result = true;
}else {
result = false;
}
}
if(result) {
logger.info("[{}]: WebSocket connection opened", arg0.getRemoteSocketAddress());
handleOpen();
}else {
logger.error("[{}]: Rejecting duplicate connection", arg0.getRemoteSocketAddress());
arg0.send(writePacket(new SPacketServerError("You already have a tab open!")));
arg0.close();
}
}
@Override
public void onMessage(WebSocket arg0, String arg1) {
if(arg0 == webSocket) {
handleMessage(arg1);
}
}
@Override
public void onMessage(WebSocket arg0, ByteBuffer arg1) {
if(arg0 == webSocket) {
handleMessage(arg1);
}
}
@Override
public void onClose(WebSocket arg0, int arg1, String arg2, boolean arg3) {
synchronized(webSockMutex) {
if(arg0 == webSocket) {
logger.info("[{}]: WebSocket connection closed", arg0.getRemoteSocketAddress());
try {
handleClose();
}finally {
webSocket = null;
}
}
}
}
@Override
public void onError(WebSocket arg0, Exception arg1) {
logger.error("[{}]: WebSocket caught exception", arg0 != null ? arg0.getRemoteSocketAddress() : "null");
logger.error(arg1);
}
public void setEaglerPacketSendCallback(IPacketSendCallback callback) {
this.callback = callback;
}
}

View File

@ -13,8 +13,8 @@ import java.util.LinkedList;
import java.util.Map.Entry;
import java.util.Properties;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem.IFilesystemProvider;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
@ -38,15 +38,16 @@ import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class JDBCFilesystem implements IFilesystemProvider {
public class JDBCFilesystem implements IEaglerFilesystem {
public static final Logger logger = LogManager.getLogger("JDBCFilesystem");
private boolean newFilesystem = true;
private static volatile boolean cleanupThreadStarted = false;
private static final Collection<JDBCFilesystem> jdbcFilesystems = new LinkedList();
private static final Collection<JDBCFilesystem> jdbcFilesystems = new LinkedList<>();
private final String dbName;
private final String jdbcUri;
private final String jdbcDriver;
@ -64,8 +65,8 @@ public class JDBCFilesystem implements IFilesystemProvider {
private final Object mutex = new Object();
public static IFilesystemProvider initialize(String jdbcUri, String jdbcDriver) {
Class driver;
public static IEaglerFilesystem initialize(String dbName, String jdbcUri, String jdbcDriver) {
Class<?> driver;
try {
driver = Class.forName(jdbcDriver);
} catch (ClassNotFoundException e) {
@ -87,8 +88,8 @@ public class JDBCFilesystem implements IFilesystemProvider {
for(Entry<Object, Object> etr : System.getProperties().entrySet()) {
if(etr.getKey() instanceof String) {
String str = (String)etr.getKey();
if(str.startsWith("eagler.jdbc.opts.")) {
props.put(str.substring(17), etr.getValue());
if(str.startsWith("eagler.jdbc." + dbName + ".opts.")) {
props.put(str.substring(18 + dbName.length()), etr.getValue());
}
}
}
@ -104,7 +105,7 @@ public class JDBCFilesystem implements IFilesystemProvider {
throw new EaglerFileSystemException("Failed to connect to database: \"" + jdbcUri + "\"", ex);
}
try {
return new JDBCFilesystem(conn, jdbcUri, jdbcDriver);
return new JDBCFilesystem(dbName, conn, jdbcUri, jdbcDriver);
} catch (SQLException ex) {
try {
conn.close();
@ -114,7 +115,8 @@ public class JDBCFilesystem implements IFilesystemProvider {
}
}
private JDBCFilesystem(Connection conn, String jdbcUri, String jdbcDriver) throws SQLException {
private JDBCFilesystem(String dbName, Connection conn, String jdbcUri, String jdbcDriver) throws SQLException {
this.dbName = dbName;
this.conn = conn;
this.jdbcUri = jdbcUri;
this.jdbcDriver = jdbcDriver;
@ -152,6 +154,16 @@ public class JDBCFilesystem implements IFilesystemProvider {
}
}
@Override
public String getFilesystemName() {
return dbName;
}
@Override
public String getInternalDBName() {
return "desktopruntime:" + jdbcUri;
}
public boolean isNewFilesystem() {
return newFilesystem;
}
@ -172,7 +184,8 @@ public class JDBCFilesystem implements IFilesystemProvider {
}
}
public void shutdown() {
@Override
public void closeHandle() {
shutdown0();
synchronized(jdbcFilesystems) {
jdbcFilesystems.remove(this);
@ -438,4 +451,9 @@ public class JDBCFilesystem implements IFilesystemProvider {
}
}
@Override
public boolean isRamdisk() {
return false;
}
}

View File

@ -7,7 +7,7 @@ import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem.IFilesystemProvider;
import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.EaglerFileSystemException;
@ -33,7 +33,7 @@ public class JDBCFilesystemConverter {
private static final Logger logger = LogManager.getLogger("JDBCFilesystemConverter");
public static void convertFilesystem(String title, File oldFS, IFilesystemProvider newFS, boolean deleteOld) {
public static void convertFilesystem(String title, File oldFS, IEaglerFilesystem newFS, boolean deleteOld) {
FilesystemConvertingDialog progressDialog = new FilesystemConvertingDialog(title);
try {
progressDialog.setProgressIndeterminate(true);
@ -41,7 +41,7 @@ public class JDBCFilesystemConverter {
progressDialog.setVisible(true);
String slug = oldFS.getAbsolutePath();
List<String> filesToCopy = new ArrayList();
List<String> filesToCopy = new ArrayList<>();
logger.info("Discovering files to convert...");
iterateFolder(slug.length(), oldFS, filesToCopy);
logger.info("Found {} files in the old directory", filesToCopy.size());

View File

@ -6,7 +6,6 @@ import javax.swing.UnsupportedLookAndFeelException;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EagUtils;
import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformANGLE;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.ShaderSource;
@ -82,11 +81,12 @@ public class LWJGLEntryPoint {
PlatformInput.setStartupFullscreen(true);
}else if(args[i].equalsIgnoreCase("highp")) {
ShaderSource.setHighP(true);
}else if(args[i].startsWith("jdbc:")) {
if(i < args.length - 1) {
PlatformFilesystem.setUseJDBC(args[i]);
PlatformFilesystem.setJDBCDriverClass(args[++i]);
}
}else if(args[i].equalsIgnoreCase("gles=200")) {
PlatformRuntime.requestGL(200);
}else if(args[i].equalsIgnoreCase("gles=300")) {
PlatformRuntime.requestGL(300);
}else if(args[i].equalsIgnoreCase("gles=310")) {
PlatformRuntime.requestGL(310);
}else {
EnumPlatformANGLE angle = EnumPlatformANGLE.fromId(args[i]);
if(angle != EnumPlatformANGLE.DEFAULT) {

View File

@ -1,4 +1,4 @@
package net.lax1dude.eaglercraft.v1_8.internal;
package net.lax1dude.eaglercraft.v1_8.internal.lwjgl;
import java.net.URI;
import java.nio.ByteBuffer;
@ -9,11 +9,10 @@ import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.extensions.permessage_deflate.PerMessageDeflateExtension;
import org.java_websocket.handshake.ServerHandshake;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState;
/**
* Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved.
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@ -27,51 +26,54 @@ import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
class WebSocketPlayClient extends WebSocketClient {
class WebSocketClientImpl extends WebSocketClient {
private static final Draft perMessageDeflateDraft = new Draft_6455(new PerMessageDeflateExtension());
public static final Logger logger = LogManager.getLogger("WebSocket");
protected final DesktopWebSocketClient clientObj;
WebSocketPlayClient(URI serverUri) {
WebSocketClientImpl(DesktopWebSocketClient clientObj, URI serverUri) {
super(serverUri, perMessageDeflateDraft);
this.clientObj = clientObj;
this.setConnectionLostTimeout(15);
this.setTcpNoDelay(true);
this.connect();
}
@Override
public void onOpen(ServerHandshake arg0) {
PlatformNetworking.playConnectState = EnumEaglerConnectionState.CONNECTED;
PlatformNetworking.serverRateLimit = EnumServerRateLimit.OK;
logger.info("Connection opened: {}", this.uri.toString());
}
@Override
public void onClose(int arg0, String arg1, boolean arg2) {
logger.info("Connection closed: {}", this.uri.toString());
}
@Override
public void onError(Exception arg0) {
logger.error("Exception thrown by websocket \"" + this.getURI().toString() + "\"!");
logger.error(arg0);
PlatformNetworking.playConnectState = EnumEaglerConnectionState.FAILED;
}
@Override
public void onMessage(String arg0) {
if(arg0.equalsIgnoreCase("BLOCKED")) {
logger.error("Reached full IP ratelimit!");
PlatformNetworking.serverRateLimit = EnumServerRateLimit.BLOCKED;
}else if(arg0.equalsIgnoreCase("LOCKED")) {
logger.error("Reached full IP ratelimit lockout!");
PlatformNetworking.serverRateLimit = EnumServerRateLimit.LOCKED_OUT;
clientObj.playConnectState = EnumEaglerConnectionState.CONNECTED;
DesktopWebSocketClient.logger.info("Connection opened: {}", this.uri.toString());
synchronized(clientObj.connectOpenMutex) {
clientObj.connectOpenMutex.notifyAll();
}
}
@Override
public void onMessage(ByteBuffer arg0) {
PlatformNetworking.recievedPlayPacket(arg0.array());
public void onClose(int arg0, String arg1, boolean arg2) {
DesktopWebSocketClient.logger.info("Connection closed: {}", this.uri.toString());
if(clientObj.playConnectState != EnumEaglerConnectionState.FAILED) {
clientObj.playConnectState = EnumEaglerConnectionState.CLOSED;
}
}
@Override
public void onError(Exception arg0) {
DesktopWebSocketClient.logger.error("Exception thrown by websocket \"" + this.getURI().toString() + "\"!");
DesktopWebSocketClient.logger.error(arg0);
if(clientObj.playConnectState == EnumEaglerConnectionState.CONNECTING) {
clientObj.playConnectState = EnumEaglerConnectionState.FAILED;
}
}
@Override
public void onMessage(String arg0) {
clientObj.handleString(arg0);
}
@Override
public void onMessage(ByteBuffer arg0) {
clientObj.handleBytes(arg0.array());
}
}

View File

@ -28,7 +28,7 @@ public class ClientPlatformSingleplayer {
private static CrashScreenPopup crashOverlay = null;
public static void startIntegratedServer() {
public static void startIntegratedServer(boolean forceSingleThread) {
DesktopIntegratedServer.startIntegratedServer();
}
@ -52,7 +52,7 @@ public class ClientPlatformSingleplayer {
if(MemoryConnection.serverToClientQueue.size() == 0) {
return null;
}else {
List<IPCPacketData> ret = new ArrayList(MemoryConnection.serverToClientQueue);
List<IPCPacketData> ret = new ArrayList<>(MemoryConnection.serverToClientQueue);
MemoryConnection.serverToClientQueue.clear();
return ret;
}
@ -71,6 +71,14 @@ public class ClientPlatformSingleplayer {
return false;
}
public static boolean isSingleThreadModeSupported() {
return false;
}
public static void updateSingleThreadMode() {
}
public static void showCrashReportOverlay(String report, int x, int y, int w, int h) {
if(crashOverlay == null) {
crashOverlay = new CrashScreenPopup();

View File

@ -2,10 +2,12 @@ package net.lax1dude.eaglercraft.v1_8.sp.server.internal;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import net.lax1dude.eaglercraft.v1_8.Filesystem;
import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.DesktopClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.lwjgl.MemoryConnection;
@ -26,8 +28,20 @@ import net.lax1dude.eaglercraft.v1_8.sp.server.internal.lwjgl.MemoryConnection;
*/
public class ServerPlatformSingleplayer {
private static IEaglerFilesystem filesystem = null;
public static void initializeContext() {
PlatformFilesystem.initialize();
if(filesystem == null) {
filesystem = Filesystem.getHandleFor(getClientConfigAdapter().getWorldsDB());
}
}
public static void initializeContextSingleThread(Consumer<IPCPacketData> packetSendCallback) {
throw new UnsupportedOperationException();
}
public static IEaglerFilesystem getWorldsDatabase() {
return filesystem;
}
public static void sendPacket(IPCPacketData packet) {
@ -50,7 +64,7 @@ public class ServerPlatformSingleplayer {
if(MemoryConnection.clientToServerQueue.size() == 0) {
return null;
}else {
List<IPCPacketData> ret = new ArrayList(MemoryConnection.clientToServerQueue);
List<IPCPacketData> ret = new ArrayList<>(MemoryConnection.clientToServerQueue);
MemoryConnection.clientToServerQueue.clear();
return ret;
}
@ -60,4 +74,17 @@ public class ServerPlatformSingleplayer {
public static IClientConfigAdapter getClientConfigAdapter() {
return DesktopClientConfigAdapter.instance;
}
public static void immediateContinue() {
}
public static void platformShutdown() {
filesystem = null;
}
public static boolean isSingleThreadMode() {
return false;
}
}

View File

@ -22,7 +22,7 @@ import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData;
*/
public class MemoryConnection {
public static final List<IPCPacketData> clientToServerQueue = new LinkedList();
public static final List<IPCPacketData> serverToClientQueue = new LinkedList();
public static final List<IPCPacketData> clientToServerQueue = new LinkedList<>();
public static final List<IPCPacketData> serverToClientQueue = new LinkedList<>();
}