using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Poco; using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Net.Sockets; using TcpServer; using UnityEngine; using Debug = UnityEngine.Debug; public class PocoManager : MonoBehaviour { public const int versionCode = 6; public int port = 5001; private bool mRunning; public AsyncTcpServer server = null; private RPCParser rpc = null; private SimpleProtocolFilter prot = null; private UnityDumper dumper = new UnityDumper(); private ConcurrentDictionary inbox = new ConcurrentDictionary(); private VRSupport vr_support = new VRSupport(); private Dictionary debugProfilingData = new Dictionary() { { "dump", 0 }, { "screenshot", 0 }, { "handleRpcRequest", 0 }, { "packRpcResponse", 0 }, { "sendRpcResponse", 0 }, }; class RPC : Attribute { } void Awake() { Application.runInBackground = true; DontDestroyOnLoad(this); prot = new SimpleProtocolFilter(); rpc = new RPCParser(); rpc.addRpcMethod("isVRSupported", vr_support.isVRSupported); rpc.addRpcMethod("hasMovementFinished", vr_support.IsQueueEmpty); rpc.addRpcMethod("RotateObject", vr_support.RotateObject); rpc.addRpcMethod("ObjectLookAt", vr_support.ObjectLookAt); rpc.addRpcMethod("Screenshot", Screenshot); rpc.addRpcMethod("GetScreenSize", GetScreenSize); rpc.addRpcMethod("Dump", Dump); rpc.addRpcMethod("GetDebugProfilingData", GetDebugProfilingData); rpc.addRpcMethod("SetText", SetText); rpc.addRpcMethod("GetSDKVersion", GetSDKVersion); mRunning = true; for (int i = 0; i < 5; i++) { this.server = new AsyncTcpServer(port + i); this.server.Encoding = Encoding.UTF8; this.server.ClientConnected += new EventHandler(server_ClientConnected); this.server.ClientDisconnected += new EventHandler(server_ClientDisconnected); this.server.DatagramReceived += new EventHandler>(server_Received); try { this.server.Start(); Debug.Log(string.Format("Tcp server started and listening at {0}", server.Port)); break; } catch (SocketException e) { Debug.Log(string.Format("Tcp server bind to port {0} Failed!", server.Port)); Debug.Log("--- Failed Trace Begin ---"); Debug.LogError(e); Debug.Log("--- Failed Trace End ---"); // try next available port this.server = null; } } if (this.server == null) { Debug.LogError(string.Format("Unable to find an unused port from {0} to {1}", port, port + 5)); } vr_support.ClearCommands(); } static void server_ClientConnected(object sender, TcpClientConnectedEventArgs e) { Debug.Log(string.Format("TCP client {0} has connected.", e.TcpClient.Client.RemoteEndPoint.ToString())); } static void server_ClientDisconnected(object sender, TcpClientDisconnectedEventArgs e) { Debug.Log(string.Format("TCP client {0} has disconnected.", e.TcpClient.Client.RemoteEndPoint.ToString())); } private void server_Received(object sender, TcpDatagramReceivedEventArgs e) { Debug.Log(string.Format("Client : {0} --> {1}", e.Client.TcpClient.Client.RemoteEndPoint.ToString(), e.Datagram.Length)); TcpClientState internalClient = e.Client; string tcpClientKey = internalClient.TcpClient.Client.RemoteEndPoint.ToString(); inbox.AddOrUpdate(tcpClientKey, internalClient, (n, o) => { return internalClient; }); } [RPC] private object Dump(List param) { var onlyVisibleNode = true; if (param.Count > 0) { onlyVisibleNode = (bool)param[0]; } var sw = new Stopwatch(); sw.Start(); var h = dumper.dumpHierarchy(onlyVisibleNode); debugProfilingData["dump"] = sw.ElapsedMilliseconds; return h; } [RPC] private object Screenshot(List param) { var sw = new Stopwatch(); sw.Start(); var tex = new Texture2D(Screen.width, Screen.height, TextureFormat.RGB24, false); tex.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0); tex.Apply(false); byte[] fileBytes = tex.EncodeToJPG(80); var b64img = Convert.ToBase64String(fileBytes); debugProfilingData["screenshot"] = sw.ElapsedMilliseconds; return new object[] { b64img, "jpg" }; } [RPC] private object GetScreenSize(List param) { return new float[] { Screen.width, Screen.height }; } public void stopListening() { mRunning = false; server?.Stop(); } [RPC] private object GetDebugProfilingData(List param) { return debugProfilingData; } [RPC] private object SetText(List param) { var instanceId = Convert.ToInt32(param[0]); var textVal = param[1] as string; foreach (var go in GameObject.FindObjectsOfType()) { if (go.GetInstanceID() == instanceId) { return UnityNode.SetText(go, textVal); } } return false; } [RPC] private object GetSDKVersion(List param) { return versionCode; } void Update() { foreach (TcpClientState client in inbox.Values) { List msgs = client.Prot.swap_msgs(); msgs.ForEach(delegate (string msg) { var sw = new Stopwatch(); sw.Start(); var t0 = sw.ElapsedMilliseconds; string response = rpc.HandleMessage(msg); var t1 = sw.ElapsedMilliseconds; byte[] bytes = prot.pack(response); var t2 = sw.ElapsedMilliseconds; server.Send(client.TcpClient, bytes); var t3 = sw.ElapsedMilliseconds; debugProfilingData["handleRpcRequest"] = t1 - t0; debugProfilingData["packRpcResponse"] = t2 - t1; TcpClientState internalClientToBeThrowAway; string tcpClientKey = client.TcpClient.Client.RemoteEndPoint.ToString(); inbox.TryRemove(tcpClientKey, out internalClientToBeThrowAway); }); } vr_support.PeekCommand(); } void OnApplicationQuit() { // stop listening thread stopListening(); } void OnDestroy() { // stop listening thread stopListening(); } } public class RPCParser { public delegate object RpcMethod(List param); protected Dictionary RPCHandler = new Dictionary(); private JsonSerializerSettings settings = new JsonSerializerSettings() { StringEscapeHandling = StringEscapeHandling.EscapeNonAscii }; public string HandleMessage(string json) { Dictionary data = JsonConvert.DeserializeObject>(json, settings); if (data.ContainsKey("method")) { string method = data["method"].ToString(); List param = null; if (data.ContainsKey("params")) { param = ((JArray)(data["params"])).ToObject>(); } object idAction = null; if (data.ContainsKey("id")) { // if it have id, it is a request idAction = data["id"]; } string response = null; object result = null; try { result = RPCHandler[method](param); } catch (Exception e) { // return error response Debug.Log(e); response = formatResponseError(idAction, null, e); return response; } // return result response response = formatResponse(idAction, result); return response; } else { // do not handle response Debug.Log("ignore message without method"); return null; } } // Call a method in the server public string formatRequest(string method, object idAction, List param = null) { Dictionary data = new Dictionary(); data["jsonrpc"] = "2.0"; data["method"] = method; if (param != null) { data["params"] = JsonConvert.SerializeObject(param, settings); } // if idAction is null, it is a notification if (idAction != null) { data["id"] = idAction; } return JsonConvert.SerializeObject(data, settings); } // Send a response from a request the server made to this client public string formatResponse(object idAction, object result) { Dictionary rpc = new Dictionary(); rpc["jsonrpc"] = "2.0"; rpc["id"] = idAction; rpc["result"] = result; return JsonConvert.SerializeObject(rpc, settings); } // Send a error to the server from a request it made to this client public string formatResponseError(object idAction, IDictionary data, Exception e) { Dictionary rpc = new Dictionary(); rpc["jsonrpc"] = "2.0"; rpc["id"] = idAction; Dictionary errorDefinition = new Dictionary(); errorDefinition["code"] = 1; errorDefinition["message"] = e.ToString(); if (data != null) { errorDefinition["data"] = data; } rpc["error"] = errorDefinition; return JsonConvert.SerializeObject(rpc, settings); } public void addRpcMethod(string name, RpcMethod method) { RPCHandler[name] = method; } }