Events Overview

Events encapsulate communication between the game process and spectators. They can perform many functions, including funneling spectator inputs, analyzing and responding to chat events, and inspecting logs. They are composed of a key:value pair.

We designed Genvid scalability around a MapReduce algorithm which filters the events coming from multiple sources (web spectators, game client, live chat, logs, etc.) and combines them into a limited number of outcomes. This drastically reduces both frequency and bandwidth impacts.

The basic event flow is:

  1. Event Generation (typically issued by the spectator in JavaScript).
  2. Event Filtering (done using MapReduce inside the Genvid Cluster).
  3. Event Consumption (by the game, the web client, or some other component inside the Genvid Cluster itself).

Event Generation

When viewers interact with the stream inside their browser, developers can trigger events in the system. Genvid sends events using a key:value pair.

Here is how the JavaScript API would work:

genvidClient.sendEvent(["player", "like"], "player1");

In this example, a spectator reports liking "player1", which corresponds to the value. Genvid creates the 2-term key with the values "player" followed by "like".

Event Filtering

Because every spectator can send events, we could end up with a massive number of events that the game process can’t handle efficiently.

To solve this problem, all events sent go through a set of MapReduce filters to transform a potentially huge number of events into a much more reasonable count. Genvid specifies those filters using JSON files deployed to the cluster.

Map example

The Map function takes key:value pairs as input and transforms each into another key:value pair when they meet certain conditions. We refer to this as the Mapping step.

Genvid defines both the map and the transformation using a JSON file.

{
  "id": "playerlike",
  "source": "userinput",
  "where": {
    "key": [ "player", "like" ],
    "name": "<like>",
    "type": "string"
  },
  "key": ["like", "<like>"],
  "value": 1
}

In this example, when a key matching ["player", "like"] enters the system, it produces a new key:value pair. The new key becomes ["like", "$value"], where "$value" corresponds to the string value assigned to that key. The mapping step finishes by assigning “1” as the value of the transformed key.

For example, the following events come in from multiple viewers:

{ "key": ["player", "like"], "value": "player1" }
{ "key": ["player", "like"], "value": "player1" }
{ "key": ["player", "like"], "value": "player2" }
{ "key": ["player", "like"], "value": "player1" }

The map playerlike transforms them into the following:

{ "key": ["like", "player1"], "value": 1 }
{ "key": ["like", "player1"], "value": 1 }
{ "key": ["like", "player2"], "value": 1 }
{ "key": ["like", "player1"], "value": 1 }

Once the data is transformed, we apply the Reduce operation.

Reduce example

The Reduce operation takes key:value pairs as input and transforms them into another set of key:value pairs, but merged together to have fewer of them.

Like the mapping step, Genvid uses JSON for the reduction step.

{
  "id": "playerlike",
  "where": {
    "key": ["like", "<playerName>"]
  },
  "key": ["like", "<playerName>"],
  "value": ["$count"],
  "period": 1000
}

During the reduction step, the operation looks for any key starting with "like"`while ignoring the second parameter. (The second parameter matched will be referable as the token ``playerName` in later steps.)

The operation merges the matching key:value will then be merged, and the resulting valuepairs and assigns the number of times it encountered the original key to the value. Genvid uses this result to transmit the event data.

For example, we receive the following results from the Map function:

[
   { "key": ["like", "player1"], "value": 1 },
   { "key": ["like", "player1"], "value": 1 },
   { "key": ["like", "player2"], "value": 1 },
   { "key": ["like", "player1"], "value": 1 }
]

The Reduce function merges them into the following:

[
  {"key": ["like", "player1"], "value": "3"},
  {"key": ["like", "player2"], "value": "1"}
]

Event Consumption

To receive the filtered results of the Filtering step, clients subscribe to the output of a Reduce rule using its id.

{
  "id": "playerlike"
}

This subscription tells the system to send the Reduce function playerlike results to the subscriber every second.

Note: The event filtering system is memory-less—once it transmits results it wipes and resets all data.