//
        public void SendVoicesInfo(IEnumerable <LocalVoice> voices, int channelId, int targetPlayerId)
        {
            object content = voiceClient.buildVoicesInfo(voices, true);

            var sendOpt = new SendOptions()
            {
                Reliability = true,
                Channel     = (byte)channelId
            };

            var opt = new LoadBalancing.RaiseEventOptions();

            if (targetPlayerId != 0)
            {
                opt.TargetActors = new int[] { targetPlayerId };
            }
            lock (sendLock)
            {
                this.OpRaiseEvent(VoiceEventCode.GetCode(channelId), content, opt, sendOpt);
            }

            if (targetPlayerId == 0) // send debug echo infos to myself if broadcast requested
            {
                SendDebugEchoVoicesInfo(channelId);
            }
        }
        private void onEventActionVoiceClient(EventData ev)
        {
            byte channel;

            // check for voice event first
            if (VoiceEventCode.TryGetChannelID(ev.Code, this.LoadBalancingPeer.ChannelCount, out channel))
            {
                // Payloads are arrays. If first array element is 0 than next is event subcode. Otherwise, the event is data frame with voiceId in 1st element.
                this.voiceClient.onVoiceEvent(ev[(byte)LoadBalancing.ParameterCode.CustomEventContent], channel, (int)ev[LoadBalancing.ParameterCode.ActorNr], this.LocalPlayer.ActorNumber);
            }
            else
            {
                int playerId;
                switch (ev.Code)
                {
                case (byte)LoadBalancing.EventCode.Join:
                    playerId = (int)ev[LoadBalancing.ParameterCode.ActorNr];
                    if (playerId == this.LocalPlayer.ActorNumber)
                    {
                    }
                    else
                    {
                        this.voiceClient.sendVoicesInfo(playerId);    // send to new joined only
                    }
                    break;

                case (byte)LoadBalancing.EventCode.Leave:
                {
                    playerId = (int)ev[LoadBalancing.ParameterCode.ActorNr];
                    if (playerId == this.LocalPlayer.ActorNumber)
                    {
                        this.voiceClient.clearRemoteVoices();
                    }
                    else
                    {
                        onPlayerLeave(playerId);
                    }
                }
                break;
                }
            }
        }
        public void SendFrame(ArraySegment <byte> data, byte evNumber, byte voiceId, int channelId, LocalVoice localVoice)
        {
            object[] content = new object[] { voiceId, evNumber, data };

            var sendOpt = new SendOptions()
            {
                Reliability = localVoice.Reliable,
                Channel     = (byte)channelId,
                Encrypt     = localVoice.Encrypt
            };

            var opt = new LoadBalancing.RaiseEventOptions();

            if (localVoice.DebugEchoMode)
            {
                opt.Receivers = LoadBalancing.ReceiverGroup.All;
            }
            opt.InterestGroup = localVoice.Group;
            lock (sendLock)
            {
                this.OpRaiseEvent((byte)VoiceEventCode.GetCode(channelId), content, opt, sendOpt);
            }
            this.LoadBalancingPeer.SendOutgoingCommands();
        }
        public void SendVoiceRemove(LocalVoice voice, int channelId, int targetPlayerId)
        {
            object content = voiceClient.buildVoiceRemoveMessage(voice);
            var    sendOpt = new SendOptions()
            {
                Reliability = true,
                Channel     = (byte)channelId
            };

            var opt = new LoadBalancing.RaiseEventOptions();

            if (targetPlayerId != 0)
            {
                opt.TargetActors = new int[] { targetPlayerId };
            }
            lock (sendLock)
            {
                if (voice.DebugEchoMode)
                {
                    opt.Receivers = ReceiverGroup.All;
                }
                this.OpRaiseEvent(VoiceEventCode.GetCode(channelId), content, opt, sendOpt);
            }
        }