diff --git a/pom.xml b/pom.xml index 8817812..f3c31f2 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ me.sat7 DynamicShop - 3.16.2 + 3.120.1 jar DynamicShop @@ -170,20 +170,5 @@ 3.2.5 provided - - com.google.code.gson - gson - 2.8.9 - - - org.apache.commons - commons-text - 1.10.0 - - - org.yaml - snakeyaml - 2.0 - diff --git a/src/main/java/me/sat7/dynamicshop/DynaShopAPI.java b/src/main/java/me/sat7/dynamicshop/DynaShopAPI.java index ae8952a..1626017 100644 --- a/src/main/java/me/sat7/dynamicshop/DynaShopAPI.java +++ b/src/main/java/me/sat7/dynamicshop/DynaShopAPI.java @@ -71,7 +71,9 @@ public static void openShopSettingGui(Player player, String shopName) // 상점 로테이트 편집기 public static void OpenRotationEditor(Player player, String shopName) { - DynamicShop.PaidOnlyMsg(player); + RotationEditor uiClass = new RotationEditor(); + Inventory inventory = uiClass.getGui(player, shopName); + UIManager.Open(player, inventory, uiClass); } // 거래화면 생성 및 열기 @@ -123,13 +125,17 @@ public static void openPageEditor(Player player, String shopName, int page) // 로그뷰어 열기 public static void openLogViewer(Player player, String shopName) { - DynamicShop.PaidOnlyMsg(player); + LogViewer uiClass = new LogViewer(); + Inventory inventory = uiClass.getGui(player, shopName); + UIManager.Open(player, inventory, uiClass); } // 재고 시뮬레이터 열기 public static void openStockSimulator(Player player, String shopName) { - DynamicShop.PaidOnlyMsg(player); + StockSimulator uiClass = new StockSimulator(); + Inventory inventory = uiClass.getGui(player, shopName); + UIManager.Open(player, inventory, uiClass); } // 스타트 페이지 @@ -225,7 +231,7 @@ public static ArrayList getShopItems(@NonNull String shopName) if (validateShopName(shopName)) { CustomConfig data = ShopUtil.shopConfigFiles.get(shopName); - + ArrayList list = new ArrayList<>(); for (String s : data.get().getKeys(false)) { diff --git a/src/main/java/me/sat7/dynamicshop/DynamicShop.java b/src/main/java/me/sat7/dynamicshop/DynamicShop.java index 6b17855..86a975b 100644 --- a/src/main/java/me/sat7/dynamicshop/DynamicShop.java +++ b/src/main/java/me/sat7/dynamicshop/DynamicShop.java @@ -40,8 +40,6 @@ import java.io.File; import java.util.*; -import static me.sat7.dynamicshop.utilities.LangUtil.t; - public final class DynamicShop extends JavaPlugin implements Listener { private static Economy econ = null; // 볼트에 물려있는 이코노미 @@ -84,6 +82,7 @@ public static String dsPrefix(Player player) private BukkitTask periodicRepetitiveTask; private BukkitTask saveLogsTask; private BukkitTask cullLogsTask; + private BukkitTask backupTask; private BukkitTask shopSaveTask; private BukkitTask userDataRepetitiveTask; @@ -149,11 +148,11 @@ public static void DebugLog() UIManager.DebugLog(); - //console.sendMessage("---------------------"); + console.sendMessage("---------------------"); - //console.sendMessage("RotationTaskMap: size" + RotationUtil.RotationTaskMap.size()); - //for(Map.Entry entry : RotationUtil.RotationTaskMap.entrySet()) - // console.sendMessage(entry.getKey() + ": " + entry.getValue()); + console.sendMessage("RotationTaskMap: size: " + RotationUtil.RotationTaskMap.size()); + for(Map.Entry entry : RotationUtil.RotationTaskMap.entrySet()) + console.sendMessage(entry.getKey() + ": " + entry.getValue()); console.sendMessage("---------------------"); @@ -195,11 +194,14 @@ private void Init() PeriodicRepetitiveTask(); startSaveLogsTask(); startCullLogsTask(); + StartBackupTask(); StartShopSaveTask(); StartUserDataTask(); hookIntoJobs(); hookIntoPlayerPoints(); InitPapi(); + + RotationUtil.RestartAllRotationTask(); // 완료 console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + " Enabled! :)"); @@ -400,6 +402,19 @@ private void RepeatAction() UIManager.RefreshUI(); } + public void StartBackupTask() + { + if (!ConfigUtil.GetShopYmlBackup_Enable()) + return; + + if (backupTask != null) + { + backupTask.cancel(); + } + long interval = (20L * 60L * (long) ConfigUtil.GetShopYmlBackup_IntervalMinutes()); + backupTask = Bukkit.getScheduler().runTaskTimer(DynamicShop.plugin, ShopUtil::ShopYMLBackup, interval, interval); + } + public void StartShopSaveTask() { if (shopSaveTask != null) @@ -471,6 +486,9 @@ private void makeFolders() File shopFolder = new File(getDataFolder(), "Shop"); shopFolder.mkdir(); // new 하고 같은줄에서 바로 하면 폴더 안만들어짐. + File rotationFolder = new File(getDataFolder(), "Rotation"); + rotationFolder.mkdir(); + File LogFolder = new File(getDataFolder(), "Log"); LogFolder.mkdir(); } @@ -519,13 +537,4 @@ public void onDisable() Bukkit.getScheduler().cancelTasks(this); console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + " Disabled"); } - - public static void PaidOnlyMsg(Player p) - { - TextComponent text = new TextComponent(""); - text.addExtra(DynamicShop.dsPrefix(p) + t(p, "PAID_VERSION.DESC")); - text.addExtra(DynamicShop.CreateLink(t(p, "PAID_VERSION.GET_PREMIUM"), false, ChatColor.WHITE, "https://spigotmc.org/resources/100058")); - - p.spigot().sendMessage(text); - } } diff --git a/src/main/java/me/sat7/dynamicshop/UpdateChecker.java b/src/main/java/me/sat7/dynamicshop/UpdateChecker.java index 2de62e9..1579b95 100644 --- a/src/main/java/me/sat7/dynamicshop/UpdateChecker.java +++ b/src/main/java/me/sat7/dynamicshop/UpdateChecker.java @@ -11,7 +11,7 @@ public class UpdateChecker { - public static final int PROJECT_ID = 65603; + public static final int PROJECT_ID = 65603;//100058; private final JavaPlugin plugin; private final int resourceId; diff --git a/src/main/java/me/sat7/dynamicshop/commands/DeleteShop.java b/src/main/java/me/sat7/dynamicshop/commands/DeleteShop.java index 423e6fb..67f3942 100644 --- a/src/main/java/me/sat7/dynamicshop/commands/DeleteShop.java +++ b/src/main/java/me/sat7/dynamicshop/commands/DeleteShop.java @@ -1,6 +1,7 @@ package me.sat7.dynamicshop.commands; import me.sat7.dynamicshop.files.CustomConfig; +import me.sat7.dynamicshop.utilities.RotationUtil; import me.sat7.dynamicshop.utilities.UserUtil; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -42,6 +43,7 @@ public void RunCMD(String[] args, CommandSender sender) ShopUtil.shopConfigFiles.remove(args[1]); ShopUtil.shopDirty.remove(args[1]); + RotationUtil.OnShopDeleted(args[1]); UserUtil.ClearTradeLimitData(args[1]); sender.sendMessage(DynamicShop.dsPrefix(sender) + t(sender, "MESSAGE.SHOP_DELETED")); diff --git a/src/main/java/me/sat7/dynamicshop/commands/Help.java b/src/main/java/me/sat7/dynamicshop/commands/Help.java index 876175e..30cbbbf 100644 --- a/src/main/java/me/sat7/dynamicshop/commands/Help.java +++ b/src/main/java/me/sat7/dynamicshop/commands/Help.java @@ -55,7 +55,7 @@ public static void showHelp(String helpcode, Player player, String[] args) if (player.hasPermission(P_ADMIN_SHOP_EDIT) || player.hasPermission(P_ADMIN_EDIT_ALL)) { player.sendMessage(" - " + t(player, "HELP.USAGE") - + ": /ds shop "); + + ": /ds shop "); } if (player.hasPermission(P_ADMIN_SHOP_EDIT)) diff --git a/src/main/java/me/sat7/dynamicshop/commands/Reload.java b/src/main/java/me/sat7/dynamicshop/commands/Reload.java index 7cc6b0e..0e35da5 100644 --- a/src/main/java/me/sat7/dynamicshop/commands/Reload.java +++ b/src/main/java/me/sat7/dynamicshop/commands/Reload.java @@ -56,6 +56,8 @@ public void RunCMD(String[] args, CommandSender sender) DynamicShop.plugin.startSaveLogsTask(); DynamicShop.plugin.startCullLogsTask(); + + DynamicShop.plugin.StartBackupTask(); DynamicShop.plugin.StartShopSaveTask(); DynamicShop.plugin.StartUserDataTask(); @@ -65,6 +67,8 @@ public void RunCMD(String[] args, CommandSender sender) LangUtil.setupLangFile(ConfigUtil.GetLanguage()); // ConfigUtil.Load() 보다 밑에 있어야함. + RotationUtil.Reload(); + sender.sendMessage(DynamicShop.dsPrefix(sender) + t(sender, "HELP.RELOADED")); } } diff --git a/src/main/java/me/sat7/dynamicshop/commands/shop/Background.java b/src/main/java/me/sat7/dynamicshop/commands/shop/Background.java index b79e9d1..2701118 100644 --- a/src/main/java/me/sat7/dynamicshop/commands/shop/Background.java +++ b/src/main/java/me/sat7/dynamicshop/commands/shop/Background.java @@ -2,6 +2,11 @@ import me.sat7.dynamicshop.DynamicShop; import me.sat7.dynamicshop.commands.DSCMD; +import me.sat7.dynamicshop.commands.Shop; +import me.sat7.dynamicshop.constants.Constants; +import me.sat7.dynamicshop.files.CustomConfig; +import me.sat7.dynamicshop.utilities.ShopUtil; +import org.bukkit.Material; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -32,8 +37,30 @@ public void RunCMD(String[] args, CommandSender sender) if(!CheckValid(args, sender)) return; - Player player = (Player) sender; - if (player != null) - DynamicShop.PaidOnlyMsg(player); + if (args[3].equalsIgnoreCase("clear")) + { + String shopName = Shop.GetShopName(args); + CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName); + shopData.get().set("Options.background", null); + shopData.save(); + + sender.sendMessage(DynamicShop.dsPrefix(sender) + t(sender, "MESSAGE.CHANGES_APPLIED") + args[2] + " " + args[3]); + + return; + } + + Material mat = Material.getMaterial(args[3].toUpperCase() + "_STAINED_GLASS_PANE"); + if (mat == null) + { + sender.sendMessage(DynamicShop.dsPrefix(sender) + t(sender, "ERR.WRONG_DATATYPE")); + return; + } + + String shopName = Shop.GetShopName(args); + CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName); + shopData.get().set("Options.background", args[3].toUpperCase()); + shopData.save(); + + sender.sendMessage(DynamicShop.dsPrefix(sender) + t(sender, "MESSAGE.CHANGES_APPLIED") + args[2] + " " + args[3]); } } diff --git a/src/main/java/me/sat7/dynamicshop/events/JoinQuit.java b/src/main/java/me/sat7/dynamicshop/events/JoinQuit.java index 6ca6f65..3df02f0 100644 --- a/src/main/java/me/sat7/dynamicshop/events/JoinQuit.java +++ b/src/main/java/me/sat7/dynamicshop/events/JoinQuit.java @@ -31,13 +31,9 @@ public void onPlayerJoin(PlayerJoinEvent e) if (e.getPlayer().hasPermission(P_ADMIN_SHOP_EDIT) || e.getPlayer().hasPermission(P_ADMIN_RELOAD)) { TextComponent text = new TextComponent(""); - text.addExtra(DynamicShop.CreateLink("DShop3", false, ChatColor.DARK_AQUA, UpdateChecker.getResourceUrl())); + text.addExtra(DynamicShop.CreateLink("DShop3 Premium", false, ChatColor.DARK_AQUA, UpdateChecker.getResourceUrl())); text.addExtra(" "); text.addExtra(DynamicShop.CreateLink("Download", false, ChatColor.WHITE, UpdateChecker.getResourceUrl())); - text.addExtra(" "); - text.addExtra(DynamicShop.CreateLink("Premium", false, ChatColor.WHITE, "https://spigotmc.org/resources/100058")); - text.addExtra(" "); - text.addExtra(DynamicShop.CreateLink("Donate", false, ChatColor.WHITE, "https://www.paypal.com/paypalme/7sat")); e.getPlayer().sendMessage(""); e.getPlayer().spigot().sendMessage(text); diff --git a/src/main/java/me/sat7/dynamicshop/events/OnChat.java b/src/main/java/me/sat7/dynamicshop/events/OnChat.java index c2c0049..6cd3459 100644 --- a/src/main/java/me/sat7/dynamicshop/events/OnChat.java +++ b/src/main/java/me/sat7/dynamicshop/events/OnChat.java @@ -9,6 +9,7 @@ import me.sat7.dynamicshop.utilities.UserUtil; import org.bukkit.Bukkit; import org.bukkit.ChatColor; +import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; diff --git a/src/main/java/me/sat7/dynamicshop/guis/ColorPicker.java b/src/main/java/me/sat7/dynamicshop/guis/ColorPicker.java index cdd8d46..b88f75a 100644 --- a/src/main/java/me/sat7/dynamicshop/guis/ColorPicker.java +++ b/src/main/java/me/sat7/dynamicshop/guis/ColorPicker.java @@ -1,6 +1,10 @@ package me.sat7.dynamicshop.guis; import me.sat7.dynamicshop.DynaShopAPI; +import me.sat7.dynamicshop.DynamicShop; +import me.sat7.dynamicshop.files.CustomConfig; +import me.sat7.dynamicshop.utilities.ShopUtil; +import me.sat7.dynamicshop.utilities.UserUtil; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; @@ -40,7 +44,29 @@ public void OnClickUpperInventory(InventoryClickEvent e) if (e.getSlot() == CLOSE) { - DynaShopAPI.openStartPageSettingGui(player, slotIndex); + if (slotIndex == -1) + { + String shopName = UserUtil.userInteractItem.get(player.getUniqueId()).split("/")[0]; + DynaShopAPI.openShopSettingGui(player, shopName); + } + else + { + DynaShopAPI.openStartPageSettingGui(player, slotIndex); + } + } + else if (slotIndex == -1) + { + String shopName = UserUtil.userInteractItem.get(player.getUniqueId()).split("/")[0]; + CustomConfig data = ShopUtil.shopConfigFiles.get(shopName); + + String color = null; + if (e.getCurrentItem() != null && !e.getCurrentItem().getType().isAir()) + color = ChatColor.stripColor(e.getCurrentItem().getItemMeta().getDisplayName()); + + data.get().set("Options.background", color); + data.save(); + + DynaShopAPI.openShopSettingGui(player, shopName); } else if(e.getCurrentItem() != null && !e.getCurrentItem().getType().isAir()) { diff --git a/src/main/java/me/sat7/dynamicshop/guis/ItemPalette.java b/src/main/java/me/sat7/dynamicshop/guis/ItemPalette.java index ecde1be..cf2ad44 100644 --- a/src/main/java/me/sat7/dynamicshop/guis/ItemPalette.java +++ b/src/main/java/me/sat7/dynamicshop/guis/ItemPalette.java @@ -7,8 +7,10 @@ import me.sat7.dynamicshop.DynaShopAPI; import me.sat7.dynamicshop.events.OnChat; import me.sat7.dynamicshop.models.DSItem; +import me.sat7.dynamicshop.utilities.ConfigUtil; import me.sat7.dynamicshop.utilities.ShopUtil; import me.sat7.dynamicshop.utilities.UserUtil; +import me.sat7.dynamicshop.utilities.WorthUtil; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -90,7 +92,7 @@ public Inventory getGui(Player player, int uiSubType, String shopName, int targe if(uiSubType == 0) { if(!paletteList.isEmpty()) - CreateButton(ADD_ALL, Material.YELLOW_STAINED_GLASS_PANE, t(player, "PALETTE.ADD_ALL"), t(player, "PALETTE.ADD_ALL_LORE_LOCKED")); + CreateButton(ADD_ALL, Material.YELLOW_STAINED_GLASS_PANE, t(player, "PALETTE.ADD_ALL"), t(player, "PALETTE.ADD_ALL_LORE")); } // Search Button @@ -219,7 +221,7 @@ public ItemStack getPotionItemStack_deprecated(Material potionMat, PotionType ty potion.setItemMeta(meta); return potion; } - + public ItemStack getPotionItemStack_mc1_20_2_or_newer(Material potionMat, PotionType potionType){ ItemStack potion = new ItemStack(potionMat, 1); PotionMeta meta = (PotionMeta) potion.getItemMeta(); @@ -394,12 +396,6 @@ private void MovePage(boolean isLeft, boolean isRight) private void AddAll(boolean applyRecommend) { - if (applyRecommend) - { - DynamicShop.PaidOnlyMsg(player); - return; - } - if(paletteList.isEmpty()) return; @@ -416,7 +412,33 @@ private void AddAll(boolean applyRecommend) targetSlotIdx = ShopUtil.findEmptyShopSlot(shopName, shopSlotIndex, true); - DSItem temp = new DSItem(itemStack, 1, 1, 0.0001, -1, 10000, 10000); + double worth = 1; + int median = 10000; + + if (applyRecommend) + { + String itemName = itemStack.getType().toString(); + worth = WorthUtil.ccWorth.get().getDouble(itemName); + if (worth == 0) + { + itemName = itemName.replace("-", ""); + itemName = itemName.replace("_", ""); + itemName = itemName.toLowerCase(); + + worth = WorthUtil.ccWorth.get().getDouble(itemName); + } + + if (worth != 0) + { + median = ShopUtil.CalcRecommendedMedian(worth, ConfigUtil.GetNumberOfPlayer()); + } else + { + if (player != null) + player.sendMessage(DynamicShop.dsPrefix(player) + t(player, "ERR.NO_RECOMMEND_DATA") + " : " + itemName); + } + } + + DSItem temp = new DSItem(itemStack, worth, worth, 0.0001, -1, median, median); ShopUtil.addItemToShop(shopName, targetSlotIdx, temp); } } diff --git a/src/main/java/me/sat7/dynamicshop/guis/LogViewer.java b/src/main/java/me/sat7/dynamicshop/guis/LogViewer.java new file mode 100644 index 0000000..8b2aea3 --- /dev/null +++ b/src/main/java/me/sat7/dynamicshop/guis/LogViewer.java @@ -0,0 +1,322 @@ +package me.sat7.dynamicshop.guis; + +import me.sat7.dynamicshop.DynaShopAPI; +import me.sat7.dynamicshop.DynamicShop; +import me.sat7.dynamicshop.utilities.ItemsUtil; +import me.sat7.dynamicshop.utilities.LogUtil; +import me.sat7.dynamicshop.utilities.MathUtil; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; + +import java.util.ArrayList; + +import static me.sat7.dynamicshop.utilities.LangUtil.n; +import static me.sat7.dynamicshop.utilities.LangUtil.t; + +public class LogViewer extends InGameUI +{ + public LogViewer() + { + uiType = InGameUI.UI_TYPE.LogViewer; + } + + private int subState; + private String selectedFileName; + + private final int TOGGLE_EXPAND = 0; + private final int PAGE = 9; + private final int CLOSE = 45; + + private Player player; + private String shopName; + private boolean isCollapsed; + private int currentPage = 1; + private int maxPage = 1; + + ArrayList logData = null; + + public Inventory getGui(Player player, String shopName) + { + this.shopName = shopName; + + this.player = player; + inventory = Bukkit.createInventory(player, 54, t(player, "LOG_VIEWER_TITLE") + "§7 | §8" + shopName); + + CreateFileSelectUI(); + + return inventory; + } + + private void CreateFileSelectUI() + { + ClearUI(); + + ArrayList files = LogUtil.GetLogFileList(shopName); + int slotIdx = 0; + for (String path : files) + { + CreateButton(slotIdx, Material.BOOK, path, t(player, "LOG_VIEWER.FILE_LORE")); + slotIdx++; + if (slotIdx > 45) + break; + } + + CreateCloseButton(player, CLOSE); + } + + private void CreateLogViewerUI() + { + ClearUI(); + + ShowLogData(currentPage); + + CreateToggleButton(); + CreatePageButton(); + + CreateCloseButton(player, CLOSE); + } + + private void CreateToggleButton() + { + String toggleName = isCollapsed ? t(null, "LOG_VIEWER.EXPAND") : t(null, "LOG_VIEWER.COLLAPSE"); + CreateButton(TOGGLE_EXPAND, Material.REDSTONE_BLOCK, toggleName, ""); + } + + private void CreatePageButton() + { + CreateButton(PAGE, InGameUI.GetPageButtonIconMat(), CreatePageButtonName(), CreatePageButtonLore(), currentPage); + } + + private String CreatePageButtonName() + { + String pageString = t(null, "LOG_VIEWER.PAGE_TITLE"); + pageString = pageString.replace("{curPage}", String.valueOf(currentPage)); + pageString = pageString.replace("{maxPage}", String.valueOf(maxPage)); + return pageString; + } + + private String CreatePageButtonLore() + { + return t(null, "LOG_VIEWER.PAGE_LORE"); + } + + private void ShowLogData(int page) + { + if (logData == null) + logData = LogUtil.LoadDataFromCSV(shopName, selectedFileName); + + int dataPerPage = isCollapsed ? 48 : 16; + maxPage = logData.size()/dataPerPage + 1; + int idx = -((page - 1) * dataPerPage); + + for (String s : logData) + { + if (!(idx < dataPerPage && idx >= 0)) + { + idx++; + continue; + } + + int slotIdx = idx; + + if (isCollapsed) + { + slotIdx += (slotIdx/8) + 1; + } else + { + slotIdx++; + if (slotIdx > 8) + slotIdx += 19; + } + + // 0DataIdx, 1User, 2Item, 3Amount, 4Date, 5Time, 6Currency, 7Price + String[] data = s.split(","); + Material mat = Material.getMaterial(data[2]); + if (mat == null) + mat = Material.DIRT; + + Player p = null; + if (!data[1].equals(shopName)) + p = Bukkit.getOfflinePlayer(data[1]).getPlayer(); + + int amount = Integer.parseInt(data[3]); + boolean sell; + Material sellBuy; + + if (amount < 0) + { + sell = true; + sellBuy = Material.GREEN_STAINED_GLASS; + } else + { + sell = false; + sellBuy = Material.RED_STAINED_GLASS; + } + + String itemTitle = "§7#" + data[0]; + ArrayList itemLore = new ArrayList<>(); + + if (sell) + { + itemLore.add("§a" + data[1] + " ▶ " + shopName); + } else + { + itemLore.add("§c" + shopName + " ▶ " + data[1]); + } + + itemLore.add("§f" + ItemsUtil.getBeautifiedName(data[2]) + " x" + Math.abs(amount)); + double price = Double.parseDouble(data[7]); + itemLore.add(t(p,"LOG_VIEWER.PRICE") + n(price) + " (" + n(price / Math.abs(amount)) + "/ea)"); + itemLore.add(" "); + itemLore.add(t(p, "LOG_VIEWER.DATE") + data[4]); + itemLore.add(t(p,"LOG_VIEWER.TIME") + data[5].replace(".", ":")); + itemLore.add(t(p,"LOG_VIEWER.CURRENCY") + data[6]); + + int amountForUI = amount; + if (amountForUI < 0) + amountForUI *= -1; + amountForUI = MathUtil.Clamp(amountForUI, 1, 64); + + if (isCollapsed) + { + CreateButton(slotIdx, mat, itemTitle, itemLore, amountForUI); + ItemStack is = inventory.getItem(slotIdx); + ItemMeta im = is.getItemMeta(); + im.addItemFlags(ItemFlag.HIDE_ATTRIBUTES); + im.addItemFlags(ItemFlag.HIDE_ENCHANTS); + im.addItemFlags(ItemFlag.HIDE_POTION_EFFECTS); + im.addItemFlags(ItemFlag.HIDE_DYE); + is.setItemMeta(im); + } else + { + CreateButton(slotIdx, Material.PLAYER_HEAD, itemTitle, itemLore); + CreateButton(slotIdx + 9, mat, itemTitle, itemLore, amountForUI); + CreateButton(slotIdx + 18, sellBuy, itemTitle, itemLore); + + ItemStack is = inventory.getItem(slotIdx + 9); + ItemMeta im = is.getItemMeta(); + im.addItemFlags(ItemFlag.HIDE_ATTRIBUTES); + im.addItemFlags(ItemFlag.HIDE_ENCHANTS); + im.addItemFlags(ItemFlag.HIDE_POTION_EFFECTS); + im.addItemFlags(ItemFlag.HIDE_DYE); + is.setItemMeta(im); + + int finalSlotIdx = slotIdx; + Player finalP = p; + Bukkit.getScheduler().runTaskAsynchronously(DynamicShop.plugin, () -> LoadAndSetSkin(finalP, finalSlotIdx)); + } + + idx++; + } + } + + private void LoadAndSetSkin(Player p, int idx) + { + if (p == null) + return; + + ItemStack tempIs = inventory.getItem(idx); + SkullMeta meta = (SkullMeta) tempIs.getItemMeta(); + try + { + meta.setOwningPlayer(p); + } catch (Exception ignore) + { + return; + } + + tempIs.setItemMeta(meta); + inventory.setItem(idx, tempIs); + } + + @Override + public void OnClickUpperInventory(InventoryClickEvent e) + { + Player player = (Player) e.getWhoClicked(); + + if (subState == 0) + { + if (e.getSlot() == CLOSE) + { + DynaShopAPI.openShopSettingGui(player, shopName); + } + else if (e.getCurrentItem() != null && e.getCurrentItem().getType() == Material.BOOK) + { + if (e.isLeftClick()) + { + subState = 1; + currentPage = 1; + selectedFileName = e.getCurrentItem().getItemMeta().getDisplayName(); + logData = null; + + CreateLogViewerUI(); + } + else if(e.isRightClick() && e.isShiftClick()) + { + selectedFileName = e.getCurrentItem().getItemMeta().getDisplayName(); + LogUtil.DeleteLogFile(shopName, selectedFileName); + logData = null; + + CreateFileSelectUI(); + } + } + } + else + { + if (e.getSlot() == CLOSE) + { + subState = 0; + + CreateFileSelectUI(); + } else if (e.getSlot() == TOGGLE_EXPAND) + { + isCollapsed = !isCollapsed; + currentPage = 1; + + ClearUI(); + ShowLogData(currentPage); + CreateToggleButton(); + CreatePageButton(); + CreateCloseButton(player, CLOSE); + } + else if (e.getSlot() == PAGE) + { + if (e.isLeftClick()) + { + if (currentPage <= 1) + return; + + currentPage--; + } + else if(e.isRightClick()) + { + if (currentPage >= maxPage) + return; + + currentPage++; + } + + ClearUI(); + ShowLogData(currentPage); + CreateToggleButton(); + CreatePageButton(); + CreateCloseButton(player, CLOSE); + } + } + } + + private void ClearUI() + { + for (int i = 0; i < 54; i++) + { + inventory.setItem(i, null); + } + } +} diff --git a/src/main/java/me/sat7/dynamicshop/guis/RotationEditor.java b/src/main/java/me/sat7/dynamicshop/guis/RotationEditor.java new file mode 100644 index 0000000..8630528 --- /dev/null +++ b/src/main/java/me/sat7/dynamicshop/guis/RotationEditor.java @@ -0,0 +1,389 @@ +package me.sat7.dynamicshop.guis; + +import me.sat7.dynamicshop.DynaShopAPI; +import me.sat7.dynamicshop.files.CustomConfig; +import me.sat7.dynamicshop.utilities.MathUtil; +import me.sat7.dynamicshop.utilities.RotationUtil; +import me.sat7.dynamicshop.utilities.ShopUtil; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; + +import java.text.SimpleDateFormat; + +import static me.sat7.dynamicshop.utilities.LangUtil.t; +import static me.sat7.dynamicshop.utilities.MathUtil.*; + +public class RotationEditor extends InGameUI +{ + public RotationEditor() + { + uiType = UI_TYPE.RotationEditor; + } + + private final int DATA_0 = 10; + private final int DATA_6 = DATA_0 + 7; + private final int CLOSE = 27; + private final int PERIOD = 31; + private final int TIMER = 32; + private final int APPLY_CHANGES = 33; + private final int TOGGLE_ENABLE = 35; + + private Player player; + private String shopName; + private CustomConfig shopData; + + private int currentRotation; + private final SimpleDateFormat sdf = new SimpleDateFormat("MM.dd.yyyy HH:mm:ss"); + private String timeLore; + + private long period; // 밀리초 + private long period_new; // 밀리초 + private long nextTimer; // 밀리초 + private long nextTimer_new; // 밀리초 + + private int moveTarget; + + public Inventory getGui(Player player, String shopName) + { + this.player = player; + this.shopName = shopName; + this.shopData = ShopUtil.shopConfigFiles.get(shopName); + this.currentRotation = RotationUtil.GetCurrentRotationIndex(shopName); + this.period = MathUtil.TickToMilliSeconds(shopData.get().getLong("Options.Rotation.Period", dayInTick)); + this.period_new = period; + this.nextTimer = shopData.get().getLong("Options.Rotation.NextTimer"); + this.nextTimer_new = nextTimer; + + this.moveTarget = -1; + + inventory = Bukkit.createInventory(player, 36, t(player, "ROTATION_EDITOR_TITLE") + "§7 | §8" + shopName); + + RefreshUI(); + + CreateCloseButton(player, CLOSE); + + return inventory; + } + + @Override + public void OnClickUpperInventory(InventoryClickEvent e) + { + player = (Player) e.getWhoClicked(); + + if (e.getSlot() == TOGGLE_ENABLE) + { + if (e.isLeftClick()) + OnClickToggleEnable(); + } else if (e.getSlot() == CLOSE) + { + DynaShopAPI.openShopSettingGui(player, shopName); + } else if (e.getSlot() == PERIOD) + { + long editAmount = 0; + if (e.isLeftClick()) + editAmount = -hourInMilliSeconds; + else if (e.isRightClick()) + editAmount = hourInMilliSeconds; + + if (e.isShiftClick()) + editAmount *= 10; + + period_new += editAmount; + period_new = Clamp(period_new, hourInMilliSeconds, dayInMilliSeconds * 3); + + nextTimer_new = nextTimer - period + period_new; + + RefreshUI(); + } else if (e.getSlot() == TIMER) + { + long editAmount = 0; + if (e.isLeftClick()) + editAmount = -hourInMilliSeconds; + else if (e.isRightClick()) + editAmount = hourInMilliSeconds; + + if (!e.isShiftClick()) + editAmount /= 6; + + nextTimer_new += editAmount; + nextTimer_new = nextTimer_new / 1000 / 60 / 10; + nextTimer_new = nextTimer_new * 1000 * 60 * 10; // 10분단위로 내림처리. + RefreshUI(); + } else if (e.getSlot() == APPLY_CHANGES) + { + OnClickApply(e.isLeftClick(), e.isRightClick()); + } else if (e.getSlot() >= DATA_0 && e.getSlot() <= DATA_6 && e.getCurrentItem() != null) + { + OnClickDataButton(e.getCurrentItem().getType() != Material.CHEST, e.getSlot() - DATA_0, + e.isLeftClick(), e.isRightClick(), e.isShiftClick()); + } + } + + @Override + public void RefreshUI() + { + if (currentRotation != -1 && System.currentTimeMillis() > nextTimer) + { + currentRotation = RotationUtil.GetCurrentRotationIndex(shopName); + nextTimer = shopData.get().getLong("Options.Rotation.NextTimer"); + nextTimer_new = nextTimer; + } + + String nextTimerString; + if (currentRotation == -1) + { + nextTimerString = "-"; + } else + { + if (period != period_new || nextTimer != nextTimer_new) + { + nextTimerString = sdf.format(nextTimer_new); + } else + { + nextTimerString = sdf.format(nextTimer); + } + } + + timeLore = t(player, "ROTATION_EDITOR.NEXT_ROTATION") + + "\n §7" + nextTimerString + + "\n\n" + + t(player, "ROTATION_EDITOR.CURRENT_TIME") + + "\n §7" + sdf.format(System.currentTimeMillis()); + + UpdateToggleButton(); + UpdateSettingButtons(); + UpdateRotationDataIcons(); + } + + private void UpdateToggleButton() + { + // 활성화 토글 + if (currentRotation != -1) + { + String lore = t(player, "ROTATION_EDITOR.CLICK_TO_DISABLE") + "\n\n" + timeLore; + CreateButton(TOGGLE_ENABLE, Material.GREEN_STAINED_GLASS, t(player, "ROTATION_EDITOR.ENABLED"), lore); + } else + { + String lore = t(player, "ROTATION_EDITOR.CLICK_TO_ENABLE") + "\n\n" + timeLore; + CreateButton(TOGGLE_ENABLE, Material.RED_STAINED_GLASS, t(player, "ROTATION_EDITOR.DISABLED"), lore); + } + } + + private void UpdateSettingButtons() + { + boolean isDirty = period != period_new || nextTimer != nextTimer_new; + String unsavedChangesLore = isDirty ? t(player, "ROTATION_EDITOR.UNSAVED_CHANGES") : ""; + + inventory.setItem(PERIOD, null); + String periodLore = t(player, "ROTATION_EDITOR.HOUR").replace("{0}", String.valueOf(period_new / 60 / 60 / 1000)); + periodLore += "\n\n" + timeLore; + periodLore += "\n" + unsavedChangesLore; + periodLore += "\n" + t(player, "ROTATION_EDITOR.PERIOD_LORE_V2"); + CreateButton(PERIOD, Material.CLOCK, t(player, "ROTATION_EDITOR.PERIOD"), periodLore); + + inventory.setItem(TIMER, null); + String timerLore = "§f" + ((nextTimer_new - nextTimer) / 1000 / 60); + timerLore += "\n\n" + timeLore; + timerLore += "\n" + unsavedChangesLore; + timerLore += "\n" + t(player, "ROTATION_EDITOR.TIMER_LORE_V2"); + CreateButton(TIMER, Material.CLOCK, t(player, "ROTATION_EDITOR.TIMER"), timerLore); + + inventory.setItem(APPLY_CHANGES, null); + String applyLore = timeLore + "\n" + unsavedChangesLore; + applyLore += "\n" + t(player, "ROTATION_EDITOR.APPLY_CHANGES_LORE"); + CreateButton(APPLY_CHANGES, Material.CLOCK, t(player, "ROTATION_EDITOR.APPLY_CHANGES"), applyLore); + } + + private void UpdateRotationDataIcons() + { + for (int i = 0; i < 27; i++) + { + inventory.setItem(i, null); + } + + CustomConfig[] rotationData = RotationUtil.rotationDataMap.get(shopName); + if (rotationData == null) + return; + + for (int i = 0; i < 7; i++) + { + String slotString = "§7[" + (i + 1) + "]"; + + if (rotationData[i] != null) + { + String lore = "§f" + RotationUtil.rotationFolderName + "/" + rotationData[i].GetFileName() + "\n"; + + if (i == currentRotation) + { + lore += t(player, "ROTATION_EDITOR.CURRENTLY_IN_USE") + "\n\n"; + lore += t(player, "ROTATION_EDITOR.OPEN") + "\n"; + lore += t(player, "ROTATION_EDITOR.MOVE") + "\n"; + lore += t(player, "ROTATION_EDITOR.REAPPLY") + "\n"; + } else + { + lore += "\n" + t(player, "ROTATION_EDITOR.APPLY_ROTATION") + "\n"; + lore += t(player, "ROTATION_EDITOR.MOVE") + "\n"; + } + + lore += t(player, "ROTATION_EDITOR.DELETE") + "\n\n"; + + lore += "§7Options.title:\n §7" + rotationData[i].get().getString("Options.title", "(empty)") + "\n"; + lore += "§7Options.lore:\n §7" + rotationData[i].get().getString("Options.lore", "(empty)") + "\n"; + lore += "§7" + (rotationData[i].get().getKeys(false).size() - 1) + " data"; + + CreateButton(i + DATA_0, Material.CHEST, slotString, lore); + + if (currentRotation == i) + { + CreateButton(i + DATA_0 - 9, Material.GREEN_STAINED_GLASS_PANE, " ", ""); + CreateButton(i + DATA_0 + 9, Material.GREEN_STAINED_GLASS_PANE, " ", ""); + } + } else + { + String lore = t(player, "ROTATION_EDITOR.CREATE") + "\n" + t(player, "ROTATION_EDITOR.COPY_AS_NEW"); + CreateButton(i + DATA_0, Material.BLACK_STAINED_GLASS_PANE, slotString, lore); + } + } + } + + private void OnClickDataButton(boolean isEmptySlot, int dataIndex, boolean isLeftClick, boolean isRightClick, boolean isShift) + { + if (isEmptySlot) + { + // 새 로테이션 파일 생성 + if (isLeftClick) + { + RotationUtil.AddNewRotationData(shopName, dataIndex, true); + RefreshUI(); + } + else if (isRightClick) + { + // 이동 + if(moveTarget != -1) + { + RotationUtil.OnRotationFileSlotMoved(shopName, moveTarget, dataIndex); + if(moveTarget == currentRotation) + { + currentRotation = dataIndex; + shopData.get().set("Options.Rotation.Current", currentRotation); + } + moveTarget = -1; + } + // 현재 상점 복사하여 새로 만들기 + else + { + RotationUtil.AddNewRotationData(shopName, dataIndex, false); + } + RefreshUI(); + } + } else + { + if (isLeftClick) + { + // 로테이션 적용 + if (currentRotation != dataIndex || isShift) + { + RotationUtil.ApplyRotation(shopName, currentRotation, dataIndex); + currentRotation = dataIndex; + RefreshUI(); + } + // 로테이션 적용된 상태의 상점 UI를 연다 + else + { + DynaShopAPI.openShopGui(player, shopName, 0); + } + } else if (isRightClick) + { + // 로테이션 데이터 삭제 + if (isShift) + { + RotationUtil.DeleteRotationFile(shopName, dataIndex); + if (currentRotation == dataIndex) + { + currentRotation = RotationUtil.FindNextRotationIndex(shopName, currentRotation); + if (currentRotation == -1) + { + RotationUtil.DisableRotation(shopName); + } else + { + RotationUtil.ApplyRotation(shopName, -1, currentRotation); + } + } + RefreshUI(); + } + // 이동 + else + { + if(moveTarget == -1) + { + moveTarget = dataIndex; + } + } + } + } + } + + private void OnClickToggleEnable() + { + if (currentRotation == -1) + { + currentRotation = 0; + shopData.get().set("Options.Rotation.Current", currentRotation); + + RotationUtil.ReadRotationYMLFiles(shopName); + + if (RotationUtil.GetRotationYmlFileCount(shopName) == 0) + { + RotationUtil.AddNewRotationData(shopName, 0, false); + RotationUtil.AddNewRotationData(shopName, 1, false); + } + + long tick = MathUtil.MilliSecondsToTick(period); + RotationUtil.ApplyRotation(shopName, -1, 0); + RotationUtil.StartRotationTask(shopName, tick, tick); + nextTimer = System.currentTimeMillis() + period; + nextTimer_new = nextTimer; + period_new = period; + + shopData.get().set("Options.Rotation.Period", tick); + shopData.get().set("Options.Rotation.NextTimer", nextTimer); + } else + { + currentRotation = -1; + RotationUtil.DisableRotation(shopName); + } + + shopData.save(); + RefreshUI(); + } + + private void OnClickApply(boolean isLeftClick, boolean isRightClick) + { + if (isLeftClick) + { + if (period == period_new && nextTimer == nextTimer_new) + return; + + period = period_new; + nextTimer = nextTimer_new; + shopData.get().set("Options.Rotation.Period", MathUtil.MilliSecondsToTick(period)); + shopData.get().set("Options.Rotation.NextTimer", nextTimer); + + long timeLeft = nextTimer - System.currentTimeMillis(); + RotationUtil.StartRotationTask(shopName, MathUtil.MilliSecondsToTick(timeLeft), MathUtil.MilliSecondsToTick(period)); + + RefreshUI(); + } else if (isRightClick) + { + if (period == period_new && nextTimer == nextTimer_new) + return; + + nextTimer_new = nextTimer; + period_new = period; + RefreshUI(); + } + } +} diff --git a/src/main/java/me/sat7/dynamicshop/guis/Shop.java b/src/main/java/me/sat7/dynamicshop/guis/Shop.java index 978398b..adaf8b0 100644 --- a/src/main/java/me/sat7/dynamicshop/guis/Shop.java +++ b/src/main/java/me/sat7/dynamicshop/guis/Shop.java @@ -84,6 +84,7 @@ public Inventory getGui(Player player, String shopName, int page) CreateButton(SHOP_INFO, InGameUI.GetShopInfoButtonIconMat(), "§3" + shopName, CreateShopInfoText()); ShowItems(); + FillBackgroundColor(); return inventory; } @@ -263,7 +264,7 @@ else if (ShopUtil.GetCurrency(shopData).equalsIgnoreCase(Constants.S_EXP)) { sellText = t(player, "SHOP.SELL_PRICE" + currencyKey).replace("{num}", n(sellPrice,isIntTypeCurrency)); } - + sellText += showValueChange ? " " + valueChanged_Sell : ""; } @@ -583,10 +584,21 @@ private void OnClickShopSettingsButton() private void OnClickItemSlot(int idx, InventoryClickEvent e) { - if (e.getCurrentItem() != null && e.getCurrentItem().getType() != Material.AIR) + boolean isEmpty = false; + if (e.getCurrentItem() == null || e.getCurrentItem().getType() == Material.AIR) + { + isEmpty = true; + } + else if (e.getCurrentItem().getItemMeta().getDisplayName().equals(" ") || + e.getCurrentItem().getItemMeta().getDisplayName().equals(t(player, "SHOP.CLICK_TO_ADD"))) + { + isEmpty = true; + } + + if (!isEmpty) { if (e.getCurrentItem().getItemMeta() != null && - e.getCurrentItem().getItemMeta().getDisplayName().equals(t(null, "SHOP.INCOMPLETE_DATA"))) + e.getCurrentItem().getItemMeta().getDisplayName().equals(t(null, "SHOP.INCOMPLETE_DATA"))) { return; } @@ -654,6 +666,9 @@ else if (player.hasPermission(P_ADMIN_SHOP_EDIT)) shopData.set(String.valueOf(selectedSlot), null); } + RotationUtil.UpdateCurrentRotationData(shopName, selectedSlot); + RotationUtil.UpdateCurrentRotationData(shopName, idx); + ShopUtil.shopConfigFiles.get(shopName).save(); selectedSlot = -1; RefreshUI(); @@ -687,6 +702,7 @@ public void RefreshUI() infoButton.setItemMeta(infoMeta); ShowItems(); + FillBackgroundColor(); } public boolean CheckShopIsEnable() @@ -708,4 +724,27 @@ public boolean CheckShopIsEnable() return ret; } + + public void FillBackgroundColor() + { + String color = shopData.getString("Options.background",""); + if (color.isEmpty()) + return; + + Material mat = Material.getMaterial( color.toUpperCase() + "_STAINED_GLASS_PANE"); + if (mat == null) + return; + + String name = " "; + if (player.hasPermission(P_ADMIN_SHOP_EDIT)) + name = t(player, "SHOP.CLICK_TO_ADD"); + + for (int i = 0; i < 54; i++) + { + if(inventory.getItem(i) == null || inventory.getItem(i).getType().isAir()) + { + CreateButton(i, mat, i < 45 ? name : " ", ""); + } + } + } } diff --git a/src/main/java/me/sat7/dynamicshop/guis/ShopSettings.java b/src/main/java/me/sat7/dynamicshop/guis/ShopSettings.java index 132da0f..d6c4272 100644 --- a/src/main/java/me/sat7/dynamicshop/guis/ShopSettings.java +++ b/src/main/java/me/sat7/dynamicshop/guis/ShopSettings.java @@ -15,7 +15,6 @@ import me.sat7.dynamicshop.utilities.ConfigUtil; import me.sat7.dynamicshop.utilities.UserUtil; import org.bukkit.Bukkit; -import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; @@ -137,12 +136,12 @@ public Inventory getGui(Player player, String shopName) Material mat = Material.getMaterial( color.toUpperCase() + "_CONCRETE"); if (mat == null) mat = Material.LIGHT_GRAY_CONCRETE; - CreateButton(BACKGROUND, mat, t(player, "SHOP_SETTING.BACKGROUND"), "§7" + ChatColor.stripColor(t(player, "SHOP_SETTING.BACKGROUND_LORE"))); + CreateButton(BACKGROUND, mat, t(player, "SHOP_SETTING.BACKGROUND"), t(player, "SHOP_SETTING.BACKGROUND_LORE")); // 로테이션 에디터 int currentRotation = confSec_Options.getInt("Rotation.Current", -1); String rotationString = currentRotation == -1 ? t(player, "ROTATION_EDITOR.DISABLED") : String.valueOf(currentRotation + 1); - CreateButton(ROTATION_EDITOR, Material.CLOCK, t(player, "SHOP_SETTING.ROTATION_EDITOR") + rotationString, "§7" + ChatColor.stripColor(t(player, "SHOP_SETTING.ROTATION_EDITOR_LORE"))); + CreateButton(ROTATION_EDITOR, Material.CLOCK, t(player, "SHOP_SETTING.ROTATION_EDITOR") + rotationString, t(player, "SHOP_SETTING.ROTATION_EDITOR_LORE")); // 영업시간 버튼 int curTime = (int) (player.getWorld().getTime()) / 1000 + 6; @@ -178,7 +177,7 @@ public Inventory getGui(Player player, String shopName) ArrayList fluctuationLore = new ArrayList<>(Arrays.asList( "§9" + t(player, "CUR_STATE") + ": " + t(player, "ON"), "§e" + t(player, "LMB") + ": " + t(player, "OFF"), - "§7" + ChatColor.stripColor(t(player, "STOCK_SIMULATOR.SIMULATOR_BUTTON_LORE")) + "§e" + t(player, "STOCK_SIMULATOR.SIMULATOR_BUTTON_LORE") )); CreateButton(FLUC, Material.COMPARATOR, t(player, "FLUCTUATION.FLUCTUATION"), fluctuationLore); @@ -200,7 +199,7 @@ public Inventory getGui(Player player, String shopName) new ArrayList<>(Arrays.asList( "§9" + t(player, "CUR_STATE") + ": " + t(player, "OFF"), "§e" + t(player, "LMB") + ": " + t(player, "ON"), - "§7" + ChatColor.stripColor(t(player, "STOCK_SIMULATOR.SIMULATOR_BUTTON_LORE")) + "§e" + t(player, "STOCK_SIMULATOR.SIMULATOR_BUTTON_LORE") )), 1); inventory.setItem(FLUC, flucToggleBtn); @@ -213,7 +212,7 @@ public Inventory getGui(Player player, String shopName) ArrayList stableLore = new ArrayList<>(Arrays.asList( "§9" + t(player, "CUR_STATE") + ": " + t(player, "ON"), "§e" + t(player, "LMB") + ": " + t(player, "OFF"), - "§7" + ChatColor.stripColor(t(player, "STOCK_SIMULATOR.SIMULATOR_BUTTON_LORE")) + "§e" + t(player, "STOCK_SIMULATOR.SIMULATOR_BUTTON_LORE") )); CreateButton(STABLE, Material.COMPARATOR, t(player, "STOCK_STABILIZING.SS"), stableLore); @@ -233,7 +232,7 @@ public Inventory getGui(Player player, String shopName) ArrayList stableLore = new ArrayList<>(Arrays.asList( "§9" + t(player, "CUR_STATE") + ": " + t(player, "OFF"), "§e" + t(player, "LMB") + ": " + t(player, "ON"), - "§7" + ChatColor.stripColor(t(player, "STOCK_SIMULATOR.SIMULATOR_BUTTON_LORE")) + "§e" + t(player, "STOCK_SIMULATOR.SIMULATOR_BUTTON_LORE") )); CreateButton(STABLE, Material.COMPARATOR, t(player, "STOCK_STABILIZING.SS"), stableLore); } @@ -353,7 +352,7 @@ public Inventory getGui(Player player, String shopName) ArrayList logLore = new ArrayList<>(); logLore.add("§9" + t(player, "CUR_STATE") + ": " + log_cur); logLore.add("§e" + t(player, "LMB") + ": " + log_set); - logLore.add(t(player, "§7" + ChatColor.stripColor(t(player, "SHOP_SETTING.LOG_TOGGLE_LORE")))); + logLore.add(t(player, "SHOP_SETTING.LOG_TOGGLE_LORE")); CreateButton(LOG_TOGGLE, Material.BOOK, t(player, "LOG.LOG"), logLore); boolean printToConsoleActive = data.get().contains("Options.log.printToConsole") && data.get().getBoolean("Options.log.printToConsole"); @@ -437,7 +436,7 @@ else if (e.getSlot() == MAX_PAGE) // 배경 색상 변경 else if (e.getSlot() == BACKGROUND) { - DynamicShop.PaidOnlyMsg(player); + DynaShopAPI.openColorPicker(player, -1); } // 로테이션 편집기 else if (e.getSlot() == ROTATION_EDITOR) diff --git a/src/main/java/me/sat7/dynamicshop/guis/StockSimulator.java b/src/main/java/me/sat7/dynamicshop/guis/StockSimulator.java new file mode 100644 index 0000000..fc3725c --- /dev/null +++ b/src/main/java/me/sat7/dynamicshop/guis/StockSimulator.java @@ -0,0 +1,527 @@ +package me.sat7.dynamicshop.guis; + +import me.sat7.dynamicshop.DynaShopAPI; +import me.sat7.dynamicshop.DynamicShop; +import me.sat7.dynamicshop.files.CustomConfig; +import me.sat7.dynamicshop.utilities.ConfigUtil; +import me.sat7.dynamicshop.utilities.ItemsUtil; +import me.sat7.dynamicshop.utilities.ShopUtil; +import org.apache.commons.lang.ArrayUtils; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Random; + +import static me.sat7.dynamicshop.utilities.LangUtil.n; +import static me.sat7.dynamicshop.utilities.LangUtil.t; +import static me.sat7.dynamicshop.utilities.MathUtil.Clamp; + +public class StockSimulator extends InGameUI +{ + public StockSimulator() + { + uiType = UI_TYPE.StockSimulator; + } + + private final int ITEM_1 = 0; + private final int ITEM_2 = 9; + private final int ITEM_3 = 18; + private final int ITEM_4 = 27; + private final int ITEM_5 = 36; + + private final int CLOSE = 45; + + private final int FLUC = 47; + private final int FLUC_INTERVAL = 48; + private final int FLUC_STRENGTH = 49; + private final int STABLE = 50; + private final int STABLE_INTERVAL = 51; + private final int STABLE_STRENGTH = 52; + + private final int RUN = 53; + + private String shopName; + private int shopItemCount; + private int sampleStartIdx; + + private boolean fluc; + private int flucInterval; + private double flucStrength; + private boolean stable; + private int stableInterval; + private double stableStrength; + + public static class SimulData + { + public String mat; + public int stock; + public int median; + public String tradeType; + public double value; + public double valueMin; + public double valueMax; + } + + final ArrayList tempDatas = new ArrayList<>(); + + public Inventory getGui(Player player, String shopName) + { + inventory = Bukkit.createInventory(player, 54, t(player, "STOCK_SIMULATOR_TITLE") + "§7 | §8" + shopName); + + this.shopName = shopName; + this.shopItemCount = ShopUtil.GetShopItemCount(shopName, true, true); + + for (int i = 0; i < 6; i++) + { + ItemStack itemStack = new ItemStack(Material.BLACK_STAINED_GLASS_PANE); + ItemMeta meta = itemStack.getItemMeta(); + meta.setDisplayName(" "); + itemStack.setItemMeta(meta); + inventory.setItem((i * 9) + 1, itemStack); + } + + CustomConfig data = ShopUtil.shopConfigFiles.get(shopName); + fluc = data.get().contains("Options.fluctuation"); + if (fluc) + { + flucInterval = data.get().getInt("Options.fluctuation.interval"); + flucStrength = data.get().getDouble("Options.fluctuation.strength"); + } + stable = data.get().contains("Options.stockStabilizing"); + if (stable) + { + stableInterval = data.get().getInt("Options.stockStabilizing.interval"); + stableStrength = data.get().getDouble("Options.stockStabilizing.strength"); + } + + CreateSampleDatas(0); + UpdateBottomButtons(); + CreateCloseButton(player, CLOSE); + + return inventory; + } + + @Override + public void OnClickUpperInventory(InventoryClickEvent e) + { + Player player = (Player) e.getWhoClicked(); + + // 닫기 버튼 + if (e.getSlot() == CLOSE) + { + DynaShopAPI.openShopSettingGui(player, shopName); + } + // 샘플 아이템 변경 + else if (e.getSlot() == ITEM_1 || e.getSlot() == ITEM_2 || e.getSlot() == ITEM_3 || e.getSlot() == ITEM_4 || e.getSlot() == ITEM_5) + { + if (e.isLeftClick()) + sampleStartIdx -= 5; + else if (e.isRightClick()) + sampleStartIdx += 5; + + sampleStartIdx = Clamp(sampleStartIdx, 0, shopItemCount - 5); + CreateSampleDatas(sampleStartIdx); + } + // 무작위 재고 + else if (e.getSlot() == FLUC || e.getSlot() == FLUC_INTERVAL || e.getSlot() == FLUC_STRENGTH) + { + if (fluc) + { + if (e.getSlot() == FLUC) + { + fluc = false; + } else if (e.getSlot() == FLUC_INTERVAL) + { + int edit = -1; + if (e.isRightClick()) edit = 1; + if (e.isShiftClick()) edit *= 5; + + flucInterval += edit; + flucInterval = Clamp(flucInterval, 1, 999); + } else if (e.getSlot() == FLUC_STRENGTH) + { + double edit = -0.1; + if (e.isRightClick()) edit = 0.1; + if (e.isShiftClick()) edit *= 5; + + flucStrength += edit; + flucStrength = Clamp(flucStrength, 0.1, 64); + flucStrength = Math.round(flucStrength * 100) / 100.0; + } + } else + { + if (e.getSlot() == FLUC) + { + fluc = true; + flucInterval = 48; + flucStrength = 0.1; + } + } + + UpdateBottomButtons(); + } + // 스톡 안정화 + else if (e.getSlot() == STABLE || e.getSlot() == STABLE_INTERVAL || e.getSlot() == STABLE_STRENGTH) + { + if (stable) + { + if (e.getSlot() == STABLE) + { + stable = false; + } else if (e.getSlot() == STABLE_INTERVAL) + { + int edit = -1; + if (e.isRightClick()) edit = 1; + if (e.isShiftClick()) edit *= 5; + + stableInterval += edit; + stableInterval = Clamp(stableInterval, 1, 999); + } else if (e.getSlot() == STABLE_STRENGTH) + { + double edit = -0.1; + if (e.isRightClick()) edit = 0.1; + if (e.isShiftClick()) edit *= 5; + + stableStrength += edit; + stableStrength = Clamp(stableStrength, 0.1, 25); + stableStrength = (Math.round(stableStrength * 100) / 100.0); + } + } else + { + if (e.getSlot() == STABLE) + { + stable = true; + stableInterval = 48; + stableStrength = 0.5; + } + } + + UpdateBottomButtons(); + } + // 시뮬레이션 실행 + else if (e.getSlot() == RUN) + { + if (e.isLeftClick()) + { + Bukkit.getScheduler().runTaskAsynchronously(DynamicShop.plugin, this::RunSimulation); + } else if (e.isRightClick()) + { + ApplySettings(); + player.sendMessage(DynamicShop.dsPrefix(player) + t(player, "MESSAGE.CHANGES_APPLIED_2")); + } + } + } + + private void CreateSampleDatas(int startIdx) + { + CustomConfig data = ShopUtil.shopConfigFiles.get(shopName); + tempDatas.clear(); + + int skipIdx = 0; + for (String item : data.get().getKeys(false)) + { + try + { + if (skipIdx < startIdx) + { + skipIdx++; + continue; + } + + int i = Integer.parseInt(item); // options에 대해 적용하지 않기 위해. + + if (!data.get().contains(item + ".value")) continue; // 장식용은 스킵 + + int stock = data.get().getInt(item + ".stock"); + if (stock < 1) continue; // 무한재고에 대해서는 스킵 + int oldMedian = data.get().getInt(item + ".median"); + if (oldMedian < 1) continue; // 고정가 상품에 대해서는 스킵 + + SimulData simulData = new SimulData(); + simulData.mat = data.get().getString(item + ".mat"); + simulData.stock = data.get().getInt(item + ".stock"); + simulData.median = data.get().getInt(item + ".median"); + simulData.value = data.get().getDouble(item + ".value"); + simulData.valueMax = data.get().getDouble(item + ".valueMax", 0); + simulData.valueMin = data.get().getDouble(item + ".valueMin", 0); + simulData.tradeType = data.get().getString(item + ".tradeType"); + tempDatas.add(simulData); + if (tempDatas.size() >= 5) + break; + } catch (Exception ignore) + { + } + } + + int i = 0; + for (SimulData simulData : tempDatas) + { + Material mat = Material.getMaterial(simulData.mat); + if (mat == null) + continue; + + ItemStack itemStack = new ItemStack(mat); + ItemMeta meta = itemStack.getItemMeta(); + + ArrayList lore = CreateItemLore(simulData, null); + lore.add(t(null, "STOCK_SIMULATOR.CHANGE_SAMPLE_LORE")); + meta.setLore(lore); + itemStack.setItemMeta(meta); + + inventory.setItem(ITEM_1 + (i * 9), itemStack); + + i++; + } + } + + private void UpdateBottomButtons() + { + for (int i = 47; i < 54; i++) + inventory.setItem(i, null); + + // 랜덤 + if (fluc) + { + ArrayList fluctuationLore = new ArrayList<>(Arrays.asList( + "§9" + t(null, "CUR_STATE") + ": " + t(null, "ON"), + "§e" + t(null, "CLICK") + ": " + t(null, "OFF") + )); + CreateButton(FLUC, Material.COMPARATOR, t(null, "FLUCTUATION.FLUCTUATION"), fluctuationLore); + + ArrayList fluctuation_interval_lore = new ArrayList<>(Arrays.asList( + t(null, "FLUCTUATION.INTERVAL_LORE"), + "§9" + t(null, "CUR_STATE") + ": " + flucInterval / 2.0 + "h", + "§e" + t(null, "CLICK") + ": " + t(null, "STOCK_SIMULATOR.L_R_SHIFT"))); + CreateButton(FLUC_INTERVAL, Material.COMPARATOR, t(null, "FLUCTUATION.INTERVAL"), fluctuation_interval_lore, Clamp(flucInterval / 2, 1, 64)); + + ArrayList fluctuation_strength_lore = new ArrayList<>(Arrays.asList( + t(null, "FLUCTUATION.STRENGTH_LORE"), + "§9" + t(null, "CUR_STATE") + ": ~" + flucStrength + "%", + "§e" + t(null, "CLICK") + ": " + t(null, "STOCK_STABILIZING.L_R_SHIFT"))); + CreateButton(FLUC_STRENGTH, Material.COMPARATOR, t(null, "FLUCTUATION.STRENGTH"), fluctuation_strength_lore, Clamp((int) (flucStrength * 10), 1, 64)); + } else + { + ItemStack flucToggleBtn = ItemsUtil.createItemStack(Material.COMPARATOR, null, + t(null, "FLUCTUATION.FLUCTUATION"), + new ArrayList<>(Arrays.asList( + "§9" + t(null, "CUR_STATE") + ": " + t(null, "OFF"), + "§e" + t(null, "CLICK") + ": " + t(null, "ON") + )), + 1); + inventory.setItem(FLUC, flucToggleBtn); + } + + // 안정화 + if (stable) + { + ArrayList stableLore = new ArrayList<>(Arrays.asList( + "§9" + t(null, "CUR_STATE") + ": " + t(null, "ON"), + "§e" + t(null, "CLICK") + ": " + t(null, "OFF") + )); + CreateButton(STABLE, Material.COMPARATOR, t(null, "STOCK_STABILIZING.SS"), stableLore); + + ArrayList stable_interval_Lore = new ArrayList<>(Arrays.asList( + t(null, "FLUCTUATION.INTERVAL_LORE"), + "§9" + t(null, "CUR_STATE") + ": " + stableInterval / 2.0 + "h", + "§e" + t(null, "CLICK") + ": " + t(null, "STOCK_SIMULATOR.L_R_SHIFT"))); + CreateButton(STABLE_INTERVAL, Material.COMPARATOR, t(null, "FLUCTUATION.INTERVAL"), stable_interval_Lore, Clamp(stableInterval / 2, 1, 64)); + + ArrayList stable_strength_Lore = new ArrayList<>(Arrays.asList( + ConfigUtil.GetUseLegacyStockStabilization() ? t(null, "STOCK_STABILIZING.STRENGTH_LORE_A") : t(null, "STOCK_STABILIZING.STRENGTH_LORE_B"), + "§9" + t(null, "CUR_STATE") + ": ~" + stableStrength + "%", + "§e" + t(null, "CLICK") + ": " + t(null, "STOCK_STABILIZING.L_R_SHIFT"))); + CreateButton(STABLE_STRENGTH, Material.COMPARATOR, t(null, "FLUCTUATION.STRENGTH"), stable_strength_Lore, Clamp((int) (stableStrength * 10), 1, 64)); + } else + { + ArrayList stableLore = new ArrayList<>(Arrays.asList( + "§9" + t(null, "CUR_STATE") + ": " + t(null, "OFF"), + "§e" + t(null, "CLICK") + ": " + t(null, "ON") + )); + CreateButton(STABLE, Material.COMPARATOR, t(null, "STOCK_STABILIZING.SS"), stableLore); + } + + CreateButton(RUN, Material.REDSTONE_BLOCK, t(null, "STOCK_SIMULATOR.RUN_TITLE"), t(null, "STOCK_SIMULATOR.RUN_LORE")); + } + + private void RunSimulation() + { + for (int i = 0; i < 5; i++) + { + for (int j = 0; j <= 6; j++) + inventory.setItem(ITEM_1 + (9 * i) + j + 2, null); + } + + final int[] time = new int[]{ + 48, 48 * 2, 48 * 3, //48 = 20분(마크 1일) + 144 * 2, 144 * 4, 144 * 6, // 144 = 1시간 + 144 * 8, 144 * 10, 144 * 12, + 144 * 14, 144 * 16, 144 * 18, + 144 * 20, 144 * 22, 144 * 24, + 3456 * 2, 3456 * 3, 3456 * 4, // 3456 = 1일 + 3456 * 5, 3456 * 6, 3456 * 7}; + + Random generator = new Random(); + boolean useLegacyStockStabilization = ConfigUtil.GetUseLegacyStockStabilization(); + + int itemIndex = 0; + int timeIndex; + + for (SimulData simulData : tempDatas) + { + SimulData tempData = new SimulData(); + tempData.mat = simulData.mat; + tempData.stock = simulData.stock; + tempData.median = simulData.median; + tempData.tradeType = simulData.tradeType; + tempData.value = simulData.value; + tempData.valueMin = simulData.valueMin; + + timeIndex = 0; + int stock = tempData.stock; + int median = tempData.median; + + ArrayList timeString = new ArrayList<>(); + timeString.add(t(null, "STOCK_SIMULATOR.MEDIAN").replace("{num}", n(median))); + timeString.add("\n"); + + int dataCount = 0; + + for (int i = 0; i <= time[time.length - 1]; i++) + { + if (fluc && i % flucInterval == 0) + { + stock = ShopUtil.RandomStockFluctuation(generator, stock, median, flucStrength); + } + if (stable && i % stableInterval == 0 && stock != median) + { + stock = ShopUtil.StockStabilizing(useLegacyStockStabilization, generator, stock, median, stableStrength); + } + + if (ArrayUtils.contains(time, i)) + { + String temp; + if(i == 48) + { + temp = t(null, "STOCK_SIMULATOR.AFTER_M").replace("{0}", String.valueOf(20)) + " " + t(null, "STOCK_SIMULATOR.REAL_TIME"); + } + else if (i <= 144) + { + temp = t(null, "STOCK_SIMULATOR.AFTER_M").replace("{0}", String.valueOf((int)(i/2.4))); + } + else if (i <= 3456) + { + temp = t(null, "STOCK_SIMULATOR.AFTER_H").replace("{0}", String.valueOf((i/144))); + } + else + { + temp = t(null, "STOCK_SIMULATOR.AFTER_D").replace("{0}", String.valueOf(i/3456)); + } + + tempData.stock = stock; + tempData.median = median; + timeString.addAll(CreateItemLore(tempData, temp)); + timeString.add("\n"); + + if ((dataCount + 1) % 3 == 0) + { + ItemStack item = new ItemStack(Material.valueOf(tempData.mat)); + ItemMeta meta = item.getItemMeta(); + meta.setLore(timeString); + item.setItemMeta(meta); + inventory.setItem(ITEM_1 + (9 * itemIndex) + timeIndex + 2, item); + + timeString.clear(); + timeString.add(t(null, "STOCK_SIMULATOR.MEDIAN").replace("{num}", n(median))); + timeString.add("\n"); + + timeIndex++; + } + + dataCount++; + } + } + + itemIndex++; + } + } + + private ArrayList CreateItemLore(SimulData simulData, String timeData) + { + ArrayList lore = new ArrayList<>(); + + if (timeData != null && !timeData.isEmpty()) + lore.add(timeData); + + lore.add(t(null, "STOCK_SIMULATOR.STOCK").replace("{num}", n(simulData.stock))); + + double price = CalcPrice(simulData); + double basePrice = simulData.value; + double priceSave1 = (price / basePrice) - 1; + double priceSave2 = 1 - (price / basePrice); + String arrow = ""; + if (price - basePrice > 0.005) + { + arrow = t(null, "ARROW.UP_2") + n(priceSave1 * 100) + "%"; + } else if (price - basePrice < -0.005) + { + arrow = t(null, "ARROW.DOWN_2") + n(priceSave2 * 100) + "%"; + } + + lore.add(t(null, "STOCK_SIMULATOR.PRICE").replace("{num}", n(price)) + " " + arrow); + + return lore; + } + + private double CalcPrice(SimulData data) + { + double price; + + double value = data.value; + double min = data.valueMin; + double max = data.valueMax; + int median = data.median; + int stock = data.stock; + + price = (median * value) / stock; + + if (min != 0 && price < min) + { + price = min; + } + if (max != 0 && price > max) + { + price = max; + } + + return price; + } + + private void ApplySettings() + { + CustomConfig data = ShopUtil.shopConfigFiles.get(shopName); + if (fluc) + { + data.get().set("Options.fluctuation.interval", flucInterval); + data.get().set("Options.fluctuation.strength", flucStrength); + } else + { + data.get().set("Options.fluctuation", null); + } + if (stable) + { + data.get().set("Options.stockStabilizing.interval", stableInterval); + data.get().set("Options.stockStabilizing.strength", stableStrength); + } else + { + data.get().set("Options.stockStabilizing", null); + } + + } +} diff --git a/src/main/java/me/sat7/dynamicshop/transactions/Buy.java b/src/main/java/me/sat7/dynamicshop/transactions/Buy.java index 0eaa8eb..e41c3ba 100644 --- a/src/main/java/me/sat7/dynamicshop/transactions/Buy.java +++ b/src/main/java/me/sat7/dynamicshop/transactions/Buy.java @@ -58,7 +58,7 @@ else if (currency.equalsIgnoreCase(Constants.S_PLAYERPOINT)) playerBalance = econ.getBalance(player); } - // 플레이어 당 거래량 제한 확인 + // 플레이어 당 거래량 제한 확인 int tradeIdxInt = Integer.parseInt(tradeIdx); int tradeLimitPerPlayer = ShopUtil.GetBuyLimitPerPlayer(shopName, tradeIdxInt); int playerTradingVolume = UserUtil.GetPlayerTradingVolume(player, shopName, HashUtil.GetItemHash(itemStack)); diff --git a/src/main/java/me/sat7/dynamicshop/transactions/Sell.java b/src/main/java/me/sat7/dynamicshop/transactions/Sell.java index 5cc2b30..8c7efda 100644 --- a/src/main/java/me/sat7/dynamicshop/transactions/Sell.java +++ b/src/main/java/me/sat7/dynamicshop/transactions/Sell.java @@ -310,7 +310,7 @@ public static void sell(String currency, Player player, String shopName, String // 이벤트 호출 ShopBuySellEvent event = new ShopBuySellEvent(false, priceBuyOld, Calc.getCurrentPrice(shopName, tradeIdx, true), priceSellOld, DynaShopAPI.getSellPrice(shopName, itemStack), stockOld, DynaShopAPI.getStock(shopName, itemStack), DynaShopAPI.getMedian(shopName, itemStack), shopName, itemStack, player); Bukkit.getPluginManager().callEvent(event); - + // UI 갱신 DynaShopAPI.openItemTradeGui(player, shopName, tradeIdx); } diff --git a/src/main/java/me/sat7/dynamicshop/utilities/LangUtil.java b/src/main/java/me/sat7/dynamicshop/utilities/LangUtil.java index e1520ec..90cd64d 100644 --- a/src/main/java/me/sat7/dynamicshop/utilities/LangUtil.java +++ b/src/main/java/me/sat7/dynamicshop/utilities/LangUtil.java @@ -302,7 +302,7 @@ public static void setupLangFile(String lang) ccLang.get().addDefault("PALETTE.LORE2", "§e좌클릭: 선택\n§e씨프트 우클릭: '{item}' 를 검색"); ccLang.get().addDefault("PALETTE.SEARCH", "§f찾기"); ccLang.get().addDefault("PALETTE.ADD_ALL", "§f모두 추가"); - ccLang.get().addDefault("PALETTE.ADD_ALL_LORE_LOCKED", "§e좌클릭: 모두 추가\n§7씨프트 좌클릭: 모두 추가하고 권장 값 적용"); + ccLang.get().addDefault("PALETTE.ADD_ALL_LORE", "§e좌클릭: 모두 추가\n§e씨프트 좌클릭: 모두 추가하고 권장 값 적용"); ccLang.get().addDefault("PALETTE.PAGE_TITLE", "§f{curPage}/{maxPage} 페이지"); ccLang.get().addDefault("PALETTE.PAGE_LORE", "§f§n좌클릭: 이전 페이지\n§f§n우클릭: 다음 페이지"); ccLang.get().addDefault("PALETTE.FILTER_APPLIED", "§f필터 적용됨 : "); @@ -472,9 +472,6 @@ public static void setupLangFile(String lang) ccLang.get().addDefault("JOB_POINTS", "Job Points"); ccLang.get().addDefault("PLAYER_POINTS", "Player Points"); - ccLang.get().addDefault("PAID_VERSION.DESC", "§f유료 버전에서 사용할 수 있습니다. "); - ccLang.get().addDefault("PAID_VERSION.GET_PREMIUM", "구매하기"); - ccLang.get().options().copyDefaults(true); ccLang.save(); } @@ -753,7 +750,7 @@ public static void setupLangFile(String lang) ccLang.get().addDefault("PALETTE.LORE2", "§eLMB: Select\n§eShift RMB: Search '{item}'"); ccLang.get().addDefault("PALETTE.SEARCH", "§fSearch"); ccLang.get().addDefault("PALETTE.ADD_ALL", "§fAdd all"); - ccLang.get().addDefault("PALETTE.ADD_ALL_LORE_LOCKED", "§eLMB: Add all\n§7Shift LMB: Add all and apply recommended values"); + ccLang.get().addDefault("PALETTE.ADD_ALL_LORE", "§eLMB: Add all\n§eShift LMB: Add all and apply recommended values"); ccLang.get().addDefault("PALETTE.PAGE_TITLE", "§f{curPage}/{maxPage} page"); ccLang.get().addDefault("PALETTE.PAGE_LORE", "§f§nLMB: Prev\n§f§nRMB: Next"); ccLang.get().addDefault("PALETTE.FILTER_APPLIED", "§fFilter Applied : "); @@ -923,9 +920,6 @@ public static void setupLangFile(String lang) ccLang.get().addDefault("JOB_POINTS", "Job Points"); ccLang.get().addDefault("PLAYER_POINTS", "Player Points"); - ccLang.get().addDefault("PAID_VERSION.DESC", "§fAvailable in paid version. "); - ccLang.get().addDefault("PAID_VERSION.GET_PREMIUM", "Get Premium"); - ccLang.get().options().copyDefaults(true); ccLang.save(); } diff --git a/src/main/java/me/sat7/dynamicshop/utilities/LogUtil.java b/src/main/java/me/sat7/dynamicshop/utilities/LogUtil.java index 54ac761..557878e 100644 --- a/src/main/java/me/sat7/dynamicshop/utilities/LogUtil.java +++ b/src/main/java/me/sat7/dynamicshop/utilities/LogUtil.java @@ -1,21 +1,22 @@ package me.sat7.dynamicshop.utilities; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +import com.opencsv.CSVReader; import com.opencsv.CSVWriter; import me.sat7.dynamicshop.DynamicShop; import me.sat7.dynamicshop.constants.Constants; import me.sat7.dynamicshop.files.CustomConfig; import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; import org.bukkit.Bukkit; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - import static me.sat7.dynamicshop.utilities.LangUtil.t; public final class LogUtil @@ -108,6 +109,29 @@ public static void SaveLogToCSV() } } + public static ArrayList LoadDataFromCSV(String shopName, String selectedFileName) + { + ArrayList resultArray = new ArrayList<>(); + + try + { + CSVReader reader = new CSVReader(new FileReader(DynamicShop.plugin.getDataFolder() + "/Log/" + shopName + "/" + selectedFileName)); + List data = reader.readAll(); + + for (int i = data.size() - 1; i >= 0; i--) + { + String[] line = data.get(i); + // User,Item,Amount,Date,Time,Currency,Price + resultArray.add(i + "," + line[7] + "," + line[3] + "," + line[4] + "," + line[0] + "," + line[1] + "," + line[6] + "," + line[5]); + } + } catch (Exception e) + { + //DynamicShop.console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + " Failed to read CSV file."); + } + + return resultArray; + } + private static String CreatePath(String shopName) { SimpleDateFormat sdf = new SimpleDateFormat(ConfigUtil.GetLogFileNameFormat()); @@ -115,6 +139,34 @@ private static String CreatePath(String shopName) return DynamicShop.plugin.getDataFolder() + "/Log/" + shopName + "/" + timeForFileName + ".csv"; } + public static ArrayList GetLogFileList(String shopName) + { + ArrayList resultArray = new ArrayList<>(); + + try + { + File f = new File(DynamicShop.plugin.getDataFolder() + "/Log/" + shopName); + File[] fList = f.listFiles(); + if (fList != null) + { + Arrays.sort(fList, Comparator.comparingLong(File::lastModified).reversed()); + + for (File temp: fList) + { + if (temp.isHidden() || !FilenameUtils.getExtension(temp.getName()).equals("csv")) + continue; + + resultArray.add(temp.getName()); + } + } + } catch (Exception e) + { + //DynamicShop.console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + " Failed to create log file list."); + } + + return resultArray; + } + public static void cullLogs() { File[] logFolders = new File(DynamicShop.plugin.getDataFolder() + "/Log").listFiles(); @@ -173,4 +225,19 @@ public static void DeleteShopLog(String shopName) } } } + + public static void DeleteLogFile(String shopName, String file) + { + File targetFile = new File(DynamicShop.plugin.getDataFolder() + "/Log/" + shopName + "/" + file); + if (targetFile.exists()) + { + try + { + FileUtils.delete(targetFile); + } catch (IOException e) + { + DynamicShop.console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + " Failed to delete csv."); + } + } + } } diff --git a/src/main/java/me/sat7/dynamicshop/utilities/RotationUtil.java b/src/main/java/me/sat7/dynamicshop/utilities/RotationUtil.java new file mode 100644 index 0000000..3b1633a --- /dev/null +++ b/src/main/java/me/sat7/dynamicshop/utilities/RotationUtil.java @@ -0,0 +1,732 @@ +package me.sat7.dynamicshop.utilities; + +import me.sat7.dynamicshop.DynamicShop; +import me.sat7.dynamicshop.constants.Constants; +import me.sat7.dynamicshop.files.CustomConfig; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.bukkit.Bukkit; +import org.bukkit.inventory.meta.ItemMeta; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; + +import static me.sat7.dynamicshop.utilities.HashUtil.CreateHashString; +import static me.sat7.dynamicshop.utilities.LangUtil.t; +import static me.sat7.dynamicshop.utilities.MathUtil.MilliSecondsToTick; +import static me.sat7.dynamicshop.utilities.MathUtil.TickToMilliSeconds; + +public final class RotationUtil +{ + public static final String rotationFolderName = "Rotation"; + public static final String rotationDataPath = DynamicShop.plugin.getDataFolder() + "/" + rotationFolderName; + + // ===== [ Private ] ===== + + public static final HashMap rotationDataMap = new HashMap<>(); + private static final HashMap sharedDataMap = new HashMap<>(); + + // 현재 상점 yml 데이터 기반으로 로테이션 yml 파일을 생성함 + private static CustomConfig CreateRotationDataFromCurrentShop(String shopName, int rotationIndex) + { + CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName); + if (shopData == null) + return null; + + CustomConfig rotationData = new CustomConfig(); + rotationData.setup(shopName + "_" + (rotationIndex + 1), rotationFolderName + "/" + shopName); + rotationData.get().set("Options.title", shopData.get().getString("Options.title", shopName)); + rotationData.get().set("Options.lore", shopData.get().getString("Options.lore", "")); + rotationData.get().set("Options.page", shopData.get().getInt("Options.page", 1)); + + // 기존의 데이터를 삭제함 + for (String s : rotationData.get().getKeys(false)) + { + try + { + Integer.parseInt(s); // 아이템 데이터만 가져오기 위함 + rotationData.get().set(s, null); + } catch (Exception ignore) + { + + } + } + + // 상점 아이템들을 로테이션 데이터에 기록 + for (String s : shopData.get().getKeys(false)) + { + try + { + Integer.parseInt(s); // 아이템 데이터만 가져오기 위함 + + rotationData.get().set(s + ".mat", shopData.get().get(s + ".mat")); + rotationData.get().set(s + ".itemStack", shopData.get().get(s + ".itemStack")); + + // merchandise + if (shopData.get().contains(s + ".value")) + { + rotationData.get().set(s + ".type", "item"); + } + // deco + else + { + rotationData.get().set(s + ".type", "deco"); + } + } catch (Exception ignore) + { + } + } + + rotationData.save(); + + return rotationData; + } + + // 아이템 없이 빈 로테이션 yml 파일을 생성함 + private static CustomConfig CreateEmptyRotationData(String shopName, int rotationIndex) + { + CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName); + if (shopData == null) + return null; + + CustomConfig rotationData = new CustomConfig(); + rotationData.setup(shopName + "_" + (rotationIndex + 1), rotationFolderName + "/" + shopName); + rotationData.get().set("Options.title", shopData.get().getString("Options.title", shopName)); + rotationData.get().set("Options.lore", shopData.get().getString("Options.lore", "")); + rotationData.get().set("Options.page", shopData.get().getInt("Options.page", 1)); + rotationData.save(); + + return rotationData; + } + + // 공용 데이터 반환 (재고, 가격, 미디안 등을 저장) + private static CustomConfig GetSharedData(String shopName) + { + if (sharedDataMap.containsKey(shopName)) + { + return sharedDataMap.get(shopName); + } else + { + CustomConfig data = new CustomConfig(); + data.setup("SharedData", rotationFolderName + "/" + shopName); + data.get().options().header("Do NOT edit this file manually."); + + + + data.get().options().copyDefaults(true); + data.save(); + + sharedDataMap.put(shopName, data); + + return data; + } + } + + // 공용 데이터에서 필요 없는 데이터 제거 (어느 로테이션 파일에도 없는 아이템들) + private static void CleanupSharedData(String shopName) + { + CustomConfig[] rotationDataList = rotationDataMap.get(shopName); + if (rotationDataList == null) + return; + + CustomConfig sharedData = GetSharedData(shopName); + if (sharedData == null) + return; + + for (String s : sharedData.get().getKeys(false)) + { + try + { + String matString = sharedData.get().getString(s + ".mat"); + ItemMeta meta = (ItemMeta) sharedData.get().get(s + ".itemStack"); + + boolean isExist = false; + for (CustomConfig rotation : rotationDataList) + { + if (rotation == null) + continue; + + for (String itemIdx : rotation.get().getKeys(false)) + { + try + { + Integer.parseInt(itemIdx); + + String otherMat = rotation.get().getString(itemIdx + ".mat"); + ItemMeta otherMeta = (ItemMeta) rotation.get().get(itemIdx + ".itemStack"); + + if (matString.equals(otherMat)) + { + if (meta != null && meta.equals(otherMeta)) + { + isExist = true; + break; + } else if (otherMeta == null) + { + isExist = true; + break; + } + } + } catch (Exception ignore) + { + } + } + + if (isExist) + break; + } + + if (!isExist) + { + sharedData.get().set(s, null); + } + } catch (Exception ignore) + { + } + } + + sharedData.save(); + } + + // ===== [ Public ] ===== + + // 원래 있는 로테이션 yml 파일들을 읽음 + public static void ReadRotationYMLFiles(String shopName) + { + rotationDataMap.remove(shopName); + + File rotationFolder = new File(rotationDataPath); + rotationFolder.mkdir(); + File subFolder = new File(rotationDataPath, shopName); + subFolder.mkdir(); + + CustomConfig[] rotationDataList = new CustomConfig[7]; + + String path = rotationDataPath + "/" + shopName; + File[] listOfFiles = new File(path).listFiles(); + + if (listOfFiles == null) + return; + + for (File f : listOfFiles) + { + if (!f.getName().contains(shopName)) + continue; + + String[] temp = FilenameUtils.getBaseName(f.getName()).split("_"); + if (temp.length != 2) + continue; + + try + { + int idx = Integer.parseInt(temp[1]) - 1; + if(idx < 0 || idx >= 7) + continue; + + CustomConfig cc = new CustomConfig(); + cc.setup(f.getName().replace(".yml", ""), rotationFolderName + "/" + shopName); + rotationDataList[idx] = cc; + } catch (Exception ignore) + { + + } + } + + rotationDataMap.put(shopName, rotationDataList); + } + + // 로테이션 YML 파일의 수 반환 + public static int GetRotationYmlFileCount(String shopName) + { + if (!rotationDataMap.containsKey(shopName)) + return 0; + + int count = 0; + CustomConfig[] data = rotationDataMap.get(shopName); + for (CustomConfig cc : data) + { + if (cc != null) + count++; + } + + return count; + } + + // 새 로테이션 데이터를 추가 + public static void AddNewRotationData(String shopName, int rotationIndex, boolean isEmpty) + { + CustomConfig data; + if (isEmpty) + { + data = CreateEmptyRotationData(shopName, rotationIndex); + } else + { + data = CreateRotationDataFromCurrentShop(shopName, rotationIndex); + } + + if (!rotationDataMap.containsKey(shopName)) + { + CustomConfig[] dataList = new CustomConfig[7]; + dataList[rotationIndex] = data; + rotationDataMap.put(shopName, dataList); + } else + { + rotationDataMap.get(shopName)[rotationIndex] = data; + } + } + + // 현재 로테이션 인덱스 반환 + public static int GetCurrentRotationIndex(String shopName) + { + if (!ShopUtil.shopConfigFiles.containsKey(shopName)) + return -1; + + CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName); + if (shopData == null) + return -1; + + return shopData.get().getInt("Options.Rotation.Current", -1); + } + + // 로테이션 데이터를 상점 데이터에 맞춰 갱신함 + public static void UpdateCurrentRotationData(String shopName, int tradeIdx) + { + int currentRotationIndex = GetCurrentRotationIndex(shopName); + if (currentRotationIndex == -1) + return; + + CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName); + if (shopData == null) + return; + + CustomConfig[] rotationYmlFiles = rotationDataMap.get(shopName); + if (rotationYmlFiles == null) + return; + + CustomConfig rotationData = rotationYmlFiles[currentRotationIndex]; + if (rotationData == null) + return; + + if (shopData.get().contains(String.valueOf(tradeIdx))) + { + rotationData.get().set(tradeIdx + ".mat", shopData.get().get(tradeIdx + ".mat")); + rotationData.get().set(tradeIdx + ".itemStack", shopData.get().get(tradeIdx + ".itemStack")); + + // merchandise + if (shopData.get().contains(tradeIdx + ".value")) + { + rotationData.get().set(tradeIdx + ".type", "item"); + } + // deco + else + { + rotationData.get().set(tradeIdx + ".type", "deco"); + } + } else + { + rotationData.get().set(String.valueOf(tradeIdx), null); + } + + rotationData.save(); + } + + // 공용 데이터 갱신 (상점이 로테이션 되기 직전에 호출되야함) + public static void SaveSharedData(String shopName) + { + CustomConfig sharedData = GetSharedData(shopName); + if (sharedData == null) + return; + + CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName); + if (shopData == null) + return; + + for (String s : shopData.get().getKeys(false)) + { + try + { + Integer.parseInt(s); // 아이템 데이터만 가져오기 위함 + + // merchandise + if (shopData.get().contains(s + ".value")) + { + String mat = shopData.get().getString(s + ".mat"); + String meta = shopData.get().getString(s + ".itemStack"); + String hash = CreateHashString(mat, meta); + + sharedData.get().set(hash, shopData.get().get(s)); + } + } catch (Exception ignore) + { + } + } + + sharedData.save(); + } + + // 다음 로테이션 적용 + public static void ApplyNextRotation(String shopName, long period) + { + CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName); + if (shopData == null) + { + StopRotationTask(shopName); + DynamicShop.console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + " Rotation task stopped because the shop does not exist. shopName: " + shopName); + return; + } + + int dataCount = GetRotationYmlFileCount(shopName); + if (dataCount <= 0) + { + DisableRotation(shopName); + + DynamicShop.console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + " Stop rotation task due to missing files. shopName: " + shopName); + return; + } + + int oldIdx = GetCurrentRotationIndex(shopName); + int newIdx = FindNextRotationIndex(shopName, oldIdx); + if (newIdx == -1) + { + DisableRotation(shopName); + + DynamicShop.console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + " Stop rotation task due to missing files. shopName: " + shopName); + return; + } + + // 상점에 로테이션 적용 + ApplyRotation(shopName, oldIdx, newIdx); + + shopData.get().set("Options.Rotation.NextTimer", System.currentTimeMillis() + TickToMilliSeconds(period)); + shopData.get().set("Options.Rotation.Period", period); + shopData.save(); + + // 쉐어드 데이터에서 불필요한 내용 지움 + CleanupSharedData(shopName); + } + + public static int FindNextRotationIndex(String shopName, int currentIndex) + { + if (GetRotationYmlFileCount(shopName) == 0) + return -1; + + CustomConfig[] data = rotationDataMap.get(shopName); + int searchIdx = currentIndex + 1; + if (searchIdx >= 7) + searchIdx = 0; + + for (int i = 0; i < 7; i++) + { + if (data[searchIdx] != null) + return searchIdx; + + searchIdx++; + if (searchIdx >= 7) + searchIdx = 0; + } + + return -1; + } + + // 로테이션 적용 + public static void ApplyRotation(String shopName, int oldIdx, int rotationIdx) + { + CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName); + CustomConfig[] rotationDatas = rotationDataMap.get(shopName); + if (shopData == null || rotationDatas == null || rotationIdx == -1 || rotationDatas[rotationIdx] == null) + return; + + // 지금 상점의 상태를 로테이션 데이터에 저장함. + if(oldIdx != -1 && oldIdx != rotationIdx) + CreateRotationDataFromCurrentShop(shopName, oldIdx); + + // 현재 상점의 데이터를 쉐어드 데이터에 저장 + SaveSharedData(shopName); + + // 상점을 비움 + for (String s : shopData.get().getKeys(false)) + { + try + { + Integer.parseInt(s); // 아이템 데이터만 가져오기 위함 + shopData.get().set(s, null); // 기존 데이터를 다 삭제함. + } catch (Exception ignore) + { + + } + } + + CustomConfig rotationData = rotationDatas[rotationIdx]; + + // 로테이션 전용 타이틀과 로어 적용 + rotationData.reload(); + shopData.get().set("Options.title", rotationData.get().getString("Options.title", shopName)); + shopData.get().set("Options.lore", rotationData.get().getString("Options.lore", "")); + shopData.get().set("Options.page", rotationData.get().getInt("Options.page", 1)); + + // 쉐어드 데이터 기반으로 상품을 다시 채워넣음 + CustomConfig sharedData = new CustomConfig(); + sharedData.setup("SharedData", rotationFolderName + "/" + shopName); + + int missingDataCount = 0; + + for (String slotIndex : rotationData.get().getKeys(false)) + { + try + { + Integer.parseInt(slotIndex); // 아이템 데이터만 가져오기 위함 + + if (rotationData.get().getString(slotIndex + ".type").equals("item")) + { + String mat = rotationData.get().getString(slotIndex + ".mat"); + String meta = rotationData.get().getString(slotIndex + ".itemStack"); + String hash = CreateHashString(mat, meta); + if (sharedData.get().contains(hash)) + { + shopData.get().set(slotIndex, sharedData.get().get(hash)); + } + else + { + shopData.get().set(slotIndex + ".mat", rotationData.get().getString(slotIndex + ".mat")); + shopData.get().set(slotIndex + ".itemStack", rotationData.get().getString(slotIndex + ".itemStack")); + missingDataCount++; + } + } else + { + shopData.get().set(slotIndex + ".mat", rotationData.get().get(slotIndex + ".mat")); + } + } catch (Exception ignore) + { + + } + } + + if (missingDataCount != 0) + { + DynamicShop.console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + t(null, "MESSAGE.ROTATION_SHARED_DATA_MISSING").replace("{0}", shopName).replace("{1}", String.valueOf(missingDataCount))); + } + + shopData.get().set("Options.Rotation.Current", rotationIdx); + shopData.save(); + + //DynamicShop.console.sendMessage("로테이션 적용됨: " + shopName + " / " + rotationIdx + "번"); + } + + public static void DisableRotation(String shopName) + { + CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName); + if (shopData != null) + { + shopData.get().set("Options.Rotation", null); + shopData.save(); + } + + StopRotationTask(shopName); + + rotationDataMap.remove(shopName); + } + + // ===== [ Task ] ===== + + public static final ConcurrentHashMap RotationTaskMap = new ConcurrentHashMap<>(); + + public static void StartRotationTask(String shopName, long delay, long period) + { + StopRotationTask(shopName); + + Integer i = Bukkit.getScheduler().runTaskTimer(DynamicShop.plugin, () -> ApplyNextRotation(shopName, period), delay, period).getTaskId(); + RotationTaskMap.put(shopName, i); + + //DynamicShop.console.sendMessage("로테이션 시작됨: " + shopName + " / 딜레이: " + (delay / 20) + " / 간격: " + (period / 20)); + } + + private static void StopRotationTask(String shopName) + { + if (RotationTaskMap.containsKey(shopName)) + { + Bukkit.getScheduler().cancelTask(RotationTaskMap.get(shopName)); + RotationTaskMap.remove(shopName); + } + } + + public static void Reload() + { + for (String s : RotationTaskMap.keySet()) + { + sharedDataMap.remove(s); + rotationDataMap.remove(s); + } + + RestartAllRotationTask(); + } + + public static void RestartAllRotationTask() + { + for (String s : RotationTaskMap.keySet()) + { + StopRotationTask(s); + } + + RotationTaskMap.clear(); + + for (String shopName : ShopUtil.shopConfigFiles.keySet()) + { + RestartRotationTask(shopName); + } + } + + public static void RestartRotationTask(String shopName) + { + CustomConfig shopData = ShopUtil.shopConfigFiles.get(shopName); + if (shopData == null) + return; + + if (shopData.get().contains("Options.Rotation")) + { + ReadRotationYMLFiles(shopName); + GetSharedData(shopName); // 없다면 새로 생성하게됨. + + long currentTime = System.currentTimeMillis(); + long timeLeft = shopData.get().getLong("Options.Rotation.NextTimer", currentTime) - currentTime; + timeLeft = MilliSecondsToTick(timeLeft); + if (timeLeft < 0) + { + timeLeft = 0; + } + else + { + int rotIdx = shopData.get().getInt("Options.Rotation.Current"); + ApplyRotation(shopName, rotIdx, rotIdx); + } + + StartRotationTask(shopName, timeLeft, shopData.get().getLong("Options.Rotation.Period", 20 * 60 * 60 * 24)); + } + } + + // ===== [ Event ] ===== + + public static void OnShopNameChanged(String oldName, String newName) + { + StopRotationTask(oldName); + + sharedDataMap.remove(oldName); + rotationDataMap.remove(oldName); + + String oldPath = rotationDataPath + "/" + oldName; + File subFolder = new File(oldPath); + if (subFolder.exists()) + { + File[] listOfFiles = new File(oldPath).listFiles(); + if (listOfFiles != null) + { + File newFolder = new File(rotationDataPath, newName); + newFolder.mkdir(); + + boolean err = false; + for (File f : listOfFiles) + { + File dest = new File(f.getPath().replace(oldName, newName)); + + if (!f.renameTo(dest)) + { + DynamicShop.console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + " Failed to copy file : " + f.getPath()); + err = true; + } + } + + if (!err) + { + try + { + FileUtils.deleteDirectory(new File(oldPath)); + } catch (IOException e) + { + DynamicShop.console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + " Failed to delete folder : " + oldPath); + } + } + } + } + + ReadRotationYMLFiles(newName); + GetSharedData(newName); + + RestartRotationTask(newName); + } + + public static void OnShopCopy(String oldName, String newName) + { + String oldPath = rotationDataPath + "/" + oldName; + File subFolder = new File(oldPath); + if (subFolder.exists()) + { + File[] listOfFiles = new File(oldPath).listFiles(); + if (listOfFiles != null) + { + File newFolder = new File(rotationDataPath, newName); + newFolder.mkdir(); + + for (File f : listOfFiles) + { + try + { + File dest = new File(f.getPath().replace(oldName, newName)); + Files.copy(f.toPath(), dest.toPath()); + } + catch (Exception e) + { + DynamicShop.console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + " Failed to copy file : " + f.getPath()); + } + } + } + } + + RestartRotationTask(newName); + } + + public static void OnShopDeleted(String shopName) + { + StopRotationTask(shopName); + + sharedDataMap.remove(shopName); + rotationDataMap.remove(shopName); + + String path = rotationDataPath + "/" + shopName; + + try + { + FileUtils.deleteDirectory(new File(path)); + } catch (IOException e) + { + DynamicShop.console.sendMessage(Constants.DYNAMIC_SHOP_PREFIX + " Failed to delete folder : " + path); + } + } + + public static void DeleteRotationFile(String shopName, int rotationIdx) + { + if (!rotationDataMap.containsKey(shopName)) + return; + + CustomConfig data = rotationDataMap.get(shopName)[rotationIdx]; + if (data != null) + data.delete(); + + rotationDataMap.get(shopName)[rotationIdx] = null; + } + + public static void OnRotationFileSlotMoved(String shopName, int oldIdx, int newIdx) + { + CustomConfig[] data = rotationDataMap.get(shopName); + if(data == null) + return; + + if (data[oldIdx] == null || data[newIdx] != null) + return; + + data[oldIdx].rename(shopName + "_" + (newIdx + 1)); + data[newIdx] = data[oldIdx]; + data[oldIdx] = null; + } +} diff --git a/src/main/java/me/sat7/dynamicshop/utilities/ShopUtil.java b/src/main/java/me/sat7/dynamicshop/utilities/ShopUtil.java index 655e614..6b0f875 100644 --- a/src/main/java/me/sat7/dynamicshop/utilities/ShopUtil.java +++ b/src/main/java/me/sat7/dynamicshop/utilities/ShopUtil.java @@ -1,12 +1,14 @@ package me.sat7.dynamicshop.utilities; import java.io.File; +import java.io.IOException; import java.text.SimpleDateFormat; import java.util.*; import lombok.NonNull; import me.sat7.dynamicshop.models.DSItem; import me.sat7.dynamicshop.transactions.Calc; +import org.apache.commons.io.FileUtils; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.command.CommandSender; @@ -391,6 +393,8 @@ public static boolean addItemToShop(String shopName, int idx, DSItem dsItem) data.save(); + RotationUtil.UpdateCurrentRotationData(shopName, idx); + return true; } catch (Exception e) @@ -470,6 +474,8 @@ public static void editShopItem(String shopName, int idx, DSItem dsItem) } data.save(); + + RotationUtil.UpdateCurrentRotationData(shopName, idx); } // 상점에서 아이탬 제거 @@ -483,6 +489,8 @@ public static void removeItemFromShop(String shopName, int idx) data.get().set(String.valueOf(idx), null); data.save(); + + RotationUtil.UpdateCurrentRotationData(shopName, idx); } // 상점 페이지 삽입 @@ -630,6 +638,7 @@ public static void renameShop(String shopName, String newName) shopDirty.put(newName, false); shopDirty.remove(shopName); + RotationUtil.OnShopNameChanged(shopName, newName); UserUtil.OnRenameShop(shopName, newName); } @@ -643,6 +652,8 @@ public static void copyShop(String shopName, String newName) data.get().set("Options.title", newName); shopConfigFiles.put(newName, data); shopDirty.put(newName, false); + + RotationUtil.OnShopCopy(shopName, newName); } // 상점 병합 @@ -928,7 +939,7 @@ public static String[] FindTheBestShopToSell(Player player, ItemStack itemStack) } // 여러 재화로 취급중인 경우 지원 안함. - if (currency.isEmpty()) + if (currency.isEmpty()) { currency = ShopUtil.GetCurrency(data); } @@ -1402,6 +1413,78 @@ public static void SortShopData(String shopName) data.save(); } + public static void ShopYMLBackup() + { + File[] listOfFiles = new File(DynamicShop.plugin.getDataFolder() + "/Shop").listFiles(); + if(listOfFiles != null) + { + long time = System.currentTimeMillis(); + for (File f : listOfFiles) + { + String path = DynamicShop.plugin.getDataFolder() + "/Shop_Backup/" + time + "/"; + File newFile = new File(path + f.getName()); + try + { + FileUtils.copyFile(f, newFile); + } catch (IOException e) + { + e.printStackTrace(); + } + } + } + + ShopYMLBackupCull(); + } + + public static void ShopYMLBackupCull() + { + File[] listOfFiles = new File(DynamicShop.plugin.getDataFolder() + "/Shop_Backup").listFiles(); + if(listOfFiles != null) + { + for(File f : listOfFiles) + { + int ageMins = (int) (System.currentTimeMillis() - f.lastModified()) / 60000; + if (ageMins >= ConfigUtil.GetShopYmlBackup_CullAgeMinutes()) + { + try + { + FileUtils.deleteDirectory(f); + } catch (IOException e) + { + //e.printStackTrace(); + } + } + } + } + } + + public static int GetShopItemCount(String shopName, Boolean excludeInfiniteStock, Boolean excludeFixedPrice) + { + CustomConfig data = shopConfigFiles.get(shopName); + if(data == null) + return -1; + + int count = 0; + for (String item : data.get().getKeys(false)) + { + try + { + int i = Integer.parseInt(item); // options에 대해 적용하지 않기 위해. + if (!data.get().contains(item + ".value")) continue; // 장식용은 스킵 + + int stock = data.get().getInt(item + ".stock"); + if (excludeInfiniteStock && stock < 1) continue; // 무한재고에 대해서는 스킵 + int oldMedian = data.get().getInt(item + ".median"); + if (excludeFixedPrice && oldMedian < 1) continue; // 고정가 상품에 대해서는 스킵 + + count++; + } + catch (Exception ignore){} + } + + return count; + } + public static int RandomStockFluctuation(Random generator, int stock, int median, double strength) { boolean down = generator.nextBoolean(); diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index e0fd01d..2a4033d 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -30,3 +30,6 @@ Log.LogFileNameFormat: MM_dd_yyyy Log.CullLogs: true Log.LogCullAgeMinutes: 7200 Log.LogCullTimeMinutes: 120 +ShopYmlBackup.Enable: true +ShopYmlBackup.IntervalMinutes: 60 +ShopYmlBackup.CullAgeMinutes: 300 \ No newline at end of file diff --git a/src/test/java/me/sat7/dynamicshop/files/FileUtil.java b/src/test/java/me/sat7/dynamicshop/files/FileUtil.java index 8ed75a4..ebaa3b8 100644 --- a/src/test/java/me/sat7/dynamicshop/files/FileUtil.java +++ b/src/test/java/me/sat7/dynamicshop/files/FileUtil.java @@ -8,7 +8,6 @@ public final class FileUtil { private FileUtil() { } - public static CustomConfig generateOutOfStockCustomConfig() { FileConfiguration config = new YamlConfiguration(); diff --git a/src/test/java/me/sat7/dynamicshop/transactions/TransactionTests.java b/src/test/java/me/sat7/dynamicshop/transactions/TransactionTests.java index 6595556..809bafc 100644 --- a/src/test/java/me/sat7/dynamicshop/transactions/TransactionTests.java +++ b/src/test/java/me/sat7/dynamicshop/transactions/TransactionTests.java @@ -1,7 +1,16 @@ package me.sat7.dynamicshop.transactions; +//import static org.junit.Assert.assertEquals; +// +//import org.junit.Before; +//import org.junit.Test; +// +//import me.sat7.dynamicshop.files.FileUtil; +//import me.sat7.dynamicshop.utilities.ShopUtil; + public class TransactionTests { -/* + + /* @Before public void setup() { ShopUtil.ccShop = FileUtil.generateOutOfStockCustomConfig(); @@ -44,5 +53,5 @@ public void staticMedianOrStockShouldCalcPrice() { double amountBuy = Calc.calcTotalCost("default", "COBBLESTONE", 3); assertEquals(30, amountBuy, 0.01); } -*/ + */ }