diff --git a/pom.xml b/pom.xml index 9d1565e5..ee82c084 100644 --- a/pom.xml +++ b/pom.xml @@ -73,37 +73,60 @@ UTF-8 yyyy-MM-dd'T'HH.mm.ss ${maven.build.timestamp} - + org/shanerx/tradeshop + src/main/resources + + + + + + + + target/output-files + + expiration - builddate.yml + builddate.yml + + + + clean + + clean + + + + skipDocs + + true - install - ${project.artifactId}-${project.version}-${maven.build.timestamp} + ${doClean} install + ${project.name}-${project.version}-${maven.build.timestamp} src/main/java . true - ${basedir}/src/main/resources/ + ${basedir}/${resourceDir} plugin.yml - ${builddate} + ${expirationFile} ./Lang false - ${basedir}/src/main/resources/Lang + ${basedir}/${resourceDir}/Lang @@ -111,7 +134,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.0 + 3.8.1 1.8 1.8 @@ -120,25 +143,34 @@ org.apache.maven.plugins maven-shade-plugin - 3.1.0 + 3.3.0 + ${outputDir}/PreShade false org.bstats:* - de.themoep:* + de.themoep:* org.bstats - org.shanerx.tradeshop + ${project.groupId}.${project.artifactId}.bstats de.themoep - io.github.apfelcreme.Pipes.libs + ${project.groupId}.${project.artifactId} + + + *:* + + META-INF/maven/** + + + @@ -152,17 +184,18 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.3.1 + 3.4.0 false + ${outputDir}/JavaDocs - ${skip_docs} + ${skipDocs} - org/shanerx/tradeshop/TradeShop.java - org/shanerx/tradeshop/commands/** - org/shanerx/tradeshop/listeners/** - org/shanerx/tradeshop/utils/Updater.java - org/shanerx/tradeshop/utils/BukkitVersion.java + ${pluginDir}/TradeShop.java + ${pluginDir}/commands/** + ${pluginDir}/shop/listeners/** + ${pluginDir}/utils/versionmanagement/** + ${pluginDir}/player/JoinEventListener.java @@ -212,15 +245,14 @@ de.themoep inventorygui - - 1.4.2-SNAPSHOT + 1.5-SNAPSHOT compile org.apache.maven.plugins maven-javadoc-plugin - 3.3.2 + 3.4.0 maven-plugin diff --git a/src/main/java/org/shanerx/tradeshop/commands/CommandTabCompleter.java b/src/main/java/org/shanerx/tradeshop/commands/CommandTabCompleter.java index 88cbaee8..7e9e513f 100644 --- a/src/main/java/org/shanerx/tradeshop/commands/CommandTabCompleter.java +++ b/src/main/java/org/shanerx/tradeshop/commands/CommandTabCompleter.java @@ -30,6 +30,7 @@ import org.bukkit.entity.Player; import org.shanerx.tradeshop.TradeShop; import org.shanerx.tradeshop.data.config.Setting; +import org.shanerx.tradeshop.player.ShopRole; import org.shanerx.tradeshop.shop.Shop; import org.shanerx.tradeshop.shop.ShopType; import org.shanerx.tradeshop.utils.Utils; @@ -99,7 +100,7 @@ public List fillShopPlayer() { } Shop shop = Shop.loadShop(s); - return shop.getUserNames(); + return shop.getUserNames(ShopRole.MANAGER, ShopRole.MEMBER); } return Collections.EMPTY_LIST; diff --git a/src/main/java/org/shanerx/tradeshop/commands/Commands.java b/src/main/java/org/shanerx/tradeshop/commands/Commands.java index 1ba23831..67b78cb5 100644 --- a/src/main/java/org/shanerx/tradeshop/commands/Commands.java +++ b/src/main/java/org/shanerx/tradeshop/commands/Commands.java @@ -63,8 +63,8 @@ public enum Commands { ADD_COST(Lists.newArrayList("addCost"), Permissions.NONE, 1, 3, true, "Add cost to shop", "/tradeshop $cmd$ [Amount] [Material]"), SET_PRODUCT(Lists.newArrayList("setProduct"), Permissions.NONE, 1, 3, true, "Set product of shop ", "/tradeshop $cmd$ [Amount] [Material]"), SET_COST(Lists.newArrayList("setCost"), Permissions.NONE, 1, 3, true, "Set cost of shop", "/tradeshop $cmd$ [Amount] [Material]"), - REMOVE_PRODUCT(Lists.newArrayList("removeProduct", "delProduct"), Permissions.NONE, 2, 2, true, "Removes a product from the shop", "/tradeshop $cmd$ "), - REMOVE_COST(Lists.newArrayList("removeCost", "delCost"), Permissions.NONE, 2, 2, true, "Removes a product from the shop", "/tradeshop $cmd$ "), + REMOVE_PRODUCT(Lists.newArrayList("removeProduct", "delProduct"), Permissions.NONE, 1, 2, true, "Removes a product from the shop", "/tradeshop $cmd$ "), + REMOVE_COST(Lists.newArrayList("removeCost", "delCost"), Permissions.NONE, 1, 2, true, "Removes a product from the shop", "/tradeshop $cmd$ "), LIST_PRODUCT(Lists.newArrayList("listProduct"), Permissions.NONE, 1, 1, true, "Lists the products in the shop", "/tradeshop $cmd$"), LIST_COST(Lists.newArrayList("listCost"), Permissions.NONE, 1, 1, true, "Lists the costs in a shop", "/tradeshop $cmd$"), diff --git a/src/main/java/org/shanerx/tradeshop/commands/commandrunners/AdminCommand.java b/src/main/java/org/shanerx/tradeshop/commands/commandrunners/AdminCommand.java index c9c732fb..1d1f4314 100644 --- a/src/main/java/org/shanerx/tradeshop/commands/commandrunners/AdminCommand.java +++ b/src/main/java/org/shanerx/tradeshop/commands/commandrunners/AdminCommand.java @@ -66,7 +66,7 @@ public void reload() { return; } - command.sendMessage(Setting.MESSAGE_PREFIX.getString() + "&6The configuration files have been reloaded!"); + command.sendMessage(Setting.MESSAGE_PREFIX.getString().trim() + "&6The configuration files have been reloaded!"); Bukkit.getPluginManager().callEvent(new TradeShopReloadEvent(plugin, command.getSender())); } diff --git a/src/main/java/org/shanerx/tradeshop/commands/commandrunners/ShopItemCommand.java b/src/main/java/org/shanerx/tradeshop/commands/commandrunners/ShopItemCommand.java index 7281bfd7..c72933af 100644 --- a/src/main/java/org/shanerx/tradeshop/commands/commandrunners/ShopItemCommand.java +++ b/src/main/java/org/shanerx/tradeshop/commands/commandrunners/ShopItemCommand.java @@ -81,10 +81,12 @@ public void removeSide() { if (shop == null) return; - int index = 0; + int index; if (isInt(command.getArgAt(1))) { index = Integer.parseInt(command.getArgAt(1)) - 1; + } else if (shop.getSideList(side).size() == 1) { + index = 0; } else { Message.INVALID_ARGUMENTS.sendMessage(pSender); return; diff --git a/src/main/java/org/shanerx/tradeshop/data/config/Message.java b/src/main/java/org/shanerx/tradeshop/data/config/Message.java index 83013b8e..44bbf55e 100644 --- a/src/main/java/org/shanerx/tradeshop/data/config/Message.java +++ b/src/main/java/org/shanerx/tradeshop/data/config/Message.java @@ -239,7 +239,7 @@ public String toString() { } public String getPrefixed() { - return PLUGIN.getMessageManager().colour(Setting.MESSAGE_PREFIX.getString() + " " + this); + return PLUGIN.getMessageManager().colour(Setting.MESSAGE_PREFIX.getString().trim() + " " + this); } diff --git a/src/main/java/org/shanerx/tradeshop/data/config/Setting.java b/src/main/java/org/shanerx/tradeshop/data/config/Setting.java index 3b39c1c7..86a6086e 100644 --- a/src/main/java/org/shanerx/tradeshop/data/config/Setting.java +++ b/src/main/java/org/shanerx/tradeshop/data/config/Setting.java @@ -57,7 +57,7 @@ public enum Setting { UNLIMITED_ADMIN(SettingSection.SYSTEM_OPTIONS, "unlimited-admin", false), // Language Options - MESSAGE_PREFIX(SettingSection.LANGUAGE_OPTIONS, "message-prefix", "&a[&eTradeShop&a] "), + MESSAGE_PREFIX(SettingSection.LANGUAGE_OPTIONS, "message-prefix", "&a[&eTradeShop&a]"), SHOP_GOOD_COLOUR(SettingSection.LANGUAGE_OPTIONS, "shop-good-colour", "&2"), SHOP_INCOMPLETE_COLOUR(SettingSection.LANGUAGE_OPTIONS, "shop-incomplete-colour", "&7"), diff --git a/src/main/java/org/shanerx/tradeshop/data/storage/DataStorage.java b/src/main/java/org/shanerx/tradeshop/data/storage/DataStorage.java index 23afbc86..429c45aa 100644 --- a/src/main/java/org/shanerx/tradeshop/data/storage/DataStorage.java +++ b/src/main/java/org/shanerx/tradeshop/data/storage/DataStorage.java @@ -25,11 +25,15 @@ package org.shanerx.tradeshop.data.storage; +import org.apache.commons.lang.NotImplementedException; import org.bukkit.Chunk; import org.bukkit.World; -import org.shanerx.tradeshop.data.storage.Json.LinkageConfiguration; -import org.shanerx.tradeshop.data.storage.Json.PlayerConfiguration; -import org.shanerx.tradeshop.data.storage.Json.ShopConfiguration; +import org.shanerx.tradeshop.data.storage.Json.JsonLinkageConfiguration; +import org.shanerx.tradeshop.data.storage.Json.JsonPlayerConfiguration; +import org.shanerx.tradeshop.data.storage.Json.JsonShopConfiguration; +import org.shanerx.tradeshop.data.storage.sqlite.SQLiteLinkageConfiguration; +import org.shanerx.tradeshop.data.storage.sqlite.SQLitePlayerConfiguration; +import org.shanerx.tradeshop.data.storage.sqlite.SQLiteShopConfiguration; import org.shanerx.tradeshop.player.PlayerSetting; import org.shanerx.tradeshop.shop.Shop; import org.shanerx.tradeshop.shoplocation.ShopChunk; @@ -54,56 +58,24 @@ public void reload(DataType dataType) { } public Shop loadShopFromSign(ShopLocation sign) { - switch (dataType) { - case FLATFILE: - return new ShopConfiguration(new ShopChunk(sign.getChunk())).load(sign); - case SQLITE: - return null; //TODO add SQLITE support - } - return null; + return getShopConfiguration(sign.getChunk()).load(sign); } public Shop loadShopFromStorage(ShopLocation chest) { - switch (dataType) { - case FLATFILE: - return loadShopFromSign(new LinkageConfiguration(chest.getWorld()).getLinkedShop(chest)); - case SQLITE: - return null; //TODO add SQLITE support - } - return null; + return loadShopFromSign(getLinkageConfiguration(chest.getWorld()).getLinkedShop(chest)); } public void saveShop(Shop shop) { - switch (dataType) { - case FLATFILE: - new ShopConfiguration(new ShopChunk(shop.getShopLocation().getChunk())).save(shop); - break; - case SQLITE: - //TODO add SQLITE support - break; - } + getShopConfiguration(shop.getShopLocation().getChunk()).save(shop); } public void removeShop(Shop shop) { - switch (dataType) { - case FLATFILE: - new ShopConfiguration(new ShopChunk(shop.getShopLocation().getChunk())).remove(shop.getShopLocationAsSL()); - new LinkageConfiguration(shop.getShopLocationAsSL().getWorld()).removeShop(shop.getShopLocationAsSL()); - break; - case SQLITE: - //TODO add SQLITE support - break; - } + getShopConfiguration(shop.getShopLocation().getChunk()).remove(shop.getShopLocationAsSL()); + getLinkageConfiguration(shop.getShopLocationAsSL().getWorld()).removeShop(shop.getShopLocationAsSL()); } public int getShopCountInChunk(Chunk chunk) { - switch (dataType) { - case FLATFILE: - return new ShopConfiguration(new ShopChunk(chunk)).size(); - case SQLITE: - return 0; //TODO add SQLITE support - } - return 0; + return getShopConfiguration(chunk).size(); } public int getShopCountInWorld(World world) { @@ -114,83 +86,79 @@ public int getShopCountInWorld(World world) { if (folder.exists() && folder.listFiles() != null) { for (File file : folder.listFiles()) { if (file.getName().contains(world.getName())) - count += new ShopConfiguration(ShopChunk.deserialize(file.getName().replace(".json", ""))).size(); + count += new JsonShopConfiguration(ShopChunk.deserialize(file.getName().replace(".json", ""))).size(); } } break; case SQLITE: //TODO add SQLITE support - break; + throw new NotImplementedException("SQLITE for getShopCountInWorld has not been implemented yet."); } return count; } public PlayerSetting loadPlayer(UUID uuid) { - PlayerSetting playerSetting = null; - switch (dataType) { - case FLATFILE: - playerSetting = new PlayerConfiguration(uuid).load(); - break; - case SQLITE: - //TODO add SQLITE support - break; - } + PlayerSetting playerSetting = getPlayerConfiguration(uuid).load(); //If playerSetting data not find create new and return return playerSetting != null ? playerSetting : new PlayerSetting(uuid); } public void savePlayer(PlayerSetting playerSetting) { - switch (dataType) { - case FLATFILE: - new PlayerConfiguration(playerSetting.getUuid()).save(playerSetting); - break; - case SQLITE: - //TODO add SQLITE support - break; - } + getPlayerConfiguration(playerSetting.getUuid()).save(playerSetting); } public void removePlayer(PlayerSetting playerSetting) { - switch (dataType) { - case FLATFILE: - new PlayerConfiguration(playerSetting.getUuid()).remove(); - break; - case SQLITE: - //TODO add SQLITE support - break; - } + getPlayerConfiguration(playerSetting.getUuid()).remove(); } public ShopLocation getChestLinkage(ShopLocation chestLocation) { - switch (dataType) { + return getLinkageConfiguration(chestLocation.getWorld()).getLinkedShop(chestLocation); + } + + public void addChestLinkage(ShopLocation chestLocation, ShopLocation shopLocation) { + getLinkageConfiguration(chestLocation.getWorld()).add(chestLocation, shopLocation); + } + + public void removeChestLinkage(ShopLocation chestLocation) { + getLinkageConfiguration(chestLocation.getWorld()).removeChest(chestLocation); + } + + protected PlayerConfiguration getPlayerConfiguration(UUID uuid) { + switch(dataType) { case FLATFILE: - return new LinkageConfiguration(chestLocation.getWorld()).getLinkedShop(chestLocation); - case SQLITE: - //TODO add SQLITE support - break; + return new JsonPlayerConfiguration(uuid); + case SQLITE: + return new SQLitePlayerConfiguration(uuid); + default: + throw new NotImplementedException("Data storage type " + dataType + " has not been implemented yet."); } + } - return null; + protected ShopConfiguration getShopConfiguration(Chunk chunk) { + return getShopConfiguration(new ShopChunk(chunk)); } - public void addChestLinkage(ShopLocation chestLocation, ShopLocation shopLocation) { + protected ShopConfiguration getShopConfiguration(ShopChunk chunk) { switch (dataType) { case FLATFILE: - new LinkageConfiguration(chestLocation.getWorld()).add(chestLocation, shopLocation); + return new JsonShopConfiguration(chunk); case SQLITE: - //TODO add SQLITE support - break; + return new SQLiteShopConfiguration(chunk); + default: + throw new NotImplementedException("Data storage type " + dataType + " has not been implemented yet."); } } - public void removeChestLinkage(ShopLocation chestLocation) { + protected LinkageConfiguration getLinkageConfiguration(World w) { switch (dataType) { case FLATFILE: - new LinkageConfiguration(chestLocation.getWorld()).removeChest(chestLocation); + return new JsonLinkageConfiguration(w); case SQLITE: - //TODO add SQLITE support - break; + return new SQLiteLinkageConfiguration(w); + default: + throw new NotImplementedException("Data storage type " + dataType + " has not been implemented yet."); } } -} \ No newline at end of file +} + diff --git a/src/main/java/org/shanerx/tradeshop/data/storage/Json/LinkageConfiguration.java b/src/main/java/org/shanerx/tradeshop/data/storage/Json/JsonLinkageConfiguration.java similarity index 53% rename from src/main/java/org/shanerx/tradeshop/data/storage/Json/LinkageConfiguration.java rename to src/main/java/org/shanerx/tradeshop/data/storage/Json/JsonLinkageConfiguration.java index ce465e11..54f665b2 100644 --- a/src/main/java/org/shanerx/tradeshop/data/storage/Json/LinkageConfiguration.java +++ b/src/main/java/org/shanerx/tradeshop/data/storage/Json/JsonLinkageConfiguration.java @@ -27,6 +27,7 @@ import com.google.gson.reflect.TypeToken; import org.bukkit.World; +import org.shanerx.tradeshop.data.storage.LinkageConfiguration; import org.shanerx.tradeshop.shop.ShopChest; import org.shanerx.tradeshop.shoplocation.ShopLocation; @@ -35,15 +36,16 @@ import java.util.List; import java.util.Map; -public class LinkageConfiguration extends JsonConfiguration { +public class JsonLinkageConfiguration extends JsonConfiguration implements LinkageConfiguration { Map linkageData; - public LinkageConfiguration(World world) { + public JsonLinkageConfiguration(World world) { super(world.getName(), "chest_linkage"); load(); } + @Override public void load() { linkageData = gson.fromJson(jsonObj.get("linkage_data"), new TypeToken>() { }.getType()); @@ -51,58 +53,15 @@ public void load() { linkageData = new HashMap<>(); } + @Override public Map getLinkageData() { return linkageData; } - public ShopLocation getLinkedShop(ShopLocation chestLocation) { - String loc = chestLocation.serialize(); - - return linkageData.containsKey(loc) ? ShopLocation.deserialize(linkageData.get(chestLocation.serialize())) : null; - } - + @Override public void save() { jsonObj.add("linkage_data", gson.toJsonTree(linkageData)); saveFile(); } - - public int size() { - return linkageData.size(); - } - - public void add(ShopLocation chestLocation, ShopLocation shopLocation) { - if (ShopChest.isDoubleChest(chestLocation.getLocation().getBlock())) { - ShopLocation otherSideLocation = new ShopLocation(ShopChest.getOtherHalfOfDoubleChest(chestLocation.getLocation().getBlock()).getLocation()); - addLinkage(otherSideLocation, shopLocation); - } - - addLinkage(chestLocation, shopLocation); - save(); - } - - public void removeChest(ShopLocation chestLocation) { - linkageData.remove(chestLocation); - save(); - } - - public void removeShop(ShopLocation shopLocation) { - List removeChests = new ArrayList<>(); - String shopLoc = shopLocation.serialize(); - - linkageData.forEach((key, value) -> { - if (value.equals(shopLoc)) - removeChests.add(key); - }); - - removeChests.forEach((k) -> linkageData.remove(k)); - } - - private void addLinkage(ShopLocation chestLocation, ShopLocation shopLocation) { - if (linkageData.containsKey(chestLocation.serialize())) - linkageData.replace(chestLocation.serialize(), shopLocation.serialize()); - else - linkageData.put(chestLocation.serialize(), shopLocation.serialize()); - } - } diff --git a/src/main/java/org/shanerx/tradeshop/data/storage/Json/PlayerConfiguration.java b/src/main/java/org/shanerx/tradeshop/data/storage/Json/JsonPlayerConfiguration.java similarity index 89% rename from src/main/java/org/shanerx/tradeshop/data/storage/Json/PlayerConfiguration.java rename to src/main/java/org/shanerx/tradeshop/data/storage/Json/JsonPlayerConfiguration.java index b67f9987..8bd4a946 100644 --- a/src/main/java/org/shanerx/tradeshop/data/storage/Json/PlayerConfiguration.java +++ b/src/main/java/org/shanerx/tradeshop/data/storage/Json/JsonPlayerConfiguration.java @@ -26,22 +26,24 @@ package org.shanerx.tradeshop.data.storage.Json; import com.google.gson.reflect.TypeToken; +import org.shanerx.tradeshop.data.storage.PlayerConfiguration; import org.shanerx.tradeshop.player.PlayerSetting; import java.util.Map; import java.util.UUID; -public class PlayerConfiguration extends JsonConfiguration { +public class JsonPlayerConfiguration extends JsonConfiguration implements PlayerConfiguration { private final transient UUID playerUUID; private transient PlayerSetting playerSetting; - public PlayerConfiguration(UUID uuid) { + public JsonPlayerConfiguration(UUID uuid) { super("Players", uuid.toString()); playerUUID = uuid; } + @Override public void save(PlayerSetting playerSetting) { this.playerSetting = playerSetting; jsonObj.add(playerSetting.getUuid().toString(), gson.toJsonTree(playerSetting)); @@ -49,6 +51,7 @@ public void save(PlayerSetting playerSetting) { saveFile(); } + @Override public PlayerSetting load() { if (jsonObj.has("data")) { playerSetting = new PlayerSetting(playerUUID, gson.fromJson(jsonObj.get("data"), new TypeToken>() { @@ -65,6 +68,7 @@ public PlayerSetting load() { return playerSetting; } + @Override public void remove() { file.delete(); } diff --git a/src/main/java/org/shanerx/tradeshop/data/storage/Json/JsonShopConfiguration.java b/src/main/java/org/shanerx/tradeshop/data/storage/Json/JsonShopConfiguration.java new file mode 100644 index 00000000..1baaad6c --- /dev/null +++ b/src/main/java/org/shanerx/tradeshop/data/storage/Json/JsonShopConfiguration.java @@ -0,0 +1,100 @@ +/* + * + * Copyright (c) 2016-2019 + * SparklingComet @ http://shanerx.org + * KillerOfPie @ http://killerofpie.github.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * NOTICE: All modifications made by others to the source code belong + * to the respective contributor. No contributor should be held liable for + * any damages of any kind, whether be material or moral, which were + * caused by their contribution(s) to the project. See the full License for more information. + * + */ + +package org.shanerx.tradeshop.data.storage.Json; + +import com.google.common.collect.Sets; +import com.google.gson.JsonElement; +import org.shanerx.tradeshop.data.storage.ShopConfiguration; +import org.shanerx.tradeshop.shop.Shop; +import org.shanerx.tradeshop.shoplocation.ShopChunk; +import org.shanerx.tradeshop.shoplocation.ShopLocation; + +import java.io.File; +import java.util.Map; + +public class JsonShopConfiguration extends JsonConfiguration implements ShopConfiguration { + + private final ShopChunk chunk; + + public JsonShopConfiguration(ShopChunk chunk) { + super(chunk.getWorld().getName(), chunk.serialize()); + this.chunk = chunk; + } + + @Override + public void loadFile() { + if (!this.file.exists()) { + // If could not find file try with old separators + String oldFile = path + File.separator + chunk.serialize().replace(";;", "_") + ".json"; + if (new File(oldFile).exists()) + new File(oldFile).renameTo(file); + } + + super.loadFile(); + + for (Map.Entry entry : Sets.newHashSet(jsonObj.entrySet())) { + if (entry.getKey().contains("l_")) { + jsonObj.add(ShopLocation.deserialize(entry.getKey()).serialize(), entry.getValue()); + jsonObj.remove(entry.getKey()); + } + } + } + + @Override + public void save(Shop shop) { + jsonObj.add(shop.getShopLocationAsSL().serialize(), gson.toJsonTree(shop)); + + saveFile(); + } + + @Override + public void remove(ShopLocation loc) { + if (jsonObj.has(loc.serialize())) + jsonObj.remove(loc.serialize()); + + saveFile(); + } + + @Override + public Shop load(ShopLocation loc) { + Shop shop; + String locStr = loc.serialize(); + + if (!jsonObj.has(locStr)) { + return null; + } + + shop = gson.fromJson(jsonObj.get(locStr), Shop.class); + + shop.fixAfterLoad(); + return shop; + } + + @Override + public int size() { + return jsonObj.size(); + } +} diff --git a/src/main/java/org/shanerx/tradeshop/data/storage/Json/ShopConfiguration.java b/src/main/java/org/shanerx/tradeshop/data/storage/Json/ShopConfiguration.java deleted file mode 100644 index 26232c78..00000000 --- a/src/main/java/org/shanerx/tradeshop/data/storage/Json/ShopConfiguration.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * - * Copyright (c) 2016-2019 - * SparklingComet @ http://shanerx.org - * KillerOfPie @ http://killerofpie.github.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * NOTICE: All modifications made by others to the source code belong - * to the respective contributor. No contributor should be held liable for - * any damages of any kind, whether be material or moral, which were - * caused by their contribution(s) to the project. See the full License for more information. - * - */ - -package org.shanerx.tradeshop.data.storage.Json; - -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import com.google.gson.JsonElement; -import org.bukkit.inventory.ItemStack; -import org.shanerx.tradeshop.item.ShopItemStack; -import org.shanerx.tradeshop.shop.Shop; -import org.shanerx.tradeshop.shoplocation.ShopChunk; -import org.shanerx.tradeshop.shoplocation.ShopLocation; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class ShopConfiguration extends JsonConfiguration { - - private final ShopChunk chunk; - - public ShopConfiguration(ShopChunk chunk) { - super(chunk.getWorld().getName(), chunk.serialize()); - this.chunk = chunk; - } - - @Override - public void loadFile() { - if (!this.file.exists()) { - // If could not find file try with old separators - String oldFile = path + File.separator + chunk.serialize().replace(";;", "_") + ".json"; - if (new File(oldFile).exists()) - new File(oldFile).renameTo(file); - } - - super.loadFile(); - - for (Map.Entry entry : Sets.newHashSet(jsonObj.entrySet())) { - if (entry.getKey().contains("l_")) { - jsonObj.add(ShopLocation.deserialize(entry.getKey()).serialize(), entry.getValue()); - jsonObj.remove(entry.getKey()); - } - } - } - - public void save(Shop shop) { - jsonObj.add(shop.getShopLocationAsSL().serialize(), gson.toJsonTree(shop)); - - saveFile(); - } - - public void remove(ShopLocation loc) { - if (jsonObj.has(loc.serialize())) - jsonObj.remove(loc.serialize()); - - saveFile(); - } - - public Shop load(ShopLocation loc) { - Shop shop; - - if (jsonObj.has(loc.serialize())) { - if (jsonObj.getAsJsonObject(loc.serialize()).getAsJsonPrimitive("productB64") != null) { - String str = jsonObj.getAsJsonObject(loc.serialize()).get("productB64").getAsString(); - jsonObj.getAsJsonObject(loc.serialize()).remove("productB64"); - jsonObj.getAsJsonObject(loc.serialize()).add("product", gson.toJsonTree(b64OverstackFixer(str))); - saveFile(); - } - - if (jsonObj.getAsJsonObject(loc.serialize()).getAsJsonPrimitive("costB64") != null) { - String str = jsonObj.getAsJsonObject(loc.serialize()).get("costB64").getAsString(); - jsonObj.getAsJsonObject(loc.serialize()).remove("costB64"); - jsonObj.getAsJsonObject(loc.serialize()).add("cost", gson.toJsonTree(b64OverstackFixer(str))); - saveFile(); - } - - if (jsonObj.getAsJsonObject(loc.serialize()).has("productListB64")) { - List productList = new ArrayList<>(); - gson.fromJson(jsonObj.getAsJsonObject(loc.serialize()).get("productListB64"), List.class).forEach(item -> productList.add(new ShopItemStack(item.toString()))); - jsonObj.getAsJsonObject(loc.serialize()).remove("productListB64"); - jsonObj.getAsJsonObject(loc.serialize()).add("product", gson.toJsonTree(productList)); - } - - if (jsonObj.getAsJsonObject(loc.serialize()).has("costListB64")) { - List costList = new ArrayList<>(); - gson.fromJson(jsonObj.getAsJsonObject(loc.serialize()).get("costListB64"), List.class).forEach(item -> costList.add(new ShopItemStack(item.toString()))); - jsonObj.getAsJsonObject(loc.serialize()).remove("costListB64"); - jsonObj.getAsJsonObject(loc.serialize()).add("cost", gson.toJsonTree(costList)); - } - - - shop = gson.fromJson(jsonObj.get(loc.serialize()), Shop.class); - } else { - return null; - } - - shop.fixAfterLoad(); - return shop; - } - - public int size() { - return jsonObj.size(); - } - - - /** - * Turns old overstacked itemstacks into individual stacks in a list - * - * @param oldB64 old B64 string to check/fix - * @return new list of ItemStacks - * @deprecated - */ - private List b64OverstackFixer(String oldB64) { - ShopItemStack oldStack = new ShopItemStack(oldB64); - - if (oldStack.hasBase64()) - return null; - - if (!(oldStack.getItemStack().getAmount() > oldStack.getItemStack().getMaxStackSize())) { - return Lists.newArrayList(oldStack); - } else { - List newStacks = new ArrayList<>(); - int amount = oldStack.getItemStack().getAmount(); - - while (amount > 0) { - if (oldStack.getItemStack().getMaxStackSize() < amount) { - ItemStack itm = oldStack.getItemStack().clone(); - itm.setAmount(oldStack.getItemStack().getMaxStackSize()); - newStacks.add(new ShopItemStack(itm)); - amount -= oldStack.getItemStack().getMaxStackSize(); - } else { - ItemStack itm = oldStack.getItemStack().clone(); - itm.setAmount(amount); - newStacks.add(new ShopItemStack(itm)); - amount -= amount; - } - } - - return newStacks; - } - } -} diff --git a/src/main/java/org/shanerx/tradeshop/data/storage/LinkageConfiguration.java b/src/main/java/org/shanerx/tradeshop/data/storage/LinkageConfiguration.java new file mode 100644 index 00000000..b3de6a29 --- /dev/null +++ b/src/main/java/org/shanerx/tradeshop/data/storage/LinkageConfiguration.java @@ -0,0 +1,60 @@ +package org.shanerx.tradeshop.data.storage; + +import org.shanerx.tradeshop.shop.ShopChest; +import org.shanerx.tradeshop.shoplocation.ShopLocation; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public interface LinkageConfiguration { + + void save(); + void load(); + Map getLinkageData(); + + default ShopLocation getLinkedShop(ShopLocation chestLocation) { + String loc = chestLocation.serialize(); + return getLinkageData().containsKey(loc) ? ShopLocation.deserialize(getLinkageData().get(chestLocation.serialize())) : null; + } + + default int size() { + return getLinkageData().size(); + } + + default void addLinkage(ShopLocation chestLocation, ShopLocation shopLocation) { + if (getLinkageData().containsKey(chestLocation.serialize())) + getLinkageData().replace(chestLocation.serialize(), shopLocation.serialize()); + else + getLinkageData().put(chestLocation.serialize(), shopLocation.serialize()); + save(); + } + + default void add(ShopLocation chestLocation, ShopLocation shopLocation) { + if (ShopChest.isDoubleChest(chestLocation.getLocation().getBlock())) { + ShopLocation otherSideLocation = new ShopLocation(ShopChest.getOtherHalfOfDoubleChest(chestLocation.getLocation().getBlock()).getLocation()); + addLinkage(otherSideLocation, shopLocation); + } + + addLinkage(chestLocation, shopLocation); + save(); + } + + default void removeChest(ShopLocation chestLocation) { + getLinkageData().remove(chestLocation); + save(); + } + + default void removeShop(ShopLocation shopLocation) { + List removeChests = new ArrayList<>(); + String shopLoc = shopLocation.serialize(); + + getLinkageData().forEach((key, value) -> { + if (value.equals(shopLoc)) + removeChests.add(key); + }); + + removeChests.forEach((k) -> getLinkageData().remove(k)); + save(); + } +} diff --git a/src/main/java/org/shanerx/tradeshop/data/storage/PlayerConfiguration.java b/src/main/java/org/shanerx/tradeshop/data/storage/PlayerConfiguration.java new file mode 100644 index 00000000..4b7cce07 --- /dev/null +++ b/src/main/java/org/shanerx/tradeshop/data/storage/PlayerConfiguration.java @@ -0,0 +1,14 @@ +package org.shanerx.tradeshop.data.storage; + +import org.shanerx.tradeshop.data.storage.sqlite.SQLitePlayerConfiguration; +import org.shanerx.tradeshop.player.PlayerSetting; + +import java.util.UUID; + +public interface PlayerConfiguration { + + void save(PlayerSetting playerSetting); + PlayerSetting load(); + + void remove(); +} diff --git a/src/main/java/org/shanerx/tradeshop/data/storage/ShopConfiguration.java b/src/main/java/org/shanerx/tradeshop/data/storage/ShopConfiguration.java new file mode 100644 index 00000000..5f802715 --- /dev/null +++ b/src/main/java/org/shanerx/tradeshop/data/storage/ShopConfiguration.java @@ -0,0 +1,37 @@ +/* + * + * Copyright (c) 2016-2019 + * SparklingComet @ http://shanerx.org + * KillerOfPie @ http://killerofpie.github.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * NOTICE: All modifications made by others to the source code belong + * to the respective contributor. No contributor should be held liable for + * any damages of any kind, whether be material or moral, which were + * caused by their contribution(s) to the project. See the full License for more information. + * + */ + +package org.shanerx.tradeshop.data.storage; + +import org.shanerx.tradeshop.shop.Shop; +import org.shanerx.tradeshop.shoplocation.ShopLocation; + +public interface ShopConfiguration { + + void save(Shop shop); + void remove(ShopLocation loc); + Shop load(ShopLocation loc); + int size(); +} diff --git a/src/main/java/org/shanerx/tradeshop/data/storage/sqlite/DatabaseManager.java b/src/main/java/org/shanerx/tradeshop/data/storage/sqlite/DatabaseManager.java new file mode 100644 index 00000000..e77d4804 --- /dev/null +++ b/src/main/java/org/shanerx/tradeshop/data/storage/sqlite/DatabaseManager.java @@ -0,0 +1,107 @@ +package org.shanerx.tradeshop.data.storage.sqlite; + +import org.bukkit.Bukkit; +import org.shanerx.tradeshop.TradeShop; +import org.shanerx.tradeshop.utils.debug.DebugLevels; + +import java.io.File; +import java.sql.*; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; +import java.util.stream.Stream; + +/** + * This class talks to the SQLITE file through JDBC. + * **No direct calls to JDBC should ever be made outside of this class!!!** + * + * Please remember to use this class responsively and NOT execute SQL queries which have not been prepared in + * order to avoid SQL Injection vulnerabilities. + */ +public class DatabaseManager { + + private final String dbpath; + private final String dburl; + private final File dbfile; + private final TradeShop plugin; + private final Properties prop; + + private Connection connection; + + /** + * Creates instance of the DatabaseManager class. + * + * This does not initiate the connection; use {@ref setupConnection} for that. + * @param path The .db path of the SQLite database file. + */ + protected DatabaseManager(String path) { + if (sqlite != null) throw new UnsupportedOperationException("Multiple initializations of DatabaseManager singleton."); + + this.dbpath = path; + this.dburl = "jdbc:sqlite:" + path; + this.dbfile = new File(dbpath); + + this.plugin = (TradeShop) Bukkit.getPluginManager().getPlugin("TradeShop"); + + sqlite = this; + this.prop = new Properties(); + fillProperties(); + } + + private void fillProperties() { + prop.put("busy_timeout", "1000"); + } + + /** + * Sets up connection to the SQLite data file. + * @param create Should the database file be created if non-existent? + */ + protected Connection setupConnection(boolean create) throws SQLException { + if (!dbfile.exists() && !create) throw new IllegalArgumentException("Database file is missing."); + + try { + + if (connection != null && connection.isValid(0)) { + return connection; + } + else if (connection != null && !connection.isClosed()) { + throw new RuntimeException("Cached connection is in a broken state."); + } + + connection = DriverManager.getConnection(dburl, prop); + return connection; + + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + /** + * Creates a {@ref PreparedStatement} object and returns it for execution. + * + * Please do not execute queries yourself. Always run SQL statements through this method for security against + * SQL Injections! + * @param query The string containing the SQL query (or queries). + * @return The prepared statement. + * @throws SQLException + */ + protected PreparedStatement prepareStatement(Connection conn, String query) throws SQLException { + plugin.getDebugger().log("Issuing SQL Statement: [" + query + "]", DebugLevels.SQLITE); + if (conn == null || !conn.isValid(0)) { + throw new IllegalStateException("No connection has been opened yet."); + } + + return conn.prepareStatement(query); + } + + private static DatabaseManager sqlite; + + protected static DatabaseManager getSqlite() { + if (sqlite == null) { + File dataDir = new File(Bukkit.getPluginManager().getPlugin("TradeShop").getDataFolder(), "Data"); + if (!dataDir.isDirectory()) dataDir.mkdirs(); + sqlite = new DatabaseManager(new File(dataDir, "database.db").getAbsolutePath()); + } + return sqlite; + } +} diff --git a/src/main/java/org/shanerx/tradeshop/data/storage/sqlite/SQLiteLinkageConfiguration.java b/src/main/java/org/shanerx/tradeshop/data/storage/sqlite/SQLiteLinkageConfiguration.java new file mode 100644 index 00000000..04ce084d --- /dev/null +++ b/src/main/java/org/shanerx/tradeshop/data/storage/sqlite/SQLiteLinkageConfiguration.java @@ -0,0 +1,89 @@ +package org.shanerx.tradeshop.data.storage.sqlite; + +import org.bukkit.World; +import org.shanerx.tradeshop.data.storage.LinkageConfiguration; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +public class SQLiteLinkageConfiguration implements LinkageConfiguration { + + private final String worldName; + private Map linkageData; + private final DatabaseManager sqlite; + + public SQLiteLinkageConfiguration(World world) { + this.worldName = world.getName(); + this.sqlite = DatabaseManager.getSqlite(); + + try { + createTableIfNotExists(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + load(); + } + + @Override + public void save() { + try (Connection conn = sqlite.setupConnection(true)) { + String sql = "DELETE FROM shop_linkage WHERE world_name = '" + worldName + "';"; + + PreparedStatement ps = sqlite.prepareStatement(conn, sql); + ps.executeUpdate(); + ps.close(); + + for (String chestData : linkageData.keySet()) { + ps = sqlite.prepareStatement(conn, + "INSERT INTO shop_linkage (chest_loc, sign_loc, world_name)" + + " VALUES ('" + chestData + "', '" + linkageData.get(chestData) + "', '" + worldName + "');"); + ps.executeUpdate(); + ps.close(); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + @Override + public void load() { + // stop accidental double loading (expensive!) + if (linkageData != null) throw new UnsupportedOperationException("Cannot load twice (expensive operation)!"); + linkageData = new HashMap<>(); + + try (Connection conn = sqlite.setupConnection(true)) { + String sql = "SELECT * FROM shop_linkage WHERE world_name = '" + worldName + "';"; + PreparedStatement ps = sqlite.prepareStatement(conn, sql); + ResultSet res = ps.executeQuery(); + + while (res.next()) { + linkageData.put(res.getString("chest_loc"), res.getString("sign_loc")); + } + + ps.close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + @Override + public Map getLinkageData() { + return linkageData; + } + + private void createTableIfNotExists() throws SQLException { + try (Connection conn = sqlite.setupConnection(true)) { + String sql = "CREATE TABLE IF NOT EXISTS shop_linkage " + + "(chest_loc TEXT not NULL, " + + " sign_loc TEXT not NULL, " + + " world_name TEXT not NULL);"; + PreparedStatement ps = sqlite.prepareStatement(conn, sql); + ps.execute(); + ps.close(); + } + } +} diff --git a/src/main/java/org/shanerx/tradeshop/data/storage/sqlite/SQLitePlayerConfiguration.java b/src/main/java/org/shanerx/tradeshop/data/storage/sqlite/SQLitePlayerConfiguration.java new file mode 100644 index 00000000..a480d076 --- /dev/null +++ b/src/main/java/org/shanerx/tradeshop/data/storage/sqlite/SQLitePlayerConfiguration.java @@ -0,0 +1,164 @@ +package org.shanerx.tradeshop.data.storage.sqlite; + +import org.shanerx.tradeshop.data.storage.PlayerConfiguration; +import org.shanerx.tradeshop.player.PlayerSetting; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.UUID; + +public class SQLitePlayerConfiguration implements PlayerConfiguration { + + private final UUID uuid; + private final DatabaseManager sqlite; + + public SQLitePlayerConfiguration(UUID uuid) { + this.uuid = uuid; + this.sqlite = DatabaseManager.getSqlite(); + + try { + createTableIfNotExists(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + @Override + public void save(PlayerSetting playerSetting) { + if (!playerSetting.getUuid().equals(uuid)) { + throw new IllegalArgumentException("uuid of playerSetting does not match uuid field."); + } + + remove(); // should happen within the try but whatever + + try (Connection conn = sqlite.setupConnection(true)) { + String sql = "INSERT INTO players (uuid, showInvolvedStatus, adminEnabled, multi) VALUES " + + "('" + uuid.toString() + "', " + (playerSetting.showInvolvedStatus() ? 1 : 0) + ", " + (playerSetting.isAdminEnabled() ? 1 : 0) + ", " + playerSetting.getMulti() + ");"; + PreparedStatement ps = sqlite.prepareStatement(conn, sql); + ps.executeUpdate(); + ps.close(); + + for (String ownedShop : playerSetting.getOwnedShops()) { + ps = sqlite.prepareStatement(conn, + "INSERT INTO players_owned_shops (uuid, shop)" + + " VALUES ('" + uuid.toString() + "', '" + ownedShop + "');"); + ps.executeUpdate(); + ps.close(); + } + + for (String staffShop : playerSetting.getStaffShops()) { + ps = sqlite.prepareStatement(conn, + "INSERT INTO players_staff_shops (uuid, shop)" + + " VALUES ('" + uuid.toString() + "', '" + staffShop + "');"); + ps.executeUpdate(); + ps.close(); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + @Override + public PlayerSetting load() { + try (Connection conn = sqlite.setupConnection(true)) { + String sql = "SELECT * FROM players WHERE uuid = '" + uuid.toString() + "';"; + String sql2 = "SELECT * FROM players_owned_shops WHERE uuid = '" + uuid.toString() + "';"; + String sql3 = "SELECT * FROM players_staff_shops WHERE uuid = '" + uuid.toString() + "';"; + + PreparedStatement ps = sqlite.prepareStatement(conn, sql); + PreparedStatement ps2 = sqlite.prepareStatement(conn, sql2); + PreparedStatement ps3 = sqlite.prepareStatement(conn, sql3); + + ResultSet res = ps.executeQuery(); + ResultSet res2 = ps2.executeQuery(); + ResultSet res3 = ps3.executeQuery(); + + if (!res.next()) { + ps.close(); + ps2.close(); + ps3.close(); + + return null; + } + + PlayerSetting playerSetting = new PlayerSetting(uuid); + playerSetting.setMulti(res.getInt("multi")); + playerSetting.setAdminEnabled(res.getBoolean("adminEnabled")); + playerSetting.setShowInvolvedStatus(res.getBoolean("showInvolvedStatus")); + + while (res2.next()) { + playerSetting.getOwnedShops().add(res2.getString("shop")); + } + + while (res3.next()) { + playerSetting.getStaffShops().add(res3.getString("shop")); + } + + ps.close(); + ps2.close(); + ps3.close(); + + return playerSetting; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + + @Override + public void remove() { + try (Connection conn = sqlite.setupConnection(true)) { + String sql = "DELETE FROM players WHERE uuid = '" + uuid.toString() + "';"; + String sql2 = "DELETE FROM players_owned_shops WHERE uuid = '" + uuid.toString() + "';"; + String sql3 = "DELETE FROM players_staff_shops WHERE uuid = '" + uuid.toString() + "';"; + + PreparedStatement ps = sqlite.prepareStatement(conn, sql); + PreparedStatement ps2 = sqlite.prepareStatement(conn, sql2); + PreparedStatement ps3 = sqlite.prepareStatement(conn, sql3); + + ps.execute(); + ps2.execute(); + ps3.execute(); + + ps.close(); + ps2.close(); + ps3.close(); + + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + private void createTableIfNotExists() throws SQLException { + try (Connection conn = sqlite.setupConnection(true)) { + String sql = "CREATE TABLE IF NOT EXISTS players " + + "(uuid TEXT not NULL, " + + " showInvolvedStatus INTEGER, " + + " adminEnabled INTEGER, " + + " multi INTEGER, " + + " PRIMARY KEY ( uuid ));"; + + PreparedStatement ps = sqlite.prepareStatement(conn, sql); + ps.execute(); + ps.close(); + + sql = "CREATE TABLE IF NOT EXISTS players_owned_shops " + + "(uuid TEXT not NULL, " + + " shop TEXT);"; + + ps = sqlite.prepareStatement(conn, sql); + ps.execute(); + ps.close(); + + sql = "CREATE TABLE IF NOT EXISTS players_staff_shops " + + "(uuid TEXT not NULL, " + + " shop TEXT);"; + + ps = sqlite.prepareStatement(conn, sql); + ps.execute(); + ps.close(); + } + } +} diff --git a/src/main/java/org/shanerx/tradeshop/data/storage/sqlite/SQLiteShopConfiguration.java b/src/main/java/org/shanerx/tradeshop/data/storage/sqlite/SQLiteShopConfiguration.java new file mode 100644 index 00000000..6c854b0a --- /dev/null +++ b/src/main/java/org/shanerx/tradeshop/data/storage/sqlite/SQLiteShopConfiguration.java @@ -0,0 +1,241 @@ +package org.shanerx.tradeshop.data.storage.sqlite; + +import org.bukkit.Location; +import org.shanerx.tradeshop.data.storage.ShopConfiguration; +import org.shanerx.tradeshop.item.ShopItemStack; +import org.shanerx.tradeshop.player.ShopRole; +import org.shanerx.tradeshop.player.ShopUser; +import org.shanerx.tradeshop.shop.Shop; +import org.shanerx.tradeshop.shop.ShopSettingKeys; +import org.shanerx.tradeshop.shop.ShopType; +import org.shanerx.tradeshop.shoplocation.ShopChunk; +import org.shanerx.tradeshop.shoplocation.ShopLocation; +import org.shanerx.tradeshop.utils.objects.Tuple; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + +public class SQLiteShopConfiguration implements ShopConfiguration { + + private final ShopChunk chunk; + private final String chunkStr; + private final DatabaseManager sqlite; + + public SQLiteShopConfiguration(ShopChunk chunk) { + this.chunk = chunk; + this.chunkStr = chunk.serialize(); + this.sqlite = DatabaseManager.getSqlite(); + + try { + createTableIfNotExists(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + @Override + public void save(Shop shop) { + remove(shop.getShopLocationAsSL()); // this should NOT be moved above the try ! + + try (Connection conn = sqlite.setupConnection(true)) { + Location chestLoc = shop.getChestAsSC().getChest().getLocation(); + + String sql = "INSERT INTO shops (owner_uuid, sign_loc_serialized, chunk_serialized, type, " + + " status, setting_hopper_import, setting_hopper_export, setting_no_cost, available_trades, " + + " sign_world_name, sign_x, sign_y, sign_z, chest_world_name, chest_x, chest_y, chest_z) " + + " VALUES " + + String.format("('%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, '%s', %d, %d, %d, '%s', %d, %d, %d);", + shop.getOwner().getUUID().toString(), shop.getShopLocationAsSL().serialize(), chunkStr, shop.getShopType().name(), + shop.getStatus().toString(), shop.getShopSetting(ShopSettingKeys.HOPPER_IMPORT).asBoolean() ? 1 : 0, + shop.getShopSetting(ShopSettingKeys.HOPPER_EXPORT).asBoolean() ? 1 : 0, shop.getShopSetting(ShopSettingKeys.NO_COST).asBoolean() ? 1 : 0, + shop.getAvailableTrades(), shop.getShopLocation().getWorld().getName(), + shop.getShopLocation().getBlockX(), shop.getShopLocation().getBlockY(), shop.getShopLocation().getBlockZ(), + chestLoc.getWorld().getName(), chestLoc.getBlockX(), chestLoc.getBlockY(), chestLoc.getBlockZ()); + + PreparedStatement ps = sqlite.prepareStatement(conn, sql); + ps.execute(); + ps.close(); + + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + @Override + public void remove(ShopLocation loc) { + try (Connection conn = sqlite.setupConnection(true)) { + String sql = "DELETE FROM shops WHERE sign_loc_serialized = '" + loc.serialize() + "';"; + PreparedStatement ps = sqlite.prepareStatement(conn, sql); + ps.executeUpdate(); + ps.close(); + + String sql2 = "DELETE FROM shop_products WHERE sign_loc_serialized = '" + loc.serialize() + "';"; + PreparedStatement ps2 = sqlite.prepareStatement(conn, sql2); + ps2.executeUpdate(); + ps2.close(); + + String sql3 = "DELETE FROM shop_costs WHERE sign_loc_serialized = '" + loc.serialize() + "';"; + PreparedStatement ps3 = sqlite.prepareStatement(conn, sql3); + ps3.executeUpdate(); + ps3.close(); + + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + @Override + public Shop load(ShopLocation loc) { + String locStr = loc.serialize(); + + try (Connection conn = sqlite.setupConnection(true)) { + PreparedStatement ps = sqlite.prepareStatement(conn, + "SELECT * FROM shops WHERE sign_loc_serialized = '" + locStr + "';"); + ResultSet res = ps.executeQuery(); + + if (!res.next()) { + ps.close(); + return null; + } + + Tuple locations = new Tuple(ShopLocation.deserialize(res.getString("sign_loc_serialized")).getLocation(), + ShopLocation.deserialize(res.getString("sign_loc_serialized")).getLocation()); + Set members = new HashSet<>(); + Set managers = new HashSet<>(); + + List products = new ArrayList<>(); + List costs = new ArrayList<>(); + + String sql2 = "SELECT * FROM shop_products WHERE sign_loc_serialized = '" + locStr + "';"; + PreparedStatement ps2 = sqlite.prepareStatement(conn, sql2); + ResultSet res2 = ps2.executeQuery(); + while (res2.next()) { + products.add(ShopItemStack.deserialize(res2.getString("product"))); + } + + String sql3 = "SELECT * FROM shop_costs WHERE sign_loc_serialized = '" + locStr + "';"; + PreparedStatement ps3 = sqlite.prepareStatement(conn, sql3); + ResultSet res3 = ps3.executeQuery(); + while (res3.next()) { + costs.add(ShopItemStack.deserialize(res3.getString("cost"))); + } + + String sql4 = "SELECT * FROM shop_managers WHERE sign_loc_serialized = '" + locStr + "';"; + PreparedStatement ps4 = sqlite.prepareStatement(conn, sql4); + ResultSet res4 = ps4.executeQuery(); + while (res4.next()) { + managers.add(UUID.fromString(res4.getString("uuid"))); + } + + String sql5 = "SELECT * FROM shop_members WHERE sign_loc_serialized = '" + locStr + "';"; + PreparedStatement ps5 = sqlite.prepareStatement(conn, sql5); + ResultSet res5 = ps5.executeQuery(); + while (res5.next()) { + managers.add(UUID.fromString(res5.getString("uuid"))); + } + + Shop shop = new Shop(locations, + ShopType.valueOf(res.getString("type")), + new ShopUser(UUID.fromString(res.getString("owner_uuid")), ShopRole.OWNER), + new Tuple<>(managers, members), + products, costs); + + if (res.next()) throw new IllegalStateException("Database contains more than one entry with the shop loc '" + locStr + "'"); + + ps.close(); + ps2.close(); + ps3.close(); + ps4.close(); + ps5.close(); + + return shop; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + @Override + public int size() { + String sql = "SELECT * FROM shops WHERE chunk_serialized = '" + chunkStr + "';"; + try (Connection conn = sqlite.setupConnection(true)) { + PreparedStatement ps = sqlite.prepareStatement(conn, sql); + ResultSet res = ps.executeQuery(); + while (res.next()); // empty body is intentional + + int row = res.getRow(); + ps.close(); + return row; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + private void createTableIfNotExists() throws SQLException { + try(Connection conn = sqlite.setupConnection(true)) { + String sql = "CREATE TABLE IF NOT EXISTS shops " + + "(owner_uuid TEXT not NULL, " + + " sign_loc_serialized TEXT not NULL, " + + " chunk_serialized TEXT not NULL, " + + " type TEXT not NULL, " + + " status TEXT not NULL, " + + + " setting_hopper_import INTEGER, " + + " setting_hopper_export INTEGER, " + + " setting_no_cost INTEGER, " + + + " available_trades INTEGER, " + + + " sign_world_name TEXT not NULL, " + + " sign_x INTEGER, " + + " sign_y INTEGER, " + + " sign_z INTEGER, " + + + " chest_world_name TEXT not NULL, " + + " chest_x INTEGER, " + + " chest_y INTEGER, " + + " chest_z INTEGER, " + + + " PRIMARY KEY ( sign_loc_serialized ));"; + + PreparedStatement ps = sqlite.prepareStatement(conn, sql); + ps.execute(); + ps.close(); + + String sql2 = "CREATE TABLE IF NOT EXISTS shop_products " + + "(sign_loc_serialized TEXT not NULL, " + + " product TEXT not NULL, " + + " PRIMARY KEY ( sign_loc_serialized ));"; + + ps = sqlite.prepareStatement(conn, sql2); + ps.execute(); + ps.close(); + + String sql3 = "CREATE TABLE IF NOT EXISTS shop_costs " + + "(sign_loc_serialized TEXT not NULL, " + + " cost TEXT not NULL, " + + " PRIMARY KEY ( sign_loc_serialized ));"; + ps = sqlite.prepareStatement(conn, sql3); + ps.execute(); + ps.close(); + + String sql4 = "CREATE TABLE IF NOT EXISTS shop_managers " + + "(sign_loc_serialized TEXT not NULL, " + + " uuid TEXT not NULL, " + + " PRIMARY KEY ( sign_loc_serialized ));"; + ps = sqlite.prepareStatement(conn, sql4); + ps.execute(); + ps.close(); + + String sql5 = "CREATE TABLE IF NOT EXISTS shop_members " + + "(sign_loc_serialized TEXT not NULL, " + + " uuid TEXT not NULL, " + + " PRIMARY KEY ( sign_loc_serialized ));"; + ps = sqlite.prepareStatement(conn, sql5); + ps.execute(); + ps.close(); + } + } +} diff --git a/src/main/java/org/shanerx/tradeshop/item/ShopItemStack.java b/src/main/java/org/shanerx/tradeshop/item/ShopItemStack.java index c8e9b38f..9f77afa8 100644 --- a/src/main/java/org/shanerx/tradeshop/item/ShopItemStack.java +++ b/src/main/java/org/shanerx/tradeshop/item/ShopItemStack.java @@ -28,6 +28,7 @@ import com.google.common.collect.Lists; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; import net.md_5.bungee.api.ChatColor; import org.apache.commons.lang.WordUtils; @@ -43,7 +44,6 @@ import org.bukkit.inventory.meta.FireworkMeta; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.util.io.BukkitObjectInputStream; -import org.bukkit.util.io.BukkitObjectOutputStream; import org.shanerx.tradeshop.utils.Utils; import org.shanerx.tradeshop.utils.debug.Debug; import org.shanerx.tradeshop.utils.debug.DebugLevels; @@ -51,7 +51,6 @@ import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; @@ -64,8 +63,13 @@ public class ShopItemStack implements Serializable, Cloneable { private transient ItemStack itemStack; private transient Debug debugger; + @Expose(serialize = false) private String itemStackB64; + @Expose + private Map serialItemStack; + + @Expose @SerializedName(value = "itemSettings", alternate = "shopSettings") private Map> itemSettings; @@ -77,7 +81,6 @@ public ShopItemStack(ItemStack itemStack, HashMap> itemS itemSettings.forEach((key, value) -> this.itemSettings.put(ShopItemStackSettingKeys.match(key), value)); buildMap(); - fromBase64(); + loadData(); + } + + public ShopItemStack(Map serialItemStack) { + this(serialItemStack, new HashMap<>()); + } + + public ShopItemStack(Map serialItemStack, HashMap> itemSettings) { + this.serialItemStack = serialItemStack; + this.itemSettings = new HashMap<>(); + + itemSettings.forEach((key, value) -> this.itemSettings.put(ShopItemStackSettingKeys.match(key), value)); + + buildMap(); + loadData(); } //Re-added for backwards compatibility @@ -117,7 +134,7 @@ public ShopItemStack(String itemStackB64, int compareDurability, boolean compare itemSettings.putIfAbsent(ShopItemStackSettingKeys.COMPARE_ENCHANTMENTS, new ObjectHolder<>(compareEnchantments)); buildMap(); - fromBase64(); + loadData(); } public Map> getItemSettings() { @@ -125,12 +142,12 @@ public Map> getItemSettings() { } public String serialize() { - return new Gson().toJson(this); + return new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create().toJson(this); } public static ShopItemStack deserialize(String serialized) { ShopItemStack item = new Gson().fromJson(serialized, ShopItemStack.class); - item.fromBase64(); + item.loadData(); item.buildMap(); return item; } @@ -198,13 +215,20 @@ public int getAmount() { public void setAmount(int amount) { itemStack.setAmount(amount); - toBase64(); + unloadData(); } public String getItemStackB64() { return itemStackB64; } + public Map getSerialItemStack() { + if (serialItemStack == null) + unloadData(); + return serialItemStack; + } + + public boolean hasBase64() { return itemStackB64 != null && !itemStackB64.isEmpty(); } @@ -494,7 +518,7 @@ public boolean isSimilar(ItemStack toCompare) { public ItemStack getItemStack() { if (itemStack == null) - fromBase64(); + loadData(); return itemStack; } @@ -561,7 +585,7 @@ public String getStateString(ObjectHolder stateSetting) { @Override public String toString() { return "ShopItemStack{" + - "itemStack=" + getItemStack() + + "itemStack=" + getItemStack().serialize() + ", shopSettings=" + itemSettings + '}'; } @@ -576,37 +600,41 @@ public String toString() { /** * Sets the objects Base64 from its {@link ItemStack} */ - private void toBase64() { - try { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream); - - // Save every element in the list - dataOutput.writeObject(itemStack); - - // Serialize that array - dataOutput.close(); - itemStackB64 = Base64Coder.encodeLines(outputStream.toByteArray()); - } catch (IOException e) { + private void unloadData() { + if (itemStack != null) { itemStackB64 = null; + serialItemStack = itemStack.serialize(); } } /** - * Sets the objects {@link ItemStack} from its Base64. + * Sets the objects {@link ItemStack} from its Base64 or Serialized values. */ - private void fromBase64() { - try { - ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(itemStackB64)); - BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream); + private void loadData() { + if (serialItemStack != null && !serialItemStack.isEmpty()) { + itemStack = ItemStack.deserialize(serialItemStack); + } - // Read the serialized inventory - itemStack = new ItemStack((ItemStack) dataInput.readObject()); + if (hasBase64()) { + try { + ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(itemStackB64)); + BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream); + + // Read the serialized inventory + itemStack = new ItemStack((ItemStack) dataInput.readObject()); - dataInput.close(); - } catch (ClassNotFoundException | IOException e) { - itemStack = null; + dataInput.close(); + } catch (ClassNotFoundException | IOException e) { + itemStack = null; + } } + + unloadData(); + } + + public int getItemSize() { + unloadData(); + return serialItemStack.toString().length(); } public String toConsoleText() { diff --git a/src/main/java/org/shanerx/tradeshop/player/PlayerSetting.java b/src/main/java/org/shanerx/tradeshop/player/PlayerSetting.java index dbb19973..98f3e00f 100644 --- a/src/main/java/org/shanerx/tradeshop/player/PlayerSetting.java +++ b/src/main/java/org/shanerx/tradeshop/player/PlayerSetting.java @@ -51,11 +51,11 @@ public class PlayerSetting implements Serializable { private transient UUID uuid; private final String uuidString; private final Set ownedShops; - + private final Set staffShops; private boolean showInvolvedStatus, adminEnabled = true; private int multi = Setting.MULTI_TRADE_DEFAULT.getInt(); - private final Set staffShops; + private transient Utils utils = new Utils(); public PlayerSetting(UUID playerUUID, Map data) { diff --git a/src/main/java/org/shanerx/tradeshop/shop/Shop.java b/src/main/java/org/shanerx/tradeshop/shop/Shop.java index bdc1c9e8..15df3ef5 100644 --- a/src/main/java/org/shanerx/tradeshop/shop/Shop.java +++ b/src/main/java/org/shanerx/tradeshop/shop/Shop.java @@ -80,13 +80,14 @@ public class Shop implements Serializable { /** * Creates a Shop object * - * @param locations Location of shop sign and chest as Tuple, left = Sign location, right = inventory location - * @param owner Owner of the shop as a ShopUser - * @param shopType Type of the shop as ShopType - * @param items Items to go into the shop as Tuple, left = Product, right = Cost - * @param players Users to be added to the shop as Tuple, left = Managers, right = Members - */ - public Shop(Tuple locations, ShopType shopType, ShopUser owner, Tuple, Set> players, Tuple items) { + * @param locations Location of shop sign and chest as Tuple, left = Sign location, right = inventory location + * @param owner Owner of the shop as a ShopUser + * @param shopType Type of the shop as ShopType + * @param productItems Product items to go into the shop + * @param costItems Cost items to go into the shop + * @param players Users to be added to the shop as Tuple, left = Managers, right = Members + */ + public Shop(Tuple locations, ShopType shopType, ShopUser owner, Tuple, Set> players, List productItems, List costItems) { shopLoc = new ShopLocation(locations.getLeft()); this.owner = owner; @@ -97,18 +98,31 @@ public Shop(Tuple locations, ShopType shopType, ShopUser own this.shopType = shopType; - managers = players.getLeft() == null ? Collections.emptySet() : players.getLeft(); - members = players.getRight() == null ? Collections.emptySet() : players.getRight(); + managers = players.getLeft() == null ? new HashSet<>() : players.getLeft(); + members = players.getRight() == null ? new HashSet<>() : players.getRight(); - product = new ArrayList<>(); - cost = new ArrayList<>(); + product = productItems != null ? new ArrayList<>(productItems) : new ArrayList<>(); + cost = costItems != null ? new ArrayList<>(costItems) : new ArrayList<>(); - if (items.getLeft() != null) product.add(new ShopItemStack(items.getLeft())); - if (items.getRight() != null) cost.add(new ShopItemStack(items.getRight())); + product.removeIf(shopItemStack -> shopItemStack.getItemStack() == null); + cost.removeIf(shopItemStack -> shopItemStack.getItemStack() == null); fixAfterLoad(); } + /** + * Creates a Shop object + * + * @param locations Location of shop sign and chest as Tuple, left = Sign location, right = inventory location + * @param owner Owner of the shop as a ShopUser + * @param shopType Type of the shop as ShopType + * @param items Items to go into the shop as Tuple, left = Product, right = Cost + * @param players Users to be added to the shop as Tuple, left = Managers, right = Members + */ + public Shop(Tuple locations, ShopType shopType, ShopUser owner, Tuple, Set> players, Tuple items) { + this(locations, shopType, owner, players, Collections.singletonList(new ShopItemStack(items.getLeft())), Collections.singletonList(new ShopItemStack(items.getRight()))); + } + /** * Creates a Shop object * @@ -725,14 +739,16 @@ public List getUsersExcluding(List excludedPlayers, ShopRole... switch (role) { case MEMBER: members.forEach(uuid -> { - if (!excludedPlayers.contains(uuid)) - users.add(new ShopUser(uuid, ShopRole.MEMBER)); + if (!excludedPlayers.contains(uuid)) { + users.add(new ShopUser(uuid, role)); + } }); break; case MANAGER: - members.forEach(uuid -> { - if (!excludedPlayers.contains(uuid)) - users.add(new ShopUser(uuid, ShopRole.MANAGER)); + managers.forEach(uuid -> { + if (!excludedPlayers.contains(uuid)) { + users.add(new ShopUser(uuid, role)); + } }); break; case OWNER: @@ -831,21 +847,18 @@ public boolean addUser(UUID newUser, ShopRole role) { if (getUsers(ShopRole.MANAGER, ShopRole.MEMBER).size() >= Setting.MAX_SHOP_USERS.getInt()) return false; - boolean ret = false; if (!getUsersUUID(ShopRole.MANAGER, ShopRole.MEMBER).contains(newUser)) { switch (role) { case MANAGER: - managers.add(newUser); - ret = true; + saveShop(managers.add(newUser)); + return true; case MEMBER: - members.add(newUser); - ret = true; + saveShop(members.add(newUser)); + return true; } } - saveShop(ret); - - return ret; + return false; } /** diff --git a/src/main/java/org/shanerx/tradeshop/shop/listeners/ShopTradeListener.java b/src/main/java/org/shanerx/tradeshop/shop/listeners/ShopTradeListener.java index 00f6ab02..07f598bf 100644 --- a/src/main/java/org/shanerx/tradeshop/shop/listeners/ShopTradeListener.java +++ b/src/main/java/org/shanerx/tradeshop/shop/listeners/ShopTradeListener.java @@ -160,7 +160,7 @@ public void onBlockInteract(PlayerInteractEvent e) { Tuple> canExchangeResult = canExchangeAll(shop, buyer.getInventory(), multiplier, e.getAction()); - PLUGIN.getDebugger().log("ExchangeResult " + canExchangeResult.getLeft(), DebugLevels.DATA_ERROR); + PLUGIN.getDebugger().log("ExchangeResult " + canExchangeResult.getLeft(), DebugLevels.TRADE); switch (canExchangeResult.getLeft()) { case SHOP_NO_PRODUCT: Message.SHOP_INSUFFICIENT_ITEMS.sendItemMultiLineMessage(buyer, Collections.singletonMap(Variable.MISSING_ITEMS, canExchangeResult.getRight())); @@ -231,7 +231,7 @@ private Tuple, List> tradeAllItems(Shop shop, int mul if (costItems.get(0) == null) { ItemStack item = costItems.get(1); Message.INSUFFICIENT_ITEMS.sendItemMultiLineMessage(buyer, Collections.singletonMap(Variable.MISSING_ITEMS, costItems)); - PLUGIN.getDebugger().log("tradeAllItems", DebugLevels.DATA_ERROR); + PLUGIN.getDebugger().log("tradeAllItems", DebugLevels.TRADE); return new Tuple<>(productItems, costItems); } } else { diff --git a/src/main/java/org/shanerx/tradeshop/shoplocation/ShopLocation.java b/src/main/java/org/shanerx/tradeshop/shoplocation/ShopLocation.java index f7fe073e..d9054572 100644 --- a/src/main/java/org/shanerx/tradeshop/shoplocation/ShopLocation.java +++ b/src/main/java/org/shanerx/tradeshop/shoplocation/ShopLocation.java @@ -34,7 +34,7 @@ public class ShopLocation implements Serializable { - final private String div = "::"; + final private transient String div = "::"; private transient World world; private final String worldName; private final double x; diff --git a/src/main/java/org/shanerx/tradeshop/utils/Utils.java b/src/main/java/org/shanerx/tradeshop/utils/Utils.java index c9a3600c..27625025 100644 --- a/src/main/java/org/shanerx/tradeshop/utils/Utils.java +++ b/src/main/java/org/shanerx/tradeshop/utils/Utils.java @@ -506,9 +506,11 @@ public Shop createShop(Sign shopSign, Player creator, ShopType shopType, ItemSta PLUGIN.getDebugger().log(shop.toDebug(), DebugLevels.SHOP_CREATION); - PLUGIN.getDebugger().log("-----Post-Event-----", DebugLevels.SHOP_CREATION); PlayerShopCreateEvent shopCreateEvent = new PlayerShopCreateEvent(creator, shop); Bukkit.getPluginManager().callEvent(shopCreateEvent); + PLUGIN.getDebugger().log("-----Post-Event-----", DebugLevels.SHOP_CREATION); + PLUGIN.getDebugger().log(shop.toDebug(), DebugLevels.SHOP_CREATION); + if (shopCreateEvent.isCancelled()) { PLUGIN.getDebugger().log("Creation Failed!", DebugLevels.SHOP_CREATION); return null; diff --git a/src/main/java/org/shanerx/tradeshop/utils/debug/DebugLevels.java b/src/main/java/org/shanerx/tradeshop/utils/debug/DebugLevels.java index db44c016..a6b16b99 100644 --- a/src/main/java/org/shanerx/tradeshop/utils/debug/DebugLevels.java +++ b/src/main/java/org/shanerx/tradeshop/utils/debug/DebugLevels.java @@ -42,7 +42,8 @@ public enum DebugLevels { SHULKERS_SUCK(8, Level.WARNING), // 128 ENCHANT_CHECKS(9, Level.WARNING), // 256 OUTPUT(10, Level.WARNING), // 512 - SHOP_CREATION(11, Level.INFO) // 1024 + SHOP_CREATION(11, Level.INFO), // 1024 + SQLITE(12, Level.INFO) ; //position is what value to check for this level in the binary string -1. diff --git a/src/main/resources/Lang/en-us.yml b/src/main/resources/Lang/en-us.yml index 8308fc1b..1ddd7793 100644 --- a/src/main/resources/Lang/en-us.yml +++ b/src/main/resources/Lang/en-us.yml @@ -48,7 +48,7 @@ setting: post-comment: "\n" system-options: data-storage-type: - pre-comment: "How would you like your servers data stored? (FLATFILE)" + pre-comment: "How would you like your servers data stored? (FLATFILE, SQLITE)" enable-debug: pre-comment: "What debug code should be run. This will add significant amounts of spam to the console/log, generally not used unless requested by Devs" check-updates: