30
a Platform-as-a-Service company Advanced Cloud Script Brendan Vanous Senior Developer Success Engineer

PlayFab Advanced Cloud Script

Embed Size (px)

Citation preview

Page 1: PlayFab Advanced Cloud Script

a Platform-as-a-Service company

Advanced Cloud ScriptBrendan Vanous

Senior Developer Success Engineer

Page 2: PlayFab Advanced Cloud Script

May 3, 2023 2

Overview

• “Getting Started with Cloud Script” Recap• Recap: Calling Cloud Script• Scenarios and Updates• Resources

Page 3: PlayFab Advanced Cloud Script

May 3, 2023 3

“Getting Started with Cloud Script” Recap

• Previous webinar “Getting Started with Cloud Script”• What is Cloud Script• Creating scripts• Revision Control• Webhooks• Debugging• Usage Scenarios• Limitations*

* Updates!

Page 4: PlayFab Advanced Cloud Script

May 3, 2023 4

[Basics] Calling a Cloud Script

• Query for the active Cloud Script URLPOST /client/GetCloudScriptUrl HTTP/1.1Host: {{TitleID}}.playfabapi.comContent-Type: application/jsonX-Authentication:{{SessionTicket}}{ "Testing": true}

• Response{ "code": 200, "status": "OK", "data": { "Url": "https://{{TitleID}}.playfablogic.com/1/test" }}

Page 5: PlayFab Advanced Cloud Script

May 3, 2023 5

[Basics] Calling a Cloud Script

• Launch the Cloud ScriptPOST /1/prod/client/RunCloudScript HTTP/1.1Host: {{TitleID}}.playfablogic.comContent-Type: application/jsonX-Authentication: {{SessionTicket}}{ "ActionId": "onLevelComplete“, "ParamsEncoded": "{\"level\":1}"}

• Response• Standard response code• Handler, version, revision• Custom response data• Log (for debugging)• Runtime

{ "code": 200, "status": "OK", "data": { "ActionId": "onLevelComplete", "Version": 1, "Revision": 43, "Results": { "rewards": [ { "PlayFabId": "14141CB68CBE4956", "Result": false, "ItemId": "TestItem2", "Annotation": "Given by completing level 1", "UnitPrice": 0 } ] }, "ResultsEncoded": "{\"rewards\":[{\"PlayFabId\":\"14141CB68CBE4956\",\"Result\":false,\"ItemId\":\"TestItem2\",\"Annotation\":\"Given by completing level 1\",\"UnitPrice\":0}]}", "ActionLog": "", "ExecutionTime": 0.1680013 }}

Page 6: PlayFab Advanced Cloud Script

May 3, 2023 6

In GitHub

• Examples of the essentials• AsynchronousMatchmaker –

Companion to our blog post• BasicSample – Provided with all

new titles• Photon-Cloud-Integration – Shows

using custom auth and webhooks• Rewards – Granting items and

virtual currency to players

Page 7: PlayFab Advanced Cloud Script

May 3, 2023 7

BasicSample – Hello World

• Because there’s always “Hello World”!handlers.helloWorld = function (args) {

// "currentPlayerId" 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. 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. return { messageValue: message };}

Page 8: PlayFab Advanced Cloud Script

May 3, 2023 8

[Basics] Scenario 1: Updates

• Updating a title is more than just the executable• Changing items• User data• Save values

• User Internal Data to track player data version• Have an “onLogin” handler• Calls current version handler (“updateToRevN”), passing in player data version• If not at this version, call previous version handler• Once returned, update for latest version• onLogin then sets player version

Yeah, don’t do that…

Page 9: PlayFab Advanced Cloud Script

May 3, 2023 9

Updating the User Version

• Roll the changes into one call

handlers.playerLogin = function (args) {

var currentVersion = "7"; var versionKey = "userVersion";

var playerData = server.GetUserInternalData({ PlayFabId: currentPlayerId, Keys: [versionKey] });

var userVersion = playerData.Data[versionKey];

if (userVersion != currentVersion) { UpdateToVersion7(userVersion, currentPlayerId); }

var updateUserDataResult = server.UpdateUserInternalData({ PlayFabId: currentPlayerId, Data: { userVersion: currentVersion } });

log.debug("Set title version for player " + currentPlayerId + " to " + currentVersion);}

function UpdateToVersion7(userVersion, userPlayFabId){ // Here's where you'd update the player's data // changing any items or stat values needed for this version

return null;}

Page 10: PlayFab Advanced Cloud Script

May 3, 2023 10

[Basics] Scenario 2: Player Actions

• For competitive games• Resolution of player action must be server authoritative• Player selects moves, which are sent to Cloud Script• Evaluate moves• Are they possible?• Does the player have the right items?• etc.

• On failure, write info to User Internal Data• Use cheat tracking to have cheaters play together

Page 11: PlayFab Advanced Cloud Script

May 3, 2023 11

BasicSample – Validating Player Action

• Has enough time passed?function processPlayerMove(playerMove) { var now = Date.now(); var playerMoveCooldownInSeconds = 15;

var playerData = server.GetUserInternalData({ PlayFabId: currentPlayerId, Keys: ["last_move_timestamp"] });

var lastMoveTimestampSetting = playerData.Data["last_move_timestamp"];

if (lastMoveTimestampSetting) { var lastMoveTime = Date.parse(lastMoveTimestampSetting.Value); var timeSinceLastMoveInSeconds = (now - lastMoveTime) / 1000; 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.") return false; } }

Page 12: PlayFab Advanced Cloud Script

May 3, 2023 12

BasicSample – Validating Player Action

• If so, update the statistics and the timestamp var playerStats = server.GetUserStatistics({ PlayFabId: currentPlayerId }).UserStatistics;

if (playerStats.movesMade) playerStats.movesMade += 1; else playerStats.movesMade = 1;

server.UpdateUserStatistics({ PlayFabId: currentPlayerId, UserStatistics: playerStats });

server.UpdateUserInternalData({ PlayFabId: currentPlayerId, Data: { last_move_timestamp: new Date(now).toUTCString() } });

return true;}

Page 13: PlayFab Advanced Cloud Script

May 3, 2023 13

[Basics] Scenario 3: Rewards

• Determination of rewards for player actions• Player actions are sent to Cloud Script• Evaluate actions• Has enough time passed?• Are the values reasonable?• etc.

• On failure, write info to User Internal Data• Use cheat tracking to decide how to manage them

Page 14: PlayFab Advanced Cloud Script

May 3, 2023 14

Rewards – onLevelComplete

• Optional: Define rewards in Title Internal Datavar LevelRewards =[

["TestItem1"],["TestItem2"],["TestItem3"],["TestItem1", "TestItem2"],["TestItem2", "TestItem2"],["TestItem3", "TestItem3"]

]

handlers.onLevelComplete = function(args){

var levelNum = args.level;

// Do some basic input validationif(levelNum < 0 || levelNum >= LevelRewards.length){log.info("Invalid level "+levelNum+" completed by "+currentPlayerId);return {};}

Page 15: PlayFab Advanced Cloud Script

May 3, 2023 15

Rewards – onLevelComplete

• Also tracking level completionvar levelCompleteKey = "LevelCompleted"+levelNum;

// Get the user's internal datavar playerInternalData = server.GetUserInternalData({

PlayFabId: currentPlayerId,Keys: [levelCompleteKey]

});

// Did they already complete this level?if(playerInternalData.Data[levelCompleteKey]){

log.info("Player "+currentPlayerId+" already completed level "+levelNum);return {};

}

Page 16: PlayFab Advanced Cloud Script

May 3, 2023 16

Rewards – onLevelComplete

• Grant the rewardvar rewards = LevelRewards[levelNum];

var resultItems = null;if(rewards){

// Grant reward items to player for completing the levelvar itemGrantResult = server.GrantItemsToUser({

PlayFabId: currentPlayerId,Annotation: "Given by completing level "+levelNum,ItemIds: rewards

});

