// -------------------------------------------------------------------------------------------------------------------- // // Part of: Photon Unity Utilities, // // // Assign numbers to Players in a room. Uses Room custom Properties // // developer@exitgames.com // -------------------------------------------------------------------------------------------------------------------- using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Linq; using Photon; using ExitGames.Client.Photon; using Hashtable = ExitGames.Client.Photon.Hashtable; namespace ExitGames.UtilityScripts { /// /// Implements consistent indexing in a room/game with help of room properties. Access them by PhotonPlayer.GetRoomIndex() extension. /// /// /// indexing ranges from 0 to the maximum number of Players. /// indexing remains for the player while in room. /// If a Player is indexed 2 and player indexes 1 leaves, index 1 become vacant and will assigned to the future player joining (the first available vacant index is assigned when joining) /// public class PlayerRoomIndexing : PunBehaviour { #region Public Properties /// /// The instance. EntryPoint to query about Room Indexing. /// public static PlayerRoomIndexing instance; /// /// OnRoomIndexingChanged delegate. Use /// public delegate void RoomIndexingChanged(); /// /// Called everytime the room Indexing was updated. Use this for discrete updates. Always better than brute force calls every frame. /// public RoomIndexingChanged OnRoomIndexingChanged; /// Defines the room custom property name to use for room player indexing tracking. public const string RoomPlayerIndexedProp = "PlayerIndexes"; /// /// Cached list of Player indexes. You can use .GetRoomIndex() /// /// The player identifiers. public int[] PlayerIds { get { return _playerIds; } } #endregion #region Private Properties int[] _playerIds; object _indexes; Dictionary _indexesLUT; List _indexesPool; PhotonPlayer _p; #endregion #region MonoBehaviours methods public void Awake() { if (instance!=null) { Debug.LogError("Existing instance of PlayerRoomIndexing found. Only One instance is required at the most. Please correct and have only one at any time."); } instance = this; } #endregion #region PunBehavior Overrides public override void OnJoinedRoom() { if (PhotonNetwork.isMasterClient) { AssignIndex(PhotonNetwork.player); }else{ RefreshData(); } } public override void OnLeftRoom() { RefreshData(); } public override void OnPhotonPlayerConnected (PhotonPlayer newPlayer) { if (PhotonNetwork.isMasterClient) { AssignIndex(newPlayer); } } public override void OnPhotonPlayerDisconnected(PhotonPlayer otherPlayer) { if (PhotonNetwork.isMasterClient) { UnAssignIndex(otherPlayer); } } public override void OnPhotonCustomRoomPropertiesChanged(Hashtable propertiesThatChanged) { if (propertiesThatChanged.ContainsKey(PlayerRoomIndexing.RoomPlayerIndexedProp)) { RefreshData(); } } public override void OnMasterClientSwitched(PhotonPlayer newMasterClient) { if (PhotonNetwork.isMasterClient) { SanitizeIndexing(); } } #endregion /// Get the room index of a particular PhotonPlayer. You can also use .GetRoomIndex() /// persistent index in room. -1 for none public int GetRoomIndex( PhotonPlayer player) { if (_indexesLUT!=null && _indexesLUT.ContainsKey(player.ID)) { return _indexesLUT[player.ID]; } return -1; } /// /// Sanitizes the indexing incase a player join while masterclient was changed and missed it. /// void SanitizeIndexing() { if (!PhotonNetwork.isMasterClient) { return; } if (PhotonNetwork.room==null) { return; } // attempt to access index props. Dictionary _indexesLUT_local = new Dictionary(); if(PhotonNetwork.room.CustomProperties.TryGetValue(PlayerRoomIndexing.RoomPlayerIndexedProp, out _indexes)) { _indexesLUT_local = _indexes as Dictionary; } // check if we need to assign if (_indexesLUT_local.Count != PhotonNetwork.room.PlayerCount) { foreach(PhotonPlayer _p in PhotonNetwork.playerList) { if (!_indexesLUT_local.ContainsKey(_p.ID)) { // Debug.Log("Sanitizing Index for "+_p); AssignIndex(_p); } } } } /// /// Internal call Refresh the cached data and call the OnRoomIndexingChanged delegate. /// void RefreshData() { if (PhotonNetwork.room!=null) { _playerIds = new int[PhotonNetwork.room.MaxPlayers]; if (PhotonNetwork.room.CustomProperties.TryGetValue(PlayerRoomIndexing.RoomPlayerIndexedProp, out _indexes)) { _indexesLUT = _indexes as Dictionary; foreach(KeyValuePair _entry in _indexesLUT) { //Debug.Log("Entry; "+_entry.Key+":"+_entry.Value); _p = PhotonPlayer.Find(_entry.Key); _playerIds[_entry.Value] = _p.ID; } } }else{ _playerIds = new int[0]; } if (OnRoomIndexingChanged!=null) OnRoomIndexingChanged(); } void AssignIndex(PhotonPlayer player) { if (PhotonNetwork.room.CustomProperties.TryGetValue(PlayerRoomIndexing.RoomPlayerIndexedProp, out _indexes)) { _indexesLUT = _indexes as Dictionary; }else{ _indexesLUT = new Dictionary(); } List _indexesPool = new List( new bool[PhotonNetwork.room.MaxPlayers] ); foreach(KeyValuePair _entry in _indexesLUT) { _indexesPool[_entry.Value] = true; } _indexesLUT[player.ID] = Mathf.Max (0,_indexesPool.IndexOf(false)); PhotonNetwork.room.SetCustomProperties(new Hashtable() {{PlayerRoomIndexing.RoomPlayerIndexedProp, _indexesLUT}}); RefreshData(); } void UnAssignIndex(PhotonPlayer player) { if (PhotonNetwork.room.CustomProperties.TryGetValue(PlayerRoomIndexing.RoomPlayerIndexedProp, out _indexes)) { _indexesLUT = _indexes as Dictionary; _indexesLUT.Remove(player.ID); PhotonNetwork.room.SetCustomProperties(new Hashtable() {{PlayerRoomIndexing.RoomPlayerIndexedProp, _indexesLUT}}); }else{ } RefreshData(); } } /// Extension used for PlayerRoomIndexing and PhotonPlayer class. public static class PlayerRoomIndexingExtensions { /// Extension for PhotonPlayer class to wrap up access to the player's custom property. /// persistent index in room. -1 for no indexing public static int GetRoomIndex(this PhotonPlayer player) { if (PlayerRoomIndexing.instance == null) { Debug.LogError("Missing PlayerRoomIndexing Component in Scene"); return -1; } return PlayerRoomIndexing.instance.GetRoomIndex(player); } } }