Пример #1
0
        /// <summary>
        /// Updates and synchronizes this Player's Custom Properties. Optionally, expectedProperties can be provided as condition.
        /// </summary>
        /// <remarks>
        /// Custom Properties are a set of string keys and arbitrary values which is synchronized
        /// for the players in a Room. They are available when the client enters the room, as
        /// they are in the response of OpJoin and OpCreate.
        ///
        /// Custom Properties either relate to the (current) Room or a Player (in that Room).
        ///
        /// Both classes locally cache the current key/values and make them available as
        /// property: CustomProperties. This is provided only to read them.
        /// You must use the method SetCustomProperties to set/modify them.
        ///
        /// Any client can set any Custom Properties anytime (when in a room).
        /// It's up to the game logic to organize how they are best used.
        ///
        /// You should call SetCustomProperties only with key/values that are new or changed. This reduces
        /// traffic and performance.
        ///
        /// Unless you define some expectedProperties, setting key/values is always permitted.
        /// In this case, the property-setting client will not receive the new values from the server but
        /// instead update its local cache in SetCustomProperties.
        ///
        /// If you define expectedProperties, the server will skip updates if the server property-cache
        /// does not contain all expectedProperties with the same values.
        /// In this case, the property-setting client will get an update from the server and update it's
        /// cached key/values at about the same time as everyone else.
        ///
        /// The benefit of using expectedProperties can be only one client successfully sets a key from
        /// one known value to another.
        /// As example: Store who owns an item in a Custom Property "ownedBy". It's 0 initally.
        /// When multiple players reach the item, they all attempt to change "ownedBy" from 0 to their
        /// actorNumber. If you use expectedProperties {"ownedBy", 0} as condition, the first player to
        /// take the item will have it (and the others fail to set the ownership).
        ///
        /// Properties get saved with the game state for Turnbased games (which use IsPersistent = true).
        /// </remarks>
        /// <param name="propertiesToSet">Hashtable of Custom Properties to be set. </param>
        /// <param name="expectedValues">If non-null, these are the property-values the server will check as condition for this update.</param>
        /// <param name="webFlags">Defines if this SetCustomProperties-operation gets forwarded to your WebHooks. Client must be in room.</param>
        public void SetCustomProperties(Hashtable propertiesToSet, Hashtable expectedValues = null, WebFlags webFlags = null)
        {
            if (propertiesToSet == null)
            {
                return;
            }

            Hashtable customProps        = propertiesToSet.StripToStringKeys() as Hashtable;
            Hashtable customPropsToCheck = expectedValues.StripToStringKeys() as Hashtable;


            // no expected values -> set and callback
            bool noCas = customPropsToCheck == null || customPropsToCheck.Count == 0;


            if (noCas)
            {
                this.CustomProperties.Merge(customProps);
                this.CustomProperties.StripKeysWithNullValues();
            }

            // send (sync) these new values if in room
            if (this.RoomReference != null && this.RoomReference.IsLocalClientInside)
            {
                this.RoomReference.LoadBalancingClient.loadBalancingPeer.OpSetPropertiesOfActor(this.actorID, customProps, customPropsToCheck, webFlags);
            }
        }
Пример #2
0
 /// <summary>Uses OpSetPropertiesOfActor to sync this player's NickName (server is being updated with this.NickName).</summary>
 private void SetPlayerNameProperty()
 {
     if (this.RoomReference != null && this.RoomReference.IsLocalClientInside)
     {
         Hashtable properties = new Hashtable();
         properties[ActorProperties.PlayerName] = this.nickName;
         this.RoomReference.LoadBalancingClient.loadBalancingPeer.OpSetPropertiesOfActor(this.ID, properties);
     }
 }
