private static void HandleMessage(EndiannessBinaryReader message)
        {
            // TODO fix provisional logics.
            //if (!_roomJoining || !_roomJoined) return;

            var headerSize  = Marshal.SizeOf(typeof(Header));
            var headerBytes = message.ReadBytes(headerSize);

            OrLog(LogLevel.VeryVerbose, "raw header:" + BitConverter.ToString(headerBytes));
            var    ptr = Marshal.AllocCoTaskMem(headerSize);
            var    gch = GCHandle.Alloc(headerBytes, GCHandleType.Pinned);
            Header header;

            try
            {
                header = Marshal.PtrToStructure <Header>(gch.AddrOfPinnedObject());
            }
            catch (Exception e)
            {
                OrLogError(LogLevel.Info, "handle error: " + e.Message);
                OrLogError(LogLevel.Verbose, "stacktrace: " + e.StackTrace);
                gch.Free();

                return;
            }
            gch.Free();

            PlayerId[] destsPids = new PlayerId[header.DestLen];
            byte[]     content   = null;
            if (header.DestLen > 0)
            {
                var destsBytes = message.ReadBytes(sizeof(PlayerId) * header.DestLen);
                Buffer.BlockCopy(destsBytes, 0, destsPids, 0, sizeof(PlayerId) * header.DestLen);
            }

            // TODO fix provisional logics.
            if ((RelayCode)header.RelayCode != RelayCode.JOIN && !(_roomJoined || _PropertiesInitializing))
            {
                return;
            }

            switch ((RelayCode)header.RelayCode)
            {
            case RelayCode.JOIN:
                var alignmentLen = (UInt16)0;
                var alignment    = new byte[] { };
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.JOIN");
                var assginPid = (PlayerId)message.ReadUInt16();
                OrLog(LogLevel.Verbose, "read bytes assginPid:" + assginPid.ToString());
                var masterPid = (PlayerId)message.ReadUInt16();
                OrLog(LogLevel.Verbose, "read bytes masterPid:" + masterPid.ToString());
                var seedLen = message.ReadUInt16();
                OrLog(LogLevel.Verbose, "read bytes seedLen:" + seedLen.ToString());
                var nameLen = message.ReadUInt16();
                OrLog(LogLevel.Verbose, "read bytes nameLen:" + nameLen.ToString());

                var joinSeedByte = message.ReadBytes(seedLen);

                if (seedLen > 0)
                {
                    alignmentLen = (UInt16)(seedLen % 4);
                    if (alignmentLen > 0)
                    {
                        message.ReadBytes(alignmentLen);
                    }
                }
                byte[] nameBytes = null;
                if (nameLen > 0)
                {
                    nameBytes = new byte[nameLen];
                    nameBytes = message.ReadBytes(nameLen);
                }

                OrLog(LogLevel.Verbose, "joinGuid Compare:" + BitConverter.ToString(joinSeedByte) + " : " + BitConverter.ToString(dealerListener.JoinGuid));
                if (BitConverter.ToString(joinSeedByte) != BitConverter.ToString(dealerListener.JoinGuid) && _roomJoined)
                {
                    var otherPlayer = new UserSession(header.SrcPid, header.SrcOid, false, header.SrcPid == masterPid);
                    if (nameLen > 0)
                    {
                        OrLog(LogLevel.Verbose, "receive name bytes: " + BitConverter.ToString(nameBytes));
                        otherPlayer.NickName = Encoding.UTF8.GetString(nameBytes);    // TODO use Unicode(UTF-16) here?
                    }
                    if (header.SrcPid == masterPid)
                    {
                        _masterClient = otherPlayer;
                    }

                    _players.Add(otherPlayer);
                    OrLog(LogLevel.Verbose, "OnOpenRelayPlayerConnectedCall :" + otherPlayer.ID.ToString());
                    OnOpenRelayPlayerConnectedCall(otherPlayer);
                }

                break;

            case RelayCode.LEAVE:
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.LEAVE");
                var newMasterPid = (PlayerId)message.ReadUInt16();
                if (header.SrcPid == Player.ID)
                {
                    OrLog(LogLevel.Verbose, "player left :" + header.SrcPid.ToString());
                    _leaveComplete = true;
                }
                else
                {
                    OrLog(LogLevel.Verbose, "other player left :" + header.SrcPid.ToString());
                    UserSession otherPlayer = null;
                    foreach (var p in _players)
                    {
                        if ((PlayerId)p.ID == header.SrcPid)
                        {
                            otherPlayer = p;
                            _players.Remove(otherPlayer);
                            break;
                        }
                    }
                    if (newMasterPid > 0)
                    {
                        _masterClient.IsMasterClient = false;
                        foreach (var p in _players)
                        {
                            if (newMasterPid == p.ID)
                            {
                                p.IsMasterClient = true;
                                _masterClient    = p;
                                break;
                            }
                        }
                    }
                    if (otherPlayer != null)
                    {
                        OrLog(LogLevel.Verbose, "player :" + otherPlayer.ID);
                        OnOpenRelayPlayerDisconnectedCall(otherPlayer);
                    }
                }

                break;

            case RelayCode.RELAY:
                if (!_roomJoined)
                {
                    return;
                }
                OrLog(LogLevel.VeryVerbose, "HandleMessage RelayCode.RELAY");
                switch ((DestinationCode)header.DestCode)
                {
                case DestinationCode.StrictBroadcast:

                    break;

                case DestinationCode.Broadcast:
                    if (header.SrcPid == _player.ID)
                    {
                        return;                                     //ignore
                    }
                    break;

                case DestinationCode.Exclude:
                    if (destsPids.Contains <PlayerId>((PlayerId)_player.ID))
                    {
                        return;                                                            //ignore
                    }
                    // exclude case
                    break;

                case DestinationCode.Include:
                    if (!destsPids.Contains <PlayerId>((PlayerId)_player.ID))
                    {
                        return;                                                             //ignore
                    }
                    // include case
                    break;

                case DestinationCode.MasterOnly:
                    if (isMasterClient)
                    {
                        break;
                    }

                    return;

                default:

                    return;        //default is ignore
                }
                content = message.ReadBytes(header.ContentLen);
                OrLog(LogLevel.Verbose, "OnEventCall from:" + header.SrcPid.ToString() + " - " + header.SrcOid.ToString() + " DestCode:" + header.DestCode.ToString() + " destsPids:" + destsPids.Length.ToString());
                if (_players.Any(x => x.ID == header.SrcPid))
                {
                    OnSyncCall((byte)header.ContentCode, content, header.SrcPid, header.SrcOid);
                }
                else
                {
                    OrLog(LogLevel.Verbose, "PlayerId: " + header.SrcPid + " addn't yet.");
                }

                break;

            case RelayCode.RELAY_STREAM:
                if (!_roomJoined)
                {
                    return;
                }
                OrLog(LogLevel.VeryVerbose, "HandleMessage RelayCode.RELAY_STREAM");

                switch ((DestinationCode)header.DestCode)
                {
                case DestinationCode.StrictBroadcast:

                    break;

                case DestinationCode.Broadcast:
                    if (header.SrcPid == _player.ID)
                    {
                        return;                                     //ignore
                    }
                    break;

                case DestinationCode.Exclude:
                    if (header.SrcPid == _player.ID)
                    {
                        return;                                     //ignore
                    }
                    if (destsPids.Contains <PlayerId>((PlayerId)_player.ID))
                    {
                        return;                                                            //ignore
                    }
                    // exclude case
                    break;

                case DestinationCode.Include:
                    if (header.SrcPid == _player.ID)
                    {
                        return;                                     //ignore
                    }
                    if (!destsPids.Contains <PlayerId>((PlayerId)_player.ID))
                    {
                        return;                                                             //ignore
                    }
                    // include case
                    break;

                case DestinationCode.MasterOnly:
                    if (isMasterClient)
                    {
                        break;
                    }

                    return;

                default:

                    return;        //default is ignore
                }
                content = message.ReadBytes(header.ContentLen);
                OrLog(LogLevel.VeryVerbose, "OnSyncVoiceCall from:" + header.SrcPid.ToString() + " - " + header.SrcOid.ToString() + " DestCode:" + header.DestCode.ToString() + " destsPids:" + destsPids.Length.ToString());
                //if (_player != null && _players != null && _players.Count > 0 && _players.Any(x => x.ID == header.SrcPid)) OnSyncVoiceCall((byte)header.ContentCode, content, header.SrcPid, header.SrcOid);
                //else OrLog(LogLevel.Verbose, "PlayerId: " + header.SrcPid + " addn't yet.");
                OnSyncStreamCall((byte)header.ContentCode, content, header.SrcPid, header.SrcOid);

                break;

            case RelayCode.UNITY_CDK_RELAY:
                if (!_roomJoined)
                {
                    return;
                }
                OrLog(LogLevel.VeryVerbose, "HandleMessage RelayCode.UNITY_CDK_RELAY");
                switch ((DestinationCode)header.DestCode)
                {
                case DestinationCode.StrictBroadcast:

                    break;

                case DestinationCode.Broadcast:
                    if (header.SrcPid == _player.ID)
                    {
                        return;                                     //ignore
                    }
                    break;

                case DestinationCode.Exclude:
                    if (destsPids.Contains <PlayerId>((PlayerId)_player.ID))
                    {
                        return;                                                            //ignore
                    }
                    // exclude case
                    break;

                case DestinationCode.Include:
                    if (!destsPids.Contains <PlayerId>((PlayerId)_player.ID))
                    {
                        return;                                                             //ignore
                    }
                    // include case
                    break;

                case DestinationCode.MasterOnly:
                    if (isMasterClient)
                    {
                        break;
                    }

                    return;

                default:

                    return;        //default is ignore
                }
                content = message.ReadBytes(header.ContentLen);
                OrLog(LogLevel.Verbose, "OnEventCall from:" + header.SrcPid.ToString() + " - " + header.SrcOid.ToString() + " DestCode:" + header.DestCode.ToString() + " destsPids:" + destsPids.Length.ToString());
                if (_players.Any(x => x.ID == header.SrcPid))
                {
                    OnSyncPlatformCall((byte)header.ContentCode, content, header.SrcPid, header.SrcOid);
                }
                else
                {
                    OrLog(LogLevel.Verbose, "PlayerId: " + header.SrcPid + " addn't yet.");
                }

                break;

            case RelayCode.TIMEOUT:
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.TIMEOUT");
                //timeout leave

                break;

            case RelayCode.REJOIN:
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.REJOIN");
                //re join

                break;

            case RelayCode.SET_LEGACY_MAP:
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.SET_LEGACY_MAP");
                var keysBytesLen = message.ReadUInt16();
                alignmentLen = (UInt16)(keysBytesLen % 4);
                var contentBytesLen = message.ReadUInt16();
                var keysBytes       = message.ReadBytes(keysBytesLen);
                if (alignmentLen > 0)
                {
                    message.ReadBytes(alignmentLen);
                }
                content = message.ReadBytes(contentBytesLen);
                UpdateHashtable(content, keysBytes, _room.Properties);
                OnOpenRelayRoomPropertiesChangedCall(_room.Properties);
                if (_PropertiesInitializing)
                {
                    _PropertiesInitializing = false;
                    _PropertiesReady        = true;
                }

                break;

            case RelayCode.GET_LEGACY_MAP:
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.GET_LEGACY_MAP");
                if (header.ContentLen > 0)
                {
                    content = message.ReadBytes(header.ContentLen);
                    UpdateHashtable(content, null, _room.Properties);
                }
                if (_PropertiesInitializing)
                {
                    _PropertiesInitializing = false;
                    _PropertiesReady        = true;
                }

                break;

            case RelayCode.GET_USERS:
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.GET_USERS");
                //get users responce

                break;

            case RelayCode.SET_MASTER:
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.SET_MASTER");
                var setMasterPid = (PlayerId)message.ReadUInt16();
                _masterClient.IsMasterClient = false;
                if (setMasterPid == Player.ID)
                {
                    _player.IsMasterClient = true;
                    _masterClient          = _player;
                }
                else
                {
                    foreach (var p in _players)
                    {
                        if (setMasterPid == p.ID)
                        {
                            p.IsMasterClient = true;
                            _masterClient    = p;
                            break;
                        }
                    }
                }

                break;

            case RelayCode.GET_MASTER:
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.GET_MASTER");
                var getMasterPid = (PlayerId)message.ReadUInt16();
                _masterClient.IsMasterClient = false;
                foreach (var p in _players)
                {
                    if (getMasterPid == p.ID)
                    {
                        p.IsMasterClient = true;
                        _masterClient    = p;
                        break;
                    }
                }

                break;

            case RelayCode.GET_SERVER_TIMESTAMP:
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.GET_SERVER_TIMESTAMP");
                //get server timestamp responce
                var timestamp = (UInt16)message.ReadUInt16();
                // TODO set server timestamp logic.

                break;

            case RelayCode.RELAY_LATEST:
                if (!_roomJoined)
                {
                    return;
                }
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.RELAY_LATEST");

                content = message.ReadBytes(header.ContentLen);
                OrLog(LogLevel.Verbose, "OnEventCall from:" + header.SrcPid.ToString() + " - " + header.SrcOid.ToString() + " DestCode:" + header.DestCode.ToString() + " destsPids:" + destsPids.Length.ToString());
                if (_players.Any(x => x.ID == header.SrcPid))
                {
                    OnSyncLatestCall((byte)header.ContentCode, content, header.SrcPid, header.SrcOid);
                }
                else
                {
                    OrLog(LogLevel.Verbose, "PlayerId: " + header.SrcPid + " addn't yet.");
                }

                break;

            case RelayCode.GET_LATEST:
                if (!_roomJoined || header.SrcPid != _player.ID)
                {
                    return;
                }
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.GET_LATEST");

                content = message.ReadBytes(header.ContentLen);
                OrLog(LogLevel.Verbose, "OnEventCall from:" + header.SrcPid.ToString() + " - " + header.SrcOid.ToString() + " DestCode:" + header.DestCode.ToString() + " destsPids:" + destsPids.Length.ToString());
                //if (_player.ID == header.SrcPid) OnRelayEventCall((byte)header.ContentCode, content, header.SrcPid, header.SrcOid);

                break;

            case RelayCode.UNITY_CDK_RELAY_LATEST:
                if (!_roomJoined)
                {
                    return;
                }
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.UNITY_CDK_RELAY_LATEST");

                content = message.ReadBytes(header.ContentLen);
                OrLog(LogLevel.Verbose, "OnEventCall from:" + header.SrcPid.ToString() + " - " + header.SrcOid.ToString() + " DestCode:" + header.DestCode.ToString() + " destsPids:" + destsPids.Length.ToString());
                if (_players.Any(x => x.ID == header.SrcPid))
                {
                    OnSyncLatestPlatformCall((byte)header.ContentCode, content, header.SrcPid, header.SrcOid);
                }
                else
                {
                    OrLog(LogLevel.Verbose, "PlayerId: " + header.SrcPid + " addn't yet.");
                }

                break;

            case RelayCode.UNITY_CDK_GET_LATEST:
                if (!_roomJoined || header.SrcPid != _player.ID)
                {
                    return;
                }
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.UNITY_CDK_GET_LATEST");

                content = message.ReadBytes(header.ContentLen);
                OrLog(LogLevel.Verbose, "OnEventCall from:" + header.SrcPid.ToString() + " - " + header.SrcOid.ToString() + " DestCode:" + header.DestCode.ToString() + " destsPids:" + destsPids.Length.ToString());
                //if (_player.ID == header.SrcPid) OnRelayEventCall((byte)header.ContentCode, content, header.SrcPid, header.SrcOid);
                //else OrLog(LogLevel.Verbose, "other target PlayerId: " + header.SrcPid);

                break;

            case RelayCode.SET_LOBBY_MAP:
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.SET_LEGACY_MAP");
                content = message.ReadBytes(header.ContentLen);
                //_room.Properties = ToHash(content);
                //OnOpenRelayRoomPropertiesChangedCall(ToHash(content));
                //_PropertiesInitialized = true;

                break;

            case RelayCode.GET_LOBBY_MAP:
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.GET_LEGACY_MAP");
                content = message.ReadBytes(header.ContentLen);
                //_room.Properties = ToHash(content);
                //_PropertiesInitialized = true;

                break;

            case RelayCode.SET_MASK:
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.SET_MASK");

                break;

            case RelayCode.GET_MASK:
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.GET_MASK");

                break;

            case RelayCode.REPLAY_JOIN:
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.REPLAY_JOIN");

                break;

            case RelayCode.LOAD_PLAYER:
                OrLog(LogLevel.Verbose, "HandleMessage RelayCode.LOAD_PLAYER");
                var total           = message.ReadByte();
                var position        = message.ReadByte();
                var nameContainsMax = (UInt16)message.ReadUInt16();
                message.ReadByte();     // 4byte alignment
                var names = new List <string>();
                for (int count = 0; count < nameContainsMax; count++)
                {
                    var nameSize = (UInt16)message.ReadUInt16();
                    var name     = message.ReadBytes(nameSize);
                    names.Add(Encoding.UTF8.GetString(name));
                    alignmentLen = (UInt16)(nameSize % 4);
                    if (alignmentLen > 0)
                    {
                        message.ReadBytes(alignmentLen);
                    }
                }

                break;

            default:
                OrLogError(LogLevel.Verbose, "HandleMessage error: RelayCode not match, invalid case:" + (RelayCode)header.RelayCode);

                break;
            }
        }