using System.Linq; using UnityEngine; using UnityEngine.Networking; using System.Collections; using System.Collections.Generic; using System.Net.Sockets; using System.Net; using System.Text; using System; public class Client : MonoBehaviour { public const int socketPort = 87; public const int webglPort = 86; public uint account_id = 0; //id акка string ACCOUNT_NAME = ""; bool login_sent; bool entered = false; bool dc = false; bool connected = false; int lag = 0; double last_ping_time = 0; static string[] Servers = { "dev.prmsys.net", "localhost", "corp.prmsys.net" }; static bool ShowPacketsSent = true; //private const bool ShowPacketsSent = false; // private const bool ShowPackets = true; //ставится только в редакторе static bool ShowPackets = false; //ставится только в редакторе double initial_timer; int internal_timer = 0; internal void SendGetUsers(object p) { throw new NotImplementedException(); } public double timer { get { return DateTime.Now.Ticks / 10000000.0; //секунды } } public static Client instance; public static string ServerName; public static bool StartConnectFlag = false; #if UNITY_WEBGL public static WebSocket Mysocket; #else public static Socket Mysocket; #endif Dictionary<int, int> PacketLength = new Dictionary<int, int>(); public delegate void OnReceive(byte[] bytedata); public static OnReceive[] packets = new OnReceive[255]; private static Dictionary<string, UnityEngine.Object> loaded_models = new Dictionary<string, UnityEngine.Object>(); private static Dictionary<string, UnityEngine.Object> loaded_icons = new Dictionary<string, UnityEngine.Object>(); public string _dataPath = ""; GameObject drone; public static ClientType clientType; public enum ClientType { Desktop, Web } public static void SendEnqueue(byte[] bytedata) { SendQueue.Enqueue(bytedata); } private void Awake() { instance = this; ServerName = PlayerPrefs.GetString("server"); if (ServerName == "") ServerName = Servers[0]; Register(1, Disconnect, 5); Register(2, Login, 5); Register(3, CoordinatesReceive); Register(30, Myping, 3); Register(47, UsersReceive); Register(48, BeaconsReceive); //set data path //if (Application.platform == RuntimePlatform.WindowsWebPlayer || // Application.platform == RuntimePlatform.OSXWebPlayer) if (Application.platform == RuntimePlatform.WebGLPlayer) { _dataPath = Application.dataPath + "/StreamingAssets"; clientType = ClientType.Web; } else if (Application.platform == RuntimePlatform.Android) { _dataPath = "jar:file://" + Application.dataPath + "!/assets"; clientType = ClientType.Web; } else { _dataPath = "file://" + Application.dataPath + "/StreamingAssets"; ; clientType = ClientType.Desktop; } _dataPath = Application.streamingAssetsPath; //Debug.Log("_dataPath " + _dataPath); } public int LagCount { get { return lagpoints.Count; } } int totallag { get { int q = 0; foreach (var g in lagpoints) { q += g; } return q; } } Queue<int> lagpoints = new Queue<int>(); public int Averagelag { get { if (lagpoints.Count > 0) return totallag / lagpoints.Count; return 0; } } public void CoordinatesReceive(byte[] bytedata) { //var coordleng = bytedata.Length - 11; var accid = BitConverter.ToUInt32(bytedata, 5); var locid = BitConverter.ToUInt32(bytedata, 9); //WorkerController.TestStructures.Clear(); //print("len " + BitConverter.ToUInt32(bytedata, 1)); var worker = new List<Structure>(); for (var read = 13; read < bytedata.Length; read += 20) { var id = BitConverter.ToUInt32(bytedata, read); var x = BitConverter.ToInt32(bytedata, read+4); var y = BitConverter.ToInt32(bytedata, read+8); var ticks = BitConverter.ToInt64(bytedata, read+12); //print($"coord accid {accid} locid {locid} {id} x {x} y{y} ticks {ticks}"); //WorkerController.markers.ElementAt(1).marker.transform.position = new Vector3(x, 0.5f, y); //WorkerController.TestStructures worker.Add(new Structure { id = id, coord_x = x, coord_y = y, ts = new DateTime(ticks), acc_id = accid, location_id = locid, zone_id = 1 }); } WorkerController.Workers[accid] = worker; WorkerController.end_send[accid] = true; } public void Myping(byte[] bytedata) { last_ping_time = timer; int lag = BitConverter.ToUInt16(bytedata, 1); //print("Myping " + lag); this.lag = lag; if (lagpoints.Count > 5) lagpoints.Dequeue(); lagpoints.Enqueue(lag); List<byte> list = new List<byte>(); list.Add(30); list.Add((byte)1); SendEnqueue(list.ToArray()); } public void Disconnect(byte[] bytedata) { uint bid = BitConverter.ToUInt32(bytedata, 1); if (bid == account_id) Exit(); else { print("disc id " + bid); } } public void Exit() { print("Client Exit"); connected = false; login_sent = false; if (Mysocket != null) { Mysocket.Close(); Mysocket = null; } connect_started = new DateTime(); totalbytes = 0; account_id = 0; ACCOUNT_NAME = ""; connected = false; sendDone = false; receiveDone = false; connectDone = false; StartConnectFlag = false; UnityEngine.SceneManagement.SceneManager.LoadScene(0); } //пакет с 1 параметром = 1 public static void SendOneBytePacket(int num) { SendOneByteParamPacket(num, 1); } public static void SendOneByteTwoParamsPacket(int num, int param1, int param2) { byte[] data = { (Byte)num, (Byte)param1, (byte)param2 }; SendEnqueue(data); } public static void SendThreeParamsIntPacket(int num, int param1, int param2, int param3) { List<byte> list = new List<byte>(); list.Add((Byte)num); list.AddRange(BitConverter.GetBytes(param1)); list.AddRange(BitConverter.GetBytes(param2)); list.AddRange(BitConverter.GetBytes(param3)); SendEnqueue(list.ToArray()); } //пакет с 1 1-байтовым параметром public static void SendOneByteParamPacket(int num, int param) { byte[] data = { (Byte)num, (Byte)param }; byteSend(data); } //пакет с 1 4-байтовым параметром public static void SendTwoByteParamPacket(int num, ushort param) { List<byte> list = new List<byte>(); list.Add((Byte)num); list.AddRange(BitConverter.GetBytes(param)); SendEnqueue(list.ToArray()); } //пакет с 1 4-байтовым параметром public static void SendFourByteParamPacket(int num, uint param) { List<byte> list = new List<byte>(); list.Add((Byte)num); list.AddRange(BitConverter.GetBytes(param)); SendEnqueue(list.ToArray()); } public static DateTime connect_started; public void Login(byte[] bytedata) { uint accid = BitConverter.ToUInt32(bytedata, 1); if (accid == 0xFFFFFFFF) { Debug.LogError("Login or password incorrect"); AuthorizationController.error = true; return; } else if (accid == 0xFFFFFFFE) { Debug.LogError("Account was already connected"); return; } else if (accid == 0xFFFFFFFD) { Debug.LogError("Incorrect client version"); return; } AuthorizationController.success = true; account_id = accid; //var ts = DateTime.Now.Ticks-1000000000000; //var te = DateTime.Now.Ticks; //CoordinatesRequest(ts, te, 1, 1); SendGetUsers(1); } #region system functions public void Register(int packnum, OnReceive func) { packets[packnum] = func; } private void Register(int packnum, OnReceive func, int len) { packets[packnum] = func; if (len > 1) PacketLength[packnum] = len; } public int GetLength(byte[] data, int num, int offset) { int leng; if (!PacketLength.ContainsKey(num)) { var rest = data.Length - offset; //packet not full if (rest < 5) return 1; leng = BitConverter.ToInt32(data, offset + 1); } else leng = PacketLength[num]; //Debug.Log("GetLength " + leng); return leng; } int maximumPacketsPerUpdate = 50; public void Start() { entered = true; } public void Update() { #if UNITY_WEBGL Receive(); #endif if (instance == null || !entered) //вход не нажат, ждем { return; } else { if (!StartConnectFlag) { StartCoroutine(Connect()); StartConnectFlag = true; } } if(AuthorizationController.send)SendLogin(); int from_connect = DateTime.Now.Subtract(connect_started).Seconds; if (from_connect > 7 && from_connect != DateTime.Now.Second && account_id == 0) { Exit(); } var processedCount = 0; while (PacketQueue.Count > 0 && processedCount < maximumPacketsPerUpdate) //если длина очереди с пакетами ==0 { byte[] packetbytes; lock (packetqueuelock) { packetbytes = PacketQueue.Dequeue(); } int offset = 0; int totallen = 0; int num = (int)packetbytes[0]; int leng = GetLength(packetbytes, packetbytes[0], 0); if (leng <= packetbytes.Length) { while (offset < packetbytes.Length) { num = (int)packetbytes[offset]; leng = GetLength(packetbytes, num, offset); totallen += leng; print("num " + num + " leng " + leng); byte[] newpack = new byte[leng]; Array.Copy(packetbytes, offset, newpack, 0, leng); offset += leng; if (ShowPackets) { if (num != 30) //не пакет пинга { //string mygg = packets[num].Method.Name + " "; //Печатаем принятые байты! string mygg = ""; foreach (byte b in newpack) { mygg += b + " "; } //Debug.Log("ticks " + DateTime.Now.Ticks / 10000 + " timesince " + Time.timeSinceLevelLoad +" "+ newpack.Length + " bytes packet: " + mygg); Debug.Log(newpack.Length + " bytes packet: " + mygg); } } processedCount++; packets[num](newpack);//запустить OnReceive функцию //print("copy "+num); } } } //Debug.Log("Update End"); } public void LateUpdate() { while (SendQueue.Count > 0) { //print("SendQueue.Count " + SendQueue.Count); byte[] sendstring = SendQueue.Dequeue(); //print("sendstring len " + sendstring.Length); byteSend(sendstring); } } public class StateObject { // Client socket. // Size of receive buffer. public const int BufferSize = 128000; // Receive buffer. public byte[] buffer = new byte[BufferSize]; // Received data string. } public static Queue<byte[]> PacketQueue = new Queue<byte[]>(); static Queue<byte[]> SendQueue = new Queue<byte[]>(); public static bool receiveDone, connectDone, sendDone = false; public static StateObject state = new StateObject(); private IEnumerator Connect() { Debug.Log("Connect"); // Connect to a remote server. #if UNITY_WEBGL var uristring = $"ws://{ServerName}:{webglPort}/ws"; Debug.Log($"WebSocket tryconnect: {uristring}"); WebSocket client = new WebSocket(new Uri(uristring)); Mysocket = client; yield return StartCoroutine(Mysocket.Connect()); connectDone = (Mysocket.error == null); if (!connectDone) { Debug.Log("WebSocket error: " + Mysocket.error); yield break; } #else // Establish the remote endpoint for the socket. /*IPAddress ip = */ new IPAddress(new byte[] { 127, 0, 0, 1 }); IPHostEntry ipHostInfo = Dns.GetHostEntry(ServerName); //string localHost = Dns.GetHostName(); //print("localHost " + ipHostInfo.AddressList[0]); //IPHostEntry ipHostInfo = Dns.GetHostEntry(localHost); IPAddress[] ipv4Addresses = Array.FindAll(ipHostInfo.AddressList, a => a.AddressFamily == AddressFamily.InterNetwork); //print("localHost v4 " + ipv4Addresses[0]); // IPAddress ipAddress = ipHostInfo.AddressList[0]; IPAddress ipAddress = ipv4Addresses[0]; //IPEndPoint remoteEP = new IPEndPoint(ip, socketPort); IPEndPoint remoteEP = new IPEndPoint(ipAddress, socketPort); // Create a TCP/IP socket. Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //Socket client = new Socket(remoteEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp); print("remoteEP.AddressFamily " + AddressFamily.InterNetwork); print("client " + client.AddressFamily); Mysocket = client; // Connect to the remote endpoint. try { client.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), client); } catch (Exception e) { print(e.Message); } int num = 0; while (!connectDone && num < 10) { num++; yield return new WaitForSeconds(0.2f); } if (!connectDone) { print(socketPort + " port failed"); yield break; } #endif print("connected"); connected = true; while (true) { if (!entered) break; while (!sendDone && connected) { yield return 1; } Receive(); while (!receiveDone && connected) { yield return 1; } receiveDone = false; break; } yield break; } private static void ConnectCallback(IAsyncResult ar) { // Retrieve the socket from the state object. Socket client = (Socket)ar.AsyncState; connect_started = DateTime.Now; // Complete the connection. try { client.EndConnect(ar); } catch (Exception e) { print(e.Message); } // Signal that the connection has been made. connectDone = true; } private void Receive() { #if UNITY_WEBGL CheckWebSocket(); if (Mysocket == null) return; byte[] packet = null; do { packet = Mysocket.Recv(); if (packet != null) { lock (packetqueuelock) { PacketQueue.Enqueue(packet); } receiveDone = true; totalbytes += packet.Length; } } while (packet != null); #else Mysocket.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state); #endif } public static object packetqueuelock = new object(); public static Queue<byte[]> tempqueue = new Queue<byte[]>(); public bool CheckLength(byte[] packetbytes) { if (packetbytes.Length > 0) //если длина очереди с пакетами ==0 { int offset = 0; //TODO проверка на мусор int num = packetbytes[0]; int leng = GetLength(packetbytes, packetbytes[0], 0); if (leng <= packetbytes.Length) { while (offset < packetbytes.Length) { num = packetbytes[offset]; leng = GetLength(packetbytes, num, offset); if (leng == 1) { print("Error: CheckLength: packet not complete"); return false; } //print("Checking Length of " + num + " len " + leng); if (leng == 0) { print("CheckLength2 break! length error " + num); dc = true; break; } offset += leng; } } if (offset == packetbytes.Length) return true; return false; } return false; } public static int totalbytes = 0; #if !UNITY_WEBGL private void ReceiveCallback(IAsyncResult ar) //TODO если приходят данные больше длины буфера, пакеты режутся на части и складываются в обратном порядке - fix, пока что увеличим буфер { // Retrieve the state object and the client socket // from the asynchronous state object. StateObject state = (StateObject)ar.AsyncState; // Read data from the remote device. int bytesRead = 0; bytesRead = Mysocket.EndReceive(ar); if (bytesRead > 0) { // All the data has arrived; put it in response. byte[] newbuf = new Byte[bytesRead]; Array.Copy(state.buffer, newbuf, bytesRead); //if (ShowPackets) //{ // if (newbuf[0] != 30) //не пакет пинга // { // string mygg = ""; //Печатаем принятые байты! // foreach (byte b in newbuf) // { // mygg += b + " "; // } // print(newbuf.Length + " bytes: " + mygg); // } // totalbytes += bytesRead; //} byte[] tocheck = newbuf; if (tempqueue.Count > 0) { Queue<byte[]> myqueue = new Queue<byte[]>(tempqueue); List<byte> list = new List<byte>(); while (myqueue.Count > 0) { list.AddRange(myqueue.Dequeue()); } list.AddRange(newbuf); tocheck = list.ToArray(); } if (!CheckLength(tocheck)) { tempqueue.Clear(); tempqueue.Enqueue(tocheck); } else { tempqueue.Clear(); lock (packetqueuelock) { PacketQueue.Enqueue(tocheck); } } receiveDone = true; Receive(); } } #endif #if UNITY_WEBGL private static void CheckWebSocket() { if (Mysocket != null && Mysocket.error != null) { Debug.LogError(Mysocket.error); instance.Exit(); } } #endif private static void byteSend(byte[] tosend) { if (tosend == null) { Debug.LogError("tosend null"); return; } if (ShowPacketsSent) { byte[] newbuf = new Byte[tosend.Length]; Array.Copy(tosend, newbuf, tosend.Length); if (newbuf[0] != 30) //не пакет пинга { string mygg = "sent: "; //Печатаем отправленные байты! foreach (byte b in newbuf) { mygg += b + " "; } print(newbuf.Length + " bytes: " + mygg); } } #if UNITY_WEBGL byte[] webglbuf = new Byte[tosend.Length]; Array.Copy(tosend, webglbuf, tosend.Length); CheckWebSocket(); if (Mysocket != null) { Mysocket.Send(webglbuf); sendDone = true; Debug.Log("websocket sendDone"); } #else try { //print("buffer size " + Mysocket.SendBufferSize + " tosend.Length " + tosend.Length); Mysocket.BeginSend(tosend, 0, tosend.Length, 0, SendCallback, Mysocket); } catch (Exception e) { if (instance != null) { print(e.Message); instance.Exit(); } } #endif } #if !UNITY_WEBGL private static void SendCallback(IAsyncResult ar) { // Retrieve the socket from the state object. Socket client = (Socket)ar.AsyncState; // Complete sending the data to the remote device. /*int bytesSent = */ client.EndSend(ar); //print("Sent " + bytesSent + " bytes to server."); // Signal that all bytes have been sent. sendDone = true; } #endif #endregion public static string GetMD5Hash(string input) { System.Security.Cryptography.MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider(); byte[] bs = Encoding.UTF8.GetBytes(input); bs = x.ComputeHash(bs); StringBuilder s = new StringBuilder(); foreach (byte b in bs) { s.Append(b.ToString("x2").ToLower()); } string password = s.ToString(); return password; } /// <summary> /// Запрос координат с сервера, timeStart, timeEnd == DateTime.Ticks /// </summary> public void CoordinatesRequest(long timeStart, long timeEnd, byte resolution, uint locationID, uint account_id) { login_sent = true; Debug.Log("SendRequestCoordinates"); List<byte> list = new List<byte>(); list.Add(3); list.AddRange(BitConverter.GetBytes(account_id)); list.AddRange(BitConverter.GetBytes(timeStart)); list.AddRange(BitConverter.GetBytes(timeEnd)); list.Add(resolution); list.AddRange(BitConverter.GetBytes(locationID)); SendEnqueue(list.ToArray()); } //companyId: 1 = Тайшет, 2 = Тестовая, 3 = Братское public void SendGetUsers(uint companyId) { List<byte> list = new List<byte>(); list.Add(47); list.AddRange(BitConverter.GetBytes(companyId)); SendEnqueue(list.ToArray()); } public void UsersReceive(byte[] bytedata) { print("UsersReceive"); WorkerController.users = new List<User>(); int read = 5; while (read < bytedata.Length) { var id = BitConverter.ToUInt32(bytedata, read); var lenname = bytedata[read+4]; var name = Encoding.UTF8.GetString(bytedata, read+5, lenname); read += 5 + lenname; //print($"user received id {id} lenname {lenname} name {name}"); WorkerController.users.Add(new User { id = id, name = name }); } WorkerController.users_load = true; } public void BeaconsReceive(byte[] bytedata) { int read = 5; while (read < bytedata.Length) { var id = BitConverter.ToUInt32(bytedata, read); var uuid = Encoding.UTF8.GetString(bytedata, read + 4, 36); var x = BitConverter.ToSingle(bytedata, read + 40); var y = BitConverter.ToSingle(bytedata, read + 44); var z = BitConverter.ToSingle(bytedata, read + 48); var minor = BitConverter.ToUInt16(bytedata, read + 52); var major = BitConverter.ToUInt16(bytedata, read + 54); print($"beacon received id {id} uuid {uuid} x {x} y {y} z {z} minor {minor} major {major}"); read += 56; WorkerController.Beacons.Add(new Beacon { id = id, uuid = uuid, x = x, y = y, z = z, minor = minor, major = major }); } WorkerController.beacons_load = true; } public void BeaconsRequest(uint locationId) { Debug.Log("BeaconsRequest connected "+ connected); //if (!connected) //{ // return; //} SendFourByteParamPacket(48, locationId); } public void SendLogin() { if (!connected) { //Debug.LogError("Couldn't connect"); //Exit(); return; } if (!login_sent) { login_sent = true; Debug.Log("SendLogin"); string passwordToEdit = AuthorizationController.password;// "8*9Efc2%"; string login = AuthorizationController.login;//"vsheiko"; string pass = GetMD5Hash(passwordToEdit); byte[] bpass = Encoding.UTF8.GetBytes(pass); byte[] blogin = Encoding.UTF8.GetBytes(login); List<byte> list = new List<byte>(); byte loginFlag = 1; int leng = (bpass.Length + blogin.Length + 6); //+имя+длина list.Add(2); list.AddRange(BitConverter.GetBytes(leng)); list.Add(loginFlag); list.AddRange(bpass); list.AddRange(blogin); SendEnqueue(list.ToArray()); } } }