diff options
author | Roman / Nea <roman.graef@gmail.com> | 2022-08-11 13:18:16 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-11 21:18:16 +1000 |
commit | 4d48d36976dae85fcd46a605408b006857f1f9fd (patch) | |
tree | b44cfd3ba9d51c1c77f1d587880622a61f1773b1 | |
parent | 74ae776a74bd6d0b24df7354e49287a42600011a (diff) | |
download | NotEnoughUpdates-4d48d36976dae85fcd46a605408b006857f1f9fd.tar.gz NotEnoughUpdates-4d48d36976dae85fcd46a605408b006857f1f9fd.tar.bz2 NotEnoughUpdates-4d48d36976dae85fcd46a605408b006857f1f9fd.zip |
Autoupdate (#191)
Co-authored-by: NopoTheGamer <40329022+NopoTheGamer@users.noreply.github.com>
15 files changed, 723 insertions, 79 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a5fa2b6a..c478982f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -74,6 +74,7 @@ public static final int HOTFIX_VERSION_ID = 0; /* Reset the HOTFIX_VERSION_ID ba "pre_version": "0.0", /* Reset to 0.0 */ "pre_version_id": 0, /* Reset to 0 */ "update_link": "https://github.com/NotEnoughUpdates/NotEnoughUpdates/releases/tag/<VERSIONNAME>", /* Change download link to the github release */ + "update_direct": "https://github.com/NotEnoughUpdates/NotEnoughUpdates/releases/download/<VERSIONNAME>/NotEnoughUpdates-<VERSIONNAME>.jar", /* Change direct link to a direct download link */ } ``` @@ -104,7 +105,8 @@ public static final int PRE_VERSION_ID = 1; /* Increment the PRE_VERSION_ID */ "pre_update_msg": "§7§m§l--------------------§5§l[§c§lNEU§5§l]§7§m§l--------------------\n\n§7A new pre-release, v§52.0-PRE{pre_version}§7, is now available!\n ", /* Update the version name. Remove old patch notes; Optionally add in a short new patch note. */ "pre_version": "0.0", /* Set to a new string (preferably increase the major version every time, except for hotfixes on the prerelease stream) */ "pre_version_id": 0, /* Set to PRE_VERSION_ID from java */ - "update_link": "https://github.com/NotEnoughUpdates/NotEnoughUpdates/releases/tag/<VERSIONNAME>", /* Change download link to the github release */ + "pre_update_link": "https://github.com/NotEnoughUpdates/NotEnoughUpdates/releases/tag/<VERSIONNAME>", /* Change download link to the github release */ + "pre_update_direct": "https://github.com/NotEnoughUpdates/NotEnoughUpdates/releases/download/<VERSIONNAME>/NotEnoughUpdates-<VERSIONNAME>.jar", /* Change direct link to a direct download link */ } ``` @@ -136,6 +138,7 @@ public static final int HOTFIX_VERSION_ID = 1; /* Increment the HOTFIX_VERSION_I "version_id": 20100, /* Same as version */ "update_msg": "§7§m§l--------------------§6§l[§c§lNEU§6§l]§7§m§l--------------------\n\n§7A new version, v§6{version}§7, is now available!\n ", /* Update the version name. Don't remove old patch notes; Optionally add in a short new patch note. Indicate that there is a hotfix present */ "update_link": "https://github.com/NotEnoughUpdates/NotEnoughUpdates/releases/tag/<VERSIONNAME>", /* Change download link to the github release */ + "update_direct": "https://github.com/NotEnoughUpdates/NotEnoughUpdates/releases/download/<VERSIONNAME>/NotEnoughUpdates-<VERSIONNAME>.jar", /* Change direct link to a direct download link */ } ``` diff --git a/Update Notes/2.1.md b/Update Notes/2.1.md index f76d3e75..c41b9ccb 100644 --- a/Update Notes/2.1.md +++ b/Update Notes/2.1.md @@ -101,10 +101,12 @@ - Added Fishing Timer over bobber - nea89 - Added [Auction Profit Viewer Overlay](https://cdn.discordapp.com/attachments/848901833119629332/993191851400101918/176946124-28ddf336-1ec7-460b-b22a-5fe2733b46a3.png) - efefury - Added Trophy Reward Overlay - hannibal2 +- Add Auto-Updater (linux and scu only) - nea89 - Added power stone feature - hannibal2 - Added abiphone warning - hannibal2 - Added blur limit at 100 - nopo + ### **Bug Fixes:** - Fixed wiki pages freezing the entire game - nea89 diff --git a/build.gradle.kts b/build.gradle.kts index 90ee7d94..581816e2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -92,6 +92,7 @@ repositories { mavenCentral() mavenLocal() maven("https://repo.spongepowered.org/maven/") + maven("https://pkgs.dev.azure.com/djtheredstoner/DevAuth/_packaging/public/maven/v1") maven("https://jitpack.io") } @@ -107,6 +108,8 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter:5.8.2") testAnnotationProcessor("org.spongepowered:mixin:0.8.4-SNAPSHOT") // modImplementation("io.github.notenoughupdates:MoulConfig:0.0.1") + + modRuntimeOnly("me.djtheredstoner:DevAuth-forge-legacy:1.1.0") } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java b/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java index 9140120a..4e5c5b7d 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java @@ -672,7 +672,8 @@ public class NEUManager { ea = ea.getCompoundTag("Properties"); ea = ea.getTagList("textures", 10).getCompoundTagAt(0); String name = ea3.getString("Name").replaceAll(" M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$", ""); - return "put(\"ID\", Utils.createSkull(EnumChatFormatting.AQUA + \"" + name + "\" ,\"" + ea2.getString("Id") + "\", \"" + ea.getString("Value") +"\"));"; + return "put(\"ID\", Utils.createSkull(EnumChatFormatting.AQUA + \"" + name + "\" ,\"" + ea2.getString("Id") + + "\", \"" + ea.getString("Value") + "\"));"; } } return null; @@ -912,6 +913,7 @@ public class NEUManager { NBTTagCompound tag = stack.getTagCompound(); return getSkullValueFromNBT(tag); } + public String getInternalNameForItem(ItemStack stack) { if (stack == null) return null; NBTTagCompound tag = stack.getTagCompound(); @@ -948,8 +950,7 @@ public class NEUManager { if (!usagesMap.containsKey(internalName)) return false; List<NeuRecipe> usages = getAvailableUsagesFor(internalName); if (usages.isEmpty()) return false; - Minecraft.getMinecraft().displayGuiScreen( - new GuiItemRecipe(usages, this)); + NotEnoughUpdates.INSTANCE.openGui = (new GuiItemRecipe(usages, this)); return true; } @@ -957,8 +958,7 @@ public class NEUManager { if (!recipesMap.containsKey(internalName)) return false; List<NeuRecipe> recipes = getAvailableRecipesFor(internalName); if (recipes.isEmpty()) return false; - Minecraft.getMinecraft().displayGuiScreen( - new GuiItemRecipe(recipes, this)); + NotEnoughUpdates.INSTANCE.openGui = (new GuiItemRecipe(recipes, this)); return true; } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java index e51f118e..19f5a780 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java @@ -813,7 +813,7 @@ public class NEUOverlay extends Gui { /** * Handles the mouse input, cancelling the forge event if a NEU gui element is clicked. */ - public boolean mouseInput() { + public synchronized boolean mouseInput() { if (disabled) { return false; } @@ -1483,7 +1483,7 @@ public class NEUOverlay extends Gui { * Returns an index-able array containing the elements in searchedItems. * Whenever searchedItems is updated in updateSearch(), the array is recreated here. */ - public List<JsonObject> getSearchedItems() { + public synchronized List<JsonObject> getSearchedItems() { if (searchedItems == null) { updateSearch(); return new ArrayList<>(); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java index a1eb03d1..25f72fe9 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java @@ -57,6 +57,7 @@ import io.github.moulberry.notenoughupdates.miscfeatures.SunTzu; import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.CustomBiomes; import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.CustomBlockSounds; import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.DwarvenMinesTextures; +import io.github.moulberry.notenoughupdates.miscfeatures.updater.AutoUpdater; import io.github.moulberry.notenoughupdates.miscgui.CalendarOverlay; import io.github.moulberry.notenoughupdates.miscgui.InventoryStorageSelector; import io.github.moulberry.notenoughupdates.miscgui.SignCalculator; @@ -172,6 +173,7 @@ public class NotEnoughUpdates { public Commands commands; public boolean packDevEnabled = false; public Color[][] colourMap = null; + public AutoUpdater autoUpdater = new AutoUpdater(this); private File configFile; private long lastChatMessage = 0; private long secondLastChatMessage = 0; diff --git a/src/main/java/io/github/moulberry/notenoughupdates/commands/Commands.java b/src/main/java/io/github/moulberry/notenoughupdates/commands/Commands.java index b152bc09..917185ab 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/commands/Commands.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/commands/Commands.java @@ -19,6 +19,7 @@ package io.github.moulberry.notenoughupdates.commands; +import io.github.moulberry.notenoughupdates.NotEnoughUpdates; import io.github.moulberry.notenoughupdates.commands.dev.DevTestCommand; import io.github.moulberry.notenoughupdates.commands.dev.DiagCommand; import io.github.moulberry.notenoughupdates.commands.dev.DungeonWinTestCommand; @@ -41,6 +42,7 @@ import io.github.moulberry.notenoughupdates.commands.misc.CalculatorCommand; import io.github.moulberry.notenoughupdates.commands.misc.CalendarCommand; import io.github.moulberry.notenoughupdates.commands.misc.CosmeticsCommand; import io.github.moulberry.notenoughupdates.commands.misc.CustomizeCommand; +import io.github.moulberry.notenoughupdates.commands.misc.UpdateCommand; import io.github.moulberry.notenoughupdates.commands.profile.CataCommand; import io.github.moulberry.notenoughupdates.commands.profile.PeekCommand; import io.github.moulberry.notenoughupdates.commands.profile.PvCommand; @@ -94,6 +96,7 @@ public class Commands { ClientCommandHandler.instance.registerCommand(new AhCommand()); ClientCommandHandler.instance.registerCommand(new CalculatorCommand()); ClientCommandHandler.instance.registerCommand(new CalendarCommand()); + ClientCommandHandler.instance.registerCommand(new UpdateCommand(NotEnoughUpdates.INSTANCE)); // Fairy Soul Commands ClientCommandHandler.instance.registerCommand(new FairySouls.FairySoulsCommand()); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/commands/misc/UpdateCommand.java b/src/main/java/io/github/moulberry/notenoughupdates/commands/misc/UpdateCommand.java new file mode 100644 index 00000000..36443b27 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/commands/misc/UpdateCommand.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2022 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.commands.misc; + +import io.github.moulberry.notenoughupdates.NotEnoughUpdates; +import io.github.moulberry.notenoughupdates.commands.ClientCommandBase; +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.util.ChatComponentText; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +public class UpdateCommand extends ClientCommandBase { + NotEnoughUpdates neu; + + public UpdateCommand(NotEnoughUpdates neu) { + super("neuupdate"); + this.neu = neu; + } + + @Override + public List<String> getCommandAliases() { + return Arrays.asList("neuupdates", "enoughupdates"); + } + + public void displayHelp(ICommandSender sender) { + sender.addChatMessage(new ChatComponentText( + "" + + "§e[NEU] §b/neuupdate help - View help.\n" + + "§e[NEU] §b/neuupdate check - Check for updates.\n" + + "§e[NEU] §b/neuupdate url <url> - Load an update from an direct download URL.\n" + + " §cONLY DO THIS WITH TRUSTED URLS OR IT MIGHT RESULT IN A RAT!\n" + + "§e[NEU] §b/neuupdate fromartifact <url> - Load an update from an artifact.\n" + + " §cIf you don't know what this is, don't use it.\n" + + "" + )); + + } + + @Override + public void processCommand(ICommandSender sender, String[] args) throws CommandException { + if ((args.length == 1 && Objects.equals(args[0], "help")) || args.length == 0) { + displayHelp(sender); + return; + } + switch (args[0].toLowerCase().intern()) { + case "check": + neu.autoUpdater.displayUpdateMessageIfOutOfDate(); + break; + case "url": + if (args.length != 2) { + sender.addChatMessage(new ChatComponentText("§e[NEU] §cPlease provide an URL")); + } + URL url; + try { + url = new URL(args[1]); + } catch (MalformedURLException e) { + e.printStackTrace(); + sender.addChatMessage(new ChatComponentText("§e[NEU] §cInvalid URL")); + return; + } + neu.autoUpdater.updateFromURL(url); + break; + case "scheduledownload": + neu.autoUpdater.scheduleDownload(); + break; + case "updatemodes": + sender.addChatMessage(new ChatComponentText("§e[NEU] §bTo ensure we do not accidentally corrupt your mod folder, we can only offer support for autoupdates on system with certain capabilities for file deletions (specifically unix systems). You can still manually update your files")); + break; + default: + displayHelp(sender); + + } + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/listener/NEUEventListener.java b/src/main/java/io/github/moulberry/notenoughupdates/listener/NEUEventListener.java index 26c2d1f9..041da446 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/listener/NEUEventListener.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/listener/NEUEventListener.java @@ -80,73 +80,6 @@ public class NEUEventListener { this.neu = neu; } - private void displayUpdateMessage(JsonObject updateJson, String updateMessage, String downloadLink) { - int firstWidth = -1; - - for (String line : Iterables.concat(Arrays.asList(updateMessage.split("\n")), Arrays.asList("Download here"))) { - FontRenderer fr = Minecraft.getMinecraft().fontRendererObj; - boolean isDownloadLink = line.equals("Download here"); - int width = fr.getStringWidth(line); - if (firstWidth == -1) { - firstWidth = width; - } - int missingLen = firstWidth - width; - if (missingLen > 0) { - StringBuilder sb = new StringBuilder(missingLen / 4 / 2 +line.length()); - for (int i = 0; i < missingLen / 4 / 2; i++) { /* fr.getCharWidth(' ') == 4 */ - sb.append(" "); - } - sb.append(line); - line = sb.toString(); - } - ChatComponentText cp = new ChatComponentText(line); - if (isDownloadLink) - cp.setChatStyle(Utils.createClickStyle(ClickEvent.Action.OPEN_URL, downloadLink)); - Minecraft.getMinecraft().thePlayer.addChatMessage(cp); - } - neu.displayLinks(updateJson, firstWidth); - NotificationHandler.displayNotification(Arrays.asList( - "§eThere is a new version of NotEnoughUpdates available.", - "§eCheck the chat for more information" - ), true); - } - - private void displayUpdateMessageIfOutOfDate() { - File repo = neu.manager.repoLocation; - if (repo.exists()) { - File updateJson = new File(repo, "update.json"); - try { - JsonObject o = neu.manager.getJsonFromFile(updateJson); - - int fullReleaseVersion = - o.has("version_id") && o.get("version_id").isJsonPrimitive() ? o.get("version_id").getAsInt() : -1; - int preReleaseVersion = - o.has("version_id") && o.get("version_id").isJsonPrimitive() ? o.get("version_id").getAsInt() : -1; - int hotfixVersion = - o.has("hotfix_id") && o.get("hotfix_id").isJsonPrimitive() ? o.get("hotfix_id").getAsInt() : -1; - - boolean hasFullReleaseAvailableForUpgrade = fullReleaseVersion > NotEnoughUpdates.VERSION_ID; - boolean hasHotfixAvailableForUpgrade = - fullReleaseVersion == NotEnoughUpdates.VERSION_ID && hotfixVersion > NotEnoughUpdates.HOTFIX_VERSION_ID; - boolean hasPreReleaseAvailableForUpdate = - fullReleaseVersion == NotEnoughUpdates.VERSION_ID && preReleaseVersion > NotEnoughUpdates.PRE_VERSION_ID; - - int updateChannel = NotEnoughUpdates.INSTANCE.config.notifications.updateChannel; /* 1 = Full, 2 = Pre */ - if (hasFullReleaseAvailableForUpgrade || (hasHotfixAvailableForUpgrade && updateChannel == 1)) { - displayUpdateMessage(o, o.get("update_msg").getAsString(), o.get("update_link").getAsString()); - } else if (hasPreReleaseAvailableForUpdate && updateChannel == 2) { - displayUpdateMessage(o, o.get("pre_update_msg").getAsString(), o.get("pre_update_link").getAsString()); - } - } catch (Exception e) { - e.printStackTrace(); - Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText( - "§e[NEU] §cThere has been an error checking for updates. Check the log or join the discord for more information.").setChatStyle( - Utils.createClickStyle( - ClickEvent.Action.OPEN_URL, "https://discord.gg/moulberry"))); - } - } - } - @SubscribeEvent public void onWorldLoad(WorldEvent.Unload event) { NotEnoughUpdates.INSTANCE.saveConfig(); @@ -266,7 +199,7 @@ public class NEUEventListener { joinedSB = true; if (NotEnoughUpdates.INSTANCE.config.notifications.updateChannel != 0) { - displayUpdateMessageIfOutOfDate(); + NotEnoughUpdates.INSTANCE.autoUpdater.displayUpdateMessageIfOutOfDate(); } if (NotEnoughUpdates.INSTANCE.config.notifications.doRamNotif) { diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.java new file mode 100644 index 00000000..7869225d --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2022 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.miscfeatures.updater; + +import com.google.common.collect.Iterables; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSyntaxException; +import io.github.moulberry.notenoughupdates.NotEnoughUpdates; +import io.github.moulberry.notenoughupdates.util.NotificationHandler; +import io.github.moulberry.notenoughupdates.util.Utils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.event.ClickEvent; +import net.minecraft.event.HoverEvent; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.ChatStyle; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IChatComponent; +import net.minecraftforge.fml.common.Loader; +import org.apache.commons.lang3.SystemUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Arrays; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +public class AutoUpdater { + + NotEnoughUpdates neu; + + public AutoUpdater(NotEnoughUpdates neu) { + this.neu = neu; + } + + public void logProgress(String str) { + logProgress(new ChatComponentText(str)); + } + + public void logProgress(IChatComponent chatComponent) { + Minecraft.getMinecraft().addScheduledTask(() -> { + IChatComponent chatComponent1 = new ChatComponentText(""); + chatComponent1.setChatStyle(new ChatStyle().setColor(EnumChatFormatting.AQUA)); + chatComponent1.appendSibling(chatComponent); + Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText("§e[NEU-AutoUpdater] ").appendSibling( + chatComponent1)); + }); + } + + public UpdateLoader getUpdateLoader(URL url) { + if (SystemUtils.IS_OS_UNIX) { + return new LinuxBasedUpdater(this, url); + } + if (Loader.isModLoaded("skyblockclientupdater") && SCUCompatUpdater.IS_ENABLED) { + return SCUCompatUpdater.tryCreate(this, url); + } + return null; + } + + UpdateLoader updateLoader; + + public void updateFromURL(URL url) { + if (updateLoader != null) { + logProgress( + "There is already an update in progress, so the auto updater cannot process this update (as it might already be installed or is currently being downloaded). Please restart your client to install another update"); + return; + } + if (!"https".equals(url.getProtocol())) { + logProgress("§cInvalid protocol in url: " + url + ". Only https is a valid protocol."); + return; + } + updateLoader = getUpdateLoader(url); + if (updateLoader == null) { + logProgress(new ChatComponentText( + "Your system does not support auto updates. Please download this update manually. Click here to read more about auto update compatibility (or the link above for manual downloads)") + .setChatStyle( + new ChatStyle() + .setChatHoverEvent(new HoverEvent( + HoverEvent.Action.SHOW_TEXT, + new ChatComponentText("Click here to read about auto update modalities") + )) + .setChatClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/neuupdate updatemodes")) + )); + return; + } + updateLoader.greet(); + logProgress(new ChatComponentText("[Start download now]") + .setChatStyle(new ChatStyle() + .setColor(EnumChatFormatting.GREEN) + .setChatHoverEvent(new HoverEvent( + HoverEvent.Action.SHOW_TEXT, + new ChatComponentText("Click to start download.") + )) + .setChatClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + "/neuupdate scheduleDownload" + )))); + } + + public void scheduleDownload() { + if (updateLoader == null) { + logProgress("§cNo update found. Try running /neuupdate check first"); + return; + } + if (updateLoader.getState() != UpdateLoader.State.NOTHING) { + logProgress("§cUpdate download already started. No need to start the download again."); + return; + } + logProgress("Download started."); + updateLoader.scheduleDownload(); + } + + private void displayUpdateMessage( + JsonObject updateJson, + String updateMessage, + String downloadLink, + String directDownload + ) { + int firstWidth = -1; + + for (String line : Iterables.concat(Arrays.asList(updateMessage.split("\n")), Arrays.asList("Download here"))) { + FontRenderer fr = Minecraft.getMinecraft().fontRendererObj; + boolean isDownloadLink = line.equals("Download here"); + int width = fr.getStringWidth(line); + if (firstWidth == -1) { + firstWidth = width; + } + int missingLen = firstWidth - width; + if (missingLen > 0) { + StringBuilder sb = new StringBuilder(missingLen / 4 / 2 + line.length()); + for (int i = 0; i < missingLen / 4 / 2; i++) { /* fr.getCharWidth(' ') == 4 */ + sb.append(" "); + } + sb.append(line); + line = sb.toString(); + } + ChatComponentText cp = new ChatComponentText(line); + if (isDownloadLink) + cp.setChatStyle(Utils.createClickStyle(ClickEvent.Action.OPEN_URL, downloadLink)); + Minecraft.getMinecraft().thePlayer.addChatMessage(cp); + } + neu.displayLinks(updateJson, firstWidth); + NotificationHandler.displayNotification(Arrays.asList( + "", + "§eThere is a new version of NotEnoughUpdates available.", + "§eCheck the chat for more information" + ), true); + try { + if (directDownload != null) + updateFromURL(new URL(directDownload)); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + } + + public void displayUpdateMessageIfOutOfDate() { + File repo = neu.manager.repoLocation; + if (repo.exists()) { + File updateJson = new File(repo, "update.json"); + try { + JsonObject o = neu.manager.getJsonFromFile(updateJson); + + int fullReleaseVersion = + o.has("version_id") && o.get("version_id").isJsonPrimitive() ? o.get("version_id").getAsInt() : -1; + int preReleaseVersion = + o.has("version_id") && o.get("version_id").isJsonPrimitive() ? o.get("version_id").getAsInt() : -1; + int hotfixVersion = + o.has("hotfix_id") && o.get("hotfix_id").isJsonPrimitive() ? o.get("hotfix_id").getAsInt() : -1; + + boolean hasFullReleaseAvailableForUpgrade = fullReleaseVersion > NotEnoughUpdates.VERSION_ID; + boolean hasHotfixAvailableForUpgrade = + fullReleaseVersion == NotEnoughUpdates.VERSION_ID && hotfixVersion > NotEnoughUpdates.HOTFIX_VERSION_ID; + boolean hasPreReleaseAvailableForUpdate = + fullReleaseVersion == NotEnoughUpdates.VERSION_ID && preReleaseVersion > NotEnoughUpdates.PRE_VERSION_ID; + + int updateChannel = NotEnoughUpdates.INSTANCE.config.notifications.updateChannel; /* 1 = Full, 2 = Pre */ + if (hasFullReleaseAvailableForUpgrade || (hasHotfixAvailableForUpgrade && updateChannel == 1)) { + displayUpdateMessage( + o, + o.get("update_msg").getAsString(), + o.get("update_link").getAsString(), + o.has("update_direct") ? o.get("update_direct").getAsString() : null + ); + } else if (hasPreReleaseAvailableForUpdate && updateChannel == 2) { + displayUpdateMessage( + o, + o.get("pre_update_msg").getAsString(), + o.get("pre_update_link").getAsString(), + o.has("pre_update_direct") ? o.get("pre_update_direct").getAsString() : null + ); + } + } catch (Exception e) { + e.printStackTrace(); + Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText( + "§e[NEU] §cThere has been an error checking for updates. Check the log or join the discord for more information.").setChatStyle( + Utils.createClickStyle( + ClickEvent.Action.OPEN_URL, "https://discord.gg/moulberry"))); + } + } + } + + private boolean validateMcModInfo(JsonArray array) { + if (array.size() != 1) return false; + JsonElement jsonElement = array.get(0); + if (!jsonElement.isJsonObject()) return false; + JsonObject jsonObject = jsonElement.getAsJsonObject(); + if (!jsonObject.has("modid")) return false; + JsonElement modid = jsonObject.get("modid"); + if (!modid.isJsonPrimitive()) return false; + JsonPrimitive primitive = modid.getAsJsonPrimitive(); + if (!primitive.isString()) return false; + return "notenoughupdates".equals(primitive.getAsString()); + } + + public boolean isNeuJar(File sus) { + try (ZipFile zipFile = new ZipFile(sus)) { + ZipEntry entry = zipFile.getEntry("mcmod.info"); + if (entry == null) { + return false; + } + try (InputStream inputStream = zipFile.getInputStream(entry)) { + JsonArray jsonArray = neu.manager.gson.fromJson( + new InputStreamReader(inputStream), + JsonArray.class + ); + return validateMcModInfo(jsonArray); + } + } catch (IOException | JsonSyntaxException e) { + e.printStackTrace(); + } + return false; + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/LinuxBasedUpdater.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/LinuxBasedUpdater.java new file mode 100644 index 00000000..3118b135 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/LinuxBasedUpdater.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2022 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.miscfeatures.updater; + +import java.io.File; +import java.net.URL; +import java.util.List; + +class LinuxBasedUpdater /* Based on what? */ extends UpdateLoader { + + LinuxBasedUpdater(AutoUpdater updater, URL url) { + super(updater, url); + } + + @Override + public void greet() { + updater.logProgress( + "Welcome Aristocrat! Your superior linux system configuration is supported for NEU auto updates."); + } + + @Override + public void deleteFiles(List<File> toDelete) { + for (File toDel : toDelete) { + if (!toDel.delete()) { + updater.logProgress("§cCould not delete old version of NEU: " + toDel + ". Please manually delete file."); + state = State.FAILED; + } + } + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/SCUCompatUpdater.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/SCUCompatUpdater.java new file mode 100644 index 00000000..3cb600cb --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/SCUCompatUpdater.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2022 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.miscfeatures.updater; + +import java.io.File; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.URL; +import java.util.List; + +/* + * Legal considerations: Skyblock Client Updater is licensed under the GNU AGPL v3.0 or later (with modifications). + * https://github.com/My-Name-Is-Jeff/SkyblockClient-Updater/blob/main/LICENSE + * + * However, even tho the AGPL License does not allow conveying covered work in combination with LGPL licensed code + * (such as our own), we do not perceive ourselves as conveying neither an unmodified version of Skyblock Client Updater + * nor a work based on Skyblock Client Updater (modified work) since our work is usable and functional in its entirety + * without presence of Skyblock Client Updater and is not to be distributed along a copy of Skyblock Client Updater + * unless that combined work is licensed with respect of both the LGPL and the AGPL, therefore is not adapting any part + * of Skyblock Client Updater unless already part of a whole distribution. + * + * In case the Copyright owner (Lily aka My-Name-Is-Jeff on Github) disagrees, we are willing to take down this module + * (or only convey this component of our work under a pure GPL license) with or without them providing legal grounds + * for this request. However, due to them not being able to be reached for comment, we will include this + * component for the time being. + * */ +public class SCUCompatUpdater extends UpdateLoader { + + public static final boolean IS_ENABLED = false; + + private SCUCompatUpdater(AutoUpdater updater, URL url) { + super(updater, url); + } + + @Override + public void greet() { + updater.logProgress("Skyblock Client Updater compatibility layer loaded."); + } + + @Override + public void deleteFiles(List<File> toDelete) { + try { + for (File f : toDelete) + ReflectionHolder.deleteFileOnShutdownHandle.invoke(ReflectionHolder.updateCheckerInstance, f, ""); + } catch (Throwable e) { + e.printStackTrace(); + updater.logProgress("Invoking SCU failed. Check the log for more info."); + state = State.FAILED; + } + } + + static class ReflectionHolder { + static boolean isSCUFullyPresent = false; + static Class<?> updateChecker; + static Object updateCheckerInstance; + static Method deleteFileOnShutdown; + static MethodHandle deleteFileOnShutdownHandle; + + static { + try { + updateChecker = Class.forName("mynameisjeff.skyblockclientupdater.utils.UpdateChecker"); + Field instanceField = updateChecker.getDeclaredField("INSTANCE"); + instanceField.setAccessible(true); + updateCheckerInstance = instanceField.get(null); + deleteFileOnShutdown = updateChecker.getDeclaredMethod("deleteFileOnShutdown", File.class, String.class); + deleteFileOnShutdownHandle = MethodHandles.publicLookup().unreflect(deleteFileOnShutdown); + isSCUFullyPresent = true; + } catch (NoSuchFieldException | ClassNotFoundException | IllegalAccessException | NoSuchMethodException e) { + e.printStackTrace(); + } + } + } + + public static UpdateLoader tryCreate(AutoUpdater updater, URL url) { + if (!ReflectionHolder.isSCUFullyPresent) { + updater.logProgress("§cFound Skyclient Updater Mod, however our hooks did not function properly."); + return null; + } + return new SCUCompatUpdater(updater, url); + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/UpdateLoader.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/UpdateLoader.java new file mode 100644 index 00000000..1a1a8504 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/UpdateLoader.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2022 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.miscfeatures.updater; + +import io.github.moulberry.notenoughupdates.util.NetUtils; +import net.minecraft.client.Minecraft; +import org.apache.commons.io.IOUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; + +abstract class UpdateLoader { + + enum State { + NOTHING, DOWNLOAD_STARTED, DOWNLOAD_FINISHED, INSTALLED, FAILED + } + + URL url; + AutoUpdater updater; + + State state = State.NOTHING; + + UpdateLoader(AutoUpdater updater, URL url) { + this.url = url; + this.updater = updater; + } + + public State getState() { + return state; + } + + public void setState(State state) { + this.state = state; + } + + public URL getUrl() { + return url; + } + + public void scheduleDownload() { + state = State.DOWNLOAD_STARTED; + try { + NetUtils.downloadAsync(url, File.createTempFile("NotEnoughUpdates-update", ".jar")) + .handle( + (f, exc) -> { + if (exc != null) { + state = State.FAILED; + updater.logProgress("§cError while downloading. Check your logs for more info."); + exc.printStackTrace(); + return null; + } + state = State.DOWNLOAD_FINISHED; + + updater.logProgress("Download completed. Trying to install"); + launchUpdate(f); + return null; + }); + } catch (IOException e) { + state = State.FAILED; + updater.logProgress("§cError while creating download. Check your logs for more info."); + e.printStackTrace(); + } + } + + public abstract void greet(); + + public void launchUpdate(File file) { + + if (state != State.DOWNLOAD_FINISHED) { + updater.logProgress("§cUpdate is invalid state " + state + " to start update."); + state = State.FAILED; + return; + } + File mcDataDir = new File(Minecraft.getMinecraft().mcDataDir, "mods"); + if (!mcDataDir.exists() || !mcDataDir.isDirectory() || !mcDataDir.canRead()) { + updater.logProgress("§cCould not find mods folder. Searched: " + mcDataDir); + state = State.FAILED; + return; + } + ArrayList<File> toDelete = new ArrayList<>(); + File[] modFiles = mcDataDir.listFiles(); + if (modFiles == null) { + updater.logProgress("§cCould not list minecraft mod folder (" + mcDataDir + ")"); + state = State.FAILED; + return; + } + for (File sus : modFiles) { + if (sus.getName().endsWith(".jar")) { + if (updater.isNeuJar(sus)) { + updater.logProgress("Found old NEU file: " + sus + ". Deleting later."); + toDelete.add(sus); + } + } + } + File dest = new File(mcDataDir, file.getName()); + try ( + InputStream i = Files.newInputStream(file.toPath()); + OutputStream o = Files.newOutputStream(dest.toPath()); + ) { + IOUtils.copyLarge(i, o); + } catch (IOException e) { + e.printStackTrace(); + updater.logProgress( + "§cFailed to copy release JAR. Not making any changes to your mod folder. Consult your logs for more info."); + state = State.FAILED; + } + deleteFiles(toDelete); + if (state != State.FAILED) { + state = State.INSTALLED; + updater.logProgress("Update successful. Thank you for your time."); + return; + } + updater.logProgress("§cFailure to delete some files. Please delte the old NEU version manually from your mods folder."); + } + + public abstract void deleteFiles(List<File> toDelete); + +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/NetUtils.java b/src/main/java/io/github/moulberry/notenoughupdates/util/NetUtils.java new file mode 100644 index 00000000..b09dd839 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/NetUtils.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.util; + +import org.apache.commons.io.IOUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.nio.file.Files; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +public class NetUtils { + + public static CompletableFuture<File> downloadAsync(URL httpURL, File outputFile) { + return CompletableFuture.supplyAsync(() -> { + try { + try ( + InputStream from = httpURL.openStream(); + OutputStream to = Files.newOutputStream(outputFile.toPath()); + ) { + IOUtils.copyLarge(from, to); + } + return outputFile; + } catch (IOException e) { + throw new CompletionException(e); + } + }); + } + +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java b/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java index a99e4e7a..9713b69a 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java @@ -877,6 +877,8 @@ public class Utils { String... lore ) { NBTTagCompound tag = itemStack.getTagCompound(); + if (tag == null) + tag = new NBTTagCompound(); NBTTagCompound display = tag.getCompoundTag("display"); NBTTagList Lore = new NBTTagList(); @@ -1940,9 +1942,13 @@ public class Utils { public static void showOutdatedRepoNotification() { NotificationHandler.displayNotification(Lists.newArrayList( EnumChatFormatting.RED + EnumChatFormatting.BOLD.toString() + "Missing repo data", - EnumChatFormatting.RED + "Data used for many NEU features is not up to date, this should normally not be the case.", - EnumChatFormatting.RED + "You can try " + EnumChatFormatting.BOLD + "/neuresetrepo" + EnumChatFormatting.RESET + EnumChatFormatting.RED +" and restart your game" + - " to see if that fixes the issue.", EnumChatFormatting.RED + "If the problem persists please join " + EnumChatFormatting.BOLD + "discord.gg/moulberry" + + EnumChatFormatting.RED + + "Data used for many NEU features is not up to date, this should normally not be the case.", + EnumChatFormatting.RED + "You can try " + EnumChatFormatting.BOLD + "/neuresetrepo" + EnumChatFormatting.RESET + + EnumChatFormatting.RED + " and restart your game" + + " to see if that fixes the issue.", + EnumChatFormatting.RED + "If the problem persists please join " + EnumChatFormatting.BOLD + + "discord.gg/moulberry" + EnumChatFormatting.RESET + EnumChatFormatting.RED + " and message in " + EnumChatFormatting.BOLD + "#neu-support" + EnumChatFormatting.RESET + EnumChatFormatting.RED + " to get support" ), |