com.whirvis.jraknet.RakNetPacket.java Source code

Java tutorial

Introduction

Here is the source code for com.whirvis.jraknet.RakNetPacket.java

Source

/*
 *    __     ______     ______     __  __     __   __     ______     ______  
 *   /\ \   /\  == \   /\  __ \   /\ \/ /    /\ "-.\ \   /\  ___\   /\__  _\
 *  _\_\ \  \ \  __<   \ \  __ \  \ \  _"-.  \ \ \-.  \  \ \  __\   \/_/\ \/  
 * /\_____\  \ \_\ \_\  \ \_\ \_\  \ \_\ \_\  \ \_\\"\_\  \ \_____\    \ \_\ 
 * \/_____/   \/_/ /_/   \/_/\/_/   \/_/\/_/   \/_/ \/_/   \/_____/     \/_/                                                                          
 *
 * the MIT License (MIT)
 *
 * Copyright (c) 2016-2019 Trent "Whirvis" Summerlin
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * the above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.whirvis.jraknet;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.UUID;
import java.util.Map.Entry;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.whirvis.jraknet.map.ShortMap;
import com.whirvis.jraknet.protocol.ConnectionType;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.socket.DatagramPacket;

/**
 * A generic RakNet packet that has the ability to get the ID of the packet
 * along with encoding and decoding.
 *
 * @author Trent "Whirvis" Summerlin
 * @since JRakNet v1.0.0
 */
public class RakNetPacket extends Packet {

    /**
     * The name of the <code>encode()</code> method.
     */
    private static final String ENCODE_METHOD_NAME = "encode";

    /**
     * The name of the <code>decode()</code> method.
     */
    private static final String DECODE_METHOD_NAME = "decode";

    /**
     * The cached packet names, mapped by their ID.
     */
    private static final ShortMap<String> PACKET_NAMES = new ShortMap<String>();

    /**
     * The cached packet IDs, mapped by their name.
     */
    private static final HashMap<String, Short> PACKET_IDS = new HashMap<String, Short>();

