handlers.helloWorld = function (args, context) { var message = "Hello " + currentPlayerId + "!"; log.info(message); var inputValue = null; if (args && args.inputValue) inputValue = args.inputValue; log.debug("helloWorld:", { input: args.inputValue }); return { messageValue: message }; }; handlers.makeAPICall = function (args, context) { var request = { PlayFabId: currentPlayerId, Statistics: [{ StatisticName: "Level", Value: 2 }] }; var playerStatResult = server.UpdatePlayerStatistics(request); }; handlers.makeEntityAPICall = function (args, context) { var entityProfile = context.currentEntity; var apiResult = entity.SetObjects({ Entity: entityProfile.Entity, Objects: [ { ObjectName: "obj1", DataObject: { foo: "some server computed value", prop1: args.prop1 } } ] }); return { profile: entityProfile, setResult: apiResult.SetResults[0].SetResult }; }; 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 response = http.request(url, httpMethod, content, contentType, headers); return { responseContent: response }; }; handlers.handlePlayStreamEventAndProfile = function (args, context) { var psEvent = context.playStreamEvent; var profile = context.playerProfile; 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 }; }; handlers.completedLevel = function (args, context) { var level = args.levelName; var monstersKilled = args.monstersKilled; var updateUserDataResult = server.UpdateUserInternalData({ PlayFabId: currentPlayerId, Data: { lastLevelCompleted: level } }); log.debug("Set lastLevelCompleted for player " + currentPlayerId + " to " + level); 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); }; handlers.updatePlayerMove = function (args) { var validMove = processPlayerMove(args); return { validMove: validMove }; }; 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; } } var playerStats = server.GetPlayerStatistics({ PlayFabId: currentPlayerId }).Statistics; 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: { last_move_timestamp: new Date(now).toUTCString(), last_move: JSON.stringify(playerMove) } }); return true; } handlers.unlockHighSkillContent = function (args, context) { var playerStatUpdatedEvent = context.playStreamEvent; var request = { PlayFabId: currentPlayerId, Data: { "HighSkillContent": "true", "XPAtHighSkillUnlock": playerStatUpdatedEvent.StatisticValue.toString() } }; var playerInternalData = server.UpdateUserInternalData(request); log.info('Unlocked HighSkillContent for ' + context.playerProfile.DisplayName); return { profile: context.playerProfile }; }; handlers.RoomCreated = function (args) { log.debug("Room Created - Game: " + args.GameId + " MaxPlayers: " + args.CreateOptions.MaxPlayers); }; handlers.RoomJoined = function (args) { log.debug("Room Joined - Game: " + args.GameId + " PlayFabId: " + args.UserId); }; handlers.RoomLeft = function (args) { log.debug("Room Left - Game: " + args.GameId + " PlayFabId: " + args.UserId); }; handlers.RoomClosed = function (args) { log.debug("Room Closed - Game: " + args.GameId); }; handlers.RoomPropertyUpdated = function (args) { log.debug("Room Property Updated - Game: " + args.GameId); }; handlers.RoomEventRaised = function (args) { var eventData = args.Data; log.debug("Event Raised - Game: " + args.GameId + " Event Type: " + eventData.eventType); switch (eventData.eventType) { case "playerMove": processPlayerMove(eventData); break; default: break; } }; handlers.OnPurchaseItem = function (args, context) { if (!context.playerId) { return { error: "Player not authenticated." }; } var price = args.Price; var virtualCurrency = args.VirtualCurrency; var itemId = args.ItemId; if (!price || !virtualCurrency || !itemId) { return { error: "Missing required parameters (Price, VirtualCurrency, ItemId)." }; } var playerCurrencyResult = server.GetUserVirtualCurrency({ PlayFabId: context.playerId, VirtualCurrency: virtualCurrency }); if (!playerCurrencyResult || !playerCurrencyResult.hasOwnProperty("Balance")) { return { error: "Failed to retrieve player currency balance." }; } var playerBalance = playerCurrencyResult.Balance; if (playerBalance < price) { return { error: "Insufficient virtual currency." }; } var inventoryResult = server.GetUserInventory({ PlayFabId: context.playerId }); if (!inventoryResult || !inventoryResult.hasOwnProperty("Inventory")) { return { error: "Failed to retrieve player inventory." }; } var alreadyOwnsItem = inventoryResult.Inventory.some(function (item) { return item.ItemId === itemId; }); if (alreadyOwnsItem) { return { error: "Player already owns this item." }; } var subtractVcResult = server.SubtractUserVirtualCurrency({ PlayFabId: context.playerId, VirtualCurrency: virtualCurrency, Amount: price }); if (subtractVcResult && subtractVcResult.hasOwnProperty("VirtualCurrency")) { var grantResult = server.GrantItemsToUser({ PlayFabId: context.playerId, ItemIds: [itemId] }); if (grantResult && grantResult.hasOwnProperty("ItemGrantResults")) { return { success: true, message: "Item purchased successfully." }; } else { return { error: "Failed to grant item." }; } } else { return { error: "Failed to deduct virtual currency." }; } }; handlers.ReportPlayer = function(Args, Context) { var ModeratorIds = ["EC74E75B1B1A734A", "460FF2EEE164627E", "7282295D242013A7", "CCFB875DF0B9AF74"]; var ReasonDurations = { "Hate Speech": 2592000, "Cheating": 2592000, "Toxicity": 604800 }; if (ModeratorIds.includes(Args.ReporterId)) { server.BanUsers({ PlayFabIds: [Args.TargetId], DurationInSeconds: ReasonDurations[Args.Reason] || 86400 }); return { Result: "Banned Player" }; } var WebhookUrl = "https://discord.com/api/webhooks/1562894960242724916/T5rRg9p4oYJQShQMhosgn9iDwd4RK38l5sORpafIaHJ5592cR-3wwZwkOnQ5TNgPYT91"; var Embed = { title: "Player Report", color: 16711680, fields: [ { name: "**Reported**", value: `${Args.TargetId}\n${Args.TargetName || "Not Found"}\n#${Args.TargetColor || "FFFFFF"}`, inline: true }, { name: "**Reporter**", value: `${Args.ReporterId}\n${Args.ReporterName || "Not Found"}\n#${Args.ReporterColor || "FFFFFF"}`, inline: true }, { name: "**Details**", value: `Room: ${Args.Room || "Not Found"}\nReason: ${Args.Reason || "Not Found"}` } ] }; http.request(WebhookUrl, "POST", JSON.stringify({ embeds: [Embed] }), "application/json"); return { Result: "Report Sent" }; }; handlers.checkUserBanStatus = function (args, context) { var bans = server.GetUserBans({ PlayFabId: currentPlayerId }).BanData; for (var i = 0; i < bans.length; i++) { var ban = bans[i]; if (!ban.Deactivated && ban.Expiration) { var now = new Date(); var expiration = new Date(ban.Expiration); var hoursRemaining = Math.max(0, Math.ceil((expiration - now) / (1000 * 60 * 60))); return { isBanned: true, message: "You are banned for " + hoursRemaining + " hour(s). Reason: " + ban.Reason }; } } return { isBanned: false }; };