Photon Teams Manager definitely broken

Hi, on my way on using Photon's implementation to handle teams in a room I had a problem when wanting to create a method that returns the team with less players and joins you to itself.

The method is an extension method properly created in a static class and there are no actual runtime errors but logic ones, more precisely the method always joins the players to the same team. Player.JoinTeam() does not even update the member count of the teams.

Here is the code along with the console logs (I'm using System.Linq but I have also tried using a for loop) (Both players call properly my method just after spawning):

        public static PhotonTeam JoinBalancedTeam(this Player player)
            IEnumerable<PhotonTeam> teams = PhotonTeamsManager.Instance.GetAvailableTeams();

            Debug.Log("Unordered teams are: ");
            foreach (var team in teams)
                Debug.Log($"{team.Name}, with {team.TeammatesAmount()} members; ");

            teams = teams.OrderBy(t => t.TeammatesAmount());

            Debug.Log("Ordered teams are: ");
            foreach (var team in teams)
                Debug.Log($"{team.Name}, with {team.TeammatesAmount()} members; ");

            var selected = teams.ToArray()[0];

            Debug.Log($"{player.NickName} joined {selected.Name} team!");

            Debug.Log($"{selected.Name} team now has {selected.TeammatesAmount()} members");
            return selected;


        public static int TeammatesAmount(this PhotonTeam team) => PhotonTeamsManager.Instance.GetTeamMembersCount(team);

This is the console for the first player who uses the method:

Second player console is identical to this one:

Any idea on why is it happening?


  • JohnTube
    JohnTube mod
    edited October 10

    Hi @ChichoRD,

    Here is what's happening:

    When you call JoinTeam, SwitchTeam or LeaveCurrentTeam the PhotonTeamsManager class will call PUN's SetProperties operation from the local client to set properties of the local actor inside the room. Doing so will not result in an immediate change in the properties locally even if the method returns true. The result of the method call means that the client has queued the operation to be sent to the server.

    Instead the client asks the server for the change and waits for the server's ACK via event. Once this event is received the properties are changed locally on the client and the team assignment will be updated. Assuming this is successful and no network issues happened that caused client disconnect.

    You can also read this.

    So in order to achieve what you're looking for you need to wait until the PhotonTeamsManager.PlayerJoinedTeam or PhotonTeamsManager.PlayerLeftTeam events are raised.

  • ChichoRD
    edited October 10

    Thanks for your response and attention @JohnTube.

    But still, I have a question: How much time is the raised event supposed to take? I know networked operations might have some delay of miliseconds or even seconds, but minutes?! for a videogame?!

    Also is it supposed to work so all players have to be in the room when the JoinTeam() happens to be correctly reflected? Because I may have not mentioned that my players join team as the join the room without waiting for any other player, since I assumed that changes would be networked whenever other players join or rejoin the room.

    Is there a code snippet somewhere so I can see what you mean by waiting until those events are raised regarding my specific problem?

  • Here is a code snippet that shows what I meant: (should be part of a MonoBehaviour)

    private voice OnEnable() {
        PhotonTeamsManager.PlayerJoinedTeam += OnPlayerJoinedTeam;
        PhotonTeamsManager.PlayerLeftTeam += OnPlayerLeftTeam;
    private voice OnDisable() {
        PhotonTeamsManager.PlayerJoinedTeam -= OnPlayerJoinedTeam;
        PhotonTeamsManager.PlayerLeftTeam -= OnPlayerLeftTeam;
    private void OnPlayerJoinedTeam(Player player, PhotonTeam team) {
        Debug.Log($"{player.NickName} joined {team.Name} team!");
        Debug.Log($"{team.Name} team now has {team.TeammatesAmount()} members");
    private void OnPlayerLeftTeam(Player player, PhotonTeam team) {
        Debug.Log($"{player.NickName} left {team.Name} team!");
        Debug.Log($"{team.Name} team now has {team.TeammatesAmount()} members");

    FYI: in your use case (joining the team with the lowest team members count) we need to handle case where two players try to join the same team at once. Best solution is to use room properties as team member count and use that as expected property (CAS). But this is not supported out of the box in PhotonTeamsManager.