From d12bf539726fbb6e2e0a46877764b4b3e92d7525 Mon Sep 17 00:00:00 2001 From: unurled Date: Fri, 15 Mar 2024 11:52:08 +0100 Subject: [PATCH] Save and load items --- .../sacredrealms/sr/components/item/Item.java | 76 ++++++++++++++++--- .../sr/components/item/ItemManager.java | 41 +++++++++- .../sr/components/item/ItemType.java | 8 ++ .../sr/components/item/abilities/Ability.java | 53 +++++-------- .../sr/data/gson/AbilityDeserializer.java | 46 +++++++++++ .../sr/data/gson/ItemDeserializer.java | 35 +++++++++ .../sr/data/gson/ItemSerializer.java | 31 ++++++++ .../unurled/sacredrealms/sr/utils/Items.java | 8 +- 8 files changed, 245 insertions(+), 53 deletions(-) create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/item/ItemType.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/data/gson/AbilityDeserializer.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/data/gson/ItemDeserializer.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/data/gson/ItemSerializer.java diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/item/Item.java b/src/main/java/me/unurled/sacredrealms/sr/components/item/Item.java index af0a714..72d4ab2 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/item/Item.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/item/Item.java @@ -2,12 +2,19 @@ package me.unurled.sacredrealms.sr.components.item; import static me.unurled.sacredrealms.sr.utils.Component.comp; import static me.unurled.sacredrealms.sr.utils.Items.lore; +import static me.unurled.sacredrealms.sr.utils.Logger.error; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import java.lang.reflect.Type; import java.util.HashMap; import java.util.List; +import java.util.Map; import me.unurled.sacredrealms.sr.components.attributes.Attribute; import me.unurled.sacredrealms.sr.components.item.abilities.Ability; import me.unurled.sacredrealms.sr.components.item.enchantments.Enchantment; +import me.unurled.sacredrealms.sr.data.gson.AbilityDeserializer; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; @@ -38,7 +45,7 @@ import org.jetbrains.annotations.NotNull; * */ public class Item { - @NotNull private final String ID; + @NotNull private String ID; @NotNull private String name; @@ -50,12 +57,27 @@ public class Item { @NotNull private Rarity rarity; + @NotNull private ItemType type; + @NotNull private HashMap attributes; @NotNull private HashMap enchantments; @NotNull private List abilities; + public Item() { + this.ID = ""; + this.name = ""; + this.material = Material.AIR; + this.description = ""; + this.rarity = Rarity.COMMON; + this.customModelData = 0; + this.abilities = List.of(); + this.attributes = new HashMap<>(); + this.enchantments = new HashMap<>(); + this.type = ItemType.MISC; + } + public Item(@NotNull String ID, @NotNull String name, @NotNull Material material) { this.ID = ID; this.name = name; @@ -66,6 +88,7 @@ public class Item { this.abilities = List.of(); this.attributes = new HashMap<>(); this.enchantments = new HashMap<>(); + this.type = ItemType.MISC; } public @NotNull String getID() { @@ -189,24 +212,55 @@ public class Item { @Override public String toString() { return "{" - + "ID: " + + "\"id\": \"" + ID - + ", name: " + + "\", \"name\": \"" + name - + ", material: " + + "\", \"material\": \"" + material - + ", description: " + + "\", \"description\": \"" + description - + ", customModelData: " + + "\", \"customModelData\": " + customModelData - + ", rarity: " + + "\", \"rarity\": \"" + rarity - + ", attributes: " + + "\", \"type\": \"" + + type + + "\", \"attributes\": \"" + attributes - + ", enchantments: " + + "\", \"enchantments\": \"" + enchantments - + ", abilities: " + + "\", \"abilities\": \"" + abilities - + "}"; + + "\"}"; + } + + public void fromString(String item) { + // extract every information from the string (it follows the same format as the toString method) + Gson gson = + new GsonBuilder().registerTypeAdapter(Ability.class, new AbilityDeserializer()).create(); + try { + Map map = + gson.fromJson(item, new TypeToken>() {}.getType()); + + this.ID = (String) map.get("id"); + this.name = (String) map.get("name"); + this.material = Material.valueOf((String) map.get("material")); + this.description = (String) map.get("description"); + this.customModelData = ((Double) map.get("customModelData")).intValue(); + this.rarity = Rarity.valueOf((String) map.get("rarity")); + this.type = ItemType.valueOf((String) map.get("type")); + + Type attributeType = new TypeToken>() {}.getType(); + this.attributes = gson.fromJson((String) map.get("attributes"), attributeType); + + Type enchantmentType = new TypeToken>() {}.getType(); + this.enchantments = gson.fromJson((String) map.get("enchantments"), enchantmentType); + + Type abilityType = new TypeToken>() {}.getType(); + this.abilities = gson.fromJson((String) map.get("abilities"), abilityType); + } catch (Exception e) { + error("Failed to parse item from string: " + item + "\n" + e.getMessage()); + } } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/item/ItemManager.java b/src/main/java/me/unurled/sacredrealms/sr/components/item/ItemManager.java index da5c046..15814e9 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/item/ItemManager.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/item/ItemManager.java @@ -1,6 +1,12 @@ package me.unurled.sacredrealms.sr.components.item; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import java.util.HashMap; +import me.unurled.sacredrealms.sr.data.DataHandler; +import me.unurled.sacredrealms.sr.data.DataManager; +import me.unurled.sacredrealms.sr.data.gson.ItemDeserializer; +import me.unurled.sacredrealms.sr.data.gson.ItemStackSerializer; import me.unurled.sacredrealms.sr.managers.Manager; import org.bukkit.NamespacedKey; import org.bukkit.inventory.ItemStack; @@ -17,14 +23,41 @@ public class ItemManager extends Manager { items = new HashMap<>(); } - /** Register all the items */ - private void registerItems() {} - /** Load the manager */ @Override public void load() { super.load(); - registerItems(); + } + + /** Save the data */ + @Override + public void saveData() { + DataManager dm = DataManager.getInstance(DataManager.class); + DataHandler dh = dm.getDataHandler(); + + Gson gson = + new GsonBuilder().registerTypeAdapter(Item.class, new ItemStackSerializer()).create(); + + items.forEach( + (id, item) -> { + dh.set("sr.items." + id, gson.toJson(item)); + }); + } + + /** Load the data */ + @Override + public void loadData() { + DataManager dm = DataManager.getInstance(DataManager.class); + DataHandler dh = dm.getDataHandler(); + + Gson gson = new GsonBuilder().registerTypeAdapter(Item.class, new ItemDeserializer()).create(); + + dh.getKeysAll("sr.items") + .forEach( + key -> { + Item item = gson.fromJson(dh.get(key), Item.class); + items.put(item.getID(), item); + }); } public void addItem(Item item) { diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/item/ItemType.java b/src/main/java/me/unurled/sacredrealms/sr/components/item/ItemType.java new file mode 100644 index 0000000..244adce --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/item/ItemType.java @@ -0,0 +1,8 @@ +package me.unurled.sacredrealms.sr.components.item; + +public enum ItemType { + WEAPON, + ARMOR, + CONSUMABLE, + MISC +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/item/abilities/Ability.java b/src/main/java/me/unurled/sacredrealms/sr/components/item/abilities/Ability.java index d0250f6..3b18d81 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/item/abilities/Ability.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/item/abilities/Ability.java @@ -1,41 +1,26 @@ package me.unurled.sacredrealms.sr.components.item.abilities; + /** Represents an ability that an item can have. TODO: Implement this class */ @SuppressWarnings("unused") -public class Ability { +public record Ability( + String name, String description, Integer cooldown, Integer manaCost, Integer damage) { - private final String name; - private final String description; - private final Integer cooldown; - private final Integer manaCost; - private final Integer damage; - - public Ability( - String name, String description, Integer cooldown, Integer manaCost, Integer damage) { - this.name = name; - this.description = description; - this.cooldown = cooldown; - this.manaCost = manaCost; - this.damage = damage; - } - - public String getName() { - return name; - } - - public String getDescription() { - return description; - } - - public Integer getCooldown() { - return cooldown; - } - - public Integer getManaCost() { - return manaCost; - } - - public Integer getDamage() { - return damage; + @Override + public String toString() { + return "Ability{" + + "name='" + + name + + '\'' + + ", description='" + + description + + '\'' + + ", cooldown=" + + cooldown + + ", manaCost=" + + manaCost + + ", damage=" + + damage + + '}'; } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/data/gson/AbilityDeserializer.java b/src/main/java/me/unurled/sacredrealms/sr/data/gson/AbilityDeserializer.java new file mode 100644 index 0000000..50f2f04 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/data/gson/AbilityDeserializer.java @@ -0,0 +1,46 @@ +package me.unurled.sacredrealms.sr.data.gson; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import java.lang.reflect.Type; +import me.unurled.sacredrealms.sr.components.item.abilities.Ability; + +public class AbilityDeserializer implements JsonDeserializer { + + /** + * Gson invokes this call-back method during deserialization when it encounters a field of the + * specified type. + * + *

In the implementation of this call-back method, you should consider invoking {@link + * JsonDeserializationContext#deserialize(JsonElement, Type)} method to create objects for any + * non-trivial field of the returned object. However, you should never invoke it on the same type + * passing {@code json} since that will cause an infinite loop (Gson will call your call-back + * method again). + * + * @param json The Json data being deserialized + * @param typeOfT The type of the Object to deserialize to + * @param context + * @return a deserialized object of the specified type typeOfT which is a subclass of {@code T} + * @throws JsonParseException if json is not in the expected format of {@code typeofT} + */ + @Override + public Ability deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + try { + JsonObject jsonObject = json.getAsJsonObject(); + + String name = jsonObject.get("name").getAsString(); + String description = jsonObject.get("description").getAsString(); + Integer cooldown = jsonObject.get("cooldown").getAsInt(); + Integer manaCost = jsonObject.get("manaCost").getAsInt(); + Integer damage = jsonObject.get("damage").getAsInt(); + + return new Ability(name, description, cooldown, manaCost, damage); + } catch (Exception e) { + throw new JsonParseException("Error deserializing Ability: " + e.getMessage()); + } + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/data/gson/ItemDeserializer.java b/src/main/java/me/unurled/sacredrealms/sr/data/gson/ItemDeserializer.java new file mode 100644 index 0000000..4c74c43 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/data/gson/ItemDeserializer.java @@ -0,0 +1,35 @@ +package me.unurled.sacredrealms.sr.data.gson; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import java.lang.reflect.Type; +import me.unurled.sacredrealms.sr.components.item.Item; + +public class ItemDeserializer implements JsonDeserializer { + + /** + * Gson invokes this call-back method during deserialization when it encounters a field of the + * specified type. + * + *

In the implementation of this call-back method, you should consider invoking {@link + * JsonDeserializationContext#deserialize(JsonElement, Type)} method to create objects for any + * non-trivial field of the returned object. However, you should never invoke it on the same type + * passing {@code json} since that will cause an infinite loop (Gson will call your call-back + * method again). + * + * @param json The Json data being deserialized + * @param typeOfT The type of the Object to deserialize to + * @param context + * @return a deserialized object of the specified type typeOfT which is a subclass of {@code T} + * @throws JsonParseException if json is not in the expected format of {@code typeofT} + */ + @Override + public Item deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + Item item = new Item(); + item.fromString(json.getAsString()); + return item; + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/data/gson/ItemSerializer.java b/src/main/java/me/unurled/sacredrealms/sr/data/gson/ItemSerializer.java new file mode 100644 index 0000000..51ea8f8 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/data/gson/ItemSerializer.java @@ -0,0 +1,31 @@ +package me.unurled.sacredrealms.sr.data.gson; + +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import java.lang.reflect.Type; +import me.unurled.sacredrealms.sr.components.item.Item; + +public class ItemSerializer implements JsonSerializer { + + /** + * Gson invokes this call-back method during serialization when it encounters a field of the + * specified type. + * + *

In the implementation of this call-back method, you should consider invoking {@link + * JsonSerializationContext#serialize(Object, Type)} method to create JsonElements for any + * non-trivial field of the {@code src} object. However, you should never invoke it on the {@code + * src} object itself since that will cause an infinite loop (Gson will call your call-back method + * again). + * + * @param src the object that needs to be converted to Json. + * @param typeOfSrc the actual type (fully genericized version) of the source object. + * @param context + * @return a JsonElement corresponding to the specified object. + */ + @Override + public JsonElement serialize(Item src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src.toString()); + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/utils/Items.java b/src/main/java/me/unurled/sacredrealms/sr/utils/Items.java index d1f3fcf..f25c378 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/utils/Items.java +++ b/src/main/java/me/unurled/sacredrealms/sr/utils/Items.java @@ -102,10 +102,10 @@ public class Items { item.getAbilities() .forEach( a -> { - lore.add(comp(" ➤ " + a.getName() + ", " + a.getDescription())); - lore.add(comp(" cooldown: " + a.getCooldown() + "s,")); - lore.add(comp(" damage: " + a.getDamage() + ",")); - lore.add(comp(" mana cost: " + a.getManaCost())); + lore.add(comp(" ➤ " + a.name() + ", " + a.description())); + lore.add(comp(" cooldown: " + a.cooldown() + "s,")); + lore.add(comp(" damage: " + a.damage() + ",")); + lore.add(comp(" mana cost: " + a.manaCost())); }); } if (!item.getEnchantments().isEmpty()) {