/// <summary>
    /// Send an event with custom code/type and any content to the other players in the same room.
    /// </summary>
    /// <remarks>This override explicitly uses another parameter order to not mix it up with the implementation for Hashtable only.</remarks>
    /// <param name="eventCode">Identifies this type of event (and the content). Your game's event codes can start with 0.</param>
    /// <param name="sendReliable">If this event has to arrive reliably (potentially repeated if it's lost).</param>
    /// <param name="customEventContent">Any serializable datatype (including Hashtable like the other OpRaiseEvent overloads).</param>
    /// <param name="channelId">Command sequence in which this command belongs. Must be less than value of ChannelCount property. Default: 0.</param>
    /// <param name="cache">Affects how the server will treat the event caching-wise. Can cache events for players joining later on or remove previously cached events. Default: DoNotCache.</param>
    /// <param name="targetActors">List of ActorNumbers (in this room) to send the event to. Overrides caching. Default: null.</param>
    /// <param name="receivers">Defines a target-player group. Default: Others.</param>
    /// <param name="interestGroup">Defines to which interest group the event is sent. Players can subscribe or unsibscribe to groups. Group 0 is always sent to all. Default: 0.</param>
    /// <returns>If operation could be enqueued for sending. Sent when calling: Service or SendOutgoingCommands.</returns>
    public virtual bool OpRaiseEvent(byte eventCode, bool sendReliable, object customEventContent, byte channelId, EventCaching cache, int[] targetActors, ReceiverGroup receivers, byte interestGroup)
    {
        Dictionary <byte, object> opParameters = new Dictionary <byte, object>();

        opParameters[(byte)LiteOpKey.Code] = (byte)eventCode;

        if (customEventContent != null)
        {
            opParameters[(byte)LiteOpKey.Data] = customEventContent;
        }
        if (cache != EventCaching.DoNotCache)
        {
            opParameters[(byte)LiteOpKey.Cache] = (byte)cache;
        }
        if (receivers != ReceiverGroup.Others)
        {
            opParameters[(byte)LiteOpKey.ReceiverGroup] = (byte)receivers;
        }
        if (interestGroup != 0)
        {
            opParameters[(byte)LiteOpKey.Group] = (byte)interestGroup;
        }
        if (targetActors != null)
        {
            opParameters[(byte)LiteOpKey.ActorList] = targetActors;
        }

        if (PhotonHandler.IsShowLog())
        {
            PhotonHandler.ShowLog("OpRaiseEvent()");
        }
        return(this.OpCustom((byte)LiteOpCode.RaiseEvent, opParameters, sendReliable, channelId, false));
    }
    protected bool OpSetPropertiesOfActor(int actorNr, Hashtable actorProperties, bool broadcast, byte channelId)
    {
        if (this.DebugOut >= DebugLevel.INFO)
        {
            this.Listener.DebugReturn(DebugLevel.INFO, "OpSetPropertiesOfActor()");
        }

        if (actorNr <= 0 || actorProperties == null)
        {
            if (this.DebugOut >= DebugLevel.INFO)
            {
                this.Listener.DebugReturn(DebugLevel.INFO, "OpSetPropertiesOfActor not sent. ActorNr must be > 0 and actorProperties != null.");
            }
            return(false);
        }

        Dictionary <byte, object> opParameters = new Dictionary <byte, object>();

        opParameters.Add(ParameterCode.Properties, actorProperties);
        opParameters.Add(ParameterCode.ActorNr, actorNr);
        if (broadcast)
        {
            opParameters.Add(ParameterCode.Broadcast, broadcast);
        }

        if (PhotonHandler.IsShowLog())
        {
            PhotonHandler.ShowLog("OpSetPropertiesOfActor()");
        }

        return(this.OpCustom((byte)OperationCode.SetProperties, opParameters, broadcast, channelId));
    }
    /// <summary>
    /// Used in a room to raise (send) an event to the other players.
    /// Multiple overloads expose different parameters to this frequently used operation.
    /// </summary>
    /// <param name="eventCode">Code for this "type" of event (use a code per "meaning" or content).</param>
    /// <param name="evData">Data to send. Hashtable that contains key-values of Photon serializable datatypes.</param>
    /// <param name="sendReliable">Use false if the event is replaced by a newer rapidly. Reliable events add overhead and add lag when repeated.</param>
    /// <param name="channelId">The "channel" to which this event should belong. Per channel, the sequence is kept in order.</param>
    /// <param name="targetActors">Defines the target players who should receive the event (use only for small target groups).</param>
    /// <param name="cache">Use EventCaching options to store this event for players who join.</param>
    /// <returns>If the operation could be sent (has to be connected).</returns>
    public virtual bool OpRaiseEvent(byte eventCode, Hashtable evData, bool sendReliable, byte channelId, int[] targetActors, EventCaching cache)
    {
        if (this.DebugOut >= DebugLevel.INFO)
        {
            this.Listener.DebugReturn(DebugLevel.INFO, "OpRaiseEvent()");
        }

        Dictionary <byte, object> opParameters = new Dictionary <byte, object>();

        opParameters[ParameterCode.Data] = evData;
        opParameters[ParameterCode.Code] = (byte)eventCode;

        if (cache != EventCaching.DoNotCache)
        {
            opParameters[ParameterCode.Cache] = (byte)cache;
        }

        if (targetActors != null)
        {
            opParameters[ParameterCode.ActorList] = targetActors;
        }

        if (PhotonHandler.IsShowLog())
        {
            PhotonHandler.ShowLog("OpRaiseEvent2()");
        }

        return(this.OpCustom(OperationCode.RaiseEvent, opParameters, sendReliable, channelId));
    }
    /// <summary>
    /// Used in a room to raise (send) an event to the other players.
    /// Multiple overloads expose different parameters to this frequently used operation.
    /// </summary>
    /// <param name="eventCode">Code for this "type" of event (use a code per "meaning" or content).</param>
    /// <param name="evData">Data to send. Hashtable that contains key-values of Photon serializable datatypes.</param>
    /// <param name="sendReliable">Use false if the event is replaced by a newer rapidly. Reliable events add overhead and add lag when repeated.</param>
    /// <param name="channelId">The "channel" to which this event should belong. Per channel, the sequence is kept in order.</param>
    /// <param name="cache">Use EventCaching options to store this event for players who join.</param>
    /// <param name="receivers">ReceiverGroup defines to which group of players the event is passed on.</param>
    /// <returns>If the operation could be sent (has to be connected).</returns>
    public virtual bool OpRaiseEvent(byte eventCode, Hashtable evData, bool sendReliable, byte channelId, EventCaching cache, ReceiverGroup receivers)
    {
        if (this.DebugOut >= DebugLevel.INFO)
        {
            this.Listener.DebugReturn(DebugLevel.INFO, "OpRaiseEvent()");
        }

        Dictionary <byte, object> opParameters = new Dictionary <byte, object>();

        opParameters[ParameterCode.Data] = evData;
        opParameters[ParameterCode.Code] = (byte)eventCode;

        if (receivers != ReceiverGroup.Others)
        {
            opParameters[ParameterCode.ReceiverGroup] = (byte)receivers;
        }

        if (cache != EventCaching.DoNotCache)
        {
            opParameters[ParameterCode.Cache] = (byte)cache;
        }

        if (PhotonHandler.IsShowLog())
        {
            PhotonHandler.ShowLog("OpRaiseEvent3()");
        }

        return(this.OpCustom((byte)OperationCode.RaiseEvent, opParameters, sendReliable, channelId));
    }
