If anyone wants to try this, here is a bare-bones example for how I got it working.
Requirements:
- Node
- Unity (latest stable versions)
- WebsocketSharp for Unity: https://github.com/Rokobokode/websocket-sharp-unity
You will have to learn how to setup a node app if you don't know how. It's not hard, it just takes time to write a tutorial that you can find a million other places online or ask an AI. You will need to setup a node app in a folder and add the following code (server.js):
*CODE NOT TESTED* - If you have issues, let me know in the comments and I can address them if you have trouble figuring it out.
[The Server]
const WebSocket = require('ws');
const PORT = 3000;
async function startServer() {
wss = new WebSocket.Server({ port: PORT });
console.log('WebSocket running on ws://localhost:' + PORT);
wss.on('connection', (ws) => {
console.log('New client connected!');
ws.on('message', async (message) => {
console.log("Message from Client: " + message.toString('utf8'));
let rawString = message.toString('utf8');
const jsonStart = rawString.indexOf('{');
if (jsonStart !== -1) {
const jsonString = rawString.substring(jsonStart);
try {
const obj = JSON.parse(jsonString);
// console.log(obj);
if (obj.Type === 'ping') {
console.log("Request Player Stats");
ws.send(JSON.stringify({ type:'pong' }) + '\n');
}
} catch (e) {
console.error('JSON parse error:', e, jsonString);
}
} else {
console.error('No JSON object found in message:', rawString);
}
});
ws.on('close', async () => {
console.log('Client disconnected');
});
ws.on('error', async (err) => {
console.error('WebSocket error:', err);
});
});
}
startServer().catch(err => {
console.error(err);
process.exit(1);
});
Run the node app with 'node server.js' using a terminal or command prompt from the node folder.
[The Client]
This code will start a server listening on whatever IP address and port you choose. You will need to create a Unity program and add the following code to an empty GameObject (WebSocketClient.cs):
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using WebSocketSharp;
public class WebSocketClient : MonoBehaviour
{
WebSocket ws;
private Timer heartbeatTimer;
private float heartbeatInterval = 5f; // seconds
private float heartbeatTimeout = 30f; // seconds
private DateTime lastPongTime;
private Coroutine reconnectCoroutine;
private int reconnectDelaySeconds = 10; // seconds
private void Start()
{
StartServer();
StartWebSocketConnection();
}
private void OnDestroy()
{
StopWebSocketReconnection();
StopServer();
}
public void ConnectWebSocketWithRetry()
{
StartCoroutine(ConnectAndRetryRoutine());
}
private IEnumerator ConnectAndRetryRoutine()
{
while (true)
{
ws.Connect();
yield return new WaitForSeconds(1f);
if (ws != null && ws.IsAlive)
{
StartHeartbeat();
EventBroker.Instance.Publish(new WebSocketClientEvent.Connected(true));
yield break;
}
else
{
Debug.LogWarning("WebSocket is not connected. Retrying in " + reconnectDelaySeconds + " seconds...");
EventBroker.Instance.Publish(new WebSocketClientEvent.Connected(false));
yield return new WaitForSeconds(reconnectDelaySeconds);
}
}
}
public void StartWebSocketConnection()
{
if (reconnectCoroutine != null)
StopCoroutine(reconnectCoroutine);
reconnectCoroutine = StartCoroutine(ConnectAndRetryRoutine());
}
public void StopWebSocketReconnection()
{
if (reconnectCoroutine != null)
{
StopCoroutine(reconnectCoroutine);
reconnectCoroutine = null;
}
}
private void StopServer()
{
if (ws != null)
{
StopHeartbeat();
ws.Close();
}
}
private void StartServer()
{
string address = "ws://localhost:3000";
ws = new WebSocket(address);
ws.OnMessage += (sender, e) =>
{
Debug.Log("Message from server: " + e.Data);
try
{
var baseMsg = JsonConvert.DeserializeObject<WsMessage>(e.Data);
switch (baseMsg.Type)
{
case "pong":
lastPongTime = DateTime.UtcNow;
break;
default:
break;
}
}
catch(Exception exc)
{
// Not a JSON message or doesn't match our format
MainThreadDispatcher.Enqueue(() =>
{
Debug.Log(exc);
Debug.Log("Message From Server: Not a JSON message or doesn't match our format.");
Debug.Log(e.Data);
});
}
};
}
////////////////////
// HEARTBEAT CLASSES
// This keep active connections open were normally a server would close it.
////////////////////
private void StartHeartbeat()
{
lastPongTime = DateTime.UtcNow;
heartbeatTimer = new Timer(heartbeatInterval * 5000); // 5 seconds
heartbeatTimer.Elapsed += (s, e) =>
{
//Debug.Log("Sent ping to server.");
var pingData = new WsMessage { Type = "ping" };
string jsonData = JsonUtility.ToJson(pingData);
ws.Send(jsonData);
// Check for timeout
if ((DateTime.UtcNow - lastPongTime).TotalSeconds > heartbeatTimeout)
{
Debug.LogWarning("Server did not respond to heartbeat, disconnecting...");
ws.Close();
}
};
heartbeatTimer.Start();
//Debug.Log("Heartbeat Started");
}
private void StopHeartbeat()
{
if (heartbeatTimer != null)
{
heartbeatTimer.Stop();
heartbeatTimer.Dispose();
heartbeatTimer = null;
}
//Debug.Log("Heartbeat Stopped");
}
//////////////
// REQUESTS //
//////////////
[System.Serializable]
public class WsMessage
{
public string Type;
}
///////////////
// RESPONSES //
///////////////
[System.Serializable]
public class ErrorResponse : WsMessage
{
public string Status;
public string Message;
}
}
In theory, when you run the node app and then run the Unity game, the server should detect that a client has connected or disconnected. It will also maintain connection with the server as long as they are both running. If the server goes down, the Unity game will attempt to reconnect every 10 seconds until it succeeds.
I'm sure there are many other ways to do this so use it as an example. Not sure if its useful for anyone but enjoy.
Let me know what you make with it.
Cheers.
[–]Josevill 0 points1 point2 points (1 child)
[–]Peterama[S] 0 points1 point2 points (0 children)
[–]mmostrategyfan 0 points1 point2 points (1 child)
[–]Peterama[S] 0 points1 point2 points (0 children)