/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.arkhamnetwork.playersync.utils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.server.v1_7_R4.NBTBase;
import net.minecraft.server.v1_7_R4.NBTNumber;
import net.minecraft.server.v1_7_R4.NBTTagByteArray;
import net.minecraft.server.v1_7_R4.NBTTagCompound;
import net.minecraft.server.v1_7_R4.NBTTagDouble;
import net.minecraft.server.v1_7_R4.NBTTagInt;
import net.minecraft.server.v1_7_R4.NBTTagIntArray;
import net.minecraft.server.v1_7_R4.NBTTagList;
import net.minecraft.server.v1_7_R4.NBTTagLong;
import net.minecraft.server.v1_7_R4.NBTTagString;
import org.apache.commons.lang.SerializationException;
import org.bukkit.Material;
import org.bukkit.craftbukkit.v1_7_R4.inventory.CraftItemStack;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
/**
*
* @author devan_000
*/
public class SerializationUtils {
private static final ItemStack AIR = new ItemStack(36, 0);
public static byte[] serializeItemStacks(ItemStack[] inv) throws SerializationException, IOException {
NBTTagCompound baseTag = new NBTTagCompound();
NBTTagList inventoryTag = new NBTTagList();
int current = 0;
for (ItemStack stack : inv) {
if (stack == null) {
stack = new ItemStack(Material.AIR, 1);
}
if (stack.getType() != Material.AIR) {
NBTTagCompound item = new NBTTagCompound();
item.setByte("InvSize", (byte) inv.length);
item.setByte("CurrentSlot", (byte) current);
inventoryTag.add(CraftItemStack.asNMSCopy(stack).save(item));
}
current++;
}
baseTag.set("Inventory", inventoryTag);
Map<String, Object> map = toMap(baseTag);
return JSONObject.toJSONString(map).getBytes();
}
public static ItemStack[] deserializeItemStacks(byte[] b) throws SerializationException, ParseException, IOException, ClassNotFoundException {
if (b == null) {
return null;
}
NBTTagCompound baseTag = toTag(JSONValue.parseWithException(new String(b)).toString());
NBTTagList inventoryTag = baseTag.getList("Inventory", 10);
Map<Integer, ItemStack> itemMap = new ConcurrentHashMap<>();
final int invSize = inventoryTag.get(0).getByte("InvSize");
for (int i = 0; i < inventoryTag.size(); i++) {
NBTTagCompound item = inventoryTag.get(i);
int slot = item.getByte("CurrentSlot");
if (!itemMap.containsKey(slot)) {
itemMap.put(slot, CraftItemStack.asCraftMirror(net.minecraft.server.v1_7_R4.ItemStack.createStack(item)));
}
}
List<ItemStack> items = new ArrayList<>();
for (int i = 0; i < invSize; i++) {
if (i > invSize) {
break;
}
if (itemMap.get(i) == null) {
items.add(AIR.clone());
continue;
}
items.add(itemMap.get(i));
//IMPORTANT
itemMap.remove(i);
}
return items.toArray(new ItemStack[items.size()]);
}
public static byte[] serializePotionEffects(PotionEffect[] ef) throws SerializationException {
List<Map<String, Object>> effects = new ArrayList<>(ef.length);
for (PotionEffect effect : ef) {
effects.add(effect.serialize());
}
return JSONValue.toJSONString(effects).getBytes();
}
public static PotionEffect[] deserializePotionEffects(byte[] b) throws SerializationException, ParseException {
if (b == null) {
return null;
}
Object o = JSONValue.parseWithException(new String(b));
try {
if (o instanceof List) {
final List<?> data = (List) o;
ArrayList<PotionEffect> items = new ArrayList<>(data.size());
for (Object t : data) {
if (t instanceof Map) {
final Map<?, ?> mdata = (Map) t;
final Map<String, Object> conv = new HashMap<>(mdata.size());
for (Map.Entry<?, ?> e : mdata.entrySet()) {
conv.put(String.valueOf(e.getKey()), convert(e.getValue()));
}
items.add(new PotionEffect(conv));
} else {
throw new IllegalArgumentException("Not a Map");
}
}
return items.toArray(new PotionEffect[items.size()]);
}
throw new IllegalArgumentException("Not a List");
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
}
return null;
}
private static Object convert(Object o) {
if (o instanceof Number) {
Long v = (Long) o;
if (Integer.MAX_VALUE > v) {
return v.intValue();
}
}
return o;
}
private static final NBTTagCompound toTag(String jsonString) throws IOException {
try {
return (NBTTagCompound) javaTypeToNBTTag(new JSONParser().parse(jsonString));
} catch (ParseException e) {
throw new IOException(e);
}
}
private static final NBTBase javaTypeToNBTTag(Object object) throws IOException {
// Handle compounds
if (object instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, ?> map = (Map<String, ?>) object;
NBTTagCompound tag = new NBTTagCompound();
for (Entry<String, ?> entry : map.entrySet()) {
tag.set(entry.getKey(), javaTypeToNBTTag(entry.getValue()));
}
return tag;
}
// Handle numbers
if (object instanceof Number) {
Number number = (Number) object;
if (number.longValue() == number.doubleValue()) {
// Whole number
if (number.intValue() == number.longValue()) {
// Fits in integer
return new NBTTagInt(number.intValue());
}
return new NBTTagLong(number.longValue());
} else {
return new NBTTagDouble(number.doubleValue());
}
}
// Handle strings
if (object instanceof String) {
return new NBTTagString((String) object);
}
// Handle lists
if (object instanceof List) {
List<?> list = (List<?>) object;
NBTTagList listTag = new NBTTagList();
// Handle int arrays
if (list.size() > 0) {
Object firstElement = list.get(0);
if (firstElement instanceof Number) {
@SuppressWarnings("unchecked")
List<Number> intList = (List<Number>) list;
return new NBTTagIntArray(unboxIntegers(intList));
}
}
for (Object entry : list) {
listTag.add(javaTypeToNBTTag(entry));
}
return listTag;
}
throw new IOException("Unknown object: (" + object.getClass() + ") " + object + "");
}
private static final int[] unboxIntegers(List<Number> boxed) {
int[] ints = new int[boxed.size()];
for (int i = 0; i < ints.length; i++) {
ints[i] = boxed.get(i).intValue();
}
return ints;
}
private static final Map<String, Object> toMap(NBTTagCompound tagCompound) throws IOException {
@SuppressWarnings("unchecked")
Collection<String> tagNames = tagCompound.c();
// Add all children
Map<String, Object> jsonObject = new HashMap<>(tagNames.size());
for (String subTagName : tagNames) {
NBTBase subTag = tagCompound.get(subTagName);
jsonObject.put(subTagName, nbtTagToJavaType(subTag));
}
return jsonObject;
}
private static final Object nbtTagToJavaType(NBTBase tag) throws IOException {
if (tag instanceof NBTTagCompound) {
return toMap((NBTTagCompound) tag);
} else if (tag instanceof NBTTagList) {
// Add all children
NBTTagList listTag = (NBTTagList) tag;
List<Object> objects = new ArrayList<>();
for (int i = 0; i < listTag.size(); i++) {
objects.add(tagInNBTListToJavaType(listTag, i));
}
return objects;
} else if (tag instanceof NBTNumber) {
// Check if double or long
NBTNumber nbtNumber = (NBTNumber) tag;
if (nbtNumber.c() == nbtNumber.g()) {
// Long, as double value == long value
return nbtNumber.c();
} else {
// Double
return nbtNumber.g();
}
} else if (tag instanceof NBTTagString) {
String value = ((NBTTagString) tag).a_();
return value;
} else if (tag instanceof NBTTagByteArray) {
return boxBytes(((NBTTagByteArray) tag).c());
} else if (tag instanceof NBTTagIntArray) {
return boxIntegers(((NBTTagIntArray) tag).c());
}
throw new IOException("Unknown tag: " + tag);
}
private static final List<Byte> boxBytes(byte[] byteArray) {
List<Byte> byteList = new ArrayList<>(byteArray.length);
for (byte aByte : byteArray) {
byteList.add(aByte); // Wraps
}
return byteList;
}
private static final List<Integer> boxIntegers(int[] intArray) {
List<Integer> integerList = new ArrayList<>(intArray.length);
for (int anInt : intArray) {
integerList.add(anInt); // Wraps
}
return integerList;
}
private static final Object tagInNBTListToJavaType(NBTTagList tagList, int position) throws IOException {
switch (tagList.d()) {
case 10:
NBTTagCompound compoundValue = tagList.get(position);
return nbtTagToJavaType(compoundValue);
case 11:
return boxIntegers(tagList.c(position));
case 6:
double doubleValue = tagList.d(position);
return doubleValue;
case 5:
float floatValue = tagList.e(position);
return floatValue;
case 8:
String stringValue = tagList.getString(position);
return stringValue;
}
throw new IOException("Unknown list: " + tagList);
}
}