package electrosphere.net.parser.net.message;

import io.github.studiorailgun.CircularByteBuffer;
import electrosphere.net.parser.util.ByteStreamUtils;
import java.util.LinkedList;
import java.util.List;

public class TerrainMessage extends NetworkMessage {

    /**
     * The types of messages available in this category.
     */
    public enum TerrainMessageType {
        REQUESTMETADATA,
        RESPONSEMETADATA,
        REQUESTEDITVOXEL,
        UPDATEVOXEL,
        REQUESTUSETERRAINPALETTE,
        SPAWNPOSITION,
        REQUESTCHUNKDATA,
        SENDCHUNKDATA,
        REQUESTREDUCEDCHUNKDATA,
        SENDREDUCEDCHUNKDATA,
        REQUESTREDUCEDBLOCKDATA,
        SENDREDUCEDBLOCKDATA,
        UPDATEBLOCK,
        REQUESTFLUIDDATA,
        SENDFLUIDDATA,
        UPDATEFLUIDDATA,
    }

    /**
     * The type of this message in particular.
     */
    TerrainMessageType messageType;
    int worldSizeDiscrete;
    int dynamicInterpolationRatio;
    float randomDampener;
    int worldMinX;
    int worldMinY;
    int worldMinZ;
    int worldMaxX;
    int worldMaxY;
    int worldMaxZ;
    float value;
    int worldX;
    int worldY;
    int worldZ;
    int voxelX;
    int voxelY;
    int voxelZ;
    double realLocationX;
    double realLocationY;
    double realLocationZ;
    byte[] chunkData;
    int homogenousValue;
    int chunkResolution;
    float terrainWeight;
    int terrainValue;
    int blockType;
    int blockMetadata;

    /**
     * Constructor
     * @param messageType The type of this message
     */
    private TerrainMessage(TerrainMessageType messageType){
        this.type = MessageType.TERRAIN_MESSAGE;
        this.messageType = messageType;
    }

    /**
     * Constructor
     */
    protected TerrainMessage(){
        this.type = MessageType.TERRAIN_MESSAGE;
    }

    public TerrainMessageType getMessageSubtype(){
        return this.messageType;
    }

    /**
     * Gets worldSizeDiscrete
     */
    public int getworldSizeDiscrete() {
        return worldSizeDiscrete;
    }

    /**
     * Sets worldSizeDiscrete
     */
    public void setworldSizeDiscrete(int worldSizeDiscrete) {
        this.worldSizeDiscrete = worldSizeDiscrete;
    }

    /**
     * Gets dynamicInterpolationRatio
     */
    public int getdynamicInterpolationRatio() {
        return dynamicInterpolationRatio;
    }

    /**
     * Sets dynamicInterpolationRatio
     */
    public void setdynamicInterpolationRatio(int dynamicInterpolationRatio) {
        this.dynamicInterpolationRatio = dynamicInterpolationRatio;
    }

    /**
     * Gets randomDampener
     */
    public float getrandomDampener() {
        return randomDampener;
    }

    /**
     * Sets randomDampener
     */
    public void setrandomDampener(float randomDampener) {
        this.randomDampener = randomDampener;
    }

    /**
     * Gets worldMinX
     */
    public int getworldMinX() {
        return worldMinX;
    }

    /**
     * Sets worldMinX
     */
    public void setworldMinX(int worldMinX) {
        this.worldMinX = worldMinX;
    }

    /**
     * Gets worldMinY
     */
    public int getworldMinY() {
        return worldMinY;
    }

    /**
     * Sets worldMinY
     */
    public void setworldMinY(int worldMinY) {
        this.worldMinY = worldMinY;
    }

    /**
     * Gets worldMinZ
     */
    public int getworldMinZ() {
        return worldMinZ;
    }

    /**
     * Sets worldMinZ
     */
    public void setworldMinZ(int worldMinZ) {
        this.worldMinZ = worldMinZ;
    }

    /**
     * Gets worldMaxX
     */
    public int getworldMaxX() {
        return worldMaxX;
    }

    /**
     * Sets worldMaxX
     */
    public void setworldMaxX(int worldMaxX) {
        this.worldMaxX = worldMaxX;
    }

    /**
     * Gets worldMaxY
     */
    public int getworldMaxY() {
        return worldMaxY;
    }

    /**
     * Sets worldMaxY
     */
    public void setworldMaxY(int worldMaxY) {
        this.worldMaxY = worldMaxY;
    }

    /**
     * Gets worldMaxZ
     */
    public int getworldMaxZ() {
        return worldMaxZ;
    }