Пример #3
0
        /// <summary>
        /// Creates a player instance.
        /// To extend and replace this Player, override LoadBalancingPeer.CreatePlayer().
        /// </summary>
        /// <param name="nickName">NickName of the player (a "well known property").</param>
        /// <param name="actorID">ID or ActorNumber of this player in the current room (a shortcut to identify each player in room)</param>
        /// <param name="isLocal">If this is the local peer's player (or a remote one).</param>
        /// <param name="playerProperties">A Hashtable of custom properties to be synced. Must use String-typed keys and serializable datatypes as values.</param>
        protected internal Player(string nickName, int actorID, bool isLocal, Hashtable playerProperties)
        {
            this.IsLocal  = isLocal;
            this.actorID  = actorID;
            this.NickName = nickName;

            this.CustomProperties = new Hashtable();
            this.InternalCacheProperties(playerProperties);
        }
Пример #4
0
        /// <summary>
        /// Attempts to remove all current expected users from the server's Slot Reservation list.
        /// </summary>
        /// <remarks>
        /// Note that this operation can conflict with new/other users joining. They might be
        /// adding users to the list of expected users before or after this client called ClearExpectedUsers.
        ///
        /// This room's expectedUsers value will update, when the server sends a successful update.
        ///
        /// Internals: This methods wraps up setting the ExpectedUsers property of a room.
        /// </remarks>
        public void ClearExpectedUsers()
        {
            Hashtable props = new Hashtable();

            props[GamePropertyKey.ExpectedUsers] = new string[0];
            Hashtable expected = new Hashtable();

            expected[GamePropertyKey.ExpectedUsers] = this.ExpectedUsers;
            this.LoadBalancingClient.OpSetPropertiesOfRoom(props, expected);
        }
Пример #5
0
        /// <summary>
        /// Enables you to define the properties available in the lobby if not all properties are needed to pick a room.
        /// </summary>
        /// <remarks>
        /// Limit the amount of properties sent to users in the lobby to improve speed and stability.
        /// </remarks>
        /// <param name="propertiesListedInLobby">An array of custom room property names to forward to the lobby.</param>
        public void SetPropertiesListedInLobby(string[] propertiesListedInLobby)
        {
            Hashtable customProps = new Hashtable();

            customProps[GamePropertyKey.PropsListedInLobby] = propertiesListedInLobby;

            bool sent = this.LoadBalancingClient.OpSetPropertiesOfRoom(customProps);

            if (sent)
            {
                this.propertiesListedInLobby = propertiesListedInLobby;
            }
        }
        /// <summary>
        /// This method copies all string-typed keys of the original into a new Hashtable.
        /// </summary>
        /// <remarks>
        /// Does not recurse (!) into hashes that might be values in the root-hash.
        /// This does not modify the original.
        /// </remarks>
        /// <param name="original">The original IDictonary to get string-typed keys from.</param>
        /// <returns>New Hashtable containing only string-typed keys of the original.</returns>
        public static Hashtable StripToStringKeys(this IDictionary original)
        {
            Hashtable target = new Hashtable();

            if (original != null)
            {
                foreach (object key in original.Keys)
                {
                    if (key is string)
                    {
                        target[key] = original[key];
                    }
                }
            }

            return(target);
        }
