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);
}
-*/
+ */
}