using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Threading; using FastArena.Network.Packets; using UnityEngine; using Object = UnityEngine.Object; namespace FastArena.Network { public enum ClientStatus { Initialized, Connected, Disconnected } public class ServerConnection { public IPAddress Ip; public short PortFirst; public Guid SessionId; public byte[] ServerKey; } public static class Client { private static TcpClient _tcp; private static UdpClient _udp; private static byte[] _clientKey; private static ServerConnection _serverConnection; public static ClientStatus Status { get; private set; } = ClientStatus.Initialized; public static void SendUserAction() { throw new NotImplementedException(); } public static void Connect(IPAddress ip, short portFirst) { _tcp = new TcpClient(); _udp = new UdpClient(); _serverConnection = new ServerConnection() { Ip = ip, PortFirst = portFirst }; _udp.Connect(ip, portFirst + 1); _tcp.BeginConnect(ip, portFirst, TCPConnectCallback, null); } private static void ReadExactly(this TcpClient tcp, byte[] buffer, int offset, int exactSize, int timeoutMs = 5000) { int msPassed = 0; while (tcp.Available < exactSize) { Thread.Sleep(1); msPassed += 1; if (msPassed > timeoutMs) { throw new TimeoutException( $"couldn't get {exactSize} bytes from socket, {tcp.Available} avaliable"); } } int readN = _tcp.GetStream().Read(buffer, offset, exactSize); if (readN != exactSize) throw new Exception($"expected to read {exactSize} bytes, got {readN}"); } private static void TCPConnectCallback(IAsyncResult ar) { _tcp.EndConnect(ar); // receive first response from server byte[] buffer = new byte[4096]; _tcp.ReadExactly(buffer, 0, Marshal.SizeOf()); //TODO: implement decryption var firstResponse = StructBinaryConverter.ReadStruct(buffer); firstResponse.ValidateResponseCode(); _serverConnection.SessionId = firstResponse.sessionId; _serverConnection.ServerKey = firstResponse.serverKey; // connect udp socket associated with current session byte[] byteMessage = StructBinaryConverter.GetBytes(new ConnectionRequestUDP(_serverConnection.SessionId)); //TODO: implement encryption _udp.Send(byteMessage, byteMessage.Length); // receive second response from server IPEndPoint remoteEndpoint = new IPEndPoint(IPAddress.None, 0); byteMessage = _udp.Receive(ref remoteEndpoint); //TODO: implement decryption var secondResponse = StructBinaryConverter.ReadStruct(byteMessage); secondResponse.ValidateResponseCode(); //TODO: start tcp read loop //TODO: start udp read loop } private static readonly Dictionary _syncTransforms = new Dictionary(); private static void CreateGameObject(ref CreateObjectPacket packet) { var prefab = Resources.Load(packet.prefabPath, typeof(GameObject)); var go = (GameObject)Object.Instantiate(prefab); var idComponent = go.AddComponent(); idComponent._SetId(packet.gameObjectId); if (go.TryGetComponent(out SyncTransform syncTransform)) { _syncTransforms.Add(packet.gameObjectId, syncTransform); } } private static void UpdateObjectTransform(ref TransformUpdatePacket packet) { if(!_syncTransforms.TryGetValue(packet.gameObjectId, out var component)) return; component.UpdateTransform(ref packet); } } }