diff --git a/src/com/massivecraft/mcore/adapter/InventoryAdapter.java b/src/com/massivecraft/mcore/adapter/InventoryAdapter.java index 5f5c9c45..f5867abe 100644 --- a/src/com/massivecraft/mcore/adapter/InventoryAdapter.java +++ b/src/com/massivecraft/mcore/adapter/InventoryAdapter.java @@ -2,11 +2,14 @@ package com.massivecraft.mcore.adapter; import java.lang.reflect.Type; -import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_5_R2.inventory.CraftInventoryCustom; +import org.bukkit.craftbukkit.v1_5_R2.inventory.CraftInventoryPlayer; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; import com.massivecraft.mcore.MCore; +import com.massivecraft.mcore.inventory.MCorePlayerInventory; import com.massivecraft.mcore.xlib.gson.JsonDeserializationContext; import com.massivecraft.mcore.xlib.gson.JsonDeserializer; import com.massivecraft.mcore.xlib.gson.JsonElement; @@ -16,6 +19,11 @@ import com.massivecraft.mcore.xlib.gson.JsonPrimitive; import com.massivecraft.mcore.xlib.gson.JsonSerializationContext; import com.massivecraft.mcore.xlib.gson.JsonSerializer; +/** + * This is my Gson adapter for Inventories. + * It handles all inventories as CraftInventoryCustom "Chest"s with size of your choice + * except for PlayerInventory which it handles pretty darn well! + */ public class InventoryAdapter implements JsonDeserializer, JsonSerializer { // -------------------------------------------- // @@ -23,6 +31,13 @@ public class InventoryAdapter implements JsonDeserializer, JsonSerial // -------------------------------------------- // public static final String SIZE = "size"; + + public static final String PLAYER = "player"; + + public static final String HELMET = "helmet"; + public static final String CHESTPLATE = "chestplate"; + public static final String LEGGINGS = "leggings"; + public static final String BOOTS = "boots"; // -------------------------------------------- // // IMPLEMENTATION @@ -46,42 +61,161 @@ public class InventoryAdapter implements JsonDeserializer, JsonSerial public static JsonElement toJson(Inventory src) { + // The return value is this object: JsonObject jsonInventory = new JsonObject(); - ItemStack[] itemStacks = src.getContents(); - jsonInventory.add(SIZE, new JsonPrimitive(itemStacks.length)); + // These variables are used in loops and repetitive logic. + ItemStack itemStack = null; + JsonElement jsonItemStack = null; + + // Every inventory has a content part. Lets handle it at once: + ItemStack[] itemStacks = src.getContents(); for (int i = 0; i < itemStacks.length; i++) { - ItemStack itemStack = itemStacks[i]; - JsonElement jsonItemStack = MCore.gson.toJsonTree(itemStack, ItemStack.class); + itemStack = itemStacks[i]; + jsonItemStack = MCore.gson.toJsonTree(itemStack, ItemStack.class); if (jsonItemStack == null) continue; jsonInventory.add(String.valueOf(i), jsonItemStack); } + if (src instanceof PlayerInventory) + { + // Add the size "player" + jsonInventory.addProperty(SIZE, PLAYER); + + // Cast to PlayerInventory + PlayerInventory psrc = (PlayerInventory)src; + + // helmet + itemStack = psrc.getHelmet(); + if (itemStack != null) + { + jsonItemStack = MCore.gson.toJsonTree(itemStack, ItemStack.class); + jsonInventory.add(HELMET, jsonItemStack); + } + + // chestplate + itemStack = psrc.getChestplate(); + if (itemStack != null) + { + jsonItemStack = MCore.gson.toJsonTree(itemStack, ItemStack.class); + jsonInventory.add(CHESTPLATE, jsonItemStack); + } + + // leggings + itemStack = psrc.getLeggings(); + if (itemStack != null) + { + jsonItemStack = MCore.gson.toJsonTree(itemStack, ItemStack.class); + jsonInventory.add(LEGGINGS, jsonItemStack); + } + + // boots + itemStack = psrc.getBoots(); + if (itemStack != null) + { + jsonItemStack = MCore.gson.toJsonTree(itemStack, ItemStack.class); + jsonInventory.add(BOOTS, jsonItemStack); + } + } + else + { + // Add the size *length* + jsonInventory.addProperty(SIZE, itemStacks.length); + } + return jsonInventory; } public static Inventory fromJson(JsonElement json) { + // If must be an object! if ( ! json.isJsonObject()) return null; JsonObject jsonInventory = json.getAsJsonObject(); + // The return value + Inventory ret = null; + + // These variables are used in loops and repetitive logic. + ItemStack itemStack = null; + JsonElement jsonItemStack = null; + + // There must be a size entry! if ( ! jsonInventory.has(SIZE)) return null; - int size = jsonInventory.get(SIZE).getAsInt(); + JsonPrimitive jsonSize = jsonInventory.get(SIZE).getAsJsonPrimitive(); + int size = 0; + // What size/type is it? + if (jsonSize.isString()) + { + // Only makes sense if stating "player". + if (!jsonSize.getAsString().equals(PLAYER)) return null; + + // We use 36 here since it's the size of the player inventory (without armor) + size = 36; + + // This is a PlayerInventory + ret = new CraftInventoryPlayer(new MCorePlayerInventory()); + PlayerInventory pret = (PlayerInventory)ret; + + // helmet + if (jsonInventory.has(HELMET)) + { + jsonItemStack = jsonInventory.get(HELMET); + itemStack = MCore.gson.fromJson(jsonItemStack, ItemStack.class); + pret.setHelmet(itemStack); + } + + // chestplate + if (jsonInventory.has(CHESTPLATE)) + { + jsonItemStack = jsonInventory.get(CHESTPLATE); + itemStack = MCore.gson.fromJson(jsonItemStack, ItemStack.class); + pret.setChestplate(itemStack); + } + + // leggings + if (jsonInventory.has(LEGGINGS)) + { + jsonItemStack = jsonInventory.get(LEGGINGS); + itemStack = MCore.gson.fromJson(jsonItemStack, ItemStack.class); + pret.setLeggings(itemStack); + } + + // boots + if (jsonInventory.has(BOOTS)) + { + jsonItemStack = jsonInventory.get(BOOTS); + itemStack = MCore.gson.fromJson(jsonItemStack, ItemStack.class); + pret.setBoots(itemStack); + } + } + else if (jsonSize.isNumber()) + { + // A custom size were specified + size = jsonSize.getAsInt(); + + // This is a "Custom" Inventory (content only). + ret = new CraftInventoryCustom(null, size); + } + else + { + // It must be either string or number + return null; + } + + // Now process content ItemStack[] itemStacks = new ItemStack[size]; - for (int i = 0; i < size; i++) { // Fetch the jsonItemStack or mark it as empty and continue String stackIdx = String.valueOf(i); - JsonElement jsonItemStack = jsonInventory.get(stackIdx); - ItemStack itemStack = MCore.gson.fromJson(jsonItemStack, ItemStack.class); + jsonItemStack = jsonInventory.get(stackIdx); + itemStack = MCore.gson.fromJson(jsonItemStack, ItemStack.class); itemStacks[i] = itemStack; } - - Inventory ret = Bukkit.createInventory(null, size); ret.setContents(itemStacks); + return ret; } diff --git a/src/com/massivecraft/mcore/inventory/MCorePlayerInventory.java b/src/com/massivecraft/mcore/inventory/MCorePlayerInventory.java new file mode 100644 index 00000000..66a554fb --- /dev/null +++ b/src/com/massivecraft/mcore/inventory/MCorePlayerInventory.java @@ -0,0 +1,111 @@ +package com.massivecraft.mcore.inventory; + +import org.bukkit.inventory.InventoryHolder; + +import net.minecraft.server.v1_5_R2.EntityHuman; +import net.minecraft.server.v1_5_R2.ItemStack; +import net.minecraft.server.v1_5_R2.PlayerInventory; + +/** + * This is an extended version of the NMS.PlayerInventory. + * It is extended in such a way that it has a no-arg constructor. + * It is mainly used for deserialization of PlayerInventor. + * + * What is tricky about the NMS.PlayerInventory is that it does hold a link/field to the holder/player. + * It is however acceptable for this field "public EntityHuman player" to be null as long as some internal methods are rewritten. + * + * NPE evasion is achieved by overriding all internal methods using the player field. + * + * How to update: + * Do go to NMS.PlayerInventory and search for references to "player". + * As of 1.5.1 these are the references: + * + * a(EntityHuman) (2 matches) + * g(int) + * getOwner() + * k() (2 matches) + * m() (2 matches) + * pickup(ItemStack) (2 matches) + * PlayerInventory(EntityHuman) + * + */ +public class MCorePlayerInventory extends PlayerInventory +{ + // -------------------------------------------- // + // CONSTRUCT + // -------------------------------------------- // + + public MCorePlayerInventory() + { + super(null); + } + + // -------------------------------------------- // + // NPE EVASION + // -------------------------------------------- // + + // Is the entityhuman within reach? + // Can it edit the inventory content? + // According to the source code design entityhuman is never null. However we go for safety first. + @Override + public boolean a(EntityHuman entityhuman) + { + // Null cases + if (entityhuman == null) return true; + if (this.player == null) return true; + + // Other people can reach at any time + if (!this.player.equals(entityhuman)) return true; + + return super.a(entityhuman); + } + + // This method handles damage dealt to the armor inside the inventory. + // We simply ignore damage if there is no player. + @Override + public void g(int arg0) + { + if (this.player == null) return; + + super.g(arg0); + } + + // If the player is null there is no owner. + @Override + public InventoryHolder getOwner() + { + if (this.player == null) return null; + + return super.getOwner(); + } + + // This method looks like some sort of recurring tick to the items but not inventories. + // Let's just ignore it if the player is elsewhere. + @Override + public void k() + { + if (this.player == null) return; + + super.k(); + } + + // Called when the player dies and no items should be kept. + // This will cause the player to drop all the items. + @Override + public void m() + { + if (this.player == null) return; + + super.m(); + } + + // Pickup the item. Return if the pickup was successful or not. + @Override + public boolean pickup(ItemStack arg0) + { + if (this.player == null) return false; + + return super.pickup(arg0); + } + +} diff --git a/src/com/massivecraft/mcore/util/InventoryUtil.java b/src/com/massivecraft/mcore/util/InventoryUtil.java index 21dd1136..8a6475b1 100644 --- a/src/com/massivecraft/mcore/util/InventoryUtil.java +++ b/src/com/massivecraft/mcore/util/InventoryUtil.java @@ -2,14 +2,18 @@ package com.massivecraft.mcore.util; import java.util.HashMap; -import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.craftbukkit.v1_5_R2.inventory.CraftInventoryCustom; +import org.bukkit.craftbukkit.v1_5_R2.inventory.CraftInventoryPlayer; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.inventory.InventoryType.SlotType; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; + +import com.massivecraft.mcore.inventory.MCorePlayerInventory; public class InventoryUtil { @@ -115,16 +119,38 @@ public class InventoryUtil public static boolean isEmpty(Inventory inv) { if (inv == null) return true; - for (ItemStack stack : inv.getContents()) + + for (ItemStack itemStack : inv.getContents()) { - if (stack == null) continue; - if (stack.getAmount() == 0) continue; - if (stack.getTypeId() == 0) continue; - return false; + if (isSomething(itemStack)) return false; } + + if (inv instanceof PlayerInventory) + { + PlayerInventory pinv = (PlayerInventory)inv; + + if (isSomething(pinv.getHelmet())) return false; + if (isSomething(pinv.getChestplate())) return false; + if (isSomething(pinv.getLeggings())) return false; + if (isSomething(pinv.getBoots())) return false; + } + return true; } + public static boolean isNothing(ItemStack itemStack) + { + if (itemStack == null) return true; + if (itemStack.getAmount() == 0) return true; + if (itemStack.getTypeId() == 0) return true; + return false; + } + + public static boolean isSomething(ItemStack itemStack) + { + return !isNothing(itemStack); + } + // -------------------------------------------- // // CLONE ITEMSTACKS/INVENTORY // -------------------------------------------- // @@ -141,18 +167,35 @@ public class InventoryUtil return ret; } - // NOTE: This method does not handle the armor part of player inventories. - // That is expected behavior for now. public static Inventory cloneInventory(Inventory inventory) { if (inventory == null) return null; - InventoryHolder holder = inventory.getHolder(); - int size = inventory.getSize(); - String title = inventory.getTitle(); - ItemStack[] contents = cloneItemStacks(inventory.getContents()); + Inventory ret = null; - Inventory ret = Bukkit.createInventory(holder, size, title); + int size = inventory.getSize(); + InventoryHolder holder = inventory.getHolder(); + String title = inventory.getTitle(); + + if (inventory instanceof PlayerInventory) + { + MCorePlayerInventory nmsret = new MCorePlayerInventory(); + CraftInventoryPlayer pret = new CraftInventoryPlayer(nmsret); + ret = pret; + + PlayerInventory pinventory = (PlayerInventory)inventory; + + pret.setHelmet(pinventory.getHelmet() == null ? null : new ItemStack(pinventory.getHelmet())); + pret.setChestplate(pinventory.getChestplate() == null ? null : new ItemStack(pinventory.getChestplate())); + pret.setLeggings(pinventory.getLeggings() == null ? null : new ItemStack(pinventory.getLeggings())); + pret.setBoots(pinventory.getBoots() == null ? null : new ItemStack(pinventory.getBoots())); + } + else + { + ret = new CraftInventoryCustom(holder, size, title); + } + + ItemStack[] contents = cloneItemStacks(inventory.getContents()); ret.setContents(contents); return ret;