Switch flatfile storage to use JSON and improve sign shops

This commit is contained in:
AppleDash 2017-01-18 05:08:16 -05:00
parent b78c353093
commit af1752b85c
8 changed files with 171 additions and 173 deletions

View File

@ -18,6 +18,11 @@
<artifactId>SaneEconomyCore</artifactId>
<version>0.10.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.appledash</groupId>
<artifactId>SaneEconomyCore</artifactId>
<version>0.10.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>

View File

@ -5,7 +5,7 @@ import org.appledash.saneeconomysignshop.listeners.BreakListener;
import org.appledash.saneeconomysignshop.listeners.InteractListener;
import org.appledash.saneeconomysignshop.listeners.SignChangeListener;
import org.appledash.saneeconomysignshop.signshop.SignShopManager;
import org.appledash.saneeconomysignshop.signshop.storage.SignShopStorageFlatfile;
import org.appledash.saneeconomysignshop.signshop.storage.SignShopStorageJSON;
import org.appledash.saneeconomysignshop.util.ItemDatabase;
import org.appledash.saneeconomysignshop.util.LimitManager;
import org.bukkit.configuration.file.YamlConfiguration;
@ -20,7 +20,7 @@ import java.io.File;
*/
public class SaneEconomySignShop extends JavaPlugin {
private ISaneEconomy saneEconomy;
private final SignShopManager signShopManager = new SignShopManager(new SignShopStorageFlatfile(new File(getDataFolder(), "shops.db")));
private final SignShopManager signShopManager = new SignShopManager(new SignShopStorageJSON(new File(getDataFolder(), "shops.json")));
private final LimitManager limitManager = new LimitManager();
@Override

View File

@ -7,14 +7,12 @@ import org.appledash.saneeconomysignshop.SaneEconomySignShop;
import org.appledash.saneeconomysignshop.signshop.SignShop;
import org.appledash.saneeconomysignshop.util.ItemDatabase;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.inventory.ItemStack;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -92,8 +90,8 @@ public class SignChangeListener implements Listener {
ItemStack itemStack;
try {
itemStack = parseGive(itemName);
} catch (InvalidItemException e) {
itemStack = ItemDatabase.parseGive(itemName);
} catch (ItemDatabase.InvalidItemException e) {
return new ParsedSignShop("Invalid item name or ID specified.");
}
@ -141,65 +139,4 @@ public class SignChangeListener implements Listener {
this.shop = shop;
}
}
private ItemStack parseGive(String rawItemName) throws InvalidItemException {
String itemName;
short damage;
if (rawItemName.contains(":")) {
String[] splitItemName = rawItemName.split(":");
itemName = splitItemName[0];
if (splitItemName.length == 1) { // They just typed 'tnt:'
damage = 0;
} else { // They typed 'tnt:something'
try {
damage = Short.valueOf(splitItemName[1]);
} catch (NumberFormatException e) {
throw new InvalidItemException("Damage value must be a number.");
}
}
} else { // No damage value
itemName = rawItemName;
damage = 0;
}
Optional<Material> materialOptional = parseMaterialFromName(itemName);
if (!materialOptional.isPresent()) {
Optional<ItemDatabase.Pair<Integer, Short>> parsedItem = ItemDatabase.getIDAndDamageForName(normalizeItemName(itemName));
if (!parsedItem.isPresent()) {
throw new InvalidItemException("Item by that name does not exist.");
}
return new ItemStack(parsedItem.get().getLeft(), 1, parsedItem.get().getRight());
}
return new ItemStack(materialOptional.get(), 1, damage);
}
private Optional<Material> parseMaterialFromName(String materialName) {
// Try to parse an integral item ID first, for legacy reasons.
try {
Material mat = Material.getMaterial(Integer.valueOf(materialName));
return Optional.ofNullable(mat);
} catch (NumberFormatException ignored) { }
for (Material mat : Material.values()) {
if (normalizeItemName(mat.name()).equals(normalizeItemName(materialName))) {
return Optional.of(mat);
}
}
return Optional.empty();
}
private String normalizeItemName(String itemName) {
return itemName.toLowerCase().replace("_", "").replace(" ", "");
}
private class InvalidItemException extends Exception {
public InvalidItemException(String message) {
super(message);
}
}
}

View File

