Help with Network Culling

I'm in the process of writing some network culling logic. I have read this: https://doc.photonengine.com/en-us/pun/current/gameplay/interestgroups

and I have also read the culling demo which lead me to the Culling Handler and Culling area components. Taking what I have learned there I was trying to cull based on player distance dynamically. So here is the code that I have, I want to make sure that I am writing/understanding this correctly.

So here is the idea of this following code. I have spheres at different distances and when the owner photonview player is within a certain circle's distance they will sub to a group, enable sending to that group, and set the photon view of the other player to that group. Is my thinking here correct?
[SerializeField] protected List<CullDistance> cullDistances = new List<CullDistance>();
[SerializeField] protected byte last_group = 255;

protected List<byte> _subToGroups = new List<byte>();
protected int _cullIndex = 0;
protected int _finalIndex = 0;
protected List<SyncPlayer> _allPlayers = new List<SyncPlayer>();
protected List<SyncPlayer> _tmpList = new List<SyncPlayer>();
protected int _loops = 0;

public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
        {
            if (photonView.IsMine)
            {
                SetInterestGroups();

                _cullIndex++;
                if (_cullIndex > _finalIndex)
                {
                    _cullIndex = -1;
                    _finalIndex--;
                    if (_finalIndex < 0)
                    {
                        _finalIndex = cullDistances.Count;
                    }
                }
                if (_cullIndex == -1 && _loops == 0)
                {
                    PhotonNetwork.SetSendingEnabled(new byte[0], null);
                    photonView.Group = 0;
                }
                else if (_cullIndex < cullDistances.Count && _subToGroups.Count > 0 && _subToGroups.Contains(cullDistances[_cullIndex].interest_group))
                {
                    PhotonNetwork.SetSendingEnabled(new byte[0], new byte[1] { cullDistances[_cullIndex].interest_group });
                    photonView.Group = cullDistances[_cullIndex].interest_group;
                }
                else if (_cullIndex == cullDistances.Count && _finalIndex == cullDistances.Count)
                {
                    _loops = (_loops+1 > 3) ? 0 : _loops + 1;
                    PhotonNetwork.SetSendingEnabled(new byte[0], new byte[1] { last_group });
                    photonView.Group = last_group;
                }
            }
        }

protected void SetInterestGroups()
        {
            _subToGroups.Clear();
            _allPlayers.Clear();
            _tmpList.Clear();
            _allPlayers.AddRange(FindObjectsOfType<SyncPlayer>());
            foreach (CullDistance item in cullDistances)
            {
                foreach (SyncPlayer player in _allPlayers)
                {
                    if (Vector3.Distance(transform.position, player.transform.position) <= item.distance)
                    {
                        _tmpList.Add(player);
                    }
                }
                if (_tmpList.Count > 0)
                {
                    foreach (SyncPlayer player in _tmpList)
                    {
                        _allPlayers.Remove(player);
                        player.photonView.Group = item.interest_group;
                    }
                    _tmpList.Clear();
                    _subToGroups.Add(item.interest_group);
                }
            }
            if (_allPlayers.Count > 0)
            {
                foreach (SyncPlayer player in _allPlayers)
                {
                    player.photonView.Group = last_group;
                }
                _subToGroups.Add(last_group);
            }
            PhotonNetwork.SetInterestGroups(new byte[0], _subToGroups.ToArray());
        }

Comments

  • Let me clarify a little bit here. I see jittery movement when the other players get further away (which is what I expect) but they smooth out again when they get to the "last_group" which is not what I would expect to happen. Is my thinking off a little bit here. I would normally expect the "last_group" to be the move jittery.
  • Could you make sense of it?
  • The code always subscribes to the last_group, right? So no matter where anyone is, the group is subscribed. If a user actually is in said group, it makes sense all updates go into that?
  • How It Works:
    The way that it works is that it will start at index 0 and loop over all group and only send data to where people are located (otherwise I just sends data to the last location where people were). If that happens to be at the "last_position" or they happen to be at the first position. If they are on the last position it will only send data to them on the 3rd loop iteration. It loops and removes the last group, loops again, removes that last group, repeat. It does this until it has nothing in the loop then it sends to 0 group and restarts the entire process.

    So in the above code I can receive data from any group that has someone in it, this is the responsibility of SetInterestGroups() function. Then I can only send data to the single group that I am currently iterating over if someone is in it, otherwise it sends to the last group that had someone in it.

    Is this right?:
    With all that said is my thinking correct in doing all of this? When I call this function:
    photonView.Group = cullDistances[_cullIndex].interest_group;
    
    My thought there is you can send ONLY to that current iterated group.

    This code means I make it so I am allowed to send to that group:
    PhotonNetwork.SetSendingEnabled(new byte[0], new byte[1] { cullDistances[_cullIndex].interest_group });
    

    Finally this code makes it so I can receivefrom all groups that actually have someone in it
    PhotonNetwork.SetInterestGroups(new byte[0], _subToGroups.ToArray());
    

    Am I doing this all right? Is my thinking flawed here? Is this actually only going to send data to my target groups?
  • I don't know why you should worry if someone is in said group and how you'd find out.
    Maybe I don't understand the benefit over the "traditional" way which is this:

    You layer areas over your map. Layer 1 is fine and Layer 2 has fewer areas (coarse?!). So no matter where I am, there are 2 or more grid areas I belong in. I subscribe to both at the same time. In Layer 1, my area will change more often while I move around but I get both layer's events.
    While I move, I send roundrobin into each layer's area I belong to, no matter who listens. I don't have to look up who's where. Whoever gets my Layer 1 updates AND my Layer 2 updates, gets more updates. Someone who just happens to be in my Layer 2 area gets only half.


    I would definitely not send updates into specific groups, depending on who's there. Often this is not known to me.
  • Okay I'll go back to the cull area component. Just FYI that component has errors in it so I will need to apply some manual fixes to it.

    I add the cull handler to player player and make a single transform in my scene to hold the cull area and scale it to make area I want to cull. Then I set my layers.

    However, when I click play I get a boat load of errors. Don't have them off hand on me right now but I will post them here again when I get the chance.

    Hence the reason I just read through the components code TRYING to figure out how it works and combine the components into one to dynamically find the cull area.