Beispiel #5
0
    /// <summary>Called by Unity on start of the application and does a setup the PhotonView.</summary>
    public void Awake()
    {
        // registration might be too late when some script (on this GO) searches this view BUT GetPhotonView() can search ALL in that case
        if (PhotonHandler.IsShowLog())
        {
            Debug.Log("johnlogref Awake");
        }
        PhotonNetwork.networkingPeer.RegisterPhotonView(this);

        this.instantiationDataField = PhotonNetwork.networkingPeer.FetchInstantiationData(this.instantiationId);
        this.didAwake = true;
    }
Beispiel #6
0
    /// <summary>
    /// ͨÐÐÖ¤µÇ¼
    /// </summary>
    public void PlayerLogin()
    {
        if (PhotonHandler.IsShowLog())
        {
            PhotonHandler.ShowLog(string.Format("PlayerLogin:{0}", txtName.text.Trim()));
        }


        //YuanUnityPhoton.GetYuanUnityPhotonInstantiate ().SetPlayerBehavior (yuan.YuanPhoton.ConsumptionType.GameSchedule,((int)GameScheduleType.Login).ToString (),SystemInfo.deviceUniqueIdentifier);


        //YuanUnityPhoton.GetYuanUnityPhotonInstantiate().SetPlayerBjoehavior (yuan.YuanPhoton.ConsumptionType.GameSchedule,((int)GameScheduleType.Login).ToString (),SystemInfo.deviceUniqueIdentifier);

        if (PhotonHandler.IsAutoLogin())
        {
            PhotonHandler.ShowLog("autologin mode");
            YuanUnityPhoton.GetYuanUnityPhotonInstantiate().PlayerLogin(PhotonHandler.GetAutoStr(), PhotonHandler.GetAutoPwd(), "ZealmPass", "UserInfo", true);
        }
        else
        {
            //YuanUnityPhoton.GetYuanUnityPhotonInstantiate().PlayerLogin(txtName.text.Trim(), txtPwd.text.Trim(), "ZealmPass", "UserInfo", true);
            StartCoroutine(BeginTimeOut(10, 2, ConnectYuanUnity, () => YuanUnityPhoton.GetYuanUnityPhotonInstantiate().PlayerLogin(txtName.text.Trim(), txtPwd.text.Trim(), "ZealmPass", "UserInfo", true), null));
        }

        //yuan.YuanClass.SwitchListOnlyOne(listMenu, 10, true, true);
        passID = txtName.text.Trim();



        //if (YuanUnityPhoton.GetYuanUnityPhotonInstantiate().ServerConnected == true)
        //{
        //    if (YuanUnityPhoton.GetYuanUnityPhotonInstantiate() != null)
        //    {
        //        if (txtName.text.Trim() != "" && txtPwd.text.Trim() != "")
        //        {
        //            YuanUnityPhoton.GetYuanUnityPhotonInstantiate().PlayerLogin(txtName.text.Trim(), txtPwd.text.Trim(), "ZealmPass", "UserInfo", true);
        //            yuan.YuanClass.SwitchListOnlyOne(listMenu, 10, true, true);
        //            passID = txtName.text.Trim();
        //            Invoke("PlayerLoginTimeOut", 20);
        //        }
        //        else
        //        {
        //            lblWarning.text = StaticLoc.Loc.Get(warningNoText);
        //        }
        //
        //    }
        //}
        //else
        //{
        //    lblWarning.text =StaticLoc.Loc.Get("info322")+ "";
        //}
    }
    /// <summary>
    /// Leaves the lobby on the Master Server.
    /// This is an async request which triggers a OnOperationResponse() call.
    /// </summary>
    /// <returns>If the operation could be sent (has to be connected).</returns>
    public virtual bool OpLeaveLobby()
    {
        if (this.DebugOut >= DebugLevel.INFO)
        {
            this.Listener.DebugReturn(DebugLevel.INFO, "OpLeaveLobby()");
        }

        if (PhotonHandler.IsShowLog())
        {
            PhotonHandler.ShowLog("OpLeaveLobby()");
        }

        return(this.OpCustom(OperationCode.LeaveLobby, null, true));
    }
    /// <summary>
    /// Sends this app's appId and appVersion to identify this application server side.
    /// This is an async request which triggers a OnOperationResponse() call.
    /// </summary>
    /// <remarks>
    /// This operation makes use of encryption, if that is established before.
    /// See: EstablishEncryption(). Check encryption with IsEncryptionAvailable.
    /// This operation is allowed only once per connection (multiple calls will have ErrorCode != Ok).
    /// </remarks>
    /// <param name="appId">Your application's name or ID to authenticate. This is assigned by Photon Cloud (webpage).</param>
    /// <param name="appVersion">The client's version (clients with differing client appVersions are separated and players don't meet).</param>
    /// <param name="userId"></param>
    /// <param name="authValues"></param>
    /// <returns>If the operation could be sent (has to be connected).</returns>
    public virtual bool OpAuthenticate(string appId, string appVersion, string userId, AuthenticationValues authValues)
    {
        if (this.DebugOut >= DebugLevel.INFO)
        {
            this.Listener.DebugReturn(DebugLevel.INFO, "OpAuthenticate()");
        }

        Dictionary <byte, object> opParameters = new Dictionary <byte, object>();

        opParameters[ParameterCode.AppVersion]    = appVersion;
        opParameters[ParameterCode.ApplicationId] = appId;

        if (!string.IsNullOrEmpty(userId))
        {
            opParameters[ParameterCode.UserId] = userId;
        }

        if (authValues != null && authValues.AuthType != CustomAuthenticationType.None)
        {
            if (!this.IsEncryptionAvailable)
            {
                this.Listener.DebugReturn(DebugLevel.ERROR, "OpAuthenticate() failed. When you want Custom Authentication encryption is mandatory.");
                return(false);
            }

            opParameters[ParameterCode.ClientAuthenticationType] = (byte)authValues.AuthType;
            if (!string.IsNullOrEmpty(authValues.Secret))
            {
                opParameters[ParameterCode.Secret] = authValues.Secret;
            }
            else
            {
                if (!string.IsNullOrEmpty(authValues.AuthParameters))
                {
                    opParameters[ParameterCode.ClientAuthenticationParams] = authValues.AuthParameters;
                }
                if (authValues.AuthPostData != null)
                {
                    opParameters[ParameterCode.ClientAuthenticationData] = authValues.AuthPostData;
                }
            }
        }
        if (PhotonHandler.IsShowLog())
        {
            PhotonHandler.ShowLog("OpAuthenticate()");
        }
        return(this.OpCustom(OperationCode.Authenticate, opParameters, true, (byte)0, false));      // this.IsEncryptionAvailable);
    }
    /// <summary>
    /// Request the rooms and online status for a list of friends (each client must set a unique username via OpAuthenticate).
    /// </summary>
    /// <remarks>
    /// Used on Master Server to find the rooms played by a selected list of users.
    /// Users identify themselves by using OpAuthenticate with a unique username.
    /// The list of usernames must be fetched from some other source (not provided by Photon).
    ///
    /// The server response includes 2 arrays of info (each index matching a friend from the request):
    /// ParameterCode.FindFriendsResponseOnlineList = bool[] of online states
    /// ParameterCode.FindFriendsResponseRoomIdList = string[] of room names (empty string if not in a room)
    /// </remarks>
    /// <param name="friendsToFind">Array of friend's names (make sure they are unique).</param>
    /// <returns>If the operation could be sent (requires connection).</returns>
    public virtual bool OpFindFriends(string[] friendsToFind)
    {
        Dictionary <byte, object> opParameters = new Dictionary <byte, object>();

        if (friendsToFind != null && friendsToFind.Length > 0)
        {
            opParameters[ParameterCode.FindFriendsRequestList] = friendsToFind;
        }

        if (PhotonHandler.IsShowLog())
        {
            PhotonHandler.ShowLog("OpFindFriends()");
        }

        return(this.OpCustom(OperationCode.FindFriends, opParameters, true));
    }
    /// <summary>
    /// Don't use this method directly, unless you know how to cache and apply customActorProperties.
    /// The PhotonNetwork methods will handle player and room properties for you and call this method.
    /// </summary>
    public virtual bool OpCreateRoom(string gameID, bool isVisible, bool isOpen, byte maxPlayers, bool autoCleanUp, Hashtable customGameProperties, Hashtable customPlayerProperties, string[] customRoomPropertiesForLobby)
    {
        Debug.Log(string.Format("OpCreateRoom:,gameid:{0},max:{1}", gameID, maxPlayers));
        if (this.DebugOut >= DebugLevel.INFO)
        {
            this.Listener.DebugReturn(DebugLevel.INFO, "OpCreateRoom()");
        }

        Hashtable gameProperties = new Hashtable();

        gameProperties[GameProperties.IsOpen]             = isOpen;
        gameProperties[GameProperties.IsVisible]          = isVisible;
        gameProperties[GameProperties.PropsListedInLobby] = customRoomPropertiesForLobby;
        gameProperties.MergeStringKeys(customGameProperties);
        if (maxPlayers > 0)
        {
            gameProperties[GameProperties.MaxPlayers] = maxPlayers;
        }

        Dictionary <byte, object> op = new Dictionary <byte, object>();

        op[ParameterCode.GameProperties] = gameProperties;
        op[ParameterCode.Broadcast]      = true;

        if (customPlayerProperties != null)
        {
            op[ParameterCode.PlayerProperties] = customPlayerProperties;
        }

        if (!string.IsNullOrEmpty(gameID))
        {
            op[ParameterCode.RoomName] = gameID;
        }

        // server's default is 'false', so we actually only need to send this when 'true'
        if (autoCleanUp)
        {
            op[ParameterCode.CleanupCacheOnLeave] = autoCleanUp;
            gameProperties[GameProperties.CleanupCacheOnLeave] = autoCleanUp;
        }
        if (PhotonHandler.IsShowLog())
        {
            PhotonHandler.ShowLog("OpCreateRoom()");
        }
        return(this.OpCustom(OperationCode.CreateGame, op, true));
    }
    /// <summary>
    /// Send your custom data as event to an "interest group" in the current Room.
    /// </summary>
    /// <remarks>
    /// No matter if reliable or not, when an event is sent to a interest Group, some users won't get this data.
    /// Clients can control the groups they are interested in by using OpChangeGroups.
    /// </remarks>
    /// <param name="eventCode">Identifies this type of event (and the content). Your game's event codes can start with 0.</param>
    /// <param name="interestGroup">The ID of the interest group this event goes to (exclusively).</param>
    /// <param name="customEventContent">Custom data you want to send along (use null, if none).</param>
    /// <param name="sendReliable">If this event has to arrive reliably (potentially repeated if it's lost).</param>
    /// <param name="channelId">The "channel" to which this event should belong. Per channel, the sequence is kept in order.</param>
    /// <returns>If operation could be enqueued for sending</returns>
    public virtual bool OpRaiseEvent(byte eventCode, byte interestGroup, Hashtable customEventContent, bool sendReliable, byte channelId)
    {
        Dictionary <byte, object> opParameters = new Dictionary <byte, object>();

        opParameters[(byte)LiteOpKey.Data] = customEventContent;
        opParameters[(byte)LiteOpKey.Code] = (byte)eventCode;
        if (interestGroup != (byte)0)
        {
            opParameters[(byte)LiteOpKey.Group] = (byte)interestGroup;
        }

        if (PhotonHandler.IsShowLog())
        {
            PhotonHandler.ShowLog("OpRaiseEvent1()");
        }

        return(this.OpCustom((byte)LiteOpCode.RaiseEvent, opParameters, sendReliable, channelId));
    }
    public static bool FallbackSendAckThread()
    {
        if (sendThreadShouldRun && PhotonNetwork.networkingPeer != null)
        {
            //

            if (!PhotonHandler.IsSendSensAsk())
            {
                return(sendThreadShouldRun);
            }

            if (PhotonHandler.IsShowLog())
            {
                //PhotonHandler.ShowLog("FallbackSendAckThread");
            }
            PhotonNetwork.networkingPeer.SendAcksOnly();
        }

        return(sendThreadShouldRun);
    }
    public bool OpSetPropertiesOfRoom(Hashtable gameProperties, bool broadcast, byte channelId)
    {
        if (this.DebugOut >= DebugLevel.INFO)
        {
            this.Listener.DebugReturn(DebugLevel.INFO, "OpSetPropertiesOfRoom()");
        }

        Dictionary <byte, object> opParameters = new Dictionary <byte, object>();

        opParameters.Add(ParameterCode.Properties, gameProperties);
        if (broadcast)
        {
            opParameters.Add(ParameterCode.Broadcast, broadcast);
        }
        if (PhotonHandler.IsShowLog())
        {
            PhotonHandler.ShowLog("OpSetPropertiesOfRoom()");
        }
        return(this.OpCustom((byte)OperationCode.SetProperties, opParameters, broadcast, channelId));
    }
    /// <summary>
    /// Operation to join a random, available room. Overloads take additional player properties.
    /// This is an async request which triggers a OnOperationResponse() call.
    /// If all rooms are closed or full, the OperationResponse will have a returnCode of ErrorCode.NoRandomMatchFound.
    /// If successful, the OperationResponse contains a gameserver address and the name of some room.
    /// </summary>
    /// <param name="expectedCustomRoomProperties">Optional. A room will only be joined, if it matches these custom properties (with string keys).</param>
    /// <param name="expectedMaxPlayers">Filters for a particular maxplayer setting. Use 0 to accept any maxPlayer value.</param>
    /// <param name="playerProperties">This player's properties (custom and well known).</param>
    /// <param name="matchingType">Selects one of the available matchmaking algorithms. See MatchmakingMode enum for options.</param>
    /// <returns>If the operation could be sent currently (requires connection).</returns>
    public virtual bool OpJoinRandomRoom(Hashtable expectedCustomRoomProperties, byte expectedMaxPlayers, Hashtable playerProperties, MatchmakingMode matchingType)
    {
        if (this.DebugOut >= DebugLevel.INFO)
        {
            this.Listener.DebugReturn(DebugLevel.INFO, "OpJoinRandomRoom()");
        }

        Hashtable expectedRoomProperties = new Hashtable();

        expectedRoomProperties.MergeStringKeys(expectedCustomRoomProperties);
        if (expectedMaxPlayers > 0)
        {
            expectedRoomProperties[GameProperties.MaxPlayers] = expectedMaxPlayers;
        }

        Dictionary <byte, object> opParameters = new Dictionary <byte, object>();

        if (expectedRoomProperties.Count > 0)
        {
            opParameters[ParameterCode.GameProperties] = expectedRoomProperties;
        }

        if (playerProperties != null && playerProperties.Count > 0)
        {
            opParameters[ParameterCode.PlayerProperties] = playerProperties;
        }

        if (matchingType != MatchmakingMode.FillRoom)
        {
            opParameters[ParameterCode.MatchMakingType] = (byte)matchingType;
        }

        if (PhotonHandler.IsShowLog())
        {
            PhotonHandler.ShowLog("OpJoinRandomRoom()");
        }

        return(this.OpCustom(OperationCode.JoinRandomGame, opParameters, true));
    }
