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:
- Event Generation (typically issued by the spectator in JavaScript).
- Event Filtering (done using MapReduce inside the Genvid Cluster).
- 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.