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

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,59 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import net.lax1dude.eaglercraft.v1_8.recording.EnumScreenRecordingCodec;
/**
* 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 PlatformScreenRecord {
public static boolean isSupported() {
return false;
}
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

@ -1,8 +1,9 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt;
package net.lax1dude.eaglercraft.v1_8.internal.lwjgl;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
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.
@ -19,37 +20,44 @@ import java.io.IOException;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class IPacket70SpecialUpdate extends IPacket {
public class DesktopWebSocketFrameString implements IWebSocketFrame {
public static final int OPERATION_UPDATE_CERTIFICATE = 0x69;
private final String string;
private final long timestamp;
public int operation;
public byte[] updatePacket;
public IPacket70SpecialUpdate() {
}
public IPacket70SpecialUpdate(int operation, byte[] updatePacket) {
this.operation = operation;
this.updatePacket = updatePacket;
public DesktopWebSocketFrameString(String string) {
this.string = string;
this.timestamp = PlatformRuntime.steadyTimeMillis();
}
@Override
public void read(DataInputStream input) throws IOException {
operation = input.read();
updatePacket = new byte[input.readUnsignedShort()];
input.read(updatePacket);
public boolean isString() {
return true;
}
@Override
public void write(DataOutputStream output) throws IOException {
output.write(operation);
output.writeShort(updatePacket.length);
output.write(updatePacket);
public String getString() {
return string;
}
@Override
public int packetLength() {
return 3 + updatePacket.length;
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<>();
}

View File

@ -140,9 +140,9 @@ public abstract class CharMatcher implements Predicate<Character> {
}
// Must be in ascending order.
private static final String ZEROES = "0\u0660\u06f0\u07c0\u0966\u09e6\u0a66\u0ae6\u0b66\u0be6"
+ "\u0c66\u0ce6\u0d66\u0e50\u0ed0\u0f20\u1040\u1090\u17e0\u1810\u1946\u19d0\u1b50\u1bb0"
+ "\u1c40\u1c50\ua620\ua8d0\ua900\uaa50\uff10";
private static final String ZEROES = new String(new char[] { '0', 0x0660, 0x06f0, 0x07c0, 0x0966, 0x09e6, 0x0a66,
0x0ae6, 0x0b66, 0x0be6, 0x0c66, 0x0ce6, 0x0d66, 0x0e50, 0x0ed0, 0x0f20, 0x1040, 0x1090, 0x17e0, 0x1810,
0x1946, 0x19d0, 0x1b50, 0x1bb0, 0x1c40, 0x1c50, 0xa620, 0xa8d0, 0xa900, 0xaa50, 0xff10 });
private static final String NINES;
static {
@ -234,10 +234,10 @@ public abstract class CharMatcher implements Predicate<Character> {
* FORMAT, SURROGATE, and PRIVATE_USE according to ICU4J.
*/
public static final CharMatcher INVISIBLE = new RangesMatcher("CharMatcher.INVISIBLE",
("\u0000\u007f\u00ad\u0600\u061c\u06dd\u070f\u1680\u180e\u2000\u2028\u205f\u2066\u2067\u2068"
+ "\u2069\u206a\u3000\ud800\ufeff\ufff9\ufffa").toCharArray(),
("\u0020\u00a0\u00ad\u0604\u061c\u06dd\u070f\u1680\u180e\u200f\u202f\u2064\u2066\u2067\u2068"
+ "\u2069\u206f\u3000\uf8ff\ufeff\ufff9\ufffb").toCharArray());
new char[] { 0x0000, 0x007f, 0x00ad, 0x0600, 0x061c, 0x06dd, 0x070f, 0x1680, 0x180e, 0x2000, 0x2028, 0x205f,
0x2066, 0x2067, 0x2068, 0x2069, 0x206a, 0x3000, 0xd800, 0xfeff, 0xfff9, 0xfffa },
new char[] { 0x0020, 0x00a0, 0x00ad, 0x0604, 0x061c, 0x06dd, 0x070f, 0x1680, 0x180e, 0x200f, 0x202f, 0x2064,
0x2066, 0x2067, 0x2068, 0x2069, 0x206f, 0x3000, 0xf8ff, 0xfeff, 0xfff9, 0xfffb });
private static String showCharacter(char c) {
String hex = "0123456789ABCDEF";
@ -260,8 +260,8 @@ public abstract class CharMatcher implements Predicate<Character> {
* keep it up to date.
*/
public static final CharMatcher SINGLE_WIDTH = new RangesMatcher("CharMatcher.SINGLE_WIDTH",
"\u0000\u05be\u05d0\u05f3\u0600\u0750\u0e00\u1e00\u2100\ufb50\ufe70\uff61".toCharArray(),
"\u04f9\u05be\u05ea\u05f4\u06ff\u077f\u0e7f\u20af\u213a\ufdff\ufeff\uffdc".toCharArray());
new char[] { 0x0000, 0x05be, 0x05d0, 0x05f3, 0x0600, 0x0750, 0x0e00, 0x1e00, 0x2100, 0xfb50, 0xfe70, 0xff61 },
new char[] { 0x04f9, 0x05be, 0x05ea, 0x05f4, 0x06ff, 0x077f, 0x0e7f, 0x20af, 0x213a, 0xfdff, 0xfeff, 0xffdc });
/** Matches any character. */
public static final CharMatcher ANY = new FastMatcher("CharMatcher.ANY") {
@ -1474,9 +1474,9 @@ public abstract class CharMatcher implements Predicate<Character> {
return description;
}
static final String WHITESPACE_TABLE = "" + "\u2002\u3000\r\u0085\u200A\u2005\u2000\u3000"
+ "\u2029\u000B\u3000\u2008\u2003\u205F\u3000\u1680" + "\u0009\u0020\u2006\u2001\u202F\u00A0\u000C\u2009"
+ "\u3000\u2004\u3000\u3000\u2028\n\u2007\u3000";
static final String WHITESPACE_TABLE = new String(new char[] { 0x2002, 0x3000, '\r', 0x0085, 0x200A, 0x2005, 0x2000,
0x3000, 0x2029, 0x000B, 0x3000, 0x2008, 0x2003, 0x205F, 0x3000, 0x1680, 0x0009, 0x0020, 0x2006, 0x2001,
0x202F, 0x00A0, 0x000C, 0x2009, 0x3000, 0x2004, 0x3000, 0x3000, 0x2028, '\n', 0x2007, 0x3000 });
static final int WHITESPACE_MULTIPLIER = 1682554634;
static final int WHITESPACE_SHIFT = Integer.numberOfLeadingZeros(WHITESPACE_TABLE.length() - 1);

View File

@ -116,6 +116,14 @@ public class Base64 extends BaseNCodec {
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 // 70-7a p-z
};
public static int lookupCharInt(char c) {
return c < 123 ? DECODE_TABLE[c] : -1;
}
public static char lookupIntChar(int i) {
return (char)STANDARD_ENCODE_TABLE[i];
}
/**
* Base64 uses 6-bit fields.
*/

View File

@ -0,0 +1,152 @@
package net.lax1dude.eaglercraft.v1_8;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
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.client.CPacketGetOtherClientUUIDV4EAG;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.AbstractClientPlayer;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
/**
* 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 ClientUUIDLoadingCache {
private static final Logger logger = LogManager.getLogger("ClientUUIDLoadingCache");
public static final EaglercraftUUID NULL_UUID = new EaglercraftUUID(0l, 0l);
public static final EaglercraftUUID PENDING_UUID = new EaglercraftUUID(0x6969696969696969l, 0x6969696969696969l);
public static final EaglercraftUUID VANILLA_UUID = new EaglercraftUUID(0x1DCE015CD384374El, 0x85030A4DE95E5736l);
/**
* For client devs, allows you to get EaglercraftVersion.clientBrandUUID of
* other players on a server, to detect other players who also use your client.
*
* Requires EaglerXBungee 1.3.0 or EaglerXVelocity 1.1.0
*
* @return NULL_UUID if not found, PENDING_UUID if pending,
* VANILLA_UUID if vanilla, or the remote player's
* client's EaglercraftVersion.clientBrandUUID
*/
public static EaglercraftUUID getPlayerClientBrandUUID(EntityPlayer player) {
EaglercraftUUID ret = null;
if(player instanceof AbstractClientPlayer) {
ret = ((AbstractClientPlayer)player).clientBrandUUIDCache;
if(ret == null) {
Minecraft mc = Minecraft.getMinecraft();
if(mc != null && mc.thePlayer != null && mc.thePlayer.sendQueue.getEaglerMessageProtocol().ver >= 4) {
ret = PENDING_UUID;
EaglercraftUUID playerUUID = player.getUniqueID();
if(!waitingUUIDs.containsKey(playerUUID) && !evictedUUIDs.containsKey(playerUUID)) {
int reqID = ++requestId & 0x3FFF;
WaitingLookup newLookup = new WaitingLookup(reqID, playerUUID, EagRuntime.steadyTimeMillis(),
(AbstractClientPlayer) player);
waitingIDs.put(reqID, newLookup);
waitingUUIDs.put(playerUUID, newLookup);
mc.thePlayer.sendQueue.sendEaglerMessage(
new CPacketGetOtherClientUUIDV4EAG(reqID, newLookup.uuid.msb, newLookup.uuid.lsb));
}
}
}
}else if(player instanceof EntityPlayerMP) {
ret = ((EntityPlayerMP)player).clientBrandUUID;
}
if(ret == null) {
ret = NULL_UUID;
}
return ret;
}
private static final Map<Integer,WaitingLookup> waitingIDs = new HashMap<>();
private static final Map<EaglercraftUUID,WaitingLookup> waitingUUIDs = new HashMap<>();
private static final Map<EaglercraftUUID,Long> evictedUUIDs = new HashMap<>();
private static int requestId = 0;
private static long lastFlushReq = EagRuntime.steadyTimeMillis();
private static long lastFlushEvict = EagRuntime.steadyTimeMillis();
public static void update() {
long timestamp = EagRuntime.steadyTimeMillis();
if(timestamp - lastFlushReq > 5000l) {
lastFlushReq = timestamp;
if(!waitingIDs.isEmpty()) {
Iterator<WaitingLookup> itr = waitingIDs.values().iterator();
while(itr.hasNext()) {
WaitingLookup lookup = itr.next();
if(timestamp - lookup.timestamp > 15000l) {
itr.remove();
waitingUUIDs.remove(lookup.uuid);
}
}
}
}
if(timestamp - lastFlushEvict > 1000l) {
lastFlushEvict = timestamp;
if(!evictedUUIDs.isEmpty()) {
Iterator<Long> evictItr = evictedUUIDs.values().iterator();
while(evictItr.hasNext()) {
if(timestamp - evictItr.next().longValue() > 3000l) {
evictItr.remove();
}
}
}
}
}
public static void flushRequestCache() {
waitingIDs.clear();
waitingUUIDs.clear();
evictedUUIDs.clear();
}
public static void handleResponse(int requestId, EaglercraftUUID clientId) {
WaitingLookup lookup = waitingIDs.remove(requestId);
if(lookup != null) {
lookup.player.clientBrandUUIDCache = clientId;
waitingUUIDs.remove(lookup.uuid);
}else {
logger.warn("Unsolicited client brand UUID lookup response #{} recieved! (Brand UUID: {})", requestId, clientId);
}
}
public static void evict(EaglercraftUUID clientId) {
evictedUUIDs.put(clientId, Long.valueOf(EagRuntime.steadyTimeMillis()));
WaitingLookup lk = waitingUUIDs.remove(clientId);
if(lk != null) {
waitingIDs.remove(lk.reqID);
}
}
private static class WaitingLookup {
private final int reqID;
private final EaglercraftUUID uuid;
private final long timestamp;
private final AbstractClientPlayer player;
public WaitingLookup(int reqID, EaglercraftUUID uuid, long timestamp, AbstractClientPlayer player) {
this.reqID = reqID;
this.uuid = uuid;
this.timestamp = timestamp;
this.player = player;
}
}
}

View File

@ -20,6 +20,8 @@ import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput;
public class Display {
private static long lastSwap = 0l;
private static long lastDPIUpdate = -250l;
private static float cacheDPI = 1.0f;
public static int getWidth() {
return PlatformInput.getWindowWidth();
@ -29,6 +31,22 @@ public class Display {
return PlatformInput.getWindowHeight();
}
public static int getVisualViewportX() {
return PlatformInput.getVisualViewportX();
}
public static int getVisualViewportY() {
return PlatformInput.getVisualViewportY();
}
public static int getVisualViewportW() {
return PlatformInput.getVisualViewportW();
}
public static int getVisualViewportH() {
return PlatformInput.getVisualViewportH();
}
public static boolean isActive() {
return PlatformInput.getWindowFocused();
}
@ -61,24 +79,32 @@ public class Display {
boolean limitFPS = limitFramerate > 0 && limitFramerate < 1000;
if(limitFPS) {
long millis = System.currentTimeMillis();
long millis = EagRuntime.steadyTimeMillis();
long frameMillis = (1000l / limitFramerate) - (millis - lastSwap);
if(frameMillis > 0l) {
EagUtils.sleep(frameMillis);
}
}
lastSwap = System.currentTimeMillis();
lastSwap = EagRuntime.steadyTimeMillis();
}
public static boolean contextLost() {
return PlatformInput.contextLost();
}
public static boolean wasResized() {
return PlatformInput.wasResized();
}
public static boolean wasVisualViewportResized() {
return PlatformInput.wasVisualViewportResized();
}
public static boolean supportsFullscreen() {
return PlatformInput.supportsFullscreen();
}
public static boolean isFullscreen() {
return PlatformInput.isFullscreen();
}
@ -87,4 +113,13 @@ public class Display {
PlatformInput.toggleFullscreen();
}
public static float getDPI() {
long millis = EagRuntime.steadyTimeMillis();
if(millis - lastDPIUpdate > 250l) {
lastDPIUpdate = millis;
cacheDPI = PlatformInput.getDPI();
}
return cacheDPI;
}
}

View File

@ -3,17 +3,16 @@ package net.lax1dude.eaglercraft.v1_8;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.InputStreamReader;
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 java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.function.Consumer;
import net.lax1dude.eaglercraft.v1_8.internal.EaglerMissingResourceException;
import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformANGLE;
import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformAgent;
import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformOS;
@ -26,6 +25,7 @@ import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU;
import net.lax1dude.eaglercraft.v1_8.recording.ScreenRecordingController;
import net.lax1dude.eaglercraft.v1_8.update.UpdateService;
/**
@ -70,6 +70,8 @@ public class EagRuntime {
UpdateService.initialize();
EaglerXBungeeVersion.initialize();
EaglercraftGPU.warmUpCache();
ScreenRecordingController.initialize();
PlatformRuntime.postCreate();
}
public static void destroy() {
@ -119,11 +121,23 @@ public class EagRuntime {
public static void freeFloatBuffer(FloatBuffer byteBuffer) {
PlatformRuntime.freeFloatBuffer(byteBuffer);
}
public static boolean getResourceExists(String path) {
return PlatformAssets.getResourceExists(path);
}
public static byte[] getResourceBytes(String path) {
return PlatformAssets.getResourceBytes(path);
}
public static byte[] getRequiredResourceBytes(String path) {
byte[] ret = PlatformAssets.getResourceBytes(path);
if(ret == null) {
throw new EaglerMissingResourceException("Could not load required resource from EPK: " + path);
}
return ret;
}
public static InputStream getResourceStream(String path) {
byte[] b = PlatformAssets.getResourceBytes(path);
if(b != null) {
@ -132,24 +146,41 @@ public class EagRuntime {
return null;
}
}
public static InputStream getRequiredResourceStream(String path) {
byte[] ret = PlatformAssets.getResourceBytes(path);
if(ret == null) {
throw new EaglerMissingResourceException("Could not load required resource from EPK: " + path);
}
return new EaglerInputStream(ret);
}
public static String getResourceString(String path) {
byte[] bytes = PlatformAssets.getResourceBytes(path);
return bytes != null ? new String(bytes, StandardCharsets.UTF_8) : null;
}
public static String getRequiredResourceString(String path) {
byte[] ret = PlatformAssets.getResourceBytes(path);
if(ret == null) {
throw new EaglerMissingResourceException("Could not load required resource from EPK: " + path);
}
return new String(ret, StandardCharsets.UTF_8);
}
public static List<String> getResourceLines(String path) {
byte[] bytes = PlatformAssets.getResourceBytes(path);
if(bytes != null) {
List<String> ret = new ArrayList();
List<String> ret = new ArrayList<>();
try {
BufferedReader rd = new BufferedReader(new StringReader(path));
BufferedReader rd = new BufferedReader(new InputStreamReader(new EaglerInputStream(bytes), StandardCharsets.UTF_8));
String s;
while((s = rd.readLine()) != null) {
ret.add(s);
}
}catch(IOException ex) {
// ??
return null;
}
return ret;
}else {
@ -157,6 +188,14 @@ public class EagRuntime {
}
}
public static List<String> getRequiredResourceLines(String path) {
List<String> ret = getResourceLines(path);
if(ret == null) {
throw new EaglerMissingResourceException("Could not load required resource from EPK: " + path);
}
return ret;
}
public static void debugPrintStackTraceToSTDERR(Throwable t) {
debugPrintStackTraceToSTDERR0("", t);
Throwable c = t.getCause();
@ -180,7 +219,7 @@ public class EagRuntime {
}
public static String[] getStackTraceElements(Throwable t) {
List<String> lst = new ArrayList();
List<String> lst = new ArrayList<>();
PlatformRuntime.getStackTrace(t, (s) -> {
lst.add(s);
});
@ -222,17 +261,23 @@ public class EagRuntime {
PlatformRuntime.exit();
}
/**
* Note to skids: This doesn't do anything in javascript runtime!
*/
public static long maxMemory() {
return PlatformRuntime.maxMemory();
}
/**
* Note to skids: This doesn't do anything in TeaVM runtime!
* Note to skids: This doesn't do anything in javascript runtime!
*/
public static long totalMemory() {
return PlatformRuntime.totalMemory();
}
/**
* Note to skids: This doesn't do anything in javascript runtime!
*/
public static long freeMemory() {
return PlatformRuntime.freeMemory();
}
@ -289,18 +334,6 @@ public class EagRuntime {
return PlatformRuntime.getClientConfigAdapter();
}
public static String getRecText() {
return PlatformRuntime.getRecText();
}
public static void toggleRec() {
PlatformRuntime.toggleRec();
}
public static boolean recSupported() {
return PlatformRuntime.recSupported();
}
public static void openCreditsPopup(String text) {
PlatformApplication.openCreditsPopup(text);
}
@ -317,16 +350,24 @@ public class EagRuntime {
PlatformApplication.showDebugConsole();
}
public static Calendar getLocaleCalendar() {
return Calendar.getInstance(); //TODO: fix teavm calendar's time zone offset
}
public static <T extends DateFormat> T fixDateFormat(T input) {
input.setCalendar(getLocaleCalendar());
return input;
public static void setDisplayBootMenuNextRefresh(boolean en) {
PlatformRuntime.setDisplayBootMenuNextRefresh(en);
}
public static void setMCServerWindowGlobal(String url) {
PlatformApplication.setMCServerWindowGlobal(url);
}
public static long steadyTimeMillis() {
return PlatformRuntime.steadyTimeMillis();
}
public static long nanoTime() {
return PlatformRuntime.nanoTime();
}
public static void immediateContinue() {
PlatformRuntime.immediateContinue();
}
}

View File

@ -1,5 +1,6 @@
package net.lax1dude.eaglercraft.v1_8;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
@ -85,4 +86,12 @@ public class EagUtils {
}
}
public static EaglercraftUUID makeClientBrandUUID(String name) {
return EaglercraftUUID.nameUUIDFromBytes(("EaglercraftXClient:" + name).getBytes(StandardCharsets.UTF_8));
}
public static EaglercraftUUID makeClientBrandUUIDLegacy(String name) {
return EaglercraftUUID.nameUUIDFromBytes(("EaglercraftXClientOld:" + name).getBytes(StandardCharsets.UTF_8));
}
}

View File

@ -74,6 +74,11 @@ public class EaglerOutputStream extends OutputStream {
return count;
}
public void skipBytes(int num) {
ensureCapacity(count + num);
count += num;
}
public void close() throws IOException {
}
}

View File

