This commit is contained in:
twoischnig committed 2023-11-29 20:28:43 +01:00
1 parent 2674eb2a10
commit 840218f119
25 files changed
+650 -169

No files matched your search

+119 -48
View File
@@ -2,6 +2,9 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
@@ -14,8 +17,21 @@ namespace Lobbies
TcpLobbbyClient tcpClient = new TcpLobbbyClient();
private readonly ConcurrentQueue<LobbyClientEvent> events = new ConcurrentQueue<LobbyClientEvent>();
BufferRental bufferRental = new BufferRental(4096);
AutoResetEvent waitForExternalIp = new AutoResetEvent(false);
private string? host;
private int port;
private int connectionId;
public string? externalIp;
public int externalPort;
public void Connect(string host, int port, CancellationToken cancellationToken)
{
this.host = host;
this.port = port;
try
{
cancellationToken.ThrowIfCancellationRequested();
@@ -30,9 +46,6 @@ namespace Lobbies
tcpClient.Disconnected -= TcpClient_Disconnected;
tcpClient.Disconnected += TcpClient_Disconnected;
tcpClient.Connected -= TcpClient_Connected;
tcpClient.Connected += TcpClient_Connected;
_ = Task.Run(() => tcpClient.Connect(host, port));
}
catch
@@ -93,19 +106,25 @@ namespace Lobbies
_ = Task.Run(async () => { await tcpClient.Send(messageData, 0, len); bufferRental.Return(messageData); });
}
public void RequestLobbyNatPunch(Guid lobbyId, string? password, string? clientIp, int clientPort)
{
var lobbyRequestNatPunch = new LobbyRequestNatPunch()
{
LobbyId = lobbyId,
PasswordHash = string.IsNullOrEmpty(password) ? null : SHA256.HashData(Encoding.UTF8.GetBytes(password)),
ClientIp = clientIp,
ClientPort = clientPort
};
byte[] messageData = bufferRental.Rent();
var len = lobbyRequestNatPunch.Serialize(messageData);
_ = Task.Run(async () => { await tcpClient.Send(messageData, 0, len); bufferRental.Return(messageData); });
public delegate void SendUdpMessageCallback(IPEndPoint remoteEndpoint, byte[] messageBuffer, int messageLength);
public void RequestLobbyNatPunch(Guid lobbyId, string? password, SendUdpMessageCallback sendUdpCallback)
{
Task.Run(() =>
{
QueryExternalIpAndPort(sendUdpCallback);
var lobbyRequestNatPunch = new LobbyRequestNatPunch()
{
LobbyId = lobbyId,
PasswordHash = string.IsNullOrEmpty(password) ? null : SHA256.HashData(Encoding.UTF8.GetBytes(password)),
ClientIp = externalIp,
ClientPort = externalPort
};
byte[] messageData = bufferRental.Rent();
var len = lobbyRequestNatPunch.Serialize(messageData);
_ = Task.Run(async () => { await tcpClient.Send(messageData, 0, len); bufferRental.Return(messageData); });
});
}
public void UpdateLobby(string name, int gameMode, int maxPlayerCount, int playerCount, string? password, string? ip, int port)
@@ -156,23 +175,75 @@ namespace Lobbies
_ = Task.Run(async () => { await tcpClient.Send(messageData, 0, len); bufferRental.Return(messageData); });
}
public void NotifyLobbyNatPunchDone(int natPunchId)
public void NotifyLobbyNatPunchDone(int natPunchId, string externalIp, int externalPort)
{
var lobbyNatPunchDone = new LobbyNatPunchDone() { NatPunchId = natPunchId };
var lobbyNatPunchDone = new LobbyNatPunchDone() { NatPunchId = natPunchId, ExternalIp = externalIp, ExternalPort = externalPort };
byte[] messageData = bufferRental.Rent();
var len = lobbyNatPunchDone.Serialize(messageData);
_ = Task.Run(async () => { await tcpClient.Send(messageData, 0, len); bufferRental.Return(messageData); });
}
public void Stop()
public static IPAddress[] GetIPsByName(string hostName, bool ip4Wanted, bool ip6Wanted)
{
tcpClient.Stop();
// Check if the hostname is already an IPAddress
IPAddress? outIpAddress;
if (IPAddress.TryParse(hostName, out outIpAddress) == true)
return new IPAddress[] { outIpAddress };
//<----------
IPAddress[] addresslist = Dns.GetHostAddresses(hostName);
if (addresslist == null || addresslist.Length == 0)
return new IPAddress[0];
//<----------
if (ip4Wanted && ip6Wanted)
return addresslist;
//<----------
if (ip4Wanted)
return addresslist.Where(o => o.AddressFamily == AddressFamily.InterNetwork).ToArray();
//<----------
if (ip6Wanted)
return addresslist.Where(o => o.AddressFamily == AddressFamily.InterNetworkV6).ToArray();
//<----------
return new IPAddress[0];
}
private void TcpClient_Connected()
public void QueryExternalIpAndPort(SendUdpMessageCallback sendUdpCallback)
{
events.Enqueue(new LobbyClientEvent { EventType = LobbyClientEventTypes.Connected, EventData = null });
byte[] messageData = bufferRental.Rent();
try
{
waitForExternalIp.Reset();
var queryExternalPortAndIp = new QueryExternalPortAndIp() { LobbyClientId = connectionId };
var len = queryExternalPortAndIp.Serialize(messageData);
var ip = GetIPsByName(host!, true, false).First();
do
{
sendUdpCallback(new IPEndPoint(ip, port), messageData, len);
}
while (!waitForExternalIp.WaitOne(100));
}
catch
{
}
finally
{
bufferRental.Return(messageData);
}
}
public void Stop()
{
waitForExternalIp.Set();
tcpClient.Stop();
}
private void TcpClient_Disconnected(bool clean, string error)
@@ -183,38 +254,24 @@ namespace Lobbies
events.Enqueue(new LobbyClientEvent { EventType = LobbyClientEventTypes.Disconnected, EventData = new LobbyClientDisconnectReason { WasError = false, ErrorMessage = string.Empty } });
}
private int PeekTypeId(ReadOnlySpan<byte> buffer)
{
int typeId = 0;
int shift = 0;
int offset = 0;
byte b;
do
{
{
// Check for a corrupted stream. Read a max of 5 bytes.
// In a future version, add a DataFormatException.
if (shift == 5 * 7) // 5 bytes max per Int32, shift += 7
throw new FormatException("Format_Bad7BitInt32");
// ReadByte handles end of stream cases for us.
b = buffer[offset++];
typeId |= (b & 0x7F) << shift;
shift += 7;
}
} while ((b & 0x80) != 0);
return typeId;
}
private void TcpClient_DataReceived(int dataLength, Memory<byte> data)
{
try
{
if (dataLength > 0)
{
switch (PeekTypeId(data.Span))
switch (LobbyMessageIdentifier.ReadLobbyMessageIdentifier(data.Span))
{
case LobbyClientConnectionInfo.TypeId:
{
var lobbyClientConnectionInfo = LobbyClientConnectionInfo.Deserialize(data.Span);
if (lobbyClientConnectionInfo != null)
{
connectionId = lobbyClientConnectionInfo.Id;
events.Enqueue(new LobbyClientEvent { EventType = LobbyClientEventTypes.Connected, EventData = null });
}
}
break;
case LobbyInfo.TypeId:
{
var lobbyInfo = LobbyInfo.Deserialize(data.Span);
@@ -260,6 +317,19 @@ namespace Lobbies
}
}
break;
case SeenExternalIpAndPort.TypeId:
{
var seenExternalIpAndPort = SeenExternalIpAndPort.Deserialize(data.Span);
if (seenExternalIpAndPort != null)
{
externalIp = seenExternalIpAndPort.Ip;
externalPort = seenExternalIpAndPort.Port;
waitForExternalIp.Set();
events.Enqueue(new LobbyClientEvent { EventType = LobbyClientEventTypes.ExternalIpAndPort, EventData = seenExternalIpAndPort });
}
}
break;
}
}
}
@@ -268,8 +338,9 @@ namespace Lobbies
public void Dispose()
{
tcpClient.Dispose();
waitForExternalIp.Dispose();
tcpClient.Dispose();
}
}
}
+1 -7
View File
@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Lobbies
namespace Lobbies
{
public class LobbyClientDisconnectReason
{
+44 -7
View File
@@ -1,27 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Lobbies
namespace Lobbies
{
/// <summary>
/// Contains a lobby client event
/// </summary>
public class LobbyClientEvent
{
/// <summary>
/// Identifier type of the event
/// </summary>
public LobbyClientEventTypes EventType { get; internal set; }
/// <summary>
/// Optional data object, contains additional information for the event. See <see cref="LobbyClientEventTypes"/> for possible values.
/// </summary>
public object? EventData { get; internal set; }
}
/// <summary>
/// The list of events that can occur.
/// </summary>
public enum LobbyClientEventTypes
{
/// <summary>
/// Client connected to lobby server. EventData is null.
/// </summary>
Connected,
/// <summary>
/// Client disconnected from lobby server graceful. EventData is <see cref="LobbyClientDisconnectReason"/>.
/// </summary>
Disconnected,
/// <summary>
/// Connection lost to lobby server. EventData is LobbyClientDisconnectReason.
/// </summary>
Failed,
/// <summary>
/// A lobby was added to the lobby list for the observed game id. EventData is <see cref="LobbyAdd"/> with the lobby data.
/// </summary>
LobbyAdd,
/// <summary>
/// A lobby was changed for the observed game id. EventData is <see cref="LobbyUpdate"/> with the lobby data.
/// </summary>
LobbyUpdate,
/// <summary>
/// A lobby was removed from the lobby list for the observed game id. EventData is <see cref="LobbyDelete"/> with the lobby id.
/// </summary>
LobbyDelete,
/// <summary>
/// The game host shared his self known address. EventData is <see cref="LobbyHostInfo"/> with the games hosts self known address, most likely internal.
/// </summary>
LobbyHostInfo,
/// <summary>
/// A nat punch was requested by another LobbyClient to be performed by the host LobbyClient. EventData is <see cref="LobbyRequestNatPunch"/> with the clients seen external address to nat punch to.
/// </summary>
LobbyRequestNatPunch,
/// <summary>
/// Game host has finished the nat punch. EventData is <see cref="LobbyRequestNatPunch"/> with the games hosts external address.
/// </summary>
LobbyNatPunchDone,
/// <summary>
/// Response to a query of our external ip and port seen by the lobby server. EventData is <see cref="SeenExternalIpAndPort"/> with the clients seen external address.
/// </summary>
ExternalIpAndPort
}
}