Пример #7
0
        /// <summary>
        /// Updates and synchronizes this Room's Custom Properties. Optionally, expectedProperties can be provided as condition.
        /// </summary>
        /// <remarks>
        /// Custom Properties are a set of string keys and arbitrary values which is synchronized
        /// for the players in a Room. They are available when the client enters the room, as
        /// they are in the response of OpJoin and OpCreate.
        ///
        /// Custom Properties either relate to the (current) Room or a Player (in that Room).
        ///
        /// Both classes locally cache the current key/values and make them available as
        /// property: CustomProperties. This is provided only to read them.
        /// You must use the method SetCustomProperties to set/modify them.
        ///
        /// Any client can set any Custom Properties anytime (when in a room).
        /// It's up to the game logic to organize how they are best used.
        ///
        /// You should call SetCustomProperties only with key/values that are new or changed. This reduces
        /// traffic and performance.
        ///
        /// Unless you define some expectedProperties, setting key/values is always permitted.
        /// In this case, the property-setting client will not receive the new values from the server but
        /// instead update its local cache in SetCustomProperties.
        ///
        /// If you define expectedProperties, the server will skip updates if the server property-cache
        /// does not contain all expectedProperties with the same values.
        /// In this case, the property-setting client will get an update from the server and update it's
        /// cached key/values at about the same time as everyone else.
        ///
        /// The benefit of using expectedProperties can be only one client successfully sets a key from
        /// one known value to another.
        /// As example: Store who owns an item in a Custom Property "ownedBy". It's 0 initally.
        /// When multiple players reach the item, they all attempt to change "ownedBy" from 0 to their
        /// actorNumber. If you use expectedProperties {"ownedBy", 0} as condition, the first player to
        /// take the item will have it (and the others fail to set the ownership).
        ///
        /// Properties get saved with the game state for Turnbased games (which use IsPersistent = true).
        /// </remarks>
        /// <param name="propertiesToSet">Hashtable of Custom Properties that changes.</param>
        /// <param name="expectedProperties">Provide some keys/values to use as condition for setting the new values. Client must be in room.</param>
        /// <param name="webFlags">Defines if this SetCustomProperties-operation gets forwarded to your WebHooks. Client must be in room.</param>
        public virtual void SetCustomProperties(Hashtable propertiesToSet, Hashtable expectedProperties = null, WebFlags webFlags = null)
        {
            Hashtable customProps = propertiesToSet.StripToStringKeys() as Hashtable;

            // merge (and delete null-values), unless we use CAS (expected props)
            if (expectedProperties == null || expectedProperties.Count == 0)
            {
                this.CustomProperties.Merge(customProps);
                this.CustomProperties.StripKeysWithNullValues();
            }

            // send (sync) these new values if in room
            if (this.IsLocalClientInside)
            {
                this.LoadBalancingClient.loadBalancingPeer.OpSetPropertiesOfRoom(customProps, expectedProperties, webFlags);
            }
        }
Пример #8
0
        /// <summary>
        /// Asks the server to assign another player as Master Client of your current room.
        /// </summary>
        /// <remarks>
        /// RaiseEvent has the option to send messages only to the Master Client of a room.
        /// SetMasterClient affects which client gets those messages.
        ///
        /// This method calls an operation on the server to set a new Master Client, which takes a roundtrip.
        /// In case of success, this client and the others get the new Master Client from the server.
        ///
        /// SetMasterClient tells the server which current Master Client should be replaced with the new one.
        /// It will fail, if anything switches the Master Client moments earlier. There is no callback for this
        /// error. All clients should get the new Master Client assigned by the server anyways.
        ///
        /// See also: MasterClientId
        /// </remarks>
        /// <param name="masterClientPlayer">The player to become the next Master Client.</param>
        /// <returns>False when this operation couldn't be done currently. Requires a v4 Photon Server.</returns>
        public bool SetMasterClient(Player masterClientPlayer)
        {
            if (!this.IsLocalClientInside)
            {
                this.LoadBalancingClient.DebugReturn(DebugLevel.WARNING, "SetMasterClient can only be called for the current room (being in one).");
                return(false);
            }

            Hashtable newProps = new Hashtable()
            {
                { GamePropertyKey.MasterClientId, masterClientPlayer.ID }
            };
            Hashtable prevProps = new Hashtable()
            {
                { GamePropertyKey.MasterClientId, this.MasterClientId }
            };

            return(this.LoadBalancingClient.OpSetPropertiesOfRoom(newProps, prevProps));
        }