@ -1,83 +0,0 @@
package org.appledash.saneeconomysignshop.signshop.storage;
import com.google.common.collect.ImmutableMap;
import org.appledash.saneeconomysignshop.signshop.SignShop;
import org.appledash.saneeconomysignshop.util.SerializableLocation;
import org.bukkit.Location;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Created by appledash on 10/6/16.
* Blackjack is best pony.
*/
public class SignShopStorageFlatfile implements SignShopStorage {
private Map<Location, SignShop> cachedSignShops;
private File file;
public SignShopStorageFlatfile(File file) {
this.file = file;
}
@Override
public void loadSignShops() {
readSignShops();
}
@Override
public synchronized void putSignShop(SignShop signShop) {
cachedSignShops.put(signShop.getLocation(), signShop);
writeSignShops();
}
@Override
public synchronized void removeSignShop(SignShop signShop) {
cachedSignShops.remove(signShop.getLocation());
writeSignShops();
}
@Override
public Map<Location, SignShop> getSignShops() {
return ImmutableMap.copyOf(cachedSignShops);
}
private void readSignShops() {
cachedSignShops = new ConcurrentHashMap<>();
if (!file.exists()) {
return;
}
try {
ObjectInput ois = new ObjectInputStream(new FileInputStream(file));
Map<SerializableLocation, SignShop> tempMap = ((Map<SerializableLocation, SignShop>) ois.readObject());
tempMap.forEach((sLoc, shop) -> {
cachedSignShops.put(sLoc.getBukkitLocation(), shop);
});
ois.close();
} catch (Exception e) {
throw new RuntimeException("Failed to load sign shop date!", e);
}
}
private void writeSignShops() {
if (file.exists()) {
file.delete();
}
try {
ObjectOutput oos = new ObjectOutputStream(new FileOutputStream(file));
Map<SerializableLocation, SignShop> tempMap = new HashMap<>();
cachedSignShops.forEach((loc, shop) -> {
tempMap.put(new SerializableLocation(loc), shop);
});
oos.writeObject(tempMap);
oos.close();
} catch (Exception e) {
throw new RuntimeException("Failed to save sign shop date!", e);
}
}
}

View File

@ -0,0 +1,71 @@
package org.appledash.saneeconomysignshop.signshop.storage;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.appledash.saneeconomysignshop.signshop.SignShop;
import org.bukkit.Location;
import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by appledash on 1/18/17.
* Blackjack is still best pony.
*/
public class SignShopStorageJSON implements SignShopStorage {
private final Gson gson = new GsonBuilder().setPrettyPrinting().serializeNulls().create();
private final File storageFile;
private Map<Location, SignShop> cachedSignShops = new HashMap<>();
public SignShopStorageJSON(File storageFile) {
this.storageFile = storageFile;
}
@Override
@SuppressWarnings("unchecked")
public void loadSignShops() {
if (!storageFile.exists()) {
return;
}
try {
List<SignShop> tempShops = gson.fromJson(new FileReader(storageFile), new TypeToken<List<SignShop>>(){}.getType());
tempShops.forEach((shop) -> cachedSignShops.put(shop.getLocation(), shop));
} catch (FileNotFoundException e) {
throw new IllegalStateException("This shouldn't happen - the file " + storageFile.getAbsolutePath() + " disappeared while we were trying to read it!", e);
}
saveSignShops();
}
@Override
public void putSignShop(SignShop signShop) {
cachedSignShops.put(signShop.getLocation(), signShop);
saveSignShops();
}
@Override
public void removeSignShop(SignShop signShop) {
cachedSignShops.remove(signShop.getLocation());
saveSignShops();
}
private synchronized void saveSignShops() {
try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(storageFile, false))) {
bufferedWriter.write(gson.toJson(ImmutableList.copyOf(cachedSignShops.values())));
bufferedWriter.close();
} catch (IOException e) {
throw new RuntimeException("Failed to save sign shops!", e);
}
}
@Override
public Map<Location, SignShop> getSignShops() {
return ImmutableMap.copyOf(cachedSignShops);
}
}

View File

@ -2,6 +2,7 @@ package org.appledash.saneeconomysignshop.util;
import com.google.common.collect.ImmutableMap;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import java.io.BufferedReader;
import java.io.IOException;
@ -47,6 +48,66 @@ public class ItemDatabase {
return Optional.ofNullable(itemMap.get(name.toLowerCase()));
}
public static ItemStack parseGive(String rawItemName) throws InvalidItemException {
String itemName;
short damage;
if (rawItemName.contains(":")) {
String[] splitItemName = rawItemName.split(":");
itemName = splitItemName[0];
if (splitItemName.length == 1) { // They just typed 'tnt:'
damage = 0;
} else { // They typed 'tnt:something'
try {
damage = Short.valueOf(splitItemName[1]);
} catch (NumberFormatException e) {
throw new InvalidItemException("Damage value must be a number.");
}
}
} else { // No damage value
itemName = rawItemName;
damage = 0;
}
Optional<Material> materialOptional = parseMaterialFromName(itemName);
if (!materialOptional.isPresent()) {
Optional<Pair<Integer, Short>> parsedItem = getIDAndDamageForName(normalizeItemName(itemName));
if (!parsedItem.isPresent()) {
throw new InvalidItemException("Item by that name does not exist.");
}
if (damage == 0) {
damage = parsedItem.get().getRight();
}
return new ItemStack(parsedItem.get().getLeft(), 1, damage);
}
return new ItemStack(materialOptional.get(), 1, damage);
}
private static Optional<Material> parseMaterialFromName(String materialName) {
// Try to parse an integral item ID first, for legacy reasons.
try {
Material mat = Material.getMaterial(Integer.valueOf(materialName));
return Optional.ofNullable(mat);
} catch (NumberFormatException ignored) { }
for (Material mat : Material.values()) {
if (normalizeItemName(mat.name()).equals(normalizeItemName(materialName))) {
return Optional.of(mat);
}
}
return Optional.empty();
}
private static String normalizeItemName(String itemName) {
return itemName.toLowerCase().replace("_", "").replace(" ", "");
}
public static class Pair<K, V> {
private K k;
private V v;
@ -67,4 +128,10 @@ public class ItemDatabase {
return new Pair<>(k, v);
}
}
public static class InvalidItemException extends Exception {
public InvalidItemException(String message) {
super(message);
}
}
}