resultItems = itemGrantResult.ItemGrantResults;}

Page 17: PlayFab Advanced Cloud Script

May 3, 2023 17

Rewards – onLevelComplete

• And finally update the level tracking and return the information on the reward

// Mark the level as being completed so they can't get the reward againvar saveData = {};saveData[levelCompleteKey] = "true";server.UpdateUserInternalData({

PlayFabId: currentPlayerId,Data: saveData

});

// Return the results of the item grant so the client can see what they gotreturn {

rewards: resultItems};

}

Page 18: PlayFab Advanced Cloud Script

May 3, 2023 18

[Basics] Scenario 4: Messaging

• Push Messages• Require Server authority• Player attacking another player’s base• Player beat a friend’s score• etc.

• Player-to-player messages• Write them to Shared Group Data, using ID of PlayFabId• Arbitrary messages, with arbitrary payloads

Page 19: PlayFab Advanced Cloud Script

May 3, 2023 19

Using the PlayFab ID as the Key, Part 1

• Really, you’d expect this to work, wouldn’t you?

handlers.messageToPlayer = function (args) {

var messageGroupId = args.toPlayerId + "_messages";

server.UpdateSharedGroupData( { "SharedGroupId": messageGroupId, "Data" : { currentPlayerId : args.messageText } } );}

Page 20: PlayFab Advanced Cloud Script

May 3, 2023 20

Using the PlayFab ID as the Key, Part 1

• But yeah, it doesn’t

