new method to query client ip

main
Thomas Woischnig 2023-12-04 01:58:46 +01:00
parent 38958bad95
commit 3b43758054
4 changed files with 76 additions and 9 deletions

View File

@ -123,9 +123,25 @@ namespace Lobbies
_ = Task.Run(async () => { await tcpClient.Send(messageData, 0, len); bufferRental.Return(messageData); });
}
/// <summary>
/// This callback is called if the nat punch requires the game server/client udp sockets sends a packet to a server so that
/// the external mapped port by the firewall can be seen by the external point.
/// </summary>
/// <param name="remoteEndpoint">The endpoint to send data to</param>
/// <param name="messageBuffer">The message byte buffer to send</param>
/// <param name="messageLength">Length of the data to send</param>
public delegate void SendUdpMessageCallback(IPEndPoint remoteEndpoint, byte[] messageBuffer, int messageLength);
public void RequestLobbyNatPunch(Guid lobbyId, string? password, SendUdpMessageCallback sendUdpCallback)
/// <summary>
/// This function requests a nat punch for this client from the game host. First the external port for our game client is detected by
/// sending a udp packet to the lobby server who then forwards the nat punch request to the host with the seen external ip and port.
/// The game host then sends a udp packet to the client to open it's nat bridge and when done sends a event to the client via the lobby server.
/// The client receives the external port and ip seen by the lobby server for the host and can connect to the host.
/// </summary>
/// <param name="lobbyId">The lobby to request a nat punch for</param>
/// <param name="password">Optional password of lobby</param>
/// <param name="sendUdpToGetExternalPortMappingCallback">A callback to send udp data if you have a udp game client ready and bound</param>
public void RequestLobbyNatPunch(Guid lobbyId, string? password, SendUdpMessageCallback sendUdpToGetExternalPortMappingCallback)
{
byte[]? passwordHash = null;
if (!string.IsNullOrEmpty(password) && lobbyInformation.ContainsKey(lobbyId) && lobbyInformation[lobbyId].PasswordSalt != null)
@ -133,7 +149,45 @@ namespace Lobbies
Task.Run(() =>
{
QueryExternalIpAndPort(sendUdpCallback);
QueryExternalIpAndPort(sendUdpToGetExternalPortMappingCallback);
var lobbyRequestNatPunch = new LobbyRequestNatPunch()
{
LobbyId = lobbyId,
PasswordHash = passwordHash,
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); });
});
}
/// <summary>
/// This function requests a nat punch for this client from the game host. First the external port for our game client is detected by
/// sending a udp packet to the lobby server who then forwards the nat punch request to the host with the seen external ip and port.
/// The game host then sends a udp packet to the client to open it's nat bridge and when done sends a event to the client via the lobby server.
/// The client receives the external port and ip seen by the lobby server for the host and can connect to the host.
/// </summary>
/// <param name="lobbyId">The lobby to request a nat punch for</param>
/// <param name="password">Optional password of lobby</param>
/// <param name="port">The port the game client will later use. We create a udp socket on it and send a packet to the lobby server to get the firewall to map that port for a short period of time
/// after that the udp client will be disposed</param>
public void RequestLobbyNatPunch(Guid lobbyId, string? password, int port = 0)
{
byte[]? passwordHash = null;
if (!string.IsNullOrEmpty(password) && lobbyInformation.ContainsKey(lobbyId) && lobbyInformation[lobbyId].PasswordSalt != null)
passwordHash = PasswordHash.Hash(password, lobbyInformation[lobbyId].PasswordSalt!);
Task.Run(() =>
{
using (var udpEchoServer = new UdpEchoServer())
{
udpEchoServer.Start(port);
QueryExternalIpAndPort(udpEchoServer.Send);
}
var lobbyRequestNatPunch = new LobbyRequestNatPunch()
{
LobbyId = lobbyId,

View File

@ -40,6 +40,18 @@ namespace Lobbies
serverSocketV6.Send(magicRequest, magicRequest.Length, remoteEndpoint);
}
public void Send(IPEndPoint remoteEndpoint, byte[] messageData, int messageLength)
{
if (!running || serverSocketV4 == null || serverSocketV6 == null)
throw new Exception("Listener not running!");
for (int i = 0; i < 16; i++)
if (remoteEndpoint.AddressFamily == AddressFamily.InterNetwork)
serverSocketV4.Send(messageData, messageLength, remoteEndpoint);
else
serverSocketV6.Send(messageData, messageLength, remoteEndpoint);
}
/// <summary>
/// Listen to requests and fire events
/// </summary>

View File

@ -21,12 +21,12 @@ int myExternalPort = -1;
bool running = true;
bool connected = false;
_ = Task.Run(async () =>
_ = Task.Run(() =>
{
while (running)
{
foreach (var lobbyEvent in lobbyClient.ReadEvents(20))
{
{
switch (lobbyEvent.EventType)
{
case LobbyClientEventTypes.Connected:
@ -80,7 +80,7 @@ _ = Task.Run(async () =>
Console.WriteLine($"Direct connection to {result.IPAddress!.ToString()} possible, using direct connection!");
Console.WriteLine($"Connecting game client!");
fakeGameHost.Send(new IPEndPoint(result.IPAddress!, currentLobbyHostInfo!.HostPort), "Hello from Game Client!");
Console.Write(">");
Console.Write(">");
}
else
{
@ -130,9 +130,9 @@ _ = Task.Run(async () =>
var p = Console.GetCursorPosition();
Console.SetCursorPosition(0, p.Top);
Console.WriteLine($"Received my external ip {seenExternalIpAndPort!.Ip}:{seenExternalIpAndPort.Port}");
Console.WriteLine($"Received my external ip {seenExternalIpAndPort!.Ip}:{seenExternalIpAndPort.Port}");
Console.Write(">");
connected = true;
}
}
@ -146,7 +146,8 @@ _ = Task.Run(async () =>
_ = Task.Run(() =>
{
lobbyClient.QueryExternalIpAndPort((remoteEndpoint, messageData, messageLength) => {
lobbyClient.QueryExternalIpAndPort((remoteEndpoint, messageData, messageLength) =>
{
fakeGameHost.Send(remoteEndpoint, messageData, messageLength);
});