From 581e0bc3e52d99d73cfeeffcaec64fd3da31cbc0 Mon Sep 17 00:00:00 2001 From: Cow Date: Thu, 1 Jul 2021 21:41:59 +0200 Subject: Added more config options to existing features - Bazaar: order 'Sell Inventory/Sacks Now' tooltips asc/desc - MC Log file search: maximum file size to analyze - Toggle: dungeon performance summary at end of dungeon - Toggle: warn when queued and entered dungeon floors are different - Toggle: shorten item quality info for non-randomized items --- CHANGELOG.md | 6 +++ README.md | 2 +- .../java/de/cowtipper/cowlection/Cowlection.java | 18 +++++--- .../cowtipper/cowlection/command/MooCommand.java | 3 +- .../de/cowtipper/cowlection/config/MooConfig.java | 37 +++++++++++++++ .../config/gui/MooConfigCategoryScrolling.java | 4 +- .../listener/skyblock/DungeonsListener.java | 47 +++++++++++-------- .../listener/skyblock/SkyBlockListener.java | 6 ++- .../de/cowtipper/cowlection/search/GuiSearch.java | 31 ++++++++----- .../cowlection/search/LogFilesSearcher.java | 48 +++++++++++++------- .../cowlection/search/LogSearchResults.java | 53 ++++++++++++++++++++++ .../java/de/cowtipper/cowlection/util/Utils.java | 5 +- .../resources/assets/cowlection/lang/en_US.lang | 10 ++++ 13 files changed, 208 insertions(+), 62 deletions(-) create mode 100644 src/main/java/de/cowtipper/cowlection/search/LogSearchResults.java diff --git a/CHANGELOG.md b/CHANGELOG.md index f92cebf..912123e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,10 +16,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - *reminder:* CTRL + C (without SHIFT) copies the whole inventory - New config options: - Output of `/moo waila` and copied inventory data can now also be saved to files, instead of being copied to clipboard + - Bazaar: order 'Sell Inventory/Sacks Now' tooltips ascending or descending + - MC Log file search (`/moo search`): maximum log file size to analyze + - Toggle: display dungeon performance summary at the end of a dungeon + - Toggle: send warning when queued and entered dungeon floors are different + - Toggle: shorten item quality info for non-randomized items ### Changed - Disabled `M` keybinding in MC Options > Controls > Cowlection by default to avoid conflicts - `/moo config` sub-category explanations now default to "tooltip *without* darkened background", as the darkened background was more irritating than helpful +- MC Log file search now skips large files to prevent huge log files from blocking the search ### Fixed - 'Show Dungeon item base stats' feature now works with HPB'd items and master stars diff --git a/README.md b/README.md index 7863e66..d23bc26 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ It is a collection of different features mainly focused on Hypixel SkyBlock. | Feature | Command/Usage | |-------------------------------------------------------------------------|-----------------------------------------| | 'Best friends' list to limit the amount of join and leave notifications (always up-to-date names even after player name changes). Also checks best friends' online status automatically | `/moo add/remove/list/online` | -| Search through your Minecraft log files (click the `?` for more info) | `/moo search` | +| Search through your Minecraft log files | `/moo search` (click the `?` for more info) | | Stalk a player (check online status, current game, ...) | `/moo stalk` | | Toggle join/leave notifications for friends, guild members or best friends separately | `/moo config` → Notifications | | Copy chat component | ALT + right click
Hold shift to copy full component | diff --git a/src/main/java/de/cowtipper/cowlection/Cowlection.java b/src/main/java/de/cowtipper/cowlection/Cowlection.java index 2d7a988..3709172 100644 --- a/src/main/java/de/cowtipper/cowlection/Cowlection.java +++ b/src/main/java/de/cowtipper/cowlection/Cowlection.java @@ -14,6 +14,7 @@ import de.cowtipper.cowlection.listener.ChatListener; import de.cowtipper.cowlection.listener.PlayerListener; import de.cowtipper.cowlection.util.ChatHelper; import de.cowtipper.cowlection.util.VersionChecker; +import net.minecraft.client.Minecraft; import net.minecraft.client.settings.KeyBinding; import net.minecraftforge.client.ClientCommandHandler; import net.minecraftforge.common.MinecraftForge; @@ -39,8 +40,8 @@ public class Cowlection { public static final String GITURL = "@GITURL@"; public static KeyBinding[] keyBindings; private static Cowlection instance; - private File configDir; private File modsDir; + private File modOutDir; private MooConfig config; private CredentialStorage moo; private FriendsHandler friendsHandler; @@ -59,7 +60,7 @@ public class Cowlection { chatHelper = new ChatHelper(); - this.configDir = new File(e.getModConfigurationDirectory(), MODID + File.separatorChar); + File configDir = new File(e.getModConfigurationDirectory(), MODID + File.separatorChar); if (!configDir.exists()) { configDir.mkdirs(); } @@ -93,6 +94,7 @@ public class Cowlection { public void postInit(FMLPostInitializationEvent e) { versionChecker = new VersionChecker(this); playerCache = new PlayerCache(); + modOutDir = new File(Minecraft.getMinecraft().mcDataDir, Cowlection.MODID.toLowerCase() + "_out"); } public MooConfig getConfig() { @@ -147,14 +149,18 @@ public class Cowlection { return chestTracker; } - public File getConfigDirectory() { - return configDir; - } - public File getModsDirectory() { return modsDir; } + public File getModOutDirectory() { + if (!modOutDir.exists() && !modOutDir.mkdirs()) { + // dir didn't exist and couldn't be created + return null; + } + return modOutDir; + } + public Logger getLogger() { return logger; } diff --git a/src/main/java/de/cowtipper/cowlection/command/MooCommand.java b/src/main/java/de/cowtipper/cowlection/command/MooCommand.java index 2849666..8bab5ee 100644 --- a/src/main/java/de/cowtipper/cowlection/command/MooCommand.java +++ b/src/main/java/de/cowtipper/cowlection/command/MooCommand.java @@ -14,7 +14,6 @@ import de.cowtipper.cowlection.config.gui.MooConfigGui; import de.cowtipper.cowlection.data.*; import de.cowtipper.cowlection.data.HySkyBlockStats.Profile.Pet; import de.cowtipper.cowlection.handler.DungeonCache; -import de.cowtipper.cowlection.listener.skyblock.DungeonsListener; import de.cowtipper.cowlection.listener.skyblock.DungeonsPartyListener; import de.cowtipper.cowlection.search.GuiSearch; import de.cowtipper.cowlection.util.*; @@ -139,7 +138,7 @@ public class MooCommand extends CommandBase { main.getConfig().theyOpenedTheConfigGui(); displayGuiScreen(new MooConfigGui(buildString(args, 1))); } else if (args[0].equalsIgnoreCase("search")) { - displayGuiScreen(new GuiSearch(main.getConfigDirectory(), CommandBase.buildString(args, 1))); + displayGuiScreen(new GuiSearch(CommandBase.buildString(args, 1))); } else if (args[0].equalsIgnoreCase("guiscale")) { handleGuiScale(args); } else if (args[0].equalsIgnoreCase("rr")) { diff --git a/src/main/java/de/cowtipper/cowlection/config/MooConfig.java b/src/main/java/de/cowtipper/cowlection/config/MooConfig.java index ea980d0..f636559 100644 --- a/src/main/java/de/cowtipper/cowlection/config/MooConfig.java +++ b/src/main/java/de/cowtipper/cowlection/config/MooConfig.java @@ -60,6 +60,7 @@ public class MooConfig { private static final String CATEGORY_LOGS_SEARCH = "logssearch"; public static String[] logsDirs; private static String defaultStartDate; + private static int maxLogFileSize; // Category: Notifications public static boolean doUpdateCheck; public static boolean showBestFriendNotifications; @@ -85,6 +86,7 @@ public class MooConfig { public static String[] tooltipAuctionHousePriceEachEnchantments; private static String auctionHouseMarkEndedAuctions; public static String bazaarSellAllOrder; + public static String bazaarSellAllOrderAscDesc; private static String bazaarConnectGraphsNodes; public static int bazaarConnectGraphsLineWidth; public static String bestiaryOverviewOrder; @@ -95,9 +97,11 @@ public class MooConfig { // Category: SkyBlock Dungeons private static String showItemQualityAndFloor; private static String dungItemQualityPos; + public static boolean dungItemQualityShortenNonRandomized; public static boolean dungItemHideGearScore; public static int dungItemToolTipToggleKeyBinding; public static boolean dungSendPerformanceOnDeath; + public static boolean dungSendPerformanceOnEndScreen; public static boolean dungOverlayEnabled; public static int dungOverlayPositionX; public static int dungOverlayPositionY; @@ -117,6 +121,7 @@ public class MooConfig { private static String dungMarkPartiesWithHealer; private static String dungMarkPartiesWithMage; private static String dungMarkPartiesWithTank; + public static boolean dungSendWrongFloorWarning; private static Configuration cfg = null; private static final List configCategories = new ArrayList<>(); @@ -310,9 +315,12 @@ public class MooConfig { 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]))$")); + Property propMaxLogFileSize = subCat.addConfigEntry(cfg.get(CATEGORY_LOGS_SEARCH, + "maxLogFileSize", 2048, "Max log file size (in KB)?", 50, 10000)); logSearchProperties = new ArrayList<>(); logSearchProperties.add(propLogsDirs); logSearchProperties.add(propDefaultStartDate); + logSearchProperties.add(propMaxLogFileSize); // Category: Notifications configCat = new MooConfigCategory("Notifications", "notifications"); @@ -443,6 +451,9 @@ public class MooConfig { "bazaarSellAllOrder", "price (sum)", "Bazaar: sell all order", new String[]{"price (sum)", "item amount", "unordered", "price (each)"}), new MooConfigPreview(MooConfigPreview.createDemoItem("chest", "§aSell Inventory Now", new String[]{"§7Instantly sell anything in", "§7your inventory that can be", "§7sold on the Bazaar.", "", " §a1§7x §aEnchanted Leather §7for §65,263.1 coins", " §a42§7x §fLeather §7for §6436.8 coins", " §a2§7x §fRabbit Hide §7for §642.0 coins", " §a79§7x §fRaw Beef §7for §6450.3 coins", " §a16§7x §aEnchanted Raw Beef §7for §69,867.2 coins", "", "§7You earn: §615,698 coins", "", "§eClick to sell!"}, Collections.emptyMap()))); + Property propBazaarSellAllOrderAscDesc = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), + "bazaarSellAllOrderAscDesc", "low → high", "Bazaar: sell all order asc/desc", new String[]{"low → high", "high → low"})); + 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│", @@ -506,6 +517,9 @@ public class MooConfig { 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 propDungItemQualityShortenNonRandomized = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), + "dungItemQualityShortenNonRandomized", false, "Shorten item quality for non-randomized items?")); + Property propDungItemHideGearScore = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungItemHideGearScore", false, "Hide Gear Score?")); @@ -523,6 +537,9 @@ public class MooConfig { Property propDungSendPerformanceOnDeath = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungSendPerformanceOnDeath", true, "Send dungeon performance after a player died?")); + Property propDungSendPerformanceOnEndScreen = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), + "dungSendPerformanceOnEndScreen", true, "Send dungeon performance on end screen?")); + Property propDungOverlayEnabled = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungOverlayEnabled", true, "Enable Dungeon performance overlay?")); @@ -624,6 +641,9 @@ public class MooConfig { "dungMarkPartiesWithTank", "do not mark", "Mark parties with Tank class?", new String[]{"always", "if duplicated", "do not mark"}), new MooConfigPreview(DataHelper.DungeonClass.TANK)); + Property propDungSendWrongFloorWarning = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), + "dungSendWrongFloorWarning", true, "Send warning when entering wrong floor?")); + boolean modifiedMooCmdAlias = false; String mooCmdAliasPreChange = mooCmdAlias; boolean modifiedTabCompletableCommandsList = false; @@ -642,6 +662,7 @@ public class MooConfig { tabCompletableNamesCommands = propTabCompletableNamesCommands.getStringList(); logsDirs = propLogsDirs.getStringList(); defaultStartDate = propDefaultStartDate.getString().trim(); + maxLogFileSize = propMaxLogFileSize.getInt(); // Category: Notifications doUpdateCheck = propDoUpdateCheck.getBoolean(); showBestFriendNotifications = propShowBestFriendNotifications.getBoolean(); @@ -667,6 +688,7 @@ public class MooConfig { tooltipAuctionHousePriceEachEnchantments = propTooltipAuctionHousePriceEachEnchantments.getStringList(); auctionHouseMarkEndedAuctions = propAuctionHouseMarkEndedAuctions.getString(); bazaarSellAllOrder = propBazaarSellAllOrder.getString(); + bazaarSellAllOrderAscDesc = propBazaarSellAllOrderAscDesc.getString(); bazaarConnectGraphsNodes = propBazaarConnectGraphsNodes.getString(); bazaarConnectGraphsLineWidth = propBazaarConnectGraphsLineWidth.getInt(); bestiaryOverviewOrder = propBestiaryOverviewOrder.getString(); @@ -676,9 +698,11 @@ public class MooConfig { // Category: SkyBlock Dungeons showItemQualityAndFloor = propShowItemQualityAndFloor.getString(); dungItemQualityPos = propDungItemQualityPos.getString(); + dungItemQualityShortenNonRandomized = propDungItemQualityShortenNonRandomized.getBoolean(); dungItemHideGearScore = propDungItemHideGearScore.getBoolean(); dungItemToolTipToggleKeyBinding = propDungItemToolTipToggleKeyBinding.getInt(); dungSendPerformanceOnDeath = propDungSendPerformanceOnDeath.getBoolean(); + dungSendPerformanceOnEndScreen = propDungSendPerformanceOnEndScreen.getBoolean(); dungOverlayEnabled = propDungOverlayEnabled.getBoolean(); dungOverlayPositionX = propDungOverlayPositionX.getInt(); dungOverlayPositionY = propDungOverlayPositionY.getInt(); @@ -698,6 +722,7 @@ public class MooConfig { dungMarkPartiesWithHealer = propDungMarkPartiesWithHealer.getString(); dungMarkPartiesWithMage = propDungMarkPartiesWithMage.getString(); dungMarkPartiesWithTank = propDungMarkPartiesWithTank.getString(); + dungSendWrongFloorWarning = propDungSendWrongFloorWarning.getBoolean(); if (!hasOpenedConfigGui && (!propDungOverlayEnabled.isDefault() || !propDungOverlayPositionX.isDefault() || !propDungOverlayPositionY.isDefault() || !propDungOverlayGuiScale.isDefault())) { // player hasn't opened config gui yet and but already moved the dungeon overlay @@ -725,6 +750,7 @@ public class MooConfig { propTabCompletableNamesCommands.set(tabCompletableNamesCommands); propLogsDirs.set(logsDirs); propDefaultStartDate.set(defaultStartDate); + propMaxLogFileSize.set(maxLogFileSize); // Category: Notifications propDoUpdateCheck.set(doUpdateCheck); propShowBestFriendNotifications.set(showBestFriendNotifications); @@ -750,6 +776,7 @@ public class MooConfig { propTooltipAuctionHousePriceEachEnchantments.set(tooltipAuctionHousePriceEachEnchantments); propAuctionHouseMarkEndedAuctions.set(auctionHouseMarkEndedAuctions); propBazaarSellAllOrder.set(bazaarSellAllOrder); + propBazaarSellAllOrderAscDesc.set(bazaarSellAllOrderAscDesc); propBazaarConnectGraphsNodes.set(bazaarConnectGraphsNodes); propBazaarConnectGraphsLineWidth.set(bazaarConnectGraphsLineWidth); propBestiaryOverviewOrder.set(bestiaryOverviewOrder); @@ -759,9 +786,11 @@ public class MooConfig { // Category: SkyBlock Dungeons propShowItemQualityAndFloor.set(showItemQualityAndFloor); propDungItemQualityPos.set(dungItemQualityPos); + propDungItemQualityShortenNonRandomized.set(dungItemQualityShortenNonRandomized); propDungItemHideGearScore.set(dungItemHideGearScore); propDungItemToolTipToggleKeyBinding.set(dungItemToolTipToggleKeyBinding); propDungSendPerformanceOnDeath.set(dungSendPerformanceOnDeath); + propDungSendPerformanceOnEndScreen.set(dungSendPerformanceOnEndScreen); propDungOverlayEnabled.set(dungOverlayEnabled); propDungOverlayPositionX.set(dungOverlayPositionX); propDungOverlayPositionY.set(dungOverlayPositionY); @@ -781,6 +810,7 @@ public class MooConfig { propDungMarkPartiesWithHealer.set(dungMarkPartiesWithHealer); propDungMarkPartiesWithMage.set(dungMarkPartiesWithMage); propDungMarkPartiesWithTank.set(dungMarkPartiesWithTank); + propDungSendWrongFloorWarning.set(dungSendWrongFloorWarning); if (saveToFile && cfg.hasChanged()) { boolean isPlayerIngame = Minecraft.getMinecraft().thePlayer != null; @@ -870,6 +900,13 @@ public class MooConfig { return logsDirs.toArray(new String[]{}); } + /** + * @return max log file size in Bytes + */ + public static long getMaxLogFileSize() { + return maxLogFileSize * 1024L; + } + // Category: General public static Setting getConfigGuiExplanationsDisplay() { return Setting.get(configGuiExplanations); diff --git a/src/main/java/de/cowtipper/cowlection/config/gui/MooConfigCategoryScrolling.java b/src/main/java/de/cowtipper/cowlection/config/gui/MooConfigCategoryScrolling.java index dedf163..a3b28e6 100644 --- a/src/main/java/de/cowtipper/cowlection/config/gui/MooConfigCategoryScrolling.java +++ b/src/main/java/de/cowtipper/cowlection/config/gui/MooConfigCategoryScrolling.java @@ -89,7 +89,7 @@ public class MooConfigCategoryScrolling extends GuiListExtended { // add control buttons to navigate to other guis if ("Other settings".equals(subCategory.getDisplayName())) { this.listEntries.add(new GuiSwitchEntry("gotoKeyBindings", "Controls", () -> mc.displayGuiScreen(new GuiControls(MooConfigCategoryScrolling.this.parent, mc.gameSettings)))); - this.listEntries.add(new GuiSwitchEntry("gotoLogSearchConfig", "Log Search", () -> mc.displayGuiScreen(new GuiSearch(Cowlection.getInstance().getConfigDirectory(), "")))); + this.listEntries.add(new GuiSwitchEntry("gotoLogSearchConfig", "Log Search", () -> mc.displayGuiScreen(new GuiSearch("")))); continue; // don't add properties to main config gui } @@ -215,7 +215,7 @@ public class MooConfigCategoryScrolling extends GuiListExtended { if (labelWidth > this.maxListLabelWidth) { this.maxListLabelWidth = labelWidth; } - this.listEntries.add(new GuiSwitchEntry("gotoLogSearchConfig", "Log Search", () -> mc.displayGuiScreen(new GuiSearch(Cowlection.getInstance().getConfigDirectory(), "")))); + this.listEntries.add(new GuiSwitchEntry("gotoLogSearchConfig", "Log Search", () -> mc.displayGuiScreen(new GuiSearch("")))); hasLogSearchBeenAdded = true; } else if (hasLogSearchBeenAdded) { // already added the replacement-entry, thus don't increase entry counter diff --git a/src/main/java/de/cowtipper/cowlection/listener/skyblock/DungeonsListener.java b/src/main/java/de/cowtipper/cowlection/listener/skyblock/DungeonsListener.java index 9ae5d7e..009d694 100644 --- a/src/main/java/de/cowtipper/cowlection/listener/skyblock/DungeonsListener.java +++ b/src/main/java/de/cowtipper/cowlection/listener/skyblock/DungeonsListener.java @@ -31,7 +31,8 @@ import net.minecraftforge.client.event.GuiScreenEvent; import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.common.util.Constants; import net.minecraftforge.event.entity.player.ItemTooltipEvent; -import net.minecraftforge.fml.common.eventhandler.*; +import net.minecraftforge.fml.common.eventhandler.EventPriority; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.TickEvent; import net.minecraftforge.fml.relauncher.Side; import org.lwjgl.input.Mouse; @@ -160,7 +161,11 @@ public class DungeonsListener { hasCustomGearScore = true; } if (!hasCustomGearScore) { - customGearScore.append(EnumChatFormatting.LIGHT_PURPLE).append("100%").append(EnumChatFormatting.DARK_GRAY).append(" (never has randomized stats)"); + if (MooConfig.dungItemQualityShortenNonRandomized) { + customGearScore.append(EnumChatFormatting.DARK_PURPLE).append("100%"); + } else { + customGearScore.append(EnumChatFormatting.LIGHT_PURPLE).append("100%").append(EnumChatFormatting.DARK_GRAY).append(" (never has randomized stats)"); + } } if (showItemQualityAndFloor) { if (MooConfig.isDungItemQualityAtTop()) { @@ -470,23 +475,23 @@ public class DungeonsListener { if (text.startsWith("[NPC] Mort: ")) { // Mort said something, probably entered dungeons main.getDungeonCache().onDungeonEnterOrLeave(true); - return; - } - Matcher dungeonEnteredMatcher = DUNGEON_ENTERED_DUNGEON.matcher(text); - if (dungeonEnteredMatcher.matches()) { - String floor = dungeonEnteredMatcher.group(1) != null ? "Entrance" : dungeonEnteredMatcher.group(2); - if (floor == null) { - // this shouldn't ever happen: neither a floor nor the entrance was entered - return; - } - String queuedFloor = main.getDungeonCache().getQueuedFloor(); - if (queuedFloor != null && !queuedFloor.equals(floor)) { - // queued and entered dungeon floors are different! - new TickDelay(() -> { - String attentionSeeker = "" + EnumChatFormatting.LIGHT_PURPLE + EnumChatFormatting.OBFUSCATED + "#"; - main.getChatHelper().sendMessage(EnumChatFormatting.RED, attentionSeeker + EnumChatFormatting.RED + " You entered dungeon floor " + EnumChatFormatting.DARK_RED + floor + EnumChatFormatting.RED + " but originally queued for floor " + EnumChatFormatting.DARK_RED + queuedFloor + " " + attentionSeeker); - Minecraft.getMinecraft().thePlayer.playSound("mob.cow.hurt", Minecraft.getMinecraft().gameSettings.getSoundLevel(SoundCategory.MASTER), 1); - }, 100); + } else if (MooConfig.dungSendWrongFloorWarning) { + Matcher dungeonEnteredMatcher = DUNGEON_ENTERED_DUNGEON.matcher(text); + if (dungeonEnteredMatcher.matches()) { + String floor = dungeonEnteredMatcher.group(1) != null ? "Entrance" : dungeonEnteredMatcher.group(2); + if (floor == null) { + // this shouldn't ever happen: neither a floor nor the entrance was entered + return; + } + String queuedFloor = main.getDungeonCache().getQueuedFloor(); + if (queuedFloor != null && !queuedFloor.equals(floor)) { + // queued and entered dungeon floors are different! + new TickDelay(() -> { + String attentionSeeker = "" + EnumChatFormatting.LIGHT_PURPLE + EnumChatFormatting.OBFUSCATED + "#"; + main.getChatHelper().sendMessage(EnumChatFormatting.RED, attentionSeeker + EnumChatFormatting.RED + " You entered dungeon floor " + EnumChatFormatting.DARK_RED + floor + EnumChatFormatting.RED + " but originally queued for floor " + EnumChatFormatting.DARK_RED + queuedFloor + " " + attentionSeeker); + Minecraft.getMinecraft().thePlayer.playSound("mob.cow.hurt", Minecraft.getMinecraft().gameSettings.getSoundLevel(SoundCategory.MASTER), 1); + }, 100); + } } } return; @@ -504,7 +509,9 @@ public class DungeonsListener { main.getDungeonCache().revivedPlayer(dungeonRevivedMatcher.group(1)); } else if (text.trim().equals("> EXTRA STATS <")) { // dungeon "end screen" - new TickDelay(() -> main.getDungeonCache().sendDungeonPerformance(), 5); + if (MooConfig.dungSendPerformanceOnEndScreen) { + new TickDelay(() -> main.getDungeonCache().sendDungeonPerformance(), 5); + } } else if (text.startsWith("PUZZLE FAIL!")) { // Skill: failed puzzle main.getDungeonCache().addFailedPuzzle(text); diff --git a/src/main/java/de/cowtipper/cowlection/listener/skyblock/SkyBlockListener.java b/src/main/java/de/cowtipper/cowlection/listener/skyblock/SkyBlockListener.java index a28905c..432489b 100644 --- a/src/main/java/de/cowtipper/cowlection/listener/skyblock/SkyBlockListener.java +++ b/src/main/java/de/cowtipper/cowlection/listener/skyblock/SkyBlockListener.java @@ -421,7 +421,11 @@ public class SkyBlockListener { numberFormatter.setMaximumFractionDigits(1); List toolTip = e.toolTip; int startIndex = 1337; - TreeMultimap sellEntries = TreeMultimap.create(Ordering.natural().reverse(), Ordering.natural()); + Ordering tooltipOrdering = Ordering.natural(); + if("high → low".equals(MooConfig.bazaarSellAllOrderAscDesc)) { + tooltipOrdering = tooltipOrdering.reverse(); + } + TreeMultimap sellEntries = TreeMultimap.create(tooltipOrdering, Ordering.natural()); for (int i = 0; i < toolTip.size(); i++) { Matcher bazaarSellMatcher = BAZAAR_SELL_ALL_PATTERN.matcher(toolTip.get(i)); if (bazaarSellMatcher.matches()) { diff --git a/src/main/java/de/cowtipper/cowlection/search/GuiSearch.java b/src/main/java/de/cowtipper/cowlection/search/GuiSearch.java index 21059ca..7e721a9 100644 --- a/src/main/java/de/cowtipper/cowlection/search/GuiSearch.java +++ b/src/main/java/de/cowtipper/cowlection/search/GuiSearch.java @@ -23,7 +23,6 @@ import net.minecraftforge.fml.client.config.IConfigElement; import net.minecraftforge.fml.relauncher.ReflectionHelper; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; -import org.apache.commons.lang3.tuple.ImmutableTriple; import org.lwjgl.input.Keyboard; import java.awt.*; @@ -79,10 +78,11 @@ public class GuiSearch extends GuiScreen { private boolean isSearchInProgress; private String analyzedFiles; private String analyzedFilesWithHits; + private String skippedFiles; private boolean areEntriesSearchResults; - public GuiSearch(File configDirectory, String initialSearchQuery) { - this.mcLogOutputFile = new File(configDirectory, "mc-log.txt"); + public GuiSearch(String initialSearchQuery) { + this.mcLogOutputFile = new File(Cowlection.getInstance().getModOutDirectory(), "cowlection-mc-log-search-temp.txt"); try { mcLogOutputFile.createNewFile(); } catch (IOException e) { @@ -287,19 +287,21 @@ public class GuiSearch extends GuiScreen { executorService.execute(() -> { try { - ImmutableTriple> searchResultsData = new LogFilesSearcher().searchFor(this.fieldSearchQuery.getText(), checkboxChatOnly.isChecked(), checkboxMatchCase.isChecked(), checkboxRemoveFormatting.isChecked(), dateStart, dateEnd); - this.searchResults = searchResultsData.right; - this.analyzedFiles = "Analyzed files: " + EnumChatFormatting.WHITE + searchResultsData.left; - this.analyzedFilesWithHits = "Files with hits: " + EnumChatFormatting.WHITE + searchResultsData.middle; + LogSearchResults searchResultsData = new LogFilesSearcher().searchFor(this.fieldSearchQuery.getText(), checkboxChatOnly.isChecked(), checkboxMatchCase.isChecked(), checkboxRemoveFormatting.isChecked(), dateStart, dateEnd); + this.searchResults = searchResultsData.getSortedSearchResults(); + this.analyzedFiles = "Analyzed files: " + EnumChatFormatting.WHITE + searchResultsData.getAnalyzedFiles(); + this.analyzedFilesWithHits = "Files with hits: " + EnumChatFormatting.WHITE + searchResultsData.getAnalyzedFilesWithHits(); + this.skippedFiles = "Skipped files: " + EnumChatFormatting.WHITE + searchResultsData.getSkippedFiles(); if (this.searchResults.isEmpty()) { this.searchResults.add(new LogEntry(EnumChatFormatting.ITALIC + "No results")); areEntriesSearchResults = false; } else { - areEntriesSearchResults = true; + areEntriesSearchResults = searchResultsData.getAnalyzedFiles() != 0; } } catch (IOException e) { if (e.getStackTrace().length > 0) { searchResults.add(new LogEntry(StringUtils.replaceEach(ExceptionUtils.getStackTrace(e), new String[]{"\t", "\r\n"}, new String[]{" ", "\n"}))); + areEntriesSearchResults = false; } } Minecraft.getMinecraft().addScheduledTask(() -> { @@ -319,6 +321,9 @@ public class GuiSearch extends GuiScreen { this.searchResults.add(new LogEntry("" + EnumChatFormatting.GOLD + EnumChatFormatting.BOLD + "Initial setup/configuration " + EnumChatFormatting.GRAY + EnumChatFormatting.ITALIC + "'Open Settings' (top right corner)")); this.searchResults.add(new LogEntry(EnumChatFormatting.GOLD + " 1) " + EnumChatFormatting.RESET + "Configure directories that should be scanned for log files (\"Directories with Minecraft log files\")")); this.searchResults.add(new LogEntry(EnumChatFormatting.GOLD + " 2) " + EnumChatFormatting.RESET + "Set default starting date (\"Start date for log file search\")")); + this.searchResults.add(new LogEntry(" ‣ can be a number (e.g. \"3\" means \"start searching 3 months ago\")")); + this.searchResults.add(new LogEntry(" ‣ or alternatively a fixed date (yyyy-mm-dd)")); + this.searchResults.add(new LogEntry(EnumChatFormatting.GOLD + " 3) " + EnumChatFormatting.RESET + "optional: change the maximum allowed log file size to be searched, but note that each log file must be unzipped before it can be analyzed, which can make the log file search take significantly longer for large files")); this.searchResults.add(new LogEntry("" + EnumChatFormatting.GOLD + EnumChatFormatting.BOLD + "Performing a search " + EnumChatFormatting.GRAY + EnumChatFormatting.ITALIC + "/moo search [initial search term]")); this.searchResults.add(new LogEntry(EnumChatFormatting.GOLD + " 1) " + EnumChatFormatting.RESET + "Enter search term")); this.searchResults.add(new LogEntry(EnumChatFormatting.GOLD + " 2) " + EnumChatFormatting.RESET + "Adjust start and end date")); @@ -360,6 +365,7 @@ public class GuiSearch extends GuiScreen { guiSearchResults.clearResults(); analyzedFiles = null; analyzedFilesWithHits = null; + skippedFiles = null; } else { buttonSearch.displayString = "Search"; } @@ -441,13 +447,16 @@ public class GuiSearch extends GuiScreen { } } else if (areEntriesSearchResults) { if (analyzedFiles != null) { - drawString(fontRendererObj, analyzedFiles, 8, 22, 0xff888888); + drawString(fontRendererObj, analyzedFiles, 8, 15, 0xff888888); } if (analyzedFilesWithHits != null) { - drawString(fontRendererObj, analyzedFilesWithHits, 8, 32, 0xff888888); + drawString(fontRendererObj, analyzedFilesWithHits, 8, 25, 0xff888888); + } + if (skippedFiles != null) { + drawString(fontRendererObj, skippedFiles, 8, 35, 0xff888888); } if (resultsCount != null) { - drawString(fontRendererObj, resultsCount, 8, 48, 0xff888888); + drawString(fontRendererObj, resultsCount, 8, 50, 0xff888888); } } if (errorMessage != null) { diff --git a/src/main/java/de/cowtipper/cowlection/search/LogFilesSearcher.java b/src/main/java/de/cowtipper/cowlection/search/LogFilesSearcher.java index c6b647e..bbfd0fb 100644 --- a/src/main/java/de/cowtipper/cowlection/search/LogFilesSearcher.java +++ b/src/main/java/de/cowtipper/cowlection/search/LogFilesSearcher.java @@ -2,18 +2,15 @@ package de.cowtipper.cowlection.search; import de.cowtipper.cowlection.config.MooConfig; import net.minecraft.util.EnumChatFormatting; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.ImmutableTriple; import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.time.*; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -32,9 +29,9 @@ class LogFilesSearcher { private static final Pattern LOG4J_PATTERN = Pattern.compile("^\\[(?[\\d]{2}):(?[\\d]{2}):(?[\\d]{2})] \\[(?[^/]+)/(?[A-Z]+)]:(? \\[CHAT])? (?.*)$"); private int analyzedFilesWithHits = 0; - ImmutableTriple> searchFor(String searchQuery, boolean chatOnly, boolean matchCase, boolean removeFormatting, LocalDate dateStart, LocalDate dateEnd) throws IOException { - AtomicInteger foundLogs = new AtomicInteger(); - List searchResults = Collections.synchronizedList(new ArrayList<>()); + LogSearchResults searchFor(String searchQuery, boolean chatOnly, boolean matchCase, boolean removeFormatting, LocalDate dateStart, LocalDate dateEnd) throws IOException { + LogSearchResults logSearchResults = new LogSearchResults(); + long fileSizeLimit = MooConfig.getMaxLogFileSize(); for (String logsDirPath : MooConfig.logsDirs) { File logsDir = new File(logsDirPath); if (!logsDir.exists() || !logsDir.isDirectory()) { @@ -55,15 +52,25 @@ class LogFilesSearcher { LocalDate fileLocalDate = LocalDate.of(Integer.parseInt(fileNameMatcher.group(1)), Integer.parseInt(fileNameMatcher.group(2)), Integer.parseInt(fileNameMatcher.group(3))); if (!fileLocalDate.isBefore(dateStart) && !fileLocalDate.isAfter(dateEnd)) { - foundLogs.incrementAndGet(); - searchResults.addAll(analyzeFile(path, true, fileLocalDate, searchQuery, chatOnly, matchCase, removeFormatting)); + if (path.toFile().length() > fileSizeLimit) { + // file too large + logSearchResults.addSkippedFile(); + } else { + logSearchResults.addAnalyzedFile(); + logSearchResults.addSearchResults(analyzeFile(path, true, fileLocalDate, searchQuery, chatOnly, matchCase, removeFormatting)); + } } } } else if (fileName.equals("latest.log")) { LocalDate lastModified = Instant.ofEpochMilli(path.toFile().lastModified()).atZone(ZoneId.systemDefault()).toLocalDate(); if (!lastModified.isBefore(dateStart) && !lastModified.isAfter(dateEnd)) { - foundLogs.incrementAndGet(); - searchResults.addAll(analyzeFile(path, false, lastModified, searchQuery, chatOnly, matchCase, removeFormatting)); + if (path.toFile().length() > fileSizeLimit) { + // file too large + logSearchResults.addSkippedFile(); + } else { + logSearchResults.addAnalyzedFile(); + logSearchResults.addSearchResults(analyzeFile(path, false, lastModified, searchQuery, chatOnly, matchCase, removeFormatting)); + } } } }); @@ -73,12 +80,21 @@ class LogFilesSearcher { } } - if (foundLogs.get() == 0) { - throw new FileNotFoundException(EnumChatFormatting.DARK_RED + "ERROR: No Minecraft log files could be found for the selected date range. Please check if the dates as well as the directories of the log files are set correctly (Log Search ➡ Settings)."); + if (logSearchResults.getAnalyzedFiles() == 0) { + // no files were analyzed + int skippedFileCounter = logSearchResults.getSkippedFiles(); + if (skippedFileCounter > 0) { + throw new FileNotFoundException(EnumChatFormatting.DARK_RED + "ERROR: No Minecraft log files could be found for the selected date range.\n" + + EnumChatFormatting.RED + skippedFileCounter + EnumChatFormatting.DARK_RED + " log files were skipped because they are too large ( >" + FileUtils.byteCountToDisplaySize(MooConfig.getMaxLogFileSize()) + ").\n" + + EnumChatFormatting.RED + "Please check if the dates as well as the directories of the log files are set correctly (Log Search ➡ Settings [top right corner]).\n" + + EnumChatFormatting.DARK_RED + "You could also increase the maximum allowed log file size to be searched (Log Search ➡ Settings), but note that each file must be unzipped before it can be analyzed, which can make the log file search take significantly longer for large files."); + } else { + throw new FileNotFoundException(EnumChatFormatting.DARK_RED + "ERROR: No Minecraft log files could be found for the selected date range.\n" + + EnumChatFormatting.RED + "Please check if the dates as well as the directories of the log files are set correctly (Log Search ➡ Settings [top right corner])."); + } } else { - List sortedSearchResults = searchResults - .stream().sorted(Comparator.comparing(LogEntry::getTime)).collect(Collectors.toList()); - return new ImmutableTriple<>(foundLogs.get(), analyzedFilesWithHits, sortedSearchResults); + logSearchResults.setAnalyzedFilesWithHits(analyzedFilesWithHits); + return logSearchResults; } } diff --git a/src/main/java/de/cowtipper/cowlection/search/LogSearchResults.java b/src/main/java/de/cowtipper/cowlection/search/LogSearchResults.java new file mode 100644 index 0000000..2b7ce80 --- /dev/null +++ b/src/main/java/de/cowtipper/cowlection/search/LogSearchResults.java @@ -0,0 +1,53 @@ +package de.cowtipper.cowlection.search; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +public class LogSearchResults { + private final AtomicInteger analyzedFiles; + private final AtomicInteger skippedFiles; + private final List searchResults; + private int analyzedFilesWithHits; + + public LogSearchResults() { + this.analyzedFiles = new AtomicInteger(); + this.skippedFiles = new AtomicInteger(); + this.searchResults = Collections.synchronizedList(new ArrayList<>()); + } + + public void addAnalyzedFile() { + analyzedFiles.incrementAndGet(); + } + + public void addSkippedFile() { + skippedFiles.incrementAndGet(); + } + + public void addSearchResults(List newResults) { + searchResults.addAll(newResults); + } + + public void setAnalyzedFilesWithHits(int analyzedFilesWithHits) { + this.analyzedFilesWithHits = analyzedFilesWithHits; + } + + public int getAnalyzedFiles() { + return analyzedFiles.get(); + } + + public int getSkippedFiles() { + return skippedFiles.get(); + } + + public List getSortedSearchResults() { + return searchResults.stream().sorted(Comparator.comparing(LogEntry::getTime)).collect(Collectors.toList()); + } + + public int getAnalyzedFilesWithHits() { + return analyzedFilesWithHits; + } +} diff --git a/src/main/java/de/cowtipper/cowlection/util/Utils.java b/src/main/java/de/cowtipper/cowlection/util/Utils.java index dccf911..f7baba2 100644 --- a/src/main/java/de/cowtipper/cowlection/util/Utils.java +++ b/src/main/java/de/cowtipper/cowlection/util/Utils.java @@ -3,7 +3,6 @@ package de.cowtipper.cowlection.util; import com.mojang.realmsclient.util.Pair; import de.cowtipper.cowlection.Cowlection; import de.cowtipper.cowlection.config.MooConfig; -import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; @@ -405,8 +404,8 @@ public final class Utils { * Based on ScreenShotHelper#getTimestampedPNGFileForDirectory */ static File getTimestampedFileForDirectory(String suffix, String fileType) { - File cowlectionOutPath = new File(Minecraft.getMinecraft().mcDataDir, Cowlection.MODID.toLowerCase() + "_out"); - if (!cowlectionOutPath.exists() && !cowlectionOutPath.mkdirs()) { + File cowlectionOutPath = Cowlection.getInstance().getModOutDirectory(); + if (cowlectionOutPath == null) { // dir didn't exist and couldn't be created return null; } diff --git a/src/main/resources/assets/cowlection/lang/en_US.lang b/src/main/resources/assets/cowlection/lang/en_US.lang index d5bf41f..a5d03f9 100644 --- a/src/main/resources/assets/cowlection/lang/en_US.lang +++ b/src/main/resources/assets/cowlection/lang/en_US.lang @@ -20,6 +20,8 @@ cowlection.config.logsDirs=Directories with Minecraft log files cowlection.config.logsDirs.tooltip=List of directories containing Minecraft logs cowlection.config.defaultStartDate=Start date for log file search §c[see tooltip!] cowlection.config.defaultStartDate.tooltip=§eCan be either a §6number§e (e.g. "§63§e" means "§6start searching 3 months ago§e"),\n§eor alternatively a §6fixed date §e(§6yyyy-mm-dd§e) +cowlection.config.maxLogFileSize=Maximum log file size (in KB) to analyze +cowlection.config.maxLogFileSize.tooltip=§eLog files larger than this value will be skipped/ignored cowlection.config.gotoKeyBindings=Change key bindings cowlection.config.gotoKeyBindings.tooltip=Some key bindings can be found in the 'Cowlection' sub-category cowlection.config.doUpdateCheck=Check for updates @@ -68,6 +70,8 @@ cowlection.config.auctionHouseMarkEndedAuctions=§7AH: §rmark sold/ended/expire cowlection.config.auctionHouseMarkEndedAuctions.tooltip=Add indicators to auctions that...\n ‣ sold: §aSold §ror §aS\n ‣ ended: §aEnded §ror §aE\n ‣ expired: §cExpired §ror §cE cowlection.config.bazaarSellAllOrder=§7Bazaar: §rOrder 'Sell Inventory/Sacks Now' by... cowlection.config.bazaarSellAllOrder.tooltip=Order the Sell All tooltip inside the Bazaar by...\n ‣ price (sum)\n ‣ price (each)\n ‣ item amount\n ‣ or keep it unorderd +cowlection.config.bazaarSellAllOrderAscDesc=§7Bazaar: §rOrder 'Sell Inventory/Sacks Now' from +cowlection.config.bazaarSellAllOrderAscDesc.tooltip=Order the Sell All tooltip inside the Bazaar from low to high, or from high to low? cowlection.config.bazaarConnectGraphsNodes=§7Bazaar: §rconnect the graph nodes cowlection.config.bazaarConnectGraphsNodes.tooltip=Draw a line through the nodes of the bazaar graphs?\n§7§oThis also (tries to) fix the graphs when using the MC unicode font. cowlection.config.bazaarConnectGraphsLineWidth=§7Bazaar: §rline width @@ -84,12 +88,16 @@ cowlection.config.showItemQualityAndFloor=Show item quality + obtained floor cowlection.config.showItemQualityAndFloor.tooltip=Should the item quality (in %%) and the obtained floor be added to the dungeon items' tooltips? cowlection.config.dungItemQualityPos=Item quality + obtained floor position cowlection.config.dungItemQualityPos.tooltip=Position of item quality and obtained floor in dungeon item tooltips +cowlection.config.dungItemQualityShortenNonRandomized=Shorten item quality for non-randomized items? +cowlection.config.dungItemQualityShortenNonRandomized.tooltip=Shortened: §7Item Quality: §5100%%\nUnabridged: §7Item Quality: §d100%% §8(never has randomized stats) cowlection.config.dungItemHideGearScore=Hide 'Gear Score'? cowlection.config.dungItemHideGearScore.tooltip=Should the 'Gear Score' be hidden from dungeon items?\n§eGear Score is a rough estimate of how powerful an item is. §rHowever, it's also affected by reforges, enchantments, and essence upgrades, so two items with identical base stats, but different reforges will have different Gear Scores. cowlection.config.dungItemToolTipToggleKeyBinding=Key binding: Show dungeon item base stats cowlection.config.dungItemToolTipToggleKeyBinding.tooltip=Hold down this key to toggle dungeon item tooltip.\nDisplays the base stats of an item without reforges and without essence upgrades.\n\n§7§odisable key binding: §e§oset key binding to §lESC cowlection.config.dungSendPerformanceOnDeath=Send player deaths overview after each death? cowlection.config.dungSendPerformanceOnDeath.tooltip=Send an overview of the §enumber of deaths of each player §rand minus points due to failed puzzles §eeach time a player dies? +cowlection.config.dungSendPerformanceOnEndScreen=Send player deaths overview at end of dungeon? +cowlection.config.dungSendPerformanceOnEndScreen.tooltip=Send an overview of the §enumber of deaths of each player §rand minus points due to failed puzzles §eat the end of a dungeon? cowlection.config.dungOverlayEnabled=Show overlay inside dungeons? cowlection.config.dungOverlayEnabled.tooltip=Show the performance overlay while inside a dungeon? cowlection.config.dungOverlayPositionX=Overlay x position (⇦/⇨ keys to fine-tune) @@ -128,6 +136,8 @@ cowlection.config.dungMarkPartiesWithMage=Mark parties with Mage class as unidea cowlection.config.dungMarkPartiesWithMage.tooltip=Mark parties with Mage class as unideal (orange background)? cowlection.config.dungMarkPartiesWithTank=Mark parties with Tank class as unideal? cowlection.config.dungMarkPartiesWithTank.tooltip=Mark parties with Tank class as unideal (orange background)? +cowlection.config.dungSendWrongFloorWarning=Warn if queued & entered floor are different +cowlection.config.dungSendWrongFloorWarning.tooltip=Send a warning when entering a dungeons floor which is different from the originally queued floor? cowlection.commands.generic.exception=%s key.cowlection.category=Cowlection key.cowlection.moo=Open Command §8(§7/moo§8) -- cgit