Documentation

Did you find this helpful?

LiveOps Example: Non-Unique Cyclic Events

Topics: PlayStream Cloud Script Handlers, Title Data, Event History Tool

Philosophy: Limited time events give players a sense of day-to-day urgency. Done properly, cycling events boost your player engagement consistently over time, by a small amount.  These types of events should be for consistent and sustainable sales behavior.  They should not be used for big store price changes, or big sales on items that do not match a consistent player engagement pattern. Players will understand that another event is coming soon, and they will anticipate purchasing content they want when it is on sale. They also know that today's sale will be available again later. Nonetheless, it gives them something to focus on today.

Example: Daily and weekly Cyclic Events

This example runs an active events list implemented with a TitleData key-value pair, and with Cloud Script modifying that list on a schedule.  Use Title Data to keep a list of activeEvents. Your client should load this TitleData entry and use it to optionally download CDN files and/or present the additional content to the player, when active.

The following Cloud Script will update the list of active events on a static cycling schedule. For this example we demonstrate day-of-the-week events, and an arbitrary selection of weekly events (colors). Other types of events, such as holidays, can co-exist in the same list of active events.  Client code only needs to load the active events, with no unique knowledge of how events update.

// Cloud Script/Javascript

// It's important for this example to have a clear idea of what this data looks like
// Your real data would only be in TitleData, stored as a json string in the "activeEvents" key
var EXAMPLE_STORE_CYCLE = {
    "daily": ["daily_monday", "daily_tuesday", "daily_wednesday", "daily_thursday", "daily_friday", "daily_saturday", "daily_sunday"],
    "weekly": ["weekly_red", "weekly_green", "weekly_blue"],
    "holiday": [null, "Thanksgiving"]
};
var DEBUG_ENABLED = true; // Allows you to call manually with ExecuteCloudScript. Set this to false in production
// Read TitleData, getting live active events, and the static information about event cycles
function GetTitleEventInfo() {
    var titleRequest = { Keys: ["activeEvents", "storeCycles"] };
    var titleResponse = server.GetTitleData(titleRequest);
    var activeEvents = null;
    if (titleResponse.Data.hasOwnProperty("activeEvents"))
        activeEvents = JSON.parse(titleResponse.Data["activeEvents"]);
    if (!activeEvents)
        activeEvents = [];
    var storeCycles = null;
    if (titleResponse.Data.hasOwnProperty("storeCycles"))
        storeCycles = JSON.parse(titleResponse.Data["storeCycles"]);
    ;
    if (!storeCycles)
        storeCycles = EXAMPLE_STORE_CYCLE;
    return {
        activeEvents: activeEvents,
        storeCycles: storeCycles
    };
}
// Update TitleData, setting new live active events
function SetTitleEventInfo(activeEvents) {
    var titleRequest = { Key: "activeEvents", Value: JSON.stringify(activeEvents) };
    server.SetTitleData(titleRequest);
}
function CycleEvent(cycleType, cycleTo) {
    if (cycleTo === void 0) { cycleTo = null; }
    var eventInfo = GetTitleEventInfo();
    var cycleList = eventInfo.storeCycles[cycleType];
    var prevIndex = 0;
    for (var i = 0; i < cycleList.length; i++) {
        for (var j = 0; j < eventInfo.activeEvents.length; j++) {
            if (eventInfo.activeEvents[j] === cycleList[i]) {
                eventInfo.activeEvents.splice(j, 1);
                prevIndex = i;
            }
        }
    }
    if (!cycleTo)
        cycleTo = cycleList[(prevIndex + 1) % cycleList.length];
    if (cycleTo)
        eventInfo.activeEvents.push(cycleTo);
    SetTitleEventInfo(eventInfo.activeEvents);
    return eventInfo.activeEvents;
}
handlers.CycleDailyEvent = function (args, context) {
    if (!DEBUG_ENABLED && !context)
        throw "This can only be called from PlayStream"; // Safety check to prevent Clients from changing events, and/or accidents
    return CycleEvent("daily");
};
handlers.CycleWeeklyEvent = function (args, context) {
    if (!DEBUG_ENABLED && !context)
        throw "This can only be called from PlayStream"; // Safety check to prevent Clients from changing events, and/or accidents
    return CycleEvent("weekly");
};
handlers.DisableHoliday = function (args, context) {
    if (!DEBUG_ENABLED && !context)
        throw "This can only be called from PlayStream"; // Safety check to prevent Clients from changing events, and/or accidents
    return CycleEvent("holiday", null);
};
// Each Holiday-Enable needs its own handler since context cannot contain any parameters.
// You could use additional title-data to determine when to activate/deactivate holidays
handlers.EnableThanksgiving = function (args, context) {
    if (!DEBUG_ENABLED && !context)
        throw "This can only be called from PlayStream"; // Safety check to prevent Clients from changing events, and/or accidents
    return CycleEvent("holiday", "Thanksgiving");
};
//# sourceMappingURL=LiveOpsExample.js.map

This code will work great for any cyclic event cycle.  Feel free to use or modify this code to implement your own cycle.  As an example, an "every day of the month" cycle may have 31 entries and read the current date as an input to which event index to load. Implementing that option is left as an exercise for the reader.

You can use Automation Tasks to schedule the cycling events automatically:

Click each event to see the details (both shown side by side)

Evaluate Your Cyclic Event

The most important aspect of LiveOps is analyzing the effectiveness of your strategy. The Event History Tool under Analytics can give you powerful query abilities into the behavior of your players and your game.

In this case, we are going to look at the event history for two iterations of an example cyclic event, specifically a cycling set of stores. The next example, the stores were called "LiveOpsStore1" and "LiveOpsStore2".  Each store ran for its set duration, and then cycled to the next. The following graph shows an Event search for the store name "LiveOpsStore1":

The red trend-line shows that the store received lots of player attention at launch, and then tapered off over time. Activity on LiveOpsStore1 stops as LiveOpsStore2 launches:

Again, LiveOpsStore2 gets lots of player attention at launch, and the trend-line tapers off over time. Searching both store names gives you a combined view:

A healthy cyclic Event/Store pattern should show a repeating pattern of player engagement. If one of the events in your cycle has low engagement, then you should evaluate why. Exact causes and effects must be determined on a case by case basis.

External factors could include: Holidays, Day-of-the-week, Other game launches, or other real life interference.

Game factors could include: Too-difficult content, Too-easy content, overpriced content, underpriced content, etc.

Use RARE and MINOR adjustments to your cycle to determine the causes and effects in your cyclic pattern, and adjust accordingly to maintain a set of guidelines for your game to keep your players engaged.



Did you find this helpful?