    /**
     * Sets worldMaxZ
     */
    public void setworldMaxZ(int worldMaxZ) {
        this.worldMaxZ = worldMaxZ;
    }

    /**
     * Gets value
     */
    public float getvalue() {
        return value;
    }

    /**
     * Sets value
     */
    public void setvalue(float value) {
        this.value = value;
    }

    /**
     * Gets worldX
     */
    public int getworldX() {
        return worldX;
    }

    /**
     * Sets worldX
     */
    public void setworldX(int worldX) {
        this.worldX = worldX;
    }

    /**
     * Gets worldY
     */
    public int getworldY() {
        return worldY;
    }

    /**
     * Sets worldY
     */
    public void setworldY(int worldY) {
        this.worldY = worldY;
    }

    /**
     * Gets worldZ
     */
    public int getworldZ() {
        return worldZ;
    }

    /**
     * Sets worldZ
     */
    public void setworldZ(int worldZ) {
        this.worldZ = worldZ;
    }

    /**
     * Gets voxelX
     */
    public int getvoxelX() {
        return voxelX;
    }

    /**
     * Sets voxelX
     */
    public void setvoxelX(int voxelX) {
        this.voxelX = voxelX;
    }

    /**
     * Gets voxelY
     */
    public int getvoxelY() {
        return voxelY;
    }

    /**
     * Sets voxelY
     */
    public void setvoxelY(int voxelY) {
        this.voxelY = voxelY;
    }

    /**
     * Gets voxelZ
     */
    public int getvoxelZ() {
        return voxelZ;
    }

    /**
     * Sets voxelZ
     */
    public void setvoxelZ(int voxelZ) {
        this.voxelZ = voxelZ;
    }

    /**
     * Gets realLocationX
     */
    public double getrealLocationX() {
        return realLocationX;
    }

    /**
     * Sets realLocationX
     */
    public void setrealLocationX(double realLocationX) {
        this.realLocationX = realLocationX;
    }

    /**
     * Gets realLocationY
     */
    public double getrealLocationY() {
        return realLocationY;
    }

    /**
     * Sets realLocationY
     */
    public void setrealLocationY(double realLocationY) {
        this.realLocationY = realLocationY;
    }

    /**
     * Gets realLocationZ
     */
    public double getrealLocationZ() {
        return realLocationZ;
    }

    /**
     * Sets realLocationZ
     */
    public void setrealLocationZ(double realLocationZ) {
        this.realLocationZ = realLocationZ;
    }

    /**
     * Gets chunkData
     */
    public byte[] getchunkData() {
        return chunkData;
    }

    /**
     * Sets chunkData
     */
    public void setchunkData(byte[] chunkData) {
        this.chunkData = chunkData;
    }

    /**
     * Gets homogenousValue
     */
    public int gethomogenousValue() {
        return homogenousValue;
    }

    /**
     * Sets homogenousValue
     */
    public void sethomogenousValue(int homogenousValue) {
        this.homogenousValue = homogenousValue;
    }

    /**
     * Gets chunkResolution
     */
    public int getchunkResolution() {
        return chunkResolution;
    }

    /**
     * Sets chunkResolution
     */
    public void setchunkResolution(int chunkResolution) {
        this.chunkResolution = chunkResolution;
    }

    /**
     * Gets terrainWeight
     */
    public float getterrainWeight() {
        return terrainWeight;
    }

    /**
     * Sets terrainWeight
     */
    public void setterrainWeight(float terrainWeight) {
        this.terrainWeight = terrainWeight;
    }

    /**
     * Gets terrainValue
     */
    public int getterrainValue() {
        return terrainValue;
    }

    /**
     * Sets terrainValue
     */
    public void setterrainValue(int terrainValue) {
        this.terrainValue = terrainValue;
    }

    /**
     * Gets blockType
     */
    public int getblockType() {
        return blockType;
    }

    /**
     * Sets blockType
     */
    public void setblockType(int blockType) {
        this.blockType = blockType;
    }

    /**
     * Gets blockMetadata
     */
    public int getblockMetadata() {
        return blockMetadata;
    }

    /**
     * Sets blockMetadata
     */
    public void setblockMetadata(int blockMetadata) {
        this.blockMetadata = blockMetadata;
    }

    /**
     * Removes the packet header from the buffer
     * @param byteBuffer The buffer
     */
    static void stripPacketHeader(CircularByteBuffer byteBuffer){
        byteBuffer.read(2);
    }

