diff options
Diffstat (limited to 'src/main/java')
6 files changed, 137 insertions, 79 deletions
diff --git a/src/main/java/de/cowtipper/cowlection/Cowlection.java b/src/main/java/de/cowtipper/cowlection/Cowlection.java index 23ff2a8..2d7a988 100644 --- a/src/main/java/de/cowtipper/cowlection/Cowlection.java +++ b/src/main/java/de/cowtipper/cowlection/Cowlection.java @@ -80,8 +80,9 @@ public class Cowlection { ClientCommandHandler.instance.registerCommand(new TabCompletableCommand(this, tabCompletableNamesCommand)); } // key bindings - keyBindings = new KeyBinding[1]; - keyBindings[0] = new KeyBinding("key.cowlection.moo", Keyboard.KEY_M, "key.cowlection.category"); + keyBindings = new KeyBinding[2]; + keyBindings[0] = new KeyBinding("key.cowlection.moo", Keyboard.KEY_NONE, "key.cowlection.category"); + keyBindings[1] = new KeyBinding("key.cowlection.waila", Keyboard.KEY_NONE, "key.cowlection.category"); for (KeyBinding keyBinding : keyBindings) { ClientRegistry.registerKeyBinding(keyBinding); diff --git a/src/main/java/de/cowtipper/cowlection/command/MooCommand.java b/src/main/java/de/cowtipper/cowlection/command/MooCommand.java index ceff45d..2849666 100644 --- a/src/main/java/de/cowtipper/cowlection/command/MooCommand.java +++ b/src/main/java/de/cowtipper/cowlection/command/MooCommand.java @@ -822,8 +822,7 @@ public class MooCommand extends CommandBase { NBTTagCompound relevantNbt = tldrInfo(nbt, showAllInfo); BlockPos skullPos = skull.getPos(); relevantNbt.setTag("__position", new NBTTagIntArray(new int[]{skullPos.getX(), skullPos.getY(), skullPos.getZ()})); - GuiScreen.setClipboardString(GsonUtils.toJson(relevantNbt, true)); - main.getChatHelper().sendMessage(EnumChatFormatting.GREEN, "Copied skull data to clipboard."); + Utils.copyToClipboardOrSaveAsFile("skull data", "skull", relevantNbt, true); return; } } else if (te instanceof TileEntitySign) { @@ -833,8 +832,7 @@ public class MooCommand extends CommandBase { nbt.setString("Text" + (lineNr + 1), sign.signText[lineNr].getFormattedText()); nbt.setString("TextUnformatted" + (lineNr + 1), sign.signText[lineNr].getUnformattedText()); } - GuiScreen.setClipboardString(GsonUtils.toJson(nbt, true)); - main.getChatHelper().sendMessage(EnumChatFormatting.GREEN, "Copied sign data to clipboard."); + Utils.copyToClipboardOrSaveAsFile("sign data", "sign", nbt, true); return; } else if (te instanceof TileEntityBanner) { List<String> possiblePatterns = Arrays.asList("b", "bl", "bo", "br", "bri", "bs", "bt", "bts", "cbo", "cr", "cre", "cs", "dls", "drs", "flo", "gra", "hh", "ld", "ls", "mc", "moj", "mr", "ms", "rd", "rs", "sc", "sku", "ss", "tl", "tr", "ts", "tt", "tts", "vh", "lud", "rud", "gru", "hhb", "vhr"); @@ -866,22 +864,18 @@ public class MooCommand extends CommandBase { if (entity instanceof EntityArmorStand) { // looking at non-invisible armor stand (e.g. Minion) EntityArmorStand armorStand = (EntityArmorStand) entity; - copyEntityInfoToClipboard(armorStand, showAllInfo); - main.getChatHelper().sendMessage(EnumChatFormatting.GREEN, "Copied armor stand '" + armorStand.getName() + EnumChatFormatting.GREEN + "' to clipboard."); + copyEntityInfoToClipboard("armor stand '" + armorStand.getName() + EnumChatFormatting.GREEN + "'", "armorstand", armorStand, showAllInfo); return; } else if (entity instanceof EntityOtherPlayerMP) { // looking at NPC or another player EntityOtherPlayerMP otherPlayer = (EntityOtherPlayerMP) entity; - copyEntityInfoToClipboard(otherPlayer, showAllInfo); - main.getChatHelper().sendMessage(EnumChatFormatting.GREEN, "Copied player/npc '" + otherPlayer.getDisplayNameString() + EnumChatFormatting.GREEN + "' to clipboard."); + copyEntityInfoToClipboard("player/npc '" + otherPlayer.getDisplayNameString() + EnumChatFormatting.GREEN + "'", "npc_" + otherPlayer.getDisplayNameString(), otherPlayer, showAllInfo); return; } else if (entity instanceof EntityItemFrame) { EntityItemFrame itemFrame = (EntityItemFrame) entity; - copyEntityInfoToClipboard(itemFrame, showAllInfo); ItemStack displayedItem = itemFrame.getDisplayedItem(); if (displayedItem != null) { - NBTTagCompound nbt = new NBTTagCompound(); if (displayedItem.getItem() == Items.filled_map) { // filled map @@ -897,14 +891,12 @@ public class MooCommand extends CommandBase { } else { displayedItem.writeToNBT(nbt); } - GuiScreen.setClipboardString(GsonUtils.toJson(nbt, true)); - main.getChatHelper().sendMessage(EnumChatFormatting.GREEN, "Copied item in item frame '" + displayedItem.getDisplayName() + EnumChatFormatting.GREEN + "' to clipboard."); + Utils.copyToClipboardOrSaveAsFile("item in item frame '" + displayedItem.getDisplayName() + EnumChatFormatting.GREEN + "'", "itemframe-item_" + displayedItem.getDisplayName(), nbt, true); return; } } else if (entity instanceof EntityLiving) { EntityLiving living = (EntityLiving) entity; - copyEntityInfoToClipboard(living, showAllInfo); - main.getChatHelper().sendMessage(EnumChatFormatting.GREEN, "Copied mob '" + living.getName() + EnumChatFormatting.GREEN + "' to clipboard."); + copyEntityInfoToClipboard("mob '" + living.getName() + EnumChatFormatting.GREEN + "'", "mob_" + living.getName(), living, showAllInfo); return; } break; @@ -934,8 +926,7 @@ public class MooCommand extends CommandBase { entities.appendTag(relevantNbt); } - GuiScreen.setClipboardString(GsonUtils.toJson(entities, true)); - main.getChatHelper().sendMessage(EnumChatFormatting.GREEN, "Copied " + nearbyEntities.size() + " nearby entities to clipboard."); + Utils.copyToClipboardOrSaveAsFile(nearbyEntities.size() + " nearby entities", "entities", entities, true); } else { main.getChatHelper().sendMessage(EnumChatFormatting.RED, "You stare into the void... and see nothing of interest. " + EnumChatFormatting.GRAY + "Try looking at: NPCs, mobs, armor stands, placed skulls, banners, signs, dropped items, item in item frames, or maps on a wall."); } @@ -988,9 +979,9 @@ public class MooCommand extends CommandBase { return relevantNbt; } - private void copyEntityInfoToClipboard(Entity entity, boolean showAllInfo) { + private void copyEntityInfoToClipboard(String what, String fileName, Entity entity, boolean showAllInfo) { NBTTagCompound relevantNbt = extractEntityInfo(entity, showAllInfo); - GuiScreen.setClipboardString(GsonUtils.toJson(relevantNbt, true)); + Utils.copyToClipboardOrSaveAsFile(what, fileName, relevantNbt, true); } private NBTTagCompound tldrInfo(NBTTagCompound nbt, boolean showAllInfo) { @@ -1143,7 +1134,7 @@ public class MooCommand extends CommandBase { .appendSibling(createCmdHelpEntry("stalkskyblock", "Get info of player's SkyBlock stats §d§l⚷")) .appendSibling(createCmdHelpEntry("analyzeChests", "Analyze chests' contents and evaluate potential Bazaar value")) .appendSibling(createCmdHelpEntry("analyzeIsland", "Analyze a SkyBlock private island (inspect minions)")) - .appendSibling(createCmdHelpEntry("waila", "Copy the 'thing' you're looking at")) + .appendSibling(createCmdHelpEntry("waila", "Copy the 'thing' you're looking at (optional keybinding: Minecraft controls > Cowlection)")) .appendSibling(createCmdHelpEntry("dungeon", "SkyBlock Dungeons: display current dungeon performance")) .appendSibling(createCmdHelpEntry("dungeon party", "SkyBlock Dungeons: Shows armor and dungeon info about current party members " + EnumChatFormatting.GRAY + "(alias: " + EnumChatFormatting.WHITE + "/" + getCommandName() + " dp" + EnumChatFormatting.GRAY + ") §d§l⚷")) .appendSibling(createCmdHelpSection(3, "Miscellaneous")) diff --git a/src/main/java/de/cowtipper/cowlection/config/MooConfig.java b/src/main/java/de/cowtipper/cowlection/config/MooConfig.java index 5006953..ea980d0 100644 --- a/src/main/java/de/cowtipper/cowlection/config/MooConfig.java +++ b/src/main/java/de/cowtipper/cowlection/config/MooConfig.java @@ -55,6 +55,7 @@ public class MooConfig { public static boolean fixReplyCmd; public static boolean enableCopyInventory; private static String wailaLevelOfDetail; + private static String copyOrSaveWailaAndInventoryData; public static String[] tabCompletableNamesCommands; private static final String CATEGORY_LOGS_SEARCH = "logssearch"; public static String[] logsDirs; @@ -250,14 +251,13 @@ public class MooConfig { // 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)", + subCat.addExplanations("Display of the explanations for each sub-section marked with §2❢§r", + " ‣ " + EnumChatFormatting.YELLOW + "as tooltip" + EnumChatFormatting.RESET + " = tooltip when hovering over sub-category heading", " ‣ " + 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"})); + "configGuiExplanations", "as tooltip", "Display config settings explanations", + new String[]{"as tooltip", "as text", "hidden"})); // (not visible in config gui: has opened config? or: knows how to move the dungeon overlay?) @@ -284,6 +284,8 @@ public class MooConfig { "enableCopyInventory", false, "Enable copy inventory with CTRL + C?")); Property propWailaLevelOfDetail = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "wailaLevelOfDetail", "main info", "Level of detail of /moo waila", new String[]{"main info", "all info"})); + Property propCopyOrSaveWailaAndInventoryData = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), + "copyOrSaveWailaAndInventoryData", "to clipboard", "Copy or save waila and inventory data?", new String[]{"to clipboard", "save file"})); // Sub-Category: Tab-completable names in commands subCat = configCat.addSubCategory("Tab-completable usernames"); @@ -636,6 +638,7 @@ public class MooConfig { fixReplyCmd = propFixReplyCmd.getBoolean(); enableCopyInventory = propEnableCopyInventory.getBoolean(); wailaLevelOfDetail = propWailaLevelOfDetail.getString(); + copyOrSaveWailaAndInventoryData = propCopyOrSaveWailaAndInventoryData.getString(); tabCompletableNamesCommands = propTabCompletableNamesCommands.getStringList(); logsDirs = propLogsDirs.getStringList(); defaultStartDate = propDefaultStartDate.getString().trim(); @@ -718,6 +721,7 @@ public class MooConfig { propFixReplyCmd.set(fixReplyCmd); propEnableCopyInventory.set(enableCopyInventory); propWailaLevelOfDetail.set(wailaLevelOfDetail); + propCopyOrSaveWailaAndInventoryData.set(copyOrSaveWailaAndInventoryData); propTabCompletableNamesCommands.set(tabCompletableNamesCommands); propLogsDirs.set(logsDirs); propDefaultStartDate.set(defaultStartDate); @@ -886,6 +890,10 @@ public class MooConfig { return "all info".equals(wailaLevelOfDetail); } + public static boolean copyWailaAndInventoryDataToClipboard() { + return "to clipboard".equals(copyOrSaveWailaAndInventoryData); + } + // Category: Notifications /** @@ -1100,7 +1108,7 @@ public class MooConfig { case "always": return ALWAYS; case "as a tooltip": - case "as tooltip ②§f⬛": + case "as tooltip": return TOOLTIP; case "in chat": case "as text": @@ -1112,7 +1120,6 @@ public class MooConfig { return DISABLED; case "on SkyBlock": case "key press": - case "as tooltip ①§0⬛": case "a letter": return SPECIAL; default: diff --git a/src/main/java/de/cowtipper/cowlection/listener/PlayerListener.java b/src/main/java/de/cowtipper/cowlection/listener/PlayerListener.java index 5e1ad0b..5b5a4e6 100644 --- a/src/main/java/de/cowtipper/cowlection/listener/PlayerListener.java +++ b/src/main/java/de/cowtipper/cowlection/listener/PlayerListener.java @@ -6,25 +6,25 @@ import de.cowtipper.cowlection.config.MooConfig; import de.cowtipper.cowlection.event.ApiErrorEvent; import de.cowtipper.cowlection.listener.skyblock.DungeonsListener; import de.cowtipper.cowlection.listener.skyblock.SkyBlockListener; -import de.cowtipper.cowlection.util.AbortableRunnable; -import de.cowtipper.cowlection.util.GsonUtils; -import de.cowtipper.cowlection.util.MooChatComponent; -import de.cowtipper.cowlection.util.TickDelay; +import de.cowtipper.cowlection.util.*; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiChat; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.inventory.GuiChest; +import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.client.gui.inventory.GuiInventory; import net.minecraft.client.multiplayer.WorldClient; import net.minecraft.client.settings.KeyBinding; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.ContainerChest; import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.scoreboard.ScoreObjective; import net.minecraft.util.EnumChatFormatting; +import net.minecraftforge.client.ClientCommandHandler; import net.minecraftforge.client.event.GuiScreenEvent; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.player.PlayerSetSpawnEvent; @@ -55,41 +55,58 @@ public class PlayerListener { if (mc.currentScreen == null && mc.gameSettings.chatVisibility != EntityPlayer.EnumChatVisibility.HIDDEN) { mc.displayGuiScreen(new GuiChat("/moo ")); } + } else if (keyBindings[1].isPressed()) { + Minecraft mc = Minecraft.getMinecraft(); + if (mc.currentScreen == null) { + ClientCommandHandler.instance.executeCommand(mc.thePlayer, "/moo waila"); + } } } @SubscribeEvent public void onKeyboardInput(GuiScreenEvent.KeyboardInputEvent.Pre e) { if (MooConfig.enableCopyInventory && Keyboard.getEventKeyState() && Keyboard.getEventKey() == Keyboard.KEY_C && GuiScreen.isCtrlKeyDown()) { - // ctrl + C - IInventory inventory; - String inventoryName; - if (e.gui instanceof GuiChest) { - // some kind of chest - ContainerChest chestContainer = (ContainerChest) ((GuiChest) e.gui).inventorySlots; - inventory = chestContainer.getLowerChestInventory(); - inventoryName = (inventory.hasCustomName() ? EnumChatFormatting.getTextWithoutFormattingCodes(inventory.getDisplayName().getUnformattedTextForChat()) : inventory.getName()); - } else if (e.gui instanceof GuiInventory) { - // player inventory - inventory = Minecraft.getMinecraft().thePlayer.inventory; - inventoryName = "Player inventory"; + if (GuiScreen.isShiftKeyDown()) { + // ctrl + shift + C + if (e.gui instanceof GuiContainer) { + Slot slotUnderMouse = GuiHelper.getSlotUnderMouse((GuiContainer) e.gui); + if (slotUnderMouse != null && slotUnderMouse.getHasStack()) { + ItemStack itemUnderMouse = slotUnderMouse.getStack(); + NBTTagCompound itemNbt = new NBTTagCompound(); + itemUnderMouse.writeToNBT(itemNbt); + Utils.copyToClipboardOrSaveAsFile(itemUnderMouse.getDisplayName() + EnumChatFormatting.RESET + EnumChatFormatting.GREEN, "item_" + itemUnderMouse.getDisplayName(), itemNbt, false); + } + } } else { - // another gui, abort! - return; - } - NBTTagList items = new NBTTagList(); - for (int slot = 0; slot < inventory.getSizeInventory(); slot++) { - ItemStack item = inventory.getStackInSlot(slot); - if (item != null) { - // slot + item - NBTTagCompound tag = new NBTTagCompound(); - tag.setByte("Slot", (byte) slot); - item.writeToNBT(tag); - items.appendTag(tag); + // ctrl + C + IInventory inventory; + String inventoryName; + if (e.gui instanceof GuiChest) { + // some kind of chest + ContainerChest chestContainer = (ContainerChest) ((GuiChest) e.gui).inventorySlots; + inventory = chestContainer.getLowerChestInventory(); + inventoryName = (inventory.hasCustomName() ? EnumChatFormatting.getTextWithoutFormattingCodes(inventory.getDisplayName().getUnformattedTextForChat()) : inventory.getName()); + } else if (e.gui instanceof GuiInventory) { + // player inventory + inventory = Minecraft.getMinecraft().thePlayer.inventory; + inventoryName = "Player inventory"; + } else { + // another gui, abort! + return; + } + NBTTagList items = new NBTTagList(); + for (int slot = 0; slot < inventory.getSizeInventory(); slot++) { + ItemStack item = inventory.getStackInSlot(slot); + if (item != null) { + // slot + item + NBTTagCompound tag = new NBTTagCompound(); + tag.setByte("Slot", (byte) slot); + item.writeToNBT(tag); + items.appendTag(tag); + } } + Utils.copyToClipboardOrSaveAsFile(items.tagCount() + " items from '" + inventoryName + "'", "inventory_" + inventoryName, items, false); } - GuiScreen.setClipboardString(GsonUtils.toJson(items)); - main.getChatHelper().sendMessage(EnumChatFormatting.GREEN, "Copied " + items.tagCount() + " items from '" + inventoryName + "' to clipboard!"); } } diff --git a/src/main/java/de/cowtipper/cowlection/util/ImageUtils.java b/src/main/java/de/cowtipper/cowlection/util/ImageUtils.java index 1d6820f..f96b439 100644 --- a/src/main/java/de/cowtipper/cowlection/util/ImageUtils.java +++ b/src/main/java/de/cowtipper/cowlection/util/ImageUtils.java @@ -13,8 +13,6 @@ import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; public class ImageUtils { public static int getTierFromTexture(String minionSkinId) { @@ -131,12 +129,10 @@ public class ImageUtils { } } try { - File cowlectionImagePath = new File(Minecraft.getMinecraft().mcDataDir, "cowlection_images"); - if (!cowlectionImagePath.exists() && !cowlectionImagePath.mkdirs()) { - // dir didn't exist and couldn't be created + File imageFile = Utils.getTimestampedFileForDirectory("map", "png"); + if(imageFile == null) { return null; } - File imageFile = getTimestampedPngFileForDirectory(cowlectionImagePath, "map"); ImageIO.write(image, "png", imageFile); return imageFile.getCanonicalFile(); } catch (IOException e) { @@ -179,19 +175,4 @@ public class ImageUtils { return new Color((shadeMul * c.getRed()) / 255, (shadeMul * c.getGreen()) / 255, (shadeMul * c.getBlue()) / 255, c.getAlpha()); } - /** - * Based on ScreenShotHelper#getTimestampedPNGFileForDirectory - */ - private static File getTimestampedPngFileForDirectory(File directory, String prefix) { - String currentDateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss")); - int i = 1; - - while (true) { - File timestampedFile = new File(directory, prefix + "_" + currentDateTime + (i == 1 ? "" : "_" + i) + ".png"); - if (!timestampedFile.exists()) { - return timestampedFile; - } - ++i; - } - } } diff --git a/src/main/java/de/cowtipper/cowlection/util/Utils.java b/src/main/java/de/cowtipper/cowlection/util/Utils.java index 4b5c73c..dccf911 100644 --- a/src/main/java/de/cowtipper/cowlection/util/Utils.java +++ b/src/main/java/de/cowtipper/cowlection/util/Utils.java @@ -1,6 +1,11 @@ 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; import net.minecraft.util.EnumChatFormatting; import org.apache.commons.lang3.StringUtils; @@ -10,9 +15,14 @@ import org.apache.commons.lang3.time.DurationFormatUtils; import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; @@ -365,4 +375,55 @@ public final class Utils { hit = sb.indexOf(search); } } + + public static void copyToClipboardOrSaveAsFile(String what, String fileName, NBTBase data, boolean sortData) { + String nbt = GsonUtils.toJson(data, sortData); + if (MooConfig.copyWailaAndInventoryDataToClipboard()) { + GuiScreen.setClipboardString(nbt); + Cowlection.getInstance().getChatHelper().sendMessage(EnumChatFormatting.GREEN, "Copied " + what + " to clipboard."); + } else { + try { + File target = getTimestampedFileForDirectory(fileName, "json"); + if (target == null) { + return; + } + Files.write(target.toPath(), nbt.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE_NEW); + + File targetNormalized = target.getCanonicalFile(); + + Cowlection.getInstance().getChatHelper().sendMessage(new MooChatComponent("Saved " + what).green() + .appendSibling(new MooChatComponent(" [open file]").gold().setOpenFile(targetNormalized)) + .appendSibling(new MooChatComponent(" [open folder]").darkAqua().setOpenFile(targetNormalized.getParentFile()))); + } catch (IOException | UnsupportedOperationException e) { + e.printStackTrace(); + Cowlection.getInstance().getChatHelper().sendMessage(EnumChatFormatting.RED, "Couldn't save " + what + ": " + e.toString()); + } + } + } + + /** + * 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()) { + // dir didn't exist and couldn't be created + return null; + } + + String currentDateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss")); + String sanitizedSuffix = StringUtils.replaceEach(EnumChatFormatting.getTextWithoutFormattingCodes(suffix).trim(), + // replacement characters from https://stackoverflow.com/a/61448658 + new String[]{"\\", "/", ":", "*", "?", "\"", "<", ">", "|"}, + new String[]{"⧵", "∕", "∶", "∗", "?", "“", "‹", "›", "∣"}); + String baseName = currentDateTime + "_" + sanitizedSuffix; + int i = 1; + while (true) { + File timestampedFile = new File(cowlectionOutPath, baseName + (i == 1 ? "" : "_" + i) + "." + fileType); + if (!timestampedFile.exists()) { + return timestampedFile; + } + ++i; + } + } } |