From 722c1a30ee65f9f03963325aba6a3878b91ba84c Mon Sep 17 00:00:00 2001 From: Matt Augustine Date: Fri, 13 May 2016 13:59:22 -0700 Subject: [PATCH 01/12] Basic sample updated with latest Cloud Script features (#2) * Basic API, HTTP and PlayStream event examples --- BasicSample/basic_sample.js | 188 ++++++++++++++++++++++++++---------- 1 file changed, 135 insertions(+), 53 deletions(-) diff --git a/BasicSample/basic_sample.js b/BasicSample/basic_sample.js index 552c141..17676f9 100644 --- a/BasicSample/basic_sample.js +++ b/BasicSample/basic_sample.js @@ -1,48 +1,120 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////// // -// Welcome to your first Cloud Script revision. -// The examples here provide a quick introduction to using Cloud Script and some -// ideas about how you might use it in your game. +// Welcome to your first Cloud Script revision! // -// There are two approaches for invoking Cloud Script: calling handler functions directly -// from the game client using the "RunCloudScript" API, or triggering Photon Webhooks associated with -// room events. Both approaches are demonstrated in this file. You can use one or the other, or both. +// Cloud Script runs in the PlayFab cloud and has full access to the PlayFab Game Server API +// (https://api.playfab.com/Documentation/Server), and it runs in the context of a securely +// authenticated player, so you can use it to implement logic for your game that is safe from +// client-side exploits. // -// Feel free to use this as a starting point for your game server logic, or to replace it altogether. -// If you have any questions or need advice on where to begin, -// check out the resources at https://playfab.com/cloud-script or check our forums at -// https://support.playfab.com. For issues which are confidential (involving sensitive intellectual -// property, for example), please contact our Developer Success team directly at devrel@playfab.com. +// Cloud Script functions can also make web requests to external HTTP +// endpoints, such as a database or private API for your title, which makes them a flexible +// way to integrate with your existing backend systems. // -// - The PlayFab Team +// There are several different options for calling Cloud Script functions: +// +// 1) Your game client calls them directly using the "ExecuteCloudScript" API, +// passing in the function name and arguments in the request and receiving the +// function return result in the response. +// (https://api.playfab.com/Documentation/Client/method/ExecuteCloudScript) +// +// 2) You create PlayStream event actions that call them when a particular +// event occurs, passing in the event and associated player profile data. +// (https://api.playfab.com/playstream/docs) +// +// 3) For titles using the Photon Add-on (https://playfab.com/marketplace/photon/), +// Photon room events trigger webhooks which call corresponding Cloud Script functions. +// +// The following examples demonstrate all three options. // /////////////////////////////////////////////////////////////////////////////////////////////////////// -// This is a Cloud Script handler function. It runs in the PlayFab cloud and -// has full access to the PlayFab Game Server API -// (https://api.playfab.com/Documentation/Server). You can invoke the function -// from your game client by calling the "RunCloudScript" API -// (https://api.playfab.com/Documentation/Client/method/RunCloudScript) and -// specifying "helloWorld" for the "ActionId" field. -handlers.helloWorld = function (args) { - - // "currentPlayerId" is initialized to the PlayFab ID of the player logged-in on the game client. +// This is a Cloud Script function. "args" is set to the value of the "FunctionParameter" +// parameter of the ExecuteCloudScript API. +// (https://api.playfab.com/Documentation/Client/method/ExecuteCloudScript) +// "context" contains additional information when the Cloud Script function is called from a PlayStream action. +handlers.helloWorld = function (args, context) { + + // The pre-defined "currentPlayerId" variable is initialized to the PlayFab ID of the player logged-in on the game client. // Cloud Script handles authenticating the player automatically. var message = "Hello " + currentPlayerId + "!"; - // You can use the "log" object to write out debugging statements. The "log" object has - // three functions corresponding to logging level: debug, info, and error. + // You can use the "log" object to write out debugging statements. It has + // three functions corresponding to logging level: debug, info, and error. These functions + // take a message string and an optional object. log.info(message); - - // Whatever value you return from a CloudScript handler function is passed back - // to the game client. It is set in the "Results" property of the object returned by the - // RunCloudScript API. Any log statments generated by the handler function are also included - // in the "ActionLog" field of the RunCloudScript result, so you can use them to assist in - // debugging and error handling. + log.debug("helloWorld:", { input: args.inputValue }); + + // The value you return from a Cloud Script function is passed back + // to the game client in the ExecuteCloudScript API response, along with any log statements + // and additional diagnostic information, such as any errors returned by API calls or external HTTP + // requests. They are also included in the optional player_executed_cloudscript PlayStream event + // generated by the function execution. + // (https://api.playfab.com/playstream/docs/PlayStreamEventModels/player/player_executed_cloudscript) return { messageValue: message }; } +// This is a simple example of making a PlayFab server API call +handlers.makeAPICall = function (args, context) { + + // The pre-defined "server" object has functions corresponding to each PlayFab server API + // (https://api.playfab.com/Documentation/Server). It is automatically + // authenticated as your title and handles all communication with + // the PlayFab API, so you don't have to write extra code to issue HTTP requests. + var playerStatResult = server.UpdateUserStatistics ( + { + PlayFabId: currentPlayerId, + UserStatistics: {Level:2} + } + ); +} + +// This is a simple example of making a web request to an external HTTP API. +handlers.makeHTTPRequest = function (args, context) { + var headers = { + "X-MyCustomHeader": "Some Value" + }; + + var body = { + input: args, + userId: currentPlayerId, + mode: "foobar" + }; + + var url = "http://httpbin.org/status/200"; + var content = JSON.stringify(body); + var httpMethod = "post"; + var contentType = "application/json"; + var logRequestAndResponse = true; + + // The pre-defined http object makes synchronous HTTP requests + var response = http.request(url, httpMethod, content, contentType, headers, logRequestAndResponse); + return { responseContent: response }; +} + +// This is a simple example of a function that is called from a +// PlayStream event action. (https://playfab.com/introducing-playstream/) +handlers.handlePlayStreamEventAndProfile = function (args, context) { + + // The event that triggered the action + // (https://api.playfab.com/playstream/docs/PlayStreamEventModels) + var psEvent = context.playStreamEvent; + + // The profile data of the player associated with the event + // (https://api.playfab.com/playstream/docs/PlayStreamProfileModels) + var profile = context.playerProfile; + + // Post data about the event to an external API + var content = JSON.stringify({user: profile.PlayerId, event: psEvent.EventName}); + var response = http.request('https://httpbin.org/status/200', 'post', content, 'application/json', null, true); + + return { externalAPIResponse: response }; +} + + +// Below are some examples of using Cloud Script in slightly more realistic scenarios + // This is a function that the game client would call whenever a player completes // a level. It updates a setting in the player's data that only game server // code can write - it is read-only on the client - and it updates a player @@ -51,19 +123,10 @@ handlers.helloWorld = function (args) { // A funtion like this could be extended to perform validation on the // level completion data to detect cheating. It could also do things like // award the player items from the game catalog based on their performance. -handlers.completedLevel = function (args) { - - // "args" is set to the value of the "Params" field of the object passed in to - // RunCloudScript from the client. It contains whatever properties you want to pass - // into your Cloud Script function. In this case it contains information about - // the level a player has completed. +handlers.completedLevel = function (args, context) { var level = args.levelName; var monstersKilled = args.monstersKilled; - - // The "server" object has functions for each PlayFab server API - // (https://api.playfab.com/Documentation/Server). It is automatically - // authenticated as your title and handles all communication with - // the PlayFab API, so you don't have to write the code to make web requests. + var updateUserDataResult = server.UpdateUserInternalData({ PlayFabId: currentPlayerId, Data: { @@ -97,10 +160,12 @@ handlers.updatePlayerMove = function (args) { // 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) +// 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 -// https://api.playfab.com/Documentation/Server/method/GetTitleData. This would allow you to +// 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. function processPlayerMove(playerMove) { @@ -142,27 +207,44 @@ function processPlayerMove(playerMove) { server.UpdateUserInternalData({ PlayFabId: currentPlayerId, Data: { - last_move_timestamp: new Date(now).toUTCString() + last_move_timestamp: new Date(now).toUTCString(), + last_move: JSON.stringify(playerMove) } }); return true; } +// This is an example of using PlayStream real-time segmentation to trigger +// game logic based on player behavior. (https://playfab.com/introducing-playstream/) +// The function is called when a player_statistic_changed PlayStream event causes a player +// to enter a segment defined for high skill players. It sets a key value in +// the player's internal data which unlocks some new content for the player. +handlers.unlockHighSkillContent = function(args, context) +{ + var playerStatUpdatedEvent = context.playStreamEvent; + + var playerInternalData = server.UpdateUserInternalData( + { + PlayFabId: currentPlayerId, + "Data": { + "HighSkillContent": true, + "XPAtHighSkillUnlock": playerStatUpdatedEvent.StatisticValue + } + }); + log.info('Unlocked HighSkillContent for ' + context.playerProfile.DisplayName); + return { profile: context.playerProfile }; +} // Photon Webhooks Integration // -// Note (April 2015): This is feature is currently in limited access Beta. To request early -// access please email beta@playfab.com. -// // The following functions are examples of Photon Cloud Webhook handlers. -// When you enable Photon integration in the Game Manager, your Photon applications -// are automatically configured to authenticate players using their PlayFab accounts -// and to fire events that trigger your CloudScript Webhook handlers, if defined. -// This makes it easier than ever to incorporate server logic into your game. -// -// For more information, see https://playfab.com/using-photon-playfab +// When you enable the Photon Add-on (https://playfab.com/marketplace/photon/) +// in the Game Manager, your Photon applications are automatically configured +// to authenticate players using their PlayFab accounts and to fire events that +// trigger your Cloud Script Webhook handlers, if defined. +// This makes it easier than ever to incorporate multiplayer server logic into your game. // Triggered automatically when a Photon room is first created From 2d7e553b7e8386f0d6aad3f9fc6c6995e1b40728 Mon Sep 17 00:00:00 2001 From: Brendan Vanous Date: Wed, 15 Jun 2016 15:48:49 -0700 Subject: [PATCH 02/12] Updating to use Get/UpdatePlayerStatistics. Summary: Changed from GetUserStatistics and UpdateUserStatistics to GetPlayerStatistics and UpdatePlayerStatistics, and changed associated logic for working with the keys/values. Test Plan: N/A Reviewers: #infrastructure_team, skatir Reviewed By: #infrastructure_team, skatir Differential Revision: https://phab.playfabdev.com/D1617 --- .arcconfig | 3 +++ BasicSample/basic_sample.js | 30 ++++++++++++++++-------------- 2 files changed, 19 insertions(+), 14 deletions(-) create mode 100644 .arcconfig diff --git a/.arcconfig b/.arcconfig new file mode 100644 index 0000000..e880677 --- /dev/null +++ b/.arcconfig @@ -0,0 +1,3 @@ +{ + "phabricator.uri" : "https://phab.playfabdev.com/" +} \ No newline at end of file diff --git a/BasicSample/basic_sample.js b/BasicSample/basic_sample.js index 17676f9..41e15a2 100644 --- a/BasicSample/basic_sample.js +++ b/BasicSample/basic_sample.js @@ -62,10 +62,10 @@ handlers.makeAPICall = function (args, context) { // (https://api.playfab.com/Documentation/Server). It is automatically // authenticated as your title and handles all communication with // the PlayFab API, so you don't have to write extra code to issue HTTP requests. - var playerStatResult = server.UpdateUserStatistics ( + var playerStatResult = server.UpdatePlayerStatistics( { PlayFabId: currentPlayerId, - UserStatistics: {Level:2} + Statistics: [{ StatisticName: "Level", Value: 2 }] } ); } @@ -136,11 +136,11 @@ handlers.completedLevel = function (args, context) { log.debug("Set lastLevelCompleted for player " + currentPlayerId + " to " + level); - server.UpdateUserStatistics({ + server.UpdatePlayerStatistics({ PlayFabId: currentPlayerId, - UserStatistics: { - level_monster_kills: monstersKilled - } + Statistics: [ + { StatisticName: "level_monster_kills", Value: monstersKilled } + ] }); log.debug("Updated level_monster_kills stat for player " + currentPlayerId + " to " + monstersKilled); @@ -190,18 +190,20 @@ function processPlayerMove(playerMove) { } } - var playerStats = server.GetUserStatistics({ + var playerStats = server.GetPlayerStatistics({ PlayFabId: currentPlayerId - }).UserStatistics; + }).Statistics; - if (playerStats.movesMade) - playerStats.movesMade += 1; - else - playerStats.movesMade = 1; + var moves = { StatisticName: "movesMade", Value: 1 }; + for (i = 0; i < playerStats.length; ++i) { + if (playerStats[i].StatisticName === "movesMade") { + moves.Value = playerStats[i].Value + 1; + } + } - server.UpdateUserStatistics({ + server.UpdatePlayerStatistics({ PlayFabId: currentPlayerId, - UserStatistics: playerStats + Statistics: [moves] }); server.UpdateUserInternalData({ From 41efdddcf5a0a70769d58c0ec8a40cb8479dc676 Mon Sep 17 00:00:00 2001 From: Brendan Vanous Date: Sun, 18 Sep 2016 15:09:15 -0700 Subject: [PATCH 03/12] Update basic_sample.js --- BasicSample/basic_sample.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/BasicSample/basic_sample.js b/BasicSample/basic_sample.js index 41e15a2..736c233 100644 --- a/BasicSample/basic_sample.js +++ b/BasicSample/basic_sample.js @@ -86,10 +86,9 @@ handlers.makeHTTPRequest = function (args, context) { var content = JSON.stringify(body); var httpMethod = "post"; var contentType = "application/json"; - var logRequestAndResponse = true; - + // The pre-defined http object makes synchronous HTTP requests - var response = http.request(url, httpMethod, content, contentType, headers, logRequestAndResponse); + var response = http.request(url, httpMethod, content, contentType, headers); return { responseContent: response }; } From 025e9933e26022fcc21b80f7f16cc3e412f72817 Mon Sep 17 00:00:00 2001 From: Calvin Kong Date: Thu, 6 Oct 2016 11:51:01 -0700 Subject: [PATCH 04/12] Fix backward compatibility with older docs --- BasicSample/basic_sample.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/BasicSample/basic_sample.js b/BasicSample/basic_sample.js index 736c233..abb0066 100644 --- a/BasicSample/basic_sample.js +++ b/BasicSample/basic_sample.js @@ -44,7 +44,12 @@ handlers.helloWorld = function (args, context) { // three functions corresponding to logging level: debug, info, and error. These functions // take a message string and an optional object. log.info(message); - log.debug("helloWorld:", { input: args.inputValue }); + var inputValue = null; + if (args != null && args != undefined) + { + inputValue = args.inputValue; + } + log.debug("helloWorld:", { input: inputValue }); // The value you return from a Cloud Script function is passed back // to the game client in the ExecuteCloudScript API response, along with any log statements From 907b964c306f01b994e1d67c40349e4eb13bd5f0 Mon Sep 17 00:00:00 2001 From: Paul Gilmore Date: Wed, 1 Feb 2017 16:52:48 -0800 Subject: [PATCH 05/12] Update and rename README.txt to README.md --- BasicSample/{README.txt => README.md} | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) rename BasicSample/{README.txt => README.md} (52%) diff --git a/BasicSample/README.txt b/BasicSample/README.md similarity index 52% rename from BasicSample/README.txt rename to BasicSample/README.md index 778543b..960d655 100644 --- a/BasicSample/README.txt +++ b/BasicSample/README.md @@ -1,11 +1,14 @@ Cloud Script Basic Sample (basic_sample.js): ----------------------------------------------------------------------- +---- + +Note to self: This is not the authoritative copy of this file. Changing this file does not update existing or new titles. The real location is stored in playfab server cloudlogic config. + This file provides basic Cloud Script examples of: - . helloWorld - Using the currentPlayerId (the logged-in user), output logging, and returning values - . completedLevel - Updating user statistics and user internal data (data which cannot be read or written by the client) - . updatePlayerMove, processPlayerMove - Calling a function from within Cloud Script, reading and updating user statistics, updating user internal data, basic server-side validation (checking that the reported value is within reason) - . RoomCreated, RoomJoined, RoomLeft, RoomClosed, RoomEventRaised - Handlers for managing webhook calls from a Photon Cloud server (see this document for more information: https://playfab.com/using-photon-playfab) + * helloWorld - Using the currentPlayerId (the logged-in user), output logging, and returning values + * completedLevel - Updating user statistics and user internal data (data which cannot be read or written by the client) + * updatePlayerMove, processPlayerMove - Calling a function from within Cloud Script, reading and updating user statistics, updating user internal data, basic server-side validation (checking that the reported value is within reason) + * RoomCreated, RoomJoined, RoomLeft, RoomClosed, RoomEventRaised - Handlers for managing webhook calls from a Photon Cloud server (see this document for more information: https://playfab.com/using-photon-playfab) -For more information on using Cloud Script in PlayFab, please refer to this guide: - https://playfab.com/cloud-script +For more information on using Cloud Script in PlayFab, please refer to our Example: + * https://github.com/PlayFab/SdkTestingCloudScript From 9ba51673ac9d0f7c7641b231380afece96bada8d Mon Sep 17 00:00:00 2001 From: Paul Gilmore Date: Wed, 1 Feb 2017 17:04:43 -0800 Subject: [PATCH 06/12] Update README.md --- BasicSample/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BasicSample/README.md b/BasicSample/README.md index 960d655..99c21d7 100644 --- a/BasicSample/README.md +++ b/BasicSample/README.md @@ -1,7 +1,7 @@ Cloud Script Basic Sample (basic_sample.js): ---- -Note to self: This is not the authoritative copy of this file. Changing this file does not update existing or new titles. The real location is stored in playfab server cloudlogic config. +Please note that the Cloud Script automatically loaded as Revision 1 in all new titles is not specifically copied from this source. We do make an effort to keep them in sync, but if you do notice any differences, please feel free to let us know via the PlayFab forums. This file provides basic Cloud Script examples of: From 13f601ec7c5001656fe2aae7c658a43e66eccf5a Mon Sep 17 00:00:00 2001 From: pgilmorepf Date: Wed, 1 Feb 2017 17:35:22 -0800 Subject: [PATCH 07/12] Updating the InitialRevision.js Summary: When this is accepted, I will go update the S3 bucket which is the real authority for this code: https://console.aws.amazon.com/s3/home?region=us-west-2#&bucket=cloudscript-samples&prefix= This is the corrections that are based on the stricter requirements defined by CloudScript. Test Plan: Manual Reviewers: calvin, matt-augustine, bvanous Reviewed By: bvanous Differential Revision: https://phab.playfabdev.com/D3196 --- BasicSample/basic_sample.js | 115 +++++++++++++++++------------------- 1 file changed, 54 insertions(+), 61 deletions(-) diff --git a/BasicSample/basic_sample.js b/BasicSample/basic_sample.js index abb0066..f2102d1 100644 --- a/BasicSample/basic_sample.js +++ b/BasicSample/basic_sample.js @@ -45,11 +45,9 @@ handlers.helloWorld = function (args, context) { // take a message string and an optional object. log.info(message); var inputValue = null; - if (args != null && args != undefined) - { + if (args && args.inputValue) inputValue = args.inputValue; - } - log.debug("helloWorld:", { input: inputValue }); + log.debug("helloWorld:", { input: args.inputValue }); // The value you return from a Cloud Script function is passed back // to the game client in the ExecuteCloudScript API response, along with any log statements @@ -58,22 +56,22 @@ handlers.helloWorld = function (args, context) { // generated by the function execution. // (https://api.playfab.com/playstream/docs/PlayStreamEventModels/player/player_executed_cloudscript) return { messageValue: message }; -} +}; // This is a simple example of making a PlayFab server API call handlers.makeAPICall = function (args, context) { - + var request = { + PlayFabId: currentPlayerId, Statistics: [{ + StatisticName: "Level", + Value: 2 + }] + }; // The pre-defined "server" object has functions corresponding to each PlayFab server API // (https://api.playfab.com/Documentation/Server). It is automatically // authenticated as your title and handles all communication with // the PlayFab API, so you don't have to write extra code to issue HTTP requests. - var playerStatResult = server.UpdatePlayerStatistics( - { - PlayFabId: currentPlayerId, - Statistics: [{ StatisticName: "Level", Value: 2 }] - } - ); -} + var playerStatResult = server.UpdatePlayerStatistics(request); +}; // This is a simple example of making a web request to an external HTTP API. handlers.makeHTTPRequest = function (args, context) { @@ -95,7 +93,7 @@ handlers.makeHTTPRequest = function (args, context) { // The pre-defined http object makes synchronous HTTP requests var response = http.request(url, httpMethod, content, contentType, headers); return { responseContent: response }; -} +}; // This is a simple example of a function that is called from a // PlayStream event action. (https://playfab.com/introducing-playstream/) @@ -110,11 +108,11 @@ handlers.handlePlayStreamEventAndProfile = function (args, context) { var profile = context.playerProfile; // Post data about the event to an external API - var content = JSON.stringify({user: profile.PlayerId, event: psEvent.EventName}); - var response = http.request('https://httpbin.org/status/200', 'post', content, 'application/json', null, true); - + var content = JSON.stringify({ user: profile.PlayerId, event: psEvent.EventName }); + var response = http.request('https://httpbin.org/status/200', 'post', content, 'application/json', null); + return { externalAPIResponse: response }; -} +}; // Below are some examples of using Cloud Script in slightly more realistic scenarios @@ -139,16 +137,15 @@ handlers.completedLevel = function (args, context) { }); log.debug("Set lastLevelCompleted for player " + currentPlayerId + " to " + level); - - server.UpdatePlayerStatistics({ - PlayFabId: currentPlayerId, - Statistics: [ - { StatisticName: "level_monster_kills", Value: monstersKilled } - ] - }); - + var request = { + PlayFabId: currentPlayerId, Statistics: [{ + StatisticName: "level_monster_kills", + Value: monstersKilled + }] + }; + server.UpdatePlayerStatistics(request); log.debug("Updated level_monster_kills stat for player " + currentPlayerId + " to " + monstersKilled); -} +}; // In addition to the Cloud Script handlers, you can define your own functions and call them from your handlers. @@ -156,7 +153,7 @@ handlers.completedLevel = function (args, context) { handlers.updatePlayerMove = function (args) { var validMove = processPlayerMove(args); return { validMove: validMove }; -} +}; // This is a helper function that verifies that the player's move wasn't made @@ -189,7 +186,7 @@ function processPlayerMove(playerMove) { log.debug("lastMoveTime: " + lastMoveTime + " now: " + now + " timeSinceLastMoveInSeconds: " + timeSinceLastMoveInSeconds); if (timeSinceLastMoveInSeconds < playerMoveCooldownInSeconds) { - log.error("Invalid move - time since last move: " + timeSinceLastMoveInSeconds + "s less than minimum of " + playerMoveCooldownInSeconds + "s.") + log.error("Invalid move - time since last move: " + timeSinceLastMoveInSeconds + "s less than minimum of " + playerMoveCooldownInSeconds + "s."); return false; } } @@ -197,19 +194,18 @@ function processPlayerMove(playerMove) { var playerStats = server.GetPlayerStatistics({ PlayFabId: currentPlayerId }).Statistics; - - var moves = { StatisticName: "movesMade", Value: 1 }; - for (i = 0; i < playerStats.length; ++i) { - if (playerStats[i].StatisticName === "movesMade") { - moves.Value = playerStats[i].Value + 1; - } - } - - server.UpdatePlayerStatistics({ - PlayFabId: currentPlayerId, - Statistics: [moves] - }); - + var movesMade = 0; + for (var i = 0; i < playerStats.length; i++) + if (playerStats[i].StatisticName === "") + movesMade = playerStats[i].Value; + movesMade += 1; + var request = { + PlayFabId: currentPlayerId, Statistics: [{ + StatisticName: "movesMade", + Value: movesMade + }] + }; + server.UpdatePlayerStatistics(request); server.UpdateUserInternalData({ PlayFabId: currentPlayerId, Data: { @@ -226,22 +222,19 @@ function processPlayerMove(playerMove) { // The function is called when a player_statistic_changed PlayStream event causes a player // to enter a segment defined for high skill players. It sets a key value in // the player's internal data which unlocks some new content for the player. -handlers.unlockHighSkillContent = function(args, context) -{ +handlers.unlockHighSkillContent = function (args, context) { var playerStatUpdatedEvent = context.playStreamEvent; - - var playerInternalData = server.UpdateUserInternalData( - { + var request = { PlayFabId: currentPlayerId, - "Data": { - "HighSkillContent": true, - "XPAtHighSkillUnlock": playerStatUpdatedEvent.StatisticValue - } - }); - + Data: { + "HighSkillContent": "true", + "XPAtHighSkillUnlock": playerStatUpdatedEvent.StatisticValue.toString() + } + }; + var playerInternalData = server.UpdateUserInternalData(request); log.info('Unlocked HighSkillContent for ' + context.playerProfile.DisplayName); return { profile: context.playerProfile }; -} +}; // Photon Webhooks Integration // @@ -256,29 +249,29 @@ handlers.unlockHighSkillContent = function(args, context) // Triggered automatically when a Photon room is first created handlers.RoomCreated = function (args) { log.debug("Room Created - Game: " + args.GameId + " MaxPlayers: " + args.CreateOptions.MaxPlayers); -} +}; // Triggered automatically when a player joins a Photon room handlers.RoomJoined = function (args) { log.debug("Room Joined - Game: " + args.GameId + " PlayFabId: " + args.UserId); -} +}; // Triggered automatically when a player leaves a Photon room handlers.RoomLeft = function (args) { log.debug("Room Left - Game: " + args.GameId + " PlayFabId: " + args.UserId); -} +}; // Triggered automatically when a Photon room closes // Note: currentPlayerId is undefined in this function handlers.RoomClosed = function (args) { log.debug("Room Closed - Game: " + args.GameId); -} +}; // Triggered automatically when a Photon room game property is updated. // Note: currentPlayerId is undefined in this function -handlers.RoomPropertyUpdated = function(args) { +handlers.RoomPropertyUpdated = function (args) { log.debug("Room Property Updated - Game: " + args.GameId); -} +}; // Triggered by calling "OpRaiseEvent" on the Photon client. The "args.Data" property is // set to the value of the "customEventContent" HashTable parameter, so you can use @@ -295,4 +288,4 @@ handlers.RoomEventRaised = function (args) { default: break; } -} +}; From 1b2038373e1f3672ac61fa261c91abf49fc39aa1 Mon Sep 17 00:00:00 2001 From: Brendan Vanous Date: Fri, 31 Mar 2017 22:49:53 -0700 Subject: [PATCH 08/12] Update basic_sample.js --- BasicSample/basic_sample.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BasicSample/basic_sample.js b/BasicSample/basic_sample.js index f2102d1..0f4b3fa 100644 --- a/BasicSample/basic_sample.js +++ b/BasicSample/basic_sample.js @@ -47,7 +47,7 @@ handlers.helloWorld = function (args, context) { var inputValue = null; if (args && args.inputValue) inputValue = args.inputValue; - log.debug("helloWorld:", { input: args.inputValue }); + log.debug("helloWorld:", { input: inputValue }); // The value you return from a Cloud Script function is passed back // to the game client in the ExecuteCloudScript API response, along with any log statements From 1df439279805c0f72fd330d79e3650bd37a417b9 Mon Sep 17 00:00:00 2001 From: Keegan Campbell Date: Mon, 25 Mar 2019 09:31:52 -0700 Subject: [PATCH 09/12] Fixed cloudscript quickstart guide link. (#4) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0f054f8..54409d2 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This repository contains several examples on how to use Cloud Script in PlayFab. 2. Prerequisites: ---- -To get the most from these samples you should be very familiar with PlayFab's Cloud Script service. Check out our Cloud Script [getting started guide](https://playfab.com/cloud-script-guide). +To get the most from these samples you should be very familiar with PlayFab's Cloud Script service. Check out our Cloud Script [getting started guide](https://docs.microsoft.com/en-us/gaming/playfab/features/automation/cloudscript/quickstart). To connect to the PlayFab service, your machine must be running TLS v1.1 or better. * For Windows, this means Windows 7 and above @@ -57,4 +57,4 @@ Our Developer Success Team can assist with answering any questions as well as pr 8. Version History: ---- * (v1.00) Initial Release -* (v1.10) Updated for latest changes to examples \ No newline at end of file +* (v1.10) Updated for latest changes to examples From 884b073af68abdbb2fffcb7b565b5f0e7b5d96ce Mon Sep 17 00:00:00 2001 From: Hamza Lazaar Date: Wed, 10 Jul 2019 21:09:05 +0100 Subject: [PATCH 10/12] FIXED: default placeholder for Photon Realtime WebHooks handlers (#5) * FIXED: incorrect info in comment removed * CHANGED: EvCode was added after initial sample * CHANGED: playstream events replacing useless logs --- BasicSample/basic_sample.js | 108 ++++++++++++++++++++++++++++++++---- 1 file changed, 98 insertions(+), 10 deletions(-) diff --git a/BasicSample/basic_sample.js b/BasicSample/basic_sample.js index 0f4b3fa..23b4d48 100644 --- a/BasicSample/basic_sample.js +++ b/BasicSample/basic_sample.js @@ -248,39 +248,127 @@ handlers.unlockHighSkillContent = function (args, context) { // Triggered automatically when a Photon room is first created handlers.RoomCreated = function (args) { - log.debug("Room Created - Game: " + args.GameId + " MaxPlayers: " + args.CreateOptions.MaxPlayers); + server.WritePlayerEvent({ + EventName : "room_created", + PlayFabId: args.UserId, + Body: { + WebHook: { + AppVersion: args.AppVersion, + Region: args.Region, + GameId: args.GameId, + Type: args.Type, + ActorNr: args.ActorNr, + CreateOptions: args.CreateOptions + } + } + }); }; // Triggered automatically when a player joins a Photon room handlers.RoomJoined = function (args) { - log.debug("Room Joined - Game: " + args.GameId + " PlayFabId: " + args.UserId); + server.WritePlayerEvent({ + EventName: "room_joined", + PlayFabId: args.UserId, + Body: { + WebHook: { + AppVersion: args.AppVersion, + Region: args.Region, + GameId: args.GameId, + ActorNr: args.ActorNr + } + } + }); }; // Triggered automatically when a player leaves a Photon room handlers.RoomLeft = function (args) { - log.debug("Room Left - Game: " + args.GameId + " PlayFabId: " + args.UserId); + server.WritePlayerEvent({ + EventName: "room_left", + PlayFabId: args.UserId, + Body: { + WebHook: { + AppVersion: args.AppVersion, + Region: args.Region, + GameId: args.GameId, + Type: args.Type, + ActorNr: args.ActorNr, + IsInactive: args.IsInactive + } + } + }); }; // Triggered automatically when a Photon room closes // Note: currentPlayerId is undefined in this function handlers.RoomClosed = function (args) { - log.debug("Room Closed - Game: " + args.GameId); + server.WriteTitleEvent({ + EventName: "room_closed", + Body: { + WebHook: { + AppVersion: args.AppVersion, + Region: args.Region, + GameId: args.GameId, + Type: args.Type, + ActorCount: args.ActorCount + } + } + }); }; // Triggered automatically when a Photon room game property is updated. -// Note: currentPlayerId is undefined in this function handlers.RoomPropertyUpdated = function (args) { - log.debug("Room Property Updated - Game: " + args.GameId); + if (args.Type === "Game") { + server.WritePlayerEvent({ + EventName: "room_properties_updated", + PlayFabId: args.UserId, + Body: { + WebHook: { + AppVersion: args.AppVersion, + Region: args.Region, + GameId: args.GameId, + ActorNr: args.ActorNr, + Properties: args.Properties + } + } + }); + } else { // "Actor" + server.WritePlayerEvent({ + EventName: "player_roperties_updated", + PlayFabId: args.UserId, + Body: { + WebHook: { + AppVersion: args.AppVersion, + Region: args.Region, + GameId: args.GameId, + ActorNr: args.ActorNr, + TargetActor: args.TargetActor, + Properties: args.Properties + } + } + }); + } }; // Triggered by calling "OpRaiseEvent" on the Photon client. The "args.Data" property is // set to the value of the "customEventContent" HashTable parameter, so you can use // it to pass in arbitrary data. handlers.RoomEventRaised = function (args) { - var eventData = args.Data; - log.debug("Event Raised - Game: " + args.GameId + " Event Type: " + eventData.eventType); - - switch (eventData.eventType) { + server.WritePlayerEvent({ + EventName: "event_raised", + PlayFabId: args.UserId, + Body: { + WebHook: { + AppVersion: args.AppVersion, + Region: args.Region, + GameId: args.GameId, + ActorNr: args.ActorNr, + EvCode: args.EvCode + } + } + }); + + var eventData = args.Data; + switch (eventData.eventType) { // use args.EvCode instead of embedding eventType in payload case "playerMove": processPlayerMove(eventData); break; From 59dca4b659428201dd8531907f9719ad592b639f Mon Sep 17 00:00:00 2001 From: Todd Bello Date: Tue, 22 Dec 2020 17:36:00 -0800 Subject: [PATCH 11/12] Create genConfig.json --- genConfig.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 genConfig.json diff --git a/genConfig.json b/genConfig.json new file mode 100644 index 0000000..8b567a0 --- /dev/null +++ b/genConfig.json @@ -0,0 +1,6 @@ +{ + "default": { + "templateFolder": "SdkTestingCloudScript", + "versionKey": "SdkTestingCloudScript" + } +} From 54fa3da662be227d7b59022494b98f9e8ca61dbb Mon Sep 17 00:00:00 2001 From: Manoj Shankar AJ <89421255+manandavijay@users.noreply.github.com> Date: Wed, 2 Nov 2022 15:36:56 -0700 Subject: [PATCH 12/12] GetPlayersInSegment API usage samples for GetProfilesAsync:true (#7) --- GetPlayersInSegmentSample/README.md | 8 +++ .../get_players_in_segment_api_sample.js | 64 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 GetPlayersInSegmentSample/README.md create mode 100644 GetPlayersInSegmentSample/get_players_in_segment_api_sample.js diff --git a/GetPlayersInSegmentSample/README.md b/GetPlayersInSegmentSample/README.md new file mode 100644 index 0000000..83f5bdd --- /dev/null +++ b/GetPlayersInSegmentSample/README.md @@ -0,0 +1,8 @@ +## Samples for GetPlayersInSegment API usage in CloudScript: + +#### API doc: [Get Players In Segment](https://learn.microsoft.com/en-us/rest/api/playfab/server/play-stream/get-players-in-segment?view=playfab-rest) + +#### The file get_players_in_segment_api_sample.js provides examples of: + +- GetPlayersInSegmentSample: Using the GetPlayersInSegment API to get all the player profiles in a Segment and do some processing on the profiles +- GetSegmentPlayerCountSample: Using the GetPlayersInSegment API to get the count of player profiles in a Segment \ No newline at end of file diff --git a/GetPlayersInSegmentSample/get_players_in_segment_api_sample.js b/GetPlayersInSegmentSample/get_players_in_segment_api_sample.js new file mode 100644 index 0000000..4dcce79 --- /dev/null +++ b/GetPlayersInSegmentSample/get_players_in_segment_api_sample.js @@ -0,0 +1,64 @@ +handlers.GetPlayersInSegmentSample = function (args, context) { + + /* + Sample code to use the GetPlayersInSegment API to process the player profiles in a Segment. + The GetPlayersInSegment API pages through the all the player profiles + in the Segment in batches of size 'MaxBatchSize'. + API Doc: https://learn.microsoft.com/en-us/rest/api/playfab/server/play-stream/get-players-in-segment?view=playfab-rest + */ + + var request = { + GetProfilesAsync: true, // setting to 'true' is highly recommended to avoid network timeouts + MaxBatchSize: 1000, // 1000 is the default value. Maximum is 10,000 + SegmentId: "AAAAAAAAA" // provide your SegmentId here OR you can add SegmentId in the JSON args sent from PlayFab caller/game client and use that + } + + // make the first GetPlayersInSegment API call + var playersInSegmentResult = server.GetPlayersInSegment(request); + + // process until continuation token is not null to get all the profiles in this Segment + while (playersInSegmentResult.ContinuationToken != null) + { + // get the current batch of player profiles + var playerProfiles = playersInSegmentResult.PlayerProfiles; + + if (playerProfiles && playerProfiles.length > 0) + { + for(let i=0;i