// ---------------------------------------------------------------------------- // // Loadbalancing Framework for Photon - Copyright (C) 2011 Exit Games GmbH // // // This class resembles info about available rooms, as sent by the Master // server's lobby. Consider all values as readonly. // // developer@exitgames.com // ---------------------------------------------------------------------------- using System; using ExitGames.Client.Photon; /// /// A simplified room with just the info required to list and join, used for the room listing in the lobby. /// The properties are not settable (open, MaxPlayers, etc). /// /// /// This class resembles info about available rooms, as sent by the Master server's lobby. /// Consider all values as readonly. None are synced (only updated by events by server). /// /// \ingroup publicApi public class RoomInfo { /// Used internally in lobby, to mark rooms that are no longer listed. public bool removedFromList { get; internal set; } /// Backing field for property. private Hashtable customPropertiesField = new Hashtable(); /// Backing field for property. protected byte maxPlayersField = 0; /// Backing field for property. protected string[] expectedUsersField; /// Backing field for property. protected bool openField = true; /// Backing field for property. protected bool visibleField = true; /// Backing field for property. False unless the GameProperty is set to true (else it's not sent). protected bool autoCleanUpField = PhotonNetwork.autoCleanUpPlayerObjects; /// Backing field for property. protected string nameField; /// Backing field for master client id (actorNumber). defined by server in room props and ev leave. protected internal int masterClientIdField; protected internal bool serverSideMasterClient { get; private set; } /// Read-only "cache" of custom properties of a room. Set via Room.SetCustomProperties (not available for RoomInfo class!). /// All keys are string-typed and the values depend on the game/application. /// public Hashtable CustomProperties { get { return this.customPropertiesField; } } /// The name of a room. Unique identifier (per Loadbalancing group) for a room/match. public string Name { get { return this.nameField; } } /// /// Only used internally in lobby, to display number of players in room (while you're not in). /// public int PlayerCount { get; private set; } /// /// State if the local client is already in the game or still going to join it on gameserver (in lobby always false). /// public bool IsLocalClientInside { get; set; } /// /// Sets a limit of players to this room. This property is shown in lobby, too. /// If the room is full (players count == maxplayers), joining this room will fail. /// /// /// As part of RoomInfo this can't be set. /// As part of a Room (which the player joined), the setter will update the server and all clients. /// public byte MaxPlayers { get { return this.maxPlayersField; } } /// /// Defines if the room can be joined. /// This does not affect listing in a lobby but joining the room will fail if not open. /// If not open, the room is excluded from random matchmaking. /// Due to racing conditions, found matches might become closed before they are joined. /// Simply re-connect to master and find another. /// Use property "IsVisible" to not list the room. /// /// /// As part of RoomInfo this can't be set. /// As part of a Room (which the player joined), the setter will update the server and all clients. /// public bool IsOpen { get { return this.openField; } } /// /// Defines if the room is listed in its lobby. /// Rooms can be created invisible, or changed to invisible. /// To change if a room can be joined, use property: open. /// /// /// As part of RoomInfo this can't be set. /// As part of a Room (which the player joined), the setter will update the server and all clients. /// public bool IsVisible { get { return this.visibleField; } } /// /// Constructs a RoomInfo to be used in room listings in lobby. /// /// /// protected internal RoomInfo(string roomName, Hashtable properties) { this.InternalCacheProperties(properties); this.nameField = roomName; } /// /// Makes RoomInfo comparable (by name). /// public override bool Equals(object other) { RoomInfo otherRoomInfo = other as RoomInfo; return (otherRoomInfo != null && this.Name.Equals(otherRoomInfo.nameField)); } /// /// Accompanies Equals, using the name's HashCode as return. /// /// public override int GetHashCode() { return this.nameField.GetHashCode(); } /// Simple printingin method. /// Summary of this RoomInfo instance. public override string ToString() { return string.Format("Room: '{0}' {1},{2} {4}/{3} players.", this.nameField, this.visibleField ? "visible" : "hidden", this.openField ? "open" : "closed", this.maxPlayersField, this.PlayerCount); } /// Simple printingin method. /// Summary of this RoomInfo instance. public string ToStringFull() { return string.Format("Room: '{0}' {1},{2} {4}/{3} players.\ncustomProps: {5}", this.nameField, this.visibleField ? "visible" : "hidden", this.openField ? "open" : "closed", this.maxPlayersField, this.PlayerCount, this.customPropertiesField.ToStringFull()); } /// Copies "well known" properties to fields (IsVisible, etc) and caches the custom properties (string-keys only) in a local hashtable. /// New or updated properties to store in this RoomInfo. protected internal void InternalCacheProperties(Hashtable propertiesToCache) { if (propertiesToCache == null || propertiesToCache.Count == 0 || this.customPropertiesField.Equals(propertiesToCache)) { return; } // check of this game was removed from the list. in that case, we don't // need to read any further properties // list updates will remove this game from the game listing if (propertiesToCache.ContainsKey(GamePropertyKey.Removed)) { this.removedFromList = (Boolean)propertiesToCache[GamePropertyKey.Removed]; if (this.removedFromList) { return; } } // fetch the "well known" properties of the room, if available if (propertiesToCache.ContainsKey(GamePropertyKey.MaxPlayers)) { this.maxPlayersField = (byte)propertiesToCache[GamePropertyKey.MaxPlayers]; } if (propertiesToCache.ContainsKey(GamePropertyKey.IsOpen)) { this.openField = (bool)propertiesToCache[GamePropertyKey.IsOpen]; } if (propertiesToCache.ContainsKey(GamePropertyKey.IsVisible)) { this.visibleField = (bool)propertiesToCache[GamePropertyKey.IsVisible]; } if (propertiesToCache.ContainsKey(GamePropertyKey.PlayerCount)) { this.PlayerCount = (int)((byte)propertiesToCache[GamePropertyKey.PlayerCount]); } if (propertiesToCache.ContainsKey(GamePropertyKey.CleanupCacheOnLeave)) { this.autoCleanUpField = (bool)propertiesToCache[GamePropertyKey.CleanupCacheOnLeave]; } if (propertiesToCache.ContainsKey(GamePropertyKey.MasterClientId)) { this.serverSideMasterClient = true; bool isUpdate = this.masterClientIdField != 0; this.masterClientIdField = (int) propertiesToCache[GamePropertyKey.MasterClientId]; if (isUpdate) { PhotonNetwork.networkingPeer.UpdateMasterClient(); } } //if (propertiesToCache.ContainsKey(GamePropertyKey.PropsListedInLobby)) //{ // // could be cached but isn't useful //} if (propertiesToCache.ContainsKey((byte)GamePropertyKey.ExpectedUsers)) { this.expectedUsersField = (string[])propertiesToCache[GamePropertyKey.ExpectedUsers]; } // merge the custom properties (from your application) to the cache (only string-typed keys will be kept) this.customPropertiesField.MergeStringKeys(propertiesToCache); } #region Obsoleted variable names [Obsolete("Please use CustomProperties (updated case for naming).")] public Hashtable customProperties { get { return this.CustomProperties; } } [Obsolete("Please use Name (updated case for naming).")] public string name { get { return this.Name; } } [Obsolete("Please use PlayerCount (updated case for naming).")] public int playerCount { get { return this.PlayerCount; } set { this.PlayerCount = value; } } [Obsolete("Please use IsLocalClientInside (updated case for naming).")] public bool isLocalClientInside { get { return this.IsLocalClientInside; } set { this.IsLocalClientInside = value; } } [Obsolete("Please use MaxPlayers (updated case for naming).")] public byte maxPlayers { get { return this.MaxPlayers; } } [Obsolete("Please use IsOpen (updated case for naming).")] public bool open { get { return this.IsOpen; } } [Obsolete("Please use IsVisible (updated case for naming).")] public bool visible { get { return this.IsVisible; } } #endregion }