    /**
     * Checks if this message can be parsed (ie are all bytes present)
     * @param byteBuffer The buffer
     * @param secondByte The second byte, signifying the subtype of the message
     * @return true if the message can be parsed, false otherwise
     */
    public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){
        switch(secondByte){
            case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTMETADATA:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTMETADATA_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_RESPONSEMETADATA:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_RESPONSEMETADATA_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTEDITVOXEL:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTEDITVOXEL_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEVOXEL:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEVOXEL_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTUSETERRAINPALETTE:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTUSETERRAINPALETTE_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_SPAWNPOSITION:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_SPAWNPOSITION_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTCHUNKDATA:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTCHUNKDATA_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDCHUNKDATA:
                return TerrainMessage.canParsesendChunkDataMessage(byteBuffer);
            case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDCHUNKDATA:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDCHUNKDATA_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDREDUCEDCHUNKDATA:
                return TerrainMessage.canParseSendReducedChunkDataMessage(byteBuffer);
            case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDBLOCKDATA:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDBLOCKDATA_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDREDUCEDBLOCKDATA:
                return TerrainMessage.canParseSendReducedBlockDataMessage(byteBuffer);
            case TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEBLOCK:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEBLOCK_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA:
                return TerrainMessage.canParsesendFluidDataMessage(byteBuffer);
            case TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA:
                return TerrainMessage.canParseupdateFluidDataMessage(byteBuffer);
        }
        return false;
    }

    /**
     * Parses a message of type RequestMetadata
     */
    public static TerrainMessage parseRequestMetadataMessage(CircularByteBuffer byteBuffer, MessagePool pool){
        TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE);
        rVal.messageType = TerrainMessageType.REQUESTMETADATA;
        TerrainMessage.stripPacketHeader(byteBuffer);
        return rVal;
    }

    /**
     * Constructs a message of type RequestMetadata
     */
    public static TerrainMessage constructRequestMetadataMessage(){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTMETADATA);
        rVal.serialize();
        return rVal;
    }

    /**
     * Parses a message of type ResponseMetadata
     */
    public static TerrainMessage parseResponseMetadataMessage(CircularByteBuffer byteBuffer, MessagePool pool){
        TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE);
        rVal.messageType = TerrainMessageType.RESPONSEMETADATA;
        TerrainMessage.stripPacketHeader(byteBuffer);
        rVal.setworldSizeDiscrete(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldMinX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldMinY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldMinZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldMaxX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldMaxY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldMaxZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        return rVal;
    }

    /**
     * Constructs a message of type ResponseMetadata
     */
    public static TerrainMessage constructResponseMetadataMessage(int worldSizeDiscrete,int worldMinX,int worldMinY,int worldMinZ,int worldMaxX,int worldMaxY,int worldMaxZ){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.RESPONSEMETADATA);
        rVal.setworldSizeDiscrete(worldSizeDiscrete);
        rVal.setworldMinX(worldMinX);
        rVal.setworldMinY(worldMinY);
        rVal.setworldMinZ(worldMinZ);
        rVal.setworldMaxX(worldMaxX);
        rVal.setworldMaxY(worldMaxY);
        rVal.setworldMaxZ(worldMaxZ);
        rVal.serialize();
        return rVal;
    }

    /**
     * Parses a message of type RequestEditVoxel
     */
    public static TerrainMessage parseRequestEditVoxelMessage(CircularByteBuffer byteBuffer, MessagePool pool){
        TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE);
        rVal.messageType = TerrainMessageType.REQUESTEDITVOXEL;
        TerrainMessage.stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setvoxelX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setvoxelY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setvoxelZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setterrainWeight(ByteStreamUtils.popFloatFromByteQueue(byteBuffer));
        rVal.setterrainValue(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        return rVal;
    }

    /**
     * Constructs a message of type RequestEditVoxel
     */
    public static TerrainMessage constructRequestEditVoxelMessage(int worldX,int worldY,int worldZ,int voxelX,int voxelY,int voxelZ,float terrainWeight,int terrainValue){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTEDITVOXEL);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.setvoxelX(voxelX);
        rVal.setvoxelY(voxelY);
        rVal.setvoxelZ(voxelZ);
        rVal.setterrainWeight(terrainWeight);
        rVal.setterrainValue(terrainValue);
        rVal.serialize();
        return rVal;
    }

    /**
     * Parses a message of type UpdateVoxel
     */
    public static TerrainMessage parseUpdateVoxelMessage(CircularByteBuffer byteBuffer, MessagePool pool){
        TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE);
        rVal.messageType = TerrainMessageType.UPDATEVOXEL;
        TerrainMessage.stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setvoxelX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setvoxelY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setvoxelZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setterrainWeight(ByteStreamUtils.popFloatFromByteQueue(byteBuffer));
        rVal.setterrainValue(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        return rVal;
    }

    /**
     * Constructs a message of type UpdateVoxel
     */
    public static TerrainMessage constructUpdateVoxelMessage(int worldX,int worldY,int worldZ,int voxelX,int voxelY,int voxelZ,float terrainWeight,int terrainValue){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEVOXEL);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.setvoxelX(voxelX);
        rVal.setvoxelY(voxelY);
        rVal.setvoxelZ(voxelZ);
        rVal.setterrainWeight(terrainWeight);
        rVal.setterrainValue(terrainValue);
        rVal.serialize();
        return rVal;
    }

    /**
     * Parses a message of type RequestUseTerrainPalette
     */
    public static TerrainMessage parseRequestUseTerrainPaletteMessage(CircularByteBuffer byteBuffer, MessagePool pool){
        TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE);
        rVal.messageType = TerrainMessageType.REQUESTUSETERRAINPALETTE;
        TerrainMessage.stripPacketHeader(byteBuffer);
        rVal.setrealLocationX(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
        rVal.setrealLocationY(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
        rVal.setrealLocationZ(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
        rVal.setvalue(ByteStreamUtils.popFloatFromByteQueue(byteBuffer));
        rVal.setterrainWeight(ByteStreamUtils.popFloatFromByteQueue(byteBuffer));
        rVal.setterrainValue(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        return rVal;
    }

    /**
     * Constructs a message of type RequestUseTerrainPalette
     */
    public static TerrainMessage constructRequestUseTerrainPaletteMessage(double realLocationX,double realLocationY,double realLocationZ,float value,float terrainWeight,int terrainValue){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTUSETERRAINPALETTE);
        rVal.setrealLocationX(realLocationX);
        rVal.setrealLocationY(realLocationY);
        rVal.setrealLocationZ(realLocationZ);
        rVal.setvalue(value);
        rVal.setterrainWeight(terrainWeight);
        rVal.setterrainValue(terrainValue);
        rVal.serialize();
        return rVal;
    }

    /**
     * Parses a message of type SpawnPosition
     */
    public static TerrainMessage parseSpawnPositionMessage(CircularByteBuffer byteBuffer, MessagePool pool){
        TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE);
        rVal.messageType = TerrainMessageType.SPAWNPOSITION;
        TerrainMessage.stripPacketHeader(byteBuffer);
        rVal.setrealLocationX(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
        rVal.setrealLocationY(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
        rVal.setrealLocationZ(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
        return rVal;
    }

    /**
     * Constructs a message of type SpawnPosition
     */
    public static TerrainMessage constructSpawnPositionMessage(double realLocationX,double realLocationY,double realLocationZ){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SPAWNPOSITION);
        rVal.setrealLocationX(realLocationX);
        rVal.setrealLocationY(realLocationY);
        rVal.setrealLocationZ(realLocationZ);
        rVal.serialize();
        return rVal;
    }

    /**
     * Parses a message of type RequestChunkData
     */
    public static TerrainMessage parseRequestChunkDataMessage(CircularByteBuffer byteBuffer, MessagePool pool){
        TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE);
        rVal.messageType = TerrainMessageType.REQUESTCHUNKDATA;
        TerrainMessage.stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        return rVal;
    }

    /**
     * Constructs a message of type RequestChunkData
     */
    public static TerrainMessage constructRequestChunkDataMessage(int worldX,int worldY,int worldZ){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTCHUNKDATA);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.serialize();
        return rVal;
    }

    /**
     * Checks if a message of type sendChunkData can be parsed from the byte stream
     */
    public static boolean canParsesendChunkDataMessage(CircularByteBuffer byteBuffer){
        int currentStreamLength = byteBuffer.getRemaining();
        List<Byte> temporaryByteQueue = new LinkedList<Byte>();
        if(currentStreamLength < 6){
            return false;
        }
        if(currentStreamLength < 10){
            return false;
        }
        if(currentStreamLength < 14){
            return false;
        }
        int chunkDataSize = 0;
        if(currentStreamLength < 18){
            return false;
        } else {
            temporaryByteQueue.add(byteBuffer.peek(14 + 0));
            temporaryByteQueue.add(byteBuffer.peek(14 + 1));
            temporaryByteQueue.add(byteBuffer.peek(14 + 2));
            temporaryByteQueue.add(byteBuffer.peek(14 + 3));
            chunkDataSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue);
        }
        if(currentStreamLength < 18 + chunkDataSize){
            return false;
        }
        return true;
    }

    /**
     * Parses a message of type sendChunkData
     */
    public static TerrainMessage parsesendChunkDataMessage(CircularByteBuffer byteBuffer, MessagePool pool){
        TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE);
        rVal.messageType = TerrainMessageType.SENDCHUNKDATA;
        TerrainMessage.stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setchunkData(ByteStreamUtils.popByteArrayFromByteQueue(byteBuffer));
        return rVal;
    }

    /**
     * Constructs a message of type sendChunkData
     */
    public static TerrainMessage constructsendChunkDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDCHUNKDATA);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.setchunkData(chunkData);
        rVal.serialize();
        return rVal;
    }

    /**
     * Parses a message of type RequestReducedChunkData
     */
    public static TerrainMessage parseRequestReducedChunkDataMessage(CircularByteBuffer byteBuffer, MessagePool pool){
        TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE);
        rVal.messageType = TerrainMessageType.REQUESTREDUCEDCHUNKDATA;
        TerrainMessage.stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setchunkResolution(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        return rVal;
    }

    /**
     * Constructs a message of type RequestReducedChunkData
     */
    public static TerrainMessage constructRequestReducedChunkDataMessage(int worldX,int worldY,int worldZ,int chunkResolution){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTREDUCEDCHUNKDATA);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.setchunkResolution(chunkResolution);
        rVal.serialize();
        return rVal;
    }

    /**
     * Checks if a message of type SendReducedChunkData can be parsed from the byte stream
     */
    public static boolean canParseSendReducedChunkDataMessage(CircularByteBuffer byteBuffer){
        int currentStreamLength = byteBuffer.getRemaining();
        List<Byte> temporaryByteQueue = new LinkedList<Byte>();
        if(currentStreamLength < 6){
            return false;
        }
        if(currentStreamLength < 10){
            return false;
        }
        if(currentStreamLength < 14){
            return false;
        }
        if(currentStreamLength < 18){
            return false;
        }
        if(currentStreamLength < 22){
            return false;
        }
        int chunkDataSize = 0;
        if(currentStreamLength < 26){
            return false;
        } else {
            temporaryByteQueue.add(byteBuffer.peek(22 + 0));
            temporaryByteQueue.add(byteBuffer.peek(22 + 1));
            temporaryByteQueue.add(byteBuffer.peek(22 + 2));
            temporaryByteQueue.add(byteBuffer.peek(22 + 3));
            chunkDataSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue);
        }
        if(currentStreamLength < 26 + chunkDataSize){
            return false;
        }
        return true;
    }

    /**
     * Parses a message of type SendReducedChunkData
     */
    public static TerrainMessage parseSendReducedChunkDataMessage(CircularByteBuffer byteBuffer, MessagePool pool){
        TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE);
        rVal.messageType = TerrainMessageType.SENDREDUCEDCHUNKDATA;
        TerrainMessage.stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setchunkResolution(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.sethomogenousValue(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setchunkData(ByteStreamUtils.popByteArrayFromByteQueue(byteBuffer));
        return rVal;
    }

    /**
     * Constructs a message of type SendReducedChunkData
     */
    public static TerrainMessage constructSendReducedChunkDataMessage(int worldX,int worldY,int worldZ,int chunkResolution,int homogenousValue,byte[] chunkData){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDREDUCEDCHUNKDATA);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.setchunkResolution(chunkResolution);
        rVal.sethomogenousValue(homogenousValue);
        rVal.setchunkData(chunkData);
        rVal.serialize();
        return rVal;
    }

    /**
     * Parses a message of type RequestReducedBlockData
     */
    public static TerrainMessage parseRequestReducedBlockDataMessage(CircularByteBuffer byteBuffer, MessagePool pool){
        TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE);
        rVal.messageType = TerrainMessageType.REQUESTREDUCEDBLOCKDATA;
        TerrainMessage.stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setchunkResolution(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        return rVal;
    }

    /**
     * Constructs a message of type RequestReducedBlockData
     */
    public static TerrainMessage constructRequestReducedBlockDataMessage(int worldX,int worldY,int worldZ,int chunkResolution){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTREDUCEDBLOCKDATA);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.setchunkResolution(chunkResolution);
        rVal.serialize();
        return rVal;
    }

    /**
     * Checks if a message of type SendReducedBlockData can be parsed from the byte stream
     */
    public static boolean canParseSendReducedBlockDataMessage(CircularByteBuffer byteBuffer){
        int currentStreamLength = byteBuffer.getRemaining();
        List<Byte> temporaryByteQueue = new LinkedList<Byte>();
        if(currentStreamLength < 6){
            return false;
        }
        if(currentStreamLength < 10){
            return false;
        }
        if(currentStreamLength < 14){
            return false;
        }
        if(currentStreamLength < 18){
            return false;
        }
        if(currentStreamLength < 22){
            return false;
        }
        int chunkDataSize = 0;
        if(currentStreamLength < 26){
            return false;
        } else {
            temporaryByteQueue.add(byteBuffer.peek(22 + 0));
            temporaryByteQueue.add(byteBuffer.peek(22 + 1));
            temporaryByteQueue.add(byteBuffer.peek(22 + 2));
            temporaryByteQueue.add(byteBuffer.peek(22 + 3));
            chunkDataSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue);
        }
        if(currentStreamLength < 26 + chunkDataSize){
            return false;
        }
        return true;
    }

    /**
     * Parses a message of type SendReducedBlockData
     */
    public static TerrainMessage parseSendReducedBlockDataMessage(CircularByteBuffer byteBuffer, MessagePool pool){
        TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE);
        rVal.messageType = TerrainMessageType.SENDREDUCEDBLOCKDATA;
        TerrainMessage.stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setchunkResolution(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.sethomogenousValue(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setchunkData(ByteStreamUtils.popByteArrayFromByteQueue(byteBuffer));
        return rVal;
    }

    /**
     * Constructs a message of type SendReducedBlockData
     */
    public static TerrainMessage constructSendReducedBlockDataMessage(int worldX,int worldY,int worldZ,int chunkResolution,int homogenousValue,byte[] chunkData){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDREDUCEDBLOCKDATA);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.setchunkResolution(chunkResolution);
        rVal.sethomogenousValue(homogenousValue);
        rVal.setchunkData(chunkData);
        rVal.serialize();
        return rVal;
    }

    /**
     * Parses a message of type UpdateBlock
     */
    public static TerrainMessage parseUpdateBlockMessage(CircularByteBuffer byteBuffer, MessagePool pool){
        TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE);
        rVal.messageType = TerrainMessageType.UPDATEBLOCK;
        TerrainMessage.stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setvoxelX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setvoxelY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setvoxelZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setblockType(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setblockMetadata(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        return rVal;
    }

    /**
     * Constructs a message of type UpdateBlock
     */
    public static TerrainMessage constructUpdateBlockMessage(int worldX,int worldY,int worldZ,int voxelX,int voxelY,int voxelZ,int blockType,int blockMetadata){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEBLOCK);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.setvoxelX(voxelX);
        rVal.setvoxelY(voxelY);
        rVal.setvoxelZ(voxelZ);
        rVal.setblockType(blockType);
        rVal.setblockMetadata(blockMetadata);
        rVal.serialize();
        return rVal;
    }

    /**
     * Parses a message of type RequestFluidData
     */
    public static TerrainMessage parseRequestFluidDataMessage(CircularByteBuffer byteBuffer, MessagePool pool){
        TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE);
        rVal.messageType = TerrainMessageType.REQUESTFLUIDDATA;
        TerrainMessage.stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        return rVal;
    }

    /**
     * Constructs a message of type RequestFluidData
     */
    public static TerrainMessage constructRequestFluidDataMessage(int worldX,int worldY,int worldZ){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTFLUIDDATA);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.serialize();
        return rVal;
    }

    /**
     * Checks if a message of type sendFluidData can be parsed from the byte stream
     */
    public static boolean canParsesendFluidDataMessage(CircularByteBuffer byteBuffer){
        int currentStreamLength = byteBuffer.getRemaining();
        List<Byte> temporaryByteQueue = new LinkedList<Byte>();
        if(currentStreamLength < 6){
            return false;
        }
        if(currentStreamLength < 10){
            return false;
        }
        if(currentStreamLength < 14){
            return false;
        }
        int chunkDataSize = 0;
        if(currentStreamLength < 18){
            return false;
        } else {
            temporaryByteQueue.add(byteBuffer.peek(14 + 0));
            temporaryByteQueue.add(byteBuffer.peek(14 + 1));
            temporaryByteQueue.add(byteBuffer.peek(14 + 2));
            temporaryByteQueue.add(byteBuffer.peek(14 + 3));
            chunkDataSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue);
        }
        if(currentStreamLength < 18 + chunkDataSize){
            return false;
        }
        return true;
    }

    /**
     * Parses a message of type sendFluidData
     */
    public static TerrainMessage parsesendFluidDataMessage(CircularByteBuffer byteBuffer, MessagePool pool){
        TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE);
        rVal.messageType = TerrainMessageType.SENDFLUIDDATA;
        TerrainMessage.stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setchunkData(ByteStreamUtils.popByteArrayFromByteQueue(byteBuffer));
        return rVal;
    }

    /**
     * Constructs a message of type sendFluidData
     */
    public static TerrainMessage constructsendFluidDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDFLUIDDATA);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.setchunkData(chunkData);
        rVal.serialize();
        return rVal;
    }

    /**
     * Checks if a message of type updateFluidData can be parsed from the byte stream
     */
    public static boolean canParseupdateFluidDataMessage(CircularByteBuffer byteBuffer){
        int currentStreamLength = byteBuffer.getRemaining();
        List<Byte> temporaryByteQueue = new LinkedList<Byte>();
        if(currentStreamLength < 6){
            return false;
        }
        if(currentStreamLength < 10){
            return false;
        }
        if(currentStreamLength < 14){
            return false;
        }
        int chunkDataSize = 0;
        if(currentStreamLength < 18){
            return false;
        } else {
            temporaryByteQueue.add(byteBuffer.peek(14 + 0));
            temporaryByteQueue.add(byteBuffer.peek(14 + 1));
            temporaryByteQueue.add(byteBuffer.peek(14 + 2));
            temporaryByteQueue.add(byteBuffer.peek(14 + 3));
            chunkDataSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue);
        }
        if(currentStreamLength < 18 + chunkDataSize){
            return false;
        }
        return true;
    }

    /**
     * Parses a message of type updateFluidData
     */
    public static TerrainMessage parseupdateFluidDataMessage(CircularByteBuffer byteBuffer, MessagePool pool){
        TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE);
        rVal.messageType = TerrainMessageType.UPDATEFLUIDDATA;
        TerrainMessage.stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setchunkData(ByteStreamUtils.popByteArrayFromByteQueue(byteBuffer));
        return rVal;
    }

    /**
     * Constructs a message of type updateFluidData
     */
    public static TerrainMessage constructupdateFluidDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEFLUIDDATA);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.setchunkData(chunkData);
        rVal.serialize();
        return rVal;
    }

    @Override
    void serialize(){
        byte[] intValues = new byte[8];
        switch(this.messageType){
            case REQUESTMETADATA:
                rawBytes = new byte[2];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTMETADATA;
                break;
            case RESPONSEMETADATA:
                rawBytes = new byte[2+4+4+4+4+4+4+4];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_RESPONSEMETADATA;
                intValues = ByteStreamUtils.serializeIntToBytes(worldSizeDiscrete);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldMinX);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldMinY);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldMinZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[14+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldMaxX);
                for(int i = 0; i < 4; i++){
                    rawBytes[18+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldMaxY);
                for(int i = 0; i < 4; i++){
                    rawBytes[22+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldMaxZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[26+i] = intValues[i];
                }
                break;
            case REQUESTEDITVOXEL:
                rawBytes = new byte[2+4+4+4+4+4+4+4+4];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTEDITVOXEL;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(voxelX);
                for(int i = 0; i < 4; i++){
                    rawBytes[14+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(voxelY);
                for(int i = 0; i < 4; i++){
                    rawBytes[18+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(voxelZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[22+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeFloatToBytes(terrainWeight);
                for(int i = 0; i < 4; i++){
                    rawBytes[26+i] = intValues[i];
                }                intValues = ByteStreamUtils.serializeIntToBytes(terrainValue);
                for(int i = 0; i < 4; i++){
                    rawBytes[30+i] = intValues[i];
                }
                break;
            case UPDATEVOXEL:
                rawBytes = new byte[2+4+4+4+4+4+4+4+4];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEVOXEL;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(voxelX);
                for(int i = 0; i < 4; i++){
                    rawBytes[14+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(voxelY);
                for(int i = 0; i < 4; i++){
                    rawBytes[18+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(voxelZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[22+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeFloatToBytes(terrainWeight);
                for(int i = 0; i < 4; i++){
                    rawBytes[26+i] = intValues[i];
                }                intValues = ByteStreamUtils.serializeIntToBytes(terrainValue);
                for(int i = 0; i < 4; i++){
                    rawBytes[30+i] = intValues[i];
                }
                break;
            case REQUESTUSETERRAINPALETTE:
                rawBytes = new byte[2+8+8+8+4+4+4];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTUSETERRAINPALETTE;
                intValues = ByteStreamUtils.serializeDoubleToBytes(realLocationX);
                for(int i = 0; i < 8; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeDoubleToBytes(realLocationY);
                for(int i = 0; i < 8; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeDoubleToBytes(realLocationZ);
                for(int i = 0; i < 8; i++){
                    rawBytes[18+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeFloatToBytes(value);
                for(int i = 0; i < 4; i++){
                    rawBytes[26+i] = intValues[i];
                }                intValues = ByteStreamUtils.serializeFloatToBytes(terrainWeight);
                for(int i = 0; i < 4; i++){
                    rawBytes[30+i] = intValues[i];
                }                intValues = ByteStreamUtils.serializeIntToBytes(terrainValue);
                for(int i = 0; i < 4; i++){
                    rawBytes[34+i] = intValues[i];
                }
                break;
            case SPAWNPOSITION:
                rawBytes = new byte[2+8+8+8];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_SPAWNPOSITION;
                intValues = ByteStreamUtils.serializeDoubleToBytes(realLocationX);
                for(int i = 0; i < 8; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeDoubleToBytes(realLocationY);
                for(int i = 0; i < 8; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeDoubleToBytes(realLocationZ);
                for(int i = 0; i < 8; i++){
                    rawBytes[18+i] = intValues[i];
                }
                break;
            case REQUESTCHUNKDATA:
                rawBytes = new byte[2+4+4+4];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTCHUNKDATA;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                break;
            case SENDCHUNKDATA:
                rawBytes = new byte[2+4+4+4+4+chunkData.length];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_SENDCHUNKDATA;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(chunkData.length);
                for(int i = 0; i < 4; i++){
                    rawBytes[14+i] = intValues[i];
                }
                for(int i = 0; i < chunkData.length; i++){
                    rawBytes[18+i] = chunkData[i];
                }
                break;
            case REQUESTREDUCEDCHUNKDATA:
                rawBytes = new byte[2+4+4+4+4];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDCHUNKDATA;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(chunkResolution);
                for(int i = 0; i < 4; i++){
                    rawBytes[14+i] = intValues[i];
                }
                break;
            case SENDREDUCEDCHUNKDATA:
                rawBytes = new byte[2+4+4+4+4+4+4+chunkData.length];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_SENDREDUCEDCHUNKDATA;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(chunkResolution);
                for(int i = 0; i < 4; i++){
                    rawBytes[14+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(homogenousValue);
                for(int i = 0; i < 4; i++){
                    rawBytes[18+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(chunkData.length);
                for(int i = 0; i < 4; i++){
                    rawBytes[22+i] = intValues[i];
                }
                for(int i = 0; i < chunkData.length; i++){
                    rawBytes[26+i] = chunkData[i];
                }
                break;
            case REQUESTREDUCEDBLOCKDATA:
                rawBytes = new byte[2+4+4+4+4];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDBLOCKDATA;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(chunkResolution);
                for(int i = 0; i < 4; i++){
                    rawBytes[14+i] = intValues[i];
                }
                break;
            case SENDREDUCEDBLOCKDATA:
                rawBytes = new byte[2+4+4+4+4+4+4+chunkData.length];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_SENDREDUCEDBLOCKDATA;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(chunkResolution);
                for(int i = 0; i < 4; i++){
                    rawBytes[14+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(homogenousValue);
                for(int i = 0; i < 4; i++){
                    rawBytes[18+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(chunkData.length);
                for(int i = 0; i < 4; i++){
                    rawBytes[22+i] = intValues[i];
                }
                for(int i = 0; i < chunkData.length; i++){
                    rawBytes[26+i] = chunkData[i];
                }
                break;
            case UPDATEBLOCK:
                rawBytes = new byte[2+4+4+4+4+4+4+4+4];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEBLOCK;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(voxelX);
                for(int i = 0; i < 4; i++){
                    rawBytes[14+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(voxelY);
                for(int i = 0; i < 4; i++){
                    rawBytes[18+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(voxelZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[22+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(blockType);
                for(int i = 0; i < 4; i++){
                    rawBytes[26+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(blockMetadata);
                for(int i = 0; i < 4; i++){
                    rawBytes[30+i] = intValues[i];
                }
                break;
            case REQUESTFLUIDDATA:
                rawBytes = new byte[2+4+4+4];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                break;
            case SENDFLUIDDATA:
                rawBytes = new byte[2+4+4+4+4+chunkData.length];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(chunkData.length);
                for(int i = 0; i < 4; i++){
                    rawBytes[14+i] = intValues[i];
                }
                for(int i = 0; i < chunkData.length; i++){
                    rawBytes[18+i] = chunkData[i];
                }
                break;
            case UPDATEFLUIDDATA:
                rawBytes = new byte[2+4+4+4+4+chunkData.length];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(chunkData.length);
                for(int i = 0; i < 4; i++){
                    rawBytes[14+i] = intValues[i];
                }
                for(int i = 0; i < chunkData.length; i++){
                    rawBytes[18+i] = chunkData[i];
                }
                break;
        }
        serialized = true;
    }

}