@ -28,10 +28,7 @@ public class EaglerXBungeeVersion {
private static String pluginFilename = null;
public static void initialize() {
String pluginVersionJson = EagRuntime.getResourceString("plugin_version.json");
if(pluginVersionJson == null) {
throw new RuntimeException("File \"plugin_version.json\" is missing in the epk!");
}
String pluginVersionJson = EagRuntime.getRequiredResourceString("plugin_version.json");
JSONObject json = new JSONObject(pluginVersionJson);
pluginName = json.getString("pluginName");
pluginVersion = json.getString("pluginVersion");
@ -76,11 +73,7 @@ public class EaglerXBungeeVersion {
}
public static byte[] getPluginDownload() {
byte[] ret = EagRuntime.getResourceBytes(pluginFileEPK);
if(ret == null) {
throw new RuntimeException("File \"" + pluginFileEPK + "\" is missing in the epk!");
}
return ret;
return EagRuntime.getRequiredResourceBytes(pluginFileEPK);
}
public static void startPluginDownload() {

View File

@ -130,10 +130,10 @@ public class EaglercraftSoundManager {
settings.getSoundLevel(SoundCategory.RECORDS), settings.getSoundLevel(SoundCategory.WEATHER),
settings.getSoundLevel(SoundCategory.BLOCKS), settings.getSoundLevel(SoundCategory.MOBS),
settings.getSoundLevel(SoundCategory.ANIMALS), settings.getSoundLevel(SoundCategory.PLAYERS),
settings.getSoundLevel(SoundCategory.AMBIENT), settings.getSoundLevel(SoundCategory.VOICE)
settings.getSoundLevel(SoundCategory.AMBIENT)
};
activeSounds = new LinkedList();
queuedSounds = new LinkedList();
activeSounds = new LinkedList<>();
queuedSounds = new LinkedList<>();
}
public void unloadSoundSystem() {

View File

@ -21,6 +21,8 @@ public class EaglercraftUUID implements Comparable<EaglercraftUUID> {
public final long msb;
public final long lsb;
private int hash = 0;
private boolean hasHash;
public EaglercraftUUID(long msb, long lsb) {
this.msb = msb;
@ -125,13 +127,21 @@ public class EaglercraftUUID implements Comparable<EaglercraftUUID> {
@Override
public int hashCode() {
long hilo = msb ^ lsb;
return ((int) (hilo >> 32)) ^ (int) hilo;
if(hash == 0 && !hasHash) {
long hilo = msb ^ lsb;
hash = ((int) (hilo >> 32)) ^ (int) hilo;
hasHash = true;
}
return hash;
}
@Override
public boolean equals(Object o) {
return (o instanceof EaglercraftUUID) && ((EaglercraftUUID) o).lsb == lsb && ((EaglercraftUUID) o).msb == msb;
if(!(o instanceof EaglercraftUUID)) return false;
EaglercraftUUID oo = (EaglercraftUUID)o;
return (hasHash && oo.hasHash)
? (hash == oo.hash && msb == oo.msb && lsb == oo.lsb)
: (msb == oo.msb && lsb == oo.lsb);
}
public long getMostSignificantBits() {

View File

@ -10,7 +10,7 @@ public class EaglercraftVersion {
/// Customize these to fit your fork:
public static final String projectForkName = "EaglercraftX";
public static final String projectForkVersion = "u36";
public static final String projectForkVersion = "u37";
public static final String projectForkVendor = "lax1dude";
public static final String projectForkURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8";
@ -20,7 +20,7 @@ public class EaglercraftVersion {
public static final String projectOriginName = "EaglercraftX";
public static final String projectOriginAuthor = "lax1dude";
public static final String projectOriginRevision = "1.8";
public static final String projectOriginVersion = "u36";
public static final String projectOriginVersion = "u37";
public static final String projectOriginURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8"; // rest in peace
@ -31,7 +31,7 @@ public class EaglercraftVersion {
public static final boolean enableUpdateService = true;
public static final String updateBundlePackageName = "net.lax1dude.eaglercraft.v1_8.client";
public static final int updateBundlePackageVersionInt = 36;
public static final int updateBundlePackageVersionInt = 37;
public static final String updateLatestLocalStorageKey = "latestUpdate_" + updateBundlePackageName;
@ -40,6 +40,13 @@ public class EaglercraftVersion {
// Client brand identification system configuration
public static final EaglercraftUUID clientBrandUUID = EagUtils.makeClientBrandUUID(projectForkName);
public static final EaglercraftUUID legacyClientUUIDInSharedWorld = EagUtils.makeClientBrandUUIDLegacy(projectOriginName);
// Miscellaneous variables:
public static final String mainMenuStringA = "Minecraft 1.8.8";

View File

@ -0,0 +1,158 @@
package net.lax1dude.eaglercraft.v1_8;
import java.util.HashMap;
import java.util.Map;
import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.RamdiskFilesystemImpl;
import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
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 Filesystem {
private static final Logger logger = LogManager.getLogger("PlatformFilesystem");
private static final Map<String,FilesystemHandle> openFilesystems = new HashMap<>();
public static IEaglerFilesystem getHandleFor(String dbName) {
FilesystemHandle handle = openFilesystems.get(dbName);
if(handle != null) {
++handle.refCount;
return new FilesystemHandleWrapper(handle);
}
IEaglerFilesystem handleImpl = null;
if(!EagRuntime.getConfiguration().isRamdiskMode()) {
handleImpl = PlatformFilesystem.initializePersist(dbName);
}
if(handleImpl == null) {
handleImpl = new RamdiskFilesystemImpl(dbName);
}
if(handleImpl.isRamdisk()) {
logger.warn("Using RAMDisk filesystem for database \"{}\", data will not be saved to local storage!", dbName);
}
handle = new FilesystemHandle(handleImpl);
openFilesystems.put(dbName, handle);
return new FilesystemHandleWrapper(handle);
}
public static void closeAllHandles() {
for(FilesystemHandle handle : openFilesystems.values()) {
handle.refCount = 0;
handle.handle.closeHandle();
}
openFilesystems.clear();
}
private static class FilesystemHandle {
private final IEaglerFilesystem handle;
private int refCount;
private FilesystemHandle(IEaglerFilesystem handle) {
this.handle = handle;
this.refCount = 1;
}
}
private static class FilesystemHandleWrapper implements IEaglerFilesystem {
private final FilesystemHandle handle;
private final IEaglerFilesystem handleImpl;
private boolean closed;
private FilesystemHandleWrapper(FilesystemHandle handle) {
this.handle = handle;
this.handleImpl = handle.handle;
this.closed = false;
}
@Override
public String getFilesystemName() {
return handleImpl.getFilesystemName();
}
@Override
public String getInternalDBName() {
return handleImpl.getInternalDBName();
}
@Override
public boolean isRamdisk() {
return handleImpl.isRamdisk();
}
@Override
public boolean eaglerDelete(String pathName) {
return handleImpl.eaglerDelete(pathName);
}
@Override
public ByteBuffer eaglerRead(String pathName) {
return handleImpl.eaglerRead(pathName);
}
@Override
public void eaglerWrite(String pathName, ByteBuffer data) {
handleImpl.eaglerWrite(pathName, data);
}
@Override
public boolean eaglerExists(String pathName) {
return handleImpl.eaglerExists(pathName);
}
@Override
public boolean eaglerMove(String pathNameOld, String pathNameNew) {
return handleImpl.eaglerMove(pathNameOld, pathNameNew);
}
@Override
public int eaglerCopy(String pathNameOld, String pathNameNew) {
return handleImpl.eaglerCopy(pathNameOld, pathNameNew);
}
@Override
public int eaglerSize(String pathName) {
return handleImpl.eaglerSize(pathName);
}
@Override
public void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive) {
handleImpl.eaglerIterate(pathName, itr, recursive);
}
@Override
public void closeHandle() {
if(!closed && handle.refCount > 0) {
closed = true;
--handle.refCount;
if(handle.refCount <= 0) {
logger.info("Releasing filesystem handle for: \"{}\"", handleImpl.getFilesystemName());
handleImpl.closeHandle();
openFilesystems.remove(handleImpl.getFilesystemName());
}
}
}
}
}

View File

@ -0,0 +1,115 @@
package net.lax1dude.eaglercraft.v1_8;
import java.util.LinkedList;
import java.util.List;
import net.lax1dude.eaglercraft.v1_8.internal.GamepadConstants;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput;
/**
* 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 Gamepad {
private static final boolean[] buttonsLastState = new boolean[24];
private static final List<VirtualButtonEvent> buttonEvents = new LinkedList<>();
private static VirtualButtonEvent currentVEvent = null;
private static class VirtualButtonEvent {
private final int button;
private final boolean state;
public VirtualButtonEvent(int button, boolean state) {
this.button = button;
this.state = state;
}
}
public static int getValidDeviceCount() {
return PlatformInput.gamepadGetValidDeviceCount();
}
public static String getDeviceName(int deviceId) {
return PlatformInput.gamepadGetDeviceName(deviceId);
}
public static void setSelectedDevice(int deviceId) {
PlatformInput.gamepadSetSelectedDevice(deviceId);
}
public static void update() {
PlatformInput.gamepadUpdate();
if(isValid()) {
for(int i = 0; i < buttonsLastState.length; ++i) {
boolean b = PlatformInput.gamepadGetButtonState(i);
if(b != buttonsLastState[i]) {
buttonsLastState[i] = b;
buttonEvents.add(new VirtualButtonEvent(i, b));
if(buttonEvents.size() > 64) {
buttonEvents.remove(0);
}
}
}
}else {
for(int i = 0; i < buttonsLastState.length; ++i) {
buttonsLastState[i] = false;
}
}
}
public static boolean next() {
currentVEvent = null;
return !buttonEvents.isEmpty() && (currentVEvent = buttonEvents.remove(0)) != null;
}
public static int getEventButton() {
return currentVEvent != null ? currentVEvent.button : -1;
}
public static boolean getEventButtonState() {
return currentVEvent != null ? currentVEvent.state : false;
}
public static boolean isValid() {
return PlatformInput.gamepadIsValid();
}
public static String getName() {
return PlatformInput.gamepadGetName();
}
public static boolean getButtonState(int button) {
return PlatformInput.gamepadGetButtonState(button);
}
public static String getButtonName(int button) {
return GamepadConstants.getButtonName(button);
}
public static float getAxis(int axis) {
return PlatformInput.gamepadGetAxis(axis);
}
public static String getAxisName(int button) {
return GamepadConstants.getAxisName(button);
}
public static void clearEventBuffer() {
buttonEvents.clear();
}
}

View File

@ -0,0 +1,54 @@
package net.lax1dude.eaglercraft.v1_8;
import java.util.Arrays;
/**
* 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 HashKey {
public final byte[] key;
public final int hash;
public HashKey(byte[] key, int hashCode) {
this.key = key;
this.hash = hashCode;
}
public HashKey(byte[] key) {
this.key = key;
this.hash = Arrays.hashCode(key);
}
public Object clone() {
return new HashKey(key, hash);
}
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || !(obj instanceof HashKey))
return false;
HashKey other = (HashKey) obj;
return hash == other.hash && Arrays.equals(key, other.key);
}
}

View File

@ -32,7 +32,7 @@ public class IOUtils {
return Arrays.asList(
new String(((EaglerInputStream) parInputStream).getAsArray(), charset).split("(\\r\\n|\\n|\\r)"));
}else {
List<String> ret = new ArrayList();
List<String> ret = new ArrayList<>();
try(InputStream is = parInputStream) {
BufferedReader rd = new BufferedReader(new InputStreamReader(is, charset));
String s;

View File

@ -1,5 +1,6 @@
package net.lax1dude.eaglercraft.v1_8;
import net.lax1dude.eaglercraft.v1_8.internal.EnumFireKeyboardEvent;
import net.lax1dude.eaglercraft.v1_8.internal.KeyboardConstants;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput;
@ -60,4 +61,8 @@ public class Keyboard {
return PlatformInput.keyboardIsRepeatEvent();
}
public static void fireEvent(EnumFireKeyboardEvent eventType, int eagKey, char keyChar) {
PlatformInput.keyboardFireEvent(eventType, eagKey, keyChar);
}
}

View File

@ -1,6 +1,7 @@
package net.lax1dude.eaglercraft.v1_8;
import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType;
import net.lax1dude.eaglercraft.v1_8.internal.EnumFireMouseEvent;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput;
/**
@ -92,6 +93,22 @@ public class Mouse {
return PlatformInput.isMouseGrabbed();
}
public static boolean isMouseGrabSupported() {
return PlatformInput.mouseGrabSupported();
}
public static void fireMoveEvent(EnumFireMouseEvent eventType, int posX, int posY) {
PlatformInput.mouseFireMoveEvent(eventType, posX, posY);
}
public static void fireButtonEvent(EnumFireMouseEvent eventType, int posX, int posY, int button) {
PlatformInput.mouseFireButtonEvent(eventType, posX, posY, button);
}
public static void fireWheelEvent(EnumFireMouseEvent eventType, int posX, int posY, float wheel) {
PlatformInput.mouseFireWheelEvent(eventType, posX, posY, wheel);
}
private static int customCursorCounter = 0;
private static EnumCursorType currentCursorType = EnumCursorType.DEFAULT;

View File

@ -0,0 +1,257 @@
package net.lax1dude.eaglercraft.v1_8;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.opengl.ImageData;
import net.lax1dude.eaglercraft.v1_8.profile.EaglerSkinTexture;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketCustomizePauseMenuV4EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.PacketImageData;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.util.ResourceLocation;
/**
* 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 PauseMenuCustomizeState {
private static final Logger logger = LogManager.getLogger("PauseMenuCustomizeState");
public static ResourceLocation icon_title_L;
public static float icon_title_L_aspect = 1.0f;
public static ResourceLocation icon_title_R;
public static float icon_title_R_aspect = 1.0f;
public static ResourceLocation icon_backToGame_L;
public static float icon_backToGame_L_aspect = 1.0f;
public static ResourceLocation icon_backToGame_R;
public static float icon_backToGame_R_aspect = 1.0f;
public static ResourceLocation icon_achievements_L;
public static float icon_achievements_L_aspect = 1.0f;
public static ResourceLocation icon_achievements_R;
public static float icon_achievements_R_aspect = 1.0f;
public static ResourceLocation icon_statistics_L;
public static float icon_statistics_L_aspect = 1.0f;
public static ResourceLocation icon_statistics_R;
public static float icon_statistics_R_aspect = 1.0f;
public static ResourceLocation icon_serverInfo_L;
public static float icon_serverInfo_L_aspect = 1.0f;
public static ResourceLocation icon_serverInfo_R;
public static float icon_serverInfo_R_aspect = 1.0f;
public static ResourceLocation icon_options_L;
public static float icon_options_L_aspect = 1.0f;
public static ResourceLocation icon_options_R;
public static float icon_options_R_aspect = 1.0f;
public static ResourceLocation icon_discord_L;
public static float icon_discord_L_aspect = 1.0f;
public static ResourceLocation icon_discord_R;
public static float icon_discord_R_aspect = 1.0f;
public static ResourceLocation icon_disconnect_L;
public static float icon_disconnect_L_aspect = 1.0f;
public static ResourceLocation icon_disconnect_R;
public static float icon_disconnect_R_aspect = 1.0f;
public static ResourceLocation icon_background_pause;
public static float icon_background_pause_aspect = 1.0f;
public static ResourceLocation icon_background_all;
public static float icon_background_all_aspect = 1.0f;
public static ResourceLocation icon_watermark_pause;
public static float icon_watermark_pause_aspect = 1.0f;
public static ResourceLocation icon_watermark_all;
public static float icon_watermark_all_aspect = 1.0f;
public static final int SERVER_INFO_MODE_NONE = SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_NONE;
public static final int SERVER_INFO_MODE_EXTERNAL_URL = SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_EXTERNAL_URL;
public static final int SERVER_INFO_MODE_SHOW_EMBED_OVER_HTTP = SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_SHOW_EMBED_OVER_HTTP;
public static final int SERVER_INFO_MODE_SHOW_EMBED_OVER_WS = SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_SHOW_EMBED_OVER_WS;
public static final int SERVER_INFO_EMBED_PERMS_JAVASCRIPT = SPacketCustomizePauseMenuV4EAG.SERVER_INFO_EMBED_PERMS_JAVASCRIPT;
public static final int SERVER_INFO_EMBED_PERMS_MESSAGE_API = SPacketCustomizePauseMenuV4EAG.SERVER_INFO_EMBED_PERMS_MESSAGE_API;
public static final int SERVER_INFO_EMBED_PERMS_STRICT_CSP = SPacketCustomizePauseMenuV4EAG.SERVER_INFO_EMBED_PERMS_STRICT_CSP;
public static final int DISCORD_MODE_NONE = SPacketCustomizePauseMenuV4EAG.DISCORD_MODE_NONE;
public static final int DISCORD_MODE_INVITE_URL = SPacketCustomizePauseMenuV4EAG.DISCORD_MODE_INVITE_URL;
public static int serverInfoMode;
public static int serverInfoEmbedPerms = SERVER_INFO_EMBED_PERMS_STRICT_CSP;
public static String serverInfoEmbedTitle;
public static String serverInfoButtonText;
public static String serverInfoURL;
public static byte[] serverInfoHash;
public static int discordButtonMode;
public static String discordButtonText;
public static String discordInviteURL;
private static final List<PauseMenuSprite> toFree = new ArrayList<>();
private static int textureId = 0;
private static class PauseMenuSprite {
private final ResourceLocation loc;
private final EaglerSkinTexture tex;
public PauseMenuSprite(EaglerSkinTexture tex) {
this.loc = newLoc();
this.tex = tex;
}
}
public static void loadPacket(SPacketCustomizePauseMenuV4EAG packet) {
reset();
serverInfoMode = packet.serverInfoMode;
switch(packet.serverInfoMode) {
case SERVER_INFO_MODE_NONE:
default:
serverInfoButtonText = null;
serverInfoURL = null;
serverInfoHash = null;
break;
case SERVER_INFO_MODE_EXTERNAL_URL:
serverInfoButtonText = packet.serverInfoButtonText;
serverInfoURL = packet.serverInfoURL;
break;
case SERVER_INFO_MODE_SHOW_EMBED_OVER_HTTP:
serverInfoButtonText = packet.serverInfoButtonText;
serverInfoEmbedPerms = packet.serverInfoEmbedPerms;
serverInfoURL = packet.serverInfoURL;
serverInfoEmbedTitle = packet.serverInfoEmbedTitle;
break;
case SERVER_INFO_MODE_SHOW_EMBED_OVER_WS:
serverInfoButtonText = packet.serverInfoButtonText;
serverInfoEmbedPerms = packet.serverInfoEmbedPerms;
serverInfoHash = packet.serverInfoHash;
serverInfoEmbedTitle = packet.serverInfoEmbedTitle;
break;
}
discordButtonMode = packet.discordButtonMode;
switch(packet.discordButtonMode) {
case DISCORD_MODE_NONE:
default:
discordButtonText = null;
serverInfoURL = null;
serverInfoHash = null;
break;
case DISCORD_MODE_INVITE_URL:
discordButtonText = packet.discordButtonText;
discordInviteURL = packet.discordInviteURL;
break;
}
if(packet.imageMappings != null) {
Map<Integer, PauseMenuSprite> spriteCache = new HashMap<>();
icon_title_L = cacheLoadHelperFunction("icon_title_L", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_title_L_aspect = a);
icon_title_R = cacheLoadHelperFunction("icon_title_R", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_title_R_aspect = a);
icon_backToGame_L = cacheLoadHelperFunction("icon_backToGame_L", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_backToGame_L_aspect = a);
icon_backToGame_R = cacheLoadHelperFunction("icon_backToGame_R", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_backToGame_R_aspect = a);
icon_achievements_L = cacheLoadHelperFunction("icon_achievements_L", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_achievements_L_aspect = a);
icon_achievements_R = cacheLoadHelperFunction("icon_achievements_R", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_achievements_R_aspect = a);
icon_statistics_L = cacheLoadHelperFunction("icon_statistics_L", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_statistics_L_aspect = a);
icon_statistics_R = cacheLoadHelperFunction("icon_statistics_R", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_statistics_R_aspect = a);
icon_serverInfo_L = cacheLoadHelperFunction("icon_serverInfo_L", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_serverInfo_L_aspect = a);
icon_serverInfo_R = cacheLoadHelperFunction("icon_serverInfo_R", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_serverInfo_R_aspect = a);
icon_options_L = cacheLoadHelperFunction("icon_options_L", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_options_L_aspect = a);
icon_options_R = cacheLoadHelperFunction("icon_options_R", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_options_R_aspect = a);
icon_discord_L = cacheLoadHelperFunction("icon_discord_L", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_discord_L_aspect = a);
icon_discord_R = cacheLoadHelperFunction("icon_discord_R", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_discord_R_aspect = a);
icon_disconnect_L = cacheLoadHelperFunction("icon_disconnect_L", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_disconnect_L_aspect = a);
icon_disconnect_R = cacheLoadHelperFunction("icon_disconnect_R", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_disconnect_R_aspect = a);
icon_background_pause = cacheLoadHelperFunction("icon_background_pause", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_background_pause_aspect = a);
icon_background_all = cacheLoadHelperFunction("icon_background_all", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_background_all_aspect = a);
icon_watermark_pause = cacheLoadHelperFunction("icon_watermark_pause", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_watermark_pause_aspect = a);
icon_watermark_all = cacheLoadHelperFunction("icon_watermark_all", packet.imageMappings, spriteCache, packet.imageData, (a) -> icon_watermark_all_aspect = a);
}
}
private static ResourceLocation cacheLoadHelperFunction(String name, Map<String, Integer> lookup,
Map<Integer, PauseMenuSprite> spriteCache,
List<PacketImageData> sourceData, Consumer<Float> aspectCB) {
Integer i = lookup.get(name);
if(i == null) {
return null;
}
PauseMenuSprite ret = spriteCache.get(i);
if(ret != null) {
if(name.startsWith("icon_background_") && ImageData.isNPOTStatic(ret.tex.getWidth(), ret.tex.getHeight())) {
logger.warn("An NPOT (non-power-of-two) texture was used for \"{}\", this texture's width and height must be powers of two for this texture to display properly on all hardware");
}
aspectCB.accept((float)ret.tex.getWidth() / ret.tex.getHeight());
return ret.loc;
}
int ii = i.intValue();
if(ii < 0 || ii >= sourceData.size()) {
return null;
}
PacketImageData data = sourceData.get(ii);
ret = new PauseMenuSprite(new EaglerSkinTexture(ImageData.swapRB(data.rgba), data.width, data.height));
Minecraft.getMinecraft().getTextureManager().loadTexture(ret.loc, ret.tex);
spriteCache.put(i, ret);
toFree.add(ret);
aspectCB.accept((float)data.width / data.height);
return ret.loc;
}
private static ResourceLocation newLoc() {
return new ResourceLocation("eagler:gui/server/custom_pause_menu/tex_" + textureId++);
}
public static void reset() {
icon_title_L = icon_title_R = null;
icon_backToGame_L = icon_backToGame_R = null;
icon_achievements_L = icon_achievements_R = null;
icon_statistics_L = icon_statistics_R = null;
icon_serverInfo_L = icon_serverInfo_R = null;
icon_options_L = icon_options_R = null;
icon_discord_L = icon_discord_R = null;
icon_disconnect_L = icon_disconnect_R = null;
icon_background_pause = icon_background_all = null;
icon_watermark_pause = icon_watermark_all = null;
icon_title_L_aspect = icon_title_R_aspect = 1.0f;
icon_backToGame_L_aspect = icon_backToGame_R_aspect = 1.0f;
icon_achievements_L_aspect = icon_achievements_R_aspect = 1.0f;
icon_statistics_L_aspect = icon_statistics_R_aspect = 1.0f;
icon_serverInfo_L_aspect = icon_serverInfo_R_aspect = 1.0f;
icon_options_L_aspect = icon_options_R_aspect = 1.0f;
icon_discord_L_aspect = icon_discord_R_aspect = 1.0f;
icon_disconnect_L_aspect = icon_disconnect_R_aspect = 1.0f;
icon_background_pause_aspect = icon_background_all_aspect = 1.0f;
icon_watermark_pause_aspect = icon_watermark_all_aspect = 1.0f;
serverInfoMode = 0;
serverInfoEmbedPerms = SERVER_INFO_EMBED_PERMS_STRICT_CSP;
serverInfoButtonText = null;
serverInfoURL = null;
serverInfoHash = null;
serverInfoEmbedTitle = null;
discordButtonMode = 0;
discordButtonText = null;
discordInviteURL = null;
if(!toFree.isEmpty()) {
TextureManager mgr = Minecraft.getMinecraft().getTextureManager();
for(PauseMenuSprite rc : toFree) {
mgr.deleteTexture(rc.loc);
}
toFree.clear();
}
}
}

View File

@ -0,0 +1,227 @@
package net.lax1dude.eaglercraft.v1_8;
import net.lax1dude.eaglercraft.v1_8.touch_gui.TouchControls;
import net.minecraft.client.Minecraft;
/**
* Copyright (c) 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
* 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 PointerInputAbstraction {
protected static Minecraft mc;
protected static int oldMX = -1;
protected static int oldMY = -1;
protected static int oldTX = -1;
protected static int oldTY = -1;
protected static int dragStartX = -1;
protected static int dragStartY = -1;
protected static int dragStepStartX = -1;
protected static int dragStepStartY = -1;
protected static int dragUID = -1;
protected static int cursorX = -1;
protected static int cursorY = -1;
protected static int cursorDX = 0;
protected static int cursorDY = 0;
protected static boolean touchingScreen = false;
protected static boolean touchingScreenNotButton = false;
protected static boolean draggingNotTouching = false;
protected static boolean touchMode = false;
public static void init(Minecraft mcIn) {
mc = mcIn;
oldMX = -1;
oldMY = -1;
oldTX = -1;
oldTY = -1;
dragStartX = -1;
dragStartY = -1;
dragStepStartX = -1;
dragStepStartY = -1;
dragUID = -1;
cursorX = -1;
cursorY = -1;
cursorDX = 0;
cursorDY = 0;
touchingScreen = false;
touchingScreenNotButton = false;
draggingNotTouching = false;
touchMode = !mcIn.mouseGrabSupported;
}
public static void runGameLoop() {
if(touchMode) {
runTouchUpdate();
}else {
oldTX = -1;
oldTY = -1;
cursorX = oldMX = Mouse.getX();
cursorY = oldMY = Mouse.getY();
cursorDX += Mouse.getDX();
cursorDY += Mouse.getDY();
}
}
private static void runTouchUpdate() {
int tc = Touch.touchPointCount();
if (tc > 0) {
TouchControls.update(true);
touchingScreen = true;
for(int i = 0; i < tc; ++i) {
int uid = Touch.touchPointUID(i);
if(TouchControls.touchControls.containsKey(uid)) {
continue;
}
int tx = Touch.touchPointX(i);
int ty = Touch.touchPointY(i);
if(TouchControls.overlappingControl(tx, ty) != null) {
continue;
}
if(mc.currentScreen == null && mc.ingameGUI.isTouchOverlapEagler(uid, tx, ty)) {
continue;
}
cursorX = oldTX = tx;
cursorY = oldTY = ty;
oldMX = Mouse.getX();
oldMY = Mouse.getY();
touchingScreenNotButton = true;
runTouchDeltaUpdate(uid);
return;
}
touchingScreenNotButton = false;
} else {
TouchControls.update(false);
touchingScreen = false;
touchingScreenNotButton = false;
dragStepStartX = -1;
dragStepStartY = -1;
dragStartX = -1;
dragStartY = -1;
dragUID = -1;
final int tmp = Mouse.getX();
final int tmp2 = Mouse.getY();
if(oldTX == -1 || oldTY == -1) {
cursorX = oldMX = tmp;
cursorY = oldMY = tmp2;
cursorDX += Mouse.getDX();
cursorDY += Mouse.getDY();
return;
}
if (oldMX == -1 || oldMY == -1) {
oldMX = tmp;
oldMY = tmp2;
}
if (oldMX == tmp && oldMY == tmp2) {
cursorX = oldTX;
cursorY = oldTY;
}else {
cursorX = oldMX = tmp;
cursorY = oldMY = tmp2;
cursorDX += Mouse.getDX();
cursorDY += Mouse.getDY();
}
}
}
private static void runTouchDeltaUpdate(int uid) {
if(uid != dragUID) {
dragStartX = oldTX;
dragStartY = oldTY;
dragStepStartX = -1;
dragStepStartY = -1;
dragUID = uid;
draggingNotTouching = false;
return;
}
if(dragStepStartX != -1) {
cursorDX += oldTX - dragStepStartX;
}
dragStepStartX = oldTX;
if(dragStepStartY != -1) {
cursorDY += oldTY - dragStepStartY;
}
dragStepStartY = oldTY;
if(dragStartX != -1 && dragStartY != -1) {
int dx = oldTX - dragStartX;
int dy = oldTY - dragStartY;
int len = dx * dx + dy * dy;
int dm = Math.max((int)(6 * Display.getDPI()), 2);
if(len > dm * dm) {
draggingNotTouching = true;
}
}
}
public static boolean isTouchMode() {
return touchMode;
}
public static boolean isTouchingScreen() {
return touchingScreen;
}
public static boolean isTouchingScreenNotButton() {
return touchingScreenNotButton;
}
public static boolean isDraggingNotTouching() {
return draggingNotTouching;
}
public static void enterTouchModeHook() {
if(!touchMode) {
touchMode = true;
if(mc.mouseGrabSupported) {
mc.mouseHelper.ungrabMouseCursor();
}
}
}
public static void enterMouseModeHook() {
if(touchMode) {
touchMode = false;
touchingScreen = false;
touchingScreenNotButton = false;
if(mc.inGameHasFocus && mc.mouseGrabSupported) {
mc.mouseHelper.grabMouseCursor();
}
}
}
public static int getVCursorX() {
return cursorX;
}
public static int getVCursorY() {
return cursorY;
}
public static int getVCursorDX() {
int tmp = cursorDX;
cursorDX = 0;
return tmp;
}
public static int getVCursorDY() {
int tmp = cursorDY;
cursorDY = 0;
return tmp;
}
public static boolean getVCursorButtonDown(int bt) {
return (touchingScreenNotButton && bt == 0) || Mouse.isButtonDown(bt);
}
}

View File

@ -0,0 +1,134 @@
package net.lax1dude.eaglercraft.v1_8;
import net.lax1dude.eaglercraft.v1_8.internal.EnumTouchEvent;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput;
/**
* 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 Touch {
public static boolean next() {
return PlatformInput.touchNext();
}
public static EnumTouchEvent getEventType() {
return PlatformInput.touchGetEventType();
}
public static int getEventTouchPointCount() {
return PlatformInput.touchGetEventTouchPointCount();
}
public static int getEventTouchX(int pointId) {
return PlatformInput.touchGetEventTouchX(pointId);
}
public static int getEventTouchY(int pointId) {
return PlatformInput.touchGetEventTouchY(pointId);
}
public static float getEventTouchRadiusX(int pointId) {
return PlatformInput.touchGetEventTouchRadiusX(pointId);
}
public static float getEventTouchRadiusY(int pointId) {
return PlatformInput.touchGetEventTouchRadiusY(pointId);
}
public static float getEventTouchRadiusMixed(int pointId) {
return PlatformInput.touchGetEventTouchRadiusMixed(pointId);
}
public static float getEventTouchForce(int pointId) {
return PlatformInput.touchGetEventTouchForce(pointId);
}
public static int getEventTouchPointUID(int pointId) {
return PlatformInput.touchGetEventTouchPointUID(pointId);
}
public static int touchPointCount() {
return PlatformInput.touchPointCount();
}
public static int touchPointX(int pointId) {
return PlatformInput.touchPointX(pointId);
}
public static int touchPointY(int pointId) {
return PlatformInput.touchPointY(pointId);
}
public static float touchPointRadiusX(int pointId) {
return PlatformInput.touchRadiusX(pointId);
}
public static float touchPointRadiusY(int pointId) {
return PlatformInput.touchRadiusY(pointId);
}
public static float touchPointRadiusMixed(int pointId) {
return PlatformInput.touchRadiusMixed(pointId);
}
public static float touchPointForce(int pointId) {
return PlatformInput.touchForce(pointId);
}
public static int touchPointUID(int pointId) {
return PlatformInput.touchPointUID(pointId);
}
public static void touchSetOpenKeyboardZone(int x, int y, int w, int h) {
PlatformInput.touchSetOpenKeyboardZone(x, y, w, h);
}
public static void closeDeviceKeyboard() {
PlatformInput.touchCloseDeviceKeyboard();
}
public static boolean isDeviceKeyboardOpenMAYBE() {
return PlatformInput.touchIsDeviceKeyboardOpenMAYBE();
}
public static String getPastedString() {
return PlatformInput.touchGetPastedString();
}
public static boolean checkPointTouching(int uid) {
int cnt = PlatformInput.touchPointCount();
if(cnt > 0) {
for(int i = 0; i < cnt; ++i) {
if(PlatformInput.touchPointUID(i) == uid) {
return true;
}
}
}
return false;
}
public static int fetchPointIdx(int uid) {
int cnt = PlatformInput.touchPointCount();
if(cnt > 0) {
for(int i = 0; i < cnt; ++i) {
if(PlatformInput.touchPointUID(i) == uid) {
return i;
}
}
}
return -1;
}
}

View File

@ -0,0 +1,54 @@
package net.lax1dude.eaglercraft.v1_8.boot_menu;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
/**
* 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 GuiScreenEnterBootMenu extends GuiScreen {
private final GuiScreen parent;
public GuiScreenEnterBootMenu(GuiScreen parent) {
this.parent = parent;
}
public void initGui() {
EagRuntime.setDisplayBootMenuNextRefresh(true);
this.buttonList.clear();
this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 96, I18n.format("gui.cancel")));
}
public void onGuiClosed() {
EagRuntime.setDisplayBootMenuNextRefresh(false);
}
public void drawScreen(int par1, int par2, float par3) {
this.drawDefaultBackground();
this.drawCenteredString(fontRendererObj, I18n.format("enterBootMenu.title"), this.width / 2, 70, 11184810);
this.drawCenteredString(fontRendererObj, I18n.format("enterBootMenu.text0"), this.width / 2, 90, 16777215);
super.drawScreen(par1, par2, par3);
}
protected void actionPerformed(GuiButton par1GuiButton) {
if(par1GuiButton.id == 0) {
this.mc.displayGuiScreen(parent);
}
}
}

View File

@ -25,7 +25,7 @@ public class EaglerLoadingCache<K, V> {
public EaglerLoadingCache(EaglerCacheProvider<K, V> provider) {
this.provider = provider;
this.cacheMap = new HashMap();
this.cacheMap = new HashMap<>();
}
public V get(K key) {

View File

@ -0,0 +1,84 @@
package net.lax1dude.eaglercraft.v1_8.cookie;
import java.text.SimpleDateFormat;
import java.util.Date;
import net.lax1dude.eaglercraft.v1_8.cookie.ServerCookieDataStore.ServerCookie;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
/**
* 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 GuiScreenInspectSessionToken extends GuiScreen {
private final GuiScreen parent;
private final ServerCookie cookie;
public GuiScreenInspectSessionToken(GuiScreenRevokeSessionToken parent, ServerCookie cookie) {
this.parent = parent;
this.cookie = cookie;
}
public void initGui() {
this.buttonList.clear();
this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 106, I18n.format("gui.done")));
}
public void drawScreen(int par1, int par2, float par3) {
this.drawDefaultBackground();
String[][] toDraw = new String[][] {
{
I18n.format("inspectSessionToken.details.server"),
I18n.format("inspectSessionToken.details.expires"),
I18n.format("inspectSessionToken.details.length")
},
{
cookie.server.length() > 32 ? cookie.server.substring(0, 30) + "..." : cookie.server,
(new SimpleDateFormat("M/d/yyyy h:mm aa")).format(new Date(cookie.expires)),
Integer.toString(cookie.cookie.length)
}
};
int[] maxWidth = new int[2];
for(int i = 0; i < 2; ++i) {
String[] strs = toDraw[i];
int w = 0;
for(int j = 0; j < strs.length; ++j) {
int k = fontRendererObj.getStringWidth(strs[j]);
if(k > w) {
w = k;
}
}
maxWidth[i] = w + 10;
}
int totalWidth = maxWidth[0] + maxWidth[1];
this.drawCenteredString(fontRendererObj, I18n.format("inspectSessionToken.title"), this.width / 2, 70, 16777215);
this.drawString(fontRendererObj, toDraw[0][0], (this.width - totalWidth) / 2, 90, 11184810);
this.drawString(fontRendererObj, toDraw[0][1], (this.width - totalWidth) / 2, 104, 11184810);
this.drawString(fontRendererObj, toDraw[0][2], (this.width - totalWidth) / 2, 118, 11184810);
this.drawString(fontRendererObj, toDraw[1][0], (this.width - totalWidth) / 2 + maxWidth[0], 90, 11184810);
this.drawString(fontRendererObj, toDraw[1][1], (this.width - totalWidth) / 2 + maxWidth[0], 104, 11184810);
this.drawString(fontRendererObj, toDraw[1][2], (this.width - totalWidth) / 2 + maxWidth[0], 118, 11184810);
super.drawScreen(par1, par2, par3);
}
protected void actionPerformed(GuiButton par1GuiButton) {
if(par1GuiButton.id == 0) {
this.mc.displayGuiScreen(parent);
}
}
}

View File

@ -0,0 +1,146 @@
package net.lax1dude.eaglercraft.v1_8.cookie;
import java.io.IOException;
import java.util.Collections;
import com.google.common.collect.Lists;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiSlot;
import net.minecraft.client.resources.I18n;
/**
* 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 GuiScreenRevokeSessionToken extends GuiScreen {
protected GuiScreen parentScreen;
private GuiScreenRevokeSessionToken.List list;
private GuiButton inspectButton;
private GuiButton revokeButton;
public GuiScreenRevokeSessionToken(GuiScreen parent) {
this.parentScreen = parent;
}
public void initGui() {
this.buttonList.clear();
this.buttonList.add(this.inspectButton = new GuiButton(10, this.width / 2 - 154, this.height - 38, 100, 20, I18n.format("revokeSessionToken.inspect")));
this.buttonList.add(this.revokeButton = new GuiButton(9, this.width / 2 - 50, this.height - 38, 100, 20, I18n.format("revokeSessionToken.revoke")));
this.buttonList.add(new GuiButton(6, this.width / 2 + 54, this.height - 38, 100, 20, I18n.format("gui.done")));
this.list = new GuiScreenRevokeSessionToken.List(this.mc);
this.list.registerScrollButtons(7, 8);
updateButtons();
}
public void handleMouseInput() throws IOException {
super.handleMouseInput();
this.list.handleMouseInput();
}
public void handleTouchInput() throws IOException {
super.handleTouchInput();
this.list.handleTouchInput();
}
protected void actionPerformed(GuiButton parGuiButton) {
if (parGuiButton.enabled) {
switch (parGuiButton.id) {
case 6:
this.mc.displayGuiScreen(this.parentScreen);
break;
case 9:
String s1 = list.getSelectedItem();
if(s1 != null) {
ServerCookieDataStore.ServerCookie cookie = ServerCookieDataStore.loadCookie(s1);
if(cookie != null) {
this.mc.displayGuiScreen(new GuiScreenSendRevokeRequest(this, cookie));
}else {
this.initGui();
}
}
break;
case 10:
String s2 = list.getSelectedItem();
if(s2 != null) {
ServerCookieDataStore.ServerCookie cookie = ServerCookieDataStore.loadCookie(s2);
if(cookie != null) {
this.mc.displayGuiScreen(new GuiScreenInspectSessionToken(this, cookie));
}else {
this.initGui();
}
}
break;
default:
this.list.actionPerformed(parGuiButton);
}
}
}
protected void updateButtons() {
inspectButton.enabled = revokeButton.enabled = list.getSelectedItem() != null;
}
public void drawScreen(int i, int j, float f) {
this.list.drawScreen(i, j, f);
this.drawCenteredString(this.fontRendererObj, I18n.format("revokeSessionToken.title"), this.width / 2, 16, 16777215);
this.drawCenteredString(this.fontRendererObj, I18n.format("revokeSessionToken.note.0"), this.width / 2, this.height - 66, 8421504);
this.drawCenteredString(this.fontRendererObj, I18n.format("revokeSessionToken.note.1"), this.width / 2, this.height - 56, 8421504);
super.drawScreen(i, j, f);
}
class List extends GuiSlot {
private final java.util.List<String> cookieNames = Lists.newArrayList();
public List(Minecraft mcIn) {
super(mcIn, GuiScreenRevokeSessionToken.this.width, GuiScreenRevokeSessionToken.this.height, 32, GuiScreenRevokeSessionToken.this.height - 75 + 4, 18);
ServerCookieDataStore.flush();
cookieNames.addAll(ServerCookieDataStore.getRevokableServers());
Collections.sort(cookieNames);
}
protected int getSize() {
return this.cookieNames.size();
}
protected void elementClicked(int i, boolean var2, int var3, int var4) {
selectedElement = i;
GuiScreenRevokeSessionToken.this.updateButtons();
}
protected boolean isSelected(int i) {
return selectedElement == i;
}
protected String getSelectedItem() {
return selectedElement == -1 ? null : cookieNames.get(selectedElement);
}
protected int getContentHeight() {
return this.getSize() * 18;
}
protected void drawBackground() {
GuiScreenRevokeSessionToken.this.drawDefaultBackground();
}
protected void drawSlot(int i, int var2, int j, int var4, int var5, int var6) {
GuiScreenRevokeSessionToken.this.drawCenteredString(GuiScreenRevokeSessionToken.this.fontRendererObj,
this.cookieNames.get(i), this.width / 2, j + 1, 16777215);
}
}
}

View File

@ -0,0 +1,177 @@
package net.lax1dude.eaglercraft.v1_8.cookie;
import org.json.JSONObject;
import net.lax1dude.eaglercraft.v1_8.cookie.ServerCookieDataStore.ServerCookie;
import net.lax1dude.eaglercraft.v1_8.internal.IServerQuery;
import net.lax1dude.eaglercraft.v1_8.internal.QueryResponse;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.minecraft.GuiScreenGenericErrorMessage;
import net.lax1dude.eaglercraft.v1_8.socket.ServerQueryDispatch;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
/**
* 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 GuiScreenSendRevokeRequest extends GuiScreen {
private static final Logger logger = LogManager.getLogger("SessionRevokeRequest");
private GuiScreen parent;
private ServerCookie cookie;
private String title;
private String message;
private int timer = 0;
private boolean cancelRequested = false;
private IServerQuery query = null;
private boolean hasSentPacket = false;
public GuiScreenSendRevokeRequest(GuiScreen parent, ServerCookie cookie) {
this.parent = parent;
this.cookie = cookie;
this.title = I18n.format("revokeSendingScreen.title");
this.message = I18n.format("revokeSendingScreen.message.opening", cookie.server);
}
public void initGui() {
this.buttonList.clear();
this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 96, I18n.format("gui.cancel")));
}
public void drawScreen(int par1, int par2, float par3) {
this.drawDefaultBackground();
this.drawCenteredString(fontRendererObj, title, this.width / 2, 70, 11184810);
this.drawCenteredString(fontRendererObj, message, this.width / 2, 90, 16777215);
super.drawScreen(par1, par2, par3);
}
public void updateScreen() {
++timer;
if (timer > 1) {
if(query == null) {
logger.info("Attempting to revoke session tokens for: {}", cookie.server);
query = ServerQueryDispatch.sendServerQuery(cookie.server, "revoke_session_token");
if(query == null) {
this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", "revokeFailure.desc.connectionError", parent));
return;
}
}else {
query.update();
QueryResponse resp = query.getResponse();
if(resp != null) {
if(resp.responseType.equalsIgnoreCase("revoke_session_token") && (hasSentPacket ? resp.isResponseJSON() : resp.isResponseString())) {
if(!hasSentPacket) {
String str = resp.getResponseString();
if("ready".equalsIgnoreCase(str)) {
hasSentPacket = true;
message = I18n.format("revokeSendingScreen.message.sending");
query.send(cookie.cookie);
return;
}else {
this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", "revokeFailure.desc.clientError", parent));
return;
}
}else {
JSONObject json = resp.getResponseJSON();
String stat = json.optString("status");
if("ok".equalsIgnoreCase(stat)) {
if(hasSentPacket) {
query.close();
this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeSuccess.title", "revokeSuccess.desc", parent));
ServerCookieDataStore.clearCookie(cookie.server);
return;
}else {
query.close();
this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", "revokeFailure.desc.clientError", parent));
return;
}
}else if("error".equalsIgnoreCase(stat)) {
int code = json.optInt("code", -1);
if(code == -1) {
query.close();
this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", "revokeFailure.desc.clientError", parent));
return;
}else {
String key;
switch(code) {
case 1:
key = "revokeFailure.desc.notSupported";
break;
case 2:
key = "revokeFailure.desc.notAllowed";
break;
case 3:
key = "revokeFailure.desc.notFound";
break;
case 4:
key = "revokeFailure.desc.serverError";
break;
default:
key = "revokeFailure.desc.genericCode";
break;
}
logger.error("Recieved error code {}! ({})", code, key);
query.close();
this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", key, parent));
if(json.optBoolean("delete", false)) {
ServerCookieDataStore.clearCookie(cookie.server);
}
return;
}
}else {
logger.error("Recieved unknown status \"{}\"!", stat);
query.close();
this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", "revokeFailure.desc.clientError", parent));
return;
}
}
}else {
query.close();
this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", "revokeFailure.desc.clientError", parent));
return;
}
}
if(query.isClosed()) {
if(!hasSentPacket || query.responsesAvailable() == 0) {
this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", "revokeFailure.desc.connectionError", parent));
return;
}
}else {
if(timer > 400) {
query.close();
this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", "revokeFailure.desc.connectionError", parent));
return;
}
}
if(cancelRequested) {
query.close();
this.mc.displayGuiScreen(new GuiScreenGenericErrorMessage("revokeFailure.title", "revokeFailure.desc.cancelled", parent));
return;
}
}
}
}
protected void actionPerformed(GuiButton par1GuiButton) {
if(par1GuiButton.id == 0) {
cancelRequested = true;
par1GuiButton.enabled = false;
}
}
}

View File

@ -0,0 +1,294 @@
package net.lax1dude.eaglercraft.v1_8.cookie;
import net.lax1dude.eaglercraft.v1_8.internal.IFramebufferGL;
import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL;
import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformAssets;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.opengl.DrawUtils;
import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU;
import net.lax1dude.eaglercraft.v1_8.opengl.GLSLHeader;
import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
import net.lax1dude.eaglercraft.v1_8.opengl.ImageData;
import net.lax1dude.eaglercraft.v1_8.opengl.VSHInputLayoutParser;
import net.minecraft.client.renderer.texture.TextureUtil;
import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*;
import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*;
import static net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.ExtGLEnums.*;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import com.google.common.collect.Lists;
import net.lax1dude.eaglercraft.v1_8.Base64;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom;
import net.lax1dude.eaglercraft.v1_8.crypto.GeneralDigest;
import net.lax1dude.eaglercraft.v1_8.crypto.SHA256Digest;
/**
* 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 HardwareFingerprint {
// This is used for generating encryption keys for storing cookies,
// its supposed to make session hijacking more difficult for skids
private static final String fingerprintIMG = "/9j/4AAQSkZJRgABAQEBLAEsAAD//gAKZnVjayBvZmb/4gKwSUNDX1BST0ZJTEUAAQEAAAKgbGNtcwRAAABtbnRyUkdCIFhZWiAH6AAGABAAAgArACNhY3NwTVNGVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWxjbXMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1kZXNjAAABIAAAAEBjcHJ0AAABYAAAADZ3dHB0AAABmAAAABRjaGFkAAABrAAAACxyWFlaAAAB2AAAABRiWFlaAAAB7AAAABRnWFlaAAACAAAAABRyVFJDAAACFAAAACBnVFJDAAACFAAAACBiVFJDAAACFAAAACBjaHJtAAACNAAAACRkbW5kAAACWAAAACRkbWRkAAACfAAAACRtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACQAAAAcAEcASQBNAFAAIABiAHUAaQBsAHQALQBpAG4AIABzAFIARwBCbWx1YwAAAAAAAAABAAAADGVuVVMAAAAaAAAAHABQAHUAYgBsAGkAYwAgAEQAbwBtAGEAaQBuAABYWVogAAAAAAAA9tYAAQAAAADTLXNmMzIAAAAAAAEMQgAABd7///MlAAAHkwAA/ZD///uh///9ogAAA9wAAMBuWFlaIAAAAAAAAG+gAAA49QAAA5BYWVogAAAAAAAAJJ8AAA+EAAC2xFhZWiAAAAAAAABilwAAt4cAABjZcGFyYQAAAAAAAwAAAAJmZgAA8qcAAA1ZAAAT0AAACltjaHJtAAAAAAADAAAAAKPXAABUfAAATM0AAJmaAAAmZwAAD1xtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAEcASQBNAFBtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEL/2wBDAAoHBwkHBgoJCAkLCwoMDxkQDw4ODx4WFxIZJCAmJSMgIyIoLTkwKCo2KyIjMkQyNjs9QEBAJjBGS0U+Sjk/QD3/2wBDAQsLCw8NDx0QEB09KSMpPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT3/wgARCACAAIADAREAAhEBAxEB/8QAGwAAAgMBAQEAAAAAAAAAAAAAAQIABAUDBgf/xAAYAQEBAQEBAAAAAAAAAAAAAAAAAQIDBP/aAAwDAQACEAMQAAABx95IwaIRghDRCFCElZ+KQhpghGDRCFCQNEzsUjUQjBDRChCENQJn4pDXfNuSvBBUJYK52Q5ahIZ+KSxLtc9jAcbO8OpA0YENodZpdJWszsUm1z3alHnuBZurO+SA5+PVPTQ7zv0zd1MLpnGzSem49OQIPMeslCpA82ueVj05fcuWdtPCBPW8elGzlRIMFCElOPZoJe0+dx2PR8+mdYoSAFQgFHq3ZsWWa+fxZ01+O6lKgqiWhxRhBQpv6mlXgdS1Loct8kAtcjiWBRxQANHWd+vEw5M67HQghXGO4AEFLms+hrxFhrW49KliDhGAABAALes79eI1CbnHpVshzAQhCEIAmpa3nJCbPHpwsgBRBQBGICrlzd1PN1DW5bSBSgAcxElOpQLo3OtX/8QAJxAAAQQBBAEEAgMAAAAAAAAAAQACAxEQBBITICEUIjAxMkEjNFD/2gAIAQEAAQUC/wBNkZevThcDVwtXC1cAXAuAoxOCMbh2iZyPqsX8HG16lhMXTSMqHAZP6jpI/Y2DVcxCCYA9s8JgfiBtaUq+zm7hHAyLATFqo+TTIeSRUbirVq1uW5bluW4K0FCE/wAilCLmkcirV4tWrVq0HprlD9Y039iQ9JXmMtO5qLqV+MbqWmfuZ+gC4we3UPPnL27mxGkTQaOhWjdhp2Na7cre0h99HsTQT20n5JxtzPzkHksX0rV/BpT7gQcyYpEZvtHbjE8x5PlmT8ELP4ItOGuw3zD3vo32r9f/xAAeEQACAQUAAwAAAAAAAAAAAAAAEQEQIDAxUAIhQP/aAAgBAwEBPwHpoQhCEIQr4qsvjSD2TbEk3Rqr5kb+CN59D7s2RrN//8QAIhEAAQMEAgIDAAAAAAAAAAAAAQACEhARIDEDMBNAIUFQ/9oACAECAQE/Af0ybKSkVIqRU1NSUgrjIm1HuiF5SmulgSAg4UBQN8H050XMiuHA3c5P44fK4zcVBvU7o5odteFqAtg5hBuEZu2mNtUejdfXeAna9B2u/ZRHeEfe33jA77BT/8QAJxAAAQIFAwMFAQAAAAAAAAAAAQARAhAhMDEgQVESImEDUGJxgZH/2gAIAQEABj8C9zosme6yVlUMsamVLVQnzDzoMXNh0zVn0xYKY42M4R+2GK7RoPIrMC6RKD7uvOHTRPZYVUL6umTnW+5TbrurpcZVbHhQ/d6hvngByuuEvyJwnxcPzib8CJNZw3PRg4gf+y//xAAkEAEAAgIBBAIDAQEAAAAAAAABABEQITFBUWFxIIGR8PFQsf/aAAgBAQABPyH/AE39Nd4GbulPd+54X8z9jhK/zHpMWqj8wa1/Xyrenl9QAoUGKcXgwMuFQdx/6oldyOPg7hqsdI6zYXz0qBrFwl/2wF1CXvFKdw5bLfcMh3g29ywwt8SBWWRJaV6wnKc4WhppgIBy6nZwoJ054y0vnrKnqeSB7xXNhc9kKwrXjOMOkdOJK/AKwo5htQdfcXWNvbK1juVGBXYldhV7sLWYuBPTEDZLisKuxC0CfyWOOGAgysrkwFkGMqaGo2zzBjudcb4i71HwzXfZBgRiblS/toGM8GGMeJo64uE47XQR03hBILpNyObZvHXHSAXdENsE8YJu33jKM7M4l4Dg64eIABpTeIibDl65XlB8BcSoy5cOMM3bz+49wcAJq8v6VfJNx1OEvC6uauwL3tBj/9oADAMBAAIAAwAAABD9mRbSTt1t0bIAF+n8iJQX9mluqdyUVE09DAPykEK/iOdbRe8nvNsaLtpZ+CB9RFxmQADt+ChzpmW03P7QNkikzbp/AkyUoZrmRKm2JYrYU5LbJaLJU0pbLQ1Z+klOwjL/xAAeEQEAAgIDAQEBAAAAAAAAAAABABEQICEwMUFAUP/aAAgBAwEBPxD+mJlZWUlZXPaI7C3AXglaVKwhErQcXHALa/CWQVGMSsioYcS2oiQo8i3HDip81qVq5qHpHuWsT1unGHsceIW4ZyS+twtw9jK63R7GeZ+fsDj/xAAfEQEAAgIDAAMBAAAAAAAAAAABABEQISAwMUFQYXH/2gAIAQIBAT8Q+zGL8IrT+IHANyqXP2DYwu8hizh6kW6cKQOBbrC2QNYWr+Mvk1UTQxTWEGpQy7UGAVGyAKOPcKEUahh04uevG+gwW1DlXIxbNHDrIYsJs4dhipUlkoZXWYFE8MJeK6TgdhtqX4cul7PdxbyaXZ4x/8QAJRABAAICAgEEAwADAAAAAAAAAQARITFBUWEQcZGhgbHhwdHw/9oACAEBAAE/EBhCDCEGEGXBhCD6DCEIMIQYQYMGDBhB9CEGDCEGEIMIMNQYegwYTVAtL0TKZ4UEAFjdsNePmS1wj2jHgfiL6Z72lLL9xIJSLqhLcAc5fqG4MuDBiZquXoQIDCA4lXuZ8F9S/iZGJdSw8QggXzHMFjupfG/2OA2aA56fMIMJcZp3wf1+oMd63WI/LNqeSMC9yq1vqaTBxMWXS4ykS/CRRTcE4ekf3GNn/BHMGDmcKrS7yiomtzOywRtvMuubjKtmDTGl52MIthPKAjqXYTiPbtv22fEGZmkB7sxbQUuaKqADe03UvVR52BbYQGDFJOU325g2S35jtRa8wEObK4akp9T3RcfVj+G4QKyH0TwImLaIPZEBLwSagO5YbMQnnXmOKy4WW2ntLn+4sOXwn6ZgTlgVmYxYTuWbLuCByahNMWaIhBT1Oj5i5xfvASx7Z+p0AGTmFsIASbaLfMOXbcJXKHuVcWYsxgBXiFgqY8kuWM0suh49AhbwQiUp8wirdaTATjBHIf1CRqVeuf8AD5Jhihwm2wzQsuHUULmLm10cxwUDiWBGZUdzpxD2g2zyRYDxCZNGOo4gMaE+4pKuLVFMBpzoBddwA5izZ+osHLwRzzFqEIllOf8AcpK2Mrr0VI9MVANBlvzEGSUt6QOUF36AT00Udeg/S3jQx9qH5isgUZobxwlkCVPJi+o4bdR+pcCEhdwgdRYnprC1givIW1+R8So0Sq6vcCVL/wAi+DUo5ihYkuLWiOSkjcMSyxC0AlodEEfALlhHUubF/STqcT//2Q==";
private static final String shaderPrecision = "precision lowp int;\nprecision mediump float;\nprecision mediump sampler2D;\n";
private static byte[] fingerprint = null;
public static final Logger logger = LogManager.getLogger("HardwareFingerprint");
public static byte[] getFingerprint() {
if(fingerprint == null) {
try {
fingerprint = generateFingerprint();
}catch(Throwable t) {
fingerprint = new byte[0];
}
if(fingerprint.length == 0) {
logger.error("Failed to calculate hardware fingerprint, server cookies will not be encrypted!");
}
}
return fingerprint;
}
private static byte[] generateFingerprint() {
ImageData img = PlatformAssets.loadImageFile(Base64.decodeBase64(fingerprintIMG), "image/jpeg");
if(img == null) {
logger.error("Input image data is corrupt!");
return new byte[0];
}
int[][] mipmapLevels = TextureUtil.generateMipmapData(7, 128,
new int[][] { img.pixels, null, null, null, null, null, null, null });
int helperTexture = GlStateManager.generateTexture();
GlStateManager.bindTexture(helperTexture);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
TextureUtil.allocateTextureImpl(helperTexture, 7, 128, 128);
TextureUtil.uploadTextureMipmap(mipmapLevels, 128, 128, 0, 0, false, false);
if(checkAnisotropicFilteringSupport()) {
_wglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, 16.0f);
}
IShaderGL vert;
List<VSHInputLayoutParser.ShaderInput> vertLayout;
if(DrawUtils.vshLocal != null) {
vert = DrawUtils.vshLocal;
vertLayout = DrawUtils.vshLocalLayout;
}else {
String vshLocalSrc = EagRuntime.getRequiredResourceString("/assets/eagler/glsl/local.vsh");
vertLayout = VSHInputLayoutParser.getShaderInputs(vshLocalSrc);
vert = _wglCreateShader(GL_VERTEX_SHADER);
_wglShaderSource(vert, GLSLHeader.getVertexHeaderCompat(vshLocalSrc, DrawUtils.vertexShaderPrecision));
_wglCompileShader(vert);
if(_wglGetShaderi(vert, GL_COMPILE_STATUS) != GL_TRUE) {
_wglDeleteShader(vert);
GlStateManager.deleteTexture(helperTexture);
return new byte[0];
}
}
IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER);
_wglShaderSource(frag, GLSLHeader.getFragmentHeaderCompat(EagRuntime.getRequiredResourceString("/assets/eagler/glsl/hw_fingerprint.fsh"), shaderPrecision));
_wglCompileShader(frag);
if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) {
_wglDeleteShader(vert);
_wglDeleteShader(frag);
GlStateManager.deleteTexture(helperTexture);
return new byte[0];
}
IProgramGL program = _wglCreateProgram();
_wglAttachShader(program, vert);
_wglAttachShader(program, frag);
if(EaglercraftGPU.checkOpenGLESVersion() == 200) {
VSHInputLayoutParser.applyLayout(program, vertLayout);
}
_wglLinkProgram(program);
_wglDetachShader(program, vert);
_wglDetachShader(program, frag);
if(DrawUtils.vshLocal == null) {
_wglDeleteShader(vert);
}
_wglDeleteShader(frag);
if(_wglGetProgrami(program, GL_LINK_STATUS) != GL_TRUE) {
_wglDeleteProgram(program);
GlStateManager.deleteTexture(helperTexture);
return new byte[0];
}
EaglercraftGPU.bindGLShaderProgram(program);
_wglUniform1i(_wglGetUniformLocation(program, "u_inputTexture"), 0);
float fovy = 90.0f;
float aspect = 1.0f;
float zNear = 0.01f;
float zFar = 100.0f;
FloatBuffer matrixUploadBuffer = EagRuntime.allocateFloatBuffer(16);
float toRad = 0.0174532925f;
float cotangent = (float) Math.cos(fovy * toRad * 0.5f) / (float) Math.sin(fovy * toRad * 0.5f);
matrixUploadBuffer.put(cotangent / aspect);
matrixUploadBuffer.put(0.0f);
matrixUploadBuffer.put(0.0f);
matrixUploadBuffer.put(0.0f);
matrixUploadBuffer.put(0.0f);
matrixUploadBuffer.put(cotangent);
matrixUploadBuffer.put(0.0f);
matrixUploadBuffer.put(0.0f);
matrixUploadBuffer.put(0.0f);
matrixUploadBuffer.put(0.0f);
matrixUploadBuffer.put((zFar + zNear) / (zFar - zNear));
matrixUploadBuffer.put(2.0f * zFar * zNear / (zFar - zNear));
matrixUploadBuffer.put(0.0f);
matrixUploadBuffer.put(0.0f);
matrixUploadBuffer.put(-1.0f);
matrixUploadBuffer.put(0.0f);
matrixUploadBuffer.flip();
_wglUniformMatrix4fv(_wglGetUniformLocation(program, "u_textureMatrix"), false, matrixUploadBuffer);
EagRuntime.freeFloatBuffer(matrixUploadBuffer);
int[] oldViewport = new int[4];
EaglercraftGPU.glGetInteger(GL_VIEWPORT, oldViewport);
IFramebufferGL framebuffer = _wglCreateFramebuffer();
int renderTexture = GlStateManager.generateTexture();
GlStateManager.bindTexture(renderTexture);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
int dataLength;
int type;
if(EaglercraftGPU.checkHDRFramebufferSupport(32)) {
dataLength = 256 * 256 * 4 * 4;
type = GL_FLOAT;
EaglercraftGPU.createFramebufferHDR32FTexture(GL_TEXTURE_2D, 0, 256, 256, GL_RGBA, false);
}else if(EaglercraftGPU.checkHDRFramebufferSupport(16)) {
dataLength = 256 * 256 * 4 * 2;
type = _GL_HALF_FLOAT;
EaglercraftGPU.createFramebufferHDR16FTexture(GL_TEXTURE_2D, 0, 256, 256, GL_RGBA, false);
}else {
dataLength = 256 * 256 * 4;
type = GL_UNSIGNED_BYTE;
EaglercraftGPU.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null);
}
_wglBindFramebuffer(_GL_FRAMEBUFFER, framebuffer);
_wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(renderTexture), 0);
_wglDrawBuffers(_GL_COLOR_ATTACHMENT0);
GlStateManager.viewport(0, 0, 256, 256);
GlStateManager.disableBlend();
GlStateManager.bindTexture(helperTexture);
DrawUtils.drawStandardQuad2D();
_wglDeleteProgram(program);
GlStateManager.deleteTexture(helperTexture);
ByteBuffer readBuffer = EagRuntime.allocateByteBuffer(dataLength);
EaglercraftGPU.glReadPixels(0, 0, 256, 256, GL_RGBA, type, readBuffer);
_wglBindFramebuffer(_GL_FRAMEBUFFER, null);
_wglDeleteFramebuffer(framebuffer);
GlStateManager.deleteTexture(renderTexture);
GlStateManager.viewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
SHA256Digest digest = new SHA256Digest();
byte[] copyBuffer = new byte[1024];
byte[] b = ("eag" + EaglercraftGPU.glGetString(GL_VENDOR) + "; eag " + EaglercraftGPU.glGetString(GL_RENDERER)).getBytes(StandardCharsets.UTF_8);
digest.update(b, 0, b.length);
digestInts(digest, _wglGetInteger(0x8869), _wglGetInteger(0x8DFB), _wglGetInteger(0x8B4C), _wglGetInteger(0x8DFC), copyBuffer);
digestInts(digest, _wglGetInteger(0x8DFD), _wglGetInteger(0x8872), _wglGetInteger(0x84E8), 69, copyBuffer);
digestInts(digest, _wglGetInteger(0x0D33), _wglGetInteger(0x851C), _wglGetInteger(0x8B4D), 69, copyBuffer);
if(EaglercraftGPU.checkOpenGLESVersion() >= 300) {
digestInts(digest, _wglGetInteger(0x8B4A), _wglGetInteger(0x8A2B), _wglGetInteger(0x9122), _wglGetInteger(0x8B4B), copyBuffer);
digestInts(digest, _wglGetInteger(0x8C8A), _wglGetInteger(0x8C8B), _wglGetInteger(0x8C80), _wglGetInteger(0x8B49), copyBuffer);
digestInts(digest, _wglGetInteger(0x8A2D), _wglGetInteger(0x9125), _wglGetInteger(0x8904), _wglGetInteger(0x8905), copyBuffer);
digestInts(digest, _wglGetInteger(0x8824), _wglGetInteger(0x8073), _wglGetInteger(0x88FF), _wglGetInteger(0x84FD), copyBuffer);
digestInts(digest, _wglGetInteger(0x8CDF), _wglGetInteger(0x8A2F), _wglGetInteger(0x8A30), _wglGetInteger(0x8A34), copyBuffer);
digestInts(digest, _wglGetInteger(0x8A2E), _wglGetInteger(0x8A31), _wglGetInteger(0x8A33), _wglGetInteger(0x8D57), copyBuffer);
}
try {
List<String> exts = Lists.newArrayList(getAllExtensions());
Collections.sort(exts);
EaglercraftRandom rand = new EaglercraftRandom(6942069420l + exts.size() * 69l + b.length);
for (int i = exts.size() - 1; i > 0; --i) {
int j = rand.nextInt(i + 1);
Collections.swap(exts, i, j);
}
b = String.join(":>", exts).getBytes(StandardCharsets.UTF_8);
digest.update(b, 0, b.length);
}catch(Throwable t) {
}
int i;
while(readBuffer.hasRemaining()) {
i = Math.min(readBuffer.remaining(), copyBuffer.length);
readBuffer.get(copyBuffer, 0, i);
digest.update(copyBuffer, 0, i);
}
EagRuntime.freeByteBuffer(readBuffer);
byte[] hashOut = new byte[32];
digest.doFinal(hashOut, 0);
return hashOut;
}
private static void digestInts(GeneralDigest digest, int i1, int i2, int i3, int i4, byte[] tmpBuffer) {
tmpBuffer[0] = (byte)(i1 >>> 24);
tmpBuffer[1] = (byte)(i1 >>> 16);
tmpBuffer[2] = (byte)(i1 >>> 8);
tmpBuffer[3] = (byte)(i1 & 0xFF);
tmpBuffer[4] = (byte)(i2 >>> 24);
tmpBuffer[5] = (byte)(i2 >>> 16);
tmpBuffer[6] = (byte)(i2 >>> 8);
tmpBuffer[7] = (byte)(i2 & 0xFF);
tmpBuffer[8] = (byte)(i3 >>> 24);
tmpBuffer[9] = (byte)(i3 >>> 16);
tmpBuffer[10] = (byte)(i3 >>> 8);
tmpBuffer[11] = (byte)(i3 & 0xFF);
tmpBuffer[12] = (byte)(i4 >>> 24);
tmpBuffer[13] = (byte)(i4 >>> 16);
tmpBuffer[14] = (byte)(i4 >>> 8);
tmpBuffer[15] = (byte)(i4 & 0xFF);
digest.update(tmpBuffer, 0, 16);
}
}

View File

@ -0,0 +1,396 @@
package net.lax1dude.eaglercraft.v1_8.cookie;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EaglerInputStream;
import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream;
import net.lax1dude.eaglercraft.v1_8.EaglerZLIB;
import net.lax1dude.eaglercraft.v1_8.crypto.AESLightEngine;
import net.lax1dude.eaglercraft.v1_8.crypto.SHA1Digest;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication;
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 ServerCookieDataStore {
private static final Logger logger = LogManager.getLogger("ServerCookieDataStore");
private static final Map<String,ServerCookie> dataStore = new HashMap<>();
public static final String localStorageKey = "c";
public static class ServerCookie {
public final String server;
public final byte[] cookie;
public final long expires;
public final boolean revokeQuerySupported;
public final boolean saveCookieToDisk;
public ServerCookie(String server, byte[] cookie, long expires, boolean revokeQuerySupported, boolean saveCookieToDisk) {
this.server = server;
this.cookie = cookie;
this.expires = expires;
this.revokeQuerySupported = revokeQuerySupported;
this.saveCookieToDisk = saveCookieToDisk;
}
}
public static void load() {
if(EagRuntime.getConfiguration().isEnableServerCookies()) {
loadData(HardwareFingerprint.getFingerprint());
}
}
public static ServerCookie loadCookie(String server) {
if(!EagRuntime.getConfiguration().isEnableServerCookies()) {
return null;
}
server = normalize(server);
ServerCookie cookie = dataStore.get(server);
if(cookie == null) {
return null;
}
long timestamp = System.currentTimeMillis();
if(timestamp > cookie.expires) {
dataStore.remove(server);
saveData(HardwareFingerprint.getFingerprint());
return null;
}
return cookie;
}
public static void saveCookie(String server, long expires, byte[] data, boolean revokeQuerySupported, boolean saveCookieToDisk) {
if(!EagRuntime.getConfiguration().isEnableServerCookies()) {
return;
}
server = normalize(server);
if(expires > 604800l) {
clearCookie(server);
logger.error("Server \"{}\" tried to set a cookie for {} days! (The max is 7 days)", server, (expires / 604800l));
return;
}
if(data.length > 255) {
clearCookie(server);
logger.error("Server \"{}\" tried to set a {} byte cookie! (The max length is 255 bytes)", server, data.length);
return;
}
if(expires < 0l || data.length == 0) {
clearCookie(server);
return;
}
long expiresRelative = System.currentTimeMillis() + expires * 1000l;
dataStore.put(server, new ServerCookie(server, data, expiresRelative, revokeQuerySupported, saveCookieToDisk));
saveData(HardwareFingerprint.getFingerprint());
}
public static void clearCookie(String server) {
if(!EagRuntime.getConfiguration().isEnableServerCookies()) {
return;
}
if(dataStore.remove(normalize(server)) != null) {
saveData(HardwareFingerprint.getFingerprint());
}
}
public static void clearCookiesLow() {
dataStore.clear();
}
private static String normalize(String server) {
int j = server.indexOf('/');
if(j != -1) {
int i = server.indexOf("://");
if(i != -1) {
j = server.indexOf('/', i + 3);
if(j == -1) {
return server.toLowerCase();
}else {
return server.substring(0, j).toLowerCase() + server.substring(j);
}
}else {
return server.substring(0, j).toLowerCase() + server.substring(j);
}
}else {
return server.toLowerCase();
}
}
public static Set<String> getAllServers() {
return dataStore.keySet();
}
public static List<String> getRevokableServers() {
List<String> ret = new ArrayList<>(dataStore.size());
for(ServerCookie c : dataStore.values()) {
if(c.revokeQuerySupported) {
ret.add(c.server);
}
}
return ret;
}
public static int size() {
return dataStore.size();
}
public static int numRevokable() {
int i = 0;
for(ServerCookie c : dataStore.values()) {
if(c.revokeQuerySupported) {
++i;
}
}
return i;
}
public static void flush() {
Iterator<ServerCookie> itr = dataStore.values().iterator();
boolean changed = false;
while(itr.hasNext()) {
long timestamp = System.currentTimeMillis();
ServerCookie cookie = itr.next();
if(timestamp > cookie.expires) {
itr.remove();
changed = true;
}
}
if(changed) {
saveData(HardwareFingerprint.getFingerprint());
}
}
private static void loadData(byte[] key) {
dataStore.clear();
byte[] cookiesTag = PlatformApplication.getLocalStorage(localStorageKey, false);
if(cookiesTag == null) {
return;
}
if(cookiesTag.length <= 25) {
PlatformApplication.setLocalStorage(localStorageKey, null, false);
return;
}
try {
byte[] decrypted;
int decryptedLen;
switch(cookiesTag[0]) {
case 2:
if(key == null || key.length == 0) {
throw new IOException("Data is encrypted!");
}
decrypted = new byte[cookiesTag.length - 5];
decryptedLen = (cookiesTag[1] << 24) | (cookiesTag[2] << 16) | (cookiesTag[3] << 8) | (cookiesTag[4] & 0xFF);
if(decryptedLen < 25) {
throw new IOException("too short!");
}
AESLightEngine aes = new AESLightEngine();
aes.init(false, key);
int bs = aes.getBlockSize();
if(decrypted.length % bs != 0) {
throw new IOException("length not aligned to block size!");
}
byte[] cbcHelper = new byte[] { (byte) 29, (byte) 163, (byte) 4, (byte) 20, (byte) 207, (byte) 26,
(byte) 140, (byte) 55, (byte) 246, (byte) 250, (byte) 141, (byte) 183, (byte) 153, (byte) 154,
(byte) 59, (byte) 4 };
for(int i = 0; i < decryptedLen; i += bs) {
processBlockDecryptHelper(aes, cookiesTag, 5 + i, decrypted, i, bs, Math.min(decryptedLen - i, bs), cbcHelper);
}
if(decrypted[0] != (byte)0x69) {
throw new IOException("Data is corrupt!");
}
break;
case 1:
if(key != null && key.length > 0) {
throw new IOException("Data isn't encrypted!");
}
decrypted = cookiesTag;
decryptedLen = cookiesTag.length;
break;
default:
throw new IOException("Unknown type!");
}
SHA1Digest digest = new SHA1Digest();
digest.update(decrypted, 25, decryptedLen - 25);
byte[] digestOut = new byte[20];
digest.doFinal(digestOut, 0);
for(int i = 0; i < 20; ++i) {
if(digestOut[i] != decrypted[5 + i]) {
throw new IOException("Invalid checksum!");
}
}
int decompressedLen = (decrypted[1] << 24) | (decrypted[2] << 16) | (decrypted[3] << 8) | (decrypted[4] & 0xFF);
byte[] decompressed = new byte[decompressedLen];
try (InputStream zstream = EaglerZLIB.newInflaterInputStream(new EaglerInputStream(decrypted, 25, decryptedLen - 25))) {
int i = 0, j;
while(i < decompressedLen && (j = zstream.read(decompressed, i, decompressedLen - i)) != -1) {
i += j;
}
if(i != decompressedLen) {
throw new IOException("Length does not match!");
}
}
DataInputStream dis = new DataInputStream(new EaglerInputStream(decompressed));
int readCount = dis.readInt();
long time = System.currentTimeMillis();
for(int i = 0; i < readCount; ++i) {
byte flags = dis.readByte();
long expires = dis.readLong();
int len = dis.readUnsignedShort();
String server = dis.readUTF();
if(len == 0) {
continue;
}
if(expires < time) {
dis.skipBytes(len);
continue;
}
byte[] cookieData = new byte[len];
dis.readFully(cookieData);
server = normalize(server);
dataStore.put(server, new ServerCookie(server, cookieData, expires, (flags & 1) != 0, (flags & 2) != 0));
}
if(dis.available() > 0) {
throw new IOException("Extra bytes remaining!");
}
}catch (IOException e) {
dataStore.clear();
PlatformApplication.setLocalStorage(localStorageKey, null, false);
return;
}
}
private static void saveData(byte[] key) {
Iterator<ServerCookie> itr = dataStore.values().iterator();
List<ServerCookie> toSave = new ArrayList<>(dataStore.size());
while(itr.hasNext()) {
long timestamp = System.currentTimeMillis();
ServerCookie cookie = itr.next();
if(timestamp > cookie.expires || cookie.cookie.length > 255 || cookie.cookie.length == 0) {
itr.remove();
}else if(cookie.saveCookieToDisk) {
toSave.add(cookie);
}
}
if(toSave.size() == 0) {
PlatformApplication.setLocalStorage(localStorageKey, null, false);
}else {
EaglerOutputStream bao = new EaglerOutputStream(1024);
bao.skipBytes(25);
int totalUncompressedLen;
try(DataOutputStream zstream = new DataOutputStream(EaglerZLIB.newDeflaterOutputStream(bao))) {
zstream.writeInt(dataStore.size());
for(Entry<String,ServerCookie> etr : dataStore.entrySet()) {
ServerCookie cookie = etr.getValue();
zstream.writeByte((cookie.revokeQuerySupported ? 1 : 0) | (cookie.saveCookieToDisk ? 2 : 0));
zstream.writeLong(cookie.expires);
zstream.writeShort(cookie.cookie.length);
zstream.writeUTF(etr.getKey());
zstream.write(cookie.cookie);
}
totalUncompressedLen = zstream.size();
} catch (IOException e) {
logger.error("Failed to write cookies to local storage!");
return;
}
byte[] toEncrypt = bao.toByteArray();
SHA1Digest hash = new SHA1Digest();
hash.update(toEncrypt, 25, toEncrypt.length - 25);
hash.doFinal(toEncrypt, 5);
toEncrypt[1] = (byte)(totalUncompressedLen >>> 24);
toEncrypt[2] = (byte)(totalUncompressedLen >>> 16);
toEncrypt[3] = (byte)(totalUncompressedLen >>> 8);
toEncrypt[4] = (byte)(totalUncompressedLen & 0xFF);
if(key != null && key.length > 0) {
toEncrypt[0] = (byte)0x69;
AESLightEngine aes = new AESLightEngine();
aes.init(true, key);
int bs = aes.getBlockSize();
int blockCount = (toEncrypt.length % bs) != 0 ? (toEncrypt.length / bs + 1) : (toEncrypt.length / bs);
byte[] encrypted = new byte[blockCount * bs + 5];
encrypted[0] = (byte)2;
encrypted[1] = (byte)(toEncrypt.length >>> 24);
encrypted[2] = (byte)(toEncrypt.length >>> 16);
encrypted[3] = (byte)(toEncrypt.length >>> 8);
encrypted[4] = (byte)(toEncrypt.length & 0xFF);
byte[] cbcHelper = new byte[] { (byte) 29, (byte) 163, (byte) 4, (byte) 20, (byte) 207, (byte) 26,
(byte) 140, (byte) 55, (byte) 246, (byte) 250, (byte) 141, (byte) 183, (byte) 153, (byte) 154,
(byte) 59, (byte) 4 };
for(int i = 0; i < toEncrypt.length; i += bs) {
processBlockEncryptHelper(aes, toEncrypt, i, encrypted, 5 + i, bs, cbcHelper);
}
PlatformApplication.setLocalStorage(localStorageKey, encrypted, false);
}else {
toEncrypt[0] = (byte)1;
PlatformApplication.setLocalStorage(localStorageKey, toEncrypt, false);
}
}
}
private static void processBlockEncryptHelper(AESLightEngine aes, byte[] in, int inOff, byte[] out, int outOff,
int len, byte[] cbcHelper) {
int clampedBlockLength = Math.min(in.length - inOff, len);
if(clampedBlockLength == len) {
for(int i = 0; i < len; ++i) {
in[i + inOff] ^= cbcHelper[i];
}
aes.processBlock(in, inOff, out, outOff);
System.arraycopy(out, outOff, cbcHelper, 0, len);
}else {
byte[] paddedBlock = new byte[len];
System.arraycopy(in, inOff, paddedBlock, 0, clampedBlockLength);
byte padValue = (byte)(len - clampedBlockLength);
for(byte i = 0; i < padValue; ++i) {
paddedBlock[clampedBlockLength + i] = padValue;
}
for(int i = 0; i < len; ++i) {
paddedBlock[i] ^= cbcHelper[i];
}
aes.processBlock(paddedBlock, 0, out, outOff);
}
}
private static void processBlockDecryptHelper(AESLightEngine aes, byte[] in, int inOff, byte[] out, int outOff,
int paddedLen, int unpaddedLen, byte[] cbcHelper) throws IOException {
aes.processBlock(in, inOff, out, outOff);
for(int i = 0; i < paddedLen; ++i) {
out[i + outOff] ^= cbcHelper[i];
}
if(unpaddedLen == paddedLen) {
System.arraycopy(in, inOff, cbcHelper, 0, paddedLen);
}else {
byte padValue = (byte)(paddedLen - unpaddedLen);
for(byte i = 0; i < padValue; ++i) {
if(out[outOff + unpaddedLen + i] != padValue) {
throw new IOException("Invalid padding!");
}
}
}
}
}

View File

@ -0,0 +1,527 @@
/*
* Copyright (c) 2000-2021 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
package net.lax1dude.eaglercraft.v1_8.crypto;
/**
* an implementation of the AES (Rijndael), from FIPS-197.
* <p>
* For further details see: <a href="https://csrc.nist.gov/encryption/aes/">https://csrc.nist.gov/encryption/aes/</a>.
*
* This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
* <a href="https://fp.gladman.plus.com/cryptography_technology/rijndael/">https://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
*
* There are three levels of tradeoff of speed vs memory
* Because java has no preprocessor, they are written as three separate classes from which to choose
*
* The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption
* and 4 for decryption.
*
* The middle performance version uses only one 256 word table for each, for a total of 2Kbytes,
* adding 12 rotate operations per round to compute the values contained in the other tables from
* the contents of the first
*
* The slowest version uses no static tables at all and computes the values
* in each round.
* <p>
* This file contains the slowest performance version with no static tables
* for round precomputation, but it has the smallest foot print.
*
*/
public class AESLightEngine {
// The S box
private static final byte[] S = { (byte) 99, (byte) 124, (byte) 119, (byte) 123, (byte) 242, (byte) 107, (byte) 111,
(byte) 197, (byte) 48, (byte) 1, (byte) 103, (byte) 43, (byte) 254, (byte) 215, (byte) 171, (byte) 118,
(byte) 202, (byte) 130, (byte) 201, (byte) 125, (byte) 250, (byte) 89, (byte) 71, (byte) 240, (byte) 173,
(byte) 212, (byte) 162, (byte) 175, (byte) 156, (byte) 164, (byte) 114, (byte) 192, (byte) 183, (byte) 253,
(byte) 147, (byte) 38, (byte) 54, (byte) 63, (byte) 247, (byte) 204, (byte) 52, (byte) 165, (byte) 229,
(byte) 241, (byte) 113, (byte) 216, (byte) 49, (byte) 21, (byte) 4, (byte) 199, (byte) 35, (byte) 195,
(byte) 24, (byte) 150, (byte) 5, (byte) 154, (byte) 7, (byte) 18, (byte) 128, (byte) 226, (byte) 235,
(byte) 39, (byte) 178, (byte) 117, (byte) 9, (byte) 131, (byte) 44, (byte) 26, (byte) 27, (byte) 110,
(byte) 90, (byte) 160, (byte) 82, (byte) 59, (byte) 214, (byte) 179, (byte) 41, (byte) 227, (byte) 47,
(byte) 132, (byte) 83, (byte) 209, (byte) 0, (byte) 237, (byte) 32, (byte) 252, (byte) 177, (byte) 91,
(byte) 106, (byte) 203, (byte) 190, (byte) 57, (byte) 74, (byte) 76, (byte) 88, (byte) 207, (byte) 208,
(byte) 239, (byte) 170, (byte) 251, (byte) 67, (byte) 77, (byte) 51, (byte) 133, (byte) 69, (byte) 249,
(byte) 2, (byte) 127, (byte) 80, (byte) 60, (byte) 159, (byte) 168, (byte) 81, (byte) 163, (byte) 64,
(byte) 143, (byte) 146, (byte) 157, (byte) 56, (byte) 245, (byte) 188, (byte) 182, (byte) 218, (byte) 33,
(byte) 16, (byte) 255, (byte) 243, (byte) 210, (byte) 205, (byte) 12, (byte) 19, (byte) 236, (byte) 95,
(byte) 151, (byte) 68, (byte) 23, (byte) 196, (byte) 167, (byte) 126, (byte) 61, (byte) 100, (byte) 93,
(byte) 25, (byte) 115, (byte) 96, (byte) 129, (byte) 79, (byte) 220, (byte) 34, (byte) 42, (byte) 144,
(byte) 136, (byte) 70, (byte) 238, (byte) 184, (byte) 20, (byte) 222, (byte) 94, (byte) 11, (byte) 219,
(byte) 224, (byte) 50, (byte) 58, (byte) 10, (byte) 73, (byte) 6, (byte) 36, (byte) 92, (byte) 194,
(byte) 211, (byte) 172, (byte) 98, (byte) 145, (byte) 149, (byte) 228, (byte) 121, (byte) 231, (byte) 200,
(byte) 55, (byte) 109, (byte) 141, (byte) 213, (byte) 78, (byte) 169, (byte) 108, (byte) 86, (byte) 244,
(byte) 234, (byte) 101, (byte) 122, (byte) 174, (byte) 8, (byte) 186, (byte) 120, (byte) 37, (byte) 46,
(byte) 28, (byte) 166, (byte) 180, (byte) 198, (byte) 232, (byte) 221, (byte) 116, (byte) 31, (byte) 75,
(byte) 189, (byte) 139, (byte) 138, (byte) 112, (byte) 62, (byte) 181, (byte) 102, (byte) 72, (byte) 3,
(byte) 246, (byte) 14, (byte) 97, (byte) 53, (byte) 87, (byte) 185, (byte) 134, (byte) 193, (byte) 29,
(byte) 158, (byte) 225, (byte) 248, (byte) 152, (byte) 17, (byte) 105, (byte) 217, (byte) 142, (byte) 148,
(byte) 155, (byte) 30, (byte) 135, (byte) 233, (byte) 206, (byte) 85, (byte) 40, (byte) 223, (byte) 140,
(byte) 161, (byte) 137, (byte) 13, (byte) 191, (byte) 230, (byte) 66, (byte) 104, (byte) 65, (byte) 153,
(byte) 45, (byte) 15, (byte) 176, (byte) 84, (byte) 187, (byte) 22, };
// The inverse S-box
private static final byte[] Si = { (byte) 82, (byte) 9, (byte) 106, (byte) 213, (byte) 48, (byte) 54, (byte) 165,
(byte) 56, (byte) 191, (byte) 64, (byte) 163, (byte) 158, (byte) 129, (byte) 243, (byte) 215, (byte) 251,
(byte) 124, (byte) 227, (byte) 57, (byte) 130, (byte) 155, (byte) 47, (byte) 255, (byte) 135, (byte) 52,
(byte) 142, (byte) 67, (byte) 68, (byte) 196, (byte) 222, (byte) 233, (byte) 203, (byte) 84, (byte) 123,
(byte) 148, (byte) 50, (byte) 166, (byte) 194, (byte) 35, (byte) 61, (byte) 238, (byte) 76, (byte) 149,
(byte) 11, (byte) 66, (byte) 250, (byte) 195, (byte) 78, (byte) 8, (byte) 46, (byte) 161, (byte) 102,
(byte) 40, (byte) 217, (byte) 36, (byte) 178, (byte) 118, (byte) 91, (byte) 162, (byte) 73, (byte) 109,
(byte) 139, (byte) 209, (byte) 37, (byte) 114, (byte) 248, (byte) 246, (byte) 100, (byte) 134, (byte) 104,
(byte) 152, (byte) 22, (byte) 212, (byte) 164, (byte) 92, (byte) 204, (byte) 93, (byte) 101, (byte) 182,
(byte) 146, (byte) 108, (byte) 112, (byte) 72, (byte) 80, (byte) 253, (byte) 237, (byte) 185, (byte) 218,
(byte) 94, (byte) 21, (byte) 70, (byte) 87, (byte) 167, (byte) 141, (byte) 157, (byte) 132, (byte) 144,
(byte) 216, (byte) 171, (byte) 0, (byte) 140, (byte) 188, (byte) 211, (byte) 10, (byte) 247, (byte) 228,
(byte) 88, (byte) 5, (byte) 184, (byte) 179, (byte) 69, (byte) 6, (byte) 208, (byte) 44, (byte) 30,
(byte) 143, (byte) 202, (byte) 63, (byte) 15, (byte) 2, (byte) 193, (byte) 175, (byte) 189, (byte) 3,
(byte) 1, (byte) 19, (byte) 138, (byte) 107, (byte) 58, (byte) 145, (byte) 17, (byte) 65, (byte) 79,
(byte) 103, (byte) 220, (byte) 234, (byte) 151, (byte) 242, (byte) 207, (byte) 206, (byte) 240, (byte) 180,
(byte) 230, (byte) 115, (byte) 150, (byte) 172, (byte) 116, (byte) 34, (byte) 231, (byte) 173, (byte) 53,
(byte) 133, (byte) 226, (byte) 249, (byte) 55, (byte) 232, (byte) 28, (byte) 117, (byte) 223, (byte) 110,
(byte) 71, (byte) 241, (byte) 26, (byte) 113, (byte) 29, (byte) 41, (byte) 197, (byte) 137, (byte) 111,
(byte) 183, (byte) 98, (byte) 14, (byte) 170, (byte) 24, (byte) 190, (byte) 27, (byte) 252, (byte) 86,
(byte) 62, (byte) 75, (byte) 198, (byte) 210, (byte) 121, (byte) 32, (byte) 154, (byte) 219, (byte) 192,
(byte) 254, (byte) 120, (byte) 205, (byte) 90, (byte) 244, (byte) 31, (byte) 221, (byte) 168, (byte) 51,
(byte) 136, (byte) 7, (byte) 199, (byte) 49, (byte) 177, (byte) 18, (byte) 16, (byte) 89, (byte) 39,
(byte) 128, (byte) 236, (byte) 95, (byte) 96, (byte) 81, (byte) 127, (byte) 169, (byte) 25, (byte) 181,
(byte) 74, (byte) 13, (byte) 45, (byte) 229, (byte) 122, (byte) 159, (byte) 147, (byte) 201, (byte) 156,
(byte) 239, (byte) 160, (byte) 224, (byte) 59, (byte) 77, (byte) 174, (byte) 42, (byte) 245, (byte) 176,
(byte) 200, (byte) 235, (byte) 187, (byte) 60, (byte) 131, (byte) 83, (byte) 153, (byte) 97, (byte) 23,
(byte) 43, (byte) 4, (byte) 126, (byte) 186, (byte) 119, (byte) 214, (byte) 38, (byte) 225, (byte) 105,
(byte) 20, (byte) 99, (byte) 85, (byte) 33, (byte) 12, (byte) 125, };
// vector used in calculating key schedule (powers of x in GF(256))
private static final int[] rcon = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab,
0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 };
private static int shift(int r, int shift) {
return (r >>> shift) | (r << -shift);
}
/* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
private static final int m1 = 0x80808080;
private static final int m2 = 0x7f7f7f7f;
private static final int m3 = 0x0000001b;
private static final int m4 = 0xC0C0C0C0;
private static final int m5 = 0x3f3f3f3f;
private static int FFmulX(int x) {
return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3));
}
private static int FFmulX2(int x) {
int t0 = (x & m5) << 2;
int t1 = (x & m4);
t1 ^= (t1 >>> 1);
return t0 ^ (t1 >>> 2) ^ (t1 >>> 5);
}
/*
The following defines provide alternative definitions of FFmulX that might
give improved performance if a fast 32-bit multiply is not available.
private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); }
private static final int m4 = 0x1b1b1b1b;
private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); }
*/
private static int mcol(int x) {
int t0, t1;
t0 = shift(x, 8);
t1 = x ^ t0;
return shift(t1, 16) ^ t0 ^ FFmulX(t1);
}
private static int inv_mcol(int x) {
int t0, t1;
t0 = x;
t1 = t0 ^ shift(t0, 8);
t0 ^= FFmulX(t1);
t1 ^= FFmulX2(t0);
t0 ^= t1 ^ shift(t1, 16);
return t0;
}
private static int subWord(int x) {
return (S[x & 255] & 255 | ((S[(x >> 8) & 255] & 255) << 8) | ((S[(x >> 16) & 255] & 255) << 16)
| S[(x >> 24) & 255] << 24);
}
private static int littleEndianToInt(byte[] bs, int off) {
int n = bs[off] & 0xff;
n |= (bs[++off] & 0xff) << 8;
n |= (bs[++off] & 0xff) << 16;
n |= bs[++off] << 24;
return n;
}
public static void intToLittleEndian(int n, byte[] bs, int off) {
bs[off] = (byte) (n);
bs[++off] = (byte) (n >>> 8);
bs[++off] = (byte) (n >>> 16);
bs[++off] = (byte) (n >>> 24);
}
/**
* Calculate the necessary round keys
* The number of calculations depends on key size and block size
* AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits
* This code is written assuming those are the only possible values
*/
private int[][] generateWorkingKey(byte[] key, boolean forEncryption) {
int keyLen = key.length;
if (keyLen < 16 || keyLen > 32 || (keyLen & 7) != 0) {
throw new IllegalArgumentException("Key length not 128/192/256 bits.");
}
int KC = keyLen >>> 2;
ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block
// sizes
int[][] W = new int[ROUNDS + 1][4]; // 4 words in a block
switch (KC) {
case 4: {
int col0 = littleEndianToInt(key, 0);
W[0][0] = col0;
int col1 = littleEndianToInt(key, 4);
W[0][1] = col1;
int col2 = littleEndianToInt(key, 8);
W[0][2] = col2;
int col3 = littleEndianToInt(key, 12);
W[0][3] = col3;
for (int i = 1; i <= 10; ++i) {
int colx = subWord(shift(col3, 8)) ^ rcon[i - 1];
col0 ^= colx;
W[i][0] = col0;
col1 ^= col0;
W[i][1] = col1;
col2 ^= col1;
W[i][2] = col2;
col3 ^= col2;
W[i][3] = col3;
}
break;
}
case 6: {
int col0 = littleEndianToInt(key, 0);
W[0][0] = col0;
int col1 = littleEndianToInt(key, 4);
W[0][1] = col1;
int col2 = littleEndianToInt(key, 8);
W[0][2] = col2;
int col3 = littleEndianToInt(key, 12);
W[0][3] = col3;
int col4 = littleEndianToInt(key, 16);
int col5 = littleEndianToInt(key, 20);
int i = 1, rcon = 1, colx;
for (;;) {
W[i][0] = col4;
W[i][1] = col5;
colx = subWord(shift(col5, 8)) ^ rcon;
rcon <<= 1;
col0 ^= colx;
W[i][2] = col0;
col1 ^= col0;
W[i][3] = col1;
col2 ^= col1;
W[i + 1][0] = col2;
col3 ^= col2;
W[i + 1][1] = col3;
col4 ^= col3;
W[i + 1][2] = col4;
col5 ^= col4;
W[i + 1][3] = col5;
colx = subWord(shift(col5, 8)) ^ rcon;
rcon <<= 1;
col0 ^= colx;
W[i + 2][0] = col0;
col1 ^= col0;
W[i + 2][1] = col1;
col2 ^= col1;
W[i + 2][2] = col2;
col3 ^= col2;
W[i + 2][3] = col3;
if ((i += 3) >= 13) {
break;
}
col4 ^= col3;
col5 ^= col4;
}
break;
}
case 8: {
int col0 = littleEndianToInt(key, 0);
W[0][0] = col0;
int col1 = littleEndianToInt(key, 4);
W[0][1] = col1;
int col2 = littleEndianToInt(key, 8);
W[0][2] = col2;
int col3 = littleEndianToInt(key, 12);
W[0][3] = col3;
int col4 = littleEndianToInt(key, 16);
W[1][0] = col4;
int col5 = littleEndianToInt(key, 20);
W[1][1] = col5;
int col6 = littleEndianToInt(key, 24);
W[1][2] = col6;
int col7 = littleEndianToInt(key, 28);
W[1][3] = col7;
int i = 2, rcon = 1, colx;
for (;;) {
colx = subWord(shift(col7, 8)) ^ rcon;
rcon <<= 1;
col0 ^= colx;
W[i][0] = col0;
col1 ^= col0;
W[i][1] = col1;
col2 ^= col1;
W[i][2] = col2;
col3 ^= col2;
W[i][3] = col3;
++i;
if (i >= 15) {
break;
}
colx = subWord(col3);
col4 ^= colx;
W[i][0] = col4;
col5 ^= col4;
W[i][1] = col5;
col6 ^= col5;
W[i][2] = col6;
col7 ^= col6;
W[i][3] = col7;
++i;
}
break;
}
default: {
throw new IllegalStateException("Should never get here");
}
}
if (!forEncryption) {
for (int j = 1; j < ROUNDS; j++) {
for (int i = 0; i < 4; i++) {
W[j][i] = inv_mcol(W[j][i]);
}
}
}
return W;
}
private int ROUNDS;
private int[][] WorkingKey = null;
private boolean forEncryption;
private static final int BLOCK_SIZE = 16;
/**
* default constructor - 128 bit block size.
*/
public AESLightEngine() {
}
/**
* initialise an AES cipher.
*
* @param forEncryption whether or not we are for encryption.
* @param params the parameters required to set up the cipher.
* @exception IllegalArgumentException if the params argument is
* inappropriate.
*/
public void init(boolean forEncryption, byte[] key) {
WorkingKey = generateWorkingKey(key, forEncryption);
this.forEncryption = forEncryption;
return;
}
public String getAlgorithmName() {
return "AES";
}
public int getBlockSize() {
return BLOCK_SIZE;
}
public int processBlock(byte[] in, int inOff, byte[] out, int outOff) {
if (WorkingKey == null) {
throw new IllegalStateException("AES engine not initialised");
}
if (inOff > (in.length - BLOCK_SIZE)) {
throw new IndexOutOfBoundsException("input buffer too short");
}
if (outOff > (out.length - BLOCK_SIZE)) {
throw new IndexOutOfBoundsException("output buffer too short");
}
if (forEncryption) {
encryptBlock(in, inOff, out, outOff, WorkingKey);
} else {
decryptBlock(in, inOff, out, outOff, WorkingKey);
}
return BLOCK_SIZE;
}
public void reset() {
}
private void encryptBlock(byte[] in, int inOff, byte[] out, int outOff, int[][] KW) {
int C0 = littleEndianToInt(in, inOff + 0);
int C1 = littleEndianToInt(in, inOff + 4);
int C2 = littleEndianToInt(in, inOff + 8);
int C3 = littleEndianToInt(in, inOff + 12);
int t0 = C0 ^ KW[0][0];
int t1 = C1 ^ KW[0][1];
int t2 = C2 ^ KW[0][2];
int r = 1, r0, r1, r2, r3 = C3 ^ KW[0][3];
while (r < ROUNDS - 1) {
r0 = mcol((S[t0 & 255] & 255) ^ ((S[(t1 >> 8) & 255] & 255) << 8) ^ ((S[(t2 >> 16) & 255] & 255) << 16)
^ (S[(r3 >> 24) & 255] << 24)) ^ KW[r][0];
r1 = mcol((S[t1 & 255] & 255) ^ ((S[(t2 >> 8) & 255] & 255) << 8) ^ ((S[(r3 >> 16) & 255] & 255) << 16)
^ (S[(t0 >> 24) & 255] << 24)) ^ KW[r][1];
r2 = mcol((S[t2 & 255] & 255) ^ ((S[(r3 >> 8) & 255] & 255) << 8) ^ ((S[(t0 >> 16) & 255] & 255) << 16)
^ (S[(t1 >> 24) & 255] << 24)) ^ KW[r][2];
r3 = mcol((S[r3 & 255] & 255) ^ ((S[(t0 >> 8) & 255] & 255) << 8) ^ ((S[(t1 >> 16) & 255] & 255) << 16)
^ (S[(t2 >> 24) & 255] << 24)) ^ KW[r++][3];
t0 = mcol((S[r0 & 255] & 255) ^ ((S[(r1 >> 8) & 255] & 255) << 8) ^ ((S[(r2 >> 16) & 255] & 255) << 16)
^ (S[(r3 >> 24) & 255] << 24)) ^ KW[r][0];
t1 = mcol((S[r1 & 255] & 255) ^ ((S[(r2 >> 8) & 255] & 255) << 8) ^ ((S[(r3 >> 16) & 255] & 255) << 16)
^ (S[(r0 >> 24) & 255] << 24)) ^ KW[r][1];
t2 = mcol((S[r2 & 255] & 255) ^ ((S[(r3 >> 8) & 255] & 255) << 8) ^ ((S[(r0 >> 16) & 255] & 255) << 16)
^ (S[(r1 >> 24) & 255] << 24)) ^ KW[r][2];
r3 = mcol((S[r3 & 255] & 255) ^ ((S[(r0 >> 8) & 255] & 255) << 8) ^ ((S[(r1 >> 16) & 255] & 255) << 16)
^ (S[(r2 >> 24) & 255] << 24)) ^ KW[r++][3];
}
r0 = mcol((S[t0 & 255] & 255) ^ ((S[(t1 >> 8) & 255] & 255) << 8) ^ ((S[(t2 >> 16) & 255] & 255) << 16)
^ (S[(r3 >> 24) & 255] << 24)) ^ KW[r][0];
r1 = mcol((S[t1 & 255] & 255) ^ ((S[(t2 >> 8) & 255] & 255) << 8) ^ ((S[(r3 >> 16) & 255] & 255) << 16)
^ (S[(t0 >> 24) & 255] << 24)) ^ KW[r][1];
r2 = mcol((S[t2 & 255] & 255) ^ ((S[(r3 >> 8) & 255] & 255) << 8) ^ ((S[(t0 >> 16) & 255] & 255) << 16)
^ (S[(t1 >> 24) & 255] << 24)) ^ KW[r][2];
r3 = mcol((S[r3 & 255] & 255) ^ ((S[(t0 >> 8) & 255] & 255) << 8) ^ ((S[(t1 >> 16) & 255] & 255) << 16)
^ (S[(t2 >> 24) & 255] << 24)) ^ KW[r++][3];
// the final round is a simple function of S
C0 = (S[r0 & 255] & 255) ^ ((S[(r1 >> 8) & 255] & 255) << 8) ^ ((S[(r2 >> 16) & 255] & 255) << 16)
^ (S[(r3 >> 24) & 255] << 24) ^ KW[r][0];
C1 = (S[r1 & 255] & 255) ^ ((S[(r2 >> 8) & 255] & 255) << 8) ^ ((S[(r3 >> 16) & 255] & 255) << 16)
^ (S[(r0 >> 24) & 255] << 24) ^ KW[r][1];
C2 = (S[r2 & 255] & 255) ^ ((S[(r3 >> 8) & 255] & 255) << 8) ^ ((S[(r0 >> 16) & 255] & 255) << 16)
^ (S[(r1 >> 24) & 255] << 24) ^ KW[r][2];
C3 = (S[r3 & 255] & 255) ^ ((S[(r0 >> 8) & 255] & 255) << 8) ^ ((S[(r1 >> 16) & 255] & 255) << 16)
^ (S[(r2 >> 24) & 255] << 24) ^ KW[r][3];
intToLittleEndian(C0, out, outOff + 0);
intToLittleEndian(C1, out, outOff + 4);
intToLittleEndian(C2, out, outOff + 8);
intToLittleEndian(C3, out, outOff + 12);
}
private void decryptBlock(byte[] in, int inOff, byte[] out, int outOff, int[][] KW) {
int C0 = littleEndianToInt(in, inOff + 0);
int C1 = littleEndianToInt(in, inOff + 4);
int C2 = littleEndianToInt(in, inOff + 8);
int C3 = littleEndianToInt(in, inOff + 12);
int t0 = C0 ^ KW[ROUNDS][0];
int t1 = C1 ^ KW[ROUNDS][1];
int t2 = C2 ^ KW[ROUNDS][2];
int r = ROUNDS - 1, r0, r1, r2, r3 = C3 ^ KW[ROUNDS][3];
while (r > 1) {
r0 = inv_mcol((Si[t0 & 255] & 255) ^ ((Si[(r3 >> 8) & 255] & 255) << 8)
^ ((Si[(t2 >> 16) & 255] & 255) << 16) ^ (Si[(t1 >> 24) & 255] << 24)) ^ KW[r][0];
r1 = inv_mcol((Si[t1 & 255] & 255) ^ ((Si[(t0 >> 8) & 255] & 255) << 8)
^ ((Si[(r3 >> 16) & 255] & 255) << 16) ^ (Si[(t2 >> 24) & 255] << 24)) ^ KW[r][1];
r2 = inv_mcol((Si[t2 & 255] & 255) ^ ((Si[(t1 >> 8) & 255] & 255) << 8)
^ ((Si[(t0 >> 16) & 255] & 255) << 16) ^ (Si[(r3 >> 24) & 255] << 24)) ^ KW[r][2];
r3 = inv_mcol((Si[r3 & 255] & 255) ^ ((Si[(t2 >> 8) & 255] & 255) << 8)
^ ((Si[(t1 >> 16) & 255] & 255) << 16) ^ (Si[(t0 >> 24) & 255] << 24)) ^ KW[r--][3];
t0 = inv_mcol((Si[r0 & 255] & 255) ^ ((Si[(r3 >> 8) & 255] & 255) << 8)
^ ((Si[(r2 >> 16) & 255] & 255) << 16) ^ (Si[(r1 >> 24) & 255] << 24)) ^ KW[r][0];
t1 = inv_mcol((Si[r1 & 255] & 255) ^ ((Si[(r0 >> 8) & 255] & 255) << 8)
^ ((Si[(r3 >> 16) & 255] & 255) << 16) ^ (Si[(r2 >> 24) & 255] << 24)) ^ KW[r][1];
t2 = inv_mcol((Si[r2 & 255] & 255) ^ ((Si[(r1 >> 8) & 255] & 255) << 8)
^ ((Si[(r0 >> 16) & 255] & 255) << 16) ^ (Si[(r3 >> 24) & 255] << 24)) ^ KW[r][2];
r3 = inv_mcol((Si[r3 & 255] & 255) ^ ((Si[(r2 >> 8) & 255] & 255) << 8)
^ ((Si[(r1 >> 16) & 255] & 255) << 16) ^ (Si[(r0 >> 24) & 255] << 24)) ^ KW[r--][3];
}
r0 = inv_mcol((Si[t0 & 255] & 255) ^ ((Si[(r3 >> 8) & 255] & 255) << 8) ^ ((Si[(t2 >> 16) & 255] & 255) << 16)
^ (Si[(t1 >> 24) & 255] << 24)) ^ KW[r][0];
r1 = inv_mcol((Si[t1 & 255] & 255) ^ ((Si[(t0 >> 8) & 255] & 255) << 8) ^ ((Si[(r3 >> 16) & 255] & 255) << 16)
^ (Si[(t2 >> 24) & 255] << 24)) ^ KW[r][1];
r2 = inv_mcol((Si[t2 & 255] & 255) ^ ((Si[(t1 >> 8) & 255] & 255) << 8) ^ ((Si[(t0 >> 16) & 255] & 255) << 16)
^ (Si[(r3 >> 24) & 255] << 24)) ^ KW[r][2];
r3 = inv_mcol((Si[r3 & 255] & 255) ^ ((Si[(t2 >> 8) & 255] & 255) << 8) ^ ((Si[(t1 >> 16) & 255] & 255) << 16)
^ (Si[(t0 >> 24) & 255] << 24)) ^ KW[r][3];
// the final round's table is a simple function of Si
C0 = (Si[r0 & 255] & 255) ^ ((Si[(r3 >> 8) & 255] & 255) << 8) ^ ((Si[(r2 >> 16) & 255] & 255) << 16)
^ (Si[(r1 >> 24) & 255] << 24) ^ KW[0][0];
C1 = (Si[r1 & 255] & 255) ^ ((Si[(r0 >> 8) & 255] & 255) << 8) ^ ((Si[(r3 >> 16) & 255] & 255) << 16)
^ (Si[(r2 >> 24) & 255] << 24) ^ KW[0][1];
C2 = (Si[r2 & 255] & 255) ^ ((Si[(r1 >> 8) & 255] & 255) << 8) ^ ((Si[(r0 >> 16) & 255] & 255) << 16)
^ (Si[(r3 >> 24) & 255] << 24) ^ KW[0][2];
C3 = (Si[r3 & 255] & 255) ^ ((Si[(r2 >> 8) & 255] & 255) << 8) ^ ((Si[(r1 >> 16) & 255] & 255) << 16)
^ (Si[(r0 >> 24) & 255] << 24) ^ KW[0][3];
intToLittleEndian(C0, out, outOff + 0);
intToLittleEndian(C1, out, outOff + 4);
intToLittleEndian(C2, out, outOff + 8);
intToLittleEndian(C3, out, outOff + 12);
}
private int bitsOfSecurity() {
if (WorkingKey == null) {
return 256;
}
return (WorkingKey.length - 7) << 5;
}
}

View File

@ -22,7 +22,7 @@ import java.util.concurrent.Executor;
*/
public class ListenableFutureTask<V> extends FutureTask<V> implements ListenableFuture<V> {
private final List<Runnable> listeners = new ArrayList();
private final List<Runnable> listeners = new ArrayList<>();
public ListenableFutureTask(Callable<V> callable) {
super(callable);
@ -54,7 +54,7 @@ public class ListenableFutureTask<V> extends FutureTask<V> implements Listenable
}
public static <V> ListenableFutureTask<V> create(Callable<V> callableToSchedule) {
return new ListenableFutureTask(callableToSchedule);
return new ListenableFutureTask<>(callableToSchedule);
}
}

View File

@ -0,0 +1,227 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* 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 abstract class AbstractWebSocketClient implements IWebSocketClient {
protected volatile int availableStringFrames = 0;
protected volatile int availableBinaryFrames = 0;
protected final List<IWebSocketFrame> recievedPacketBuffer = new LinkedList<>();
protected String currentURI;
protected AbstractWebSocketClient(String currentURI) {
this.currentURI = currentURI;
}
protected void addRecievedFrame(IWebSocketFrame frame) {
boolean str = frame.isString();
synchronized(recievedPacketBuffer) {
recievedPacketBuffer.add(frame);
if(str) {
++availableStringFrames;
}else {
++availableBinaryFrames;
}
}
}
@Override
public int availableFrames() {
synchronized(recievedPacketBuffer) {
return availableStringFrames + availableBinaryFrames;
}
}
@Override
public IWebSocketFrame getNextFrame() {
synchronized(recievedPacketBuffer) {
if(!recievedPacketBuffer.isEmpty()) {
IWebSocketFrame frame = recievedPacketBuffer.remove(0);
if(frame.isString()) {
--availableStringFrames;
}else {
--availableBinaryFrames;
}
return frame;
}else {
return null;
}
}
}
@Override
public List<IWebSocketFrame> getNextFrames() {
synchronized(recievedPacketBuffer) {
if(!recievedPacketBuffer.isEmpty()) {
List<IWebSocketFrame> ret = new ArrayList<>(recievedPacketBuffer);
recievedPacketBuffer.clear();
availableStringFrames = 0;
availableBinaryFrames = 0;
return ret;
}else {
return null;
}
}
}
@Override
public void clearFrames() {
synchronized(recievedPacketBuffer) {
recievedPacketBuffer.clear();
}
}
@Override
public int availableStringFrames() {
synchronized(recievedPacketBuffer) {
return availableStringFrames;
}
}
@Override
public IWebSocketFrame getNextStringFrame() {
synchronized(recievedPacketBuffer) {
if(availableStringFrames > 0) {
Iterator<IWebSocketFrame> itr = recievedPacketBuffer.iterator();
while(itr.hasNext()) {
IWebSocketFrame r = itr.next();
if(r.isString()) {
itr.remove();
--availableStringFrames;
return r;
}
}
availableStringFrames = 0;
return null;
}else {
return null;
}
}
}
@Override
public List<IWebSocketFrame> getNextStringFrames() {
synchronized(recievedPacketBuffer) {
if(availableStringFrames > 0) {
List<IWebSocketFrame> ret = new ArrayList<>(availableStringFrames);
Iterator<IWebSocketFrame> itr = recievedPacketBuffer.iterator();
while(itr.hasNext()) {
IWebSocketFrame r = itr.next();
if(r.isString()) {
itr.remove();
ret.add(r);
}
}
availableStringFrames = 0;
return ret;
}else {
return null;
}
}
}
@Override
public void clearStringFrames() {
synchronized(recievedPacketBuffer) {
if(availableStringFrames > 0) {
Iterator<IWebSocketFrame> itr = recievedPacketBuffer.iterator();
while(itr.hasNext()) {
IWebSocketFrame r = itr.next();
if(r.isString()) {
itr.remove();
}
}
availableStringFrames = 0;
}
}
}
@Override
public int availableBinaryFrames() {
synchronized(recievedPacketBuffer) {
return availableBinaryFrames;
}
}
@Override
public IWebSocketFrame getNextBinaryFrame() {
synchronized(recievedPacketBuffer) {
if(availableBinaryFrames > 0) {
Iterator<IWebSocketFrame> itr = recievedPacketBuffer.iterator();
while(itr.hasNext()) {
IWebSocketFrame r = itr.next();
if(!r.isString()) {
itr.remove();
--availableBinaryFrames;
return r;
}
}
availableBinaryFrames = 0;
return null;
}else {
return null;
}
}
}
@Override
public List<IWebSocketFrame> getNextBinaryFrames() {
synchronized(recievedPacketBuffer) {
if(availableBinaryFrames > 0) {
List<IWebSocketFrame> ret = new ArrayList<>(availableBinaryFrames);
Iterator<IWebSocketFrame> itr = recievedPacketBuffer.iterator();
while(itr.hasNext()) {
IWebSocketFrame r = itr.next();
if(!r.isString()) {
itr.remove();
ret.add(r);
}
}
availableBinaryFrames = 0;
return ret;
}else {
return null;
}
}
}
@Override
public void clearBinaryFrames() {
synchronized(recievedPacketBuffer) {
if(availableBinaryFrames > 0) {
Iterator<IWebSocketFrame> itr = recievedPacketBuffer.iterator();
while(itr.hasNext()) {
IWebSocketFrame r = itr.next();
if(!r.isString()) {
itr.remove();
}
}
availableBinaryFrames = 0;
}
}
}
@Override
public String getCurrentURI() {
return currentURI;
}
}

View File

@ -1,10 +1,7 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer;
/**
* Copyright (c) 2022-2023 lax1dude. 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 +15,21 @@ import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class PlatformBufferFunctions {
public static void put(ByteBuffer newBuffer, ByteBuffer flip) {
newBuffer.put(flip);
public class EaglerMissingResourceException extends RuntimeException {
public EaglerMissingResourceException() {
}
public static void put(IntBuffer intBuffer, int index, int[] data) {
int p = intBuffer.position();
intBuffer.position(index);
intBuffer.put(data);
intBuffer.position(p);
public EaglerMissingResourceException(String message, Throwable cause) {
super(message, cause);
}
public EaglerMissingResourceException(String message) {
super(message);
}
public EaglerMissingResourceException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,20 @@
package net.lax1dude.eaglercraft.v1_8.internal;
/**
* 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 enum EnumFireKeyboardEvent {
KEY_DOWN, KEY_UP, KEY_REPEAT;
}

View File

@ -0,0 +1,20 @@
package net.lax1dude.eaglercraft.v1_8.internal;
/**
* 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 enum EnumFireMouseEvent {
MOUSE_DOWN, MOUSE_UP, MOUSE_MOVE, MOUSE_WHEEL;
}

View File

@ -18,6 +18,7 @@ package net.lax1dude.eaglercraft.v1_8.internal;
public enum EnumPlatformANGLE {
DEFAULT(225281 /* GLFW_ANGLE_PLATFORM_TYPE_NONE */, "default", "Default"),
D3D9(225284 /* GLFW_ANGLE_PLATFORM_TYPE_D3D9 */, "d3d9", "Direct3D9"),
D3D11(225285 /* GLFW_ANGLE_PLATFORM_TYPE_D3D11 */, "d3d11", "Direct3D11"),
OPENGL(225282 /* GLFW_ANGLE_PLATFORM_TYPE_OPENGL */, "opengl", "OpenGL"),
OPENGLES(225283 /* GLFW_ANGLE_PLATFORM_TYPE_OPENGLES */, "opengles", "OpenGL ES"),
@ -41,6 +42,8 @@ public enum EnumPlatformANGLE {
public static EnumPlatformANGLE fromId(String id) {
if(id.equals("d3d11") || id.equals("d3d") || id.equals("dx11")) {
return D3D11;
}else if(id.equals("d3d9") || id.equals("dx9")) {
return D3D9;
}else if(id.equals("opengl")) {
return OPENGL;
}else if(id.equals("opengles")) {
@ -58,6 +61,8 @@ public enum EnumPlatformANGLE {
str = str.toLowerCase();
if(str.contains("direct3d11") || str.contains("d3d11")) {
return D3D11;
}else if(str.contains("direct3d9") || str.contains("d3d9")) {
return D3D9;
}else if(str.contains("opengl es")) {
return OPENGLES;
}else if(str.contains("opengl")) {

View File

@ -35,12 +35,15 @@ public enum EnumPlatformAgent {
}
public static EnumPlatformAgent getFromUA(String ua) {
if(ua == null) {
return UNKNOWN;
}
ua = " " + ua.toLowerCase();
if(ua.contains(" edg/")) {
return EDGE;
}else if(ua.contains(" opr/")) {
return OPERA;
}else if(ua.contains(" chrome/")) {
}else if(ua.contains(" chrome/") || ua.contains(" chromium/")) {
return CHROME;
}else if(ua.contains(" firefox/")) {
return FIREFOX;

View File

@ -42,6 +42,9 @@ public enum EnumPlatformOS {
}
public static EnumPlatformOS getFromJVM(String osNameProperty) {
if(osNameProperty == null) {
return OTHER;
}
osNameProperty = osNameProperty.toLowerCase();
if(osNameProperty.contains("chrome")) {
return CHROMEBOOK_LINUX;
@ -57,6 +60,9 @@ public enum EnumPlatformOS {
}
public static EnumPlatformOS getFromUA(String ua) {
if(ua == null) {
return OTHER;
}
ua = " " + ua.toLowerCase();
if(ua.contains(" cros")) {
return CHROMEBOOK_LINUX;

View File

@ -1,11 +1,7 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
package net.lax1dude.eaglercraft.v1_8.internal;
/**
* Copyright (c) 2022 lax1dude. 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
@ -19,27 +15,29 @@ import java.io.IOException;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class IPacket05ClientSuccess extends IPacket {
public enum EnumTouchEvent {
TOUCHSTART(0), TOUCHMOVE(1), TOUCHEND(2);
public String clientId;
public final int id;
public IPacket05ClientSuccess() {
private EnumTouchEvent(int id) {
this.id = id;
}
public IPacket05ClientSuccess(String clientId) {
this.clientId = clientId;
}
public void read(DataInputStream input) throws IOException {
clientId = readASCII8(input);
}
public void write(DataOutputStream output) throws IOException {
writeASCII8(output, clientId);
public static EnumTouchEvent getById(int id) {
if(id >= 0 && id < lookup.length) {
return lookup[id];
}else {
return null;
}
}
public int packetLength() {
return 1 + clientId.length();
}
private static final EnumTouchEvent[] lookup = new EnumTouchEvent[3];
static {
EnumTouchEvent[] v = values();
for(int i = 0; i < v.length; ++i) {
lookup[v[i].id] = v[i];
}
}
}

View File

@ -0,0 +1,20 @@
package net.lax1dude.eaglercraft.v1_8.internal;
/**
* 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 enum EnumWebViewContentMode {
URL_BASED, BLOB_BASED;
}

View File

@ -71,4 +71,11 @@ public class GLObjectMap<T> {
values = new Object[size];
System.arraycopy(oldValues, 0, values, 0, oldSize);
}
public void clear() {
if(allocatedObjects == 0) return;
values = new Object[size];
insertIndex = 0;
allocatedObjects = 0;
}
}

View File

@ -0,0 +1,134 @@
package net.lax1dude.eaglercraft.v1_8.internal;
/**
* 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 GamepadConstants {
private static final String[] buttonNames = new String[24];
private static final String[] axisNames = new String[4];
private static final int[] eaglerButtonsToGLFW = new int[24];
private static final int[] glfwButtonsToEagler = new int[24];
public static final int GAMEPAD_NONE = -1;
public static final int GAMEPAD_A = 0;
public static final int GAMEPAD_B = 1;
public static final int GAMEPAD_X = 2;
public static final int GAMEPAD_Y = 3;
public static final int GAMEPAD_LEFT_BUTTON = 4;
public static final int GAMEPAD_RIGHT_BUTTON = 5;
public static final int GAMEPAD_LEFT_TRIGGER = 6;
public static final int GAMEPAD_RIGHT_TRIGGER = 7;
public static final int GAMEPAD_BACK = 8;
public static final int GAMEPAD_START = 9;
public static final int GAMEPAD_LEFT_STICK_BUTTON = 10;
public static final int GAMEPAD_RIGHT_STICK_BUTTON = 11;
public static final int GAMEPAD_DPAD_UP = 12;
public static final int GAMEPAD_DPAD_DOWN = 13;
public static final int GAMEPAD_DPAD_LEFT = 14;
public static final int GAMEPAD_DPAD_RIGHT = 15;
public static final int GAMEPAD_GUIDE = 16;
public static final int GAMEPAD_AXIS_NONE = -1;
public static final int GAMEPAD_AXIS_LEFT_STICK_X = 0;
public static final int GAMEPAD_AXIS_LEFT_STICK_Y = 1;
public static final int GAMEPAD_AXIS_RIGHT_STICK_X = 2;
public static final int GAMEPAD_AXIS_RIGHT_STICK_Y = 3;
private static final int GLFW_GAMEPAD_BUTTON_A = 0, GLFW_GAMEPAD_BUTTON_B = 1, GLFW_GAMEPAD_BUTTON_X = 2,
GLFW_GAMEPAD_BUTTON_Y = 3, GLFW_GAMEPAD_BUTTON_LEFT_BUMPER = 4, GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER = 5,
GLFW_GAMEPAD_BUTTON_BACK = 6, GLFW_GAMEPAD_BUTTON_START = 7, GLFW_GAMEPAD_BUTTON_GUIDE = 8,
GLFW_GAMEPAD_BUTTON_LEFT_THUMB = 9, GLFW_GAMEPAD_BUTTON_RIGHT_THUMB = 10, GLFW_GAMEPAD_BUTTON_DPAD_UP = 11,
GLFW_GAMEPAD_BUTTON_DPAD_RIGHT = 12, GLFW_GAMEPAD_BUTTON_DPAD_DOWN = 13, GLFW_GAMEPAD_BUTTON_DPAD_LEFT = 14;
private static void registerBtn(int eaglerBtn, int glfwBtn, String name) {
if(eaglerButtonsToGLFW[eaglerBtn] != 0) throw new IllegalArgumentException("Duplicate eaglerButtonsToGLFW entry: " + eaglerBtn + " -> " + glfwBtn);
if(glfwBtn != -1 && glfwButtonsToEagler[glfwBtn] != 0) throw new IllegalArgumentException("Duplicate glfwButtonsToEAGLER entry: " + glfwBtn + " -> " + eaglerBtn);
eaglerButtonsToGLFW[eaglerBtn] = glfwBtn;
if(glfwBtn != -1) glfwButtonsToEagler[glfwBtn] = eaglerBtn;
if(buttonNames[eaglerBtn] != null) throw new IllegalArgumentException("Duplicate buttonNames entry: " + eaglerBtn);
buttonNames[eaglerBtn] = name;
}
private static void registerAxis(int eaglerAxis, String name) {
if(axisNames[eaglerAxis] != null) throw new IllegalArgumentException("Duplicate axisNames entry: " + eaglerAxis);
axisNames[eaglerAxis] = name;
}
static {
registerBtn(GAMEPAD_A, GLFW_GAMEPAD_BUTTON_A, "A");
registerBtn(GAMEPAD_B, GLFW_GAMEPAD_BUTTON_B, "B");
registerBtn(GAMEPAD_X, GLFW_GAMEPAD_BUTTON_X, "X");
registerBtn(GAMEPAD_Y, GLFW_GAMEPAD_BUTTON_Y, "Y");
registerBtn(GAMEPAD_LEFT_BUTTON, GLFW_GAMEPAD_BUTTON_LEFT_BUMPER, "Left Button");
registerBtn(GAMEPAD_LEFT_TRIGGER, -1, "Left Trigger");
registerBtn(GAMEPAD_RIGHT_BUTTON, GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER, "Right Button");
registerBtn(GAMEPAD_RIGHT_TRIGGER, -1, "Right Trigger");
registerBtn(GAMEPAD_BACK, GLFW_GAMEPAD_BUTTON_BACK, "Back");
registerBtn(GAMEPAD_START, GLFW_GAMEPAD_BUTTON_START, "Start");
registerBtn(GAMEPAD_LEFT_STICK_BUTTON, GLFW_GAMEPAD_BUTTON_LEFT_THUMB, "L. Stick Button");
registerBtn(GAMEPAD_RIGHT_STICK_BUTTON, GLFW_GAMEPAD_BUTTON_RIGHT_THUMB, "R. Stick Button");
registerBtn(GAMEPAD_DPAD_UP, GLFW_GAMEPAD_BUTTON_DPAD_UP, "D-Pad Up");
registerBtn(GAMEPAD_DPAD_DOWN, GLFW_GAMEPAD_BUTTON_DPAD_DOWN, "D-Pad Down");
registerBtn(GAMEPAD_DPAD_LEFT, GLFW_GAMEPAD_BUTTON_DPAD_LEFT, "D-Pad Left");
registerBtn(GAMEPAD_DPAD_RIGHT, GLFW_GAMEPAD_BUTTON_DPAD_RIGHT, "D-Pad Right");
registerBtn(GAMEPAD_GUIDE, GLFW_GAMEPAD_BUTTON_GUIDE, "Guide");
registerAxis(GAMEPAD_AXIS_LEFT_STICK_X, "Left Stick X");
registerAxis(GAMEPAD_AXIS_LEFT_STICK_Y, "Left Stick Y");
registerAxis(GAMEPAD_AXIS_RIGHT_STICK_X, "Right Stick X");
registerAxis(GAMEPAD_AXIS_RIGHT_STICK_Y, "Right Stick Y");
}
public static int getEaglerButtonFromBrowser(int button) {
return button;
}
public static int getBrowserButtonFromEagler(int button) {
return button;
}
public static int getEaglerButtonFromGLFW(int button) {
if(button >= 0 && button < glfwButtonsToEagler.length) {
return glfwButtonsToEagler[button];
}else {
return -1;
}
}
public static int getGLFWButtonFromEagler(int button) {
if(button >= 0 && button < eaglerButtonsToGLFW.length) {
return eaglerButtonsToGLFW[button];
}else {
return -1;
}
}
public static String getButtonName(int button) {
if(button >= 0 && button < buttonNames.length) {
return buttonNames[button];
}else {
return "Button " + button;
}
}
public static String getAxisName(int button) {
if(button >= 0 && button < axisNames.length) {
return axisNames[button];
}else {
return "Axis " + button;
}
}
}

View File

@ -26,10 +26,12 @@ public interface IClientConfigAdapter {
public final String name;
public final String addr;
public final boolean hideAddress;
public DefaultServer(String name, String addr) {
public DefaultServer(String name, String addr, boolean hideAddress) {
this.name = name;
this.addr = addr;
this.hideAddress = hideAddress;
}
}
@ -76,6 +78,24 @@ public interface IClientConfigAdapter {
boolean isEnableMinceraft();
boolean isEnableServerCookies();
boolean isAllowServerRedirects();
boolean isOpenDebugConsoleOnLaunch();
boolean isForceWebViewSupport();
boolean isEnableWebViewCSP();
boolean isAllowBootMenu();
boolean isForceProfanityFilter();
boolean isEaglerNoDelay();
boolean isRamdiskMode();
IClientConfigAdapterHooks getHooks();
}

View File

@ -25,4 +25,6 @@ public interface IClientConfigAdapterHooks {
void callCrashReportHook(String crashReport, Consumer<String> customMessageCB);
void callScreenChangedHook(String screenName, int scaledWidth, int scaledHeight, int realWidth, int realHeight, int scaleFactor);
}

View File

@ -0,0 +1,46 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
/**
* 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 interface IEaglerFilesystem {
String getFilesystemName();
String getInternalDBName();
boolean isRamdisk();
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);
void closeHandle();
}

View File

@ -2,6 +2,8 @@ package net.lax1dude.eaglercraft.v1_8.internal;
import org.json.JSONObject;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
/**
* Copyright (c) 2022 lax1dude. All Rights Reserved.
*
@ -42,6 +44,8 @@ public interface IServerQuery {
}
void update();
void send(String str);
default void send(JSONObject json) {
@ -73,8 +77,8 @@ public interface IServerQuery {
EnumServerRateLimit getRateLimit();
default boolean awaitResponseAvailable(long timeout) {
long start = System.currentTimeMillis();
while(isOpen() && responsesAvailable() <= 0 && (timeout <= 0l || System.currentTimeMillis() - start < timeout)) {
long start = EagRuntime.steadyTimeMillis();
while(isOpen() && responsesAvailable() <= 0 && (timeout <= 0l || EagRuntime.steadyTimeMillis() - start < timeout)) {
try {
Thread.sleep(0l, 250000);
} catch (InterruptedException e) {
@ -88,8 +92,8 @@ public interface IServerQuery {
}
default boolean awaitResponseBinaryAvailable(long timeout) {
long start = System.currentTimeMillis();
while(isOpen() && binaryResponsesAvailable() <= 0 && (timeout <= 0l || System.currentTimeMillis() - start < timeout)) {
long start = EagRuntime.steadyTimeMillis();
while(isOpen() && binaryResponsesAvailable() <= 0 && (timeout <= 0l || EagRuntime.steadyTimeMillis() - start < timeout)) {
try {
Thread.sleep(0l, 250000);
} catch (InterruptedException e) {

View File

@ -1,12 +1,9 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt;
package net.lax1dude.eaglercraft.v1_8.internal;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Copyright (c) 2022 lax1dude. 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
@ -20,31 +17,46 @@ import java.util.List;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class IPacket07LocalWorlds extends IPacket {
public static class LocalWorld {
public final String worldName;
public final String worldCode;
public LocalWorld(String worldName, String worldCode) {
this.worldName = worldName;
this.worldCode = worldCode;
}
}
public final List<LocalWorld> worldsList;
public IPacket07LocalWorlds() {
this.worldsList = new ArrayList();
}
public interface IWebSocketClient {
EnumEaglerConnectionState getState();
boolean connectBlocking(int timeoutMS);
boolean isOpen();
boolean isClosed();
void close();
int availableFrames();
IWebSocketFrame getNextFrame();
List<IWebSocketFrame> getNextFrames();
void clearFrames();
int availableStringFrames();
IWebSocketFrame getNextStringFrame();
List<IWebSocketFrame> getNextStringFrames();
void clearStringFrames();
int availableBinaryFrames();
IWebSocketFrame getNextBinaryFrame();
List<IWebSocketFrame> getNextBinaryFrames();
void clearBinaryFrames();
void send(String str);
void send(byte[] bytes);
String getCurrentURI();
public void read(DataInputStream input) throws IOException {
int l = input.read();
for(int i = 0; i < l; ++i) {
worldsList.add(new LocalWorld(readASCII8(input), readASCII8(input)));
}
}
}

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 java.io.InputStream;
/**
* 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,18 @@ import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class PlatformBufferFunctions {
public interface IWebSocketFrame {
public static void put(ByteBuffer newBuffer, ByteBuffer flip) {
newBuffer.put(flip);
}
boolean isString();
String getString();
byte[] getByteArray();
InputStream getInputStream();
int getLength();
long getTimestamp();
public static void put(IntBuffer intBuffer, int index, int[] data) {
int p = intBuffer.position();
intBuffer.position(index);
intBuffer.put(data);
intBuffer.position(p);
}
}

View File

@ -2,6 +2,8 @@ package net.lax1dude.eaglercraft.v1_8.internal;
import org.json.JSONObject;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
/**
* Copyright (c) 2022 lax1dude. All Rights Reserved.
*
@ -37,7 +39,7 @@ public class QueryResponse {
this.serverBrand = obj.getString("brand");
this.serverName = obj.getString("name");
this.serverTime = obj.getLong("time");
this.clientTime = System.currentTimeMillis();
this.clientTime = EagRuntime.steadyTimeMillis();
this.serverCracked = obj.optBoolean("cracked", false);
}

View File

@ -0,0 +1,131 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.util.Map;
import java.util.TreeMap;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
/**
* 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 RamdiskFilesystemImpl implements IEaglerFilesystem {
protected final String filesystemName;
protected final Map<String,byte[]> filesystemMap = new TreeMap<>();
public RamdiskFilesystemImpl(String filesystemName) {
this.filesystemName = filesystemName;
}
@Override
public String getFilesystemName() {
return filesystemName;
}
@Override
public String getInternalDBName() {
return "ramdisk:" + filesystemName;
}
@Override
public boolean isRamdisk() {
return true;
}
@Override
public boolean eaglerDelete(String pathName) {
return filesystemMap.remove(pathName) != null;
}
@Override
public ByteBuffer eaglerRead(String pathName) {
byte[] data = filesystemMap.get(pathName);
if(data != null) {
ByteBuffer buf = PlatformRuntime.castPrimitiveByteArray(data);
if(buf == null) {
buf = PlatformRuntime.allocateByteBuffer(data.length);
buf.put(data);
buf.flip();
}
return buf;
}else {
return null;
}
}
@Override
public void eaglerWrite(String pathName, ByteBuffer data) {
byte[] arr = PlatformRuntime.castNativeByteBuffer(data);
if(arr == null) {
arr = new byte[data.remaining()];
int i = data.position();
data.get(arr);
data.position(i);
}
filesystemMap.put(pathName, arr);
}
@Override
public boolean eaglerExists(String pathName) {
return filesystemMap.containsKey(pathName);
}
@Override
public boolean eaglerMove(String pathNameOld, String pathNameNew) {
byte[] dat = filesystemMap.remove(pathNameOld);
if(dat != null) {
filesystemMap.put(pathNameNew, dat);
return true;
}
return false;
}
@Override
public int eaglerCopy(String pathNameOld, String pathNameNew) {
byte[] dat = filesystemMap.get(pathNameOld);
if(dat != null) {
filesystemMap.put(pathNameNew, dat);
return dat.length;
}
return -1;
}
@Override
public int eaglerSize(String pathName) {
byte[] dat = filesystemMap.get(pathName);
return dat != null ? dat.length : -1;
}
@Override
public void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive) {
if(!recursive) {
eaglerIterate(pathName, new VFSFilenameIteratorNonRecursive(itr,
VFSFilenameIteratorNonRecursive.countSlashes(pathName) + 1), true);
}else {
boolean b = pathName.length() == 0;
for(String key : filesystemMap.keySet()) {
if(b || key.startsWith(pathName)) {
itr.next(key);
}
}
}
}
@Override
public void closeHandle() {
filesystemMap.clear();
}
}

View File

@ -0,0 +1,37 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import net.lax1dude.eaglercraft.v1_8.recording.EnumScreenRecordingCodec;
/**
* 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 ScreenRecordParameters {
public final EnumScreenRecordingCodec codec;
public final int resolutionDivisior;
public final int videoBitsPerSecond;
public final int audioBitsPerSecond;
public final int captureFrameRate;
public ScreenRecordParameters(EnumScreenRecordingCodec codec, int resolutionDivisior, int videoBitsPerSecond,
int audioBitsPerSecond, int captureFrameRate) {
this.codec = codec;
this.resolutionDivisior = resolutionDivisior;
this.videoBitsPerSecond = videoBitsPerSecond;
this.audioBitsPerSecond = audioBitsPerSecond;
this.captureFrameRate = captureFrameRate;
}
}

View File

@ -1,10 +1,7 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt;
import java.io.DataInputStream;
import java.io.IOException;
package net.lax1dude.eaglercraft.v1_8.internal;
/**
* Copyright (c) 2022 lax1dude. 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
@ -18,28 +15,33 @@ import java.io.IOException;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class IPacket69Pong extends IPacket {
public class VFSFilenameIteratorNonRecursive implements VFSFilenameIterator {
public int protcolVersion;
public String comment;
public String brand;
public IPacket69Pong(int protcolVersion, String comment, String brand) {
if(comment.length() > 255) {
comment = comment.substring(0, 256);
private final VFSFilenameIterator child;
private final int pathCount;
public VFSFilenameIteratorNonRecursive(VFSFilenameIterator child, int pathCount) {
this.child = child;
this.pathCount = pathCount;
}
@Override
public void next(String entry) {
int i = countSlashes(entry);
if(i == pathCount) {
child.next(entry);
}
this.protcolVersion = protcolVersion;
this.comment = comment;
this.brand = brand;
}
public IPacket69Pong() {
public static int countSlashes(String str) {
if(str.length() == 0) return -1;
int j = 0;
for(int i = 0, l = str.length(); i < l; ++i) {
if(str.charAt(i) == '/') {
++j;
}
}
return j;
}
public void read(DataInputStream output) throws IOException {
protcolVersion = output.read();
comment = readASCII8(output);
brand = readASCII8(output);
}
}

View File

@ -0,0 +1,67 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
/**
* 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 WebViewOptions {
public EnumWebViewContentMode contentMode = EnumWebViewContentMode.BLOB_BASED;
public String fallbackTitle = "WebView";
public boolean scriptEnabled = false;
public boolean strictCSPEnable = true;
public boolean serverMessageAPIEnabled = false;
public URI url = null;
public byte[] blob = null;
public EaglercraftUUID permissionsOriginUUID = null;
public WebViewOptions() {
}
public WebViewOptions(boolean script, boolean serverMessageAPIEnabled, boolean strictCSPEnable, URI url) {
this.contentMode = EnumWebViewContentMode.URL_BASED;
this.scriptEnabled = script;
this.strictCSPEnable = strictCSPEnable;
this.serverMessageAPIEnabled = serverMessageAPIEnabled;
this.url = url;
this.permissionsOriginUUID = getURLOriginUUID(url);
}
public WebViewOptions(boolean script, boolean serverMessageAPIEnabled, boolean strictCSPEnable, byte[] data, EaglercraftUUID permissionsOriginUUID) {
this.contentMode = EnumWebViewContentMode.BLOB_BASED;
this.scriptEnabled = script;
this.strictCSPEnable = strictCSPEnable;
this.serverMessageAPIEnabled = serverMessageAPIEnabled;
this.blob = data;
this.permissionsOriginUUID = permissionsOriginUUID;
}
public static EaglercraftUUID getURLOriginUUID(URI url) {
return EaglercraftUUID.nameUUIDFromBytes(("URLOrigin:" + url.toString()).getBytes(StandardCharsets.UTF_8));
}
public static EaglercraftUUID getEmbedOriginUUID(byte[] sha256) {
byte[] vigg = "BlobOrigin:".getBytes(StandardCharsets.UTF_8);
byte[] eagler = new byte[sha256.length + vigg.length];
System.arraycopy(vigg, 0, eagler, 0, vigg.length);
System.arraycopy(sha256, 0, eagler, vigg.length, sha256.length);
return EaglercraftUUID.nameUUIDFromBytes(eagler);
}
}

View File

@ -41,14 +41,14 @@ public interface Buffer {
boolean hasRemaining();
boolean isReadOnly();
boolean hasArray();
Object array();
int arrayOffset();
boolean isDirect();
static IndexOutOfBoundsException makeIOOBE(int idx) {
return new IndexOutOfBoundsException("Index out of range: " + idx);
}
}

View File

@ -18,12 +18,8 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer;
*/
public interface ByteBuffer extends Buffer {
ByteBuffer slice();
ByteBuffer duplicate();
ByteBuffer asReadOnlyBuffer();
byte get();
ByteBuffer put(byte b);
@ -42,10 +38,6 @@ public interface ByteBuffer extends Buffer {
ByteBuffer put(byte[] src);
int arrayOffset();
ByteBuffer compact();
char getChar();
ByteBuffer putChar(char value);
@ -54,7 +46,7 @@ public interface ByteBuffer extends Buffer {
ByteBuffer putChar(int index, char value);
public abstract short getShort();
short getShort();
ByteBuffer putShort(short value);
@ -106,4 +98,6 @@ public interface ByteBuffer extends Buffer {
ByteBuffer position(int newPosition);
byte[] array();
}

View File

@ -3,7 +3,6 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import java.io.IOException;
import java.io.InputStream;
/**
* Copyright (c) 2022 lax1dude. All Rights Reserved.
*

View File

@ -17,12 +17,8 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer;
*/
public interface FloatBuffer extends Buffer {
FloatBuffer slice();
FloatBuffer duplicate();
FloatBuffer asReadOnlyBuffer();
float get();
FloatBuffer put(float b);
@ -45,10 +41,6 @@ public interface FloatBuffer extends Buffer {
FloatBuffer put(float[] src);
int getArrayOffset();
FloatBuffer compact();
boolean isDirect();
FloatBuffer mark();
@ -64,6 +56,8 @@ public interface FloatBuffer extends Buffer {
FloatBuffer limit(int newLimit);
FloatBuffer position(int newPosition);
float[] array();
}

View File

@ -17,12 +17,8 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer;
*/
public interface IntBuffer extends Buffer {
IntBuffer slice();
IntBuffer duplicate();
IntBuffer asReadOnlyBuffer();
int get();
IntBuffer put(int b);
@ -45,10 +41,6 @@ public interface IntBuffer extends Buffer {
IntBuffer put(int[] src);
int getArrayOffset();
IntBuffer compact();
boolean isDirect();
IntBuffer mark();
@ -64,6 +56,8 @@ public interface IntBuffer extends Buffer {
IntBuffer limit(int newLimit);
IntBuffer position(int newPosition);
int[] array();
}

View File

@ -17,12 +17,8 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer;
*/
public interface ShortBuffer extends Buffer {
ShortBuffer slice();
ShortBuffer duplicate();
ShortBuffer asReadOnlyBuffer();
short get();
ShortBuffer put(short b);
@ -45,10 +41,6 @@ public interface ShortBuffer extends Buffer {
ShortBuffer put(short[] src);
int getArrayOffset();
ShortBuffer compact();
boolean isDirect();
ShortBuffer mark();
@ -64,5 +56,7 @@ public interface ShortBuffer extends Buffer {
ShortBuffer limit(int newLimit);
ShortBuffer position(int newPosition);
short[] array();
}

View File

@ -1,5 +1,6 @@
package net.lax1dude.eaglercraft.v1_8.internal.vfs2;
import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator;
/**
@ -19,15 +20,17 @@ import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator;
*/
class VFSFilenameIteratorImpl implements VFSFilenameIterator {
protected IEaglerFilesystem fs;
protected VFSIterator2 itr;
VFSFilenameIteratorImpl(VFSIterator2 itr) {
VFSFilenameIteratorImpl(IEaglerFilesystem fs, VFSIterator2 itr) {
this.fs = fs;
this.itr = itr;
}
@Override
public void next(String entry) {
itr.next(new VFile2(entry));
itr.next(VFile2.create(fs, entry));
}
}

View File

@ -2,6 +2,7 @@ package net.lax1dude.eaglercraft.v1_8.internal.vfs2;
import java.util.List;
import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator;
/**
@ -21,15 +22,17 @@ import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator;
*/
class VFSListFilesIteratorImpl implements VFSFilenameIterator {
protected IEaglerFilesystem fs;
protected List<VFile2> list;
VFSListFilesIteratorImpl(List<VFile2> list) {
VFSListFilesIteratorImpl(IEaglerFilesystem fs, List<VFile2> list) {
this.fs = fs;
this.list = list;
}
@Override
public void next(String entry) {
list.add(new VFile2(entry));
list.add(VFile2.create(fs, entry));
}
}

View File

@ -5,9 +5,10 @@ import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import net.lax1dude.eaglercraft.v1_8.EagUtils;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem;
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;
@ -31,6 +32,12 @@ public class VFile2 {
public static final String pathSeperator = "/";
public static final String[] altPathSeperator = new String[] { "\\" };
static IEaglerFilesystem primaryFilesystem = null;
public static void setPrimaryFilesystem(IEaglerFilesystem fs) {
primaryFilesystem = fs;
}
public static String normalizePath(String p) {
for(int i = 0; i < altPathSeperator.length; ++i) {
p = p.replace(altPathSeperator[i], pathSeperator);
@ -53,9 +60,11 @@ public class VFile2 {
}
protected String path;
protected IEaglerFilesystem myFilesystem;
protected Supplier<IEaglerFilesystem> myFilesystemProvider;
public static String createPath(Object... p) {
ArrayList<String> r = new ArrayList();
ArrayList<String> r = new ArrayList<>();
for(int i = 0; i < p.length; ++i) {
if(p[i] == null) {
continue;
@ -94,13 +103,45 @@ public class VFile2 {
}
}
public VFile2(Object... p) {
this.path = createPath(p);
public static VFile2 create(IEaglerFilesystem fs, Object... path) {
return new VFile2(createPath(path), fs);
}
public static VFile2 create(Supplier<IEaglerFilesystem> fs, Object... path) {
return new VFile2(createPath(path), fs);
}
public VFile2(Object... path) {
this(createPath(path), primaryFilesystem);
}
private VFile2(String path, IEaglerFilesystem fs) {
this.path = path;
this.myFilesystem = fs;
}
private VFile2(String path, Supplier<IEaglerFilesystem> fs) {
this.path = path;
this.myFilesystemProvider = fs;
}
protected IEaglerFilesystem getFS() {
if(myFilesystem == null) {
if(myFilesystemProvider != null) {
myFilesystem = myFilesystemProvider.get();
}else {
myFilesystem = primaryFilesystem;
}
if(myFilesystem == null) {
throw new IllegalStateException("The filesystem has not been initialized yet!");
}
}
return myFilesystem;
}
public InputStream getInputStream() {
assertNotRelative();
return new VFileInputStream(PlatformFilesystem.eaglerRead(path));
return new VFileInputStream(getFS().eaglerRead(path));
}
public OutputStream getOutputStream() {
@ -121,7 +162,7 @@ public class VFile2 {
}
public boolean canRead() {
return !isRelative() && PlatformFilesystem.eaglerExists(path);
return !isRelative() && getFS().eaglerExists(path);
}
public String getPath() {
@ -160,15 +201,15 @@ public class VFile2 {
}
public boolean exists() {
return !isRelative() && PlatformFilesystem.eaglerExists(path);
return !isRelative() && getFS().eaglerExists(path);
}
public boolean delete() {
return !isRelative() && PlatformFilesystem.eaglerDelete(path);
return !isRelative() && getFS().eaglerDelete(path);
}
public boolean renameTo(String p) {
if(!isRelative() && PlatformFilesystem.eaglerMove(path, p)) {
if(!isRelative() && getFS().eaglerMove(path, p)) {
return true;
}
return false;
@ -179,7 +220,7 @@ public class VFile2 {
}
public int length() {
return isRelative() ? -1 : PlatformFilesystem.eaglerSize(path);
return isRelative() ? -1 : getFS().eaglerSize(path);
}
public byte[] getAllBytes() {
@ -187,7 +228,7 @@ public class VFile2 {
if(!exists()) {
return null;
}
ByteBuffer readBuffer = PlatformFilesystem.eaglerRead(path);
ByteBuffer readBuffer = getFS().eaglerRead(path);
byte[] copyBuffer = PlatformRuntime.castNativeByteBuffer(readBuffer);
if(copyBuffer != null) {
return copyBuffer;
@ -225,14 +266,14 @@ public class VFile2 {
assertNotRelative();
ByteBuffer copyBuffer = PlatformRuntime.castPrimitiveByteArray(bytes);
if(copyBuffer != null) {
PlatformFilesystem.eaglerWrite(path, copyBuffer);
getFS().eaglerWrite(path, copyBuffer);
return;
}
copyBuffer = PlatformRuntime.allocateByteBuffer(bytes.length);
try {
copyBuffer.put(bytes);
copyBuffer.flip();
PlatformFilesystem.eaglerWrite(path, copyBuffer);
getFS().eaglerWrite(path, copyBuffer);
}finally {
PlatformRuntime.freeByteBuffer(copyBuffer);
}
@ -240,24 +281,30 @@ public class VFile2 {
public void iterateFiles(VFSIterator2 itr, boolean recursive) {
assertNotRelative();
PlatformFilesystem.eaglerIterate(path, new VFSFilenameIteratorImpl(itr), recursive);
IEaglerFilesystem fs = getFS();
fs.eaglerIterate(path, new VFSFilenameIteratorImpl(fs, itr), recursive);
}
public List<String> listFilenames(boolean recursive) {
List<String> ret = new ArrayList();
PlatformFilesystem.eaglerIterate(path, new VFSListFilenamesIteratorImpl(ret), recursive);
List<String> ret = new ArrayList<>();
getFS().eaglerIterate(path, new VFSListFilenamesIteratorImpl(ret), recursive);
return ret;
}
public List<VFile2> listFiles(boolean recursive) {
List<VFile2> ret = new ArrayList();
PlatformFilesystem.eaglerIterate(path, new VFSListFilesIteratorImpl(ret), recursive);
List<VFile2> ret = new ArrayList<>();
IEaglerFilesystem fs = getFS();
fs.eaglerIterate(path, new VFSListFilesIteratorImpl(fs, ret), recursive);
return ret;
}
public static int copyFile(VFile2 src, VFile2 dst) {
src.assertNotRelative();
dst.assertNotRelative();
return PlatformFilesystem.eaglerCopy(src.path, dst.path);
IEaglerFilesystem sfs = src.getFS();
if(sfs != dst.getFS()) {
throw new UnsupportedOperationException("Cannot copy file between filesystems!");
}
return sfs.eaglerCopy(src.path, dst.path);
}
}

View File

@ -3,7 +3,6 @@ package net.lax1dude.eaglercraft.v1_8.internal.vfs2;
import java.io.IOException;
import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
@ -41,7 +40,7 @@ class VFileOutputStream extends EaglerOutputStream {
copyBuffer.put(buf, 0, count);
copyBuffer.flip();
try {
PlatformFilesystem.eaglerWrite(vfsFile.path, copyBuffer);
vfsFile.getFS().eaglerWrite(vfsFile.path, copyBuffer);
}catch(Throwable t) {
throw new IOException("Could not write stream contents to file!", t);
}

View File

@ -54,10 +54,10 @@ import net.minecraft.world.gen.ChunkProviderSettings;
*/
public class JSONTypeProvider {
private static final Map<Class<?>,JSONTypeSerializer<?,?>> serializers = new HashMap();
private static final Map<Class<?>,JSONTypeDeserializer<?,?>> deserializers = new HashMap();
private static final Map<Class<?>,JSONTypeSerializer<?,?>> serializers = new HashMap<>();
private static final Map<Class<?>,JSONTypeDeserializer<?,?>> deserializers = new HashMap<>();
private static final List<JSONDataParserImpl> parsers = new ArrayList();
private static final List<JSONDataParserImpl> parsers = new ArrayList<>();
public static <J> J serialize(Object object) throws JSONException {
JSONTypeSerializer<Object,J> ser = (JSONTypeSerializer<Object,J>) serializers.get(object.getClass());

View File

@ -30,7 +30,7 @@ public class SoundMapDeserializer implements JSONTypeDeserializer<JSONObject, So
@Override
public SoundMap deserialize(JSONObject json) throws JSONException {
Map<String, SoundList> soundsMap = new HashMap();
Map<String, SoundList> soundsMap = new HashMap<>();
for(String str : json.keySet()) {
soundsMap.put(str, JSONTypeProvider.deserialize(json.getJSONObject(str), SoundList.class));
}

View File

@ -20,7 +20,7 @@ import java.util.Map;
*/
public class LogManager {
private static final Map<String,Logger> loggerInstances = new HashMap();
private static final Map<String,Logger> loggerInstances = new HashMap<>();
public static final Object logLock = new Object();
public static Level logLevel = Level.DEBUG;

View File

@ -101,7 +101,7 @@ public class Logger {
log(Level.FATAL, msg);
}
private static final SimpleDateFormat fmt = EagRuntime.fixDateFormat(new SimpleDateFormat("hh:mm:ss+SSS"));
private static final SimpleDateFormat fmt = new SimpleDateFormat("hh:mm:ss+SSS");
private static final Date dateInstance = new Date();
public void log(Level level, String msg) {

Some files were not shown because too many files have changed in this diff Show More