{ "code": 200, "status": "OK", "data": { "Data": { "currentPlayerId": { "Value": "Hi there!", "LastUpdated": "2015-10-14T07:25:22.749Z", "Permission": "Private" } } }} Really, you need to watch:

https://www.destroyallsoftware.com/talks/wat

Page 21: PlayFab Advanced Cloud Script

May 3, 2023 21

Using the PlayFab ID as the Key, Part 2

• Here’s how to do it

handlers.messageToPlayer = function (args) {

var messageGroupId = args.toPlayerId + "_messages";

var dataPayload = {}; var keyString = currentPlayerId; dataPayload[keyString] = args.messageText;

server.UpdateSharedGroupData( { "SharedGroupId": messageGroupId, "Data" : dataPayload } );}

Page 22: PlayFab Advanced Cloud Script

May 3, 2023 22

Consuming the Message

• Space is limited – once the message is received, remove ithandlers.checkMessages = function (args) {

var messageGroupId = currentPlayerId + "_messages";

var messageList = server.GetSharedGroupData({"SharedGroupId": messageGroupId}); var dataPayload = {};

for (var key in messageList.Data) { if (messageList.Data.hasOwnProperty(key)) { var message = JSON.parse(messageList.Data[key].Value);

// Take action on the key - display to user, etc.

var keyString = key; dataPayload[keyString] = null; } } server.UpdateSharedGroupData({ "SharedGroupId": messageGroupId, "Data" : dataPayload });}

Page 23: PlayFab Advanced Cloud Script

May 3, 2023 23

[Basics] Scenario 5: Extra Leaderboard Columns• Shared Group Data• Storage not tied to a particular player• Readable by any player (permission set Public)

• Data value per player• Key is the PlayFab ID of the player• Write extra data when updating score• One API call to read players in display

Page 24: PlayFab Advanced Cloud Script

May 3, 2023 24

[New and Improved] Scenario 5: Web API Calls• Whether your own or a third party• The http function of Cloud Script allows for secure calls• Enables integration with any third-party Web API• The question to ask is, what are your needs?

Page 25: PlayFab Advanced Cloud Script

May 3, 2023 25

Option 1: Use Session Ticket

• Using basic authentication mechanism from PlayFab• Use Server/AuthenticateSessionTicket to validate

handlers.externalCall = function (args) {

var url = "https://api.yourdomainhere.com/playfab/someaction";

var method = "post";

var obj = {"dataValue1":value1, "dataValue2":value2}; var contentBody = JSON.stringify(obj);

var contentType = "application/json";

var headers = {}; headers["Authorization"] = args.sessionTicket;

var response = http.request(url,method,contentBody,contentType,headers);}

Page 26: PlayFab Advanced Cloud Script

May 3, 2023 26

Option 2: OAuth2

• More secure solutions may require OAuth2, or similar• Which, as a Web API, we support – no problem• Normal process for OAuth2-type systems• Use a locally stored secret key to request a token from the OAuth2

service• Use that token to access secure calls• Recommendation: Short token lifetime

Page 27: PlayFab Advanced Cloud Script

May 3, 2023 27

Obtaining the Bearer Token

• Use a secret key to obtain a client-specific token• Optional: Store the secret key in PlayFab, and the client never even

sees itfunction GetAccesToken(secretKey){ var url = "https://api.yourdomainhere.com/RequestToken"; var method = "post"; var contentBody = "token_type=Bearer"; var contentType = "application/x-www-form-urlencoded"; var headers = {}; headers["Authorization"] = "Basic "+secretKey;

var response = http.request(url,method,contentBody,contentType,headers);

var finalData = JSON.parse(response); var access_token = finalData["access_token"];

return access_token;}

Page 28: PlayFab Advanced Cloud Script

May 3, 2023 28

Using the Bearer Token

• Once you have the Bearer Token, use it to access custom functionality

• Note: Do not store the Bearer Token for re-use – regenerate it each time

handlers.externalCall = function (args) {

var url = "https://api.yourdomainhere.com/playfab/someaction";

var method = "post";

var obj = {"dataValue1":value1, "dataValue2":value2}; var contentBody = JSON.stringify(obj);

var contentType = "application/json";

    var bearerAccessToken = GetAccessToken(args.secretKey);

    var headers = {};    headers["Authorization"] = "Bearer "+ bearerAccesToken;  

    var response =  http.request(url,method,contentBody,contentType,headers);}

Page 29: PlayFab Advanced Cloud Script

May 3, 2023 29

Resources

• PlayFab Cloud Script Tutorial• https://playfab.com/?post_type=pf_docs&p=1003

• GitHub• https://github.com/PlayFab/CloudScriptSamples

• Dave & Buster’s usage• https://playfab.com/blog/dave-busters-goes-mobile-with-playfabs-help/

Page 30: PlayFab Advanced Cloud Script

QUESTIONS?Twitter @playfabnetwork