Interest Groups Frustration - Operation ChangeGroups (248) Errors | OpChangeGroups(Byte[], Byte[])

Greetings,

I've been hacking at the Photon Interest Groups and Voice implementation for quite some time with constant failures. I did a search on this forum and looked through many messages to try and answer my own question (and general google search). I consider myself technical savvy and advanced (Microsoft certified C# .Net developer) but am not yet SME in Unity/Photon so I would appreciate your serious and specific guided help in answers.

The implementation example/errors is where the first and only logged in user is MasterClient (yes, I am working with multiple players, but this example is for 1 logged in player). After logging in to a room I wait awhile, about 20 or so seconds before attempting to change the Interest Groups. I am also logging to the screen proof that my client PhotonVoiceNetwork.Instance.Client.InRoom? is true. So the test works like this: I (only one player) try to change my Interest Groups in Photon and Voice. I print some Debug information about what my code was doing. I encounter either 1 of these errors but not both:

Error #1

Operation ChangeGroups (248) not allowed on current server (MasterServer)
UnityEngine.Debug:LogError(Object)
Photon.Realtime.LoadBalancingClient:DebugReturn(DebugLevel, String) (at Assets/Photon/PhotonRealtime/Code/LoadBalancingClient.cs:2382)
Photon.Realtime.LoadBalancingClient:CheckIfOpCanBeSent(Byte, ServerConnection, String) (at Assets/Photon/PhotonRealtime/Code/LoadBalancingClient.cs:2305)
Photon.Realtime.LoadBalancingClient:OpChangeGroups(Byte[], Byte[]) (at Assets/Photon/PhotonRealtime/Code/LoadBalancingClient.cs:2047)
PlayerInterestGroup:SetInterestGroup(GameObject, Byte, Boolean) (at ....


Error #2

Operation ChangeGroups (248) not called because client is not connected or not ready yet, client state: ConnectingToGameServer
UnityEngine.Debug:LogError(Object)
Photon.Realtime.LoadBalancingClient:DebugReturn(DebugLevel, String) (at Assets/Photon/PhotonRealtime/Code/LoadBalancingClient.cs:2382)
Photon.Realtime.LoadBalancingClient:CheckIfOpCanBeSent(Byte, ServerConnection, String) (at Assets/Photon/PhotonRealtime/Code/LoadBalancingClient.cs:2313)
Photon.Realtime.LoadBalancingClient:OpChangeGroups(Byte[], Byte[]) (at Assets/Photon/PhotonRealtime/Code/LoadBalancingClient.cs:2047)
PlayerInterestGroup:SetInterestGroup(GameObject, Byte, Boolean) (at


Given that I have proven via the log that PhotonVoiceNetwork.Instance.Client.InRoom? is true , what other conditions, scenarios, etc.. would potentially cause these errors on 1 player attempting to run a Photon.Realtime.LoadBalancingClient:OpChangeGroups(Byte[], Byte[]) command?

Thank you and have a wonderful day,

-Kathleen

Comments

  • JohnTube
    JohnTube ✭✭✭✭✭
    edited September 2020
    Hi @KathleenWest,

    Thank you for choosing Photon and I'm sorry you ran into these issues!

    When you use PhotonVoiceNetwork you use PUN 2 integration of Photon Voice 2 which means there are two LoadBalancingClient being used:

    - one for PUN 2: PhotonNetwork.NetworkingClient which has most if not all its Op calls wrapped in static PhotonNetwork methods
    - one for Photon Voice 2: PhotonVoiceNetwork.Instance.Client

    So I think you are trying to change interest groups for the PUN 2 client (using PhotonNetwork.SetInterestGroups which calls PhotonNetwork.NetworkingClient.OpChangeGroups) but you are checking Photon Voice 2's client state (using PhotonVoiceNetwork.Instance.Client.InRoom instead)! So there is a "client mismatch" here.

    If you want to change Photon Voice 2 interest groups use PhotonVoiceNetwork.Instance.Client.OpChangeGroups.

    If the PhotonVoiceNetwork.Instance.Client.InRoom is true then PhotonVoiceNetwork.Instance.Client.OpChangeGroups will work without errors.

    If you want to change the groups no matter the client state, take a look at DemoProximityVoiceChat, especially ProximityVoiceTrigger implementation and how we cache the groups to add and the groups to remove and we call OpChangeGroups when the client is ready in Update.
  • Thanks for the advice. I will try some of these suggestions.

    I had actually previously tried both routes in my code to try and set the interest groups.

    PhotonNetwork.SetInterestGroups(InterestGroup, enabled);
    and
    player.GetComponent<PhotonVoiceNetwork>().Client.OpChangeGroups(new byte[] { InterestGroup }, new byte[] { group });

    I will comment out the PhotonNetwork code and try it.
  • Hi @JohnTube , Others

    I did do some more dev work and testing trying to get the Photon Interest groups to work, without success. I am hoping someone could help and would be gracious if any comments or ideas of potential trials.

    The objective is for players to be separated into one of two interest groups. We call them groups: Dragon and Totem. Then based on their assignment, the players should only be able do Voice chat conversations with players in their group. So if there are 4 players, each is divided into one of two groups of two players.

    Here is the code I use to assign this on a singleton game object script. that gets called on menu action item to assign all players logged in (via Players enumerable property) to a custom group with minimum of 4 players...

    // Assign each player either to Dragon or Totem Group
    foreach (GameObject player in Players)
    {
    // Send All Even Players
    if (count % 2 == 0)
    {
    player.GetPhotonView().Group = (byte)InterestGroupTypes["Dragon"];

    // Enable the Custom Group
    player.GetComponent<PlayerInterestGroup>().SetInterestGroup(player, (byte)InterestGroupTypes["Dragon"]);
    }
    else
    {
    player.GetPhotonView().Group = (byte)InterestGroupTypes["Totem"];

    // Enable the Custom Group
    player.GetComponent<PlayerInterestGroup>().SetInterestGroup(player, (byte)InterestGroupTypes["Totem"]);
    }

    count++;

    } // end of foreach



    Then in the PlayerInterestGroup script.. that is attached to the player prefab.... which is set



    public void SetInterestGroup(GameObject player, byte group = 0)
    {
    // Group to Remove, Group to Add
    PhotonVoiceNetwork.Instance.Client.OpChangeGroups(new byte[] { InterestGroup }, new byte[] { group });

    // Set the InterestGroup to the new group, this is a Property of type byte
    InterestGroup = group;

    // Set the Global Audio
    PhotonVoiceNetwork.Instance.Client.GlobalInterestGroup = InterestGroup;

    Debug.Log($"Client: {PV.name}, {PV.Owner}");
    Debug.Log("Set the Interest Group to: " + InterestGroup);

    } // end of method

    .....................
    So with this code, the players are still able to talk/hear everyone and are not separated into groups.
    Any ideas?
  • JohnTube
    JohnTube ✭✭✭✭✭
    public void SetInterestGroup(GameObject player, byte group = 0)
    {
    // Group to Remove, Group to Add
    PhotonVoiceNetwork.Instance.Client.OpChangeGroups(new byte[] { InterestGroup }, new byte[] { group });
    
    // Set the InterestGroup to the new group, this is a Property of type byte
    InterestGroup = group;
    
    // Set the Global Audio
    PhotonVoiceNetwork.Instance.Client.GlobalInterestGroup = InterestGroup;
    
    Debug.Log($"Client: {PV.name}, {PV.Owner}");
    Debug.Log("Set the Interest Group to: " + InterestGroup);
    
    } // end of method
    

    player GameObject is not used here or is the code stripped.
    Also group, for some reason is not useful?

    If you use the same group to talk and listen then use GlobalInterestGroup.
    If you use GlobalInterestGroup don't use OpChangeGroups.

    You can't unsubscribe or remove default group 0.

    so if you simplify things you can debug this easily.
    just log on each client PhotonVoiceNetwork.Instance.Client.GlobalInterestGroup and Recorder.InterestGroup and make sure they match and they also match the expected interest group you set Dragon or Totem.

    Photon Voice's interest groups are not coupled or tied to PUN's PhotonView.Group so I won't discuss PhotonView.Group.
  • Thanks I will try these suggestions.
  • Oh yes, thought I should answer your questions about the GameObject player parameter not being used. You're right... it WAS being used/tried previously.. and I just did some massive code rewrites, and forgot to take that out. Was going to clean up code after getting features to work ;)
  • @JohnTube | Others

    Once again, thank you so much for your help.

    I made a quick/dirty code modification (see code block below) and ran a 3-Player (2-Person) Test (2 players in one group, 1 by themselves). I attached screen captures to show that each client "player" PhotonVoiceNetwork.Instance.Client.GlobalInterestGroup and Recorder.InterestGroup match .

    However, this is what happened after the Interest Group assignment.: The Instructor (first who logged in/ MasterClient) could hear Student1 and Student2 players. However, none of the student players could hear the instructor. Note: We could not/did not test tonight if Student1 and Student2 could hear/comm together (only 2 people, graphics cards that suck, so we were lucky to get 3 players on 3 separate devices among 2 people). However, we muted/unmuted the Students to test the comms/Student group. One person had two devices playing Student1 and Student2 separately. The result was that Student1 and Student2 did not hear the Instructor/MasterClient. Is per chance, the first player to login/masterclient always assigned to Group 0? I had a theory about this and it might explain why the Instructor/masterclient could hear everyone but they could not hear them even though they were assigned to group 2.
    public void SetInterestGroup(GameObject player, byte group = 0)
        {
            // Group to Remove, Group to Add
            //PhotonVoiceNetwork.Instance.Client.OpChangeGroups(new byte[] { InterestGroup }, new byte[] { group });
    
            // Set the InterestGroup to the new group
            InterestGroup = group;
    
            // Set the Global Audio
            PhotonVoiceNetwork.Instance.Client.GlobalInterestGroup = InterestGroup;
    
            // Set the Recorder Interest Group
            player.GetComponent<Recorder>().InterestGroup = InterestGroup;
    
            Debug.Log($"Client: {PV.name}, {PV.Owner}");
            Debug.Log("Set the Interest Group to: " + InterestGroup);
            Debug.Log("PhotonVoiceNetwork.Instance.Client.GlobalInterestGroup: " + PhotonVoiceNetwork.Instance.Client.GlobalInterestGroup);
            Debug.Log("player.GetComponent<Recorder>().InterestGroup: " + player.GetComponent<Recorder>().InterestGroup);
    
        } // end of method
    

    Here are the Test Logs that show the players assignments from the Debug.Log code

    testrun1.jpg

    testrun1b.jpg

    testrun1c.jpg


  • JohnTube
    JohnTube ✭✭✭✭✭
    edited September 2020
    Hi @KathleenWest,

    You don't need to set the interest group in the recorder if you set a global one (unless you set a different target interest group, individually, per recorder).

    so I would do this:
    public void SetInterestGroup(byte group)
    {
            // Set the InterestGroup to the new group
            InterestGroup = group;
    
            // Set the Global Audio
            PhotonVoiceNetwork.Instance.Client.GlobalInterestGroup = InterestGroup;
    
            Debug.Log($"Client: {PV.name}, {PV.Owner}, Set the Interest Group to:{InterstGroup}");
    }
    

    I would first make sure everyone can hear everyone in group 0. So, either don't call SetInterestGroup, or set group to 0 (SetInterestGroup(0)) on all clients and make sure they all can hear each other.
    If without using interest groups you have an issue hearing each other, start by matchmaking checklist (for PUN and Photon Voice clients) then, if you use PhotonVoiceView (I guess you do), you could use VoiceDebugScript attached to the same GameObject as PhotonVoiceView and then call "VoiceDebugScript.CantHearYou" for the person you can't hear.
  • JohnTube
    JohnTube ✭✭✭✭✭
    edited September 2020
    Hi,

    After some email exchange, I thought it would be good to share some clarifications about interest groups (hopefully in the future we improve interest groups APIs in all clients and make them easier to use):

    Interest Groups is a feature of Photon Realtime.
    It's basically:

    - a client joined to a room chooses a list of groups to subscribe to in order to receive events sent to those groups. This is done via OpChangeGroups (C# Reference API).
    - a client joined to a room can raise events to a specific interest group. This is done via OpRaiseEvent (C# Reference API).

    PUN and Photon Voice are built on top of Photon Realtime.
    PUN and Photon Voice make use of interest groups and have wrapper around the Photon Realtime API call.
    PUN and Photon Voice may add some client driven/specific high level behaviour:
    - both can cache the list of groups and call OpChangeGroups when joined to a room.
    - both have entities/components that have target interest groups (PhotonView.Group in PUN 2 and Recorder.InterestGroup in PhotonVoice 2)
    - PUN has disabled groups where you choose to selectively prevent/pause raising events to those disabled groups without having to change the groups of the PhotonViews or all RaiseEvent calls.
    - Photon Voice has a global group concept, which is a single group to which the client subscribes to and remove all others AND all Recorders will use the same group as target.

    Photon Voice relies heavily on interest groups to separate voice streams/conversation.
    Still, interest groups is an optional in Photon Realtime, PUN and Photon Voice.

    In C# the Photon Realtime networking client is called LoadBalancingClient.
    Photon Voice has/uses a LoadBalancingClient:
    in Photon Voice 2, it's VoiceConnection.Client or PhotonVoiceNetwork.Instance.Client
    PUN 2 has/uses a LoadBalancingClient: it's PhotonNetwork.NetworkingClient.

    In Photon Voice 2 with PUN 2 integration, you have two networking clients, two LoadBalancingClient instances, two connections, two rooms. So each client can be subscribe to different interest groups. Clients are decoupled here. But you can choose to bind them and subscribe to the same groups from both clients: PUN 2 and Photon Voice 2.
    You can do this via PhotonVoiceView.RecorderInUse.InterestGroup and PhotonView.Group easily.
    Also PhotonNetwork.SetInterestGroups AND PhotonVoiceNetwork.Instance.Client.OpChangeGroups OR PhotonVoiceNetwork.Instance.Client.GlobalInterestGroup.

    Documentation links:
    Demos:
  • Sorry the delay in response. I appreciate your help on the topic.

    Some more information about our setups. Before the Instructor clicks the magic button to break the students into Interest Groups, we have confirmed the following in our tests:
    • Each player can see each other in the scene
    • Each player can talk into their microphone and be heard by all other players
    • All players can hear each other talk (Group Zero)

    Question: Given we confirmed the mic/sound checks do we still need to do the matchmaking checklist test and voice speaker checks that you recommended for testing the Interest Groups?

    Test: The Instructor clicks on the button to break students into Interest Groups. After the Interest Groups matchmaking code is executed, we confirmed the following:
    • The Instructor (MasterClient) could hear all the players
    • The individual player "PhotonVoiceNetwork.Instance.Client.GlobalInterestGroup" was matched correctly to the right group number (see my screen captures in the above thread)
    • The individual player Recorder player.GetComponent<Recorder>().InterestGroup was also confirmed to be matched to the right group assignment
    • Players could hear everyone except the Instructor

    After the test fail confirmation, the Instructor clicks on a button to turn off the Interest Group assignments and revert back to Group 0. Everyone can speak/hear each other as normal.

    Now I have a hunch about something and wanted to share an architecture setup and question. Disclaimer: I did not design/code/implement the original Photon Voice in the product or on the player prefab. I'm assuming it was done correctly since everyone could hear/speak to each other. So I'm not deeply familar with it yet, but could be if I need to be.

    Scene
    > Object "VoiceManager" --> Scripts Photon Voice Network and Recorder are attached
    > Object "GameManager" --> InterestGroupBehavior.cs Script attached
    > Players...

    Player Prefab
    The following base scripts are attached:
    Photon Voice View
    Audio Source
    Speaker
    Recorder
    Photon Voice Network
    + custom PlayerInterestGroup.cs

    Question: Given there is both a GameLevel Photon Voice Network, Recorder and a PlayerLevel Photon Voice Network, Recorder.. would there be any conflicts in getting the Interest Group feature to work with this architecture? Which one should be selected?
  • JohnTube
    JohnTube ✭✭✭✭✭
    edited October 2020
    Sorry the delay in response. I appreciate your help on the topic.
    No worries, I'm happy to help.
    Question: Given we confirmed the mic/sound checks do we still need to do the matchmaking checklist test and voice speaker checks that you recommended for testing the Interest Groups?
    No need for extra checks/tests as long as everyone can hear everyone else and can be heard by everyone else.
    The individual player Recorder player.GetComponent<Recorder>().InterestGroup was also confirmed to be matched to the right group assignment
    Please see my comment here. No need to assign, in your case, Recorder.InterestGroup since this will be automatically done using GlobalInterestGroup. (you can log it without setting it to debug/check this).
    Question: Given there is both a GameLevel Photon Voice Network, Recorder and a PlayerLevel Photon Voice Network, Recorder.. would there be any conflicts in getting the Interest Group feature to work with this architecture? Which one should be selected?
    Ah yes this is ca. What Photon Voice 2 version are you using? you should see an error about this in the logs, PhotonVoiceNetwork, only one should exist.
    So please change this and follow the recommended suggested flow here:

    - Remove PhotonVoiceNetwork from the player prefab.
    - Remove Recorder from player prefab and assign the Recorder in the scene as PrimaryRecorder in PhotonVoiceNetwork in the scene and check/tick UsePrimaryRecorder in PhotonVoiceView.