package de.cowtipper.cowlection.config; import com.google.common.collect.Maps; import de.cowtipper.cowlection.Cowlection; import de.cowtipper.cowlection.command.MooCommand; import de.cowtipper.cowlection.command.TabCompletableCommand; import de.cowtipper.cowlection.config.gui.MooConfigGui; import de.cowtipper.cowlection.config.gui.MooConfigPreview; import de.cowtipper.cowlection.data.DataHelper; import de.cowtipper.cowlection.util.MooChatComponent; import de.cowtipper.cowlection.util.Utils; import net.minecraft.client.Minecraft; import net.minecraft.client.audio.SoundCategory; import net.minecraft.command.ICommand; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagInt; import net.minecraft.nbt.NBTTagString; import net.minecraft.util.ChatComponentText; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.IChatComponent; import net.minecraft.util.Util; import net.minecraftforge.client.ClientCommandHandler; import net.minecraftforge.common.ForgeModContainer; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.config.ConfigCategory; import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Property; import net.minecraftforge.fml.client.event.ConfigChangedEvent; import net.minecraftforge.fml.common.eventhandler.EventPriority; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.lwjgl.input.Keyboard; import java.io.File; import java.time.LocalDate; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; import java.util.*; import java.util.regex.Pattern; /** * Mod configuration *

* Based on TheGreyGhost's MinecraftByExample * * @see ForgeModContainer */ public class MooConfig { // Category: General private static String configGuiExplanations; public static String mooCmdAlias; public static boolean fixReplyCmd; public static boolean enableCopyInventory; public static String[] tabCompletableNamesCommands; private static final String CATEGORY_LOGS_SEARCH = "logssearch"; public static String[] logsDirs; private static String defaultStartDate; // Category: Notifications public static boolean doUpdateCheck; public static boolean showBestFriendNotifications; public static boolean enableBestFriendNotificationSound; public static boolean showFriendNotifications; public static boolean showGuildNotifications; public static boolean doBestFriendsOnlineCheck; // Category: SkyBlock private static String enableSkyBlockOnlyFeatures; public static int notifyFreshServer; public static int notifyOldServer; public static boolean notifyServerAge; public static int tooltipToggleKeyBinding; private static String tooltipItemAge; public static boolean tooltipItemAgeShortened; private static String tooltipItemTimestamp; private static String numeralSystem; private static String tooltipAuctionHousePriceEach; private static String bazaarConnectGraphsNodes; public static int bazaarConnectGraphsLineWidth; private static int lookupWikiKeyBinding; private static int lookupPriceKeyBinding; public static boolean lookupItemDirectly; // Category: SkyBlock Dungeons private static String showItemQualityAndFloor; private static String dungItemQualityPos; public static int dungItemToolTipToggleKeyBinding; public static boolean dungSendPerformanceOnDeath; public static boolean dungOverlayEnabled; public static int dungOverlayPositionX; public static int dungOverlayPositionY; public static int dungOverlayGuiScale; public static boolean dungOverlayTextShadow; private static String dungPartyFinderPlayerLookup; public static boolean dungPartyFinderPartyLookup; public static boolean dungPartiesSize; public static int dungClassMin; public static boolean dungFilterPartiesWithCarry; private static boolean dungFilterPartiesWithArcherDupes; private static boolean dungFilterPartiesWithBerserkDupes; private static boolean dungFilterPartiesWithHealerDupes; private static boolean dungFilterPartiesWithMageDupes; private static boolean dungFilterPartiesWithTankDupes; private static Configuration cfg = null; private static final List configCategories = new ArrayList<>(); private final Cowlection main; private Property propMooCmdAlias; private Property propTabCompletableNamesCommands; private List logSearchProperties; public MooConfig(Cowlection main, Configuration configuration) { this.main = main; cfg = configuration; if (cfg.getLoadedConfigVersion() == null || !cfg.getLoadedConfigVersion().equals(cfg.getDefinedConfigVersion())) { updateConfig(cfg.getLoadedConfigVersion()); } initConfig(); } private void updateConfig(String oldVersion) { if (oldVersion == null) { // config of Cowlection v1.8.9-0.10.2 and older // leave log search settings as is if (cfg.hasCategory(Configuration.CATEGORY_CLIENT)) { // copy old 'moo' value to new, separate config if (cfg.hasKey(Configuration.CATEGORY_CLIENT, "moo")) { String oldMoo = cfg.getString("moo", Configuration.CATEGORY_CLIENT, "00000000-0000-0000-0000-000000000000", "Temporary config entry, should be deleted automatically.", Utils.VALID_UUID_PATTERN); if (StringUtils.isNotEmpty(oldMoo) && Utils.isValidUuid(oldMoo)) { // save into new cfg: main.getMoo().setMooIfValid(oldMoo, false); } } // delete client category (no longer used) ConfigCategory oldClientCategory = cfg.getCategory(Configuration.CATEGORY_CLIENT); cfg.removeCategory(oldClientCategory); } cfg.save(); } } private void initConfig() { syncFromFile(); MinecraftForge.EVENT_BUS.register(new ConfigEventHandler()); } /** * Load the configuration values from the configuration file */ private void syncFromFile() { syncConfig(true, true, true); } /** * Save the GUI-altered values to disk */ public void syncFromGui() { syncConfig(false, true, true); } /** * Save the GUI-altered values to the properties; don't save to disk - only memory */ public void syncFromGuiWithoutSaving() { syncConfig(false, true, false); } /** * Save the Configuration variables (fields) to disk */ public void syncFromFields() { syncConfig(false, false, true); } /** * Synchronise the three copies of the data * 1) loadConfigFromFile && readFieldsFromConfig -> initialise everything from the disk file * 2) !loadConfigFromFile && readFieldsFromConfig -> copy everything from the config file (altered by GUI) * 3) !loadConfigFromFile && !readFieldsFromConfig -> copy everything from the native fields * * @param loadConfigFromFile if true, load the config field from the configuration file on disk * @param readFieldsFromConfig if true, reload the member variables from the config field * @param saveToFile if true, save changes to config file */ @SuppressWarnings("DuplicatedCode") private void syncConfig(boolean loadConfigFromFile, boolean readFieldsFromConfig, boolean saveToFile) { if (loadConfigFromFile) { cfg.load(); } // reset previous entries configCategories.clear(); // Category: General MooConfigCategory configCat = new MooConfigCategory("General", "general"); configCategories.add(configCat); // Sub-Category: Cowlection config gui MooConfigCategory.SubCategory subCat = configCat.addSubCategory("Cowlection config gui"); subCat.addExplanations("Display of the explanations for each sub-section:", " ‣ " + EnumChatFormatting.YELLOW + "as tooltip ①" + EnumChatFormatting.DARK_GRAY + "⬛" + EnumChatFormatting.RESET + " = tooltip when hovering over sub-category heading (with darkened background)", " ‣ " + EnumChatFormatting.YELLOW + "as tooltip ②" + EnumChatFormatting.WHITE + "⬛" + EnumChatFormatting.RESET + " = tooltip when hovering over sub-category heading (no extra background)", " ‣ " + EnumChatFormatting.YELLOW + "as text" + EnumChatFormatting.RESET + " = below each sub-category heading", " ‣ " + EnumChatFormatting.YELLOW + "hidden" + EnumChatFormatting.RESET + " = "); Property propConfigGuiExplanations = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "configGuiExplanations", "tooltip ① §0⬛", "Display config settings explanations", new String[]{"as tooltip ①§0⬛", "as tooltip ②§f⬛", "as text", "hidden"})); // Sub-Category: API settings subCat = configCat.addSubCategory("API settings"); subCat.addExplanations("Some features use the official Hypixel API and therefore require your API key.", "Use " + EnumChatFormatting.YELLOW + "/moo apikey " + EnumChatFormatting.RESET + "to see how to request a new API key from Hypixel", "The API key is stored " + EnumChatFormatting.ITALIC + "locally " + EnumChatFormatting.ITALIC + "on your computer."); subCat.addConfigEntry(main.getMoo().getPropIsMooValid()); // Sub-Category: Command settings subCat = configCat.addSubCategory("Command settings"); propMooCmdAlias = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "mooCmdAlias", "m", "Alias for /moo command") .setValidationPattern(Pattern.compile("^[A-Za-z]*$"))); Property propFixReplyCmd = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "fixReplyCmd", true, "Auto-replace /r?")); Property propEnableCopyInventory = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "enableCopyInventory", false, "Enable copy inventory with CTRL + C?")); // Sub-Category: Tab-completable names in commands subCat = configCat.addSubCategory("Tab-completable usernames"); subCat.addExplanations("For certain commands you can use " + EnumChatFormatting.YELLOW + "TAB " + EnumChatFormatting.RESET + "to autocomplete player names", EnumChatFormatting.UNDERLINE + "Uses player names from:", " ‣ Guild and Party chat", " ‣ Party and game (duels) invites", " ‣ SkyBlock Dungeon party finder: when a player joins the group", " ‣ Online best friends (if the best friend online checker is enabled)"); propTabCompletableNamesCommands = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "tabCompletableNamesCommands", new String[]{"party", "p", "invite", "visit", "ah", "ignore", "msg", "tell", "w", "boop", "profile", "friend", "friends"}, "List of commands with a Tab-completable username argument.") .setValidationPattern(Pattern.compile("^[A-Za-z]+$"))); // Sub-Category: Other settings subCat = configCat.addSubCategory("Other settings"); subCat.addExplanations("Other settings that are located in other GUIs"); Property propLogsDirs = subCat.addConfigEntry(cfg.get(CATEGORY_LOGS_SEARCH, "logsDirs", resolveDefaultLogsDirs(), "Directories with Minecraft log files")); Property propDefaultStartDate = subCat.addConfigEntry(cfg.get(CATEGORY_LOGS_SEARCH, "defaultStartDate", "3", "Default start date (a number means X months ago, alternatively a fixed date à la yyyy-mm-dd can be used)")) .setValidationPattern(Pattern.compile("^[1-9][0-9]{0,2}|(2[0-9]{3}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]))$")); logSearchProperties = new ArrayList<>(); logSearchProperties.add(propLogsDirs); logSearchProperties.add(propDefaultStartDate); // Category: Notifications configCat = new MooConfigCategory("Notifications", "notifications"); configCategories.add(configCat); // Sub-Category: Mod update checker subCat = configCat.addSubCategory("Mod update checker"); Property propDoUpdateCheck = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "doUpdateCheck", true, "Check for mod updates?")); // Sub-Category: Login & Logout subCat = configCat.addSubCategory("Login & Logout"); subCat.addExplanations("Hides selected login/logout notifications ", "while still showing notifications of best friends (if enabled).", "Add someone to the best friends list with " + EnumChatFormatting.YELLOW + "/moo add " + EnumChatFormatting.RESET); Property propShowBestFriendNotifications = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "showBestFriendNotifications", true, "Set to true to receive best friends' login/logout messages, set to false hide them."), new MooConfigPreview(new ChatComponentText("§a§lBest friend §a> §6Cow §r§ejoined."))); Property propEnableBestFriendNotificationSound = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "enableBestFriendNotificationSound", false, "Set to true to play a notification sound when a best friend comes online"), new MooConfigPreview("random.pop", Minecraft.getMinecraft().gameSettings.getSoundLevel(SoundCategory.MASTER), 1)); Property propShowFriendNotifications = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "showFriendNotifications", true, "Set to true to receive friends' login/logout messages, set to false hide them."), new MooConfigPreview(new ChatComponentText("§aFriend > §r§aBob §ejoined."))); Property propShowGuildNotifications = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "showGuildNotifications", true, "Set to true to receive guild members' login/logout messages, set to false hide them."), new MooConfigPreview(new ChatComponentText("§2Guild > §r§7Herobrian §eleft."))); // Sub-Category: Best friends online status subCat = configCat.addSubCategory("Best friend online checker"); subCat.addExplanations("Check which best friends are online when you join the server.", "About once a day, a check for new name changes is also performed automatically."); IChatComponent spacer = new MooChatComponent(", ").green(); Property propDoBestFriendsOnlineCheck = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "doBestFriendsOnlineCheck", true, "Set to true to check best friends' online status when joining a server, set to false to disable."), new MooConfigPreview(new MooChatComponent("§a⬤ Online best friends (§24§a/§216§a): ") .appendSibling(MooConfigPreview.createDemoOnline("Alice", "Housing", "1 hour 13 minutes 37 seconds")).appendSibling(spacer) .appendSibling(MooConfigPreview.createDemoOnline("Bob", "Build Battle", "2 hours 13 minutes 37 seconds")).appendSibling(spacer) .appendSibling(MooConfigPreview.createDemoOnline("Cow", "SkyBlock", "13 minutes 37 seconds")).appendSibling(spacer) .appendSibling(MooConfigPreview.createDemoOnline("Herobrian", "Murder Mystery", "13 hours 33 minutes 37 seconds")))); // Category: SkyBlock configCat = new MooConfigCategory("SkyBlock", "skyblock"); configCategories.add(configCat); // Sub-Category: SkyBlock-only features subCat = configCat.addSubCategory("SkyBlock-only features"); Property propEnableSkyBlockOnlyFeatures = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "enableSkyBlockOnlyFeatures", "on SkyBlock", "Enable SkyBlock-only features?", new String[]{"on SkyBlock", "always", "never"})); // Sub-Category: Server age notifications subCat = configCat.addSubCategory("Server age notifications"); subCat.addExplanations("Servers usually restart once they exceed " + EnumChatFormatting.YELLOW + "30-38 ingame days " + EnumChatFormatting.RESET + "(10-13 hours)", "Use the command " + EnumChatFormatting.YELLOW + "/moo worldage " + EnumChatFormatting.RESET + "to check how long the current world is loaded.", EnumChatFormatting.ITALIC + "Set a value to 0 to disable that notification."); Property propNotifyFreshServer = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "notifyFreshServer", 1, "Notify when a world is loaded X ingame days", 0, 40)); Property propNotifyServerAge = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "notifyServerAge", true, "Show server age notifications?")); // Sub-Category: Tooltip enhancements subCat = configCat.addSubCategory("Tooltip enhancements"); Property propTooltipToggleKeyBinding = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "tooltipToggleKeyBinding", Keyboard.KEY_LSHIFT, "Key to toggle tooltip")); Map demoItemExtraAttributes = new HashMap<>(); demoItemExtraAttributes.put("new_years_cake", new NBTTagInt(1)); demoItemExtraAttributes.put("originTag", new NBTTagString("REWARD_NEW_YEARS_CAKE_NPC")); demoItemExtraAttributes.put("id", new NBTTagString("NEW_YEAR_CAKE")); demoItemExtraAttributes.put("uuid", new NBTTagString("64b3a60b-74f2-4ebd-818d-d019c5b7f3e0")); demoItemExtraAttributes.put("timestamp", new NBTTagString("6/16/19 5:05 PM")); MooConfigPreview nonStackableItemPreview = new MooConfigPreview(MooConfigPreview.createDemoItem("cake", "§dNew Year Cake", new String[]{"§7Given to every player as a", "§7celebration for the 1st SkyBlock", "§7year!", "", "§d§lSPECIAL"}, demoItemExtraAttributes)); Property propTooltipItemAge = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "tooltipItemAge", "always", "Show item age", new String[]{"always", "key press", "never"}), nonStackableItemPreview); Property propTooltipItemAgeShortened = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "tooltipItemAgeShortened", true, "Shorten item age?")); Property propTooltipItemTimestamp = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "tooltipItemTimestamp", "key press", "Show item creation date", new String[]{"always", "key press", "never"})); Property propNumeralSystem = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "numeralSystem", "Arabic: 1, 4, 10", "Use Roman or Arabic numeral system?", new String[]{"Arabic: 1, 4, 10", "Roman: I, IV, X"})); Map demoAhItemExtraAttributes = new HashMap<>(); demoItemExtraAttributes.put("id", new NBTTagString("BEACON")); ItemStack demoAhItem = MooConfigPreview.createDemoItem("beacon", "§764x §fB§8e§facon Block", new String[]{"§f§lCOMMON", "§8§m-----------------", "§7Seller: §6[MVP§0++§6] Enlightener", "§7Buy it now: §63,900,000 coins", "", "§7Ends in: §e13h 33m 37s", "", "§eDon't click to inspect!"}, demoAhItemExtraAttributes); demoAhItem.stackSize = 64; MooConfigPreview ahItemPreview = new MooConfigPreview(demoAhItem); Property propTooltipAuctionHousePriceEach = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "tooltipAuctionHousePriceEach", "always", "Add price per item if multiple items are bought or sold", new String[]{"always", "key press", "never"}), ahItemPreview); MooConfigPreview bazaarGraphPreview = new MooConfigPreview(MooConfigPreview.createDemoItem("paper", "§aBuy Price §731d §77d §e24h", new String[]{ "§7The price at which buy orders have been filled.", "", "§r┌----------------------------------------------┐", "§r│§66. 1k§r+§bxxxxxx§8·································§bxx§r│", "§r│§8····§r│§8······································§bx§8··§r│", "§r│§66. 1k§r+§8·····§bx§8···················§bx§8·······§bxxxxx§8···§r│", "§r│§8····§r│§8···············§bx§8········§bxxxxxxxxx§8········§r│", "§r│§8··§66k§r+§8··············§bx§8····§bxx§8··§bx§8·················§r│", "§r│§8····§r│§8············§bx§8··§bxxxx§8·§bxxx§8··················§r│", "§r│§8··§66k§r+§8······§bx§8·§bxxxx§8·§bx§8···························§r│", "§r│§8····§r│§8·······§bx§8·································§r│", "§r│§8··§66k§r+---------+----------+---------+---------+│", "§r│§8····§r24h§8······§r18h§8········§r12h§8·······§r6h§8·······§rnow│", "§r└----------------------------------------------┘"}, Maps.newHashMap())); Property propBazaarConnectGraphsNodes = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "bazaarConnectGraphsNodes", "always", "Bazaar: connect the graph nodes", new String[]{"always", "key press", "never"}), bazaarGraphPreview); Property propBazaarConnectGraphsLineWidth = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "bazaarConnectGraphsLineWidth", 3, "Line width of bazaar graph", 1, 10)); // Sub-Category: Item lookup subCat = configCat.addSubCategory("Item lookup"); subCat.addExplanations("Lookup item prices or wiki articles for any SkyBlock item in any inventory."); Property propLookupWikiKeyBinding = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "lookupWikiKeyBinding", Keyboard.KEY_I, "Key to lookup wiki")); Property propLookupPriceKeyBinding = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "lookupPriceKeyBinding", Keyboard.KEY_P, "Key to lookup item price")); Property propLookupItemDirectly = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "lookupItemDirectly", true, "Open website directly?")); // Category: SkyBlock Dungeons configCat = new MooConfigCategory("SkyBlock Dungeons", "skyblockdungeons"); configCat.setMenuDisplayName("SB Dungeons"); configCategories.add(configCat); // Sub-Category: Tooltip enhancements subCat = configCat.addSubCategory("Dungeon item tooltip enhancements"); subCat.addExplanations("Shows " + EnumChatFormatting.YELLOW + "item quality " + EnumChatFormatting.RESET + "and " + EnumChatFormatting.YELLOW + "dungeon floor" + EnumChatFormatting.RESET + " (if enabled)", "", "Hold left " + EnumChatFormatting.YELLOW + "SHIFT " + EnumChatFormatting.RESET + "while hovering over a dungeon item,", "to remove stats from reforges and essences (✪)", "which normally makes the comparison of dungeon items difficult.", "Instead, the tooltip shows...", " ‣ base/default stats " + EnumChatFormatting.GRAY + "(outside dungeons; 1st value - usually red or green)", " ‣ stats inside dungeons " + EnumChatFormatting.GRAY + "(including dungeon level stat boost, but without essences [₀ₓ✪])", " ‣ stats inside dungeons with 5x essence upgrades " + EnumChatFormatting.GRAY + "(₅ₓ✪)"); Property propShowItemQualityAndFloor = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "showItemQualityAndFloor", "always", "show item quality + obtained floor?", new String[]{"always", "key press", "never"})); Property propDungItemQualityPos = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungItemQualityPos", "top", "Position of item quality in tooltip", new String[]{"top", "bottom"}), new MooConfigPreview( MooConfigPreview.createDungeonItem("light", "7/17/20 7:22 PM", "§7Gear Score: §d336 §8(526)", "§7Crit Chance: §c+5% §9(Light +2%)", "§7Crit Damage: §c+30% §9(Light +4%) §8(+48.9%)", "§7Bonus Attack Speed: §c+4% §9(Light +4%)", "", "§7Health: §a+126 HP §9(Light +15 HP) §8(+205.38 HP)", "§7Defense: §a+76 §9(Light +4) §8(+123.88)", "§7Speed: §a+4 §9(Light +4) §8(+6.52)", "", "§9Growth V, §9Protection V", "§9Thorns III", "", "§7Increase the damage you deal", "§7with arrows by §c5%§7.", "", "§6Full Set Bonus: Skeleton Soldier", "§7Increase the damage you deal", "§7with arrows by an extra §c25%§7.", "", "§aPerfect 52500 / 52500", "§5§lEPIC DUNGEON LEGGINGS"), MooConfigPreview.createDungeonItem("clean", "7/11/20 12:27 PM", "§7Gear Score: §d359 §8(561)", "§7Crit Chance: §c+11% §9(Clean +8%)", "§7Crit Damage: §c+26% §8(+42.38%)", "", "§7Health: §a+126 HP §9(Clean +15 HP) §8(+205.38 HP)", "§7Defense: §a+87 §9(Clean +15) §8(+141.81)", "", "§9Growth V, §9Protection V", "§9Thorns III", "", "§7Increase the damage you deal", "§7with arrows by §c5%§7.", "", "§6Full Set Bonus: Skeleton Soldier", "§7Increase the damage you deal", "§7with arrows by an extra §c25%§7.", "", "§aPerfect 52500 / 52500", "§5§lEPIC DUNGEON LEGGINGS"))); Property propDungItemToolTipToggleKeyBinding = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungItemToolTipToggleKeyBinding", Keyboard.KEY_LSHIFT, "Key to toggle dungeon item tooltip")); // Sub-Category: Performance Overlay subCat = configCat.addSubCategory("Performance Overlay"); subCat.addExplanations(EnumChatFormatting.UNDERLINE + "Keeps track of:", " ‣ skill score " + EnumChatFormatting.GRAY + "(reduced by deaths and failed puzzles)", " ‣ speed score " + EnumChatFormatting.GRAY + "(-2.2 points/minute when over 20 minutes)", " ‣ bonus score " + EnumChatFormatting.GRAY + "(+1 [max 5] for each destroyed crypt; if 'enhanced tab list' is disabled: limited to ~50 blocks away from the player)", "Does " + EnumChatFormatting.ITALIC + "not" + EnumChatFormatting.RESET + " track explorer score " + EnumChatFormatting.GRAY + "(explored rooms, secrets, ...)"); Property propDungSendPerformanceOnDeath = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungSendPerformanceOnDeath", true, "Send dungeon performance after a player died?")); Property propDungOverlayEnabled = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungOverlayEnabled", true, "Enable Dungeon performance overlay?")); Property propDungOverlayPositionX = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungOverlayPositionX", 1, "Dungeon performance overlay position: x value", 0, 1000), null, "‰", // per mille (slider) -> { MooConfig.dungOverlayPositionX = slider.getValueInt(); MooConfigGui.showDungeonPerformanceOverlayUntil = System.currentTimeMillis() + 500; }); Property propDungOverlayPositionY = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungOverlayPositionY", 1, "Dungeon performance overlay position: y value", 0, 1000), null, "‰", // per mille (slider) -> { MooConfig.dungOverlayPositionY = slider.getValueInt(); MooConfigGui.showDungeonPerformanceOverlayUntil = System.currentTimeMillis() + 500; }); Property propDungOverlayGuiScale = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungOverlayGuiScale", 100, "Dungeon performance overlay GUI scale", 50, 200), null, "%", (slider) -> { MooConfig.dungOverlayGuiScale = slider.getValueInt(); MooConfigGui.showDungeonPerformanceOverlayUntil = System.currentTimeMillis() + 500; }); Property propDungOverlayTextShadow = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungOverlayTextShadow", true, "Dungeon performance overlay GUI scale")); // Sub-Category: Party Finder subCat = configCat.addSubCategory("Dungeon Party Finder"); subCat.addExplanations("Adds various indicators to the dungeon party finder", "to make it easier to find the perfect party:", "", "Marks parties...", " ‣ you cannot join: " + EnumChatFormatting.RED + "⬛", " ‣ that do not meet all your criteria: " + EnumChatFormatting.GOLD + "⬛", " ‣ with someone below a certain class level: " + EnumChatFormatting.RED + EnumChatFormatting.BOLD + "ᐯ" + EnumChatFormatting.RESET, " ‣ with duplicated roles you specify below: " + EnumChatFormatting.GOLD + "²⁺", " ‣ with 'carry' in their notes: " + EnumChatFormatting.AQUA + "carry", " ‣ that match your criteria: " + EnumChatFormatting.GREEN + "⬛"); Property propDungPartyFinderPlayerLookup = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungPartyFinderPlayerLookup", "as a tooltip", "Show armor + dungeons stats of player joining via party finder as a tooltip or in chat?", new String[]{"as a tooltip", "in chat", "disabled"})); Property propDungPartyFinderPartyLookup = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungPartyFinderPartyLookup", true, "Lookup info when joining another party?")); Property propDungPartiesSize = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungPartiesSize", true, "Show size of parties?"), new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.WHITE + "1 - 4").gray())); Property propDungClassMin = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungClassMin", 0, "Marks parties with members with lower class level than this value") .setMinValue(0).setMaxValue(50), new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.RED + EnumChatFormatting.BOLD + "ᐯ").gray())); Property propDungFilterPartiesWithCarry = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungFilterPartiesWithCarry", true, "Mark parties with carry in the notes"), new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.AQUA + "carry" + EnumChatFormatting.GRAY + " or " + EnumChatFormatting.GREEN + "carry " + EnumChatFormatting.GRAY + "('free' carries)").gray())); Property propDungFilterPartiesWithArcherDupes = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungFilterPartiesWithArcherDupes", true, "Mark parties with duplicated Archer class?"), new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.GOLD + "²⁺" + EnumChatFormatting.YELLOW + "A").gray())); Property propDungFilterPartiesWithBerserkDupes = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungFilterPartiesWithBerserkDupes", false, "Mark parties with duplicated Berserk class?"), new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.GOLD + "²⁺" + EnumChatFormatting.YELLOW + "B").gray())); Property propDungFilterPartiesWithHealerDupes = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungFilterPartiesWithHealerDupes", false, "Mark parties with duplicated Healer class?"), new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.GOLD + "²⁺" + EnumChatFormatting.YELLOW + "H").gray())); Property propDungFilterPartiesWithMageDupes = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungFilterPartiesWithMageDupes", false, "Mark parties with duplicated Mage class?"), new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.GOLD + "²⁺" + EnumChatFormatting.YELLOW + "M").gray())); Property propDungFilterPartiesWithTankDupes = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungFilterPartiesWithTankDupes", false, "Mark parties with duplicated Tank class?"), new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.GOLD + "²⁺" + EnumChatFormatting.YELLOW + "T").gray())); boolean modifiedMooCmdAlias = false; String mooCmdAliasPreChange = mooCmdAlias; boolean modifiedTabCompletableCommandsList = false; String[] tabCompletableCommandsPreChange = tabCompletableNamesCommands != null ? tabCompletableNamesCommands.clone() : null; if (readFieldsFromConfig) { // Category: General configGuiExplanations = propConfigGuiExplanations.getString(); mooCmdAlias = propMooCmdAlias.getString(); fixReplyCmd = propFixReplyCmd.getBoolean(); enableCopyInventory = propEnableCopyInventory.getBoolean(); tabCompletableNamesCommands = propTabCompletableNamesCommands.getStringList(); logsDirs = propLogsDirs.getStringList(); defaultStartDate = propDefaultStartDate.getString().trim(); // Category: Notifications doUpdateCheck = propDoUpdateCheck.getBoolean(); showBestFriendNotifications = propShowBestFriendNotifications.getBoolean(); enableBestFriendNotificationSound = propEnableBestFriendNotificationSound.getBoolean(); showFriendNotifications = propShowFriendNotifications.getBoolean(); showGuildNotifications = propShowGuildNotifications.getBoolean(); doBestFriendsOnlineCheck = propDoBestFriendsOnlineCheck.getBoolean(); // Category: SkyBlock enableSkyBlockOnlyFeatures = propEnableSkyBlockOnlyFeatures.getString(); notifyFreshServer = propNotifyFreshServer.getInt(); notifyOldServer = propNotifyOldServer.getInt(); notifyServerAge = propNotifyServerAge.getBoolean(); tooltipToggleKeyBinding = propTooltipToggleKeyBinding.getInt(); tooltipItemAge = propTooltipItemAge.getString(); tooltipItemAgeShortened = propTooltipItemAgeShortened.getBoolean(); tooltipItemTimestamp = propTooltipItemTimestamp.getString(); numeralSystem = propNumeralSystem.getString(); tooltipAuctionHousePriceEach = propTooltipAuctionHousePriceEach.getString(); bazaarConnectGraphsNodes = propBazaarConnectGraphsNodes.getString(); bazaarConnectGraphsLineWidth = propBazaarConnectGraphsLineWidth.getInt(); lookupWikiKeyBinding = propLookupWikiKeyBinding.getInt(); lookupPriceKeyBinding = propLookupPriceKeyBinding.getInt(); lookupItemDirectly = propLookupItemDirectly.getBoolean(); // Category: SkyBlock Dungeons showItemQualityAndFloor = propShowItemQualityAndFloor.getString(); dungItemQualityPos = propDungItemQualityPos.getString(); dungItemToolTipToggleKeyBinding = propDungItemToolTipToggleKeyBinding.getInt(); dungSendPerformanceOnDeath = propDungSendPerformanceOnDeath.getBoolean(); dungOverlayEnabled = propDungOverlayEnabled.getBoolean(); dungOverlayPositionX = propDungOverlayPositionX.getInt(); dungOverlayPositionY = propDungOverlayPositionY.getInt(); dungOverlayGuiScale = propDungOverlayGuiScale.getInt(); dungOverlayTextShadow = propDungOverlayTextShadow.getBoolean(); dungPartyFinderPlayerLookup = propDungPartyFinderPlayerLookup.getString(); dungPartyFinderPartyLookup = propDungPartyFinderPartyLookup.getBoolean(); dungPartiesSize = propDungPartiesSize.getBoolean(); dungClassMin = propDungClassMin.getInt(); dungFilterPartiesWithCarry = propDungFilterPartiesWithCarry.getBoolean(); dungFilterPartiesWithArcherDupes = propDungFilterPartiesWithArcherDupes.getBoolean(); dungFilterPartiesWithBerserkDupes = propDungFilterPartiesWithBerserkDupes.getBoolean(); dungFilterPartiesWithHealerDupes = propDungFilterPartiesWithHealerDupes.getBoolean(); dungFilterPartiesWithMageDupes = propDungFilterPartiesWithMageDupes.getBoolean(); dungFilterPartiesWithTankDupes = propDungFilterPartiesWithTankDupes.getBoolean(); if (!StringUtils.equals(mooCmdAliasPreChange, mooCmdAlias)) { modifiedMooCmdAlias = true; } if (!Arrays.equals(tabCompletableCommandsPreChange, tabCompletableNamesCommands)) { modifiedTabCompletableCommandsList = true; } } // Category: General propConfigGuiExplanations.set(configGuiExplanations); propMooCmdAlias.set(mooCmdAlias); propFixReplyCmd.set(fixReplyCmd); propEnableCopyInventory.set(enableCopyInventory); propTabCompletableNamesCommands.set(tabCompletableNamesCommands); propLogsDirs.set(logsDirs); propDefaultStartDate.set(defaultStartDate); // Category: Notifications propDoUpdateCheck.set(doUpdateCheck); propShowBestFriendNotifications.set(showBestFriendNotifications); propEnableBestFriendNotificationSound.set(enableBestFriendNotificationSound); propShowFriendNotifications.set(showFriendNotifications); propShowGuildNotifications.set(showGuildNotifications); propDoBestFriendsOnlineCheck.set(doBestFriendsOnlineCheck); // Category: SkyBlock propEnableSkyBlockOnlyFeatures.set(enableSkyBlockOnlyFeatures); propNotifyFreshServer.set(notifyFreshServer); propNotifyOldServer.set(notifyOldServer); propNotifyServerAge.set(notifyServerAge); propTooltipToggleKeyBinding.set(tooltipToggleKeyBinding); propTooltipItemAge.set(tooltipItemAge); propTooltipItemAgeShortened.set(tooltipItemAgeShortened); propTooltipItemTimestamp.set(tooltipItemTimestamp); propNumeralSystem.set(numeralSystem); propTooltipAuctionHousePriceEach.set(tooltipAuctionHousePriceEach); propBazaarConnectGraphsNodes.set(bazaarConnectGraphsNodes); propBazaarConnectGraphsLineWidth.set(bazaarConnectGraphsLineWidth); propLookupWikiKeyBinding.set(lookupWikiKeyBinding); propLookupPriceKeyBinding.set(lookupPriceKeyBinding); propLookupItemDirectly.set(lookupItemDirectly); // Category: SkyBlock Dungeons propShowItemQualityAndFloor.set(showItemQualityAndFloor); propDungItemQualityPos.set(dungItemQualityPos); propDungItemToolTipToggleKeyBinding.set(dungItemToolTipToggleKeyBinding); propDungSendPerformanceOnDeath.set(dungSendPerformanceOnDeath); propDungOverlayEnabled.set(dungOverlayEnabled); propDungOverlayPositionX.set(dungOverlayPositionX); propDungOverlayPositionY.set(dungOverlayPositionY); propDungOverlayGuiScale.set(dungOverlayGuiScale); propDungOverlayTextShadow.set(dungOverlayTextShadow); propDungPartyFinderPlayerLookup.set(dungPartyFinderPlayerLookup); propDungPartyFinderPartyLookup.set(dungPartyFinderPartyLookup); propDungPartiesSize.set(dungPartiesSize); propDungClassMin.set(dungClassMin); propDungFilterPartiesWithCarry.set(dungFilterPartiesWithCarry); propDungFilterPartiesWithArcherDupes.set(dungFilterPartiesWithArcherDupes); propDungFilterPartiesWithBerserkDupes.set(dungFilterPartiesWithBerserkDupes); propDungFilterPartiesWithHealerDupes.set(dungFilterPartiesWithHealerDupes); propDungFilterPartiesWithMageDupes.set(dungFilterPartiesWithMageDupes); propDungFilterPartiesWithTankDupes.set(dungFilterPartiesWithTankDupes); if (saveToFile && cfg.hasChanged()) { boolean isPlayerIngame = Minecraft.getMinecraft().thePlayer != null; if (modifiedMooCmdAlias) { Map clientCommandsMap = ClientCommandHandler.instance.getCommands(); ICommand possibleClientCommand = clientCommandsMap.get(mooCmdAlias); if (possibleClientCommand != null && !(possibleClientCommand instanceof MooCommand)) { // tried to use a command name which is already used by another client side command; however, this would overwrite the original command if (isPlayerIngame) { main.getChatHelper().sendMessage(EnumChatFormatting.GOLD, " ⚠ " + EnumChatFormatting.GOLD + "Client-side commands from other mods cannot be used as a command alias. " + EnumChatFormatting.RED + "This would overwrite the other command! Therefore the alias for " + EnumChatFormatting.DARK_RED + "/moo" + EnumChatFormatting.RED + " was not changed to " + EnumChatFormatting.DARK_RED + "/" + mooCmdAlias); } mooCmdAlias = mooCmdAliasPreChange; propMooCmdAlias.set(mooCmdAlias); } else if (isPlayerIngame) { if (StringUtils.isEmpty(mooCmdAlias)) { main.getChatHelper().sendMessage(EnumChatFormatting.RED, "Removed command alias for " + EnumChatFormatting.DARK_RED + "/moo " + EnumChatFormatting.RED + "which takes effect after a game restart."); } else { main.getChatHelper().sendMessage(EnumChatFormatting.RED, "Changed command alias for " + EnumChatFormatting.DARK_RED + "/moo " + EnumChatFormatting.RED + "to " + EnumChatFormatting.DARK_RED + "/" + mooCmdAlias + EnumChatFormatting.RED + " which takes effect after a game restart."); } } } if (modifiedTabCompletableCommandsList) { if (isPlayerIngame) { main.getChatHelper().sendMessage(EnumChatFormatting.RED, "Added or removed commands with tab-completable usernames take effect after a game restart! If player names cannot be tab-completed for a command after a game restart, check the capitalization of the command name."); } Map clientCommandsMap = ClientCommandHandler.instance.getCommands(); List removedCommands = new ArrayList<>(); for (String tabCompletableCommandName : tabCompletableNamesCommands) { ICommand possibleClientCommand = clientCommandsMap.get(tabCompletableCommandName); if (possibleClientCommand != null && !(possibleClientCommand instanceof TabCompletableCommand)) { // tried to add a client side command to tab-completable commands; however, this would overwrite the original command removedCommands.add(tabCompletableCommandName); } } if (removedCommands.size() > 0) { if (isPlayerIngame) { main.getChatHelper().sendMessage(EnumChatFormatting.GOLD, " ⚠ " + EnumChatFormatting.GOLD + "Client-side commands from other mods cannot be added to commands with tab-completable usernames. " + EnumChatFormatting.RED + "This would overwrite the other command! Therefore the following commands have been removed from the list of commands with tab-completable usernames: " + EnumChatFormatting.GOLD + String.join(EnumChatFormatting.RED + ", " + EnumChatFormatting.GOLD, removedCommands)); } tabCompletableNamesCommands = (String[]) ArrayUtils.removeElements(tabCompletableNamesCommands, removedCommands.toArray()); propTabCompletableNamesCommands.set(tabCompletableNamesCommands); } } cfg.save(); } } /** * Tries to find/resolve default directories containing minecraft logfiles (in .log.gz format) * * @return list of /logs/ directories */ private String[] resolveDefaultLogsDirs() { List logsDirs = new ArrayList<>(); File currentMcLogsDirFile = new File(Minecraft.getMinecraft().mcDataDir, "logs"); if (currentMcLogsDirFile.exists() && currentMcLogsDirFile.isDirectory()) { String currentMcLogsDir = Utils.toRealPath(currentMcLogsDirFile); logsDirs.add(currentMcLogsDir); } String defaultMcLogsDir = System.getProperty("user.home"); Util.EnumOS osType = Util.getOSType(); // default directories for .minecraft: https://minecraft.gamepedia.com/.minecraft switch (osType) { case WINDOWS: defaultMcLogsDir += "\\AppData\\Roaming\\.minecraft\\logs"; break; case OSX: defaultMcLogsDir += "/Library/Application Support/minecraft/logs"; break; default: defaultMcLogsDir += "/.minecraft/logs"; } File defaultMcLogsDirFile = new File(defaultMcLogsDir); if (defaultMcLogsDirFile.exists() && defaultMcLogsDirFile.isDirectory() && !currentMcLogsDirFile.equals(defaultMcLogsDirFile)) { logsDirs.add(Utils.toRealPath(defaultMcLogsDirFile)); } return logsDirs.toArray(new String[]{}); } // Category: General public static Setting getConfigGuiExplanationsDisplay() { return Setting.get(configGuiExplanations); } // Category: Notifications /** * Should login/logout notifications be modified and thus monitored? * * @return true if notifications should be monitored */ public static boolean doMonitorNotifications() { return showBestFriendNotifications || enableBestFriendNotificationSound || !showFriendNotifications || !showGuildNotifications; } // Category: SkyBlock public static Setting getEnableSkyBlockOnlyFeatures() { return Setting.get(enableSkyBlockOnlyFeatures); } public static Setting getTooltipAuctionHousePriceEachDisplay() { return Setting.get(tooltipAuctionHousePriceEach); } public static Setting getBazaarConnectGraphsNodes() { return Setting.get(bazaarConnectGraphsNodes); } public static Setting getTooltipItemAgeDisplay() { return Setting.get(tooltipItemAge); } public static Setting getTooltipItemTimestampDisplay() { return Setting.get(tooltipItemTimestamp); } public static boolean useRomanNumerals() { return numeralSystem.startsWith("Roman"); } public static boolean isTooltipToggleKeyBindingPressed() { return tooltipToggleKeyBinding > 0 && Keyboard.isKeyDown(tooltipToggleKeyBinding); } public static boolean isLookupWikiKeyBindingPressed() { return lookupWikiKeyBinding > 0 && Keyboard.isKeyDown(lookupWikiKeyBinding); } public static boolean isLookupPriceKeyBindingPressed() { return lookupPriceKeyBinding > 0 && Keyboard.isKeyDown(lookupPriceKeyBinding); } // Category: SkyBlock Dungeons public static Setting getShowItemQualityAndFloorDisplay() { return Setting.get(showItemQualityAndFloor); } public static boolean isDungItemQualityAtTop() { return dungItemQualityPos.equals("top"); } public static boolean isDungeonItemTooltipToggleKeyBindingPressed() { return dungItemToolTipToggleKeyBinding > 0 && Keyboard.isKeyDown(dungItemToolTipToggleKeyBinding); } public static Setting getDungPartyFinderPlayerLookupDisplay() { return Setting.get(dungPartyFinderPlayerLookup); } public static boolean filterDungPartiesWithDupes(DataHelper.DungeonClass dungeonClass) { switch (dungeonClass) { case ARCHER: return dungFilterPartiesWithArcherDupes; case BERSERK: return dungFilterPartiesWithBerserkDupes; case HEALER: return dungFilterPartiesWithHealerDupes; case MAGE: return dungFilterPartiesWithMageDupes; case TANK: return dungFilterPartiesWithTankDupes; default: return false; } } // MC Log Search: public static LocalDate calculateStartDate() { try { // date format: yyyy-mm-dd return LocalDate.parse(defaultStartDate); } catch (DateTimeParseException e) { // fall-through } try { int months = Integer.parseInt(defaultStartDate); return LocalDate.now().minus(months, ChronoUnit.MONTHS); } catch (NumberFormatException e) { // default: 1 month return LocalDate.now().minus(1, ChronoUnit.MONTHS); } } // other stuff public static List getConfigCategories() { return configCategories; } public Property getMooCmdAliasProperty() { return propMooCmdAlias; } public Property getTabCompletableNamesCommandsProperty() { return propTabCompletableNamesCommands; } public List getLogSearchProperties() { return logSearchProperties; } public class ConfigEventHandler { @SubscribeEvent(priority = EventPriority.NORMAL) public void onEvent(ConfigChangedEvent.OnConfigChangedEvent e) { if (Cowlection.MODID.equals(e.modID)) { syncFromGui(); } } } public enum Setting { UNKNOWN, DISABLED, // ALWAYS, TOOLTIP, TEXT, // SPECIAL; public static Setting get(String configValue) { switch (configValue) { case "always": return ALWAYS; case "as a tooltip": case "as tooltip ②§f⬛": return TOOLTIP; case "in chat": case "as text": return TEXT; case "hidden": case "never": case "disabled": return DISABLED; case "on SkyBlock": case "key press": case "as tooltip ①§0⬛": return SPECIAL; default: return UNKNOWN; } } } }