I can pass data from a different thread to the main thread in c# by using invoke (see below code), but how do I pass data the other direction? trying to pass by reference doesnt seem to be working. Using a global doesnt work either since there are multiple threads.
Ideally I would constantly be updating the local variable in the WS_Client function and then whenever I get a request in mywebsocketserver, it triggers the handle/action which would grab that constantly updated variable and pass it to the mywebsocketserver thread. Mywebsocketserver would then send the data out via the socket.
using UnityEngine;
using WebSocketSharp;
using WebSocketSharp.Server;
using System;
public class OrientationEvent
{
public event Action<Vector3> OnOrientationUpdate;
}
public class WS_Client : MonoBehaviour
{
public static GameObject _cube;
private Action<Vector3> callback;
WebSocket ws;
[SerializeField] float speed = 50.0f;
private Vector3 _orientation;
// Start is called before the first frame update
void Start()
{
_cube = GameObject.Find("MyCube");
if (_cube == null)
{
Debug.Log("couldnt find MyCube at first");
}
WebSocketServer wssv = new WebSocketServer(8080);
Action<Vector3> callback = HandleOrientationUpdate;
MyWebSocketServer wsService = new MyWebSocketServer(callback);
wssv.AddWebSocketService<MyWebSocketServer>("/MyWebSocket", () => wsService);
wssv.Start();
}
public WS_Client(OrientationEvent orientationEvent)
{
callback = HandleOrientationUpdate;
}
private void Update()
{
_cube.transform.rotation = Quaternion.Euler(_orientation);
}
private void HandleOrientationUpdate(Vector3 orientation)
{
Debug.Log("tries to rotate mycube");
Debug.Log(orientation);
// Update the cube's orientation using the data from the event
try
{
_orientation = orientation;
Debug.Log("translates the orientation");
}
catch (Exception ex)
{
Debug.LogError("An error occurred: " ex.Message);
}
}
}
public class MyWebSocketServer : WebSocketBehavior
{
private Action<Vector3> callback;
public MyWebSocketServer(Action<Vector3> callback)
{
this.callback = callback;
}
protected override void OnMessage(MessageEventArgs e)
{
string data = e.Data;
string[] components = data.Split(' ');
Debug.Log("splits components");
float x = float.Parse(components[0]);
float y = float.Parse(components[1]);
float z = float.Parse(components[2]);
Debug.Log("parses components");
Vector3 orientation = new Vector3(x, y, z);
// Vector3 vector = new Vector3((float)x, (float)y, (float)z);
Debug.Log("puts them in vector3");
try
{
callback?.Invoke(orientation);
Debug.Log("invokes action");
}
catch (Exception ex)
{
Debug.LogError("An error occurred: " ex.Message);
}
}
protected override void OnOpen()
{
// Handle client connection here
Debug.Log("someone connected");
}
protected override void OnClose(CloseEventArgs e)
{
// Handle client disconnection here
Debug.Log("someone disconnected");
}
}
CodePudding user response:
As said I don't see the nead to pass anything as a ref
here.
I would rather do it explicit through a kind of "MainThreadDispatcher" thing like e.g.
public class WS_Client : MonoBehaviour
{
...
// Thread-safe stack
readonly ConcurrentStack<Vector3> lastReceived = new();
void Update()
{
// On the main thread handle the latest received value
if(lastReceived.TryPop(out var value)
{
_cube.transform.rotation = Quaternion.Euler(value);
// erase the others as they are outdated
lastReceived.Clear();
}
}
void HandleOrientationUpdate(Vector3 v)
{
// here only store for being handled on main thread
lastReceived.Push(v);
}
}
The same way in the other direction, here it depends a bit in your needs whether you need those reliable as a queue or again only the latest value (in that case I'd rather use a stack as above)
public class MyWebSocketServer : WebSocketBehavior
{
readonly ConcurrentQueue<Vector3> toSend = new();
readonly WaitHandle wait = new AutoResetEvent(false);
public MyWebSocketServer(...)
{
...
var sendThread = new Thread(SendThread);
sendThread.Start();
}
public void Send(Vector3 v)
{
toSend.Enqueue(v);
wait.Set();
}
private void SendThread()
{
while(true)
{
// Allows this thread to go idle => not eating unnecessary performance
wait.WaitOne();
// if using the queue go through all items, otherwise again only pop the newest value and erase the others
while(toSend.TryDequeue(out var v))
{
// However you finally send your value using ToString etc
// tbh I would prefer a byte[] based protocol over string
// it would be faster to parse and also more efficient bandwidth wise
SubmitVector(v);
}
}
}
So now you only need to call Send
with the newest values to submit.
You most probably will not want to do this every frame as this will overload the network bandwidth a lot. Rather use a time interval like e.g. 0.2
seconds and interpolate at the receiver side.
CodePudding user response:
I also found a simpler way to do everything, just share the variable in a shared class:
using UnityEngine;
using WebSocketSharp;
using WebSocketSharp.Server;
using System;
public class MySharedData
{
public Vector3 handPosition { get; set; }
public Quaternion handRotation { get; set; }
// public Transform handData { get; set; }
}
public class WS_Client : MonoBehaviour
{
MySharedData sharedData = new MySharedData();
WebSocket ws;
public Transform handR;
void Start()
{
handR = GameObject.Find("LeftHandAnchor").GetComponent<Transform>();
// handR = GameObject.Find("RightHandAnchor").GetComponent<Transform>();
WebSocketServer wssv = new WebSocketServer(8080); //this websocketserver sets up a place for cpus to talk
MyWebSocketServer wsService = new MyWebSocketServer(sharedData); //this is actually a type of websocketbehavior (with a callback connected to the main thread) which
wssv.AddWebSocketService<MyWebSocketServer>("/MyWebSocket", () => wsService); //then gets implemented onto the websocketserver
wssv.Start();
}
private void Update()
{
sharedData.handPosition = handR.transform.position;
sharedData.handRotation = handR.transform.rotation;
}
}
public class MyWebSocketServer : WebSocketBehavior
{
private MySharedData _sharedData;
public MyWebSocketServer(MySharedData sharedData)
{
this._sharedData = sharedData;
}
protected override void OnMessage(MessageEventArgs e)
{
try
{
string data = e.Data;
Console.WriteLine("Received message: " e.Data);
Vector3 handPosition = _sharedData.handPosition;
Quaternion handRotation = _sharedData.handRotation;
string responseMessage = string.Format("Position:({0},{1},{2}), Rotation:({3},{4},{5})",
handPosition.x, handPosition.y, handPosition.z,
handRotation.x, handRotation.y, handRotation.z);
Debug.Log(responseMessage);
Send(responseMessage);
}
catch (Exception ex)
{
Debug.LogError("An error occurred: " ex.Message);
}
}
protected override void OnOpen()
{
// Handle client connection here
Debug.Log("someone connected");
}
protected override void OnClose(CloseEventArgs e)
{
// Handle client disconnection here
Debug.Log("someone disconnected");
}
}