// -------------------------------------------------------------------------------------------------------------------- // // Part of: Photon Unity Utilities, // // // Assign numbers to Players in a room. Uses Room custom Properties // // developer@exitgames.com // -------------------------------------------------------------------------------------------------------------------- using System.Collections.Generic; using System.Linq; using UnityEngine; using Photon.Pun; using Photon.Realtime; using Hashtable = ExitGames.Client.Photon.Hashtable; namespace Photon.Pun.UtilityScripts { /// /// Implements consistent numbering in a room/game with help of room properties. Access them by Player.GetPlayerNumber() extension. /// /// /// indexing ranges from 0 to the maximum number of Players. /// indexing remains for the player while in room. /// If a Player is numbered 2 and player numbered 1 leaves, numbered 1 become vacant and will assigned to the future player joining (the first available vacant number is assigned when joining) /// public class PlayerNumbering : MonoBehaviourPunCallbacks { //TODO: Add a "numbers available" bool, to allow easy access to this?! #region Public Properties /// /// The instance. EntryPoint to query about Room Indexing. /// public static PlayerNumbering instance; public static Player[] SortedPlayers; /// /// OnPlayerNumberingChanged delegate. Use /// public delegate void PlayerNumberingChanged(); /// /// Called everytime the room Indexing was updated. Use this for discrete updates. Always better than brute force calls every frame. /// public static event PlayerNumberingChanged OnPlayerNumberingChanged; /// Defines the room custom property name to use for room player indexing tracking. public const string RoomPlayerIndexedProp = "pNr"; /// /// dont destroy on load flag for this Component's GameObject to survive Level Loading. /// public bool dontDestroyOnLoad = false; #endregion #region MonoBehaviours methods public void Awake() { if (instance != null && instance != this && instance.gameObject != null) { GameObject.DestroyImmediate(instance.gameObject); } instance = this; if (dontDestroyOnLoad) { DontDestroyOnLoad(this.gameObject); } this.RefreshData(); } #endregion #region PunBehavior Overrides public override void OnJoinedRoom() { this.RefreshData(); } public override void OnLeftRoom() { PhotonNetwork.LocalPlayer.CustomProperties.Remove(PlayerNumbering.RoomPlayerIndexedProp); } public override void OnPlayerEnteredRoom(Player newPlayer) { this.RefreshData(); } public override void OnPlayerLeftRoom(Player otherPlayer) { this.RefreshData(); } public override void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps) { if (changedProps != null && changedProps.ContainsKey(PlayerNumbering.RoomPlayerIndexedProp)) { this.RefreshData(); } } #endregion // each player can select it's own playernumber in a room, if all "older" players already selected theirs /// /// Internal call Refresh the cached data and call the OnPlayerNumberingChanged delegate. /// public void RefreshData() { if (PhotonNetwork.CurrentRoom == null) { return; } if (PhotonNetwork.LocalPlayer.GetPlayerNumber() >= 0) { SortedPlayers = PhotonNetwork.CurrentRoom.Players.Values.OrderBy((p) => p.GetPlayerNumber()).ToArray(); if (OnPlayerNumberingChanged != null) { OnPlayerNumberingChanged(); } return; } HashSet usedInts = new HashSet(); Player[] sorted = PhotonNetwork.PlayerList.OrderBy((p) => p.ActorNumber).ToArray(); string allPlayers = "all players: "; foreach (Player player in sorted) { allPlayers += player.ActorNumber + "=pNr:"+player.GetPlayerNumber()+", "; int number = player.GetPlayerNumber(); // if it's this user, select a number and break // else: // check if that user has a number // if not, break! // else remember used numbers if (player.IsLocal) { Debug.Log ("PhotonNetwork.CurrentRoom.PlayerCount = " + PhotonNetwork.CurrentRoom.PlayerCount); // select a number for (int i = 0; i < PhotonNetwork.CurrentRoom.PlayerCount; i++) { if (!usedInts.Contains(i)) { player.SetPlayerNumber(i); break; } } // then break break; } else { if (number < 0) { break; } else { usedInts.Add(number); } } } //Debug.Log(allPlayers); //Debug.Log(PhotonNetwork.LocalPlayer.ToStringFull() + " has PhotonNetwork.player.GetPlayerNumber(): " + PhotonNetwork.LocalPlayer.GetPlayerNumber()); SortedPlayers = PhotonNetwork.CurrentRoom.Players.Values.OrderBy((p) => p.GetPlayerNumber()).ToArray(); if (OnPlayerNumberingChanged != null) { OnPlayerNumberingChanged(); } } } /// Extension used for PlayerRoomIndexing and Player class. public static class PlayerNumberingExtensions { /// Extension for Player class to wrap up access to the player's custom property. /// Make sure you use the delegate 'OnPlayerNumberingChanged' to knoiw when you can query the PlayerNumber. Numbering can changes over time or not be yet assigned during the initial phase ( when player creates a room for example) /// /// persistent index in room. -1 for no indexing public static int GetPlayerNumber(this Player player) { if (player == null) { return -1; } if (PhotonNetwork.OfflineMode) { return 0; } if (!PhotonNetwork.IsConnectedAndReady) { return -1; } object value; if (player.CustomProperties.TryGetValue (PlayerNumbering.RoomPlayerIndexedProp, out value)) { return (byte)value; } return -1; } /// /// Sets the player number. /// It's not recommanded to manually interfere with the playerNumbering, but possible. /// /// Player. /// Player number. public static void SetPlayerNumber(this Player player, int playerNumber) { if (player == null) { return; } if (PhotonNetwork.OfflineMode) { return; } if (playerNumber < 0) { Debug.LogWarning("Setting invalid playerNumber: " + playerNumber + " for: " + player.ToStringFull()); } if (!PhotonNetwork.IsConnectedAndReady) { Debug.LogWarning("SetPlayerNumber was called in state: " + PhotonNetwork.NetworkClientState + ". Not IsConnectedAndReady."); return; } int current = player.GetPlayerNumber(); if (current != playerNumber) { Debug.Log("PlayerNumbering: Set number "+playerNumber); player.SetCustomProperties(new Hashtable() { { PlayerNumbering.RoomPlayerIndexedProp, (byte)playerNumber } }); } } } }