quantum-space-buddies/EpicOnlineTransport/Client.cs
2022-03-02 19:46:33 -08:00

196 lines
4.8 KiB
C#

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<byte[], int> OnReceivedData;
private event Action OnConnected;
public event Action OnDisconnected;
private Action<string> SetTransportError;
private TimeSpan ConnectionTimeout;
public bool isConnecting = false;
public string hostAddress = "";
private ProductUserId hostProductId = null;
private TaskCompletionSource<Task> 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<byte>(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<Task>();
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();
}