// --------------------------------------------------------------------------------------------------------------------
//
// 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);
}
}
}