    /**
     * The magic identifier.
     */
    public final static byte[] MAGIC = new byte[] { (byte) 0x00, (byte) 0xFF, (byte) 0xFF, 0x00, (byte) 0xFE,
            (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0xFD, (byte) 0xFD, (byte) 0xFD, (byte) 0xFD, (byte) 0x12,
            (byte) 0x34, (byte) 0x56, (byte) 0x78 };

    /**
     * The ID of the {@link com.whirvis.jraknet.protocol.status.ConnectedPing
     * CONNECTED_PING} packet.
     */
    public static final short ID_CONNECTED_PING = 0x00;

    /**
     * The ID of the {@link com.whirvis.jraknet.protocol.status.UnconnectedPing
     * UNCONNECTED_PING} packet.
     */
    public static final short ID_UNCONNECTED_PING = 0x01;

    /**
     * The ID of the
     * {@link com.whirvis.jraknet.protocol.status.UnconnectedPingOpenConnections
     * UNCONNECTED_PING_OPEN_CONNECTIONS} packet.
     */
    public static final short ID_UNCONNECTED_PING_OPEN_CONNECTIONS = 0x02;

    /**
     * The ID of the {@link com.whirvis.jraknet.protocol.status.ConnectedPong
     * CONNECTED_PONG} packet.
     */
    public static final short ID_CONNECTED_PONG = 0x03;

    /**
     * The ID of the <code>DETECT_LOST_CONNECTIONS</code> packet.
     */
    public static final short ID_DETECT_LOST_CONNECTIONS = 0x04;

    /**
     * The ID of the
     * {@link com.whirvis.jraknet.protocol.connection.OpenConnectionRequestOne
     * OPEN_CONNECTION_REQUEST_1} packet.
     */
    public static final short ID_OPEN_CONNECTION_REQUEST_1 = 0x05;

    /**
     * The ID of the
     * {@link com.whirvis.jraknet.protocol.connection.OpenConnectionResponseOne
     * OPEN_CONNECTION_REPLY_1} packet.
     */
    public static final short ID_OPEN_CONNECTION_REPLY_1 = 0x06;

    /**
     * The ID of the
     * {@link com.whirvis.jraknet.protocol.connection.OpenConnectionRequestTwo
     * OPEN_CONNECTION_REQUEST_2} packet.
     */
    public static final short ID_OPEN_CONNECTION_REQUEST_2 = 0x07;

    /**
     * The ID of the
     * {@link com.whirvis.jraknet.protocol.connection.OpenConnectionResponseTwo
     * OPEN_CONNECTION_REPLY_2} packet.
     */
    public static final short ID_OPEN_CONNECTION_REPLY_2 = 0x08;

    /**
     * The ID of the {@link com.whirvis.jraknet.protocol.login.ConnectionRequest
     * CONNECTION_REQUEST} packet.
     */
    public static final short ID_CONNECTION_REQUEST = 0x09;

    /**
     * The ID of the <code>REMOVE_SYSTEM_REQUIRES_PUBLIC_KEY</code> packet.
     */
    public static final short ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY = 0x0A;

    /**
     * The ID of the <code>OUR_SYSTEM_REQUIRES_SECURITY</code> packet.
     */
    public static final short ID_OUR_SYSTEM_REQUIRES_SECURITY = 0x0B;

    /**
     * The ID of the <code>PUBLIC_KEY_MISMATCH</code> packet.
     */
    public static final short ID_PUBLIC_KEY_MISMATCH = 0x0C;

    /**
     * The ID of the <code>OUT_OF_BAND_INTERNAL</code> packet.
     */
    public static final short ID_OUT_OF_BAND_INTERNAL = 0x0D;

    /**
     * The ID of the <code>SND_RECEIPT_ACKED</code> packet.
     * <p>
     * In the original implementation of RakNet, when a packet is acknowledged
     * by a peer this packet is sent back through loopback to the original
     * sender of the packet with the acknoweldgeable reliability. Since this
     * implementation has listeners will special built in acknowledgement and
     * loss methods, this packet has no need for implementation.
     */
    public static final short ID_SND_RECEIPT_ACKED = 0x0E;

    /**
     * The ID of the <code>SND_RECEIPT_LOSS</code> packet.
     * <p>
     * In the original implementation of RakNet, when a packet is acknowledged
     * by a peer this packet is sent back through loopback to the original
     * sender of the packet with the acknoweldgeable reliability. Since this
     * implementation has listeners will special built in acknowledgement and
     * loss methods, this packet has no need for implementation.
     */
    public static final short ID_SND_RECEIPT_LOSS = 0x0F;

    /**
     * The ID of the
     * {@link com.whirvis.jraknet.protocol.login.ConnectionRequestAccepted
     * CONNECTION_REQUEST_ACCEPTED} packet.
     */
    public static final short ID_CONNECTION_REQUEST_ACCEPTED = 0x10;

    /**
     * The ID of the <code>CONNECTION_ATTEMPT_FAILED</code> packet.
     */
    public static final short ID_CONNECTION_ATTEMPT_FAILED = 0x11;

    /**
     * The ID of the <code>ALREADY_CONNECTED</code> packet.
     */
    public static final short ID_ALREADY_CONNECTED = 0x12;

    /**
     * The ID of the
     * {@link com.whirvis.jraknet.protocol.login.NewIncomingConnection
     * NEW_INCOMING_CONNECTION} packet.
     */
    public static final short ID_NEW_INCOMING_CONNECTION = 0x13;

    /**
     * The ID of the <code>NO_FREE_INCOMING_CONNECTIONS</code> packet.
     */
    public static final short ID_NO_FREE_INCOMING_CONNECTIONS = 0x14;

    /**
     * The ID of the <code>DISCONNECTION_NOTIFICATION</code> packet.
     */
    public static final short ID_DISCONNECTION_NOTIFICATION = 0x15;

    /**
     * The ID of the <code>CONNECTION_LOST</code> packet.
     */
    public static final short ID_CONNECTION_LOST = 0x16;

    /**
     * The ID of the
     * {@link com.whirvis.jraknet.protocol.connection.ConnectionBanned
     * CONNECTION_BANNED} packet.
     */
    public static final short ID_CONNECTION_BANNED = 0x17;

    /**
     * The ID of the <code>INVALID_PASSWORD</code> packet.
     */
    public static final short ID_INVALID_PASSWORD = 0x18;

    /**
     * The ID of the
     * {@link com.whirvis.jraknet.protocol.connection.IncompatibleProtocolVersion
     * INCOMPATIBLE_PROTOCOL_VERSION} packet.
     */
    public static final short ID_INCOMPATIBLE_PROTOCOL_VERSION = 0x19;

    /**
     * The ID of the <code>IP_RECENTLY_CONNECTED</code> packet.
     */
    public static final short ID_IP_RECENTLY_CONNECTED = 0x1A;

    /**
     * The ID of the <code>TIMESTAMP</code> packet.
     */
    public static final short ID_TIMESTAMP = 0x1B;

    /**
     * The ID of the {@link com.whirvis.jraknet.protocol.status.UnconnectedPong
     * UNCONNECTED_PONG} packet.
     */
    public static final short ID_UNCONNECTED_PONG = 0x1C;

    /**
     * The ID of the <code>ADVERTISE_SYSTEM</code> packet.
     */
    public static final short ID_ADVERTISE_SYSTEM = 0x1D;

    /**
     * The ID of the <code>DOWNLOAD_PROGRESS</code> packet.
     */
    public static final short ID_DOWNLOAD_PROGRESS = 0x1E;

    /**
     * The ID of the <code>REMOTE_DISCONNECTION_NOTIFICATION</code> packet.
     */
    public static final short ID_REMOTE_DISCONNECTION_NOTIFICATION = 0x1F;

    /**
     * The ID of the <code>REMOTE_CONNECTION_LOST</code> packet.
     */
    public static final short ID_REMOTE_CONNECTION_LOST = 0x20;

    /**
     * The ID of the <code>REMOTE_NEW_INCOMING_CONNECTION</code> packet.
     */
    public static final short ID_REMOTE_NEW_INCOMING_CONNECTION = 0x21;

    /**
     * The ID of the <code>FILE_LIST_TRANSFER_HEADER</code> packet.
     */
    public static final short ID_FILE_LIST_TRANSFER_HEADER = 0x22;

    /**
     * The ID of the <code>FILE_LIST_TRANSFER_FILE</code> packet.
     */
    public static final short ID_FILE_LIST_TRANSFER_FILE = 0x23;

    /**
     * The ID of the <code>FILE_LIST_REFERENCE_PUSH_ACK</code> packet.
     */
    public static final short ID_FILE_LIST_REFERENCE_PUSH_ACK = 0x24;

    /**
     * The ID of the <code>DDT_DOWNLOAD_REQUEST</code> packet.
     */
    public static final short ID_DDT_DOWNLOAD_REQUEST = 0x25;

    /**
     * The ID of the <code>TRANSPORT_STRING</code> packet.
     */
    public static final short ID_TRANSPORT_STRING = 0x26;

    /**
     * The ID of the <code>REPLICA_MANAGER_CONSTRUCTION</code> packet.
     */
    public static final short ID_REPLICA_MANAGER_CONSTRUCTION = 0x27;

    /**
     * The ID of the <code>REPLICA_MANAGER_SCOPE_CHANGE</code> packet.
     */
    public static final short ID_REPLICA_MANAGER_SCOPE_CHANGE = 0x28;

    /**
     * The ID of the <code>REPLICA_MANAGER_SERIALIZE</code> packet.
     */
    public static final short ID_REPLICA_MANAGER_SERIALIZE = 0x29;

    /**
     * The ID of the <code>REPLICA_MANAGER_DOWNLOAD_STARTED</code> packet.
     */
    public static final short ID_REPLICA_MANAGER_DOWNLOAD_STARTED = 0x2A;

    /**
     * The ID of the <code>REPLICA_MANAGER_DOWNLOAD_COMPLETE</code> packet.
     */
    public static final short ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE = 0x2B;

    /**
     * The ID of the <code>RAKVOICE_OPEN_CHANNEL_REQUEST</code> packet.
     */
    public static final short ID_RAKVOICE_OPEN_CHANNEL_REQUEST = 0x2C;

    /**
     * The ID of the <code>RAKVOICE_OPEN_CHANNEL_REPLY</code> packet.
     */
    public static final short ID_RAKVOICE_OPEN_CHANNEL_REPLY = 0x2D;

    /**
     * The ID of the <code>RAKVOICE_CLOSE_CHANNEL</code> packet.
     */
    public static final short ID_RAKVOICE_CLOSE_CHANNEL = 0x2E;

    /**
     * The ID of the <code>RAKVOICE_DATA</code> packet.
     */
    public static final short ID_RAKVOICE_DATA = 0x2F;

    /**
     * The ID of the <code>AUTOPATHER_GET_CHANGELIST_SINCE_DATE</code> packet.
     */
    public static final short ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE = 0x30;

    /**
     * The ID of the <code>AUTOPATCHER_CREATION_LIST</code> packet.
     */
    public static final short ID_AUTOPATCHER_CREATION_LIST = 0x31;

    /**
     * The ID of the <code>AUTOPATCHER_DELETION_LIST</code> packet.
     */
    public static final short ID_AUTOPATCHER_DELETION_LIST = 0x32;

    /**
     * The ID of the <code>AUTOPATCHER_GET_PATCH</code> packet.
     */
    public static final short ID_AUTOPATCHER_GET_PATCH = 0x33;

    /**
     * The ID of the <code>AUTOPATCHER_PATCH_LIST</code> packet.
     */
    public static final short ID_AUTOPATCHER_PATCH_LIST = 0x34;

    /**
     * The ID of the <code>AUTOPATHER_REPOSITORY_FATAL_ERROR</code> packet.
     */
    public static final short ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR = 0x35;

    /**
     * The ID of the
     * <code>AUTOPATHER_CANNOT_DOWNLOAD_ORIGINAL_UNMODIFIED_FILES</code> packet.
     */
    public static final short ID_AUTOPATCHER_CANNOT_DOWNLOAD_ORIGINAL_UNMODIFIED_FILES = 0x36;

    /**
     * The ID of the <code>AUTOPATHER_FINISHED_INTERNAL</code> packet.
     */
    public static final short ID_AUTOPATCHER_FINISHED_INTERNAL = 0x37;

    /**
     * The ID of the <code>AUTOPATHER_FINISHED</code> packet.
     */
    public static final short ID_AUTOPATCHER_FINISHED = 0x38;

    /**
     * The ID of the <code>AUTOPATCHER_RESTART_APPLICATION</code> packet.
     */
    public static final short ID_AUTOPATCHER_RESTART_APPLICATION = 0x39;

    /**
     * The ID of the <code>NAT_PUNCHTHROUGH_REQUEST</code> packet.
     */
    public static final short ID_NAT_PUNCHTHROUGH_REQUEST = 0x3A;

    /**
     * The ID of the <code>NAT_CONNECT_AT_TIME</code> packet.
     */
    public static final short ID_NAT_CONNECT_AT_TIME = 0x3B;

    /**
     * The ID of the <code>NAT_GET_MOST_RECENT_PORT</code> packet.
     */
    public static final short ID_NAT_GET_MOST_RECENT_PORT = 0x3C;

    /**
     * The ID of the <code>NAT_CLIENT_READY</code> packet.
     */
    public static final short ID_NAT_CLIENT_READY = 0x3D;

    /**
     * The ID of the <code>NAT_TARGET_NOT_CONNECT</code> packet.
     */
    public static final short ID_NAT_TARGET_NOT_CONNECTED = 0x3E;

    /**
     * The ID of the <code>NAT_TARGET_UNRESPONSIVE</code> packet.
     */
    public static final short ID_NAT_TARGET_UNRESPONSIVE = 0x3F;

    /**
     * The ID of the <code>NAT_CONNECTION_TO_TARGET_LOST</code> packet.
     */
    public static final short ID_NAT_CONNECTION_TO_TARGET_LOST = 0x40;

    /**
     * The ID of the <code>NAT_ALREADY_IN_PROGRESS</code> packet.
     */
    public static final short ID_NAT_ALREADY_IN_PROGRESS = 0x41;

    /**
     * The ID of the <code>NAT_PUNCHTHROUGH_FAILED</code> packet.
     */
    public static final short ID_NAT_PUNCHTHROUGH_FAILED = 0x42;

    /**
     * The ID of the <code>NAT_PUNCHTHROUGH_SUCCEEDED</code> packet.
     */
    public static final short ID_NAT_PUNCHTHROUGH_SUCCEEDED = 0x43;

    /**
     * The ID of the <code>READY_EVENT_SET</code> packet.
     */
    public static final short ID_READY_EVENT_SET = 0x44;

    /**
     * The ID of the <code>READY_EVENT_UNSET</code> packet.
     */
    public static final short ID_READY_EVENT_UNSET = 0x45;

    /**
     * The ID of the <code>READY_EVENT_ALL_SET</code> packet.
     */
    public static final short ID_READY_EVENT_ALL_SET = 0x46;

    /**
     * The ID of the <code>READY_EVENT_QUERY</code> packet.
     */
    public static final short ID_READY_EVENT_QUERY = 0x47;

    /**
     * The ID of the <code>LOBBY_GENERAL</code> packet.
     */
    public static final short ID_LOBBY_GENERAL = 0x48;

    /**
     * The ID of the <code>RPC_REMOTE_ERROR</code> packet.
     */
    public static final short ID_RPC_REMOTE_ERROR = 0x49;

    /**
     * The ID of the <code>RPC_PLUGIN</code> packet.
     */
    public static final short ID_RPC_PLUGIN = 0x4A;

    /**
     * The ID of the <code>FILE_LIST_REFERENCE_PUSH</code> packet.
     */
    public static final short ID_FILE_LIST_REFERENCE_PUSH = 0x4B;

    /**
     * The ID of the <code>READY_EVENT_FORCE_ALL_SET</code> packet.
     */
    public static final short ID_READY_EVENT_FORCE_ALL_SET = 0x4C;

    /**
     * The ID of the <code>ROOMS_EXECUTE_FUNC</code> packet.
     */
    public static final short ID_ROOMS_EXECUTE_FUNC = 0x4D;

    /**
     * The ID of the <code>ROOMS_LOGON_STATUS</code> packet.
     */
    public static final short ID_ROOMS_LOGON_STATUS = 0x4E;

    /**
     * The ID of the <code>ROOMS_HANDLE_CHANGE</code> packet.
     */
    public static final short ID_ROOMS_HANDLE_CHANGE = 0x4F;

    /**
     * The ID of the <code>LOBBY2_SEND_MESSAGE</code> packet.
     */
    public static final short ID_LOBBY2_SEND_MESSAGE = 0x50;

    /**
     * The ID of the <code>LOBBY2_SERVER_ERROR</code> packet.
     */
    public static final short ID_LOBBY2_SERVER_ERROR = 0x51;

    /**
     * The ID of the <code>FMC2_NEW_HOST</code> packet.
     */
    public static final short ID_FCM2_NEW_HOST = 0x52;

    /**
     * The ID of the <code>FCM2_REQUEST_FCMGUID</code> packet.
     */
    public static final short ID_FCM2_REQUEST_FCMGUID = 0x53;

    /**
     * The ID of the <code>FCM2_RESPOND_CONNECTION_COUNT</code> packet.
     */
    public static final short ID_FCM2_RESPOND_CONNECTION_COUNT = 0x54;

    /**
     * The ID of the <code>FMC2_INFORM_FCMGUID</code> packet.
     */
    public static final short ID_FCM2_INFORM_FCMGUID = 0x55;

    /**
     * The ID of the <code>FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT</code> packet.
     */
    public static final short ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT = 0x56;

    /**
     * The ID of the <code>FCM2_VERIFIED_JOIN_START</code> packet.
     */
    public static final short ID_FCM2_VERIFIED_JOIN_START = 0x57;

    /**
     * The ID of the <code>FCM2_VERIFIED_JOIN_CAPABLE</code> packet.
     */
    public static final short ID_FCM2_VERIFIED_JOIN_CAPABLE = 0x58;

    /**
     * The ID of the <code>FCM2_VERIFIED_JOIN_FAILED</code> packet.
     */
    public static final short ID_FCM2_VERIFIED_JOIN_FAILED = 0x59;

    /**
     * The ID of the <code>FCM2_VERIFIED_JOIN_ACCEPTED</code> packet.
     */
    public static final short ID_FCM2_VERIFIED_JOIN_ACCEPTED = 0x5A;

    /**
     * The ID of the <code>FCM2_VERIFIED_JOIN_REJECTED</code> packet.
     */
    public static final short ID_FCM2_VERIFIED_JOIN_REJECTED = 0x5B;

    /**
     * The ID of the <code>UDP_PROXY_GENERAL</code> packet.
     */
    public static final short ID_UDP_PROXY_GENERAL = 0x5C;

    /**
     * The ID of the <code>SQLITE3_EXEC</code> packet.
     */
    public static final short ID_SQLITE3_EXEC = 0x5D;

    /**
     * The ID of the <code>SQLITE3_UNKNOWN_DB</code> packet.
     */
    public static final short ID_SQLITE3_UNKNOWN_DB = 0x5E;

    /**
     * The ID of the <code>SQLLITE_LOGGER</code> packet.
     */
    public static final short ID_SQLLITE_LOGGER = 0x5F;

    /**
     * The ID of the <code>NAT_TYPE_DETECTION_REQUEST</code> packet.
     */
    public static final short ID_NAT_TYPE_DETECTION_REQUEST = 0x60;

    /**
     * The ID of the <code>NAT_TYPE_DETECTION_RESULT</code> packet.
     */
    public static final short ID_NAT_TYPE_DETECTION_RESULT = 0x61;

    /**
     * The ID of the <code>ROUTER_2_INTERNAL</code> packet.
     */
    public static final short ID_ROUTER_2_INTERNAL = 0x62;

    /**
     * The ID of the <code>ROUTER_2_FOWARDING_NO_PATH</code> packet.
     */
    public static final short ID_ROUTER_2_FORWARDING_NO_PATH = 0x63;

    /**
     * The ID of the <code>ROUTER_2_FORWARDING_ESTABLISHED</code> packet.
     */
    public static final short ID_ROUTER_2_FORWARDING_ESTABLISHED = 0x64;

    /**
     * The ID of the <code>ROUTER_2_REROUTED</code> packet.
     */
    public static final short ID_ROUTER_2_REROUTED = 0x65;

    /**
     * The ID of the <code>TEAM_BALANCER_INTERNAL</code> packet.
     */
    public static final short ID_TEAM_BALANCER_INTERNAL = 0x66;

    /**
     * The ID of the <code>TEAM_BALANCER_REQUESTED_TEAM_FULL</code> packet.
     */
    public static final short ID_TEAM_BALANCER_REQUESTED_TEAM_FULL = 0x67;

    /**
     * The ID of the <code>TEAM_BALANCER_REQUESTED_TEAM_LOCKED</code> packet.
     */
    public static final short ID_TEAM_BALANCER_REQUESTED_TEAM_LOCKED = 0x68;

    /**
     * The ID of the <code>TEAM_BALANCER_TEAM_REQUESTED_CANCELLED</code> packet.
     */
    public static final short ID_TEAM_BALANCER_TEAM_REQUESTED_CANCELLED = 0x69;

    /**
     * The ID of the <code>TEAM_BALANCER_TEAM_ASSIGNED</code> packet.
     */
    public static final short ID_TEAM_BALANCER_TEAM_ASSIGNED = 0x6A;

    /**
     * The ID of the <code>LIGHTSPEED_INTEGRATION</code> packet.
     */
    public static final short ID_LIGHTSPEED_INTEGRATION = 0x6B;

    /**
     * The ID of the <code>XBOX_LOBBY</code> packet.
     */
    public static final short ID_XBOX_LOBBY = 0x6C;

    /**
     * The ID of the
     * <code>TWO_WAY_AUTHENTICATION_INCOMING_CHALLENEGE_SUCCESS</code> packet.
     */
    public static final short ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS = 0x6D;

    /**
     * The ID of the
     * <code>TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS</code> packet.
     */
    public static final short ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS = 0x6E;

    /**
     * The ID of the
     * <code>TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE</code> packet.
     */
    public static final short ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE = 0x6F;

    /**
     * The ID of the
     * <code>TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE</code> packet.
     */
    public static final short ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE = 0x70;

    /**
     * The ID of the
     * <code>TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT</code> packet.
     */
    public static final short ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT = 0x71;

    /**
     * The ID of the <code>TWO_WAY_AUTHENTICATION_NEGOTIATION</code> packet.
     */
    public static final short ID_TWO_WAY_AUTHENTICATION_NEGOTIATION = 0x72;

    /**
     * The ID of the <code>CLOUD_POST_REQUEST</code> packet.
     */
    public static final short ID_CLOUD_POST_REQUEST = 0x73;

    /**
     * The ID of the <code>CLOUD_RELEASE_REQUEST</code> packet.
     */
    public static final short ID_CLOUD_RELEASE_REQUEST = 0x74;

    /**
     * The ID of the <code>CLOUD_GET_REQUEST</code> packet.
     */
    public static final short ID_CLOUD_GET_REQUEST = 0x75;

    /**
     * The ID of the <code>CLOUD_GET_RESPONSE</code> packet.
     */
    public static final short ID_CLOUD_GET_RESPONSE = 0x76;

    /**
     * The ID of the <code>CLOUD_UNSUBSCRIBE_REQUEST</code> packet.
     */
    public static final short ID_CLOUD_UNSUBSCRIBE_REQUEST = 0x77;

    /**
     * The ID of the <code>CLOUD_SERVER_TO_SERVER_COMMAND</code> packet.
     */
    public static final short ID_CLOUD_SERVER_TO_SERVER_COMMAND = 0x78;

    /**
     * The ID of the <code>CLOUD_SUBSCRIPTION_NOTIFICATION</code> packet.
     */
    public static final short ID_CLOUD_SUBSCRIPTION_NOTIFICATION = 0x79;

    /**
     * The ID of the <code>LIB_VOICE</code> packet.
     */
    public static final short ID_LIB_VOICE = 0x7A;

    /**
     * The ID of the <code>RELAY_PLUGIN</code> packet.
     */
    public static final short ID_RELAY_PLUGIN = 0x7B;

    /**
     * The ID of the <code>NAT_REQUEST_BOUND_ADDRESSES</code> packet.
     */
    public static final short ID_NAT_REQUEST_BOUND_ADDRESSES = 0x7C;

    /**
     * The ID of the <code>NAT_RESPOND_BOUND_ADDRESSES</code> packet.
     */
    public static final short ID_NAT_RESPOND_BOUND_ADDRESSES = 0x7D;

    /**
     * The ID of the <code>FCM2_UPDATE_USER_CONTENT</code> packet.
     */
    public static final short ID_FCM2_UPDATE_USER_CONTEXT = 0x7E;

    /**
     * The ID of the <code>RESERVED_3</code> packet.
     */
    public static final short ID_RESERVED_3 = 0x7F;

    /**
     * The ID of the <code>RESERVED_4</code> packet.
     */
    public static final short ID_RESERVED_4 = 0x80;

    /**
     * The ID of the <code>RESERVED_5</code> packet.
     */
    public static final short ID_RESERVED_5 = 0x81;

    /**
     * The ID of the <code>RESERVED_6</code> packet.
     */
    public static final short ID_RESERVED_6 = 0x82;

    /**
     * The ID of the <code>RESERVERD_7</code> packet.
     */
    public static final short ID_RESERVED_7 = 0x83;

    /**
     * The ID of the <code>RESERVED_8</code> packet.
     */
    public static final short ID_RESERVED_8 = 0x84;

    /**
     * The ID of the <code>RESERVED_9</code> packet.
     */
    public static final short ID_RESERVED_9 = 0x85;

    /**
     * This is the first ID that the user can for the IDs of their packets.
     * Since packet IDs are written using {@link #writeUnsignedByte(int)}, the
     * highest packet ID that can be used is <code>0xFF</code>.
     * <p>
     * If one must have more than <code>121</code> packet IDs
     * (<code>0xFF - ID_USER_PACKET_ENUM</code>), then one can have a singular
     * ID that they use for all of their user packets with another field to be
     * the packet ID.
     */
    public static final short ID_USER_PACKET_ENUM = 0x86;

    /**
     * The ID of the <code>CUSTOM_0</code> packet.
     */
    public static final short ID_CUSTOM_0 = 0x80;

    /**
     * The ID of the <code>CUSTOM_1</code> packet.
     */
    public static final short ID_CUSTOM_1 = 0x81;

    /**
     * The ID of the <code>CUSTOM_2</code> packet.
     */
    public static final short ID_CUSTOM_2 = 0x82;

    /**
     * The ID of the <code>CUSTOM_3</code> packet.
     */
    public static final short ID_CUSTOM_3 = 0x83;

    /**
     * The ID of the
     * {@link com.whirvis.jraknet.protocol.message.CustomFourPacket CUSTOM_4}
     * packet.
     */
    public static final short ID_CUSTOM_4 = 0x84;

    /**
     * The ID of the <code>CUSTOM_5</code> packet.
     */
    public static final short ID_CUSTOM_5 = 0x85;

    /**
     * The ID of the <code>CUSTOM_6</code> packet.
     */
    public static final short ID_CUSTOM_6 = 0x86;

    /**
     * The ID of the <code>CUSTOM_7</code> packet.
     */
    public static final short ID_CUSTOM_7 = 0x87;

    /**
     * The ID of the <code>CUSTOM_8</code> packet.
     */
    public static final short ID_CUSTOM_8 = 0x88;

    /**
     * The ID of the <code>CUSTOM_9</code> packet.
     */
    public static final short ID_CUSTOM_9 = 0x89;

    /**
     * The ID of the <code>CUSTOM_A</code> packet.
     */
    public static final short ID_CUSTOM_A = 0x8A;

    /**
     * The ID of the <code>CUSTOM_B</code> packet.
     */
    public static final short ID_CUSTOM_B = 0x8B;

    /**
     * The ID of the <code>CUSTOM_C</code> packet.
     */
    public static final short ID_CUSTOM_C = 0x8C;

    /**
     * The ID of the <code>CUSTOM_D</code> packet.
     */
    public static final short ID_CUSTOM_D = 0x8D;

    /**
     * The ID of the <code>CUSTOM_E</code> packet.
     */
    public static final short ID_CUSTOM_E = 0x8E;

    /**
     * The ID of the <code>CUSTOM_F</code> packet.
     */
    public static final short ID_CUSTOM_F = 0x8F;

    /**
     * The ID of the
     * {@link com.whirvis.jraknet.protocol.message.acknowledge.AcknowledgedPacket
     * ACK} packet.
     */
    public static final short ID_ACK = 0xC0;

    /**
     * The ID of the
     * {@link com.whirvis.jraknet.protocol.message.acknowledge.NotAcknowledgedPacket
     * NACK} packet.
     */
    public static final short ID_NACK = 0xA0;

    private static boolean mappedNameIds;

    /**
     * Maps all <code>public</code> packet IDs to their respective field names
     * and vice-versa.
     * <p>
     * Packet IDs {@link #ID_CUSTOM_0}, {@link #ID_CUSTOM_1},
     * {@link #ID_CUSTOM_2}, {@link #ID_CUSTOM_3}, {@link #ID_CUSTOM_4},
     * {@link #ID_CUSTOM_5}, {@link #ID_CUSTOM_6}, {@link #ID_CUSTOM_7},
     * {@link #ID_CUSTOM_8}, {@link #ID_CUSTOM_9}, {@link #ID_CUSTOM_A},
     * {@link #ID_CUSTOM_B}, {@link #ID_CUSTOM_C}, {@link #ID_CUSTOM_D},
     * {@link #ID_CUSTOM_E}, {@link #ID_CUSTOM_F}, {@link #ID_ACK}, and
     * {@link #ID_NACK} will never are ignored as they are not only internal
     * packets but they also override other packets with the same ID.
     */
    private static void mapNameIds() {
        if (mappedNameIds == false) {
            Logger log = LogManager.getLogger(RakNetPacket.class);
            for (Field field : RakNetPacket.class.getFields()) {
                if (field.getType().equals(short.class)) {
                    try {
                        short packetId = field.getShort(null);
                        if ((packetId >= ID_CUSTOM_0 && packetId <= ID_CUSTOM_F) || packetId == ID_ACK
                                || packetId == ID_NACK) {
                            continue; // Ignored as they override other packet
                                      // IDs
                        }
                        String packetName = field.getName();
                        String currentName = PACKET_NAMES.put(packetId, packetName);
                        PACKET_IDS.put(packetName, packetId);
                        if (currentName != null) {
                            if (!currentName.equals(packetName)) {
                                log.warn("Found duplicate ID " + RakNet.toHexStringId(packetId) + " for \""
                                        + packetName + "\" and \"" + currentName + "\", overriding name and ID");
                            }
                        } else {
                            log.debug("Assigned packet ID " + RakNet.toHexStringId(packetId) + " to " + packetName);
                        }
                    } catch (ReflectiveOperationException e) {
                        e.printStackTrace();
                    }
                }
            }
            mappedNameIds = true;
        }
    }

    /**
     * Returns whether or not a packet with the specified ID exists as a RakNet
     * packet.
     * 
     * @param id
     *            the ID of the packet.
     * @return <code>true</code> if a packet with the ID exists as a RakNet
     *         packet, <code>false</code>.
     */
    public static boolean hasPacket(int id) {
        return PACKET_NAMES.containsKey((short) id);
    }

    /**
     * Returns whether or not a packet with the specified name exists as a
     * RakNet packet.
     * 
     * @param name
     *            the name of the packet.
     * @return <code>true</code> if a packet with the name exists as a RakNet
     *         packet, <code>false</code>.
     */
    public static boolean hasPacket(String name) {
        return PACKET_IDS.containsKey(name);
    }

    /**
     * Returns the name of the packet with the specified ID.
     * <p>
     * Packet IDs {@link #ID_CUSTOM_0}, {@link #ID_CUSTOM_1},
     * {@link #ID_CUSTOM_2}, {@link #ID_CUSTOM_3}, {@link #ID_CUSTOM_4},
     * {@link #ID_CUSTOM_5}, {@link #ID_CUSTOM_6}, {@link #ID_CUSTOM_7},
     * {@link #ID_CUSTOM_8}, {@link #ID_CUSTOM_9}, {@link #ID_CUSTOM_A},
     * {@link #ID_CUSTOM_B}, {@link #ID_CUSTOM_C}, {@link #ID_CUSTOM_D},
     * {@link #ID_CUSTOM_E}, {@link #ID_CUSTOM_F}, {@link #ID_ACK}, and
     * {@link #ID_NACK} will never be returned as they are not only internal
     * packets but they also override other packets with the same ID.
     * 
     * @param id
     *            the ID of the packet.
     * @return the name of the packet with the specified ID, its hexadecimal ID
     *         according to {@link RakNet#toHexStringId(int)} if it does not
     *         exist.
     */
    public static String getName(int id) {
        if (mappedNameIds == false) {
            mapNameIds();
        }
        if (!PACKET_NAMES.containsKey((short) id)) {
            return RakNet.toHexStringId(id & 0xFF);
        }
        return PACKET_NAMES.get((short) id);
    }

    /**
     * Returns the name of the specified packet.
     * <p>
     * Packet IDs {@link #ID_CUSTOM_0}, {@link #ID_CUSTOM_1},
     * {@link #ID_CUSTOM_2}, {@link #ID_CUSTOM_3}, {@link #ID_CUSTOM_4},
     * {@link #ID_CUSTOM_5}, {@link #ID_CUSTOM_6}, {@link #ID_CUSTOM_7},
     * {@link #ID_CUSTOM_8}, {@link #ID_CUSTOM_9}, {@link #ID_CUSTOM_A},
     * {@link #ID_CUSTOM_B}, {@link #ID_CUSTOM_C}, {@link #ID_CUSTOM_D},
     * {@link #ID_CUSTOM_E}, {@link #ID_CUSTOM_F}, {@link #ID_ACK}, and
     * {@link #ID_NACK} will never be returned as they are not only internal
     * packets but they also override other packets with the same ID.
     * 
     * @param packet
     *            the packet.
     * @return the name of the packet, its hexadecimal ID according to
     *         {@link RakNet#toHexStringId(int)} if it does not exist.
     * @throws NullPointerException
     *             if the <code>packet</code> is <code>null</code>.
     */
    public static String getName(RakNetPacket packet) throws NullPointerException {
        if (packet == null) {
            throw new NullPointerException("Packet cannot be null");
        }
        return getName(packet.getId());
    }

    /**
     * Returns the ID of the packet with the specified name.
     * 
     * @param name
     *            the name of the packet.
     * @return the ID of the packet with the specified name, <code>-1</code> if
     *         it does not exist.
     */
    public static short getId(String name) {
        if (mappedNameIds == false) {
            mapNameIds();
        }
        return PACKET_IDS.containsKey(name) ? PACKET_IDS.get(name) : -1;
    }

    /**
     * Returns whether or not a method with the specified name has been
     * overridden the method in the original specified class by the specified
     * class instance.
     * 
     * @param instance
     *            the class instance.
     * @param clazz
     *            the original class.
     * @param methodName
     *            the name of the method.
     * @return <code>true</code> if the method has been overridden,
     *         <code>false</code> otherwise.
     */
    private static boolean isMethodOverriden(Class<?> instance, Class<?> clazz, String methodName) {
        try {
            return !clazz.getMethod(ENCODE_METHOD_NAME).getDeclaringClass().equals(clazz);
        } catch (NoSuchMethodException | SecurityException e) {
            return false;
        }
    }

    private short id;
    private final boolean supportsEncoding;
    private final boolean supportsDecoding;

    /**
     * Creates a RakNet packet.
     * 
     * @param id
     *            the ID of the packet.
     * @throws IllegalArgumentException
     *             if the <code>id</code> is not in between <code>0-255</code>.
     */
    public RakNetPacket(int id) throws IllegalArgumentException {
        super();
        if (id < 0x00 || id > 0xFF) {
            throw new IllegalArgumentException("ID must be in between 0-255");
        }
        this.writeUnsignedByte(this.id = (short) id);
        this.supportsEncoding = isMethodOverriden(this.getClass(), RakNetPacket.class, ENCODE_METHOD_NAME);
        this.supportsDecoding = isMethodOverriden(this.getClass(), RakNetPacket.class, DECODE_METHOD_NAME);
    }

    /**
     * Creates a RakNet packet.
     * 
     * @param buffer
     *            the buffer to read from and write to. The buffer must have at
     *            least one byte to be read from for the ID.
     * @throws IllegalArgumentException
     *             if the <code>buffer</code> has less than <code>1</code>
     *             readable <code>byte</code>.
     */
    public RakNetPacket(ByteBuf buffer) throws IllegalArgumentException {
        super(buffer);
        if (this.remaining() < 1) {
            throw new IllegalArgumentException("Buffer must have at least one readable byte for the ID");
        }
        this.id = this.readUnsignedByte();
        this.supportsEncoding = isMethodOverriden(this.getClass(), RakNetPacket.class, ENCODE_METHOD_NAME);
        this.supportsDecoding = isMethodOverriden(this.getClass(), RakNetPacket.class, DECODE_METHOD_NAME);
    }

    /**
     * Creates a RakNet packet.
     * 
     * @param datagram
     *            the datagram packet to read from. The datagram must have at
     *            least one byte to be read from for the ID.
     * @throws IllegalArgumentException
     *             if the buffer contained within the datagram has less than
     *             <code>1</code> readable <code>byte</code>.
     * @see #RakNetPacket(ByteBuf)
     */
    public RakNetPacket(DatagramPacket datagram) throws IllegalArgumentException {
        this(datagram.content());
    }

    /**
     * Creates a RakNet packet.
     * 
     * @param data
     *            the byte array to read to read from. The byte array must have
     *            at least one byte to be read from for the ID.
     * @throws IllegalArgumentException
     *             if the length of the <code>data</code> is less than
     *             <code>1</code>.
     * @see #RakNetPacket(ByteBuf)
     */
    public RakNetPacket(byte[] data) throws IllegalArgumentException {
        this(Unpooled.copiedBuffer(data));
    }

    /**
     * Creates a RakNet packet.
     * 
     * @param packet
     *            the packet to read from and write to. The packet must have at
     *            least one byte to be read from for the ID. If the packet is an
     *            instance of {@link RakNetPacket}, it will be casted and have
     *            its ID retrieved via {@link #getId()}.
     * @throws IllegalArgumentException
     *             if the packet size has less than <code>1</code> readable
     *             <code>byte</code> and is not an instance of
     *             {@link RakNetPacket}.
     */
    public RakNetPacket(Packet packet) throws IllegalArgumentException {
        super(packet);
        if (packet instanceof RakNetPacket) {
            this.id = ((RakNetPacket) packet).id;
        } else {
            if (this.remaining() < 1) {
                throw new IllegalArgumentException("The packet must have at least one byte to read the ID");
            }
            this.id = this.readUnsignedByte();
        }
        this.supportsEncoding = isMethodOverriden(this.getClass(), RakNetPacket.class, ENCODE_METHOD_NAME);
        this.supportsDecoding = isMethodOverriden(this.getClass(), RakNetPacket.class, DECODE_METHOD_NAME);
    }

    /**
     * Returns the ID of the packet.
     * 
     * @return the ID of the packet.
     */
    public final short getId() {
        return this.id;
    }

    /**
     * Reads a magic array and returns whether or not it is valid.
     * 
     * @return <code>true</code> if the magic array was valid,
     *         <code>false</code> otherwise.
     */
    public final boolean readMagic() {
        byte[] magicCheck = this.read(MAGIC.length);
        return Arrays.equals(MAGIC, magicCheck);
    }

    /**
     * Reads a {@link ConnectionType}.
     * <p>
     * This method will check to make sure if there is at least enough data to
     * read the the connection type magic before reading the data. This is due
     * to the fact that this is meant to be used strictly at the end of packets
     * that can be used to signify the protocol implementation of the sender.
     * 
     * @return a {@link ConnectionType}, {@link ConnectionType#VANILLA} if not
     *         enough data to read one is present.
     * @throws RakNetException
     *             if not enough data is present in the packet after the
     *             connection type magic or there are duplicate keys in the
     *             metadata.
     */
    public final ConnectionType readConnectionType() throws RakNetException {
        if (this.remaining() >= ConnectionType.MAGIC.length) {
            byte[] connectionMagicCheck = this.read(ConnectionType.MAGIC.length);
            if (Arrays.equals(ConnectionType.MAGIC, connectionMagicCheck)) {
                UUID uuid = this.readUUID();
                String name = this.readString();
                String language = this.readString();
                String version = this.readString();
                HashMap<String, String> metadata = new HashMap<String, String>();
                int metadataLength = this.readUnsignedByte();
                for (int i = 0; i < metadataLength; i++) {
                    String key = this.readString();
                    String value = this.readString();
                    if (metadata.containsKey(key)) {
                        throw new RakNetException("Duplicate metadata key \"" + key + "\"");
                    }
                    metadata.put(key, value);
                }
                return new ConnectionType(uuid, name, language, version, metadata);
            }
        }
        return ConnectionType.VANILLA;
    }

    /**
     * Writes the magic sequence to the packet.
     * 
     * @return the packet.
     */
    public final RakNetPacket writeMagic() {
        this.write(MAGIC);
        return this;
    }

    /**
     * Writes a {@link ConnectionType} to the packet.
     * 
     * @param connectionType
     *            the connection type, a <code>null</code> value will have
     *            {@link ConnectionType#JRAKNET} connection type be used
     *            instead.
     * @return the packet.
     * @throws RakNetException
     *             if there are too many values in the metadata.
     */
    public final Packet writeConnectionType(ConnectionType connectionType) throws RakNetException {
        connectionType = (connectionType != null ? connectionType : ConnectionType.JRAKNET);
        this.write(ConnectionType.MAGIC);
        this.writeUUID(connectionType.getUUID());
        this.writeString(connectionType.getName());
        this.writeString(connectionType.getLanguage());
        this.writeString(connectionType.getVersion());
        if (connectionType.getMetaData().size() > ConnectionType.MAX_METADATA_VALUES) {
            throw new RakNetException("Too many metadata values");
        }
        this.writeUnsignedByte(connectionType.getMetaData().size());
        for (Entry<String, String> metadataEntry : connectionType.getMetaData().entrySet()) {
            this.writeString(metadataEntry.getKey());
            this.writeString(metadataEntry.getValue());
        }
        return this;
    }

    /**
     * Writes the {@link ConnectionType#JRAKNET} connection type to the packet.
     * 
     * @return the packet.
     * @throws RuntimeException
     *             if a <code>RakNetException</code> is caught despite the fact
     *             that this method should never throw an error in the first
     *             place.
     */
    public final Packet writeConnectionType() throws RuntimeException {
        try {
            return this.writeConnectionType(null);
        } catch (RakNetException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Returns whether or not encoding is supported. If encoding is not
     * supported, calling {@link #encode()} will yield an
     * <code>UnsupportedOperationException</code>.
     * 
     * @return <code>true</code> if encoding is supported, <code>false</code>
     *         otherwise.
     */
    public final boolean supportsEncoding() {
        return this.supportsEncoding;
    }

    /**
     * Encodes the packet.
     * 
     * @throws UnsupportedOperationException
     *             if encoding the packet is not supported.
     */
    public void encode() throws UnsupportedOperationException {
        throw new UnsupportedOperationException("Encoding not supported");
    }

    /**
     * Returns whether or not decoding is supported. If decoding is not
     * supported, calling {@link #decode()} will yield an
     * <code>UnsupportedOperationException</code>.
     * 
     * @return <code>true</code> if decoding is supported, <code>false</code>
     *         otherwise.
     */
    public final boolean supportsDecoding() {
        return this.supportsDecoding;
    }

    /**
     * Decodes the packet.
     * 
     * @throws UnsupportedOperationException
     *             if decoding the packet is not supported.
     */
    public void decode() throws UnsupportedOperationException {
        throw new UnsupportedOperationException("Decoding not supported");
    }

    /**
     * Updates the buffer.
     * 
     * @param buffer
     *            the buffer to read from and write to, a <code>null</code>
     *            value will have a new buffer be used instead.
     * @param updateId
     *            <code>true</code> if the ID should be updated,
     *            <code>false</code> otherwise.
     * @return the packet.
     * @throws IndexOutOfBoundsException
     *             if <code>updateId</code> is <code>true</code> and the new
     *             buffer has less than <code>1</code> readable
     *             <code>byte</code>.
     * @see #setBuffer(ByteBuf)
     */
    public final RakNetPacket setBuffer(ByteBuf buffer, boolean updateId) throws IndexOutOfBoundsException {
        super.setBuffer(buffer);
        if (updateId == true) {
            this.id = this.readUnsignedByte();
        }
        return this;
    }

    /**
     * Updates the buffer.
     * 
     * @param datagram
     *            the {@link DatagramPacket} whose buffer to read from and write
     *            to.
     * @param updateId
     *            <code>true</code> if the ID should be updated,
     *            <code>false</code> otherwise.
     * @return the packet.
     * @throws NullPointerException
     *             if the <code>datagram</code> packet is <code>null</code>.
     * @throws IndexOutOfBoundsException
     *             if <code>updateId</code> is <code>true</code> and the new
     *             buffer has less than <code>1</code> readable
     *             <code>byte</code>.
     * @see #setBuffer(DatagramPacket)
     */
    public final RakNetPacket setBuffer(DatagramPacket datagram, boolean updateId)
            throws NullPointerException, IndexOutOfBoundsException {
        if (datagram == null) {
            throw new NullPointerException("Datagram packet cannot be null");
        }
        return this.setBuffer(datagram.content(), updateId);
    }

    /**
     * Updates the buffer.
     * 
     * @param data
     *            the <code>byte[]</code> to create the new buffer from.
     * @param updateId
     *            <code>true</code> if the ID should be updated,
     *            <code>false</code> otherwise.
     * @return the packet.
     * @throws NullPointerException
     *             if the <code>data</code> is <code>null</code>.
     * @throws IndexOutOfBoundsException
     *             if <code>updateId</code> is <code>true</code> and the new
     *             buffer has less than <code>1</code> readable
     *             <code>byte</code>.
     * @see #setBuffer(byte[])
     */
    public final RakNetPacket setBuffer(byte[] data, boolean updateId)
            throws NullPointerException, IndexOutOfBoundsException {
        return this.setBuffer(Unpooled.copiedBuffer(data), updateId);
    }

    /**
     * Updates the buffer.
     * 
     * @param packet
     *            the packet whose buffer to copy to read from and write to.
     * @param updateId
     *            <code>true</code> if the ID should be updated,
     *            <code>false</code> otherwise.
     * @return the packet.
     * @throws IndexOutOfBoundsException
     *             if <code>updateId</code> is <code>true</code> and the new
     *             buffer has less than <code>1</code> readable
     *             <code>byte</code>.
     * @see #setBuffer(Packet)
     */
    public final RakNetPacket setBuffer(Packet packet, boolean updateId)
            throws NullPointerException, IndexOutOfBoundsException {
        return this.setBuffer(packet.copy(), updateId);
    }

    /**
     * Flips the packet.
     * 
     * @param updateId
     *            <code>true</code> if ID should be updated, <code>false</code>
     *            otherwise.
     * @return the packet.
     * @throws IndexOutOfBoundsException
     *             if <code>updateId</code> is <code>true</code> and the buffer
     *             has less than <code>1</code> readable <code>byte</code>.
     * @see #flip()
     */
    public final RakNetPacket flip(boolean updateId) throws IndexOutOfBoundsException {
        super.flip();
        if (updateId == true) {
            this.id = this.readUnsignedByte();
        }
        return this;
    }

    /**
     * {@inheritDoc} After the packet has been flipped, an unsigined
     * <code>byte</code> will be read to get the ID.
     * 
     * @throws IndexOutOfBoundsException
     *             if the buffer has less than <code>1</code> readable
     *             <code>byte</code>.
     */
    @Override
    public Packet flip() throws IndexOutOfBoundsException {
        return this.flip(true);
    }

    @Override
    public String toString() {
        return "RakNetPacket [id=" + id + ", size()=" + size() + ", remaining()=" + remaining() + "]";
    }

}