package net.kennux.cubicworld.serialization; import java.nio.ByteBuffer; import java.util.ArrayList; import net.kennux.cubicworld.voxel.VoxelData; import com.badlogic.gdx.math.Vector3; /** * Gets used to build datapackets. * * @author KennuX * */ public class BitWriter { /** * This array list holds all bytes already added to the packet. */ private ArrayList<Byte> bytes; public BitWriter() { this.bytes = new ArrayList<Byte>(); } public int getLength() { return this.bytes.size(); } /** * Builds the packet and returns a byte array ready 2 send. * * @return */ public byte[] getPacket() { byte[] data = new byte[this.bytes.size()]; int counter = 0; for (Byte b : this.bytes) { data[counter] = b.byteValue(); counter++; } return data; } /** * Writes a boolean (1 byte) to the packet. * * @param b */ public void writeBool(boolean b) { if (b) this.bytes.add((byte) 1); else this.bytes.add((byte) 0); } /** * Writes 1 byte to the packet. * * @param b */ public void writeByte(byte b) { this.bytes.add(b); } /** * Writes a 4 byte integer (data length) and then all bytes. * * @param bytes */ public void writeBytes(byte[] bytes) { // Write len this.writeInt(bytes.length); // Write data for (int i = 0; i < bytes.length; i++) { this.writeByte(bytes[i]); } } /** * Writes the given character to the packet data. * * @param c */ public void writeChar(char c) { this.bytes.add((byte) c); } /** * Writes a float (4 bytes) to the stream. * * @param i */ public void writeFloat(float f) { // int intBits = Float.floatToRawIntBits(f); byte[] b = ByteBuffer.allocate(4).putFloat(f).array(); this.bytes.add(b[0]); this.bytes.add(b[1]); this.bytes.add(b[2]); this.bytes.add(b[3]); } /** * Writes a integer (4 bytes) to the stream. * * @param i */ public void writeInt(int i) { byte[] b = ByteBuffer.allocate(4).putInt(i).array(); this.bytes.add(b[0]); this.bytes.add(b[1]); this.bytes.add(b[2]); this.bytes.add(b[3]); } /** * Writes a long (8 bytes) to the stream. * * @param i */ public void writeLong(long i) { byte[] b = ByteBuffer.allocate(8).putLong(i).array(); this.bytes.add(b[0]); this.bytes.add(b[1]); this.bytes.add(b[2]); this.bytes.add(b[3]); this.bytes.add(b[4]); this.bytes.add(b[5]); this.bytes.add(b[6]); this.bytes.add(b[7]); } /** * Writes a short (2 bytes) to the stream. * * @param s */ public void writeShort(short s) { byte[] b = ByteBuffer.allocate(2).putShort(s).array(); this.bytes.add(b[0]); this.bytes.add(b[1]); } /** * Writes the given string s to the packet data. * Writing a string will first write an integer with the string's length * followed by the characters. * * @param s * @param len */ public void writeString(String s) { this.writeInt(s.length()); char[] characters = s.toCharArray(); // Write characters for (int i = 0; i < characters.length; i++) { this.writeChar(characters[i]); } } /** * Writes a vector3 (12 bytes) to the stream. * * @param i */ public void writeVector3(Vector3 v) { this.writeFloat(v.x); this.writeFloat(v.y); this.writeFloat(v.z); } /** * Writes a voxel data object to the packet. * * @param v */ public void writeVoxelData(VoxelData v) { VoxelData.serialize(v, this); } /** * Reads an object with given type serializationtypes. * This method uses a switch-case to forward the calls to readInt(), readShort(), etc. * Returns null in case of an error. * @param type * @return */ public void writeField(SerializationTypes type, Object value) { switch (type) { case BYTE: this.writeByte((byte) value); return; case SHORT: this.writeShort((short) value); return; case CHAR: this.writeChar((char) value); return; case BYTEARRAY: this.writeBytes((byte[]) value); return; case FLOAT: this.writeFloat((float) value); return; case INTEGER: this.writeInt((int) value); return; case LONG: this.writeLong((long) value); return; case STRING: this.writeString((String) value); return; case VECTOR3: this.writeVector3((Vector3) value); return; case VOXELDATA: this.writeVoxelData((VoxelData) value); return; case BOOLEAN: this.writeBool((boolean) value); return; } } }