package micdoodle8.mods.galacticraft.core.network;
import com.google.common.math.DoubleMath;
import io.netty.buffer.ByteBuf;
import micdoodle8.mods.galacticraft.api.vector.BlockVec3;
import micdoodle8.mods.galacticraft.api.vector.Vector3;
import micdoodle8.mods.galacticraft.core.energy.tile.EnergyStorage;
import micdoodle8.mods.galacticraft.core.tile.FluidTankGC;
import micdoodle8.mods.galacticraft.core.util.GCLog;
import micdoodle8.mods.galacticraft.core.wrappers.FlagData;
import micdoodle8.mods.galacticraft.core.wrappers.Footprint;
import net.minecraft.entity.Entity;
import net.minecraft.item.EnumDyeColor;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.world.World;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidTank;
import net.minecraftforge.fml.common.network.ByteBufUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.UUID;
public class NetworkUtil
{
public static void encodeData(ByteBuf buffer, Collection<Object> sendData) throws IOException
{
for (Object dataValue : sendData)
{
if (dataValue instanceof Integer)
{
buffer.writeInt((Integer) dataValue);
}
else if (dataValue instanceof Float)
{
buffer.writeFloat((Float) dataValue);
}
else if (dataValue instanceof Double)
{
buffer.writeDouble((Double) dataValue);
}
else if (dataValue instanceof Byte)
{
buffer.writeByte((Byte) dataValue);
}
else if (dataValue instanceof Boolean)
{
buffer.writeBoolean((Boolean) dataValue);
}
else if (dataValue instanceof String)
{
ByteBufUtils.writeUTF8String(buffer, (String) dataValue);
}
else if (dataValue instanceof Short)
{
buffer.writeShort((Short) dataValue);
}
else if (dataValue instanceof Long)
{
buffer.writeLong((Long) dataValue);
}
else if (dataValue instanceof EnergyStorage)
{
EnergyStorage storage = (EnergyStorage) dataValue;
buffer.writeFloat(storage.getCapacityGC());
buffer.writeFloat(storage.getMaxReceive());
buffer.writeFloat(storage.getMaxExtract());
buffer.writeFloat(storage.getEnergyStoredGC());
}
else if (dataValue instanceof NBTTagCompound)
{
NetworkUtil.writeNBTTagCompound((NBTTagCompound) dataValue, buffer);
}
else if (dataValue instanceof FluidTankGC)
{
FluidTankGC tankGC = (FluidTankGC) dataValue;
BlockPos pos = tankGC.getTilePosition();
buffer.writeInt(pos.getX());
buffer.writeInt(pos.getY());
buffer.writeInt(pos.getZ());
NetworkUtil.writeFluidTank((FluidTank) dataValue, buffer);
}
else if (dataValue instanceof FluidTank)
{
NetworkUtil.writeFluidTank((FluidTank) dataValue, buffer);
}
else if (dataValue instanceof Entity)
{
buffer.writeInt(((Entity) dataValue).getEntityId());
}
else if (dataValue instanceof Vector3)
{
buffer.writeDouble(((Vector3) dataValue).x);
buffer.writeDouble(((Vector3) dataValue).y);
buffer.writeDouble(((Vector3) dataValue).z);
}
else if (dataValue instanceof BlockVec3)
{
buffer.writeInt(((BlockVec3) dataValue).x);
buffer.writeInt(((BlockVec3) dataValue).y);
buffer.writeInt(((BlockVec3) dataValue).z);
}
else if (dataValue instanceof byte[])
{
buffer.writeInt(((byte[]) dataValue).length);
for (int i = 0; i < ((byte[]) dataValue).length; i++)
{
buffer.writeByte(((byte[]) dataValue)[i]);
}
}
else if (dataValue instanceof UUID)
{
buffer.writeLong(((UUID) dataValue).getLeastSignificantBits());
buffer.writeLong(((UUID) dataValue).getMostSignificantBits());
}
else if (dataValue instanceof Collection)
{
NetworkUtil.encodeData(buffer, (Collection<Object>) dataValue);
}
else if (dataValue instanceof FlagData)
{
buffer.writeInt(((FlagData) dataValue).getWidth());
buffer.writeInt(((FlagData) dataValue).getHeight());
for (int i = 0; i < ((FlagData) dataValue).getWidth(); i++)
{
for (int j = 0; j < ((FlagData) dataValue).getHeight(); j++)
{
Vector3 vec = ((FlagData) dataValue).getColorAt(i, j);
buffer.writeByte((byte) (vec.x * 256 - 128));
buffer.writeByte((byte) (vec.y * 256 - 128));
buffer.writeByte((byte) (vec.z * 256 - 128));
}
}
}
else if (dataValue instanceof Integer[])
{
Integer[] array = (Integer[]) dataValue;
buffer.writeInt(array.length);
for (int i = 0; i < array.length; i++)
{
buffer.writeInt(array[i]);
}
}
else if (dataValue instanceof String[])
{
String[] array = (String[]) dataValue;
buffer.writeInt(array.length);
for (int i = 0; i < array.length; i++)
{
ByteBufUtils.writeUTF8String(buffer, array[i]);
}
}
else if (dataValue instanceof Footprint[])
{
Footprint[] array = (Footprint[]) dataValue;
buffer.writeInt(array.length);
for (int i = 0; i < array.length; i++)
{
buffer.writeInt(array[i].dimension);
buffer.writeFloat((float) array[i].position.x);
buffer.writeFloat((float) array[i].position.y + 1);
buffer.writeFloat((float) array[i].position.z);
buffer.writeFloat(array[i].rotation);
buffer.writeShort(array[i].age);
ByteBufUtils.writeUTF8String(buffer, array[i].owner);
}
}
else if (dataValue instanceof EnumFacing)
{
buffer.writeInt(((EnumFacing) dataValue).getIndex());
}
else if (dataValue instanceof BlockPos)
{
BlockPos pos = (BlockPos) dataValue;
buffer.writeInt(pos.getX());
buffer.writeInt(pos.getY());
buffer.writeInt(pos.getZ());
}
else if (dataValue instanceof EnumDyeColor)
{
buffer.writeInt(((EnumDyeColor) dataValue).getDyeDamage());
}
else
{
if (dataValue == null)
{
GCLog.severe("Cannot construct PacketSimple with null data, this is a bug.");
}
GCLog.info("Could not find data type to encode!: " + dataValue);
}
}
}
public static ArrayList<Object> decodeData(Class<?>[] types, ByteBuf buffer)
{
ArrayList<Object> objList = new ArrayList<Object>();
for (Class clazz : types)
{
if (clazz.equals(Integer.class))
{
objList.add(buffer.readInt());
}
else if (clazz.equals(Float.class))
{
objList.add(buffer.readFloat());
}
else if (clazz.equals(Double.class))
{
objList.add(buffer.readDouble());
}
else if (clazz.equals(Byte.class))
{
objList.add(buffer.readByte());
}
else if (clazz.equals(Boolean.class))
{
objList.add(buffer.readBoolean());
}
else if (clazz.equals(String.class))
{
objList.add(ByteBufUtils.readUTF8String(buffer));
}
else if (clazz.equals(Short.class))
{
objList.add(buffer.readShort());
}
else if (clazz.equals(Long.class))
{
objList.add(buffer.readLong());
}
else if (clazz.equals(byte[].class))
{
byte[] bytes = new byte[buffer.readInt()];
for (int i = 0; i < bytes.length; i++)
{
bytes[i] = buffer.readByte();
}
objList.add(bytes);
}
else if (clazz.equals(EnergyStorage.class))
{
EnergyStorage storage = new EnergyStorage(buffer.readFloat(), buffer.readFloat(), buffer.readFloat());
storage.setEnergyStored(buffer.readFloat());
objList.add(storage);
}
else if (clazz.equals(NBTTagCompound.class))
{
try
{
objList.add(NetworkUtil.readNBTTagCompound(buffer));
}
catch (IOException e)
{
e.printStackTrace();
}
}
else if (clazz.equals(BlockVec3.class))
{
objList.add(new BlockVec3(buffer.readInt(), buffer.readInt(), buffer.readInt()));
}
else if (clazz.equals(UUID.class))
{
objList.add(new UUID(buffer.readLong(), buffer.readLong()));
}
else if (clazz.equals(Vector3.class))
{
objList.add(new Vector3(buffer.readDouble(), buffer.readDouble(), buffer.readDouble()));
}
else if (clazz.equals(FlagData.class))
{
int width = buffer.readInt();
int height = buffer.readInt();
FlagData flagData = new FlagData(width, height);
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
flagData.setColorAt(i, j, new Vector3(buffer.readByte() + 128, buffer.readByte() + 128, buffer.readByte() + 128));
}
}
objList.add(flagData);
}
else if (clazz.equals(Integer[].class))
{
int size = buffer.readInt();
for (int i = 0; i < size; i++)
{
objList.add(buffer.readInt());
}
}
else if (clazz.equals(String[].class))
{
int size = buffer.readInt();
for (int i = 0; i < size; i++)
{
objList.add(ByteBufUtils.readUTF8String(buffer));
}
}
else if (clazz.equals(Footprint[].class))
{
int size = buffer.readInt();
for (int i = 0; i < size; i++)
{
objList.add(new Footprint(buffer.readInt(), new Vector3(buffer.readFloat(), buffer.readFloat(), buffer.readFloat()), buffer.readFloat(), buffer.readShort(), ByteBufUtils.readUTF8String(buffer)));
}
}
else if (clazz.equals(EnumFacing.class))
{
objList.add(EnumFacing.getFront(buffer.readInt()));
}
else if (clazz.equals(BlockPos.class))
{
objList.add(new BlockPos(buffer.readInt(), buffer.readInt(), buffer.readInt()));
}
else if (clazz.equals(EnumDyeColor.class))
{
objList.add(EnumDyeColor.byDyeDamage(buffer.readInt()));
}
}
return objList;
}
public static Object getFieldValueFromStream(Field field, ByteBuf buffer, World world) throws IOException
{
Class<?> dataValue = field.getType();
if (dataValue.equals(int.class))
{
return buffer.readInt();
}
else if (dataValue.equals(float.class))
{
return buffer.readFloat();
}
else if (dataValue.equals(double.class))
{
return buffer.readDouble();
}
else if (dataValue.equals(byte.class))
{
return buffer.readByte();
}
else if (dataValue.equals(boolean.class))
{
return buffer.readBoolean();
}
else if (dataValue.equals(String.class))
{
return ByteBufUtils.readUTF8String(buffer);
}
else if (dataValue.equals(short.class))
{
return buffer.readShort();
}
else if (dataValue.equals(Long.class))
{
return buffer.readLong();
}
else if (dataValue.equals(NBTTagCompound.class))
{
return NetworkUtil.readNBTTagCompound(buffer);
}
else if (dataValue.equals(FluidTankGC.class))
{
return NetworkUtil.readFluidTankGC(buffer, world);
}
else if (dataValue.equals(FluidTank.class))
{
return NetworkUtil.readFluidTank(buffer);
}
else if (dataValue.equals(Vector3.class))
{
return new Vector3(buffer.readDouble(), buffer.readDouble(), buffer.readDouble());
}
else if (dataValue.equals(BlockVec3.class))
{
return new BlockVec3(buffer.readInt(), buffer.readInt(), buffer.readInt());
}
else if (dataValue.equals(UUID.class))
{
return new UUID(buffer.readLong(), buffer.readLong());
}
else if (dataValue.equals(byte[].class))
{
byte[] bytes = new byte[buffer.readInt()];
for (int i = 0; i < bytes.length; i++)
{
bytes[i] = buffer.readByte();
}
return bytes;
}
else if (dataValue.equals(EnergyStorage.class))
{
float capacity = buffer.readFloat();
float maxReceive = buffer.readFloat();
float maxExtract = buffer.readFloat();
EnergyStorage storage = new EnergyStorage(capacity, maxReceive, maxExtract);
storage.setEnergyStored(buffer.readFloat());
return storage;
}
else if (dataValue.equals(EnumFacing.class))
{
return EnumFacing.getFront(buffer.readInt());
}
else if (dataValue.equals(BlockPos.class))
{
return new BlockPos(buffer.readInt(), buffer.readInt(), buffer.readInt());
}
else if (dataValue.equals(EnumDyeColor.class))
{
return EnumDyeColor.byDyeDamage(buffer.readInt());
}
else
{
Class<?> c = dataValue;
while (c != null)
{
if (c.equals(Entity.class))
{
return world.getEntityByID(buffer.readInt());
}
c = c.getSuperclass();
}
}
throw new NullPointerException("Field type not found: " + field.getType().getSimpleName());
}
public static ItemStack readItemStack(ByteBuf buffer) throws IOException
{
ItemStack itemstack = null;
short itemID = buffer.readShort();
if (itemID >= 0)
{
byte stackSize = buffer.readByte();
short meta = buffer.readShort();
itemstack = new ItemStack(Item.getItemById(itemID), stackSize, meta);
if (buffer.readBoolean())
{
itemstack.setTagCompound(readNBTTagCompound(buffer));
}
}
return itemstack;
}
public static void writeItemStack(ItemStack itemStack, ByteBuf buffer) throws IOException
{
if (itemStack == null)
{
buffer.writeShort(-1);
}
else
{
buffer.writeShort(Item.getIdFromItem(itemStack.getItem()));
buffer.writeByte(itemStack.stackSize);
buffer.writeShort(itemStack.getItemDamage());
NBTTagCompound nbttagcompound = null;
if (itemStack.getItem().isDamageable() || itemStack.getItem().getShareTag())
{
nbttagcompound = itemStack.getTagCompound();
}
buffer.writeBoolean(nbttagcompound != null);
if (nbttagcompound != null)
{
NetworkUtil.writeNBTTagCompound(nbttagcompound, buffer);
}
}
}
public static NBTTagCompound readNBTTagCompound(ByteBuf buffer) throws IOException
{
try
{
int length = buffer.readInt();
byte[] compressed = new byte[length];
buffer.readBytes(compressed);
ByteArrayInputStream bais = new ByteArrayInputStream(compressed);
return CompressedStreamTools.readCompressed(bais);
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
public static void writeNBTTagCompound(NBTTagCompound nbt, ByteBuf buffer) throws IOException
{
try
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
CompressedStreamTools.writeCompressed(nbt, baos);
byte[] compressed = baos.toByteArray();
buffer.writeInt(compressed.length);
buffer.writeBytes(compressed);
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void writeFluidTank(FluidTank fluidTank, ByteBuf buffer) throws IOException
{
if (fluidTank == null)
{
buffer.writeInt(0);
ByteBufUtils.writeUTF8String(buffer, "");
buffer.writeInt(0);
}
else
{
buffer.writeInt(fluidTank.getCapacity());
ByteBufUtils.writeUTF8String(buffer, fluidTank.getFluid() == null ? "" : fluidTank.getFluid().getFluid().getName());
buffer.writeInt(fluidTank.getFluidAmount());
}
}
public static FluidTankGC readFluidTankGC(ByteBuf buffer, World world) throws IOException
{
BlockPos pos = new BlockPos(buffer.readInt(), buffer.readInt(), buffer.readInt());
TileEntity tile = world.getTileEntity(pos);
int capacity = buffer.readInt();
String fluidName = ByteBufUtils.readUTF8String(buffer);
FluidTankGC fluidTank = new FluidTankGC(capacity, tile);
int amount = buffer.readInt();
if (fluidName.equals(""))
{
fluidTank.setFluid(null);
}
else
{
Fluid fluid = FluidRegistry.getFluid(fluidName);
fluidTank.setFluid(new FluidStack(fluid, amount));
}
return fluidTank;
}
public static FluidTank readFluidTank(ByteBuf buffer) throws IOException
{
int capacity = buffer.readInt();
String fluidName = ByteBufUtils.readUTF8String(buffer);
FluidTank fluidTank = new FluidTank(capacity);
int amount = buffer.readInt();
if (fluidName.equals(""))
{
fluidTank.setFluid(null);
}
else
{
Fluid fluid = FluidRegistry.getFluid(fluidName);
fluidTank.setFluid(new FluidStack(fluid, amount));
}
return fluidTank;
}
public static boolean fuzzyEquals(Object a, Object b)
{
if ((a == null) != (b == null))
{
return false;
}
else if (a == null)
{
return true;
}
else if (a instanceof Float && b instanceof Float)
{
float af = (Float) a;
float bf = (Float) b;
return af == bf || Math.abs(af - bf) < 0.01F;
}
else if (a instanceof Double && b instanceof Double)
{
return DoubleMath.fuzzyEquals((Double) a, (Double) b, 0.01);
}
else if (a instanceof Entity && b instanceof Entity)
{
Entity a2 = (Entity) a;
Entity b2 = (Entity) b;
return fuzzyEquals(a2.getEntityId(), b2.getEntityId());
}
else if (a instanceof Vector3 && b instanceof Vector3)
{
Vector3 a2 = (Vector3) a;
Vector3 b2 = (Vector3) b;
return fuzzyEquals(a2.x, b2.x) &&
fuzzyEquals(a2.y, b2.y) &&
fuzzyEquals(a2.z, b2.z);
}
else if (a instanceof EnergyStorage && b instanceof EnergyStorage)
{
EnergyStorage a2 = (EnergyStorage) a;
EnergyStorage b2 = (EnergyStorage) b;
return fuzzyEquals(a2.getEnergyStoredGC(), b2.getEnergyStoredGC()) &&
fuzzyEquals(a2.getCapacityGC(), b2.getCapacityGC()) &&
fuzzyEquals(a2.getMaxReceive(), b2.getMaxReceive()) &&
fuzzyEquals(a2.getMaxExtract(), b2.getMaxExtract());
}
else if (a instanceof FluidTank && b instanceof FluidTank)
{
FluidTank a2 = (FluidTank) a;
FluidTank b2 = (FluidTank) b;
FluidStack fluidA = a2.getFluid();
FluidStack fluidB = b2.getFluid();
return fuzzyEquals(a2.getCapacity(), b2.getCapacity()) &&
fuzzyEquals(fluidA != null ? fluidA.getFluid().getName() : "", fluidB != null ? fluidB.getFluid().getName() : "") &&
fuzzyEquals(a2.getFluidAmount(), b2.getFluidAmount());
}
else
{
return a.equals(b);
}
}
public static Object cloneNetworkedObject(Object a)
{
// We only need to clone mutable objects
if (a instanceof EnergyStorage)
{
EnergyStorage prevStorage = (EnergyStorage) a;
EnergyStorage storage = new EnergyStorage(prevStorage.getCapacityGC(), prevStorage.getMaxReceive(), prevStorage.getMaxExtract());
storage.setEnergyStored(prevStorage.getEnergyStoredGC());
return storage;
}
else if (a instanceof FluidTankGC)
{
FluidTankGC prevTank = (FluidTankGC) a;
FluidTankGC tank = new FluidTankGC(prevTank.getFluid(), prevTank.getCapacity(), prevTank.getTile());
return tank;
}
else if (a instanceof FluidTank)
{
FluidTank prevTank = (FluidTank)a;
FluidStack prevFluid = prevTank.getFluid();
prevFluid = prevFluid == null ? null : prevFluid.copy();
FluidTank tank = new FluidTank(prevFluid, prevTank.getCapacity());
return tank;
}
else
{
return a;
}
}
}