Unreal Commands & Events Guide
This guide was written as a quick reference step by step guide for setting up a simple Unreal command/event. For more in depth information on each step please check the documentation at https://www.genvidtech.com/for-developers/sdk-latest/
Stop Command (Blueprints)
Create a blueprint that inherits from GameState if you have not already
Set this game state as the default game state in your game mode
- Open your GenvidCommands blueprint. In Begin Play add the following:
-
GenvidCommands code
Stop CollapsedGraph
- We add a new command to the Commands array
The new command is named “Stop” and is Replicated
See documentation for more information on what “replicated” does
We bind this command to a new event named “OnStopCommand”
“OnStopCommand” gets a reference to our game state and calls an event called “ConfirmCommandStop” we will create next
In your Game State BP create an event called “ConfirmCommandStop”
Set it’s Replicates property to Multicast and Reliable
It has a single input of type string named “FullCommand”
Add the following code to it:
-
Event "ConfirmCommandStop" on the Game State BP
Normally you would confirm the command is being sent from a valid location and the command itself is valid. However since this is just a local implementation and there is no input to confirm we just get a reference to our Game Instance and call “CommandStop”
In your GameInstance create a function called “CommandStop”
Add the following code:
GameInstance function "CommandStop"
MyClock should be whatever your clocks class is
If you did not download your clock asset: in your clock BP create a boolean called “StopClock”.
In your clock blueprint - open up the event graph and go to your tick. Add a check to stop clock before doing anything in the tick. If it’s true then don’t do anything. If it’s false then continue as normal
Pulling this all together - we bind a function to a “Stop” command. That function eventually finds its way to the clock actor and flips a bool that causes it to stop ticking
Stop Command (Web)
In your web/config folder open up the web.hcl and make sure the “admin” link templates all end in “/admin”. Then in your web/public folder create an “admin.html” file.
Add the following code to the new html file:
<!doctype html> <html> <head> <title>Genvid Overlay</title> <link rel="stylesheet" href="style.css"> <script src="genvid.umd.js"></script> <script src="genvid-math.umd.js"></script> <script src="overlay-admin.js"></script> </head> <body style="background:black"> <div id="video_player"></div> <div id="overlay"> <tr> <td><button class='commandButton' id='admin_stop_button'>STOP</button></td> </tr> </div> </body> </html>=
We are creating a button with the text “STOP” on the admin link
We reference a new .js file named “overlay-admin.js” file. We will create that next
In the public folder create a file named “overlay-admin.js”. This will be the javascript file that will back the admin version of the steam
Add the following code to the new js file:
class AdminController { constructor(videoPlayerId) { this.videoPlayerId = videoPlayerId; } start() { fetch("/api/public/channels/join", { method: "POST", }) .then((data) => data.json()) .then((res) => this.onChannelJoin(res)) .catch((error) => genvid.error(`Can't get the stream info: ${error}`)); } onChannelJoin(joinRep) { this.client = genvid.createGenvidClient( joinRep.info, joinRep.uri, joinRep.token, this.videoPlayerId ); let StopButton = document.getElementById( "admin_stop_button" ); StopButton.addEventListener( "click", () => { this.stopClock(); }, false ); this.client.start(); } sendCommands(bodyCommands, successMessage, errorMessage) { fetch("/api/admin/commands/game", { method: "POST", body: JSON.stringify(bodyCommands), headers: { "Content-Type": "application/json", }, }) } stopClock(){ const commands = { id: "Stop", value: "Clock:Stop", }; const successMessage = "Command sent."; const errorMessage = "Stop Clock Error"; this.sendCommands(commands, successMessage, errorMessage); } } let admin = new AdminController("video_player"); admin.start();
Notice this file is a bit different then the last js file. This version gives us more control over the controller class. We will be updating the other js file in a similar fashion later
We bind a function that sends a command with the id “Stop” to a button click. Recall we named our command “Stop” in the project. This is how they connect
Go through the steps of setting up the local cluster, package the project (or play in editor), and start up the services
Click on the admin link. The username and password will both be “admin”
Click on the stop command. The clock should immediately stop ticking in the project and eventually stop on stream

