206 lines
8.5 KiB
Java
206 lines
8.5 KiB
Java
package com.Acrobot.ChestShop.Metadata;
|
|
|
|
import com.Acrobot.Breeze.Utils.Encoding.Base62;
|
|
import com.Acrobot.Breeze.Utils.Encoding.Base64;
|
|
import com.Acrobot.ChestShop.ChestShop;
|
|
import com.Acrobot.ChestShop.Database.DaoCreator;
|
|
import com.Acrobot.ChestShop.Database.Item;
|
|
import com.j256.ormlite.dao.CloseableIterator;
|
|
import com.j256.ormlite.dao.Dao;
|
|
import com.j256.ormlite.stmt.SelectArg;
|
|
import org.bukkit.Material;
|
|
import org.bukkit.configuration.file.YamlConfiguration;
|
|
import org.bukkit.configuration.file.YamlConstructor;
|
|
import org.bukkit.configuration.file.YamlRepresenter;
|
|
import org.bukkit.inventory.ItemStack;
|
|
import org.bukkit.inventory.meta.ItemMeta;
|
|
import org.yaml.snakeyaml.DumperOptions;
|
|
import org.yaml.snakeyaml.Yaml;
|
|
import org.yaml.snakeyaml.error.YAMLException;
|
|
import org.yaml.snakeyaml.nodes.Tag;
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.sql.SQLException;
|
|
import java.util.Map;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import java.util.logging.Level;
|
|
|
|
/**
|
|
* Saves items with Metadata in database, which allows for saving items on signs easily.
|
|
*
|
|
* @author Acrobot
|
|
*/
|
|
public class ItemDatabase {
|
|
private Dao<Item, Integer> itemDao;
|
|
|
|
private final Yaml yaml;
|
|
|
|
public ItemDatabase() {
|
|
yaml = new Yaml(new YamlBukkitConstructor(), new YamlRepresenter(), new DumperOptions());
|
|
|
|
try {
|
|
itemDao = DaoCreator.getDaoAndCreateTable(Item.class);
|
|
handleMetadataUpdate();
|
|
} catch (SQLException e) {
|
|
ChestShop.getBukkitLogger().log(Level.SEVERE, "Error while loading items database", e);
|
|
}
|
|
}
|
|
|
|
private void handleMetadataUpdate() {
|
|
File configFile = ChestShop.loadFile("version");
|
|
YamlConfiguration versionConfig = YamlConfiguration.loadConfiguration(configFile);
|
|
|
|
int previousVersion = versionConfig.getInt("metadata-version", -1);
|
|
int newVersion = getCurrentMetadataVersion();
|
|
if (previousVersion < newVersion) {
|
|
if (updateMetadataVersion(previousVersion, newVersion)) {
|
|
versionConfig.set("metadata-version", newVersion);
|
|
try {
|
|
versionConfig.save(configFile);
|
|
} catch (IOException e) {
|
|
ChestShop.getBukkitLogger().log(Level.SEVERE, "Error while updating metadata-version from " + previousVersion + " to " + newVersion, e);
|
|
}
|
|
} else {
|
|
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while updating Item Metadata database! While the plugin will still run it will work less efficiently.");
|
|
}
|
|
}
|
|
}
|
|
|
|
private int getCurrentMetadataVersion() {
|
|
ItemStack item = new ItemStack(Material.STONE);
|
|
ItemMeta meta = item.getItemMeta();
|
|
if (meta != null) {
|
|
meta.setDisplayName("GetCurrentMetadataVersion");
|
|
item.setItemMeta(meta);
|
|
}
|
|
Map<String, Object> serialized = item.serialize();
|
|
return (int) serialized.getOrDefault("v", -1);
|
|
}
|
|
|
|
private boolean updateMetadataVersion(int previousVersion, int newVersion) {
|
|
if (previousVersion > -1) {
|
|
ChestShop.getBukkitLogger().info("Data version change detected! Previous version was " + previousVersion);
|
|
}
|
|
ChestShop.getBukkitLogger().info("Updating Item Metadata database to data version " + newVersion + "...");
|
|
|
|
AtomicInteger i = new AtomicInteger();
|
|
AtomicInteger updated = new AtomicInteger();
|
|
CloseableIterator<Item> it = itemDao.iterator();
|
|
|
|
long start = System.currentTimeMillis();
|
|
try {
|
|
itemDao.callBatchTasks(() -> {
|
|
while (it.hasNext()) {
|
|
i.getAndIncrement();
|
|
Item item = it.next();
|
|
|
|
try {
|
|
String serialized = (String) Base64.decodeToObject(item.getBase64ItemCode());
|
|
if (previousVersion < 0 || !serialized.contains("\nv: " + newVersion + "\n")) { // Hacky way to quickly check the version as it's not too big of an issue if some items don't convert
|
|
try {
|
|
ItemStack itemStack = yaml.loadAs(serialized, ItemStack.class);
|
|
item.setBase64ItemCode(Base64.encodeObject(yaml.dump(itemStack)));
|
|
itemDao.update(item);
|
|
updated.getAndIncrement();
|
|
} catch (YAMLException | ClassCastException e) {
|
|
ChestShop.getBukkitLogger().log(Level.SEVERE, "YAML of the item with ID " + Base62.encode(item.getId()) + " (" + item.getId() + ") is corrupted: \n" + serialized);
|
|
}
|
|
}
|
|
} catch (IOException | ClassNotFoundException | SQLException e) {
|
|
ChestShop.getBukkitLogger().log(Level.SEVERE, "Unable to convert item with ID " + Base62.encode(item.getId()) + " (" + item.getId() + ")", e);
|
|
} catch (StackOverflowError e) {
|
|
ChestShop.getBukkitLogger().log(Level.SEVERE, "Item with ID " + Base62.encode(item.getId()) + " (" + item.getId() + ") is corrupted. Sorry :(");
|
|
}
|
|
if (i.get() % 1000 == 0) {
|
|
ChestShop.getBukkitLogger().info("Checked " + i + " items. Updated " + updated + "...");
|
|
}
|
|
}
|
|
return true;
|
|
});
|
|
} catch (Exception e) {
|
|
ChestShop.getBukkitLogger().log(Level.SEVERE, "Unable to update metadata version of all items from " + previousVersion + " to " + newVersion, e);
|
|
return false;
|
|
} finally {
|
|
it.closeQuietly();
|
|
}
|
|
|
|
ChestShop.getBukkitLogger().info("Finished updating database in " + (System.currentTimeMillis() - start) / 1000.0 + "s. " +
|
|
updated + " items out of " + i + " were updated!");
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Gets the item code for this item
|
|
*
|
|
* @param item Item
|
|
* @return Item code for this item
|
|
*/
|
|
public String getItemCode(ItemStack item) {
|
|
try {
|
|
ItemStack clone = new ItemStack(item);
|
|
clone.setAmount(1);
|
|
clone.setDurability((short) 0);
|
|
|
|
String dumped = yaml.dump(clone);
|
|
ItemStack loadedItem = yaml.loadAs(dumped, ItemStack.class);
|
|
if (!loadedItem.isSimilar(item)) {
|
|
dumped = yaml.dump(loadedItem);
|
|
}
|
|
String code = Base64.encodeObject(dumped);
|
|
|
|
Item itemEntity = itemDao.queryBuilder().where().eq("code", new SelectArg(code)).queryForFirst();
|
|
if (itemEntity == null) {
|
|
itemEntity = new Item(code);
|
|
itemDao.create(itemEntity);
|
|
}
|
|
return Base62.encode(itemEntity.getId());
|
|
} catch (SQLException | IOException e) {
|
|
ChestShop.getBukkitLogger().log(Level.SEVERE, "Unable to get code of item " + item, e);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Gets an ItemStack from a item code
|
|
*
|
|
* @param code Item code
|
|
* @return ItemStack represented by this code
|
|
*/
|
|
public ItemStack getFromCode(String code)
|
|
{
|
|
// TODO java.lang.StackOverflowError - http://pastebin.com/eRD8wUFM - Corrupt item DB?
|
|
|
|
int id = Base62.decode(code);
|
|
try {
|
|
Item item = itemDao.queryBuilder().where().eq("id", new SelectArg(id)).queryForFirst();
|
|
|
|
if (item == null) {
|
|
return null;
|
|
}
|
|
|
|
String serialized = item.getBase64ItemCode();
|
|
|
|
try {
|
|
return yaml.loadAs((String) Base64.decodeToObject(serialized), ItemStack.class);
|
|
} catch (YAMLException e) {
|
|
ChestShop.getBukkitLogger().log(Level.SEVERE, "YAML of the item with ID " + Base62.encode(item.getId()) + " (" + item.getId() + ") is corrupted: \n" + serialized);
|
|
}
|
|
} catch (IOException | ClassNotFoundException | SQLException | YAMLException e) {
|
|
ChestShop.getBukkitLogger().log(Level.SEVERE, "Unable to load item with ID " + code + " (" + id + ")", e);
|
|
} catch (StackOverflowError e) {
|
|
ChestShop.getBukkitLogger().log(Level.SEVERE, "Item with ID " + code + " (" + id + ") is corrupted. Sorry :(");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static class YamlBukkitConstructor extends YamlConstructor {
|
|
public YamlBukkitConstructor() {
|
|
this.yamlConstructors.put(new Tag(Tag.PREFIX + "org.bukkit.inventory.ItemStack"), yamlConstructors.get(Tag.MAP));
|
|
}
|
|
|
|
}
|
|
}
|