You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

267 lines
8.6 KiB

// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PlayerNumbering.cs" company="Exit Games GmbH">
// Part of: Photon Unity Utilities,
// </copyright>
// <summary>
// Assign numbers to Players in a room. Uses Room custom Properties
// </summary>
// <author>developer@exitgames.com</author>
// --------------------------------------------------------------------------------------------------------------------
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
{
/// <summary>
/// Implements consistent numbering in a room/game with help of room properties. Access them by Player.GetPlayerNumber() extension.
/// </summary>
/// <remarks>
/// 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)
/// </remarks>
public class PlayerNumbering : MonoBehaviourPunCallbacks
{
//TODO: Add a "numbers available" bool, to allow easy access to this?!
#region Public Properties
/// <summary>
/// The instance. EntryPoint to query about Room Indexing.
/// </summary>
public static PlayerNumbering instance;
public static Player[] SortedPlayers;
/// <summary>
/// OnPlayerNumberingChanged delegate. Use
/// </summary>
public delegate void PlayerNumberingChanged();
/// <summary>
/// Called everytime the room Indexing was updated. Use this for discrete updates. Always better than brute force calls every frame.
/// </summary>
public static event PlayerNumberingChanged OnPlayerNumberingChanged;
/// <summary>Defines the room custom property name to use for room player indexing tracking.</summary>
public const string RoomPlayerIndexedProp = "pNr";
/// <summary>
/// dont destroy on load flag for this Component's GameObject to survive Level Loading.
/// </summary>
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
/// <summary>
/// Internal call Refresh the cached data and call the OnPlayerNumberingChanged delegate.
/// </summary>
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<int> usedInts = new HashSet<int>();
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();
}
}
}
/// <summary>Extension used for PlayerRoomIndexing and Player class.</summary>
public static class PlayerNumberingExtensions
{
/// <summary>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)
/// </summary>
/// <returns>persistent index in room. -1 for no indexing</returns>
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;
}
/// <summary>
/// Sets the player number.
/// It's not recommanded to manually interfere with the playerNumbering, but possible.
/// </summary>
/// <param name="player">Player.</param>
/// <param name="playerNumber">Player number.</param>
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 } });
}
}
}
}