This document discusses using WebSockets in Unity games and describes how to implement a finite state machine to handle asynchronous WebSocket events on the main thread. It begins with an overview of WebSockets and their advantages over polling. It then demonstrates how to connect to a WebSocket server and handle events like connection, receiving messages, and disconnection using the WebSocket Sharp library. To address Unity's single-threaded model, it presents a finite state machine pattern that queues asynchronous WebSocket events to be processed on the main thread. Sample code is provided to illustrate setting up and transitioning between states to safely coordinate the WebSocket connection and events.
What’s a WebSocket?
●Bidirectional TCP connection
● Initiated from an HTTP connection by
sending Upgrade header
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYU=
Sec-WebSocket-Protocol: chat
4.
Why WebSockets?
● Servercan push messages instead of being
polled
● Polling often leads to large percentage of
request load being worthless
● Client disconnection can be part of API
5.
Why not WebSockets?
●Servers can’t be purely stateless
● How does a client on server A interact with a
client on server B?
● What happens when a server crashes?
● What if a client has a spotty network
connection?
6.
Mix and match
●Use WebSockets where remote push is a
killer app
● Use plain old REST for everything else
● Gracefully degrade from WebSockets to
REST when network connectivity is poor
● HTTP Kit is a perfect fit!
7.
● Open sourceC# WebSockets client
https://github.com/sta/websocket-sharp/
● Available on the Unity Asset Store
https://www.assetstore.unity3d.com/en/#!/content/8959
● With Unity 5, drop in your Assets folder and
compile away (full .NET 2.0 API only)
8.
Connecting
using WebSocketSharp;
var ws= new WebSocket("ws://echo.websocket.org");
// We'll get to this
ws.OnOpen += OnOpenHandler;
ws.OnMessage += OnMessageHandler;
ws.OnClose += OnCloseHandler;
ws.OnError += OnErrorHandler;
ws.ConnectAsync();
9.
private void OnOpenHandler(objectsender, System.EventArgs e) {
Debug.Log("WebSocket connected!");
// Here is where you do useful stuff
}
How do I know when I’m connected?
10.
private void OnSendComplete(boolsuccess) {
Debug.Log("Message sent successfully? " + success);
}
ws.SendAsync("This WebSockets stuff is a breeze!", OnSendComplete);
Sending a message
Unity threading model
●Single-threaded with coroutines
● API generally not thread-safe
● Anything that updates the screen must
happen on the main thread
16.
websocket-sharp threading model
●.NET threads
● Anything done asynchronously happens on
a background thread
● Event handlers (OnOpen, OnMessage, etc.)
are invoked on background threads
17.
And never thetwain shall meet?
● This would be a (more) boring talk
● Enter our old Computer Science buddy, the
Finite State Machine
18.
A simple statemachine
using UnityEngine;
public enum State { NotRunning, Running, Connected, Ping, Pong, Done }
public delegate void Handler();
public class StateMachine : MonoBehaviour {
public void Run() { … }
public void Transition(State state) { … }
public void AddHandler(State state, Handler handler) { … }
public void Update() { … }
}
19.
using System.Collections.Generic;
public classStateMachine : MonoBehaviour {
private readonly object syncLock = new object();
private readonly Queue<State> pending = new Queue<State>();
public void Transition(State state) {
lock(syncLock) {
pending.Enqueue(state);
}
}
}
Requesting transitions
20.
public class StateMachine: MonoBehaviour {
private readonly Dictionary<State, Handler> handlers
= new Dictionary<State, Handler>();
public void AddHandler(State state, Handler handler) {
handlers.Add(state, handler);
}
}
Transition handlers
21.
Handling transitions
public classStateMachine : MonoBehaviour {
public void Update() {
while (pending.Count > 0) {
currentState = pending.Dequeue();
Handler handler;
if (handlers.TryGetValue(currentState, out handler)) {
handler();
}
}
}
}
22.
Start me up!
publicclass StateMachine : MonoBehaviour {
private State currentState = State.NotRunning;
public void Run() {
Transition(State.Running);
}
}
23.
Putting it alltogether (1)
using UnityEngine;
using WebSocketSharp;
public class StatefulMain : MonoBehaviour {
public StateMachine stateMachine;
private WebSocket ws;
void Start() {
ws = new WebSocket("ws://echo.websocket.org");
ws.OnOpen += OnOpenHandler;
ws.OnMessage += OnMessageHandler;
ws.OnClose += OnCloseHandler;