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

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

View File

@ -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,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,21 @@ import java.io.IOException;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class IPacket06ClientFailure extends IPacket {
public String clientId;
public IPacket06ClientFailure() {
}
public IPacket06ClientFailure(String clientId) {
this.clientId = clientId;
}
public void read(DataInputStream input) throws IOException {
clientId = readASCII8(input);
public class EaglerMissingResourceException extends RuntimeException {
public EaglerMissingResourceException() {
}
public void write(DataOutputStream output) throws IOException {
writeASCII8(output, clientId);
public EaglerMissingResourceException(String message, Throwable cause) {
super(message, cause);
}
public int packetLength() {
return 1 + clientId.length();
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

@ -0,0 +1,34 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.io.InputStream;
/**
* 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 IWebSocketFrame {
boolean isString();
String getString();
byte[] getByteArray();
InputStream getInputStream();
int getLength();
long getTimestamp();
}

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) {

View File

@ -5,6 +5,7 @@ import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*;
import java.util.LinkedList;
import java.util.List;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU;
@ -35,7 +36,7 @@ public class ChunkUpdateManager {
private int chunkUpdatesQueuedLast = 0;
private long chunkUpdatesTotalLastUpdate = 0l;
private final List<ChunkCompileTaskGenerator> queue = new LinkedList();
private final List<ChunkCompileTaskGenerator> queue = new LinkedList<>();
public ChunkUpdateManager() {
worldVertexUploader = new WorldVertexBufferUploader();
@ -108,8 +109,8 @@ public class ChunkUpdateManager {
return false;
}else {
boolean flag = false;
long millis = System.currentTimeMillis();
List<ChunkCompileTaskGenerator> droppedUpdates = new LinkedList();
long millis = EagRuntime.steadyTimeMillis();
List<ChunkCompileTaskGenerator> droppedUpdates = new LinkedList<>();
while(!queue.isEmpty()) {
ChunkCompileTaskGenerator generator = queue.remove(0);
@ -125,7 +126,7 @@ public class ChunkUpdateManager {
++chunkUpdatesTotal;
if(timeout < System.nanoTime()) {
if(timeout < EagRuntime.nanoTime()) {
break;
}
}
@ -176,7 +177,7 @@ public class ChunkUpdateManager {
if (chunkcompiletaskgenerator == null) {
return true;
}
chunkcompiletaskgenerator.goddamnFuckingTimeout = System.currentTimeMillis();
chunkcompiletaskgenerator.goddamnFuckingTimeout = EagRuntime.steadyTimeMillis();
if(queue.size() < 100) {
chunkcompiletaskgenerator.addFinishRunnable(new Runnable() {
@Override
@ -219,7 +220,7 @@ public class ChunkUpdateManager {
}
public String getDebugInfo() {
long millis = System.currentTimeMillis();
long millis = EagRuntime.steadyTimeMillis();
if(millis - chunkUpdatesTotalLastUpdate > 500l) {
chunkUpdatesTotalLastUpdate = millis;

View File

@ -105,7 +105,7 @@ public class EaglerFolderResourcePack extends AbstractResourcePack {
}
try {
JSONArray json = (new JSONObject(str)).getJSONArray("resourcePacks");
List<EaglerFolderResourcePack> ret = new ArrayList(json.length());
List<EaglerFolderResourcePack> ret = new ArrayList<>(json.length());
for(int i = 0, l = json.length(); i < l; ++i) {
JSONObject jp = json.getJSONObject(i);
String folderName = jp.getString("folder");

View File

@ -30,6 +30,15 @@ public class EaglerFontRenderer extends FontRenderer {
private final int[] temporaryCodepointArray = new int[6553];
public static FontRenderer createSupportedFontRenderer(GameSettings gameSettingsIn, ResourceLocation location,
TextureManager textureManagerIn, boolean unicode) {
if(EaglercraftGPU.checkInstancingCapable()) {
return new EaglerFontRenderer(gameSettingsIn, location, textureManagerIn, unicode);
}else {
return new FontRenderer(gameSettingsIn, location, textureManagerIn, unicode);
}
}
public EaglerFontRenderer(GameSettings gameSettingsIn, ResourceLocation location, TextureManager textureManagerIn,
boolean unicode) {
super(gameSettingsIn, location, textureManagerIn, unicode);
@ -116,15 +125,16 @@ public class EaglerFontRenderer extends FontRenderer {
++i;
} else {
int j = temporaryCodepointArray[i];
if(j > 255) continue;
if (this.randomStyle && j != -1) {
int k = this.getCharWidth(c0);
String chars = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000";
char[] chars = FontRenderer.codepointLookup;
char c1;
while (true) {
j = this.fontRandom.nextInt(chars.length());
c1 = chars.charAt(j);
j = this.fontRandom.nextInt(chars.length);
c1 = chars[j];
if (k == this.getCharWidth(c1)) {
break;
}
@ -220,8 +230,7 @@ public class EaglerFontRenderer extends FontRenderer {
private boolean decodeASCIICodepointsAndValidate(String str) {
for(int i = 0, l = str.length(); i < l; ++i) {
int j = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000\u00a7"
.indexOf(str.charAt(i));
int j = FontMappingHelper.lookupChar(str.charAt(i), true);
if(j != -1) {
temporaryCodepointArray[i] = j;
}else {

View File

@ -1,7 +1,6 @@
package net.lax1dude.eaglercraft.v1_8.minecraft;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
@ -229,10 +228,10 @@ public class EaglerTextureAtlasSprite {
int l = i;
this.height = this.width;
if (meta.getFrameCount() > 0) {
Iterator iterator = meta.getFrameIndexSet().iterator();
Iterator<Integer> iterator = meta.getFrameIndexSet().iterator();
while (iterator.hasNext()) {
int i1 = ((Integer) iterator.next()).intValue();
int i1 = iterator.next().intValue();
if (i1 >= j1) {
throw new RuntimeException("invalid frameindex " + i1);
}
@ -243,7 +242,7 @@ public class EaglerTextureAtlasSprite {
this.animationMetadata = meta;
} else {
ArrayList arraylist = Lists.newArrayList();
List<AnimationFrame> arraylist = Lists.newArrayList();
for (int l1 = 0; l1 < j1; ++l1) {
this.framesTextureData.add(getFrameTextureData(aint, k1, l, l1));
@ -258,10 +257,10 @@ public class EaglerTextureAtlasSprite {
}
public void generateMipmaps(int level) {
ArrayList arraylist = Lists.newArrayList();
List<int[][]> arraylist = Lists.newArrayList();
for (int i = 0; i < this.framesTextureData.size(); ++i) {
final int[][] aint = (int[][]) this.framesTextureData.get(i);
final int[][] aint = this.framesTextureData.get(i);
if (aint != null) {
try {
arraylist.add(TextureUtil.generateMipmapData(level, this.width, aint));

View File

@ -0,0 +1,20 @@
package net.lax1dude.eaglercraft.v1_8.minecraft;
/**
* 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 EnumInputEvent {
CLIPBOARD_COPY, CLIPBOARD_PASTE;
}

View File

@ -0,0 +1,525 @@
package net.lax1dude.eaglercraft.v1_8.minecraft;
/**
* 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 FontMappingHelper {
public static int lookupChar(char c, boolean incSel) {
switch(c) {
case 167:
return incSel ? 256 : -1;
case 192:
return 0;
case 193:
return 1;
case 194:
return 2;
case 200:
return 3;
case 202:
return 4;
case 203:
return 5;
case 205:
return 6;
case 211:
return 7;
case 212:
return 8;
case 213:
return 9;
case 218:
return 10;
case 223:
return 11;
case 227:
return 12;
case 245:
return 13;
case 287:
return 14;
case 304:
return 15;
case 305:
return 16;
case 338:
return 17;
case 339:
return 18;
case 350:
return 19;
case 351:
return 20;
case 372:
return 21;
case 373:
return 22;
case 382:
return 23;
case 519:
return 24;
case 0:
return 25;
case 32:
return 32;
case 33:
return 33;
case 34:
return 34;
case 35:
return 35;
case 36:
return 36;
case 37:
return 37;
case 38:
return 38;
case 39:
return 39;
case 40:
return 40;
case 41:
return 41;
case 42:
return 42;
case 43:
return 43;
case 44:
return 44;
case 45:
return 45;
case 46:
return 46;
case 47:
return 47;
case 48:
return 48;
case 49:
return 49;
case 50:
return 50;
case 51:
return 51;
case 52:
return 52;
case 53:
return 53;
case 54:
return 54;
case 55:
return 55;
case 56:
return 56;
case 57:
return 57;
case 58:
return 58;
case 59:
return 59;
case 60:
return 60;
case 61:
return 61;
case 62:
return 62;
case 63:
return 63;
case 64:
return 64;
case 65:
return 65;
case 66:
return 66;
case 67:
return 67;
case 68:
return 68;
case 69:
return 69;
case 70:
return 70;
case 71:
return 71;
case 72:
return 72;
case 73:
return 73;
case 74:
return 74;
case 75:
return 75;
case 76:
return 76;
case 77:
return 77;
case 78:
return 78;
case 79:
return 79;
case 80:
return 80;
case 81:
return 81;
case 82:
return 82;
case 83:
return 83;
case 84:
return 84;
case 85:
return 85;
case 86:
return 86;
case 87:
return 87;
case 88:
return 88;
case 89:
return 89;
case 90:
return 90;
case 91:
return 91;
case 92:
return 92;
case 93:
return 93;
case 94:
return 94;
case 95:
return 95;
case 96:
return 96;
case 97:
return 97;
case 98:
return 98;
case 99:
return 99;
case 100:
return 100;
case 101:
return 101;
case 102:
return 102;
case 103:
return 103;
case 104:
return 104;
case 105:
return 105;
case 106:
return 106;
case 107:
return 107;
case 108:
return 108;
case 109:
return 109;
case 110:
return 110;
case 111:
return 111;
case 112:
return 112;
case 113:
return 113;
case 114:
return 114;
case 115:
return 115;
case 116:
return 116;
case 117:
return 117;
case 118:
return 118;
case 119:
return 119;
case 120:
return 120;
case 121:
return 121;
case 122:
return 122;
case 123:
return 123;
case 124:
return 124;
case 125:
return 125;
case 126:
return 126;
case 199:
return 128;
case 252:
return 129;
case 233:
return 130;
case 226:
return 131;
case 228:
return 132;
case 224:
return 133;
case 229:
return 134;
case 231:
return 135;
case 234:
return 136;
case 235:
return 137;
case 232:
return 138;
case 239:
return 139;
case 238:
return 140;
case 236:
return 141;
case 196:
return 142;
case 197:
return 143;
case 201:
return 144;
case 230:
return 145;
case 198:
return 146;
case 244:
return 147;
case 246:
return 148;
case 242:
return 149;
case 251:
return 150;
case 249:
return 151;
case 255:
return 152;
case 214:
return 153;
case 220:
return 154;
case 248:
return 155;
case 163:
return 156;
case 216:
return 157;
case 215:
return 158;
case 402:
return 159;
case 225:
return 160;
case 237:
return 161;
case 243:
return 162;
case 250:
return 163;
case 241:
return 164;
case 209:
return 165;
case 170:
return 166;
case 186:
return 167;
case 191:
return 168;
case 174:
return 169;
case 172:
return 170;
case 189:
return 171;
case 188:
return 172;
case 161:
return 173;
case 171:
return 174;
case 187:
return 175;
case 9617:
return 176;
case 9618:
return 177;
case 9619:
return 178;
case 9474:
return 179;
case 9508:
return 180;
case 9569:
return 181;
case 9570:
return 182;
case 9558:
return 183;
case 9557:
return 184;
case 9571:
return 185;
case 9553:
return 186;
case 9559:
return 187;
case 9565:
return 188;
case 9564:
return 189;
case 9563:
return 190;
case 9488:
return 191;
case 9492:
return 192;
case 9524:
return 193;
case 9516:
return 194;
case 9500:
return 195;
case 9472:
return 196;
case 9532:
return 197;
case 9566:
return 198;
case 9567:
return 199;
case 9562:
return 200;
case 9556:
return 201;
case 9577:
return 202;
case 9574:
return 203;
case 9568:
return 204;
case 9552:
return 205;
case 9580:
return 206;
case 9575:
return 207;
case 9576:
return 208;
case 9572:
return 209;
case 9573:
return 210;
case 9561:
return 211;
case 9560:
return 212;
case 9554:
return 213;
case 9555:
return 214;
case 9579:
return 215;
case 9578:
return 216;
case 9496:
return 217;
case 9484:
return 218;
case 9608:
return 219;
case 9604:
return 220;
case 9612:
return 221;
case 9616:
return 222;
case 9600:
return 223;
case 945:
return 224;
case 946:
return 225;
case 915:
return 226;
case 960:
return 227;
case 931:
return 228;
case 963:
return 229;
case 956:
return 230;
case 964:
return 231;
case 934:
return 232;
case 920:
return 233;
case 937:
return 234;
case 948:
return 235;
case 8734:
return 236;
case 8709:
return 237;
case 8712:
return 238;
case 8745:
return 239;
case 8801:
return 240;
case 177:
return 241;
case 8805:
return 242;
case 8804:
return 243;
case 8992:
return 244;
case 8993:
return 245;
case 247:
return 246;
case 8776:
return 247;
case 176:
return 248;
case 8729:
return 249;
case 183:
return 250;
case 8730:
return 251;
case 8319:
return 252;
case 178:
return 253;
case 9632:
return 254;
default:
return -1;
}
}
}

View File

@ -0,0 +1,132 @@
package net.lax1dude.eaglercraft.v1_8.minecraft;
import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*;
import net.lax1dude.eaglercraft.v1_8.Mouse;
import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType;
import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
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 GuiButtonWithStupidIcons extends GuiButton {
protected ResourceLocation leftIcon;
protected float leftIconAspect;
protected ResourceLocation rightIcon;
protected float rightIconAspect;
public GuiButtonWithStupidIcons(int buttonId, int x, int y, int widthIn, int heightIn, String buttonText) {
super(buttonId, x, y, widthIn, heightIn, buttonText);
}
public GuiButtonWithStupidIcons(int buttonId, int x, int y, String buttonText) {
super(buttonId, x, y, buttonText);
}
public GuiButtonWithStupidIcons(int buttonId, int x, int y, int widthIn, int heightIn, String buttonText,
ResourceLocation leftIcon, float leftIconAspect, ResourceLocation rightIcon, float rightIconAspect) {
super(buttonId, x, y, widthIn, heightIn, buttonText);
this.leftIcon = leftIcon;
this.leftIconAspect = leftIconAspect;
this.rightIcon = rightIcon;
this.rightIconAspect = rightIconAspect;
}
public GuiButtonWithStupidIcons(int buttonId, int x, int y, String buttonText, ResourceLocation leftIcon,
float leftIconAspect, ResourceLocation rightIcon, float rightIconAspect) {
super(buttonId, x, y, buttonText);
this.leftIcon = leftIcon;
this.leftIconAspect = leftIconAspect;
this.rightIcon = rightIcon;
this.rightIconAspect = rightIconAspect;
}
public ResourceLocation getLeftIcon() {
return leftIcon;
}
public ResourceLocation getRightIcon() {
return rightIcon;
}
public void setLeftIcon(ResourceLocation leftIcon, float aspectRatio) {
this.leftIcon = leftIcon;
this.leftIconAspect = aspectRatio;
}
public void setRightIcon(ResourceLocation rightIcon, float aspectRatio) {
this.rightIcon = rightIcon;
this.rightIconAspect = aspectRatio;
}
public void drawButton(Minecraft mc, int mouseX, int mouseY) {
if (this.visible) {
FontRenderer fontrenderer = mc.fontRendererObj;
mc.getTextureManager().bindTexture(buttonTextures);
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
this.hovered = mouseX >= this.xPosition && mouseY >= this.yPosition && mouseX < this.xPosition + this.width
&& mouseY < this.yPosition + this.height;
if (this.enabled && this.hovered) {
Mouse.showCursor(EnumCursorType.HAND);
}
int i = this.getHoverState(this.hovered);
GlStateManager.enableBlend();
GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0);
GlStateManager.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
this.drawTexturedModalRect(this.xPosition, this.yPosition, 0, 46 + i * 20, this.width / 2, this.height);
this.drawTexturedModalRect(this.xPosition + this.width / 2, this.yPosition, 200 - this.width / 2,
46 + i * 20, this.width / 2, this.height);
this.mouseDragged(mc, mouseX, mouseY);
int j = 14737632;
if (!this.enabled) {
j = 10526880;
} else if (this.hovered) {
j = 16777120;
}
int strWidth = fontrenderer.getStringWidth(displayString);
int strWidthAdj = strWidth - (leftIcon != null ? (int) (16 * leftIconAspect) : 0)
+ (rightIcon != null ? (int) (16 * rightIconAspect) : 0);
this.drawString(fontrenderer, this.displayString, this.xPosition + (this.width - strWidthAdj) / 2,
this.yPosition + (this.height - 8) / 2, j);
if(leftIcon != null) {
GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
mc.getTextureManager().bindTexture(leftIcon);
GlStateManager.pushMatrix();
GlStateManager.translate(this.xPosition + (this.width - strWidthAdj) / 2 - 3 - 16 * leftIconAspect, this.yPosition + 2, 0.0f);
float f = 16.0f / 256.0f;
GlStateManager.scale(f * leftIconAspect, f, f);
this.drawTexturedModalRect(0, 0, 0, 0, 256, 256);
GlStateManager.popMatrix();
}
if(rightIcon != null) {
GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
mc.getTextureManager().bindTexture(rightIcon);
GlStateManager.pushMatrix();
GlStateManager.translate(this.xPosition + (this.width - strWidthAdj) / 2 + strWidth + 3, this.yPosition + 2, 0.0f);
float f = 16.0f / 256.0f;
GlStateManager.scale(f * rightIconAspect, f, f);
this.drawTexturedModalRect(0, 0, 0, 0, 256, 256);
GlStateManager.popMatrix();
}
}
}
}

View File

@ -1,5 +1,7 @@
package net.lax1dude.eaglercraft.v1_8.minecraft;
import org.apache.commons.lang3.StringUtils;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
@ -26,8 +28,8 @@ public class GuiScreenGenericErrorMessage extends GuiScreen {
private GuiScreen cont;
public GuiScreenGenericErrorMessage(String str1, String str2, GuiScreen cont) {
this.str1 = I18n.format(str1);
this.str2 = I18n.format(str2);
this.str1 = StringUtils.isAllEmpty(str1) ? "" : I18n.format(str1);
this.str2 = StringUtils.isAllEmpty(str2) ? "" : I18n.format(str2);
this.cont = cont;
}

View File

@ -0,0 +1,144 @@
package net.lax1dude.eaglercraft.v1_8.minecraft;
import net.lax1dude.eaglercraft.v1_8.Display;
import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
/**
* 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 GuiScreenVisualViewport extends GuiScreen {
protected int offsetX;
protected int offsetY;
@Override
public final void setWorldAndResolution(Minecraft mc, int width, int height) {
Display.wasVisualViewportResized(); // clear state
offsetX = Display.getVisualViewportX() * width / mc.displayWidth;
offsetY = Display.getVisualViewportY() * height / mc.displayHeight;
setWorldAndResolution0(mc, Display.getVisualViewportW() * width / mc.displayWidth,
Display.getVisualViewportH() * height / mc.displayHeight);
}
protected void setWorldAndResolution0(Minecraft mc, int width, int height) {
super.setWorldAndResolution(mc, width, height);
}
@Override
public final void updateScreen() {
if(Display.wasVisualViewportResized()) {
setWorldAndResolution(mc, mc.scaledResolution.getScaledWidth(), mc.scaledResolution.getScaledHeight());
}
updateScreen0();
}
protected void updateScreen0() {
super.updateScreen();
}
@Override
public final void drawScreen(int i, int j, float var3) {
i -= offsetX;
j -= offsetY;
GlStateManager.pushMatrix();
GlStateManager.translate(offsetX, offsetY, 0.0f);
drawScreen0(i, j, var3);
GlStateManager.popMatrix();
}
protected void drawScreen0(int i, int j, float var3) {
super.drawScreen(i, j, var3);
}
@Override
protected final void mouseClicked(int parInt1, int parInt2, int parInt3) {
parInt1 -= offsetX;
parInt2 -= offsetY;
mouseClicked0(parInt1, parInt2, parInt3);
}
protected void mouseClicked0(int parInt1, int parInt2, int parInt3) {
super.mouseClicked(parInt1, parInt2, parInt3);
}
@Override
protected final void mouseReleased(int i, int j, int k) {
i -= offsetX;
j -= offsetY;
mouseReleased0(i, j, k);
}
protected void mouseReleased0(int i, int j, int k) {
super.mouseReleased(i, j, k);
}
@Override
protected final void mouseClickMove(int var1, int var2, int var3, long var4) {
var1 -= offsetX;
var2 -= offsetY;
mouseClickMove0(var1, var2, var3, var4);
}
protected void mouseClickMove0(int var1, int var2, int var3, long var4) {
super.mouseClickMove(var1, var2, var3, var4);
}
@Override
protected final void touchEndMove(int parInt1, int parInt2, int parInt3) {
parInt1 -= offsetX;
parInt2 -= offsetY;
touchEndMove0(parInt1, parInt2, parInt3);
}
protected void touchEndMove0(int parInt1, int parInt2, int parInt3) {
super.touchEndMove(parInt1, parInt2, parInt3);
}
@Override
protected final void touchMoved(int parInt1, int parInt2, int parInt3) {
parInt1 -= offsetX;
parInt2 -= offsetY;
touchMoved0(parInt1, parInt2, parInt3);
}
protected void touchMoved0(int parInt1, int parInt2, int parInt3) {
super.touchMoved(parInt1, parInt2, parInt3);
}
@Override
protected final void touchStarted(int parInt1, int parInt2, int parInt3) {
parInt1 -= offsetX;
parInt2 -= offsetY;
touchStarted0(parInt1, parInt2, parInt3);
}
protected void touchStarted0(int parInt1, int parInt2, int parInt3) {
super.touchStarted(parInt1, parInt2, parInt3);
}
@Override
protected void touchTapped(int parInt1, int parInt2, int parInt3) {
parInt1 -= offsetX;
parInt2 -= offsetY;
touchTapped0(parInt1, parInt2, parInt3);
}
protected void touchTapped0(int parInt1, int parInt2, int parInt3) {
super.touchTapped(parInt1, parInt2, parInt3);
}
}

View File

@ -54,8 +54,8 @@ public class TextureAnimationCache {
for(int i = 0; i < cacheTextures.length; ++i) {
cacheTextures[i] = GlStateManager.generateTexture();
GlStateManager.bindTexture(cacheTextures[i]);
EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
@ -123,6 +123,8 @@ public class TextureAnimationCache {
if(cacheTextures == null) {
throw new IllegalStateException("Cannot copy from uninitialized TextureAnimationCache");
}
GlStateManager.disableBlend();
GlStateManager.disableAlpha();
GlStateManager.bindTexture(cacheTextures[level]);
TextureCopyUtil.srcSize(width >> level, (height >> level) * frameCount);
TextureCopyUtil.blitTextureUsingViewports(0, h * animationFrame, dx, dy, w, h);

View File

@ -0,0 +1,46 @@
package net.lax1dude.eaglercraft.v1_8.notifications;
import java.util.List;
import net.minecraft.util.IChatComponent;
/**
* 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 CachedNotifBadgeTexture {
public final int glTexture;
public final int scaleFactor;
public final int width;
public final int height;
public final List<ClickEventZone> cursorEvents;
public final IChatComponent rootClickEvent;
public final boolean hasClickEvents;
public final boolean hasHoverEvents;
protected CachedNotifBadgeTexture(int glTexture, int scaleFactor, int width, int height,
List<ClickEventZone> cursorEvents, IChatComponent rootClickEvent, boolean hasClickEvents,
boolean hasHoverEvents) {
this.glTexture = glTexture;
this.scaleFactor = scaleFactor;
this.width = width;
this.height = height;
this.cursorEvents = cursorEvents;
this.rootClickEvent = rootClickEvent;
this.hasClickEvents = hasClickEvents;
this.hasHoverEvents = hasHoverEvents;
}
}

View File

@ -0,0 +1,41 @@
package net.lax1dude.eaglercraft.v1_8.notifications;
import net.minecraft.util.IChatComponent;
/**
* 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 ClickEventZone {
public final int posX;
public final int posY;
public final int width;
public final int height;
public final IChatComponent chatComponent;
public final boolean hasHoverEvent;
public final boolean hasClickEvent;
public ClickEventZone(int posX, int posY, int width, int height, IChatComponent chatComponent,
boolean hasHoverEvent, boolean hasClickEvent) {
this.posX = posX;
this.posY = posY;
this.width = width;
this.height = height;
this.chatComponent = chatComponent;
this.hasHoverEvent = hasHoverEvent;
this.hasClickEvent = hasClickEvent;
}
}

View File

@ -0,0 +1,69 @@
package net.lax1dude.eaglercraft.v1_8.notifications;
import net.lax1dude.eaglercraft.v1_8.Mouse;
import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType;
import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
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 GuiButtonNotifBell extends GuiButton {
private static final ResourceLocation eaglerTextures = new ResourceLocation("eagler:gui/eagler_gui.png");
private int unread = 0;
public GuiButtonNotifBell(int buttonID, int xPos, int yPos) {
super(buttonID, xPos, yPos, 20, 20, "");
}
public void setUnread(int num) {
unread = num;
}
public void drawButton(Minecraft minecraft, int i, int j) {
if (this.visible) {
minecraft.getTextureManager().bindTexture(eaglerTextures);
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
boolean flag = i >= this.xPosition && j >= this.yPosition && i < this.xPosition + this.width
&& j < this.yPosition + this.height;
int k = 0;
int c = 14737632;
if (flag) {
k += this.height;
c = 16777120;
Mouse.showCursor(EnumCursorType.HAND);
}
drawTexturedModalRect(xPosition, yPosition, unread > 0 ? 116 : 136, k, width, height);
if(unread > 0) {
GlStateManager.pushMatrix();
GlStateManager.translate(xPosition + 15.5f, yPosition + 11.0f, 0.0f);
if(unread >= 10) {
GlStateManager.translate(0.0f, 1.0f, 0.0f);
GlStateManager.scale(0.5f, 0.5f, 0.5f);
}else {
GlStateManager.scale(0.75f, 0.75f, 0.75f);
}
drawCenteredString(minecraft.fontRendererObj, Integer.toString(unread), 0, 0, c);
GlStateManager.popMatrix();
}
}
}
}

View File

@ -0,0 +1,172 @@
package net.lax1dude.eaglercraft.v1_8.notifications;
import java.io.IOException;
import java.util.List;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeShowV4EAG.EnumBadgePriority;
import net.minecraft.client.Minecraft;
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 GuiScreenNotifications extends GuiScreen {
private static final String[] priorityLangKeys = new String[] {
"notifications.priority.low",
"notifications.priority.normal",
"notifications.priority.higher",
"notifications.priority.highest"
};
private static final int[] priorityOrder = new int[] {
0, 3, 2, 1
};
GuiScreen parent;
int selected;
GuiSlotNotifications slots;
GuiButton clearAllButton;
GuiButton priorityButton;
int showPriority = 0;
EnumBadgePriority selectedMaxPriority = EnumBadgePriority.LOW;
int lastUpdate = -1;
public GuiScreenNotifications(GuiScreen parent) {
this.parent = parent;
}
public void initGui() {
selected = -1;
buttonList.clear();
buttonList.add(new GuiButton(0, this.width / 2 + 54, this.height - 32, 100, 20, I18n.format("gui.done")));
buttonList.add(clearAllButton = new GuiButton(1, this.width / 2 - 154, this.height - 32, 100, 20,
I18n.format("notifications.clearAll")));
int i = priorityOrder[showPriority];
buttonList.add(priorityButton = new GuiButton(2, this.width / 2 - 50, this.height - 32, 100, 20,
I18n.format("notifications.priority", I18n.format(priorityLangKeys[i]))));
selectedMaxPriority = EnumBadgePriority.getByID(i);
slots = new GuiSlotNotifications(this);
lastUpdate = -69420;
updateList();
updateButtons();
}
void updateButtons() {
clearAllButton.enabled = !slots.currentDisplayNotifs.isEmpty();
}
void updateList() {
if(mc.thePlayer == null) return;
ServerNotificationManager mgr = mc.thePlayer.sendQueue.getNotifManager();
int verHash = showPriority | (mgr.getNotifListUpdateCount() << 2);
if(verHash != lastUpdate) {
lastUpdate = verHash;
EaglercraftUUID selectedUUID = null;
List<GuiSlotNotifications.NotifBadgeSlot> lst = slots.currentDisplayNotifs;
int oldSelectedId = selected;
if(oldSelectedId >= 0 && oldSelectedId < lst.size()) {
selectedUUID = lst.get(oldSelectedId).badge.badgeUUID;
}
lst.clear();
lst.addAll(Collections2.transform(Collections2.filter(mgr.getNotifLongHistory(), new Predicate<NotificationBadge>() {
@Override
public boolean apply(NotificationBadge input) {
return input.priority.priority >= priorityOrder[showPriority];
}
}), GuiSlotNotifications.NotifBadgeSlot::new));
selected = -1;
if(selectedUUID != null) {
for(int i = 0, l = lst.size(); i < l; ++i) {
if(selectedUUID.equals(lst.get(i).badge.badgeUUID)) {
selected = i;
break;
}
}
}
if(selected != -1) {
if(oldSelectedId != selected) {
slots.scrollBy((selected - oldSelectedId) * slots.getSlotHeight());
}
}
updateButtons();
}
}
public void updateScreen() {
if(mc.thePlayer == null) {
mc.displayGuiScreen(parent);
return;
}
updateList();
}
static Minecraft getMinecraft(GuiScreenNotifications screen) {
return screen.mc;
}
public void actionPerformed(GuiButton btn) {
switch(btn.id) {
case 0:
mc.displayGuiScreen(parent);
break;
case 1:
if(mc.thePlayer != null) {
ServerNotificationManager mgr = mc.thePlayer.sendQueue.getNotifManager();
mgr.removeAllNotifFromActiveList(mgr.getNotifLongHistory());
clearAllButton.enabled = false;
}
break;
case 2:
showPriority = (showPriority + 1) & 3;
int i = priorityOrder[showPriority];
priorityButton.displayString = I18n.format("notifications.priority", I18n.format(priorityLangKeys[i]));
selectedMaxPriority = EnumBadgePriority.getByID(i);
updateList();
break;
default:
break;
}
}
public void drawScreen(int par1, int par2, float par3) {
if(mc.thePlayer == null) return;
slots.drawScreen(par1, par2, par3);
this.drawCenteredString(fontRendererObj, I18n.format("notifications.title"), this.width / 2, 16, 16777215);
super.drawScreen(par1, par2, par3);
}
public void handleMouseInput() throws IOException {
super.handleMouseInput();
slots.handleMouseInput();
}
public void handleTouchInput() throws IOException {
super.handleTouchInput();
slots.handleTouchInput();
}
public void onGuiClosed() {
if(mc.thePlayer != null) {
mc.thePlayer.sendQueue.getNotifManager().commitUnreadFlag();
}
}
}

View File

@ -0,0 +1,338 @@
package net.lax1dude.eaglercraft.v1_8.notifications;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeShowV4EAG.EnumBadgePriority;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.client.gui.GuiSlot;
import net.minecraft.client.gui.GuiUtilRenderComponents;
import net.minecraft.event.ClickEvent;
import net.minecraft.event.HoverEvent;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.MathHelper;
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 GuiSlotNotifications extends GuiSlot {
private static final ResourceLocation eaglerGui = new ResourceLocation("eagler:gui/eagler_gui.png");
private static final ResourceLocation largeNotifBk = new ResourceLocation("eagler:gui/notif_bk_large.png");
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("hh:mm a");
final GuiScreenNotifications parent;
final List<NotifBadgeSlot> currentDisplayNotifs;
int mouseX;
int mouseY;
protected static class NotifBadgeSlot {
protected final NotificationBadge badge;
protected final List<ClickEventZone> cursorEvents = new ArrayList<>();
protected int currentScreenX = -69420;
protected int currentScreenY = -69420;
protected NotifBadgeSlot(NotificationBadge badge) {
this.badge = badge;
}
}
public GuiSlotNotifications(GuiScreenNotifications parent) {
super(GuiScreenNotifications.getMinecraft(parent), parent.width, parent.height, 32, parent.height - 44, 68);
this.parent = parent;
this.currentDisplayNotifs = new ArrayList<>();
}
@Override
protected int getSize() {
return currentDisplayNotifs.size();
}
@Override
protected void elementClicked(int id, boolean doubleClk, int xx, int yy) {
if(selectedElement != id) return; //workaround for vanilla bs
if(id < currentDisplayNotifs.size()) {
NotifBadgeSlot slot = currentDisplayNotifs.get(id);
if(slot.currentScreenY != -69420) {
int w = getListWidth();
int localX = xx - slot.currentScreenX;
int localY = yy - slot.currentScreenY;
if(localX >= w - 22 && localX < w - 5 && localY >= 5 && localY < 21) {
slot.badge.removeNotif();
mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F));
return;
}
IChatComponent cmp = slot.badge.bodyComponent;
if(cmp != null) {
if(doubleClk) {
if (cmp.getChatStyle().getChatClickEvent() != null
&& cmp.getChatStyle().getChatClickEvent().getAction().shouldAllowInChat()) {
if(parent.handleComponentClick(cmp)) {
mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F));
return;
}
}
}else {
if(parent.selected != id) {
parent.selected = id;
}else {
List<ClickEventZone> cursorEvents = slot.cursorEvents;
if(cursorEvents != null && !cursorEvents.isEmpty()) {
for(int j = 0, m = cursorEvents.size(); j < m; ++j) {
ClickEventZone evt = cursorEvents.get(j);
if(evt.hasClickEvent) {
int offsetPosX = slot.currentScreenX + evt.posX;
int offsetPosY = slot.currentScreenY + evt.posY;
if(xx >= offsetPosX && yy >= offsetPosY && xx < offsetPosX + evt.width && yy < offsetPosY + evt.height) {
if(parent.handleComponentClick(evt.chatComponent)) {
mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F));
return;
}
}
}
}
}
}
}
}
}
}
}
@Override
protected boolean isSelected(int var1) {
return var1 == parent.selected;
}
@Override
protected void drawBackground() {
parent.drawBackground(0);
}
@Override
protected void drawSlot(int id, int xx, int yy, int width, int height, int ii) {
if(id < currentDisplayNotifs.size()) {
NotifBadgeSlot slot = currentDisplayNotifs.get(id);
slot.currentScreenX = xx;
slot.currentScreenY = yy;
NotificationBadge bd = slot.badge;
if(yy + 32 > this.top && yy + 32 < this.bottom) {
bd.markRead();
}
GlStateManager.pushMatrix();
GlStateManager.translate(xx, yy, 0.0f);
mc.getTextureManager().bindTexture(largeNotifBk);
int badgeWidth = getListWidth() - 4;
int badgeHeight = getSlotHeight() - 4;
float r = ((bd.backgroundColor >> 16) & 0xFF) * 0.00392156f;
float g = ((bd.backgroundColor >> 8) & 0xFF) * 0.00392156f;
float b = (bd.backgroundColor & 0xFF) * 0.00392156f;
if(parent.selected != id) {
r *= 0.85f;
g *= 0.85f;
b *= 0.85f;
}
GlStateManager.color(r, g, b, 1.0f);
parent.drawTexturedModalRect(0, 0, 0, bd.unreadFlagRender ? 64 : 0, badgeWidth - 32, 64);
parent.drawTexturedModalRect(badgeWidth - 32, 0, 224, bd.unreadFlagRender ? 64 : 0, 32, 64);
mc.getTextureManager().bindTexture(eaglerGui);
if(bd.priority == EnumBadgePriority.LOW) {
parent.drawTexturedModalRect(badgeWidth - 21, badgeHeight - 21, 192, 176, 16, 16);
}
GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
switch(bd.priority) {
default:
break;
case NORMAL:
parent.drawTexturedModalRect(badgeWidth - 21, badgeHeight - 21, 208, 176, 16, 16);
break;
case HIGHER:
parent.drawTexturedModalRect(badgeWidth - 21, badgeHeight - 21, 224, 176, 16, 16);
break;
case HIGHEST:
parent.drawTexturedModalRect(badgeWidth - 21, badgeHeight - 21, 240, 176, 16, 16);
break;
}
int bodyYOffset = 16;
int leftPadding = 6;
int rightPadding = 26;
int mainIconSW = 32;
boolean mainIconEn = bd.mainIcon != null && bd.mainIcon.isValid();
if(mainIconEn) {
int iw = bd.mainIcon.texture.getWidth();
int ih = bd.mainIcon.texture.getHeight();
float iaspect = (float)iw / (float)ih;
mainIconSW = (int)(32 * iaspect);
leftPadding += Math.min(mainIconSW, 64) + 3;
}
int textZoneWidth = badgeWidth - leftPadding - rightPadding;
if(mainIconEn) {
mc.getTextureManager().bindTexture(bd.mainIcon.resource);
ServerNotificationRenderer.drawTexturedRect(6, bodyYOffset, mainIconSW, 32);
}
boolean titleIconEn = bd.titleIcon != null && bd.titleIcon.isValid();
if(titleIconEn) {
mc.getTextureManager().bindTexture(bd.titleIcon.resource);
ServerNotificationRenderer.drawTexturedRect(6, 5, 8, 8);
}
String titleText = "";
IChatComponent titleComponent = bd.getTitleProfanityFilter();
if(titleComponent != null) {
titleText = titleComponent.getFormattedText();
}
titleText += EnumChatFormatting.GRAY + (titleText.length() > 0 ? " @ " : "@ ")
+ (bd.unreadFlagRender ? EnumChatFormatting.YELLOW : EnumChatFormatting.GRAY)
+ formatAge(bd.serverTimestamp);
GlStateManager.pushMatrix();
GlStateManager.translate(6 + (titleIconEn ? 10 : 0), 6, 0.0f);
GlStateManager.scale(0.75f, 0.75f, 0.75f);
mc.fontRendererObj.drawStringWithShadow(titleText, 0, 0, bd.titleTxtColor);
GlStateManager.popMatrix();
String sourceText = null;
IChatComponent sourceComponent = bd.getSourceProfanityFilter();
if(sourceComponent != null) {
sourceText = sourceComponent.getFormattedText();
if(sourceText.length() == 0) {
sourceText = null;
}
}
List<IChatComponent> bodyLines = null;
float bodyFontSize = (sourceText != null || titleIconEn) ? 0.75f : 1.0f;
IChatComponent bodyComponent = bd.getBodyProfanityFilter();
if(bodyComponent != null) {
bodyLines = GuiUtilRenderComponents.func_178908_a(bodyComponent, (int) (textZoneWidth / bodyFontSize),
mc.fontRendererObj, true, true);
int maxHeight = badgeHeight - (sourceText != null ? 32 : 22);
int maxLines = MathHelper.floor_float(maxHeight / (9 * bodyFontSize));
if(bodyLines.size() > maxLines) {
bodyLines = bodyLines.subList(0, maxLines);
IChatComponent cmp = bodyLines.get(maxLines - 1);
List<IChatComponent> siblings = cmp.getSiblings();
IChatComponent dots = new ChatComponentText("...");
if(siblings != null && siblings.size() > 0) {
dots.setChatStyle(siblings.get(siblings.size() - 1).getChatStyle());
}
cmp.appendSibling(dots);
}
}
slot.cursorEvents.clear();
if(bodyLines != null && !bodyLines.isEmpty()) {
GlStateManager.pushMatrix();
GlStateManager.translate(leftPadding, bodyYOffset, 0.0f);
int l = bodyLines.size();
GlStateManager.scale(bodyFontSize, bodyFontSize, bodyFontSize);
IChatComponent toolTip = null;
for(int i = 0; i < l; ++i) {
int startXLocal = 0;
int startXReal = leftPadding;
for(IChatComponent comp : bodyLines.get(i)) {
int w = mc.fontRendererObj.drawStringWithShadow(
comp.getChatStyle().getFormattingCode() + comp.getUnformattedTextForChat(), startXLocal,
i * 9, bd.bodyTxtColor) - startXLocal;
ClickEvent clickEvent = comp.getChatStyle().getChatClickEvent();
HoverEvent hoverEvent = toolTip == null ? comp.getChatStyle().getChatHoverEvent() : null;
if(clickEvent != null && !clickEvent.getAction().shouldAllowInChat()) {
clickEvent = null;
}
if(hoverEvent != null && !hoverEvent.getAction().shouldAllowInChat()) {
hoverEvent = null;
}
if(clickEvent != null) {
slot.cursorEvents.add(new ClickEventZone(startXReal + (int) (startXLocal * bodyFontSize),
bodyYOffset + (int) (i * 9 * bodyFontSize), (int) (w * bodyFontSize),
(int) (9 * bodyFontSize), comp, clickEvent != null, hoverEvent != null));
}
if(hoverEvent != null) {
int px = xx + startXReal + (int) (startXLocal * bodyFontSize);
int py = yy + bodyYOffset + (int) (i * 9 * bodyFontSize);
if (mouseX >= px && mouseX < px + (int) (w * bodyFontSize) && mouseY >= py
&& mouseY < py + (int) (9 * bodyFontSize)) {
toolTip = comp;
}
}
startXLocal += w;
}
}
GlStateManager.popMatrix();
if(toolTip != null) {
parent.handleComponentHover(toolTip, mouseX - xx, mouseY - yy);
}
}
if(sourceText != null) {
GlStateManager.pushMatrix();
GlStateManager.translate(badgeWidth - 21, badgeHeight - 5, 0.0f);
GlStateManager.scale(0.75f, 0.75f, 0.75f);
mc.fontRendererObj.drawStringWithShadow(sourceText, -mc.fontRendererObj.getStringWidth(sourceText) - 4, -10, bd.sourceTxtColor);
GlStateManager.popMatrix();
}
GlStateManager.popMatrix();
}
}
private String formatAge(long serverTimestamp) {
long cur = System.currentTimeMillis();
long daysAgo = Math.round((cur - serverTimestamp) / 86400000.0);
String ret = dateFormat.format(new Date(serverTimestamp));
if(daysAgo > 0l) {
ret += " (" + daysAgo + (daysAgo == 1l ? " day" : " days") + " ago)";
}else if(daysAgo < 0l) {
ret += " (in " + -daysAgo + (daysAgo == -1l ? " day" : " days") + ")";
}
return ret;
}
@Override
public int getListWidth() {
return 224;
}
@Override
public void drawScreen(int mouseXIn, int mouseYIn, float parFloat1) {
mouseX = mouseXIn;
mouseY = mouseYIn;
for(int i = 0, l = currentDisplayNotifs.size(); i < l; ++i) {
NotifBadgeSlot slot = currentDisplayNotifs.get(i);
slot.currentScreenX = -69420;
slot.currentScreenY = -69420;
}
super.drawScreen(mouseXIn, mouseYIn, parFloat1);
}
}

View File

@ -0,0 +1,171 @@
package net.lax1dude.eaglercraft.v1_8.notifications;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
import net.lax1dude.eaglercraft.v1_8.profanity_filter.ProfanityFilter;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeShowV4EAG.EnumBadgePriority;
import net.minecraft.client.Minecraft;
import net.minecraft.util.IChatComponent;
/**
* 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 NotificationBadge {
public final ServerNotificationManager mgr;
public final EaglercraftUUID badgeUUID;
public final IChatComponent bodyComponent;
protected IChatComponent bodyComponentProfanityFilter;
public final IChatComponent titleComponent;
protected IChatComponent titleComponentProfanityFilter;
public final IChatComponent sourceComponent;
protected IChatComponent sourceComponentProfanityFilter;
public final long clientTimestamp;
public final long serverTimestamp;
public final boolean silent;
public final EnumBadgePriority priority;
public final NotificationIcon mainIcon;
public final NotificationIcon titleIcon;
public final int hideAfterSec;
public final int expireAfterSec;
public final int backgroundColor;
public final int bodyTxtColor;
public final int titleTxtColor;
public final int sourceTxtColor;
protected CachedNotifBadgeTexture currentCacheGLTexture = null;
protected int currentCacheScaleFac = -1;
protected boolean currentCacheXButton = false;
protected boolean currentCacheProfanityFilter = false;
protected long hideAtMillis = -1l;
protected boolean unreadFlag = true;
protected boolean unreadFlagRender = true;
protected NotificationBadge(ServerNotificationManager mgr, EaglercraftUUID badgeUUID, IChatComponent bodyComponent,
IChatComponent titleComponent, IChatComponent sourceComponent, long clientTimestamp, long serverTimestamp,
boolean silent, EnumBadgePriority priority, NotificationIcon mainIcon, NotificationIcon titleIcon,
int hideAfterSec, int expireAfterSec, int backgroundColor, int bodyTxtColor, int titleTxtColor,
int sourceTxtColor) {
this.mgr = mgr;
this.badgeUUID = badgeUUID;
this.bodyComponent = bodyComponent;
this.titleComponent = titleComponent;
this.sourceComponent = sourceComponent;
this.clientTimestamp = clientTimestamp;
this.serverTimestamp = serverTimestamp;
this.silent = silent;
this.priority = priority;
this.mainIcon = mainIcon;
this.titleIcon = titleIcon;
this.hideAfterSec = hideAfterSec;
this.expireAfterSec = expireAfterSec;
this.backgroundColor = backgroundColor;
this.bodyTxtColor = bodyTxtColor;
this.titleTxtColor = titleTxtColor;
this.sourceTxtColor = sourceTxtColor;
}
protected void incrIconRefcounts() {
if(mainIcon != null) {
mainIcon.retain();
}
if(titleIcon != null) {
titleIcon.retain();
}
}
protected void decrIconRefcounts() {
deleteGLTexture();
if(mainIcon != null) {
mainIcon.release();
}
if(titleIcon != null) {
titleIcon.release();
}
}
protected CachedNotifBadgeTexture getGLTexture(ServerNotificationRenderer renderer, int scaleFactor, boolean showXButton) {
boolean profanityFilter = Minecraft.getMinecraft().isEnableProfanityFilter();
if(currentCacheGLTexture == null || currentCacheScaleFac != scaleFactor || currentCacheXButton != showXButton || currentCacheProfanityFilter != profanityFilter) {
deleteGLTexture();
currentCacheGLTexture = renderer.renderBadge(this, scaleFactor, showXButton);
currentCacheScaleFac = scaleFactor;
currentCacheXButton = showXButton;
currentCacheProfanityFilter = profanityFilter;
}
return currentCacheGLTexture;
}
protected void deleteGLTexture() {
if(currentCacheGLTexture != null) {
GlStateManager.deleteTexture(currentCacheGLTexture.glTexture);
currentCacheGLTexture = null;
}
}
public void hideNotif() {
if(hideAtMillis == -1l) {
markRead();
unreadFlagRender = false;
hideAtMillis = EagRuntime.steadyTimeMillis();
}
}
public void removeNotif() {
mgr.removeNotifFromActiveList(badgeUUID);
}
public void markRead() {
if(unreadFlag) {
unreadFlag = false;
--mgr.unreadCounter;
}
}
public IChatComponent getBodyProfanityFilter() {
if(Minecraft.getMinecraft().isEnableProfanityFilter()) {
if(bodyComponentProfanityFilter == null && bodyComponent != null) {
bodyComponentProfanityFilter = ProfanityFilter.getInstance().profanityFilterChatComponent(bodyComponent);
}
return bodyComponentProfanityFilter;
}else {
return bodyComponent;
}
}
public IChatComponent getTitleProfanityFilter() {
if(Minecraft.getMinecraft().isEnableProfanityFilter()) {
if(titleComponentProfanityFilter == null && titleComponent != null) {
titleComponentProfanityFilter = ProfanityFilter.getInstance().profanityFilterChatComponent(titleComponent);
}
return titleComponentProfanityFilter;
}else {
return titleComponent;
}
}
public IChatComponent getSourceProfanityFilter() {
if(Minecraft.getMinecraft().isEnableProfanityFilter()) {
if(sourceComponentProfanityFilter == null && sourceComponent != null) {
sourceComponentProfanityFilter = ProfanityFilter.getInstance().profanityFilterChatComponent(sourceComponent);
}
return sourceComponentProfanityFilter;
}else {
return sourceComponent;
}
}
}

View File

@ -0,0 +1,51 @@
package net.lax1dude.eaglercraft.v1_8.notifications;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
import net.lax1dude.eaglercraft.v1_8.profile.EaglerSkinTexture;
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 NotificationIcon {
private static int notifIconTmpId = 0;
protected int refCount = 0;
protected boolean serverRegistered = true;
public final EaglercraftUUID iconUUID;
public final EaglerSkinTexture texture;
public final ResourceLocation resource;
protected NotificationIcon(EaglercraftUUID iconUUID, EaglerSkinTexture texture) {
this.iconUUID = iconUUID;
this.texture = texture;
this.resource = new ResourceLocation("eagler:gui/server/notifs/tex_" + notifIconTmpId++);
}
public void retain() {
++refCount;
}
public void release() {
--refCount;
}
public boolean isValid() {
return serverRegistered || refCount > 0;
}
}

View File

@ -0,0 +1,277 @@
package net.lax1dude.eaglercraft.v1_8.notifications;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
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.SPacketNotifBadgeHideV4EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeShowV4EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifIconsRegisterV4EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifIconsReleaseV4EAG;
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.IChatComponent;
/**
* 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 ServerNotificationManager {
private static final Logger logger = LogManager.getLogger("ServerNotificationManager");
private final Map<EaglercraftUUID,NotificationIcon> activeIcons = new HashMap<>();
private final Map<EaglercraftUUID,NotificationBadge> activeNotifications = new HashMap<>();
private List<NotificationBadge> sortedNotifList = new ArrayList<>(0);
private List<NotificationBadge> sortedDisplayNotifList = new ArrayList<>(0);
private int updateCounter = 0;
private long lastCleanup = EagRuntime.steadyTimeMillis();
private final TextureManager textureMgr;
protected int unreadCounter = 0;
public ServerNotificationManager() {
this.textureMgr = Minecraft.getMinecraft().getTextureManager();
}
public void processPacketAddIcons(SPacketNotifIconsRegisterV4EAG packet) {
for(SPacketNotifIconsRegisterV4EAG.CreateIcon icn : packet.iconsToCreate) {
if(icn.uuidMost == 0 && icn.uuidLeast == 0) {
logger.error("Skipping notification icon with UUID 0!");
continue;
}
EaglercraftUUID uuid = new EaglercraftUUID(icn.uuidMost, icn.uuidLeast);
PacketImageData imageData = icn.imageData;
NotificationIcon existing = activeIcons.get(uuid);
if(existing != null) {
if (existing.texture.getWidth() != imageData.width
|| existing.texture.getHeight() != imageData.height) {
logger.error("Error: server tried to change the dimensions of icon {}!", uuid);
}else if(!Arrays.equals(existing.texture.getData(), imageData.rgba)) {
existing.texture.copyPixelsIn(ImageData.swapRB(imageData.rgba));
}
existing.serverRegistered = true;
continue;
}
NotificationIcon newIcon = new NotificationIcon(uuid,
new EaglerSkinTexture(ImageData.swapRB(imageData.rgba), imageData.width, imageData.height));
textureMgr.loadTexture(newIcon.resource, newIcon.texture);
activeIcons.put(uuid, newIcon);
}
}
public void processPacketRemIcons(SPacketNotifIconsReleaseV4EAG packet) {
for(SPacketNotifIconsReleaseV4EAG.DestroyIcon icn : packet.iconsToDestroy) {
NotificationIcon existing = activeIcons.get(new EaglercraftUUID(icn.uuidMost, icn.uuidLeast));
if(existing != null) {
existing.serverRegistered = false;
}
}
}
public void processPacketShowBadge(SPacketNotifBadgeShowV4EAG packet) {
EaglercraftUUID newUuid = new EaglercraftUUID(packet.badgeUUIDMost, packet.badgeUUIDLeast);
NotificationBadge existing = activeNotifications.get(newUuid);
if(existing != null) {
logger.error("Duplicate notification UUID {}, all notifications should have unique UUIDs!", newUuid);
return;
}
NotificationBadge newBadge = new NotificationBadge(this, newUuid,
!StringUtils.isAllBlank(packet.bodyComponent) ? IChatComponent.Serializer.jsonToComponent(packet.bodyComponent) : null,
!StringUtils.isAllBlank(packet.titleComponent) ? IChatComponent.Serializer.jsonToComponent(packet.titleComponent) : null,
!StringUtils.isAllBlank(packet.sourceComponent) ? IChatComponent.Serializer.jsonToComponent(packet.sourceComponent) : null,
EagRuntime.steadyTimeMillis(), packet.originalTimestampSec * 1000l, packet.silent, packet.priority,
getIcon(packet.mainIconUUIDMost, packet.mainIconUUIDLeast),
getIcon(packet.titleIconUUIDMost, packet.titleIconUUIDLeast), packet.hideAfterSec, packet.expireAfterSec,
packet.backgroundColor, packet.bodyTxtColor, packet.titleTxtColor, packet.sourceTxtColor);
++unreadCounter;
addNotifToActiveList(newBadge);
}
private NotificationIcon getIcon(long uuidMost, long uuidLeast) {
if(uuidMost == 0l && uuidLeast == 0l) {
return null;
}
return activeIcons.get(new EaglercraftUUID(uuidMost, uuidLeast));
}
public void processPacketHideBadge(SPacketNotifBadgeHideV4EAG packet) {
removeNotifFromActiveList(new EaglercraftUUID(packet.badgeUUIDLeast, packet.badgeUUIDMost));
}
public int getNotifListUpdateCount() {
return updateCounter;
}
public List<NotificationBadge> getNotifBadgesToDisplay() {
return sortedDisplayNotifList;
}
public List<NotificationBadge> getNotifLongHistory() {
return sortedNotifList;
}
protected void addNotifToActiveList(NotificationBadge badge) {
NotificationBadge exists = activeNotifications.put(badge.badgeUUID, badge);
if(exists != null) {
exists.decrIconRefcounts();
}
badge.incrIconRefcounts();
resortLists();
}
protected void removeNotifFromActiveList(EaglercraftUUID badge) {
NotificationBadge exists = activeNotifications.remove(badge);
if(exists != null) {
exists.decrIconRefcounts();
resortLists();
}
}
protected void removeAllNotifFromActiveList(Collection<NotificationBadge> badges) {
boolean resort = false;
for(NotificationBadge badge : badges) {
NotificationBadge exists = activeNotifications.remove(badge.badgeUUID);
if(exists != null) {
exists.decrIconRefcounts();
resort = true;
}
}
if(resort) {
resortLists();
}
}
protected static final Comparator<NotificationBadge> clientAgeComparator = (a, b) -> {
return (int)(b.clientTimestamp - a.clientTimestamp);
};
private void resortLists() {
updateCounter++;
int ll = activeNotifications.size();
if(!sortedNotifList.isEmpty()) sortedNotifList = new ArrayList<>(ll);
if(!sortedDisplayNotifList.isEmpty()) sortedDisplayNotifList = new ArrayList<>(Math.min(ll, 4));
if(ll > 0) {
sortedNotifList.addAll(activeNotifications.values());
Collections.sort(sortedNotifList, clientAgeComparator);
long millis = EagRuntime.steadyTimeMillis();
for(int i = 0, l = sortedNotifList.size(); i < l; ++i) {
NotificationBadge bd = sortedNotifList.get(i);
if(millis - bd.clientTimestamp < (long)(bd.hideAfterSec * 1000)) {
sortedDisplayNotifList.add(bd);
}else {
bd.deleteGLTexture();
}
}
}
}
public void runTick() {
long millis = EagRuntime.steadyTimeMillis();
if(millis - lastCleanup > 2500l) {
lastCleanup = millis;
int len = sortedNotifList.size();
if(len > 128) {
removeAllNotifFromActiveList(new ArrayList<NotificationBadge>(sortedNotifList.subList(128, len)));
}
Iterator<NotificationIcon> itr = activeIcons.values().iterator();
while(itr.hasNext()) {
NotificationIcon icn = itr.next();
if(!icn.isValid()) {
itr.remove();
textureMgr.deleteTexture(icn.resource);
}
}
if(!sortedDisplayNotifList.isEmpty()) {
Iterator<NotificationBadge> itr2 = sortedDisplayNotifList.iterator();
while(itr2.hasNext()) {
NotificationBadge bd = itr2.next();
if(bd.hideAtMillis != -1l) {
if(millis - bd.hideAtMillis > 500l) {
bd.deleteGLTexture();
itr2.remove();
}
}else {
long age = millis - bd.clientTimestamp;
if(age > (long)(bd.hideAfterSec * 1000) || age > (long)(bd.expireAfterSec * 1000)) {
bd.deleteGLTexture();
itr2.remove();
}
}
}
}
if(!activeNotifications.isEmpty()) {
Iterator<NotificationBadge> itr3 = activeNotifications.values().iterator();
List<NotificationBadge> toDelete = null;
while(itr3.hasNext()) {
NotificationBadge bd = itr3.next();
long age = millis - bd.clientTimestamp;
if(age > (long)(bd.expireAfterSec * 1000)) {
if(toDelete == null) {
toDelete = new ArrayList<>();
}
toDelete.add(bd);
}
}
if(toDelete != null) {
removeAllNotifFromActiveList(toDelete);
}
}
}
}
public int getUnread() {
if(unreadCounter < 0) unreadCounter = 0;
return unreadCounter;
}
public void commitUnreadFlag() {
for(NotificationBadge badge : activeNotifications.values()) {
badge.unreadFlagRender = badge.unreadFlag;
}
}
public void markRead() {
for(NotificationBadge badge : activeNotifications.values()) {
badge.unreadFlag = false;
badge.unreadFlagRender = false;
}
unreadCounter = 0;
}
public void destroy() {
for(NotificationIcon icn : activeIcons.values()) {
textureMgr.deleteTexture(icn.resource);
}
activeIcons.clear();
activeNotifications.clear();
sortedNotifList = null;
sortedDisplayNotifList = null;
}
}

View File

@ -0,0 +1,539 @@
package net.lax1dude.eaglercraft.v1_8.notifications;
import java.util.ArrayList;
import java.util.List;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.IFramebufferGL;
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;
import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU;
import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
import net.lax1dude.eaglercraft.v1_8.opengl.VertexFormat;
import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.client.gui.GuiChat;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiUtilRenderComponents;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.event.ClickEvent;
import net.minecraft.event.HoverEvent;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
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.*;
/**
* 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 ServerNotificationRenderer {
protected static final Logger logger = LogManager.getLogger("ServerNotificationRenderer");
protected Minecraft mc;
protected int width;
protected int height;
protected int scaleFactor;
protected IFramebufferGL rendererFramebuffer;
protected static final int BADGE_WIDTH = 160;
protected static final int BADGE_HEIGHT = 64;
private static final ResourceLocation eaglerGui = new ResourceLocation("eagler:gui/eagler_gui.png");
public ServerNotificationRenderer() {
}
public void init() {
destroy();
rendererFramebuffer = _wglCreateFramebuffer();
}
public void setResolution(Minecraft mc, int w, int h, int scaleFactor) {
this.mc = mc;
this.width = w;
this.height = h;
this.scaleFactor = scaleFactor;
}
public boolean handleClicked(GuiScreen currentScreen, int posX, int posY) {
if(mc.thePlayer == null) return false;
ServerNotificationManager mgr = mc.thePlayer.sendQueue.getNotifManager();
List<NotificationBadge> lst = mgr.getNotifBadgesToDisplay();
if(!lst.isEmpty()) {
int baseOffset = mc.guiAchievement.getHeight();
boolean showX = (currentScreen instanceof GuiChat);
if(showX) {
baseOffset += 25; // exit button in chat screen;
}
long millis = EagRuntime.steadyTimeMillis();
for(int i = 0, l = lst.size(); i < l; ++i) {
NotificationBadge badge = lst.get(i);
CachedNotifBadgeTexture tex = badge.currentCacheGLTexture;
if(tex != null) {
int baseX = width - tex.width;
float texHeight = tex.height;
float timeRemainingSec;
long age = millis - badge.clientTimestamp;
if(badge.hideAtMillis != -1l) {
timeRemainingSec = (float)((double)(500l - (millis - badge.hideAtMillis)) * 0.001);
}else {
timeRemainingSec = (float)((double)((long)badge.hideAfterSec * 1000l - age) * 0.001);
}
timeRemainingSec = Math.min((float)(age * 0.001) + 0.001f, timeRemainingSec);
float f = MathHelper.clamp_float(timeRemainingSec * 3.0F, 0.0F, 1.0F);
f *= f;
texHeight *= f;
if(badge.hideAtMillis == -1l) {
if(posX >= baseX && posX < width && posY >= baseOffset && posY < baseOffset + texHeight) {
if(showX) {
int xposX = baseX + tex.width - 21;
int xposY = baseOffset + 5;
if(posX >= xposX && posY >= xposY && posX < xposX + 16 && posY < xposY + 16) {
badge.hideNotif();
mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F));
return true;
}
}
if(tex.rootClickEvent != null) {
if(currentScreen.handleComponentClick(tex.rootClickEvent)) {
mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F));
return true;
}
}
List<ClickEventZone> cursorEvents = tex.cursorEvents;
if(tex.hasClickEvents && cursorEvents != null) {
for(int j = 0, m = cursorEvents.size(); j < m; ++j) {
ClickEventZone evt = cursorEvents.get(j);
if(evt.hasClickEvent) {
int offsetPosX = baseX + evt.posX;
int offsetPosY = baseOffset + evt.posY;
if(posX >= offsetPosX && posY >= offsetPosY && posX < offsetPosX + evt.width && posY < offsetPosY + evt.height) {
if(currentScreen.handleComponentClick(evt.chatComponent)) {
mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F));
return true;
}
}
}
}
}
}
}
baseOffset += texHeight;
}
}
}
return false;
}
public void renderOverlay(int mouseX, int mouseY) {
if(mc.thePlayer == null) return;
ServerNotificationManager mgr = mc.thePlayer.sendQueue.getNotifManager();
List<NotificationBadge> lst = mgr.getNotifBadgesToDisplay();
if(!lst.isEmpty()) {
GlStateManager.clear(GL_DEPTH_BUFFER_BIT);
boolean showXButtons = false;
int baseOffset = mc.guiAchievement.getHeight();
if(mc.currentScreen != null) {
if(mc.currentScreen instanceof GuiChat) {
baseOffset += 25; // exit button in chat screen;
showXButtons = true;
}else if(mc.currentScreen instanceof GuiScreenNotifications) {
return;
}
}
long millis = EagRuntime.steadyTimeMillis();
boolean isBlend = false;
for(int i = 0, l = lst.size(); i < l; ++i) {
NotificationBadge badge = lst.get(i);
boolean isHiding = false;
if(badge.hideAtMillis != -1l) {
isHiding = true;
if(millis - badge.hideAtMillis > 500l) {
continue;
}
}
CachedNotifBadgeTexture tex = badge.getGLTexture(this, scaleFactor, showXButtons);
if(tex != null) {
GlStateManager.bindTexture(tex.glTexture);
float alphaTop = 1.0f;
float alphaBottom = 1.0f;
float timeRemainingSec;
long age = millis - badge.clientTimestamp;
if(isHiding) {
timeRemainingSec = (float)((double)(500l - (millis - badge.hideAtMillis)) * 0.001);
}else {
timeRemainingSec = (float)((double)((long)badge.hideAfterSec * 1000l - age) * 0.001);
}
timeRemainingSec = Math.min((float)(age * 0.001) + 0.001f, timeRemainingSec);
alphaTop *= MathHelper.clamp_float(timeRemainingSec * 3.0F, 0.0F, 1.0F);
alphaTop *= alphaTop;
alphaBottom *= MathHelper.clamp_float(timeRemainingSec * 2.0F, 0.0F, 1.0F);
alphaBottom *= alphaBottom;
if(alphaTop == 0.0F && alphaBottom == 0.0F) {
continue;
}
boolean blend = alphaTop < 1.0f || alphaBottom < 1.0f;
if(blend != isBlend) {
if(blend) {
GlStateManager.enableBlend();
GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0);
}else {
GlStateManager.disableBlend();
}
isBlend = blend;
}
int px = width - tex.width;
drawTexturedGradientFBRect(px, baseOffset, tex.width, tex.height,
((int) (alphaTop * 255.0f) << 24) | 0xFFFFFF,
((int) (alphaBottom * 255.0f) << 24) | 0xFFFFFF, 200.0f);
if(showXButtons && tex.hasHoverEvents) {
if(mouseX >= px && mouseY >= baseOffset && mouseX < px + tex.width && mouseY < baseOffset + tex.height) {
List<ClickEventZone> cursorEvents = tex.cursorEvents;
if(cursorEvents != null) {
for(int j = 0, m = cursorEvents.size(); j < m; ++j) {
ClickEventZone evt = cursorEvents.get(j);
if(evt.hasHoverEvent) {
int offsetPosX = px + evt.posX;
int offsetPosY = baseOffset + evt.posY;
if(mouseX >= offsetPosX && mouseY >= offsetPosY && mouseX < offsetPosX + evt.width
&& mouseY < offsetPosY + evt.height) {
if(isBlend) {
GlStateManager.disableBlend();
isBlend = false;
}
mc.currentScreen.handleComponentHover(evt.chatComponent, mouseX, mouseY);
}
}
}
}
}
}
baseOffset += tex.height * alphaTop;
}
}
if(isBlend) {
GlStateManager.disableBlend();
}
}
}
protected CachedNotifBadgeTexture renderBadge(NotificationBadge badge, int scaleFactor, boolean showXButton) {
int badgeWidth = BADGE_WIDTH;
int badgeHeight = 10;
int leftPadding = 6;
int rightPadding = 26;
int mainIconSW = 32;
if(badge.mainIcon != null) {
int iw = badge.mainIcon.texture.getWidth();
int ih = badge.mainIcon.texture.getHeight();
float iaspect = (float)iw / (float)ih;
mainIconSW = (int)(32 * iaspect);
leftPadding += Math.min(mainIconSW, 64) + 3;
}
int textZoneWidth = badgeWidth - leftPadding - rightPadding;
int bodyYOffset = 5;
String titleText = null;
IChatComponent titleComponent = badge.getTitleProfanityFilter();
if(titleComponent != null) {
titleText = titleComponent.getFormattedText();
if(titleText.length() > 0) {
badgeHeight += 12;
bodyYOffset += 12;
}else {
titleText = null;
}
}
if(badge.titleIcon != null && titleText == null) {
badgeHeight += 12;
bodyYOffset += 12;
}
float bodyFontSize = 0.75f;
List<IChatComponent> bodyLines = null;
List<ClickEventZone> clickEvents = null;
IChatComponent rootClickEvt = null;
boolean hasClickEvents = false;
boolean hasHoverEvents = false;
int bodyHeight = 0;
IChatComponent bodyComponent = badge.getBodyProfanityFilter();
if(bodyComponent != null) {
if (bodyComponent.getChatStyle().getChatClickEvent() != null
&& bodyComponent.getChatStyle().getChatClickEvent().getAction().shouldAllowInChat()) {
rootClickEvt = bodyComponent;
}
bodyLines = GuiUtilRenderComponents.func_178908_a(bodyComponent, (int) (textZoneWidth / bodyFontSize),
mc.fontRendererObj, true, true);
int maxHeight = BADGE_HEIGHT - 32;
int maxLines = MathHelper.floor_float(maxHeight / (9 * bodyFontSize));
if(bodyLines.size() > maxLines) {
bodyLines = bodyLines.subList(0, maxLines);
bodyComponent = bodyLines.get(maxLines - 1);
List<IChatComponent> siblings = bodyComponent.getSiblings();
IChatComponent dots = new ChatComponentText("...");
if(siblings != null && siblings.size() > 0) {
dots.setChatStyle(siblings.get(siblings.size() - 1).getChatStyle());
}
bodyComponent.appendSibling(dots);
}
bodyHeight = MathHelper.floor_float(bodyLines.size() * (9 * bodyFontSize));
}
String sourceText = null;
IChatComponent sourceComponent = badge.getSourceProfanityFilter();
if(sourceComponent != null) {
sourceText = sourceComponent.getFormattedText();
if(sourceText.length() == 0) {
sourceText = null;
}
}
if(badge.mainIcon != null) {
bodyHeight = Math.max(sourceText != null ? 30 : 32, bodyHeight);
}
if(sourceText != null) {
badgeHeight += 6;
}
badgeHeight += bodyHeight;
badgeHeight = Math.max(badgeHeight, showXButton ? 42 : 26);
if(badgeHeight > BADGE_HEIGHT) {
logger.info("Warning: Badge {} was {} pixels too high!", badge.badgeUUID, BADGE_HEIGHT - badgeHeight);
badgeHeight = BADGE_HEIGHT;
}
int glTex = GlStateManager.generateTexture();
GlStateManager.bindTexture(glTex);
EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
EaglercraftGPU.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, badgeWidth * scaleFactor, badgeHeight * scaleFactor, 0, GL_RGBA,
GL_UNSIGNED_BYTE, (ByteBuffer) null);
_wglBindFramebuffer(_GL_FRAMEBUFFER, rendererFramebuffer);
_wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(glTex), 0);
_wglDrawBuffers(_GL_COLOR_ATTACHMENT0);
int[] oldViewport = new int[4];
EaglercraftGPU.glGetInteger(GL_VIEWPORT, oldViewport);
GlStateManager.viewport(0, 0, badgeWidth * scaleFactor, badgeHeight * scaleFactor);
GlStateManager.disableDepth();
GlStateManager.depthMask(false);
GlStateManager.enableTexture2D();
GlStateManager.disableLighting();
GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
GlStateManager.matrixMode(GL_PROJECTION);
GlStateManager.pushMatrix();
GlStateManager.loadIdentity();
GlStateManager.ortho(0.0D, badgeWidth, badgeHeight, 0.0D, 1000.0D, 3000.0D);
GlStateManager.matrixMode(GL_MODELVIEW);
GlStateManager.pushMatrix();
GlStateManager.loadIdentity();
GlStateManager.translate(0.0F, 0.0F, -2000.0F);
Tessellator tess = Tessellator.getInstance();
WorldRenderer worldRenderer = tess.getWorldRenderer();
worldRenderer.begin(GL_QUADS, VertexFormat.POSITION_TEX_COLOR);
mc.getTextureManager().bindTexture(eaglerGui);
drawTexturedColoredRect(worldRenderer, 0, 0, 96, 192, 160, 8, (badge.backgroundColor >>> 16) & 0xFF,
(badge.backgroundColor >>> 8) & 0xFF, badge.backgroundColor & 0xFF, 0xFF);
drawTexturedColoredRect(worldRenderer, 0, 8, 96, 192 + (BADGE_HEIGHT - badgeHeight + 8), 160, (badgeHeight - 8),
(badge.backgroundColor >>> 16) & 0xFF, (badge.backgroundColor >>> 8) & 0xFF,
badge.backgroundColor & 0xFF, 0xFF);
switch(badge.priority) {
case LOW:
default:
drawTexturedColoredRect(worldRenderer, badgeWidth - 21, badgeHeight - 21, 192, 176, 16, 16, (badge.backgroundColor >>> 16) & 0xFF,
(badge.backgroundColor >>> 8) & 0xFF, badge.backgroundColor & 0xFF, 0xFF);
break;
case NORMAL:
drawTexturedColoredRect(worldRenderer, badgeWidth - 21, badgeHeight - 21, 208, 176, 16, 16, 0xFF, 0xFF, 0xFF, 0xFF);
break;
case HIGHER:
drawTexturedColoredRect(worldRenderer, badgeWidth - 21, badgeHeight - 21, 224, 176, 16, 16, 0xFF, 0xFF, 0xFF, 0xFF);
break;
case HIGHEST:
drawTexturedColoredRect(worldRenderer, badgeWidth - 21, badgeHeight - 21, 240, 176, 16, 16, 0xFF, 0xFF, 0xFF, 0xFF);
break;
}
if(showXButton) {
drawTexturedColoredRect(worldRenderer, badgeWidth - 21, 5, 80, 208, 16, 16, 0xFF, 0xFF, 0xFF, 0xFF);
}
tess.draw();
if(badge.mainIcon != null) {
mc.getTextureManager().bindTexture(badge.mainIcon.resource);
drawTexturedRect(6, bodyYOffset, mainIconSW, 32);
}
if(badge.titleIcon != null) {
mc.getTextureManager().bindTexture(badge.titleIcon.resource);
drawTexturedRect(6, 5, 8, 8);
}
if(titleText != null) {
GlStateManager.pushMatrix();
GlStateManager.translate(6 + (badge.titleIcon != null ? 10 : 0), 6, 0.0f);
GlStateManager.scale(0.75f, 0.75f, 0.75f);
mc.fontRendererObj.drawStringWithShadow(titleText, 0, 0, badge.titleTxtColor);
GlStateManager.popMatrix();
}
if(bodyLines != null && !bodyLines.isEmpty()) {
GlStateManager.pushMatrix();
if(!showXButton && badge.mainIcon == null && titleText != null) {
bodyYOffset -= 2;
}
GlStateManager.translate(leftPadding, bodyYOffset, 0.0f);
int l = bodyLines.size();
GlStateManager.scale(bodyFontSize, bodyFontSize, bodyFontSize);
for(int i = 0; i < l; ++i) {
int startXLocal = 0;
int startXReal = leftPadding;
for(IChatComponent comp : bodyLines.get(i)) {
int w = mc.fontRendererObj.drawStringWithShadow(
comp.getChatStyle().getFormattingCode() + comp.getUnformattedTextForChat(), startXLocal,
i * 9, badge.bodyTxtColor) - startXLocal;
ClickEvent clickEvent = comp.getChatStyle().getChatClickEvent();
HoverEvent hoverEvent = comp.getChatStyle().getChatHoverEvent();
if(clickEvent != null && !clickEvent.getAction().shouldAllowInChat()) {
clickEvent = null;
}
if(hoverEvent != null && !hoverEvent.getAction().shouldAllowInChat()) {
hoverEvent = null;
}
if(clickEvent != null || hoverEvent != null) {
hasClickEvents |= clickEvent != null;
hasHoverEvents |= hoverEvent != null;
if(clickEvents == null) {
clickEvents = new ArrayList<>();
}
clickEvents.add(new ClickEventZone(startXReal + (int) (startXLocal * bodyFontSize),
bodyYOffset + (int) (i * 9 * bodyFontSize), (int) (w * bodyFontSize),
(int) (9 * bodyFontSize), comp, clickEvent != null, hoverEvent != null));
}
startXLocal += w;
}
}
GlStateManager.popMatrix();
}
if(sourceText != null) {
GlStateManager.pushMatrix();
GlStateManager.translate(badgeWidth - 21, badgeHeight - 5, 0.0f);
GlStateManager.scale(0.5f, 0.5f, 0.5f);
mc.fontRendererObj.drawStringWithShadow(sourceText, -mc.fontRendererObj.getStringWidth(sourceText) - 4, -10, badge.sourceTxtColor);
GlStateManager.popMatrix();
}
GlStateManager.matrixMode(GL_PROJECTION);
GlStateManager.popMatrix();
GlStateManager.matrixMode(GL_MODELVIEW);
GlStateManager.popMatrix();
GlStateManager.depthMask(true);
GlStateManager.enableDepth();
_wglBindFramebuffer(_GL_FRAMEBUFFER, null);
GlStateManager.viewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
return new CachedNotifBadgeTexture(glTex, scaleFactor, badgeWidth, badgeHeight, clickEvents, rootClickEvt, hasClickEvents, hasHoverEvents);
}
static void drawTexturedColoredRect(WorldRenderer worldRenderer, float xCoord, float yCoord, int minU,
int minV, int width, int height, int r, int g, int b, int a) {
float f = 0.00390625F;
float f1 = 0.00390625F;
worldRenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + (float) height), 0.0).color(r, g, b, a)
.tex((double) ((float) (minU + 0) * f), (double) ((float) (minV + height) * f1)).endVertex();
worldRenderer.pos((double) (xCoord + (float) width), (double) (yCoord + (float) height), 0.0).color(r, g, b, a)
.tex((double) ((float) (minU + width) * f), (double) ((float) (minV + height) * f1)).endVertex();
worldRenderer.pos((double) (xCoord + (float) width), (double) (yCoord + 0.0F), 0.0).color(r, g, b, a)
.tex((double) ((float) (minU + width) * f), (double) ((float) (minV + 0) * f1)).endVertex();
worldRenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + 0.0F), 0.0).color(r, g, b, a)
.tex((double) ((float) (minU + 0) * f), (double) ((float) (minV + 0) * f1)).endVertex();
}
static void drawTexturedGradientFBRect(float xCoord, float yCoord, int width, int height, int rgbaTop, int rgbaBottom, float zIndex) {
int topR = (rgbaTop >>> 16) & 0xFF;
int topG = (rgbaTop >>> 8) & 0xFF;
int topB = rgbaTop & 0xFF;
int topA = (rgbaTop >>> 24) & 0xFF;
int bottomR = (rgbaBottom >>> 16) & 0xFF;
int bottomG = (rgbaBottom >>> 8) & 0xFF;
int bottomB = rgbaBottom & 0xFF;
int bottomA = (rgbaBottom >>> 24) & 0xFF;
Tessellator tess = Tessellator.getInstance();
WorldRenderer worldRenderer = tess.getWorldRenderer();
worldRenderer.begin(GL_QUADS, VertexFormat.POSITION_TEX_COLOR);
worldRenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + (float) height), zIndex)
.color(bottomR, bottomG, bottomB, bottomA).tex(0.0, 0.0).endVertex();
worldRenderer.pos((double) (xCoord + (float) width), (double) (yCoord + (float) height), zIndex)
.color(bottomR, bottomG, bottomB, bottomA).tex(1.0, 0.0).endVertex();
worldRenderer.pos((double) (xCoord + (float) width), (double) (yCoord + 0.0F), zIndex)
.color(topR, topG, topB, topA).tex(1.0, 1.0).endVertex();
worldRenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + 0.0F), zIndex).color(topR, topG, topB, topA)
.tex(0.0, 1.0).endVertex();
tess.draw();
}
static void drawTexturedRect(float xCoord, float yCoord, int width, int height) {
Tessellator tess = Tessellator.getInstance();
WorldRenderer worldRenderer = tess.getWorldRenderer();
worldRenderer.begin(GL_QUADS, VertexFormat.POSITION_TEX);
worldRenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + (float) height), 0.0).tex(0.0, 1.0).endVertex();
worldRenderer.pos((double) (xCoord + (float) width), (double) (yCoord + (float) height), 0.0).tex(1.0, 1.0).endVertex();
worldRenderer.pos((double) (xCoord + (float) width), (double) (yCoord + 0.0F), 0.0).tex(1.0, 0.0).endVertex();
worldRenderer.pos((double) (xCoord + 0.0F), (double) (yCoord + 0.0F), 0.0).tex(0.0, 0.0).endVertex();
tess.draw();
}
public void destroy() {
if(rendererFramebuffer != null) {
_wglDeleteFramebuffer(rendererFramebuffer);
rendererFramebuffer = null;
}
}
}

View File

@ -3,15 +3,16 @@ package net.lax1dude.eaglercraft.v1_8.opengl;
import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*;
import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*;
import java.util.List;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.IBufferArrayGL;
import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL;
import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer;
import net.lax1dude.eaglercraft.v1_8.opengl.FixedFunctionShader.FixedFunctionConstants;
/**
* Copyright (c) 2022-2023 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
@ -28,17 +29,19 @@ import net.lax1dude.eaglercraft.v1_8.opengl.FixedFunctionShader.FixedFunctionCon
public class DrawUtils {
public static final String vertexShaderPath = "/assets/eagler/glsl/local.vsh";
public static final String vertexShaderPrecision = "precision highp float;\n";
public static IBufferArrayGL standardQuad2DVAO = null;
public static IBufferArrayGL standardQuad3DVAO = null;
public static IBufferGL standardQuadVBO = null;
public static IShaderGL vshLocal = null;
public static List<VSHInputLayoutParser.ShaderInput> vshLocalLayout = null;
static void init() {
if(standardQuad2DVAO == null) {
standardQuad2DVAO = _wglGenVertexArrays();
standardQuad3DVAO = _wglGenVertexArrays();
standardQuad2DVAO = EaglercraftGPU.createGLBufferArray();
standardQuad3DVAO = EaglercraftGPU.createGLBufferArray();
standardQuadVBO = _wglGenBuffers();
FloatBuffer verts = EagRuntime.allocateFloatBuffer(18);
@ -48,30 +51,29 @@ public class DrawUtils {
});
verts.flip();
EaglercraftGPU.bindGLArrayBuffer(standardQuadVBO);
EaglercraftGPU.bindVAOGLArrayBufferNow(standardQuadVBO);
_wglBufferData(GL_ARRAY_BUFFER, verts, GL_STATIC_DRAW);
EagRuntime.freeFloatBuffer(verts);
EaglercraftGPU.bindGLBufferArray(standardQuad2DVAO);
_wglEnableVertexAttribArray(0);
_wglVertexAttribPointer(0, 2, GL_FLOAT, false, 12, 0);
EaglercraftGPU.enableVertexAttribArray(0);
EaglercraftGPU.vertexAttribPointer(0, 2, GL_FLOAT, false, 12, 0);
EaglercraftGPU.bindGLBufferArray(standardQuad3DVAO);
_wglEnableVertexAttribArray(0);
_wglVertexAttribPointer(0, 3, GL_FLOAT, false, 12, 0);
EaglercraftGPU.enableVertexAttribArray(0);
EaglercraftGPU.vertexAttribPointer(0, 3, GL_FLOAT, false, 12, 0);
}
if(vshLocal == null) {
String vertexSource = EagRuntime.getResourceString(vertexShaderPath);
if(vertexSource == null) {
throw new RuntimeException("vertex shader \"" + vertexShaderPath + "\" is missing!");
}
String vertexSource = EagRuntime.getRequiredResourceString(vertexShaderPath);
vshLocalLayout = VSHInputLayoutParser.getShaderInputs(vertexSource);
vshLocal = _wglCreateShader(GL_VERTEX_SHADER);
_wglShaderSource(vshLocal, FixedFunctionConstants.VERSION + "\n" + vertexSource);
_wglShaderSource(vshLocal, GLSLHeader.getVertexHeaderCompat(vertexSource, vertexShaderPrecision));
_wglCompileShader(vshLocal);
if(_wglGetShaderi(vshLocal, GL_COMPILE_STATUS) != GL_TRUE) {
@ -90,12 +92,32 @@ public class DrawUtils {
public static void drawStandardQuad2D() {
EaglercraftGPU.bindGLBufferArray(standardQuad2DVAO);
_wglDrawArrays(GL_TRIANGLES, 0, 6);
EaglercraftGPU.doDrawArrays(GL_TRIANGLES, 0, 6);
}
public static void drawStandardQuad3D() {
EaglercraftGPU.bindGLBufferArray(standardQuad3DVAO);
_wglDrawArrays(GL_TRIANGLES, 0, 6);
EaglercraftGPU.doDrawArrays(GL_TRIANGLES, 0, 6);
}
public static void destroy() {
if(standardQuad2DVAO != null) {
EaglercraftGPU.destroyGLBufferArray(standardQuad2DVAO);
standardQuad2DVAO = null;
}
if(standardQuad3DVAO != null) {
EaglercraftGPU.destroyGLBufferArray(standardQuad3DVAO);
standardQuad3DVAO = null;
}
if(standardQuadVBO != null) {
_wglDeleteBuffers(standardQuadVBO);
standardQuadVBO = null;
}
if(vshLocal != null) {
vshLocal.free();
vshLocal = null;
vshLocalLayout = null;
}
}
}

View File

@ -39,7 +39,7 @@ public class EaglerMeshLoader implements IResourceManagerReloadListener {
private static final Logger logger = LogManager.getLogger("EaglerMeshLoader");
private static final Map<ResourceLocation, HighPolyMesh> meshCache = new HashMap();
private static final Map<ResourceLocation, HighPolyMesh> meshCache = new HashMap<>();
public static HighPolyMesh getEaglerMesh(ResourceLocation meshLoc) {
if(meshLoc.cachedPointerType == ResourceLocation.CACHED_POINTER_EAGLER_MESH) {
@ -104,7 +104,7 @@ public class EaglerMeshLoader implements IResourceManagerReloadListener {
}
if(meshStruct.vertexArray == null) {
meshStruct.vertexArray = _wglGenVertexArrays();
meshStruct.vertexArray = EaglercraftGPU.createGLBufferArray();
}
if(meshStruct.vertexBuffer == null) {
meshStruct.vertexBuffer = _wglGenBuffers();
@ -115,29 +115,29 @@ public class EaglerMeshLoader implements IResourceManagerReloadListener {
up1.position(0).limit(intsOfVertex);
EaglercraftGPU.bindGLArrayBuffer(meshStruct.vertexBuffer);
EaglercraftGPU.bindVAOGLArrayBufferNow(meshStruct.vertexBuffer);
_wglBufferData(GL_ARRAY_BUFFER, up1, GL_STATIC_DRAW);
EaglercraftGPU.bindGLBufferArray(meshStruct.vertexArray);
up1.position(intsOfVertex).limit(intsTotal);
_wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, meshStruct.indexBuffer);
EaglercraftGPU.bindVAOGLElementArrayBufferNow(meshStruct.indexBuffer);
_wglBufferData(GL_ELEMENT_ARRAY_BUFFER, up1, GL_STATIC_DRAW);
_wglEnableVertexAttribArray(0);
_wglVertexAttribPointer(0, 3, GL_FLOAT, false, stride, 0);
EaglercraftGPU.enableVertexAttribArray(0);
EaglercraftGPU.vertexAttribPointer(0, 3, GL_FLOAT, false, stride, 0);
if(meshStruct.hasTexture) {
_wglEnableVertexAttribArray(1);
_wglVertexAttribPointer(1, 2, GL_FLOAT, false, stride, 16);
EaglercraftGPU.enableVertexAttribArray(1);
EaglercraftGPU.vertexAttribPointer(1, 2, GL_FLOAT, false, stride, 16);
}
_wglEnableVertexAttribArray(meshStruct.hasTexture ? 2 : 1);
_wglVertexAttribPointer(meshStruct.hasTexture ? 2 : 1, 4, GL_BYTE, true, stride, 12);
EaglercraftGPU.enableVertexAttribArray(meshStruct.hasTexture ? 2 : 1);
EaglercraftGPU.vertexAttribPointer(meshStruct.hasTexture ? 2 : 1, 4, GL_BYTE, true, stride, 12);
}catch(Throwable ex) {
if(meshStruct.vertexArray != null) {
_wglDeleteVertexArrays(meshStruct.vertexArray);
EaglercraftGPU.destroyGLBufferArray(meshStruct.vertexArray);
meshStruct.vertexArray = null;
}
if(meshStruct.vertexBuffer != null) {

View File

@ -5,6 +5,7 @@ import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.minecraft.util.MathHelper;
import java.util.HashMap;
import java.util.Map;
@ -16,7 +17,6 @@ import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL;
import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL;
import net.lax1dude.eaglercraft.v1_8.internal.IQueryGL;
import net.lax1dude.eaglercraft.v1_8.internal.ITextureGL;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformBufferFunctions;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL;
import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*;
@ -39,12 +39,15 @@ import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*;
*/
public class EaglercraftGPU {
static final GLObjectMap<ITextureGL> mapTexturesGL = new GLObjectMap(32767);
static final GLObjectMap<IQueryGL> mapQueriesGL = new GLObjectMap(32767);
static final GLObjectMap<DisplayList> mapDisplayListsGL = new GLObjectMap(32767);
static final GLObjectMap<ITextureGL> mapTexturesGL = new GLObjectMap<>(8192);
static final GLObjectMap<IQueryGL> mapQueriesGL = new GLObjectMap<>(8192);
static final GLObjectMap<DisplayList> mapDisplayListsGL = new GLObjectMap<>(8192);
static final Logger logger = LogManager.getLogger("EaglercraftGPU");
static boolean emulatedVAOs = false;
static SoftGLBufferState emulatedVAOState = new SoftGLBufferState();
public static final String gluErrorString(int i) {
switch(i) {
case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
@ -87,16 +90,16 @@ public class EaglercraftGPU {
EaglercraftGPU.bindGLBufferArray(dp.vertexArray);
int c = 0;
if((dp.attribs & ATTRIB_TEXTURE) == ATTRIB_TEXTURE) {
_wglDisableVertexAttribArray(++c);
EaglercraftGPU.disableVertexAttribArray(++c);
}
if((dp.attribs & ATTRIB_COLOR) == ATTRIB_COLOR) {
_wglDisableVertexAttribArray(++c);
EaglercraftGPU.disableVertexAttribArray(++c);
}
if((dp.attribs & ATTRIB_NORMAL) == ATTRIB_NORMAL) {
_wglDisableVertexAttribArray(++c);
EaglercraftGPU.disableVertexAttribArray(++c);
}
if((dp.attribs & ATTRIB_LIGHTMAP) == ATTRIB_LIGHTMAP) {
_wglDisableVertexAttribArray(++c);
EaglercraftGPU.disableVertexAttribArray(++c);
}
}
dp.attribs = -1;
@ -109,7 +112,7 @@ public class EaglercraftGPU {
if(displayListBuffer.capacity() < wantSize) {
int newSize = (wantSize & 0xFFFE0000) + 0x40000;
ByteBuffer newBuffer = EagRuntime.allocateByteBuffer(newSize);
PlatformBufferFunctions.put(newBuffer, (ByteBuffer)displayListBuffer.flip());
newBuffer.put((ByteBuffer)displayListBuffer.flip());
EagRuntime.freeByteBuffer(displayListBuffer);
displayListBuffer = newBuffer;
}
@ -123,7 +126,7 @@ public class EaglercraftGPU {
if(dp.attribs == -1) {
if(dp.vertexArray != null) {
_wglDeleteVertexArrays(dp.vertexArray);
EaglercraftGPU.destroyGLBufferArray(dp.vertexArray);
dp.vertexArray = null;
}
if(dp.vertexBuffer != null) {
@ -135,7 +138,7 @@ public class EaglercraftGPU {
}
if(dp.vertexArray == null) {
dp.vertexArray = _wglGenVertexArrays();
dp.vertexArray = createGLBufferArray();
dp.bindQuad16 = false;
dp.bindQuad32 = false;
}
@ -143,7 +146,7 @@ public class EaglercraftGPU {
dp.vertexBuffer = _wglGenBuffers();
}
bindGLArrayBuffer(dp.vertexBuffer);
bindVAOGLArrayBufferNow(dp.vertexBuffer);
displayListBuffer.flip();
_wglBufferData(GL_ARRAY_BUFFER, displayListBuffer, GL_STATIC_DRAW);
displayListBuffer.clear();
@ -194,7 +197,7 @@ public class EaglercraftGPU {
}
dp.attribs = -1;
if(dp.vertexArray != null) {
_wglDeleteVertexArrays(dp.vertexArray);
EaglercraftGPU.destroyGLBufferArray(dp.vertexArray);
dp.vertexArray = null;
}
if(dp.vertexBuffer != null) {
@ -210,7 +213,7 @@ public class EaglercraftGPU {
++GlStateManager.stateNormalSerial;
}
private static final Map<Integer,String> stringCache = new HashMap();
private static final Map<Integer,String> stringCache = new HashMap<>();
public static final String glGetString(int param) {
String str = stringCache.get(param);
@ -238,9 +241,24 @@ public class EaglercraftGPU {
return _wglGetInteger(param);
}
public static final void glTexImage2D(int target, int level, int internalFormat, int w, int h, int unused,
int format, int type, ByteBuffer pixels) {
if(glesVers >= 300) {
_wglTexImage2D(target, level, internalFormat, w, h, unused, format, type, pixels);
}else {
int tv = TextureFormatHelper.trivializeInternalFormatToGLES20(internalFormat);
_wglTexImage2D(target, level, tv, w, h, unused, tv, type, pixels);
}
}
public static final void glTexImage2D(int target, int level, int internalFormat, int w, int h, int unused,
int format, int type, IntBuffer pixels) {
_wglTexImage2D(target, level, internalFormat, w, h, unused, format, type, pixels);
if(glesVers >= 300) {
_wglTexImage2D(target, level, internalFormat, w, h, unused, format, type, pixels);
}else {
int tv = TextureFormatHelper.trivializeInternalFormatToGLES20(internalFormat);
_wglTexImage2D(target, level, tv, w, h, unused, tv, type, pixels);
}
}
public static final void glTexSubImage2D(int target, int level, int x, int y, int w, int h, int format,
@ -249,9 +267,32 @@ public class EaglercraftGPU {
}
public static final void glTexStorage2D(int target, int levels, int internalFormat, int w, int h) {
_wglTexStorage2D(target, levels, internalFormat, w, h);
if(texStorageCapable && (glesVers >= 300 || levels == 1 || (MathHelper.calculateLogBaseTwo(Math.max(w, h)) + 1) == levels)) {
_wglTexStorage2D(target, levels, internalFormat, w, h);
}else {
int tv = TextureFormatHelper.trivializeInternalFormatToGLES20(internalFormat);
int type = TextureFormatHelper.getTypeFromInternal(internalFormat);
for(int i = 0; i < levels; ++i) {
_wglTexImage2D(target, i, tv, Math.max(w >> i, 1), Math.max(h >> i, 1), 0, tv, type, (ByteBuffer)null);
}
}
}
public static final void glReadPixels(int x, int y, int width, int height, int format, int type, ByteBuffer buffer) {
switch(type) {
case GL_FLOAT:
_wglReadPixels(x, y, width, height, format, GL_FLOAT, buffer.asFloatBuffer());
break;
case 0x140B: // GL_HALF_FLOAT
_wglReadPixels_u16(x, y, width, height, format, glesVers == 200 ? 0x8D61 : 0x140B, buffer);
break;
case GL_UNSIGNED_BYTE:
default:
_wglReadPixels(x, y, width, height, format, type, buffer);
break;
}
}
public static final void glLineWidth(float f) {
_wglLineWidth(f);
}
@ -284,7 +325,7 @@ public class EaglercraftGPU {
DisplayList d = mapDisplayListsGL.free(id);
if(d != null) {
if(d.vertexArray != null) {
_wglDeleteVertexArrays(d.vertexArray);
EaglercraftGPU.destroyGLBufferArray(d.vertexArray);
}
if(d.vertexBuffer != null) {
_wglDeleteBuffers(d.vertexBuffer);
@ -302,18 +343,197 @@ public class EaglercraftGPU {
GlStateManager.stateBlendEquation = equation;
}
}
private static IBufferArrayGL currentBufferArray = null;
public static final void bindGLBufferArray(IBufferArrayGL buffer) {
if(currentBufferArray != buffer) {
_wglBindVertexArray(buffer);
currentBufferArray = buffer;
public static final boolean areVAOsEmulated() {
return emulatedVAOs;
}
public static final IBufferArrayGL createGLBufferArray() {
if(emulatedVAOs) {
return new SoftGLBufferArray();
}else {
return _wglGenVertexArrays();
}
}
public static final void destroyGLBufferArray(IBufferArrayGL buffer) {
if(!emulatedVAOs) {
_wglDeleteVertexArrays(buffer);
}
}
public static final void enableVertexAttribArray(int index) {
if(emulatedVAOs) {
if(currentBufferArray == null) {
logger.warn("Skipping enable attrib with emulated VAO because no known VAO is bound!");
return;
}
((SoftGLBufferArray)currentBufferArray).enableAttrib(index, true);
}else {
_wglEnableVertexAttribArray(index);
}
}
public static final void disableVertexAttribArray(int index) {
if(emulatedVAOs) {
if(currentBufferArray == null) {
logger.warn("Skipping disable attrib with emulated VAO because no known VAO is bound!");
return;
}
((SoftGLBufferArray)currentBufferArray).enableAttrib(index, false);
}else {
_wglDisableVertexAttribArray(index);
}
}
public static final void vertexAttribPointer(int index, int size, int format, boolean normalized, int stride, int offset) {
if(emulatedVAOs) {
if(currentBufferArray == null) {
logger.warn("Skipping vertexAttribPointer with emulated VAO because no known VAO is bound!");
return;
}
if(currentVAOArrayBuffer == null) {
logger.warn("Skipping vertexAttribPointer with emulated VAO because no VAO array buffer is bound!");
return;
}
((SoftGLBufferArray)currentBufferArray).setAttrib(currentVAOArrayBuffer, index, size, format, normalized, stride, offset);
}else {
_wglVertexAttribPointer(index, size, format, normalized, stride, offset);
}
}
public static final void vertexAttribDivisor(int index, int divisor) {
if(emulatedVAOs) {
if(currentBufferArray == null) {
logger.warn("Skipping vertexAttribPointer with emulated VAO because no known VAO is bound!");
return;
}
((SoftGLBufferArray)currentBufferArray).setAttribDivisor(index, divisor);
}else {
_wglVertexAttribDivisor(index, divisor);
}
}
public static final void doDrawArrays(int mode, int first, int count) {
if(emulatedVAOs) {
if(currentBufferArray == null) {
logger.warn("Skipping draw call with emulated VAO because no known VAO is bound!");
return;
}
((SoftGLBufferArray)currentBufferArray).transitionToState(emulatedVAOState, false);
}
_wglDrawArrays(mode, first, count);
}
public static final void doDrawElements(int mode, int count, int type, int offset) {
if(emulatedVAOs) {
if(currentBufferArray == null) {
logger.warn("Skipping draw call with emulated VAO because no known VAO is bound!");
return;
}
((SoftGLBufferArray)currentBufferArray).transitionToState(emulatedVAOState, true);
}
_wglDrawElements(mode, count, type, offset);
}
public static final void doDrawArraysInstanced(int mode, int first, int count, int instances) {
if(emulatedVAOs) {
if(currentBufferArray == null) {
logger.warn("Skipping instanced draw call with emulated VAO because no known VAO is bound!");
return;
}
((SoftGLBufferArray)currentBufferArray).transitionToState(emulatedVAOState, false);
}
_wglDrawArraysInstanced(mode, first, count, instances);
}
public static final void doDrawElementsInstanced(int mode, int count, int type, int offset, int instances) {
if(emulatedVAOs) {
if(currentBufferArray == null) {
logger.warn("Skipping instanced draw call with emulated VAO because no known VAO is bound!");
return;
}
((SoftGLBufferArray)currentBufferArray).transitionToState(emulatedVAOState, true);
}
_wglDrawElementsInstanced(mode, count, type, offset, instances);
}
static IBufferArrayGL currentBufferArray = null;
private static IBufferGL currentArrayBuffer = null;
public static final void bindGLBufferArray(IBufferArrayGL buffer) {
if(emulatedVAOs) {
currentBufferArray = buffer;
}else {
if(currentBufferArray != buffer) {
_wglBindVertexArray(buffer);
currentBufferArray = buffer;
}
}
}
static IBufferGL currentArrayBuffer = null;
// only used when VAOs are emulated
static IBufferGL currentVAOArrayBuffer = null;
public static final void bindVAOGLArrayBuffer(IBufferGL buffer) {
if(emulatedVAOs) {
currentVAOArrayBuffer = buffer;
}else {
if(currentArrayBuffer != buffer) {
_wglBindBuffer(GL_ARRAY_BUFFER, buffer);
currentArrayBuffer = buffer;
}
}
}
public static final void bindVAOGLArrayBufferNow(IBufferGL buffer) {
if(emulatedVAOs) {
currentVAOArrayBuffer = buffer;
}
if(currentArrayBuffer != buffer) {
_wglBindBuffer(GL_ARRAY_BUFFER, buffer);
currentArrayBuffer = buffer;
}
}
public static final void bindVAOGLElementArrayBuffer(IBufferGL buffer) {
if(emulatedVAOs) {
if(currentBufferArray == null) {
logger.warn("Skipping set element array buffer with emulated VAO because no known VAO is bound!");
return;
}
((SoftGLBufferArray)currentBufferArray).setIndexBuffer(buffer);
}else {
_wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
}
}
static final void bindVAOGLElementArrayBufferNow(IBufferGL buffer) {
if(emulatedVAOs) {
if(currentBufferArray == null) {
logger.warn("Skipping set element array buffer with emulated VAO because no known VAO is bound!");
return;
}
((SoftGLBufferArray)currentBufferArray).setIndexBuffer(buffer);
if(currentEmulatedVAOIndexBuffer != buffer) {
_wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
currentEmulatedVAOIndexBuffer = buffer;
}
}else {
_wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
}
}
static IBufferGL currentEmulatedVAOIndexBuffer = null;
static final void bindEmulatedVAOIndexBuffer(IBufferGL buffer) {
if(currentEmulatedVAOIndexBuffer != buffer) {
_wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
currentEmulatedVAOIndexBuffer = buffer;
}
}
public static final void bindGLArrayBuffer(IBufferGL buffer) {
if(currentArrayBuffer != buffer) {
_wglBindBuffer(GL_ARRAY_BUFFER, buffer);
@ -321,7 +541,7 @@ public class EaglercraftGPU {
}
}
private static IBufferGL currentUniformBuffer = null;
static IBufferGL currentUniformBuffer = null;
public static final void bindGLUniformBuffer(IBufferGL buffer) {
if(currentUniformBuffer != buffer) {
@ -330,7 +550,7 @@ public class EaglercraftGPU {
}
}
private static IProgramGL currentShaderProgram = null;
static IProgramGL currentShaderProgram = null;
public static final void bindGLShaderProgram(IProgramGL prog) {
if(currentShaderProgram != prog) {
@ -352,6 +572,38 @@ public class EaglercraftGPU {
currentUniformBlockBindingSize[index] = size;
}
}
public static final int CLEAR_BINDING_TEXTURE = 1;
public static final int CLEAR_BINDING_TEXTURE0 = 2;
public static final int CLEAR_BINDING_ACTIVE_TEXTURE = 4;
public static final int CLEAR_BINDING_BUFFER_ARRAY = 8;
public static final int CLEAR_BINDING_ARRAY_BUFFER = 16;
public static final int CLEAR_BINDING_SHADER_PROGRAM = 32;
public static final void clearCurrentBinding(int mask) {
if((mask & CLEAR_BINDING_TEXTURE) != 0) {
int[] i = GlStateManager.boundTexture;
for(int j = 0; j < i.length; ++j) {
i[j] = -1;
}
}
if((mask & CLEAR_BINDING_TEXTURE0) != 0) {
GlStateManager.boundTexture[0] = -1;
}
if((mask & CLEAR_BINDING_ACTIVE_TEXTURE) != 0) {
GlStateManager.activeTexture = 0;
_wglActiveTexture(GL_TEXTURE0);
}
if((mask & CLEAR_BINDING_BUFFER_ARRAY) != 0) {
currentBufferArray = null;
}
if((mask & CLEAR_BINDING_ARRAY_BUFFER) != 0) {
currentArrayBuffer = currentVAOArrayBuffer = null;
}
if((mask & CLEAR_BINDING_SHADER_PROGRAM) != 0) {
currentShaderProgram = null;
}
}
public static final int ATTRIB_TEXTURE = 1;
public static final int ATTRIB_COLOR = 2;
@ -414,7 +666,7 @@ public class EaglercraftGPU {
if(newSize > 0xFFFF) {
newSize = 0xFFFF;
}
_wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf);
EaglercraftGPU.bindVAOGLElementArrayBufferNow(buf);
resizeQuad16EmulationBuffer(newSize >> 2);
}else {
int cnt = quad16EmulationBufferSize;
@ -423,10 +675,10 @@ public class EaglercraftGPU {
if(newSize > 0xFFFF) {
newSize = 0xFFFF;
}
_wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf);
EaglercraftGPU.bindVAOGLElementArrayBufferNow(buf);
resizeQuad16EmulationBuffer(newSize >> 2);
}else if(bind) {
_wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf);
EaglercraftGPU.bindVAOGLElementArrayBuffer(buf);
}
}
}
@ -436,16 +688,16 @@ public class EaglercraftGPU {
if(buf == null) {
quad32EmulationBuffer = buf = _wglGenBuffers();
int newSize = quad32EmulationBufferSize = (vertexCount & 0xFFFFC000) + 0x8000;
_wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf);
EaglercraftGPU.bindVAOGLElementArrayBufferNow(buf);
resizeQuad32EmulationBuffer(newSize >> 2);
}else {
int cnt = quad32EmulationBufferSize;
if(cnt < vertexCount) {
int newSize = quad32EmulationBufferSize = (vertexCount & 0xFFFFC000) + 0x8000;
_wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf);
EaglercraftGPU.bindVAOGLElementArrayBufferNow(buf);
resizeQuad32EmulationBuffer(newSize >> 2);
}else if(bind) {
_wglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf);
EaglercraftGPU.bindVAOGLElementArrayBuffer(buf);
}
}
}
@ -508,9 +760,19 @@ public class EaglercraftGPU {
p.drawElements(GL_TRIANGLES, mesh.indexCount, GL_UNSIGNED_SHORT, 0);
}
static int glesVers = -1;
static boolean hasFramebufferHDR16FSupport = false;
static boolean hasFramebufferHDR32FSupport = false;
static boolean hasLinearHDR16FSupport = false;
static boolean hasLinearHDR32FSupport = false;
static boolean fboRenderMipmapCapable = false;
static boolean vertexArrayCapable = false;
static boolean instancingCapable = false;
static boolean texStorageCapable = false;
static boolean textureLODCapable = false;
static boolean shader5Capable = false;
static boolean npotCapable = false;
static int uniformBufferOffsetAlignment = -1;
public static final void createFramebufferHDR16FTexture(int target, int level, int w, int h, int format, boolean allow32bitFallback) {
createFramebufferHDR16FTexture(target, level, w, h, format, allow32bitFallback, null);
@ -525,19 +787,24 @@ public class EaglercraftGPU {
int internalFormat;
switch(format) {
case GL_RED:
internalFormat = 0x822D; // GL_R16F
if(glesVers == 200) {
format = GL_LUMINANCE;
internalFormat = GL_LUMINANCE;
}else {
internalFormat = glesVers == 200 ? GL_LUMINANCE : 0x822D; // GL_R16F
}
break;
case 0x8227: // GL_RG
internalFormat = 0x822F; // GL_RG16F
internalFormat = glesVers == 200 ? 0x8227 : 0x822F; // GL_RG16F
case GL_RGB:
throw new UnsupportedOperationException("GL_RGB16F isn't supported specifically in WebGL 2.0 for some goddamn reason");
case GL_RGBA:
internalFormat = 0x881A; // GL_RGBA16F
internalFormat = glesVers == 200 ? GL_RGBA : 0x881A; // GL_RGBA16F
break;
default:
throw new UnsupportedOperationException("Unknown format: " + format);
}
_wglTexImage2Du16(target, level, internalFormat, w, h, 0, format, 0x140B, pixelData);
_wglTexImage2Du16(target, level, internalFormat, w, h, 0, format, glesVers == 200 ? 0x8D61 : 0x140B, pixelData);
}else {
if(allow32bitFallback) {
if(hasFramebufferHDR32FSupport) {
@ -567,7 +834,7 @@ public class EaglercraftGPU {
internalFormat = 0x822E; // GL_R32F
break;
case 0x8227: // GL_RG
internalFormat = 0x822F; // GL_RG32F
internalFormat = 0x8230; // GL_RG32F
case GL_RGB:
throw new UnsupportedOperationException("GL_RGB32F isn't supported specifically in WebGL 2.0 for some goddamn reason");
case GL_RGBA:
@ -594,19 +861,38 @@ public class EaglercraftGPU {
EaglercraftGPU.glGetString(7936);
EaglercraftGPU.glGetString(7937);
EaglercraftGPU.glGetString(7938);
glesVers = PlatformOpenGL.checkOpenGLESVersion();
vertexArrayCapable = PlatformOpenGL.checkVAOCapable();
emulatedVAOs = !vertexArrayCapable;
fboRenderMipmapCapable = PlatformOpenGL.checkFBORenderMipmapCapable();
instancingCapable = PlatformOpenGL.checkInstancingCapable();
texStorageCapable = PlatformOpenGL.checkTexStorageCapable();
textureLODCapable = PlatformOpenGL.checkTextureLODCapable();
shader5Capable = PlatformOpenGL.checkOESGPUShader5Capable() || PlatformOpenGL.checkEXTGPUShader5Capable();
npotCapable = PlatformOpenGL.checkNPOTCapable();
uniformBufferOffsetAlignment = glesVers >= 300 ? _wglGetInteger(0x8A34) : -1;
if(!npotCapable) {
logger.warn("NPOT texture support detected as false, texture wrapping must be set to GL_CLAMP_TO_EDGE if the texture's width or height is not a power of 2");
}
hasFramebufferHDR16FSupport = PlatformOpenGL.checkHDRFramebufferSupport(16);
if(hasFramebufferHDR16FSupport) {
logger.info("16-bit HDR render target support: true");
}else {
logger.error("16-bit HDR render target support: false");
}
hasLinearHDR16FSupport = PlatformOpenGL.checkLinearHDRFilteringSupport(16);
if(hasLinearHDR16FSupport) {
logger.info("16-bit HDR linear filter support: true");
}else {
logger.error("16-bit HDR linear filter support: false");
}
hasFramebufferHDR32FSupport = PlatformOpenGL.checkHDRFramebufferSupport(32);
if(hasFramebufferHDR32FSupport) {
logger.info("32-bit HDR render target support: true");
}else {
logger.error("32-bit HDR render target support: false");
}
hasLinearHDR32FSupport = PlatformOpenGL.checkLinearHDR32FSupport();
hasLinearHDR32FSupport = PlatformOpenGL.checkLinearHDRFilteringSupport(32);
if(hasLinearHDR32FSupport) {
logger.info("32-bit HDR linear filter support: true");
}else {
@ -615,16 +901,86 @@ public class EaglercraftGPU {
if(!checkHasHDRFramebufferSupportWithFilter()) {
logger.error("No HDR render target support was detected! Shaders will be disabled.");
}
if(emulatedVAOs) {
logger.info("Note: Could not unlock VAOs via OpenGL extensions, emulating them instead");
}
if(!instancingCapable) {
logger.info("Note: Could not unlock instancing via OpenGL extensions, using slow vanilla font and particle rendering");
}
emulatedVAOState = emulatedVAOs ? new SoftGLBufferState() : null;
PlatformOpenGL.enterVAOEmulationHook();
GLSLHeader.init();
DrawUtils.init();
SpriteLevelMixer.initialize();
InstancedFontRenderer.initialize();
InstancedParticleRenderer.initialize();
if(instancingCapable) {
InstancedFontRenderer.initialize();
InstancedParticleRenderer.initialize();
}
EffectPipelineFXAA.initialize();
TextureCopyUtil.initialize();
DrawUtils.vshLocal.free();
DrawUtils.vshLocal = null;
}
public static final void destroyCache() {
stringCache.clear();
mapTexturesGL.clear();
mapQueriesGL.clear();
mapDisplayListsGL.clear();
emulatedVAOs = false;
emulatedVAOState = null;
glesVers = -1;
fboRenderMipmapCapable = false;
vertexArrayCapable = false;
instancingCapable = false;
hasFramebufferHDR16FSupport = false;
hasFramebufferHDR32FSupport = false;
hasLinearHDR32FSupport = false;
GLSLHeader.destroy();
DrawUtils.destroy();
SpriteLevelMixer.destroy();
InstancedFontRenderer.destroy();
InstancedParticleRenderer.destroy();
EffectPipelineFXAA.destroy();
TextureCopyUtil.destroy();
}
public static final int checkOpenGLESVersion() {
return glesVers;
}
public static final boolean checkFBORenderMipmapCapable() {
return fboRenderMipmapCapable;
}
public static final boolean checkVAOCapable() {
return vertexArrayCapable;
}
public static final boolean checkInstancingCapable() {
return instancingCapable;
}
public static final boolean checkTexStorageCapable() {
return texStorageCapable;
}
public static final boolean checkTextureLODCapable() {
return textureLODCapable;
}
public static final boolean checkShader5Capable() {
return shader5Capable;
}
public static final boolean checkNPOTCapable() {
return npotCapable;
}
public static final int getUniformBufferOffsetAlignment() {
return uniformBufferOffsetAlignment;
}
public static final boolean checkHDRFramebufferSupport(int bits) {
switch(bits) {
case 16:
@ -636,15 +992,28 @@ public class EaglercraftGPU {
}
}
public static final boolean checkLinearHDRFilteringSupport(int bits) {
switch(bits) {
case 16:
return hasLinearHDR16FSupport;
case 32:
return hasLinearHDR32FSupport;
default:
return false;
}
}
public static final boolean checkHasHDRFramebufferSupport() {
return hasFramebufferHDR16FSupport || hasFramebufferHDR32FSupport;
}
public static final boolean checkHasHDRFramebufferSupportWithFilter() {
return hasFramebufferHDR16FSupport || (hasFramebufferHDR32FSupport && hasLinearHDR32FSupport);
return (hasFramebufferHDR16FSupport && hasLinearHDR16FSupport) || (hasFramebufferHDR32FSupport && hasLinearHDR32FSupport);
}
//legacy
public static final boolean checkLinearHDR32FSupport() {
return hasLinearHDR32FSupport;
}
}

View File

@ -8,7 +8,6 @@ import net.lax1dude.eaglercraft.v1_8.internal.IUniformGL;
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;
import net.lax1dude.eaglercraft.v1_8.opengl.FixedFunctionShader.FixedFunctionConstants;
import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*;
import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*;
@ -35,11 +34,13 @@ public class EffectPipelineFXAA {
private static final Logger logger = LogManager.getLogger("EffectPipelineFXAA");
public static final String fragmentShaderPath = "/assets/eagler/glsl/post_fxaa.fsh";
public static final String fragmentShaderPrecision = "precision lowp int;\nprecision mediump float;\nprecision mediump sampler2D;\n";
private static final int _GL_FRAMEBUFFER = 0x8D40;
private static final int _GL_RENDERBUFFER = 0x8D41;
private static final int _GL_COLOR_ATTACHMENT0 = 0x8CE0;
private static final int _GL_DEPTH_ATTACHMENT = 0x8D00;
private static final int _GL_DEPTH_COMPONENT16 = 0x81A5;
private static final int _GL_DEPTH_COMPONENT32F = 0x8CAC;
private static IProgramGL shaderProgram = null;
@ -53,14 +54,11 @@ public class EffectPipelineFXAA {
private static int currentHeight = -1;
static void initialize() {
String fragmentSource = EagRuntime.getResourceString(fragmentShaderPath);
if(fragmentSource == null) {
throw new RuntimeException("EffectPipelineFXAA shader \"" + fragmentShaderPath + "\" is missing!");
}
String fragmentSource = EagRuntime.getRequiredResourceString(fragmentShaderPath);
IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER);
_wglShaderSource(frag, FixedFunctionConstants.VERSION + "\n" + fragmentSource);
_wglShaderSource(frag, GLSLHeader.getFragmentHeaderCompat(fragmentSource, fragmentShaderPrecision));
_wglCompileShader(frag);
if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) {
@ -80,6 +78,10 @@ public class EffectPipelineFXAA {
_wglAttachShader(shaderProgram, DrawUtils.vshLocal);
_wglAttachShader(shaderProgram, frag);
if(EaglercraftGPU.checkOpenGLESVersion() == 200) {
VSHInputLayoutParser.applyLayout(shaderProgram, DrawUtils.vshLocalLayout);
}
_wglLinkProgram(shaderProgram);
_wglDetachShader(shaderProgram, DrawUtils.vshLocal);
@ -130,10 +132,10 @@ public class EffectPipelineFXAA {
currentHeight = height;
GlStateManager.bindTexture(framebufferColor);
_wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null);
EaglercraftGPU.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null);
_wglBindRenderbuffer(_GL_RENDERBUFFER, framebufferDepth);
_wglRenderbufferStorage(_GL_RENDERBUFFER, _GL_DEPTH_COMPONENT32F, width, height);
_wglRenderbufferStorage(_GL_RENDERBUFFER, EaglercraftGPU.checkOpenGLESVersion() == 200 ? _GL_DEPTH_COMPONENT16 : _GL_DEPTH_COMPONENT32F, width, height);
}
_wglBindFramebuffer(_GL_FRAMEBUFFER, framebuffer);
@ -155,4 +157,24 @@ public class EffectPipelineFXAA {
DrawUtils.drawStandardQuad2D();
}
public static void destroy() {
if(shaderProgram != null) {
_wglDeleteProgram(shaderProgram);
shaderProgram = null;
}
u_screenSize2f = null;
if(framebuffer != null) {
_wglDeleteFramebuffer(framebuffer);
framebuffer = null;
}
if(framebufferColor != -1) {
GlStateManager.deleteTexture(framebufferColor);
framebufferColor = -2;
}
if(framebufferDepth != null) {
_wglDeleteRenderbuffer(framebufferDepth);
framebufferDepth = null;
}
}
}

View File

@ -98,33 +98,33 @@ public class FixedFunctionPipeline {
}
EaglercraftGPU.bindGLBufferArray(list.vertexArray);
EaglercraftGPU.bindGLArrayBuffer(list.vertexBuffer);
EaglercraftGPU.bindVAOGLArrayBuffer(list.vertexBuffer);
_wglEnableVertexAttribArray(0);
_wglVertexAttribPointer(0, VertexFormat.COMPONENT_POSITION_SIZE,
EaglercraftGPU.enableVertexAttribArray(0);
EaglercraftGPU.vertexAttribPointer(0, VertexFormat.COMPONENT_POSITION_SIZE,
VertexFormat.COMPONENT_POSITION_FORMAT, false, self.attribStride, 0);
if(self.attribTextureIndex != -1) {
_wglEnableVertexAttribArray(self.attribTextureIndex);
_wglVertexAttribPointer(self.attribTextureIndex, VertexFormat.COMPONENT_TEX_SIZE,
EaglercraftGPU.enableVertexAttribArray(self.attribTextureIndex);
EaglercraftGPU.vertexAttribPointer(self.attribTextureIndex, VertexFormat.COMPONENT_TEX_SIZE,
VertexFormat.COMPONENT_TEX_FORMAT, false, self.attribStride, self.attribTextureOffset);
}
if(self.attribColorIndex != -1) {
_wglEnableVertexAttribArray(self.attribColorIndex);
_wglVertexAttribPointer(self.attribColorIndex, VertexFormat.COMPONENT_COLOR_SIZE,
EaglercraftGPU.enableVertexAttribArray(self.attribColorIndex);
EaglercraftGPU.vertexAttribPointer(self.attribColorIndex, VertexFormat.COMPONENT_COLOR_SIZE,
VertexFormat.COMPONENT_COLOR_FORMAT, true, self.attribStride, self.attribColorOffset);
}
if(self.attribNormalIndex != -1) {
_wglEnableVertexAttribArray(self.attribNormalIndex);
_wglVertexAttribPointer(self.attribNormalIndex, VertexFormat.COMPONENT_NORMAL_SIZE,
EaglercraftGPU.enableVertexAttribArray(self.attribNormalIndex);
EaglercraftGPU.vertexAttribPointer(self.attribNormalIndex, VertexFormat.COMPONENT_NORMAL_SIZE,
VertexFormat.COMPONENT_NORMAL_FORMAT, true, self.attribStride, self.attribNormalOffset);
}
if(self.attribLightmapIndex != -1) {
_wglEnableVertexAttribArray(self.attribLightmapIndex);
_wglVertexAttribPointer(self.attribLightmapIndex, VertexFormat.COMPONENT_LIGHTMAP_SIZE,
EaglercraftGPU.enableVertexAttribArray(self.attribLightmapIndex);
EaglercraftGPU.vertexAttribPointer(self.attribLightmapIndex, VertexFormat.COMPONENT_LIGHTMAP_SIZE,
VertexFormat.COMPONENT_LIGHTMAP_FORMAT, false, self.attribStride, self.attribLightmapOffset);
}
@ -145,7 +145,7 @@ public class FixedFunctionPipeline {
void drawArrays(int mode, int offset, int count) {
EaglercraftGPU.bindGLShaderProgram(shaderProgram);
PlatformOpenGL._wglDrawArrays(mode, offset, count);
EaglercraftGPU.doDrawArrays(mode, offset, count);
}
void drawDirectArrays(int mode, int offset, int count) {
@ -160,7 +160,7 @@ public class FixedFunctionPipeline {
}else {
EaglercraftGPU.attachQuad32EmulationBuffer(count, false);
}
PlatformOpenGL._wglDrawElements(GL_TRIANGLES, count + (count >> 1),
EaglercraftGPU.doDrawElements(GL_TRIANGLES, count + (count >> 1),
GL_UNSIGNED_INT, 0);
}else {
if(!sb.bindQuad16) {
@ -170,17 +170,17 @@ public class FixedFunctionPipeline {
}else {
EaglercraftGPU.attachQuad16EmulationBuffer(count, false);
}
PlatformOpenGL._wglDrawElements(GL_TRIANGLES, count + (count >> 1),
EaglercraftGPU.doDrawElements(GL_TRIANGLES, count + (count >> 1),
GL_UNSIGNED_SHORT, 0);
}
}else {
PlatformOpenGL._wglDrawArrays(mode, offset, count);
EaglercraftGPU.doDrawArrays(mode, offset, count);
}
}
void drawElements(int mode, int count, int type, int offset) {
EaglercraftGPU.bindGLShaderProgram(shaderProgram);
PlatformOpenGL._wglDrawElements(mode, count, type, offset);
EaglercraftGPU.doDrawElements(mode, count, type, offset);
}
private static IExtPipelineCompiler extensionProvider;
@ -192,7 +192,7 @@ public class FixedFunctionPipeline {
private static final FixedFunctionPipeline[] pipelineStateCache = new FixedFunctionPipeline[fixedFunctionStatesBits + 1];
private static final FixedFunctionPipeline[][] pipelineExtStateCache = new FixedFunctionPipeline[fixedFunctionStatesBits + 1][];
private static final List<FixedFunctionPipeline> pipelineListTracker = new ArrayList(1024);
private static final List<FixedFunctionPipeline> pipelineListTracker = new ArrayList<>(1024);
private static String shaderSourceCacheVSH = null;
private static String shaderSourceCacheFSH = null;
@ -232,22 +232,16 @@ public class FixedFunctionPipeline {
fshSource = extSource[1];
}else {
if(shaderSourceCacheVSH == null) {
shaderSourceCacheVSH = EagRuntime.getResourceString(FILENAME_VSH);
if(shaderSourceCacheVSH == null) {
throw new RuntimeException("Could not load: " + FILENAME_VSH);
}
shaderSourceCacheVSH = EagRuntime.getRequiredResourceString(FILENAME_VSH);
}
vshSource = shaderSourceCacheVSH;
if(shaderSourceCacheFSH == null) {
shaderSourceCacheFSH = EagRuntime.getResourceString(FILENAME_FSH);
if(shaderSourceCacheFSH == null) {
throw new RuntimeException("Could not load: " + FILENAME_FSH);
}
shaderSourceCacheFSH = EagRuntime.getRequiredResourceString(FILENAME_FSH);
}
fshSource = shaderSourceCacheFSH;
}
StringBuilder macros = new StringBuilder(VERSION + "\n");
StringBuilder macros = new StringBuilder();
if((coreBits & STATE_HAS_ATTRIB_TEXTURE) != 0) {
macros.append("#define " + MACRO_ATTRIB_TEXTURE + "\n");
}
@ -291,7 +285,8 @@ public class FixedFunctionPipeline {
IShaderGL vsh = _wglCreateShader(GL_VERTEX_SHADER);
_wglShaderSource(vsh, macros.toString() + vshSource);
String macrosStr = macros.toString();
_wglShaderSource(vsh, GLSLHeader.getVertexHeaderCompat(vshSource, macrosStr));
_wglCompileShader(vsh);
if(_wglGetShaderi(vsh, GL_COMPILE_STATUS) != GL_TRUE) {
@ -309,7 +304,7 @@ public class FixedFunctionPipeline {
IShaderGL fsh = _wglCreateShader(GL_FRAGMENT_SHADER);
_wglShaderSource(fsh, macros.toString() + fshSource);
_wglShaderSource(fsh, GLSLHeader.getFragmentHeaderCompat(fshSource, macrosStr));
_wglCompileShader(fsh);
if(_wglGetShaderi(fsh, GL_COMPILE_STATUS) != GL_TRUE) {
@ -581,45 +576,45 @@ public class FixedFunctionPipeline {
streamBuffer = new StreamBuffer(FixedFunctionShader.initialSize, FixedFunctionShader.initialCount,
FixedFunctionShader.maxCount, (vertexArray, vertexBuffer) -> {
EaglercraftGPU.bindGLBufferArray(vertexArray);
EaglercraftGPU.bindGLArrayBuffer(vertexBuffer);
EaglercraftGPU.bindVAOGLArrayBuffer(vertexBuffer);
_wglEnableVertexAttribArray(0);
_wglVertexAttribPointer(0, VertexFormat.COMPONENT_POSITION_SIZE,
EaglercraftGPU.enableVertexAttribArray(0);
EaglercraftGPU.vertexAttribPointer(0, VertexFormat.COMPONENT_POSITION_SIZE,
VertexFormat.COMPONENT_POSITION_FORMAT, false, attribStride, 0);
if(attribTextureIndex != -1) {
_wglEnableVertexAttribArray(attribTextureIndex);
_wglVertexAttribPointer(attribTextureIndex, VertexFormat.COMPONENT_TEX_SIZE,
EaglercraftGPU.enableVertexAttribArray(attribTextureIndex);
EaglercraftGPU.vertexAttribPointer(attribTextureIndex, VertexFormat.COMPONENT_TEX_SIZE,
VertexFormat.COMPONENT_TEX_FORMAT, false, attribStride, attribTextureOffset);
}
if(attribColorIndex != -1) {
_wglEnableVertexAttribArray(attribColorIndex);
_wglVertexAttribPointer(attribColorIndex, VertexFormat.COMPONENT_COLOR_SIZE,
EaglercraftGPU.enableVertexAttribArray(attribColorIndex);
EaglercraftGPU.vertexAttribPointer(attribColorIndex, VertexFormat.COMPONENT_COLOR_SIZE,
VertexFormat.COMPONENT_COLOR_FORMAT, true, attribStride, attribColorOffset);
}
if(attribNormalIndex != -1) {
_wglEnableVertexAttribArray(attribNormalIndex);
_wglVertexAttribPointer(attribNormalIndex, VertexFormat.COMPONENT_NORMAL_SIZE,
EaglercraftGPU.enableVertexAttribArray(attribNormalIndex);
EaglercraftGPU.vertexAttribPointer(attribNormalIndex, VertexFormat.COMPONENT_NORMAL_SIZE,
VertexFormat.COMPONENT_NORMAL_FORMAT, true, attribStride, attribNormalOffset);
}
if(attribLightmapIndex != -1) {
_wglEnableVertexAttribArray(attribLightmapIndex);
_wglVertexAttribPointer(attribLightmapIndex, VertexFormat.COMPONENT_LIGHTMAP_SIZE,
EaglercraftGPU.enableVertexAttribArray(attribLightmapIndex);
EaglercraftGPU.vertexAttribPointer(attribLightmapIndex, VertexFormat.COMPONENT_LIGHTMAP_SIZE,
VertexFormat.COMPONENT_LIGHTMAP_FORMAT, false, attribStride, attribLightmapOffset);
}
});
stateEnableTexture2D = (bits & STATE_ENABLE_TEXTURE2D) == STATE_ENABLE_TEXTURE2D;
stateEnableLightmap = (bits & STATE_ENABLE_LIGHTMAP) == STATE_ENABLE_LIGHTMAP;
stateEnableAlphaTest = (bits & STATE_ENABLE_ALPHA_TEST) == STATE_ENABLE_ALPHA_TEST;
stateEnableMCLighting = (bits & STATE_ENABLE_MC_LIGHTING) == STATE_ENABLE_MC_LIGHTING;
stateEnableEndPortal = (bits & STATE_ENABLE_END_PORTAL) == STATE_ENABLE_END_PORTAL;
stateEnableAnisotropicFix = (bits & STATE_ENABLE_ANISOTROPIC_FIX) == STATE_ENABLE_ANISOTROPIC_FIX;
stateEnableFog = (bits & STATE_ENABLE_FOG) == STATE_ENABLE_FOG;
stateEnableBlendAdd = (bits & STATE_ENABLE_BLEND_ADD) == STATE_ENABLE_BLEND_ADD;
stateEnableTexture2D = (bits & STATE_ENABLE_TEXTURE2D) != 0;
stateEnableLightmap = (bits & STATE_ENABLE_LIGHTMAP) != 0;
stateEnableAlphaTest = (bits & STATE_ENABLE_ALPHA_TEST) != 0;
stateEnableMCLighting = (bits & STATE_ENABLE_MC_LIGHTING) != 0;
stateEnableEndPortal = (bits & STATE_ENABLE_END_PORTAL) != 0;
stateEnableAnisotropicFix = (bits & STATE_ENABLE_ANISOTROPIC_FIX) != 0;
stateEnableFog = (bits & STATE_ENABLE_FOG) != 0;
stateEnableBlendAdd = (bits & STATE_ENABLE_BLEND_ADD) != 0;
for(int i = 0; i < stateLightsVectors.length; ++i) {
stateLightsVectors[i] = new Vector4f(-999.0f, -999.0f, -999.0f, 0.0f);
@ -1068,7 +1063,6 @@ public class FixedFunctionPipeline {
}
static void optimize() {
FixedFunctionPipeline pp;
for(int i = 0, l = pipelineListTracker.size(); i < l; ++i) {
pipelineListTracker.get(i).streamBuffer.optimize();
}

View File

@ -44,7 +44,6 @@ public class FixedFunctionShader {
public class FixedFunctionConstants {
public static final String VERSION = "#version 300 es";
public static final String FILENAME_VSH = "/assets/eagler/glsl/core.vsh";
public static final String FILENAME_FSH = "/assets/eagler/glsl/core.fsh";

View File

@ -0,0 +1,99 @@
package net.lax1dude.eaglercraft.v1_8.opengl;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL;
/**
* 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 GLSLHeader {
public static final String GLES2_COMPAT_FILE_NAME = "/assets/eagler/glsl/gles2_compat.glsl";
private static String header = null;
private static String gles2CompatFile = null;
static void init() {
gles2CompatFile = EagRuntime.getRequiredResourceString(GLES2_COMPAT_FILE_NAME);
int glesVersion = EaglercraftGPU.checkOpenGLESVersion();
StringBuilder headerBuilder;
if(glesVersion >= 310) {
headerBuilder = new StringBuilder("#version 310 es");
boolean oes5 = PlatformOpenGL.checkOESGPUShader5Capable();
boolean ext5 = !oes5 && PlatformOpenGL.checkEXTGPUShader5Capable();
if(oes5) {
headerBuilder.append("\n#extension GL_OES_gpu_shader5 : enable");
}else if(ext5) {
headerBuilder.append("\n#extension GL_EXT_gpu_shader5 : enable");
}
headerBuilder.append("\n#define EAGLER_IS_GLES_310");
headerBuilder.append("\n#define EAGLER_HAS_GLES_310");
headerBuilder.append("\n#define EAGLER_HAS_GLES_300");
headerBuilder.append("\n#define EAGLER_HAS_GLES_200");
if(oes5 || ext5) {
headerBuilder.append("\n#define EAGLER_HAS_GLES_310_SHADER_5");
}
}else if(glesVersion == 300) {
headerBuilder = new StringBuilder("#version 300 es");
headerBuilder.append("\n#define EAGLER_IS_GLES_300");
headerBuilder.append("\n#define EAGLER_HAS_GLES_300");
headerBuilder.append("\n#define EAGLER_HAS_GLES_200");
}else if(glesVersion == 200) {
boolean texLOD = PlatformOpenGL.checkTextureLODCapable();
headerBuilder = new StringBuilder("#version 100");
if(texLOD) {
headerBuilder.append("\n#extension GL_EXT_shader_texture_lod : enable");
}
headerBuilder.append("\n#define EAGLER_HAS_GLES_200");
headerBuilder.append("\n#define EAGLER_IS_GLES_200");
if(texLOD) {
headerBuilder.append("\n#define EAGLER_HAS_GLES_200_SHADER_TEXTURE_LOD");
}
}else {
throw new IllegalStateException("Unsupported OpenGL ES version: " + glesVersion);
}
header = headerBuilder.append('\n').toString();
}
static void destroy() {
header = null;
}
public static String getHeader() {
if(header == null) throw new IllegalStateException();
return header;
}
public static String getVertexHeader(String shaderSrc) {
if(header == null) throw new IllegalStateException();
return header + "#define EAGLER_IS_VERTEX_SHADER\n" + shaderSrc;
}
public static String getFragmentHeader(String shaderSrc) {
if(header == null) throw new IllegalStateException();
return header + "#define EAGLER_IS_FRAGMENT_SHADER\n" + shaderSrc;
}
public static String getVertexHeaderCompat(String shaderSrc, String precisions) {
if(header == null || gles2CompatFile == null) throw new IllegalStateException();
return header + "#define EAGLER_IS_VERTEX_SHADER\n" + (precisions == null ? "" : precisions + "\n") + gles2CompatFile + "\n" + shaderSrc;
}
public static String getFragmentHeaderCompat(String shaderSrc, String precisions) {
if(header == null || gles2CompatFile == null) throw new IllegalStateException();
return header + "#define EAGLER_IS_FRAGMENT_SHADER\n"+ (precisions == null ? "" : precisions + "\n") + gles2CompatFile + "\n" + shaderSrc;
}
}

View File

@ -5,10 +5,13 @@ import net.lax1dude.eaglercraft.v1_8.internal.IRenderbufferGL;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*;
/**
* Copyright (c) 2022-2023 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
@ -40,10 +43,20 @@ public class GameOverlayFramebuffer {
private int framebufferColor = -1;
public void beginRender(int width, int height) {
private final boolean enableDepth;
public GameOverlayFramebuffer() {
this(true);
}
public GameOverlayFramebuffer(boolean enableDepth) {
this.enableDepth = enableDepth;
}
public boolean beginRender(int width, int height) {
if(framebuffer == null) {
framebuffer = _wglCreateFramebuffer();
depthBuffer = _wglCreateRenderbuffer();
depthBuffer = enableDepth ? _wglCreateRenderbuffer() : null;
framebufferColor = GlStateManager.generateTexture();
_wglBindFramebuffer(_GL_FRAMEBUFFER, framebuffer);
GlStateManager.bindTexture(framebufferColor);
@ -52,29 +65,35 @@ public class GameOverlayFramebuffer {
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
_wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, EaglercraftGPU.getNativeTexture(framebufferColor), 0);
_wglBindRenderbuffer(_GL_RENDERBUFFER, depthBuffer);
_wglFramebufferRenderbuffer(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, _GL_RENDERBUFFER, depthBuffer);
if(enableDepth) {
_wglBindRenderbuffer(_GL_RENDERBUFFER, depthBuffer);
_wglFramebufferRenderbuffer(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, _GL_RENDERBUFFER, depthBuffer);
}
}
if(currentWidth != width || currentHeight != height) {
boolean resized = currentWidth != width || currentHeight != height;
if(resized) {
currentWidth = width;
currentHeight = height;
GlStateManager.bindTexture(framebufferColor);
_wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null);
_wglBindRenderbuffer(_GL_RENDERBUFFER, depthBuffer);
_wglRenderbufferStorage(_GL_RENDERBUFFER, _GL_DEPTH_COMPONENT16, width, height);
EaglercraftGPU.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null);
if(enableDepth) {
_wglBindRenderbuffer(_GL_RENDERBUFFER, depthBuffer);
_wglRenderbufferStorage(_GL_RENDERBUFFER, _GL_DEPTH_COMPONENT16, width, height);
}
}
_wglBindFramebuffer(_GL_FRAMEBUFFER, framebuffer);
return resized;
}
public void endRender() {
_wglBindFramebuffer(_GL_FRAMEBUFFER, null);
age = System.currentTimeMillis();
age = EagRuntime.steadyTimeMillis();
}
public long getAge() {
return age == -1l ? -1l : (System.currentTimeMillis() - age);
return age == -1l ? -1l : (EagRuntime.steadyTimeMillis() - age);
}
public int getTexture() {
@ -84,7 +103,9 @@ public class GameOverlayFramebuffer {
public void destroy() {
if(framebuffer != null) {
_wglDeleteFramebuffer(framebuffer);
_wglDeleteRenderbuffer(depthBuffer);
if(enableDepth) {
_wglDeleteRenderbuffer(depthBuffer);
}
GlStateManager.deleteTexture(framebufferColor);
framebuffer = null;
depthBuffer = null;

View File

@ -30,10 +30,12 @@ public class GlStateManager {
static final Logger logger = LogManager.getLogger("GlStateManager");
static boolean stateDepthTest = false;
static boolean stateDepthTestStash = false;
static int stateDepthFunc = -1;
static boolean stateDepthMask = true;
static boolean stateCull = false;
static boolean stateCullStash = false;
static int stateCullFace = GL_BACK;
static boolean statePolygonOffset = false;
@ -58,6 +60,7 @@ public class GlStateManager {
static boolean stateEnableShaderBlendColor = false;
static boolean stateBlend = false;
static boolean stateBlendStash = false;
static boolean stateGlobalBlend = true;
static int stateBlendEquation = -1;
static int stateBlendSRC = -1;
@ -312,6 +315,30 @@ public class GlStateManager {
}
}
public static final void eagPushStateForGLES2BlitHack() {
stateDepthTestStash = stateDepthTest;
stateCullStash = stateCull;
stateBlendStash = stateBlend;
}
public static final void eagPopStateForGLES2BlitHack() {
if(stateDepthTestStash) {
enableDepth();
}else {
disableDepth();
}
if(stateCullStash) {
enableCull();
}else {
disableCull();
}
if(stateBlendStash) {
enableBlend();
}else {
disableBlend();
}
}
public static final void depthFunc(int depthFunc) {
int rev = depthFunc;
switch(depthFunc) {
@ -603,7 +630,9 @@ public class GlStateManager {
f2 = f1;
}
_wglBindTexture(GL_TEXTURE_2D, null);
_wglBindTexture(GL_TEXTURE_3D, null);
if(EaglercraftGPU.checkOpenGLESVersion() >= 300) {
_wglBindTexture(GL_TEXTURE_3D, null);
}
boundTexture[i] = -1;
}
}

View File

@ -56,6 +56,22 @@ public class ImageData {
return new ImageData(pw, ph, img, alpha);
}
public static String getMimeFromType(String nameOrPath) {
if(nameOrPath == null) return "image/png";
nameOrPath = nameOrPath.toLowerCase();
if(nameOrPath.endsWith(".png")) {
return "image/png";
}else if(nameOrPath.endsWith(".jpg") || nameOrPath.endsWith(".jpeg")) {
return "image/jpeg";
}else if(nameOrPath.endsWith(".gif")) {
return "image/gif";
}else if(nameOrPath.endsWith(".bmp")) {
return "image/bmp";
}else {
return "image/png"; // rip
}
}
public static final ImageData loadImageFile(String path) {
byte[] fileData = EagRuntime.getResourceBytes(path);
if(fileData != null) {
@ -73,6 +89,23 @@ public class ImageData {
return PlatformAssets.loadImageFile(data);
}
public static final ImageData loadImageFile(String path, String mime) {
byte[] fileData = EagRuntime.getResourceBytes(path);
if(fileData != null) {
return loadImageFile(fileData, mime);
}else {
return null;
}
}
public static final ImageData loadImageFile(InputStream data, String mime) {
return PlatformAssets.loadImageFile(data, mime);
}
public static final ImageData loadImageFile(byte[] data, String mime) {
return PlatformAssets.loadImageFile(data, mime);
}
public void getRGB(int startX, int startY, int w, int h,
int[] rgbArray, int offset, int scansize) {
for(int y = 0; y < h; ++y) {
@ -147,5 +180,22 @@ public class ImageData {
public static int swapRB(int c) {
return (c & 0xFF00FF00) | ((c & 0x00FF0000) >>> 16) | ((c & 0x000000FF) << 16);
}
public static int[] swapRB(int[] arr) {
for(int i = 0; i < arr.length; ++i) {
int j = arr[i];
arr[i] = (j & 0xFF00FF00) | ((j & 0x00FF0000) >>> 16) |
((j & 0x000000FF) << 16);
}
return arr;
}
public boolean isNPOT() {
return (width & (width - 1)) != 0 || (height & (height - 1)) != 0;
}
public static boolean isNPOTStatic(int w, int h) {
return (w & (w - 1)) != 0 || (h & (h - 1)) != 0;
}
}

View File

@ -13,13 +13,12 @@ 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.FixedFunctionShader.FixedFunctionConstants;
import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.DeferredStateManager;
import net.lax1dude.eaglercraft.v1_8.vector.Matrix4f;
import net.lax1dude.eaglercraft.v1_8.vector.Vector4f;
/**
* 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
@ -38,7 +37,10 @@ public class InstancedFontRenderer {
private static final Logger logger = LogManager.getLogger("InstancedFontRenderer");
public static final String vertexShaderPath = "/assets/eagler/glsl/accel_font.vsh";
public static final String vertexShaderPrecision = "precision lowp int;\nprecision highp float;\nprecision mediump sampler2D;\n";
public static final String fragmentShaderPath = "/assets/eagler/glsl/accel_font.fsh";
public static final String fragmentShaderPrecision = "precision lowp int;\nprecision highp float;\nprecision mediump sampler2D;\n";
private static final int BYTES_PER_CHARACTER = 10;
private static final int CHARACTER_LIMIT = 6553;
@ -78,21 +80,13 @@ public class InstancedFontRenderer {
private static float charCoordHeightValue = -1.0f;
static void initialize() {
String vertexSource = EagRuntime.getResourceString(vertexShaderPath);
if(vertexSource == null) {
throw new RuntimeException("InstancedFontRenderer shader \"" + vertexShaderPath + "\" is missing!");
}
String fragmentSource = EagRuntime.getResourceString(fragmentShaderPath);
if(fragmentSource == null) {
throw new RuntimeException("InstancedFontRenderer shader \"" + fragmentShaderPath + "\" is missing!");
}
String vertexSource = EagRuntime.getRequiredResourceString(vertexShaderPath);
String fragmentSource = EagRuntime.getRequiredResourceString(fragmentShaderPath);
IShaderGL vert = _wglCreateShader(GL_VERTEX_SHADER);
IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER);
_wglShaderSource(vert, FixedFunctionConstants.VERSION + "\n" + vertexSource);
_wglShaderSource(vert, GLSLHeader.getVertexHeaderCompat(vertexSource, vertexShaderPrecision));
_wglCompileShader(vert);
if(_wglGetShaderi(vert, GL_COMPILE_STATUS) != GL_TRUE) {
@ -107,7 +101,7 @@ public class InstancedFontRenderer {
throw new IllegalStateException("Vertex shader \"" + vertexShaderPath + "\" could not be compiled!");
}
_wglShaderSource(frag, FixedFunctionConstants.VERSION + "\n" + fragmentSource);
_wglShaderSource(frag, GLSLHeader.getFragmentHeaderCompat(fragmentSource, fragmentShaderPrecision));
_wglCompileShader(frag);
if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) {
@ -127,6 +121,10 @@ public class InstancedFontRenderer {
_wglAttachShader(shaderProgram, vert);
_wglAttachShader(shaderProgram, frag);
if(EaglercraftGPU.checkOpenGLESVersion() == 200) {
VSHInputLayoutParser.applyLayout(shaderProgram, VSHInputLayoutParser.getShaderInputs(vertexSource));
}
_wglLinkProgram(shaderProgram);
_wglDetachShader(shaderProgram, vert);
@ -161,60 +159,62 @@ public class InstancedFontRenderer {
_wglUniform1i(_wglGetUniformLocation(shaderProgram, "u_inputTexture"), 0);
vertexArray = _wglGenVertexArrays();
vertexArray = EaglercraftGPU.createGLBufferArray();
vertexBuffer = _wglGenBuffers();
instancesBuffer = _wglGenBuffers();
FloatBuffer verts = EagRuntime.allocateFloatBuffer(108);
float paddingA = 0.005f;
float paddingB = 1.0f - paddingA;
verts.put(new float[] {
// (0 - 6 - 12) regular:
0.0f, 0.0f, 0.25f, 0.0f, 1.0f, 0.25f, 1.0f, 0.0f, 0.25f,
1.0f, 0.0f, 0.25f, 0.0f, 1.0f, 0.25f, 1.0f, 1.0f, 0.25f,
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,
paddingA, paddingA, 0.25f, paddingA, paddingB, 0.25f, paddingB, paddingA, 0.25f,
paddingB, paddingA, 0.25f, paddingA, paddingB, 0.25f, paddingB, paddingB, 0.25f,
paddingA, paddingA, 0.0f, paddingA, paddingB, 0.0f, paddingB, paddingA, 0.0f,
paddingB, paddingA, 0.0f, paddingA, paddingB, 0.0f, paddingB, paddingB, 0.0f,
// (12 - 24 - 36) bold shadow:
0.0f, 0.0f, 0.25f, 0.0f, 1.0f, 0.25f, 1.0f, 0.0f, 0.25f,
1.0f, 0.0f, 0.25f, 0.0f, 1.0f, 0.25f, 1.0f, 1.0f, 0.25f,
0.0f, 0.0f, 0.75f, 0.0f, 1.0f, 0.75f, 1.0f, 0.0f, 0.75f,
1.0f, 0.0f, 0.75f, 0.0f, 1.0f, 0.75f, 1.0f, 1.0f, 0.75f,
paddingA, paddingA, 0.25f, paddingA, paddingB, 0.25f, paddingB, paddingA, 0.25f,
paddingB, paddingA, 0.25f, paddingA, paddingB, 0.25f, paddingB, paddingB, 0.25f,
paddingA, paddingA, 0.75f, paddingA, paddingB, 0.75f, paddingB, paddingA, 0.75f,
paddingB, paddingA, 0.75f, paddingA, paddingB, 0.75f, paddingB, paddingB, 0.75f,
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.5f, 0.0f, 1.0f, 0.5f, 1.0f, 0.0f, 0.5f,
1.0f, 0.0f, 0.5f, 0.0f, 1.0f, 0.5f, 1.0f, 1.0f, 0.5f
paddingA, paddingA, 0.0f, paddingA, paddingB, 0.0f, paddingB, paddingA, 0.0f,
paddingB, paddingA, 0.0f, paddingA, paddingB, 0.0f, paddingB, paddingB, 0.0f,
paddingA, paddingA, 0.5f, paddingA, paddingB, 0.5f, paddingB, paddingA, 0.5f,
paddingB, paddingA, 0.5f, paddingA, paddingB, 0.5f, paddingB, paddingB, 0.5f
});
verts.flip();
EaglercraftGPU.bindGLBufferArray(vertexArray);
EaglercraftGPU.bindGLArrayBuffer(vertexBuffer);
EaglercraftGPU.bindVAOGLArrayBufferNow(vertexBuffer);
_wglBufferData(GL_ARRAY_BUFFER, verts, GL_STATIC_DRAW);
EagRuntime.freeFloatBuffer(verts);
_wglEnableVertexAttribArray(0);
_wglVertexAttribPointer(0, 3, GL_FLOAT, false, 12, 0);
_wglVertexAttribDivisor(0, 0);
EaglercraftGPU.enableVertexAttribArray(0);
EaglercraftGPU.vertexAttribPointer(0, 3, GL_FLOAT, false, 12, 0);
EaglercraftGPU.vertexAttribDivisor(0, 0);
EaglercraftGPU.bindGLArrayBuffer(instancesBuffer);
EaglercraftGPU.bindVAOGLArrayBufferNow(instancesBuffer);
_wglBufferData(GL_ARRAY_BUFFER, fontDataBuffer.remaining(), GL_STREAM_DRAW);
_wglEnableVertexAttribArray(1);
_wglVertexAttribPointer(1, 2, GL_SHORT, false, 10, 0);
_wglVertexAttribDivisor(1, 1);
EaglercraftGPU.enableVertexAttribArray(1);
EaglercraftGPU.vertexAttribPointer(1, 2, GL_SHORT, false, 10, 0);
EaglercraftGPU.vertexAttribDivisor(1, 1);
_wglEnableVertexAttribArray(2);
_wglVertexAttribPointer(2, 2, GL_UNSIGNED_BYTE, false, 10, 4);
_wglVertexAttribDivisor(2, 1);
EaglercraftGPU.enableVertexAttribArray(2);
EaglercraftGPU.vertexAttribPointer(2, 2, GL_UNSIGNED_BYTE, false, 10, 4);
EaglercraftGPU.vertexAttribDivisor(2, 1);
_wglEnableVertexAttribArray(3);
_wglVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, true, 10, 6);
_wglVertexAttribDivisor(3, 1);
EaglercraftGPU.enableVertexAttribArray(3);
EaglercraftGPU.vertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, true, 10, 6);
EaglercraftGPU.vertexAttribDivisor(3, 1);
}
@ -381,7 +381,7 @@ public class InstancedFontRenderer {
fontDataBuffer.position(p);
fontDataBuffer.limit(l);
_wglDrawArraysInstanced(GL_TRIANGLES, shadow ? 0 : 6, shadow ? 12 : 6, charactersDrawn);
EaglercraftGPU.doDrawArraysInstanced(GL_TRIANGLES, shadow ? 0 : 6, shadow ? 12 : 6, charactersDrawn);
}
if(boldCharactersDrawn > 0) {
@ -394,7 +394,7 @@ public class InstancedFontRenderer {
fontBoldDataBuffer.position(p);
fontBoldDataBuffer.limit(l);
_wglDrawArraysInstanced(GL_TRIANGLES, shadow ? 12 : 24, shadow ? 24 : 12, boldCharactersDrawn);
EaglercraftGPU.doDrawArraysInstanced(GL_TRIANGLES, shadow ? 12 : 24, shadow ? 24 : 12, boldCharactersDrawn);
}
}
@ -455,4 +455,40 @@ public class InstancedFontRenderer {
if(y > heightCalcMost || heightCalcMost == Integer.MAX_VALUE) heightCalcMost = y;
}
public static void destroy() {
if(fontDataBuffer != null) {
EagRuntime.freeByteBuffer(fontDataBuffer);
fontDataBuffer = null;
}
if(fontBoldDataBuffer != null) {
EagRuntime.freeByteBuffer(fontBoldDataBuffer);
fontBoldDataBuffer = null;
}
if(shaderProgram != null) {
_wglDeleteProgram(shaderProgram);
shaderProgram = null;
}
if(matrixCopyBuffer != null) {
EagRuntime.freeFloatBuffer(matrixCopyBuffer);
matrixCopyBuffer = null;
}
u_matrixTransform = null;
u_charSize2f = null;
u_charCoordSize2f = null;
u_color4f = null;
u_colorBias4f = null;
if(vertexArray != null) {
EaglercraftGPU.destroyGLBufferArray(vertexArray);
vertexArray = null;
}
if(vertexBuffer != null) {
_wglDeleteBuffers(vertexBuffer);
vertexBuffer = null;
}
if(instancesBuffer != null) {
_wglDeleteBuffers(instancesBuffer);
instancesBuffer = null;
}
}
}

View File

@ -13,11 +13,10 @@ 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.FixedFunctionShader.FixedFunctionConstants;
import net.lax1dude.eaglercraft.v1_8.vector.Matrix4f;
/**
* 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
@ -36,7 +35,10 @@ public class InstancedParticleRenderer {
private static final Logger logger = LogManager.getLogger("InstancedParticleRenderer");
public static final String vertexShaderPath = "/assets/eagler/glsl/accel_particle.vsh";
public static final String vertexShaderPrecision = "precision lowp int;\nprecision highp float;\nprecision mediump sampler2D;\n";
public static final String fragmentShaderPath = "/assets/eagler/glsl/accel_particle.fsh";
public static final String fragmentShaderPrecision = "precision lowp int;\nprecision mediump float;\nprecision mediump sampler2D;\n";
private static ByteBuffer particleBuffer = null;
private static int particleCount = 0;
@ -79,20 +81,13 @@ public class InstancedParticleRenderer {
private static float stateTransformParam5 = -999.0f;
static void initialize() {
String vertexSource = EagRuntime.getResourceString(vertexShaderPath);
if(vertexSource == null) {
throw new RuntimeException("InstancedParticleRenderer shader \"" + vertexShaderPath + "\" is missing!");
}
String fragmentSource = EagRuntime.getResourceString(fragmentShaderPath);
if(fragmentSource == null) {
throw new RuntimeException("InstancedParticleRenderer shader \"" + fragmentShaderPath + "\" is missing!");
}
String vertexSource = EagRuntime.getRequiredResourceString(vertexShaderPath);
String fragmentSource = EagRuntime.getRequiredResourceString(fragmentShaderPath);
IShaderGL vert = _wglCreateShader(GL_VERTEX_SHADER);
IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER);
_wglShaderSource(vert, FixedFunctionConstants.VERSION + "\n" + vertexSource);
_wglShaderSource(vert, GLSLHeader.getVertexHeaderCompat(vertexSource, vertexShaderPrecision));
_wglCompileShader(vert);
if(_wglGetShaderi(vert, GL_COMPILE_STATUS) != GL_TRUE) {
@ -107,7 +102,7 @@ public class InstancedParticleRenderer {
throw new IllegalStateException("Vertex shader \"" + vertexShaderPath + "\" could not be compiled!");
}
_wglShaderSource(frag, FixedFunctionConstants.VERSION + "\n" + fragmentSource);
_wglShaderSource(frag, GLSLHeader.getFragmentHeaderCompat(fragmentSource, fragmentShaderPrecision));
_wglCompileShader(frag);
if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) {
@ -127,6 +122,10 @@ public class InstancedParticleRenderer {
_wglAttachShader(shaderProgram, vert);
_wglAttachShader(shaderProgram, frag);
if(EaglercraftGPU.checkOpenGLESVersion() == 200) {
VSHInputLayoutParser.applyLayout(shaderProgram, VSHInputLayoutParser.getShaderInputs(vertexSource));
}
_wglLinkProgram(shaderProgram);
_wglDetachShader(shaderProgram, vert);
@ -161,7 +160,7 @@ public class InstancedParticleRenderer {
_wglUniform1i(_wglGetUniformLocation(shaderProgram, "u_inputTexture"), 0);
_wglUniform1i(_wglGetUniformLocation(shaderProgram, "u_lightmapTexture"), 1);
vertexArray = _wglGenVertexArrays();
vertexArray = EaglercraftGPU.createGLBufferArray();
vertexBuffer = _wglGenBuffers();
instancesBuffer = _wglGenBuffers();
@ -174,37 +173,37 @@ public class InstancedParticleRenderer {
EaglercraftGPU.bindGLBufferArray(vertexArray);
EaglercraftGPU.bindGLArrayBuffer(vertexBuffer);
EaglercraftGPU.bindVAOGLArrayBufferNow(vertexBuffer);
_wglBufferData(GL_ARRAY_BUFFER, verts, GL_STATIC_DRAW);
EagRuntime.freeFloatBuffer(verts);
_wglEnableVertexAttribArray(0);
_wglVertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0);
_wglVertexAttribDivisor(0, 0);
EaglercraftGPU.enableVertexAttribArray(0);
EaglercraftGPU.vertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0);
EaglercraftGPU.vertexAttribDivisor(0, 0);
EaglercraftGPU.bindGLArrayBuffer(instancesBuffer);
EaglercraftGPU.bindVAOGLArrayBufferNow(instancesBuffer);
_wglBufferData(GL_ARRAY_BUFFER, particleBuffer.remaining(), GL_STREAM_DRAW);
_wglEnableVertexAttribArray(1);
_wglVertexAttribPointer(1, 3, GL_FLOAT, false, 24, 0);
_wglVertexAttribDivisor(1, 1);
EaglercraftGPU.enableVertexAttribArray(1);
EaglercraftGPU.vertexAttribPointer(1, 3, GL_FLOAT, false, 24, 0);
EaglercraftGPU.vertexAttribDivisor(1, 1);
_wglEnableVertexAttribArray(2);
_wglVertexAttribPointer(2, 2, GL_UNSIGNED_SHORT, false, 24, 12);
_wglVertexAttribDivisor(2, 1);
EaglercraftGPU.enableVertexAttribArray(2);
EaglercraftGPU.vertexAttribPointer(2, 2, GL_UNSIGNED_SHORT, false, 24, 12);
EaglercraftGPU.vertexAttribDivisor(2, 1);
_wglEnableVertexAttribArray(3);
_wglVertexAttribPointer(3, 2, GL_UNSIGNED_BYTE, true, 24, 16);
_wglVertexAttribDivisor(3, 1);
EaglercraftGPU.enableVertexAttribArray(3);
EaglercraftGPU.vertexAttribPointer(3, 2, GL_UNSIGNED_BYTE, true, 24, 16);
EaglercraftGPU.vertexAttribDivisor(3, 1);
_wglEnableVertexAttribArray(4);
_wglVertexAttribPointer(4, 2, GL_UNSIGNED_BYTE, false, 24, 18);
_wglVertexAttribDivisor(4, 1);
EaglercraftGPU.enableVertexAttribArray(4);
EaglercraftGPU.vertexAttribPointer(4, 2, GL_UNSIGNED_BYTE, false, 24, 18);
EaglercraftGPU.vertexAttribDivisor(4, 1);
_wglEnableVertexAttribArray(5);
_wglVertexAttribPointer(5, 4, GL_UNSIGNED_BYTE, true, 24, 20);
_wglVertexAttribDivisor(5, 1);
EaglercraftGPU.enableVertexAttribArray(5);
EaglercraftGPU.vertexAttribPointer(5, 4, GL_UNSIGNED_BYTE, true, 24, 20);
EaglercraftGPU.vertexAttribDivisor(5, 1);
}
@ -316,11 +315,43 @@ public class InstancedParticleRenderer {
particleBuffer.position(p);
particleBuffer.limit(l);
_wglDrawArraysInstanced(GL_TRIANGLES, 0, 6, particleCount);
EaglercraftGPU.doDrawArraysInstanced(GL_TRIANGLES, 0, 6, particleCount);
}
public static void stupidColorSetHack(IUniformGL color4f) {
_wglUniform4f(color4f, GlStateManager.stateColorR, GlStateManager.stateColorG, GlStateManager.stateColorB, GlStateManager.stateColorA);
}
public static void destroy() {
if(particleBuffer != null) {
EagRuntime.freeByteBuffer(particleBuffer);
particleBuffer = null;
}
if(shaderProgram != null) {
_wglDeleteProgram(shaderProgram);
shaderProgram = null;
}
if(matrixCopyBuffer != null) {
EagRuntime.freeFloatBuffer(matrixCopyBuffer);
matrixCopyBuffer = null;
}
u_matrixTransform = null;
u_texCoordSize2f_particleSize1f = null;
u_transformParam_1_2_5_f = null;
u_transformParam_3_4_f = null;
u_color4f = null;
if(vertexArray != null) {
EaglercraftGPU.destroyGLBufferArray(vertexArray);
vertexArray = null;
}
if(vertexBuffer != null) {
_wglDeleteBuffers(vertexBuffer);
vertexBuffer = null;
}
if(instancesBuffer != null) {
_wglDeleteBuffers(instancesBuffer);
instancesBuffer = null;
}
}
}

View File

@ -1,7 +1,7 @@
package net.lax1dude.eaglercraft.v1_8.opengl;
/**
* Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved.
* Copyright (c) 2022-2023 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

View File

@ -0,0 +1,225 @@
package net.lax1dude.eaglercraft.v1_8.opengl;
import net.lax1dude.eaglercraft.v1_8.internal.IBufferArrayGL;
import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL;
import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*;
/**
* 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 SoftGLBufferArray implements IBufferArrayGL {
Attrib[] attribs = new Attrib[4];
int[] attribDivisors = null;
int hasAttribDivisorMask = 0;
int enabled = 0;
int enabledCnt = -1;
IBufferGL indexBuffer = null;
SoftGLBufferArray() {
}
void setAttrib(IBufferGL buffer, int index, int size, int format, boolean normalized, int stride, int offset) {
if(index >= attribs.length) {
int newLen = attribs.length << 1;
while(newLen <= index) {
newLen <<= 1;
}
Attrib[] newAttrib = new Attrib[newLen];
System.arraycopy(attribs, 0, newAttrib, 0, attribs.length);
attribs = newAttrib;
}
attribs[index] = new Attrib(buffer, size, format, normalized, stride, offset);
}
void setAttribDivisor(int index, int divisor) {
if(attribDivisors == null) {
if(divisor != 0) {
int newLen = 8;
while(newLen <= index) {
newLen <<= 1;
}
attribDivisors = new int[newLen];
}
}else if(index >= attribDivisors.length) {
int newLen = attribDivisors.length << 1;
while(newLen <= index) {
newLen <<= 1;
}
int[] newDivisor = new int[newLen];
System.arraycopy(attribDivisors, 0, newDivisor, 0, attribDivisors.length);
attribDivisors = newDivisor;
}
if(attribDivisors != null) {
attribDivisors[index] = divisor;
if(divisor != 0) {
hasAttribDivisorMask |= (1 << index);
}else {
hasAttribDivisorMask &= ~(1 << index);
}
}
}
void enableAttrib(int index, boolean en) {
if(en) {
enabled |= (1 << index);
}else {
enabled &= ~(1 << index);
}
enabledCnt = 32 - Integer.numberOfLeadingZeros(enabled);
}
void setIndexBuffer(IBufferGL buffer) {
indexBuffer = buffer;
}
void transitionToState(SoftGLBufferState previousState, boolean elements) {
int oldEnabled = previousState.oldEnabled;
int oldEnabledCnt = previousState.oldEnabledCnt;
int[] oldAttribDivisors = previousState.attribDivisors;
int oldHasAttribDivisorMask = previousState.hasAttribDivisorMask;
Attrib[] oldAttrs = previousState.attribs;
boolean instancingCapable = EaglercraftGPU.checkInstancingCapable();
int enCnt = enabledCnt;
int en = enabled;
Attrib[] attrs = attribs;
int[] divs = attribDivisors;
int hasDivs = hasAttribDivisorMask;
if(oldEnabledCnt >= 0) {
int enMax = Math.max(enCnt, oldEnabledCnt);
for(int i = 0, ii; i < enMax; ++i) {
ii = (1 << i);
boolean old = i < oldEnabledCnt && (oldEnabled & ii) != 0;
boolean _new = i < enCnt && (en & ii) != 0;
if(_new) {
if(!old) {
_wglEnableVertexAttribArray(i);
}
Attrib attr = i < attrs.length ? attrs[i] : null;
if(attr != null) {
Attrib oldAttr = oldAttrs[i];
if(oldAttr == null || !oldAttr.equalsExplicit(attr)) {
EaglercraftGPU.bindGLArrayBuffer(attr.buffer);
_wglVertexAttribPointer(i, attr.size, attr.format, attr.normalized, attr.stride, attr.offset);
oldAttrs[i] = attr;
}
}else {
oldAttrs[i] = null;
}
if(instancingCapable) {
// instancing is uncommon
if((hasDivs & ii) != 0) {
int newDivisor = divs[i];
if((oldHasAttribDivisorMask & ii) == 0 || newDivisor != oldAttribDivisors[i]) {
_wglVertexAttribDivisor(i, newDivisor);
oldAttribDivisors[i] = newDivisor;
previousState.hasAttribDivisorMask |= ii;
}
}else {
if((oldHasAttribDivisorMask & ii) != 0) {
_wglVertexAttribDivisor(i, 0);
oldAttribDivisors[i] = 0;
previousState.hasAttribDivisorMask &= ~ii;
}
}
}
}else if(old) {
_wglDisableVertexAttribArray(i);
}
}
}else {
// Bootstrap code for the emulator's first draw
for(int i = 0; i < enCnt; ++i) {
int ii = (1 << i);
if((en & ii) != 0) {
_wglEnableVertexAttribArray(i);
Attrib attr = attrs[i];
if(attr != null) {
EaglercraftGPU.bindGLArrayBuffer(attr.buffer);
_wglVertexAttribPointer(i, attr.size, attr.format, attr.normalized, attr.stride, attr.offset);
oldAttrs[i] = attr;
}else {
oldAttrs[i] = null;
}
if(instancingCapable) {
if((hasDivs & ii) != 0) {
int newDivisor = divs[i];
_wglVertexAttribDivisor(i, newDivisor);
oldAttribDivisors[i] = newDivisor;
previousState.hasAttribDivisorMask |= ii;
}else {
_wglVertexAttribDivisor(i, 0);
oldAttribDivisors[i] = 0;
}
}
}
}
}
if(elements) {
IBufferGL indexBufferL = indexBuffer;
if(indexBufferL != null) {
EaglercraftGPU.bindEmulatedVAOIndexBuffer(indexBufferL);
}
}
previousState.oldEnabled = en & ((1 << enCnt) - 1);
previousState.oldEnabledCnt = enCnt;
}
@Override
public void free() {
}
static class Attrib {
final IBufferGL buffer;
final int size;
final int format;
final boolean normalized;
final int stride;
final int offset;
final int hash;
final int checkVal;
Attrib(IBufferGL buffer, int size, int format, boolean normalized, int stride, int offset) {
this.buffer = buffer;
this.size = size;
this.format = format;
this.normalized = normalized;
this.stride = stride;
this.offset = offset;
this.checkVal = ((size - 1) & 3) | (normalized ? 4 : 0) | (format << 4);
this.hash = (((((31 + buffer.hashCode()) * 31 + size) * 31 + format) * 31 + (normalized ? 1 : 0)) * 31
+ stride) * 31 + offset;
}
public int hashCode() {
return hash;
}
public boolean equals(Object obj) {
if(obj == this) return true;
if(!(obj instanceof Attrib)) return false;
Attrib o2 = (Attrib)obj;
return o2.hash == hash && o2.buffer == buffer && o2.checkVal == checkVal && o2.stride == stride && o2.offset == offset;
}
public boolean equalsExplicit(Attrib o2) {
return o2 == this || (o2.hash == hash && o2.buffer == buffer && o2.checkVal == checkVal && o2.stride == stride && o2.offset == offset);
}
}
}

View File

@ -1,10 +1,9 @@
package net.lax1dude.eaglercraft.v1_8.sp.relay.pkt;
package net.lax1dude.eaglercraft.v1_8.opengl;
import java.io.DataInputStream;
import java.io.IOException;
import net.lax1dude.eaglercraft.v1_8.opengl.SoftGLBufferArray.Attrib;
/**
* 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
@ -18,19 +17,15 @@ import java.io.IOException;
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class IPacket02NewClient extends IPacket {
public String clientId;
public IPacket02NewClient(String clientId) {
this.clientId = clientId;
class SoftGLBufferState {
final Attrib[] attribs = new Attrib[24];
int[] attribDivisors = new int[24];
int hasAttribDivisorMask = 0;
int oldEnabled = 0;
int oldEnabledCnt = -1;
SoftGLBufferState() {
}
public IPacket02NewClient() {
}
public void read(DataInputStream input) throws IOException {
clientId = readASCII8(input);
}
}

View File

@ -10,7 +10,6 @@ import net.lax1dude.eaglercraft.v1_8.internal.IUniformGL;
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.FixedFunctionShader.FixedFunctionConstants;
import net.lax1dude.eaglercraft.v1_8.vector.Matrix3f;
/**
@ -33,6 +32,7 @@ public class SpriteLevelMixer {
private static final Logger LOGGER = LogManager.getLogger("SpriteLevelMixer");
public static final String fragmentShaderPath = "/assets/eagler/glsl/texture_mix.fsh";
public static final String fragmentShaderPrecision = "precision lowp int;\nprecision highp float;\nprecision highp sampler2D;\n";
private static IProgramGL shaderProgram = null;
@ -61,15 +61,11 @@ public class SpriteLevelMixer {
private static final Matrix3f identityMatrix = new Matrix3f();
static void initialize() {
String fragmentSource = EagRuntime.getResourceString(fragmentShaderPath);
if(fragmentSource == null) {
throw new RuntimeException("SpriteLevelMixer shader \"" + fragmentShaderPath + "\" is missing!");
}
String fragmentSource = EagRuntime.getRequiredResourceString(fragmentShaderPath);
IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER);
_wglShaderSource(frag, FixedFunctionConstants.VERSION + "\n" + fragmentSource);
_wglShaderSource(frag, GLSLHeader.getFragmentHeaderCompat(fragmentSource, fragmentShaderPrecision));
_wglCompileShader(frag);
if(_wglGetShaderi(frag, GL_COMPILE_STATUS) != GL_TRUE) {
@ -89,6 +85,10 @@ public class SpriteLevelMixer {
_wglAttachShader(shaderProgram, DrawUtils.vshLocal);
_wglAttachShader(shaderProgram, frag);
if(EaglercraftGPU.checkOpenGLESVersion() == 200) {
VSHInputLayoutParser.applyLayout(shaderProgram, DrawUtils.vshLocalLayout);
}
_wglLinkProgram(shaderProgram);
_wglDetachShader(shaderProgram, DrawUtils.vshLocal);
@ -155,7 +155,14 @@ public class SpriteLevelMixer {
public static void drawSprite(float level) {
EaglercraftGPU.bindGLShaderProgram(shaderProgram);
_wglUniform1f(u_textureLod1f, level);
if(EaglercraftGPU.checkTextureLODCapable()) {
_wglUniform1f(u_textureLod1f, level);
}else {
if(level != 0.0f) {
LOGGER.error("Tried to copy from mipmap level {}, but this GPU does not support textureLod!", level);
}
_wglUniform1f(u_textureLod1f, 0.0f);
}
if(blendColorChanged) {
_wglUniform4f(u_blendFactor4f, blendColorR, blendColorG, blendColorB, blendColorA);
@ -178,4 +185,19 @@ public class SpriteLevelMixer {
DrawUtils.drawStandardQuad2D();
}
public static void destroy() {
if(matrixCopyBuffer != null) {
EagRuntime.freeFloatBuffer(matrixCopyBuffer);
matrixCopyBuffer = null;
}
if(shaderProgram != null) {
_wglDeleteProgram(shaderProgram);
shaderProgram = null;
}
u_textureLod1f = null;
u_blendFactor4f = null;
u_blendBias4f = null;
u_matrixTransform = null;
}
}

View File

@ -74,7 +74,7 @@ public class StreamBuffer {
next.vertexBuffer = _wglGenBuffers();
}
if(next.vertexArray == null) {
next.vertexArray = _wglGenVertexArrays();
next.vertexArray = EaglercraftGPU.createGLBufferArray();
initializer.initialize(next.vertexArray, next.vertexBuffer);
}
if(next.vertexBufferSize < requiredMemory) {
@ -100,7 +100,7 @@ public class StreamBuffer {
newArray[i] = buffers[i];
}else {
if(buffers[i].vertexArray != null) {
_wglDeleteVertexArrays(buffers[i].vertexArray);
EaglercraftGPU.destroyGLBufferArray(buffers[i].vertexArray);
}
if(buffers[i].vertexBuffer != null) {
_wglDeleteBuffers(buffers[i].vertexBuffer);
@ -135,7 +135,7 @@ public class StreamBuffer {
for(int i = 0; i < buffers.length; ++i) {
StreamBufferInstance next = buffers[i];
if(next.vertexArray != null) {
_wglDeleteVertexArrays(next.vertexArray);
EaglercraftGPU.destroyGLBufferArray(next.vertexArray);
}
if(next.vertexBuffer != null) {
_wglDeleteBuffers(next.vertexBuffer);

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