using Epic.OnlineServices; using Epic.OnlineServices.P2P; using System; using System.Threading; using System.Threading.Tasks; using UnityEngine; namespace EpicTransport; public class Client : Common { public SocketId socketId; public ProductUserId serverId; public bool Connected { get; private set; } public bool Error { get; private set; } private event Action OnReceivedData; private event Action OnConnected; public event Action OnDisconnected; private Action SetTransportError; private TimeSpan ConnectionTimeout; public bool isConnecting = false; public string hostAddress = ""; private ProductUserId hostProductId = null; private TaskCompletionSource connectedComplete; private CancellationTokenSource cancelToken; private Client(EosTransport transport) : base(transport) => ConnectionTimeout = TimeSpan.FromSeconds(Math.Max(1, transport.timeout)); public static Client CreateClient(EosTransport transport, string host) { var c = new Client(transport); c.hostAddress = host; c.socketId = new SocketId() { SocketName = RandomString.Generate(20) }; c.OnConnected += () => transport.OnClientConnected.Invoke(); c.OnDisconnected += () => transport.OnClientDisconnected.Invoke(); c.OnReceivedData += (data, channel) => transport.OnClientDataReceived.Invoke(new ArraySegment(data), channel); c.SetTransportError = transport.SetTransportError; return c; } public async void Connect(string host) { cancelToken = new CancellationTokenSource(); try { hostProductId = ProductUserId.FromString(host); serverId = hostProductId; connectedComplete = new TaskCompletionSource(); OnConnected += SetConnectedComplete; SendInternal(hostProductId, socketId, InternalMessages.CONNECT); Task connectedCompleteTask = connectedComplete.Task; if (await Task.WhenAny(connectedCompleteTask, Task.Delay(ConnectionTimeout /*, cancelToken.Token*/)) != connectedCompleteTask) { SetTransportError($"Connection to {host} timed out."); Debug.LogError($"Connection to {host} timed out."); OnConnected -= SetConnectedComplete; OnConnectionFailed(hostProductId); } OnConnected -= SetConnectedComplete; } catch (FormatException) { SetTransportError("Connection string was not in the right format. Did you enter a ProductId?"); Debug.LogError($"Connection string was not in the right format. Did you enter a ProductId?"); Error = true; OnConnectionFailed(hostProductId); } catch (Exception ex) { SetTransportError(ex.Message); Debug.LogError(ex.Message); Error = true; OnConnectionFailed(hostProductId); } finally { if (Error) { OnConnectionFailed(null); } } } public void Disconnect() { if (serverId != null) { CloseP2PSessionWithUser(serverId, socketId); serverId = null; } else { return; } SendInternal(hostProductId, socketId, InternalMessages.DISCONNECT); Dispose(); cancelToken?.Cancel(); WaitForClose(hostProductId, socketId); } private void SetConnectedComplete() => connectedComplete.SetResult(connectedComplete.Task); protected override void OnReceiveData(byte[] data, ProductUserId clientUserId, int channel) { if (ignoreAllMessages) { return; } if (clientUserId != hostProductId) { Debug.LogError("Received a message from an unknown"); return; } OnReceivedData.Invoke(data, channel); } protected override void OnNewConnection(OnIncomingConnectionRequestInfo result) { if (ignoreAllMessages) { return; } if (deadSockets.Contains(result.SocketId.SocketName)) { Debug.LogError("Received incoming connection request from dead socket"); return; } if (hostProductId == result.RemoteUserId) { EOSSDKComponent.GetP2PInterface().AcceptConnection( new AcceptConnectionOptions() { LocalUserId = EOSSDKComponent.LocalUserProductId, RemoteUserId = result.RemoteUserId, SocketId = result.SocketId }); } else { Debug.LogError("P2P Acceptance Request from unknown host ID."); } } protected override void OnReceiveInternalData(InternalMessages type, ProductUserId clientUserId, SocketId socketId) { if (ignoreAllMessages) { return; } switch (type) { case InternalMessages.ACCEPT_CONNECT: Connected = true; OnConnected.Invoke(); Debug.Log("Connection established."); break; case InternalMessages.DISCONNECT: SetTransportError("host disconnected"); Connected = false; Debug.Log("Disconnected."); OnDisconnected.Invoke(); break; default: Debug.Log("Received unknown message type"); break; } } public void Send(byte[] data, int channelId) => Send(hostProductId, socketId, data, (byte)channelId); protected override void OnConnectionFailed(ProductUserId remoteId) => OnDisconnected.Invoke(); public void EosNotInitialized() => OnDisconnected.Invoke(); }