diff --git a/desktopRuntime/UnsafeMemcpy.dll b/desktopRuntime/UnsafeMemcpy.dll new file mode 100644 index 0000000..067de85 Binary files /dev/null and b/desktopRuntime/UnsafeMemcpy.dll differ diff --git a/desktopRuntime/UnsafeMemcpy.jar b/desktopRuntime/UnsafeMemcpy.jar new file mode 100644 index 0000000..59fa359 Binary files /dev/null and b/desktopRuntime/UnsafeMemcpy.jar differ diff --git a/desktopRuntime/eclipseProject/deps_fix/UnsafeMemcpy.jar b/desktopRuntime/eclipseProject/deps_fix/UnsafeMemcpy.jar new file mode 100644 index 0000000..59fa359 Binary files /dev/null and b/desktopRuntime/eclipseProject/deps_fix/UnsafeMemcpy.jar differ diff --git a/desktopRuntime/libUnsafeMemcpy.so b/desktopRuntime/libUnsafeMemcpy.so new file mode 100644 index 0000000..61ec556 Binary files /dev/null and b/desktopRuntime/libUnsafeMemcpy.so differ diff --git a/desktopRuntime/resources/assets/eagler/capes/01.minecon_2011.png b/desktopRuntime/resources/assets/eagler/capes/01.minecon_2011.png new file mode 100644 index 0000000..77e0ccc Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/01.minecon_2011.png differ diff --git a/desktopRuntime/resources/assets/eagler/capes/02.minecon_2012.png b/desktopRuntime/resources/assets/eagler/capes/02.minecon_2012.png new file mode 100644 index 0000000..6390662 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/02.minecon_2012.png differ diff --git a/desktopRuntime/resources/assets/eagler/capes/03.minecon_2013.png b/desktopRuntime/resources/assets/eagler/capes/03.minecon_2013.png new file mode 100644 index 0000000..1cd6a12 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/03.minecon_2013.png differ diff --git a/desktopRuntime/resources/assets/eagler/capes/04.minecon_2015.png b/desktopRuntime/resources/assets/eagler/capes/04.minecon_2015.png new file mode 100644 index 0000000..a272284 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/04.minecon_2015.png differ diff --git a/desktopRuntime/resources/assets/eagler/capes/05.minecon_2016.png b/desktopRuntime/resources/assets/eagler/capes/05.minecon_2016.png new file mode 100644 index 0000000..492eee1 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/05.minecon_2016.png differ diff --git a/desktopRuntime/resources/assets/eagler/capes/06.microsoft_account.png b/desktopRuntime/resources/assets/eagler/capes/06.microsoft_account.png new file mode 100644 index 0000000..7fccd56 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/06.microsoft_account.png differ diff --git a/desktopRuntime/resources/assets/eagler/capes/07.mapmaker.png b/desktopRuntime/resources/assets/eagler/capes/07.mapmaker.png new file mode 100644 index 0000000..58a217a Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/07.mapmaker.png differ diff --git a/desktopRuntime/resources/assets/eagler/capes/08.mojang_old.png b/desktopRuntime/resources/assets/eagler/capes/08.mojang_old.png new file mode 100644 index 0000000..438edff Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/08.mojang_old.png differ diff --git a/desktopRuntime/resources/assets/eagler/capes/09.mojang_new.png b/desktopRuntime/resources/assets/eagler/capes/09.mojang_new.png new file mode 100644 index 0000000..2eca0d3 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/09.mojang_new.png differ diff --git a/desktopRuntime/resources/assets/eagler/capes/10.jira_mod.png b/desktopRuntime/resources/assets/eagler/capes/10.jira_mod.png new file mode 100644 index 0000000..f3d4fe8 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/10.jira_mod.png differ diff --git a/desktopRuntime/resources/assets/eagler/capes/11.mojang_very_old.png b/desktopRuntime/resources/assets/eagler/capes/11.mojang_very_old.png new file mode 100644 index 0000000..375aea8 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/11.mojang_very_old.png differ diff --git a/desktopRuntime/resources/assets/eagler/capes/12.scrolls.png b/desktopRuntime/resources/assets/eagler/capes/12.scrolls.png new file mode 100644 index 0000000..25e1447 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/12.scrolls.png differ diff --git a/desktopRuntime/resources/assets/eagler/capes/13.cobalt.png b/desktopRuntime/resources/assets/eagler/capes/13.cobalt.png new file mode 100644 index 0000000..50d24b7 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/13.cobalt.png differ diff --git a/desktopRuntime/resources/assets/eagler/capes/14.translator.png b/desktopRuntime/resources/assets/eagler/capes/14.translator.png new file mode 100644 index 0000000..b681c26 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/14.translator.png differ diff --git a/desktopRuntime/resources/assets/eagler/capes/15.millionth_account.png b/desktopRuntime/resources/assets/eagler/capes/15.millionth_account.png new file mode 100644 index 0000000..c2f0a02 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/15.millionth_account.png differ diff --git a/desktopRuntime/resources/assets/eagler/capes/16.prismarine.png b/desktopRuntime/resources/assets/eagler/capes/16.prismarine.png new file mode 100644 index 0000000..3ee69b2 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/16.prismarine.png differ diff --git a/desktopRuntime/resources/assets/eagler/capes/17.snowman.png b/desktopRuntime/resources/assets/eagler/capes/17.snowman.png new file mode 100644 index 0000000..f4ad552 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/17.snowman.png differ diff --git a/desktopRuntime/resources/assets/eagler/capes/18.spade.png b/desktopRuntime/resources/assets/eagler/capes/18.spade.png new file mode 100644 index 0000000..7cf8e8b Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/18.spade.png differ diff --git a/desktopRuntime/resources/assets/eagler/capes/19.birthday.png b/desktopRuntime/resources/assets/eagler/capes/19.birthday.png new file mode 100644 index 0000000..048ef8e Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/19.birthday.png differ diff --git a/desktopRuntime/resources/assets/eagler/capes/20.db.png b/desktopRuntime/resources/assets/eagler/capes/20.db.png new file mode 100644 index 0000000..8110bd2 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/capes/20.db.png differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/charles.fallback.png b/desktopRuntime/resources/assets/eagler/mesh/charles.fallback.png new file mode 100644 index 0000000..f1d90ca Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/charles.fallback.png differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/charles.png b/desktopRuntime/resources/assets/eagler/mesh/charles.png new file mode 100644 index 0000000..a23587f Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/charles.png differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/charles0.mdl b/desktopRuntime/resources/assets/eagler/mesh/charles0.mdl new file mode 100644 index 0000000..84138ca Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/charles0.mdl differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/charles1.mdl b/desktopRuntime/resources/assets/eagler/mesh/charles1.mdl new file mode 100644 index 0000000..549898a Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/charles1.mdl differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/charles2.mdl b/desktopRuntime/resources/assets/eagler/mesh/charles2.mdl new file mode 100644 index 0000000..614cada Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/charles2.mdl differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/laxativedude.fallback.png b/desktopRuntime/resources/assets/eagler/mesh/laxativedude.fallback.png new file mode 100644 index 0000000..27b1f9c Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/laxativedude.fallback.png differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/laxativedude.png b/desktopRuntime/resources/assets/eagler/mesh/laxativedude.png new file mode 100644 index 0000000..ece3e4a Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/laxativedude.png differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/laxativedude0.mdl b/desktopRuntime/resources/assets/eagler/mesh/laxativedude0.mdl new file mode 100644 index 0000000..90da430 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/laxativedude0.mdl differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/laxativedude1.mdl b/desktopRuntime/resources/assets/eagler/mesh/laxativedude1.mdl new file mode 100644 index 0000000..3f4d94f Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/laxativedude1.mdl differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/laxativedude2.mdl b/desktopRuntime/resources/assets/eagler/mesh/laxativedude2.mdl new file mode 100644 index 0000000..1dffcbc Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/laxativedude2.mdl differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/laxativedude3.mdl b/desktopRuntime/resources/assets/eagler/mesh/laxativedude3.mdl new file mode 100644 index 0000000..56dee1c Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/laxativedude3.mdl differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/longarms.fallback.png b/desktopRuntime/resources/assets/eagler/mesh/longarms.fallback.png new file mode 100644 index 0000000..b506534 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/longarms.fallback.png differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/longarms.png b/desktopRuntime/resources/assets/eagler/mesh/longarms.png new file mode 100644 index 0000000..c021684 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/longarms.png differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/longarms0.mdl b/desktopRuntime/resources/assets/eagler/mesh/longarms0.mdl new file mode 100644 index 0000000..29895c1 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/longarms0.mdl differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/longarms1.mdl b/desktopRuntime/resources/assets/eagler/mesh/longarms1.mdl new file mode 100644 index 0000000..a72acfa Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/longarms1.mdl differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/longarms2.mdl b/desktopRuntime/resources/assets/eagler/mesh/longarms2.mdl new file mode 100644 index 0000000..57544d6 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/longarms2.mdl differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/weirdclimber.fallback.png b/desktopRuntime/resources/assets/eagler/mesh/weirdclimber.fallback.png new file mode 100644 index 0000000..da5d052 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/weirdclimber.fallback.png differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/weirdclimber.png b/desktopRuntime/resources/assets/eagler/mesh/weirdclimber.png new file mode 100644 index 0000000..792cac5 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/weirdclimber.png differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/weirdclimber0.mdl b/desktopRuntime/resources/assets/eagler/mesh/weirdclimber0.mdl new file mode 100644 index 0000000..72efddb Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/weirdclimber0.mdl differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/weirdclimber1.mdl b/desktopRuntime/resources/assets/eagler/mesh/weirdclimber1.mdl new file mode 100644 index 0000000..933d3c8 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/weirdclimber1.mdl differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/weirdclimber2.mdl b/desktopRuntime/resources/assets/eagler/mesh/weirdclimber2.mdl new file mode 100644 index 0000000..728db6a Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/weirdclimber2.mdl differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/winston.fallback.png b/desktopRuntime/resources/assets/eagler/mesh/winston.fallback.png new file mode 100644 index 0000000..b0da89c Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/winston.fallback.png differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/winston.png b/desktopRuntime/resources/assets/eagler/mesh/winston.png new file mode 100644 index 0000000..fceadbb Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/winston.png differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/winston0.mdl b/desktopRuntime/resources/assets/eagler/mesh/winston0.mdl new file mode 100644 index 0000000..f66da49 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/winston0.mdl differ diff --git a/desktopRuntime/resources/assets/eagler/mesh/winston1.mdl b/desktopRuntime/resources/assets/eagler/mesh/winston1.mdl new file mode 100644 index 0000000..c8cced5 Binary files /dev/null and b/desktopRuntime/resources/assets/eagler/mesh/winston1.mdl differ diff --git a/desktopRuntime/resources/assets/minecraft/lang/en_US.lang b/desktopRuntime/resources/assets/minecraft/lang/en_US.lang index a3e5723..d739587 100644 --- a/desktopRuntime/resources/assets/minecraft/lang/en_US.lang +++ b/desktopRuntime/resources/assets/minecraft/lang/en_US.lang @@ -18,7 +18,7 @@ gui.all=All eaglercraft.recording.unsupported=Recording Unsupported! eaglercraft.recording.stop=Stop Recording eaglercraft.recording.start=Record Screen... -eaglercraft.soundCategory.voice=Voice +eaglercraft.soundCategory.voice=Recording Voice eaglercraft.resourcePack.prompt.title=What do you want to do with '%s'? eaglercraft.resourcePack.prompt.text=Tip: Hold Shift to skip this screen when selecting a resource pack! @@ -44,6 +44,15 @@ eaglercraft.editProfile.playerSkin=Player Skin eaglercraft.editProfile.addSkin=Add Skin eaglercraft.editProfile.clearSkin=Clear List +eaglercraft.editProfile.capes=Capes +eaglercraft.editProfile.disableFNAW=(Note: go to 'Video Settings...' > 'Other...' to disable FNAW skins) +eaglercraft.editProfile.enableFNAW=(Note: go to 'Video Settings...' > 'Other...' to enable FNAW skins) + +eaglercraft.editCape.title=Edit Cape +eaglercraft.editCape.playerCape=Player Cape +eaglercraft.editCape.addCape=Add Cape +eaglercraft.editCape.clearCape=Clear List + eaglercraft.editProfile.importExport=Import/Export eaglercraft.settingsBackup.importExport.title=What do you wanna do? @@ -599,6 +608,60 @@ eaglercraft.updateList.refresh=Refresh eaglercraft.updateList.note.0=Note: Updates are digitally signed, EaglercraftX will block any eaglercraft.updateList.note.1=updates that were not created by lax1dude or ayunami2000 +eaglercraft.voice.title=Voice Channel +eaglercraft.voice.titleNoVoice=Voice is disabled on this server +eaglercraft.voice.titleVoiceUnavailable=Voice is unavailable +eaglercraft.voice.titleVoiceBrowserError=(browser issue) +eaglercraft.voice.ptt=Press '%s' to speak +eaglercraft.voice.pttChangeDesc=(Press Any Key) +eaglercraft.voice.changeKey=Change +eaglercraft.voice.off=OFF +eaglercraft.voice.radius=NEARBY +eaglercraft.voice.global=GLOBAL +eaglercraft.voice.volumeTitle=Change Volume +eaglercraft.voice.volumeListen=Speakers Volume: +eaglercraft.voice.volumeSpeak=Microphone Volume: +eaglercraft.voice.radiusTitle=Change Listener Radius +eaglercraft.voice.radiusLabel=Players Within: +eaglercraft.voice.radiusChange=change +eaglercraft.voice.notConnected=Not Connected +eaglercraft.voice.connecting=Connecting... +eaglercraft.voice.unavailable=Could not connect! +eaglercraft.voice.connectedGlobal=Connected - Global +eaglercraft.voice.connectedRadius=Connected - $f$Within $radius$m +eaglercraft.voice.playersListening=Players Listening: +eaglercraft.voice.muted=Players Muted: +eaglercraft.voice.unmute=unmute +eaglercraft.voice.mute=mute +eaglercraft.voice.apply=Apply +eaglercraft.voice.volumeSpeakerLabel=Speakers: +eaglercraft.voice.volumeMicrophoneLabel=Microphone: + +eaglercraft.voice.unsupportedWarning1=Voice Warning +eaglercraft.voice.unsupportedWarning2=Your network's firewall may not support +eaglercraft.voice.unsupportedWarning3=eaglercraft's voice chat. +eaglercraft.voice.unsupportedWarning4=If your game doesn't work it's your issue +eaglercraft.voice.unsupportedWarning5=to solve, not ayunami2000's or lax1dude's. +eaglercraft.voice.unsupportedWarning6=Don't ask them to 'fix' it for you because +eaglercraft.voice.unsupportedWarning7=they won't help you fix a problem that only +eaglercraft.voice.unsupportedWarning8=you or your network's administrator has the +eaglercraft.voice.unsupportedWarning9=ability to correctly resolve. +eaglercraft.voice.unsupportedWarning10=Continue +eaglercraft.voice.unsupportedWarning11=Cancel + +eaglercraft.voice.ipGrabWarning1=IP Logger Warning +eaglercraft.voice.ipGrabWarning2=Using Eaglercraft voice chat may allow your +eaglercraft.voice.ipGrabWarning3=IP address to be logged by other players +eaglercraft.voice.ipGrabWarning4=also using voice on the server. +eaglercraft.voice.ipGrabWarning5=This issue will not be fixed, it is an +eaglercraft.voice.ipGrabWarning6=internal browser issue, not a mistake in the +eaglercraft.voice.ipGrabWarning7=game. Fortunately, this can only be done if +eaglercraft.voice.ipGrabWarning8=the other player uses a hacked web browser +eaglercraft.voice.ipGrabWarning9=or has Wireshark to capture the voice +eaglercraft.voice.ipGrabWarning10=packets, as there exists no real javascript +eaglercraft.voice.ipGrabWarning11=method to log IPs using a normal skidded +eaglercraft.voice.ipGrabWarning12=eaglercraft hacked client. + selectServer.title=Select Server selectServer.empty=empty selectServer.select=Join Server @@ -799,6 +862,7 @@ options.chat.height.focused=Focused Height options.chat.height.unfocused=Unfocused Height options.skinCustomisation=Skin Customization... options.skinCustomisation.title=Skin Customization +options.skinCustomisation.enableFNAWSkins=Show FNAW Skins options.modelPart.cape=Cape options.modelPart.hat=Hat options.modelPart.jacket=Jacket diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAssets.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAssets.java index ba16a63..03785ec 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAssets.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAssets.java @@ -60,6 +60,9 @@ public class PlatformAssets { public static final ImageData loadImageFile(InputStream data) { try { BufferedImage img = ImageIO.read(data); + if(img == null) { + throw new IOException("Data is not a supported image format!"); + } int w = img.getWidth(); int h = img.getHeight(); boolean a = img.getColorModel().hasAlpha(); diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformBufferFunctions.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformBufferFunctions.java index 9adf74c..6cab7cc 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformBufferFunctions.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformBufferFunctions.java @@ -21,10 +21,7 @@ import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; public class PlatformBufferFunctions { public static void put(ByteBuffer newBuffer, ByteBuffer flip) { - int len = flip.remaining(); - for(int i = 0; i < len; ++i) { - newBuffer.put(flip.get()); - } + newBuffer.put(flip); } public static void put(IntBuffer intBuffer, int index, int[] data) { diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformFilesystem.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformFilesystem.java index ece3fff..55bd718 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformFilesystem.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformFilesystem.java @@ -1,13 +1,11 @@ package net.lax1dude.eaglercraft.v1_8.internal; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.vfs2.EaglerFileSystemException; -import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFSIterator2.BreakLoop; +import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.DebugFilesystem; +import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.JDBCFilesystem; +import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.JDBCFilesystemConverter; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; @@ -30,183 +28,104 @@ public class PlatformFilesystem { public static final Logger logger = LogManager.getLogger("PlatformFilesystem"); - public static final File filesystemRoot = (new File("filesystem/sp")).getAbsoluteFile(); + public static final File debugFilesystemRoot = (new File("filesystem/sp")).getAbsoluteFile(); + + private static IFilesystemProvider provider = null; + + public static String jdbcUri = null; + public static String jdbcDriver = null; public static void initialize() { - if(!filesystemRoot.isDirectory() && !filesystemRoot.mkdirs()) { - throw new EaglerFileSystemException("Could not create directory for virtual filesystem: " + filesystemRoot.getAbsolutePath()); + if(provider == null) { + if(jdbcUri != null && jdbcDriver != null) { + provider = JDBCFilesystem.initialize(jdbcUri, jdbcDriver); + if(((JDBCFilesystem)provider).isNewFilesystem() && debugFilesystemRoot.isDirectory() && debugFilesystemRoot.list().length > 0) { + JDBCFilesystemConverter.convertFilesystem("Converting filesystem, please wait...", debugFilesystemRoot, provider, true); + } + }else { + provider = DebugFilesystem.initialize(debugFilesystemRoot); + } } } + public static void setUseJDBC(String uri) { + jdbcUri = uri; + } + + public static void setJDBCDriverClass(String driver) { + jdbcDriver = driver; + } + + public static interface IFilesystemProvider { + + boolean eaglerDelete(String pathName); + + ByteBuffer eaglerRead(String pathName); + + void eaglerWrite(String pathName, ByteBuffer data); + + boolean eaglerExists(String pathName); + + boolean eaglerMove(String pathNameOld, String pathNameNew); + + int eaglerCopy(String pathNameOld, String pathNameNew); + + int eaglerSize(String pathName); + + void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive); + + } + + private static void throwNotInitialized() { + throw new UnsupportedOperationException("Filesystem has not been initialized!"); + } + public static boolean eaglerDelete(String pathName) { - File f = getJREFile(pathName); - if(!f.exists()) { - logger.warn("Tried to delete file that doesn't exist: \"{}\"", pathName); - return false; - } - if(f.delete()) { - deleteParentIfEmpty(f); - return true; - } - return false; + if(provider == null) throwNotInitialized(); + return provider.eaglerDelete(pathName); } public static ByteBuffer eaglerRead(String pathName) { - File f = getJREFile(pathName); - if(f.isFile()) { - long fileSize = f.length(); - if(fileSize > 2147483647L) throw new EaglerFileSystemException("Too large: " + fileSize + " @ " + f.getAbsolutePath()); - ByteBuffer buf = PlatformRuntime.allocateByteBuffer((int)fileSize); - try(FileInputStream is = new FileInputStream(f)) { - byte[] copyBuffer = new byte[4096]; - int i; - while((i = is.read(copyBuffer, 0, copyBuffer.length)) != -1) { - buf.put(copyBuffer, 0, i); - } - if(buf.remaining() > 0) { - throw new EaglerFileSystemException("ERROR: " + buf.remaining() + " bytes are remaining after reading: " + f.getAbsolutePath()); - } - buf.flip(); - ByteBuffer tmp = buf; - buf = null; - return tmp; - }catch (IOException e) { - throw new EaglerFileSystemException("Failed to read: " + f.getAbsolutePath(), e); - }catch(ArrayIndexOutOfBoundsException ex) { - throw new EaglerFileSystemException("ERROR: Expected " + fileSize + " bytes, buffer overflow reading: " + f.getAbsolutePath(), ex); - }finally { - if(buf != null) { - PlatformRuntime.freeByteBuffer(buf); - } - } - }else { - logger.warn("Tried to read file that doesn't exist: \"{}\"", f.getAbsolutePath()); - return null; - } + if(provider == null) throwNotInitialized(); + return provider.eaglerRead(pathName); } public static void eaglerWrite(String pathName, ByteBuffer data) { - File f = getJREFile(pathName); - File p = f.getParentFile(); - if(!p.isDirectory()) { - if(!p.mkdirs()) { - throw new EaglerFileSystemException("Could not create parent directory: " + p.getAbsolutePath()); - } - } - try(FileOutputStream fos = new FileOutputStream(f)) { - byte[] copyBuffer = new byte[Math.min(4096, data.remaining())]; - int i; - while((i = data.remaining()) > 0) { - if(i > copyBuffer.length) { - i = copyBuffer.length; - } - data.get(copyBuffer, 0, i); - fos.write(copyBuffer, 0, i); - } - }catch (IOException e) { - throw new EaglerFileSystemException("Failed to write: " + f.getAbsolutePath(), e); - } + if(provider == null) throwNotInitialized(); + provider.eaglerWrite(pathName, data); } public static boolean eaglerExists(String pathName) { - return getJREFile(pathName).isFile(); + if(provider == null) throwNotInitialized(); + return provider.eaglerExists(pathName); } public static boolean eaglerMove(String pathNameOld, String pathNameNew) { - File f1 = getJREFile(pathNameOld); - File f2 = getJREFile(pathNameNew); - if(f2.exists()) { - logger.warn("Tried to rename file \"{}\" to \"{}\" which already exists! File will be replaced"); - if(!f2.delete()) { - return false; - } - } - if(f1.renameTo(f2)) { - deleteParentIfEmpty(f1); - return true; - } - return false; + if(provider == null) throwNotInitialized(); + return provider.eaglerMove(pathNameOld, pathNameNew); } public static int eaglerCopy(String pathNameOld, String pathNameNew) { - File f1 = getJREFile(pathNameOld); - File f2 = getJREFile(pathNameNew); - if(!f1.isFile()) { - return -1; - } - if(f2.isDirectory()) { - throw new EaglerFileSystemException("Destination file is a directory: " + f2.getAbsolutePath()); - } - File p = f2.getParentFile(); - if(!p.isDirectory()) { - if(!p.mkdirs()) { - throw new EaglerFileSystemException("Could not create parent directory: " + p.getAbsolutePath()); - } - } - int sz = 0; - try(FileInputStream is = new FileInputStream(f1)) { - try(FileOutputStream os = new FileOutputStream(f2)) { - byte[] copyBuffer = new byte[4096]; - int i; - while((i = is.read(copyBuffer, 0, copyBuffer.length)) != -1) { - os.write(copyBuffer, 0, i); - sz += i; - } - } - }catch (IOException e) { - throw new EaglerFileSystemException("Failed to copy \"" + f1.getAbsolutePath() + "\" to file \"" + f2.getAbsolutePath() + "\"", e); - } - return sz; + if(provider == null) throwNotInitialized(); + return provider.eaglerCopy(pathNameOld, pathNameNew); } public static int eaglerSize(String pathName) { - File f = getJREFile(pathName); - if(f.isFile()) { - long fileSize = f.length(); - if(fileSize > 2147483647L) throw new EaglerFileSystemException("Too large: " + fileSize + " @ " + f.getAbsolutePath()); - return (int)fileSize; - }else { - return -1; - } + if(provider == null) throwNotInitialized(); + return provider.eaglerSize(pathName); } public static void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive) { - try { - iterateFile(pathName, getJREFile(pathName), itr, recursive); - }catch(BreakLoop ex) { - } + if(provider == null) throwNotInitialized(); + provider.eaglerIterate(pathName, itr, recursive); } - private static void iterateFile(String pathName, File f, VFSFilenameIterator itr, boolean recursive) { - if(!f.exists()) { - return; - } - if(!f.isDirectory()) { - itr.next(pathName); - return; - } - File[] fa = f.listFiles(); - for(int i = 0; i < fa.length; ++i) { - File ff = fa[i]; - String fn = pathName + "/" + ff.getName(); - if(ff.isDirectory()) { - if(recursive) { - iterateFile(fn, ff, itr, true); - } - }else { - itr.next(fn); + public static void platformShutdown() { + if(provider != null) { + if(provider instanceof JDBCFilesystem) { + ((JDBCFilesystem)provider).shutdown(); } + provider = null; } } - - private static File getJREFile(String path) { - return new File(filesystemRoot, path); - } - - private static void deleteParentIfEmpty(File f) { - String[] s; - while((f = f.getParentFile()) != null && (s = f.list()) != null && s.length == 0) { - f.delete(); - } - } -} +} \ No newline at end of file diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java index 074cace..ce8d776 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java @@ -58,6 +58,9 @@ public class PlatformInput { private static final List keyboardCharList = new LinkedList(); + private static boolean vsync = true; + private static boolean glfwVSyncState = false; + private static class KeyboardEvent { protected final int key; @@ -213,9 +216,17 @@ public class PlatformInput { public static boolean isCloseRequested() { return glfwWindowShouldClose(win); } + + public static void setVSync(boolean enable) { + vsync = enable; + } public static void update() { glfwPollEvents(); + if(vsync != glfwVSyncState) { + glfwSwapInterval(vsync ? 1 : 0); + glfwVSyncState = vsync; + } glfwSwapBuffers(win); } diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java index 7b65703..acaee45 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java @@ -8,6 +8,7 @@ import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; import static org.lwjgl.opengles.GLES30.*; import org.lwjgl.opengles.GLES31; +import org.lwjgl.opengles.GLESCapabilities; /** * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. @@ -25,6 +26,12 @@ import org.lwjgl.opengles.GLES31; * */ public class PlatformOpenGL { + + private static boolean hasLinearHDR32FSupport = false; + + static void setCurrentContext(GLESCapabilities caps) { + hasLinearHDR32FSupport = caps.GL_OES_texture_float_linear; + } public static final void _wglEnable(int glEnum) { glEnable(glEnum); @@ -270,6 +277,12 @@ public class PlatformOpenGL { nglTexImage2D(target, level, internalFormat, width, height, border, format, type, data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); } + + public static final void _wglTexImage2Df32(int target, int level, int internalFormat, int width, int height, + int border, int format, int type, ByteBuffer data) { + nglTexImage2D(target, level, internalFormat, width, height, border, format, type, + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer data) { @@ -532,5 +545,9 @@ public class PlatformOpenGL { public static int _wglGetTexLevelParameteri(int glTexture2d, int i, int glTextureWidth) { return GLES31.glGetTexLevelParameteri(glTexture2d, i, glTextureWidth); } + + public static final boolean checkLinearHDR32FSupport() { + return hasLinearHDR32FSupport; + } } diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java index 0538934..43eb008 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java @@ -171,7 +171,7 @@ public class PlatformRuntime { EGL.createDisplayCapabilities(glfw_eglHandle, major[0], minor[0]); glfwMakeContextCurrent(windowHandle); - GLES.createCapabilities(); + PlatformOpenGL.setCurrentContext(GLES.createCapabilities()); logger.info("OpenGL Version: {}", (glVersion = GLES30.glGetString(GLES30.GL_VERSION))); logger.info("OpenGL Renderer: {}", (glRenderer = GLES30.glGetString(GLES30.GL_RENDERER))); @@ -245,6 +245,7 @@ public class PlatformRuntime { public static void destroy() { PlatformAudio.platformShutdown(); + PlatformFilesystem.platformShutdown(); GLES.destroy(); EGL.destroy(); glfwDestroyWindow(windowHandle); @@ -340,15 +341,27 @@ public class PlatformRuntime { public static class NativeNIO { public static java.nio.ByteBuffer allocateByteBuffer(int length) { - return MemoryUtil.memByteBuffer(JEmalloc.nje_malloc(length), length); + long ret = JEmalloc.nje_malloc(length); + if(ret == 0l) { + throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + } + return MemoryUtil.memByteBuffer(ret, length); } public static java.nio.IntBuffer allocateIntBuffer(int length) { - return MemoryUtil.memIntBuffer(JEmalloc.nje_malloc(length << 2), length); + long ret = JEmalloc.nje_malloc(length << 2); + if(ret == 0l) { + throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + } + return MemoryUtil.memIntBuffer(ret, length); } public static java.nio.FloatBuffer allocateFloatBuffer(int length) { - return MemoryUtil.memFloatBuffer(JEmalloc.nje_malloc(length << 2), length); + long ret = JEmalloc.nje_malloc(length << 2); + if(ret == 0l) { + throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + } + return MemoryUtil.memFloatBuffer(ret, length); } public static java.nio.IntBuffer getIntBuffer(java.nio.ByteBuffer byteBuffer) { diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java new file mode 100644 index 0000000..0e676e0 --- /dev/null +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java @@ -0,0 +1,116 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.voice.EnumVoiceChannelPeerState; +import net.lax1dude.eaglercraft.v1_8.voice.EnumVoiceChannelReadyState; + +/** + * Copyright (c) 2022-2024 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 PlatformVoiceClient { + + public static void initialize() { + + } + + public static void initializeDevices() { + + } + + public static boolean isSupported() { + return false; + } + + public static void setVoiceListenVolume(float f) { + + } + + public static void setVoiceSpeakVolume(float f) { + + } + + public static void activateVoice(boolean talk) { + + } + + public static void setICEServers(String[] servs) { + + } + + public static void signalConnect(EaglercraftUUID user, boolean offer) { + + } + + public static void signalDisconnect(EaglercraftUUID user, boolean b) { + + } + + public static void signalICECandidate(EaglercraftUUID user, String ice) { + + } + + public static void signalDescription(EaglercraftUUID user, String desc) { + + } + + public static void tickVoiceClient() { + + } + + public static void updateVoicePosition(EaglercraftUUID uuid, double x, double y, double z) { + + } + + public static void resetPeerStates() { + + } + + public static void setVoiceProximity(int prox) { + + } + + public static void setMicVolume(float f) { + + } + + public static void mutePeer(EaglercraftUUID uuid, boolean mute) { + + } + + public static EnumVoiceChannelPeerState getPeerState() { + return EnumVoiceChannelPeerState.LOADING; + } + + public static EnumVoiceChannelReadyState getReadyState() { + return EnumVoiceChannelReadyState.NONE; + } + + public static EnumVoiceChannelPeerState getPeerStateConnect() { + return EnumVoiceChannelPeerState.LOADING; + } + + public static EnumVoiceChannelPeerState getPeerStateInitial() { + return EnumVoiceChannelPeerState.LOADING; + } + + public static EnumVoiceChannelPeerState getPeerStateDesc() { + return EnumVoiceChannelPeerState.LOADING; + } + + public static EnumVoiceChannelPeerState getPeerStateIce() { + return EnumVoiceChannelPeerState.LOADING; + } + +} \ No newline at end of file diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLAllocator.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLAllocator.java index 8167730..7b06078 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLAllocator.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLAllocator.java @@ -3,7 +3,7 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; import org.lwjgl.system.jemalloc.JEmalloc; /** - * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -26,19 +26,35 @@ public class EaglerLWJGLAllocator { } public static ByteBuffer allocByteBuffer(int len) { - return new EaglerLWJGLByteBuffer(JEmalloc.nje_malloc(len), len, true); + long ret = JEmalloc.nje_malloc(len); + if(ret == 0l) { + throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + } + return new EaglerLWJGLByteBuffer(ret, len, true); } public static ShortBuffer allocShortBuffer(int len) { - return new EaglerLWJGLShortBuffer(JEmalloc.nje_malloc(len << 1), len, true); + long ret = JEmalloc.nje_malloc(len << 1); + if(ret == 0l) { + throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + } + return new EaglerLWJGLShortBuffer(ret, len, true); } public static IntBuffer allocIntBuffer(int len) { - return new EaglerLWJGLIntBuffer(JEmalloc.nje_malloc(len << 2), len, true); + long ret = JEmalloc.nje_malloc(len << 2); + if(ret == 0l) { + throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + } + return new EaglerLWJGLIntBuffer(ret, len, true); } public static FloatBuffer allocFloatBuffer(int len) { - return new EaglerLWJGLFloatBuffer(JEmalloc.nje_malloc(len << 2), len, true); + long ret = JEmalloc.nje_malloc(len << 2); + if(ret == 0l) { + throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + } + return new EaglerLWJGLFloatBuffer(ret, len, true); } public static void freeByteBuffer(ByteBuffer buffer) { @@ -145,4 +161,4 @@ public class EaglerLWJGLAllocator { return new WrongBufferClassType("Tried to pass a " + clazz.getClass().getSimpleName() + " which is not a native eagler buffer"); } -} +} \ No newline at end of file diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLByteBuffer.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLByteBuffer.java index d21926f..fed5de4 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLByteBuffer.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLByteBuffer.java @@ -1,10 +1,12 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; -import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.jemalloc.JEmalloc; +import net.lax1dude.unsafememcpy.UnsafeMemcpy; +import net.lax1dude.unsafememcpy.UnsafeUtils; + /** - * 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 @@ -104,35 +106,33 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { @Override public byte get() { if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - return MemoryUtil.memGetByte(address + position++); + return UnsafeUtils.getMemByte(address + position++); } @Override public ByteBuffer put(byte b) { if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - MemoryUtil.memPutByte(address + position++, b); + UnsafeUtils.setMemByte(address + position++, b); return this; } @Override public byte get(int index) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - return MemoryUtil.memGetByte(address + index); + return UnsafeUtils.getMemByte(address + index); } @Override public ByteBuffer put(int index, byte b) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - MemoryUtil.memPutByte(address + index, b); + UnsafeUtils.setMemByte(address + index, b); return this; } @Override public ByteBuffer get(byte[] dst, int offset, int length) { if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); - for(int i = 0; i < length; ++i) { - dst[offset + i] = MemoryUtil.memGetByte(address + position + i); - } + UnsafeMemcpy.memcpy(dst, offset, address + position, length); position += length; return this; } @@ -140,9 +140,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { @Override public ByteBuffer get(byte[] dst) { if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); - for(int i = 0; i < dst.length; ++i) { - dst[position + i] = MemoryUtil.memGetByte(address + position + i); - } + UnsafeMemcpy.memcpy(dst, 0, address + position, dst.length); position += dst.length; return this; } @@ -153,14 +151,14 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { EaglerLWJGLByteBuffer c = (EaglerLWJGLByteBuffer)src; int l = c.limit - c.position; if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); - MemoryUtil.memCopy(c.address + c.position, address + position, l); + UnsafeMemcpy.memcpy(address + position, c.address + c.position, l); position += l; c.position += l; }else { int l = src.remaining(); if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); for(int i = 0; i < l; ++i) { - MemoryUtil.memPutByte(address + position + l, src.get()); + UnsafeUtils.setMemByte(address + position + l, src.get()); } position += l; } @@ -170,9 +168,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { @Override public ByteBuffer put(byte[] src, int offset, int length) { if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); - for(int i = 0; i < length; ++i) { - MemoryUtil.memPutByte(address + position + i, src[offset + i]); - } + UnsafeMemcpy.memcpy(address + position, src, offset, length); position += length; return this; } @@ -180,9 +176,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { @Override public ByteBuffer put(byte[] src) { if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); - for(int i = 0; i < src.length; ++i) { - MemoryUtil.memPutByte(address + position + i, src[i]); - } + UnsafeMemcpy.memcpy(address + position, src, 0, src.length); position += src.length; return this; } @@ -203,7 +197,10 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { int newLen = limit - position; long newAlloc = JEmalloc.nje_malloc(newLen); - MemoryUtil.memCopy(address + position, newAlloc, newLen); + if(newAlloc == 0l) { + throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + } + UnsafeMemcpy.memcpy(newAlloc, address + position, newLen); return new EaglerLWJGLByteBuffer(newAlloc, newLen, true); } @@ -211,7 +208,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { @Override public char getChar() { if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position); - char c = (char)MemoryUtil.memGetShort(address + position); + char c = UnsafeUtils.getMemChar(address + position); position += 2; return c; } @@ -219,7 +216,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { @Override public ByteBuffer putChar(char value) { if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position); - MemoryUtil.memPutShort(address + position, (short)value); + UnsafeUtils.setMemChar(address + position, value); position += 2; return this; } @@ -227,20 +224,20 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { @Override public char getChar(int index) { if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index); - return (char)MemoryUtil.memGetShort(address + index); + return UnsafeUtils.getMemChar(address + index); } @Override public ByteBuffer putChar(int index, char value) { if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index); - MemoryUtil.memPutShort(address + index, (short)value); + UnsafeUtils.setMemChar(address + index, value); return this; } @Override public short getShort() { if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position); - short s = MemoryUtil.memGetShort(address + position); + short s = UnsafeUtils.getMemShort(address + position); position += 2; return s; } @@ -248,7 +245,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { @Override public ByteBuffer putShort(short value) { if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position); - MemoryUtil.memPutShort(address + position, value); + UnsafeUtils.setMemShort(address + position, value); position += 2; return this; } @@ -256,13 +253,13 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { @Override public short getShort(int index) { if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index); - return MemoryUtil.memGetShort(address + index); + return UnsafeUtils.getMemShort(address + index); } @Override public ByteBuffer putShort(int index, short value) { if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index); - MemoryUtil.memPutShort(address + index, value); + UnsafeUtils.setMemShort(address + index, value); return this; } @@ -274,7 +271,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { @Override public int getInt() { if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position); - int i = MemoryUtil.memGetInt(address + position); + int i = UnsafeUtils.getMemInt(address + position); position += 4; return i; } @@ -282,7 +279,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { @Override public ByteBuffer putInt(int value) { if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position); - MemoryUtil.memPutInt(address + position, value); + UnsafeUtils.setMemInt(address + position, value); position += 4; return this; } @@ -290,13 +287,13 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { @Override public int getInt(int index) { if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index); - return MemoryUtil.memGetInt(address + index); + return UnsafeUtils.getMemInt(address + index); } @Override public ByteBuffer putInt(int index, int value) { if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index); - MemoryUtil.memPutInt(address + index, value); + UnsafeUtils.setMemInt(address + index, value); return this; } @@ -308,7 +305,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { @Override public long getLong() { if(position + 8 > limit) throw new ArrayIndexOutOfBoundsException(position); - long l = MemoryUtil.memGetLong(address + position); + long l = UnsafeUtils.getMemLong(address + position); position += 8; return l; } @@ -316,7 +313,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { @Override public ByteBuffer putLong(long value) { if(position + 8 > limit) throw new ArrayIndexOutOfBoundsException(position); - MemoryUtil.memPutLong(address + position, value); + UnsafeUtils.setMemLong(address + position, value); position += 8; return this; } @@ -324,20 +321,20 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { @Override public long getLong(int index) { if(index + 8 > limit) throw new ArrayIndexOutOfBoundsException(index); - return MemoryUtil.memGetLong(address + index); + return UnsafeUtils.getMemLong(address + index); } @Override public ByteBuffer putLong(int index, long value) { if(index + 8 > limit) throw new ArrayIndexOutOfBoundsException(index); - MemoryUtil.memPutLong(address + index, value); + UnsafeUtils.setMemLong(address + index, value); return this; } @Override public float getFloat() { if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position); - float f = MemoryUtil.memGetFloat(address + position); + float f = UnsafeUtils.getMemFloat(address + position); position += 4; return f; } @@ -345,7 +342,7 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { @Override public ByteBuffer putFloat(float value) { if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position); - MemoryUtil.memPutFloat(address + position, value); + UnsafeUtils.setMemFloat(address + position, value); position += 4; return this; } @@ -353,13 +350,13 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { @Override public float getFloat(int index) { if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index); - return MemoryUtil.memGetFloat(address + index); + return UnsafeUtils.getMemFloat(address + index); } @Override public ByteBuffer putFloat(int index, float value) { if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index); - MemoryUtil.memPutFloat(address + index, value); + UnsafeUtils.setMemFloat(address + index, value); return this; } @@ -419,4 +416,4 @@ public class EaglerLWJGLByteBuffer implements ByteBuffer { return this; } -} +} \ No newline at end of file diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLFloatBuffer.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLFloatBuffer.java index 5110c17..9ca315b 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLFloatBuffer.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLFloatBuffer.java @@ -1,10 +1,12 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; -import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.jemalloc.JEmalloc; +import net.lax1dude.unsafememcpy.UnsafeMemcpy; +import net.lax1dude.unsafememcpy.UnsafeUtils; + /** - * 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 @@ -106,47 +108,45 @@ public class EaglerLWJGLFloatBuffer implements FloatBuffer { @Override public float get() { if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - return MemoryUtil.memGetFloat(address + ((position++) << SHIFT)); + return UnsafeUtils.getMemFloat(address + ((position++) << SHIFT)); } @Override public FloatBuffer put(float b) { if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - MemoryUtil.memPutFloat(address + ((position++) << SHIFT), b); + UnsafeUtils.setMemFloat(address + ((position++) << SHIFT), b); return this; } @Override public float get(int index) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - return MemoryUtil.memGetFloat(address + (index << SHIFT)); + return UnsafeUtils.getMemFloat(address + (index << SHIFT)); } @Override public FloatBuffer put(int index, float b) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - MemoryUtil.memPutFloat(address + (index << SHIFT), b); + UnsafeUtils.setMemFloat(address + (index << SHIFT), b); return this; } @Override public float getElement(int index) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - return MemoryUtil.memGetFloat(address + (index << SHIFT)); + return UnsafeUtils.getMemFloat(address + (index << SHIFT)); } @Override public void putElement(int index, float value) { if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - MemoryUtil.memPutFloat(address + ((position++) << SHIFT), value); + UnsafeUtils.setMemFloat(address + ((position++) << SHIFT), value); } @Override public FloatBuffer get(float[] dst, int offset, int length) { if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); - for(int i = 0; i < length; ++i) { - dst[offset + i] = MemoryUtil.memGetFloat(address + ((position + i) << SHIFT)); - } + UnsafeMemcpy.memcpyAlignDst(dst, offset << SHIFT, address + (position << SHIFT), length); position += length; return this; } @@ -154,9 +154,7 @@ public class EaglerLWJGLFloatBuffer implements FloatBuffer { @Override public FloatBuffer get(float[] dst) { if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); - for(int i = 0; i < dst.length; ++i) { - dst[i] = MemoryUtil.memGetFloat(address + ((position + i) << SHIFT)); - } + UnsafeMemcpy.memcpyAlignDst(dst, 0, address + (position << SHIFT), dst.length); position += dst.length; return this; } @@ -167,14 +165,14 @@ public class EaglerLWJGLFloatBuffer implements FloatBuffer { EaglerLWJGLFloatBuffer c = (EaglerLWJGLFloatBuffer)src; int l = c.limit - c.position; if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); - MemoryUtil.memCopy(c.address + (c.position << SHIFT), address + (position << SHIFT), l << SHIFT); + UnsafeMemcpy.memcpy(address + (position << SHIFT), c.address + (c.position << SHIFT), l << SHIFT); position += l; c.position += l; }else { int l = src.remaining(); if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); for(int i = 0; i < l; ++i) { - MemoryUtil.memPutFloat(address + ((position + l) << SHIFT), src.get()); + UnsafeUtils.setMemFloat(address + ((position + l) << SHIFT), src.get()); } position += l; } @@ -184,9 +182,7 @@ public class EaglerLWJGLFloatBuffer implements FloatBuffer { @Override public FloatBuffer put(float[] src, int offset, int length) { if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); - for(int i = 0; i < length; ++i) { - MemoryUtil.memPutFloat(address + ((position + i) << SHIFT), src[offset + i]); - } + UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, offset << SHIFT, length); position += length; return this; } @@ -194,9 +190,7 @@ public class EaglerLWJGLFloatBuffer implements FloatBuffer { @Override public FloatBuffer put(float[] src) { if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); - for(int i = 0; i < src.length; ++i) { - MemoryUtil.memPutFloat(address + ((position + i) << SHIFT), src[i]); - } + UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, 0, src.length); position += src.length; return this; } @@ -217,7 +211,10 @@ public class EaglerLWJGLFloatBuffer implements FloatBuffer { int newLen = limit - position; long newAlloc = JEmalloc.nje_malloc(newLen); - MemoryUtil.memCopy(address + (position << SHIFT), newAlloc, newLen << SHIFT); + if(newAlloc == 0l) { + throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + } + UnsafeMemcpy.memcpy(newAlloc, address + (position << SHIFT), newLen << SHIFT); return new EaglerLWJGLFloatBuffer(newAlloc, newLen, true); } @@ -278,4 +275,4 @@ public class EaglerLWJGLFloatBuffer implements FloatBuffer { return this; } -} +} \ No newline at end of file diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLIntBuffer.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLIntBuffer.java index 6471eed..2068099 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLIntBuffer.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLIntBuffer.java @@ -1,10 +1,12 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; -import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.jemalloc.JEmalloc; +import net.lax1dude.unsafememcpy.UnsafeMemcpy; +import net.lax1dude.unsafememcpy.UnsafeUtils; + /** - * 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 @@ -106,47 +108,45 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { @Override public int get() { if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - return MemoryUtil.memGetInt(address + ((position++) << SHIFT)); + return UnsafeUtils.getMemInt(address + ((position++) << SHIFT)); } @Override public IntBuffer put(int b) { if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - MemoryUtil.memPutInt(address + ((position++) << SHIFT), b); + UnsafeUtils.setMemInt(address + ((position++) << SHIFT), b); return this; } @Override public int get(int index) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - return MemoryUtil.memGetInt(address + (index << SHIFT)); + return UnsafeUtils.getMemInt(address + (index << SHIFT)); } @Override public IntBuffer put(int index, int b) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - MemoryUtil.memPutInt(address + (index << SHIFT), b); + UnsafeUtils.setMemInt(address + (index << SHIFT), b); return this; } @Override public int getElement(int index) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - return MemoryUtil.memGetInt(address + (index << SHIFT)); + return UnsafeUtils.getMemInt(address + (index << SHIFT)); } @Override public void putElement(int index, int value) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - MemoryUtil.memPutInt(address + (index << SHIFT), value); + UnsafeUtils.setMemInt(address + (index << SHIFT), value); } @Override public IntBuffer get(int[] dst, int offset, int length) { if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); - for(int i = 0; i < length; ++i) { - dst[offset + i] = MemoryUtil.memGetInt(address + ((position + i) << SHIFT)); - } + UnsafeMemcpy.memcpyAlignDst(dst, offset << SHIFT, address + (position << SHIFT), length); position += length; return this; } @@ -154,9 +154,7 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { @Override public IntBuffer get(int[] dst) { if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); - for(int i = 0; i < dst.length; ++i) { - dst[i] = MemoryUtil.memGetInt(address + ((position + i) << SHIFT)); - } + UnsafeMemcpy.memcpyAlignDst(dst, 0, address + (position << SHIFT), dst.length); position += dst.length; return this; } @@ -167,14 +165,14 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { EaglerLWJGLIntBuffer c = (EaglerLWJGLIntBuffer)src; int l = c.limit - c.position; if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); - MemoryUtil.memCopy(c.address + (c.position << SHIFT), address + (position << SHIFT), l << SHIFT); + UnsafeMemcpy.memcpy(address + (position << SHIFT), c.address + (c.position << SHIFT), l << SHIFT); position += l; c.position += l; }else { int l = src.remaining(); if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); for(int i = 0; i < l; ++i) { - MemoryUtil.memPutInt(address + ((position + l) << SHIFT), src.get()); + UnsafeUtils.setMemInt(address + ((position + l) << SHIFT), src.get()); } position += l; } @@ -184,9 +182,7 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { @Override public IntBuffer put(int[] src, int offset, int length) { if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); - for(int i = 0; i < length; ++i) { - MemoryUtil.memPutInt(address + ((position + i) << SHIFT), src[offset + i]); - } + UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, offset << SHIFT, length); position += length; return this; } @@ -194,9 +190,7 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { @Override public IntBuffer put(int[] src) { if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); - for(int i = 0; i < src.length; ++i) { - MemoryUtil.memPutInt(address + ((position + i) << SHIFT), src[i]); - } + UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, 0, src.length); position += src.length; return this; } @@ -217,7 +211,10 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { int newLen = limit - position; long newAlloc = JEmalloc.nje_malloc(newLen); - MemoryUtil.memCopy(address + (position << SHIFT), newAlloc, newLen << SHIFT); + if(newAlloc == 0l) { + throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + } + UnsafeMemcpy.memcpy(newAlloc, address + (position << SHIFT), newLen << SHIFT); return new EaglerLWJGLIntBuffer(newAlloc, newLen, true); } @@ -278,4 +275,4 @@ public class EaglerLWJGLIntBuffer implements IntBuffer { return this; } -} +} \ No newline at end of file diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLShortBuffer.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLShortBuffer.java index 01e0198..59f4eb8 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLShortBuffer.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLShortBuffer.java @@ -1,10 +1,12 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; -import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.jemalloc.JEmalloc; +import net.lax1dude.unsafememcpy.UnsafeMemcpy; +import net.lax1dude.unsafememcpy.UnsafeUtils; + /** - * 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 @@ -106,47 +108,45 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer { @Override public short get() { if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - return MemoryUtil.memGetShort(address + ((position++) << SHIFT)); + return UnsafeUtils.getMemShort(address + ((position++) << SHIFT)); } @Override public ShortBuffer put(short b) { if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - MemoryUtil.memPutShort(address + ((position++) << SHIFT), b); + UnsafeUtils.setMemShort(address + ((position++) << SHIFT), b); return this; } @Override public short get(int index) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - return MemoryUtil.memGetShort(address + (index << SHIFT)); + return UnsafeUtils.getMemShort(address + (index << SHIFT)); } @Override public ShortBuffer put(int index, short b) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - MemoryUtil.memPutShort(address + (index << SHIFT), b); + UnsafeUtils.setMemShort(address + (index << SHIFT), b); return this; } @Override public short getElement(int index) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - return MemoryUtil.memGetShort(address + (index << SHIFT)); + return UnsafeUtils.getMemShort(address + (index << SHIFT)); } @Override public void putElement(int index, short value) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - MemoryUtil.memPutShort(address + (index << SHIFT), value); + UnsafeUtils.setMemShort(address + (index << SHIFT), value); } @Override public ShortBuffer get(short[] dst, int offset, int length) { if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); - for(int i = 0; i < length; ++i) { - dst[offset + i] = MemoryUtil.memGetShort(address + ((position + i) << SHIFT)); - } + UnsafeMemcpy.memcpyAlignDst(dst, offset << SHIFT, address + (position << SHIFT), length); position += length; return this; } @@ -154,9 +154,7 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer { @Override public ShortBuffer get(short[] dst) { if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); - for(int i = 0; i < dst.length; ++i) { - dst[i] = MemoryUtil.memGetShort(address + ((position + i) << SHIFT)); - } + UnsafeMemcpy.memcpyAlignDst(dst, 0, address + (position << SHIFT), dst.length); position += dst.length; return this; } @@ -167,14 +165,14 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer { EaglerLWJGLShortBuffer c = (EaglerLWJGLShortBuffer)src; int l = c.limit - c.position; if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); - MemoryUtil.memCopy(c.address + (c.position << SHIFT), address + (position << SHIFT), l << SHIFT); + UnsafeMemcpy.memcpy(address + (position << SHIFT), c.address + (c.position << SHIFT), l << SHIFT); position += l; c.position += l; }else { int l = src.remaining(); if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); for(int i = 0; i < l; ++i) { - MemoryUtil.memPutShort(address + ((position + l) << SHIFT), src.get()); + UnsafeUtils.setMemInt(address + ((position + l) << SHIFT), src.get()); } position += l; } @@ -184,9 +182,7 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer { @Override public ShortBuffer put(short[] src, int offset, int length) { if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); - for(int i = 0; i < length; ++i) { - MemoryUtil.memPutShort(address + ((position + i) << SHIFT), src[offset + i]); - } + UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, offset << SHIFT, length); position += length; return this; } @@ -194,9 +190,7 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer { @Override public ShortBuffer put(short[] src) { if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); - for(int i = 0; i < src.length; ++i) { - MemoryUtil.memPutShort(address + ((position + i) << SHIFT), src[i]); - } + UnsafeMemcpy.memcpyAlignSrc(address + (position << SHIFT), src, 0, src.length); position += src.length; return this; } @@ -217,7 +211,10 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer { int newLen = limit - position; long newAlloc = JEmalloc.nje_malloc(newLen); - MemoryUtil.memCopy(address + (position << SHIFT), newAlloc, newLen << SHIFT); + if(newAlloc == 0l) { + throw new OutOfMemoryError("Native je_malloc call returned null pointer!"); + } + UnsafeMemcpy.memcpy(newAlloc, address + (position << SHIFT), newLen << SHIFT); return new EaglerLWJGLShortBuffer(newAlloc, newLen, true); } @@ -278,4 +275,4 @@ public class EaglerLWJGLShortBuffer implements ShortBuffer { return this; } -} +} \ No newline at end of file diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DebugFilesystem.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DebugFilesystem.java new file mode 100644 index 0000000..41afa22 --- /dev/null +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DebugFilesystem.java @@ -0,0 +1,224 @@ +package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.vfs2.EaglerFileSystemException; +import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFSIterator2.BreakLoop; + +/** + * 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 DebugFilesystem implements PlatformFilesystem.IFilesystemProvider { + + public static DebugFilesystem initialize(File filesystemRoot) { + if(!filesystemRoot.isDirectory() && !filesystemRoot.mkdirs()) { + throw new EaglerFileSystemException("Could not create directory for virtual filesystem: " + filesystemRoot.getAbsolutePath()); + } + return new DebugFilesystem(filesystemRoot); + } + + private final File filesystemRoot; + + private DebugFilesystem(File root) { + this.filesystemRoot = root; + } + + @Override + public boolean eaglerDelete(String pathName) { + File f = getJREFile(pathName); + if(!f.exists()) { + PlatformFilesystem.logger.warn("Tried to delete file that doesn't exist: \"{}\"", pathName); + return false; + } + if(f.delete()) { + deleteParentIfEmpty(f); + return true; + } + return false; + } + + @Override + public ByteBuffer eaglerRead(String pathName) { + File f = getJREFile(pathName); + if(f.isFile()) { + long fileSize = f.length(); + if(fileSize > 2147483647L) throw new EaglerFileSystemException("Too large: " + fileSize + " @ " + f.getAbsolutePath()); + ByteBuffer buf = PlatformRuntime.allocateByteBuffer((int)fileSize); + try(FileInputStream is = new FileInputStream(f)) { + byte[] copyBuffer = new byte[4096]; + int i; + while((i = is.read(copyBuffer, 0, copyBuffer.length)) != -1) { + buf.put(copyBuffer, 0, i); + } + if(buf.remaining() > 0) { + throw new EaglerFileSystemException("ERROR: " + buf.remaining() + " bytes are remaining after reading: " + f.getAbsolutePath()); + } + buf.flip(); + ByteBuffer tmp = buf; + buf = null; + return tmp; + }catch (IOException e) { + throw new EaglerFileSystemException("Failed to read: " + f.getAbsolutePath(), e); + }catch(ArrayIndexOutOfBoundsException ex) { + throw new EaglerFileSystemException("ERROR: Expected " + fileSize + " bytes, buffer overflow reading: " + f.getAbsolutePath(), ex); + }finally { + if(buf != null) { + PlatformRuntime.freeByteBuffer(buf); + } + } + }else { + PlatformFilesystem.logger.warn("Tried to read file that doesn't exist: \"{}\"", f.getAbsolutePath()); + return null; + } + } + + @Override + public void eaglerWrite(String pathName, ByteBuffer data) { + File f = getJREFile(pathName); + File p = f.getParentFile(); + if(!p.isDirectory()) { + if(!p.mkdirs()) { + throw new EaglerFileSystemException("Could not create parent directory: " + p.getAbsolutePath()); + } + } + try(FileOutputStream fos = new FileOutputStream(f)) { + byte[] copyBuffer = new byte[Math.min(4096, data.remaining())]; + int i; + while((i = data.remaining()) > 0) { + if(i > copyBuffer.length) { + i = copyBuffer.length; + } + data.get(copyBuffer, 0, i); + fos.write(copyBuffer, 0, i); + } + }catch (IOException e) { + throw new EaglerFileSystemException("Failed to write: " + f.getAbsolutePath(), e); + } + } + + @Override + public boolean eaglerExists(String pathName) { + return getJREFile(pathName).isFile(); + } + + @Override + public boolean eaglerMove(String pathNameOld, String pathNameNew) { + File f1 = getJREFile(pathNameOld); + File f2 = getJREFile(pathNameNew); + if(f2.exists()) { + PlatformFilesystem.logger.warn("Tried to rename file \"{}\" to \"{}\" which already exists! File will be replaced"); + if(!f2.delete()) { + return false; + } + } + if(f1.renameTo(f2)) { + deleteParentIfEmpty(f1); + return true; + } + return false; + } + + @Override + public int eaglerCopy(String pathNameOld, String pathNameNew) { + File f1 = getJREFile(pathNameOld); + File f2 = getJREFile(pathNameNew); + if(!f1.isFile()) { + return -1; + } + if(f2.isDirectory()) { + throw new EaglerFileSystemException("Destination file is a directory: " + f2.getAbsolutePath()); + } + File p = f2.getParentFile(); + if(!p.isDirectory()) { + if(!p.mkdirs()) { + throw new EaglerFileSystemException("Could not create parent directory: " + p.getAbsolutePath()); + } + } + int sz = 0; + try(FileInputStream is = new FileInputStream(f1)) { + try(FileOutputStream os = new FileOutputStream(f2)) { + byte[] copyBuffer = new byte[4096]; + int i; + while((i = is.read(copyBuffer, 0, copyBuffer.length)) != -1) { + os.write(copyBuffer, 0, i); + sz += i; + } + } + }catch (IOException e) { + throw new EaglerFileSystemException("Failed to copy \"" + f1.getAbsolutePath() + "\" to file \"" + f2.getAbsolutePath() + "\"", e); + } + return sz; + } + + @Override + public int eaglerSize(String pathName) { + File f = getJREFile(pathName); + if(f.isFile()) { + long fileSize = f.length(); + if(fileSize > 2147483647L) throw new EaglerFileSystemException("Too large: " + fileSize + " @ " + f.getAbsolutePath()); + return (int)fileSize; + }else { + return -1; + } + } + + @Override + public void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive) { + try { + iterateFile(pathName, getJREFile(pathName), itr, recursive); + }catch(BreakLoop ex) { + } + } + + private void iterateFile(String pathName, File f, VFSFilenameIterator itr, boolean recursive) { + if(!f.exists()) { + return; + } + if(!f.isDirectory()) { + itr.next(pathName); + return; + } + File[] fa = f.listFiles(); + for(int i = 0; i < fa.length; ++i) { + File ff = fa[i]; + String fn = pathName + "/" + ff.getName(); + if(ff.isDirectory()) { + if(recursive) { + iterateFile(fn, ff, itr, true); + } + }else { + itr.next(fn); + } + } + } + + private File getJREFile(String path) { + return new File(filesystemRoot, path); + } + + private void deleteParentIfEmpty(File f) { + String[] s; + while((f = f.getParentFile()) != null && (s = f.list()) != null && s.length == 0) { + f.delete(); + } + } +} \ No newline at end of file diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopClientConfigAdapter.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopClientConfigAdapter.java index 26b1be6..7f02c9a 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopClientConfigAdapter.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopClientConfigAdapter.java @@ -123,5 +123,10 @@ public class DesktopClientConfigAdapter implements IClientConfigAdapter { public boolean isEnableSignatureBadge() { return false; } + + @Override + public boolean isAllowVoiceClient() { + return false; + } } diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/FilesystemConvertingDialog.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/FilesystemConvertingDialog.java new file mode 100644 index 0000000..fe34b6d --- /dev/null +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/FilesystemConvertingDialog.java @@ -0,0 +1,78 @@ +package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; +import java.awt.Color; +import java.awt.BorderLayout; +import javax.swing.JProgressBar; +import java.awt.Dimension; +import java.awt.Toolkit; + +import javax.swing.JLabel; +import javax.swing.SwingConstants; +import javax.swing.UIManager; + +/** + * 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 FilesystemConvertingDialog extends JFrame { + + private static final long serialVersionUID = 1L; + private JPanel contentPane; + private JProgressBar progressBar; + + public FilesystemConvertingDialog(String title) { + setIconImage(Toolkit.getDefaultToolkit().getImage("icon32.png")); + setResizable(false); + setAlwaysOnTop(true); + setTitle("EaglercraftX 1.8"); + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + setBounds(100, 100, 420, 100); + contentPane = new JPanel(); + contentPane.setBackground(new Color(255, 255, 255)); + contentPane.setBorder(null); + + setContentPane(contentPane); + contentPane.setLayout(new BorderLayout(0, 0)); + + JPanel panel = new JPanel(); + panel.setBorder(new EmptyBorder(10, 10, 10, 10)); + panel.setBackground(new Color(255, 255, 255)); + contentPane.add(panel, BorderLayout.SOUTH); + panel.setLayout(new BorderLayout(0, 0)); + + progressBar = new JProgressBar(); + progressBar.setIndeterminate(true); + progressBar.setPreferredSize(new Dimension(146, 20)); + progressBar.setMinimum(0); + progressBar.setMaximum(512); + panel.add(progressBar, BorderLayout.CENTER); + + JLabel lblNewLabel = new JLabel(title); + lblNewLabel.setFont(UIManager.getFont("PopupMenu.font")); + lblNewLabel.setHorizontalAlignment(SwingConstants.CENTER); + contentPane.add(lblNewLabel, BorderLayout.CENTER); + } + + public void setProgressIndeterminate(boolean itr) { + progressBar.setIndeterminate(itr); + } + + public void setProgressValue(int val) { + progressBar.setValue(val); + } + +} \ No newline at end of file diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/JDBCFilesystem.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/JDBCFilesystem.java new file mode 100644 index 0000000..bafb146 --- /dev/null +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/JDBCFilesystem.java @@ -0,0 +1,441 @@ +package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; + +import java.sql.Connection; +import java.sql.Driver; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Collection; +import java.util.Enumeration; +import java.util.LinkedList; +import java.util.Map.Entry; +import java.util.Properties; + +import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem.IFilesystemProvider; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem; +import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.vfs2.EaglerFileSystemException; +import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFSIterator2; +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 JDBCFilesystem implements IFilesystemProvider { + + public static final Logger logger = LogManager.getLogger("JDBCFilesystem"); + + private boolean newFilesystem = true; + + private static volatile boolean cleanupThreadStarted = false; + private static final Collection jdbcFilesystems = new LinkedList(); + + private final String jdbcUri; + private final String jdbcDriver; + + private final Connection conn; + private final PreparedStatement createStatement; + private final PreparedStatement updateStatement; + private final PreparedStatement readStatement; + private final PreparedStatement existsStatement; + private final PreparedStatement sizeStatement; + private final PreparedStatement deleteStatement; + private final PreparedStatement renameStatement; + private final PreparedStatement iterateNonRecursive; + private final PreparedStatement iterateRecursive; + private boolean hasClosed = false; + + private final Object mutex = new Object(); + + public static IFilesystemProvider initialize(String jdbcUri, String jdbcDriver) { + Class driver; + try { + driver = Class.forName(jdbcDriver); + } catch (ClassNotFoundException e) { + throw new EaglerFileSystemException("JDBC driver class not found in JRE: " + jdbcDriver, e); + } + Driver driverObj = null; + Enumeration registeredDriversItr = DriverManager.getDrivers(); + while(registeredDriversItr.hasMoreElements()) { + Driver drv = registeredDriversItr.nextElement(); + if(drv.getClass().equals(driver)) { + driverObj = drv; + break; + } + } + if(driverObj == null) { + logger.warn("The class \"{}\" is not a registered JDBC driver, eaglercraft will try all registered drivers...", jdbcDriver); + } + Properties props = new Properties(); + for(Entry etr : System.getProperties().entrySet()) { + if(etr.getKey() instanceof String) { + String str = (String)etr.getKey(); + if(str.startsWith("eagler.jdbc.opts.")) { + props.put(str.substring(17), etr.getValue()); + } + } + } + logger.info("Connecting to database: \"{}\"", jdbcUri); + Connection conn; + try { + if(driverObj != null) { + conn = driverObj.connect(jdbcUri, props); + }else { + conn = DriverManager.getConnection(jdbcUri, props); + } + }catch(SQLException ex) { + throw new EaglerFileSystemException("Failed to connect to database: \"" + jdbcUri + "\"", ex); + } + try { + return new JDBCFilesystem(conn, jdbcUri, jdbcDriver); + } catch (SQLException ex) { + try { + conn.close(); + }catch(SQLException ex2) { + } + throw new EaglerFileSystemException("Failed to initialize database: \"" + jdbcUri + "\"", ex); + } + } + + private JDBCFilesystem(Connection conn, String jdbcUri, String jdbcDriver) throws SQLException { + this.conn = conn; + this.jdbcUri = jdbcUri; + this.jdbcDriver = jdbcDriver; + try(Statement stmt = conn.createStatement()) { + stmt.execute("CREATE TABLE IF NOT EXISTS " + + "\"eaglercraft_desktop_runtime_filesystem\" (" + + "\"FileName\" VARCHAR(1024) NOT NULL," + + "\"FileSize\" INT NOT NULL," + + "\"FileData\" BLOB NOT NULL," + + "PRIMARY KEY(\"FileName\"))"); + + int totalFiles = 0; + try(ResultSet resultSet = stmt.executeQuery("SELECT COUNT(*) AS total_files FROM eaglercraft_desktop_runtime_filesystem")) { + if(resultSet.next()) { + totalFiles = resultSet.getInt(1); + } + } + logger.info("Loaded JDBC filesystem with {} files: \"{}\"", totalFiles, jdbcUri); + if(totalFiles > 0) { + newFilesystem = false; + } + } + this.createStatement = conn.prepareStatement("INSERT INTO eaglercraft_desktop_runtime_filesystem (FileName, FileSize, FileData) VALUES(?,?,?)"); + this.updateStatement = conn.prepareStatement("UPDATE eaglercraft_desktop_runtime_filesystem SET FileSize = ?, FileData = ? WHERE FileName = ?"); + this.readStatement = conn.prepareStatement("SELECT FileData FROM eaglercraft_desktop_runtime_filesystem WHERE FileName = ? LIMIT 1"); + this.existsStatement = conn.prepareStatement("SELECT COUNT(FileName) AS has_object FROM eaglercraft_desktop_runtime_filesystem WHERE FileName = ? LIMIT 1"); + this.sizeStatement = conn.prepareStatement("SELECT FileSize FROM eaglercraft_desktop_runtime_filesystem WHERE FileName = ? LIMIT 1"); + this.deleteStatement = conn.prepareStatement("DELETE FROM eaglercraft_desktop_runtime_filesystem WHERE FileName = ?"); + this.renameStatement = conn.prepareStatement("UPDATE eaglercraft_desktop_runtime_filesystem SET FileName = ? WHERE FileName = ?"); + this.iterateNonRecursive = conn.prepareStatement("SELECT FileName FROM eaglercraft_desktop_runtime_filesystem WHERE FileName LIKE ? AND NOT FileName LIKE ?"); + this.iterateRecursive = conn.prepareStatement("SELECT FileName FROM eaglercraft_desktop_runtime_filesystem WHERE FileName LIKE ?"); + startCleanupThread(); + synchronized(jdbcFilesystems) { + jdbcFilesystems.add(this); + } + } + + public boolean isNewFilesystem() { + return newFilesystem; + } + + private static void startCleanupThread() { + if(!cleanupThreadStarted) { + cleanupThreadStarted = true; + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + synchronized(jdbcFilesystems) { + if(!jdbcFilesystems.isEmpty()) { + for(JDBCFilesystem fs : jdbcFilesystems) { + fs.shutdown0(); + } + jdbcFilesystems.clear(); + } + } + }, "JDBCFilesystemCleanup")); + } + } + + public void shutdown() { + shutdown0(); + synchronized(jdbcFilesystems) { + jdbcFilesystems.remove(this); + } + } + + private void shutdown0() { + synchronized(mutex) { + if(!hasClosed) { + hasClosed = true; + logger.info("Disconnecting from database: \"{}\"", jdbcUri); + try { + shutdown1(); + }catch(Throwable t) { + logger.error("Failed to disconnect from database: \"{}\""); + logger.error(t); + } + } + } + } + + private void shutdown1() throws SQLException { + if(!conn.isClosed()) { + quietClose(createStatement); + quietClose(updateStatement); + quietClose(readStatement); + quietClose(existsStatement); + quietClose(sizeStatement); + quietClose(deleteStatement); + quietClose(renameStatement); + quietClose(iterateNonRecursive); + quietClose(iterateRecursive); + conn.close(); + } + } + + private static void quietClose(Statement stmt) { + try { + stmt.close(); + }catch(Throwable t) { + } + } + + @Override + public boolean eaglerDelete(String pathName) { + try { + synchronized(mutex) { + if(hasClosed || conn.isClosed()) { + throw new SQLException("Filesystem database connection is closed!"); + } + deleteStatement.setString(1, pathName); + int ret = deleteStatement.executeUpdate(); + if(ret == 0) { + PlatformFilesystem.logger.warn("Tried to delete file that doesn't exist: \"{}\"", pathName); + } + return ret > 0; + } + }catch(SQLException ex) { + throw new EaglerFileSystemException("JDBC exception thrown while executing delete!", ex); + } + } + + @Override + public ByteBuffer eaglerRead(String pathName) { + try { + synchronized(mutex) { + if(hasClosed || conn.isClosed()) { + throw new SQLException("Filesystem database connection is closed!"); + } + readStatement.setString(1, pathName); + byte[] has = null; + try(ResultSet resultSet = readStatement.executeQuery()) { + if(resultSet.next()) { + has = resultSet.getBytes(1); + } + } + if(has == null) { + PlatformFilesystem.logger.warn("Tried to read file that doesn't exist: \"{}\"", pathName); + return null; + } + ByteBuffer byteBuf = PlatformRuntime.allocateByteBuffer(has.length); + byteBuf.put(has); + byteBuf.flip(); + return byteBuf; + } + }catch(SQLException ex) { + throw new EaglerFileSystemException("JDBC exception thrown while executing read!", ex); + } + } + + @Override + public void eaglerWrite(String pathName, ByteBuffer data) { + try { + synchronized(mutex) { + if(hasClosed || conn.isClosed()) { + throw new SQLException("Filesystem database connection is closed!"); + } + existsStatement.setString(1, pathName); + boolean exists; + try(ResultSet resultSet = existsStatement.executeQuery()) { + if(resultSet.next()) { + exists = resultSet.getInt(1) > 0; + }else { + exists = false; + } + } + byte[] cp = new byte[data.remaining()]; + data.get(cp); + if(exists) { + updateStatement.setInt(1, cp.length); + updateStatement.setBytes(2, cp); + updateStatement.setString(3, pathName); + if(updateStatement.executeUpdate() == 0) { + throw new EaglerFileSystemException("SQL file update query did not update any rows!"); + } + }else { + createStatement.setString(1, pathName); + createStatement.setInt(2, cp.length); + createStatement.setBytes(3, cp); + createStatement.executeUpdate(); + } + } + }catch(SQLException ex) { + throw new EaglerFileSystemException("JDBC exception thrown while executing write!", ex); + } + } + + @Override + public boolean eaglerExists(String pathName) { + try { + synchronized(mutex) { + if(hasClosed || conn.isClosed()) { + throw new SQLException("Filesystem database connection is closed!"); + } + existsStatement.setString(1, pathName); + try(ResultSet resultSet = existsStatement.executeQuery()) { + if(resultSet.next()) { + return resultSet.getInt(1) > 0; + }else { + return false; + } + } + } + }catch(SQLException ex) { + throw new EaglerFileSystemException("JDBC exception thrown while executing exists!", ex); + } + } + + @Override + public boolean eaglerMove(String pathNameOld, String pathNameNew) { + try { + synchronized(mutex) { + if(hasClosed || conn.isClosed()) { + throw new SQLException("Filesystem database connection is closed!"); + } + renameStatement.setString(1, pathNameNew); + renameStatement.setString(2, pathNameOld); + return renameStatement.executeUpdate() > 0; + } + }catch(SQLException ex) { + throw new EaglerFileSystemException("JDBC exception thrown while executing move!", ex); + } + } + + @Override + public int eaglerCopy(String pathNameOld, String pathNameNew) { + try { + synchronized(mutex) { + if(hasClosed || conn.isClosed()) { + throw new SQLException("Filesystem database connection is closed!"); + } + readStatement.setString(1, pathNameOld); + try(ResultSet resultSet = readStatement.executeQuery()) { + byte[] has = null; + if(resultSet.next()) { + has = resultSet.getBytes(1); + } + if(has == null) { + return -1; + } + existsStatement.setString(1, pathNameNew); + boolean exists; + try(ResultSet resultSet2 = existsStatement.executeQuery()) { + if(resultSet2.next()) { + exists = resultSet2.getInt(1) > 0; + }else { + exists = false; + } + } + if(exists) { + updateStatement.setInt(1, has.length); + updateStatement.setBytes(2, has); + updateStatement.setString(3, pathNameNew); + if(updateStatement.executeUpdate() == 0) { + throw new EaglerFileSystemException("SQL file update query did not update any rows!"); + } + }else { + createStatement.setString(1, pathNameNew); + createStatement.setInt(2, has.length); + createStatement.setBytes(3, has); + createStatement.executeUpdate(); + } + return has.length; + } + } + }catch(SQLException ex) { + throw new EaglerFileSystemException("JDBC exception thrown while executing copy!", ex); + } + } + + @Override + public int eaglerSize(String pathName) { + try { + synchronized(mutex) { + if(hasClosed || conn.isClosed()) { + throw new SQLException("Filesystem database connection is closed!"); + } + sizeStatement.setString(1, pathName); + try(ResultSet resultSet = sizeStatement.executeQuery()) { + if(resultSet.next()) { + return resultSet.getInt(1); + }else { + return -1; + } + } + } + }catch(SQLException ex) { + throw new EaglerFileSystemException("JDBC exception thrown while executing size!", ex); + } + } + + @Override + public void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive) { + try { + synchronized(mutex) { + if(hasClosed || conn.isClosed()) { + throw new SQLException("Filesystem database connection is closed!"); + } + PreparedStatement stmt; + if(recursive) { + stmt = iterateRecursive; + stmt.setString(1, pathName + (!pathName.endsWith("/") ? "/%" : "%"));; + }else { + stmt = iterateNonRecursive; + if(!pathName.endsWith("/")) { + pathName += "/"; + } + stmt.setString(1, pathName + "%"); + stmt.setString(2, pathName + "%/%"); + } + try(ResultSet resultSet = stmt.executeQuery()) { + while(resultSet.next()) { + try { + itr.next(resultSet.getString(1)); + }catch(VFSIterator2.BreakLoop exx) { + break; + } + } + } + } + }catch(SQLException ex) { + throw new EaglerFileSystemException("JDBC exception thrown while executing iterate!", ex); + } + } + +} \ No newline at end of file diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/JDBCFilesystemConverter.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/JDBCFilesystemConverter.java new file mode 100644 index 0000000..ea9034f --- /dev/null +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/JDBCFilesystemConverter.java @@ -0,0 +1,130 @@ +package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem.IFilesystemProvider; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.vfs2.EaglerFileSystemException; +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 JDBCFilesystemConverter { + + private static final Logger logger = LogManager.getLogger("JDBCFilesystemConverter"); + + public static void convertFilesystem(String title, File oldFS, IFilesystemProvider newFS, boolean deleteOld) { + FilesystemConvertingDialog progressDialog = new FilesystemConvertingDialog(title); + try { + progressDialog.setProgressIndeterminate(true); + progressDialog.setLocationRelativeTo(null); + progressDialog.setVisible(true); + + String slug = oldFS.getAbsolutePath(); + List filesToCopy = new ArrayList(); + logger.info("Discovering files to convert..."); + iterateFolder(slug.length(), oldFS, filesToCopy); + logger.info("Found {} files in the old directory", filesToCopy.size()); + + progressDialog.setProgressIndeterminate(false); + progressDialog.setProgressValue(0); + + int progCounter = 0; + int lastProgUpdate = 0; + byte[] copyArray = new byte[4096]; + + int l = filesToCopy.size(); + for(int i = 0; i < l; ++i) { + String str = filesToCopy.get(i); + File f = new File(oldFS, str); + try(InputStream is = new FileInputStream(f)) { + ByteBuffer copyBuffer = PlatformRuntime.allocateByteBuffer((int)f.length()); + try { + int j; + while(copyBuffer.hasRemaining() && (j = is.read(copyArray, 0, copyArray.length)) != -1) { + copyBuffer.put(copyArray, 0, j); + } + copyBuffer.flip(); + progCounter += copyBuffer.remaining(); + newFS.eaglerWrite(str, copyBuffer); + }finally { + PlatformRuntime.freeByteBuffer(copyBuffer); + } + if(progCounter - lastProgUpdate > 25000) { + lastProgUpdate = progCounter; + logger.info("Converted {}/{} files, {} bytes to JDBC format...", (i + 1), l, progCounter); + } + }catch(IOException ex) { + throw new EaglerFileSystemException("Failed to convert file: \"" + f.getAbsolutePath() + "\"", ex); + } + progressDialog.setProgressValue(i * 512 / (l - 1)); + } + + logger.info("Converted {}/{} files successfully!", l, l); + + if(deleteOld) { + logger.info("Deleting old filesystem..."); + progressDialog.setProgressIndeterminate(true); + deleteOldFolder(oldFS); + logger.info("Delete complete!"); + } + }finally { + progressDialog.setVisible(false); + progressDialog.dispose(); + } + } + + private static void iterateFolder(int slug, File file, List ret) { + File[] f = file.listFiles(); + if(f == null) { + return; + } + for(int i = 0; i < f.length; ++i) { + File ff = f[i]; + if(ff.isDirectory()) { + iterateFolder(slug, ff, ret); + }else { + String str = ff.getAbsolutePath(); + if(str.length() > slug) { + str = str.substring(slug).replace('\\', '/'); + if(str.startsWith("/")) { + str = str.substring(1); + } + ret.add(str); + } + } + } + } + + private static void deleteOldFolder(File file) { + File[] f = file.listFiles(); + for(int i = 0; i < f.length; ++i) { + if(f[i].isDirectory()) { + deleteOldFolder(f[i]); + }else { + f[i].delete(); + } + } + file.delete(); + } +} \ No newline at end of file diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/LWJGLEntryPoint.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/LWJGLEntryPoint.java index 23af40b..849e836 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/LWJGLEntryPoint.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/LWJGLEntryPoint.java @@ -6,6 +6,7 @@ import javax.swing.UnsupportedLookAndFeelException; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EagUtils; import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformANGLE; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem; import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.ShaderSource; @@ -43,15 +44,9 @@ public class LWJGLEntryPoint { boolean hideRenderDocDialog = false; for(int i = 0; i < args.length; ++i) { - if(args[i].equalsIgnoreCase("highp")) { - ShaderSource.setHighP(true); - } if(args[i].equalsIgnoreCase("hide-renderdoc")) { hideRenderDocDialog = true; } - if(args[i].equalsIgnoreCase("fullscreen")) { - PlatformInput.setStartupFullscreen(true); - } } if(!hideRenderDocDialog) { @@ -66,7 +61,7 @@ public class LWJGLEntryPoint { lr.dispose(); } - getANGLEPlatformFromArgs(args); + getPlatformOptionsFromArgs(args); RelayManager.relayManager.load(EagRuntime.getStorage("r")); @@ -81,12 +76,22 @@ public class LWJGLEntryPoint { } - private static void getANGLEPlatformFromArgs(String[] args) { + private static void getPlatformOptionsFromArgs(String[] args) { for(int i = 0; i < args.length; ++i) { - EnumPlatformANGLE angle = EnumPlatformANGLE.fromId(args[i]); - if(angle != EnumPlatformANGLE.DEFAULT) { - PlatformRuntime.requestANGLE(angle); - break; + if(args[i].equalsIgnoreCase("fullscreen")) { + PlatformInput.setStartupFullscreen(true); + }else if(args[i].equalsIgnoreCase("highp")) { + ShaderSource.setHighP(true); + }else if(args[i].startsWith("jdbc:")) { + if(i < args.length - 1) { + PlatformFilesystem.setUseJDBC(args[i]); + PlatformFilesystem.setJDBCDriverClass(args[++i]); + } + }else { + EnumPlatformANGLE angle = EnumPlatformANGLE.fromId(args[i]); + if(angle != EnumPlatformANGLE.DEFAULT) { + PlatformRuntime.requestANGLE(angle); + } } } } diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/TestProgram.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/TestProgram.java deleted file mode 100644 index 26f348e..0000000 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/TestProgram.java +++ /dev/null @@ -1,37 +0,0 @@ -package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; - -import net.lax1dude.eaglercraft.v1_8.Display; -import net.lax1dude.eaglercraft.v1_8.EagRuntime; -import net.lax1dude.eaglercraft.v1_8.EagUtils; -import net.lax1dude.eaglercraft.v1_8.Keyboard; -import net.lax1dude.eaglercraft.v1_8.Mouse; -import net.lax1dude.eaglercraft.v1_8.internal.KeyboardConstants; - -public class TestProgram { - - private static boolean grab = false; - - public static void main_(String[] args) { - - while(!Display.isCloseRequested()) { - - Keyboard.enableRepeatEvents(true); - Display.update(); - - while(Keyboard.next()) { - if(Keyboard.getEventKey() == KeyboardConstants.KEY_E && Keyboard.getEventKeyState()) { - grab = !grab; - Mouse.setGrabbed(grab); - } - } - - System.out.println("" + Mouse.getDX() + ", " + Mouse.getDY()); - - EagUtils.sleep(100l); - } - - EagRuntime.destroy(); - - } - -} diff --git a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/opengl/GlStateManager.java b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/opengl/GlStateManager.java index b231677..a51779a 100644 --- a/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/opengl/GlStateManager.java +++ b/src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/opengl/GlStateManager.java @@ -11,7 +11,7 @@ import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; /** - * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -982,6 +982,30 @@ public class GlStateManager { Matrix4f.mul(modeMatrix, paramMatrix, modeMatrix); } + + public static final void multMatrix(Matrix4f matrix) { + Matrix4f modeMatrix; + + switch(stateMatrixMode) { + case GL_MODELVIEW: + default: + modeMatrix = modelMatrixStack[modelMatrixStackPointer]; + modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; + break; + case GL_PROJECTION: + modeMatrix = projectionMatrixStack[projectionMatrixStackPointer]; + projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; + break; + case GL_TEXTURE: + int ptr = textureMatrixStackPointer[activeTexture]; + modeMatrix = textureMatrixStack[activeTexture][ptr]; + textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = + ++textureMatrixAccessSerial[activeTexture]; + break; + } + + Matrix4f.mul(modeMatrix, matrix, modeMatrix); + } public static final void color(float colorRed, float colorGreen, float colorBlue, float colorAlpha) { stateColorR = colorRed; diff --git a/src/lwjgl/java/net/minecraft/client/Minecraft.java b/src/lwjgl/java/net/minecraft/client/Minecraft.java index 7b64380..028195b 100644 --- a/src/lwjgl/java/net/minecraft/client/Minecraft.java +++ b/src/lwjgl/java/net/minecraft/client/Minecraft.java @@ -39,6 +39,7 @@ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFolderResourcePack; import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFontRenderer; +import net.lax1dude.eaglercraft.v1_8.opengl.EaglerMeshLoader; import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; @@ -67,6 +68,8 @@ import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiScreenSingleplayerConnecting; import net.lax1dude.eaglercraft.v1_8.sp.internal.ClientPlatformSingleplayer; import net.lax1dude.eaglercraft.v1_8.sp.lan.LANServerController; import net.lax1dude.eaglercraft.v1_8.update.RelayUpdateChecker; +import net.lax1dude.eaglercraft.v1_8.voice.GuiVoiceOverlay; +import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.client.audio.MusicTicker; @@ -307,6 +310,8 @@ public class Minecraft implements IThreadListener { private boolean isLANOpen = false; public SkullCommand eagskullCommand; + + public GuiVoiceOverlay voiceOverlay; public Minecraft(GameConfiguration gameConfig) { theMinecraft = this; @@ -421,6 +426,7 @@ public class Minecraft implements IThreadListener { this.mcResourceManager.registerReloadListener(new MetalsLUT()); this.mcResourceManager.registerReloadListener(new EmissiveItems()); this.mcResourceManager.registerReloadListener(new BlockVertexIDs()); + this.mcResourceManager.registerReloadListener(new EaglerMeshLoader()); AchievementList.openInventory.setStatStringFormatter(new IStatStringFormat() { public String formatString(String parString1) { try { @@ -471,6 +477,9 @@ public class Minecraft implements IThreadListener { this.checkGLError("Post startup"); this.ingameGUI = new GuiIngame(this); this.eagskullCommand = new SkullCommand(this); + this.voiceOverlay = new GuiVoiceOverlay(this); + ScaledResolution voiceRes = new ScaledResolution(this); + this.voiceOverlay.setResolution(voiceRes.getScaledWidth(), voiceRes.getScaledHeight()); ServerList.initServerList(this); EaglerProfile.read(); @@ -868,6 +877,7 @@ public class Minecraft implements IThreadListener { public void updateDisplay() { this.mcProfiler.startSection("display_update"); + Display.setVSync(this.gameSettings.enableVsync); Display.update(); this.mcProfiler.endSection(); this.checkWindowResize(); @@ -1214,12 +1224,14 @@ public class Minecraft implements IThreadListener { private void resize(int width, int height) { this.displayWidth = Math.max(1, width); this.displayHeight = Math.max(1, height); + ScaledResolution scaledresolution = new ScaledResolution(this); if (this.currentScreen != null) { - ScaledResolution scaledresolution = new ScaledResolution(this); this.currentScreen.onResize(this, scaledresolution.getScaledWidth(), scaledresolution.getScaledHeight()); } this.loadingScreen = new LoadingScreenRenderer(this); + + this.voiceOverlay.setResolution(scaledresolution.getScaledWidth(), scaledresolution.getScaledHeight()); } public MusicTicker func_181535_r() { @@ -1257,6 +1269,9 @@ public class Minecraft implements IThreadListener { if (!this.isGamePaused) { this.ingameGUI.updateTick(); } + + this.mcProfiler.endStartSection("eaglerVoice"); + VoiceClientController.tickVoiceClient(this); this.mcProfiler.endSection(); this.entityRenderer.getMouseOver(1.0F); @@ -1764,6 +1779,7 @@ public class Minecraft implements IThreadListener { */ public void launchIntegratedServer(String folderName, String worldName, WorldSettings worldSettingsIn) { this.loadWorld((WorldClient) null); + Minecraft.getMinecraft().getRenderManager().setEnableFNAWSkins(this.gameSettings.enableFNAWSkins); session.reset(); SingleplayerServerController.launchEaglercraftServer(folderName, gameSettings.difficulty.getDifficultyId(), Math.max(gameSettings.renderDistanceChunks, 2), worldSettingsIn); @@ -2308,4 +2324,12 @@ public class Minecraft implements IThreadListener { public void clearTitles() { ingameGUI.displayTitle(null, null, -1, -1, -1); } + + public boolean getEnableFNAWSkins() { + boolean ret = this.gameSettings.enableFNAWSkins; + if (this.thePlayer != null) { + ret &= this.thePlayer.sendQueue.currentFNAWSkinAllowedState; + } + return ret; + } } \ No newline at end of file diff --git a/src/lwjgl/java/net/minecraft/client/gui/GuiIngame.java b/src/lwjgl/java/net/minecraft/client/gui/GuiIngame.java index a3a9b19..1f3c13a 100644 --- a/src/lwjgl/java/net/minecraft/client/gui/GuiIngame.java +++ b/src/lwjgl/java/net/minecraft/client/gui/GuiIngame.java @@ -287,6 +287,10 @@ public class GuiIngame extends Gui { if (scoreobjective1 != null) { this.renderScoreboard(scoreobjective1, scaledresolution); } + + if (this.mc.currentScreen == null) { + this.mc.voiceOverlay.drawOverlay(); + } GlStateManager.enableBlend(); GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); diff --git a/src/lwjgl/java/net/minecraft/client/gui/GuiIngameMenu.java b/src/lwjgl/java/net/minecraft/client/gui/GuiIngameMenu.java index 420cabc..1b17165 100644 --- a/src/lwjgl/java/net/minecraft/client/gui/GuiIngameMenu.java +++ b/src/lwjgl/java/net/minecraft/client/gui/GuiIngameMenu.java @@ -1,5 +1,6 @@ package net.minecraft.client.gui; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.Mouse; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; @@ -8,6 +9,8 @@ import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiScreenLANNotSupported; import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiShareToLan; import net.lax1dude.eaglercraft.v1_8.sp.lan.LANServerController; import net.lax1dude.eaglercraft.v1_8.update.GuiUpdateCheckerOverlay; +import net.lax1dude.eaglercraft.v1_8.voice.GuiVoiceMenu; +import net.minecraft.client.Minecraft; import net.minecraft.client.audio.PositionedSoundRecord; import net.minecraft.client.gui.achievement.GuiAchievements; import net.minecraft.client.gui.achievement.GuiStats; @@ -44,9 +47,13 @@ public class GuiIngameMenu extends GuiScreen { boolean hasSentAutoSave = !SingleplayerServerController.isWorldRunning(); private GuiUpdateCheckerOverlay updateCheckerOverlay; + private GuiVoiceMenu voiceMenu; public GuiIngameMenu() { updateCheckerOverlay = new GuiUpdateCheckerOverlay(true, this); + if (EagRuntime.getConfiguration().isAllowVoiceClient()) { + voiceMenu = new GuiVoiceMenu(this); + } } /**+ @@ -75,6 +82,10 @@ public class GuiIngameMenu extends GuiScreen { I18n.format("gui.achievements", new Object[0]))); this.buttonList.add(new GuiButton(6, this.width / 2 + 2, this.height / 4 + 48 + b0, 98, 20, I18n.format("gui.stats", new Object[0]))); + GuiButton mods; + this.buttonList.add(mods = new GuiButton(69420, this.width / 2 - 100, this.height / 4 + 73 + b0, + I18n.format("EaglerForge Mods", new Object[0]))); + mods.enabled = false; lanButton.enabled = SingleplayerServerController.isWorldRunning(); if (!hasSentAutoSave) { hasSentAutoSave = true; @@ -139,6 +150,10 @@ public class GuiIngameMenu extends GuiScreen { */ public void updateScreen() { super.updateScreen(); + if (EagRuntime.getConfiguration().isAllowVoiceClient() + && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { + voiceMenu.updateScreen(); + } if (Mouse.isActuallyGrabbed()) { Mouse.setGrabbed(false); } @@ -156,37 +171,67 @@ public class GuiIngameMenu extends GuiScreen { this.updateCheckerOverlay.drawScreen(i, j, f); if (LANServerController.isLANOpen()) { + int offset = this.updateCheckerOverlay.getSharedWorldInfoYOffset(); String str = I18n.format("lanServer.pauseMenu0"); - drawString(fontRendererObj, str, 6, 32, 0xFFFF55); + drawString(fontRendererObj, str, 6, 10 + offset, 0xFFFF55); if (mc.gameSettings.hideJoinCode) { GlStateManager.pushMatrix(); - GlStateManager.translate(7.0f, 47.0f, 0.0f); + GlStateManager.translate(7.0f, 25.0f + offset, 0.0f); GlStateManager.scale(0.75f, 0.75f, 0.75f); str = I18n.format("lanServer.showCode"); int w = fontRendererObj.getStringWidth(str); - boolean hover = i > 6 && i < 8 + w * 3 / 4 && j > 46 && j < 47 + 8; + boolean hover = i > 4 && i < 8 + w * 3 / 4 && j > 24 + offset && j < 25 + offset + 8; drawString(fontRendererObj, EnumChatFormatting.UNDERLINE + str, 0, 0, hover ? 0xEEEEAA : 0xCCCC55); GlStateManager.popMatrix(); } else { int w = fontRendererObj.getStringWidth(str); GlStateManager.pushMatrix(); - GlStateManager.translate(6 + w + 3, 33, 0.0f); + GlStateManager.translate(6 + w + 3, 11 + offset, 0.0f); GlStateManager.scale(0.75f, 0.75f, 0.75f); str = I18n.format("lanServer.hideCode"); int w2 = fontRendererObj.getStringWidth(str); - boolean hover = i > 6 + w + 2 && i < 6 + w + 3 + w2 * 3 / 4 && j > 33 - 1 && j < 33 + 6; + boolean hover = i > 6 + w + 2 && i < 6 + w + 3 + w2 * 3 / 4 && j > 11 + offset - 1 + && j < 11 + offset + 6; drawString(fontRendererObj, EnumChatFormatting.UNDERLINE + str, 0, 0, hover ? 0xEEEEAA : 0xCCCC55); GlStateManager.popMatrix(); - drawString(fontRendererObj, EnumChatFormatting.GRAY + I18n.format("lanServer.pauseMenu1") + " " - + EnumChatFormatting.RESET + LANServerController.getCurrentURI(), 6, 47, 0xFFFFFF); - drawString(fontRendererObj, EnumChatFormatting.GRAY + I18n.format("lanServer.pauseMenu2") + " " - + EnumChatFormatting.RESET + LANServerController.getCurrentCode(), 6, 57, 0xFFFFFF); + drawString( + fontRendererObj, EnumChatFormatting.GRAY + I18n.format("lanServer.pauseMenu1") + " " + + EnumChatFormatting.RESET + LANServerController.getCurrentURI(), + 6, 25 + offset, 0xFFFFFF); + drawString( + fontRendererObj, EnumChatFormatting.GRAY + I18n.format("lanServer.pauseMenu2") + " " + + EnumChatFormatting.RESET + LANServerController.getCurrentCode(), + 6, 35 + offset, 0xFFFFFF); } } - super.drawScreen(i, j, f); + try { + if (EagRuntime.getConfiguration().isAllowVoiceClient() + && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { + if (voiceMenu.isBlockingInput()) { + super.drawScreen(0, 0, f); + } else { + super.drawScreen(i, j, f); + } + voiceMenu.drawScreen(i, j, f); + } else { + super.drawScreen(i, j, f); + } + } catch (GuiVoiceMenu.AbortedException ex) { + } + } + + protected void keyTyped(char par1, int par2) { + try { + if (EagRuntime.getConfiguration().isAllowVoiceClient() + && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { + voiceMenu.keyTyped(par1, par2); + } + super.keyTyped(par1, par2); + } catch (GuiVoiceMenu.AbortedException ex) { + } } public void confirmClicked(boolean par1, int par2) { @@ -201,11 +246,20 @@ public class GuiIngameMenu extends GuiScreen { } protected void mouseClicked(int par1, int par2, int par3) { + try { + if (EagRuntime.getConfiguration().isAllowVoiceClient() + && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { + voiceMenu.mouseClicked(par1, par2, par3); + } + } catch (GuiVoiceMenu.AbortedException ex) { + return; + } if (par3 == 0) { + int offset = this.updateCheckerOverlay.getSharedWorldInfoYOffset(); if (mc.gameSettings.hideJoinCode) { String str = I18n.format("lanServer.showCode"); int w = fontRendererObj.getStringWidth(str); - if (par1 > 6 && par1 < 8 + w * 3 / 4 && par2 > 46 && par2 < 47 + 8) { + if (par1 > 4 && par1 < 8 + w * 3 / 4 && par2 > 24 + offset && par2 < 25 + offset + 8) { mc.gameSettings.hideJoinCode = false; this.mc.getSoundHandler() .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); @@ -216,7 +270,8 @@ public class GuiIngameMenu extends GuiScreen { int w = fontRendererObj.getStringWidth(str); str = I18n.format("lanServer.hideCode"); int w2 = fontRendererObj.getStringWidth(str); - if (par1 > 6 + w + 2 && par1 < 6 + w + 3 + w2 * 3 / 4 && par2 > 33 - 1 && par2 < 33 + 6) { + if (par1 > 6 + w + 2 && par1 < 6 + w + 3 + w2 * 3 / 4 && par2 > 11 + offset - 1 + && par2 < 11 + offset + 6) { mc.gameSettings.hideJoinCode = true; this.mc.getSoundHandler() .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); @@ -228,4 +283,22 @@ public class GuiIngameMenu extends GuiScreen { this.updateCheckerOverlay.mouseClicked(par1, par2, par3); super.mouseClicked(par1, par2, par3); } + + public void setWorldAndResolution(Minecraft par1Minecraft, int par2, int par3) { + super.setWorldAndResolution(par1Minecraft, par2, par3); + if (EagRuntime.getConfiguration().isAllowVoiceClient()) { + voiceMenu.setResolution(par1Minecraft, par2, par3); + } + } + + protected void mouseReleased(int par1, int par2, int par3) { + try { + if (EagRuntime.getConfiguration().isAllowVoiceClient() + && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { + voiceMenu.mouseReleased(par1, par2, par3); + } + super.mouseReleased(par1, par2, par3); + } catch (GuiVoiceMenu.AbortedException ex) { + } + } } \ No newline at end of file diff --git a/src/lwjgl/java/net/minecraft/client/gui/GuiScreen.java b/src/lwjgl/java/net/minecraft/client/gui/GuiScreen.java index 837f008..5bc126e 100644 --- a/src/lwjgl/java/net/minecraft/client/gui/GuiScreen.java +++ b/src/lwjgl/java/net/minecraft/client/gui/GuiScreen.java @@ -673,4 +673,8 @@ public abstract class GuiScreen extends Gui implements GuiYesNoCallback { public boolean shouldHangupIntegratedServer() { return true; } + + public boolean blockPTTKey() { + return false; + } } \ No newline at end of file diff --git a/src/lwjgl/java/net/minecraft/client/network/NetHandlerPlayClient.java b/src/lwjgl/java/net/minecraft/client/network/NetHandlerPlayClient.java index 88b4c48..e888bb9 100644 --- a/src/lwjgl/java/net/minecraft/client/network/NetHandlerPlayClient.java +++ b/src/lwjgl/java/net/minecraft/client/network/NetHandlerPlayClient.java @@ -13,12 +13,15 @@ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; import com.google.common.collect.Maps; import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.lax1dude.eaglercraft.v1_8.profile.CapePackets; +import net.lax1dude.eaglercraft.v1_8.profile.ServerCapeCache; import net.lax1dude.eaglercraft.v1_8.profile.ServerSkinCache; import net.lax1dude.eaglercraft.v1_8.profile.SkinPackets; import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; import net.lax1dude.eaglercraft.v1_8.sp.lan.LANClientNetworkManager; import net.lax1dude.eaglercraft.v1_8.sp.socket.ClientIntegratedServerNetworkManager; import net.lax1dude.eaglercraft.v1_8.update.UpdateService; +import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFolderResourcePack; @@ -242,6 +245,8 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { */ private final EaglercraftRandom avRandomizer = new EaglercraftRandom(); private final ServerSkinCache skinCache; + private final ServerCapeCache capeCache; + public boolean currentFNAWSkinAllowedState = true; public NetHandlerPlayClient(Minecraft mcIn, GuiScreen parGuiScreen, EaglercraftNetworkManager parNetworkManager, GameProfile parGameProfile) { @@ -250,6 +255,7 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { this.netManager = parNetworkManager; this.profile = parGameProfile; this.skinCache = new ServerSkinCache(parNetworkManager, mcIn.getTextureManager()); + this.capeCache = new ServerCapeCache(parNetworkManager, mcIn.getTextureManager()); this.isIntegratedServer = (parNetworkManager instanceof ClientIntegratedServerNetworkManager) || (parNetworkManager instanceof LANClientNetworkManager); } @@ -261,12 +267,17 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { public void cleanup() { this.clientWorldController = null; this.skinCache.destroy(); + this.capeCache.destroy(); } public ServerSkinCache getSkinCache() { return this.skinCache; } + public ServerCapeCache getCapeCache() { + return this.capeCache; + } + /**+ * Registers some server properties * (gametype,hardcore-mode,terraintype,difficulty,player limit), @@ -290,6 +301,10 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { this.gameController.gameSettings.sendSettingsToServer(); this.netManager.sendPacket(new C17PacketCustomPayload("MC|Brand", (new PacketBuffer(Unpooled.buffer())).writeString(ClientBrandRetriever.getClientModName()))); + if (VoiceClientController.isClientSupported()) { + VoiceClientController.initializeVoiceClient((pkt) -> this.netManager + .sendPacket(new C17PacketCustomPayload(VoiceClientController.SIGNAL_CHANNEL, pkt))); + } } /**+ @@ -712,6 +727,9 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { * describing the reason for termination */ public void onDisconnect(IChatComponent ichatcomponent) { + VoiceClientController.handleServerDisconnect(); + Minecraft.getMinecraft().getRenderManager() + .setEnableFNAWSkins(this.gameController.gameSettings.enableFNAWSkins); if (this.gameController.theWorld != null) { this.gameController.loadWorld((WorldClient) null); } @@ -1419,6 +1437,7 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { EaglercraftUUID uuid = s38packetplayerlistitem$addplayerdata.getProfile().getId(); this.playerInfoMap.remove(uuid); this.skinCache.evictSkin(uuid); + this.capeCache.evictCape(uuid); } else { NetworkPlayerInfo networkplayerinfo = (NetworkPlayerInfo) this.playerInfoMap .get(s38packetplayerlistitem$addplayerdata.getProfile().getId()); @@ -1600,6 +1619,13 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { logger.error("Couldn't read EAG|Skins-1.8 packet!"); logger.error(e); } + } else if ("EAG|Capes-1.8".equals(packetIn.getChannelName())) { + try { + CapePackets.readPluginMessage(packetIn.getBufferData(), capeCache); + } catch (IOException e) { + logger.error("Couldn't read EAG|Capes-1.8 packet!"); + logger.error(e); + } } else if ("EAG|UpdateCert-1.8".equals(packetIn.getChannelName())) { if (EagRuntime.getConfiguration().allowUpdateSvc()) { try { @@ -1612,6 +1638,14 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient { logger.error(e); } } + } else if (VoiceClientController.SIGNAL_CHANNEL.equals(packetIn.getChannelName())) { + if (VoiceClientController.isClientSupported()) { + VoiceClientController.handleVoiceSignalPacket(packetIn.getBufferData()); + } + } else if ("EAG|FNAWSEn-1.8".equals(packetIn.getChannelName())) { + this.currentFNAWSkinAllowedState = packetIn.getBufferData().readBoolean(); + Minecraft.getMinecraft().getRenderManager().setEnableFNAWSkins( + this.currentFNAWSkinAllowedState && Minecraft.getMinecraft().gameSettings.enableFNAWSkins); } } diff --git a/src/lwjgl/java/net/minecraft/client/settings/GameSettings.java b/src/lwjgl/java/net/minecraft/client/settings/GameSettings.java index 87660d2..6f1ead7 100644 --- a/src/lwjgl/java/net/minecraft/client/settings/GameSettings.java +++ b/src/lwjgl/java/net/minecraft/client/settings/GameSettings.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.Set; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager; +import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; import org.json.JSONArray; import com.google.common.collect.ImmutableSet; @@ -33,6 +34,7 @@ import net.lax1dude.eaglercraft.v1_8.EaglerZLIB; import net.lax1dude.eaglercraft.v1_8.HString; import net.lax1dude.eaglercraft.v1_8.Keyboard; import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformType; import net.lax1dude.eaglercraft.v1_8.internal.KeyboardConstants; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; @@ -110,7 +112,7 @@ public class GameSettings { public boolean chatLinksPrompt = true; public float chatOpacity = 1.0F; public boolean snooperEnabled = true; - public boolean enableVsync = true; + public boolean enableVsync = EagRuntime.getPlatformType() != EnumPlatformType.DESKTOP; public boolean allowBlockAlternatives = true; public boolean reducedDebugInfo = false; public boolean hideServerAddress; @@ -265,6 +267,12 @@ public class GameSettings { public boolean ofSmoothBiomes = true; public boolean ofCustomColors = true; public boolean hidePassword = true; + + public boolean enableFNAWSkins = true; + public int voiceListenRadius = 16; + public float voiceListenVolume = 0.5f; + public float voiceSpeakVolume = 0.5f; + public int voicePTTKey = 47; // V public GameSettings(Minecraft mcIn) { this.keyBindings = (KeyBinding[]) ArrayUtils.addAll(new KeyBinding[] { this.keyBindAttack, this.keyBindUseItem, @@ -896,6 +904,15 @@ public class GameSettings { if (parOptions == GameSettings.Options.HIDE_PASSWORD) { hidePassword =! hidePassword; } + + if (parOptions == GameSettings.Options.FNAW_SKINS) { + this.enableFNAWSkins = !this.enableFNAWSkins; + this.mc.getRenderManager().setEnableFNAWSkins(this.mc.getEnableFNAWSkins()); + } + + if (parOptions == GameSettings.Options.EAGLER_VSYNC) { + this.enableVsync = !this.enableVsync; + } this.saveOptions(); } @@ -1019,6 +1036,10 @@ public class GameSettings { return this.ofCustomColors; case HIDE_PASSWORD: return hidePassword; + case FNAW_SKINS: + return this.enableFNAWSkins; + case EAGLER_VSYNC: + return this.enableVsync; default: return false; } @@ -1474,7 +1495,7 @@ public class GameSettings { this.snooperEnabled = astring[1].equals("true"); } - if (astring[0].equals("enableVsync")) { + if (astring[0].equals("enableVsyncEag")) { this.enableVsync = astring[1].equals("true"); } @@ -1869,7 +1890,21 @@ public class GameSettings { hidePassword = Boolean.valueOf(astring[1]).booleanValue(); } - Keyboard.setFunctionKeyModifier(keyBindFunction.getKeyCode()); + if (astring[0].equals("voiceListenRadius")) { + voiceListenRadius = Integer.parseInt(astring[1]); + } + + if (astring[0].equals("voiceListenVolume")) { + voiceListenVolume = this.parseFloat(astring[1]); + } + + if (astring[0].equals("voiceSpeakVolume")) { + voiceSpeakVolume = this.parseFloat(astring[1]); + } + + if (astring[0].equals("voicePTTKey")) { + voicePTTKey = Integer.parseInt(astring[1]); + } for (SoundCategory soundcategory : SoundCategory._VALUES) { if (astring[0].equals("soundCategory_" + soundcategory.getCategoryName())) { @@ -1882,6 +1917,10 @@ public class GameSettings { this.setModelPartEnabled(enumplayermodelparts, astring[1].equals("true")); } } + + if (astring[0].equals("enableFNAWSkins")) { + this.enableFNAWSkins = astring[1].equals("true"); + } deferredShaderConf.readOption(astring[0], astring[1]); } catch (Exception var8) { @@ -1890,8 +1929,16 @@ public class GameSettings { } KeyBinding.resetKeyBindingArrayAndHash(); + + Keyboard.setFunctionKeyModifier(keyBindFunction.getKeyCode()); + VoiceClientController.setVoiceListenVolume(voiceListenVolume); + VoiceClientController.setVoiceSpeakVolume(voiceSpeakVolume); + VoiceClientController.setVoiceProximity(voiceListenRadius); + if (this.mc.getRenderManager() != null) + this.mc.getRenderManager().setEnableFNAWSkins(this.enableFNAWSkins); } catch (Exception exception) { - logger.error("Failed to load options", exception); + logger.error("Failed to load options"); + logger.error(exception); } } @@ -1958,7 +2005,7 @@ public class GameSettings { printwriter.println("chatLinksPrompt:" + this.chatLinksPrompt); printwriter.println("chatOpacity:" + this.chatOpacity); printwriter.println("snooperEnabled:" + this.snooperEnabled); - printwriter.println("enableVsync:" + this.enableVsync); + printwriter.println("enableVsyncEag:" + this.enableVsync); printwriter.println("hideServerAddress:" + this.hideServerAddress); printwriter.println("advancedItemTooltips:" + this.advancedItemTooltips); printwriter.println("pauseOnLostFocus:" + this.pauseOnLostFocus); @@ -2047,6 +2094,11 @@ public class GameSettings { printwriter.println("ofSmoothBiomes:" + this.ofSmoothBiomes); printwriter.println("ofCustomColors:" + this.ofCustomColors); printwriter.println("hidePassword:" + hidePassword); + printwriter.println("voiceListenRadius:" + this.voiceListenRadius); + printwriter.println("voiceListenVolume:" + this.voiceListenVolume); + printwriter.println("voiceSpeakVolume:" + this.voiceSpeakVolume); + printwriter.println("voicePTTKey:" + this.voicePTTKey); + printwriter.println("enableFNAWSkins:" + this.enableFNAWSkins); for (KeyBinding keybinding : this.keyBindings) { printwriter.println("key_" + keybinding.getKeyDescription() + ":" + keybinding.getKeyCode()); @@ -2069,7 +2121,8 @@ public class GameSettings { printwriter.close(); return bao.toByteArray(); } catch (Exception exception) { - logger.error("Failed to save options", exception); + logger.error("Failed to save options"); + logger.error(exception); return null; } @@ -2255,7 +2308,9 @@ public class GameSettings { SWAMP_COLORS("Swamp Colors", false, false), SMOOTH_BIOMES("Smooth Biomes", false, false), CUSTOM_COLORS("Custom Colors", false, false), - HIDE_PASSWORD("Hide Password", false, false); + HIDE_PASSWORD("Hide Password", false, false), + FNAW_SKINS("options.skinCustomisation.enableFNAWSkins", false, true), + EAGLER_VSYNC("options.vsync", false, true); private final boolean enumFloat; private final boolean enumBoolean; diff --git a/src/main/java/net/PeytonPlayz585/shadow/GuiOtherSettingsOF.java b/src/main/java/net/PeytonPlayz585/shadow/GuiOtherSettingsOF.java index 098d6fd..02b0851 100644 --- a/src/main/java/net/PeytonPlayz585/shadow/GuiOtherSettingsOF.java +++ b/src/main/java/net/PeytonPlayz585/shadow/GuiOtherSettingsOF.java @@ -12,7 +12,7 @@ public class GuiOtherSettingsOF extends GuiScreen implements GuiYesNoCallback { private GuiScreen prevScreen; protected String title; private GameSettings settings; - private static GameSettings.Options[] enumOptions = new GameSettings.Options[] {GameSettings.Options.LAGOMETER, GameSettings.Options.PROFILER, GameSettings.Options.WEATHER, GameSettings.Options.TIME, GameSettings.Options.FULLSCREEN, GameSettings.Options.HUD_FPS, GameSettings.Options.AUTOSAVE_TICKS, GameSettings.Options.ANAGLYPH, GameSettings.Options.HUD_COORDS, GameSettings.Options.HUD_PLAYER, GameSettings.Options.HUD_STATS, GameSettings.Options.HUD_WORLD, GameSettings.Options.HUD_24H}; + private static GameSettings.Options[] enumOptions = new GameSettings.Options[] {GameSettings.Options.LAGOMETER, GameSettings.Options.PROFILER, GameSettings.Options.WEATHER, GameSettings.Options.TIME, GameSettings.Options.FULLSCREEN, GameSettings.Options.FNAW_SKINS, GameSettings.Options.HUD_FPS, GameSettings.Options.AUTOSAVE_TICKS, GameSettings.Options.ANAGLYPH, GameSettings.Options.HUD_COORDS, GameSettings.Options.HUD_PLAYER, GameSettings.Options.HUD_STATS, GameSettings.Options.HUD_WORLD, GameSettings.Options.HUD_24H}; public GuiOtherSettingsOF(GuiScreen p_i51_1_, GameSettings p_i51_2_) { this.prevScreen = p_i51_1_; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/Display.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/Display.java index 2f46e14..33569cd 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/Display.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/Display.java @@ -44,6 +44,10 @@ public class Display { public static boolean isCloseRequested() { return PlatformInput.isCloseRequested(); } + + public static void setVSync(boolean enable) { + PlatformInput.setVSync(enable); + } public static void update() { PlatformInput.update(); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java index f6d71a1..1e1f4ea 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java @@ -35,7 +35,7 @@ public class EaglercraftVersion { public static final boolean enableUpdateService = false; public static final String updateBundlePackageName = "net.lax1dude.eaglercraft.v1_8.client"; - public static final int updateBundlePackageVersionInt = 27; + public static final int updateBundlePackageVersionInt = 29; public static final String updateLatestLocalStorageKey = "latestUpdate_" + updateBundlePackageName; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IClientConfigAdapter.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IClientConfigAdapter.java index dc519b1..684275c 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IClientConfigAdapter.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/IClientConfigAdapter.java @@ -67,4 +67,6 @@ public interface IClientConfigAdapter { boolean isCheckRelaysForUpdates(); boolean isEnableSignatureBadge(); + + boolean isAllowVoiceClient(); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglerMeshLoader.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglerMeshLoader.java new file mode 100644 index 0000000..7f4f258 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglerMeshLoader.java @@ -0,0 +1,172 @@ +package net.lax1dude.eaglercraft.v1_8.opengl; + +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +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.client.Minecraft; +import net.minecraft.client.resources.IResourceManager; +import net.minecraft.client.resources.IResourceManagerReloadListener; +import net.minecraft.util.ResourceLocation; + +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; +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. + * + */ +public class EaglerMeshLoader implements IResourceManagerReloadListener { + + private static final Logger logger = LogManager.getLogger("EaglerMeshLoader"); + + private static final Map meshCache = new HashMap(); + + public static HighPolyMesh getEaglerMesh(ResourceLocation meshLoc) { + if(meshLoc.cachedPointerType == ResourceLocation.CACHED_POINTER_EAGLER_MESH) { + return (HighPolyMesh)meshLoc.cachedPointer; + } + HighPolyMesh theMesh = meshCache.get(meshLoc); + if(theMesh == null) { + theMesh = new HighPolyMesh(); + reloadMesh(meshLoc, theMesh, Minecraft.getMinecraft().getResourceManager()); + } + meshLoc.cachedPointerType = ResourceLocation.CACHED_POINTER_EAGLER_MESH; + meshLoc.cachedPointer = theMesh; + return theMesh; + } + + private static void reloadMesh(ResourceLocation meshLoc, HighPolyMesh meshStruct, IResourceManager resourceManager) { + IntBuffer up1 = null; + try { + int intsOfVertex, intsOfIndex, intsTotal, stride; + try(DataInputStream dis = new DataInputStream(resourceManager.getResource(meshLoc).getInputStream())) { + byte[] header = new byte[8]; + dis.read(header); + if(!Arrays.equals(header, new byte[] { (byte) 33, (byte) 69, (byte) 65, (byte) 71, (byte) 36, + (byte) 109, (byte) 100, (byte) 108 })) { + throw new IOException("File is not an eaglercraft high-poly mesh!"); + } + + char CT = (char)dis.read(); + + if(CT == 'C') { + meshStruct.hasTexture = false; + }else if(CT == 'T') { + meshStruct.hasTexture = true; + }else { + throw new IOException("Unsupported mesh type '" + CT + "'!"); + } + + dis.skipBytes(dis.readUnsignedShort()); + + meshStruct.vertexCount = dis.readInt(); + meshStruct.indexCount = dis.readInt(); + int byteIndexCount = meshStruct.indexCount; + if(byteIndexCount % 2 != 0) { // must round up to int + byteIndexCount += 1; + } + stride = meshStruct.hasTexture ? 24 : 16; + + intsOfVertex = meshStruct.vertexCount * stride / 4; + intsOfIndex = byteIndexCount / 2; + intsTotal = intsOfIndex + intsOfVertex; + up1 = EagRuntime.allocateIntBuffer(intsTotal); + + for(int i = 0; i < intsTotal; ++i) { + int ch1 = dis.read(); + int ch2 = dis.read(); + int ch3 = dis.read(); + int ch4 = dis.read(); + if ((ch1 | ch2 | ch3 | ch4) < 0) throw new EOFException(); // rip + up1.put((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0)); + } + + } + + if(meshStruct.vertexArray == null) { + meshStruct.vertexArray = _wglGenVertexArrays(); + } + if(meshStruct.vertexBuffer == null) { + meshStruct.vertexBuffer = _wglGenBuffers(); + } + if(meshStruct.indexBuffer == null) { + meshStruct.indexBuffer = _wglGenBuffers(); + } + + up1.position(0).limit(intsOfVertex); + + EaglercraftGPU.bindGLArrayBuffer(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); + _wglBufferData(GL_ELEMENT_ARRAY_BUFFER, up1, GL_STATIC_DRAW); + + _wglEnableVertexAttribArray(0); + _wglVertexAttribPointer(0, 3, GL_FLOAT, false, stride, 0); + + if(meshStruct.hasTexture) { + _wglEnableVertexAttribArray(1); + _wglVertexAttribPointer(1, 2, GL_FLOAT, false, stride, 16); + } + + _wglEnableVertexAttribArray(meshStruct.hasTexture ? 2 : 1); + _wglVertexAttribPointer(meshStruct.hasTexture ? 2 : 1, 4, GL_BYTE, true, stride, 12); + }catch(Throwable ex) { + if(meshStruct.vertexArray != null) { + _wglDeleteVertexArrays(meshStruct.vertexArray); + meshStruct.vertexArray = null; + } + if(meshStruct.vertexBuffer != null) { + _wglDeleteBuffers(meshStruct.vertexBuffer); + meshStruct.vertexBuffer = null; + } + if(meshStruct.indexBuffer != null) { + _wglDeleteBuffers(meshStruct.indexBuffer); + meshStruct.indexBuffer = null; + } + + meshStruct.vertexCount = 0; + meshStruct.indexCount = 0; + meshStruct.hasTexture = false; + + logger.error("Failed to load eaglercraft high-poly mesh: \"{}\"", meshLoc); + logger.error(ex); + }finally { + if(up1 != null) { + EagRuntime.freeIntBuffer(up1); + } + } + } + + @Override + public void onResourceManagerReload(IResourceManager var1) { + for(Entry meshEntry : meshCache.entrySet()) { + reloadMesh(meshEntry.getKey(), meshEntry.getValue(), var1); + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java index ed06b8c..26801df 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/EaglercraftGPU.java @@ -23,7 +23,7 @@ import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; /** - * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -152,7 +152,7 @@ public class EaglercraftGPU { currentList = null; } - public static void glCallList(int displayList) { + public static final void glCallList(int displayList) { DisplayList dp = mapDisplayListsGL.get(displayList); if(dp == null) { throw new NullPointerException("Tried to call a display list that does not exist: " + displayList); @@ -491,11 +491,21 @@ public class EaglercraftGPU { public static final ITextureGL getNativeTexture(int tex) { return mapTexturesGL.get(tex); } + + public static final void drawHighPoly(HighPolyMesh mesh) { + if(mesh.vertexCount == 0 || mesh.indexCount == 0 || mesh.vertexArray == null) { + return; + } + FixedFunctionPipeline p = FixedFunctionPipeline.setupRenderDisplayList(mesh.getAttribBits()).update(); + EaglercraftGPU.bindGLBufferArray(mesh.vertexArray); + p.drawElements(GL_TRIANGLES, mesh.indexCount, GL_UNSIGNED_SHORT, 0); + } static boolean hasFramebufferHDR16FSupport = false; static boolean hasFramebufferHDR32FSupport = false; + static boolean hasLinearHDR32FSupport = false; - public static void createFramebufferHDR16FTexture(int target, int level, int w, int h, int format, boolean allow32bitFallback) { + 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); } @@ -503,7 +513,7 @@ public class EaglercraftGPU { createFramebufferHDR16FTexture(target, level, w, h, format, false, pixelData); } - private static void createFramebufferHDR16FTexture(int target, int level, int w, int h, int format, boolean allow32bitFallback, ByteBuffer pixelData) { + private static final void createFramebufferHDR16FTexture(int target, int level, int w, int h, int format, boolean allow32bitFallback, ByteBuffer pixelData) { if(hasFramebufferHDR16FSupport) { int internalFormat; switch(format) { @@ -534,15 +544,15 @@ public class EaglercraftGPU { } } - public static void createFramebufferHDR32FTexture(int target, int level, int w, int h, int format, boolean allow16bitFallback) { + public static final void createFramebufferHDR32FTexture(int target, int level, int w, int h, int format, boolean allow16bitFallback) { createFramebufferHDR32FTexture(target, level, w, h, format, allow16bitFallback, null); } - public static void createFramebufferHDR32FTexture(int target, int level, int w, int h, int format, ByteBuffer pixelData) { + public static final void createFramebufferHDR32FTexture(int target, int level, int w, int h, int format, ByteBuffer pixelData) { createFramebufferHDR32FTexture(target, level, w, h, format, false, pixelData); } - private static void createFramebufferHDR32FTexture(int target, int level, int w, int h, int format, boolean allow16bitFallback, ByteBuffer pixelData) { + private static final void createFramebufferHDR32FTexture(int target, int level, int w, int h, int format, boolean allow16bitFallback, ByteBuffer pixelData) { if(hasFramebufferHDR32FSupport) { int internalFormat; switch(format) { @@ -559,7 +569,7 @@ public class EaglercraftGPU { default: throw new UnsupportedOperationException("Unknown format: " + format); } - _wglTexImage2D(target, level, internalFormat, w, h, 0, format, GL_FLOAT, pixelData); + _wglTexImage2Df32(target, level, internalFormat, w, h, 0, format, GL_FLOAT, pixelData); }else { if(allow16bitFallback) { if(hasFramebufferHDR16FSupport) { @@ -589,7 +599,13 @@ public class EaglercraftGPU { }else { logger.error("32-bit HDR render target support: false"); } - if(!checkHasHDRFramebufferSupport()) { + hasLinearHDR32FSupport = PlatformOpenGL.checkLinearHDR32FSupport(); + if(hasLinearHDR32FSupport) { + logger.info("32-bit HDR linear filter support: true"); + }else { + logger.error("32-bit HDR linear filter support: false"); + } + if(!checkHasHDRFramebufferSupportWithFilter()) { logger.error("No HDR render target support was detected! Shaders will be disabled."); } DrawUtils.init(); @@ -620,4 +636,12 @@ public class EaglercraftGPU { public static int glGetTexLevelParameteri(int glTexture2d, int i, int glTextureWidth) { return PlatformOpenGL._wglGetTexLevelParameteri(glTexture2d, i, glTextureWidth); } + + public static final boolean checkHasHDRFramebufferSupportWithFilter() { + return hasFramebufferHDR16FSupport || (hasFramebufferHDR32FSupport && hasLinearHDR32FSupport); + } + + public static final boolean checkLinearHDR32FSupport() { + return hasLinearHDR32FSupport; + } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/HighPolyMesh.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/HighPolyMesh.java new file mode 100644 index 0000000..5cbc430 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/HighPolyMesh.java @@ -0,0 +1,66 @@ +package net.lax1dude.eaglercraft.v1_8.opengl; + +import net.lax1dude.eaglercraft.v1_8.internal.IBufferArrayGL; +import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL; +import net.lax1dude.eaglercraft.v1_8.opengl.FixedFunctionShader.FixedFunctionState; + +/** + * 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 HighPolyMesh { + + IBufferArrayGL vertexArray; + IBufferGL vertexBuffer; + IBufferGL indexBuffer; + + int vertexCount; + int indexCount; + + boolean hasTexture; + + public HighPolyMesh(IBufferArrayGL vertexArray, IBufferGL vertexBuffer, IBufferGL indexBuffer, int vertexCount, + int indexCount, boolean hasTexture) { + this.vertexArray = vertexArray; + this.vertexBuffer = vertexBuffer; + this.indexBuffer = indexBuffer; + this.vertexCount = vertexCount; + this.indexCount = indexCount; + this.hasTexture = hasTexture; + } + + HighPolyMesh() { + + } + + public boolean isNull() { + return vertexArray == null; + } + + public int getVertexCount() { + return vertexCount; + } + + public int getIndexCount() { + return indexCount; + } + + public boolean getHasTexture() { + return hasTexture; + } + + public int getAttribBits() { + return hasTexture ? (FixedFunctionState.STATE_HAS_ATTRIB_TEXTURE | FixedFunctionState.STATE_HAS_ATTRIB_NORMAL) : FixedFunctionState.STATE_HAS_ATTRIB_NORMAL; + } +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java index a1c2254..f08117f 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/EaglerDeferredPipeline.java @@ -3984,11 +3984,11 @@ public class EaglerDeferredPipeline { } public static final boolean isSupported() { - return EaglercraftGPU.checkHasHDRFramebufferSupport(); + return EaglercraftGPU.checkHasHDRFramebufferSupportWithFilter(); } public static final String getReasonUnsupported() { - if(!EaglercraftGPU.checkHasHDRFramebufferSupport()) { + if(!EaglercraftGPU.checkHasHDRFramebufferSupportWithFilter()) { return I18n.format("shaders.gui.unsupported.reason.hdrFramebuffer"); }else { return null; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/CapePackets.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/CapePackets.java new file mode 100644 index 0000000..ae93e41 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/CapePackets.java @@ -0,0 +1,83 @@ +package net.lax1dude.eaglercraft.v1_8.profile; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.minecraft.network.PacketBuffer; + +/** + * 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 CapePackets { + + public static final int PACKET_MY_CAPE_PRESET = 0x01; + public static final int PACKET_MY_CAPE_CUSTOM = 0x02; + public static final int PACKET_GET_OTHER_CAPE = 0x03; + public static final int PACKET_OTHER_CAPE_PRESET = 0x04; + public static final int PACKET_OTHER_CAPE_CUSTOM = 0x05; + + public static void readPluginMessage(PacketBuffer buffer, ServerCapeCache capeCache) throws IOException { + try { + int type = (int)buffer.readByte() & 0xFF; + switch(type) { + case PACKET_OTHER_CAPE_PRESET: { + EaglercraftUUID responseUUID = buffer.readUuid(); + int responsePreset = buffer.readInt(); + if(buffer.isReadable()) { + throw new IOException("PACKET_OTHER_CAPE_PRESET had " + buffer.readableBytes() + " remaining bytes!"); + } + capeCache.cacheCapePreset(responseUUID, responsePreset); + break; + } + case PACKET_OTHER_CAPE_CUSTOM: { + EaglercraftUUID responseUUID = buffer.readUuid(); + byte[] readCape = new byte[1173]; + buffer.readBytes(readCape); + if(buffer.isReadable()) { + throw new IOException("PACKET_OTHER_CAPE_CUSTOM had " + buffer.readableBytes() + " remaining bytes!"); + } + capeCache.cacheCapeCustom(responseUUID, readCape); + break; + } + default: + throw new IOException("Unknown skin packet type: " + type); + } + }catch(IOException ex) { + throw ex; + }catch(Throwable t) { + throw new IOException("Failed to parse cape packet!", t); + } + } + + public static byte[] writeMyCapePreset(int capeId) { + return new byte[] { (byte) PACKET_MY_CAPE_PRESET, (byte) (capeId >> 24), (byte) (capeId >> 16), + (byte) (capeId >> 8), (byte) (capeId & 0xFF) }; + } + + public static byte[] writeMyCapeCustom(CustomCape customCape) { + byte[] packet = new byte[1 + customCape.texture.length]; + packet[0] = (byte) PACKET_MY_CAPE_CUSTOM; + System.arraycopy(customCape.texture, 0, packet, 1, customCape.texture.length); + return packet; + } + + public static PacketBuffer writeGetOtherCape(EaglercraftUUID playerId) throws IOException { + PacketBuffer ret = new PacketBuffer(Unpooled.buffer(17, 17)); + ret.writeByte(PACKET_GET_OTHER_CAPE); + ret.writeUuid(playerId); + return ret; + } +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/CustomCape.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/CustomCape.java new file mode 100644 index 0000000..76b8cf0 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/CustomCape.java @@ -0,0 +1,58 @@ +package net.lax1dude.eaglercraft.v1_8.profile; + +import net.minecraft.client.Minecraft; +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 CustomCape { + + public final String name; + public final byte[] texture; + + private EaglerSkinTexture textureInstance; + private ResourceLocation resourceLocation; + + private static int texId = 0; + + public CustomCape(String name, byte[] texture) { + this.name = name; + this.texture = texture; + byte[] texture2 = new byte[4096]; + SkinConverter.convertCape23x17RGBto32x32RGBA(texture, texture2); + this.textureInstance = new EaglerSkinTexture(texture2, 32, 32); + this.resourceLocation = null; + } + + public void load() { + if(resourceLocation == null) { + resourceLocation = new ResourceLocation("eagler:capes/custom/tex_" + texId++); + Minecraft.getMinecraft().getTextureManager().loadTexture(resourceLocation, textureInstance); + } + } + + public ResourceLocation getResource() { + return resourceLocation; + } + + public void delete() { + if(resourceLocation != null) { + Minecraft.getMinecraft().getTextureManager().deleteTexture(resourceLocation); + resourceLocation = null; + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/DefaultCapes.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/DefaultCapes.java new file mode 100644 index 0000000..2f53813 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/DefaultCapes.java @@ -0,0 +1,75 @@ +package net.lax1dude.eaglercraft.v1_8.profile; + +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 enum DefaultCapes { + + NO_CAPE(0, "No Cape", null), + MINECON_2011(1, "Minecon 2011", new ResourceLocation("eagler:capes/01.minecon_2011.png")), + MINECON_2012(2, "Minecon 2012", new ResourceLocation("eagler:capes/02.minecon_2012.png")), + MINECON_2013(3, "Minecon 2013", new ResourceLocation("eagler:capes/03.minecon_2013.png")), + MINECON_2015(4, "Minecon 2015", new ResourceLocation("eagler:capes/04.minecon_2015.png")), + MINECON_2016(5, "Minecon 2016", new ResourceLocation("eagler:capes/05.minecon_2016.png")), + MICROSOFT_ACCOUNT(6, "Microsoft Account", new ResourceLocation("eagler:capes/06.microsoft_account.png")), + MAPMAKER(7, "Realms Mapmaker", new ResourceLocation("eagler:capes/07.mapmaker.png")), + MOJANG_OLD(8, "Mojang Old", new ResourceLocation("eagler:capes/08.mojang_old.png")), + MOJANG_NEW(9, "Mojang New", new ResourceLocation("eagler:capes/09.mojang_new.png")), + JIRA_MOD(10, "Jira Moderator", new ResourceLocation("eagler:capes/10.jira_mod.png")), + MOJANG_VERY_OLD(11, "Mojang Very Old", new ResourceLocation("eagler:capes/11.mojang_very_old.png")), + SCROLLS(12, "Scrolls", new ResourceLocation("eagler:capes/12.scrolls.png")), + COBALT(13, "Cobalt", new ResourceLocation("eagler:capes/13.cobalt.png")), + TRANSLATOR(14, "Lang Translator", new ResourceLocation("eagler:capes/14.translator.png")), + MILLIONTH_ACCOUNT(15, "Millionth Player", new ResourceLocation("eagler:capes/15.millionth_account.png")), + PRISMARINE(16, "Prismarine", new ResourceLocation("eagler:capes/16.prismarine.png")), + SNOWMAN(17, "Snowman", new ResourceLocation("eagler:capes/17.snowman.png")), + SPADE(18, "Spade", new ResourceLocation("eagler:capes/18.spade.png")), + BIRTHDAY(19, "Birthday", new ResourceLocation("eagler:capes/19.birthday.png")), + DB(20, "dB", new ResourceLocation("eagler:capes/20.db.png")); + + public static final DefaultCapes[] defaultCapesMap = new DefaultCapes[21]; + + public final int id; + public final String name; + public final ResourceLocation location; + + private DefaultCapes(int id, String name, ResourceLocation location) { + this.id = id; + this.name = name; + this.location = location; + } + + public static DefaultCapes getCapeFromId(int id) { + DefaultCapes e = null; + if(id >= 0 && id < defaultCapesMap.length) { + e = defaultCapesMap[id]; + } + if(e != null) { + return e; + }else { + return NO_CAPE; + } + } + + static { + DefaultCapes[] capes = values(); + for(int i = 0; i < capes.length; ++i) { + defaultCapesMap[capes[i].id] = capes[i]; + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/DefaultSkins.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/DefaultSkins.java index 5c67d07..8f88b24 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/DefaultSkins.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/DefaultSkins.java @@ -42,9 +42,14 @@ public enum DefaultSkins { CREEPER(20, "Creeper", new ResourceLocation("eagler:skins/21.creeper.png"), SkinModel.STEVE), ZOMBIE(21, "Zombie", new ResourceLocation("eagler:skins/22.zombie.png"), SkinModel.ZOMBIE), PIG(22, "Pig", new ResourceLocation("eagler:skins/23.pig.png"), SkinModel.STEVE), - MOOSHROOM(23, "Mooshroom", new ResourceLocation("eagler:skins/24.mooshroom.png"), SkinModel.STEVE); + MOOSHROOM(23, "Mooshroom", new ResourceLocation("eagler:skins/24.mooshroom.png"), SkinModel.STEVE), + LONG_ARMS(24, "Long Arms", new ResourceLocation("eagler:mesh/longarms.fallback.png"), SkinModel.LONG_ARMS), + WEIRD_CLIMBER_DUDE(25, "Weird Climber Dude", new ResourceLocation("eagler:mesh/weirdclimber.fallback.png"), SkinModel.WEIRD_CLIMBER_DUDE), + LAXATIVE_DUDE(26, "Laxative Dude", new ResourceLocation("eagler:mesh/laxativedude.fallback.png"), SkinModel.LAXATIVE_DUDE), + BABY_CHARLES(27, "Baby Charles", new ResourceLocation("eagler:mesh/charles.fallback.png"), SkinModel.BABY_CHARLES), + BABY_WINSTON(28, "Baby Winston", new ResourceLocation("eagler:mesh/winston.fallback.png"), SkinModel.BABY_WINSTON); - public static final DefaultSkins[] defaultSkinsMap = new DefaultSkins[24]; + public static final DefaultSkins[] defaultSkinsMap = new DefaultSkins[29]; public final int id; public final String name; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerProfile.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerProfile.java index 2a6e212..4e8c4f2 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerProfile.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerProfile.java @@ -16,7 +16,7 @@ import net.minecraft.nbt.NBTTagList; import net.minecraft.util.ResourceLocation; /** - * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -37,7 +37,11 @@ public class EaglerProfile { public static int presetSkinId; public static int customSkinId; + public static int presetCapeId; + public static int customCapeId; + public static final List customSkins = new ArrayList(); + public static final List customCapes = new ArrayList(); public static final EaglercraftRandom rand; @@ -78,6 +82,25 @@ public class EaglerProfile { } } } + + public static ResourceLocation getActiveCapeResourceLocation() { + if(presetCapeId == -1) { + if(customCapeId >= 0 && customCapeId < customCapes.size()) { + return customCapes.get(customCapeId).getResource(); + }else { + customCapeId = -1; + presetCapeId = 0; + return DefaultCapes.defaultCapesMap[0].location; + } + }else { + if(presetCapeId >= 0 && presetCapeId < DefaultCapes.defaultCapesMap.length) { + return DefaultCapes.defaultCapesMap[presetCapeId].location; + }else { + presetCapeId = 0; + return DefaultCapes.defaultCapesMap[0].location; + } + } + } public static EaglercraftUUID getPlayerUUID() { return Minecraft.getMinecraft().getSession().getProfile().getId(); @@ -113,6 +136,25 @@ public class EaglerProfile { } } } + + public static byte[] getCapePacket() { + if(presetCapeId == -1) { + if(customCapeId >= 0 && customCapeId < customCapes.size()) { + return CapePackets.writeMyCapeCustom(customCapes.get(customCapeId)); + }else { + customCapeId = -1; + presetCapeId = 0; + return CapePackets.writeMyCapePreset(0); + } + }else { + if(presetCapeId >= 0 && presetCapeId < DefaultCapes.defaultCapesMap.length) { + return CapePackets.writeMyCapePreset(presetCapeId); + }else { + presetCapeId = 0; + return CapePackets.writeMyCapePreset(0); + } + } + } private static boolean doesSkinExist(String name) { for(int i = 0, l = customSkins.size(); i < l; ++i) { @@ -122,6 +164,15 @@ public class EaglerProfile { } return false; } + + private static boolean doesCapeExist(String name) { + for(int i = 0, l = customCapes.size(); i < l; ++i) { + if(customCapes.get(i).name.equalsIgnoreCase(name)) { + return true; + } + } + return false; + } public static int addCustomSkin(String fileName, byte[] rawSkin) { if(doesSkinExist(fileName)) { @@ -138,6 +189,22 @@ public class EaglerProfile { customSkins.add(newSkin); return r; } + + public static int addCustomCape(String fileName, byte[] rawCape23x17RGB) { + if(doesCapeExist(fileName)) { + String newName; + int i = 2; + while(doesCapeExist(newName = fileName + " (" + i + ")")) { + ++i; + } + fileName = newName; + } + CustomCape newCape = new CustomCape(fileName, rawCape23x17RGB); + newCape.load(); + int r = customCapes.size(); + customCapes.add(newCape); + return r; + } public static void clearCustomSkins() { for(int i = 0, l = customSkins.size(); i < l; ++i) { @@ -145,6 +212,13 @@ public class EaglerProfile { } customSkins.clear(); } + + public static void clearCustomCapes() { + for(int i = 0, l = customCapes.size(); i < l; ++i) { + customCapes.get(i).delete(); + } + customCapes.clear(); + } public static void read() { read(EagRuntime.getStorage("p")); @@ -168,6 +242,9 @@ public class EaglerProfile { presetSkinId = profile.getInteger("presetSkin"); customSkinId = profile.getInteger("customSkin"); + + if(profile.hasKey("presetCape", 99)) presetCapeId = profile.getInteger("presetCape"); + if(profile.hasKey("customCape", 99)) customCapeId = profile.getInteger("customCape"); String loadUsername = profile.getString("username").trim(); @@ -194,6 +271,20 @@ public class EaglerProfile { customSkins.add(newSkin); } + if(profile.hasKey("capes", 9)) { + clearCustomCapes(); + NBTTagList capesList = profile.getTagList("capes", 10); + for(int i = 0, l = capesList.tagCount(); i < l; ++i) { + NBTTagCompound cape = capesList.getCompoundTagAt(i); + String capeName = cape.getString("name"); + byte[] capeData = cape.getByteArray("data"); + if(capeData.length != 1173) continue; + CustomCape newCape = new CustomCape(capeName, capeData); + newCape.load(); + customCapes.add(newCape); + } + } + if(presetSkinId == -1) { if(customSkinId < 0 || customSkinId >= customSkins.size()) { presetSkinId = 0; @@ -205,6 +296,18 @@ public class EaglerProfile { presetSkinId = 0; } } + + if(presetCapeId == -1) { + if(customCapeId < 0 || customCapeId >= customCapes.size()) { + presetCapeId = 0; + customCapeId = -1; + } + }else { + customCapeId = -1; + if(presetCapeId < 0 || presetCapeId >= DefaultCapes.defaultCapesMap.length) { + presetCapeId = 0; + } + } } @@ -212,6 +315,8 @@ public class EaglerProfile { NBTTagCompound profile = new NBTTagCompound(); profile.setInteger("presetSkin", presetSkinId); profile.setInteger("customSkin", customSkinId); + profile.setInteger("presetCape", presetCapeId); + profile.setInteger("customCape", customCapeId); profile.setString("username", username); NBTTagList skinsList = new NBTTagList(); for(int i = 0, l = customSkins.size(); i < l; ++i) { @@ -223,6 +328,15 @@ public class EaglerProfile { skinsList.appendTag(skin); } profile.setTag("skins", skinsList); + NBTTagList capesList = new NBTTagList(); + for(int i = 0, l = customCapes.size(); i < l; ++i) { + CustomCape cp = customCapes.get(i); + NBTTagCompound cape = new NBTTagCompound(); + cape.setString("name", cp.name); + cape.setByteArray("data", cp.texture); + capesList.appendTag(cape); + } + profile.setTag("capes", capesList); EaglerOutputStream bao = new EaglerOutputStream(); try { CompressedStreamTools.writeCompressed(profile, bao); @@ -253,9 +367,14 @@ public class EaglerProfile { setName(username); - presetSkinId = rand.nextInt(DefaultSkins.defaultSkinsMap.length); + do { + presetSkinId = rand.nextInt(DefaultSkins.defaultSkinsMap.length); + }while(DefaultSkins.defaultSkinsMap[presetSkinId].model.highPoly != null); customSkinId = -1; + presetCapeId = 0; + customCapeId = -1; + } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenEditCape.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenEditCape.java new file mode 100644 index 0000000..f9a6837 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenEditCape.java @@ -0,0 +1,359 @@ +package net.lax1dude.eaglercraft.v1_8.profile; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType; +import net.lax1dude.eaglercraft.v1_8.internal.FileChooserResult; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; +import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.ResourceLocation; + +import java.io.IOException; + +/** + * 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 + * 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 GuiScreenEditCape extends GuiScreen { + + private final GuiScreenEditProfile parent; + + private boolean dropDownOpen = false; + private String[] dropDownOptions; + private int slotsVisible = 0; + protected int selectedSlot = 0; + private int scrollPos = -1; + private int skinsHeight = 0; + private boolean dragging = false; + private int mousex = 0; + private int mousey = 0; + + private static final ResourceLocation eaglerGui = new ResourceLocation("eagler:gui/eagler_gui.png"); + + protected String screenTitle = "Edit Cape"; + + public GuiScreenEditCape(GuiScreenEditProfile parent) { + this.parent = parent; + updateOptions(); + } + + public void initGui() { + Keyboard.enableRepeatEvents(true); + screenTitle = I18n.format("editCape.title"); + selectedSlot = EaglerProfile.presetCapeId == -1 ? EaglerProfile.customCapeId : (EaglerProfile.presetCapeId + EaglerProfile.customCapes.size()); + buttonList.add(new GuiButton(0, width / 2 - 100, height / 6 + 168, I18n.format("gui.done"))); + buttonList.add(new GuiButton(1, width / 2 - 21, height / 6 + 80, 71, 20, I18n.format("editCape.addCape"))); + buttonList.add(new GuiButton(2, width / 2 - 21 + 71, height / 6 + 80, 72, 20, I18n.format("editCape.clearCape"))); + } + + private void updateOptions() { + int numCustom = EaglerProfile.customCapes.size(); + String[] n = new String[numCustom + DefaultCapes.defaultCapesMap.length]; + for(int i = 0; i < numCustom; ++i) { + n[i] = EaglerProfile.customCapes.get(i).name; + } + int numDefault = DefaultCapes.defaultCapesMap.length; + for(int j = 0; j < numDefault; ++j) { + n[numCustom + j] = DefaultCapes.defaultCapesMap[j].name; + } + dropDownOptions = n; + } + + public void drawScreen(int mx, int my, float partialTicks) { + drawDefaultBackground(); + drawCenteredString(fontRendererObj, screenTitle, width / 2, 15, 16777215); + drawString(fontRendererObj, I18n.format("editCape.playerCape"), width / 2 - 20, height / 6 + 36, 10526880); + + mousex = mx; + mousey = my; + + int skinX = width / 2 - 120; + int skinY = height / 6 + 8; + int skinWidth = 80; + int skinHeight = 130; + + drawRect(skinX, skinY, skinX + skinWidth, skinY + skinHeight, 0xFFA0A0A0); + drawRect(skinX + 1, skinY + 1, skinX + skinWidth - 1, skinY + skinHeight - 1, 0xFF000015); + + int skid = selectedSlot - EaglerProfile.customCapes.size(); + if(skid < 0) { + skid = 0; + } + + if(dropDownOpen) { + super.drawScreen(0, 0, partialTicks); + }else { + super.drawScreen(mx, my, partialTicks); + } + + int numberOfCustomSkins = EaglerProfile.customSkins.size(); + int numberOfCustomCapes = EaglerProfile.customCapes.size(); + ResourceLocation skinTexture; + SkinModel model; + if(parent.selectedSlot < numberOfCustomSkins) { + CustomSkin customSkin = EaglerProfile.customSkins.get(parent.selectedSlot); + skinTexture = customSkin.getResource(); + model = customSkin.model; + }else { + DefaultSkins defaultSkin = DefaultSkins.getSkinFromId(parent.selectedSlot - numberOfCustomSkins); + skinTexture = defaultSkin.location; + model = defaultSkin.model; + } + + if(model.highPoly != null) { + drawCenteredString(fontRendererObj, I18n.format(this.mc.gameSettings.enableFNAWSkins ? "editProfile.disableFNAW" : "editProfile.enableFNAW"), width / 2, height / 6 + 150, 10526880); + } + + skinX = width / 2 - 20; + skinY = height / 6 + 52; + skinWidth = 140; + skinHeight = 22; + + drawRect(skinX, skinY, skinX + skinWidth, skinY + skinHeight, -6250336); + drawRect(skinX + 1, skinY + 1, skinX + skinWidth - 21, skinY + skinHeight - 1, -16777216); + drawRect(skinX + skinWidth - 20, skinY + 1, skinX + skinWidth - 1, skinY + skinHeight - 1, -16777216); + + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + + mc.getTextureManager().bindTexture(eaglerGui); + drawTexturedModalRect(skinX + skinWidth - 18, skinY + 3, 0, 0, 16, 16); + + drawString(fontRendererObj, dropDownOptions[selectedSlot], skinX + 5, skinY + 7, 14737632); + + skinX = width / 2 - 20; + skinY = height / 6 + 73; + skinWidth = 140; + skinHeight = (height - skinY - 10); + slotsVisible = (skinHeight / 10); + if(slotsVisible > dropDownOptions.length) slotsVisible = dropDownOptions.length; + skinHeight = slotsVisible * 10 + 7; + skinsHeight = skinHeight; + if(scrollPos == -1) { + scrollPos = selectedSlot - 2; + } + if(scrollPos > (dropDownOptions.length - slotsVisible)) { + scrollPos = (dropDownOptions.length - slotsVisible); + } + if(scrollPos < 0) { + scrollPos = 0; + } + if(dropDownOpen) { + drawRect(skinX, skinY, skinX + skinWidth, skinY + skinHeight, -6250336); + drawRect(skinX + 1, skinY + 1, skinX + skinWidth - 1, skinY + skinHeight - 1, -16777216); + for(int i = 0; i < slotsVisible; i++) { + if(i + scrollPos < dropDownOptions.length) { + if(selectedSlot == i + scrollPos) { + drawRect(skinX + 1, skinY + i*10 + 4, skinX + skinWidth - 1, skinY + i*10 + 14, 0x77ffffff); + }else if(mx >= skinX && mx < (skinX + skinWidth - 10) && my >= (skinY + i*10 + 5) && my < (skinY + i*10 + 15)) { + drawRect(skinX + 1, skinY + i*10 + 4, skinX + skinWidth - 1, skinY + i*10 + 14, 0x55ffffff); + } + drawString(fontRendererObj, dropDownOptions[i + scrollPos], skinX + 5, skinY + 5 + i*10, 14737632); + } + } + int scrollerSize = skinHeight * slotsVisible / dropDownOptions.length; + int scrollerPos = skinHeight * scrollPos / dropDownOptions.length; + drawRect(skinX + skinWidth - 4, skinY + scrollerPos + 1, skinX + skinWidth - 1, skinY + scrollerPos + scrollerSize, 0xff888888); + } + + if(!EagRuntime.getConfiguration().isDemo()) { + GlStateManager.pushMatrix(); + GlStateManager.scale(0.75f, 0.75f, 0.75f); + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + String text = I18n.format("editProfile.importExport"); + + int w = mc.fontRendererObj.getStringWidth(text); + boolean hover = mx > 1 && my > 1 && mx < (w * 3 / 4) + 7 && my < 12; + if(hover) { + Mouse.showCursor(EnumCursorType.HAND); + } + + drawString(mc.fontRendererObj, EnumChatFormatting.UNDERLINE + text, 5, 5, hover ? 0xFFEEEE22 : 0xFFCCCCCC); + + GlStateManager.popMatrix(); + } + + int xx = width / 2 - 80; + int yy = height / 6 + 130; + + skinX = this.width / 2 - 120; + skinY = this.height / 6 + 8; + skinWidth = 80; + skinHeight = 130; + + ResourceLocation capeTexture; + if(selectedSlot < numberOfCustomCapes) { + capeTexture = EaglerProfile.customCapes.get(selectedSlot).getResource(); + }else { + capeTexture = DefaultCapes.getCapeFromId(selectedSlot - numberOfCustomCapes).location; + } + + SkinPreviewRenderer.renderPreview(xx, yy, mx, my, true, model, skinTexture, capeTexture); + } + + public void handleMouseInput() throws IOException { + super.handleMouseInput(); + if(dropDownOpen) { + int var1 = Mouse.getEventDWheel(); + if(var1 < 0) { + scrollPos += 3; + } + if(var1 > 0) { + scrollPos -= 3; + if(scrollPos < 0) { + scrollPos = 0; + } + } + } + } + + protected void actionPerformed(GuiButton par1GuiButton) { + if(!dropDownOpen) { + if(par1GuiButton.id == 0) { + safeProfile(); + this.mc.displayGuiScreen((GuiScreen) parent); + }else if(par1GuiButton.id == 1) { + EagRuntime.displayFileChooser("image/png", "png"); + }else if(par1GuiButton.id == 2) { + EaglerProfile.clearCustomCapes(); + safeProfile(); + updateOptions(); + selectedSlot = 0; + } + } + } + + public void updateScreen() { + if(EagRuntime.fileChooserHasResult()) { + FileChooserResult result = EagRuntime.getFileChooserResult(); + if(result != null) { + ImageData loadedCape = ImageData.loadImageFile(result.fileData); + if(loadedCape != null) { + if((loadedCape.width == 32 || loadedCape.width == 64) && loadedCape.height == 32) { + byte[] resized = new byte[1173]; + SkinConverter.convertCape32x32RGBAto23x17RGB(loadedCape, resized); + int k; + if((k = EaglerProfile.addCustomCape(result.fileName, resized)) != -1) { + selectedSlot = k; + updateOptions(); + safeProfile(); + } + }else { + EagRuntime.showPopup("The selected image '" + result.fileName + "' is not the right size!\nEaglercraft only supports 32x32 or 64x32 capes"); + } + }else { + EagRuntime.showPopup("The selected file '" + result.fileName + "' is not a PNG file!"); + } + } + } + if(dropDownOpen) { + if(Mouse.isButtonDown(0)) { + int skinX = width / 2 - 20; + int skinY = height / 6 + 73; + int skinWidth = 140; + if(mousex >= (skinX + skinWidth - 10) && mousex < (skinX + skinWidth) && mousey >= skinY && mousey < (skinY + skinsHeight)) { + dragging = true; + } + if(dragging) { + int scrollerSize = skinsHeight * slotsVisible / dropDownOptions.length; + scrollPos = (mousey - skinY - (scrollerSize / 2)) * dropDownOptions.length / skinsHeight; + } + }else { + dragging = false; + } + }else { + dragging = false; + } + } + + public void onGuiClosed() { + Keyboard.enableRepeatEvents(false); + } + + protected void keyTyped(char c, int k) { + if(k == 200 && selectedSlot > 0) { + --selectedSlot; + scrollPos = selectedSlot - 2; + } + if(k == 208 && selectedSlot < (dropDownOptions.length - 1)) { + ++selectedSlot; + scrollPos = selectedSlot - 2; + } + } + + protected void mouseClicked(int mx, int my, int button) { + if (button == 0) { + if(!EagRuntime.getConfiguration().isDemo()) { + int w = mc.fontRendererObj.getStringWidth(I18n.format("editProfile.importExport")); + if(mx > 1 && my > 1 && mx < (w * 3 / 4) + 7 && my < 12) { + safeProfile(); + mc.displayGuiScreen(new GuiScreenImportExportProfile(parent)); + mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + return; + } + } + + int skinX = width / 2 + 140 - 40; + int skinY = height / 6 + 52; + + if(mx >= skinX && mx < (skinX + 20) && my >= skinY && my < (skinY + 22)) { + dropDownOpen = !dropDownOpen; + return; + } + + skinX = width / 2 - 20; + skinY = height / 6 + 52; + int skinWidth = 140; + int skinHeight = skinsHeight; + + if(!(mx >= skinX && mx < (skinX + skinWidth) && my >= skinY && my < (skinY + skinHeight + 22))) { + dragging = false; + if(dropDownOpen) { + dropDownOpen = false; + return; + } + }else if(dropDownOpen && !dragging) { + skinY += 21; + for(int i = 0; i < slotsVisible; i++) { + if(i + scrollPos < dropDownOptions.length) { + if(mx >= skinX && mx < (skinX + skinWidth - 10) && my >= (skinY + i * 10 + 5) && my < (skinY + i * 10 + 15) && selectedSlot != i + scrollPos) { + selectedSlot = i + scrollPos; + dropDownOpen = false; + dragging = false; + return; + } + } + } + } + } + super.mouseClicked(mx, my, button); + } + + protected void safeProfile() { + int customLen = EaglerProfile.customCapes.size(); + if(selectedSlot < customLen) { + EaglerProfile.presetCapeId = -1; + EaglerProfile.customCapeId = selectedSlot; + }else { + EaglerProfile.presetCapeId = selectedSlot - customLen; + EaglerProfile.customCapeId = -1; + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenEditProfile.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenEditProfile.java index ac9f393..947e701 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenEditProfile.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenEditProfile.java @@ -20,7 +20,7 @@ import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; import java.io.IOException; /** - * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -42,7 +42,7 @@ public class GuiScreenEditProfile extends GuiScreen { private boolean dropDownOpen = false; private String[] dropDownOptions; private int slotsVisible = 0; - private int selectedSlot = 0; + protected int selectedSlot = 0; private int scrollPos = -1; private int skinsHeight = 0; private boolean dragging = false; @@ -102,11 +102,25 @@ public class GuiScreenEditProfile extends GuiScreen { drawRect(skinX, skinY, skinX + skinWidth, skinY + skinHeight, 0xFFA0A0A0); drawRect(skinX + 1, skinY + 1, skinX + skinWidth - 1, skinY + skinHeight - 1, 0xFF000015); - int skid = selectedSlot - EaglerProfile.customSkins.size(); - if(skid < 0) { - skid = 0; + GlStateManager.pushMatrix(); + GlStateManager.translate(skinX + 2, skinY - 9, 0.0f); + GlStateManager.scale(0.75f, 0.75f, 0.75f); + + int numberOfCustomSkins = EaglerProfile.customSkins.size(); + int skid = selectedSlot - numberOfCustomSkins; + SkinModel selectedSkinModel = skid < 0 ? EaglerProfile.customSkins.get(selectedSlot).model : DefaultSkins.getSkinFromId(skid).model; + if(selectedSkinModel == SkinModel.STEVE || selectedSkinModel == SkinModel.ALEX || (selectedSkinModel.highPoly != null && !this.mc.gameSettings.enableFNAWSkins)) { + String capesText = I18n.format("editProfile.capes"); + int color = 10526880; + if(mx > skinX - 10 && my > skinY - 16 && mx < skinX + (fontRendererObj.getStringWidth(capesText) * 0.75f) + 10 && my < skinY + 7) { + color = 0xFFCCCC44; + Mouse.showCursor(EnumCursorType.HAND); + } + this.drawString(this.fontRendererObj, EnumChatFormatting.UNDERLINE + capesText, 0, 0, color); } + GlStateManager.popMatrix(); + usernameField.drawTextBox(); if(dropDownOpen || newSkinWaitSteveOrAlex) { super.drawScreen(0, 0, partialTicks); @@ -114,6 +128,10 @@ public class GuiScreenEditProfile extends GuiScreen { super.drawScreen(mx, my, partialTicks); } + if(selectedSkinModel.highPoly != null) { + drawCenteredString(fontRendererObj, I18n.format(this.mc.gameSettings.enableFNAWSkins ? "editProfile.disableFNAW" : "editProfile.enableFNAW"), width / 2, height / 6 + 150, 10526880); + } + skinX = width / 2 - 20; skinY = height / 6 + 82; skinWidth = 140; @@ -184,7 +202,6 @@ public class GuiScreenEditProfile extends GuiScreen { int xx = width / 2 - 80; int yy = height / 6 + 130; - int numberOfCustomSkins = EaglerProfile.customSkins.size(); if(newSkinWaitSteveOrAlex && selectedSlot < numberOfCustomSkins) { skinWidth = 70; @@ -217,8 +234,8 @@ public class GuiScreenEditProfile extends GuiScreen { drawCenteredString(fontRendererObj, "Steve", skinX + skinWidth / 2, skinY + skinHeight + 6, cc); } - mc.getTextureManager().bindTexture(newSkin.getResource()); - SkinPreviewRenderer.renderBiped(xx, yy, mx, my, SkinModel.STEVE); + SkinPreviewRenderer.renderPreview(xx, yy, mx, my, false, SkinModel.STEVE, newSkin.getResource(), + EaglerProfile.getActiveCapeResourceLocation()); skinX = width / 2 + 20; skinY = height / 4; @@ -242,8 +259,8 @@ public class GuiScreenEditProfile extends GuiScreen { drawCenteredString(fontRendererObj, "Alex", skinX + skinWidth / 2, skinY + skinHeight + 8, cc); } - mc.getTextureManager().bindTexture(newSkin.getResource()); - SkinPreviewRenderer.renderBiped(xx, yy, mx, my, SkinModel.ALEX); + SkinPreviewRenderer.renderPreview(xx, yy, mx, my, false, SkinModel.ALEX, newSkin.getResource(), + EaglerProfile.getActiveCapeResourceLocation()); }else { skinX = this.width / 2 - 120; skinY = this.height / 6 + 8; @@ -251,19 +268,15 @@ public class GuiScreenEditProfile extends GuiScreen { skinHeight = 130; ResourceLocation texture; - SkinModel model; - if(selectedSlot < numberOfCustomSkins) { - CustomSkin customSkin = EaglerProfile.customSkins.get(selectedSlot); - texture = customSkin.getResource(); - model = customSkin.model; + if(skid < 0) { + texture = EaglerProfile.customSkins.get(selectedSlot).getResource(); }else { - DefaultSkins defaultSkin = DefaultSkins.defaultSkinsMap[selectedSlot - numberOfCustomSkins]; - texture = defaultSkin.location; - model = defaultSkin.model; + texture = DefaultSkins.getSkinFromId(skid).location; } - mc.getTextureManager().bindTexture(texture); - SkinPreviewRenderer.renderBiped(xx, yy, newSkinWaitSteveOrAlex ? width / 2 : mx, newSkinWaitSteveOrAlex ? height / 2 : my, model); + SkinPreviewRenderer.renderPreview(xx, yy, newSkinWaitSteveOrAlex ? width / 2 : mx, + newSkinWaitSteveOrAlex ? height / 2 : my, false, selectedSkinModel, texture, + EaglerProfile.getActiveCapeResourceLocation()); } } @@ -293,6 +306,7 @@ public class GuiScreenEditProfile extends GuiScreen { }else if(par1GuiButton.id == 2) { EaglerProfile.clearCustomSkins(); safeProfile(); + EaglerProfile.save(); updateOptions(); selectedSlot = 0; } @@ -335,6 +349,7 @@ public class GuiScreenEditProfile extends GuiScreen { newSkinWaitSteveOrAlex = true; updateOptions(); safeProfile(); + EaglerProfile.save(); } }else { EagRuntime.showPopup("The selected image '" + result.fileName + "' is not the right size!\nEaglercraft only supports 64x32 or 64x64 skins"); @@ -387,21 +402,37 @@ public class GuiScreenEditProfile extends GuiScreen { } protected void mouseClicked(int mx, int my, int button) { - super.mouseClicked(mx, my, button); usernameField.mouseClicked(mx, my, button); if (button == 0) { if(!EagRuntime.getConfiguration().isDemo()) { int w = mc.fontRendererObj.getStringWidth(I18n.format("editProfile.importExport")); if(mx > 1 && my > 1 && mx < (w * 3 / 4) + 7 && my < 12) { + safeProfile(); + EaglerProfile.save(); mc.displayGuiScreen(new GuiScreenImportExportProfile(this)); mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); return; } } + int skinX, skinY; + int skid = selectedSlot - EaglerProfile.customSkins.size(); + SkinModel selectedSkinModel = skid < 0 ? EaglerProfile.customSkins.get(selectedSlot).model : DefaultSkins.getSkinFromId(skid).model; + if(selectedSkinModel == SkinModel.STEVE || selectedSkinModel == SkinModel.ALEX || (selectedSkinModel.highPoly != null && !this.mc.gameSettings.enableFNAWSkins)) { + skinX = this.width / 2 - 120; + skinY = this.height / 6 + 8; + String capesText = I18n.format("editProfile.capes"); + if(mx > skinX - 10 && my > skinY - 16 && mx < skinX + (fontRendererObj.getStringWidth(capesText) * 0.75f) + 10 && my < skinY + 7) { + safeProfile(); + this.mc.displayGuiScreen(new GuiScreenEditCape(this)); + mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + return; + } + } + if(newSkinWaitSteveOrAlex) { - int skinX = width / 2 - 90; - int skinY = height / 4; + skinX = width / 2 - 90; + skinY = height / 4; int skinWidth = 70; int skinHeight = 120; if(mx >= skinX && my >= skinY && mx < skinX + skinWidth && my < skinY + skinHeight) { @@ -423,8 +454,8 @@ public class GuiScreenEditProfile extends GuiScreen { } return; }else if(selectedSlot < EaglerProfile.customSkins.size()) { - int skinX = width / 2 - 120; - int skinY = height / 6 + 18; + skinX = width / 2 - 120; + skinY = height / 6 + 18; int skinWidth = 80; int skinHeight = 120; if(mx >= skinX && my >= skinY && mx < skinX + skinWidth && my < skinY + skinHeight) { @@ -434,8 +465,8 @@ public class GuiScreenEditProfile extends GuiScreen { } } } - int skinX = width / 2 + 140 - 40; - int skinY = height / 6 + 82; + skinX = width / 2 + 140 - 40; + skinY = height / 6 + 82; if(mx >= skinX && mx < (skinX + 20) && my >= skinY && my < (skinY + 22)) { dropDownOpen = !dropDownOpen; @@ -448,27 +479,26 @@ public class GuiScreenEditProfile extends GuiScreen { int skinHeight = skinsHeight; if(!(mx >= skinX && mx < (skinX + skinWidth) && my >= skinY && my < (skinY + skinHeight + 22))) { - dropDownOpen = false; dragging = false; - return; - } - - skinY += 21; - - if(dropDownOpen && !dragging) { + if(dropDownOpen) { + dropDownOpen = false; + return; + } + }else if(dropDownOpen && !dragging) { + skinY += 21; for(int i = 0; i < slotsVisible; i++) { if(i + scrollPos < dropDownOptions.length) { - if(selectedSlot != i + scrollPos) { - if(mx >= skinX && mx < (skinX + skinWidth - 10) && my >= (skinY + i * 10 + 5) && my < (skinY + i * 10 + 15) && selectedSlot != i + scrollPos) { - selectedSlot = i + scrollPos; - dropDownOpen = false; - dragging = false; - } + if(mx >= skinX && mx < (skinX + skinWidth - 10) && my >= (skinY + i * 10 + 5) && my < (skinY + i * 10 + 15)) { + selectedSlot = i + scrollPos; + dropDownOpen = false; + dragging = false; + return; } } } } } + super.mouseClicked(mx, my, button); } protected void safeProfile() { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/HighPolySkin.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/HighPolySkin.java new file mode 100644 index 0000000..c7f6427 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/HighPolySkin.java @@ -0,0 +1,113 @@ +package net.lax1dude.eaglercraft.v1_8.profile; + +import net.minecraft.util.ResourceLocation; + +/** + * 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 + * 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 HighPolySkin { + + LONG_ARMS( + new ResourceLocation("eagler:mesh/longarms.png"), + new ResourceLocation("eagler:mesh/longarms0.mdl"), + null, + new ResourceLocation("eagler:mesh/longarms2.mdl"), + new ResourceLocation[] { + new ResourceLocation("eagler:mesh/longarms1.mdl") + }, + new float[] { + 1.325f + }, + 0.0f, + new ResourceLocation("eagler:mesh/longarms.fallback.png") + ), + + WEIRD_CLIMBER_DUDE( + new ResourceLocation("eagler:mesh/weirdclimber.png"), + new ResourceLocation("eagler:mesh/weirdclimber0.mdl"), + null, + new ResourceLocation("eagler:mesh/weirdclimber2.mdl"), + new ResourceLocation[] { + new ResourceLocation("eagler:mesh/weirdclimber1.mdl") + }, + new float[] { + 2.62f + }, + -90.0f, + new ResourceLocation("eagler:mesh/weirdclimber.fallback.png") + ), + + LAXATIVE_DUDE( + new ResourceLocation("eagler:mesh/laxativedude.png"), + new ResourceLocation("eagler:mesh/laxativedude0.mdl"), + null, + new ResourceLocation("eagler:mesh/laxativedude3.mdl"), + new ResourceLocation[] { + new ResourceLocation("eagler:mesh/laxativedude1.mdl"), + new ResourceLocation("eagler:mesh/laxativedude2.mdl") + }, + new float[] { + 2.04f + }, + 0.0f, + new ResourceLocation("eagler:mesh/laxativedude.fallback.png") + ), + + BABY_CHARLES( + new ResourceLocation("eagler:mesh/charles.png"), + new ResourceLocation("eagler:mesh/charles0.mdl"), + new ResourceLocation("eagler:mesh/charles1.mdl"), + new ResourceLocation("eagler:mesh/charles2.mdl"), + new ResourceLocation[] {}, + new float[] {}, + 0.0f, + new ResourceLocation("eagler:mesh/charles.fallback.png") + ), + + BABY_WINSTON( + new ResourceLocation("eagler:mesh/winston.png"), + new ResourceLocation("eagler:mesh/winston0.mdl"), + null, + new ResourceLocation("eagler:mesh/winston1.mdl"), + new ResourceLocation[] {}, + new float[] {}, + 0.0f, + new ResourceLocation("eagler:mesh/winston.fallback.png") + ); + + public static float highPolyScale = 0.5f; + + public final ResourceLocation texture; + public final ResourceLocation bodyModel; + public final ResourceLocation headModel; + public final ResourceLocation eyesModel; + public final ResourceLocation[] limbsModel; + public final float[] limbsOffset; + public final float limbsInitialRotation; + public final ResourceLocation fallbackTexture; + + HighPolySkin(ResourceLocation texture, ResourceLocation bodyModel, ResourceLocation headModel, ResourceLocation eyesModel, + ResourceLocation[] limbsModel, float[] limbsOffset, float limbsInitialRotation, ResourceLocation fallbackTexture) { + this.texture = texture; + this.bodyModel = bodyModel; + this.headModel = headModel; + this.eyesModel = eyesModel; + this.limbsModel = limbsModel; + this.limbsOffset = limbsOffset; + this.limbsInitialRotation = limbsInitialRotation; + this.fallbackTexture = fallbackTexture; + } + +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/RenderHighPoly.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/RenderHighPoly.java new file mode 100644 index 0000000..377c008 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/RenderHighPoly.java @@ -0,0 +1,471 @@ +package net.lax1dude.eaglercraft.v1_8.profile; + +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.opengl.EaglerMeshLoader; +import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; +import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.DeferredStateManager; +import net.lax1dude.eaglercraft.v1_8.vector.Matrix4f; +import net.minecraft.block.Block; +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.AbstractClientPlayer; +import net.minecraft.client.model.ModelBase; +import net.minecraft.client.renderer.block.model.ItemCameraTransforms; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.entity.RenderPlayer; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.util.MathHelper; + +/** + * 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 + * 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 RenderHighPoly extends RenderPlayer { + + private static final Logger logger = LogManager.getLogger("RenderHighPoly"); + + public RenderHighPoly(RenderManager renderManager, ModelBase fallbackModel, float fallbackScale) { + super(renderManager, fallbackModel, fallbackScale); + } + + private static final Matrix4f tmpMatrix = new Matrix4f(); + + public void doRender(AbstractClientPlayer abstractclientplayer, double d0, double d1, double d2, float f, + float f1) { + if (!abstractclientplayer.isUser() || this.renderManager.livingPlayer == abstractclientplayer) { + double nameY = d1; + HighPolySkin highPolySkin = abstractclientplayer.getEaglerSkinModel().highPoly; + + if(highPolySkin == null) { + super.doRender(abstractclientplayer, d0, d1, d2, f, f1); + return; + }else if(highPolySkin == HighPolySkin.LAXATIVE_DUDE) { + nameY += 0.1; + }else if(highPolySkin == HighPolySkin.BABY_WINSTON) { + nameY -= 1.0; + } + + GlStateManager.pushMatrix(); + GlStateManager.disableCull(); + + try { + Minecraft mc = Minecraft.getMinecraft(); + float f2 = this.interpolateRotation(abstractclientplayer.prevRenderYawOffset, abstractclientplayer.renderYawOffset, + f1); + float f3 = this.interpolateRotation(abstractclientplayer.prevRotationYawHead, abstractclientplayer.rotationYawHead, + f1); + float f4 = f3 - f2; + if (abstractclientplayer.isRiding() && abstractclientplayer.ridingEntity instanceof EntityLivingBase) { + EntityLivingBase entitylivingbase1 = (EntityLivingBase) abstractclientplayer.ridingEntity; + f2 = this.interpolateRotation(entitylivingbase1.prevRenderYawOffset, entitylivingbase1.renderYawOffset, + f1); + f4 = f3 - f2; + float f5 = MathHelper.wrapAngleTo180_float(f4); + if (f5 < -85.0F) { + f5 = -85.0F; + } + + if (f5 >= 85.0F) { + f5 = 85.0F; + } + + f2 = f3 - f5; + if (f5 * f5 > 2500.0F) { + f2 += f5 * 0.2F; + } + } + + this.renderLivingAt(abstractclientplayer, d0, d1, d2); + float f10 = this.handleRotationFloat(abstractclientplayer, f1); + this.rotateCorpse(abstractclientplayer, f10, f2, f1); + GlStateManager.enableRescaleNormal(); + this.preRenderCallback(abstractclientplayer, f1); + float f6 = 0.0625F; + GlStateManager.scale(HighPolySkin.highPolyScale, HighPolySkin.highPolyScale, HighPolySkin.highPolyScale); + mc.getTextureManager().bindTexture(highPolySkin.texture); + + if(abstractclientplayer.isPlayerSleeping()) { + if(highPolySkin == HighPolySkin.LAXATIVE_DUDE || highPolySkin == HighPolySkin.WEIRD_CLIMBER_DUDE) { + GlStateManager.translate(0.0f, -3.7f, 0.0f); + }else if(highPolySkin == HighPolySkin.BABY_WINSTON) { + GlStateManager.translate(0.0f, -2.4f, 0.0f); + }else { + GlStateManager.translate(0.0f, -3.0f, 0.0f); + } + } + + float var15 = abstractclientplayer.prevLimbSwingAmount + (abstractclientplayer.limbSwingAmount - abstractclientplayer.prevLimbSwingAmount) * f1; + float var16 = abstractclientplayer.limbSwing - abstractclientplayer.limbSwingAmount * (1.0F - f1); + + if(highPolySkin == HighPolySkin.LONG_ARMS) { + GlStateManager.rotate(MathHelper.sin(var16) * 20f * var15, 0.0f, 1.0f, 0.0f); + GlStateManager.rotate(MathHelper.cos(var16) * 7f * var15, 0.0f, 0.0f, 1.0f); + }else if(highPolySkin == HighPolySkin.WEIRD_CLIMBER_DUDE) { + GlStateManager.rotate(MathHelper.sin(var16) * 7f * var15, 0.0f, 1.0f, 0.0f); + GlStateManager.rotate(MathHelper.cos(var16) * 3f * var15, 0.0f, 0.0f, 1.0f); + GlStateManager.rotate(-f3, 0.0f, 1.0f, 0.0f); + float xd = (float)(abstractclientplayer.posX - abstractclientplayer.prevPosX); + GlStateManager.rotate(xd * 70.0f * var15, 0.0f, 0.0f, 1.0f); + float zd = (float)(abstractclientplayer.posZ - abstractclientplayer.prevPosZ); + GlStateManager.rotate(zd * 70.0f * var15, 1.0f, 0.0f, 0.0f); + GlStateManager.rotate(f3, 0.0f, 1.0f, 0.0f); + }else if(highPolySkin == HighPolySkin.LAXATIVE_DUDE) { + GlStateManager.rotate(-f3, 0.0f, 1.0f, 0.0f); + float xd = (float)(abstractclientplayer.posX - abstractclientplayer.prevPosX); + GlStateManager.rotate(-xd * 40.0f * var15, 0.0f, 0.0f, 1.0f); + float zd = (float)(abstractclientplayer.posZ - abstractclientplayer.prevPosZ); + GlStateManager.rotate(-zd * 40.0f * var15, 1.0f, 0.0f, 0.0f); + GlStateManager.rotate(f3, 0.0f, 1.0f, 0.0f); + }else if(highPolySkin == HighPolySkin.BABY_WINSTON) { + GlStateManager.translate(0.0f, (MathHelper.cos(f10 % 100000.0f) + 1.0f) * var15 * 0.2f, 0.0f); + GlStateManager.rotate(MathHelper.sin(var16) * 5f * var15, 0.0f, 1.0f, 0.0f); + GlStateManager.rotate(MathHelper.cos(var16) * 5f * var15, 0.0f, 0.0f, 1.0f); + } + + if (abstractclientplayer.hurtTime > 0 || abstractclientplayer.deathTime > 0) { + GlStateManager.color(1.2f, 0.8F, 0.8F, 1.0F); + } + + if(DeferredStateManager.isInDeferredPass()) { + DeferredStateManager.setDefaultMaterialConstants(); + DeferredStateManager.setRoughnessConstant(0.5f); + DeferredStateManager.setMetalnessConstant(0.05f); + } + + if(highPolySkin.bodyModel != null) { + EaglercraftGPU.drawHighPoly(EaglerMeshLoader.getEaglerMesh(highPolySkin.bodyModel)); + } + float jumpFactor = 0.0f; + + if(highPolySkin.headModel != null) { + if(highPolySkin == HighPolySkin.BABY_CHARLES) { + long millis = System.currentTimeMillis(); + float partialTicks = (float) ((millis - abstractclientplayer.eaglerHighPolyAnimationTick) * 0.02); + //long l50 = millis / 50l * 50l; + //boolean runTick = par1EntityPlayer.eaglerHighPolyAnimationTick < l50 && millis >= l50; + abstractclientplayer.eaglerHighPolyAnimationTick = millis; + + if(partialTicks < 0.0f) { + partialTicks = 0.0f; + } + if(partialTicks > 1.0f) { + partialTicks = 1.0f; + } + + float jumpFac = (float)(abstractclientplayer.posY - abstractclientplayer.prevPosY); + if(jumpFac < 0.0f && !abstractclientplayer.isCollidedVertically) { + jumpFac = -jumpFac; + jumpFac *= 0.1f; + } + jumpFac -= 0.05f; + if(jumpFac > 0.1f && !abstractclientplayer.isCollidedVertically) { + jumpFac = 0.1f; + }else if(jumpFac < 0.0f) { + jumpFac = 0.0f; + }else if(jumpFac > 0.1f && abstractclientplayer.isCollidedVertically) { + jumpFac = 0.1f; + }else if(jumpFac > 0.4f) { + jumpFac = 0.4f; + } + jumpFac *= 10.0f; + + abstractclientplayer.eaglerHighPolyAnimationFloat3 += (jumpFac / (jumpFac + 1.0f)) * 6.0f * partialTicks; + + if(Float.isInfinite(abstractclientplayer.eaglerHighPolyAnimationFloat3)) { + abstractclientplayer.eaglerHighPolyAnimationFloat3 = 1.0f; + }else if(abstractclientplayer.eaglerHighPolyAnimationFloat3 > 1.0f) { + abstractclientplayer.eaglerHighPolyAnimationFloat3 = 1.0f; + }else if(abstractclientplayer.eaglerHighPolyAnimationFloat3 < -1.0f) { + abstractclientplayer.eaglerHighPolyAnimationFloat3 = -1.0f; + } + + abstractclientplayer.eaglerHighPolyAnimationFloat2 += abstractclientplayer.eaglerHighPolyAnimationFloat3 * partialTicks; + + abstractclientplayer.eaglerHighPolyAnimationFloat5 += partialTicks; + while(abstractclientplayer.eaglerHighPolyAnimationFloat5 > 0.05f) { + abstractclientplayer.eaglerHighPolyAnimationFloat5 -= 0.05f; + abstractclientplayer.eaglerHighPolyAnimationFloat3 *= 0.99f; + abstractclientplayer.eaglerHighPolyAnimationFloat2 *= 0.9f; + } + + jumpFactor = abstractclientplayer.eaglerHighPolyAnimationFloat2; //(abstractclientplayer.eaglerHighPolyAnimationFloat1 - abstractclientplayer.eaglerHighPolyAnimationFloat2) * partialTicks + abstractclientplayer.eaglerHighPolyAnimationFloat2; + jumpFactor -= 0.12f; + if(jumpFactor < 0.0f) { + jumpFactor = 0.0f; + } + jumpFactor = jumpFactor / (jumpFactor + 2.0f); + if(jumpFactor > 1.0f) { + jumpFactor = 1.0f; + } + } + if(jumpFactor > 0.0f) { + GlStateManager.pushMatrix(); + GlStateManager.translate(0.0f, jumpFactor * 3.0f, 0.0f); + } + + EaglercraftGPU.drawHighPoly(EaglerMeshLoader.getEaglerMesh(highPolySkin.headModel)); + + if(jumpFactor > 0.0f) { + GlStateManager.popMatrix(); + } + } + + if(highPolySkin.limbsModel != null && highPolySkin.limbsModel.length > 0) { + for(int i = 0; i < highPolySkin.limbsModel.length; ++i) { + DeferredStateManager.setRoughnessConstant(0.023f); + DeferredStateManager.setMetalnessConstant(0.902f); + float offset = 0.0f; + if(highPolySkin.limbsOffset != null) { + if(highPolySkin.limbsOffset.length == 1) { + offset = highPolySkin.limbsOffset[0]; + }else { + offset = highPolySkin.limbsOffset[i]; + } + } + + GlStateManager.pushMatrix(); + + if(offset != 0.0f || highPolySkin.limbsInitialRotation != 0.0f) { + if(offset != 0.0f) { + GlStateManager.translate(0.0f, offset, 0.0f); + } + if(highPolySkin.limbsInitialRotation != 0.0f) { + GlStateManager.rotate(highPolySkin.limbsInitialRotation, 1.0f, 0.0f, 0.0f); + } + } + + if(highPolySkin == HighPolySkin.LONG_ARMS) { + if(abstractclientplayer.isSwingInProgress) { + float var17 = MathHelper.cos(-abstractclientplayer.getSwingProgress(f1) * (float)Math.PI * 2.0f - 1.2f) - 0.362f; + var17 *= var17; + GlStateManager.rotate(-var17 * 20.0f, 1.0f, 0.0f, 0.0f); + } + }else if(highPolySkin == HighPolySkin.WEIRD_CLIMBER_DUDE) { + if(abstractclientplayer.isSwingInProgress) { + float var17 = MathHelper.cos(-abstractclientplayer.getSwingProgress(f1) * (float)Math.PI * 2.0f - 1.2f) - 0.362f; + var17 *= var17; + GlStateManager.rotate(var17 * 60.0f, 1.0f, 0.0f, 0.0f); + } + GlStateManager.rotate(40.0f * var15, 1.0f, 0.0f, 0.0f); + }else if(highPolySkin == HighPolySkin.LAXATIVE_DUDE) { + float fff = (i == 0) ? 1.0f : -1.0f; + float swing = (MathHelper.cos(f10 % 100000.0f) * fff + 0.2f) * var15; + float swing2 = (MathHelper.cos(f10 % 100000.0f) * fff * 0.5f + 0.0f) * var15; + GlStateManager.rotate(swing * 25.0f, 1.0f, 0.0f, 0.0f); + if(abstractclientplayer.isSwingInProgress) { + float var17 = MathHelper.cos(-abstractclientplayer.getSwingProgress(f1) * (float)Math.PI * 2.0f - 1.2f) - 0.362f; + var17 *= var17; + GlStateManager.rotate(-var17 * 25.0f, 1.0f, 0.0f, 0.0f); + } + + // shear matrix + tmpMatrix.setIdentity(); + tmpMatrix.m21 = swing2; + tmpMatrix.m23 = swing2 * -0.2f; + GlStateManager.multMatrix(tmpMatrix); + } + + if(i != 0) { + mc.getTextureManager().bindTexture(highPolySkin.texture); + if (abstractclientplayer.hurtTime > 0 || abstractclientplayer.deathTime > 0) { + GlStateManager.color(1.2f, 0.8F, 0.8F, 1.0F); + }else { + GlStateManager.color(1.0f, 1.0F, 1.0F, 1.0F); + } + } + EaglercraftGPU.drawHighPoly(EaglerMeshLoader.getEaglerMesh(highPolySkin.limbsModel[i])); + + if(i == 0) { + GlStateManager.pushMatrix(); + + GlStateManager.translate(-0.287f, 0.05f, 0.0f); + + if(highPolySkin == HighPolySkin.LONG_ARMS) { + GlStateManager.translate(1.72f, 2.05f, -0.24f); + ItemStack stk = abstractclientplayer.getHeldItem(); + if(stk != null) { + Item itm = stk.getItem(); + if(itm != null) { + if(itm == Items.bow) { + GlStateManager.translate(-0.22f, 0.8f, 0.6f); + GlStateManager.rotate(-90.0f, 1.0f, 0.0f, 0.0f); + }else if(itm instanceof ItemBlock && !((ItemBlock)itm).getBlock().isNormalCube()) { + GlStateManager.translate(0.0f, -0.1f, 0.13f); + }else if(!itm.isFull3D()) { + GlStateManager.translate(-0.08f, -0.1f, 0.16f); + } + } + } + }else if(highPolySkin == HighPolySkin.WEIRD_CLIMBER_DUDE) { + GlStateManager.translate(-0.029f, 1.2f, -3f); + GlStateManager.rotate(-5.0f, 0.0f, 1.0f, 0.0f); + float var17 = -1.2f * var15; + if(abstractclientplayer.isSwingInProgress) { + float vvar17 = MathHelper.cos(-abstractclientplayer.getSwingProgress(f1) * (float)Math.PI * 2.0f - 1.2f) - 0.362f; + var17 = vvar17 < var17 ? vvar17 : var17; + } + GlStateManager.translate(-0.02f * var17, 0.42f * var17, var17 * 0.35f); + GlStateManager.rotate(var17 * 30.0f, 1.0f, 0.0f, 0.0f); + GlStateManager.rotate(110.0f, 1.0f, 0.0f, 0.0f); + ItemStack stk = abstractclientplayer.getHeldItem(); + if(stk != null) { + Item itm = stk.getItem(); + if(itm != null) { + if(itm == Items.bow) { + GlStateManager.translate(-0.18f, 1.0f, 0.4f); + GlStateManager.rotate(-95.0f, 1.0f, 0.0f, 0.0f); + }else if(itm instanceof ItemBlock && !((ItemBlock)itm).getBlock().isNormalCube()) { + GlStateManager.translate(0.0f, -0.1f, 0.13f); + }else if(!itm.isFull3D()) { + GlStateManager.translate(-0.08f, -0.1f, 0.16f); + } + } + } + }else if(highPolySkin == HighPolySkin.LAXATIVE_DUDE) { + GlStateManager.translate(1.291f, 2.44f, -2.18f); + GlStateManager.rotate(95.0f, 1.0f, 0.0f, 0.0f); + ItemStack stk = abstractclientplayer.getHeldItem(); + if(stk != null) { + Item itm = stk.getItem(); + if(itm != null) { + if(itm == Items.bow) { + GlStateManager.translate(-0.65f, 1.3f, -0.1f); + GlStateManager.rotate(180.0f, 0.0f, 0.0f, 1.0f); + GlStateManager.rotate(20.0f, 1.0f, 0.0f, 0.0f); + }else if(itm instanceof ItemBlock && !((ItemBlock)itm).getBlock().isNormalCube()) { + GlStateManager.translate(0.0f, -0.35f, 0.4f); + }else if(!itm.isFull3D()) { + GlStateManager.translate(-0.1f, -0.1f, 0.16f); + } + } + } + } + + DeferredStateManager.setDefaultMaterialConstants(); + renderHeldItem(abstractclientplayer, f1); + GlStateManager.popMatrix(); + } + + GlStateManager.popMatrix(); + } + } + + if(highPolySkin.eyesModel != null && !DeferredStateManager.isEnableShadowRender()) { + float ff = 0.00416f; + int brightness = abstractclientplayer.getBrightnessForRender(0.0f); + float blockLight = (brightness % 65536) * ff; + float skyLight = (brightness / 65536) * ff; + float sunCurve = (float)((abstractclientplayer.worldObj.getWorldTime() + 4000l) % 24000) / 24000.0f; + sunCurve = MathHelper.clamp_float(9.8f - MathHelper.abs(sunCurve * 5.0f + sunCurve * sunCurve * 45.0f - 14.3f) * 0.7f, 0.0f, 1.0f); + skyLight = skyLight * (sunCurve * 0.85f + 0.15f); + blockLight = blockLight * (sunCurve * 0.3f + 0.7f); + float eyeBrightness = blockLight; + if(skyLight > eyeBrightness) { + eyeBrightness = skyLight; + } + eyeBrightness += blockLight * 0.2f; + eyeBrightness = 1.0f - eyeBrightness; + eyeBrightness = MathHelper.clamp_float(eyeBrightness * 1.9f - 1.0f, 0.0f, 1.0f); + if(eyeBrightness > 0.1f) { + if(DeferredStateManager.isInDeferredPass()) { + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + DeferredStateManager.setDefaultMaterialConstants(); + DeferredStateManager.setEmissionConstant(eyeBrightness); + }else { + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL_ONE, GL_ONE); + GlStateManager.color(eyeBrightness * 7.0f, eyeBrightness * 7.0f, eyeBrightness * 7.0f, 1.0f); + if(jumpFactor > 0.0f) { + GlStateManager.pushMatrix(); + GlStateManager.translate(0.0f, jumpFactor * 3.0f, 0.0f); + } + } + GlStateManager.disableTexture2D(); + GlStateManager.disableLighting(); + GlStateManager.enableCull(); + + EaglercraftGPU.drawHighPoly(EaglerMeshLoader.getEaglerMesh(highPolySkin.eyesModel)); + + GlStateManager.enableTexture2D(); + GlStateManager.enableLighting(); + GlStateManager.disableCull(); + if(jumpFactor > 0.0f) { + GlStateManager.popMatrix(); + } + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + if(!DeferredStateManager.isInDeferredPass()) { + GlStateManager.disableBlend(); + } + } + } + }catch(Throwable t) { + logger.error("Couldn\'t render entity"); + logger.error(t); + } + GlStateManager.setActiveTexture(OpenGlHelper.lightmapTexUnit); + GlStateManager.enableTexture2D(); + GlStateManager.setActiveTexture(OpenGlHelper.defaultTexUnit); + GlStateManager.enableCull(); + GlStateManager.popMatrix(); + if (!this.renderOutlines) { + this.renderName(abstractclientplayer, d0, nameY, d2); + } + } + } + + public void renderRightArm(AbstractClientPlayer clientPlayer) { + + } + + public void renderLeftArm(AbstractClientPlayer clientPlayer) { + + } + + protected void renderHeldItem(AbstractClientPlayer clientPlayer, float partialTicks) { + ItemStack itemstack = clientPlayer.getHeldItem(); + if (itemstack != null) { + GlStateManager.pushMatrix(); + GlStateManager.translate(-0.11F, 0.475F, 0.25F); + if (clientPlayer.fishEntity != null) { + itemstack = new ItemStack(Items.fishing_rod, 0); + } + + Item item = itemstack.getItem(); + Minecraft minecraft = Minecraft.getMinecraft(); + if (item instanceof ItemBlock && Block.getBlockFromItem(item).getRenderType() == 2) { + GlStateManager.translate(0.0F, 0.1875F, -0.3125F); + GlStateManager.rotate(20.0F, 1.0F, 0.0F, 0.0F); + GlStateManager.rotate(45.0F, 0.0F, 1.0F, 0.0F); + float f1 = 0.375F; + GlStateManager.scale(-f1, -f1, f1); + } + + if (clientPlayer.isSneaking()) { + GlStateManager.translate(0.0F, 0.203125F, 0.0F); + } + + minecraft.getItemRenderer().renderItem(clientPlayer, itemstack, + ItemCameraTransforms.TransformType.THIRD_PERSON); + GlStateManager.popMatrix(); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerCapeCache.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerCapeCache.java new file mode 100644 index 0000000..7a7810d --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerCapeCache.java @@ -0,0 +1,242 @@ +package net.lax1dude.eaglercraft.v1_8.profile; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +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.socket.EaglercraftNetworkManager; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.network.PacketBuffer; +import net.minecraft.network.play.client.C17PacketCustomPayload; +import net.minecraft.util.ResourceLocation; + +/** + * 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 + * 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 ServerCapeCache { + + private static final Logger logger = LogManager.getLogger("ServerCapeCache"); + + public class CapeCacheEntry { + + protected final boolean isPresetCape; + protected final int presetCapeId; + protected final CacheCustomCape customCape; + + protected long lastCacheHit = System.currentTimeMillis(); + + protected CapeCacheEntry(EaglerSkinTexture textureInstance, ResourceLocation resourceLocation) { + this.isPresetCape = false; + this.presetCapeId = -1; + this.customCape = new CacheCustomCape(textureInstance, resourceLocation); + ServerCapeCache.this.textureManager.loadTexture(resourceLocation, textureInstance); + } + + /** + * Use only for the constant for the client player + */ + protected CapeCacheEntry(ResourceLocation resourceLocation) { + this.isPresetCape = false; + this.presetCapeId = -1; + this.customCape = new CacheCustomCape(null, resourceLocation); + } + + protected CapeCacheEntry(int presetSkinId) { + this.isPresetCape = true; + this.presetCapeId = presetSkinId; + this.customCape = null; + } + + public ResourceLocation getResourceLocation() { + if(isPresetCape) { + return DefaultCapes.getCapeFromId(presetCapeId).location; + }else { + if(customCape != null) { + return customCape.resourceLocation; + }else { + return null; + } + } + } + + protected void free() { + if(!isPresetCape && customCape.resourceLocation != null) { + ServerCapeCache.this.textureManager.deleteTexture(customCape.resourceLocation); + } + } + + } + + protected static class CacheCustomCape { + + protected final EaglerSkinTexture textureInstance; + protected final ResourceLocation resourceLocation; + + protected CacheCustomCape(EaglerSkinTexture textureInstance, ResourceLocation resourceLocation) { + this.textureInstance = textureInstance; + this.resourceLocation = resourceLocation; + } + + } + + private final CapeCacheEntry defaultCacheEntry = new CapeCacheEntry(0); + private final Map capesCache = new HashMap(); + private final Map waitingCapes = new HashMap(); + private final Map evictedCapes = new HashMap(); + + private final EaglercraftNetworkManager networkManager; + protected final TextureManager textureManager; + + private final EaglercraftUUID clientPlayerId; + private final CapeCacheEntry clientPlayerCacheEntry; + + private long lastFlush = System.currentTimeMillis(); + private long lastFlushReq = System.currentTimeMillis(); + private long lastFlushEvict = System.currentTimeMillis(); + + private static int texId = 0; + + public ServerCapeCache(EaglercraftNetworkManager networkManager, TextureManager textureManager) { + this.networkManager = networkManager; + this.textureManager = textureManager; + this.clientPlayerId = EaglerProfile.getPlayerUUID(); + this.clientPlayerCacheEntry = new CapeCacheEntry(EaglerProfile.getActiveCapeResourceLocation()); + } + + public CapeCacheEntry getClientPlayerCape() { + return clientPlayerCacheEntry; + } + + public CapeCacheEntry getCape(EaglercraftUUID player) { + if(player.equals(clientPlayerId)) { + return clientPlayerCacheEntry; + } + CapeCacheEntry etr = capesCache.get(player); + if(etr == null) { + if(!waitingCapes.containsKey(player) && !evictedCapes.containsKey(player)) { + waitingCapes.put(player, System.currentTimeMillis()); + PacketBuffer buffer; + try { + buffer = CapePackets.writeGetOtherCape(player); + }catch(IOException ex) { + logger.error("Could not write cape request packet!"); + logger.error(ex); + return defaultCacheEntry; + } + networkManager.sendPacket(new C17PacketCustomPayload("EAG|Capes-1.8", buffer)); + } + return defaultCacheEntry; + }else { + etr.lastCacheHit = System.currentTimeMillis(); + return etr; + } + } + + public void cacheCapePreset(EaglercraftUUID player, int presetId) { + if(waitingCapes.remove(player) != null) { + CapeCacheEntry etr = capesCache.remove(player); + if(etr != null) { + etr.free(); + } + capesCache.put(player, new CapeCacheEntry(presetId)); + }else { + logger.error("Unsolicited cape response recieved for \"{}\"! (preset {})", player, presetId); + } + } + + public void cacheCapeCustom(EaglercraftUUID player, byte[] pixels) { + if(waitingCapes.remove(player) != null) { + CapeCacheEntry etr = capesCache.remove(player); + if(etr != null) { + etr.free(); + } + byte[] pixels32x32 = new byte[4096]; + SkinConverter.convertCape23x17RGBto32x32RGBA(pixels, pixels32x32); + try { + etr = new CapeCacheEntry(new EaglerSkinTexture(pixels32x32, 32, 32), + new ResourceLocation("eagler:capes/multiplayer/tex_" + texId++)); + }catch(Throwable t) { + etr = new CapeCacheEntry(0); + logger.error("Could not process custom skin packet for \"{}\"!", player); + logger.error(t); + } + capesCache.put(player, etr); + }else { + logger.error("Unsolicited skin response recieved for \"{}\"!", player); + } + } + + public void flush() { + long millis = System.currentTimeMillis(); + if(millis - lastFlushReq > 5000l) { + lastFlushReq = millis; + if(!waitingCapes.isEmpty()) { + Iterator waitingItr = waitingCapes.values().iterator(); + while(waitingItr.hasNext()) { + if(millis - waitingItr.next().longValue() > 30000l) { + waitingItr.remove(); + } + } + } + } + if(millis - lastFlushEvict > 1000l) { + lastFlushEvict = millis; + if(!evictedCapes.isEmpty()) { + Iterator evictItr = evictedCapes.values().iterator(); + while(evictItr.hasNext()) { + if(millis - evictItr.next().longValue() > 3000l) { + evictItr.remove(); + } + } + } + } + if(millis - lastFlush > 60000l) { + lastFlush = millis; + if(!capesCache.isEmpty()) { + Iterator entryItr = capesCache.values().iterator(); + while(entryItr.hasNext()) { + CapeCacheEntry etr = entryItr.next(); + if(millis - etr.lastCacheHit > 900000l) { // 15 minutes + entryItr.remove(); + etr.free(); + } + } + } + } + } + + public void destroy() { + Iterator entryItr = capesCache.values().iterator(); + while(entryItr.hasNext()) { + entryItr.next().free(); + } + capesCache.clear(); + waitingCapes.clear(); + evictedCapes.clear(); + } + + public void evictCape(EaglercraftUUID uuid) { + evictedCapes.put(uuid, Long.valueOf(System.currentTimeMillis())); + CapeCacheEntry etr = capesCache.remove(uuid); + if(etr != null) { + etr.free(); + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerSkinCache.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerSkinCache.java index f2f6abd..5a88c28 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerSkinCache.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/ServerSkinCache.java @@ -321,6 +321,7 @@ public class ServerSkinCache { } skinsCache.clear(); waitingSkins.clear(); + evictedSkins.clear(); } public void evictSkin(EaglercraftUUID uuid) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinConverter.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinConverter.java index a91a05c..5242f8b 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinConverter.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinConverter.java @@ -3,7 +3,7 @@ package net.lax1dude.eaglercraft.v1_8.profile; import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; /** - * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -34,6 +34,56 @@ public class SkinConverter { copyRawPixels(skinIn.pixels, skinOut.pixels, 44, 52, 40, 64, 40, 20, 44, 32, 64, 64); copyRawPixels(skinIn.pixels, skinOut.pixels, 48, 52, 44, 64, 52, 20, 56, 32, 64, 64); } + + public static void convertCape32x32RGBAto23x17RGB(ImageData skinIn, byte[] skinOut) { + int i, j; + for(int y = 0; y < 17; ++y) { + for(int x = 0; x < 22; ++x) { + i = (y * 23 + x) * 3; + j = skinIn.pixels[y * skinIn.width + x]; + if((j & 0xFF000000) != 0) { + skinOut[i] = (byte)(j >> 16); + skinOut[i + 1] = (byte)(j >> 8); + skinOut[i + 2] = (byte)(j & 0xFF); + }else { + skinOut[i] = skinOut[i + 1] = skinOut[i + 2] = 0; + } + } + } + for(int y = 0; y < 11; ++y) { + i = ((y + 6) * 23 + 22) * 3; + j = skinIn.pixels[(y + 11) * skinIn.width + 22]; + if((j & 0xFF000000) != 0) { + skinOut[i] = (byte)(j >> 16); + skinOut[i + 1] = (byte)(j >> 8); + skinOut[i + 2] = (byte)(j & 0xFF); + }else { + skinOut[i] = skinOut[i + 1] = skinOut[i + 2] = 0; + } + } + } + + public static void convertCape23x17RGBto32x32RGBA(byte[] skinIn, byte[] skinOut) { + int i, j; + for(int y = 0; y < 17; ++y) { + for(int x = 0; x < 22; ++x) { + i = (y * 32 + x) << 2; + j = (y * 23 + x) * 3; + skinOut[i] = (byte)0xFF; + skinOut[i + 1] = skinIn[j]; + skinOut[i + 2] = skinIn[j + 1]; + skinOut[i + 3] = skinIn[j + 2]; + } + } + for(int y = 0; y < 11; ++y) { + i = ((y + 11) * 32 + 22) << 2; + j = ((y + 6) * 23 + 22) * 3; + skinOut[i] = (byte)0xFF; + skinOut[i + 1] = skinIn[j]; + skinOut[i + 2] = skinIn[j + 1]; + skinOut[i + 3] = skinIn[j + 2]; + } + } private static void copyRawPixels(int[] imageIn, int[] imageOut, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, int imgSrcWidth, int imgDstWidth) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinModel.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinModel.java index 7956881..8dbb655 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinModel.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinModel.java @@ -19,15 +19,19 @@ import java.util.Map; * */ public enum SkinModel { - STEVE(0, 64, 64, "default", false), ALEX(1, 64, 64, "slim", false), ZOMBIE(2, 64, 64, "zombie", true); + STEVE(0, 64, 64, "default", false), ALEX(1, 64, 64, "slim", false), ZOMBIE(2, 64, 64, "zombie", true), + LONG_ARMS(3, HighPolySkin.LONG_ARMS), WEIRD_CLIMBER_DUDE(4, HighPolySkin.WEIRD_CLIMBER_DUDE), + LAXATIVE_DUDE(5, HighPolySkin.LAXATIVE_DUDE), BABY_CHARLES(6, HighPolySkin.BABY_CHARLES), + BABY_WINSTON(7, HighPolySkin.BABY_WINSTON); public final int id; public final int width; public final int height; public final String profileSkinType; public final boolean sanitize; + public final HighPolySkin highPoly; - public static final SkinModel[] skinModels = new SkinModel[3]; + public static final SkinModel[] skinModels = new SkinModel[8]; private static final Map skinModelsByName = new HashMap(); private SkinModel(int id, int w, int h, String profileSkinType, boolean sanitize) { @@ -36,6 +40,16 @@ public enum SkinModel { this.height = h; this.profileSkinType = profileSkinType; this.sanitize = sanitize; + this.highPoly = null; + } + + private SkinModel(int id, HighPolySkin highPoly) { + this.id = id; + this.width = 256; + this.height = 128; + this.profileSkinType = "eagler"; + this.sanitize = true; + this.highPoly = highPoly; } public static SkinModel getModelFromId(String str) { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinPackets.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinPackets.java index 93dc6b6..510bd89 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinPackets.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinPackets.java @@ -58,6 +58,9 @@ public class SkinPackets { modelId = SkinModel.STEVE; } } + if(modelId.highPoly != null) { + modelId = SkinModel.STEVE; + } int bytesToRead = modelId.width * modelId.height * 4; byte[] readSkin = new byte[bytesToRead]; buffer.readBytes(readSkin); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinPreviewRenderer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinPreviewRenderer.java index 1608c04..d7dcd47 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinPreviewRenderer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinPreviewRenderer.java @@ -1,10 +1,14 @@ package net.lax1dude.eaglercraft.v1_8.profile; +import net.lax1dude.eaglercraft.v1_8.opengl.EaglerMeshLoader; +import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.minecraft.client.Minecraft; import net.minecraft.client.model.ModelBiped; import net.minecraft.client.model.ModelPlayer; import net.minecraft.client.model.ModelZombie; import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.util.ResourceLocation; /** * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. @@ -36,7 +40,11 @@ public class SkinPreviewRenderer { playerModelZombie.isChild = false; } - public static void renderBiped(int x, int y, int mx, int my, SkinModel skinModel) { + public static void renderPreview(int x, int y, int mx, int my, SkinModel skinModel) { + renderPreview(x, y, mx, my, false, skinModel, null, null); + } + + public static void renderPreview(int x, int y, int mx, int my, boolean capeMode, SkinModel skinModel, ResourceLocation skinTexture, ResourceLocation capeTexture) { ModelBiped model; switch(skinModel) { case STEVE: @@ -49,6 +57,17 @@ public class SkinPreviewRenderer { case ZOMBIE: model = playerModelZombie; break; + case LONG_ARMS: + case WEIRD_CLIMBER_DUDE: + case LAXATIVE_DUDE: + case BABY_CHARLES: + case BABY_WINSTON: + if(skinModel.highPoly != null && Minecraft.getMinecraft().gameSettings.enableFNAWSkins) { + renderHighPoly(x, y, mx, my, skinModel.highPoly); + return; + } + model = playerModelSteve; + break; } GlStateManager.enableTexture2D(); @@ -65,12 +84,95 @@ public class SkinPreviewRenderer { RenderHelper.enableGUIStandardItemLighting(); GlStateManager.translate(0.0f, 1.0f, 0.0f); - GlStateManager.rotate(((y - my) * -0.06f), 1.0f, 0.0f, 0.0f); + if(capeMode) { + GlStateManager.rotate(140.0f, 0.0f, 1.0f, 0.0f); + mx = x - (x - mx) - 20; + GlStateManager.rotate(((y - my) * -0.02f), 1.0f, 0.0f, 0.0f); + }else { + GlStateManager.rotate(((y - my) * -0.06f), 1.0f, 0.0f, 0.0f); + } GlStateManager.rotate(((x - mx) * 0.06f), 0.0f, 1.0f, 0.0f); GlStateManager.translate(0.0f, -1.0f, 0.0f); + if(skinTexture != null) { + Minecraft.getMinecraft().getTextureManager().bindTexture(skinTexture); + } + model.render(null, 0.0f, 0.0f, (float)(System.currentTimeMillis() % 2000000) / 50f, ((x - mx) * 0.06f), ((y - my) * -0.1f), 0.0625f); + if(capeTexture != null && model instanceof ModelPlayer) { + Minecraft.getMinecraft().getTextureManager().bindTexture(capeTexture); + GlStateManager.pushMatrix(); + GlStateManager.translate(0.0F, 0.0F, 0.125F); + GlStateManager.rotate(6.0F, 1.0F, 0.0F, 0.0F); + GlStateManager.rotate(180.0F, 0.0F, 1.0F, 0.0F); + ((ModelPlayer)model).renderCape(0.0625f); + GlStateManager.popMatrix(); + } + + GlStateManager.popMatrix(); + GlStateManager.disableLighting(); + } + + private static void renderHighPoly(int x, int y, int mx, int my, HighPolySkin msh) { + GlStateManager.enableTexture2D(); + GlStateManager.disableBlend(); + GlStateManager.disableCull(); + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + + GlStateManager.pushMatrix(); + GlStateManager.translate(x, y - 80.0f, 100.0f); + GlStateManager.scale(50.0f, 50.0f, 50.0f); + GlStateManager.rotate(180.0f, 1.0f, 0.0f, 0.0f); + GlStateManager.scale(1.0f, -1.0f, 1.0f); + + RenderHelper.enableGUIStandardItemLighting(); + + GlStateManager.translate(0.0f, 1.0f, 0.0f); + GlStateManager.rotate(((y - my) * -0.06f), 1.0f, 0.0f, 0.0f); + GlStateManager.rotate(((x - mx) * 0.06f), 0.0f, 1.0f, 0.0f); + GlStateManager.rotate(180.0f, 0.0f, 0.0f, 1.0f); + GlStateManager.translate(0.0f, -0.6f, 0.0f); + + GlStateManager.scale(HighPolySkin.highPolyScale, HighPolySkin.highPolyScale, HighPolySkin.highPolyScale); + Minecraft.getMinecraft().getTextureManager().bindTexture(msh.texture); + + if(msh.bodyModel != null) { + EaglercraftGPU.drawHighPoly(EaglerMeshLoader.getEaglerMesh(msh.bodyModel)); + } + + if(msh.headModel != null) { + EaglercraftGPU.drawHighPoly(EaglerMeshLoader.getEaglerMesh(msh.headModel)); + } + + if(msh.limbsModel != null && msh.limbsModel.length > 0) { + for(int i = 0; i < msh.limbsModel.length; ++i) { + float offset = 0.0f; + if(msh.limbsOffset != null) { + if(msh.limbsOffset.length == 1) { + offset = msh.limbsOffset[0]; + }else { + offset = msh.limbsOffset[i]; + } + } + if(offset != 0.0f || msh.limbsInitialRotation != 0.0f) { + GlStateManager.pushMatrix(); + if(offset != 0.0f) { + GlStateManager.translate(0.0f, offset, 0.0f); + } + if(msh.limbsInitialRotation != 0.0f) { + GlStateManager.rotate(msh.limbsInitialRotation, 1.0f, 0.0f, 0.0f); + } + } + + EaglercraftGPU.drawHighPoly(EaglerMeshLoader.getEaglerMesh(msh.limbsModel[i])); + + if(offset != 0.0f || msh.limbsInitialRotation != 0.0f) { + GlStateManager.popMatrix(); + } + } + } + GlStateManager.popMatrix(); GlStateManager.disableLighting(); } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/ConnectionHandshake.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/ConnectionHandshake.java index b51965b..318118d 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/ConnectionHandshake.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/socket/ConnectionHandshake.java @@ -293,6 +293,19 @@ public class ConnectionHandshake { d.write(packetSkin); PlatformNetworking.writePlayPacket(bao.toByteArray()); + bao.reset(); + d.writeByte(HandshakePacketTypes.PROTOCOL_CLIENT_PROFILE_DATA); + profileDataType = "cape_v1"; + d.writeByte(profileDataType.length()); + d.writeBytes(profileDataType); + byte[] packetCape = EaglerProfile.getCapePacket(); + if(packetCape.length > 0xFFFF) { + throw new IOException("Cape packet is too long: " + packetCape.length); + } + d.writeShort(packetCape.length); + d.write(packetCape); + PlatformNetworking.writePlayPacket(bao.toByteArray()); + byte[] packetSignatureData = UpdateService.getClientSignatureData(); if(packetSignatureData != null) { bao.reset(); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenAddRelay.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenAddRelay.java index 7fa49f4..e66104a 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenAddRelay.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenAddRelay.java @@ -139,5 +139,9 @@ public class GuiScreenAddRelay extends GuiScreen { this.serverAddress.drawTextBox(); super.drawScreen(par1, par2, par3); } + + public boolean blockPTTKey() { + return this.serverName.isFocused() || this.serverAddress.isFocused(); + } } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenLANConnecting.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenLANConnecting.java index 38b6ecb..6445e8d 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenLANConnecting.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenLANConnecting.java @@ -117,7 +117,7 @@ public class GuiScreenLANConnecting extends GuiScreen { this.mc.clearTitles(); networkManager.setConnectionState(EnumConnectionState.LOGIN); networkManager.setNetHandler(new NetHandlerSingleplayerLogin(networkManager, mc, parent)); - networkManager.sendPacket(new C00PacketLoginStart(this.mc.getSession().getProfile(), EaglerProfile.getSkinPacket())); + networkManager.sendPacket(new C00PacketLoginStart(this.mc.getSession().getProfile(), EaglerProfile.getSkinPacket(), EaglerProfile.getCapePacket())); } } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenSingleplayerConnecting.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenSingleplayerConnecting.java index ff7d193..fb38a61 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenSingleplayerConnecting.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiScreenSingleplayerConnecting.java @@ -88,7 +88,7 @@ public class GuiScreenSingleplayerConnecting extends GuiScreen { this.mc.clearTitles(); this.networkManager.setConnectionState(EnumConnectionState.LOGIN); this.networkManager.setNetHandler(new NetHandlerSingleplayerLogin(this.networkManager, this.mc, this.menu)); - this.networkManager.sendPacket(new C00PacketLoginStart(this.mc.getSession().getProfile(), EaglerProfile.getSkinPacket())); + this.networkManager.sendPacket(new C00PacketLoginStart(this.mc.getSession().getProfile(), EaglerProfile.getSkinPacket(), EaglerProfile.getCapePacket())); } try { this.networkManager.processReceivedPackets(); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiShareToLan.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiShareToLan.java index 7ab3dc3..8b40fa2 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiShareToLan.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiShareToLan.java @@ -195,4 +195,8 @@ public class GuiShareToLan extends GuiScreen { super.updateScreen(); this.codeTextField.updateCursorCounter(); } + + public boolean blockPTTKey() { + return this.codeTextField.isFocused(); + } } \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java index f695ebe..c5d87ee 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java @@ -361,6 +361,9 @@ public class EaglerIntegratedServerWorker { case IPCPacket17ConfigureLAN.ID: { IPCPacket17ConfigureLAN pkt = (IPCPacket17ConfigureLAN)ipc; + if(!pkt.iceServers.isEmpty() && ServerPlatformSingleplayer.getClientConfigAdapter().isAllowVoiceClient()) { + currentProcess.enableVoice(pkt.iceServers.toArray(new String[pkt.iceServers.size()])); + } currentProcess.getConfigurationManager().configureLAN(pkt.gamemode, pkt.cheats); // don't use iceServers break; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerMinecraftServer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerMinecraftServer.java index 85a6969..656c7e5 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerMinecraftServer.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerMinecraftServer.java @@ -16,7 +16,9 @@ import net.minecraft.world.EnumDifficulty; import net.minecraft.world.WorldServer; import net.minecraft.world.WorldSettings; import net.minecraft.world.WorldSettings.GameType; +import net.lax1dude.eaglercraft.v1_8.sp.server.skins.IntegratedCapeService; import net.lax1dude.eaglercraft.v1_8.sp.server.skins.IntegratedSkinService; +import net.lax1dude.eaglercraft.v1_8.sp.server.voice.IntegratedVoiceService; /** * Copyright (c) 2023-2024 lax1dude, ayunami2000. All Rights Reserved. @@ -45,6 +47,8 @@ public class EaglerMinecraftServer extends MinecraftServer { protected boolean paused; protected EaglerSaveHandler saveHandler; protected IntegratedSkinService skinService; + protected IntegratedCapeService capeService; + protected IntegratedVoiceService voiceService; private long lastTPSUpdate = 0l; @@ -62,6 +66,8 @@ public class EaglerMinecraftServer extends MinecraftServer { Bootstrap.register(); this.saveHandler = new EaglerSaveHandler(savesDir, world); this.skinService = new IntegratedSkinService(new VFile2(saveHandler.getWorldDirectory(), "eagler/skulls")); + this.capeService = new IntegratedCapeService(); + this.voiceService = null; this.setServerOwner(owner); logger.info("server owner: " + owner); this.setDemo(demo); @@ -75,6 +81,27 @@ public class EaglerMinecraftServer extends MinecraftServer { public IntegratedSkinService getSkinService() { return skinService; } + + public IntegratedCapeService getCapeService() { + return capeService; + } + + public IntegratedVoiceService getVoiceService() { + return voiceService; + } + + public void enableVoice(String[] iceServers) { + if(iceServers != null) { + if(voiceService != null) { + voiceService.changeICEServers(iceServers); + }else { + voiceService = new IntegratedVoiceService(iceServers); + for(EntityPlayerMP player : getConfigurationManager().func_181057_v()) { + voiceService.handlePlayerLoggedIn(player); + } + } + } + } public void setBaseServerProperties(EnumDifficulty difficulty, GameType gamemode) { this.difficulty = difficulty; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerPlayerList.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerPlayerList.java index 32e2b88..525110b 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerPlayerList.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerPlayerList.java @@ -43,6 +43,8 @@ public class EaglerPlayerList extends ServerConfigurationManager { public void playerLoggedOut(EntityPlayerMP playerIn) { super.playerLoggedOut(playerIn); - ((EaglerMinecraftServer)getServerInstance()).skinService.unregisterPlayer(playerIn.getUniqueID()); + EaglerMinecraftServer svr = (EaglerMinecraftServer)getServerInstance(); + svr.skinService.unregisterPlayer(playerIn.getUniqueID()); + svr.capeService.unregisterPlayer(playerIn.getUniqueID()); } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapePackets.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapePackets.java new file mode 100644 index 0000000..90088cd --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapePackets.java @@ -0,0 +1,110 @@ +package net.lax1dude.eaglercraft.v1_8.sp.server.skins; + +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +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 IntegratedCapePackets { + + public static final int PACKET_MY_CAPE_PRESET = 0x01; + public static final int PACKET_MY_CAPE_CUSTOM = 0x02; + public static final int PACKET_GET_OTHER_CAPE = 0x03; + public static final int PACKET_OTHER_CAPE_PRESET = 0x04; + public static final int PACKET_OTHER_CAPE_CUSTOM = 0x05; + + public static void processPacket(byte[] data, EntityPlayerMP sender, IntegratedCapeService capeService) throws IOException { + if(data.length == 0) { + throw new IOException("Zero-length packet recieved"); + } + int packetId = (int)data[0] & 0xFF; + try { + switch(packetId) { + case PACKET_GET_OTHER_CAPE: + processGetOtherCape(data, sender, capeService); + break; + default: + throw new IOException("Unknown packet type " + packetId); + } + }catch(IOException ex) { + throw ex; + }catch(Throwable t) { + throw new IOException("Unhandled exception handling packet type " + packetId, t); + } + } + + private static void processGetOtherCape(byte[] data, EntityPlayerMP sender, IntegratedCapeService capeService) throws IOException { + if(data.length != 17) { + throw new IOException("Invalid length " + data.length + " for skin request packet"); + } + EaglercraftUUID searchUUID = IntegratedSkinPackets.bytesToUUID(data, 1); + capeService.processGetOtherCape(searchUUID, sender); + } + + public static void registerEaglerPlayer(EaglercraftUUID clientUUID, byte[] bs, IntegratedCapeService capeService) throws IOException { + if(bs.length == 0) { + throw new IOException("Zero-length packet recieved"); + } + byte[] generatedPacket; + int packetType = (int)bs[0] & 0xFF; + switch(packetType) { + case PACKET_MY_CAPE_PRESET: + if(bs.length != 5) { + throw new IOException("Invalid length " + bs.length + " for preset cape packet"); + } + generatedPacket = IntegratedCapePackets.makePresetResponse(clientUUID, (bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF)); + break; + case PACKET_MY_CAPE_CUSTOM: + if(bs.length != 1174) { + throw new IOException("Invalid length " + bs.length + " for custom cape packet"); + } + generatedPacket = IntegratedCapePackets.makeCustomResponse(clientUUID, bs, 1, 1173); + break; + default: + throw new IOException("Unknown skin packet type: " + packetType); + } + capeService.registerEaglercraftPlayer(clientUUID, generatedPacket); + } + + public static void registerEaglerPlayerFallback(EaglercraftUUID clientUUID, IntegratedCapeService capeService) { + capeService.registerEaglercraftPlayer(clientUUID, IntegratedCapePackets.makePresetResponse(clientUUID, 0)); + } + + public static byte[] makePresetResponse(EaglercraftUUID uuid, int presetId) { + byte[] ret = new byte[1 + 16 + 4]; + ret[0] = (byte)PACKET_OTHER_CAPE_PRESET; + IntegratedSkinPackets.UUIDToBytes(uuid, ret, 1); + ret[17] = (byte)(presetId >> 24); + ret[18] = (byte)(presetId >> 16); + ret[19] = (byte)(presetId >> 8); + ret[20] = (byte)(presetId & 0xFF); + return ret; + } + + public static byte[] makeCustomResponse(EaglercraftUUID uuid, byte[] pixels) { + return makeCustomResponse(uuid, pixels, 0, pixels.length); + } + + public static byte[] makeCustomResponse(EaglercraftUUID uuid, byte[] pixels, int offset, int length) { + byte[] ret = new byte[1 + 16 + length]; + ret[0] = (byte)PACKET_OTHER_CAPE_CUSTOM; + IntegratedSkinPackets.UUIDToBytes(uuid, ret, 1); + System.arraycopy(pixels, offset, ret, 17, length); + return ret; + } +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapeService.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapeService.java new file mode 100644 index 0000000..21c7da3 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/skins/IntegratedCapeService.java @@ -0,0 +1,77 @@ +package net.lax1dude.eaglercraft.v1_8.sp.server.skins; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +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.netty.Unpooled; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.PacketBuffer; +import net.minecraft.network.play.server.S3FPacketCustomPayload; + +/** + * 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 IntegratedCapeService { + + public static final Logger logger = LogManager.getLogger("IntegratedCapeService"); + + public static final int masterRateLimitPerPlayer = 250; + + public static final String CHANNEL = "EAG|Capes-1.8"; + + private final Map capesCache = new HashMap(); + + public void processPacket(byte[] packetData, EntityPlayerMP sender) { + try { + IntegratedCapePackets.processPacket(packetData, sender, this); + } catch (IOException e) { + logger.error("Invalid skin request packet recieved from player {}!", sender.getName()); + logger.error(e); + sender.playerNetServerHandler.kickPlayerFromServer("Invalid skin request packet recieved!"); + } + } + + public void processLoginPacket(byte[] packetData, EntityPlayerMP sender) { + try { + IntegratedCapePackets.registerEaglerPlayer(sender.getUniqueID(), packetData, this); + } catch (IOException e) { + logger.error("Invalid skin data packet recieved from player {}!", sender.getName()); + logger.error(e); + sender.playerNetServerHandler.kickPlayerFromServer("Invalid skin data packet recieved!"); + } + } + + public void registerEaglercraftPlayer(EaglercraftUUID playerUUID, byte[] capePacket) { + capesCache.put(playerUUID, capePacket); + } + + public void processGetOtherCape(EaglercraftUUID searchUUID, EntityPlayerMP sender) { + byte[] maybeCape = capesCache.get(searchUUID); + if(maybeCape == null) { + maybeCape = IntegratedCapePackets.makePresetResponse(searchUUID, 0); + } + sender.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, new PacketBuffer(Unpooled.buffer(maybeCape, maybeCape.length).writerIndex(maybeCape.length)))); + } + + public void unregisterPlayer(EaglercraftUUID playerUUID) { + synchronized(capesCache) { + capesCache.remove(playerUUID); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/voice/IntegratedVoiceService.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/voice/IntegratedVoiceService.java new file mode 100644 index 0000000..7b9c682 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/voice/IntegratedVoiceService.java @@ -0,0 +1,255 @@ +package net.lax1dude.eaglercraft.v1_8.sp.server.voice; + +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +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.netty.Unpooled; +import net.lax1dude.eaglercraft.v1_8.voice.ExpiringSet; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.PacketBuffer; +import net.minecraft.network.play.server.S3FPacketCustomPayload; + +/** + * 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 IntegratedVoiceService { + + public static final Logger logger = LogManager.getLogger("IntegratedVoiceService"); + + public static final String CHANNEL = "EAG|Voice-1.8"; + + private byte[] iceServersPacket; + + private final Map voicePlayers = new HashMap<>(); + private final Map> voiceRequests = new HashMap<>(); + private final Set voicePairs = new HashSet<>(); + + public IntegratedVoiceService(String[] iceServers) { + iceServersPacket = IntegratedVoiceSignalPackets.makeVoiceSignalPacketAllowed(true, iceServers); + } + + public void changeICEServers(String[] iceServers) { + iceServersPacket = IntegratedVoiceSignalPackets.makeVoiceSignalPacketAllowed(true, iceServers); + } + + private static class VoicePair { + + private final EaglercraftUUID uuid1; + private final EaglercraftUUID uuid2; + + @Override + public int hashCode() { + return uuid1.hashCode() ^ uuid2.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + VoicePair other = (VoicePair) obj; + return (uuid1.equals(other.uuid1) && uuid2.equals(other.uuid2)) + || (uuid1.equals(other.uuid2) && uuid2.equals(other.uuid1)); + } + + private VoicePair(EaglercraftUUID uuid1, EaglercraftUUID uuid2) { + this.uuid1 = uuid1; + this.uuid2 = uuid2; + } + + private boolean anyEquals(EaglercraftUUID uuid) { + return uuid1.equals(uuid) || uuid2.equals(uuid); + } + } + + public void handlePlayerLoggedIn(EntityPlayerMP player) { + player.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, new PacketBuffer( + Unpooled.buffer(iceServersPacket, iceServersPacket.length).writerIndex(iceServersPacket.length)))); + } + + public void handlePlayerLoggedOut(EntityPlayerMP player) { + removeUser(player.getUniqueID()); + } + + public void processPacket(PacketBuffer packetData, EntityPlayerMP sender) { + try { + IntegratedVoiceSignalPackets.processPacket(packetData, sender, this); + } catch (IOException e) { + logger.error("Invalid voice signal packet recieved from player {}!", sender.getName()); + logger.error(e); + sender.playerNetServerHandler.kickPlayerFromServer("Invalid voice signal packet recieved!"); + } + } + + void handleVoiceSignalPacketTypeRequest(EaglercraftUUID player, EntityPlayerMP sender) { + EaglercraftUUID senderUUID = sender.getUniqueID(); + if (senderUUID.equals(player)) + return; // prevent duplicates + if (!voicePlayers.containsKey(senderUUID)) + return; + EntityPlayerMP targetPlayerCon = voicePlayers.get(player); + if (targetPlayerCon == null) + return; + VoicePair newPair = new VoicePair(player, senderUUID); + if (voicePairs.contains(newPair)) + return; // already paired + ExpiringSet senderRequestSet = voiceRequests.get(senderUUID); + if (senderRequestSet == null) { + voiceRequests.put(senderUUID, senderRequestSet = new ExpiringSet<>(2000)); + } + if (!senderRequestSet.add(player)) { + return; + } + + // check if other has requested earlier + ExpiringSet theSet; + if ((theSet = voiceRequests.get(player)) != null && theSet.contains(senderUUID)) { + theSet.remove(senderUUID); + if (theSet.isEmpty()) + voiceRequests.remove(player); + senderRequestSet.remove(player); + if (senderRequestSet.isEmpty()) + voiceRequests.remove(senderUUID); + // send each other add data + voicePairs.add(newPair); + targetPlayerCon.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, + IntegratedVoiceSignalPackets.makeVoiceSignalPacketConnect(senderUUID, false))); + sender.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, + IntegratedVoiceSignalPackets.makeVoiceSignalPacketConnect(player, true))); + } + } + + void handleVoiceSignalPacketTypeConnect(EntityPlayerMP sender) { + if (voicePlayers.containsKey(sender.getUniqueID())) { + return; + } + boolean hasNoOtherPlayers = voicePlayers.isEmpty(); + voicePlayers.put(sender.getUniqueID(), sender); + if (hasNoOtherPlayers) { + return; + } + byte[] packetToBroadcast = IntegratedVoiceSignalPackets.makeVoiceSignalPacketGlobal(voicePlayers.values()); + for (EntityPlayerMP userCon : voicePlayers.values()) { + userCon.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, new PacketBuffer(Unpooled + .buffer(packetToBroadcast, packetToBroadcast.length).writerIndex(packetToBroadcast.length)))); + } + } + + void handleVoiceSignalPacketTypeICE(EaglercraftUUID player, String str, EntityPlayerMP sender) { + VoicePair pair = new VoicePair(player, sender.getUniqueID()); + EntityPlayerMP pass = voicePairs.contains(pair) ? voicePlayers.get(player) : null; + if (pass != null) { + pass.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, + IntegratedVoiceSignalPackets.makeVoiceSignalPacketICE(sender.getUniqueID(), str))); + } + } + + void handleVoiceSignalPacketTypeDesc(EaglercraftUUID player, String str, EntityPlayerMP sender) { + VoicePair pair = new VoicePair(player, sender.getUniqueID()); + EntityPlayerMP pass = voicePairs.contains(pair) ? voicePlayers.get(player) : null; + if (pass != null) { + pass.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, + IntegratedVoiceSignalPackets.makeVoiceSignalPacketDesc(sender.getUniqueID(), str))); + } + } + + void handleVoiceSignalPacketTypeDisconnect(EaglercraftUUID player, EntityPlayerMP sender) { + if (player != null) { + if (!voicePlayers.containsKey(player)) { + return; + } + byte[] userDisconnectPacket = null; + Iterator pairsItr = voicePairs.iterator(); + while (pairsItr.hasNext()) { + VoicePair voicePair = pairsItr.next(); + EaglercraftUUID target = null; + if (voicePair.uuid1.equals(player)) { + target = voicePair.uuid2; + } else if (voicePair.uuid2.equals(player)) { + target = voicePair.uuid1; + } + if (target != null) { + pairsItr.remove(); + EntityPlayerMP conn = voicePlayers.get(target); + if (conn != null) { + if (userDisconnectPacket == null) { + userDisconnectPacket = IntegratedVoiceSignalPackets.makeVoiceSignalPacketDisconnect(player); + } + conn.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, + new PacketBuffer(Unpooled.buffer(userDisconnectPacket, userDisconnectPacket.length) + .writerIndex(userDisconnectPacket.length)))); + } + sender.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, + IntegratedVoiceSignalPackets.makeVoiceSignalPacketDisconnectPB(target))); + } + } + } else { + removeUser(sender.getUniqueID()); + } + } + + public void removeUser(EaglercraftUUID user) { + if (voicePlayers.remove(user) == null) { + return; + } + voiceRequests.remove(user); + if (voicePlayers.size() > 0) { + byte[] voicePlayersPkt = IntegratedVoiceSignalPackets.makeVoiceSignalPacketGlobal(voicePlayers.values()); + for (EntityPlayerMP userCon : voicePlayers.values()) { + if (!user.equals(userCon.getUniqueID())) { + userCon.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, + new PacketBuffer(Unpooled.buffer(voicePlayersPkt, voicePlayersPkt.length) + .writerIndex(voicePlayersPkt.length)))); + } + } + } + byte[] userDisconnectPacket = null; + Iterator pairsItr = voicePairs.iterator(); + while (pairsItr.hasNext()) { + VoicePair voicePair = pairsItr.next(); + EaglercraftUUID target = null; + if (voicePair.uuid1.equals(user)) { + target = voicePair.uuid2; + } else if (voicePair.uuid2.equals(user)) { + target = voicePair.uuid1; + } + if (target != null) { + pairsItr.remove(); + if (voicePlayers.size() > 0) { + EntityPlayerMP conn = voicePlayers.get(target); + if (conn != null) { + if (userDisconnectPacket == null) { + userDisconnectPacket = IntegratedVoiceSignalPackets.makeVoiceSignalPacketDisconnect(user); + } + conn.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload(CHANNEL, + new PacketBuffer(Unpooled.buffer(userDisconnectPacket, userDisconnectPacket.length) + .writerIndex(userDisconnectPacket.length)))); + } + } + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/voice/IntegratedVoiceSignalPackets.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/voice/IntegratedVoiceSignalPackets.java new file mode 100644 index 0000000..0422dc3 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/voice/IntegratedVoiceSignalPackets.java @@ -0,0 +1,198 @@ +package net.lax1dude.eaglercraft.v1_8.sp.server.voice; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collection; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.netty.ByteBuf; +import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.PacketBuffer; + +/** + * 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 IntegratedVoiceSignalPackets { + + static final int VOICE_SIGNAL_ALLOWED = 0; + static final int VOICE_SIGNAL_REQUEST = 0; + static final int VOICE_SIGNAL_CONNECT = 1; + static final int VOICE_SIGNAL_DISCONNECT = 2; + static final int VOICE_SIGNAL_ICE = 3; + static final int VOICE_SIGNAL_DESC = 4; + static final int VOICE_SIGNAL_GLOBAL = 5; + + public static void processPacket(PacketBuffer buffer, EntityPlayerMP sender, IntegratedVoiceService voiceService) throws IOException { + int packetId = -1; + if(buffer.readableBytes() == 0) { + throw new IOException("Zero-length packet recieved"); + } + try { + packetId = buffer.readUnsignedByte(); + switch(packetId) { + case VOICE_SIGNAL_REQUEST: { + voiceService.handleVoiceSignalPacketTypeRequest(buffer.readUuid(), sender); + break; + } + case VOICE_SIGNAL_CONNECT: { + voiceService.handleVoiceSignalPacketTypeConnect(sender); + break; + } + case VOICE_SIGNAL_ICE: { + voiceService.handleVoiceSignalPacketTypeICE(buffer.readUuid(), buffer.readStringFromBuffer(32767), sender); + break; + } + case VOICE_SIGNAL_DESC: { + voiceService.handleVoiceSignalPacketTypeDesc(buffer.readUuid(), buffer.readStringFromBuffer(32767), sender); + break; + } + case VOICE_SIGNAL_DISCONNECT: { + voiceService.handleVoiceSignalPacketTypeDisconnect(buffer.readableBytes() > 0 ? buffer.readUuid() : null, sender); + break; + } + default: { + throw new IOException("Unknown packet type " + packetId); + } + } + if(buffer.readableBytes() > 0) { + throw new IOException("Voice packet is too long!"); + } + }catch(IOException ex) { + throw ex; + }catch(Throwable t) { + throw new IOException("Unhandled exception handling voice packet type " + packetId, t); + } + } + + static byte[] makeVoiceSignalPacketAllowed(boolean allowed, String[] iceServers) { + if (iceServers == null) { + byte[] ret = new byte[2]; + ByteBuf wrappedBuffer = Unpooled.buffer(ret, ret.length); + wrappedBuffer.writeByte(VOICE_SIGNAL_ALLOWED); + wrappedBuffer.writeBoolean(allowed); + return ret; + } + byte[][] iceServersBytes = new byte[iceServers.length][]; + int totalLen = 2 + PacketBuffer.getVarIntSize(iceServers.length); + for(int i = 0; i < iceServers.length; ++i) { + byte[] b = iceServersBytes[i] = iceServers[i].getBytes(StandardCharsets.UTF_8); + totalLen += PacketBuffer.getVarIntSize(b.length) + b.length; + } + byte[] ret = new byte[totalLen]; + PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length)); + wrappedBuffer.writeByte(VOICE_SIGNAL_ALLOWED); + wrappedBuffer.writeBoolean(allowed); + wrappedBuffer.writeVarIntToBuffer(iceServersBytes.length); + for(int i = 0; i < iceServersBytes.length; ++i) { + byte[] b = iceServersBytes[i]; + wrappedBuffer.writeVarIntToBuffer(b.length); + wrappedBuffer.writeBytes(b); + } + return ret; + } + + static byte[] makeVoiceSignalPacketGlobal(Collection users) { + int cnt = users.size(); + byte[][] displayNames = new byte[cnt][]; + int i = 0; + for(EntityPlayerMP user : users) { + String name = user.getName(); + if(name.length() > 16) name = name.substring(0, 16); + displayNames[i++] = name.getBytes(StandardCharsets.UTF_8); + } + int totalLength = 1 + PacketBuffer.getVarIntSize(cnt) + (cnt << 4); + for(i = 0; i < cnt; ++i) { + totalLength += PacketBuffer.getVarIntSize(displayNames[i].length) + displayNames[i].length; + } + byte[] ret = new byte[totalLength]; + PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length)); + wrappedBuffer.writeByte(VOICE_SIGNAL_GLOBAL); + wrappedBuffer.writeVarIntToBuffer(cnt); + for(EntityPlayerMP user : users) { + wrappedBuffer.writeUuid(user.getUniqueID()); + } + for(i = 0; i < cnt; ++i) { + wrappedBuffer.writeVarIntToBuffer(displayNames[i].length); + wrappedBuffer.writeBytes(displayNames[i]); + } + return ret; + } + + static PacketBuffer makeVoiceSignalPacketConnect(EaglercraftUUID player, boolean offer) { + byte[] ret = new byte[18]; + PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length)); + wrappedBuffer.writeByte(VOICE_SIGNAL_CONNECT); + wrappedBuffer.writeUuid(player); + wrappedBuffer.writeBoolean(offer); + return wrappedBuffer; + } + + static byte[] makeVoiceSignalPacketConnectAnnounce(EaglercraftUUID player) { + byte[] ret = new byte[17]; + PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length)); + wrappedBuffer.writeByte(VOICE_SIGNAL_CONNECT); + wrappedBuffer.writeUuid(player); + return ret; + } + + static byte[] makeVoiceSignalPacketDisconnect(EaglercraftUUID player) { + if(player == null) { + return new byte[] { (byte)VOICE_SIGNAL_DISCONNECT }; + } + byte[] ret = new byte[17]; + PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length)); + wrappedBuffer.writeByte(VOICE_SIGNAL_DISCONNECT); + wrappedBuffer.writeUuid(player); + return ret; + } + + static PacketBuffer makeVoiceSignalPacketDisconnectPB(EaglercraftUUID player) { + if(player == null) { + byte[] ret = new byte[1]; + PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length)); + wrappedBuffer.writeByte(VOICE_SIGNAL_DISCONNECT); + return wrappedBuffer; + } + byte[] ret = new byte[17]; + PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length)); + wrappedBuffer.writeByte(VOICE_SIGNAL_DISCONNECT); + wrappedBuffer.writeUuid(player); + return wrappedBuffer; + } + + static PacketBuffer makeVoiceSignalPacketICE(EaglercraftUUID player, String str) { + byte[] strBytes = str.getBytes(StandardCharsets.UTF_8); + byte[] ret = new byte[17 + PacketBuffer.getVarIntSize(strBytes.length) + strBytes.length]; + PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length)); + wrappedBuffer.writeByte(VOICE_SIGNAL_ICE); + wrappedBuffer.writeUuid(player); + wrappedBuffer.writeVarIntToBuffer(strBytes.length); + wrappedBuffer.writeBytes(strBytes); + return wrappedBuffer; + } + + static PacketBuffer makeVoiceSignalPacketDesc(EaglercraftUUID player, String str) { + byte[] strBytes = str.getBytes(StandardCharsets.UTF_8); + byte[] ret = new byte[17 + PacketBuffer.getVarIntSize(strBytes.length) + strBytes.length]; + PacketBuffer wrappedBuffer = new PacketBuffer(Unpooled.buffer(ret, ret.length)); + wrappedBuffer.writeByte(VOICE_SIGNAL_DESC); + wrappedBuffer.writeUuid(player); + wrappedBuffer.writeVarIntToBuffer(strBytes.length); + wrappedBuffer.writeBytes(strBytes); + return wrappedBuffer; + } + +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/update/GuiUpdateCheckerOverlay.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/update/GuiUpdateCheckerOverlay.java index 0e1cc0c..caeac63 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/update/GuiUpdateCheckerOverlay.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/update/GuiUpdateCheckerOverlay.java @@ -41,6 +41,8 @@ public class GuiUpdateCheckerOverlay extends Gui { private int width; private int height; + private int totalHeightOffset = 0; + private boolean isIngame; private GuiScreen backScreen; @@ -61,10 +63,10 @@ public class GuiUpdateCheckerOverlay extends Gui { this.mc = mc; this.width = w; this.height = h; - checkForUpdatesButton = new GuiButton(0, w - 150, 0, 150, 20, I18n.format("update.button") + " " + I18n.format(mc.gameSettings.enableUpdateSvc ? "gui.yes" : "gui.no")); - startDownloadButton = new GuiButton(1, w - 115, 0, 115, 20, I18n.format("update.startDownload")); - viewAllUpdatesButton = new GuiButton(2, w - 115, 0, 115, 20, I18n.format("update.viewAll", 0)); - dismissUpdatesButton = new GuiButton(3, w - 115, 0, 115, 20, I18n.format("update.dismiss")); + checkForUpdatesButton = new GuiButton(0, 0, 0, 150, 20, I18n.format("update.button") + " " + I18n.format(mc.gameSettings.enableUpdateSvc ? "gui.yes" : "gui.no")); + startDownloadButton = new GuiButton(1, 1, 0, 115, 20, I18n.format("update.startDownload")); + viewAllUpdatesButton = new GuiButton(2, 1, 0, 115, 20, I18n.format("update.viewAll", 0)); + dismissUpdatesButton = new GuiButton(3, 1, 0, 115, 20, I18n.format("update.dismiss")); } public void drawScreen(int mx, int my, float partialTicks) { @@ -81,6 +83,7 @@ public class GuiUpdateCheckerOverlay extends Gui { startDownloadButton.visible = false; viewAllUpdatesButton.visible = false; dismissUpdatesButton.visible = false; + totalHeightOffset = 0; int i = UpdateService.getAvailableUpdates().size(); boolean shownSP = i > 0 || !mc.isSingleplayer() || LANServerController.isHostingLAN(); @@ -95,7 +98,7 @@ public class GuiUpdateCheckerOverlay extends Gui { dismissUpdatesButton.visible = true; viewAllUpdatesButton.displayString = I18n.format("update.viewAll", i); str = I18n.format("update.found"); - mc.fontRendererObj.drawStringWithShadow(str, width - mc.fontRendererObj.getStringWidth(str) - 3, 22, 0xFFFFAA); + mc.fontRendererObj.drawStringWithShadow(str, 3, 22, 0xFFFFAA); int embedY = 35; int embedWidth = 115; @@ -109,7 +112,7 @@ public class GuiUpdateCheckerOverlay extends Gui { } GlStateManager.pushMatrix(); - GlStateManager.translate(width - embedWidth - 1, embedY, 0.0f); + GlStateManager.translate(1.0f, embedY, 0.0f); GlStateManager.scale(0.75f, 0.75f, 0.75f); int embedHeight2 = (int)(embedHeight / 0.75f); @@ -143,16 +146,20 @@ public class GuiUpdateCheckerOverlay extends Gui { startDownloadButton.yPosition = embedHeight + embedY + 5; viewAllUpdatesButton.yPosition = startDownloadButton.yPosition + 22; dismissUpdatesButton.yPosition = viewAllUpdatesButton.yPosition + 22; + totalHeightOffset = dismissUpdatesButton.yPosition + 20; GlStateManager.popMatrix(); }else if(isIngame) { if(shownSP) { str = I18n.format("update.noneNew"); - mc.fontRendererObj.drawString(str, width - mc.fontRendererObj.getStringWidth(str) - 3, 22, 0xDDDDDD); + mc.fontRendererObj.drawString(str, 3, 22, 0xDDDDDD); if(i > 0) { viewAllUpdatesButton.yPosition = 40; viewAllUpdatesButton.visible = true; viewAllUpdatesButton.displayString = I18n.format("update.viewAll", i); + totalHeightOffset = 60; + }else { + totalHeightOffset = 32; } } } @@ -173,23 +180,23 @@ public class GuiUpdateCheckerOverlay extends Gui { viewAllUpdatesButton.visible = false; dismissUpdatesButton.visible = false; GlStateManager.pushMatrix(); - GlStateManager.translate(width, isIngame ? 0.0f : 10.0f, 0.0f); + GlStateManager.translate(1.0f, isIngame ? 0.0f : 18.0f, 0.0f); String str = I18n.format("update.downloading"); - mc.fontRendererObj.drawStringWithShadow(str, -mc.fontRendererObj.getStringWidth(str) - 2, 2, 0xFFFFAA); + mc.fontRendererObj.drawStringWithShadow(str, 2, 2, 0xFFFFAA); GlStateManager.translate(0.0f, 14.0f, 0.0f); GlStateManager.scale(0.75f, 0.75f, 0.75f); if(!StringUtils.isAllBlank(progressState.statusString1)) { str = progressState.statusString1; - mc.fontRendererObj.drawStringWithShadow(str, -mc.fontRendererObj.getStringWidth(str) - 3, 0, 0xFFFFFF); + mc.fontRendererObj.drawStringWithShadow(str, 3, 0, 0xFFFFFF); } int cc = isIngame ? 0xBBBBBB : 0xFFFFFF; if(!StringUtils.isAllBlank(progressState.statusString2)) { str = progressState.statusString2; - mc.fontRendererObj.drawStringWithShadow(str, -mc.fontRendererObj.getStringWidth(str) - 3, 11, cc); + mc.fontRendererObj.drawStringWithShadow(str, 3, 11, cc); } - int progX1 = -135; + int progX1 = 3; int progY1 = 22; - int progX2 = -3; + int progX2 = 135; int progY2 = 32; float prog = progressState.progressBar; if(prog >= 0.0f) { @@ -202,6 +209,7 @@ public class GuiUpdateCheckerOverlay extends Gui { drawGradientRect(progX1, progY1 + 1, progX1 + 1, progY2 - 1, 0xFF000000, 0xFF000000); drawGradientRect(progX2 - 1, progY1 + 1, progX2, progY2 - 1, 0xFF000000, 0xFF000000); } + totalHeightOffset = 32; if(!StringUtils.isAllBlank(progressState.statusString3)) { GlStateManager.translate(0.0f, progY2 + 2, 0.0f); GlStateManager.scale(0.66f, 0.66f, 0.66f); @@ -209,8 +217,9 @@ public class GuiUpdateCheckerOverlay extends Gui { List wrappedURL = mc.fontRendererObj.listFormattedStringToWidth(str, (int)((progX2 - progX1) * 1.5f)); for(int i = 0, l = wrappedURL.size(); i < l; ++i) { str = wrappedURL.get(i); - mc.fontRendererObj.drawStringWithShadow(str, -mc.fontRendererObj.getStringWidth(str) - 5, i * 11, cc); + mc.fontRendererObj.drawStringWithShadow(str, 5, i * 11, cc); } + totalHeightOffset += (int)(wrappedURL.size() * 5.5f); } GlStateManager.popMatrix(); } @@ -244,4 +253,8 @@ public class GuiUpdateCheckerOverlay extends Gui { } } } + + public int getSharedWorldInfoYOffset() { + return totalHeightOffset; + } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/EnumVoiceChannelPeerState.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/EnumVoiceChannelPeerState.java new file mode 100644 index 0000000..86e9fdc --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/EnumVoiceChannelPeerState.java @@ -0,0 +1,20 @@ +package net.lax1dude.eaglercraft.v1_8.voice; + +/** + * Copyright (c) 2024 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 enum EnumVoiceChannelPeerState { + FAILED, SUCCESS, LOADING; +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/EnumVoiceChannelReadyState.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/EnumVoiceChannelReadyState.java new file mode 100644 index 0000000..bb29ba5 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/EnumVoiceChannelReadyState.java @@ -0,0 +1,20 @@ +package net.lax1dude.eaglercraft.v1_8.voice; + +/** + * Copyright (c) 2024 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 enum EnumVoiceChannelReadyState { + NONE, ABORTED, DEVICE_INITIALIZED; +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/EnumVoiceChannelStatus.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/EnumVoiceChannelStatus.java new file mode 100644 index 0000000..1d410bf --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/EnumVoiceChannelStatus.java @@ -0,0 +1,20 @@ +package net.lax1dude.eaglercraft.v1_8.voice; + +/** + * 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 + * 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 EnumVoiceChannelStatus { + DISCONNECTED, CONNECTING, CONNECTED, UNAVAILABLE; +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/EnumVoiceChannelType.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/EnumVoiceChannelType.java new file mode 100644 index 0000000..c7300d9 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/EnumVoiceChannelType.java @@ -0,0 +1,20 @@ +package net.lax1dude.eaglercraft.v1_8.voice; + +/** + * 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 + * 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 EnumVoiceChannelType { + NONE, GLOBAL, PROXIMITY; +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/ExpiringSet.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/ExpiringSet.java new file mode 100644 index 0000000..b23e3d6 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/ExpiringSet.java @@ -0,0 +1,84 @@ +package net.lax1dude.eaglercraft.v1_8.voice; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; + +/** + * Copyright (c) 2022 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 ExpiringSet extends HashSet { + private final long expiration; + private final ExpiringEvent event; + + private final Map timestamps = new HashMap<>(); + + public ExpiringSet(long expiration) { + this.expiration = expiration; + this.event = null; + } + + public ExpiringSet(long expiration, ExpiringEvent event) { + this.expiration = expiration; + this.event = event; + } + + public interface ExpiringEvent { + void onExpiration(T item); + } + + public void checkForExpirations() { + Iterator iterator = this.timestamps.keySet().iterator(); + long now = System.currentTimeMillis(); + while (iterator.hasNext()) { + T element = iterator.next(); + if (super.contains(element)) { + if (this.timestamps.get(element) + this.expiration < now) { + if (this.event != null) this.event.onExpiration(element); + iterator.remove(); + super.remove(element); + } + } else { + iterator.remove(); + super.remove(element); + } + } + } + + public boolean add(T o) { + checkForExpirations(); + boolean success = super.add(o); + if (success) timestamps.put(o, System.currentTimeMillis()); + return success; + } + + public boolean remove(Object o) { + checkForExpirations(); + boolean success = super.remove(o); + if (success) timestamps.remove(o); + return success; + } + + public void clear() { + this.timestamps.clear(); + super.clear(); + } + + public boolean contains(Object o) { + checkForExpirations(); + return super.contains(o); + } +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/GuiVoiceMenu.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/GuiVoiceMenu.java new file mode 100644 index 0000000..cf5233b --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/GuiVoiceMenu.java @@ -0,0 +1,772 @@ +package net.lax1dude.eaglercraft.v1_8.voice; + +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import java.util.List; +import java.util.Set; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiSlider2; +import net.minecraft.client.Minecraft; +import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.MathHelper; +import net.minecraft.util.ResourceLocation; + +/** + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * 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 GuiVoiceMenu extends Gui { + + public class AbortedException extends RuntimeException { + } + + private static final ResourceLocation voiceGuiIcons = new ResourceLocation("eagler:gui/eagler_gui.png"); + + protected final GuiScreen parent; + + protected Minecraft mc; + protected FontRenderer fontRendererObj; + protected int width; + protected int height; + + protected int voiceButtonOFFposX; + protected int voiceButtonOFFposY; + protected int voiceButtonOFFposW; + protected int voiceButtonOFFposH; + + protected int voiceButtonRADIUSposX; + protected int voiceButtonRADIUSposY; + protected int voiceButtonRADIUSposW; + protected int voiceButtonRADIUSposH; + + protected int voiceButtonGLOBALposX; + protected int voiceButtonGLOBALposY; + protected int voiceButtonGLOBALposW; + protected int voiceButtonGLOBALposH; + + protected int voiceScreenButtonOFFposX; + protected int voiceScreenButtonOFFposY; + protected int voiceScreenButtonOFFposW; + protected int voiceScreenButtonOFFposH; + + protected int voiceScreenButtonRADIUSposX; + protected int voiceScreenButtonRADIUSposY; + protected int voiceScreenButtonRADIUSposW; + protected int voiceScreenButtonRADIUSposH; + + protected int voiceScreenButtonGLOBALposX; + protected int voiceScreenButtonGLOBALposY; + protected int voiceScreenButtonGLOBALposW; + protected int voiceScreenButtonGLOBALposH; + + protected int voiceScreenButtonChangeRadiusposX; + protected int voiceScreenButtonChangeRadiusposY; + protected int voiceScreenButtonChangeRadiusposW; + protected int voiceScreenButtonChangeRadiusposH; + + protected int voiceScreenVolumeIndicatorX; + protected int voiceScreenVolumeIndicatorY; + protected int voiceScreenVolumeIndicatorW; + protected int voiceScreenVolumeIndicatorH; + + protected boolean showSliderBlocks = false; + protected boolean showSliderVolume = false; + protected boolean showPTTKeyConfig = false; + protected int showNewPTTKey = 0; + protected GuiSlider2 sliderBlocks = null; + protected GuiSlider2 sliderListenVolume = null; + protected GuiSlider2 sliderSpeakVolume = null; + + protected GuiButton applyRadiusButton = null; + protected GuiButton applyVolumeButton = null; + protected GuiButton noticeContinueButton = null; + protected GuiButton noticeCancelButton = null; + + protected static boolean showingCompatWarning = false; + protected static boolean showCompatWarning = true; + + protected static boolean showingTrackingWarning = false; + protected static boolean showTrackingWarning = true; + + protected static EnumVoiceChannelType continueChannel = null; + + public GuiVoiceMenu(GuiScreen parent) { + this.parent = parent; + } + + public void setResolution(Minecraft mc, int w, int h) { + this.mc = mc; + this.fontRendererObj = mc.fontRendererObj; + this.width = w; + this.height = h; + initGui(); + } + + public void initGui() { + this.sliderBlocks = new GuiSlider2(-1, (width - 150) / 2, height / 3 + 20, 150, 20, (VoiceClientController.getVoiceProximity() - 5) / 17.0f, 1.0f) { + public boolean mousePressed(Minecraft par1Minecraft, int par2, int par3) { + if(super.mousePressed(par1Minecraft, par2, par3)) { + this.displayString = "" + (int)((sliderValue * 17.0f) + 5.0f) + " Blocks"; + return true; + }else { + return false; + } + } + public void mouseDragged(Minecraft par1Minecraft, int par2, int par3) { + super.mouseDragged(par1Minecraft, par2, par3); + this.displayString = "" + (int)((sliderValue * 17.0f) + 5.0f) + " Blocks"; + } + }; + sliderBlocks.displayString = "" + VoiceClientController.getVoiceProximity() + " Blocks"; + this.sliderListenVolume = new GuiSlider2(-1, (width - 150) / 2, height / 3 + 10, 150, 20, VoiceClientController.getVoiceListenVolume(), 1.0f); + this.sliderSpeakVolume = new GuiSlider2(-1, (width - 150) / 2, height / 3 + 56, 150, 20, VoiceClientController.getVoiceSpeakVolume(), 1.0f); + + applyRadiusButton = new GuiButton(2, (width - 150) / 2, height / 3 + 49, 150, 20, I18n.format("voice.apply")); + applyVolumeButton = new GuiButton(3, (width - 150) / 2, height / 3 + 90, 150, 20, I18n.format("voice.apply")); + noticeContinueButton = new GuiButton(5, (width - 150) / 2, height / 3 + 60, 150, 20, I18n.format("voice.unsupportedWarning10")); + noticeCancelButton = new GuiButton(6, (width - 150) / 2, height / 3 + 90, 150, 20, I18n.format("voice.unsupportedWarning11")); + applyRadiusButton.visible = applyVolumeButton.visible = noticeContinueButton.visible = noticeCancelButton.visible = false; + } + + private void drawButtons(int mx, int my, float partialTicks) { + applyRadiusButton.drawButton(mc, mx, my); + applyVolumeButton.drawButton(mc, mx, my); + noticeContinueButton.drawButton(mc, mx, my); + noticeCancelButton.drawButton(mc, mx, my); + } + + public void drawScreen(int mx, int my, float partialTicks) { + String txt = I18n.format("voice.title"); + drawString(fontRendererObj, txt, width - 5 - fontRendererObj.getStringWidth(txt), 5, 0xFFCC22); + + applyRadiusButton.visible = showSliderBlocks; + applyVolumeButton.visible = showSliderVolume; + + if(showSliderBlocks || showSliderVolume || showPTTKeyConfig) { + + drawRect(0, 0, this.width, this.height, 0xB0101010); + + if(showSliderBlocks) { + + drawRect(width / 2 - 86, height / 4 - 1, this.width / 2 + 86, height / 3 + 64 + height / 16, 0xFFDDDDDD); + drawRect(width / 2 - 85, height / 4 + 0, this.width / 2 + 85, height / 3 + 63 + height / 16, 0xFF333333); + + drawCenteredString(this.fontRendererObj, I18n.format("voice.radiusTitle"), this.width / 2, height / 4 + 9, 16777215); + drawString(this.fontRendererObj, I18n.format("voice.radiusLabel"), (this.width - 150) / 2 + 3, height / 3 + 6, 0xCCCCCC); + sliderBlocks.drawButton(mc, mx, my); + + }else if(showSliderVolume) { + + drawRect(width / 2 - 86, height / 4 - 11, this.width / 2 + 86, height / 3 + 104 + height / 16, 0xFFDDDDDD); + drawRect(width / 2 - 85, height / 4 - 10, this.width / 2 + 85, height / 3 + 103 + height / 16, 0xFF333333); + + drawCenteredString(this.fontRendererObj, I18n.format("voice.volumeTitle"), this.width / 2, height / 4 - 1, 16777215); + drawString(this.fontRendererObj, I18n.format("voice.volumeListen"), (this.width - 150) / 2 + 3, height / 3 - 4, 0xCCCCCC); + sliderListenVolume.drawButton(mc, mx, my); + + drawString(this.fontRendererObj, I18n.format("voice.volumeSpeak"), (this.width - 150) / 2 + 3, height / 3 + 42, 0xCCCCCC); + sliderSpeakVolume.drawButton(mc, mx, my); + + }else if(showPTTKeyConfig) { + + drawRect(width / 2 - 86, height / 3 - 10, this.width / 2 + 86, height / 3 + 35, 0xFFDDDDDD); + drawRect(width / 2 - 85, height / 3 - 9, this.width / 2 + 85, height / 3 + 34, 0xFF333333); + + if(showNewPTTKey > 0) { + GlStateManager.pushMatrix(); + GlStateManager.translate(this.width / 2, height / 3 + 5, 0.0f); + GlStateManager.scale(2.0f, 2.0f, 2.0f); + drawCenteredString(this.fontRendererObj, Keyboard.getKeyName(mc.gameSettings.voicePTTKey), 0, 0, 0xFFCC11); + GlStateManager.popMatrix(); + }else { + drawCenteredString(this.fontRendererObj, I18n.format("voice.pttChangeDesc"), this.width / 2, height / 3 + 8, 16777215); + } + } + + drawButtons(mx, my, partialTicks); + throw new AbortedException(); + } + + GlStateManager.pushMatrix(); + + GlStateManager.translate(width - 6, 15, 0.0f); + GlStateManager.scale(0.75f, 0.75f, 0.75f); + + if(!VoiceClientController.isClientSupported()) { + txt = I18n.format("voice.titleVoiceUnavailable"); + drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 6, 0xFF7777); + txt = I18n.format("voice.titleVoiceBrowserError"); + drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 19, 0xAA4444); + GlStateManager.popMatrix(); + return; + } + + if(!VoiceClientController.isServerSupported()) { + txt = I18n.format("voice.titleNoVoice"); + drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 5, 0xFF7777); + GlStateManager.popMatrix(); + return; + } + + int xo = 0; + // this feature is optional + //if(VoiceClientController.voiceRelayed()) { + // txt = I18n.format("voice.warning1"); + // drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 8, 0xBB9999); + // txt = I18n.format("voice.warning2"); + // drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 18, 0xBB9999); + // txt = I18n.format("voice.warning3"); + // drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 28, 0xBB9999); + // xo = 43; + // GlStateManager.translate(0.0f, xo, 0.0f); + //} + + EnumVoiceChannelStatus status = VoiceClientController.getVoiceStatus(); + EnumVoiceChannelType channel = VoiceClientController.getVoiceChannel(); + + boolean flag = false; + + if(channel == EnumVoiceChannelType.NONE) { + flag = true; + }else { + if(status == EnumVoiceChannelStatus.CONNECTED) { + + if(channel == EnumVoiceChannelType.PROXIMITY) { + txt = I18n.format("voice.connectedRadius").replace("$radius$", "" + VoiceClientController.getVoiceProximity()).replace("$f$", ""); + int w = fontRendererObj.getStringWidth(txt); + int xx = width - 5 - (w * 3 / 4); + int yy = 15 + (xo * 3 / 4); + voiceScreenButtonChangeRadiusposX = xx; + voiceScreenButtonChangeRadiusposY = yy; + voiceScreenButtonChangeRadiusposW = width - 3 - xx; + voiceScreenButtonChangeRadiusposH = 12; + if(mx >= xx && my >= yy && mx < xx + voiceScreenButtonChangeRadiusposW && my < yy + 12) { + txt = I18n.format("voice.connectedRadius").replace("$radius$", "" + VoiceClientController.getVoiceProximity()) + .replace("$f$", "" + EnumChatFormatting.UNDERLINE) + EnumChatFormatting.RESET; + } + }else { + txt = I18n.format("voice.connectedGlobal"); + } + + voiceScreenVolumeIndicatorX = width - 15 - (104 * 3 / 4); + voiceScreenVolumeIndicatorY = 15 + (xo * 3 / 4) + 30; + voiceScreenVolumeIndicatorW = width - voiceScreenVolumeIndicatorX - 4; + voiceScreenVolumeIndicatorH = 23; + + drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 5, 0x66DD66); + + drawRect(-90, 42, 2, 52, 0xFFAAAAAA); + drawRect(-89, 43, 1, 51, 0xFF222222); + + float vol = VoiceClientController.getVoiceListenVolume(); + drawRect(-89, 43, -89 + (int)(vol * 90), 51, 0xFF993322); + + for(float f = 0.07f; f < vol; f += 0.08f) { + int ww = (int)(f * 90); + drawRect(-89 + ww, 43, -89 + ww + 1, 51, 0xFF999999); + } + + drawRect(-90, 57, 2, 67, 0xFFAAAAAA); + drawRect(-89, 58, 1, 66, 0xFF222222); + + vol = VoiceClientController.getVoiceSpeakVolume(); + drawRect(-89, 58, -89 + (int)(vol * 90), 66, 0xFF993322); + + for(float f = 0.07f; f < vol; f += 0.08f) { + int ww = (int)(f * 90); + drawRect(-89 + ww, 58, -89 + ww + 1, 66, 0xFF999999); + } + + mc.getTextureManager().bindTexture(voiceGuiIcons); + GlStateManager.color(0.7f, 0.7f, 0.7f, 1.0f); + + GlStateManager.pushMatrix(); + GlStateManager.translate(-104.0f, 41.5f, 0.0f); + GlStateManager.scale(0.7f, 0.7f, 0.7f); + drawTexturedModalRect(0, 0, 64, 144, 16, 16); + GlStateManager.popMatrix(); + + GlStateManager.pushMatrix(); + GlStateManager.translate(-104.0f, 56.5f, 0.0f); + GlStateManager.scale(0.7f, 0.7f, 0.7f); + if((mc.currentScreen == null || !mc.currentScreen.blockPTTKey()) && Keyboard.isKeyDown(mc.gameSettings.voicePTTKey)) { + GlStateManager.color(0.9f, 0.4f, 0.4f, 1.0f); + drawTexturedModalRect(0, 0, 64, 64, 16, 16); + }else { + drawTexturedModalRect(0, 0, 64, 32, 16, 16); + } + GlStateManager.popMatrix(); + + txt = I18n.format("voice.ptt", Keyboard.getKeyName(mc.gameSettings.voicePTTKey)); + drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt) - 10, 76, 0x66DD66); + + mc.getTextureManager().bindTexture(voiceGuiIcons); + GlStateManager.color(0.4f, 0.9f, 0.4f, 1.0f); + GlStateManager.pushMatrix(); + GlStateManager.translate(-7.0f, 74.5f, 0.0f); + GlStateManager.scale(0.35f, 0.35f, 0.35f); + drawTexturedModalRect(0, 0, 32, 224, 32, 32); + GlStateManager.popMatrix(); + + txt = I18n.format("voice.playersListening"); + + GlStateManager.pushMatrix(); + GlStateManager.translate(0.0f, 98.0f, 0.0f); + GlStateManager.scale(1.2f, 1.2f, 1.2f); + drawString(fontRendererObj, txt, -fontRendererObj.getStringWidth(txt), 0, 0xFF7777); + GlStateManager.popMatrix(); + + List playersToRender = VoiceClientController.getVoiceRecent(); + + if(playersToRender.size() > 0) { + EaglercraftUUID uuid; + Set playersSpeaking = VoiceClientController.getVoiceSpeaking(); + Set playersMuted = VoiceClientController.getVoiceMuted(); + for(int i = 0, l = playersToRender.size(); i < l; ++i) { + uuid = playersToRender.get(i); + txt = VoiceClientController.getVoiceUsername(uuid); + + boolean muted = playersMuted.contains(uuid); + boolean speaking = !muted && playersSpeaking.contains(uuid); + + int mhy = voiceScreenVolumeIndicatorY + voiceScreenVolumeIndicatorH + 33 + i * 9; + boolean hovered = mx >= voiceScreenVolumeIndicatorX - 3 && my >= mhy && mx < voiceScreenVolumeIndicatorX + voiceScreenVolumeIndicatorW + 2 && my < mhy + 9; + float cm = hovered ? 1.5f : 1.0f; + mc.getTextureManager().bindTexture(voiceGuiIcons); + + GlStateManager.pushMatrix(); + GlStateManager.translate(-100.0f, 115.0f + i * 12.0f, 0.0f); + GlStateManager.scale(0.78f, 0.78f, 0.78f); + + if(muted) { + GlStateManager.color(1.0f * cm, 0.2f * cm, 0.2f * cm, 1.0f); + drawTexturedModalRect(0, 0, 64, 208, 16, 16); + }else if(speaking) { + GlStateManager.color(1.0f * cm, 1.0f * cm, 1.0f * cm, 1.0f); + drawTexturedModalRect(0, 0, 64, 176, 16, 16); + }else { + GlStateManager.color(0.65f * cm, 0.65f * cm, 0.65f * cm, 1.0f); + drawTexturedModalRect(0, 0, 64, 144, 16, 16); + } + + GlStateManager.popMatrix(); + + if(muted) { + drawString(fontRendererObj, txt, -84, 117 + i * 12, attenuate(0xCC4444, cm)); + }else if(speaking) { + drawString(fontRendererObj, txt, -84, 117 + i * 12, attenuate(0xCCCCCC, cm)); + }else { + drawString(fontRendererObj, txt, -84, 117 + i * 12, attenuate(0x999999, cm)); + } + + } + }else { + txt = "(none)"; + drawString(fontRendererObj, txt, -fontRendererObj.getStringWidth(txt), 112, 0xAAAAAA); + } + + }else if(status == EnumVoiceChannelStatus.CONNECTING) { + float fadeTimer = MathHelper.sin((float)((System.currentTimeMillis() % 700l) * 0.0014d) * 3.14159f) * 0.35f + 0.3f; + txt = I18n.format("voice.connecting"); + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 5, (0xFFDD77 | ((int)(Math.pow(fadeTimer, 1.0d / 2.2d) * 255.0f) << 24))); + GlStateManager.disableBlend(); + }else if(status == EnumVoiceChannelStatus.UNAVAILABLE) { + txt = I18n.format("voice.unavailable"); + drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 5, 0xFF3333); + }else { + flag = true; + } + } + + if(flag) { + txt = I18n.format("voice.notConnected"); + drawString(fontRendererObj, txt, 1 - fontRendererObj.getStringWidth(txt), 5, 0xBB9999); + } + + String OFFstring = I18n.format("voice.off"); + String RADIUSstring = I18n.format("voice.radius"); + String GLOBALstring = I18n.format("voice.global"); + + int OFFwidth = fontRendererObj.getStringWidth(OFFstring); + int RADIUSwidth = fontRendererObj.getStringWidth(RADIUSstring); + int GLOBALwidth = fontRendererObj.getStringWidth(GLOBALstring); + + voiceButtonOFFposX = 0 - OFFwidth - 8 - RADIUSwidth - 8 - GLOBALwidth; + voiceButtonOFFposY = 20; + voiceButtonOFFposW = OFFwidth + 5; + voiceButtonOFFposH = 15; + + voiceScreenButtonOFFposX = voiceButtonOFFposX * 3 / 4 + width - 6; + voiceScreenButtonOFFposY = 15 + (voiceButtonOFFposY + xo) * 3 / 4; + voiceScreenButtonOFFposW = voiceButtonOFFposW * 3 / 4; + voiceScreenButtonOFFposH = voiceButtonOFFposH * 3 / 4; + + voiceButtonRADIUSposX = 0 - RADIUSwidth - 8 - GLOBALwidth; + voiceButtonRADIUSposY = 20; + voiceButtonRADIUSposW = RADIUSwidth + 5; + voiceButtonRADIUSposH = 15; + + voiceScreenButtonRADIUSposX = voiceButtonRADIUSposX * 3 / 4 + width - 6; + voiceScreenButtonRADIUSposY = 15 + (voiceButtonRADIUSposY + xo) * 3 / 4; + voiceScreenButtonRADIUSposW = voiceButtonRADIUSposW * 3 / 4; + voiceScreenButtonRADIUSposH = voiceButtonRADIUSposH * 3 / 4; + + voiceButtonGLOBALposX = 0 - GLOBALwidth; + voiceButtonGLOBALposY = 20; + voiceButtonGLOBALposW = GLOBALwidth + 5; + voiceButtonGLOBALposH = 15; + + voiceScreenButtonGLOBALposX = voiceButtonGLOBALposX * 3 / 4 + width - 6; + voiceScreenButtonGLOBALposY = 15 + (voiceButtonGLOBALposY + xo) * 3 / 4; + voiceScreenButtonGLOBALposW = voiceButtonGLOBALposW * 3 / 4; + voiceScreenButtonGLOBALposH = voiceButtonGLOBALposH * 3 / 4; + + if(channel == EnumVoiceChannelType.NONE) { + drawOutline(voiceButtonOFFposX, voiceButtonOFFposY, voiceButtonOFFposW, voiceButtonOFFposH, 0xFFCCCCCC); + drawRect(voiceButtonOFFposX + 1, voiceButtonOFFposY + 1, voiceButtonOFFposX + voiceButtonOFFposW - 2, + voiceButtonOFFposY + voiceButtonOFFposH - 1, 0xFF222222); + }else if(mx >= voiceScreenButtonOFFposX && my >= voiceScreenButtonOFFposY && mx < voiceScreenButtonOFFposX + + voiceScreenButtonOFFposW && my < voiceScreenButtonOFFposY + voiceScreenButtonOFFposH) { + drawOutline(voiceButtonOFFposX, voiceButtonOFFposY, voiceButtonOFFposW, voiceButtonOFFposH, 0xFF777777); + } + + if(channel == EnumVoiceChannelType.PROXIMITY) { + drawOutline(voiceButtonRADIUSposX, voiceButtonRADIUSposY, voiceButtonRADIUSposW, voiceButtonRADIUSposH, 0xFFCCCCCC); + drawRect(voiceButtonRADIUSposX + 1, voiceButtonRADIUSposY + 1, voiceButtonRADIUSposX + voiceButtonRADIUSposW - 2, + voiceButtonRADIUSposY + voiceButtonRADIUSposH - 1, 0xFF222222); + }else if(mx >= voiceScreenButtonRADIUSposX && my >= voiceScreenButtonRADIUSposY && mx < voiceScreenButtonRADIUSposX + + voiceScreenButtonRADIUSposW && my < voiceScreenButtonRADIUSposY + voiceScreenButtonRADIUSposH) { + drawOutline(voiceButtonRADIUSposX, voiceButtonRADIUSposY, voiceButtonRADIUSposW, voiceButtonRADIUSposH, 0xFF777777); + } + + if(channel == EnumVoiceChannelType.GLOBAL) { + drawOutline(voiceButtonGLOBALposX, voiceButtonGLOBALposY, voiceButtonGLOBALposW, voiceButtonGLOBALposH, 0xFFCCCCCC); + drawRect(voiceButtonGLOBALposX + 1, voiceButtonGLOBALposY + 1, voiceButtonGLOBALposX + voiceButtonGLOBALposW - 2, + voiceButtonGLOBALposY + voiceButtonGLOBALposH - 1, 0xFF222222); + }else if(mx >= voiceScreenButtonGLOBALposX && my >= voiceScreenButtonGLOBALposY && mx < voiceScreenButtonGLOBALposX + + voiceScreenButtonGLOBALposW && my < voiceScreenButtonGLOBALposY + voiceScreenButtonGLOBALposH) { + drawOutline(voiceButtonGLOBALposX, voiceButtonGLOBALposY, voiceButtonGLOBALposW, voiceButtonGLOBALposH, 0xFF777777); + } + + int enabledColor = (status == EnumVoiceChannelStatus.CONNECTED || channel == EnumVoiceChannelType.NONE) ? 0x66DD66 : 0xDDCC66; + int disabledColor = 0xDD4444; + + if(channel != EnumVoiceChannelType.NONE && status == EnumVoiceChannelStatus.UNAVAILABLE) { + enabledColor = disabledColor; + } + + drawString(fontRendererObj, OFFstring, 3 - OFFwidth - 8 - RADIUSwidth - 8 - GLOBALwidth, 24, channel == EnumVoiceChannelType.NONE ? enabledColor : disabledColor); + drawString(fontRendererObj, RADIUSstring, 3 - RADIUSwidth - 8 - GLOBALwidth, 24, channel == EnumVoiceChannelType.PROXIMITY ? enabledColor : disabledColor); + drawString(fontRendererObj, GLOBALstring, 3 - GLOBALwidth, 24, channel == EnumVoiceChannelType.GLOBAL ? enabledColor : disabledColor); + + GlStateManager.popMatrix(); + + if(showingCompatWarning) { + + drawNotice(I18n.format("voice.unsupportedWarning1"), false, I18n.format("voice.unsupportedWarning2"), I18n.format("voice.unsupportedWarning3"), + "", I18n.format("voice.unsupportedWarning4"), I18n.format("voice.unsupportedWarning5"), I18n.format("voice.unsupportedWarning6"), + I18n.format("voice.unsupportedWarning7"), I18n.format("voice.unsupportedWarning8"), I18n.format("voice.unsupportedWarning9")); + + noticeContinueButton.visible = true; + noticeCancelButton.visible = false; + }else if(showingTrackingWarning) { + + drawNotice(I18n.format("voice.ipGrabWarning1"), true, I18n.format("voice.ipGrabWarning2"), I18n.format("voice.ipGrabWarning3"), + I18n.format("voice.ipGrabWarning4"), "", I18n.format("voice.ipGrabWarning5"), I18n.format("voice.ipGrabWarning6"), + I18n.format("voice.ipGrabWarning7"), I18n.format("voice.ipGrabWarning8"), I18n.format("voice.ipGrabWarning9"), + I18n.format("voice.ipGrabWarning10"), I18n.format("voice.ipGrabWarning11"), I18n.format("voice.ipGrabWarning12")); + + noticeContinueButton.visible = true; + noticeCancelButton.visible = true; + }else { + noticeContinueButton.visible = false; + noticeCancelButton.visible = false; + } + + drawButtons(mx, my, partialTicks); + + if(showingCompatWarning || showingTrackingWarning) { + throw new AbortedException(); + } + } + + private void drawNotice(String title, boolean showCancel, String... lines) { + + int widthAccum = 0; + + for(int i = 0; i < lines.length; ++i) { + int w = fontRendererObj.getStringWidth(lines[i]); + if(widthAccum < w) { + widthAccum = w; + } + } + + int margin = 15; + + int x = (width - widthAccum) / 2; + int y = (height - lines.length * 10 - 60 - margin) / 2; + + drawRect(x - margin - 1, y - margin - 1, x + widthAccum + margin + 1, + y + lines.length * 10 + 49 + margin, 0xFFCCCCCC); + drawRect(x - margin, y - margin, x + widthAccum + margin, + y + lines.length * 10 + 48 + margin, 0xFF111111); + + drawCenteredString(fontRendererObj, EnumChatFormatting.BOLD + title, width / 2, y, 0xFF7766); + + for(int i = 0; i < lines.length; ++i) { + drawString(fontRendererObj, lines[i], x, y + i * 10 + 18, 0xDDAAAA); + } + + if(!showCancel) { + noticeContinueButton.width = 150; + noticeContinueButton.xPosition = (width - 150) / 2; + noticeContinueButton.yPosition = y + lines.length * 10 + 29; + }else { + noticeContinueButton.width = widthAccum / 2 - 10; + noticeContinueButton.xPosition = (width - widthAccum) / 2 + widthAccum / 2 + 3; + noticeContinueButton.yPosition = y + lines.length * 10 + 28; + noticeCancelButton.width = widthAccum / 2 - 10; + noticeCancelButton.xPosition = (width - widthAccum) / 2 + 4; + noticeCancelButton.yPosition = y + lines.length * 10 + 28; + } + + } + + public static int attenuate(int cin, float f) { + return attenuate(cin, f, f, f, 1.0f); + } + + public static int attenuate(int cin, float r, float g, float b, float a) { + float var10 = (float) (cin >> 24 & 255) / 255.0F; + float var6 = (float) (cin >> 16 & 255) / 255.0F; + float var7 = (float) (cin >> 8 & 255) / 255.0F; + float var8 = (float) (cin & 255) / 255.0F; + var10 *= a; + var6 *= r; + var7 *= g; + var8 *= b; + if(var10 > 1.0f) { + var10 = 1.0f; + } + if(var6 > 1.0f) { + var6 = 1.0f; + } + if(var7 > 1.0f) { + var7 = 1.0f; + } + if(var8 > 1.0f) { + var8 = 1.0f; + } + return (((int)(var10 * 255.0f) << 24) | ((int)(var6 * 255.0f) << 16) | ((int)(var7 * 255.0f) << 8) | (int)(var8 * 255.0f)); + } + + private void drawOutline(int x, int y, int w, int h, int color) { + drawRect(x, y, x + w, y + 1, color); + drawRect(x + w - 1, y + 1, x + w, y + h - 1, color); + drawRect(x, y + h - 1, x + w, y + h, color); + drawRect(x, y + 1, x + 1, y + h - 1, color); + } + + public void mouseReleased(int par1, int par2, int par3) { + applyRadiusButton.mouseReleased(par1, par2); + applyVolumeButton.mouseReleased(par1, par2); + noticeContinueButton.mouseReleased(par1, par2); + noticeCancelButton.mouseReleased(par1, par2); + if(showSliderBlocks || showSliderVolume) { + if(showSliderBlocks) { + if(par3 == 0) { + sliderBlocks.mouseReleased(par1, par2); + } + }else if(showSliderVolume) { + if(par3 == 0) { + sliderListenVolume.mouseReleased(par1, par2); + sliderSpeakVolume.mouseReleased(par1, par2); + } + } + throw new AbortedException(); + } + } + + public void keyTyped(char par1, int par2) { + if(showSliderBlocks || showSliderVolume || showPTTKeyConfig) { + if(showPTTKeyConfig) { + if(par2 == 1) { + showPTTKeyConfig = false; + }else { + mc.gameSettings.voicePTTKey = par2; + showNewPTTKey = 10; + } + } + throw new AbortedException(); + } + } + + public void mouseClicked(int mx, int my, int button) { + if(showSliderBlocks || showSliderVolume || showPTTKeyConfig || showingCompatWarning || showingTrackingWarning) { + if(showSliderBlocks) { + sliderBlocks.mousePressed(mc, mx, my); + }else if(showSliderVolume) { + sliderListenVolume.mousePressed(mc, mx, my); + sliderSpeakVolume.mousePressed(mc, mx, my); + } + if(button == 0) { + if(applyRadiusButton.mousePressed(mc, mx, my)) actionPerformed(applyRadiusButton); + if(applyVolumeButton.mousePressed(mc, mx, my)) actionPerformed(applyVolumeButton); + if(noticeContinueButton.mousePressed(mc, mx, my)) actionPerformed(noticeContinueButton); + if(noticeCancelButton.mousePressed(mc, mx, my)) actionPerformed(noticeCancelButton); + } + throw new AbortedException(); + } + + EnumVoiceChannelStatus status = VoiceClientController.getVoiceStatus(); + EnumVoiceChannelType channel = VoiceClientController.getVoiceChannel(); + + if(button == 0) { + if(VoiceClientController.isSupported()) { + if(mx >= voiceScreenButtonOFFposX && my >= voiceScreenButtonOFFposY && mx < voiceScreenButtonOFFposX + + voiceScreenButtonOFFposW && my < voiceScreenButtonOFFposY + voiceScreenButtonOFFposH) { + VoiceClientController.setVoiceChannel(EnumVoiceChannelType.NONE); + this.mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + }else if(mx >= voiceScreenButtonRADIUSposX && my >= voiceScreenButtonRADIUSposY && mx < voiceScreenButtonRADIUSposX + + voiceScreenButtonRADIUSposW && my < voiceScreenButtonRADIUSposY + voiceScreenButtonRADIUSposH) { + + if(showCompatWarning) { + continueChannel = EnumVoiceChannelType.PROXIMITY; + showingCompatWarning = true; + }else if(showTrackingWarning) { + continueChannel = EnumVoiceChannelType.PROXIMITY; + showingTrackingWarning = true; + }else { + VoiceClientController.setVoiceChannel(EnumVoiceChannelType.PROXIMITY); + } + + this.mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + + }else if(mx >= voiceScreenButtonGLOBALposX && my >= voiceScreenButtonGLOBALposY && mx < voiceScreenButtonGLOBALposX + + voiceScreenButtonGLOBALposW && my < voiceScreenButtonGLOBALposY + voiceScreenButtonGLOBALposH) { + + if(showCompatWarning) { + continueChannel = EnumVoiceChannelType.GLOBAL; + showingCompatWarning = true; + }else if(showTrackingWarning) { + continueChannel = EnumVoiceChannelType.GLOBAL; + showingTrackingWarning = true; + }else { + VoiceClientController.setVoiceChannel(EnumVoiceChannelType.GLOBAL); + } + + this.mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + + this.mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + }else if(channel == EnumVoiceChannelType.PROXIMITY && status == EnumVoiceChannelStatus.CONNECTED && mx >= voiceScreenButtonChangeRadiusposX && + my >= voiceScreenButtonChangeRadiusposY && mx < voiceScreenButtonChangeRadiusposX + voiceScreenButtonChangeRadiusposW && + my < voiceScreenButtonChangeRadiusposY + voiceScreenButtonChangeRadiusposH) { + showSliderBlocks = true; + sliderBlocks.sliderValue = (VoiceClientController.getVoiceProximity() - 5) / 17.0f; + this.mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + }else if(status == EnumVoiceChannelStatus.CONNECTED && channel != EnumVoiceChannelType.NONE && mx >= voiceScreenVolumeIndicatorX && + my >= voiceScreenVolumeIndicatorY && mx < voiceScreenVolumeIndicatorX + voiceScreenVolumeIndicatorW && + my < voiceScreenVolumeIndicatorY + voiceScreenVolumeIndicatorH) { + showSliderVolume = true; + sliderListenVolume.sliderValue = VoiceClientController.getVoiceListenVolume(); + sliderSpeakVolume.sliderValue = VoiceClientController.getVoiceSpeakVolume(); + this.mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + }else if(status == EnumVoiceChannelStatus.CONNECTED && channel != EnumVoiceChannelType.NONE && mx >= voiceScreenVolumeIndicatorX - 1 && + my >= voiceScreenVolumeIndicatorY + voiceScreenVolumeIndicatorH + 2 && mx < voiceScreenVolumeIndicatorX + voiceScreenVolumeIndicatorW + 2 && + my < voiceScreenVolumeIndicatorY + voiceScreenVolumeIndicatorH + 12) { + showPTTKeyConfig = true; + this.mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + }else if(status == EnumVoiceChannelStatus.CONNECTED) { + List playersToRender = VoiceClientController.getVoiceRecent(); + if(playersToRender.size() > 0) { + Set playersMuted = VoiceClientController.getVoiceMuted(); + for(int i = 0, l = playersToRender.size(); i < l; ++i) { + EaglercraftUUID uuid = playersToRender.get(i); + String txt = VoiceClientController.getVoiceUsername(uuid); + boolean muted = playersMuted.contains(uuid); + int mhy = voiceScreenVolumeIndicatorY + voiceScreenVolumeIndicatorH + 33 + i * 9; + if(mx >= voiceScreenVolumeIndicatorX - 3 && my >= mhy && mx < voiceScreenVolumeIndicatorX + voiceScreenVolumeIndicatorW + 2 && my < mhy + 9) { + VoiceClientController.setVoiceMuted(uuid, !muted); + this.mc.getSoundHandler().playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); + break; + } + } + } + } + } + } + + } + + private void actionPerformed(GuiButton btn) { + if(btn.id == 2) { + showSliderBlocks = false; + VoiceClientController.setVoiceProximity(mc.gameSettings.voiceListenRadius = (int)((sliderBlocks.sliderValue * 17.0f) + 5.0f)); + mc.gameSettings.saveOptions(); + }else if(btn.id == 3) { + showSliderVolume = false; + VoiceClientController.setVoiceListenVolume(mc.gameSettings.voiceListenVolume = sliderListenVolume.sliderValue); + VoiceClientController.setVoiceSpeakVolume(mc.gameSettings.voiceSpeakVolume = sliderSpeakVolume.sliderValue); + mc.gameSettings.saveOptions(); + }else if(btn.id == 4) { + showPTTKeyConfig = false; + mc.gameSettings.saveOptions(); + }else if(btn.id == 5) { + if(showingCompatWarning) { + showingCompatWarning = false; + showCompatWarning = false; + if(showTrackingWarning) { + showingTrackingWarning = true; + }else { + VoiceClientController.setVoiceChannel(continueChannel); + } + }else if(showingTrackingWarning) { + showingTrackingWarning = false; + showTrackingWarning = false; + VoiceClientController.setVoiceChannel(continueChannel); + } + }else if(btn.id == 6) { + if(showingTrackingWarning) { + showingTrackingWarning = false; + VoiceClientController.setVoiceChannel(EnumVoiceChannelType.NONE); + } + } + } + + public void updateScreen() { + if(showNewPTTKey > 0) { + --showNewPTTKey; + if(showNewPTTKey == 0) { + showPTTKeyConfig = false; + mc.gameSettings.saveOptions(); + } + } + } + + public boolean isBlockingInput() { + return showSliderBlocks || showSliderVolume || showPTTKeyConfig || showingCompatWarning || showingTrackingWarning; + } + +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/GuiVoiceOverlay.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/GuiVoiceOverlay.java new file mode 100644 index 0000000..8d0e639 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/GuiVoiceOverlay.java @@ -0,0 +1,258 @@ +package net.lax1dude.eaglercraft.v1_8.voice; + +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.GL_GREATER; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.gui.GuiChat; +import net.minecraft.client.gui.GuiIngameMenu; +import net.minecraft.util.ResourceLocation; + +/** + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * 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 GuiVoiceOverlay extends Gui { + + public final Minecraft mc; + public int width; + public int height; + + private long pttTimer = 0l; + + public GuiVoiceOverlay(Minecraft mc) { + this.mc = mc; + } + + public void setResolution(int w, int h) { + this.width = w; + this.height = h; + } + + private static final ResourceLocation voiceGuiIcons = new ResourceLocation("eagler:gui/eagler_gui.png"); + + public void drawOverlay() { + if(mc.theWorld != null && VoiceClientController.getVoiceStatus() == EnumVoiceChannelStatus.CONNECTED && VoiceClientController.getVoiceChannel() != EnumVoiceChannelType.NONE && + !(mc.currentScreen != null && (mc.currentScreen instanceof GuiIngameMenu))) { + + if(mc.currentScreen != null && mc.currentScreen.doesGuiPauseGame()) { + return; + } + + GlStateManager.disableLighting(); + GlStateManager.disableBlend(); + GlStateManager.enableAlpha(); + GlStateManager.alphaFunc(GL_GREATER, 0.1F); + GlStateManager.pushMatrix(); + + if(mc.currentScreen == null || (mc.currentScreen instanceof GuiChat)) { + GlStateManager.translate(width / 2 + 77, height - 56, 0.0f); + if(mc.thePlayer == null || mc.thePlayer.capabilities.isCreativeMode) { + GlStateManager.translate(0.0f, 16.0f, 0.0f); + } + }else { + GlStateManager.translate(width / 2 + 10, 4, 0.0f); + } + + GlStateManager.scale(0.75f, 0.75f, 0.75f); + + String txxt = "press '" + Keyboard.getKeyName(mc.gameSettings.voicePTTKey) + "'"; + drawString(mc.fontRendererObj, txxt, -3 - mc.fontRendererObj.getStringWidth(txxt), 9, 0xDDDDDD); + + GlStateManager.scale(0.66f, 0.66f, 0.66f); + + mc.getTextureManager().bindTexture(voiceGuiIcons); + + if((mc.currentScreen == null || !mc.currentScreen.blockPTTKey()) && Keyboard.isKeyDown(mc.gameSettings.voicePTTKey)) { + long millis = System.currentTimeMillis(); + if(pttTimer == 0l) { + pttTimer = millis; + } + GlStateManager.color(0.2f, 0.2f, 0.2f, 1.0f); + drawTexturedModalRect(0, 0, 0, 64, 32, 32); + GlStateManager.translate(-1.5f, -1.5f, 0.0f); + if(millis - pttTimer < 1050l) { + if((millis - pttTimer) % 300l < 150l) { + GlStateManager.color(0.9f, 0.2f, 0.2f, 1.0f); + }else { + GlStateManager.color(0.9f, 0.7f, 0.7f, 1.0f); + } + }else { + GlStateManager.color(0.9f, 0.3f, 0.3f, 1.0f); + } + drawTexturedModalRect(0, 0, 0, 64, 32, 32); + }else { + pttTimer = 0l; + GlStateManager.color(0.2f, 0.2f, 0.2f, 1.0f); + drawTexturedModalRect(0, 0, 0, 32, 32, 32); + GlStateManager.translate(-1.5f, -1.5f, 0.0f); + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + drawTexturedModalRect(0, 0, 0, 32, 32, 32); + GlStateManager.translate(-0.5f, -0.5f, 0.0f); + drawTexturedModalRect(0, 0, 0, 32, 32, 32); + } + + GlStateManager.popMatrix(); + + if(VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.PROXIMITY) { + Set listeners = VoiceClientController.getVoiceListening(); + if(listeners.size() > 0) { + Set speakers = VoiceClientController.getVoiceSpeaking(); + Set muted = VoiceClientController.getVoiceMuted(); + + List listenerList = new ArrayList(); + listenerList.addAll(listeners); + listenerList.removeAll(muted); + + while(listenerList.size() > 5) { + boolean flag = false; + for(int i = 0, l = listenerList.size(); i < l; ++i) { + if(!speakers.contains(listenerList.get(i))) { + listenerList.remove(i); + flag = true; + break; + } + } + if(!flag) { + break; + } + } + + int more = listenerList.size() - 5; + + int ww = width; + int hh = height; + + if(mc.currentScreen != null && (mc.currentScreen instanceof GuiChat)) { + hh -= 15; + } + + List listenerListStr = new ArrayList(Math.min(5, listenerList.size())); + + int left = 50; + for(int i = 0, l = listenerList.size(); i < l && i < 5; ++i) { + String txt = VoiceClientController.getVoiceUsername(listenerList.get(i)); + listenerListStr.add(txt); + int j = mc.fontRendererObj.getStringWidth(txt) + 4; + if(j > left) { + left = j; + } + } + + if(more > 0) { + GlStateManager.pushMatrix(); + GlStateManager.translate(ww - left + 3, hh - 10, left); + GlStateManager.scale(0.75f, 0.75f, 0.75f); + drawString(mc.fontRendererObj, "(" + more + " more)", 0, 0, 0xBBBBBB); + GlStateManager.popMatrix(); + hh -= 9; + } + + for(int i = 0, l = listenerList.size(); i < l && i < 5; ++i) { + boolean speaking = speakers.contains(listenerList.get(i)); + float speakf = speaking ? 1.0f : 0.75f; + + drawString(mc.fontRendererObj, listenerListStr.get(i), ww - left, hh - 13 - i * 11, speaking ? 0xEEEEEE : 0xBBBBBB); + + mc.getTextureManager().bindTexture(voiceGuiIcons); + + GlStateManager.pushMatrix(); + GlStateManager.translate(ww - left - 14, hh - 14 - i * 11, 0.0f); + + GlStateManager.scale(0.75f, 0.75f, 0.75f); + GlStateManager.color(speakf * 0.2f, speakf * 0.2f, speakf * 0.2f, 1.0f); + drawTexturedModalRect(0, 0, 64, speaking ? 176 : 208, 16, 16); + GlStateManager.translate(0.25f, 0.25f, 0.0f); + drawTexturedModalRect(0, 0, 64, speaking ? 176 : 208, 16, 16); + + GlStateManager.translate(-1.25f, -1.25f, 0.0f); + GlStateManager.color(speakf, speakf, speakf, 1.0f); + drawTexturedModalRect(0, 0, 64, speaking ? 176 : 208, 16, 16); + + GlStateManager.popMatrix(); + + } + + } + }else if(VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.GLOBAL) { + Set speakers = VoiceClientController.getVoiceSpeaking(); + Set muted = VoiceClientController.getVoiceMuted(); + + List listenerList = new ArrayList(); + listenerList.addAll(speakers); + listenerList.removeAll(muted); + + int more = listenerList.size() - 5; + + int ww = width; + int hh = height; + + if(mc.currentScreen != null && (mc.currentScreen instanceof GuiChat)) { + hh -= 15; + } + + List listenerListStr = new ArrayList(Math.min(5, listenerList.size())); + + int left = 50; + for(int i = 0, l = listenerList.size(); i < l && i < 5; ++i) { + String txt = VoiceClientController.getVoiceUsername(listenerList.get(i)); + listenerListStr.add(txt); + int j = mc.fontRendererObj.getStringWidth(txt) + 4; + if(j > left) { + left = j; + } + } + + if(more > 0) { + GlStateManager.pushMatrix(); + GlStateManager.translate(ww - left + 3, hh - 10, left); + GlStateManager.scale(0.75f, 0.75f, 0.75f); + drawString(mc.fontRendererObj, "(" + more + " more)", 0, 0, 0xBBBBBB); + GlStateManager.popMatrix(); + hh -= 9; + } + + for(int i = 0, l = listenerList.size(); i < l && i < 5; ++i) { + drawString(mc.fontRendererObj, listenerListStr.get(i), ww - left, hh - 13 - i * 11, 0xEEEEEE); + + mc.getTextureManager().bindTexture(voiceGuiIcons); + + GlStateManager.pushMatrix(); + GlStateManager.translate(ww - left - 14, hh - 14 - i * 11, 0.0f); + + GlStateManager.scale(0.75f, 0.75f, 0.75f); + GlStateManager.color(0.2f, 0.2f, 0.2f, 1.0f); + drawTexturedModalRect(0, 0, 64, 176, 16, 16); + GlStateManager.translate(0.25f, 0.25f, 0.0f); + drawTexturedModalRect(0, 0, 64, 176, 16, 16); + + GlStateManager.translate(-1.25f, -1.25f, 0.0f); + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + drawTexturedModalRect(0, 0, 64, 176, 16, 16); + + GlStateManager.popMatrix(); + + } + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceClientController.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceClientController.java new file mode 100644 index 0000000..08be439 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceClientController.java @@ -0,0 +1,367 @@ +package net.lax1dude.eaglercraft.v1_8.voice; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformVoiceClient; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.network.PacketBuffer; + +/** + * Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * 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 VoiceClientController { + + public static final String SIGNAL_CHANNEL = "EAG|Voice-1.8"; + + static final Logger logger = LogManager.getLogger("VoiceClientController"); + + private static boolean clientSupport = false; + private static boolean serverSupport = false; + private static Consumer packetSendCallback = null; + private static EnumVoiceChannelType voiceChannel = EnumVoiceChannelType.NONE; + private static final HashSet nearbyPlayers = new HashSet<>(); + private static final ExpiringSet recentlyNearbyPlayers = new ExpiringSet<>(5000, uuid -> { + if (!nearbyPlayers.contains(uuid)) { + PlatformVoiceClient.signalDisconnect(uuid, false); + } + }); + private static final Map uuidToNameLookup = new HashMap<>(256); + + public static boolean isSupported() { + return isClientSupported() && isServerSupported(); + } + + private static boolean checked = false; + + public static boolean isClientSupported() { + if (!checked) { + checked = true; + clientSupport = EagRuntime.getConfiguration().isAllowVoiceClient() && PlatformVoiceClient.isSupported(); + } + return clientSupport; + } + + public static boolean isServerSupported() { + return serverSupport; + } + + public static void initializeVoiceClient(Consumer signalSendCallbackIn) { + packetSendCallback = signalSendCallbackIn; + uuidToNameLookup.clear(); + if (getVoiceChannel() != EnumVoiceChannelType.NONE) sendInitialVoice(); + } + + public static void handleVoiceSignalPacket(PacketBuffer packetData) { + VoiceSignalPackets.handleVoiceSignal(packetData); + } + + static void handleVoiceSignalPacketTypeGlobal(EaglercraftUUID[] voicePlayers, String[] voiceNames) { + uuidToNameLookup.clear(); + for (int i = 0; i < voicePlayers.length; i++) { + if(voiceNames != null) { + uuidToNameLookup.put(voicePlayers[i], voiceNames[i]); + } + sendPacketRequestIfNeeded(voicePlayers[i]); + } + } + + public static void handleServerDisconnect() { + if(!isClientSupported()) return; + serverSupport = false; + uuidToNameLookup.clear(); + for (EaglercraftUUID uuid : nearbyPlayers) { + PlatformVoiceClient.signalDisconnect(uuid, false); + } + for (EaglercraftUUID uuid : recentlyNearbyPlayers) { + PlatformVoiceClient.signalDisconnect(uuid, false); + } + nearbyPlayers.clear(); + recentlyNearbyPlayers.clear(); + Set antiConcurrentModificationUUIDs = new HashSet<>(listeningSet); + for (EaglercraftUUID uuid : antiConcurrentModificationUUIDs) { + PlatformVoiceClient.signalDisconnect(uuid, false); + } + activateVoice(false); + } + + static void handleVoiceSignalPacketTypeAllowed(boolean voiceAvailableStat, String[] servs) { + serverSupport = voiceAvailableStat; + PlatformVoiceClient.setICEServers(servs); + if(isSupported()) { + EnumVoiceChannelType ch = getVoiceChannel(); + setVoiceChannel(EnumVoiceChannelType.NONE); + setVoiceChannel(ch); + } + } + + static void handleVoiceSignalPacketTypeConnect(EaglercraftUUID user, boolean offer) { + PlatformVoiceClient.signalConnect(user, offer); + } + + static void handleVoiceSignalPacketTypeConnectAnnounce(EaglercraftUUID user) { + sendPacketRequest(user); + } + + static void handleVoiceSignalPacketTypeDisconnect(EaglercraftUUID user) { + PlatformVoiceClient.signalDisconnect(user, true); + } + + static void handleVoiceSignalPacketTypeICECandidate(EaglercraftUUID user, String ice) { + PlatformVoiceClient.signalICECandidate(user, ice); + } + + static void handleVoiceSignalPacketTypeDescription(EaglercraftUUID user, String desc) { + PlatformVoiceClient.signalDescription(user, desc); + } + + public static void tickVoiceClient(Minecraft mc) { + if(!isClientSupported()) return; + recentlyNearbyPlayers.checkForExpirations(); + speakingSet.clear(); + PlatformVoiceClient.tickVoiceClient(); + + if (getVoiceChannel() != EnumVoiceChannelType.NONE && (getVoiceStatus() == EnumVoiceChannelStatus.CONNECTING || getVoiceStatus() == EnumVoiceChannelStatus.CONNECTED)) { + activateVoice((mc.currentScreen == null || !mc.currentScreen.blockPTTKey()) && Keyboard.isKeyDown(mc.gameSettings.voicePTTKey)); + + if (mc.theWorld != null && mc.thePlayer != null) { + HashSet seenPlayers = new HashSet<>(); + for (EntityPlayer player : mc.theWorld.playerEntities) { + if (player == mc.thePlayer) continue; + if (getVoiceChannel() == EnumVoiceChannelType.PROXIMITY) updateVoicePosition(player.getUniqueID(), player.posX, player.posY + player.getEyeHeight(), player.posZ); + int prox = 22; + // cube + if (Math.abs(mc.thePlayer.posX - player.posX) <= prox && Math.abs(mc.thePlayer.posY - player.posY) <= prox && Math.abs(mc.thePlayer.posZ - player.posZ) <= prox) { + if (!uuidToNameLookup.containsKey(player.getUniqueID())) { + uuidToNameLookup.put(player.getUniqueID(), player.getName()); + } + if (addNearbyPlayer(player.getUniqueID())) { + seenPlayers.add(player.getUniqueID()); + } + } + } + cleanupNearbyPlayers(seenPlayers); + } + } + } + + public static final boolean addNearbyPlayer(EaglercraftUUID uuid) { + recentlyNearbyPlayers.remove(uuid); + if (nearbyPlayers.add(uuid)) { + sendPacketRequestIfNeeded(uuid); + return true; + } + return false; + } + + public static final void removeNearbyPlayer(EaglercraftUUID uuid) { + if (nearbyPlayers.remove(uuid)) { + if (getVoiceStatus() == EnumVoiceChannelStatus.DISCONNECTED || getVoiceStatus() == EnumVoiceChannelStatus.UNAVAILABLE) return; + if (voiceChannel == EnumVoiceChannelType.PROXIMITY) recentlyNearbyPlayers.add(uuid); + } + } + + public static final void cleanupNearbyPlayers(HashSet existingPlayers) { + nearbyPlayers.stream().filter(ud -> !existingPlayers.contains(ud)).collect(Collectors.toSet()).forEach(VoiceClientController::removeNearbyPlayer); + } + + public static final void updateVoicePosition(EaglercraftUUID uuid, double x, double y, double z) { + PlatformVoiceClient.updateVoicePosition(uuid, x, y, z); + } + + public static void setVoiceChannel(EnumVoiceChannelType channel) { + if (voiceChannel == channel) return; + if (channel != EnumVoiceChannelType.NONE) PlatformVoiceClient.initializeDevices(); + PlatformVoiceClient.resetPeerStates(); + if (channel == EnumVoiceChannelType.NONE) { + for (EaglercraftUUID uuid : nearbyPlayers) { + PlatformVoiceClient.signalDisconnect(uuid, false); + } + for (EaglercraftUUID uuid : recentlyNearbyPlayers) { + PlatformVoiceClient.signalDisconnect(uuid, false); + } + nearbyPlayers.clear(); + recentlyNearbyPlayers.clear(); + Set antiConcurrentModificationUUIDs = new HashSet<>(listeningSet); + for (EaglercraftUUID uuid : antiConcurrentModificationUUIDs) { + PlatformVoiceClient.signalDisconnect(uuid, false); + } + sendPacketDisconnect(null); + activateVoice(false); + } else if (voiceChannel == EnumVoiceChannelType.PROXIMITY) { + for (EaglercraftUUID uuid : nearbyPlayers) { + PlatformVoiceClient.signalDisconnect(uuid, false); + } + for (EaglercraftUUID uuid : recentlyNearbyPlayers) { + PlatformVoiceClient.signalDisconnect(uuid, false); + } + nearbyPlayers.clear(); + recentlyNearbyPlayers.clear(); + sendPacketDisconnect(null); + } else if(voiceChannel == EnumVoiceChannelType.GLOBAL) { + Set antiConcurrentModificationUUIDs = new HashSet<>(listeningSet); + antiConcurrentModificationUUIDs.removeAll(nearbyPlayers); + antiConcurrentModificationUUIDs.removeAll(recentlyNearbyPlayers); + for (EaglercraftUUID uuid : antiConcurrentModificationUUIDs) { + PlatformVoiceClient.signalDisconnect(uuid, false); + } + sendPacketDisconnect(null); + } + voiceChannel = channel; + if (channel != EnumVoiceChannelType.NONE) { + sendInitialVoice(); + } + } + + public static void sendInitialVoice() { + sendPacketConnect(); + for (EaglercraftUUID uuid : nearbyPlayers) { + sendPacketRequest(uuid); + } + } + + public static EnumVoiceChannelType getVoiceChannel() { + return voiceChannel; + } + + private static boolean voicePeerErrored() { + return PlatformVoiceClient.getPeerState() == EnumVoiceChannelPeerState.FAILED || PlatformVoiceClient.getPeerStateConnect() == EnumVoiceChannelPeerState.FAILED || PlatformVoiceClient.getPeerStateInitial() == EnumVoiceChannelPeerState.FAILED || PlatformVoiceClient.getPeerStateDesc() == EnumVoiceChannelPeerState.FAILED || PlatformVoiceClient.getPeerStateIce() == EnumVoiceChannelPeerState.FAILED; + } + public static EnumVoiceChannelStatus getVoiceStatus() { + return (!isClientSupported() || !isServerSupported()) ? EnumVoiceChannelStatus.UNAVAILABLE : + (PlatformVoiceClient.getReadyState() != EnumVoiceChannelReadyState.DEVICE_INITIALIZED ? + EnumVoiceChannelStatus.CONNECTING : (voicePeerErrored() ? EnumVoiceChannelStatus.UNAVAILABLE : EnumVoiceChannelStatus.CONNECTED)); + } + + private static boolean talkStatus = false; + + public static void activateVoice(boolean talk) { + if (talkStatus != talk) { + PlatformVoiceClient.activateVoice(talk); + talkStatus = talk; + } + } + + private static int proximity = 16; + + public static void setVoiceProximity(int prox) { + PlatformVoiceClient.setVoiceProximity(prox); + proximity = prox; + } + + public static int getVoiceProximity() { + return proximity; + } + + private static float volumeListen = 0.5f; + + public static void setVoiceListenVolume(float f) { + PlatformVoiceClient.setVoiceListenVolume(f); + volumeListen = f; + } + + public static float getVoiceListenVolume() { + return volumeListen; + } + + private static float volumeSpeak = 0.5f; + + public static void setVoiceSpeakVolume(float f) { + if (volumeSpeak != f) { + PlatformVoiceClient.setMicVolume(f); + } + volumeSpeak = f; + } + + public static float getVoiceSpeakVolume() { + return volumeSpeak; + } + + private static final Set listeningSet = new HashSet<>(); + private static final Set speakingSet = new HashSet<>(); + private static final Set mutedSet = new HashSet<>(); + + public static Set getVoiceListening() { + return listeningSet; + } + + public static Set getVoiceSpeaking() { + return speakingSet; + } + + public static void setVoiceMuted(EaglercraftUUID uuid, boolean mute) { + PlatformVoiceClient.mutePeer(uuid, mute); + if (mute) { + mutedSet.add(uuid); + } else { + mutedSet.remove(uuid); + } + } + + public static Set getVoiceMuted() { + return mutedSet; + } + + public static List getVoiceRecent() { + return new ArrayList<>(listeningSet); + } + + public static String getVoiceUsername(EaglercraftUUID uuid) { + if(uuid == null) { + return "null"; + } + String ret = uuidToNameLookup.get(uuid); + return ret == null ? uuid.toString() : ret; + } + + public static void sendPacketICE(EaglercraftUUID peerId, String candidate) { + packetSendCallback.accept(VoiceSignalPackets.makeVoiceSignalPacketICE(peerId, candidate)); + } + + public static void sendPacketDesc(EaglercraftUUID peerId, String desc) { + packetSendCallback.accept(VoiceSignalPackets.makeVoiceSignalPacketDesc(peerId, desc)); + } + + public static void sendPacketDisconnect(EaglercraftUUID peerId) { + packetSendCallback.accept(VoiceSignalPackets.makeVoiceSignalPacketDisconnect(peerId)); + } + + public static void sendPacketConnect() { + packetSendCallback.accept(VoiceSignalPackets.makeVoiceSignalPacketConnect()); + } + + public static void sendPacketRequest(EaglercraftUUID peerId) { + packetSendCallback.accept(VoiceSignalPackets.makeVoiceSignalPacketRequest(peerId)); + } + + private static void sendPacketRequestIfNeeded(EaglercraftUUID uuid) { + if (getVoiceStatus() == EnumVoiceChannelStatus.DISCONNECTED || getVoiceStatus() == EnumVoiceChannelStatus.UNAVAILABLE) return; + if(uuid.equals(EaglerProfile.getPlayerUUID())) return; + if (!getVoiceListening().contains(uuid)) sendPacketRequest(uuid); + } +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceSignalPackets.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceSignalPackets.java new file mode 100644 index 0000000..be36777 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceSignalPackets.java @@ -0,0 +1,142 @@ +package net.lax1dude.eaglercraft.v1_8.voice; + +import java.nio.charset.StandardCharsets; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.minecraft.network.PacketBuffer; + +/** + * 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 VoiceSignalPackets { + + static final int VOICE_SIGNAL_ALLOWED = 0; + static final int VOICE_SIGNAL_REQUEST = 0; + static final int VOICE_SIGNAL_CONNECT = 1; + static final int VOICE_SIGNAL_DISCONNECT = 2; + static final int VOICE_SIGNAL_ICE = 3; + static final int VOICE_SIGNAL_DESC = 4; + static final int VOICE_SIGNAL_GLOBAL = 5; + + static void handleVoiceSignal(PacketBuffer streamIn) { + try { + int sig = streamIn.readUnsignedByte(); + switch(sig) { + case VOICE_SIGNAL_ALLOWED: { + boolean voiceAvailableStat = streamIn.readUnsignedByte() == 1; + String[] servs = null; + if(voiceAvailableStat) { + servs = new String[streamIn.readVarIntFromBuffer()]; + for(int i = 0; i < servs.length; i++) { + servs[i] = streamIn.readStringFromBuffer(1024); + } + } + VoiceClientController.handleVoiceSignalPacketTypeAllowed(voiceAvailableStat, servs); + break; + } + case VOICE_SIGNAL_GLOBAL: { + if (VoiceClientController.getVoiceChannel() != EnumVoiceChannelType.GLOBAL) return; + EaglercraftUUID[] voiceIds = new EaglercraftUUID[streamIn.readVarIntFromBuffer()]; + for(int i = 0; i < voiceIds.length; i++) { + voiceIds[i] = streamIn.readUuid(); + } + String[] voiceNames = null; + if (streamIn.isReadable()) { + voiceNames = new String[voiceIds.length]; + for(int i = 0; i < voiceNames.length; i++) { + voiceNames[i] = streamIn.readStringFromBuffer(16); + } + } + VoiceClientController.handleVoiceSignalPacketTypeGlobal(voiceIds, voiceNames); + break; + } + case VOICE_SIGNAL_CONNECT: { + EaglercraftUUID uuid = streamIn.readUuid(); + if (streamIn.isReadable()) { + VoiceClientController.handleVoiceSignalPacketTypeConnect(uuid, streamIn.readBoolean()); + } else if (VoiceClientController.getVoiceChannel() != EnumVoiceChannelType.PROXIMITY || VoiceClientController.getVoiceListening().contains(uuid)) { + VoiceClientController.handleVoiceSignalPacketTypeConnectAnnounce(uuid); + } + break; + } + case VOICE_SIGNAL_DISCONNECT: { + VoiceClientController.handleVoiceSignalPacketTypeDisconnect(streamIn.readableBytes() > 0 ? streamIn.readUuid() : null); + break; + } + case VOICE_SIGNAL_ICE: { + VoiceClientController.handleVoiceSignalPacketTypeICECandidate(streamIn.readUuid(), streamIn.readStringFromBuffer(32767)); + break; + } + case VOICE_SIGNAL_DESC: { + VoiceClientController.handleVoiceSignalPacketTypeDescription(streamIn.readUuid(), streamIn.readStringFromBuffer(32767)); + break; + } + default: { + VoiceClientController.logger.error("Unknown voice signal packet '{}'!", sig); + break; + } + } + }catch(Throwable ex) { + VoiceClientController.logger.error("Failed to handle signal packet!"); + VoiceClientController.logger.error(ex); + } + } + + static PacketBuffer makeVoiceSignalPacketRequest(EaglercraftUUID user) { + PacketBuffer ret = new PacketBuffer(Unpooled.buffer(17, 17)); + ret.writeByte(VOICE_SIGNAL_REQUEST); + ret.writeUuid(user); + return ret; + } + + static PacketBuffer makeVoiceSignalPacketICE(EaglercraftUUID user, String icePacket) { + byte[] str = icePacket.getBytes(StandardCharsets.UTF_8); + int estLen = 17 + PacketBuffer.getVarIntSize(str.length) + str.length; + PacketBuffer ret = new PacketBuffer(Unpooled.buffer(estLen, estLen)); + ret.writeByte(VOICE_SIGNAL_ICE); + ret.writeUuid(user); + ret.writeByteArray(str); + return ret; + } + + static PacketBuffer makeVoiceSignalPacketDesc(EaglercraftUUID user, String descPacket) { + byte[] str = descPacket.getBytes(StandardCharsets.UTF_8); + int estLen = 17 + PacketBuffer.getVarIntSize(str.length) + str.length; + PacketBuffer ret = new PacketBuffer(Unpooled.buffer(estLen, estLen)); + ret.writeByte(VOICE_SIGNAL_DESC); + ret.writeUuid(user); + ret.writeByteArray(str); + return ret; + } + + static PacketBuffer makeVoiceSignalPacketDisconnect(EaglercraftUUID user) { + if (user == null) { + PacketBuffer ret = new PacketBuffer(Unpooled.buffer(1, 1)); + ret.writeByte(VOICE_SIGNAL_DISCONNECT); + return ret; + } + PacketBuffer ret = new PacketBuffer(Unpooled.buffer(17, 17)); + ret.writeByte(VOICE_SIGNAL_DISCONNECT); + ret.writeUuid(user); + return ret; + } + + public static PacketBuffer makeVoiceSignalPacketConnect() { + PacketBuffer ret = new PacketBuffer(Unpooled.buffer(1, 1)); + ret.writeByte(VOICE_SIGNAL_CONNECT); + return ret; + } +} \ No newline at end of file diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceTagRenderer.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceTagRenderer.java new file mode 100644 index 0000000..17ba1c8 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/voice/VoiceTagRenderer.java @@ -0,0 +1,118 @@ +package net.lax1dude.eaglercraft.v1_8.voice; + +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import java.util.HashSet; +import java.util.Set; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityOtherPlayerMP; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.ResourceLocation; + +/** + * 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 + * 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 VoiceTagRenderer { + + private static final ResourceLocation voiceGuiIcons = new ResourceLocation("eagler:gui/eagler_gui.png"); + + private static final Set voiceTagsDrawnThisFrame = new HashSet(); + + public static void renderVoiceNameTag(Minecraft mc, EntityOtherPlayerMP player, int offset) { + EaglercraftUUID uuid = player.getUniqueID(); + boolean mute = VoiceClientController.getVoiceMuted().contains(uuid); + if((mute || VoiceClientController.getVoiceSpeaking().contains(uuid)) && voiceTagsDrawnThisFrame.add(uuid)) { + GlStateManager.disableLighting(); + GlStateManager.disableTexture2D(); + GlStateManager.enableAlpha(); + GlStateManager.depthMask(false); + GlStateManager.disableDepth(); + GlStateManager.enableBlend(); + + GlStateManager.pushMatrix(); + GlStateManager.translate(-8.0f, -18.0f + offset, 0.0f); + + GlStateManager.scale(16.0f, 16.0f, 16.0f); + + Tessellator tessellator = Tessellator.getInstance(); + WorldRenderer worldrenderer = tessellator.getWorldRenderer(); + worldrenderer.begin(7, DefaultVertexFormats.POSITION_COLOR); + float a = 0.25F; + worldrenderer.pos(-0.02, -0.02, 0.0).color(0.0F, 0.0F, 0.0F, a).endVertex(); + worldrenderer.pos(-0.02, 1.02, 0.0).color(0.0F, 0.0F, 0.0F, a).endVertex(); + worldrenderer.pos(1.02, 1.02, 0.0).color(0.0F, 0.0F, 0.0F, a).endVertex(); + worldrenderer.pos(1.02, -0.02, 0.0).color(0.0F, 0.0F, 0.0F, a).endVertex(); + tessellator.draw(); + + GlStateManager.enableTexture2D(); + GlStateManager.enableAlpha(); + GlStateManager.alphaFunc(GL_GREATER, 0.02f); + + mc.getTextureManager().bindTexture(voiceGuiIcons); + + int u = 0; + int v = mute ? 192 : 160; + + float var7 = 0.00390625F; + float var8 = 0.00390625F; + + if(mute) { + GlStateManager.color(0.9F, 0.3F, 0.3F, 0.125F); + }else { + GlStateManager.color(1.0F, 1.0F, 1.0F, 0.125F); + } + + worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX); + worldrenderer.pos(0, 1.0, 0).tex((double) ((float) (u + 0.2f) * var7), (double) ((float) (v + 32 - 0.2f) * var8)).endVertex(); + worldrenderer.pos(1.0, 1.0, 0).tex((double) ((float) (u + 32 - 0.2f) * var7), (double) ((float) (v + 32 - 0.2f) * var8)).endVertex(); + worldrenderer.pos(1.0, 0, 0).tex((double) ((float) (u + 32 - 0.2f) * var7), (double) ((float) (v + 0.2f) * var8)).endVertex(); + worldrenderer.pos(0, 0, 0).tex((double) ((float) (u + 0.2f) * var7), (double) ((float) (v + 0.2f) * var8)).endVertex(); + tessellator.draw(); + + GlStateManager.alphaFunc(GL_GREATER, 0.1f); + GlStateManager.enableDepth(); + GlStateManager.depthMask(true); + + if(mute) { + GlStateManager.color(0.9F, 0.3F, 0.3F, 1.0F); + }else { + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + } + + worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX); + worldrenderer.pos(0, 1.0, 0).tex((double) ((float) (u + 0.2f) * var7), (double) ((float) (v + 32 - 0.2f) * var8)).endVertex(); + worldrenderer.pos(1.0, 1.0, 0).tex((double) ((float) (u + 32 - 0.2f) * var7), (double) ((float) (v + 32 - 0.2f) * var8)).endVertex(); + worldrenderer.pos(1.0, 0, 0).tex((double) ((float) (u + 32 - 0.2f) * var7), (double) ((float) (v + 0.2f) * var8)).endVertex(); + worldrenderer.pos(0, 0, 0).tex((double) ((float) (u + 0.2f) * var7), (double) ((float) (v + 0.2f) * var8)).endVertex(); + tessellator.draw(); + + GlStateManager.enableLighting(); + GlStateManager.disableBlend(); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + + GlStateManager.popMatrix(); + } + } + + public static void clearTagsDrawnSet() { + voiceTagsDrawnThisFrame.clear(); + } + +} \ No newline at end of file diff --git a/src/main/java/net/minecraft/block/BlockRedstoneWire.java b/src/main/java/net/minecraft/block/BlockRedstoneWire.java index 332db33..e9ea362 100644 --- a/src/main/java/net/minecraft/block/BlockRedstoneWire.java +++ b/src/main/java/net/minecraft/block/BlockRedstoneWire.java @@ -99,7 +99,7 @@ public class BlockRedstoneWire extends Block { BlockPos posTmp = new BlockPos(0, 0, 0); Block block = worldIn.getBlockState(blockpos).getBlock(); if (!canConnectTo(worldIn.getBlockState(blockpos), direction) && (block.isBlockNormalCube() - || !canConnectUpwardsTo(worldIn.getBlockState(blockpos.offsetEvenFaster(EnumFacing.UP, posTmp))))) { + || !canConnectUpwardsTo(worldIn.getBlockState(blockpos.offsetEvenFaster(EnumFacing.DOWN, posTmp))))) { Block block1 = worldIn.getBlockState(pos.up()).getBlock(); return !block1.isBlockNormalCube() && block.isBlockNormalCube() && canConnectUpwardsTo(worldIn.getBlockState(blockpos.offsetEvenFaster(EnumFacing.UP, posTmp))) @@ -177,11 +177,13 @@ public class BlockRedstoneWire extends Block { if (worldIn.getBlockState(blockpos).getBlock().isNormalCube() && !worldIn.getBlockState(pos1.up()).getBlock().isNormalCube()) { if (flag && pos1.getY() >= pos2.getY()) { - l = this.getMaxCurrentStrength(worldIn, blockpos.up(), l); + ++blockpos.y; + l = this.getMaxCurrentStrength(worldIn, blockpos, l); } } else if (!worldIn.getBlockState(blockpos).getBlock().isNormalCube() && flag && pos1.getY() <= pos2.getY()) { - l = this.getMaxCurrentStrength(worldIn, blockpos.down(), l); + --blockpos.y; + l = this.getMaxCurrentStrength(worldIn, blockpos, l); } } @@ -205,6 +207,7 @@ public class BlockRedstoneWire extends Block { this.blocksNeedingUpdate.add(pos1); + facings = EnumFacing._VALUES; for (int m = 0; m < facings.length; ++m) { this.blocksNeedingUpdate.add(pos1.offset(facings[m])); } diff --git a/src/main/java/net/minecraft/client/entity/AbstractClientPlayer.java b/src/main/java/net/minecraft/client/entity/AbstractClientPlayer.java index 9657182..f0042c9 100644 --- a/src/main/java/net/minecraft/client/entity/AbstractClientPlayer.java +++ b/src/main/java/net/minecraft/client/entity/AbstractClientPlayer.java @@ -1,6 +1,7 @@ package net.minecraft.client.entity; import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; +import net.lax1dude.eaglercraft.v1_8.profile.SkinModel; import net.minecraft.client.Minecraft; import net.minecraft.client.network.NetworkPlayerInfo; import net.minecraft.client.resources.DefaultPlayerSkin; @@ -34,6 +35,14 @@ import net.minecraft.world.WorldSettings; */ public abstract class AbstractClientPlayer extends EntityPlayer { private NetworkPlayerInfo playerInfo; + + public long eaglerHighPolyAnimationTick = System.currentTimeMillis(); + public float eaglerHighPolyAnimationFloat1 = 0.0f; + public float eaglerHighPolyAnimationFloat2 = 0.0f; + public float eaglerHighPolyAnimationFloat3 = 0.0f; + public float eaglerHighPolyAnimationFloat4 = 0.0f; + public float eaglerHighPolyAnimationFloat5 = 0.0f; + public float eaglerHighPolyAnimationFloat6 = 0.0f; public AbstractClientPlayer(World worldIn, GameProfile playerProfile) { super(worldIn, playerProfile); @@ -91,6 +100,11 @@ public abstract class AbstractClientPlayer extends EntityPlayer { return networkplayerinfo == null ? DefaultPlayerSkin.getSkinType(this.getUniqueID()) : networkplayerinfo.getSkinType(); } + + public SkinModel getEaglerSkinModel() { + NetworkPlayerInfo networkplayerinfo = this.getPlayerInfo(); + return networkplayerinfo == null ? SkinModel.STEVE : networkplayerinfo.getEaglerSkinModel(); + } public float getFovModifier() { float f = 1.0F; diff --git a/src/main/java/net/minecraft/client/gui/GuiButton.java b/src/main/java/net/minecraft/client/gui/GuiButton.java index 39e831f..f7a6fbd 100644 --- a/src/main/java/net/minecraft/client/gui/GuiButton.java +++ b/src/main/java/net/minecraft/client/gui/GuiButton.java @@ -32,8 +32,8 @@ import net.minecraft.util.ResourceLocation; */ public class GuiButton extends Gui { protected static final ResourceLocation buttonTextures = new ResourceLocation("textures/gui/widgets.png"); - protected int width; - protected int height; + public int width; + public int height; public int xPosition; public int yPosition; public String displayString; diff --git a/src/main/java/net/minecraft/client/gui/GuiChat.java b/src/main/java/net/minecraft/client/gui/GuiChat.java index 6fea820..6d38229 100644 --- a/src/main/java/net/minecraft/client/gui/GuiChat.java +++ b/src/main/java/net/minecraft/client/gui/GuiChat.java @@ -341,4 +341,8 @@ public class GuiChat extends GuiScreen { public boolean doesGuiPauseGame() { return false; } + + public boolean blockPTTKey() { + return true; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiCommandBlock.java b/src/main/java/net/minecraft/client/gui/GuiCommandBlock.java index f6813d0..05a2bf0 100644 --- a/src/main/java/net/minecraft/client/gui/GuiCommandBlock.java +++ b/src/main/java/net/minecraft/client/gui/GuiCommandBlock.java @@ -189,4 +189,8 @@ public class GuiCommandBlock extends GuiScreen { } } + + public boolean blockPTTKey() { + return commandTextField.isFocused(); + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiCustomizeSkin.java b/src/main/java/net/minecraft/client/gui/GuiCustomizeSkin.java index 92fa729..2ac38b7 100644 --- a/src/main/java/net/minecraft/client/gui/GuiCustomizeSkin.java +++ b/src/main/java/net/minecraft/client/gui/GuiCustomizeSkin.java @@ -26,6 +26,8 @@ import net.minecraft.entity.player.EnumPlayerModelParts; public class GuiCustomizeSkin extends GuiScreen { private final GuiScreen parentScreen; private String title; + + private GuiButton enableFNAWSkinsButton; public GuiCustomizeSkin(GuiScreen parentScreenIn) { this.parentScreen = parentScreenIn; @@ -53,7 +55,10 @@ public class GuiCustomizeSkin extends GuiScreen { ++i; } - this.buttonList.add(new GuiButton(200, this.width / 2 - 100, this.height / 6 + 24 * (i >> 1), + this.buttonList.add(enableFNAWSkinsButton = new GuiButton(201, this.width / 2 - 100, + this.height / 6 + 10 + 24 * (i >> 1), I18n.format("options.skinCustomisation.enableFNAWSkins") + ": " + + I18n.format(mc.gameSettings.enableFNAWSkins ? "options.on" : "options.off"))); + this.buttonList.add(new GuiButton(200, this.width / 2 - 100, this.height / 6 + 40 + 24 * (i >> 1), I18n.format("gui.done", new Object[0]))); } @@ -66,6 +71,11 @@ public class GuiCustomizeSkin extends GuiScreen { if (parGuiButton.id == 200) { this.mc.gameSettings.saveOptions(); this.mc.displayGuiScreen(this.parentScreen); + } else if (parGuiButton.id == 201) { + mc.gameSettings.enableFNAWSkins = !mc.gameSettings.enableFNAWSkins; + mc.getRenderManager().setEnableFNAWSkins(mc.getEnableFNAWSkins()); + enableFNAWSkinsButton.displayString = I18n.format("options.skinCustomisation.enableFNAWSkins") + ": " + + I18n.format(mc.gameSettings.enableFNAWSkins ? "options.on" : "options.off"); } else if (parGuiButton instanceof GuiCustomizeSkin.ButtonPart) { EnumPlayerModelParts enumplayermodelparts = ((GuiCustomizeSkin.ButtonPart) parGuiButton).playerModelParts; this.mc.gameSettings.switchModelPartEnabled(enumplayermodelparts); diff --git a/src/main/java/net/minecraft/client/gui/GuiOptions.java b/src/main/java/net/minecraft/client/gui/GuiOptions.java index ba7084f..9044be0 100644 --- a/src/main/java/net/minecraft/client/gui/GuiOptions.java +++ b/src/main/java/net/minecraft/client/gui/GuiOptions.java @@ -133,7 +133,7 @@ public class GuiOptions extends GuiScreen implements GuiYesNoCallback { chatcomponenttext.appendText(": "); chatcomponenttext.appendSibling( new ChatComponentTranslation(parEnumDifficulty.getDifficultyResourceKey(), new Object[0])); - return chatcomponenttext.getFormattedText(); + return chatcomponenttext.getUnformattedText(); } public void confirmClicked(boolean flag, int i) { diff --git a/src/main/java/net/minecraft/client/gui/GuiRepair.java b/src/main/java/net/minecraft/client/gui/GuiRepair.java index a10a1e0..8a2b454 100644 --- a/src/main/java/net/minecraft/client/gui/GuiRepair.java +++ b/src/main/java/net/minecraft/client/gui/GuiRepair.java @@ -224,4 +224,8 @@ public class GuiRepair extends GuiContainer implements ICrafting { public void func_175173_a(Container parContainer, IInventory parIInventory) { } + + public boolean blockPTTKey() { + return nameField.isFocused(); + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiScreenBook.java b/src/main/java/net/minecraft/client/gui/GuiScreenBook.java index 39f1564..51253e6 100644 --- a/src/main/java/net/minecraft/client/gui/GuiScreenBook.java +++ b/src/main/java/net/minecraft/client/gui/GuiScreenBook.java @@ -551,4 +551,8 @@ public class GuiScreenBook extends GuiScreen { } } } + + public boolean blockPTTKey() { + return this.bookIsUnsigned; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/GuiVideoSettings.java b/src/main/java/net/minecraft/client/gui/GuiVideoSettings.java index 4908d29..6c69e95 100644 --- a/src/main/java/net/minecraft/client/gui/GuiVideoSettings.java +++ b/src/main/java/net/minecraft/client/gui/GuiVideoSettings.java @@ -49,7 +49,7 @@ public class GuiVideoSettings extends GuiScreen { GameSettings.Options.AO_LEVEL, GameSettings.Options.VIEW_BOBBING, GameSettings.Options.GUI_SCALE, - GameSettings.Options.USE_VBO, + GameSettings.Options.EAGLER_VSYNC, GameSettings.Options.GAMMA, GameSettings.Options.BLOCK_ALTERNATIVES, GameSettings.Options.FOG_FANCY, @@ -182,6 +182,7 @@ public class GuiVideoSettings extends GuiScreen { int j = scaledresolution.getScaledWidth(); int k = scaledresolution.getScaledHeight(); this.setWorldAndResolution(this.mc, j, k); + this.mc.voiceOverlay.setResolution(j, k); } } diff --git a/src/main/java/net/minecraft/client/gui/inventory/GuiContainerCreative.java b/src/main/java/net/minecraft/client/gui/inventory/GuiContainerCreative.java index 9cb035b..446e65c 100644 --- a/src/main/java/net/minecraft/client/gui/inventory/GuiContainerCreative.java +++ b/src/main/java/net/minecraft/client/gui/inventory/GuiContainerCreative.java @@ -854,4 +854,8 @@ public class GuiContainerCreative extends InventoryEffectRenderer { return this.slot.isHere(inv, slotIn); } } + + public boolean blockPTTKey() { + return searchField.isFocused(); + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/gui/inventory/GuiEditSign.java b/src/main/java/net/minecraft/client/gui/inventory/GuiEditSign.java index e4d0f13..247c32e 100644 --- a/src/main/java/net/minecraft/client/gui/inventory/GuiEditSign.java +++ b/src/main/java/net/minecraft/client/gui/inventory/GuiEditSign.java @@ -172,4 +172,8 @@ public class GuiEditSign extends GuiScreen { GlStateManager.popMatrix(); super.drawScreen(i, j, f); } + + public boolean blockPTTKey() { + return true; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/client/model/ModelPlayer.java b/src/main/java/net/minecraft/client/model/ModelPlayer.java index 7be4264..c3f2ec5 100644 --- a/src/main/java/net/minecraft/client/model/ModelPlayer.java +++ b/src/main/java/net/minecraft/client/model/ModelPlayer.java @@ -1,5 +1,7 @@ package net.minecraft.client.model; +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.minecraft.entity.Entity; @@ -119,7 +121,14 @@ public class ModelPlayer extends ModelBiped { } public void renderCape(float parFloat1) { + GlStateManager.matrixMode(GL_TEXTURE); + GlStateManager.pushMatrix(); + GlStateManager.scale(2.0f, 1.0f, 1.0f); + GlStateManager.matrixMode(GL_MODELVIEW); this.bipedCape.render(parFloat1); + GlStateManager.matrixMode(GL_TEXTURE); + GlStateManager.popMatrix(); + GlStateManager.matrixMode(GL_MODELVIEW); } /**+ diff --git a/src/main/java/net/minecraft/client/multiplayer/PlayerControllerMP.java b/src/main/java/net/minecraft/client/multiplayer/PlayerControllerMP.java index 2d73c1f..de58e59 100644 --- a/src/main/java/net/minecraft/client/multiplayer/PlayerControllerMP.java +++ b/src/main/java/net/minecraft/client/multiplayer/PlayerControllerMP.java @@ -313,6 +313,7 @@ public class PlayerControllerMP { .closeChannel(new ChatComponentText("Exception thrown: " + ex.toString())); } this.netClientHandler.getSkinCache().flush(); + this.netClientHandler.getCapeCache().flush(); } else { this.netClientHandler.getNetworkManager().checkDisconnected(); } diff --git a/src/main/java/net/minecraft/client/network/NetworkPlayerInfo.java b/src/main/java/net/minecraft/client/network/NetworkPlayerInfo.java index 76b3ee3..5a1da5b 100644 --- a/src/main/java/net/minecraft/client/network/NetworkPlayerInfo.java +++ b/src/main/java/net/minecraft/client/network/NetworkPlayerInfo.java @@ -1,6 +1,7 @@ package net.minecraft.client.network; import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; +import net.lax1dude.eaglercraft.v1_8.profile.SkinModel; import net.minecraft.client.Minecraft; import net.minecraft.network.play.server.S38PacketPlayerListItem; import net.minecraft.scoreboard.ScorePlayerTeam; @@ -83,13 +84,17 @@ public class NetworkPlayerInfo { return Minecraft.getMinecraft().getNetHandler().getSkinCache().getSkin(this.gameProfile) .getSkinModel().profileSkinType; } + + public SkinModel getEaglerSkinModel() { + return Minecraft.getMinecraft().getNetHandler().getSkinCache().getSkin(this.gameProfile).getSkinModel(); + } public ResourceLocation getLocationSkin() { return Minecraft.getMinecraft().getNetHandler().getSkinCache().getSkin(this.gameProfile).getResourceLocation(); } public ResourceLocation getLocationCape() { - return null; + return Minecraft.getMinecraft().getNetHandler().getCapeCache().getCape(this.gameProfile.getId()).getResourceLocation(); } public ScorePlayerTeam getPlayerTeam() { diff --git a/src/main/java/net/minecraft/client/renderer/EntityRenderer.java b/src/main/java/net/minecraft/client/renderer/EntityRenderer.java index 4e4092d..3c9693c 100644 --- a/src/main/java/net/minecraft/client/renderer/EntityRenderer.java +++ b/src/main/java/net/minecraft/client/renderer/EntityRenderer.java @@ -39,6 +39,7 @@ import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.ShadersRenderPassFuture import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.gui.GuiShaderConfig; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.texture.EmissiveItems; import net.lax1dude.eaglercraft.v1_8.vector.Vector4f; +import net.lax1dude.eaglercraft.v1_8.voice.VoiceTagRenderer; import net.lax1dude.eaglercraft.v1_8.vector.Matrix4f; import net.minecraft.block.Block; import net.minecraft.block.BlockBed; @@ -182,6 +183,8 @@ public class EntityRenderer implements IResourceManagerReloadListener { private boolean initialized = false; private DebugChunkRenderer chunkRenderer; private float clipDistance = 128.0F; + + public float currentProjMatrixFOV = 0.0f; public EntityRenderer(Minecraft mcIn, IResourceManager resourceManagerIn) { this.useShader = false; @@ -650,9 +653,9 @@ public class EntityRenderer implements IResourceManagerReloadListener { float farPlane = this.farPlaneDistance * 2.0f * MathHelper.SQRT_2; if(this.mc.gameSettings.renderDistanceChunks >= 2) { - GlStateManager.gluPerspective(this.getFOVModifier(partialTicks, true), (float)this.mc.displayWidth / (float)this.mc.displayHeight, 0.05F, this.clipDistance); + GlStateManager.gluPerspective(currentProjMatrixFOV = this.getFOVModifier(partialTicks, true), (float)this.mc.displayWidth / (float)this.mc.displayHeight, 0.05F, this.clipDistance); } else { - GlStateManager.gluPerspective(this.getFOVModifier(partialTicks, true), (float) this.mc.displayWidth / (float) this.mc.displayHeight, 0.05F, farPlane); + GlStateManager.gluPerspective(currentProjMatrixFOV = this.getFOVModifier(partialTicks, true), (float) this.mc.displayWidth / (float) this.mc.displayHeight, 0.05F, farPlane); } DeferredStateManager.setGBufferNearFarPlanes(0.05f, farPlane); GlStateManager.matrixMode(GL_MODELVIEW); @@ -1081,6 +1084,8 @@ public class EntityRenderer implements IResourceManagerReloadListener { }); throw new ReportedException(crashreport); } + + this.mc.voiceOverlay.drawOverlay(); } } @@ -1158,6 +1163,8 @@ public class EntityRenderer implements IResourceManagerReloadListener { if (fxaa) { EffectPipelineFXAA.begin(this.mc.displayWidth, this.mc.displayHeight); } + + VoiceTagRenderer.clearTagsDrawnSet(); GlStateManager.enableDepth(); GlStateManager.enableAlpha(); @@ -1221,19 +1228,20 @@ public class EntityRenderer implements IResourceManagerReloadListener { this.mc.mcProfiler.endStartSection("sky"); GlStateManager.matrixMode(GL_PROJECTION); GlStateManager.loadIdentity(); + float vigg = this.getFOVModifier(partialTicks, true); if(this.mc.gameSettings.renderDistanceChunks >= 2) { - GlStateManager.gluPerspective(this.getFOVModifier(partialTicks, true), (float)this.mc.displayWidth / (float)this.mc.displayHeight, 0.05F, this.clipDistance); + GlStateManager.gluPerspective(vigg, (float)this.mc.displayWidth / (float)this.mc.displayHeight, 0.05F, this.clipDistance); } else { - GlStateManager.gluPerspective(this.getFOVModifier(partialTicks, true), (float) this.mc.displayWidth / (float) this.mc.displayHeight, 0.05F, this.farPlaneDistance * 4.0F); + GlStateManager.gluPerspective(vigg, (float) this.mc.displayWidth / (float) this.mc.displayHeight, 0.05F, this.farPlaneDistance * 4.0F); } GlStateManager.matrixMode(GL_MODELVIEW); renderglobal.renderSky(partialTicks, pass); GlStateManager.matrixMode(GL_PROJECTION); GlStateManager.loadIdentity(); if(this.mc.gameSettings.renderDistanceChunks >= 2) { - GlStateManager.gluPerspective(this.getFOVModifier(partialTicks, true), (float)this.mc.displayWidth / (float)this.mc.displayHeight, 0.05F, this.clipDistance); + GlStateManager.gluPerspective(vigg, (float)this.mc.displayWidth / (float)this.mc.displayHeight, 0.05F, this.clipDistance); } else { - GlStateManager.gluPerspective(this.getFOVModifier(partialTicks, true), (float) this.mc.displayWidth / (float) this.mc.displayHeight, 0.05F, this.farPlaneDistance * MathHelper.SQRT_2); + GlStateManager.gluPerspective(vigg, (float) this.mc.displayWidth / (float) this.mc.displayHeight, 0.05F, this.farPlaneDistance * MathHelper.SQRT_2); } GlStateManager.matrixMode(GL_MODELVIEW); } else { diff --git a/src/main/java/net/minecraft/client/renderer/RenderGlobal.java b/src/main/java/net/minecraft/client/renderer/RenderGlobal.java index f4f9350..eadc01a 100644 --- a/src/main/java/net/minecraft/client/renderer/RenderGlobal.java +++ b/src/main/java/net/minecraft/client/renderer/RenderGlobal.java @@ -169,6 +169,7 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene private double lastViewEntityZ = Double.MIN_VALUE; private double lastViewEntityPitch = Double.MIN_VALUE; private double lastViewEntityYaw = Double.MIN_VALUE; + private float lastViewProjMatrixFOV = Float.MIN_VALUE; private final ChunkUpdateManager renderDispatcher = new ChunkUpdateManager(); private ChunkRenderContainer renderContainer; private int renderDistanceChunks = -1; @@ -894,12 +895,14 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene || viewEntity.posX != this.lastViewEntityX || viewEntity.posY != this.lastViewEntityY || viewEntity.posZ != this.lastViewEntityZ || (double) viewEntity.rotationPitch != this.lastViewEntityPitch - || (double) viewEntity.rotationYaw != this.lastViewEntityYaw; + || (double) viewEntity.rotationYaw != this.lastViewEntityYaw + || this.mc.entityRenderer.currentProjMatrixFOV != this.lastViewProjMatrixFOV; this.lastViewEntityX = viewEntity.posX; this.lastViewEntityY = viewEntity.posY; this.lastViewEntityZ = viewEntity.posZ; this.lastViewEntityPitch = (double) viewEntity.rotationPitch; this.lastViewEntityYaw = (double) viewEntity.rotationYaw; + this.lastViewProjMatrixFOV = this.mc.entityRenderer.currentProjMatrixFOV; boolean flag = this.debugFixedClippingHelper != null; Lagometer.timerVisibility.start(); if (!flag && this.displayListEntitiesDirty) { diff --git a/src/main/java/net/minecraft/client/renderer/entity/Render.java b/src/main/java/net/minecraft/client/renderer/entity/Render.java index d2f0ede..6a27da3 100644 --- a/src/main/java/net/minecraft/client/renderer/entity/Render.java +++ b/src/main/java/net/minecraft/client/renderer/entity/Render.java @@ -8,8 +8,12 @@ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.DeferredStateManager; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.NameTagRenderer; +import net.lax1dude.eaglercraft.v1_8.voice.EnumVoiceChannelStatus; +import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; +import net.lax1dude.eaglercraft.v1_8.voice.VoiceTagRenderer; import net.minecraft.block.Block; import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityOtherPlayerMP; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.culling.ICamera; @@ -126,6 +130,9 @@ public abstract class Render { * partialTickTime */ private void renderEntityOnFire(Entity entity, double x, double y, double z, float partialTicks) { + if (entity.width == 0 || entity.height == 0) { + return; + } GlStateManager.disableLighting(); TextureMap texturemap = Minecraft.getMinecraft().getTextureMapBlocks(); EaglerTextureAtlasSprite textureatlassprite = texturemap.getAtlasSprite("minecraft:blocks/fire_layer_0"); @@ -386,6 +393,13 @@ public abstract class Render { GlStateManager.enableLighting(); GlStateManager.disableBlend(); GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + + if (entityIn instanceof EntityOtherPlayerMP) { + if (VoiceClientController.getVoiceStatus() == EnumVoiceChannelStatus.CONNECTED) { + VoiceTagRenderer.renderVoiceNameTag(Minecraft.getMinecraft(), (EntityOtherPlayerMP) entityIn, b0); + } + } + GlStateManager.popMatrix(); } } diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderManager.java b/src/main/java/net/minecraft/client/renderer/entity/RenderManager.java index dc23c21..fd7bcaf 100644 --- a/src/main/java/net/minecraft/client/renderer/entity/RenderManager.java +++ b/src/main/java/net/minecraft/client/renderer/entity/RenderManager.java @@ -8,9 +8,11 @@ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.DeferredStateManager; +import net.lax1dude.eaglercraft.v1_8.profile.RenderHighPoly; import net.minecraft.block.Block; import net.minecraft.block.BlockBed; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; import net.minecraft.client.entity.AbstractClientPlayer; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.model.ModelChicken; @@ -128,6 +130,7 @@ public class RenderManager { private Map, Render> entityRenderMap = Maps.newHashMap(); private Map skinMap = Maps.newHashMap(); private RenderPlayer playerRenderer; + private RenderPlayer eaglerRenderer; private FontRenderer textRenderer; private double renderPosX; private double renderPosY; @@ -215,6 +218,14 @@ public class RenderManager { this.skinMap.put("default", this.playerRenderer); this.skinMap.put("slim", new RenderPlayer(this, true, false)); this.skinMap.put("zombie", new RenderPlayer(this, false, true)); + this.eaglerRenderer = new RenderHighPoly(this, this.playerRenderer.getMainModel(), + this.playerRenderer.shadowSize); + this.skinMap.put("eagler", + Minecraft.getMinecraft().gameSettings.enableFNAWSkins ? this.eaglerRenderer : this.playerRenderer); + } + + public void setEnableFNAWSkins(boolean en) { + this.skinMap.put("eagler", en ? this.eaglerRenderer : this.playerRenderer); } public void setRenderPosition(double renderPosXIn, double renderPosYIn, double renderPosZIn) { diff --git a/src/main/java/net/minecraft/client/renderer/entity/RenderPlayer.java b/src/main/java/net/minecraft/client/renderer/entity/RenderPlayer.java index 2c20a0f..ef92a0d 100644 --- a/src/main/java/net/minecraft/client/renderer/entity/RenderPlayer.java +++ b/src/main/java/net/minecraft/client/renderer/entity/RenderPlayer.java @@ -3,6 +3,7 @@ package net.minecraft.client.renderer.entity; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.minecraft.client.entity.AbstractClientPlayer; import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.model.ModelBase; import net.minecraft.client.model.ModelBiped; import net.minecraft.client.model.ModelPlayer; import net.minecraft.client.model.ModelZombie; @@ -59,6 +60,10 @@ public class RenderPlayer extends RendererLivingEntity { this.addLayer(new LayerCape(this)); this.addLayer(new LayerCustomHead(this.getMainModel().bipedHead)); } + + protected RenderPlayer(RenderManager renderManager, ModelBase modelBase, float size) { + super(renderManager, modelBase, size); + } public ModelBiped getMainModel() { return (ModelBiped) super.getMainModel(); diff --git a/src/main/java/net/minecraft/client/renderer/entity/RendererLivingEntity.java b/src/main/java/net/minecraft/client/renderer/entity/RendererLivingEntity.java index 90a6336..8528108 100644 --- a/src/main/java/net/minecraft/client/renderer/entity/RendererLivingEntity.java +++ b/src/main/java/net/minecraft/client/renderer/entity/RendererLivingEntity.java @@ -193,7 +193,8 @@ public abstract class RendererLivingEntity extends R GlStateManager.disableRescaleNormal(); } catch (Exception exception) { - logger.error("Couldn\'t render entity", exception); + logger.error("Couldn\'t render entity"); + logger.error(exception); } GlStateManager.setActiveTexture(OpenGlHelper.lightmapTexUnit); diff --git a/src/main/java/net/minecraft/client/renderer/texture/TextureManager.java b/src/main/java/net/minecraft/client/renderer/texture/TextureManager.java index 66e4893..99ce6bc 100644 --- a/src/main/java/net/minecraft/client/renderer/texture/TextureManager.java +++ b/src/main/java/net/minecraft/client/renderer/texture/TextureManager.java @@ -58,8 +58,8 @@ public class TextureManager implements ITickable, IResourceManagerReloadListener public void bindTexture(ResourceLocation resource) { int glTex; - if (resource.cachedPointer != null) { - TextureUtil.bindTexture(glTex = ((ITextureObject) resource.cachedPointer).getGlTextureId()); // unsafe, lol + if (resource.cachedPointerType == ResourceLocation.CACHED_POINTER_TEXTURE) { + TextureUtil.bindTexture(glTex = ((ITextureObject) resource.cachedPointer).getGlTextureId()); } else { Object object = (ITextureObject) this.mapTextureObjects.get(resource); if (object == null) { @@ -68,6 +68,7 @@ public class TextureManager implements ITickable, IResourceManagerReloadListener } resource.cachedPointer = object; + resource.cachedPointerType = ResourceLocation.CACHED_POINTER_TEXTURE; TextureUtil.bindTexture(glTex = ((ITextureObject) object).getGlTextureId()); } if (DeferredStateManager.isInDeferredPass()) { @@ -121,9 +122,10 @@ public class TextureManager implements ITickable, IResourceManagerReloadListener } public ITextureObject getTexture(ResourceLocation textureLocation) { - if (textureLocation.cachedPointer != null) { + if (textureLocation.cachedPointerType == ResourceLocation.CACHED_POINTER_TEXTURE) { return (ITextureObject) textureLocation.cachedPointer; } else { + textureLocation.cachedPointerType = ResourceLocation.CACHED_POINTER_TEXTURE; return (ITextureObject) (textureLocation.cachedPointer = this.mapTextureObjects.get(textureLocation)); } } diff --git a/src/main/java/net/minecraft/network/NetHandlerPlayServer.java b/src/main/java/net/minecraft/network/NetHandlerPlayServer.java index 5eb64d5..b8826a3 100644 --- a/src/main/java/net/minecraft/network/NetHandlerPlayServer.java +++ b/src/main/java/net/minecraft/network/NetHandlerPlayServer.java @@ -90,6 +90,7 @@ import net.minecraft.util.IntHashMap; import net.minecraft.util.ReportedException; import net.minecraft.world.WorldServer; import net.lax1dude.eaglercraft.v1_8.sp.server.socket.IntegratedServerPlayerNetworkManager; +import net.lax1dude.eaglercraft.v1_8.sp.server.voice.IntegratedVoiceService; import org.apache.commons.lang3.StringUtils; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; @@ -1239,6 +1240,15 @@ public class NetHandlerPlayServer implements INetHandlerPlayServer, ITickable { byte[] r = new byte[c17packetcustompayload.getBufferData().readableBytes()]; c17packetcustompayload.getBufferData().readBytes(r); ((EaglerMinecraftServer) serverController).getSkinService().processPacket(r, playerEntity); + } else if ("EAG|Capes-1.8".equals(c17packetcustompayload.getChannelName())) { + byte[] r = new byte[c17packetcustompayload.getBufferData().readableBytes()]; + c17packetcustompayload.getBufferData().readBytes(r); + ((EaglerMinecraftServer) serverController).getCapeService().processPacket(r, playerEntity); + } else if ("EAG|Voice-1.8".equals(c17packetcustompayload.getChannelName())) { + IntegratedVoiceService vcs = ((EaglerMinecraftServer) serverController).getVoiceService(); + if (vcs != null) { + vcs.processPacket(c17packetcustompayload.getBufferData(), playerEntity); + } } else if ("EAG|MyUpdCert-1.8".equals(c17packetcustompayload.getChannelName())) { if (playerEntity.updateCertificate == null) { PacketBuffer pb = c17packetcustompayload.getBufferData(); diff --git a/src/main/java/net/minecraft/network/login/client/C00PacketLoginStart.java b/src/main/java/net/minecraft/network/login/client/C00PacketLoginStart.java index 8f862e5..6debecd 100644 --- a/src/main/java/net/minecraft/network/login/client/C00PacketLoginStart.java +++ b/src/main/java/net/minecraft/network/login/client/C00PacketLoginStart.java @@ -30,13 +30,15 @@ import net.minecraft.network.login.INetHandlerLoginServer; public class C00PacketLoginStart implements Packet { private GameProfile profile; private byte[] skin; + private byte[] cape; public C00PacketLoginStart() { } - public C00PacketLoginStart(GameProfile profileIn, byte[] skin) { + public C00PacketLoginStart(GameProfile profileIn, byte[] skin, byte[] cape) { this.profile = profileIn; this.skin = skin; + this.cape = cape; } /**+ @@ -45,6 +47,7 @@ public class C00PacketLoginStart implements Packet { public void readPacketData(PacketBuffer parPacketBuffer) throws IOException { this.profile = new GameProfile((EaglercraftUUID) null, parPacketBuffer.readStringFromBuffer(16)); this.skin = parPacketBuffer.readByteArray(); + this.cape = parPacketBuffer.readableBytes() > 0 ? parPacketBuffer.readByteArray() : null; } /**+ @@ -53,6 +56,7 @@ public class C00PacketLoginStart implements Packet { public void writePacketData(PacketBuffer parPacketBuffer) throws IOException { parPacketBuffer.writeString(this.profile.getName()); parPacketBuffer.writeByteArray(this.skin); + parPacketBuffer.writeByteArray(this.cape); } /**+ @@ -69,4 +73,8 @@ public class C00PacketLoginStart implements Packet { public byte[] getSkin() { return this.skin; } + + public byte[] getCape() { + return this.cape; + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java b/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java index 2285bd3..79b2181 100644 --- a/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java +++ b/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java @@ -60,7 +60,9 @@ import net.minecraft.world.border.WorldBorder; import net.minecraft.world.demo.DemoWorldManager; import net.minecraft.world.storage.IPlayerFileData; import net.minecraft.world.storage.WorldInfo; +import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerMinecraftServer; import net.lax1dude.eaglercraft.v1_8.sp.server.socket.IntegratedServerPlayerNetworkManager; +import net.lax1dude.eaglercraft.v1_8.sp.server.voice.IntegratedVoiceService; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; @@ -346,6 +348,13 @@ public abstract class ServerConfigurationManager { this.uuidToPlayerMap.remove(uuid); this.playerStatFiles.remove(entityplayermp.getName()); } + + ((EaglerMinecraftServer) mcServer).getSkinService().unregisterPlayer(uuid); + ((EaglerMinecraftServer) mcServer).getCapeService().unregisterPlayer(uuid); + IntegratedVoiceService vcs = ((EaglerMinecraftServer) mcServer).getVoiceService(); + if (vcs != null) { + vcs.handlePlayerLoggedOut(playerIn); + } this.sendPacketToAllPlayers(new S38PacketPlayerListItem(S38PacketPlayerListItem.Action.REMOVE_PLAYER, new EntityPlayerMP[] { playerIn })); diff --git a/src/main/java/net/minecraft/server/network/NetHandlerLoginServer.java b/src/main/java/net/minecraft/server/network/NetHandlerLoginServer.java index 7442874..43ef083 100644 --- a/src/main/java/net/minecraft/server/network/NetHandlerLoginServer.java +++ b/src/main/java/net/minecraft/server/network/NetHandlerLoginServer.java @@ -17,6 +17,7 @@ import net.minecraft.util.ChatComponentText; import net.minecraft.util.IChatComponent; import net.minecraft.util.ITickable; import net.lax1dude.eaglercraft.v1_8.sp.server.socket.IntegratedServerPlayerNetworkManager; +import net.lax1dude.eaglercraft.v1_8.sp.server.voice.IntegratedVoiceService; import org.apache.commons.lang3.Validate; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; @@ -52,6 +53,7 @@ public class NetHandlerLoginServer implements INetHandlerLoginServer, ITickable private int connectionTimer; private GameProfile loginGameProfile; private byte[] loginSkinPacket; + private byte[] loginCapePacket; private String serverId = ""; private EntityPlayerMP field_181025_l; @@ -77,6 +79,14 @@ public class NetHandlerLoginServer implements INetHandlerLoginServer, ITickable this.field_181025_l); ((EaglerMinecraftServer) field_181025_l.mcServer).getSkinService() .processLoginPacket(this.loginSkinPacket, field_181025_l); + if (this.loginCapePacket != null) { + ((EaglerMinecraftServer) field_181025_l.mcServer).getCapeService() + .processLoginPacket(this.loginCapePacket, field_181025_l); + } + IntegratedVoiceService svc = ((EaglerMinecraftServer) field_181025_l.mcServer).getVoiceService(); + if (svc != null) { + svc.handlePlayerLoggedIn(entityplayermp); + } this.field_181025_l = null; } } @@ -117,6 +127,14 @@ public class NetHandlerLoginServer implements INetHandlerLoginServer, ITickable this.server.getConfigurationManager().initializeConnectionToPlayer(this.networkManager, entityplayermp); ((EaglerMinecraftServer) entityplayermp.mcServer).getSkinService() .processLoginPacket(this.loginSkinPacket, entityplayermp); + if (this.loginCapePacket != null) { + ((EaglerMinecraftServer) entityplayermp.mcServer).getCapeService() + .processLoginPacket(this.loginCapePacket, entityplayermp); + } + IntegratedVoiceService svc = ((EaglerMinecraftServer) entityplayermp.mcServer).getVoiceService(); + if (svc != null) { + svc.handlePlayerLoggedIn(entityplayermp); + } } } @@ -141,6 +159,7 @@ public class NetHandlerLoginServer implements INetHandlerLoginServer, ITickable new Object[0]); this.loginGameProfile = this.getOfflineProfile(c00packetloginstart.getProfile()); this.loginSkinPacket = c00packetloginstart.getSkin(); + this.loginCapePacket = c00packetloginstart.getCape(); this.currentLoginState = NetHandlerLoginServer.LoginState.READY_TO_ACCEPT; } diff --git a/src/main/java/net/minecraft/util/ResourceLocation.java b/src/main/java/net/minecraft/util/ResourceLocation.java index d384748..0055cf5 100644 --- a/src/main/java/net/minecraft/util/ResourceLocation.java +++ b/src/main/java/net/minecraft/util/ResourceLocation.java @@ -28,6 +28,11 @@ public class ResourceLocation { public String resourceName; public Object cachedPointer = null; + public int cachedPointerType = 0; + + public static final int CACHED_POINTER_NONE = 0; + public static final int CACHED_POINTER_TEXTURE = 1; + public static final int CACHED_POINTER_EAGLER_MESH = 2; protected ResourceLocation(int parInt1, String... resourceName) { this.resourceName = resourceName[0]; diff --git a/src/main/java/net/minecraft/world/World.java b/src/main/java/net/minecraft/world/World.java index 12d5f88..c0f305e 100644 --- a/src/main/java/net/minecraft/world/World.java +++ b/src/main/java/net/minecraft/world/World.java @@ -2718,9 +2718,10 @@ public abstract class World implements IBlockAccess { int i = 0; EnumFacing[] facings = EnumFacing._VALUES; + BlockPos tmp = new BlockPos(0, 0, 0); for (int k = 0; k < facings.length; ++k) { EnumFacing enumfacing = facings[k]; - int j = this.getRedstonePower(pos.offset(enumfacing), enumfacing); + int j = this.getRedstonePower(pos.offsetEvenFaster(enumfacing, tmp), enumfacing); if (j >= 15) { return 15; } diff --git a/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java b/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java index bf722c8..cea4041 100644 --- a/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java @@ -172,7 +172,8 @@ public class ChunkProviderServer implements IChunkProvider { return chunk; } catch (Exception exception) { - logger.error("Couldn\'t load chunk", exception); + logger.error("Couldn\'t load chunk"); + logger.error(exception); return null; } } @@ -183,7 +184,8 @@ public class ChunkProviderServer implements IChunkProvider { try { this.chunkLoader.saveExtraChunkData(this.worldObj, parChunk); } catch (Exception exception) { - logger.error("Couldn\'t save entities", exception); + logger.error("Couldn\'t save entities"); + logger.error(exception); } } @@ -196,10 +198,11 @@ public class ChunkProviderServer implements IChunkProvider { this.chunkLoader.saveChunk(this.worldObj, parChunk); ++EaglerMinecraftServer.counterChunkWrite; } catch (IOException ioexception) { - logger.error("Couldn\'t save chunk", ioexception); + logger.error("Couldn\'t save chunk"); + logger.error(ioexception); } catch (MinecraftException minecraftexception) { - logger.error("Couldn\'t save chunk; already in use by another instance of Minecraft?", - minecraftexception); + logger.error("Couldn\'t save chunk; already in use by another instance of Minecraft?"); + logger.error(minecraftexception); } } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java index b0087b1..07d0c9c 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java @@ -46,8 +46,8 @@ public class PlatformAudio { static final Logger logger = LogManager.getLogger("BrowserAudio"); - private static AudioContext audioctx = null; - private static MediaStreamAudioDestinationNode recDest = null; + static AudioContext audioctx = null; + static MediaStreamAudioDestinationNode recDest = null; private static final Map soundCache = new HashMap(); private static long cacheFreeTimer = 0l; diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformBufferFunctions.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformBufferFunctions.java index 17f28cc..c3b6593 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformBufferFunctions.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformBufferFunctions.java @@ -21,10 +21,7 @@ import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; public class PlatformBufferFunctions { public static void put(ByteBuffer newBuffer, ByteBuffer flip) { - int len = flip.remaining(); - for(int i = 0; i < len; ++i) { - newBuffer.put(flip.get()); - } + newBuffer.put(flip); } public static void put(IntBuffer intBuffer, int index, int[] data) { diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformFilesystem.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformFilesystem.java index 813c779..5d948ae 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformFilesystem.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformFilesystem.java @@ -18,7 +18,7 @@ import org.teavm.jso.indexeddb.IDBRequest; import org.teavm.jso.indexeddb.IDBTransaction; import org.teavm.jso.indexeddb.IDBVersionChangeEvent; import org.teavm.jso.typedarrays.ArrayBuffer; -import org.teavm.jso.typedarrays.DataView; +import org.teavm.jso.typedarrays.Int8Array; import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; import net.lax1dude.eaglercraft.v1_8.internal.buffer.EaglerArrayBufferAllocator; @@ -85,11 +85,11 @@ public class PlatformFilesystem { if(ar == null) { return null; } - return EaglerArrayBufferAllocator.wrapByteBufferTeaVM(DataView.create(ar)); + return EaglerArrayBufferAllocator.wrapByteBufferTeaVM(Int8Array.create(ar)); } public static void eaglerWrite(String pathName, ByteBuffer data) { - if(!AsyncHandlers.writeWholeFile(database, pathName, EaglerArrayBufferAllocator.getDataViewStupid(data).getBuffer()).bool) { + if(!AsyncHandlers.writeWholeFile(database, pathName, EaglerArrayBufferAllocator.getDataView8Unsigned(data).getBuffer()).bool) { throw new RuntimeException("Failed to write " + data.remaining() + " byte file to indexeddb table: " + pathName); } } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java index 55774fd..648772f 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java @@ -4,6 +4,9 @@ import java.util.LinkedList; import java.util.List; import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; + +import org.teavm.interop.Async; +import org.teavm.interop.AsyncCallback; import org.teavm.jso.JSBody; import org.teavm.jso.JSObject; import org.teavm.jso.browser.TimerHandler; @@ -100,6 +103,8 @@ public class PlatformInput { public static boolean keyboardLockSupported = false; public static boolean lockKeys = false; + private static boolean vsync = true; + @JSBody(params = { }, script = "window.onbeforeunload = () => {return false;};") private static native void onBeforeCloseRegister(); @@ -290,6 +295,10 @@ public class PlatformInput { public static boolean isCloseRequested() { return false; } + + public static void setVSync(boolean enable) { + vsync = enable; + } public static void update() { double r = win.getDevicePixelRatio(); @@ -304,7 +313,39 @@ public class PlatformInput { canvas.setHeight(h2); } flipBuffer(); - EagUtils.sleep(1l); + if (PlatformRuntime.recording) { + long t = System.currentTimeMillis(); + if(t - PlatformRuntime.lastFrame > (1000 / 30)) { + PlatformRuntime.recFrame(); + PlatformRuntime.lastFrame = t; + } + } + if(vsync) { + asyncRequestAnimationFrame(); + }else { + EagUtils.sleep(0l); + } + } + + @Async + private static native void asyncRequestAnimationFrame(); + + private static void asyncRequestAnimationFrame(AsyncCallback cb) { + final boolean[] hasCompleted = new boolean[1]; + final int[] timeout = new int[] { -1 }; + Window.requestAnimationFrame((d) -> { + if(!hasCompleted[0]) { + hasCompleted[0] = true; + Window.clearTimeout(timeout[0]); + cb.complete(null); + } + }); + timeout[0] = Window.setTimeout(() -> { + if(!hasCompleted[0]) { + hasCompleted[0] = true; + cb.complete(null); + } + }, 50); } static void initFramebuffer(WebGL2RenderingContext ctx, WebGLFramebuffer fbo, int sw, int sh) { @@ -593,7 +634,7 @@ public class PlatformInput { @JSBody(params = { }, script = "window.navigator.keyboard.unlock();") private static native void unlockKeys(); - @JSBody(params = { }, script = "return 'keyboard' in window.navigator && 'lock' in window.navigator.keyboard;") + @JSBody(params = { }, script = "return !!(window.navigator.keyboard && window.navigator.keyboard.lock);") private static native boolean checkKeyboardLockSupported(); @JSBody(params = { }, script = "document.exitFullscreen();") diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java index 01ed53f..9398c09 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java @@ -38,12 +38,14 @@ public class PlatformOpenGL { static boolean hasDebugRenderInfoExt = false; static boolean hasFramebufferHDR16FSupport = false; static boolean hasFramebufferHDR32FSupport = false; + static boolean hasLinearHDR32FSupport = false; static void setCurrentContext(WebGL2RenderingContext context) { ctx = context; hasDebugRenderInfoExt = ctx.getExtension("WEBGL_debug_renderer_info") != null; hasFramebufferHDR16FSupport = ctx.getExtension("EXT_color_buffer_half_float") != null; hasFramebufferHDR32FSupport = ctx.getExtension("EXT_color_buffer_float") != null; + hasLinearHDR32FSupport = ctx.getExtension("OES_texture_float_linear") != null; _wglClearColor(1.0f, 1.0f, 1.0f, 1.0f); } @@ -193,15 +195,15 @@ public class PlatformOpenGL { } public static final void _wglBufferData(int target, ByteBuffer data, int usage) { - ctx.bufferData(target, data == null ? null : EaglerArrayBufferAllocator.getDataView(data), usage); + ctx.bufferData(target, data == null ? null : EaglerArrayBufferAllocator.getDataView8(data), usage); } public static final void _wglBufferData(int target, IntBuffer data, int usage) { - ctx.bufferData(target, data == null ? null : EaglerArrayBufferAllocator.getDataView(data), usage); + ctx.bufferData(target, data == null ? null : EaglerArrayBufferAllocator.getDataView32(data), usage); } public static final void _wglBufferData(int target, FloatBuffer data, int usage) { - ctx.bufferData(target, data == null ? null : EaglerArrayBufferAllocator.getDataView(data), usage); + ctx.bufferData(target, data == null ? null : EaglerArrayBufferAllocator.getDataView32F(data), usage); } public static final void _wglBufferData(int target, int size, int usage) { @@ -209,15 +211,15 @@ public class PlatformOpenGL { } public static final void _wglBufferSubData(int target, int offset, ByteBuffer data) { - ctx.bufferSubData(target, offset, data == null ? null : EaglerArrayBufferAllocator.getDataView(data)); + ctx.bufferSubData(target, offset, data == null ? null : EaglerArrayBufferAllocator.getDataView8(data)); } public static final void _wglBufferSubData(int target, int offset, IntBuffer data) { - ctx.bufferSubData(target, offset, data == null ? null : EaglerArrayBufferAllocator.getDataView(data)); + ctx.bufferSubData(target, offset, data == null ? null : EaglerArrayBufferAllocator.getDataView32(data)); } public static final void _wglBufferSubData(int target, int offset, FloatBuffer data) { - ctx.bufferSubData(target, offset, data == null ? null : EaglerArrayBufferAllocator.getDataView(data)); + ctx.bufferSubData(target, offset, data == null ? null : EaglerArrayBufferAllocator.getDataView32F(data)); } public static final void _wglBindVertexArray(IBufferArrayGL obj) { @@ -260,55 +262,61 @@ public class PlatformOpenGL { public static final void _wglTexImage3D(int target, int level, int internalFormat, int width, int height, int depth, int border, int format, int type, ByteBuffer data) { ctx.texImage3D(target, level, internalFormat, width, height, depth, border, format, type, - data == null ? null : EaglerArrayBufferAllocator.getDataViewStupid(data)); + data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); } public static final void _wglTexImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, ByteBuffer data) { ctx.texImage2D(target, level, internalFormat, width, height, border, format, type, - data == null ? null : EaglerArrayBufferAllocator.getDataViewStupid(data)); + data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); } public static final void _wglTexImage2Du16(int target, int level, int internalFormat, int width, int height, int border, int format, int type, ByteBuffer data) { ctx.texImage2D(target, level, internalFormat, width, height, border, format, type, - data == null ? null : EaglerArrayBufferAllocator.getDataViewStupid16(data)); + data == null ? null : EaglerArrayBufferAllocator.getDataView16Unsigned(data)); + } + + public static final void _wglTexImage2Df32(int target, int level, int internalFormat, int width, + int height, int border, int format, int type, ByteBuffer data) { + ctx.texImage2D(target, level, internalFormat, width, height, border, format, type, + data == null ? null : EaglerArrayBufferAllocator.getDataView32F(data)); } public static final void _wglTexImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, IntBuffer data) { ctx.texImage2D(target, level, internalFormat, width, height, border, format, type, - data == null ? null : EaglerArrayBufferAllocator.getDataViewStupid(data)); + data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); } public static final void _wglTexImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, FloatBuffer data) { ctx.texImage2D(target, level, internalFormat, width, height, border, format, type, - data == null ? null : EaglerArrayBufferAllocator.getDataViewStupid(data)); + data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); } public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer data) { ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, - data == null ? null : EaglerArrayBufferAllocator.getDataViewStupid(data)); + data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); } public static final void _wglTexSubImage2Du16(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer data) { ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, - data == null ? null : EaglerArrayBufferAllocator.getDataViewStupid16(data)); + data == null ? null : EaglerArrayBufferAllocator.getDataView16Unsigned(data)); } public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, IntBuffer data) { ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, - data == null ? null : EaglerArrayBufferAllocator.getDataViewStupid(data)); + data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); } public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, FloatBuffer data) { ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, - data == null ? null : EaglerArrayBufferAllocator.getDataViewStupid(data)); + data == null ? null : EaglerArrayBufferAllocator.getDataView8Unsigned(data)); } public static final void _wglCopyTexSubImage2D(int target, int level, int xoffset, int yoffset, @@ -457,32 +465,32 @@ public class PlatformOpenGL { public static final void _wglUniformMatrix2fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { if(obj != null) ctx.uniformMatrix2fv(((OpenGLObjects.UniformGL)obj).ptr, transpose, - mat == null ? null : EaglerArrayBufferAllocator.getFloatArrayStupid(mat)); + mat == null ? null : EaglerArrayBufferAllocator.getDataView32F(mat)); } public static final void _wglUniformMatrix3fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { if(obj != null) ctx.uniformMatrix3fv(((OpenGLObjects.UniformGL)obj).ptr, transpose, - mat == null ? null : EaglerArrayBufferAllocator.getFloatArrayStupid(mat)); + mat == null ? null : EaglerArrayBufferAllocator.getDataView32F(mat)); } public static final void _wglUniformMatrix3x2fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { if(obj != null) ctx.uniformMatrix3x2fv(((OpenGLObjects.UniformGL)obj).ptr, transpose, - mat == null ? null : EaglerArrayBufferAllocator.getFloatArrayStupid(mat)); + mat == null ? null : EaglerArrayBufferAllocator.getDataView32F(mat)); } public static final void _wglUniformMatrix4fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { if(obj != null) ctx.uniformMatrix4fv(((OpenGLObjects.UniformGL)obj).ptr, transpose, - mat == null ? null : EaglerArrayBufferAllocator.getFloatArrayStupid(mat)); + mat == null ? null : EaglerArrayBufferAllocator.getDataView32F(mat)); } public static final void _wglUniformMatrix4x2fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { if(obj != null) ctx.uniformMatrix4x2fv(((OpenGLObjects.UniformGL)obj).ptr, transpose, - mat == null ? null : EaglerArrayBufferAllocator.getFloatArrayStupid(mat)); + mat == null ? null : EaglerArrayBufferAllocator.getDataView32F(mat)); } public static final void _wglUniformMatrix4x3fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { if(obj != null) ctx.uniformMatrix4x3fv(((OpenGLObjects.UniformGL)obj).ptr, transpose, - mat == null ? null : EaglerArrayBufferAllocator.getFloatArrayStupid(mat)); + mat == null ? null : EaglerArrayBufferAllocator.getDataView32F(mat)); } public static final void _wglBindFramebuffer(int target, IFramebufferGL framebuffer) { @@ -576,7 +584,11 @@ public class PlatformOpenGL { public static final void _wglFinish() { ctx.finish(); } - + + public static final boolean checkLinearHDR32FSupport() { + return hasLinearHDR32FSupport; + } + private static final void checkErr(String name) { int i = ctx.getError(); if(i != 0) { @@ -600,3 +612,4 @@ public class PlatformOpenGL { return parameterValue; } } + diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java index f2f6e97..91c7371 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java @@ -28,7 +28,6 @@ import org.teavm.jso.dom.html.HTMLCanvasElement; import org.teavm.jso.dom.html.HTMLDocument; import org.teavm.jso.dom.html.HTMLElement; import org.teavm.jso.typedarrays.ArrayBuffer; -import org.teavm.jso.typedarrays.DataView; import org.teavm.jso.typedarrays.Uint8Array; import org.teavm.jso.webaudio.MediaStream; import org.teavm.jso.webgl.WebGLFramebuffer; @@ -275,27 +274,27 @@ public class PlatformRuntime { } public static ByteBuffer castPrimitiveByteArray(byte[] array) { - return EaglerArrayBufferAllocator.wrapByteBufferTeaVM(DataView.create(TeaVMUtils.unwrapArrayBuffer(array))); + return EaglerArrayBufferAllocator.wrapByteBufferTeaVM(TeaVMUtils.unwrapByteArray(array)); } public static IntBuffer castPrimitiveIntArray(int[] array) { - return EaglerArrayBufferAllocator.wrapIntBufferTeaVM(DataView.create(TeaVMUtils.unwrapArrayBuffer(array))); + return EaglerArrayBufferAllocator.wrapIntBufferTeaVM(TeaVMUtils.unwrapIntArray(array)); } public static FloatBuffer castPrimitiveFloatArray(float[] array) { - return EaglerArrayBufferAllocator.wrapFloatBufferTeaVM(DataView.create(TeaVMUtils.unwrapArrayBuffer(array))); + return EaglerArrayBufferAllocator.wrapFloatBufferTeaVM(TeaVMUtils.unwrapFloatArray(array)); } public static byte[] castNativeByteBuffer(ByteBuffer buffer) { - return TeaVMUtils.wrapUnsignedByteArray(EaglerArrayBufferAllocator.getDataViewStupid(buffer)); + return TeaVMUtils.wrapUnsignedByteArray(EaglerArrayBufferAllocator.getDataView8Unsigned(buffer)); } public static int[] castNativeIntBuffer(IntBuffer buffer) { - return TeaVMUtils.wrapIntArray(EaglerArrayBufferAllocator.getDataViewStupid32(buffer)); + return TeaVMUtils.wrapIntArray(EaglerArrayBufferAllocator.getDataView32(buffer)); } public static float[] castNativeFloatBuffer(FloatBuffer buffer) { - return TeaVMUtils.wrapFloatArray(EaglerArrayBufferAllocator.getFloatArrayStupid(buffer)); + return TeaVMUtils.wrapFloatArray(EaglerArrayBufferAllocator.getDataView32F(buffer)); } public static void freeByteBuffer(ByteBuffer byteBuffer) { @@ -500,12 +499,13 @@ public class PlatformRuntime { return TeaVMClientConfigAdapter.instance; } - private static boolean canRec = false; - private static boolean recording = false; - private static JSObject mediaRec = null; - private static HTMLCanvasElement recCanvas = null; - private static CanvasRenderingContext2D recCtx = null; - private static MediaStream recStream = null; + static boolean canRec = false; + static boolean recording = false; + static long lastFrame = 0l; + static JSObject mediaRec = null; + static HTMLCanvasElement recCanvas = null; + static CanvasRenderingContext2D recCtx = null; + static MediaStream recStream = null; public static boolean isRec() { return recording && canRec; @@ -534,7 +534,7 @@ public class PlatformRuntime { return recording ? "recording.stop" : "recording.start"; } - private static void recFrame() { + static void recFrame() { if (mediaRec != null) { int w = PlatformRuntime.canvas.getWidth(); int h = PlatformRuntime.canvas.getHeight(); @@ -546,21 +546,6 @@ public class PlatformRuntime { } } - private static void onRecFrame() { - if (recording) { - recFrame(); - long t = System.currentTimeMillis(); - Window.requestAnimationFrame(timestamp -> { - long d = (1000 / 30) - (System.currentTimeMillis() - t); - if (d <= 0) { - onRecFrame(); - } else { - Window.setTimeout(PlatformRuntime::onRecFrame, d); - } - }); - } - } - @JSFunctor private static interface MediaHandler extends JSObject { void onMedia(MediaStream stream); @@ -630,7 +615,6 @@ public class PlatformRuntime { }, logger::info); } }); - onRecFrame(); } else { stopRec(mediaRec); mediaRec = null; diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java new file mode 100644 index 0000000..0700c95 --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformVoiceClient.java @@ -0,0 +1,439 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.lax1dude.eaglercraft.v1_8.voice.EnumVoiceChannelPeerState; +import net.lax1dude.eaglercraft.v1_8.voice.EnumVoiceChannelReadyState; +import net.lax1dude.eaglercraft.v1_8.voice.EnumVoiceChannelType; +import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; +import org.json.JSONObject; +import org.json.JSONWriter; +import org.teavm.jso.JSBody; +import org.teavm.jso.JSObject; +import org.teavm.jso.dom.events.Event; +import org.teavm.jso.dom.events.EventListener; +import org.teavm.jso.dom.html.HTMLAudioElement; +import org.teavm.jso.dom.html.HTMLDocument; +import org.teavm.jso.json.JSON; +import org.teavm.jso.typedarrays.Uint8Array; +import org.teavm.jso.webaudio.*; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Copyright (c) 2022-2024 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 PlatformVoiceClient { + + private static final Logger logger = LogManager.getLogger("PlatformVoiceClient"); + + private static final HashMap voiceAnalysers = new HashMap<>(); + private static final HashMap voiceGains = new HashMap<>(); + private static final HashMap voicePanners = new HashMap<>(); + + @JSBody(params = {}, script = "return typeof window.RTCPeerConnection !== \"undefined\" && typeof navigator.mediaDevices !== \"undefined\" && typeof navigator.mediaDevices.getUserMedia !== \"undefined\";") + public static native boolean isSupported(); + + @JSBody(params = { "item" }, script = "return item.streams[0];") + static native MediaStream getFirstStream(JSObject item); + + @JSBody(params = { "aud", "stream" }, script = "return aud.srcObject = stream;") + static native void setSrcObject(HTMLAudioElement aud, MediaStream stream); + + @JSBody(params = { "aud" }, script = "return aud.remove();") + static native void removeAud(HTMLAudioElement aud); + + @JSBody(params = { "pc", "stream" }, script = "return stream.getTracks().forEach((track) => { pc.addTrack(track, stream); });") + static native void addStream(JSObject pc, MediaStream stream); + + @JSBody(params = { "rawStream", "muted" }, script = "return rawStream.getAudioTracks()[0].enabled = !muted;") + static native void mute(MediaStream rawStream, boolean muted); + + @JSBody(params = { "peerConnection", "str" }, script = "return peerConnection.addIceCandidate(new RTCIceCandidate(JSON.parse(str)));") + static native void addIceCandidate(JSObject peerConnection, String str); + + public static void disconnect(JSObject peerConnection) { + PlatformWebRTC.closeIt(peerConnection); + } + + public static void setVoiceProximity(int prox) { + for (PannerNode panner : voicePanners.values()) { + panner.setMaxDistance(VoiceClientController.getVoiceListenVolume() * 2 * prox + 0.1f); + } + } + + public static void updateVoicePosition(EaglercraftUUID uuid, double x, double y, double z) { + if (voicePanners.containsKey(uuid)) voicePanners.get(uuid).setPosition((float) x, (float) y, (float) z); + } + + public static class VoicePeer { + public final EaglercraftUUID peerId; + public final JSObject peerConnection; + public MediaStream rawStream; + public VoicePeer(EaglercraftUUID peerId, JSObject peerConnection, boolean offer) { + this.peerId = peerId; + this.peerConnection = peerConnection; + + TeaVMUtils.addEventListener(peerConnection, "icecandidate", (EventListener) evt -> { + if (PlatformWebRTC.hasCandidate(evt)) { + Map m = new HashMap<>(); + m.put("sdpMLineIndex", "" + PlatformWebRTC.getSdpMLineIndex(evt)); + m.put("candidate", PlatformWebRTC.getCandidate(evt)); + handleIceCandidate(peerId, JSONWriter.valueToString(m)); + } + }); + TeaVMUtils.addEventListener(peerConnection, "track", (EventListener) evt -> { + rawStream = getFirstStream(evt); + HTMLAudioElement aud = (HTMLAudioElement) HTMLDocument.current().createElement("audio"); + aud.setAutoplay(true); + aud.setMuted(true); + TeaVMUtils.addEventListener(aud, "ended", (EventListener) evt2 -> { + removeAud(aud); + }); + setSrcObject(aud, rawStream); + handlePeerTrack(peerId, rawStream); + }); + + addStream(peerConnection, localMediaStream.getStream()); + if (offer) { + PlatformWebRTC.createOffer(peerConnection, desc -> { + PlatformWebRTC.setLocalDescription(peerConnection, desc, () -> { + handleDescription(peerId, JSON.stringify(desc)); + }, err -> { + logger.error("Failed to set local description for \"{}\"! {}", peerId, err); + if (peerStateInitial == EnumVoiceChannelPeerState.LOADING) { + peerStateInitial = EnumVoiceChannelPeerState.FAILED; + } + signalDisconnect(peerId, false); + }); + }, err -> { + logger.error("Failed to set create offer for \"{}\"! {}", peerId, err); + if (peerStateInitial == EnumVoiceChannelPeerState.LOADING) { + peerStateInitial = EnumVoiceChannelPeerState.FAILED; + } + signalDisconnect(peerId, false); + }); + } + + TeaVMUtils.addEventListener(peerConnection, "connectionstatechange", (EventListener) evt -> { + String cs = PlatformWebRTC.getConnectionState(peerConnection); + if ("disconnected".equals(cs)) { + signalDisconnect(peerId, false); + } else if ("connected".equals(cs)) { + if (peerState != EnumVoiceChannelPeerState.SUCCESS) { + peerState = EnumVoiceChannelPeerState.SUCCESS; + } + } else if ("failed".equals(cs)) { + if (peerState == EnumVoiceChannelPeerState.LOADING) { + peerState = EnumVoiceChannelPeerState.FAILED; + } + signalDisconnect(peerId, false); + } + }); + } + + public void disconnect() { + PlatformVoiceClient.disconnect(peerConnection); + } + + public void mute(boolean muted) { + PlatformVoiceClient.mute(rawStream, muted); + } + + public void setRemoteDescription(String descJSON) { + try { + JSONObject remoteDesc = new JSONObject(descJSON); + PlatformWebRTC.setRemoteDescription2(peerConnection, descJSON, () -> { + if (remoteDesc.has("type") && "offer".equals(remoteDesc.getString("type"))) { + PlatformWebRTC.createAnswer(peerConnection, desc -> { + PlatformWebRTC.setLocalDescription(peerConnection, desc, () -> { + handleDescription(peerId, JSON.stringify(desc)); + if (peerStateDesc != EnumVoiceChannelPeerState.SUCCESS) peerStateDesc = EnumVoiceChannelPeerState.SUCCESS; + }, err -> { + logger.error("Failed to set local description for \"{}\"! {}", peerId, err.getMessage()); + if (peerStateDesc == EnumVoiceChannelPeerState.LOADING) peerStateDesc = EnumVoiceChannelPeerState.FAILED; + signalDisconnect(peerId, false); + }); + }, err -> { + logger.error("Failed to create answer for \"{}\"! {}", peerId, err.getMessage()); + if (peerStateDesc == EnumVoiceChannelPeerState.LOADING) peerStateDesc = EnumVoiceChannelPeerState.FAILED; + signalDisconnect(peerId, false); + }); + } + }, err -> { + logger.error("Failed to set remote description for \"{}\"! {}", peerId, err.getMessage()); + if (peerStateDesc == EnumVoiceChannelPeerState.LOADING) peerStateDesc = EnumVoiceChannelPeerState.FAILED; + signalDisconnect(peerId, false); + }); + } catch (Throwable err) { + logger.error("Failed to parse remote description for \"{}\"! {}", peerId, err.getMessage()); + if (peerStateDesc == EnumVoiceChannelPeerState.LOADING) peerStateDesc = EnumVoiceChannelPeerState.FAILED; + signalDisconnect(peerId, false); + } + } + + public void addICECandidate(String candidate) { + try { + addIceCandidate(peerConnection, candidate); + if (peerStateIce != EnumVoiceChannelPeerState.SUCCESS) peerStateIce = EnumVoiceChannelPeerState.SUCCESS; + } catch (Throwable err) { + logger.error("Failed to parse ice candidate for \"{}\"! {}", peerId, err.getMessage()); + if (peerStateIce == EnumVoiceChannelPeerState.LOADING) peerStateIce = EnumVoiceChannelPeerState.FAILED; + signalDisconnect(peerId, false); + } + } + } + + public static Set> iceServers = new HashSet<>(); + public static boolean hasInit = false; + public static Map peerList = new HashMap<>(); + public static MediaStreamAudioDestinationNode localMediaStream; + public static GainNode localMediaStreamGain; + public static MediaStream localRawMediaStream; + public static EnumVoiceChannelReadyState readyState = EnumVoiceChannelReadyState.NONE; + public static EnumVoiceChannelPeerState peerState = EnumVoiceChannelPeerState.LOADING; + public static EnumVoiceChannelPeerState peerStateConnect = EnumVoiceChannelPeerState.LOADING; + public static EnumVoiceChannelPeerState peerStateInitial = EnumVoiceChannelPeerState.LOADING; + public static EnumVoiceChannelPeerState peerStateDesc = EnumVoiceChannelPeerState.LOADING; + public static EnumVoiceChannelPeerState peerStateIce = EnumVoiceChannelPeerState.LOADING; + public static AudioContext microphoneVolumeAudioContext = null; + + public static void setICEServers(String[] urls) { + iceServers.clear(); + if (urls == null) return; + for (String url : urls) { + String[] etr = url.split(";"); + if (etr.length == 1) { + Map m = new HashMap<>(); + m.put("urls", etr[0]); + iceServers.add(m); + } else if (etr.length == 3) { + Map m = new HashMap<>(); + m.put("urls", etr[0]); + m.put("username", etr[1]); + m.put("credential", etr[2]); + iceServers.add(m); + } + } + } + + public static void activateVoice(boolean talk) { + if (hasInit) { + PlatformVoiceClient.mute(localRawMediaStream, !talk); + } + } + + public static void initializeDevices() { + if (!hasInit) { + localRawMediaStream = PlatformRuntime.getMic(); + if (localRawMediaStream == null) { + readyState = EnumVoiceChannelReadyState.ABORTED; + return; + } + microphoneVolumeAudioContext = AudioContext.create(); + mute(localRawMediaStream, true); + localMediaStream = microphoneVolumeAudioContext.createMediaStreamDestination(); + localMediaStreamGain = microphoneVolumeAudioContext.createGain(); + microphoneVolumeAudioContext.createMediaStreamSource(localRawMediaStream).connect(localMediaStreamGain); + localMediaStreamGain.connect(localMediaStream); + localMediaStreamGain.getGain().setValue(1.0F); + readyState = EnumVoiceChannelReadyState.DEVICE_INITIALIZED; + hasInit = true; + } else { + readyState = EnumVoiceChannelReadyState.DEVICE_INITIALIZED; + } + } + + public static void tickVoiceClient() { + for (EaglercraftUUID uuid : voiceAnalysers.keySet()) { + AnalyserNode analyser = voiceAnalysers.get(uuid); + Uint8Array array = Uint8Array.create(analyser.getFrequencyBinCount()); + analyser.getByteFrequencyData(array); + int len = array.getLength(); + for (int i = 0; i < len; i++) { + if (array.get(i) >= 0.1f) { + VoiceClientController.getVoiceSpeaking().add(uuid); + break; + } + } + } + } + + public static void setMicVolume(float val) { + if (hasInit) { + if(val > 0.5F) val = 0.5F + (val - 0.5F) * 2.0F; + if(val > 1.5F) val = 1.5F; + if(val < 0.0F) val = 0.0F; + localMediaStreamGain.getGain().setValue(val * 2.0F); + } + } + + public static void resetPeerStates() { + peerState = peerStateConnect = peerStateInitial = peerStateDesc = peerStateIce = EnumVoiceChannelPeerState.LOADING; + } + + public static EnumVoiceChannelPeerState getPeerState() { + return peerState; + } + + public static EnumVoiceChannelPeerState getPeerStateConnect() { + return peerStateConnect; + } + + public static EnumVoiceChannelPeerState getPeerStateInitial() { + return peerStateInitial; + } + + public static EnumVoiceChannelPeerState getPeerStateDesc() { + return peerStateDesc; + } + + public static EnumVoiceChannelPeerState getPeerStateIce() { + return peerStateIce; + } + + public static EnumVoiceChannelReadyState getReadyState() { + return readyState; + } + + public static void signalConnect(EaglercraftUUID peerId, boolean offer) { + if (!hasInit) initializeDevices(); + try { + JSObject peerConnection = PlatformWebRTC.createRTCPeerConnection(JSONWriter.valueToString(iceServers)); + VoicePeer peerInstance = new VoicePeer(peerId, peerConnection, offer); + peerList.put(peerId, peerInstance); + if (peerStateConnect != EnumVoiceChannelPeerState.SUCCESS) peerStateConnect = EnumVoiceChannelPeerState.SUCCESS; + } catch (Throwable e) { + if (peerStateConnect == EnumVoiceChannelPeerState.LOADING) peerStateConnect = EnumVoiceChannelPeerState.FAILED; + } + } + + public static void signalDescription(EaglercraftUUID peerId, String descJSON) { + VoicePeer peer = peerList.get(peerId); + if (peer != null) { + peer.setRemoteDescription(descJSON); + } + } + + public static void signalDisconnect(EaglercraftUUID peerId, boolean quiet) { + VoicePeer peer = peerList.get(peerId); + if (peer != null) { + peerList.remove(peerId, peer); + try { + peer.disconnect(); + } catch (Throwable ignored) {} + handlePeerDisconnect(peerId, quiet); + } + } + + public static void mutePeer(EaglercraftUUID peerId, boolean muted) { + VoicePeer peer = peerList.get(peerId); + if (peer != null) { + peer.mute(muted); + } + } + + public static void signalICECandidate(EaglercraftUUID peerId, String candidate) { + VoicePeer peer = peerList.get(peerId); + if (peer != null) { + peer.addICECandidate(candidate); + } + } + + public static void handleIceCandidate(EaglercraftUUID peerId, String candidate) { + VoiceClientController.sendPacketICE(peerId, candidate); + } + + public static void handleDescription(EaglercraftUUID peerId, String desc) { + VoiceClientController.sendPacketDesc(peerId, desc); + } + + public static void handlePeerTrack(EaglercraftUUID peerId, MediaStream audioStream) { + if (VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.NONE) return; + MediaStreamAudioSourceNode audioNode = PlatformAudio.audioctx.createMediaStreamSource(audioStream); + AnalyserNode analyser = PlatformAudio.audioctx.createAnalyser(); + analyser.setSmoothingTimeConstant(0f); + analyser.setFftSize(32); + audioNode.connect(analyser); + voiceAnalysers.put(peerId, analyser); + if (VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.GLOBAL) { + GainNode gain = PlatformAudio.audioctx.createGain(); + gain.getGain().setValue(VoiceClientController.getVoiceListenVolume()); + analyser.connect(gain); + gain.connect(PlatformAudio.audioctx.getDestination()); + gain.connect(PlatformAudio.recDest); + voiceGains.put(peerId, gain); + VoiceClientController.getVoiceListening().add(peerId); + } else if (VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.PROXIMITY) { + PannerNode panner = PlatformAudio.audioctx.createPanner(); + panner.setRolloffFactor(1f); + panner.setDistanceModel("linear"); + panner.setPanningModel("HRTF"); + panner.setConeInnerAngle(360f); + panner.setConeOuterAngle(0f); + panner.setConeOuterGain(0f); + panner.setOrientation(0f, 1f, 0f); + panner.setPosition(0, 0, 0); + float vol = VoiceClientController.getVoiceListenVolume(); + panner.setMaxDistance(vol * 2 * VoiceClientController.getVoiceProximity() + 0.1f); + GainNode gain = PlatformAudio.audioctx.createGain(); + gain.getGain().setValue(vol); + analyser.connect(gain); + gain.connect(panner); + panner.connect(PlatformAudio.audioctx.getDestination()); + panner.connect(PlatformAudio.recDest); + voiceGains.put(peerId, gain); + VoiceClientController.getVoiceListening().add(peerId); + voicePanners.put(peerId, panner); + } + if (VoiceClientController.getVoiceMuted().contains(peerId)) mutePeer(peerId, true); + } + + public static void handlePeerDisconnect(EaglercraftUUID peerId, boolean quiet) { + if (voiceAnalysers.containsKey(peerId)) { + voiceAnalysers.get(peerId).disconnect(); + voiceAnalysers.remove(peerId); + } + if (voiceGains.containsKey(peerId)) { + voiceGains.get(peerId).disconnect(); + voiceGains.remove(peerId); + VoiceClientController.getVoiceListening().remove(peerId); + } + if (voicePanners.containsKey(peerId)) { + voicePanners.get(peerId).disconnect(); + voicePanners.remove(peerId); + } + if (!quiet) { + VoiceClientController.sendPacketDisconnect(peerId); + } + } + + public static void setVoiceListenVolume(float f) { + for (EaglercraftUUID uuid : voiceGains.keySet()) { + GainNode gain = voiceGains.get(uuid); + float val = f; + if(val > 0.5f) val = 0.5f + (val - 0.5f) * 3.0f; + if(val > 2.0f) val = 2.0f; + if(val < 0.0f) val = 0.0f; + gain.getGain().setValue(val * 2.0f); + if (voicePanners.containsKey(uuid)) voicePanners.get(uuid).setMaxDistance(f * 2 * VoiceClientController.getVoiceProximity() + 0.1f); + } + } +} \ No newline at end of file diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayBufferAllocator.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayBufferAllocator.java index fd2e3b6..755046a 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayBufferAllocator.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayBufferAllocator.java @@ -1,9 +1,9 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; -import org.teavm.jso.typedarrays.ArrayBuffer; import org.teavm.jso.typedarrays.DataView; import org.teavm.jso.typedarrays.Float32Array; import org.teavm.jso.typedarrays.Int32Array; +import org.teavm.jso.typedarrays.Int8Array; import org.teavm.jso.typedarrays.Uint16Array; import org.teavm.jso.typedarrays.Uint8Array; @@ -31,27 +31,31 @@ public class EaglerArrayBufferAllocator { } public static ByteBuffer allocateByteBuffer(int size) { - return new EaglerArrayByteBuffer(DataView.create(ArrayBuffer.create(size))); + return new EaglerArrayByteBuffer(Int8Array.create(size)); } public static ByteBuffer wrapByteBufferTeaVM(DataView dv) { return new EaglerArrayByteBuffer(dv); } - public static IntBuffer allocateIntBuffer(int size) { - return new EaglerArrayIntBuffer(DataView.create(ArrayBuffer.create(size << 2))); + public static ByteBuffer wrapByteBufferTeaVM(Int8Array typedArray) { + return new EaglerArrayByteBuffer(typedArray); } - public static IntBuffer wrapIntBufferTeaVM(DataView dv) { - return new EaglerArrayIntBuffer(dv); + public static IntBuffer allocateIntBuffer(int size) { + return new EaglerArrayIntBuffer(Int32Array.create(size)); + } + + public static IntBuffer wrapIntBufferTeaVM(Int32Array typedArray) { + return new EaglerArrayIntBuffer(typedArray); } public static FloatBuffer allocateFloatBuffer(int size) { - return new EaglerArrayFloatBuffer(DataView.create(ArrayBuffer.create(size << 2))); + return new EaglerArrayFloatBuffer(Float32Array.create(size)); } - public static FloatBuffer wrapFloatBufferTeaVM(DataView dv) { - return new EaglerArrayFloatBuffer(dv); + public static FloatBuffer wrapFloatBufferTeaVM(Float32Array typedArray) { + return new EaglerArrayFloatBuffer(typedArray); } public static DataView getDataView(ByteBuffer buffer) { @@ -63,121 +67,115 @@ public class EaglerArrayBufferAllocator { if(p == 0 && l == b.capacity) { return d; }else { - int i = d.getByteOffset(); - return DataView.create(d.getBuffer(), i + p, l - p); + return DataView.create(d.getBuffer(), d.getByteOffset() + p, l - p); } }else { throw notEagler(buffer); } } - public static Uint8Array getDataViewStupid(ByteBuffer buffer) { + public static Int8Array getDataView8(ByteBuffer buffer) { if(buffer instanceof EaglerArrayByteBuffer) { EaglerArrayByteBuffer b = (EaglerArrayByteBuffer)buffer; - DataView d = b.dataView; - int p = b.position; - int l = b.limit; - int i = d.getByteOffset(); - return Uint8Array.create(d.getBuffer(), i + p, l - p); - }else { - throw notEagler(buffer); - } - } - - public static Uint16Array getDataViewStupid16(ByteBuffer buffer) { - if(buffer instanceof EaglerArrayByteBuffer) { - EaglerArrayByteBuffer b = (EaglerArrayByteBuffer)buffer; - DataView d = b.dataView; - int p = b.position; - int l = b.limit; - int i = d.getByteOffset(); - return Uint16Array.create(d.getBuffer(), i + p, (l - p) >> 1); - }else { - throw notEagler(buffer); - } - } - - public static DataView getDataView(IntBuffer buffer) { - if(buffer instanceof EaglerArrayIntBuffer) { - EaglerArrayIntBuffer b = (EaglerArrayIntBuffer)buffer; - DataView d = b.dataView; + Int8Array d = b.typedArray; int p = b.position; int l = b.limit; if(p == 0 && l == b.capacity) { return d; }else { int i = d.getByteOffset(); - return DataView.create(d.getBuffer(), i + (p << 2), (l - p) << 2); + return Int8Array.create(d.getBuffer(), d.getByteOffset() + p, l - p); } }else { throw notEagler(buffer); } } - public static Uint8Array getDataViewStupid(IntBuffer buffer) { - if(buffer instanceof EaglerArrayIntBuffer) { - EaglerArrayIntBuffer b = (EaglerArrayIntBuffer)buffer; - DataView d = b.dataView; + public static Uint8Array getDataView8Unsigned(ByteBuffer buffer) { + if(buffer instanceof EaglerArrayByteBuffer) { + EaglerArrayByteBuffer b = (EaglerArrayByteBuffer)buffer; + Int8Array d = b.typedArray; int p = b.position; - int l = b.limit; int i = d.getByteOffset(); - return Uint8Array.create(d.getBuffer(), i + (p << 2), (l - p) << 2); + return Uint8Array.create(d.getBuffer(), i + p, b.limit - p); }else { throw notEagler(buffer); } } - public static Int32Array getDataViewStupid32(IntBuffer buffer) { - if(buffer instanceof EaglerArrayIntBuffer) { - EaglerArrayIntBuffer b = (EaglerArrayIntBuffer)buffer; - DataView d = b.dataView; + public static Uint16Array getDataView16Unsigned(ByteBuffer buffer) { + if(buffer instanceof EaglerArrayByteBuffer) { + EaglerArrayByteBuffer b = (EaglerArrayByteBuffer)buffer; + Int8Array d = b.typedArray; int p = b.position; - int l = b.limit; - int i = d.getByteOffset(); - return Int32Array.create(d.getBuffer(), i + (p << 2), (l - p) << 2); + return Uint16Array.create(d.getBuffer(), d.getByteOffset() + p, (b.limit - p) >> 1); }else { throw notEagler(buffer); } } - public static DataView getDataView(FloatBuffer buffer) { - if(buffer instanceof EaglerArrayFloatBuffer) { - EaglerArrayFloatBuffer b = (EaglerArrayFloatBuffer)buffer; - DataView d = b.dataView; + public static Float32Array getDataView32F(ByteBuffer buffer) { + if(buffer instanceof EaglerArrayByteBuffer) { + EaglerArrayByteBuffer b = (EaglerArrayByteBuffer)buffer; + Int8Array d = b.typedArray; + int p = b.position; + return Float32Array.create(d.getBuffer(), d.getByteOffset() + p, (b.limit - p) >> 2); + }else { + throw notEagler(buffer); + } + } + + public static Int32Array getDataView32(IntBuffer buffer) { + if(buffer instanceof EaglerArrayIntBuffer) { + EaglerArrayIntBuffer b = (EaglerArrayIntBuffer)buffer; + Int32Array d = b.typedArray; int p = b.position; int l = b.limit; if(p == 0 && l == b.capacity) { return d; }else { - int i = d.getByteOffset(); - return DataView.create(d.getBuffer(), i + (p << 2), (l - p) << 2); + return Int32Array.create(d.getBuffer(), d.getByteOffset() + (p << 2), l - p); } }else { throw notEagler(buffer); } } - public static Uint8Array getDataViewStupid(FloatBuffer buffer) { - if(buffer instanceof EaglerArrayFloatBuffer) { - EaglerArrayFloatBuffer b = (EaglerArrayFloatBuffer)buffer; - DataView d = b.dataView; + public static Uint8Array getDataView8Unsigned(IntBuffer buffer) { + if(buffer instanceof EaglerArrayIntBuffer) { + EaglerArrayIntBuffer b = (EaglerArrayIntBuffer)buffer; + Int32Array d = b.typedArray; int p = b.position; int l = b.limit; - int i = d.getByteOffset(); - return Uint8Array.create(d.getBuffer(), i + (p << 2), (l - p) << 2); + return Uint8Array.create(d.getBuffer(), d.getByteOffset() + (p << 2), (l - p) << 2); }else { throw notEagler(buffer); } } - public static Float32Array getFloatArrayStupid(FloatBuffer buffer) { + public static Float32Array getDataView32F(FloatBuffer buffer) { if(buffer instanceof EaglerArrayFloatBuffer) { EaglerArrayFloatBuffer b = (EaglerArrayFloatBuffer)buffer; - DataView d = b.dataView; + Float32Array d = b.typedArray; int p = b.position; int l = b.limit; - int i = d.getByteOffset(); - return Float32Array.create(d.getBuffer(), i + p, l - p); + if(p == 0 && l == b.capacity) { + return d; + }else { + return Float32Array.create(d.getBuffer(), d.getByteOffset() + (p << 2), l - p); + } + }else { + throw notEagler(buffer); + } + } + + public static Uint8Array getDataView8Unsigned(FloatBuffer buffer) { + if(buffer instanceof EaglerArrayFloatBuffer) { + EaglerArrayFloatBuffer b = (EaglerArrayFloatBuffer)buffer; + Float32Array d = b.typedArray; + int p = b.position; + int l = b.limit; + return Uint8Array.create(d.getBuffer(), d.getByteOffset() + (p << 2), (l - p) << 2); }else { throw notEagler(buffer); } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayByteBuffer.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayByteBuffer.java index 5f2760c..adda4b2 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayByteBuffer.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayByteBuffer.java @@ -1,8 +1,12 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; -import org.teavm.jso.typedarrays.ArrayBuffer; import org.teavm.jso.typedarrays.DataView; -import org.teavm.jso.typedarrays.Uint8Array; +import org.teavm.jso.typedarrays.Float32Array; +import org.teavm.jso.typedarrays.Int16Array; +import org.teavm.jso.typedarrays.Int32Array; +import org.teavm.jso.typedarrays.Int8Array; + +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; /** * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. @@ -22,16 +26,18 @@ import org.teavm.jso.typedarrays.Uint8Array; public class EaglerArrayByteBuffer implements ByteBuffer { final DataView dataView; + final Int8Array typedArray; final int capacity; int position; int limit; int mark; - static final DataView ZERO_LENGTH_BUFFER = DataView.create(ArrayBuffer.create(0)); + static final Int8Array ZERO_LENGTH_BUFFER = Int8Array.create(0); EaglerArrayByteBuffer(DataView dataView) { this.dataView = dataView; + this.typedArray = Int8Array.create(dataView.getBuffer(), dataView.getByteOffset(), dataView.getByteLength()); this.capacity = dataView.getByteLength(); this.position = 0; this.limit = this.capacity; @@ -40,11 +46,30 @@ public class EaglerArrayByteBuffer implements ByteBuffer { EaglerArrayByteBuffer(DataView dataView, int position, int limit, int mark) { this.dataView = dataView; + this.typedArray = Int8Array.create(dataView.getBuffer(), dataView.getByteOffset(), dataView.getByteLength()); this.capacity = dataView.getByteLength(); this.position = position; this.limit = limit; this.mark = mark; } + + EaglerArrayByteBuffer(Int8Array typedArray) { + this.typedArray = typedArray; + this.dataView = DataView.create(typedArray.getBuffer(), typedArray.getByteOffset(), typedArray.getByteLength()); + this.capacity = typedArray.getByteLength(); + this.position = 0; + this.limit = this.capacity; + this.mark = -1; + } + + EaglerArrayByteBuffer(Int8Array typedArray, int position, int limit, int mark) { + this.typedArray = typedArray; + this.dataView = DataView.create(typedArray.getBuffer(), typedArray.getByteOffset(), typedArray.getByteLength()); + this.capacity = typedArray.getByteLength(); + this.position = position; + this.limit = limit; + this.mark = mark; + } @Override public int capacity() { @@ -93,8 +118,12 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public ByteBuffer slice() { - int o = dataView.getByteOffset(); - return new EaglerArrayByteBuffer(DataView.create(dataView.getBuffer(), o + position, limit - position)); + if(position == limit) { + return new EaglerArrayByteBuffer(ZERO_LENGTH_BUFFER); + }else { + if(position > limit) throw new ArrayIndexOutOfBoundsException(position); + return new EaglerArrayByteBuffer(Int8Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + position, limit - position)); + } } @Override @@ -110,35 +139,33 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public byte get() { if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - return dataView.getInt8(position++); + return typedArray.get(position++); } @Override public ByteBuffer put(byte b) { if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - dataView.setInt8(position++, b); + typedArray.set(position++, b); return this; } @Override public byte get(int index) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - return dataView.getInt8(index); + return typedArray.get(index); } @Override public ByteBuffer put(int index, byte b) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - dataView.setInt8(index, b); + typedArray.set(index, b); return this; } @Override public ByteBuffer get(byte[] dst, int offset, int length) { if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); - for(int i = 0; i < length; ++i) { - dst[offset + i] = dataView.getInt8(position + i); - } + TeaVMUtils.unwrapArrayBufferView(dst).set(Int8Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + position, length), offset); position += length; return this; } @@ -146,9 +173,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public ByteBuffer get(byte[] dst) { if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); - for(int i = 0; i < dst.length; ++i) { - dst[position + i] = dataView.getInt8(position + i); - } + TeaVMUtils.unwrapArrayBufferView(dst).set(Int8Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + position, dst.length)); position += dst.length; return this; } @@ -159,10 +184,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { EaglerArrayByteBuffer c = (EaglerArrayByteBuffer)src; int l = c.limit - c.position; if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); - int o = c.dataView.getByteOffset(); - Uint8Array.create(dataView.getBuffer()).set( - Uint8Array.create(c.dataView.getBuffer(), o + c.position, c.limit - c.position), - dataView.getByteOffset() + position); + typedArray.set(Int8Array.create(c.typedArray.getBuffer(), c.typedArray.getByteOffset() + c.position, l), position); position += l; c.position += l; }else { @@ -179,8 +201,10 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public ByteBuffer put(byte[] src, int offset, int length) { if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); - for(int i = 0; i < length; ++i) { - dataView.setInt8(position + i, src[offset + i]); + if(offset == 0 && length == src.length) { + typedArray.set(TeaVMUtils.unwrapArrayBufferView(src), position); + }else { + typedArray.set(Int8Array.create(TeaVMUtils.unwrapArrayBuffer(src), offset, length), position); } position += length; return this; @@ -189,10 +213,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public ByteBuffer put(byte[] src) { if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); - //dataView.set(src, position); // doesn't work - for(int i = 0; i < src.length; ++i) { - dataView.setInt8(position + i, src[i]); - } + typedArray.set(TeaVMUtils.unwrapArrayBufferView(src), position); position += src.length; return this; } @@ -211,12 +232,10 @@ public class EaglerArrayByteBuffer implements ByteBuffer { return new EaglerArrayByteBuffer(ZERO_LENGTH_BUFFER); } - int o = dataView.getByteOffset(); + Int8Array dst = Int8Array.create(limit - position); + dst.set(Int8Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + position, limit - position)); - Uint8Array dst = Uint8Array.create(ArrayBuffer.create(limit - position)); - dst.set(Uint8Array.create(dataView.getBuffer(), o + position, limit - position)); - - return new EaglerArrayByteBuffer(DataView.create(dst.getBuffer())); + return new EaglerArrayByteBuffer(dst); } @Override @@ -279,7 +298,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public ShortBuffer asShortBuffer() { - return new EaglerArrayShortBuffer(dataView); + return new EaglerArrayShortBuffer(Int16Array.create(typedArray.getBuffer(), typedArray.getByteOffset(), typedArray.getLength() >> 1)); } @Override @@ -313,7 +332,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public IntBuffer asIntBuffer() { - return new EaglerArrayIntBuffer(dataView); + return new EaglerArrayIntBuffer(Int32Array.create(typedArray.getBuffer(), typedArray.getByteOffset(), typedArray.getLength() >> 2)); } @Override @@ -388,7 +407,7 @@ public class EaglerArrayByteBuffer implements ByteBuffer { @Override public FloatBuffer asFloatBuffer() { - return new EaglerArrayFloatBuffer(dataView); + return new EaglerArrayFloatBuffer(Float32Array.create(typedArray.getBuffer(), typedArray.getByteOffset(), typedArray.getLength() >> 2)); } @Override diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayFloatBuffer.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayFloatBuffer.java index d69d580..4e9ffe9 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayFloatBuffer.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayFloatBuffer.java @@ -1,8 +1,8 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; -import org.teavm.jso.typedarrays.ArrayBuffer; -import org.teavm.jso.typedarrays.DataView; -import org.teavm.jso.typedarrays.Uint8Array; +import org.teavm.jso.typedarrays.Float32Array; + +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; /** * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. @@ -21,7 +21,7 @@ import org.teavm.jso.typedarrays.Uint8Array; */ public class EaglerArrayFloatBuffer implements FloatBuffer { - final DataView dataView; + final Float32Array typedArray; final int capacity; int position; @@ -30,17 +30,19 @@ public class EaglerArrayFloatBuffer implements FloatBuffer { private static final int SHIFT = 2; - EaglerArrayFloatBuffer(DataView dataView) { - this.dataView = dataView; - this.capacity = dataView.getByteLength() >> SHIFT; + static final Float32Array ZERO_LENGTH_BUFFER = Float32Array.create(0); + + EaglerArrayFloatBuffer(Float32Array typedArray) { + this.typedArray = typedArray; + this.capacity = typedArray.getLength(); this.position = 0; this.limit = this.capacity; this.mark = -1; } - EaglerArrayFloatBuffer(DataView dataView, int position, int limit, int mark) { - this.dataView = dataView; - this.capacity = dataView.getByteLength() >> SHIFT; + EaglerArrayFloatBuffer(Float32Array typedArray, int position, int limit, int mark) { + this.typedArray = typedArray; + this.capacity = typedArray.getLength(); this.position = position; this.limit = limit; this.mark = mark; @@ -93,64 +95,66 @@ public class EaglerArrayFloatBuffer implements FloatBuffer { @Override public FloatBuffer slice() { - int o = dataView.getByteOffset(); - return new EaglerArrayFloatBuffer(DataView.create(dataView.getBuffer(), o + (position << SHIFT), (limit - position) << SHIFT)); + if(position == limit) { + return new EaglerArrayFloatBuffer(ZERO_LENGTH_BUFFER); + }else { + if(position > limit) throw new ArrayIndexOutOfBoundsException(position); + return new EaglerArrayFloatBuffer(Float32Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), limit - position)); + } } @Override public FloatBuffer duplicate() { - return new EaglerArrayFloatBuffer(dataView, position, limit, mark); + return new EaglerArrayFloatBuffer(typedArray, position, limit, mark); } @Override public FloatBuffer asReadOnlyBuffer() { - return new EaglerArrayFloatBuffer(dataView, position, limit, mark); + return new EaglerArrayFloatBuffer(typedArray, position, limit, mark); } @Override public float get() { if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - return dataView.getFloat32((position++) << SHIFT, true); + return typedArray.get(position++); } @Override public FloatBuffer put(float b) { if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - dataView.setFloat32((position++) << SHIFT, b, true); + typedArray.set(position++, b); return this; } @Override public float get(int index) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - return dataView.getFloat32(index << SHIFT, true); + return typedArray.get(index); } @Override public FloatBuffer put(int index, float b) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - dataView.setFloat32(index << SHIFT, b, true); + typedArray.set(index, b); return this; } @Override public float getElement(int index) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - return dataView.getFloat32(index << SHIFT, true); + return typedArray.get(index); } @Override public void putElement(int index, float value) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - dataView.setFloat32(index << SHIFT, value, true); + typedArray.set(index, value); } @Override public FloatBuffer get(float[] dst, int offset, int length) { if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); - for(int i = 0; i < length; ++i) { - dst[offset + i] = dataView.getFloat32((position + i) << SHIFT, true); - } + TeaVMUtils.unwrapArrayBufferView(dst).set(Float32Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), length), offset); position += length; return this; } @@ -158,9 +162,7 @@ public class EaglerArrayFloatBuffer implements FloatBuffer { @Override public FloatBuffer get(float[] dst) { if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); - for(int i = 0; i < dst.length; ++i) { - dst[i] = dataView.getFloat32((position + i) << SHIFT, true); - } + TeaVMUtils.unwrapArrayBufferView(dst).set(Float32Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), dst.length)); position += dst.length; return this; } @@ -171,17 +173,14 @@ public class EaglerArrayFloatBuffer implements FloatBuffer { EaglerArrayFloatBuffer c = (EaglerArrayFloatBuffer)src; int l = c.limit - c.position; if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); - int o = c.dataView.getByteOffset(); - Uint8Array.create(dataView.getBuffer()).set( - Uint8Array.create(c.dataView.getBuffer(), o + (c.position << SHIFT), (c.limit - c.position) << SHIFT), - dataView.getByteOffset() + (position << SHIFT)); + typedArray.set(Float32Array.create(c.typedArray.getBuffer(), c.typedArray.getByteOffset() + (c.position << SHIFT), l), position); position += l; c.position += l; }else { int l = src.remaining(); if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); for(int i = 0; i < l; ++i) { - dataView.setFloat32((position + l) << SHIFT, src.get(), true); + typedArray.set(position + l, src.get()); } position += l; } @@ -191,8 +190,10 @@ public class EaglerArrayFloatBuffer implements FloatBuffer { @Override public FloatBuffer put(float[] src, int offset, int length) { if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); - for(int i = 0; i < length; ++i) { - dataView.setFloat32((position + i) << SHIFT, src[offset + i], true); + if(offset == 0 && length == src.length) { + typedArray.set(TeaVMUtils.unwrapArrayBufferView(src), position); + }else { + typedArray.set(Float32Array.create(TeaVMUtils.unwrapArrayBuffer(src), offset << SHIFT, length), position); } position += length; return this; @@ -201,9 +202,7 @@ public class EaglerArrayFloatBuffer implements FloatBuffer { @Override public FloatBuffer put(float[] src) { if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); - for(int i = 0; i < src.length; ++i) { - dataView.setFloat32((position + i) << SHIFT, src[i], true); - } + typedArray.set(TeaVMUtils.unwrapArrayBufferView(src), position); position += src.length; return this; } @@ -219,15 +218,13 @@ public class EaglerArrayFloatBuffer implements FloatBuffer { if(position > limit) throw new ArrayIndexOutOfBoundsException(position); if(position == limit) { - return new EaglerArrayFloatBuffer(EaglerArrayByteBuffer.ZERO_LENGTH_BUFFER); + return new EaglerArrayFloatBuffer(ZERO_LENGTH_BUFFER); } - int o = dataView.getByteOffset(); + Float32Array dst = Float32Array.create(limit - position); + dst.set(Float32Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), limit - position)); - Uint8Array dst = Uint8Array.create(ArrayBuffer.create((limit - position) << SHIFT)); - dst.set(Uint8Array.create(dataView.getBuffer(), o + (position << SHIFT), (limit - position) << SHIFT)); - - return new EaglerArrayFloatBuffer(DataView.create(dst.getBuffer())); + return new EaglerArrayFloatBuffer(dst); } @Override diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayIntBuffer.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayIntBuffer.java index e2eaba8..7aee34a 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayIntBuffer.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayIntBuffer.java @@ -1,8 +1,8 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; -import org.teavm.jso.typedarrays.ArrayBuffer; -import org.teavm.jso.typedarrays.DataView; -import org.teavm.jso.typedarrays.Uint8Array; +import org.teavm.jso.typedarrays.Int32Array; + +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; /** * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. @@ -21,7 +21,7 @@ import org.teavm.jso.typedarrays.Uint8Array; */ public class EaglerArrayIntBuffer implements IntBuffer { - final DataView dataView; + final Int32Array typedArray; final int capacity; int position; @@ -30,17 +30,19 @@ public class EaglerArrayIntBuffer implements IntBuffer { private static final int SHIFT = 2; - EaglerArrayIntBuffer(DataView dataView) { - this.dataView = dataView; - this.capacity = dataView.getByteLength() >> SHIFT; + static final Int32Array ZERO_LENGTH_BUFFER = Int32Array.create(0); + + EaglerArrayIntBuffer(Int32Array typedArray) { + this.typedArray = typedArray; + this.capacity = typedArray.getLength(); this.position = 0; this.limit = this.capacity; this.mark = -1; } - EaglerArrayIntBuffer(DataView dataView, int position, int limit, int mark) { - this.dataView = dataView; - this.capacity = dataView.getByteLength() >> SHIFT; + EaglerArrayIntBuffer(Int32Array typedArray, int position, int limit, int mark) { + this.typedArray = typedArray; + this.capacity = typedArray.getLength(); this.position = position; this.limit = limit; this.mark = mark; @@ -93,64 +95,66 @@ public class EaglerArrayIntBuffer implements IntBuffer { @Override public IntBuffer slice() { - int o = dataView.getByteOffset(); - return new EaglerArrayIntBuffer(DataView.create(dataView.getBuffer(), o + (position << SHIFT), (limit - position) << SHIFT)); + if(position == limit) { + return new EaglerArrayIntBuffer(ZERO_LENGTH_BUFFER); + }else { + if(position > limit) throw new ArrayIndexOutOfBoundsException(position); + return new EaglerArrayIntBuffer(Int32Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), limit - position)); + } } @Override public IntBuffer duplicate() { - return new EaglerArrayIntBuffer(dataView, position, limit, mark); + return new EaglerArrayIntBuffer(typedArray, position, limit, mark); } @Override public IntBuffer asReadOnlyBuffer() { - return new EaglerArrayIntBuffer(dataView, position, limit, mark); + return new EaglerArrayIntBuffer(typedArray, position, limit, mark); } @Override public int get() { if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - return dataView.getInt32((position++) << SHIFT, true); + return typedArray.get(position++); } @Override public IntBuffer put(int b) { if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - dataView.setInt32((position++) << SHIFT, b, true); + typedArray.set(position++, b); return this; } @Override public int get(int index) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - return dataView.getInt32(index << SHIFT, true); + return typedArray.get(index); } @Override public IntBuffer put(int index, int b) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - dataView.setInt32(index << SHIFT, b, true); + typedArray.set(index, b); return this; } @Override public int getElement(int index) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - return dataView.getInt32(index << SHIFT, true); + return typedArray.get(index); } @Override public void putElement(int index, int value) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - dataView.setInt32(index << SHIFT, value, true); + typedArray.set(index, value); } @Override public IntBuffer get(int[] dst, int offset, int length) { if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); - for(int i = 0; i < length; ++i) { - dst[offset + i] = dataView.getInt32((position + i) << SHIFT, true); - } + TeaVMUtils.unwrapArrayBufferView(dst).set(Int32Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), length), offset); position += length; return this; } @@ -158,9 +162,7 @@ public class EaglerArrayIntBuffer implements IntBuffer { @Override public IntBuffer get(int[] dst) { if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); - for(int i = 0; i < dst.length; ++i) { - dst[i] = dataView.getInt32((position + i) << SHIFT, true); - } + TeaVMUtils.unwrapArrayBufferView(dst).set(Int32Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), dst.length)); position += dst.length; return this; } @@ -171,17 +173,14 @@ public class EaglerArrayIntBuffer implements IntBuffer { EaglerArrayIntBuffer c = (EaglerArrayIntBuffer)src; int l = c.limit - c.position; if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); - int o = c.dataView.getByteOffset(); - Uint8Array.create(dataView.getBuffer()).set( - Uint8Array.create(c.dataView.getBuffer(), o + (c.position << SHIFT), (c.limit - c.position) << SHIFT), - dataView.getByteOffset() + (position << SHIFT)); + typedArray.set(Int32Array.create(c.typedArray.getBuffer(), c.typedArray.getByteOffset() + (c.position << SHIFT), l), position); position += l; c.position += l; }else { int l = src.remaining(); if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); for(int i = 0; i < l; ++i) { - dataView.setInt32((position + l) << SHIFT, src.get(), true); + typedArray.set(position + l, src.get()); } position += l; } @@ -191,8 +190,10 @@ public class EaglerArrayIntBuffer implements IntBuffer { @Override public IntBuffer put(int[] src, int offset, int length) { if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); - for(int i = 0; i < length; ++i) { - dataView.setInt32((position + i) << SHIFT, src[offset + i], true); + if(offset == 0 && length == src.length) { + typedArray.set(TeaVMUtils.unwrapArrayBufferView(src), position); + }else { + typedArray.set(Int32Array.create(TeaVMUtils.unwrapArrayBuffer(src), offset << SHIFT, length), position); } position += length; return this; @@ -201,9 +202,7 @@ public class EaglerArrayIntBuffer implements IntBuffer { @Override public IntBuffer put(int[] src) { if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); - for(int i = 0; i < src.length; ++i) { - dataView.setInt32((position + i) << SHIFT, src[i], true); - } + typedArray.set(TeaVMUtils.unwrapArrayBufferView(src), position); position += src.length; return this; } @@ -219,15 +218,13 @@ public class EaglerArrayIntBuffer implements IntBuffer { if(position > limit) throw new ArrayIndexOutOfBoundsException(position); if(position == limit) { - return new EaglerArrayIntBuffer(EaglerArrayByteBuffer.ZERO_LENGTH_BUFFER); + return new EaglerArrayIntBuffer(ZERO_LENGTH_BUFFER); } - int o = dataView.getByteOffset(); + Int32Array dst = Int32Array.create(limit - position); + dst.set(Int32Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), limit - position)); - Uint8Array dst = Uint8Array.create(ArrayBuffer.create((limit - position) << SHIFT)); - dst.set(Uint8Array.create(dataView.getBuffer(), o + (position << SHIFT), (limit - position) << SHIFT)); - - return new EaglerArrayIntBuffer(DataView.create(dst.getBuffer())); + return new EaglerArrayIntBuffer(dst); } @Override diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayShortBuffer.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayShortBuffer.java index a21e63e..16eabb5 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayShortBuffer.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerArrayShortBuffer.java @@ -1,8 +1,8 @@ package net.lax1dude.eaglercraft.v1_8.internal.buffer; -import org.teavm.jso.typedarrays.ArrayBuffer; -import org.teavm.jso.typedarrays.DataView; -import org.teavm.jso.typedarrays.Uint8Array; +import org.teavm.jso.typedarrays.Int16Array; + +import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils; /** * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. @@ -21,7 +21,7 @@ import org.teavm.jso.typedarrays.Uint8Array; */ public class EaglerArrayShortBuffer implements ShortBuffer { - final DataView dataView; + final Int16Array typedArray; final int capacity; int position; @@ -30,17 +30,19 @@ public class EaglerArrayShortBuffer implements ShortBuffer { private static final int SHIFT = 1; - EaglerArrayShortBuffer(DataView dataView) { - this.dataView = dataView; - this.capacity = dataView.getByteLength() >> SHIFT; + static final Int16Array ZERO_LENGTH_BUFFER = Int16Array.create(0); + + EaglerArrayShortBuffer(Int16Array typedArray) { + this.typedArray = typedArray; + this.capacity = typedArray.getLength(); this.position = 0; this.limit = this.capacity; this.mark = -1; } - EaglerArrayShortBuffer(DataView dataView, int position, int limit, int mark) { - this.dataView = dataView; - this.capacity = dataView.getByteLength() >> SHIFT; + EaglerArrayShortBuffer(Int16Array typedArray, int position, int limit, int mark) { + this.typedArray = typedArray; + this.capacity = typedArray.getLength(); this.position = position; this.limit = limit; this.mark = mark; @@ -93,64 +95,66 @@ public class EaglerArrayShortBuffer implements ShortBuffer { @Override public ShortBuffer slice() { - int o = dataView.getByteOffset(); - return new EaglerArrayShortBuffer(DataView.create(dataView.getBuffer(), o + (position << SHIFT), (limit - position) << SHIFT)); + if(position == limit) { + return new EaglerArrayShortBuffer(ZERO_LENGTH_BUFFER); + }else { + if(position > limit) throw new ArrayIndexOutOfBoundsException(position); + return new EaglerArrayShortBuffer(Int16Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), limit - position)); + } } @Override public ShortBuffer duplicate() { - return new EaglerArrayShortBuffer(dataView, position, limit, mark); + return new EaglerArrayShortBuffer(typedArray, position, limit, mark); } @Override public ShortBuffer asReadOnlyBuffer() { - return new EaglerArrayShortBuffer(dataView, position, limit, mark); + return new EaglerArrayShortBuffer(typedArray, position, limit, mark); } @Override public short get() { if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - return dataView.getInt16((position++) << SHIFT, true); + return typedArray.get(position++); } @Override public ShortBuffer put(short b) { if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); - dataView.setInt16((position++) << SHIFT, b, true); + typedArray.set(position++, b); return this; } @Override public short get(int index) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - return dataView.getInt16(index << SHIFT, true); + return typedArray.get(index); } @Override public ShortBuffer put(int index, short b) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - dataView.setInt16(index << SHIFT, b, true); + typedArray.set(index, b); return this; } @Override public short getElement(int index) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - return dataView.getInt16(index << SHIFT, true); + return typedArray.get(index); } @Override public void putElement(int index, short value) { if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); - dataView.setInt16(index << SHIFT, value, true); + typedArray.set(index, value); } @Override public ShortBuffer get(short[] dst, int offset, int length) { if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); - for(int i = 0; i < length; ++i) { - dst[offset + i] = dataView.getInt16((position + i) << SHIFT, true); - } + TeaVMUtils.unwrapArrayBufferView(dst).set(Int16Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), length), offset); position += length; return this; } @@ -158,9 +162,7 @@ public class EaglerArrayShortBuffer implements ShortBuffer { @Override public ShortBuffer get(short[] dst) { if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); - for(int i = 0; i < dst.length; ++i) { - dst[i] = dataView.getInt16((position + i) << SHIFT, true); - } + TeaVMUtils.unwrapArrayBufferView(dst).set(Int16Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), dst.length)); position += dst.length; return this; } @@ -171,17 +173,14 @@ public class EaglerArrayShortBuffer implements ShortBuffer { EaglerArrayShortBuffer c = (EaglerArrayShortBuffer)src; int l = c.limit - c.position; if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); - int o = c.dataView.getByteOffset(); - Uint8Array.create(dataView.getBuffer()).set( - Uint8Array.create(c.dataView.getBuffer(), o + (c.position << SHIFT), (c.limit - c.position) << SHIFT), - dataView.getByteOffset() + (position << SHIFT)); + typedArray.set(Int16Array.create(c.typedArray.getBuffer(), c.typedArray.getByteOffset() + (c.position << SHIFT), l), position); position += l; c.position += l; }else { int l = src.remaining(); if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); for(int i = 0; i < l; ++i) { - dataView.setInt16((position + l) << SHIFT, src.get(), true); + typedArray.set(position + l, src.get()); } position += l; } @@ -191,8 +190,10 @@ public class EaglerArrayShortBuffer implements ShortBuffer { @Override public ShortBuffer put(short[] src, int offset, int length) { if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); - for(int i = 0; i < length; ++i) { - dataView.setInt16((position + i) << SHIFT, src[offset + i], true); + if(offset == 0 && length == src.length) { + typedArray.set(TeaVMUtils.unwrapArrayBufferView(src), position); + }else { + typedArray.set(Int16Array.create(TeaVMUtils.unwrapArrayBuffer(src), offset << SHIFT, length), position); } position += length; return this; @@ -201,9 +202,7 @@ public class EaglerArrayShortBuffer implements ShortBuffer { @Override public ShortBuffer put(short[] src) { if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); - for(int i = 0; i < src.length; ++i) { - dataView.setInt16((position + i) << SHIFT, src[i], true); - } + typedArray.set(TeaVMUtils.unwrapArrayBufferView(src), position); position += src.length; return this; } @@ -219,15 +218,13 @@ public class EaglerArrayShortBuffer implements ShortBuffer { if(position > limit) throw new ArrayIndexOutOfBoundsException(position); if(position == limit) { - return new EaglerArrayShortBuffer(EaglerArrayByteBuffer.ZERO_LENGTH_BUFFER); + return new EaglerArrayShortBuffer(ZERO_LENGTH_BUFFER); } - int o = dataView.getByteOffset(); + Int16Array dst = Int16Array.create(limit - position); + dst.set(Int16Array.create(typedArray.getBuffer(), typedArray.getByteOffset() + (position << SHIFT), limit - position)); - Uint8Array dst = Uint8Array.create(ArrayBuffer.create((limit - position) << SHIFT)); - dst.set(Uint8Array.create(dataView.getBuffer(), o + (position << SHIFT), (limit - position) << SHIFT)); - - return new EaglerArrayShortBuffer(DataView.create(dst.getBuffer())); + return new EaglerArrayShortBuffer(dst); } @Override diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMClientConfigAdapter.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMClientConfigAdapter.java index e9ad8a2..b0f6e33 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMClientConfigAdapter.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMClientConfigAdapter.java @@ -55,6 +55,7 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter { private boolean logInvalidCerts = false; private boolean checkRelaysForUpdates = false; private boolean enableSignatureBadge = false; + private boolean allowVoiceClient = true; public void loadNative(JSObject jsObject) { integratedServerOpts = new JSONObject(); @@ -73,12 +74,14 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter { useSpecialCursors = eaglercraftXOpts.getHtml5CursorSupport(false); logInvalidCerts = EaglercraftVersion.enableUpdateService && !demoMode && eaglercraftXOpts.getLogInvalidCerts(false); enableSignatureBadge = eaglercraftXOpts.getEnableSignatureBadge(false); + allowVoiceClient = eaglercraftXOpts.getAllowVoiceClient(true); integratedServerOpts.put("worldsDB", worldsDB); integratedServerOpts.put("demoMode", demoMode); integratedServerOpts.put("lang", defaultLocale); integratedServerOpts.put("allowUpdateSvc", isAllowUpdateSvc); integratedServerOpts.put("allowUpdateDL", isAllowUpdateDL); + integratedServerOpts.put("allowVoiceClient", allowVoiceClient); JSEaglercraftXOptsServersArray serversArray = eaglercraftXOpts.getServers(); if(serversArray != null) { @@ -158,6 +161,7 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter { useSpecialCursors = eaglercraftOpts.optBoolean("html5CursorSupport", false); logInvalidCerts = EaglercraftVersion.enableUpdateService && !demoMode && eaglercraftOpts.optBoolean("logInvalidCerts", false); enableSignatureBadge = eaglercraftOpts.optBoolean("enableSignatureBadge", false); + allowVoiceClient = eaglercraftOpts.optBoolean("allowVoiceClient", true); JSONArray serversArray = eaglercraftOpts.optJSONArray("servers"); if(serversArray != null) { for(int i = 0, l = serversArray.length(); i < l; ++i) { @@ -299,6 +303,11 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter { public boolean isEnableSignatureBadge() { return enableSignatureBadge; } + + @Override + public boolean isAllowVoiceClient() { + return allowVoiceClient; + } @Override public String toString() { @@ -317,6 +326,7 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter { jsonObject.put("logInvalidCerts", logInvalidCerts); jsonObject.put("checkRelaysForUpdates", checkRelaysForUpdates); jsonObject.put("enableSignatureBadge", enableSignatureBadge); + jsonObject.put("allowVoiceClient", allowVoiceClient); JSONArray serversArr = new JSONArray(); for(int i = 0, l = defaultServers.size(); i < l; ++i) { DefaultServer srv = defaultServers.get(i); diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMUtils.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMUtils.java index 98a6963..0a8edf7 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMUtils.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMUtils.java @@ -9,6 +9,7 @@ import org.teavm.jso.browser.Window; import org.teavm.jso.typedarrays.ArrayBuffer; import org.teavm.jso.typedarrays.ArrayBufferView; import org.teavm.jso.typedarrays.Float32Array; +import org.teavm.jso.typedarrays.Int16Array; import org.teavm.jso.typedarrays.Int32Array; import org.teavm.jso.typedarrays.Int8Array; import org.teavm.jso.typedarrays.Uint8Array; @@ -57,6 +58,10 @@ public class TeaVMUtils { return ((TeaVMArrayObject)(Object)buf).getData().getBuffer(); } + public static ArrayBufferView unwrapArrayBufferView(byte[] buf) { + return ((TeaVMArrayObject)(Object)buf).getData(); + } + @JSBody(params = { "buf" }, script = "return $rt_createByteArray(buf.buffer)") private static native JSObject wrapByteArray0(JSObject buf); @@ -80,6 +85,10 @@ public class TeaVMUtils { return ((TeaVMArrayObject)(Object)buf).getData().getBuffer(); } + public static ArrayBufferView unwrapArrayBufferView(int[] buf) { + return ((TeaVMArrayObject)(Object)buf).getData(); + } + @JSBody(params = { "buf" }, script = "return $rt_createIntArray(buf.buffer)") private static native JSObject wrapIntArray0(JSObject buf); @@ -95,6 +104,10 @@ public class TeaVMUtils { return ((TeaVMArrayObject)(Object)buf).getData().getBuffer(); } + public static ArrayBufferView unwrapArrayBufferView(float[] buf) { + return ((TeaVMArrayObject)(Object)buf).getData(); + } + @JSBody(params = { "buf" }, script = "return $rt_createFloatArray(buf.buffer)") private static native JSObject wrapFloatArray0(JSObject buf); @@ -102,6 +115,25 @@ public class TeaVMUtils { return (float[])(Object)wrapFloatArray0(buf); } + public static Int16Array unwrapShortArray(short[] buf) { + return Int16Array.create(((TeaVMArrayObject)(Object)buf).getData().getBuffer()); + } + + public static ArrayBuffer unwrapArrayBuffer(short[] buf) { + return ((TeaVMArrayObject)(Object)buf).getData().getBuffer(); + } + + public static ArrayBufferView unwrapArrayBufferView(short[] buf) { + return ((TeaVMArrayObject)(Object)buf).getData(); + } + + @JSBody(params = { "buf" }, script = "return $rt_createShortArray(buf.buffer)") + private static native JSObject wrapShortArray0(JSObject buf); + + public static short[] wrapShortArray(Int16Array buf) { + return (short[])(Object)wrapShortArray0(buf); + } + @Async public static native void sleepSetTimeout(int millis); diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsRoot.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsRoot.java index 7449bd8..9e335f4 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsRoot.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsRoot.java @@ -79,5 +79,8 @@ public abstract class JSEaglercraftXOptsRoot implements JSObject { @JSBody(params = { "def" }, script = "return (typeof this.checkRelaysForUpdates === \"boolean\") ? this.checkRelaysForUpdates : def;") public native boolean getCheckRelaysForUpdates(boolean defaultValue); + + @JSBody(params = { "def" }, script = "return (typeof this.allowVoiceClient === \"boolean\") ? this.allowVoiceClient : def;") + public native boolean getAllowVoiceClient(boolean defaultValue); } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/opengl/GlStateManager.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/opengl/GlStateManager.java index 597592f..dde15a3 100644 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/opengl/GlStateManager.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/opengl/GlStateManager.java @@ -13,7 +13,7 @@ import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*; /** - * Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved. + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -984,6 +984,30 @@ public class GlStateManager extends ModData { Matrix4f.mul(modeMatrix, paramMatrix, modeMatrix); } + + public static final void multMatrix(Matrix4f matrix) { + Matrix4f modeMatrix; + + switch(stateMatrixMode) { + case GL_MODELVIEW: + default: + modeMatrix = modelMatrixStack[modelMatrixStackPointer]; + modelMatrixStackAccessSerial[modelMatrixStackPointer] = ++modelMatrixAccessSerial; + break; + case GL_PROJECTION: + modeMatrix = projectionMatrixStack[projectionMatrixStackPointer]; + projectionMatrixStackAccessSerial[projectionMatrixStackPointer] = ++projectionMatrixAccessSerial; + break; + case GL_TEXTURE: + int ptr = textureMatrixStackPointer[activeTexture]; + modeMatrix = textureMatrixStack[activeTexture][ptr]; + textureMatrixStackAccessSerial[activeTexture][textureMatrixStackPointer[activeTexture]] = + ++textureMatrixAccessSerial[activeTexture]; + break; + } + + Matrix4f.mul(modeMatrix, matrix, modeMatrix); + } public static final void color(float colorRed, float colorGreen, float colorBlue, float colorAlpha) { stateColorR = colorRed; diff --git a/src/teavm/java/net/minecraft/client/Minecraft.java b/src/teavm/java/net/minecraft/client/Minecraft.java index d42ae4f..01d4999 100644 --- a/src/teavm/java/net/minecraft/client/Minecraft.java +++ b/src/teavm/java/net/minecraft/client/Minecraft.java @@ -45,6 +45,7 @@ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFolderResourcePack; import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFontRenderer; +import net.lax1dude.eaglercraft.v1_8.opengl.EaglerMeshLoader; import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; @@ -73,6 +74,8 @@ import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiScreenSingleplayerConnecting; import net.lax1dude.eaglercraft.v1_8.sp.internal.ClientPlatformSingleplayer; import net.lax1dude.eaglercraft.v1_8.sp.lan.LANServerController; import net.lax1dude.eaglercraft.v1_8.update.RelayUpdateChecker; +import net.lax1dude.eaglercraft.v1_8.voice.GuiVoiceOverlay; +import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.client.audio.MusicTicker; @@ -315,6 +318,8 @@ public class Minecraft extends ModData implements IThreadListener { public ModAPI modapi; public SkullCommand eagskullCommand; + + public GuiVoiceOverlay voiceOverlay; public Minecraft(GameConfiguration gameConfig) { theMinecraft = this; @@ -431,6 +436,7 @@ public class Minecraft extends ModData implements IThreadListener { this.mcResourceManager.registerReloadListener(new MetalsLUT()); this.mcResourceManager.registerReloadListener(new EmissiveItems()); this.mcResourceManager.registerReloadListener(new BlockVertexIDs()); + this.mcResourceManager.registerReloadListener(new EaglerMeshLoader()); AchievementList.openInventory.setStatStringFormatter(new IStatStringFormat() { public String formatString(String parString1) { try { @@ -481,6 +487,9 @@ public class Minecraft extends ModData implements IThreadListener { this.checkGLError("Post startup"); this.ingameGUI = new GuiIngame(this); this.eagskullCommand = new SkullCommand(this); + this.voiceOverlay = new GuiVoiceOverlay(this); + ScaledResolution voiceRes = new ScaledResolution(this); + this.voiceOverlay.setResolution(voiceRes.getScaledWidth(), voiceRes.getScaledHeight()); ServerList.initServerList(this); EaglerProfile.read(); @@ -888,6 +897,7 @@ public class Minecraft extends ModData implements IThreadListener { public void updateDisplay() { this.mcProfiler.startSection("display_update"); + Display.setVSync(this.gameSettings.enableVsync); Display.update(); this.mcProfiler.endSection(); this.checkWindowResize(); @@ -1234,12 +1244,14 @@ public class Minecraft extends ModData implements IThreadListener { private void resize(int width, int height) { this.displayWidth = Math.max(1, width); this.displayHeight = Math.max(1, height); + ScaledResolution scaledresolution = new ScaledResolution(this); if (this.currentScreen != null) { - ScaledResolution scaledresolution = new ScaledResolution(this); this.currentScreen.onResize(this, scaledresolution.getScaledWidth(), scaledresolution.getScaledHeight()); } this.loadingScreen = new LoadingScreenRenderer(this); + + this.voiceOverlay.setResolution(scaledresolution.getScaledWidth(), scaledresolution.getScaledHeight()); } public MusicTicker func_181535_r() { @@ -1278,6 +1290,9 @@ public class Minecraft extends ModData implements IThreadListener { if (!this.isGamePaused) { this.ingameGUI.updateTick(); } + + this.mcProfiler.endStartSection("eaglerVoice"); + VoiceClientController.tickVoiceClient(this); this.mcProfiler.endSection(); this.entityRenderer.getMouseOver(1.0F); @@ -1794,6 +1809,7 @@ public class Minecraft extends ModData implements IThreadListener { */ public void launchIntegratedServer(String folderName, String worldName, WorldSettings worldSettingsIn) { this.loadWorld((WorldClient) null); + Minecraft.getMinecraft().getRenderManager().setEnableFNAWSkins(this.gameSettings.enableFNAWSkins); session.reset(); SingleplayerServerController.launchEaglercraftServer(folderName, gameSettings.difficulty.getDifficultyId(), Math.max(gameSettings.renderDistanceChunks, 2), worldSettingsIn); @@ -2338,4 +2354,12 @@ public class Minecraft extends ModData implements IThreadListener { public void clearTitles() { ingameGUI.displayTitle(null, null, -1, -1, -1); } + + public boolean getEnableFNAWSkins() { + boolean ret = this.gameSettings.enableFNAWSkins; + if (this.thePlayer != null) { + ret &= this.thePlayer.sendQueue.currentFNAWSkinAllowedState; + } + return ret; + } } \ No newline at end of file diff --git a/src/teavm/java/net/minecraft/client/gui/GuiIngame.java b/src/teavm/java/net/minecraft/client/gui/GuiIngame.java index b7ab960..c8aef88 100644 --- a/src/teavm/java/net/minecraft/client/gui/GuiIngame.java +++ b/src/teavm/java/net/minecraft/client/gui/GuiIngame.java @@ -289,6 +289,10 @@ public class GuiIngame extends Gui { if (scoreobjective1 != null) { this.renderScoreboard(scoreobjective1, scaledresolution); } + + if (this.mc.currentScreen == null) { + this.mc.voiceOverlay.drawOverlay(); + } GlStateManager.enableBlend(); GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 1, 0); diff --git a/src/teavm/java/net/minecraft/client/gui/GuiIngameMenu.java b/src/teavm/java/net/minecraft/client/gui/GuiIngameMenu.java index b436341..dd0c57f 100644 --- a/src/teavm/java/net/minecraft/client/gui/GuiIngameMenu.java +++ b/src/teavm/java/net/minecraft/client/gui/GuiIngameMenu.java @@ -1,5 +1,6 @@ package net.minecraft.client.gui; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.Mouse; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.sp.SingleplayerServerController; @@ -8,6 +9,8 @@ import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiScreenLANNotSupported; import net.lax1dude.eaglercraft.v1_8.sp.gui.GuiShareToLan; import net.lax1dude.eaglercraft.v1_8.sp.lan.LANServerController; import net.lax1dude.eaglercraft.v1_8.update.GuiUpdateCheckerOverlay; +import net.lax1dude.eaglercraft.v1_8.voice.GuiVoiceMenu; +import net.minecraft.client.Minecraft; import net.minecraft.client.audio.PositionedSoundRecord; import net.minecraft.client.gui.achievement.GuiAchievements; import net.minecraft.client.gui.achievement.GuiStats; @@ -45,9 +48,13 @@ public class GuiIngameMenu extends GuiScreen { boolean hasSentAutoSave = !SingleplayerServerController.isWorldRunning(); private GuiUpdateCheckerOverlay updateCheckerOverlay; + private GuiVoiceMenu voiceMenu; public GuiIngameMenu() { updateCheckerOverlay = new GuiUpdateCheckerOverlay(true, this); + if (EagRuntime.getConfiguration().isAllowVoiceClient()) { + voiceMenu = new GuiVoiceMenu(this); + } } /**+ @@ -146,6 +153,10 @@ public class GuiIngameMenu extends GuiScreen { */ public void updateScreen() { super.updateScreen(); + if (EagRuntime.getConfiguration().isAllowVoiceClient() + && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { + voiceMenu.updateScreen(); + } if (Mouse.isActuallyGrabbed()) { Mouse.setGrabbed(false); } @@ -163,37 +174,67 @@ public class GuiIngameMenu extends GuiScreen { this.updateCheckerOverlay.drawScreen(i, j, f); if (LANServerController.isLANOpen()) { + int offset = this.updateCheckerOverlay.getSharedWorldInfoYOffset(); String str = I18n.format("lanServer.pauseMenu0"); - drawString(fontRendererObj, str, 6, 32, 0xFFFF55); + drawString(fontRendererObj, str, 6, 10 + offset, 0xFFFF55); if (mc.gameSettings.hideJoinCode) { GlStateManager.pushMatrix(); - GlStateManager.translate(7.0f, 47.0f, 0.0f); + GlStateManager.translate(7.0f, 25.0f + offset, 0.0f); GlStateManager.scale(0.75f, 0.75f, 0.75f); str = I18n.format("lanServer.showCode"); int w = fontRendererObj.getStringWidth(str); - boolean hover = i > 6 && i < 8 + w * 3 / 4 && j > 46 && j < 47 + 8; + boolean hover = i > 4 && i < 8 + w * 3 / 4 && j > 24 + offset && j < 25 + offset + 8; drawString(fontRendererObj, EnumChatFormatting.UNDERLINE + str, 0, 0, hover ? 0xEEEEAA : 0xCCCC55); GlStateManager.popMatrix(); } else { int w = fontRendererObj.getStringWidth(str); GlStateManager.pushMatrix(); - GlStateManager.translate(6 + w + 3, 33, 0.0f); + GlStateManager.translate(6 + w + 3, 11 + offset, 0.0f); GlStateManager.scale(0.75f, 0.75f, 0.75f); str = I18n.format("lanServer.hideCode"); int w2 = fontRendererObj.getStringWidth(str); - boolean hover = i > 6 + w + 2 && i < 6 + w + 3 + w2 * 3 / 4 && j > 33 - 1 && j < 33 + 6; + boolean hover = i > 6 + w + 2 && i < 6 + w + 3 + w2 * 3 / 4 && j > 11 + offset - 1 + && j < 11 + offset + 6; drawString(fontRendererObj, EnumChatFormatting.UNDERLINE + str, 0, 0, hover ? 0xEEEEAA : 0xCCCC55); GlStateManager.popMatrix(); - drawString(fontRendererObj, EnumChatFormatting.GRAY + I18n.format("lanServer.pauseMenu1") + " " - + EnumChatFormatting.RESET + LANServerController.getCurrentURI(), 6, 47, 0xFFFFFF); - drawString(fontRendererObj, EnumChatFormatting.GRAY + I18n.format("lanServer.pauseMenu2") + " " - + EnumChatFormatting.RESET + LANServerController.getCurrentCode(), 6, 57, 0xFFFFFF); + drawString( + fontRendererObj, EnumChatFormatting.GRAY + I18n.format("lanServer.pauseMenu1") + " " + + EnumChatFormatting.RESET + LANServerController.getCurrentURI(), + 6, 25 + offset, 0xFFFFFF); + drawString( + fontRendererObj, EnumChatFormatting.GRAY + I18n.format("lanServer.pauseMenu2") + " " + + EnumChatFormatting.RESET + LANServerController.getCurrentCode(), + 6, 35 + offset, 0xFFFFFF); } } - super.drawScreen(i, j, f); + try { + if (EagRuntime.getConfiguration().isAllowVoiceClient() + && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { + if (voiceMenu.isBlockingInput()) { + super.drawScreen(0, 0, f); + } else { + super.drawScreen(i, j, f); + } + voiceMenu.drawScreen(i, j, f); + } else { + super.drawScreen(i, j, f); + } + } catch (GuiVoiceMenu.AbortedException ex) { + } + } + + protected void keyTyped(char par1, int par2) { + try { + if (EagRuntime.getConfiguration().isAllowVoiceClient() + && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { + voiceMenu.keyTyped(par1, par2); + } + super.keyTyped(par1, par2); + } catch (GuiVoiceMenu.AbortedException ex) { + } } public void confirmClicked(boolean par1, int par2) { @@ -208,11 +249,20 @@ public class GuiIngameMenu extends GuiScreen { } protected void mouseClicked(int par1, int par2, int par3) { + try { + if (EagRuntime.getConfiguration().isAllowVoiceClient() + && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { + voiceMenu.mouseClicked(par1, par2, par3); + } + } catch (GuiVoiceMenu.AbortedException ex) { + return; + } if (par3 == 0) { + int offset = this.updateCheckerOverlay.getSharedWorldInfoYOffset(); if (mc.gameSettings.hideJoinCode) { String str = I18n.format("lanServer.showCode"); int w = fontRendererObj.getStringWidth(str); - if (par1 > 6 && par1 < 8 + w * 3 / 4 && par2 > 46 && par2 < 47 + 8) { + if (par1 > 4 && par1 < 8 + w * 3 / 4 && par2 > 24 + offset && par2 < 25 + offset + 8) { mc.gameSettings.hideJoinCode = false; this.mc.getSoundHandler() .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); @@ -223,7 +273,8 @@ public class GuiIngameMenu extends GuiScreen { int w = fontRendererObj.getStringWidth(str); str = I18n.format("lanServer.hideCode"); int w2 = fontRendererObj.getStringWidth(str); - if (par1 > 6 + w + 2 && par1 < 6 + w + 3 + w2 * 3 / 4 && par2 > 33 - 1 && par2 < 33 + 6) { + if (par1 > 6 + w + 2 && par1 < 6 + w + 3 + w2 * 3 / 4 && par2 > 11 + offset - 1 + && par2 < 11 + offset + 6) { mc.gameSettings.hideJoinCode = true; this.mc.getSoundHandler() .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); @@ -235,4 +286,22 @@ public class GuiIngameMenu extends GuiScreen { this.updateCheckerOverlay.mouseClicked(par1, par2, par3); super.mouseClicked(par1, par2, par3); } + + public void setWorldAndResolution(Minecraft par1Minecraft, int par2, int par3) { + super.setWorldAndResolution(par1Minecraft, par2, par3); + if (EagRuntime.getConfiguration().isAllowVoiceClient()) { + voiceMenu.setResolution(par1Minecraft, par2, par3); + } + } + + protected void mouseReleased(int par1, int par2, int par3) { + try { + if (EagRuntime.getConfiguration().isAllowVoiceClient() + && (!mc.isSingleplayer() || LANServerController.isHostingLAN())) { + voiceMenu.mouseReleased(par1, par2, par3); + } + super.mouseReleased(par1, par2, par3); + } catch (GuiVoiceMenu.AbortedException ex) { + } + } } \ No newline at end of file diff --git a/src/teavm/java/net/minecraft/client/gui/GuiScreen.java b/src/teavm/java/net/minecraft/client/gui/GuiScreen.java index d512358..0165809 100644 --- a/src/teavm/java/net/minecraft/client/gui/GuiScreen.java +++ b/src/teavm/java/net/minecraft/client/gui/GuiScreen.java @@ -678,4 +678,8 @@ public abstract class GuiScreen extends Gui implements GuiYesNoCallback { public boolean shouldHangupIntegratedServer() { return true; } + + public boolean blockPTTKey() { + return false; + } } \ No newline at end of file diff --git a/src/teavm/java/net/minecraft/client/network/NetHandlerPlayClient.java b/src/teavm/java/net/minecraft/client/network/NetHandlerPlayClient.java index a15a956..8020d40 100644 --- a/src/teavm/java/net/minecraft/client/network/NetHandlerPlayClient.java +++ b/src/teavm/java/net/minecraft/client/network/NetHandlerPlayClient.java @@ -16,12 +16,15 @@ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; import com.google.common.collect.Maps; import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +import net.lax1dude.eaglercraft.v1_8.profile.CapePackets; +import net.lax1dude.eaglercraft.v1_8.profile.ServerCapeCache; import net.lax1dude.eaglercraft.v1_8.profile.ServerSkinCache; import net.lax1dude.eaglercraft.v1_8.profile.SkinPackets; import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; import net.lax1dude.eaglercraft.v1_8.sp.lan.LANClientNetworkManager; import net.lax1dude.eaglercraft.v1_8.sp.socket.ClientIntegratedServerNetworkManager; import net.lax1dude.eaglercraft.v1_8.update.UpdateService; +import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFolderResourcePack; @@ -245,6 +248,8 @@ public class NetHandlerPlayClient extends ModData implements INetHandlerPlayClie */ private final EaglercraftRandom avRandomizer = new EaglercraftRandom(); private final ServerSkinCache skinCache; + private final ServerCapeCache capeCache; + public boolean currentFNAWSkinAllowedState = true; public NetHandlerPlayClient(Minecraft mcIn, GuiScreen parGuiScreen, EaglercraftNetworkManager parNetworkManager, GameProfile parGameProfile) { @@ -253,6 +258,7 @@ public class NetHandlerPlayClient extends ModData implements INetHandlerPlayClie this.netManager = parNetworkManager; this.profile = parGameProfile; this.skinCache = new ServerSkinCache(parNetworkManager, mcIn.getTextureManager()); + this.capeCache = new ServerCapeCache(parNetworkManager, mcIn.getTextureManager()); this.isIntegratedServer = (parNetworkManager instanceof ClientIntegratedServerNetworkManager) || (parNetworkManager instanceof LANClientNetworkManager); } @@ -264,11 +270,16 @@ public class NetHandlerPlayClient extends ModData implements INetHandlerPlayClie public void cleanup() { this.clientWorldController = null; this.skinCache.destroy(); + this.capeCache.destroy(); } public ServerSkinCache getSkinCache() { return this.skinCache; } + + public ServerCapeCache getCapeCache() { + return this.capeCache; + } public void loadModData(BaseData data) { doneLoadingTerrain = data.getBoolean("doneLoadingTerrain"); @@ -441,6 +452,10 @@ public class NetHandlerPlayClient extends ModData implements INetHandlerPlayClie this.gameController.gameSettings.sendSettingsToServer(); this.netManager.sendPacket(new C17PacketCustomPayload("MC|Brand", (new PacketBuffer(Unpooled.buffer())).writeString(ClientBrandRetriever.getClientModName()))); + if (VoiceClientController.isClientSupported()) { + VoiceClientController.initializeVoiceClient((pkt) -> this.netManager + .sendPacket(new C17PacketCustomPayload(VoiceClientController.SIGNAL_CHANNEL, pkt))); + } } /**+ @@ -863,6 +878,9 @@ public class NetHandlerPlayClient extends ModData implements INetHandlerPlayClie * describing the reason for termination */ public void onDisconnect(IChatComponent ichatcomponent) { + VoiceClientController.handleServerDisconnect(); + Minecraft.getMinecraft().getRenderManager() + .setEnableFNAWSkins(this.gameController.gameSettings.enableFNAWSkins); if (this.gameController.theWorld != null) { this.gameController.loadWorld((WorldClient) null); } @@ -1975,6 +1993,7 @@ public class NetHandlerPlayClient extends ModData implements INetHandlerPlayClie EaglercraftUUID uuid = s38packetplayerlistitem$addplayerdata.getProfile().getId(); this.playerInfoMap.remove(uuid); this.skinCache.evictSkin(uuid); + this.capeCache.evictCape(uuid); } else { NetworkPlayerInfo networkplayerinfo = (NetworkPlayerInfo) this.playerInfoMap .get(s38packetplayerlistitem$addplayerdata.getProfile().getId()); @@ -2168,6 +2187,13 @@ public class NetHandlerPlayClient extends ModData implements INetHandlerPlayClie logger.error("Couldn't read EAG|Skins-1.8 packet!"); logger.error(e); } + } else if ("EAG|Capes-1.8".equals(packetIn.getChannelName())) { + try { + CapePackets.readPluginMessage(packetIn.getBufferData(), capeCache); + } catch (IOException e) { + logger.error("Couldn't read EAG|Capes-1.8 packet!"); + logger.error(e); + } } else if ("EAG|UpdateCert-1.8".equals(packetIn.getChannelName())) { if (EagRuntime.getConfiguration().allowUpdateSvc()) { try { @@ -2180,6 +2206,14 @@ public class NetHandlerPlayClient extends ModData implements INetHandlerPlayClie logger.error(e); } } + } else if (VoiceClientController.SIGNAL_CHANNEL.equals(packetIn.getChannelName())) { + if (VoiceClientController.isClientSupported()) { + VoiceClientController.handleVoiceSignalPacket(packetIn.getBufferData()); + } + } else if ("EAG|FNAWSEn-1.8".equals(packetIn.getChannelName())) { + this.currentFNAWSkinAllowedState = packetIn.getBufferData().readBoolean(); + Minecraft.getMinecraft().getRenderManager().setEnableFNAWSkins( + this.currentFNAWSkinAllowedState && Minecraft.getMinecraft().gameSettings.enableFNAWSkins); } } diff --git a/src/teavm/java/net/minecraft/client/settings/GameSettings.java b/src/teavm/java/net/minecraft/client/settings/GameSettings.java index 6935ded..ff6e6c4 100644 --- a/src/teavm/java/net/minecraft/client/settings/GameSettings.java +++ b/src/teavm/java/net/minecraft/client/settings/GameSettings.java @@ -20,6 +20,7 @@ import net.PeytonPlayz585.shadow.Lang; import net.eaglerforge.api.BaseData; import net.eaglerforge.api.ModData; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager; +import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController; import org.json.JSONArray; import com.google.common.collect.ImmutableSet; @@ -35,6 +36,7 @@ import net.lax1dude.eaglercraft.v1_8.EaglerZLIB; import net.lax1dude.eaglercraft.v1_8.HString; import net.lax1dude.eaglercraft.v1_8.Keyboard; import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformType; import net.lax1dude.eaglercraft.v1_8.internal.KeyboardConstants; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.Logger; @@ -112,7 +114,7 @@ public class GameSettings extends ModData { public boolean chatLinksPrompt = true; public float chatOpacity = 1.0F; public boolean snooperEnabled = true; - public boolean enableVsync = true; + public boolean enableVsync = EagRuntime.getPlatformType() != EnumPlatformType.DESKTOP; public boolean allowBlockAlternatives = true; public boolean reducedDebugInfo = false; public boolean hideServerAddress; @@ -267,6 +269,12 @@ public class GameSettings extends ModData { public boolean ofSmoothBiomes = true; public boolean ofCustomColors = true; public boolean hidePassword = true; + public boolean enableFNAWSkins = true; + + public int voiceListenRadius = 16; + public float voiceListenVolume = 0.5f; + public float voiceSpeakVolume = 0.5f; + public int voicePTTKey = 47; // V public GameSettings(Minecraft mcIn) { this.keyBindings = (KeyBinding[]) ArrayUtils.addAll(new KeyBinding[] { this.keyBindAttack, this.keyBindUseItem, @@ -1060,6 +1068,15 @@ public class GameSettings extends ModData { if (parOptions == GameSettings.Options.HIDE_PASSWORD) { hidePassword =! hidePassword; } + + if (parOptions == GameSettings.Options.FNAW_SKINS) { + this.enableFNAWSkins = !this.enableFNAWSkins; + this.mc.getRenderManager().setEnableFNAWSkins(this.mc.getEnableFNAWSkins()); + } + + if (parOptions == GameSettings.Options.EAGLER_VSYNC) { + this.enableVsync = !this.enableVsync; + } this.saveOptions(); } @@ -1181,6 +1198,10 @@ public class GameSettings extends ModData { return this.ofSmoothBiomes; case CUSTOM_COLORS: return this.ofCustomColors; + case FNAW_SKINS: + return this.enableFNAWSkins; + case EAGLER_VSYNC: + return this.enableVsync; default: return false; } @@ -1636,7 +1657,7 @@ public class GameSettings extends ModData { this.snooperEnabled = astring[1].equals("true"); } - if (astring[0].equals("enableVsync")) { + if (astring[0].equals("enableVsyncEag")) { this.enableVsync = astring[1].equals("true"); } @@ -2031,7 +2052,21 @@ public class GameSettings extends ModData { hidePassword = Boolean.valueOf(astring[1]).booleanValue(); } - Keyboard.setFunctionKeyModifier(keyBindFunction.getKeyCode()); + if (astring[0].equals("voiceListenRadius")) { + voiceListenRadius = Integer.parseInt(astring[1]); + } + + if (astring[0].equals("voiceListenVolume")) { + voiceListenVolume = this.parseFloat(astring[1]); + } + + if (astring[0].equals("voiceSpeakVolume")) { + voiceSpeakVolume = this.parseFloat(astring[1]); + } + + if (astring[0].equals("voicePTTKey")) { + voicePTTKey = Integer.parseInt(astring[1]); + } for (SoundCategory soundcategory : SoundCategory._VALUES) { if (astring[0].equals("soundCategory_" + soundcategory.getCategoryName())) { @@ -2044,6 +2079,10 @@ public class GameSettings extends ModData { this.setModelPartEnabled(enumplayermodelparts, astring[1].equals("true")); } } + + if (astring[0].equals("enableFNAWSkins")) { + this.enableFNAWSkins = astring[1].equals("true"); + } deferredShaderConf.readOption(astring[0], astring[1]); } catch (Exception var8) { @@ -2052,8 +2091,16 @@ public class GameSettings extends ModData { } KeyBinding.resetKeyBindingArrayAndHash(); + + Keyboard.setFunctionKeyModifier(keyBindFunction.getKeyCode()); + VoiceClientController.setVoiceListenVolume(voiceListenVolume); + VoiceClientController.setVoiceSpeakVolume(voiceSpeakVolume); + VoiceClientController.setVoiceProximity(voiceListenRadius); + if (this.mc.getRenderManager() != null) + this.mc.getRenderManager().setEnableFNAWSkins(this.enableFNAWSkins); } catch (Exception exception) { - logger.error("Failed to load options", exception); + logger.error("Failed to load options"); + logger.error(exception); } } @@ -2120,7 +2167,7 @@ public class GameSettings extends ModData { printwriter.println("chatLinksPrompt:" + this.chatLinksPrompt); printwriter.println("chatOpacity:" + this.chatOpacity); printwriter.println("snooperEnabled:" + this.snooperEnabled); - printwriter.println("enableVsync:" + this.enableVsync); + printwriter.println("enableVsyncEag:" + this.enableVsync); printwriter.println("hideServerAddress:" + this.hideServerAddress); printwriter.println("advancedItemTooltips:" + this.advancedItemTooltips); printwriter.println("pauseOnLostFocus:" + this.pauseOnLostFocus); @@ -2209,6 +2256,11 @@ public class GameSettings extends ModData { printwriter.println("ofSmoothBiomes:" + this.ofSmoothBiomes); printwriter.println("ofCustomColors:" + this.ofCustomColors); printwriter.println("hidePassword:" + hidePassword); + printwriter.println("voiceListenRadius:" + this.voiceListenRadius); + printwriter.println("voiceListenVolume:" + this.voiceListenVolume); + printwriter.println("voiceSpeakVolume:" + this.voiceSpeakVolume); + printwriter.println("voicePTTKey:" + this.voicePTTKey); + printwriter.println("enableFNAWSkins:" + this.enableFNAWSkins); for (KeyBinding keybinding : this.keyBindings) { printwriter.println("key_" + keybinding.getKeyDescription() + ":" + keybinding.getKeyCode()); @@ -2231,7 +2283,8 @@ public class GameSettings extends ModData { printwriter.close(); return bao.toByteArray(); } catch (Exception exception) { - logger.error("Failed to save options", exception); + logger.error("Failed to save options"); + logger.error(exception); return null; } @@ -2417,7 +2470,9 @@ public class GameSettings extends ModData { SWAMP_COLORS("Swamp Colors", false, false), SMOOTH_BIOMES("Smooth Biomes", false, false), CUSTOM_COLORS("Custom Colors", false, false), - HIDE_PASSWORD("Hide Password", false, false); + HIDE_PASSWORD("Hide Password", false, false), + FNAW_SKINS("options.skinCustomisation.enableFNAWSkins", false, true), + EAGLER_VSYNC("options.vsync", false, true); private final boolean enumFloat; private final boolean enumBoolean;