Skip to content

Instantly share code, notes, and snippets.

@MarinusLeeuwerik
Last active August 29, 2015 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MarinusLeeuwerik/38eea3d361df6e0702dd to your computer and use it in GitHub Desktop.
Save MarinusLeeuwerik/38eea3d361df6e0702dd to your computer and use it in GitHub Desktop.
Base64 inventory serialization with NTB (Named Binary Tag).
package code.MarinusLeeuwerik.utils;
/**
* Copyright (c) 2014 Marinus Leeuwerik
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL MARINUS LEEUWERIK BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import net.minecraft.server.NBTBase;
import net.minecraft.server.NBTTagCompound;
import net.minecraft.server.NBTTagList;
import org.apache.commons.lang.StringUtils;
import org.bukkit.craftbukkit.inventory.CraftInventoryCustom;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import com.google.common.primitives.Bytes;
public class InventorySerialization {
private final static String textBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
private final static char[] alphabetBase64 = textBase64.toCharArray();
private final static String regexBase64 = "[^" + textBase64 + "=]";
public static String toBase64(Inventory inventory) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
DataOutputStream dataOutput = new DataOutputStream(outputStream);
NBTTagList itemList = new NBTTagList();
for (int i = 0; i < inventory.getSize(); i++) {
NBTTagCompound outputObject = new NBTTagCompound();
CraftItemStack craft = getCraftVersion(inventory.getItem(i));
if (craft != null)
craft.getHandle().save(outputObject);
itemList.add(outputObject);
}
NBTBase.a(itemList, dataOutput);
return new BigInteger(1, outputStream.toByteArray()).toString(32);
}
public static Inventory fromBase64(String data) {
ByteArrayInputStream inputStream = new ByteArrayInputStream(new BigInteger(data, 32).toByteArray());
NBTTagList itemList = (NBTTagList) NBTBase.b(new DataInputStream(inputStream));
Inventory inventory = new CraftInventoryCustom(null, itemList.size());
for (int i = 0; i < itemList.size(); i++) {
NBTTagCompound inputObject = (NBTTagCompound) itemList.get(i);
if (!inputObject.d()) {
inventory.setItem(i, new CraftItemStack(net.minecraft.server.ItemStack.a(inputObject)));
}
}
return inventory;
}
private static String encodeBase64(byte[] data) {
StringBuilder result = new StringBuilder();
int padding = data.length % 3;
int readCount = 0;
if (padding != 0) {
padding = 3 - padding;
data = Bytes.concat(data, new byte[padding]);
}
for (int i = 0; i < data.length; i += 3) {
if (i > 0 && readCount % 76 == 0)
result.append("\r\n");
int window = (data[i] << 16) + (data[i + 1] << 8) + (data[i + 2]);
int n1 = (window >> 18) & 0x3F, n2 = (window >> 12) & 0x3F, n3 = (window >> 6) & 0x3F, n4 = window & 0x3F;
result.append(alphabetBase64[n1]);
result.append(alphabetBase64[n2]);
result.append(alphabetBase64[n3]);
result.append(alphabetBase64[n4]);
readCount += 4;
}
return result.substring(0, result.length() - padding) + StringUtils.repeat("=", padding);
}
private static byte[] decodeBase64(String data) {
String subset = data.replaceAll(regexBase64, "");
byte[] result = new byte[3 * subset.length() / 4];
int count = 0;
Map<Character, Integer> lookup = new HashMap<Character, Integer>();
for (int i = 0; i < alphabetBase64.length; i++)
lookup.put(alphabetBase64[i], i);
lookup.put('=', 0);
for (int i = 0; i < subset.length(); i += 4) {
int n = (lookup.get(subset.charAt(i)) << 18) | (lookup.get(subset.charAt(i + 1)) << 12) |
(lookup.get(subset.charAt(i + 2)) << 6) | lookup.get(subset.charAt(i + 3));
result[count] = (byte) ((n >>> 16) & 0xFF);
result[count + 1] = (byte) ((n >>> 8) & 0xFF);
result[count + 2] = (byte) (n & 0xFF);
count += 3;
}
return result;
}
private static CraftItemStack getCraftVersion(ItemStack stack) {
if (stack instanceof CraftItemStack)
return (CraftItemStack) stack;
else if (stack != null)
return new CraftItemStack(stack);
else
return null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment