diff options
author | Linnea Gräf <roman.graef@gmail.com> | 2023-11-02 15:28:49 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-02 15:28:49 +0100 |
commit | 2a6d3a96d40ca425e661737fb4fc467b2040437b (patch) | |
tree | bb89ff8177ebb3abdc2a047f16b9671bc18cacd8 /src | |
parent | e1c24ecc30132fc98aa9cf33b928232b2efabcd8 (diff) | |
download | NotEnoughUpdates-2a6d3a96d40ca425e661737fb4fc467b2040437b.tar.gz NotEnoughUpdates-2a6d3a96d40ca425e661737fb4fc467b2040437b.tar.bz2 NotEnoughUpdates-2a6d3a96d40ca425e661737fb4fc467b2040437b.zip |
Add custom TODOs (#870)
Co-authored-by: Lulonaut <lulonaut@lulonaut.tech>
Diffstat (limited to 'src')
18 files changed, 1067 insertions, 111 deletions
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java index f878dbf1..f20c5338 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java @@ -56,6 +56,7 @@ import io.github.moulberry.notenoughupdates.recipes.RecipeGenerator; import io.github.moulberry.notenoughupdates.util.Utils; import io.github.moulberry.notenoughupdates.util.brigadier.BrigadierRoot; import io.github.moulberry.notenoughupdates.util.hypixelapi.HypixelItemAPI; +import io.github.moulberry.notenoughupdates.util.kotlin.KotlinTypeAdapterFactory; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.GuiScreen; @@ -150,7 +151,8 @@ public class NotEnoughUpdates { put("MYTHIC", EnumChatFormatting.LIGHT_PURPLE.toString()); }}; public static ProfileViewer profileViewer; - private final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create(); + private final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation() + .registerTypeAdapterFactory(KotlinTypeAdapterFactory.INSTANCE).create(); public NEUManager manager; public NEUOverlay overlay; public NEUConfig config; diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiEnchantColour.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiEnchantColour.java index 41208681..174650d9 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiEnchantColour.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiEnchantColour.java @@ -25,10 +25,12 @@ import com.google.gson.JsonArray; import com.google.gson.JsonParseException; import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; +import info.bliki.api.Template; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; import io.github.moulberry.notenoughupdates.core.util.lerp.LerpingInteger; import io.github.moulberry.notenoughupdates.itemeditor.GuiElementTextField; import io.github.moulberry.notenoughupdates.options.NEUConfig; +import io.github.moulberry.notenoughupdates.util.TemplateUtil; import io.github.moulberry.notenoughupdates.util.Utils; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Gui; @@ -51,6 +53,7 @@ import java.util.ArrayList; import java.util.Base64; import java.util.HashMap; import java.util.List; +import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -527,16 +530,7 @@ public class GuiEnchantColour extends GuiScreen { private boolean validShareContents() { try { String base64 = (String) Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.stringFlavor); - - if (base64.length() <= sharePrefix.length()) return false; - - base64 = base64.trim(); - - try { - return new String(Base64.getDecoder().decode(base64)).startsWith(sharePrefix); - } catch (IllegalArgumentException e) { - return false; - } + return Objects.equals(TemplateUtil.getTemplatePrefix(base64), sharePrefix); } catch (HeadlessException | IOException | UnsupportedFlavorException | IllegalStateException e) { return false; } @@ -647,26 +641,7 @@ public class GuiEnchantColour extends GuiScreen { } catch (HeadlessException | IOException | UnsupportedFlavorException e) { return; } - - if (base64.length() <= sharePrefix.length()) return; - - base64 = base64.trim(); - - String jsonString; - try { - jsonString = new String(Base64.getDecoder().decode(base64)); - if (!jsonString.startsWith(sharePrefix)) return; - jsonString = jsonString.substring(sharePrefix.length()); - } catch (IllegalArgumentException e) { - return; - } - - JsonArray presetArray; - try { - presetArray = new JsonParser().parse(jsonString).getAsJsonArray(); - } catch (IllegalStateException | JsonParseException e) { - return; - } + JsonArray presetArray = TemplateUtil.maybeDecodeTemplate(sharePrefix, base64, JsonArray.class); ArrayList<String> presetList = new ArrayList<>(); for (int i = 0; i < presetArray.size(); i++) { @@ -690,8 +665,8 @@ public class GuiEnchantColour extends GuiScreen { for (String s : result) { jsonArray.add(new JsonPrimitive(s)); } - String base64String = Base64.getEncoder().encodeToString((sharePrefix + - jsonArray).getBytes(StandardCharsets.UTF_8)); + + String base64String = TemplateUtil.encodeTemplate(sharePrefix, jsonArray); Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(base64String), null); } else if (mouseY > guiTopSidebar + 2 + (24 * 2) && mouseY < guiTopSidebar + 20 + 2 + 24 * 2) { NotEnoughUpdates.INSTANCE.config.hidden.enchantColours = NEUConfig.createDefaultEnchantColours(); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java b/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java index 2d64db59..200b1c0c 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java @@ -35,6 +35,7 @@ import io.github.moulberry.notenoughupdates.miscfeatures.IQTest; import io.github.moulberry.notenoughupdates.miscgui.GuiEnchantColour; import io.github.moulberry.notenoughupdates.miscgui.GuiInvButtonEditor; import io.github.moulberry.notenoughupdates.miscgui.NEUOverlayPlacements; +import io.github.moulberry.notenoughupdates.miscgui.customtodos.CustomTodo; import io.github.moulberry.notenoughupdates.options.customtypes.NEUDebugFlag; import io.github.moulberry.notenoughupdates.options.separatesections.AHGraph; import io.github.moulberry.notenoughupdates.options.separatesections.AHTweaks; @@ -493,6 +494,8 @@ public class NEUConfig extends Config { public static class Hidden { @Expose + public List<CustomTodo> customTodos = new ArrayList<>(); + @Expose public HashMap<String, NEUConfig.HiddenProfileSpecific> profileSpecific = new HashMap<>(); @Expose public HashMap<String, NEUConfig.HiddenLocationSpecific> locationSpecific = new HashMap<>(); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/overlays/TextOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/overlays/TextOverlay.java index cefb6929..c4768b3e 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/overlays/TextOverlay.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/overlays/TextOverlay.java @@ -184,6 +184,9 @@ public abstract class TextOverlay { for (String s2 : s.split("\n")) { Vector2f pos = new Vector2f(x + paddingX, y + paddingY + yOff); renderLine(s2, pos, dummy); + if (s2.startsWith("CUSTOM")) { + s2 = s2.split(":", 2)[1]; + } int xPad = (int) pos.x; int yPad = (int) pos.y; diff --git a/src/main/java/io/github/moulberry/notenoughupdates/overlays/TimersOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/overlays/TimersOverlay.java index bf73e2ec..ca244303 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/overlays/TimersOverlay.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/overlays/TimersOverlay.java @@ -22,6 +22,7 @@ package io.github.moulberry.notenoughupdates.overlays; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; import io.github.moulberry.notenoughupdates.core.config.Position; import io.github.moulberry.notenoughupdates.events.SlotClickEvent; +import io.github.moulberry.notenoughupdates.miscgui.customtodos.CustomTodoHud; import io.github.moulberry.notenoughupdates.options.NEUConfig; import io.github.moulberry.notenoughupdates.util.ItemUtils; import io.github.moulberry.notenoughupdates.util.SBInfo; @@ -168,91 +169,97 @@ public class TimersOverlay extends TextTabOverlay { return; } GlStateManager.enableDepth(); - ItemStack icon = null; String clean = Utils.cleanColour(line); String beforeColon = clean.split(":")[0]; - switch (beforeColon) { - case "Cakes": - icon = CAKES_ICON; - break; - case "Puzzler": - icon = PUZZLER_ICON; - break; - case "Godpot": - icon = NotEnoughUpdates.INSTANCE.manager.jsonToStack(NotEnoughUpdates.INSTANCE.manager - .getItemInformation() - .get("GOD_POTION")); - break; - case "Fetchur": { - if (FETCHUR_ICONS == null) { - FETCHUR_ICONS = new ItemStack[]{ - new ItemStack(Blocks.wool, 50, 14), - new ItemStack(Blocks.stained_glass, 20, 4), - new ItemStack(Items.compass, 1, 0), - new ItemStack(Items.prismarine_crystals, 20, 0), - new ItemStack(Items.fireworks, 1, 0), - NotEnoughUpdates.INSTANCE.manager.jsonToStack(NotEnoughUpdates.INSTANCE.manager - .getItemInformation() - .get("CHEAP_COFFEE")), - new ItemStack(Items.oak_door, 1, 0), - new ItemStack(Items.rabbit_foot, 3, 0), - NotEnoughUpdates.INSTANCE.manager.jsonToStack(NotEnoughUpdates.INSTANCE.manager - .getItemInformation() - .get("SUPERBOOM_TNT")), - new ItemStack(Blocks.pumpkin, 1, 0), - new ItemStack(Items.flint_and_steel, 1, 0), - new ItemStack(Items.emerald, 50, 0), - //new ItemStack(Items.ender_pearl, 16, 0) - }; - } + if (beforeColon.startsWith("CUSTOM")) { + var item = Item.getByNameOrId(beforeColon.substring(6)); + if (item == null) { + item = Items.paper; + } + icon = new ItemStack(item); + } else + switch (beforeColon) { + case "Cakes": + icon = CAKES_ICON; + break; + case "Puzzler": + icon = PUZZLER_ICON; + break; + case "Godpot": + icon = NotEnoughUpdates.INSTANCE.manager.jsonToStack(NotEnoughUpdates.INSTANCE.manager + .getItemInformation() + .get("GOD_POTION")); + break; + case "Fetchur": { + if (FETCHUR_ICONS == null) { + FETCHUR_ICONS = new ItemStack[]{ + new ItemStack(Blocks.wool, 50, 14), + new ItemStack(Blocks.stained_glass, 20, 4), + new ItemStack(Items.compass, 1, 0), + new ItemStack(Items.prismarine_crystals, 20, 0), + new ItemStack(Items.fireworks, 1, 0), + NotEnoughUpdates.INSTANCE.manager.jsonToStack(NotEnoughUpdates.INSTANCE.manager + .getItemInformation() + .get("CHEAP_COFFEE")), + new ItemStack(Items.oak_door, 1, 0), + new ItemStack(Items.rabbit_foot, 3, 0), + NotEnoughUpdates.INSTANCE.manager.jsonToStack(NotEnoughUpdates.INSTANCE.manager + .getItemInformation() + .get("SUPERBOOM_TNT")), + new ItemStack(Blocks.pumpkin, 1, 0), + new ItemStack(Items.flint_and_steel, 1, 0), + new ItemStack(Blocks.emerald_ore, 50, 0), + //new ItemStack(Items.ender_pearl, 16, 0) + }; + } - ZonedDateTime currentTimeEST = ZonedDateTime.now(ZoneId.of("America/Atikokan")); + ZonedDateTime currentTimeEST = ZonedDateTime.now(ZoneId.of("America/Atikokan")); - long fetchurIndex = ((currentTimeEST.getDayOfMonth() + 1) % 12) - 1; - //Added because disabled fetchur and enabled it again but it was showing the wrong item - //Lets see if this stays correct + long fetchurIndex = ((currentTimeEST.getDayOfMonth() + 1) % 12) - 1; + //Added because disabled fetchur and enabled it again but it was showing the wrong item + //Lets see if this stays correct - if (fetchurIndex < 0) fetchurIndex += 12; + if (fetchurIndex < 0) fetchurIndex += 12; - icon = FETCHUR_ICONS[(int) fetchurIndex]; - break; + icon = FETCHUR_ICONS[(int) fetchurIndex]; + break; + } + case "Commissions": + icon = COMMISSIONS_ICON; + break; + case "Experiments": + icon = EXPERIMENTS_ICON; + break; + case "Cookie Buff": + icon = COOKIE_ICON; + break; + case "Mithril Powder": + icon = NotEnoughUpdates.INSTANCE.manager.jsonToStack(NotEnoughUpdates.INSTANCE.manager + .getItemInformation() + .get("MITHRIL_ORE")); + break; + case "Gemstone Powder": + icon = NotEnoughUpdates.INSTANCE.manager.jsonToStack(NotEnoughUpdates.INSTANCE.manager + .getItemInformation() + .get("PERFECT_AMETHYST_GEM")); + break; + case "Heavy Pearls": + icon = NotEnoughUpdates.INSTANCE.manager.jsonToStack(NotEnoughUpdates.INSTANCE.manager + .getItemInformation() + .get("HEAVY_PEARL")); + break; + case "Free Rift Infusion": + icon = new ItemStack(Blocks.double_plant, 1, 1); + break; + case "Crimson Isle Quests": + icon = QUEST_ICON; + break; + case "NPC Buy Daily Limit": + icon = SHOP_ICON; + break; } - case "Commissions": - icon = COMMISSIONS_ICON; - break; - case "Experiments": - icon = EXPERIMENTS_ICON; - break; - case "Cookie Buff": - icon = COOKIE_ICON; - break; - case "Mithril Powder": - icon = NotEnoughUpdates.INSTANCE.manager.jsonToStack(NotEnoughUpdates.INSTANCE.manager - .getItemInformation() - .get("MITHRIL_ORE")); - break; - case "Gemstone Powder": - icon = NotEnoughUpdates.INSTANCE.manager.jsonToStack(NotEnoughUpdates.INSTANCE.manager - .getItemInformation() - .get("PERFECT_AMETHYST_GEM")); - break; - case "Heavy Pearls": - icon = NotEnoughUpdates.INSTANCE.manager.jsonToStack(NotEnoughUpdates.INSTANCE.manager - .getItemInformation() - .get("HEAVY_PEARL")); - break; - case "Free Rift Infusion": - icon = new ItemStack(Blocks.double_plant, 1, 1); - break; - case "Crimson Isle Quests": - icon = QUEST_ICON; - break; - case "NPC Buy Daily Limit": - icon = SHOP_ICON; - break; - } if (icon != null) { GlStateManager.pushMatrix(); @@ -1047,6 +1054,9 @@ public class TimersOverlay extends TextTabOverlay { overlayStrings.add(text); } } + + CustomTodoHud.processInto(overlayStrings); + if (overlayStrings.isEmpty()) overlayStrings = null; } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java b/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java index 82708669..ad0000d4 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java @@ -23,6 +23,7 @@ import com.google.common.reflect.TypeToken; import com.google.gson.JsonObject; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe; +import io.github.moulberry.notenoughupdates.events.SidebarChangeEvent; import io.github.moulberry.notenoughupdates.miscfeatures.CookieWarning; import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.LocationChangeEvent; import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager; @@ -310,6 +311,7 @@ public class SBInfo { public ArrayList<String> completedQuests = new ArrayList<>(); private static final Pattern SKILL_LEVEL_PATTERN = Pattern.compile("([^0-9:]+) (\\d{1,2})"); + private static List<String> lastLines = new ArrayList<>(); public void tick() { @@ -364,6 +366,11 @@ public class SBInfo { try { List<String> lines = SidebarUtil.readSidebarLines(true, false); + + if (lines.equals(lastLines)) return; + new SidebarChangeEvent(lines, lastLines).post(); + lastLines = lines; + boolean tempIsInDungeon = false; for (String line : lines) { if (line.contains("Cleared:") && line.contains("%")) { diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/TabListUtils.java b/src/main/java/io/github/moulberry/notenoughupdates/util/TabListUtils.java index a0266122..f36db034 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/util/TabListUtils.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/TabListUtils.java @@ -21,10 +21,14 @@ package io.github.moulberry.notenoughupdates.util; import com.google.common.collect.ComparisonChain; import com.google.common.collect.Ordering; +import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe; +import io.github.moulberry.notenoughupdates.events.TabListChangeEvent; import net.minecraft.client.Minecraft; import net.minecraft.client.network.NetworkPlayerInfo; import net.minecraft.scoreboard.ScorePlayerTeam; import net.minecraft.world.WorldSettings; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; @@ -32,6 +36,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; +@NEUAutoSubscribe public class TabListUtils { private static final Ordering<NetworkPlayerInfo> playerOrdering = Ordering.from(new PlayerComparator()); @@ -55,7 +60,23 @@ public class TabListUtils { } } + public static List<String> tabList = new ArrayList<>(); + public static List<String> tabListLastTick = new ArrayList<>(); + + @SubscribeEvent + public void onTick(TickEvent.ClientTickEvent event) { + if (Minecraft.getMinecraft().thePlayer == null) return; + if (event.phase != TickEvent.Phase.END) return; + tabListLastTick = tabList; + tabList = getTabList0(); + new TabListChangeEvent(tabList, tabListLastTick).post(); + } + public static List<String> getTabList() { + return tabList; + } + + private List<String> getTabList0() { List<NetworkPlayerInfo> players = playerOrdering.sortedCopy(Minecraft.getMinecraft().thePlayer.sendQueue.getPlayerInfoMap()); diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/help/SettingsCommand.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/help/SettingsCommand.kt index eb5d51b1..40919a8e 100644 --- a/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/help/SettingsCommand.kt +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/help/SettingsCommand.kt @@ -21,6 +21,7 @@ package io.github.moulberry.notenoughupdates.commands.help import io.github.moulberry.moulconfig.GuiTextures import io.github.moulberry.moulconfig.annotations.ConfigOption +import io.github.moulberry.moulconfig.common.MyResourceLocation import io.github.moulberry.moulconfig.gui.GuiOptionEditor import io.github.moulberry.moulconfig.gui.GuiScreenElementWrapper import io.github.moulberry.moulconfig.gui.MoulConfigEditor @@ -34,7 +35,6 @@ import io.github.moulberry.notenoughupdates.miscfeatures.IQTest import io.github.moulberry.notenoughupdates.options.NEUConfig import io.github.moulberry.notenoughupdates.util.brigadier.* import net.minecraft.client.gui.GuiScreen -import net.minecraft.util.ResourceLocation import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import java.lang.reflect.Field @@ -118,6 +118,7 @@ object SettingsCommand { return object : GuiScreenElementWrapper(createConfigElement(search)) { } } + fun createConfigElement(search: String): MoulConfigEditor<NEUConfig> { val processor = BlockingMoulConfigProcessor() BuiltinMoulConfigGuis.addProcessors(processor) @@ -131,7 +132,8 @@ object SettingsCommand { lastEditor = editor return editor } + init { - GuiTextures.setTextureRoot(ResourceLocation("notenoughupdates:core")) + GuiTextures.setTextureRoot(MyResourceLocation("notenoughupdates", "core")) } } diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/events/SidebarChangeEvent.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/events/SidebarChangeEvent.kt new file mode 100644 index 00000000..0dc4b9f6 --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/events/SidebarChangeEvent.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2023 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.events + +class SidebarChangeEvent( + val lines: List<String>, + val lastLines: List<String>, +) : NEUEvent() diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/events/TabListChangeEvent.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/events/TabListChangeEvent.kt new file mode 100644 index 00000000..b5677598 --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/events/TabListChangeEvent.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2023 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.events + +class TabListChangeEvent( + val lastLines: List<String>, + val newLines: List<String>, +) : NEUEvent() { +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/miscgui/customtodos/CustomTodo.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/miscgui/customtodos/CustomTodo.kt new file mode 100644 index 00000000..7f8c6d1a --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/miscgui/customtodos/CustomTodo.kt @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2023 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.miscgui.customtodos + +import com.google.gson.annotations.Expose +import io.github.moulberry.notenoughupdates.util.SBInfo +import io.github.moulberry.notenoughupdates.util.TemplateUtil +import io.github.moulberry.notenoughupdates.util.kotlin.KSerializable + +@KSerializable +data class CustomTodo( + @Expose var label: String, + @Expose var timer: Int, + @Expose var trigger: String, + @Expose var icon: String, + @Expose var isResetOffset: Boolean, + @Expose var triggerTarget: TriggerTarget = TriggerTarget.CHAT, + @Expose var triggerMatcher: TriggerMatcher = TriggerMatcher.CONTAINS, + @Expose var readyAt: MutableMap<String, Long> = mutableMapOf(), + @Expose var enabled: MutableMap<String, Boolean> = mutableMapOf(), +) { + enum class TriggerMatcher { + REGEX, STARTS_WITH, CONTAINS, EQUALS + } + + enum class TriggerTarget { + CHAT, ACTIONBAR, TAB_LIST, SIDEBAR + } + + fun isValid(): Boolean { + return timer >= 0 && !trigger.isBlank() + } + + fun setDoneNow() { + val t = System.currentTimeMillis() + readyAt[SBInfo.getInstance().currentProfile ?: return] = + if (isResetOffset) { + t + DAY - t % DAY + timer * 1000L + } else { + t + timer * 1000L + } + } + + var readyAtOnCurrentProfile: Long? + get() { + return readyAt[SBInfo.getInstance().currentProfile ?: return null] + } + set(value) { + readyAt[SBInfo.getInstance().currentProfile ?: return] = value ?: return + } + + var isEnabledOnCurrentProfile: Boolean + get() { + return enabled[SBInfo.getInstance().currentProfile ?: return true] ?: true + } + set(value) { + enabled[SBInfo.getInstance().currentProfile ?: return] = value + } + + + companion object { + val templatePrefix = "NEU:CUSTOMTODO/" + val DAY = (24 * 60 * 60 * 100) + fun fromTemplate(data: String): CustomTodo? { + return TemplateUtil.maybeDecodeTemplate(templatePrefix, data, CustomTodo::class.java) + ?.also { + it.enabled.clear() + it.readyAt.clear() + } + } + } + + fun toTemplate(): String { + return TemplateUtil.encodeTemplate( + templatePrefix, + this.copy(enabled = mutableMapOf(), readyAt = mutableMapOf()) + ) + } +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/miscgui/customtodos/CustomTodoEditor.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/miscgui/customtodos/CustomTodoEditor.kt new file mode 100644 index 00000000..1c5c16ed --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/miscgui/customtodos/CustomTodoEditor.kt @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2023 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.miscgui.customtodos + +import io.github.moulberry.moulconfig.common.IItemStack +import io.github.moulberry.moulconfig.forge.ForgeItemStack +import io.github.moulberry.moulconfig.internal.ClipboardUtils +import io.github.moulberry.moulconfig.observer.ObservableList +import io.github.moulberry.moulconfig.xml.Bind +import io.github.moulberry.moulconfig.xml.XMLUniverse +import io.github.moulberry.notenoughupdates.util.SBInfo +import io.github.moulberry.notenoughupdates.util.Utils +import io.github.moulberry.notenoughupdates.util.loadResourceLocation +import net.minecraft.client.Minecraft +import net.minecraft.init.Items +import net.minecraft.item.Item +import net.minecraft.item.ItemStack +import net.minecraft.util.ResourceLocation + +class CustomTodoEditor( + val from: CustomTodo, + val todos: ObservableList<CustomTodoEditor>, + val xmlUniverse: XMLUniverse +) { + @field:Bind + var label: String = from.label + + @field:Bind + var enabled: Boolean = from.isEnabledOnCurrentProfile + + @field:Bind + var timer: String = from.timer.toString() + + @field:Bind + var trigger: String = from.trigger + + @field:Bind + var icon: String = from.icon + + @field:Bind + var isResetOffset: Boolean = from.isResetOffset + + var target = from.triggerTarget + var matchMode = from.triggerMatcher + var lastCustomTodo: CustomTodo? = null + + fun into(): CustomTodo { + val nextCustomTodo = CustomTodo( + label, + timer.toIntOrNull() ?: 0, + trigger, + icon, + isResetOffset, + target, matchMode, + from.readyAt, + from.enabled.toMutableMap().also { it[SBInfo.getInstance().currentProfile ?: return@also] = enabled } + ) + if (nextCustomTodo != lastCustomTodo) { + lastCustomTodo = nextCustomTodo + CustomTodoList(todos, xmlUniverse).save() + } + return nextCustomTodo + } + + @Bind + fun setChat() { + target = CustomTodo.TriggerTarget.CHAT + } + + @Bind + fun setActionbar() { + target = CustomTodo.TriggerTarget.ACTIONBAR + } + + @Bind + fun setSidebar() { + target = CustomTodo.TriggerTarget.SIDEBAR + } + + @Bind + fun setTablist() { + target = CustomTodo.TriggerTarget.TAB_LIST + } + + private fun colorFromBool(b: Boolean): String { + return if (b) "§a" else "§c" + } + + @Bind + fun getChat(): String { + return colorFromBool(target == CustomTodo.TriggerTarget.CHAT) + "Chat" + } + + @Bind + fun getActionbar(): String { + return colorFromBool(target == CustomTodo.TriggerTarget.ACTIONBAR) + "Actionbar" + } + + @Bind + fun getSidebar(): String { + return colorFromBool(target == CustomTodo.TriggerTarget.SIDEBAR) + "Sidebar" + } + + @Bind + fun getTablist(): String { + return colorFromBool(target == CustomTodo.TriggerTarget.TAB_LIST) + "Tablist" + } + + @Bind + fun setRegex() { + matchMode = CustomTodo.TriggerMatcher.REGEX + } + + @Bind + fun setStartsWith() { + matchMode = CustomTodo.TriggerMatcher.STARTS_WITH + } + + @Bind + fun setContains() { + matchMode = CustomTodo.TriggerMatcher.CONTAINS + } + + @Bind + fun setEquals() { + matchMode = CustomTodo.TriggerMatcher.EQUALS + } + + @Bind + fun getRegex(): String { + return colorFromBool(matchMode == CustomTodo.TriggerMatcher.REGEX) + "Regex" + } + + @Bind + fun getStartsWith(): String { + return colorFromBool(matchMode == CustomTodo.TriggerMatcher.STARTS_WITH) + "Starts With" + } + + @Bind + fun getContains(): String { + return colorFromBool(matchMode == CustomTodo.TriggerMatcher.CONTAINS) + "Contains" + } + + @Bind + fun getEquals(): String { + return colorFromBool(matchMode == CustomTodo.TriggerMatcher.EQUALS) + "Equals" + } + + @Bind + fun getItemStack(): IItemStack { + val item = Item.getByNameOrId(icon) ?: (Items.paper) + return ForgeItemStack.of(ItemStack(item)) + } + + @Bind + fun copyTemplate() { + ClipboardUtils.copyToClipboard(into().toTemplate()) + } + + @Bind + fun markAsReady() { + from.readyAtOnCurrentProfile = System.currentTimeMillis() + } + + @Bind + fun markAsCompleted() { + from.setDoneNow() + } + + @Bind + fun getFancyTime(): String { + val tint = timer.toIntOrNull() ?: return "§3Invalid Time" + val timeFormat = Utils.prettyTime(tint * 1000L) + if (isResetOffset) { + return "Reset $timeFormat after 00:00 GMT" + } + return "Reset $timeFormat after completion" + } + + fun changeTimer(value: Int) { + timer = ((timer.toIntOrNull() ?: 0) + value).coerceAtLeast(0).toString() + } + + @Bind + fun plusDay() { + changeTimer(60 * 60 * 24) + } + + @Bind + fun minusDay() { + changeTimer(-60 * 60 * 24) + } + + @Bind + fun minusHour() { + changeTimer(-60 * 60) + } + + @Bind + fun plusHour() { + changeTimer(60 * 60) + } + + @Bind + fun plusMinute() { + changeTimer(60) + } + + @Bind + fun minusMinute() { + changeTimer(-60) + } + + @Bind + fun delete() { + todos.remove(this) + CustomTodoList(todos, xmlUniverse).save() + } + + @Bind + fun getTitle(): String { + return "Editing ${into().label}" + } + + @Bind + fun close() { + Minecraft.getMinecraft().displayGuiScreen( + CustomTodoList( + todos, xmlUniverse + ).open() + ) + } + + @Bind + fun edit() { + Minecraft.getMinecraft().displayGuiScreen( + xmlUniverse.loadResourceLocation(this, ResourceLocation("notenoughupdates:gui/customtodos/edit.xml")) + ) + } +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/miscgui/customtodos/CustomTodoHud.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/miscgui/customtodos/CustomTodoHud.kt new file mode 100644 index 00000000..591bcedb --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/miscgui/customtodos/CustomTodoHud.kt @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2023 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.miscgui.customtodos + +import io.github.moulberry.notenoughupdates.NotEnoughUpdates +import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe +import io.github.moulberry.notenoughupdates.core.util.StringUtils +import io.github.moulberry.notenoughupdates.events.SidebarChangeEvent +import io.github.moulberry.notenoughupdates.events.TabListChangeEvent +import io.github.moulberry.notenoughupdates.util.Utils +import net.minecraft.util.EnumChatFormatting +import net.minecraftforge.client.event.ClientChatReceivedEvent +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +@NEUAutoSubscribe +object CustomTodoHud { + + private fun matchString(todo: CustomTodo, text: String): Boolean { + return when (todo.triggerMatcher) { + CustomTodo.TriggerMatcher.REGEX -> text.matches(todo.trigger.toRegex()) + CustomTodo.TriggerMatcher.STARTS_WITH -> text.startsWith(todo.trigger) + CustomTodo.TriggerMatcher.CONTAINS -> text.contains(todo.trigger) + CustomTodo.TriggerMatcher.EQUALS -> text == todo.trigger + } + } + + @SubscribeEvent + fun onTabList(event: TabListChangeEvent) { + NotEnoughUpdates.INSTANCE.config.hidden.customTodos + .forEach { todo -> + if (todo.triggerTarget != CustomTodo.TriggerTarget.TAB_LIST) return@forEach + event.newLines.forEach { text -> + val doesMatch = matchString(todo, text) + if (doesMatch) { + todo.setDoneNow() + } + } + } + } + + @SubscribeEvent + fun onSidebar(event: SidebarChangeEvent) { + NotEnoughUpdates.INSTANCE.config.hidden.customTodos + .forEach { todo -> + if (todo.triggerTarget != CustomTodo.TriggerTarget.SIDEBAR) return@forEach + event.lines.forEach { text -> + val doesMatch = matchString(todo, text) + if (doesMatch) { + todo.setDoneNow() + } + } + } + } + + @SubscribeEvent + fun onChat(event: ClientChatReceivedEvent) { + val text = StringUtils.cleanColour(event.message.unformattedText) + NotEnoughUpdates.INSTANCE.config.hidden.customTodos + .forEach { + val isCorrectTrigger = when (it.triggerTarget) { + CustomTodo.TriggerTarget.CHAT -> event.type != 2.toByte() + CustomTodo.TriggerTarget.ACTIONBAR -> event.type == 2.toByte() + CustomTodo.TriggerTarget.TAB_LIST -> false + CustomTodo.TriggerTarget.SIDEBAR -> false + } + val doesMatch = matchString(it, text) + if (isCorrectTrigger && doesMatch) + it.setDoneNow() + } + } + + @JvmStatic + fun processInto(strings: MutableList<String>) { + NotEnoughUpdates.INSTANCE.config.hidden.customTodos + .filter { it.isEnabledOnCurrentProfile } + .forEach { + val readyAt = it.readyAtOnCurrentProfile ?: (System.currentTimeMillis() - 1000L) + val until = readyAt - System.currentTimeMillis() + strings.add( + "CUSTOM" + it.icon + ":§3" + it.label + ": " + + if (until <= 0) + EnumChatFormatting.values()[NotEnoughUpdates.INSTANCE.config.miscOverlays.readyColour].toString() + "Ready" + else if (until < 60 * 30 * 1000L) + EnumChatFormatting.values()[NotEnoughUpdates.INSTANCE.config.miscOverlays.verySoonColour].toString() + + Utils.prettyTime(until) + else if (until < 60 * 60 * 1000L) + EnumChatFormatting.values()[NotEnoughUpdates.INSTANCE.config.miscOverlays.soonColour].toString() + + Utils.prettyTime(until) + else if (until < 3 * 60 * 60 * 1000L) + EnumChatFormatting.values()[NotEnoughUpdates.INSTANCE.config.miscOverlays.kindaSoonColour].toString() + + Utils.prettyTime(until) + else + EnumChatFormatting.values()[NotEnoughUpdates.INSTANCE.config.miscOverlays.defaultColour].toString() + + Utils.prettyTime(until) + ) + } + } + +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/miscgui/customtodos/CustomTodoList.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/miscgui/customtodos/CustomTodoList.kt new file mode 100644 index 00000000..1b278990 --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/miscgui/customtodos/CustomTodoList.kt @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2023 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.miscgui.customtodos + +import io.github.moulberry.moulconfig.internal.ClipboardUtils +import io.github.moulberry.moulconfig.observer.ObservableList +import io.github.moulberry.moulconfig.xml.Bind +import io.github.moulberry.moulconfig.xml.XMLUniverse +import io.github.moulberry.notenoughupdates.NotEnoughUpdates +import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe +import io.github.moulberry.notenoughupdates.events.RegisterBrigadierCommandEvent +import io.github.moulberry.notenoughupdates.util.brigadier.thenExecute +import io.github.moulberry.notenoughupdates.util.brigadier.withHelp +import io.github.moulberry.notenoughupdates.util.loadResourceLocation +import net.minecraft.client.gui.GuiScreen +import net.minecraft.util.ResourceLocation +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class CustomTodoList( + @field:Bind + val todos: ObservableList<CustomTodoEditor>, + val xmlUniverse: XMLUniverse, +) { + @NEUAutoSubscribe + companion object { + @SubscribeEvent + fun onCommand(event: RegisterBrigadierCommandEvent) { + event.command("neutodos", "neucustomtodos") { + thenExecute { + NotEnoughUpdates.INSTANCE.openGui = create().open() + } + }.withHelp("Edit NEUs custom TODOs") + } + + fun create(): CustomTodoList { + val universe = XMLUniverse.getDefaultUniverse() + val list = ObservableList<CustomTodoEditor>(mutableListOf()) + NotEnoughUpdates.INSTANCE.config.hidden.customTodos.forEach { + list.add(CustomTodoEditor(it, list, universe)) + } + return CustomTodoList( + list, + universe + ) + } + } + + fun open(): GuiScreen { + return xmlUniverse.loadResourceLocation(this, ResourceLocation("notenoughupdates:gui/customtodos/overview.xml")) + } + + @Bind + fun pasteTodo() { + val customTodo = CustomTodo.fromTemplate(ClipboardUtils.getClipboardContent()) + ?: return + todos.add(CustomTodoEditor(customTodo, todos, xmlUniverse)) + save() + } + + fun save() { + NotEnoughUpdates.INSTANCE.config.hidden.customTodos = todos.map { it.into() }.toMutableList() + NotEnoughUpdates.INSTANCE.saveConfig() + } + + @Bind + fun addTodo() { + todos.add( + CustomTodoEditor( + CustomTodo( + "Custom Todo # ${todos.size + 1}", + 0, + "", + "", + false, + ), + todos, + xmlUniverse, + ) + ) + save() + } +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/MoulConfig.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/MoulConfig.kt new file mode 100644 index 00000000..9c626627 --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/MoulConfig.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 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 io.github.moulberry.moulconfig.gui.GuiContext +import io.github.moulberry.moulconfig.gui.GuiScreenElementWrapperNew +import io.github.moulberry.moulconfig.xml.XMLUniverse +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.GuiScreen +import net.minecraft.util.ResourceLocation + + +fun XMLUniverse.loadResourceLocation(obj: Any, resourceLocation: ResourceLocation): GuiScreen { + return GuiScreenElementWrapperNew( + GuiContext( + load( + obj, + Minecraft.getMinecraft().resourceManager.getResource(resourceLocation).inputStream + ) + ) + ) +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/util/TemplateUtil.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/TemplateUtil.kt new file mode 100644 index 00000000..2cd90acc --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/util/TemplateUtil.kt @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2023 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 com.google.gson.GsonBuilder +import io.github.moulberry.notenoughupdates.util.kotlin.KotlinTypeAdapterFactory +import java.util.* + +object TemplateUtil { + val gson = GsonBuilder() + .registerTypeAdapterFactory(KotlinTypeAdapterFactory) + .create() + + @JvmStatic + fun getTemplatePrefix(data: String): String? { + val decoded = maybeFromBase64Encoded(data) ?: return null + return decoded.replaceAfter("/", "", "").ifBlank { null } + } + + @JvmStatic + fun intoBase64Encoded(raw: String): String { + return Base64.getEncoder().encodeToString(raw.encodeToByteArray()) + } + + private val base64Alphabet = charArrayOf( + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '=' + ) + + @JvmStatic + fun maybeFromBase64Encoded(raw: String): String? { + val raw = raw.trim() + if (raw.any { it !in base64Alphabet }) { + return null + } + return try { + Base64.getDecoder().decode(raw).decodeToString() + } catch (ex: Exception) { + null + } + } + + + /** + * Returns a base64 encoded string, truncated such that for all `x`, `x.startsWith(prefix)` implies + * `base64Encoded(x).startsWith(getPrefixComparisonSafeBase64Encoding(prefix))` + * (however, the inverse may not always be true). + */ + @JvmStatic + fun getPrefixComparisonSafeBase64Encoding(prefix: String): String { + val rawEncoded = + Base64.getEncoder().encodeToString(prefix.encodeToByteArray()) + .replace("=", "") + return rawEncoded.substring(0, rawEncoded.length - rawEncoded.length % 4) + } + + @JvmStatic + fun encodeTemplate(sharePrefix: String, data: Any): String { + require(sharePrefix.endsWith("/")) + return intoBase64Encoded(sharePrefix + gson.toJson(data)) + } + + @JvmStatic + fun <T : Any> maybeDecodeTemplate(sharePrefix: String, data: String, type: Class<T>): T? { + require(sharePrefix.endsWith("/")) + val data = data.trim() + if (!data.startsWith(getPrefixComparisonSafeBase64Encoding(sharePrefix))) + return null + val decoded = maybeFromBase64Encoded(data) ?: return null + if (!decoded.startsWith(sharePrefix)) + return null + return try { + gson.fromJson(decoded.substring(sharePrefix.length), type) + } catch (e: Exception) { + null + } + } + +} diff --git a/src/main/resources/assets/notenoughupdates/gui/customtodos/edit.xml b/src/main/resources/assets/notenoughupdates/gui/customtodos/edit.xml new file mode 100644 index 00000000..9ccf71c2 --- /dev/null +++ b/src/main/resources/assets/notenoughupdates/gui/customtodos/edit.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- + ~ Copyright (C) 2023 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/>. + --> + +<Root xmlns="http://notenoughupdates.org/moulconfig"> + <Gui> + <Column> + + <Row> + <Button onClick="@close"> + <Text text="←"/> + </Button> + <Text text="@getTitle"/> + </Row> + <Row> + <Text text="Enabled: " width="150"/> + <Switch value="@enabled"/> + </Row> + <Row> + <Text text="Label: " width="150"/> + <TextField width="300" value="@label" suggestion="Label"/> + </Row> + <Row> + <Text text="Icon: " width="132"/> + <ItemStack value="@getItemStack"/> + <TextField width="300" value="@icon" suggestion="Item ID"/> + </Row> + <Row> + <Text text="Reset after (seconds): " width="150"/> + <TextField width="300" value="@timer"/> + </Row> + <Row> + <Text text="Rest at fixed time: " width="150"/> + <Switch value="@isResetOffset"/> + </Row> + <Row> + <Text text="@getFancyTime" width="300"/> + </Row> + <Row> + <Button onClick="@minusDay"> + <Text text="-Day"/> + </Button> + <Button onClick="@minusHour"> + <Text text="-Hour"/> + </Button> + <Button onClick="@minusMinute"> + <Text text="-Minute"/> + </Button> + <Button onClick="@plusMinute"> + <Text text="+Minute"/> + </Button> + <Button onClick="@plusHour"> + <Text text="+Hour"/> + </Button> + <Button onClick="@plusDay"> + <Text text="+Day"/> + </Button> + </Row> + <Row> + <Text text="Trigger: " width="150"/> + <TextField value="@trigger" width="300" suggestion="Trigger"/> + </Row> + <Row> + <Button onClick="@setChat"> + <Text text="@getChat"/> + </Button> + <Button onClick="@setActionbar"> + <Text text="@getActionbar"/> + </Button> + + <Button onClick="@setSidebar"> + <Text text="@getSidebar"/> + </Button> + + <Button onClick="@setTablist"> + <Text text="@getTablist"/> + </Button> + </Row> + <Row> + <Button onClick="@setRegex"> + <Text text="@getRegex"/> + </Button> + <Button onClick="@setStartsWith"> + <Text text="@getStartsWith"/> + </Button> + <Button onClick="@setContains"> + <Text text="@getContains"/> + </Button> + <Button onClick="@setEquals"> + <Text text="@getEquals"/> + </Button> + </Row> + <Row> + <Button onClick="@copyTemplate"> + <Text text="Copy Preset"/> + </Button> + <Button onClick="@markAsReady"> + <Text text="Mark as Ready"/> + </Button> + <Button onClick="@markAsCompleted"> + <Text text="Mark as Done"/> + </Button> + </Row> + </Column> + </Gui> +</Root> diff --git a/src/main/resources/assets/notenoughupdates/gui/customtodos/overview.xml b/src/main/resources/assets/notenoughupdates/gui/customtodos/overview.xml new file mode 100644 index 00000000..b1cb1521 --- /dev/null +++ b/src/main/resources/assets/notenoughupdates/gui/customtodos/overview.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- + ~ Copyright (C) 2023 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/>. + --> + +<Root xmlns="http://notenoughupdates.org/moulconfig"> + <Gui> + <Column> + <Center> + <Scale scale="2"> + <Text text="Custom TODOs"/> + </Scale> + </Center> + <Spacer height="5"/> + <ScrollPanel width="350" height="200"> + <Array data="@todos"> + <Row> + <Text text="@label" width="200"/> + <Switch value="@enabled"/> + <Button onClick="@edit"> + <Text text="Edit"/> + </Button> + <Button onClick="@delete"> + <Text text="Delete"/> + </Button> + </Row> + </Array> + </ScrollPanel> + <Row> + <Button onClick="@addTodo"> + <Text text="Add new TODO"/> + </Button> + <Button onClick="@pasteTodo"> + <Text text="Paste TODO from Clipboard"/> + </Button> + </Row> + </Column> + </Gui> +</Root> |