FastArena/Assets/Network/Client.cs

122 lines
4.5 KiB
C#

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<ConnectionResponseTCP>());
//TODO: implement decryption
var firstResponse = StructBinaryConverter.ReadStruct<ConnectionResponseTCP>(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<ConnectionResponseUDP>(byteMessage);
secondResponse.ValidateResponseCode();
//TODO: start tcp read loop
//TODO: start udp read loop
}
private static readonly Dictionary<int, SyncTransform> _syncTransforms = new Dictionary<int, SyncTransform>();
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<GameObjectId>();
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);
}
}
}