Пример #9
0
        /// <summary>Caches properties for new Players or when updates of remote players are received. Use SetCustomProperties() for a synced update.</summary>
        /// <remarks>
        /// This only updates the CustomProperties and doesn't send them to the server.
        /// Mostly used when creating new remote players, where the server sends their properties.
        /// </remarks>
        public virtual void InternalCacheProperties(Hashtable properties)
        {
            if (properties == null || properties.Count == 0 || this.CustomProperties.Equals(properties))
            {
                return;
            }

            if (properties.ContainsKey(ActorProperties.PlayerName))
            {
                string nameInServersProperties = (string)properties[ActorProperties.PlayerName];
                if (nameInServersProperties != null)
                {
                    if (this.IsLocal)
                    {
                        // the local playername is different than in the properties coming from the server
                        // so the local nickName was changed and the server is outdated -> update server
                        // update property instead of using the outdated nickName coming from server
                        if (!nameInServersProperties.Equals(this.nickName))
                        {
                            this.SetPlayerNameProperty();
                        }
                    }
                    else
                    {
                        this.NickName = nameInServersProperties;
                    }
                }
            }
            if (properties.ContainsKey(ActorProperties.UserId))
            {
                this.UserId = (string)properties[ActorProperties.UserId];
            }
            if (properties.ContainsKey(ActorProperties.IsInactive))
            {
                this.IsInactive = (bool)properties[ActorProperties.IsInactive]; //TURNBASED new well-known propery for players
            }

            this.CustomProperties.MergeStringKeys(properties);
            this.CustomProperties.StripKeysWithNullValues();
        }
Пример #10
0
        /// <summary>Copies "well known" properties to fields (IsVisible, etc) and caches the custom properties (string-keys only) in a local hashtable.</summary>
        /// <param name="propertiesToCache">New or updated properties to store in this RoomInfo.</param>
        protected internal virtual void InternalCacheProperties(Hashtable propertiesToCache)
        {
            if (propertiesToCache == null || propertiesToCache.Count == 0 || this.customProperties.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 = (bool)propertiesToCache[GamePropertyKey.Removed];
                if (this.removedFromList)
                {
                    return;
                }
            }

            // fetch the "well known" properties of the room, if available
            if (propertiesToCache.ContainsKey(GamePropertyKey.MaxPlayers))
            {
                this.maxPlayers = (byte)propertiesToCache[GamePropertyKey.MaxPlayers];
            }

            if (propertiesToCache.ContainsKey(GamePropertyKey.IsOpen))
            {
                this.isOpen = (bool)propertiesToCache[GamePropertyKey.IsOpen];
            }

            if (propertiesToCache.ContainsKey(GamePropertyKey.IsVisible))
            {
                this.isVisible = (bool)propertiesToCache[GamePropertyKey.IsVisible];
            }

            if (propertiesToCache.ContainsKey(GamePropertyKey.PlayerCount))
            {
                this.PlayerCount = (int)((byte)propertiesToCache[GamePropertyKey.PlayerCount]);
            }

            if (propertiesToCache.ContainsKey(GamePropertyKey.CleanupCacheOnLeave))
            {
                this.autoCleanUp = (bool)propertiesToCache[GamePropertyKey.CleanupCacheOnLeave];
            }

            if (propertiesToCache.ContainsKey(GamePropertyKey.MasterClientId))
            {
                this.masterClientId = (int)propertiesToCache[GamePropertyKey.MasterClientId];
            }

            if (propertiesToCache.ContainsKey(GamePropertyKey.PropsListedInLobby))
            {
                this.propertiesListedInLobby = propertiesToCache[GamePropertyKey.PropsListedInLobby] as string[];
            }

            if (propertiesToCache.ContainsKey((byte)GamePropertyKey.ExpectedUsers))
            {
                this.expectedUsers = (string[])propertiesToCache[GamePropertyKey.ExpectedUsers];
            }

            // merge the custom properties (from your application) to the cache (only string-typed keys will be kept)
            this.customProperties.MergeStringKeys(propertiesToCache);
            this.customProperties.StripKeysWithNullValues();
        }
Пример #11
0
        /// <summary>
        /// Constructs a RoomInfo to be used in room listings in lobby.
        /// </summary>
        /// <param name="roomName">Name of the room and unique ID at the same time.</param>
        /// <param name="roomProperties">Properties for this room.</param>
        protected internal RoomInfo(string roomName, Hashtable roomProperties)
        {
            this.InternalCacheProperties(roomProperties);

            this.name = roomName;
        }