From 2daf47f202e31d05b758c5421a74d615816f71a2 Mon Sep 17 00:00:00 2001 From: coolGi Date: Thu, 17 Nov 2022 21:44:43 +1030 Subject: [PATCH 1/4] Updated updater screen (thanks a lot to Pankakes#0686) --- .../wrappers/gui/updater/ChangelogScreen.java | 89 +++++++++++++++++++ .../gui/{ => updater}/UpdateModScreen.java | 47 +++++++--- .../lod/mixins/client/MixinMinecraft.java | 2 +- .../lod/mixins/client/MixinMinecraft.java | 2 +- 4 files changed, 124 insertions(+), 16 deletions(-) create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/gui/updater/ChangelogScreen.java rename common/src/main/java/com/seibel/lod/common/wrappers/gui/{ => updater}/UpdateModScreen.java (69%) diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/gui/updater/ChangelogScreen.java b/common/src/main/java/com/seibel/lod/common/wrappers/gui/updater/ChangelogScreen.java new file mode 100644 index 000000000..cfe32d491 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/gui/updater/ChangelogScreen.java @@ -0,0 +1,89 @@ +package com.seibel.lod.common.wrappers.gui.updater; + +import com.mojang.blaze3d.platform.NativeImage; +import com.mojang.blaze3d.vertex.PoseStack; +import com.seibel.lod.common.wrappers.gui.TexturedButtonWidget; +import com.seibel.lod.core.ModInfo; +import com.seibel.lod.core.config.Config; +import com.seibel.lod.core.jar.JarUtils; +import com.seibel.lod.core.jar.updater.SelfUpdater; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.ImageButton; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.renderer.texture.DynamicTexture; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.resources.ResourceLocation; + +import java.util.Objects; + +/** + * The screen that pops up if the mod has an update. + * + * @author coolGi + */ +// TODO: After finishing the config, rewrite this in openGL as well +// TODO: Make this +public class ChangelogScreen extends Screen { + private Screen parent; + private String version; + + + public ChangelogScreen(Screen parent, String version) { + super(translate(ModInfo.ID + ".updater.title")); + this.parent = parent; + this.version = version; + } + + @Override + protected void init() { + super.init(); + + + this.addBtn( // Close + new Button(5, this.height - 25, 100, 20, translate(ModInfo.ID + ".general.back"), (btn) -> { + this.onClose(); + }) + ); + + } + + @Override + public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { + this.renderBackground(matrices); // Render background + + + // Render the text + drawCenteredString(matrices, this.font, new TextComponent("Some changelog thing\nIsn't done yet so pls wait"), this.width / 2, this.height / 2 - 35, 0xFFFFFF); + + super.render(matrices, mouseX, mouseY, delta); // Render the buttons + } + + @Override + public void onClose() { + Objects.requireNonNull(minecraft).setScreen(this.parent); // Goto the parent screen + } + + + + + // addRenderableWidget in 1.17 and over + // addButton in 1.16 and below + private void addBtn(Button button) { + #if PRE_MC_1_17_1 + this.addButton(button); + #else + this.addRenderableWidget(button); + #endif + } + + #if PRE_MC_1_19 + public static net.minecraft.network.chat.TranslatableComponent translate (String str, Object... args) { + return new net.minecraft.network.chat.TranslatableComponent(str, args); + } + #else + public static net.minecraft.network.chat.MutableComponent translate (String str, Object... args) { + return net.minecraft.network.chat.Component.translatable(str, args); + } + #endif +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/gui/UpdateModScreen.java b/common/src/main/java/com/seibel/lod/common/wrappers/gui/updater/UpdateModScreen.java similarity index 69% rename from common/src/main/java/com/seibel/lod/common/wrappers/gui/UpdateModScreen.java rename to common/src/main/java/com/seibel/lod/common/wrappers/gui/updater/UpdateModScreen.java index 78b3f7197..80d34d081 100644 --- a/common/src/main/java/com/seibel/lod/common/wrappers/gui/UpdateModScreen.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/gui/updater/UpdateModScreen.java @@ -1,7 +1,8 @@ -package com.seibel.lod.common.wrappers.gui; +package com.seibel.lod.common.wrappers.gui.updater; import com.mojang.blaze3d.platform.NativeImage; import com.mojang.blaze3d.vertex.PoseStack; +import com.seibel.lod.common.wrappers.gui.TexturedButtonWidget; import com.seibel.lod.core.ModInfo; import com.seibel.lod.core.config.Config; import com.seibel.lod.core.jar.JarUtils; @@ -48,15 +49,16 @@ public class UpdateModScreen extends Screen { ); + // Logo image this.addBtn(new ImageButton( // Where the button is on the screen - this.width / 2 - 100, this.height / 2 - 110, + this.width / 2 - 65, this.height / 2 - 110, // Width and height of the button - 200, 100, + 130, 65, // Offset 0, 0, // Some textuary stuff - 0, logoLocation, 200, 100, + 0, logoLocation, 130, 65, // Create the button and tell it where to go // For now it goes to the client option by default (buttonWidget) -> System.out.println("Nice, you found an easter egg :)"), // TODO: Add a proper easter egg to pressing the logo (maybe with confetti) @@ -66,27 +68,44 @@ public class UpdateModScreen extends Screen { } catch (Exception e) { e.printStackTrace(); } - this.addBtn( - new Button(this.width / 2 - 155, this.height / 2 + 40, 150, 20, translate(ModInfo.ID + ".updater.update"), (btn) -> { + this.addBtn(new TexturedButtonWidget( + // Where the button is on the screen + this.width / 2 - 97, this.height / 2 + 8, + // Width and height of the button + 20, 20, + // Offset + 0, 0, + // Some textuary stuff + 0, new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"), 20, 20, + // Create the button and tell it where to go + // For now it goes to the client option by default + (buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(new ChangelogScreen(this, this.newVersion)), // TODO: Add a proper easter egg to pressing the logo (maybe with confetti) + // Add a title to the button + translate(ModInfo.ID + ".updater.title") + )); + + + this.addBtn( // Update + new Button(this.width / 2 - 75, this.height / 2 + 8, 150, 20, translate(ModInfo.ID + ".updater.update"), (btn) -> { SelfUpdater.deleteOldOnClose = true; SelfUpdater.updateMod(); this.onClose(); }) ); - this.addBtn( - new Button(this.width / 2 + 5, this.height / 2 + 40, 150, 20, translate(ModInfo.ID + ".updater.silent"), (btn) -> { + this.addBtn( // Silent update + new Button(this.width / 2 - 75, this.height / 2 + 30, 150, 20, translate(ModInfo.ID + ".updater.silent"), (btn) -> { Config.Client.AutoUpdater.promptForUpdate.set(false); SelfUpdater.updateMod(); this.onClose(); }) ); - this.addBtn( - new Button(this.width / 2 - 155, this.height / 2 + 65, 150, 20, translate(ModInfo.ID + ".updater.later"), (btn) -> { + this.addBtn( // Later (not now) + new Button(this.width / 2 + 2, this.height / 2 + 70, 100, 20, translate(ModInfo.ID + ".updater.later"), (btn) -> { this.onClose(); }) ); - this.addBtn( - new Button(this.width / 2 + 5, this.height / 2 + 65, 150, 20, translate(ModInfo.ID + ".updater.never"), (btn) -> { + this.addBtn( // Never + new Button(this.width / 2 - 102, this.height / 2 + 70, 100, 20, translate(ModInfo.ID + ".updater.never"), (btn) -> { Config.Client.AutoUpdater.enableAutoUpdater.set(false); this.onClose(); }) @@ -100,8 +119,8 @@ public class UpdateModScreen extends Screen { // Render the text's - drawCenteredString(matrices, this.font, translate(ModInfo.ID + ".updater.text1"), this.width / 2, this.height / 2, 0xFFFFFF); - drawCenteredString(matrices, this.font, translate(ModInfo.ID + ".updater.text2", ModInfo.VERSION, this.newVersion), this.width / 2, this.height / 2 + 15, 0x52FD52); + drawCenteredString(matrices, this.font, translate(ModInfo.ID + ".updater.text1"), this.width / 2, this.height / 2 - 35, 0xFFFFFF); + drawCenteredString(matrices, this.font, translate(ModInfo.ID + ".updater.text2", ModInfo.VERSION, this.newVersion), this.width / 2, this.height / 2 -20, 0x52FD52); // TODO: add the tooltips for the buttons super.render(matrices, mouseX, mouseY, delta); // Render the buttons diff --git a/fabric/src/main/java/com/seibel/lod/mixins/client/MixinMinecraft.java b/fabric/src/main/java/com/seibel/lod/mixins/client/MixinMinecraft.java index 8052c9e4e..db16cdaa7 100644 --- a/fabric/src/main/java/com/seibel/lod/mixins/client/MixinMinecraft.java +++ b/fabric/src/main/java/com/seibel/lod/mixins/client/MixinMinecraft.java @@ -1,6 +1,6 @@ package com.seibel.lod.mixins.client; -import com.seibel.lod.common.wrappers.gui.UpdateModScreen; +import com.seibel.lod.common.wrappers.gui.updater.UpdateModScreen; import com.seibel.lod.core.config.Config; import com.seibel.lod.core.dependencyInjection.SingletonInjector; import com.seibel.lod.core.jar.installer.ModrinthGetter; diff --git a/forge/src/main/java/com/seibel/lod/mixins/client/MixinMinecraft.java b/forge/src/main/java/com/seibel/lod/mixins/client/MixinMinecraft.java index ab8d7e6e3..4afdd056d 100644 --- a/forge/src/main/java/com/seibel/lod/mixins/client/MixinMinecraft.java +++ b/forge/src/main/java/com/seibel/lod/mixins/client/MixinMinecraft.java @@ -1,6 +1,6 @@ package com.seibel.lod.mixins.client; -import com.seibel.lod.common.wrappers.gui.UpdateModScreen; +import com.seibel.lod.common.wrappers.gui.updater.UpdateModScreen; import com.seibel.lod.core.config.Config; import com.seibel.lod.core.dependencyInjection.SingletonInjector; import com.seibel.lod.core.jar.installer.ModrinthGetter; From 7c5ffe3f10c48b3a3bb65d30a47156858ea32943 Mon Sep 17 00:00:00 2001 From: coolGi Date: Thu, 17 Nov 2022 21:46:30 +1030 Subject: [PATCH 2/4] Core doesn't like to push itself --- coreSubProjects | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreSubProjects b/coreSubProjects index cd1c12be1..77519bde9 160000 --- a/coreSubProjects +++ b/coreSubProjects @@ -1 +1 @@ -Subproject commit cd1c12be12e86c4bc79674c60864654747933878 +Subproject commit 77519bde9d8a09edc4cd919eb468ed6615b546f5 From de60b15d2b2e36c5a987511343129000ec83f59a Mon Sep 17 00:00:00 2001 From: coolGi Date: Fri, 18 Nov 2022 19:12:25 +1030 Subject: [PATCH 3/4] Added the ChangelogScreen. New changelog screen done --- .../common/wrappers/gui/ClassicConfigGUI.java | 2 +- .../wrappers/gui/updater/ChangelogScreen.java | 117 ++++++++++++++++-- .../wrappers/gui/updater/UpdateModScreen.java | 11 +- .../lod/mixins/client/MixinMinecraft.java | 2 +- .../lod/mixins/client/MixinMinecraft.java | 2 +- 5 files changed, 117 insertions(+), 17 deletions(-) diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/gui/ClassicConfigGUI.java b/common/src/main/java/com/seibel/lod/common/wrappers/gui/ClassicConfigGUI.java index 272a7d12c..c9f557af6 100644 --- a/common/src/main/java/com/seibel/lod/common/wrappers/gui/ClassicConfigGUI.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/gui/ClassicConfigGUI.java @@ -281,7 +281,7 @@ public abstract class ClassicConfigGUI { drawCenteredString(matrices, font, title, width / 2, 15, 0xFFFFFF); // Render title // Render the tooltip only if it can find a tooltip in the language file - for (AbstractConfigType info : ConfigBase.INSTANCE.entries) { // idk why this is using the normal entries but as long as it works, it works + for (AbstractConfigType info : ConfigBase.INSTANCE.entries) { if (info.getCategory().matches(category) && info.getAppearance().showInGui) { if (list.getHoveredButton(mouseX, mouseY).isPresent()) { AbstractWidget buttonWidget = list.getHoveredButton(mouseX, mouseY).get(); diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/gui/updater/ChangelogScreen.java b/common/src/main/java/com/seibel/lod/common/wrappers/gui/updater/ChangelogScreen.java index cfe32d491..31df00cbe 100644 --- a/common/src/main/java/com/seibel/lod/common/wrappers/gui/updater/ChangelogScreen.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/gui/updater/ChangelogScreen.java @@ -1,21 +1,35 @@ package com.seibel.lod.common.wrappers.gui.updater; +import com.google.common.collect.Lists; import com.mojang.blaze3d.platform.NativeImage; import com.mojang.blaze3d.vertex.PoseStack; -import com.seibel.lod.common.wrappers.gui.TexturedButtonWidget; +import com.seibel.lod.common.wrappers.gui.ClassicConfigGUI; import com.seibel.lod.core.ModInfo; import com.seibel.lod.core.config.Config; import com.seibel.lod.core.jar.JarUtils; +import com.seibel.lod.core.jar.installer.MarkdownFormatter; +import com.seibel.lod.core.jar.installer.ModrinthGetter; import com.seibel.lod.core.jar.updater.SelfUpdater; import net.minecraft.client.Minecraft; +import net.minecraft.client.StringSplitter; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.ContainerObjectSelectionList; import net.minecraft.client.gui.components.ImageButton; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.renderer.texture.DynamicTexture; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.FormattedText; +import net.minecraft.network.chat.Style; import net.minecraft.network.chat.TextComponent; import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.FormattedCharSequence; -import java.util.Objects; +import java.util.*; /** * The screen that pops up if the mod has an update. @@ -26,13 +40,30 @@ import java.util.Objects; // TODO: Make this public class ChangelogScreen extends Screen { private Screen parent; - private String version; + private String versionID; + private List changelog; + private TextArea changelogArea; - public ChangelogScreen(Screen parent, String version) { + public ChangelogScreen(Screen parent, String versionID) { super(translate(ModInfo.ID + ".updater.title")); this.parent = parent; - this.version = version; + this.versionID = versionID; + + this.changelog = new ArrayList<>(); + // Get the release changelog and split it by the new lines + List unwrappedChangelog = + List.of(new MarkdownFormatter.MinecraftFormat().convertTo( // This formats markdown to minecraft's "ยง" characters + ModrinthGetter.changeLogs.get(versionID) + ).split("\\n")); + // Makes the words wrap around to not go off the screen + for (String str: unwrappedChangelog) { + this.changelog.addAll( + MarkdownFormatter.splitString(str, 75) + ); + } + // Debugging +// System.out.println(this.changelog); } @Override @@ -46,17 +77,29 @@ public class ChangelogScreen extends Screen { }) ); + + this.changelogArea = new TextArea(this.minecraft, this.width*2, this.height, 32, this.height - 32, 10); + for (int i = 0; i < changelog.size(); i++) { + this.changelogArea.addButton(new TextComponent(changelog.get(i))); +// drawString(matrices, this.font, changelog.get(i), this.width / 2 - 175, this.height / 2 - 100 + i*10, 0xFFFFFF); + } + } @Override public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { this.renderBackground(matrices); // Render background + // Set the scroll position to the mouse height relative to the screen + this.changelogArea.setScrollAmount( + ((double) mouseY)/((double) this.height) * this.changelogArea.getMaxScroll() + ); - // Render the text - drawCenteredString(matrices, this.font, new TextComponent("Some changelog thing\nIsn't done yet so pls wait"), this.width / 2, this.height / 2 - 35, 0xFFFFFF); + this.changelogArea.render(matrices, mouseX, mouseY, delta); // Render the changelog super.render(matrices, mouseX, mouseY, delta); // Render the buttons + + drawCenteredString(matrices, font, title, width / 2, 15, 0xFFFFFF); // Render title } @Override @@ -65,8 +108,6 @@ public class ChangelogScreen extends Screen { } - - // addRenderableWidget in 1.17 and over // addButton in 1.16 and below private void addBtn(Button button) { @@ -86,4 +127,62 @@ public class ChangelogScreen extends Screen { return net.minecraft.network.chat.Component.translatable(str, args); } #endif + + + + + + + + + + + public static class TextArea extends ContainerObjectSelectionList { + Font textRenderer; + + public TextArea(Minecraft minecraftClient, int i, int j, int k, int l, int m) { + super(minecraftClient, i, j, k, l, m); + this.centerListVertically = false; + textRenderer = minecraftClient.font; + } + + public void addButton(Component text) { + this.addEntry(ButtonEntry.create(text)); + } + + @Override + public int getRowWidth() { + return 10000; + } + } + + public static class ButtonEntry extends ContainerObjectSelectionList.Entry { + private static final Font textRenderer = Minecraft.getInstance().font; + private final Component text; + private final List children = new ArrayList<>(); + + private ButtonEntry(Component text) { + this.text = text; + } + + public static ButtonEntry create(Component text) { + return new ButtonEntry(text); + } + + @Override + public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { + GuiComponent.drawString(matrices, textRenderer, text, 12, y + 5, 0xFFFFFF); + } + + @Override + public List children() { + return children; + } + #if POST_MC_1_17_1 + @Override + public List narratables() { + return children; + } + #endif + } } \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/gui/updater/UpdateModScreen.java b/common/src/main/java/com/seibel/lod/common/wrappers/gui/updater/UpdateModScreen.java index 80d34d081..b91b2ca6d 100644 --- a/common/src/main/java/com/seibel/lod/common/wrappers/gui/updater/UpdateModScreen.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/gui/updater/UpdateModScreen.java @@ -6,6 +6,7 @@ import com.seibel.lod.common.wrappers.gui.TexturedButtonWidget; import com.seibel.lod.core.ModInfo; import com.seibel.lod.core.config.Config; import com.seibel.lod.core.jar.JarUtils; +import com.seibel.lod.core.jar.installer.ModrinthGetter; import com.seibel.lod.core.jar.updater.SelfUpdater; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.Button; @@ -25,13 +26,13 @@ import java.util.*; // and also maybe add this suggestion https://discord.com/channels/881614130614767666/1035863487110467625/1035949054485594192 public class UpdateModScreen extends Screen { private Screen parent; - private String newVersion; + private String newVersionID; - public UpdateModScreen(Screen parent, String newVersion) { + public UpdateModScreen(Screen parent, String newVersionID) { super(translate(ModInfo.ID + ".updater.title")); this.parent = parent; - this.newVersion = newVersion; + this.newVersionID = newVersionID; } @Override @@ -79,7 +80,7 @@ public class UpdateModScreen extends Screen { 0, new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"), 20, 20, // Create the button and tell it where to go // For now it goes to the client option by default - (buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(new ChangelogScreen(this, this.newVersion)), // TODO: Add a proper easter egg to pressing the logo (maybe with confetti) + (buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(new ChangelogScreen(this, this.newVersionID)), // TODO: Add a proper easter egg to pressing the logo (maybe with confetti) // Add a title to the button translate(ModInfo.ID + ".updater.title") )); @@ -120,7 +121,7 @@ public class UpdateModScreen extends Screen { // Render the text's drawCenteredString(matrices, this.font, translate(ModInfo.ID + ".updater.text1"), this.width / 2, this.height / 2 - 35, 0xFFFFFF); - drawCenteredString(matrices, this.font, translate(ModInfo.ID + ".updater.text2", ModInfo.VERSION, this.newVersion), this.width / 2, this.height / 2 -20, 0x52FD52); + drawCenteredString(matrices, this.font, translate(ModInfo.ID + ".updater.text2", ModInfo.VERSION, ModrinthGetter.releaseNames.get(this.newVersionID)), this.width / 2, this.height / 2 -20, 0x52FD52); // TODO: add the tooltips for the buttons super.render(matrices, mouseX, mouseY, delta); // Render the buttons diff --git a/fabric/src/main/java/com/seibel/lod/mixins/client/MixinMinecraft.java b/fabric/src/main/java/com/seibel/lod/mixins/client/MixinMinecraft.java index db16cdaa7..6741eec00 100644 --- a/fabric/src/main/java/com/seibel/lod/mixins/client/MixinMinecraft.java +++ b/fabric/src/main/java/com/seibel/lod/mixins/client/MixinMinecraft.java @@ -34,7 +34,7 @@ public class MixinMinecraft if (SelfUpdater.onStart()) { instance.setScreen(new UpdateModScreen( new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons - ModrinthGetter.getLatestNameForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()) + ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()) )); } else { instance.setScreen(guiScreen); // Sets the screen back to the vanilla screen as if nothing ever happened diff --git a/forge/src/main/java/com/seibel/lod/mixins/client/MixinMinecraft.java b/forge/src/main/java/com/seibel/lod/mixins/client/MixinMinecraft.java index 4afdd056d..1cf4923ab 100644 --- a/forge/src/main/java/com/seibel/lod/mixins/client/MixinMinecraft.java +++ b/forge/src/main/java/com/seibel/lod/mixins/client/MixinMinecraft.java @@ -34,7 +34,7 @@ public class MixinMinecraft if (SelfUpdater.onStart()) { instance.setScreen(new UpdateModScreen( new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons - ModrinthGetter.getLatestNameForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()) + ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()) )); } else { instance.setScreen(guiScreen); // Sets the screen back to the vanilla screen as if nothing ever happened From 19578f21044e7053cf626d1eda5c610b2e1adef0 Mon Sep 17 00:00:00 2001 From: coolGi Date: Fri, 18 Nov 2022 19:13:00 +1030 Subject: [PATCH 4/4] pushed core --- coreSubProjects | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreSubProjects b/coreSubProjects index 77519bde9..b64fdf214 160000 --- a/coreSubProjects +++ b/coreSubProjects @@ -1 +1 @@ -Subproject commit 77519bde9d8a09edc4cd919eb468ed6615b546f5 +Subproject commit b64fdf214eeeb3aac8f20cd84c4693df67e2bfb0