Beispiel #15
0
    void Start()
    {
        PhotonHandler.ReadIsShowLog();
        PhotonHandler.ShowLog("start btnmanager");
        if (PhotonHandler.IsShowLog())
        {
            if (btn != null)
            {
                PhotonHandler.ShowLog("set btn visible");
                btn.localScale = Vector3.one;
            }
        }

        //yuan.YuanClass.SwitchListOnlyOne (listMenu,7,true,true);
        if (Application.loadedLevelName == "Login-1")
        {
            if (isOhterLogin)
            {
                OtherLogin();
            }
            SelectBtnSize();
        }
        yuan.YuanClass.SwitchList(listPlayerInfo, false, false);
    }
    /// <summary>
    /// Joins a room by name and sets this player's properties.
    /// </summary>
    /// <param name="roomName"></param>
    /// <param name="playerProperties"></param>
    /// <param name="createIfNotExists"></param>
    /// <returns>If the operation could be sent (has to be connected).</returns>
    public virtual bool OpJoinRoom(string roomName, Hashtable playerProperties, bool createIfNotExists)
    {
        if (this.DebugOut >= DebugLevel.INFO)
        {
            this.Listener.DebugReturn(DebugLevel.INFO, "OpJoinRoom()");
        }

        if (string.IsNullOrEmpty(roomName))
        {
            this.Listener.DebugReturn(DebugLevel.ERROR, "OpJoinRoom() failed. Please specify a roomname.");
            return(false);
        }

        Dictionary <byte, object> op = new Dictionary <byte, object>();

        op[ParameterCode.RoomName]  = roomName;
        op[ParameterCode.Broadcast] = true;

        if (createIfNotExists)
        {
            op[ParameterCode.CreateIfNotExists] = createIfNotExists;
        }
        if (playerProperties != null)
        {
            op[ParameterCode.PlayerProperties] = playerProperties;
        }
        if (m_LogicObj != null)
        {
            m_LogicObj.SendJoinRoom(roomName);
        }
        if (PhotonHandler.IsShowLog())
        {
            PhotonHandler.ShowLog("OpJoinRoom()");
        }
        return(this.OpCustom(OperationCode.JoinGame, op, true));
    }
    /// <summary>
    /// Operation to handle this client's interest groups (for events in room).
    /// </summary>
    /// <remarks>
    /// Note the difference between passing null and byte[0]:
    ///   null won't add/remove any groups.
    ///   byte[0] will add/remove all (existing) groups.
    /// First, removing groups is executed. This way, you could leave all groups and join only the ones provided.
    /// </remarks>
    /// <param name="groupsToRemove">Groups to remove from interest. Null will not remove any. A byte[0] will remove all.</param>
    /// <param name="groupsToAdd">Groups to add to interest. Null will not add any. A byte[0] will add all current.</param>
    /// <returns></returns>
    public virtual bool OpChangeGroups(byte[] groupsToRemove, byte[] groupsToAdd)
    {
        if (this.DebugOut >= DebugLevel.ALL)
        {
            this.Listener.DebugReturn(DebugLevel.ALL, "OpChangeGroups()");
        }

        Dictionary <byte, object> opParameters = new Dictionary <byte, object>();

        if (groupsToRemove != null)
        {
            opParameters[(byte)LiteOpKey.Remove] = groupsToRemove;
        }
        if (groupsToAdd != null)
        {
            opParameters[(byte)LiteOpKey.Add] = groupsToAdd;
        }

        if (PhotonHandler.IsShowLog())
        {
            PhotonHandler.ShowLog("OpChangeGroups()");
        }
        return(this.OpCustom((byte)LiteOpCode.ChangeGroups, opParameters, true, 0));
    }