View File

@ -8,7 +8,6 @@ import org.bukkit.inventory.ItemStack;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.logging.Logger;
@ -80,15 +79,17 @@ public class LimitManager {
String itemName = String.valueOf(map.get("item"));
int sellLimit = Integer.valueOf(String.valueOf(map.get("limit")));
int hourlyGain = Integer.valueOf(String.valueOf(map.get("gain")));
ItemStack stack;
Optional<ItemDatabase.Pair<Integer, Short>> pair = ItemDatabase.getIDAndDamageForName(itemName);
if (!pair.isPresent()) {
try {
stack = ItemDatabase.parseGive(itemName);
} catch (ItemDatabase.InvalidItemException e) {
LOGGER.warning(String.format("You tried to load the item '%s' in limits.yml, but I have no idea what that is.", map.get("item")));
continue;
}
ItemInfo itemInfo = new ItemInfo(new ItemStack(pair.get().getLeft(), pair.get().getRight()));
ItemInfo itemInfo = new ItemInfo(stack);
sellItemLimits.put(itemInfo, new ItemLimits(sellLimit, hourlyGain));
}

View File

@ -53,7 +53,7 @@ sell:
- item: PORKCHOP
limit: 1280
gain: 128
- item: CHICKEN
- item: RAW_CHICKEN
limit: 1280
gain: 128
- item: DYE:3
@ -162,7 +162,7 @@ sell:
- item: CACTUS
limit: 1600
gain: 160
- item: WHEAT_SEEDS
- item: SEEDS
limit: 1600
gain: 160
- item: PUMPKIN_SEEDS
@ -399,51 +399,51 @@ sell:
- item: WOOL:15
limit: 1600
gain: 80
- item: STAINED_HARDENED_CLAY:14
- item: STAINED_CLAY:14
limit: 1600
gain: 80
- item: STAINED_HARDENED_CLAY:1
- item: STAINED_CLAY:1
limit: 1600
gain: 80
- item: STAINED_HARDENED_CLAY:4
- item: STAINED_CLAY:4
limit: 1600
gain: 80
- item: STAINED_HARDENED_CLAY:5
- item: STAINED_CLAY:5
limit: 1600
gain: 80
- item: STAINED_HARDENED_CLAY:13
- item: STAINED_CLAY:13
limit: 1600
gain: 80
- item: STAINED_HARDENED_CLAY:9
- item: STAINED_CLAY:9
limit: 1600
gain: 80
- item: STAINED_HARDENED_CLAY:3
- item: STAINED_CLAY:3
limit: 1600
gain: 80
- item: STAINED_HARDENED_CLAY:11
- item: STAINED_CLAY:11
limit: 1600
gain: 80
- item: STAINED_HARDENED_CLAY:10
- item: STAINED_CLAY:10
limit: 1600
gain: 80
- item: STAINED_HARDENED_CLAY:2
- item: STAINED_CLAY:2
limit: 1600
gain: 80
- item: STAINED_HARDENED_CLAY:6
- item: STAINED_CLAY:6
limit: 1600
gain: 80
- item: STAINED_HARDENED_CLAY:12
- item: STAINED_CLAY:12
limit: 1600
gain: 80
- item: STAINED_HARDENED_CLAY:0
- item: STAINED_CLAY:0
limit: 1600
gain: 80
- item: STAINED_HARDENED_CLAY:8
- item: STAINED_CLAY:8
limit: 1600
gain: 80
- item: STAINED_HARDENED_CLAY:7
- item: STAINED_CLAY:7
limit: 1600
gain: 80
- item: STAINED_HARDENED_CLAY:15
- item: STAINED_CLAY:15
limit: 1600
gain: 80