/// <summary> /// This is a helper function that verifies that the player's move wasn't made /// too quickly following their previous move, according to the rules of the game. /// If the move is valid, then it updates the player's statistics and profile data. /// This function is called from the "UpdatePlayerMove" handler above and also is /// triggered by the "RoomEventRaised" Photon room event in the Webhook handler /// below. /// /// For this example, the script defines the cooldown period (playerMoveCooldownInSeconds) /// as 15 seconds.A recommended approach for values like this would be to create them in Title /// Data, so that they can be queries in the script with a call to GetTitleData /// (https://api.playfab.com/Documentation/Server/method/GetTitleData). This would allow you to /// make adjustments to these values over time, without having to edit, test, and roll out an /// updated script. /// </summary> /// <param name="playerMove">The player's move object</param> /// <param name="currentPlayerId">The player's PlayFab ID</param> /// <param name="log">The logger object to log to</param> /// <returns>True if the player's move was valid, false otherwise</returns> private static async Task <bool> ProcessPlayerMove(PlayFabServerInstanceAPI serverApi, dynamic playerMove, string currentPlayerId, ILogger log) { var now = DateTime.Now; var playerMoveCooldownInSeconds = -15; var userInternalDataRequest = new GetUserDataRequest { PlayFabId = currentPlayerId, Keys = new List <string> { "last_move_timestamp" } }; var playerDataResponse = await serverApi.GetUserInternalDataAsync(userInternalDataRequest); var playerData = playerDataResponse.Result.Data; var lastMoveTimeStampSetting = playerData["last_move_timestamp"]; if (lastMoveTimeStampSetting != null) { var lastMoveTime = DateTime.Parse(lastMoveTimeStampSetting.Value); var timeSinceLastMoveInSeconds = (now - lastMoveTime) / 1000; log.LogDebug($"lastMoveTime: {lastMoveTime} now: {now} timeSinceLastMoveInSeconds: {timeSinceLastMoveInSeconds}"); if (timeSinceLastMoveInSeconds.TotalSeconds < playerMoveCooldownInSeconds) { log.LogError($"Invalid move - time since last move: {timeSinceLastMoveInSeconds}s less than minimum of {playerMoveCooldownInSeconds}s."); return(false); } } var getStatsRequest = new GetPlayerStatisticsRequest { PlayFabId = currentPlayerId }; var playerStats = (await serverApi.GetPlayerStatisticsAsync(getStatsRequest)).Result.Statistics; var movesMade = 0; for (var i = 0; i < playerStats.Count; i++) { if (string.IsNullOrEmpty(playerStats[i].StatisticName)) { movesMade = playerStats[i].Value; } } movesMade += 1; var updateStatsRequest = new UpdatePlayerStatisticsRequest { PlayFabId = currentPlayerId, Statistics = new List <StatisticUpdate> { new StatisticUpdate { StatisticName = "movesMade", Value = movesMade } } }; await serverApi.UpdatePlayerStatisticsAsync(updateStatsRequest); await serverApi.UpdateUserInternalDataAsync(new UpdateUserInternalDataRequest { PlayFabId = currentPlayerId, Data = new Dictionary <string, string> { { "last_move_timestamp", DateTime.Now.ToUniversalTime().ToString() }, { "last_move", PlayFabSimpleJson.SerializeObject(playerMove) } } }); return(true); }