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<string, TcpClientState> inbox = new ConcurrentDictionary<string, TcpClientState>(); 
 | 
    private VRSupport vr_support = new VRSupport(); 
 | 
    private Dictionary<string, long> debugProfilingData = new Dictionary<string, long>() { 
 | 
        { "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<TcpClientConnectedEventArgs>(server_ClientConnected); 
 | 
            this.server.ClientDisconnected += 
 | 
                new EventHandler<TcpClientDisconnectedEventArgs>(server_ClientDisconnected); 
 | 
            this.server.DatagramReceived += 
 | 
                new EventHandler<TcpDatagramReceivedEventArgs<byte[]>>(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<byte[]> 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<object> 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<object> 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<object> param) 
 | 
    { 
 | 
        return new float[] { Screen.width, Screen.height }; 
 | 
    } 
 | 
  
 | 
    public void stopListening() 
 | 
    { 
 | 
        mRunning = false; 
 | 
        server?.Stop(); 
 | 
    } 
 | 
  
 | 
    [RPC] 
 | 
    private object GetDebugProfilingData(List<object> param) 
 | 
    { 
 | 
        return debugProfilingData; 
 | 
    } 
 | 
  
 | 
    [RPC] 
 | 
    private object SetText(List<object> param) 
 | 
    { 
 | 
        var instanceId = Convert.ToInt32(param[0]); 
 | 
        var textVal = param[1] as string; 
 | 
        foreach (var go in GameObject.FindObjectsOfType<GameObject>()) 
 | 
        { 
 | 
            if (go.GetInstanceID() == instanceId) 
 | 
            { 
 | 
                return UnityNode.SetText(go, textVal); 
 | 
            } 
 | 
        } 
 | 
        return false; 
 | 
    } 
 | 
  
 | 
    [RPC] 
 | 
    private object GetSDKVersion(List<object> param) 
 | 
    { 
 | 
        return versionCode; 
 | 
    } 
 | 
  
 | 
    void Update() 
 | 
    { 
 | 
        foreach (TcpClientState client in inbox.Values) 
 | 
        { 
 | 
            List<string> 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<object> param); 
 | 
  
 | 
    protected Dictionary<string, RpcMethod> RPCHandler = new Dictionary<string, RpcMethod>(); 
 | 
    private JsonSerializerSettings settings = new JsonSerializerSettings() 
 | 
    { 
 | 
        StringEscapeHandling = StringEscapeHandling.EscapeNonAscii 
 | 
    }; 
 | 
  
 | 
    public string HandleMessage(string json) 
 | 
    { 
 | 
        Dictionary<string, object> data = JsonConvert.DeserializeObject<Dictionary<string, object>>(json, settings); 
 | 
        if (data.ContainsKey("method")) 
 | 
        { 
 | 
            string method = data["method"].ToString(); 
 | 
            List<object> param = null; 
 | 
            if (data.ContainsKey("params")) 
 | 
            { 
 | 
                param = ((JArray)(data["params"])).ToObject<List<object>>(); 
 | 
            } 
 | 
  
 | 
            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<object> param = null) 
 | 
    { 
 | 
        Dictionary<string, object> data = new Dictionary<string, object>(); 
 | 
        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<string, object> rpc = new Dictionary<string, object>(); 
 | 
        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<string, object> data, Exception e) 
 | 
    { 
 | 
        Dictionary<string, object> rpc = new Dictionary<string, object>(); 
 | 
        rpc["jsonrpc"] = "2.0"; 
 | 
        rpc["id"] = idAction; 
 | 
  
 | 
        Dictionary<string, object> errorDefinition = new Dictionary<string, object>(); 
 | 
        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; 
 | 
    } 
 | 
} 
 |