Experimental PlayerInventory serdes support.
This commit is contained in:
parent
4c70897d87
commit
9cc687dab0
@ -2,11 +2,14 @@ package com.massivecraft.mcore.adapter;
|
|||||||
|
|
||||||
import java.lang.reflect.Type;
|
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.Inventory;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.PlayerInventory;
|
||||||
|
|
||||||
import com.massivecraft.mcore.MCore;
|
import com.massivecraft.mcore.MCore;
|
||||||
|
import com.massivecraft.mcore.inventory.MCorePlayerInventory;
|
||||||
import com.massivecraft.mcore.xlib.gson.JsonDeserializationContext;
|
import com.massivecraft.mcore.xlib.gson.JsonDeserializationContext;
|
||||||
import com.massivecraft.mcore.xlib.gson.JsonDeserializer;
|
import com.massivecraft.mcore.xlib.gson.JsonDeserializer;
|
||||||
import com.massivecraft.mcore.xlib.gson.JsonElement;
|
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.JsonSerializationContext;
|
||||||
import com.massivecraft.mcore.xlib.gson.JsonSerializer;
|
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<Inventory>, JsonSerializer<Inventory>
|
public class InventoryAdapter implements JsonDeserializer<Inventory>, JsonSerializer<Inventory>
|
||||||
{
|
{
|
||||||
// -------------------------------------------- //
|
// -------------------------------------------- //
|
||||||
@ -23,6 +31,13 @@ public class InventoryAdapter implements JsonDeserializer<Inventory>, JsonSerial
|
|||||||
// -------------------------------------------- //
|
// -------------------------------------------- //
|
||||||
|
|
||||||
public static final String SIZE = "size";
|
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
|
// IMPLEMENTATION
|
||||||
@ -46,42 +61,161 @@ public class InventoryAdapter implements JsonDeserializer<Inventory>, JsonSerial
|
|||||||
|
|
||||||
public static JsonElement toJson(Inventory src)
|
public static JsonElement toJson(Inventory src)
|
||||||
{
|
{
|
||||||
|
// The return value is this object:
|
||||||
JsonObject jsonInventory = new JsonObject();
|
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++)
|
for (int i = 0; i < itemStacks.length; i++)
|
||||||
{
|
{
|
||||||
ItemStack itemStack = itemStacks[i];
|
itemStack = itemStacks[i];
|
||||||
JsonElement jsonItemStack = MCore.gson.toJsonTree(itemStack, ItemStack.class);
|
jsonItemStack = MCore.gson.toJsonTree(itemStack, ItemStack.class);
|
||||||
if (jsonItemStack == null) continue;
|
if (jsonItemStack == null) continue;
|
||||||
jsonInventory.add(String.valueOf(i), jsonItemStack);
|
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;
|
return jsonInventory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Inventory fromJson(JsonElement json)
|
public static Inventory fromJson(JsonElement json)
|
||||||
{
|
{
|
||||||
|
// If must be an object!
|
||||||
if ( ! json.isJsonObject()) return null;
|
if ( ! json.isJsonObject()) return null;
|
||||||
JsonObject jsonInventory = json.getAsJsonObject();
|
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;
|
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];
|
ItemStack[] itemStacks = new ItemStack[size];
|
||||||
|
|
||||||
for (int i = 0; i < size; i++)
|
for (int i = 0; i < size; i++)
|
||||||
{
|
{
|
||||||
// Fetch the jsonItemStack or mark it as empty and continue
|
// Fetch the jsonItemStack or mark it as empty and continue
|
||||||
String stackIdx = String.valueOf(i);
|
String stackIdx = String.valueOf(i);
|
||||||
JsonElement jsonItemStack = jsonInventory.get(stackIdx);
|
jsonItemStack = jsonInventory.get(stackIdx);
|
||||||
ItemStack itemStack = MCore.gson.fromJson(jsonItemStack, ItemStack.class);
|
itemStack = MCore.gson.fromJson(jsonItemStack, ItemStack.class);
|
||||||
itemStacks[i] = itemStack;
|
itemStacks[i] = itemStack;
|
||||||
}
|
}
|
||||||
|
|
||||||
Inventory ret = Bukkit.createInventory(null, size);
|
|
||||||
ret.setContents(itemStacks);
|
ret.setContents(itemStacks);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
111
src/com/massivecraft/mcore/inventory/MCorePlayerInventory.java
Normal file
111
src/com/massivecraft/mcore/inventory/MCorePlayerInventory.java
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,14 +2,18 @@ package com.massivecraft.mcore.util;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.Material;
|
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.InventoryClickEvent;
|
||||||
import org.bukkit.event.inventory.InventoryType;
|
import org.bukkit.event.inventory.InventoryType;
|
||||||
import org.bukkit.event.inventory.InventoryType.SlotType;
|
import org.bukkit.event.inventory.InventoryType.SlotType;
|
||||||
import org.bukkit.inventory.Inventory;
|
import org.bukkit.inventory.Inventory;
|
||||||
import org.bukkit.inventory.InventoryHolder;
|
import org.bukkit.inventory.InventoryHolder;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.PlayerInventory;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore.inventory.MCorePlayerInventory;
|
||||||
|
|
||||||
public class InventoryUtil
|
public class InventoryUtil
|
||||||
{
|
{
|
||||||
@ -115,16 +119,38 @@ public class InventoryUtil
|
|||||||
public static boolean isEmpty(Inventory inv)
|
public static boolean isEmpty(Inventory inv)
|
||||||
{
|
{
|
||||||
if (inv == null) return true;
|
if (inv == null) return true;
|
||||||
for (ItemStack stack : inv.getContents())
|
|
||||||
|
for (ItemStack itemStack : inv.getContents())
|
||||||
{
|
{
|
||||||
if (stack == null) continue;
|
if (isSomething(itemStack)) return false;
|
||||||
if (stack.getAmount() == 0) continue;
|
|
||||||
if (stack.getTypeId() == 0) continue;
|
|
||||||
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;
|
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
|
// CLONE ITEMSTACKS/INVENTORY
|
||||||
// -------------------------------------------- //
|
// -------------------------------------------- //
|
||||||
@ -141,18 +167,35 @@ public class InventoryUtil
|
|||||||
return ret;
|
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)
|
public static Inventory cloneInventory(Inventory inventory)
|
||||||
{
|
{
|
||||||
if (inventory == null) return null;
|
if (inventory == null) return null;
|
||||||
|
|
||||||
InventoryHolder holder = inventory.getHolder();
|
Inventory ret = null;
|
||||||
int size = inventory.getSize();
|
|
||||||
String title = inventory.getTitle();
|
|
||||||
ItemStack[] contents = cloneItemStacks(inventory.getContents());
|
|
||||||
|
|
||||||
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);
|
ret.setContents(contents);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
Loading…
Reference in New Issue
Block a user