Color Change Event (Blueprints)
In your blueprint for genvid events add the following code:
GenvidEvents Blueprint code
-
ColorChange Collapsed Graph
We add a new GenvidEvent to the existing Events property
The name of the event is colorChange and marked as replicated
The event is then bound to “OnColorChangeEvent” which gets our gamestate blueprint and calls “Confirm EventColor Change” which we will make next
In your Game State blueprint create a custom event named “ConfirmEventColorChange”
In the “Replicates” property in the blueprint make it Multicast and Reliable
Give it a single input named “Summary” of type Genvid Event
Create two local ints. One named “Pink” the other named “Black”
Add the following code:
Event "ConfirmEventColorChange" in Game State BP
-
Collapsed Graph "CountColors"
-
Collapsed Graph "ChooseWinner"
Reset the Pink and Black value counts. We will be adding to these
For every result received we loop through their values in CountColors
For every value we see if the value is a “Count” operation. If it is then we check the key for that value.
If the Key is Pink or Blue we add the count value to one or the other.
Once we are done looping we choose the winner
Get our game instance and call “EventColorChange” - a function we will make next passing in a bool that is true if Black is greater than Pink.
In the Game Instance blueprint create a function called “Color Change” that takes in a boolean called “IsBlack”. Then add the following code:
"ColorChange" Function in GameInstance
Get the clock actor and call Color Change which we will make next
Create a new material. Make it pink.
In the clock blueprint implement “ColorChange”. Add the following code:
If the clock was imported everything from the branch onward will already be done. You will only need to create ColorChange and then connect
"ColorChange" on the clock actor BP
Color Change Event (Web)
Go to your web/public folder
Open your index.html file. Add the following code after the existing streams, notifications, and annotations:
<div> <button class='eventButton' id='pink_button'>Pink</button> </div> <div> <button class='eventButton' id='black_button'>Black</button> </div>
We Declare two buttons. One with the text “Pink” the other with the text “Black”
Open your overlay.js file. Replace the contents with the following code:
var genvidClient; class WebController { constructor(videoPlayerId) { this.videoPlayerId = videoPlayerId; } start() { fetch("/api/public/channels/join", { method: "POST", }) .then(function (data) { return data.json() }) .then((res) => this.onChannelJoin(res)) .catch((error) => genvid.error(`Can't get the stream info: ${error}`)); } onChannelJoin(joinRep) { this.genvidClient = genvid.createGenvidClient( joinRep.info, joinRep.uri, joinRep.token, this.videoPlayerId ); this.genvidClient.onStreamsReceived(function (dataStreams) { for (let stream of [...dataStreams.streams, ...dataStreams.annotations]) { for (let frame of stream.frames) { try { frame.user = JSON.parse(frame.data); } catch (e) { console.log(e, frame.data); } } } }); this.genvidClient.onVideoPlayerReady(function () { console.log("Video Player is Ready"); }); this.genvidClient.onDraw(function (frame) { let gameDataFrame = frame.streams["ClockTime"]; if (gameDataFrame && gameDataFrame.user) { update(gameDataFrame.user); } if ("ClockAnnotation" in frame.annotations && frame.annotations["ClockAnnotation"].length){ for (let gameAnnotation of frame.annotations["ClockAnnotation"]) { if (gameAnnotation && gameAnnotation.user) { annotationUpdate(gameAnnotation.user); } } } }); this.genvidClient.onNotificationsReceived(function (notifications) { for (let notification of notifications.notifications) { try { notification.user = JSON.parse(notification.data); notificationUpdate(notification.user); } catch (e) { console.log(e, notification); } } }); let PinkButton = document.getElementById( "pink_button" ); PinkButton.addEventListener( "click", () => { this.onColorChange("pink"); }, false ); let BlackButton = document.getElementById( "black_button" ); BlackButton.addEventListener( "click", () => { this.onColorChange("black"); }, false ); this.genvidClient.start(); } onColorChange(color) { this.genvidClient.sendEventObject({ colorChange: color, }); } } function update(data) { document.getElementById("hour").innerHTML = "Hour: " + Math.round(data.hour); document.getElementById("minute").innerHTML = "Minute: " + Math.round(data.minute); document.getElementById("second").innerHTML = "Second: " + Math.round(data.second); } function notificationUpdate(data) { document.getElementById("notification").innerHTML = "Notification: " + Math.round(data.second); } function annotationUpdate(data) { document.getElementById("annotation").innerHTML = "Annotation: " + Math.round(data.second); } let web = new WebController("video_player"); web.start();
Don’t be intimidated by the changes. Most of it has remained the same - just restructured in a way that gives us more control.
Note there is some new code related to events
We get the pink button and black button and bind to a function named “onColorChanged” but pass a different string value
This is the string value we look for in MulticastConfirmEventColorChange_Implementation
onColorChange sends the string with the key “colorChange”. Recall we named our event in the C++ “colorChange” as well.
Go to your web/config folder and open events.json. Replace the contents with the following code:
{ "version": "1.7.0", "event": { "game": { "maps": [ { "id": "colorChange", "source": "userinput", "where": {"key": ["colorChange"], "name": "<color>", "type": "string"}, "key": ["colorChange", "<color>"], "value": 1 } ], "reductions": [ { "id": "colorChange", "where": {"key": ["colorChange", "<color>"]}, "key": ["<color>"], "value": ["$count"], "period": 250 } ] } } }
Update your “version” number if necessary
This sets up the map reduce of the “colorChange” event
See the documentation for more info on map reduce. The important part is multiple instances of “pink” and “black” are put together and counted
Copy the events.json file to your config folder that is one step above your project
This is where “game.hcl” and “sample.hcl” live
Both the web and client need to know about the event format
Go through the steps of setting up the local cluster, package the project (or play in editor), and start up the services
Click on the non-admin link
Click on the pink or black. The clock hands will shift color depending on what is pressed. If multiple sessions were connected and clicking at the same time, whatever had the most clicks per map-reduce would win out.
Stream with the pink and black event buttons
Events Improvements (Blueprints)
The event is functional. But we can do better.
We will send notifications updating the current bid count of each event and instead only update when the clock strikes a 15 second interval
In your Game State BP promote the “Choose Winner” node to a function also named “ChooseWinner”.
This can be done by right clicking on it
Modify “ChooseWinner” to the following:
Skip Pink & Black being the same
- We no longer do anything if Black and Pink are the same
Then change “ConfirmEventColorChange” to the following:
Don't reset Pink and Black and don't choose a winner
We no longer reset the count of Pink and black
We no longer choose a winner here (although we saved that logic off as a function for use later)
Open your Genvid Stream BP. We will instead choose the winner every 15 seconds on tick. Add the following to the end of your tick function:
-
Choose winner at the end of the tick function
Now we want a stream notification containing the number of bids for each color. Add the following to BeginPlay:
Add the ColorBidNotification Collapsed Graph to GenvidStreams blueprint
-
ColorBidNotification Collapsed Graph
We add a new node named “ColorBindNotification”
This node creates a stream named “ColorBidNotification”
This stream binds to “OnSubmitColorBidsNotification” which creates a string containing the vote numbers via a function we will make below named “GetColorBidsStringNotification”
Create a function named “GetColorBidsStringNotification” with a return called “ColorBidString” of type String. Add the following code:
construct the color bids notification JSON
We get the values of Pink and Black and add them to a JSON string
Events Improvements (Web)
We now have a notification being sent to us we want to intercept. However, first some general improvements.
First open the style.css file and add the following classes:
.btn { opacity: 0.7; border-radius: 50%; padding: 12px; margin: 12px; width:16px; } .black { background-color:rgb(0, 0, 0); border-color:rgb(251,72,196); } .pink { background-color:rgb(251,72,196); border-color:rgb(0, 0, 0); }
We will now have colored buttons that users can click on for the black and pink bids
Open index.html. Replace the contents with the following:
<!doctype html> <html> <head> <title>Genvid Overlay</title> <link rel="stylesheet" href="style.css"> <script src="genvid.umd.js"></script> <script src="genvid-math.umd.js"></script> <script src="overlay.js"></script> </head> <body style="background:black"> <div id="video_player"></div> <div id="overlay"> <div class="child" id="hour">Hour:</div> <div class="child" id="minute">Minute:</div> <div class="child" id="second">Second:</div> <div class="child" id="notification">Notification:</div> <div class="child" id="annotation">Annotation:</div> <div> <button class='btn pink' id='pink_button'></button> </div> <div class="child" id="PinkBid">Pink Bids:</div> <div> <button class='btn black' id='black_button'></button> </div> <div class="child" id="BlackBid">Black Bids:</div> </div> </html>
We now have a place to display the number of bids for each color next to the buttons
Additionally we added some default values to all the streams
We are now ready to receive the new notification. Open “overlay.js”
Replace the onNotificationsReceived override with the following:
this.genvidClient.onNotificationsReceived(function (notifications) { for (let notification of notifications.notifications) { try { notification.user = JSON.parse(notification.data); if (notification.id == "ClockNotification") { notificationUpdate(notification.user); } else if (notification.id == "ColorBidNotification") { colorBidUpdate(notification.user); } } catch (e) { console.log(e, notification); } } });
We now only call notificationUpdate if the id is “ClockNotification”
There is a new call to a function we will create below called “colorBidUpdate” if the notification has the name “ColorBidNotification”
Add the following function at the bottom of the overlay.js file:
function colorBidUpdate(data) { document.getElementById("PinkBid").innerHTML = "Pink Bids: " + Math.round(data.pink); document.getElementById("BlackBid").innerHTML = "Black Bids: " + Math.round(data.black); }
We use the notification to update our overlay with the current bid numbers
Run through the usual steps detailed in the web guide to deploy the client and local cluster. You can now vote for the colors and have their values updated in real time. Additionally the colors only change when a clock hand hit’s a 15 second mark. However the votes are tallied later due to the stream delay.
