diff --git a/README.md b/README.md index 89d7447..c8137ce 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,37 @@ The default eaglercraftXOpts values is this: - `enableSignatureBadge:` show a badge on the title screen indicating if digital signature is valid - `checkRelaysForUpdates:` proprietary feature used in offline downloads - `allowVoiceClient:` can be used to disable the voice chat feature +- `allowFNAWSkins:` can be used to disable the high poly FNAW skins +- `localStorageNamespace:` can be used to change the prefix of the local storage keys (Default: `"_eaglercraftX"`) +- `hooks:` can be used to define JavaScript callbacks for certain events + * `localStorageSaved:` JavaScript callback to save local storage keys + * `localStorageLoaded:` JavaScript callback to load local storage keys + +### Using Hooks + +You may want to implement some custom logic for loading/saving certain local storage keys. The eaglercraftXOpts hooks section can be used to override the client's local storage load and save functions. Currently, local storage keys are used to save game settings, the user's profile, custom servers, and shared world relays. Worlds and resource packs do not use local storage keys because modern browsers limit local storage keys to only 5 megabytes per domain which is too small for saving entire worlds and resource packs. Worlds and resource packs are saved using [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API). + + window.eaglercraftXOpts = { + ... + ... + ... + hooks: { + localStorageSaved: function(key, data) { + // 'key' is local storage key name as a string + // 'data' is base64-encoded byte array as a string + // function returns nothing + }, + localStorageLoaded: function(key) { + // 'key' is local storage key name as a string + // function returns a base64-encoded byte array as a string + // function returns null if the key does not exist + } + } + } + +Be aware that the client will still save the key to the browser's local storage anyway even if you define a custom save handler, and will just attempt to load the key from the browser's local storage normally if you return null, these are meant to be used like event handlers for creating backups of keys instead of completely replacing the local storage save and load functions. + +On a normal client you will only ever need to handle local storage keys called `_eaglercraftX.p` (profile), `_eaglercraftX.g` (game settings), `_eaglercraftX.s` (server list), `_eaglercraftX.r` (shared world relays), feel free to just ignore any other keys. It is guaranteed that the data the client stores will always be valid base64, so it is best practice to decode it to raw binary first if possible to reduce it's size before saving it to something like a MySQL database in your backend if you are trying to implement some kind of profile syncing system for your website. The keys already have GZIP compression applied to them by default so don't bother trying to compress them yourself a second time because it won't reduce their size. ## Developing a Client diff --git a/client_version b/client_version index a02af7c..92a7e70 100644 --- a/client_version +++ b/client_version @@ -1 +1 @@ -u29 \ No newline at end of file +u30 \ No newline at end of file diff --git a/patches/minecraft/net/minecraft/client/Minecraft.edit.java b/patches/minecraft/net/minecraft/client/Minecraft.edit.java index e31364f..c7ee4d5 100644 --- a/patches/minecraft/net/minecraft/client/Minecraft.edit.java +++ b/patches/minecraft/net/minecraft/client/Minecraft.edit.java @@ -20,7 +20,7 @@ > DELETE 1 @ 1 : 4 -> CHANGE 1 : 55 @ 1 : 4 +> CHANGE 1 : 56 @ 1 : 4 ~ ~ import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput; @@ -31,6 +31,7 @@ ~ ~ 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.EaglerXBungeeVersion; ~ import net.lax1dude.eaglercraft.v1_8.HString; ~ import net.lax1dude.eaglercraft.v1_8.IOUtils; @@ -430,7 +431,21 @@ ~ Util.func_181617_a((FutureTask) this.scheduledTasks.remove(0), logger); -> DELETE 18 @ 18 : 26 +> CHANGE 7 : 18 @ 7 : 8 + +~ if (this.timer.elapsedTicks > 1) { +~ long watchdog = System.currentTimeMillis(); +~ for (int j = 0; j < this.timer.elapsedTicks; ++j) { +~ this.runTick(); +~ long millis = System.currentTimeMillis(); +~ if (millis - watchdog > 50l) { +~ watchdog = millis; +~ EagUtils.sleep(0l); +~ } +~ } +~ } else if (this.timer.elapsedTicks == 1) { + +> DELETE 10 @ 10 : 18 > CHANGE 1 : 4 @ 1 : 5 @@ -503,9 +518,13 @@ + Mouse.tickCursorShape(); -> INSERT 5 : 6 @ 5 +> INSERT 5 : 10 @ 5 -+ Display.setVSync(this.gameSettings.enableVsync); ++ if (Display.isVSyncSupported()) { ++ Display.setVSync(this.gameSettings.enableVsync); ++ } else { ++ this.gameSettings.enableVsync = false; ++ } > DELETE 34 @ 34 : 52 diff --git a/patches/minecraft/net/minecraft/client/audio/SoundHandler.edit.java b/patches/minecraft/net/minecraft/client/audio/SoundHandler.edit.java index 0ec2942..eca9fb6 100644 --- a/patches/minecraft/net/minecraft/client/audio/SoundHandler.edit.java +++ b/patches/minecraft/net/minecraft/client/audio/SoundHandler.edit.java @@ -30,7 +30,9 @@ > DELETE 7 @ 7 : 11 -> DELETE 3 @ 3 : 18 +> CHANGE 3 : 4 @ 3 : 18 + +~ private static final Logger tipLogger = LogManager.getLogger("EaglercraftX"); > CHANGE 3 : 4 @ 3 : 4 @@ -44,7 +46,14 @@ ~ for (Entry entry : (Set) map.entrySet()) { -> INSERT 14 : 24 @ 14 +> INSERT 12 : 16 @ 12 + ++ if (this.sndRegistry.getObject(new ResourceLocation("minecraft:sounds/music/game/calm1.ogg")) == null) { ++ tipLogger.info( ++ "Download this resource pack if you want music: https://bafybeiayojww5jfyzvlmtuk7l5ufkt7nlfto7mhwmzf2vs4bvsjd5ouiuq.ipfs.nftstorage.link/?filename=Music_For_Eaglercraft.zip"); ++ } + +> INSERT 2 : 12 @ 2 + public static class SoundMap { + diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderManager.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderManager.edit.java index 1dfb3d3..c59f9a2 100644 --- a/patches/minecraft/net/minecraft/client/renderer/entity/RenderManager.edit.java +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderManager.edit.java @@ -7,11 +7,12 @@ > DELETE 2 @ 2 : 3 -> INSERT 1 : 9 @ 1 +> INSERT 1 : 10 @ 1 + + import com.google.common.collect.Maps; + ++ import net.lax1dude.eaglercraft.v1_8.EagRuntime; + import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; + import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; @@ -32,12 +33,16 @@ + private RenderPlayer eaglerRenderer; -> CHANGE 82 : 88 @ 82 : 83 +> CHANGE 82 : 92 @ 82 : 83 ~ 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); +~ if (EagRuntime.getConfiguration().isAllowFNAWSkins()) { +~ this.eaglerRenderer = new RenderHighPoly(this, this.playerRenderer.getMainModel(), +~ this.playerRenderer.shadowSize); +~ } else { +~ this.eaglerRenderer = this.playerRenderer; +~ } ~ this.skinMap.put("eagler", ~ Minecraft.getMinecraft().gameSettings.enableFNAWSkins ? this.eaglerRenderer : this.playerRenderer); diff --git a/patches/minecraft/net/minecraft/server/management/ServerConfigurationManager.edit.java b/patches/minecraft/net/minecraft/server/management/ServerConfigurationManager.edit.java index 99168d7..18aca1d 100644 --- a/patches/minecraft/net/minecraft/server/management/ServerConfigurationManager.edit.java +++ b/patches/minecraft/net/minecraft/server/management/ServerConfigurationManager.edit.java @@ -225,7 +225,7 @@ ~ worldIn.theItemInWorldManager.initializeGameType(parWorld.getWorldInfo().getGameType()); ~ } else { -~ parEntityPlayerMP2.theItemInWorldManager.setGameType(lanGamemode); +~ worldIn.theItemInWorldManager.setGameType(lanGamemode); ~ } > CHANGE 7 : 8 @ 7 : 8 diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java index ceda34c..8926416 100644 --- a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java @@ -64,8 +64,12 @@ public class PlatformApplication { String str = glfwGetClipboardString(win); return str == null ? "" : str; } - + public static void setLocalStorage(String name, byte[] data) { + setLocalStorage(name, data, true); + } + + public static void setLocalStorage(String name, byte[] data, boolean hooks) { if(data == null) { (new File("_eagstorage."+name+".dat")).delete(); }else { @@ -76,8 +80,12 @@ public class PlatformApplication { } } } - + public static byte[] getLocalStorage(String data) { + return getLocalStorage(data, true); + } + + public static byte[] getLocalStorage(String data, boolean hooks) { File f = new File("_eagstorage."+data+".dat"); if(!f.isFile()) { return null; diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java index d15647e..d864871 100644 --- a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java @@ -230,6 +230,10 @@ public class PlatformInput { glfwSwapBuffers(win); } + public static boolean isVSyncSupported() { + return true; + } + public static boolean wasResized() { boolean b = windowResized; windowResized = false; diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopClientConfigAdapter.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopClientConfigAdapter.java index 3ce88b5..1380861 100644 --- a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopClientConfigAdapter.java +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopClientConfigAdapter.java @@ -8,6 +8,7 @@ import org.json.JSONObject; import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter; +import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapterHooks; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayEntry; /** @@ -31,6 +32,8 @@ public class DesktopClientConfigAdapter implements IClientConfigAdapter { public final List defaultServers = new ArrayList(); + private final DesktopClientConfigAdapterHooks hooks = new DesktopClientConfigAdapterHooks(); + @Override public String getDefaultLocale() { return "en_US"; @@ -129,4 +132,32 @@ public class DesktopClientConfigAdapter implements IClientConfigAdapter { return false; } + @Override + public boolean isAllowFNAWSkins() { + return true; + } + + @Override + public String getLocalStorageNamespace() { + return EaglercraftVersion.localStorageNamespace; + } + + @Override + public IClientConfigAdapterHooks getHooks() { + return hooks; + } + + private static class DesktopClientConfigAdapterHooks implements IClientConfigAdapterHooks { + + @Override + public void callLocalStorageSavedHook(String key, String base64) { + + } + + @Override + public String callLocalStorageLoadHook(String key) { + return null; + } + + } } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/Display.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/Display.java index 6f40077..956ae1e 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/Display.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/Display.java @@ -49,6 +49,10 @@ public class Display { PlatformInput.setVSync(enable); } + public static boolean isVSyncSupported() { + return PlatformInput.isVSyncSupported(); + } + public static void update() { PlatformInput.update(); } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java index b272274..50b840a 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/EaglercraftVersion.java @@ -10,7 +10,7 @@ public class EaglercraftVersion { /// Customize these to fit your fork: public static final String projectForkName = "EaglercraftX"; - public static final String projectForkVersion = "u29"; + public static final String projectForkVersion = "u30"; public static final String projectForkVendor = "lax1dude"; public static final String projectForkURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8"; @@ -20,7 +20,7 @@ public class EaglercraftVersion { public static final String projectOriginName = "EaglercraftX"; public static final String projectOriginAuthor = "lax1dude"; public static final String projectOriginRevision = "1.8"; - public static final String projectOriginVersion = "u29"; + public static final String projectOriginVersion = "u30"; public static final String projectOriginURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8"; // rest in peace @@ -31,7 +31,7 @@ public class EaglercraftVersion { public static final boolean enableUpdateService = true; public static final String updateBundlePackageName = "net.lax1dude.eaglercraft.v1_8.client"; - public static final int updateBundlePackageVersionInt = 29; + public static final int updateBundlePackageVersionInt = 30; public static final String updateLatestLocalStorageKey = "latestUpdate_" + updateBundlePackageName; @@ -60,4 +60,6 @@ public class EaglercraftVersion { public static final boolean forceDemoMode = false; + public static final String localStorageNamespace = "_eaglercraftX"; + } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/IClientConfigAdapter.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/IClientConfigAdapter.java index 684275c..d2e0492 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/IClientConfigAdapter.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/IClientConfigAdapter.java @@ -69,4 +69,11 @@ public interface IClientConfigAdapter { boolean isEnableSignatureBadge(); boolean isAllowVoiceClient(); + + boolean isAllowFNAWSkins(); + + String getLocalStorageNamespace(); + + IClientConfigAdapterHooks getHooks(); + } diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsRelaysArray.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/IClientConfigAdapterHooks.java similarity index 73% rename from sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsRelaysArray.java rename to sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/IClientConfigAdapterHooks.java index c120824..397f8b5 100644 --- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsRelaysArray.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/internal/IClientConfigAdapterHooks.java @@ -1,8 +1,4 @@ -package net.lax1dude.eaglercraft.v1_8.internal.teavm.opts; - -import org.teavm.jso.JSIndexer; -import org.teavm.jso.JSObject; -import org.teavm.jso.JSProperty; +package net.lax1dude.eaglercraft.v1_8.internal; /** * Copyright (c) 2024 lax1dude. All Rights Reserved. @@ -19,12 +15,10 @@ import org.teavm.jso.JSProperty; * POSSIBILITY OF SUCH DAMAGE. * */ -public interface JSEaglercraftXOptsRelaysArray extends JSObject { +public interface IClientConfigAdapterHooks { - @JSIndexer - JSEaglercraftXOptsRelay get(int idx); + void callLocalStorageSavedHook(String key, String base64); - @JSProperty - int getLength(); + String callLocalStorageLoadHook(String key); } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/BlockVertexIDs.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/BlockVertexIDs.java index fd41992..2a00144 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/BlockVertexIDs.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/opengl/ext/deferred/BlockVertexIDs.java @@ -13,6 +13,21 @@ import net.minecraft.client.resources.IResourceManager; import net.minecraft.client.resources.IResourceManagerReloadListener; import net.minecraft.util.ResourceLocation; +/** + * Copyright (c) 2023 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * 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 BlockVertexIDs implements IResourceManagerReloadListener { private static final Logger logger = LogManager.getLogger("BlockVertexIDsCSV"); diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerSkinTexture.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerSkinTexture.java index 2b746c1..bea6cf6 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerSkinTexture.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/EaglerSkinTexture.java @@ -61,7 +61,7 @@ public class EaglerSkinTexture implements ITextureObject { } System.arraycopy(pixels, 0, this.pixels, 0, pixels.length); if(textureId != -1) { - TextureUtil.uploadTextureImageAllocate(textureId, new ImageData(width, height, pixels, true), false, false); + TextureUtil.uploadTextureImageSub(textureId, new ImageData(width, height, pixels, true), 0, 0, false, false); } } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenEditProfile.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenEditProfile.java index fe319bc..d7343b1 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenEditProfile.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenEditProfile.java @@ -73,14 +73,20 @@ public class GuiScreenEditProfile extends GuiScreen { } 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 + DefaultSkins.defaultSkinsMap.length]; + String[] n = new String[numCustom + arr.length]; for(int i = 0; i < numCustom; ++i) { n[i] = EaglerProfile.customSkins.get(i).name; } - int numDefault = DefaultSkins.defaultSkinsMap.length; + int numDefault = arr.length; for(int j = 0; j < numDefault; ++j) { - n[numCustom + j] = DefaultSkins.defaultSkinsMap[j].name; + n[numCustom + j] = arr[j].name; } dropDownOptions = n; } @@ -106,6 +112,10 @@ public class GuiScreenEditProfile extends GuiScreen { 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; diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenImportExportProfile.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenImportExportProfile.java index e709d2d..eed4aae 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenImportExportProfile.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenImportExportProfile.java @@ -56,11 +56,15 @@ public class GuiScreenImportExportProfile extends GuiScreen { FileChooserResult result = EagRuntime.getFileChooserResult(); if(result != null) { mc.loadingScreen.eaglerShow(I18n.format("settingsBackup.importing.1"), "settingsBackup.importing.2"); + ProfileImporter importer = new ProfileImporter(result.fileData); try { - ProfileImporter importer = new ProfileImporter(result.fileData); importer.readHeader(); mc.displayGuiScreen(new GuiScreenImportProfile(importer, back)); }catch(IOException ex) { + try { + importer.close(); + } catch (IOException e) { + } EagRuntime.debugPrintStackTrace(ex); mc.displayGuiScreen(new GuiScreenGenericErrorMessage("settingsBackup.importing.failed.1", "settingsBackup.importing.failed.2", back)); } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenImportProfile.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenImportProfile.java index 7eecc81..825974a 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenImportProfile.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/GuiScreenImportProfile.java @@ -62,6 +62,14 @@ public class GuiScreenImportProfile extends GuiScreen { this.buttonList.add(new GuiButton(1, this.width / 2 - 100, this.height / 4 + 140, I18n.format("gui.cancel"))); } + @Override + public void onGuiClosed() { + try { + importer.close(); + } catch (IOException e) { + } + } + protected void actionPerformed(GuiButton par1GuiButton) { if(par1GuiButton.id == 0) { if(!doImportProfile && !doImportSettings && !doImportServers && !doImportResourcePacks) { diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/ProfileExporter.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/ProfileExporter.java index c309239..7686683 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/ProfileExporter.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/ProfileExporter.java @@ -69,98 +69,97 @@ public class ProfileExporter { osb.write(new byte[]{(byte)255,(byte)255,(byte)255,(byte)255}); // this will be replaced with the file count osb.write('G'); - OutputStream os = EaglerZLIB.newGZIPOutputStream(osb); - - os.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD - os.write(new byte[]{(byte)9,(byte)102,(byte)105,(byte)108,(byte)101,(byte)45,(byte)116,(byte)121, - (byte)112,(byte)101}); // 9 + file-type - os.write(new byte[]{(byte)0,(byte)0,(byte)0,(byte)14,(byte)101,(byte)112,(byte)107,(byte)47,(byte)112,(byte)114,(byte)111, - (byte)102,(byte)105,(byte)108,(byte)101,(byte)49,(byte)56,(byte)56}); // 14 + epk/profile188 - os.write('>'); - - os.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD - os.write(new byte[]{(byte)12,(byte)102,(byte)105,(byte)108,(byte)101,(byte)45,(byte)101,(byte)120, - (byte)112,(byte)111,(byte)114,(byte)116,(byte)115,(byte)0,(byte)0,(byte)0,(byte)1}); // 12 + file-exports + 1 - os.write((doExportProfile ? 1 : 0) | (doExportSettings ? 2 : 0) | (doExportServers ? 4 : 0) | (doExportResourcePacks ? 8 : 0)); - os.write('>'); - int fileCount = 2; - - if(doExportProfile) { - byte[] profileData = EaglerProfile.write(); - if(profileData == null) { - throw new IOException("Could not write profile data!"); - } - exportFileToEPK("_eaglercraftX.p", profileData, os); - ++fileCount; - } - - if(doExportSettings) { - logger.info("Exporting game settings..."); - byte[] gameSettings = Minecraft.getMinecraft().gameSettings.writeOptions(); - if(gameSettings == null) { - throw new IOException("Could not write game settings!"); - } - exportFileToEPK("_eaglercraftX.g", gameSettings, os); - ++fileCount; - logger.info("Exporting relay settings..."); - byte[] relays = RelayManager.relayManager.write(); - if(relays == null) { - throw new IOException("Could not write relay settings!"); - } - exportFileToEPK("_eaglercraftX.r", relays, os); - ++fileCount; - } - - if(doExportServers) { - logger.info("Exporting server list..."); - byte[] servers = ServerList.getServerList().writeServerList(); - if(servers == null) { - throw new IOException("Could not write server list!"); - } - exportFileToEPK("_eaglercraftX.s", servers, os); - ++fileCount; - } - - logger.info("Exporting certificates..."); - UpdateCertificate cert = UpdateService.getClientCertificate(); - if(cert != null) { - exportFileToEPK("certs/main.cert", cert.rawCertData, os); - ++fileCount; - } - Collection updatesExport = UpdateService.getAvailableUpdates(); - int cc = 0; - for(UpdateCertificate cert2 : updatesExport) { - exportFileToEPK("certs/c" + (cc++) + ".cert", cert2.rawCertData, os); - ++fileCount; - } - - if(doExportResourcePacks) { - logger.info("Exporting resource packs..."); - byte[] packManifest = (new VFile2(EaglerFolderResourcePack.RESOURCE_PACKS + "/manifest.json")).getAllBytes(); - if(packManifest != null) { - exportFileToEPK(EaglerFolderResourcePack.RESOURCE_PACKS + "/manifest.json", packManifest, os); + try(OutputStream os = EaglerZLIB.newGZIPOutputStream(osb)) { + os.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD + os.write(new byte[]{(byte)9,(byte)102,(byte)105,(byte)108,(byte)101,(byte)45,(byte)116,(byte)121, + (byte)112,(byte)101}); // 9 + file-type + os.write(new byte[]{(byte)0,(byte)0,(byte)0,(byte)14,(byte)101,(byte)112,(byte)107,(byte)47,(byte)112,(byte)114,(byte)111, + (byte)102,(byte)105,(byte)108,(byte)101,(byte)49,(byte)56,(byte)56}); // 14 + epk/profile188 + os.write('>'); + + os.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD + os.write(new byte[]{(byte)12,(byte)102,(byte)105,(byte)108,(byte)101,(byte)45,(byte)101,(byte)120, + (byte)112,(byte)111,(byte)114,(byte)116,(byte)115,(byte)0,(byte)0,(byte)0,(byte)1}); // 12 + file-exports + 1 + os.write((doExportProfile ? 1 : 0) | (doExportSettings ? 2 : 0) | (doExportServers ? 4 : 0) | (doExportResourcePacks ? 8 : 0)); + os.write('>'); + + + if(doExportProfile) { + byte[] profileData = EaglerProfile.write(); + if(profileData == null) { + throw new IOException("Could not write profile data!"); + } + exportFileToEPK("_eaglercraftX.p", profileData, os); ++fileCount; - VFile2 baseDir = new VFile2(EaglerFolderResourcePack.RESOURCE_PACKS); - List files = baseDir.listFiles(true); - logger.info("({} files to export)", files.size()); - for(int i = 0, l = files.size(); i < l; ++i) { - VFile2 f = files.get(i); - if(f.getPath().equals(EaglerFolderResourcePack.RESOURCE_PACKS + "/manifest.json")) { - continue; - } - exportFileToEPK(f.getPath(), f.getAllBytes(), os); + } + + if(doExportSettings) { + logger.info("Exporting game settings..."); + byte[] gameSettings = Minecraft.getMinecraft().gameSettings.writeOptions(); + if(gameSettings == null) { + throw new IOException("Could not write game settings!"); + } + exportFileToEPK("_eaglercraftX.g", gameSettings, os); + ++fileCount; + logger.info("Exporting relay settings..."); + byte[] relays = RelayManager.relayManager.write(); + if(relays == null) { + throw new IOException("Could not write relay settings!"); + } + exportFileToEPK("_eaglercraftX.r", relays, os); + ++fileCount; + } + + if(doExportServers) { + logger.info("Exporting server list..."); + byte[] servers = ServerList.getServerList().writeServerList(); + if(servers == null) { + throw new IOException("Could not write server list!"); + } + exportFileToEPK("_eaglercraftX.s", servers, os); + ++fileCount; + } + + logger.info("Exporting certificates..."); + UpdateCertificate cert = UpdateService.getClientCertificate(); + if(cert != null) { + exportFileToEPK("certs/main.cert", cert.rawCertData, os); + ++fileCount; + } + Collection updatesExport = UpdateService.getAvailableUpdates(); + int cc = 0; + for(UpdateCertificate cert2 : updatesExport) { + exportFileToEPK("certs/c" + (cc++) + ".cert", cert2.rawCertData, os); + ++fileCount; + } + + if(doExportResourcePacks) { + logger.info("Exporting resource packs..."); + byte[] packManifest = (new VFile2(EaglerFolderResourcePack.RESOURCE_PACKS + "/manifest.json")).getAllBytes(); + if(packManifest != null) { + exportFileToEPK(EaglerFolderResourcePack.RESOURCE_PACKS + "/manifest.json", packManifest, os); ++fileCount; - if(i > 0 && i % 100 == 0) { - logger.info("Exported {} files", i); + VFile2 baseDir = new VFile2(EaglerFolderResourcePack.RESOURCE_PACKS); + List files = baseDir.listFiles(true); + logger.info("({} files to export)", files.size()); + for(int i = 0, l = files.size(); i < l; ++i) { + VFile2 f = files.get(i); + if(f.getPath().equals(EaglerFolderResourcePack.RESOURCE_PACKS + "/manifest.json")) { + continue; + } + exportFileToEPK(f.getPath(), f.getAllBytes(), os); + ++fileCount; + if(i > 0 && i % 100 == 0) { + logger.info("Exported {} files", i); + } } } } + + os.write(new byte[]{(byte)69,(byte)78,(byte)68,(byte)36}); // END$ } - os.write(new byte[]{(byte)69,(byte)78,(byte)68,(byte)36}); // END$ - os.close(); - osb.write(new byte[]{(byte)58,(byte)58,(byte)58,(byte)89,(byte)69,(byte)69,(byte)58,(byte)62}); // :::YEE:> byte[] ret = osb.toByteArray(); diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/ProfileImporter.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/ProfileImporter.java index e626b69..4758b1c 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/ProfileImporter.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/ProfileImporter.java @@ -1,5 +1,6 @@ package net.lax1dude.eaglercraft.v1_8.profile; +import java.io.Closeable; import java.io.IOException; import net.lax1dude.eaglercraft.v1_8.EagRuntime; @@ -29,7 +30,7 @@ import net.minecraft.client.multiplayer.ServerList; * POSSIBILITY OF SUCH DAMAGE. * */ -public class ProfileImporter { +public class ProfileImporter implements Closeable { private static final Logger logger = LogManager.getLogger("ProfileImporter"); @@ -148,4 +149,9 @@ public class ProfileImporter { } logger.info("Import complete!"); } + + @Override + public void close() throws IOException { + epkDecompiler.close(); + } } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/RenderHighPoly.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/RenderHighPoly.java index 035d68e..515b1ea 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/RenderHighPoly.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/RenderHighPoly.java @@ -468,4 +468,13 @@ public class RenderHighPoly extends RenderPlayer { GlStateManager.popMatrix(); } } + + public void renderLivingAt(AbstractClientPlayer abstractclientplayer, double d0, double d1, double d2) { + if (abstractclientplayer.isEntityAlive() && abstractclientplayer.isPlayerSleeping()) { + super.renderLivingAt(abstractclientplayer, d0 - (double) abstractclientplayer.renderOffsetX, + d1 - (double) abstractclientplayer.renderOffsetY, d2 - (double) abstractclientplayer.renderOffsetZ); + } else { + super.renderLivingAt(abstractclientplayer, d0, d1, d2); + } + } } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinModel.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinModel.java index 8dbb655..735deea 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinModel.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/profile/SkinModel.java @@ -46,7 +46,7 @@ public enum SkinModel { private SkinModel(int id, HighPolySkin highPoly) { this.id = id; this.width = 256; - this.height = 128; + this.height = 256; this.profileSkinType = "eagler"; this.sanitize = true; this.highPoly = highPoly; diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiShareToLan.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiShareToLan.java index 9190a4e..cbf896f 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiShareToLan.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/gui/GuiShareToLan.java @@ -156,7 +156,6 @@ public class GuiShareToLan extends GuiScreen { this.mc.ingameGUI.getChatGUI().printChatMessage(new ChatComponentText(I18n.format("lanServer.opened") .replace("$relay$", LANServerController.getCurrentURI()).replace("$code$", code))); } else { - SingleplayerServerController.configureLAN(mc.theWorld.getWorldInfo().getGameType(), false); this.mc.displayGuiScreen(new GuiScreenNoRelays(this, "noRelay.titleFail")); } } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientNetworkManager.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientNetworkManager.java index 418d182..d442f9f 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientNetworkManager.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/lan/LANClientNetworkManager.java @@ -340,9 +340,11 @@ public class LANClientNetworkManager extends EaglercraftNetworkManager { } EaglerInputStream bi = new EaglerInputStream(fullData); int i = (bi.read() << 24) | (bi.read() << 16) | (bi.read() << 8) | bi.read(); - InputStream inflaterInputStream = EaglerZLIB.newInflaterInputStream(bi); fullData = new byte[i]; - int r = IOUtils.readFully(inflaterInputStream, fullData); + int r; + try(InputStream inflaterInputStream = EaglerZLIB.newInflaterInputStream(bi)) { + r = IOUtils.readFully(inflaterInputStream, fullData); + } if (i != r) { logger.warn("Decompressed packet expected size {} differs from actual size {}!", i, r); } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java index c5d87ee..73fb100 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/EaglerIntegratedServerWorker.java @@ -479,7 +479,7 @@ public class EaglerIntegratedServerWorker { while(true) { mainLoop(); - EagUtils.sleep(1l); + EagUtils.sleep(0l); } }catch(Throwable tt) { if(tt instanceof ReportedException) { diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/EPKDecompiler.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/EPKDecompiler.java index 10f536e..fb07f6d 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/EPKDecompiler.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/EPKDecompiler.java @@ -1,6 +1,7 @@ package net.lax1dude.eaglercraft.v1_8.sp.server.export; import java.io.ByteArrayInputStream; +import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; @@ -24,7 +25,7 @@ import net.lax1dude.eaglercraft.v1_8.IOUtils; * POSSIBILITY OF SUCH DAMAGE. * */ -public class EPKDecompiler { +public class EPKDecompiler implements Closeable { public static class FileEntry { public final String type; @@ -178,4 +179,9 @@ public class EPKDecompiler { return new String(charIn); } + @Override + public void close() throws IOException { + zis.close(); + } + } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterEPK.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterEPK.java index 5818c77..fc085c0 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterEPK.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterEPK.java @@ -40,44 +40,45 @@ public class WorldConverterEPK { folderName += "_"; worldDir = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(folderName, false).getWorldDirectory(); } - EPKDecompiler dc = new EPKDecompiler(archiveContents); - EPKDecompiler.FileEntry f = null; - int lastProgUpdate = 0; - int prog = 0; - String hasReadType = null; - boolean has152Format = false; - int cnt = 0; - while((f = dc.readFile()) != null) { - byte[] b = f.data; - if(hasReadType == null) { - if (f.type.equals("HEAD") && f.name.equals("file-type") - && ((hasReadType = EPKDecompiler.readASCII(f.data)).equals("epk/world188") - || (has152Format = hasReadType.equals("epk/world152")))) { - if(has152Format) { - logger.warn("World type detected as 1.5.2, it will be converted to 1.8.8 format"); + try(EPKDecompiler dc = new EPKDecompiler(archiveContents)) { + EPKDecompiler.FileEntry f = null; + int lastProgUpdate = 0; + int prog = 0; + String hasReadType = null; + boolean has152Format = false; + int cnt = 0; + while((f = dc.readFile()) != null) { + byte[] b = f.data; + if(hasReadType == null) { + if (f.type.equals("HEAD") && f.name.equals("file-type") + && ((hasReadType = EPKDecompiler.readASCII(f.data)).equals("epk/world188") + || (has152Format = hasReadType.equals("epk/world152")))) { + if(has152Format) { + logger.warn("World type detected as 1.5.2, it will be converted to 1.8.8 format"); + } + continue; + }else { + throw new IOException("file does not contain a singleplayer 1.5.2 or 1.8.8 world!"); } - continue; - }else { - throw new IOException("file does not contain a singleplayer 1.5.2 or 1.8.8 world!"); } - } - if(f.type.equals("FILE")) { - if(f.name.equals("level.dat") || f.name.equals("level.dat_old")) { - NBTTagCompound worldDatNBT = CompressedStreamTools.readCompressed(new EaglerInputStream(b)); - worldDatNBT.getCompoundTag("Data").setString("LevelName", newName); - worldDatNBT.getCompoundTag("Data").setLong("LastPlayed", System.currentTimeMillis()); - EaglerOutputStream tmp = new EaglerOutputStream(); - CompressedStreamTools.writeCompressed(worldDatNBT, tmp); - b = tmp.toByteArray(); - } - VFile2 ff = new VFile2(worldDir, f.name); - ff.setAllBytes(b); - prog += b.length; - ++cnt; - if(prog - lastProgUpdate > 25000) { - lastProgUpdate = prog; - logger.info("Extracted {} files, {} bytes from EPK...", cnt, prog); - EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.importing.1", prog); + if(f.type.equals("FILE")) { + if(f.name.equals("level.dat") || f.name.equals("level.dat_old")) { + NBTTagCompound worldDatNBT = CompressedStreamTools.readCompressed(new EaglerInputStream(b)); + worldDatNBT.getCompoundTag("Data").setString("LevelName", newName); + worldDatNBT.getCompoundTag("Data").setLong("LastPlayed", System.currentTimeMillis()); + EaglerOutputStream tmp = new EaglerOutputStream(); + CompressedStreamTools.writeCompressed(worldDatNBT, tmp); + b = tmp.toByteArray(); + } + VFile2 ff = new VFile2(worldDir, f.name); + ff.setAllBytes(b); + prog += b.length; + ++cnt; + if(prog - lastProgUpdate > 25000) { + lastProgUpdate = prog; + logger.info("Extracted {} files, {} bytes from EPK...", cnt, prog); + EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.importing.1", prog); + } } } } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterMCA.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterMCA.java index e027530..b7a6d5b 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterMCA.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/export/WorldConverterMCA.java @@ -50,111 +50,113 @@ public class WorldConverterMCA { folderName += "_"; worldDir = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(folderName, false).getWorldDirectory(); } - ZipInputStream zis = new ZipInputStream(new EaglerInputStream(archiveContents)); - ZipEntry folderNameFile = null; List fileNames = new ArrayList<>(); - while((folderNameFile = zis.getNextEntry()) != null) { - if (folderNameFile.getName().contains("__MACOSX/")) continue; - if (folderNameFile.isDirectory()) continue; - String lowerName = folderNameFile.getName().toLowerCase(); - if (!(lowerName.endsWith(".dat") || lowerName.endsWith(".dat_old") || lowerName.endsWith(".mca") || lowerName.endsWith(".mcr"))) continue; - fileNames.add(folderNameFile.getName().toCharArray()); + try(ZipInputStream zis = new ZipInputStream(new EaglerInputStream(archiveContents))) { + ZipEntry folderNameFile = null; + while((folderNameFile = zis.getNextEntry()) != null) { + if (folderNameFile.getName().contains("__MACOSX/")) continue; + if (folderNameFile.isDirectory()) continue; + String lowerName = folderNameFile.getName().toLowerCase(); + if (!(lowerName.endsWith(".dat") || lowerName.endsWith(".dat_old") || lowerName.endsWith(".mca") || lowerName.endsWith(".mcr"))) continue; + fileNames.add(folderNameFile.getName().toCharArray()); + } } final int[] i = new int[] { 0 }; while(fileNames.get(0).length > i[0] && fileNames.stream().allMatch(w -> w[i[0]] == fileNames.get(0)[i[0]])) i[0]++; int folderPrefixOffset = i[0]; - zis = new ZipInputStream(new EaglerInputStream(archiveContents)); - ZipEntry f = null; - int lastProgUpdate = 0; - int prog = 0; - byte[] bb = new byte[16384]; - while ((f = zis.getNextEntry()) != null) { - if (f.getName().contains("__MACOSX/")) continue; - if (f.isDirectory()) continue; - String lowerName = f.getName().toLowerCase(); - if (!(lowerName.endsWith(".dat") || lowerName.endsWith(".dat_old") || lowerName.endsWith(".mca") || lowerName.endsWith(".mcr") || lowerName.endsWith(".bmp"))) continue; - EaglerOutputStream baos = new EaglerOutputStream(); - int len; - while ((len = zis.read(bb)) != -1) { - baos.write(bb, 0, len); - } - baos.close(); - byte[] b = baos.toByteArray(); - String fileName = f.getName().substring(folderPrefixOffset); - if (fileName.equals("level.dat") || fileName.equals("level.dat_old")) { - NBTTagCompound worldDatNBT = CompressedStreamTools.readCompressed(new EaglerInputStream(b)); - - NBTTagCompound gameRulesNBT = worldDatNBT.getCompoundTag("Data").getCompoundTag("GameRules"); - gameRulesNBT.setString("loadSpawnChunks", (gameRules & 2) != 0 ? "true" : "false"); - String s = (gameRules & 1) != 0 ? "true" : "false"; - gameRulesNBT.setString("bedSpawnPoint", s); - gameRulesNBT.setString("clickToRide", "false"); - gameRulesNBT.setString("clickToSit", s); - gameRulesNBT.setString("colorCodes", s); - gameRulesNBT.setString("doSignEditing", s); - worldDatNBT.getCompoundTag("Data").setTag("GameRules", gameRulesNBT); - - worldDatNBT.getCompoundTag("Data").setString("LevelName", newName); - worldDatNBT.getCompoundTag("Data").setLong("LastPlayed", System.currentTimeMillis()); - EaglerOutputStream bo = new EaglerOutputStream(); - CompressedStreamTools.writeCompressed(worldDatNBT, bo); - b = bo.toByteArray(); - VFile2 ff = new VFile2(worldDir, fileName); - ff.setAllBytes(b); - prog += b.length; - } else if ((fileName.endsWith(".mcr") || fileName.endsWith(".mca")) && (fileName.startsWith("region/") || fileName.startsWith("DIM1/region/") || fileName.startsWith("DIM-1/region/"))) { - VFile2 chunkFolder = new VFile2(worldDir, fileName.startsWith("DIM1") ? "level1" : (fileName.startsWith("DIM-1") ? "level-1" : "level0")); - RegionFile mca = new RegionFile(new RandomAccessMemoryFile(b, b.length)); - int loadChunksCount = 0; - for(int j = 0; j < 32; ++j) { - for(int k = 0; k < 32; ++k) { - if(mca.isChunkSaved(j, k)) { - NBTTagCompound chunkNBT; - NBTTagCompound chunkLevel; - try { - chunkNBT = CompressedStreamTools.read(mca.getChunkDataInputStream(j, k)); - if(!chunkNBT.hasKey("Level", 10)) { - throw new IOException("Chunk is missing level data!"); + try(ZipInputStream zis = new ZipInputStream(new EaglerInputStream(archiveContents))) { + ZipEntry f = null; + int lastProgUpdate = 0; + int prog = 0; + byte[] bb = new byte[16384]; + while ((f = zis.getNextEntry()) != null) { + if (f.getName().contains("__MACOSX/")) continue; + if (f.isDirectory()) continue; + String lowerName = f.getName().toLowerCase(); + if (!(lowerName.endsWith(".dat") || lowerName.endsWith(".dat_old") || lowerName.endsWith(".mca") || lowerName.endsWith(".mcr") || lowerName.endsWith(".bmp"))) continue; + EaglerOutputStream baos = new EaglerOutputStream(); + int len; + while ((len = zis.read(bb)) != -1) { + baos.write(bb, 0, len); + } + baos.close(); + byte[] b = baos.toByteArray(); + String fileName = f.getName().substring(folderPrefixOffset); + if (fileName.equals("level.dat") || fileName.equals("level.dat_old")) { + NBTTagCompound worldDatNBT = CompressedStreamTools.readCompressed(new EaglerInputStream(b)); + + NBTTagCompound gameRulesNBT = worldDatNBT.getCompoundTag("Data").getCompoundTag("GameRules"); + gameRulesNBT.setString("loadSpawnChunks", (gameRules & 2) != 0 ? "true" : "false"); + String s = (gameRules & 1) != 0 ? "true" : "false"; + gameRulesNBT.setString("bedSpawnPoint", s); + gameRulesNBT.setString("clickToRide", "false"); + gameRulesNBT.setString("clickToSit", s); + gameRulesNBT.setString("colorCodes", s); + gameRulesNBT.setString("doSignEditing", s); + worldDatNBT.getCompoundTag("Data").setTag("GameRules", gameRulesNBT); + + worldDatNBT.getCompoundTag("Data").setString("LevelName", newName); + worldDatNBT.getCompoundTag("Data").setLong("LastPlayed", System.currentTimeMillis()); + EaglerOutputStream bo = new EaglerOutputStream(); + CompressedStreamTools.writeCompressed(worldDatNBT, bo); + b = bo.toByteArray(); + VFile2 ff = new VFile2(worldDir, fileName); + ff.setAllBytes(b); + prog += b.length; + } else if ((fileName.endsWith(".mcr") || fileName.endsWith(".mca")) && (fileName.startsWith("region/") || fileName.startsWith("DIM1/region/") || fileName.startsWith("DIM-1/region/"))) { + VFile2 chunkFolder = new VFile2(worldDir, fileName.startsWith("DIM1") ? "level1" : (fileName.startsWith("DIM-1") ? "level-1" : "level0")); + RegionFile mca = new RegionFile(new RandomAccessMemoryFile(b, b.length)); + int loadChunksCount = 0; + for(int j = 0; j < 32; ++j) { + for(int k = 0; k < 32; ++k) { + if(mca.isChunkSaved(j, k)) { + NBTTagCompound chunkNBT; + NBTTagCompound chunkLevel; + try { + chunkNBT = CompressedStreamTools.read(mca.getChunkDataInputStream(j, k)); + if(!chunkNBT.hasKey("Level", 10)) { + throw new IOException("Chunk is missing level data!"); + } + chunkLevel = chunkNBT.getCompoundTag("Level"); + }catch(Throwable t) { + logger.error("{}: Could not read chunk: {}, {}", fileName, j, k); + logger.error(t); + continue; } - chunkLevel = chunkNBT.getCompoundTag("Level"); - }catch(Throwable t) { - logger.error("{}: Could not read chunk: {}, {}", fileName, j, k); - logger.error(t); - continue; + int chunkX = chunkLevel.getInteger("xPos"); + int chunkZ = chunkLevel.getInteger("zPos"); + VFile2 chunkOut = new VFile2(chunkFolder, EaglerChunkLoader.getChunkPath(chunkX, chunkZ) + ".dat"); + if(chunkOut.exists()) { + logger.error("{}: Chunk already exists: {}", fileName, chunkOut.getPath()); + continue; + } + EaglerOutputStream bao = new EaglerOutputStream(); + CompressedStreamTools.writeCompressed(chunkNBT, bao); + b = bao.toByteArray(); + chunkOut.setAllBytes(b); + prog += b.length; + if (prog - lastProgUpdate > 25000) { + lastProgUpdate = prog; + EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.importing.2", prog); + } + ++loadChunksCount; } - int chunkX = chunkLevel.getInteger("xPos"); - int chunkZ = chunkLevel.getInteger("zPos"); - VFile2 chunkOut = new VFile2(chunkFolder, EaglerChunkLoader.getChunkPath(chunkX, chunkZ) + ".dat"); - if(chunkOut.exists()) { - logger.error("{}: Chunk already exists: {}", fileName, chunkOut.getPath()); - continue; - } - EaglerOutputStream bao = new EaglerOutputStream(); - CompressedStreamTools.writeCompressed(chunkNBT, bao); - b = bao.toByteArray(); - chunkOut.setAllBytes(b); - prog += b.length; - if (prog - lastProgUpdate > 25000) { - lastProgUpdate = prog; - EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.importing.2", prog); - } - ++loadChunksCount; } } + logger.info("{}: Imported {} chunks successfully ({} bytes)", fileName, loadChunksCount, prog); + } else if (fileName.startsWith("playerdata/") || fileName.startsWith("stats/")) { + //TODO: LAN player inventories + } else if (fileName.startsWith("data/") || fileName.startsWith("players/") || fileName.startsWith("eagler/skulls/")) { + VFile2 ff = new VFile2(worldDir, fileName); + ff.setAllBytes(b); + prog += b.length; + } else if (!fileName.equals("level.dat_mcr") && !fileName.equals("session.lock")) { + logger.info("Skipping file: {}", fileName); + } + if (prog - lastProgUpdate > 25000) { + lastProgUpdate = prog; + EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.importing.2", prog); } - logger.info("{}: Imported {} chunks successfully ({} bytes)", fileName, loadChunksCount, prog); - } else if (fileName.startsWith("playerdata/") || fileName.startsWith("stats/")) { - //TODO: LAN player inventories - } else if (fileName.startsWith("data/") || fileName.startsWith("players/") || fileName.startsWith("eagler/skulls/")) { - VFile2 ff = new VFile2(worldDir, fileName); - ff.setAllBytes(b); - prog += b.length; - } else if (!fileName.equals("level.dat_mcr") && !fileName.equals("session.lock")) { - logger.info("Skipping file: {}", fileName); - } - if (prog - lastProgUpdate > 25000) { - lastProgUpdate = prog; - EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.importing.2", prog); } } logger.info("MCA was successfully extracted into directory \"{}\"", worldDir.getPath()); @@ -172,129 +174,130 @@ public class WorldConverterMCA { public static byte[] exportWorld(String folderName) throws IOException { EaglerOutputStream bao = new EaglerOutputStream(); - ZipOutputStream zos = new ZipOutputStream(bao); - zos.setComment("contains backup of world '" + folderName + "'"); - VFile2 worldFolder = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(folderName, false).getWorldDirectory(); - logger.info("Exporting world directory \"{}\" as MCA", worldFolder.getPath()); - VFile2 vf = new VFile2(worldFolder, "level.dat"); - byte[] b; - int lastProgUpdate = 0; - int prog = 0; - boolean safe = false; - if(vf.exists()) { - zos.putNextEntry(new ZipEntry(folderName + "/level.dat")); - b = vf.getAllBytes(); - zos.write(b); - prog += b.length; - safe = true; - } - vf = new VFile2(worldFolder, "level.dat_old"); - if(vf.exists()) { - zos.putNextEntry(new ZipEntry(folderName + "/level.dat_old")); - b = vf.getAllBytes(); - zos.write(b); - prog += b.length; - safe = true; - } - if (prog - lastProgUpdate > 25000) { - lastProgUpdate = prog; - EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog); - } - String[] srcFolderNames = new String[] { "level0", "level-1", "level1" }; - String[] dstFolderNames = new String[] { "/region/", "/DIM-1/region/", "/DIM1/region/" }; - List fileList; - for(int i = 0; i < 3; ++i) { - vf = new VFile2(worldFolder, srcFolderNames[i]); - fileList = vf.listFiles(true); - String regionFolder = folderName + dstFolderNames[i]; - logger.info("Converting chunks in \"{}\" as MCA to \"{}\"...", vf.getPath(), regionFolder); - Map regionFiles = new HashMap(); - for(int k = 0, l = fileList.size(); k < l; ++k) { - VFile2 chunkFile = fileList.get(k); - NBTTagCompound chunkNBT; - NBTTagCompound chunkLevel; - try { - b = chunkFile.getAllBytes(); - chunkNBT = CompressedStreamTools.readCompressed(new EaglerInputStream(b)); - if(!chunkNBT.hasKey("Level", 10)) { - throw new IOException("Chunk is missing level data!"); + VFile2 worldFolder; + try(ZipOutputStream zos = new ZipOutputStream(bao)) { + zos.setComment("contains backup of world '" + folderName + "'"); + worldFolder = EaglerIntegratedServerWorker.saveFormat.getSaveLoader(folderName, false).getWorldDirectory(); + logger.info("Exporting world directory \"{}\" as MCA", worldFolder.getPath()); + VFile2 vf = new VFile2(worldFolder, "level.dat"); + byte[] b; + int lastProgUpdate = 0; + int prog = 0; + boolean safe = false; + if(vf.exists()) { + zos.putNextEntry(new ZipEntry(folderName + "/level.dat")); + b = vf.getAllBytes(); + zos.write(b); + prog += b.length; + safe = true; + } + vf = new VFile2(worldFolder, "level.dat_old"); + if(vf.exists()) { + zos.putNextEntry(new ZipEntry(folderName + "/level.dat_old")); + b = vf.getAllBytes(); + zos.write(b); + prog += b.length; + safe = true; + } + if (prog - lastProgUpdate > 25000) { + lastProgUpdate = prog; + EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog); + } + String[] srcFolderNames = new String[] { "level0", "level-1", "level1" }; + String[] dstFolderNames = new String[] { "/region/", "/DIM-1/region/", "/DIM1/region/" }; + List fileList; + for(int i = 0; i < 3; ++i) { + vf = new VFile2(worldFolder, srcFolderNames[i]); + fileList = vf.listFiles(true); + String regionFolder = folderName + dstFolderNames[i]; + logger.info("Converting chunks in \"{}\" as MCA to \"{}\"...", vf.getPath(), regionFolder); + Map regionFiles = new HashMap(); + for(int k = 0, l = fileList.size(); k < l; ++k) { + VFile2 chunkFile = fileList.get(k); + NBTTagCompound chunkNBT; + NBTTagCompound chunkLevel; + try { + b = chunkFile.getAllBytes(); + chunkNBT = CompressedStreamTools.readCompressed(new EaglerInputStream(b)); + if(!chunkNBT.hasKey("Level", 10)) { + throw new IOException("Chunk is missing level data!"); + } + chunkLevel = chunkNBT.getCompoundTag("Level"); + }catch(IOException t) { + logger.error("Could not read chunk: {}", chunkFile.getPath()); + logger.error(t); + continue; } - chunkLevel = chunkNBT.getCompoundTag("Level"); - }catch(IOException t) { - logger.error("Could not read chunk: {}", chunkFile.getPath()); - logger.error(t); + int chunkX = chunkLevel.getInteger("xPos"); + int chunkZ = chunkLevel.getInteger("zPos"); + String regionFileName = "r." + (chunkX >> 5) + "." + (chunkZ >> 5) + ".mca"; + RegionFile rf = regionFiles.get(regionFileName); + if(rf == null) { + rf = new RegionFile(new RandomAccessMemoryFile(new byte[65536], 0)); + regionFiles.put(regionFileName, rf); + } + try(DataOutputStream dos = rf.getChunkDataOutputStream(chunkX & 31, chunkZ & 31)) { + CompressedStreamTools.write(chunkNBT, dos); + }catch(IOException t) { + logger.error("Could not write chunk to {}: {}", regionFileName, chunkFile.getPath()); + logger.error(t); + continue; + } + prog += b.length; + if (prog - lastProgUpdate > 25000) { + lastProgUpdate = prog; + EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog); + } + } + if(regionFiles.isEmpty()) { + logger.info("No region files were generated"); continue; } - int chunkX = chunkLevel.getInteger("xPos"); - int chunkZ = chunkLevel.getInteger("zPos"); - String regionFileName = "r." + (chunkX >> 5) + "." + (chunkZ >> 5) + ".mca"; - RegionFile rf = regionFiles.get(regionFileName); - if(rf == null) { - rf = new RegionFile(new RandomAccessMemoryFile(new byte[65536], 0)); - regionFiles.put(regionFileName, rf); - } - try(DataOutputStream dos = rf.getChunkDataOutputStream(chunkX & 31, chunkZ & 31)) { - CompressedStreamTools.write(chunkNBT, dos); - }catch(IOException t) { - logger.error("Could not write chunk to {}: {}", regionFileName, chunkFile.getPath()); - logger.error(t); - continue; + for(Entry etr : regionFiles.entrySet()) { + String regionPath = regionFolder + etr.getKey(); + logger.info("Writing region file: {}", regionPath); + zos.putNextEntry(new ZipEntry(regionPath)); + zos.write(etr.getValue().getFile().getByteArray()); } + } + logger.info("Copying extra world data..."); + fileList = (new VFile2(worldFolder, "data")).listFiles(false); + for(int k = 0, l = fileList.size(); k < l; ++k) { + VFile2 dataFile = fileList.get(k); + zos.putNextEntry(new ZipEntry(folderName + "/data/" + dataFile.getName())); + b = dataFile.getAllBytes(); + zos.write(b); prog += b.length; if (prog - lastProgUpdate > 25000) { lastProgUpdate = prog; EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog); } } - if(regionFiles.isEmpty()) { - logger.info("No region files were generated"); - continue; + fileList = (new VFile2(worldFolder, "players")).listFiles(false); + for(int k = 0, l = fileList.size(); k < l; ++k) { + VFile2 dataFile = fileList.get(k); + zos.putNextEntry(new ZipEntry(folderName + "/players/" + dataFile.getName())); + b = dataFile.getAllBytes(); + zos.write(b); + prog += b.length; + if (prog - lastProgUpdate > 25000) { + lastProgUpdate = prog; + EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog); + } } - for(Entry etr : regionFiles.entrySet()) { - String regionPath = regionFolder + etr.getKey(); - logger.info("Writing region file: {}", regionPath); - zos.putNextEntry(new ZipEntry(regionPath)); - zos.write(etr.getValue().getFile().getByteArray()); + fileList = (new VFile2(worldFolder, "eagler/skulls")).listFiles(false); + for(int k = 0, l = fileList.size(); k < l; ++k) { + VFile2 dataFile = fileList.get(k); + zos.putNextEntry(new ZipEntry(folderName + "/eagler/skulls/" + dataFile.getName())); + b = dataFile.getAllBytes(); + zos.write(b); + prog += b.length; + if (prog - lastProgUpdate > 25000) { + lastProgUpdate = prog; + EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog); + } } } - logger.info("Copying extra world data..."); - fileList = (new VFile2(worldFolder, "data")).listFiles(false); - for(int k = 0, l = fileList.size(); k < l; ++k) { - VFile2 dataFile = fileList.get(k); - zos.putNextEntry(new ZipEntry(folderName + "/data/" + dataFile.getName())); - b = dataFile.getAllBytes(); - zos.write(b); - prog += b.length; - if (prog - lastProgUpdate > 25000) { - lastProgUpdate = prog; - EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog); - } - } - fileList = (new VFile2(worldFolder, "players")).listFiles(false); - for(int k = 0, l = fileList.size(); k < l; ++k) { - VFile2 dataFile = fileList.get(k); - zos.putNextEntry(new ZipEntry(folderName + "/players/" + dataFile.getName())); - b = dataFile.getAllBytes(); - zos.write(b); - prog += b.length; - if (prog - lastProgUpdate > 25000) { - lastProgUpdate = prog; - EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog); - } - } - fileList = (new VFile2(worldFolder, "eagler/skulls")).listFiles(false); - for(int k = 0, l = fileList.size(); k < l; ++k) { - VFile2 dataFile = fileList.get(k); - zos.putNextEntry(new ZipEntry(folderName + "/eagler/skulls/" + dataFile.getName())); - b = dataFile.getAllBytes(); - zos.write(b); - prog += b.length; - if (prog - lastProgUpdate > 25000) { - lastProgUpdate = prog; - EaglerIntegratedServerWorker.sendProgress("singleplayer.busy.exporting.2", prog); - } - } - zos.close(); logger.info("World directory \"{}\" was successfully exported as MCA", worldFolder.getPath()); return bao.toByteArray(); } diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/IntegratedServerPlayerNetworkManager.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/IntegratedServerPlayerNetworkManager.java index 3041090..bccbaee 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/IntegratedServerPlayerNetworkManager.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/sp/server/socket/IntegratedServerPlayerNetworkManager.java @@ -243,9 +243,9 @@ public class IntegratedServerPlayerNetworkManager { temporaryOutputStream.write((len >> 16) & 0xFF); temporaryOutputStream.write((len >> 8) & 0xFF); temporaryOutputStream.write(len & 0xFF); - OutputStream os = EaglerZLIB.newDeflaterOutputStream(temporaryOutputStream); - temporaryBuffer.readBytes(os, len); - os.close(); + try(OutputStream os = EaglerZLIB.newDeflaterOutputStream(temporaryOutputStream)) { + temporaryBuffer.readBytes(os, len); + } compressedData = temporaryOutputStream.toByteArray(); }catch(IOException ex) { logger.error("Failed to compress packet {}!", pkt.getClass().getSimpleName()); diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/update/RelayUpdateChecker.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/update/RelayUpdateChecker.java index be5cd15..2ee5eeb 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/update/RelayUpdateChecker.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/update/RelayUpdateChecker.java @@ -64,7 +64,7 @@ public class RelayUpdateChecker { for(net.lax1dude.eaglercraft.v1_8.sp.relay.RelayEntry etr : EagRuntime.getConfiguration().getRelays()) { relaysList.add(new RelayEntry(etr.address)); } - byte[] b = PlatformApplication.getLocalStorage("lastRelayUpdate"); + byte[] b = PlatformApplication.getLocalStorage("lastRelayUpdate", false); if(b != null) { try { lastUpdateCheck = (new DataInputStream(new EaglerInputStream(b))).readLong(); @@ -79,7 +79,7 @@ public class RelayUpdateChecker { try { EaglerOutputStream bao = new EaglerOutputStream(8); (new DataOutputStream(bao)).writeLong(lastUpdateCheck); - PlatformApplication.setLocalStorage("lastRelayUpdate", bao.toByteArray()); + PlatformApplication.setLocalStorage("lastRelayUpdate", bao.toByteArray(), false); } catch (IOException e) { } for (int i = 0, l = relaysList.size(); i < l; ++i) { diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/update/UpdateCertificate.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/update/UpdateCertificate.java index 2bfdc43..a3bc785 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/update/UpdateCertificate.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/update/UpdateCertificate.java @@ -148,7 +148,16 @@ public class UpdateCertificate { throw new CertificateInvalidException("SHA256 checksum of signature payload is invalid!"); } - return new UpdateCertificate(certData, EaglerZLIB.newGZIPInputStream(new EaglerInputStream(signaturePayload)), vers); + UpdateCertificate cert; + try(InputStream gis = EaglerZLIB.newGZIPInputStream(new EaglerInputStream(signaturePayload))) { + cert = new UpdateCertificate(certData, gis, vers); + } + + if(System.currentTimeMillis() < cert.sigTimestamp) { + throw new CertificateInvalidException("Update certificate timestamp is from the future!?"); + } + + return cert; } private UpdateCertificate(byte[] certData, InputStream is, int sigVers) throws IOException { diff --git a/sources/main/java/net/lax1dude/eaglercraft/v1_8/update/UpdateService.java b/sources/main/java/net/lax1dude/eaglercraft/v1_8/update/UpdateService.java index 49aa9bb..32c1b68 100644 --- a/sources/main/java/net/lax1dude/eaglercraft/v1_8/update/UpdateService.java +++ b/sources/main/java/net/lax1dude/eaglercraft/v1_8/update/UpdateService.java @@ -94,7 +94,7 @@ public class UpdateService { } } } - byte[] latestUpdate = PlatformApplication.getLocalStorage(EaglercraftVersion.updateLatestLocalStorageKey); + byte[] latestUpdate = PlatformApplication.getLocalStorage(EaglercraftVersion.updateLatestLocalStorageKey, false); if(latestUpdate != null) { addCertificateToSet(latestUpdate, false); } @@ -150,7 +150,7 @@ public class UpdateService { latestUpdateFound = cert; if (saveLatest) { PlatformApplication.setLocalStorage(EaglercraftVersion.updateLatestLocalStorageKey, - certificateData); + certificateData, false); } } }else if(EagRuntime.getConfiguration().isLogInvalidCerts()) { diff --git a/sources/resources/relay_download.zip b/sources/resources/relay_download.zip index 7b644bc..dc97f94 100644 Binary files a/sources/resources/relay_download.zip and b/sources/resources/relay_download.zip differ diff --git a/sources/setup/workspace_template/desktopRuntime/d3dcompiler_47.dll b/sources/setup/workspace_template/desktopRuntime/d3dcompiler_47.dll index 7f2da62..8e07cf7 100644 Binary files a/sources/setup/workspace_template/desktopRuntime/d3dcompiler_47.dll and b/sources/setup/workspace_template/desktopRuntime/d3dcompiler_47.dll differ diff --git a/sources/setup/workspace_template/desktopRuntime/libEGL.dll b/sources/setup/workspace_template/desktopRuntime/libEGL.dll index 6332cc1..3c5b773 100644 Binary files a/sources/setup/workspace_template/desktopRuntime/libEGL.dll and b/sources/setup/workspace_template/desktopRuntime/libEGL.dll differ diff --git a/sources/setup/workspace_template/desktopRuntime/libEGL.so b/sources/setup/workspace_template/desktopRuntime/libEGL.so index 7d0cf97..12e971c 100644 Binary files a/sources/setup/workspace_template/desktopRuntime/libEGL.so and b/sources/setup/workspace_template/desktopRuntime/libEGL.so differ diff --git a/sources/setup/workspace_template/desktopRuntime/libGLESv2.dll b/sources/setup/workspace_template/desktopRuntime/libGLESv2.dll index c179d16..0450fe4 100644 Binary files a/sources/setup/workspace_template/desktopRuntime/libGLESv2.dll and b/sources/setup/workspace_template/desktopRuntime/libGLESv2.dll differ diff --git a/sources/setup/workspace_template/desktopRuntime/libGLESv2.so b/sources/setup/workspace_template/desktopRuntime/libGLESv2.so index 354cb86..da548ec 100644 Binary files a/sources/setup/workspace_template/desktopRuntime/libGLESv2.so and b/sources/setup/workspace_template/desktopRuntime/libGLESv2.so differ diff --git a/sources/setup/workspace_template/desktopRuntime/libvulkan.so.1 b/sources/setup/workspace_template/desktopRuntime/libvulkan.so.1 index bdfd842..735842f 100644 Binary files a/sources/setup/workspace_template/desktopRuntime/libvulkan.so.1 and b/sources/setup/workspace_template/desktopRuntime/libvulkan.so.1 differ diff --git a/sources/setup/workspace_template/desktopRuntime/vulkan-1.dll b/sources/setup/workspace_template/desktopRuntime/vulkan-1.dll index 2b703e3..20093ef 100644 Binary files a/sources/setup/workspace_template/desktopRuntime/vulkan-1.dll and b/sources/setup/workspace_template/desktopRuntime/vulkan-1.dll differ diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java index 2858d8d..073fa6a 100644 --- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java +++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java @@ -95,34 +95,66 @@ public class PlatformApplication { private static native void setClipboard0(String str); public static void setLocalStorage(String name, byte[] data) { + setLocalStorage(name, data, true); + } + + public static void setLocalStorage(String name, byte[] data, boolean hooks) { + IClientConfigAdapter adapter = PlatformRuntime.getClientConfigAdapter(); + String eagName = adapter.getLocalStorageNamespace() + "." + name; + String b64 = Base64.encodeBase64String(data); try { Storage s = Window.current().getLocalStorage(); if(s != null) { if(data != null) { - s.setItem("_eaglercraftX." + name, Base64.encodeBase64String(data)); + s.setItem(eagName, b64); }else { - s.removeItem("_eaglercraftX." + name); + s.removeItem(eagName); } } }catch(Throwable t) { } + if(hooks) { + adapter.getHooks().callLocalStorageSavedHook(name, b64); + } } public static byte[] getLocalStorage(String name) { - try { - Storage s = Window.current().getLocalStorage(); - if(s != null) { - String str = s.getItem("_eaglercraftX." + name); - if(str != null) { - return Base64.decodeBase64(str); + return getLocalStorage(name, true); + } + + public static byte[] getLocalStorage(String name, boolean hooks) { + IClientConfigAdapter adapter = PlatformRuntime.getClientConfigAdapter(); + String eagName = adapter.getLocalStorageNamespace() + "." + name; + byte[] hooked = null; + if(hooks) { + String hookedStr = adapter.getHooks().callLocalStorageLoadHook(eagName); + if(hookedStr != null) { + try { + hooked = Base64.decodeBase64(hookedStr); + }catch(Throwable t) { + PlatformRuntime.logger.error("Invalid Base64 recieved from local storage hook!"); + hooked = null; + } + } + } + if(hooked == null) { + try { + Storage s = Window.current().getLocalStorage(); + if(s != null) { + String str = s.getItem(eagName); + if(str != null) { + return Base64.decodeBase64(str); + }else { + return null; + } }else { return null; } - }else { + }catch(Throwable t) { return null; } - }catch(Throwable t) { - return null; + }else { + return hooked; } } diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java index 07d0c9c..c110a3a 100644 --- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java +++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java @@ -30,16 +30,18 @@ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.minecraft.util.MathHelper; /** - * Copyright (c) 2022-2023 LAX1DUDE. All Rights Reserved. + * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. * - * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES - * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED - * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE - * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. - * - * NOT FOR COMMERCIAL OR MALICIOUS USE - * - * (please read the 'LICENSE' file this repo's root directory for more info) + * 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 PlatformAudio { diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java index 5e0098e..e713497 100644 --- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java +++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java @@ -102,8 +102,9 @@ public class PlatformInput { public static boolean keyboardLockSupported = false; public static boolean lockKeys = false; - + private static boolean vsync = true; + private static boolean vsyncSupport = false; @JSBody(params = { }, script = "window.onbeforeunload = () => {return false;};") private static native void onBeforeCloseRegister(); @@ -252,7 +253,18 @@ public class PlatformInput { mouseDY = 0.0D; } }); - onBeforeCloseRegister(); + + try { + onBeforeCloseRegister(); + }catch(Throwable t) { + } + + try { + asyncRequestAnimationFrame(); + vsyncSupport = true; + }catch(Throwable t) { + PlatformRuntime.logger.error("VSync is not supported on this browser!"); + } fullscreenQuery = fullscreenMediaQuery(); if (keyboardLockSupported = checkKeyboardLockSupported()) { @@ -300,6 +312,9 @@ public class PlatformInput { vsync = enable; } + @JSBody(params = { "doc" }, script = "return (doc.visibilityState === \"visible\");") + private static native boolean getVisibilityState(JSObject doc); + public static void update() { double r = win.getDevicePixelRatio(); int w = PlatformRuntime.parent.getClientWidth(); @@ -320,10 +335,14 @@ public class PlatformInput { PlatformRuntime.lastFrame = t; } } - if(vsync) { - asyncRequestAnimationFrame(); + if(getVisibilityState(win.getDocument())) { + if(vsyncSupport && vsync) { + asyncRequestAnimationFrame(); + }else { + EagUtils.sleep(0l); + } }else { - EagUtils.sleep(0l); + EagUtils.sleep(50l); } } @@ -348,6 +367,10 @@ public class PlatformInput { }, 50); } + public static boolean isVSyncSupported() { + return vsyncSupport; + } + static void initFramebuffer(WebGL2RenderingContext ctx, WebGLFramebuffer fbo, int sw, int sh) { context = ctx; mainFramebuffer = fbo; @@ -599,7 +622,7 @@ public class PlatformInput { keyEvents.clear(); } - @JSBody(params = {}, script = "return window.matchMedia('(display-mode: fullscreen)');") + @JSBody(params = {}, script = "return window.matchMedia(\"(display-mode: fullscreen)\");") private static native JSObject fullscreenMediaQuery(); @JSBody(params = { "mediaQuery" }, script = "return mediaQuery.matches;") diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ClientMain.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ClientMain.java index c92acb7..b6dfb61 100644 --- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ClientMain.java +++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/ClientMain.java @@ -9,6 +9,7 @@ import org.teavm.jso.JSBody; import org.teavm.jso.JSFunctor; import org.teavm.jso.JSObject; import org.teavm.jso.browser.Window; +import org.teavm.jso.core.JSArrayReader; import org.teavm.jso.core.JSError; import org.teavm.jso.dom.css.CSSStyleDeclaration; import org.teavm.jso.dom.html.HTMLCanvasElement; @@ -21,7 +22,6 @@ import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication; import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsAssetsURI; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsAssetsURIsArray; import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsRoot; import net.lax1dude.eaglercraft.v1_8.log4j.ILogRedirector; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; @@ -87,7 +87,7 @@ public class ClientMain { if(epkSingleURL != null) { configEPKFiles = new EPKFileEntry[] { new EPKFileEntry(epkSingleURL, "") }; }else { - JSEaglercraftXOptsAssetsURIsArray epkURLs = eaglercraftOpts.getAssetsURIArray(); + JSArrayReader epkURLs = eaglercraftOpts.getAssetsURIArray(); int len = epkURLs.getLength(); if(len == 0) { throw new JSONException("assetsURI array cannot be empty!"); diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/DebugConsoleWindow.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/DebugConsoleWindow.java index f00fa87..26bedb3 100644 --- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/DebugConsoleWindow.java +++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/DebugConsoleWindow.java @@ -13,6 +13,7 @@ import org.teavm.jso.dom.html.HTMLDocument; import org.teavm.jso.dom.html.HTMLElement; import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; /** @@ -63,13 +64,13 @@ public class DebugConsoleWindow { destroyWindow(); } }); - if("true".equals(parent.getLocalStorage().getItem("_eaglercraftX.showDebugConsole"))) { + if("true".equals(parent.getLocalStorage().getItem(PlatformRuntime.getClientConfigAdapter().getLocalStorageNamespace() + ".showDebugConsole"))) { showDebugConsole0(); } } public static void showDebugConsole() { - parent.getLocalStorage().setItem("_eaglercraftX.showDebugConsole", "true"); + parent.getLocalStorage().setItem(PlatformRuntime.getClientConfigAdapter().getLocalStorageNamespace() + ".showDebugConsole", "true"); showDebugConsole0(); } diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMClientConfigAdapter.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMClientConfigAdapter.java index 61d1791..2b89f13 100644 --- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMClientConfigAdapter.java +++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMClientConfigAdapter.java @@ -10,13 +10,14 @@ import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager; import org.json.JSONArray; import org.json.JSONObject; import org.teavm.jso.JSObject; +import org.teavm.jso.core.JSArrayReader; import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter; +import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapterHooks; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsHooks; import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsRelay; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsRelaysArray; import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsRoot; import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsServer; -import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsServersArray; import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayEntry; /** @@ -56,6 +57,9 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter { private boolean checkRelaysForUpdates = false; private boolean enableSignatureBadge = false; private boolean allowVoiceClient = true; + private boolean allowFNAWSkins = true; + private String localStorageNamespace = "_eaglercraftX"; + private final TeaVMClientConfigAdapterHooks hooks = new TeaVMClientConfigAdapterHooks(); public void loadNative(JSObject jsObject) { integratedServerOpts = new JSONObject(); @@ -75,6 +79,12 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter { logInvalidCerts = EaglercraftVersion.enableUpdateService && !demoMode && eaglercraftXOpts.getLogInvalidCerts(false); enableSignatureBadge = eaglercraftXOpts.getEnableSignatureBadge(false); allowVoiceClient = eaglercraftXOpts.getAllowVoiceClient(true); + allowFNAWSkins = eaglercraftXOpts.getAllowFNAWSkins(true); + localStorageNamespace = eaglercraftXOpts.getLocalStorageNamespace(EaglercraftVersion.localStorageNamespace); + JSEaglercraftXOptsHooks hooksObj = eaglercraftXOpts.getHooks(); + if(hooksObj != null) { + hooks.loadHooks(hooksObj); + } integratedServerOpts.put("worldsDB", worldsDB); integratedServerOpts.put("demoMode", demoMode); @@ -82,8 +92,9 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter { integratedServerOpts.put("allowUpdateSvc", isAllowUpdateSvc); integratedServerOpts.put("allowUpdateDL", isAllowUpdateDL); integratedServerOpts.put("allowVoiceClient", allowVoiceClient); + integratedServerOpts.put("allowFNAWSkins", allowFNAWSkins); - JSEaglercraftXOptsServersArray serversArray = eaglercraftXOpts.getServers(); + JSArrayReader serversArray = eaglercraftXOpts.getServers(); if(serversArray != null) { for(int i = 0, l = serversArray.getLength(); i < l; ++i) { JSEaglercraftXOptsServer serverEntry = serversArray.get(i); @@ -95,7 +106,7 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter { } } - JSEaglercraftXOptsRelaysArray relaysArray = eaglercraftXOpts.getRelays(); + JSArrayReader relaysArray = eaglercraftXOpts.getRelays(); if(relaysArray != null) { boolean gotAPrimary = false; for(int i = 0, l = relaysArray.getLength(); i < l; ++i) { @@ -162,6 +173,8 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter { logInvalidCerts = EaglercraftVersion.enableUpdateService && !demoMode && eaglercraftOpts.optBoolean("logInvalidCerts", false); enableSignatureBadge = eaglercraftOpts.optBoolean("enableSignatureBadge", false); allowVoiceClient = eaglercraftOpts.optBoolean("allowVoiceClient", true); + allowFNAWSkins = eaglercraftOpts.optBoolean("allowFNAWSkins", true); + localStorageNamespace = eaglercraftOpts.optString("localStorageNamespace", EaglercraftVersion.localStorageNamespace); JSONArray serversArray = eaglercraftOpts.optJSONArray("servers"); if(serversArray != null) { for(int i = 0, l = serversArray.length(); i < l; ++i) { @@ -309,6 +322,21 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter { return allowVoiceClient; } + @Override + public boolean isAllowFNAWSkins() { + return allowFNAWSkins; + } + + @Override + public String getLocalStorageNamespace() { + return localStorageNamespace; + } + + @Override + public IClientConfigAdapterHooks getHooks() { + return hooks; + } + @Override public String toString() { JSONObject jsonObject = new JSONObject(); @@ -327,6 +355,8 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter { jsonObject.put("checkRelaysForUpdates", checkRelaysForUpdates); jsonObject.put("enableSignatureBadge", enableSignatureBadge); jsonObject.put("allowVoiceClient", allowVoiceClient); + jsonObject.put("allowFNAWSkins", allowFNAWSkins); + jsonObject.put("localStorageNamespace", localStorageNamespace); JSONArray serversArr = new JSONArray(); for(int i = 0, l = defaultServers.size(); i < l; ++i) { DefaultServer srv = defaultServers.get(i); diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMClientConfigAdapterHooks.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMClientConfigAdapterHooks.java new file mode 100644 index 0000000..2051738 --- /dev/null +++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/TeaVMClientConfigAdapterHooks.java @@ -0,0 +1,100 @@ +package net.lax1dude.eaglercraft.v1_8.internal.teavm; + +import java.util.function.Supplier; + +import org.teavm.interop.Async; +import org.teavm.interop.AsyncCallback; +import org.teavm.jso.JSFunctor; +import org.teavm.jso.JSObject; +import org.teavm.jso.browser.Window; + +import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapterHooks; +import net.lax1dude.eaglercraft.v1_8.internal.teavm.opts.JSEaglercraftXOptsHooks; +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 TeaVMClientConfigAdapterHooks implements IClientConfigAdapterHooks { + + private static final Logger logger = LogManager.getLogger("TeaVMClientConfigAdapterHooks"); + + private LocalStorageSaveHook saveHook = null; + private LocalStorageLoadHook loadHook = null; + + @JSFunctor + private static interface LocalStorageSaveHook extends JSObject { + void call(String key, String base64); + } + + @Override + public void callLocalStorageSavedHook(String key, String base64) { + if(saveHook != null) { + callHookSafe("localStorageSaved", () -> { + saveHook.call(key, base64); + }); + } + } + + @JSFunctor + private static interface LocalStorageLoadHook extends JSObject { + String call(String key); + } + + @Override + public String callLocalStorageLoadHook(String key) { + if(loadHook != null) { + return (String)callHookSafeWithReturn("localStorageLoaded", () -> { + return loadHook.call(key); + }); + }else { + return null; + } + } + + private static void callHookSafe(String identifer, Runnable hooker) { + Window.setTimeout(() -> { + try { + hooker.run(); + }catch(Throwable t) { + logger.error("Caught exception while invoking eaglercraftXOpts \"{}\" hook!", identifer); + logger.error(t); + } + }, 0); + } + + @Async + private static native Object callHookSafeWithReturn(String identifer, Supplier hooker); + + private static void callHookSafeWithReturn(String identifer, Supplier hooker, final AsyncCallback cb) { + Window.setTimeout(() -> { + Object res = null; + try { + res = hooker.get(); + }catch(Throwable t) { + logger.error("Caught exception while invoking eaglercraftXOpts \"{}\" hook!", identifer); + logger.error(t); + }finally { + cb.complete(res); + } + }, 0); + } + + public void loadHooks(JSEaglercraftXOptsHooks hooks) { + saveHook = (LocalStorageSaveHook)hooks.getLocalStorageSavedHook(); + loadHook = (LocalStorageLoadHook)hooks.getLocalStorageLoadedHook(); + } +} diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsAssetsURIsArray.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsHooks.java similarity index 69% rename from sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsAssetsURIsArray.java rename to sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsHooks.java index de08a40..19b43b1 100644 --- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsAssetsURIsArray.java +++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsHooks.java @@ -1,8 +1,7 @@ package net.lax1dude.eaglercraft.v1_8.internal.teavm.opts; -import org.teavm.jso.JSIndexer; +import org.teavm.jso.JSBody; import org.teavm.jso.JSObject; -import org.teavm.jso.JSProperty; /** * Copyright (c) 2024 lax1dude. All Rights Reserved. @@ -19,12 +18,12 @@ import org.teavm.jso.JSProperty; * POSSIBILITY OF SUCH DAMAGE. * */ -public interface JSEaglercraftXOptsAssetsURIsArray extends JSObject { +public abstract class JSEaglercraftXOptsHooks implements JSObject { - @JSIndexer - JSEaglercraftXOptsAssetsURI get(int idx); + @JSBody(script = "return (typeof this.localStorageSaved === \"function\") ? this.localStorageSaved : null;") + public native JSObject getLocalStorageSavedHook(); - @JSProperty - int getLength(); + @JSBody(script = "return (typeof this.localStorageLoaded === \"function\") ? this.localStorageLoaded : null;") + public native JSObject getLocalStorageLoadedHook(); } diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsRoot.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsRoot.java index c5c9483..a2be173 100644 --- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsRoot.java +++ b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsRoot.java @@ -2,6 +2,7 @@ package net.lax1dude.eaglercraft.v1_8.internal.teavm.opts; import org.teavm.jso.JSBody; import org.teavm.jso.JSObject; +import org.teavm.jso.core.JSArrayReader; /** * Copyright (c) 2024 lax1dude. All Rights Reserved. @@ -27,7 +28,7 @@ public abstract class JSEaglercraftXOptsRoot implements JSObject { public native String getAssetsURI(); @JSBody(script = "return (typeof this.assetsURI === \"object\") ? this.assetsURI : null;") - public native JSEaglercraftXOptsAssetsURIsArray getAssetsURIArray(); + public native JSArrayReader getAssetsURIArray(); @JSBody(params = { "def" }, script = "return (typeof this.lang === \"string\") ? this.lang : def;") public native String getLang(String defaultValue); @@ -48,10 +49,10 @@ public abstract class JSEaglercraftXOptsRoot implements JSObject { public native boolean getDemoMode(boolean defaultValue); @JSBody(script = "return (typeof this.servers === \"object\") ? this.servers : null;") - public native JSEaglercraftXOptsServersArray getServers(); + public native JSArrayReader getServers(); @JSBody(script = "return (typeof this.relays === \"object\") ? this.relays : null;") - public native JSEaglercraftXOptsRelaysArray getRelays(); + public native JSArrayReader getRelays(); @JSBody(params = { "def" }, script = "return (typeof this.checkShaderGLErrors === \"boolean\") ? this.checkShaderGLErrors : def;") public native boolean getCheckShaderGLErrors(boolean defaultValue); @@ -83,4 +84,13 @@ public abstract class JSEaglercraftXOptsRoot implements JSObject { @JSBody(params = { "def" }, script = "return (typeof this.allowVoiceClient === \"boolean\") ? this.allowVoiceClient : def;") public native boolean getAllowVoiceClient(boolean defaultValue); + @JSBody(params = { "def" }, script = "return (typeof this.allowFNAWSkins === \"boolean\") ? this.allowFNAWSkins : def;") + public native boolean getAllowFNAWSkins(boolean defaultValue); + + @JSBody(script = "return (typeof this.hooks === \"object\") ? this.hooks : null;") + public native JSEaglercraftXOptsHooks getHooks(); + + @JSBody(params = { "def" }, script = "return (typeof this.localStorageNamespace === \"string\") ? this.localStorageNamespace : def;") + public native String getLocalStorageNamespace(String defaultValue); + } diff --git a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsServersArray.java b/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsServersArray.java deleted file mode 100644 index 3299302..0000000 --- a/sources/teavm/java/net/lax1dude/eaglercraft/v1_8/internal/teavm/opts/JSEaglercraftXOptsServersArray.java +++ /dev/null @@ -1,30 +0,0 @@ -package net.lax1dude.eaglercraft.v1_8.internal.teavm.opts; - -import org.teavm.jso.JSIndexer; -import org.teavm.jso.JSObject; -import org.teavm.jso.JSProperty; - -/** - * Copyright (c) 2024 lax1dude. All Rights Reserved. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -public interface JSEaglercraftXOptsServersArray extends JSObject { - - @JSIndexer - JSEaglercraftXOptsServer get(int idx); - - @JSProperty - int getLength(); - -}