From df4c705f600eea597c031d912e7095ffb5aa7e73 Mon Sep 17 00:00:00 2001 From: eaglercraft Date: Thu, 26 Sep 2024 22:34:10 -0700 Subject: [PATCH] u39 --- CompileJS.bat | 2 +- EAGLERCRAFTX_README.md | 1 + resources/resources/EPKVersionIdentifier.txt | 1 + .../opts_template_eaglercraftX_1_8.txt | 3 +- .../opts_template_eaglercraftX_1_8_demo.txt | 3 +- ...template_eaglercraftX_1_8_html5Cursors.txt | 3 +- .../assets/minecraft/lang/en_US.lang | 8 + .../java/net/minecraft/client/Minecraft.java | 2 +- .../client/settings/GameSettings.java | 6 + .../v1_8/internal/PlatformInput.java | 6 +- .../lax1dude/eaglercraft/v1_8/ArrayUtils.java | 2 +- .../eaglercraft/v1_8/EaglercraftVersion.java | 8 +- .../minecraft/EaglerFolderResourcePack.java | 6 +- .../v1_8/profile/EaglerProfile.java | 13 +- .../profile/GuiScreenDefaultUsernameNote.java | 61 + .../v1_8/profile/GuiScreenEditProfile.java | 1100 +++++++++-------- .../boot_menu/teavm/BootMenuEntryPoint.java | 7 + .../v1_8/internal/PlatformInput.java | 8 +- .../v1_8/internal/PlatformRuntime.java | 36 +- .../internal/teavm/EPKDownloadHelper.java | 160 +++ .../teavm/TeaVMClientConfigAdapter.java | 8 + .../v1_8/internal/teavm/TeaVMFetchJS.java | 2 +- .../teavm/opts/JSEaglercraftXOptsRoot.java | 3 + 23 files changed, 860 insertions(+), 589 deletions(-) create mode 100755 resources/resources/EPKVersionIdentifier.txt create mode 100755 src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenDefaultUsernameNote.java create mode 100755 src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/EPKDownloadHelper.java diff --git a/CompileJS.bat b/CompileJS.bat index 7969fc9..b7f7fd2 100644 --- a/CompileJS.bat +++ b/CompileJS.bat @@ -1,4 +1,4 @@ @echo off title gradlew generateJavascript -gradlew generateJavascript +call gradlew generateJavascript pause \ No newline at end of file diff --git a/EAGLERCRAFTX_README.md b/EAGLERCRAFTX_README.md index 7a33236..5f36b25 100755 --- a/EAGLERCRAFTX_README.md +++ b/EAGLERCRAFTX_README.md @@ -196,6 +196,7 @@ The default eaglercraftXOpts values is this: - `eaglerNoDelay:` can be used to disable "Vigg's Algorithm", an algorithm that delays and combines multiple EaglercraftX packets together if they are sent in the same tick (does not affect regular Minecraft 1.8 packets) - `ramdiskMode:` if worlds and resource packs should be stored in RAM instead of IndexedDB - `singleThreadMode:` if the game should run the client and integrated server in the same context instead of creating a worker object +- `enableEPKVersionCheck:` if the game should attempt to bypass the browser's cache and retry downloading assets.epk when its outdated - `hooks:` can be used to define JavaScript callbacks for certain events * `localStorageSaved:` JavaScript callback to save local storage keys (key, data) * `localStorageLoaded:` JavaScript callback to load local storage keys (key) returns data diff --git a/resources/resources/EPKVersionIdentifier.txt b/resources/resources/EPKVersionIdentifier.txt new file mode 100755 index 0000000..60582c5 --- /dev/null +++ b/resources/resources/EPKVersionIdentifier.txt @@ -0,0 +1 @@ +u39 \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8.txt b/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8.txt index bfe059b..f59d15c 100755 --- a/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8.txt +++ b/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8.txt @@ -62,5 +62,6 @@ "disableBlobURLs": false, "eaglerNoDelay": false, "ramdiskMode": false, - "singleThreadMode": false + "singleThreadMode": false, + "enableEPKVersionCheck": true } \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8_demo.txt b/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8_demo.txt index 00c65c3..86218ab 100755 --- a/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8_demo.txt +++ b/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8_demo.txt @@ -62,5 +62,6 @@ "disableBlobURLs": false, "eaglerNoDelay": false, "ramdiskMode": false, - "singleThreadMode": false + "singleThreadMode": false, + "enableEPKVersionCheck": true } \ No newline at end of file diff --git a/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8_html5Cursors.txt b/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8_html5Cursors.txt index 9743e60..f8b9915 100755 --- a/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8_html5Cursors.txt +++ b/resources/resources/assets/eagler/boot_menu/opts_template_eaglercraftX_1_8_html5Cursors.txt @@ -62,5 +62,6 @@ "disableBlobURLs": false, "eaglerNoDelay": false, "ramdiskMode": false, - "singleThreadMode": false + "singleThreadMode": false, + "enableEPKVersionCheck": true } \ No newline at end of file diff --git a/resources/resources/assets/minecraft/lang/en_US.lang b/resources/resources/assets/minecraft/lang/en_US.lang index aed7974..82f9cf8 100644 --- a/resources/resources/assets/minecraft/lang/en_US.lang +++ b/resources/resources/assets/minecraft/lang/en_US.lang @@ -49,6 +49,14 @@ eaglercraft.editCape.clearCape=Clear List eaglercraft.editProfile.importExport=Import/Export +eaglercraft.defaultUsernameDetected.title=Default Username Detected +eaglercraft.defaultUsernameDetected.text0=The username "%s" was auto-generated by Eaglercraft +eaglercraft.defaultUsernameDetected.text1=It may already be taken on the largest servers +eaglercraft.defaultUsernameDetected.text2=Would you like to pick a different username instead? +eaglercraft.defaultUsernameDetected.changeUsername=Change Username +eaglercraft.defaultUsernameDetected.continueAnyway=Continue Anyway +eaglercraft.defaultUsernameDetected.doNotShow=Do not show again + eaglercraft.settingsBackup.importExport.title=What do you wanna do? eaglercraft.settingsBackup.importExport.import=Import Profile and Settings... eaglercraft.settingsBackup.importExport.export=Export Profile and Settings... diff --git a/src/game/java/net/minecraft/client/Minecraft.java b/src/game/java/net/minecraft/client/Minecraft.java index 5589c96..8dd2ac2 100644 --- a/src/game/java/net/minecraft/client/Minecraft.java +++ b/src/game/java/net/minecraft/client/Minecraft.java @@ -1394,7 +1394,7 @@ public class Minecraft implements IThreadListener { int j = Mouse.getEventDWheel(); if (j != 0) { if (this.isZoomKey) { - this.adjustedZoomValue = MathHelper.clamp_float(adjustedZoomValue - j * 4.0f, 5.0f, + this.adjustedZoomValue = MathHelper.clamp_float(adjustedZoomValue - j * 4.0f, 4.0f, 32.0f); } else if (this.thePlayer.isSpectator()) { j = j < 0 ? -1 : 1; diff --git a/src/game/java/net/minecraft/client/settings/GameSettings.java b/src/game/java/net/minecraft/client/settings/GameSettings.java index e5a5755..5ce7054 100644 --- a/src/game/java/net/minecraft/client/settings/GameSettings.java +++ b/src/game/java/net/minecraft/client/settings/GameSettings.java @@ -226,6 +226,7 @@ public class GameSettings { public boolean enableProfanityFilter = false; public boolean hasShownProfanityFilter = false; public float touchControlOpacity = 1.0f; + public boolean hideDefaultUsernameWarning = false; public int voiceListenRadius = 16; public float voiceListenVolume = 0.5f; @@ -1168,6 +1169,10 @@ public class GameSettings { touchControlOpacity = parseFloat(astring[1]); } + if (astring[0].equals("hideDefaultUsernameWarning")) { + this.hideDefaultUsernameWarning = astring[1].equals("true"); + } + deferredShaderConf.readOption(astring[0], astring[1]); } catch (Exception var8) { logger.warn("Skipping bad option: " + s); @@ -1315,6 +1320,7 @@ public class GameSettings { printwriter.println("screenRecordGameVolume:" + this.screenRecordGameVolume); printwriter.println("screenRecordMicVolume:" + this.screenRecordMicVolume); printwriter.println("touchControlOpacity:" + this.touchControlOpacity); + printwriter.println("hideDefaultUsernameWarning:" + this.hideDefaultUsernameWarning); for (KeyBinding keybinding : this.keyBindings) { printwriter.println("key_" + keybinding.getKeyDescription() + ":" + keybinding.getKeyCode()); 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 fc5a43e..93288ac 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 @@ -479,7 +479,11 @@ public class PlatformInput { } public static int mouseGetEventDWheel() { - return (int)currentMouseEvent.wheel; + return fixWheel(currentMouseEvent.wheel); + } + + private static int fixWheel(float val) { + return (val > 0.0f ? 1 : (val < 0.0f ? -1 : 0)); } public static int mouseGetX() { diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/ArrayUtils.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/ArrayUtils.java index 8335246..7a90f35 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/ArrayUtils.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/ArrayUtils.java @@ -61,7 +61,7 @@ public class ArrayUtils { public static String hexString(byte[] bytesIn) { char[] ret = new char[bytesIn.length << 1]; for(int i = 0; i < bytesIn.length; ++i) { - ret[i << 1] = hex.charAt((bytesIn[i] >> 4) & 15); + ret[i << 1] = hex.charAt((bytesIn[i] >>> 4) & 15); ret[(i << 1) + 1] = hex.charAt(bytesIn[i] & 15); } return new String(ret); 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 7d87c5e..73477cf 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java @@ -20,10 +20,14 @@ public class EaglercraftVersion { public static final String projectOriginName = "EaglercraftX"; public static final String projectOriginAuthor = "lax1dude"; public static final String projectOriginRevision = "1.8"; - public static final String projectOriginVersion = "u38"; + public static final String projectOriginVersion = "u39"; public static final String projectOriginURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8"; // rest in peace - + + // EPK Version Identifier + + public static final String EPKVersionIdentifier = "u39"; // Set to null to disable EPK version check + // Updating configuration public static final boolean enableUpdateService = true; diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFolderResourcePack.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFolderResourcePack.java index 633492d..519c668 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFolderResourcePack.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/minecraft/EaglerFolderResourcePack.java @@ -271,6 +271,7 @@ public class EaglerFolderResourcePack extends AbstractResourcePack { public static void loadRemoteResourcePack(String url, String hash, Consumer cb, Consumer ast, Runnable loading) { if (!isSupported || !hash.matches("^[a-f0-9]{40}$")) { + logger.error("Invalid character in resource pack hash! (is it lowercase?)"); cb.accept(null); return; } @@ -292,8 +293,9 @@ public class EaglerFolderResourcePack extends AbstractResourcePack { digest.update(arr, 0, arr.length); byte[] hashOut = new byte[20]; digest.doFinal(hashOut, 0); - if(!hash.equals(ArrayUtils.hexString(hashOut))) { - logger.error("Downloaded resource pack hash does not equal expected resource pack hash!"); + String hashOutStr = ArrayUtils.hexString(hashOut); + if(!hash.equals(hashOutStr)) { + logger.error("Downloaded resource pack hash does not equal expected resource pack hash! ({} != {})", hashOutStr, hash); cb.accept(null); return; } 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 a97aa3b..9fac84b 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 @@ -11,6 +11,7 @@ import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; import net.lax1dude.eaglercraft.v1_8.EaglerOutputStream; import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.HString; import net.minecraft.client.Minecraft; import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTTagCompound; @@ -493,10 +494,9 @@ public class EaglerProfile { rand = new EaglercraftRandom(); do { - username = defaultNames[rand.nextInt(defaultNames.length)] + defaultNames[rand.nextInt(defaultNames.length)] - + (100 + rand.nextInt(900)); - } while (username.length() > 16); - + username = HString.format("%s%s%04d", defaultNames[rand.nextInt(defaultNames.length)], defaultNames[rand.nextInt(defaultNames.length)], rand.nextInt(10000)); + }while(username.length() > 16); + setName(username); do { @@ -506,6 +506,11 @@ public class EaglerProfile { presetCapeId = 0; customCapeId = -1; + + } + + public static boolean isDefaultUsername(String str) { + return str.toLowerCase().matches("^(yeeish|yee|yeer|yeeler|eagler|eagl|darver|darvler|vool|vigg|deev|yigg|yeeg){2}\\d{2,4}$"); } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenDefaultUsernameNote.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenDefaultUsernameNote.java new file mode 100755 index 0000000..ba20525 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenDefaultUsernameNote.java @@ -0,0 +1,61 @@ +package net.lax1dude.eaglercraft.v1_8.profile; + +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class GuiScreenDefaultUsernameNote extends GuiScreen { + + private final GuiScreen back; + private final GuiScreen cont; + + public GuiScreenDefaultUsernameNote(GuiScreen back, GuiScreen cont) { + this.back = back; + this.cont = cont; + } + + public void initGui() { + this.buttonList.clear(); + this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 112, I18n.format("defaultUsernameDetected.changeUsername"))); + this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 6 + 142, I18n.format("defaultUsernameDetected.continueAnyway"))); + this.buttonList.add(new GuiButton(2, this.width / 2 - 100, this.height / 6 + 172, I18n.format("defaultUsernameDetected.doNotShow"))); + } + + public void drawScreen(int par1, int par2, float par3) { + this.drawDefaultBackground(); + this.drawCenteredString(fontRendererObj, I18n.format("defaultUsernameDetected.title"), this.width / 2, 70, 11184810); + this.drawCenteredString(fontRendererObj, I18n.format("defaultUsernameDetected.text0", EaglerProfile.getName()), this.width / 2, 90, 16777215); + this.drawCenteredString(fontRendererObj, I18n.format("defaultUsernameDetected.text1"), this.width / 2, 105, 16777215); + this.drawCenteredString(fontRendererObj, I18n.format("defaultUsernameDetected.text2"), this.width / 2, 120, 16777215); + super.drawScreen(par1, par2, par3); + } + + @Override + protected void actionPerformed(GuiButton parGuiButton) { + if(parGuiButton.id == 0) { + this.mc.displayGuiScreen(back); + }else if(parGuiButton.id == 1) { + this.mc.displayGuiScreen(cont); + }else if(parGuiButton.id == 2) { + this.mc.gameSettings.hideDefaultUsernameWarning = true; + this.mc.gameSettings.saveOptions(); + this.mc.displayGuiScreen(cont); + } + } + +} 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 4910f39..d78e191 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 @@ -1,548 +1,552 @@ -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.PointerInputAbstraction; -import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType; -import net.lax1dude.eaglercraft.v1_8.internal.FileChooserResult; -import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; -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.gui.GuiTextField; -import net.minecraft.client.resources.I18n; -import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.ResourceLocation; - -import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; - -import java.io.IOException; - -/** - * 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 GuiScreenEditProfile extends GuiScreen { - - private final GuiScreen parent; - private GuiTextField usernameField; - - 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 boolean newSkinWaitSteveOrAlex = false; - - private static final ResourceLocation eaglerGui = new ResourceLocation("eagler:gui/eagler_gui.png"); - - protected String screenTitle = "Edit Profile"; - - public GuiScreenEditProfile(GuiScreen parent) { - this.parent = parent; - } - - public void initGui() { - Keyboard.enableRepeatEvents(true); - screenTitle = I18n.format("editProfile.title"); - usernameField = new GuiTextField(0, fontRendererObj, width / 2 - 20 + 1, height / 6 + 24 + 1, 138, 20); - usernameField.setFocused(true); - usernameField.setText(EaglerProfile.getName()); - usernameField.setMaxStringLength(16); - selectedSlot = EaglerProfile.presetSkinId == -1 ? EaglerProfile.customSkinId : (EaglerProfile.presetSkinId + EaglerProfile.customSkins.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 + 110, 71, 20, I18n.format("editProfile.addSkin"))); - buttonList.add(new GuiButton(2, width / 2 - 21 + 71, height / 6 + 110, 72, 20, I18n.format("editProfile.clearSkin"))); - updateOptions(); - } - - private void updateOptions() { - DefaultSkins[] arr = DefaultSkins.defaultSkinsMap; - if(!EagRuntime.getConfiguration().isAllowFNAWSkins()) { - DefaultSkins[] arrNoFNAW = new DefaultSkins[arr.length - 5]; - System.arraycopy(arr, 0, arrNoFNAW, 0, arrNoFNAW.length); - arr = arrNoFNAW; - } - int numCustom = EaglerProfile.customSkins.size(); - String[] n = new String[numCustom + arr.length]; - for(int i = 0; i < numCustom; ++i) { - n[i] = EaglerProfile.customSkins.get(i).name; - } - int numDefault = arr.length; - for(int j = 0; j < numDefault; ++j) { - n[numCustom + j] = arr[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("editProfile.username"), width / 2 - 20, height / 6 + 8, 10526880); - drawString(fontRendererObj, I18n.format("editProfile.playerSkin"), width / 2 - 20, height / 6 + 66, 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); - - GlStateManager.pushMatrix(); - GlStateManager.translate(skinX + 2, skinY - 9, 0.0f); - GlStateManager.scale(0.75f, 0.75f, 0.75f); - - if(selectedSlot > dropDownOptions.length - 1) { - selectedSlot = 0; - } - - 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); - }else { - 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; - 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 + 103; - 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; - - if(newSkinWaitSteveOrAlex && selectedSlot < numberOfCustomSkins) { - skinWidth = 70; - skinHeight = 120; - - CustomSkin newSkin = EaglerProfile.customSkins.get(selectedSlot); - - GlStateManager.clear(GL_DEPTH_BUFFER_BIT); - - skinX = width / 2 - 90; - skinY = height / 4; - xx = skinX + 35; - yy = skinY + 117; - - boolean mouseOver = mx >= skinX && my >= skinY && mx < skinX + skinWidth && my < skinY + skinHeight; - int cc = mouseOver ? 0xFFDDDD99 : 0xFF555555; - - GlStateManager.enableBlend(); - GlStateManager.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - drawRect(0, 0, width, height, 0xbb000000); - drawRect(skinX, skinY, skinX + skinWidth, skinY + skinHeight, 0xbb000000); - GlStateManager.disableBlend(); - - drawRect(skinX, skinY, skinX + 1, skinY + skinHeight, cc); - drawRect(skinX, skinY, skinX + skinWidth, skinY + 1, cc); - drawRect(skinX + skinWidth - 1, skinY, skinX + skinWidth, skinY + skinHeight, cc); - drawRect(skinX, skinY + skinHeight - 1, skinX + skinWidth, skinY + skinHeight, cc); - - if(mouseOver) { - drawCenteredString(fontRendererObj, "Steve", skinX + skinWidth / 2, skinY + skinHeight + 6, cc); - } - - SkinPreviewRenderer.renderPreview(xx, yy, mx, my, false, SkinModel.STEVE, newSkin.getResource(), - EaglerProfile.getActiveCapeResourceLocation()); - - skinX = width / 2 + 20; - skinY = height / 4; - xx = skinX + 35; - yy = skinY + 117; - - mouseOver = mx >= skinX && my >= skinY && mx < skinX + skinWidth && my < skinY + skinHeight; - cc = mouseOver ? 0xFFDDDD99 : 0xFF555555; - - GlStateManager.enableBlend(); - GlStateManager.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - drawRect(skinX, skinY, skinX + skinWidth, skinY + skinHeight, 0xbb000000); - GlStateManager.disableBlend(); - - drawRect(skinX, skinY, skinX + 1, skinY + skinHeight, cc); - drawRect(skinX, skinY, skinX + skinWidth, skinY + 1, cc); - drawRect(skinX + skinWidth - 1, skinY, skinX + skinWidth, skinY + skinHeight, cc); - drawRect(skinX, skinY + skinHeight - 1, skinX + skinWidth, skinY + skinHeight, cc); - - if(mouseOver) { - drawCenteredString(fontRendererObj, "Alex", skinX + skinWidth / 2, skinY + skinHeight + 8, cc); - } - - SkinPreviewRenderer.renderPreview(xx, yy, mx, my, false, SkinModel.ALEX, newSkin.getResource(), - EaglerProfile.getActiveCapeResourceLocation()); - }else { - skinX = this.width / 2 - 120; - skinY = this.height / 6 + 8; - skinWidth = 80; - skinHeight = 130; - - ResourceLocation texture; - if(skid < 0) { - texture = EaglerProfile.customSkins.get(selectedSlot).getResource(); - }else { - texture = DefaultSkins.getSkinFromId(skid).location; - } - - SkinPreviewRenderer.renderPreview(xx, yy, newSkinWaitSteveOrAlex ? width / 2 : mx, - newSkinWaitSteveOrAlex ? height / 2 : my, false, selectedSkinModel, texture, - EaglerProfile.getActiveCapeResourceLocation()); - } - - } - - 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(); - EaglerProfile.save(); - this.mc.displayGuiScreen((GuiScreen) parent); - }else if(par1GuiButton.id == 1) { - EagRuntime.displayFileChooser("image/png", "png"); - }else if(par1GuiButton.id == 2) { - EaglerProfile.clearCustomSkins(); - safeProfile(); - EaglerProfile.save(); - updateOptions(); - selectedSlot = 0; - } - } - } - - public void updateScreen() { - usernameField.updateCursorCounter(); - if(EagRuntime.fileChooserHasResult()) { - FileChooserResult result = EagRuntime.getFileChooserResult(); - if(result != null) { - ImageData loadedSkin = ImageData.loadImageFile(result.fileData, ImageData.getMimeFromType(result.fileName)); - if(loadedSkin != null) { - boolean isLegacy = loadedSkin.width == 64 && loadedSkin.height == 32; - boolean isModern = loadedSkin.width == 64 && loadedSkin.height == 64; - if(isLegacy) { - ImageData newSkin = new ImageData(64, 64, true); - SkinConverter.convert64x32to64x64(loadedSkin, newSkin); - loadedSkin = newSkin; - isModern = true; - } - if(isModern) { - byte[] rawSkin = new byte[16384]; - for(int i = 0, j, k; i < 4096; ++i) { - j = i << 2; - k = loadedSkin.pixels[i]; - rawSkin[j] = (byte)(k >>> 24); - rawSkin[j + 1] = (byte)(k >>> 16); - rawSkin[j + 2] = (byte)(k >>> 8); - rawSkin[j + 3] = (byte)(k & 0xFF); - } - for(int y = 20; y < 32; ++y) { - for(int x = 16; x < 40; ++x) { - rawSkin[(y << 8) | (x << 2)] = (byte)0xff; - } - } - int k; - if((k = EaglerProfile.addCustomSkin(result.fileName, rawSkin)) != -1) { - selectedSlot = k; - 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"); - } - }else { - EagRuntime.showPopup("The selected file '" + result.fileName + "' is not a supported format!"); - } - } - } - if(dropDownOpen) { - if(PointerInputAbstraction.getVCursorButtonDown(0)) { - int skinX = width / 2 - 20; - int skinY = height / 6 + 103; - 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) { - usernameField.textboxKeyTyped(c, k); - - String text = usernameField.getText(); - if(text.length() > 16) text = text.substring(0, 16); - text = text.replaceAll("[^A-Za-z0-9]", "_"); - usernameField.updateText(text); - - 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) { - 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) { - skinX = width / 2 - 90; - skinY = height / 4; - int skinWidth = 70; - int skinHeight = 120; - if(mx >= skinX && my >= skinY && mx < skinX + skinWidth && my < skinY + skinHeight) { - if(selectedSlot < EaglerProfile.customSkins.size()) { - newSkinWaitSteveOrAlex = false; - EaglerProfile.customSkins.get(selectedSlot).model = SkinModel.STEVE; - safeProfile(); - } - return; - } - skinX = width / 2 + 20; - skinY = height / 4; - if(mx >= skinX && my >= skinY && mx < skinX + skinWidth && my < skinY + skinHeight) { - if(selectedSlot < EaglerProfile.customSkins.size()) { - EaglerProfile.customSkins.get(selectedSlot).model = SkinModel.ALEX; - newSkinWaitSteveOrAlex = false; - safeProfile(); - } - } - return; - }else if(selectedSlot < EaglerProfile.customSkins.size()) { - 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) { - if(selectedSlot < EaglerProfile.customSkins.size()) { - newSkinWaitSteveOrAlex = true; - return; - } - } - } - skinX = width / 2 + 140 - 40; - skinY = height / 6 + 82; - - if(mx >= skinX && mx < (skinX + 20) && my >= skinY && my < (skinY + 22)) { - dropDownOpen = !dropDownOpen; - return; - } - - skinX = width / 2 - 20; - skinY = height / 6 + 82; - 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; - dropDownOpen = false; - dragging = false; - return; - } - } - } - } - } - super.mouseClicked(mx, my, button); - } - - protected void safeProfile() { - int customLen = EaglerProfile.customSkins.size(); - if(selectedSlot < customLen) { - EaglerProfile.presetSkinId = -1; - EaglerProfile.customSkinId = selectedSlot; - }else { - EaglerProfile.presetSkinId = selectedSlot - customLen; - EaglerProfile.customSkinId = -1; - } - String name = usernameField.getText().trim(); - while(name.length() < 3) { - name = name + "_"; - } - if(name.length() > 16) { - name = name.substring(0, 16); - } - EaglerProfile.setName(name); - } - - @Override - public boolean showCopyPasteButtons() { - return usernameField.isFocused(); - } - - @Override - public void fireInputEvent(EnumInputEvent event, String param) { - usernameField.fireInputEvent(event, param); - } - -} +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.PointerInputAbstraction; +import net.lax1dude.eaglercraft.v1_8.internal.EnumCursorType; +import net.lax1dude.eaglercraft.v1_8.internal.FileChooserResult; +import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent; +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.gui.GuiTextField; +import net.minecraft.client.resources.I18n; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.ResourceLocation; + +import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +import java.io.IOException; + +/** + * 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 GuiScreenEditProfile extends GuiScreen { + + private final GuiScreen parent; + private GuiTextField usernameField; + + 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 boolean newSkinWaitSteveOrAlex = false; + + private static final ResourceLocation eaglerGui = new ResourceLocation("eagler:gui/eagler_gui.png"); + + protected String screenTitle = "Edit Profile"; + + public GuiScreenEditProfile(GuiScreen parent) { + this.parent = parent; + } + + public void initGui() { + Keyboard.enableRepeatEvents(true); + screenTitle = I18n.format("editProfile.title"); + usernameField = new GuiTextField(0, fontRendererObj, width / 2 - 20 + 1, height / 6 + 24 + 1, 138, 20); + usernameField.setFocused(true); + usernameField.setText(EaglerProfile.getName()); + usernameField.setMaxStringLength(16); + selectedSlot = EaglerProfile.presetSkinId == -1 ? EaglerProfile.customSkinId : (EaglerProfile.presetSkinId + EaglerProfile.customSkins.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 + 110, 71, 20, I18n.format("editProfile.addSkin"))); + buttonList.add(new GuiButton(2, width / 2 - 21 + 71, height / 6 + 110, 72, 20, I18n.format("editProfile.clearSkin"))); + updateOptions(); + } + + private void updateOptions() { + DefaultSkins[] arr = DefaultSkins.defaultSkinsMap; + if(!EagRuntime.getConfiguration().isAllowFNAWSkins()) { + DefaultSkins[] arrNoFNAW = new DefaultSkins[arr.length - 5]; + System.arraycopy(arr, 0, arrNoFNAW, 0, arrNoFNAW.length); + arr = arrNoFNAW; + } + int numCustom = EaglerProfile.customSkins.size(); + String[] n = new String[numCustom + arr.length]; + for(int i = 0; i < numCustom; ++i) { + n[i] = EaglerProfile.customSkins.get(i).name; + } + int numDefault = arr.length; + for(int j = 0; j < numDefault; ++j) { + n[numCustom + j] = arr[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("editProfile.username"), width / 2 - 20, height / 6 + 8, 10526880); + drawString(fontRendererObj, I18n.format("editProfile.playerSkin"), width / 2 - 20, height / 6 + 66, 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); + + GlStateManager.pushMatrix(); + GlStateManager.translate(skinX + 2, skinY - 9, 0.0f); + GlStateManager.scale(0.75f, 0.75f, 0.75f); + + if(selectedSlot > dropDownOptions.length - 1) { + selectedSlot = 0; + } + + 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); + }else { + 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; + 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 + 103; + 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; + + if(newSkinWaitSteveOrAlex && selectedSlot < numberOfCustomSkins) { + skinWidth = 70; + skinHeight = 120; + + CustomSkin newSkin = EaglerProfile.customSkins.get(selectedSlot); + + GlStateManager.clear(GL_DEPTH_BUFFER_BIT); + + skinX = width / 2 - 90; + skinY = height / 4; + xx = skinX + 35; + yy = skinY + 117; + + boolean mouseOver = mx >= skinX && my >= skinY && mx < skinX + skinWidth && my < skinY + skinHeight; + int cc = mouseOver ? 0xFFDDDD99 : 0xFF555555; + + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + drawRect(0, 0, width, height, 0xbb000000); + drawRect(skinX, skinY, skinX + skinWidth, skinY + skinHeight, 0xbb000000); + GlStateManager.disableBlend(); + + drawRect(skinX, skinY, skinX + 1, skinY + skinHeight, cc); + drawRect(skinX, skinY, skinX + skinWidth, skinY + 1, cc); + drawRect(skinX + skinWidth - 1, skinY, skinX + skinWidth, skinY + skinHeight, cc); + drawRect(skinX, skinY + skinHeight - 1, skinX + skinWidth, skinY + skinHeight, cc); + + if(mouseOver) { + drawCenteredString(fontRendererObj, "Steve", skinX + skinWidth / 2, skinY + skinHeight + 6, cc); + } + + SkinPreviewRenderer.renderPreview(xx, yy, mx, my, false, SkinModel.STEVE, newSkin.getResource(), + EaglerProfile.getActiveCapeResourceLocation()); + + skinX = width / 2 + 20; + skinY = height / 4; + xx = skinX + 35; + yy = skinY + 117; + + mouseOver = mx >= skinX && my >= skinY && mx < skinX + skinWidth && my < skinY + skinHeight; + cc = mouseOver ? 0xFFDDDD99 : 0xFF555555; + + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + drawRect(skinX, skinY, skinX + skinWidth, skinY + skinHeight, 0xbb000000); + GlStateManager.disableBlend(); + + drawRect(skinX, skinY, skinX + 1, skinY + skinHeight, cc); + drawRect(skinX, skinY, skinX + skinWidth, skinY + 1, cc); + drawRect(skinX + skinWidth - 1, skinY, skinX + skinWidth, skinY + skinHeight, cc); + drawRect(skinX, skinY + skinHeight - 1, skinX + skinWidth, skinY + skinHeight, cc); + + if(mouseOver) { + drawCenteredString(fontRendererObj, "Alex", skinX + skinWidth / 2, skinY + skinHeight + 8, cc); + } + + SkinPreviewRenderer.renderPreview(xx, yy, mx, my, false, SkinModel.ALEX, newSkin.getResource(), + EaglerProfile.getActiveCapeResourceLocation()); + }else { + skinX = this.width / 2 - 120; + skinY = this.height / 6 + 8; + skinWidth = 80; + skinHeight = 130; + + ResourceLocation texture; + if(skid < 0) { + texture = EaglerProfile.customSkins.get(selectedSlot).getResource(); + }else { + texture = DefaultSkins.getSkinFromId(skid).location; + } + + SkinPreviewRenderer.renderPreview(xx, yy, newSkinWaitSteveOrAlex ? width / 2 : mx, + newSkinWaitSteveOrAlex ? height / 2 : my, false, selectedSkinModel, texture, + EaglerProfile.getActiveCapeResourceLocation()); + } + + } + + 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(); + EaglerProfile.save(); + if(!this.mc.gameSettings.hideDefaultUsernameWarning && EaglerProfile.isDefaultUsername(EaglerProfile.getName())) { + this.mc.displayGuiScreen(new GuiScreenDefaultUsernameNote(this, parent)); + }else { + this.mc.displayGuiScreen(parent); + } + }else if(par1GuiButton.id == 1) { + EagRuntime.displayFileChooser("image/png", "png"); + }else if(par1GuiButton.id == 2) { + EaglerProfile.clearCustomSkins(); + safeProfile(); + EaglerProfile.save(); + updateOptions(); + selectedSlot = 0; + } + } + } + + public void updateScreen() { + usernameField.updateCursorCounter(); + if(EagRuntime.fileChooserHasResult()) { + FileChooserResult result = EagRuntime.getFileChooserResult(); + if(result != null) { + ImageData loadedSkin = ImageData.loadImageFile(result.fileData, ImageData.getMimeFromType(result.fileName)); + if(loadedSkin != null) { + boolean isLegacy = loadedSkin.width == 64 && loadedSkin.height == 32; + boolean isModern = loadedSkin.width == 64 && loadedSkin.height == 64; + if(isLegacy) { + ImageData newSkin = new ImageData(64, 64, true); + SkinConverter.convert64x32to64x64(loadedSkin, newSkin); + loadedSkin = newSkin; + isModern = true; + } + if(isModern) { + byte[] rawSkin = new byte[16384]; + for(int i = 0, j, k; i < 4096; ++i) { + j = i << 2; + k = loadedSkin.pixels[i]; + rawSkin[j] = (byte)(k >>> 24); + rawSkin[j + 1] = (byte)(k >>> 16); + rawSkin[j + 2] = (byte)(k >>> 8); + rawSkin[j + 3] = (byte)(k & 0xFF); + } + for(int y = 20; y < 32; ++y) { + for(int x = 16; x < 40; ++x) { + rawSkin[(y << 8) | (x << 2)] = (byte)0xff; + } + } + int k; + if((k = EaglerProfile.addCustomSkin(result.fileName, rawSkin)) != -1) { + selectedSlot = k; + 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"); + } + }else { + EagRuntime.showPopup("The selected file '" + result.fileName + "' is not a supported format!"); + } + } + } + if(dropDownOpen) { + if(PointerInputAbstraction.getVCursorButtonDown(0)) { + int skinX = width / 2 - 20; + int skinY = height / 6 + 103; + 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) { + usernameField.textboxKeyTyped(c, k); + + String text = usernameField.getText(); + if(text.length() > 16) text = text.substring(0, 16); + text = text.replaceAll("[^A-Za-z0-9]", "_"); + usernameField.updateText(text); + + 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) { + 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) { + skinX = width / 2 - 90; + skinY = height / 4; + int skinWidth = 70; + int skinHeight = 120; + if(mx >= skinX && my >= skinY && mx < skinX + skinWidth && my < skinY + skinHeight) { + if(selectedSlot < EaglerProfile.customSkins.size()) { + newSkinWaitSteveOrAlex = false; + EaglerProfile.customSkins.get(selectedSlot).model = SkinModel.STEVE; + safeProfile(); + } + return; + } + skinX = width / 2 + 20; + skinY = height / 4; + if(mx >= skinX && my >= skinY && mx < skinX + skinWidth && my < skinY + skinHeight) { + if(selectedSlot < EaglerProfile.customSkins.size()) { + EaglerProfile.customSkins.get(selectedSlot).model = SkinModel.ALEX; + newSkinWaitSteveOrAlex = false; + safeProfile(); + } + } + return; + }else if(selectedSlot < EaglerProfile.customSkins.size()) { + 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) { + if(selectedSlot < EaglerProfile.customSkins.size()) { + newSkinWaitSteveOrAlex = true; + return; + } + } + } + skinX = width / 2 + 140 - 40; + skinY = height / 6 + 82; + + if(mx >= skinX && mx < (skinX + 20) && my >= skinY && my < (skinY + 22)) { + dropDownOpen = !dropDownOpen; + return; + } + + skinX = width / 2 - 20; + skinY = height / 6 + 82; + 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; + dropDownOpen = false; + dragging = false; + return; + } + } + } + } + } + super.mouseClicked(mx, my, button); + } + + protected void safeProfile() { + int customLen = EaglerProfile.customSkins.size(); + if(selectedSlot < customLen) { + EaglerProfile.presetSkinId = -1; + EaglerProfile.customSkinId = selectedSlot; + }else { + EaglerProfile.presetSkinId = selectedSlot - customLen; + EaglerProfile.customSkinId = -1; + } + String name = usernameField.getText().trim(); + while(name.length() < 3) { + name = name + "_"; + } + if(name.length() > 16) { + name = name.substring(0, 16); + } + EaglerProfile.setName(name); + } + + @Override + public boolean showCopyPasteButtons() { + return usernameField.isFocused(); + } + + @Override + public void fireInputEvent(EnumInputEvent event, String param) { + usernameField.fireInputEvent(event, param); + } + +} diff --git a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuEntryPoint.java b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuEntryPoint.java index d03bea4..599253d 100755 --- a/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuEntryPoint.java +++ b/src/teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm/BootMenuEntryPoint.java @@ -60,12 +60,14 @@ public class BootMenuEntryPoint { return ((flag & 1) != 0 || IBootMenuConfigAdapter.instance.isShowBootMenuOnLaunch()) && !getHasAlreadyBooted(); } + private static boolean hasInit = false; private static byte[] signatureData = null; private static byte[] bundleData = null; public static void launchMenu(Window parentWindow, HTMLElement parentElement) { signatureData = PlatformUpdateSvc.getClientSignatureData(); bundleData = PlatformUpdateSvc.getClientBundleData(); + hasInit = true; BootMenuMain.launchMenu(parentWindow, parentElement); } @@ -83,6 +85,11 @@ public class BootMenuEntryPoint { } public static boolean isSignedClient() { + if(!hasInit) { + signatureData = PlatformUpdateSvc.getClientSignatureData(); + bundleData = PlatformUpdateSvc.getClientBundleData(); + hasInit = true; + } return signatureData != null && bundleData != null; } 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 0edef7e..541b02c 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 @@ -597,7 +597,7 @@ public class PlatformInput { public void handleEvent(WheelEvent evt) { evt.preventDefault(); evt.stopPropagation(); - double delta = evt.getDeltaY(); + double delta = -evt.getDeltaY(); mouseDWheel += delta; if(hasShownPressAnyKey) { int eventX = (int)(getOffsetX(evt, touchOffsetXTeaVM) * windowDPI); @@ -1246,7 +1246,11 @@ public class PlatformInput { } public static int mouseGetEventDWheel() { - return (currentEvent.type == EVENT_MOUSE_WHEEL) ? (currentEvent.wheel == 0.0f ? 0 : (currentEvent.wheel > 0.0f ? -1 : 1)) : 0; + return (currentEvent.type == EVENT_MOUSE_WHEEL) ? fixWheel(currentEvent.wheel) : 0; + } + + private static int fixWheel(float val) { + return (val > 0.0f ? 1 : (val < 0.0f ? -1 : 0)); } public static int mouseGetX() { 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 9f1096c..2d0d010 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 @@ -5,6 +5,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -13,6 +14,7 @@ import java.util.function.Consumer; import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EagUtils; import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; import net.lax1dude.eaglercraft.v1_8.Filesystem; import net.lax1dude.eaglercraft.v1_8.boot_menu.teavm.BootMenuEntryPoint; import org.teavm.interop.Async; @@ -48,7 +50,6 @@ import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; import net.lax1dude.eaglercraft.v1_8.internal.buffer.EaglerArrayBufferAllocator; import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.EPKLoader; import net.lax1dude.eaglercraft.v1_8.internal.teavm.ES6ShimStatus; import net.lax1dude.eaglercraft.v1_8.internal.teavm.EarlyLoadScreen; import net.lax1dude.eaglercraft.v1_8.internal.teavm.EnumES6ShimStatus; @@ -58,8 +59,8 @@ import net.lax1dude.eaglercraft.v1_8.internal.teavm.ImmediateContinue; import net.lax1dude.eaglercraft.v1_8.internal.teavm.MessageChannel; import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMBlobURLManager; import net.lax1dude.eaglercraft.v1_8.internal.teavm.ClientMain; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.ClientMain.EPKFileEntry; import net.lax1dude.eaglercraft.v1_8.internal.teavm.DebugConsoleWindow; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.EPKDownloadHelper; import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMClientConfigAdapter; import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMDataURLManager; import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMEnterBootMenuException; @@ -433,29 +434,14 @@ public class PlatformRuntime { EarlyLoadScreen.paintScreen(glesVer, PlatformOpenGL.checkVAOCapable(), allowBootMenu); - EPKFileEntry[] epkFiles = ClientMain.configEPKFiles; - - for (int i = 0; i < epkFiles.length; ++i) { - String url = epkFiles[i].url; - String logURL = url.startsWith("data:") ? "" : url; - - logger.info("Downloading: {}", logURL); - - ArrayBuffer epkFileData = downloadRemoteURI(url); - - if (epkFileData == null) { - throw new RuntimeInitializationFailureException("Could not download EPK file \"" + url + "\""); - } - - logger.info("Decompressing: {}", logURL); - - try { - EPKLoader.loadEPK(epkFileData, epkFiles[i].path, PlatformAssets.assets); - } catch (Throwable t) { - throw new RuntimeInitializationFailureException("Could not extract EPK file \"" + url + "\"", t); - } + if(PlatformAssets.assets == null || !PlatformAssets.assets.isEmpty()) { + PlatformAssets.assets = new HashMap<>(); } + EPKDownloadHelper.downloadEPKFilesOfVersion(ClientMain.configEPKFiles, + teavmCfg.isEnableEPKVersionCheckTeaVM() ? EaglercraftVersion.EPKVersionIdentifier : null, + PlatformAssets.assets); + logger.info("Loaded {} resources from EPKs", PlatformAssets.assets.size()); if(allowBootMenu && BootMenuEntryPoint.checkShouldLaunchFlag(win)) { @@ -613,6 +599,10 @@ public class PlatformRuntime { } + public static boolean hasFetchSupportTeaVM() { + return hasFetchSupport; + } + public static void downloadRemoteURIByteArray(String assetPackageURI, final Consumer cb) { downloadRemoteURI(assetPackageURI, arr -> cb.accept(TeaVMUtils.wrapByteArrayBuffer(arr))); } diff --git a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/EPKDownloadHelper.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/EPKDownloadHelper.java new file mode 100755 index 0000000..ac9476e --- /dev/null +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/EPKDownloadHelper.java @@ -0,0 +1,160 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Map; + +import org.teavm.jso.browser.Window; +import org.teavm.jso.typedarrays.ArrayBuffer; + +import net.lax1dude.eaglercraft.v1_8.Base64; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.lax1dude.eaglercraft.v1_8.internal.RuntimeInitializationFailureException; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.ClientMain.EPKFileEntry; +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 EPKDownloadHelper { + + private static final Logger logger = LogManager.getLogger("BrowserRuntime"); + + public static void downloadEPKFilesOfVersion(EPKFileEntry[] epkFiles, String expectedVersionIdentifier, + Map loadedFiles) { + byte[] bTrue = Base64.decodeBase64("true"); + boolean oldEPKInvalidFlag = Arrays.equals(bTrue, PlatformApplication.getLocalStorage("epkInvalidFlag", false)); + boolean epkInvalidFlag = oldEPKInvalidFlag; + attempt_loop: for(int a = 0; a < 3; ++a) { + if(a == 1 && !PlatformRuntime.hasFetchSupportTeaVM()) continue; + loadedFiles.clear(); + boolean canBeInvalid = expectedVersionIdentifier != null; + for(int i = 0; i < epkFiles.length; ++i) { + boolean noCache = false; + String url = null; + switch(a) { + case 0: + url = epkFiles[i].url; + noCache = false; + break; + case 1: + logger.warn("Failed to download one or more correct/valid files, attempting to bypass the browser's cache..."); + url = epkFiles[i].url; + noCache = true; + break; + case 2: + logger.warn("Failed to download one or more correct/valid files, attempting to bypass the server's cache..."); + url = injectCacheInvalidationHack(epkFiles[i].url, expectedVersionIdentifier); + noCache = true; + break; + } + boolean b = url.startsWith("data:"); + boolean c = !b && !url.startsWith("blob:"); + String toCheck = url.indexOf("://") != -1 ? url : PlatformRuntime.win.getLocation().getFullURL(); + boolean canBeCorrupt = c && (a < 1 || toCheck.startsWith("http:") || toCheck.startsWith("https:")); + canBeInvalid &= c; + String logURL = b ? "" : url; + + logger.info("Downloading: {}", logURL); + + ArrayBuffer epkFileData = PlatformRuntime.downloadRemoteURI(url, !noCache); + + if(epkFileData == null) { + if(a < 2 && canBeCorrupt) { + logger.error("Could not download EPK file \"{}\"", logURL); + continue attempt_loop; + }else { + throw new RuntimeInitializationFailureException("Could not download EPK file \"" + logURL + "\""); + } + } + + logger.info("Decompressing: {}", logURL); + + try { + EPKLoader.loadEPK(epkFileData, epkFiles[i].path, loadedFiles); + }catch(Throwable t) { + if(a < 2 && canBeCorrupt) { + logger.error("Could not extract EPK file \"{}\"", logURL); + continue attempt_loop; + }else { + throw new RuntimeInitializationFailureException("Could not extract EPK file \"" + logURL + "\"", t); + } + } + } + if(canBeInvalid) { + byte[] dat = loadedFiles.get("EPKVersionIdentifier.txt"); + if(dat != null) { + String epkStr = (new String(dat, StandardCharsets.UTF_8)).trim(); + if(expectedVersionIdentifier.equals(epkStr)) { + epkInvalidFlag = false; + break; + } + logger.error("EPK version identifier \"{}\" does not match the expected identifier \"{}\"", epkStr, expectedVersionIdentifier); + }else { + logger.error("Version identifier file is missing from the EPK, expecting \"{}\"", expectedVersionIdentifier); + } + if(epkInvalidFlag) { + break; + }else { + if(a < 2) { + continue; + }else { + logger.error("Nothing we can do about this, ignoring the invalid EPK version and setting invalid flag to true"); + epkInvalidFlag = true; + } + } + }else { + epkInvalidFlag = false; + break; + } + } + if(epkInvalidFlag != oldEPKInvalidFlag) { + PlatformApplication.setLocalStorage("epkInvalidFlag", epkInvalidFlag ? bTrue : null, false); + } + } + + private static String injectCacheInvalidationHack(String url, String cacheFixStr) { + if(cacheFixStr != null) { + cacheFixStr = Window.encodeURIComponent(cacheFixStr); + }else { + cacheFixStr = "t" + System.currentTimeMillis(); + } + String toCheck = url.indexOf("://") != -1 ? url : PlatformRuntime.win.getLocation().getFullURL(); + if(toCheck.startsWith("http:") || toCheck.startsWith("https:")) { + int i = url.indexOf('?'); + if(i == url.length() - 1) { + return url + "eaglerCacheFix=" + cacheFixStr; + }else if(i != -1) { + String s = url.substring(i + 1); + if(!s.startsWith("&") && !s.startsWith("#")) { + s = "&" + s; + } + return url.substring(0, i + 1) + "eaglerCacheFix=" + cacheFixStr + s; + }else { + i = url.indexOf('#'); + if(i != -1) { + return url.substring(0, i) + "?eaglerCacheFix=" + cacheFixStr + url.substring(i); + }else { + return url + "?eaglerCacheFix=" + cacheFixStr; + } + } + }else { + return url; + } + } + +} 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 0aaa1fc..d2ef435 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 @@ -87,6 +87,7 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter, IBootMenu private boolean eaglerNoDelay = false; private boolean ramdiskMode = false; private boolean singleThreadMode = false; + private boolean enableEPKVersionCheck = true; public void loadNative(JSObject jsObject) { integratedServerOpts = new JSONObject(); @@ -134,6 +135,7 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter, IBootMenu eaglerNoDelay = eaglercraftXOpts.getEaglerNoDelay(false); ramdiskMode = eaglercraftXOpts.getRamdiskMode(false); singleThreadMode = eaglercraftXOpts.getSingleThreadMode(false); + enableEPKVersionCheck = eaglercraftXOpts.getEnableEPKVersionCheck(true); JSEaglercraftXOptsHooks hooksObj = eaglercraftXOpts.getHooks(); if(hooksObj != null) { hooks.loadHooks(hooksObj); @@ -263,6 +265,7 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter, IBootMenu eaglerNoDelay = eaglercraftOpts.optBoolean("eaglerNoDelay", false); ramdiskMode = eaglercraftOpts.optBoolean("ramdiskMode", false); singleThreadMode = eaglercraftOpts.optBoolean("singleThreadMode", false); + enableEPKVersionCheck = eaglercraftOpts.optBoolean("enableEPKVersionCheck", true); defaultServers.clear(); JSONArray serversArray = eaglercraftOpts.optJSONArray("servers"); if(serversArray != null) { @@ -505,6 +508,10 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter, IBootMenu return singleThreadMode; } + public boolean isEnableEPKVersionCheckTeaVM() { + return enableEPKVersionCheck; + } + @Override public boolean isShowBootMenuOnLaunch() { return showBootMenuOnLaunch; @@ -585,6 +592,7 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter, IBootMenu jsonObject.put("eaglerNoDelay", eaglerNoDelay); jsonObject.put("ramdiskMode", ramdiskMode); jsonObject.put("singleThreadMode", singleThreadMode); + jsonObject.put("enableEPKVersionCheck", enableEPKVersionCheck); 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/TeaVMFetchJS.java b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMFetchJS.java index fcfadb9..df8c4be 100755 --- a/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMFetchJS.java +++ b/src/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMFetchJS.java @@ -30,7 +30,7 @@ public class TeaVMFetchJS { @JSBody(params = { }, script = "return (typeof fetch === \"function\");") public static native boolean checkFetchSupport(); - @JSBody(params = { "uri", "forceCache", "callback" }, script = "fetch(uri, { cache: forceCache, mode: \"no-cors\" })" + @JSBody(params = { "uri", "forceCache", "callback" }, script = "fetch(uri, { cache: forceCache, mode: \"cors\" })" + ".then(function(res) { return res.arrayBuffer(); }).then(function(arr) { callback(arr); })" + ".catch(function(err) { console.error(err); callback(null); });") public static native void doFetchDownload(String uri, String forceCache, FetchHandler callback); 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 b6291aa..4a583a2 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 @@ -171,4 +171,7 @@ public abstract class JSEaglercraftXOptsRoot implements JSObject { @JSBody(params = { "def" }, script = "return (typeof this.singleThreadMode === \"boolean\") ? this.singleThreadMode : def;") public native boolean getSingleThreadMode(boolean deobfStackTraces); + @JSBody(params = { "def" }, script = "return (typeof this.enableEPKVersionCheck === \"boolean\") ? this.enableEPKVersionCheck : def;") + public native boolean getEnableEPKVersionCheck(boolean deobfStackTraces); + }