Direct connection support
parent
a99b097a83
commit
fc25760c2f
Binary file not shown.
Binary file not shown.
|
|
@ -4,6 +4,7 @@ using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -18,6 +19,8 @@ namespace Lobbies
|
||||||
|
|
||||||
AutoResetEvent waitForExternalIp = new AutoResetEvent(false);
|
AutoResetEvent waitForExternalIp = new AutoResetEvent(false);
|
||||||
|
|
||||||
|
UdpEchoServer udpEchoServer = new UdpEchoServer();
|
||||||
|
|
||||||
private Dictionary<Guid, LobbyInfo> lobbyInformation = new Dictionary<Guid, LobbyInfo>();
|
private Dictionary<Guid, LobbyInfo> lobbyInformation = new Dictionary<Guid, LobbyInfo>();
|
||||||
private string? host;
|
private string? host;
|
||||||
private int port;
|
private int port;
|
||||||
|
|
@ -75,6 +78,8 @@ namespace Lobbies
|
||||||
|
|
||||||
public void HostLobby(Guid gameId, string name, int gameMode, int maxPlayerCount, string? password, string? ip, int port)
|
public void HostLobby(Guid gameId, string name, int gameMode, int maxPlayerCount, string? password, string? ip, int port)
|
||||||
{
|
{
|
||||||
|
udpEchoServer.Start(0);
|
||||||
|
|
||||||
byte[]? hash = null, salt = null;
|
byte[]? hash = null, salt = null;
|
||||||
|
|
||||||
if(!string.IsNullOrEmpty(password))
|
if(!string.IsNullOrEmpty(password))
|
||||||
|
|
@ -91,8 +96,9 @@ namespace Lobbies
|
||||||
PlayerCount = 0,
|
PlayerCount = 0,
|
||||||
PasswordHash = hash,
|
PasswordHash = hash,
|
||||||
PasswordSalt = salt,
|
PasswordSalt = salt,
|
||||||
HostIp = ip,
|
HostIps = GatherLocalIpAddresses().ToArray(),
|
||||||
HostPort = port
|
HostPort = port,
|
||||||
|
HostTryPort = udpEchoServer.Port
|
||||||
};
|
};
|
||||||
|
|
||||||
byte[] messageData = bufferRental.Rent();
|
byte[] messageData = bufferRental.Rent();
|
||||||
|
|
@ -142,7 +148,7 @@ namespace Lobbies
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateLobby(string name, int gameMode, int maxPlayerCount, int playerCount, string? password, string? ip, int port)
|
public void UpdateLobby(string name, int gameMode, int maxPlayerCount, int playerCount, string? password, int port)
|
||||||
{
|
{
|
||||||
byte[]? hash = null, salt = null;
|
byte[]? hash = null, salt = null;
|
||||||
|
|
||||||
|
|
@ -159,8 +165,9 @@ namespace Lobbies
|
||||||
PlayerCount = playerCount,
|
PlayerCount = playerCount,
|
||||||
PasswordHash = hash,
|
PasswordHash = hash,
|
||||||
PasswordSalt = salt,
|
PasswordSalt = salt,
|
||||||
HostIp = ip,
|
HostIps = GatherLocalIpAddresses().ToArray(),
|
||||||
HostPort = port
|
HostPort = port,
|
||||||
|
HostTryPort = udpEchoServer.Port
|
||||||
};
|
};
|
||||||
|
|
||||||
byte[] messageData = bufferRental.Rent();
|
byte[] messageData = bufferRental.Rent();
|
||||||
|
|
@ -170,6 +177,8 @@ namespace Lobbies
|
||||||
|
|
||||||
public void CloseLobby()
|
public void CloseLobby()
|
||||||
{
|
{
|
||||||
|
udpEchoServer.Stop();
|
||||||
|
|
||||||
var lobbyDelete = new LobbyDelete()
|
var lobbyDelete = new LobbyDelete()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -207,6 +216,32 @@ namespace Lobbies
|
||||||
_ = Task.Run(async () => { await tcpClient.Send(messageData, 0, len); bufferRental.Return(messageData); });
|
_ = Task.Run(async () => { await tcpClient.Send(messageData, 0, len); bufferRental.Return(messageData); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IPAddress?> TryDirectConnection(IPAddress[] ipAddressesToTry, int tryPort)
|
||||||
|
{
|
||||||
|
return await Task.Run(() =>
|
||||||
|
{
|
||||||
|
IPAddress? ret = null;
|
||||||
|
using (var udpEchoClient = new UdpEchoServer())
|
||||||
|
{
|
||||||
|
udpEchoClient.Reached += (ep) =>
|
||||||
|
{
|
||||||
|
ret = ep.Address;
|
||||||
|
};
|
||||||
|
|
||||||
|
udpEchoClient.Start(0);
|
||||||
|
|
||||||
|
foreach (var ip in ipAddressesToTry)
|
||||||
|
{
|
||||||
|
udpEchoClient.CheckConnectionPossible(new IPEndPoint(ip, tryPort));
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.Sleep(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static IPAddress[] GetIPsByName(string hostName, bool ip4Wanted, bool ip6Wanted)
|
public static IPAddress[] GetIPsByName(string hostName, bool ip4Wanted, bool ip6Wanted)
|
||||||
{
|
{
|
||||||
// Check if the hostname is already an IPAddress
|
// Check if the hostname is already an IPAddress
|
||||||
|
|
@ -361,10 +396,24 @@ namespace Lobbies
|
||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<IPAddress> GatherLocalIpAddresses()
|
||||||
|
{
|
||||||
|
foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces())
|
||||||
|
{
|
||||||
|
IPInterfaceProperties ipProps = netInterface.GetIPProperties();
|
||||||
|
|
||||||
|
foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses)
|
||||||
|
{
|
||||||
|
yield return addr.Address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
waitForExternalIp.Dispose();
|
waitForExternalIp.Dispose();
|
||||||
tcpClient.Dispose();
|
tcpClient.Dispose();
|
||||||
|
udpEchoServer.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,197 @@
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Threading;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Lobbies
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Small udp server to receive udp packets and echo the data back to source
|
||||||
|
/// </summary>
|
||||||
|
internal class UdpEchoServer : IDisposable
|
||||||
|
{
|
||||||
|
public const int SIO_UDP_CONNRESET = -1744830452;
|
||||||
|
|
||||||
|
private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
private bool running = false;
|
||||||
|
private bool isDisposed = false;
|
||||||
|
private UdpClient? serverSocketV4, serverSocketV6;
|
||||||
|
|
||||||
|
public delegate void ReachableEventArgs(IPEndPoint remoteEndpoint);
|
||||||
|
/// <summary>
|
||||||
|
/// If a valid request for a ip and port query comes in call this event with the seen remote ip and port for a lobby server client id
|
||||||
|
/// </summary>
|
||||||
|
public event ReachableEventArgs? Reached;
|
||||||
|
|
||||||
|
public int Port { get; private set; }
|
||||||
|
private bool isRunningV4 = false, isRunningV6 = false;
|
||||||
|
|
||||||
|
public void CheckConnectionPossible(IPEndPoint remoteEndpoint)
|
||||||
|
{
|
||||||
|
if (!running || serverSocketV4 == null || serverSocketV6 == null)
|
||||||
|
throw new Exception("Listener not running!");
|
||||||
|
|
||||||
|
byte[] magicRequest = new byte[] { 0 };
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
if(remoteEndpoint.AddressFamily == AddressFamily.InterNetwork)
|
||||||
|
serverSocketV4.Send(magicRequest, magicRequest.Length, remoteEndpoint);
|
||||||
|
else
|
||||||
|
serverSocketV6.Send(magicRequest, magicRequest.Length, remoteEndpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Listen to requests and fire events
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="port">The port to listen on</param>
|
||||||
|
private async void ListenV4(int port)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cancellationTokenSource.Token.ThrowIfCancellationRequested();
|
||||||
|
serverSocketV4 = new UdpClient(port, AddressFamily.InterNetwork);
|
||||||
|
serverSocketV4.Client.IOControl(
|
||||||
|
(IOControlCode)SIO_UDP_CONNRESET,
|
||||||
|
new byte[] { 0, 0, 0, 0 },
|
||||||
|
null
|
||||||
|
);
|
||||||
|
Port = ((IPEndPoint)serverSocketV4.Client.LocalEndPoint).Port;
|
||||||
|
byte[] magicAnswer = new byte[] { 1 };
|
||||||
|
using (cancellationTokenSource.Token.Register(() => { serverSocketV4.Close(); }))
|
||||||
|
{
|
||||||
|
isRunningV4 = true;
|
||||||
|
while (running)
|
||||||
|
{
|
||||||
|
var receiveResult = await serverSocketV4.ReceiveAsync();
|
||||||
|
if (receiveResult.Buffer.Length == 1)
|
||||||
|
{
|
||||||
|
if (receiveResult.Buffer[0] == 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
serverSocketV4.Send(magicAnswer, magicAnswer.Length, receiveResult.RemoteEndPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receiveResult.Buffer[0] == 1)
|
||||||
|
{
|
||||||
|
Reached?.Invoke(receiveResult.RemoteEndPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch when (cancellationTokenSource.IsCancellationRequested || !running) //Cancel requested
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
serverSocketV4?.Dispose();
|
||||||
|
serverSocketV4 = null;
|
||||||
|
running = false;
|
||||||
|
isRunningV4 = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Listen to requests and fire events
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="port">The port to listen on</param>
|
||||||
|
private async void ListenV6(int port)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cancellationTokenSource.Token.ThrowIfCancellationRequested();
|
||||||
|
serverSocketV6 = new UdpClient(port, AddressFamily.InterNetworkV6);
|
||||||
|
serverSocketV6.Client.IOControl(
|
||||||
|
(IOControlCode)SIO_UDP_CONNRESET,
|
||||||
|
new byte[] { 0, 0, 0, 0 },
|
||||||
|
null
|
||||||
|
);
|
||||||
|
byte[] magicAnswer = new byte[] { 1 };
|
||||||
|
using (cancellationTokenSource.Token.Register(() => { serverSocketV6.Close(); }))
|
||||||
|
{
|
||||||
|
isRunningV6 = true;
|
||||||
|
while (running)
|
||||||
|
{
|
||||||
|
var receiveResult = await serverSocketV6.ReceiveAsync();
|
||||||
|
if (receiveResult.Buffer.Length == 1)
|
||||||
|
{
|
||||||
|
if (receiveResult.Buffer[0] == 0)
|
||||||
|
{
|
||||||
|
Reached?.Invoke(receiveResult.RemoteEndPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receiveResult.Buffer[0] == 1)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
serverSocketV6.Send(magicAnswer, magicAnswer.Length, receiveResult.RemoteEndPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch when (cancellationTokenSource.IsCancellationRequested || !running) //Cancel requested
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
serverSocketV6?.Dispose();
|
||||||
|
serverSocketV6 = null;
|
||||||
|
running = false;
|
||||||
|
isRunningV6 = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Start udp listener
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="port">The port to listen on</param>
|
||||||
|
public void Start(int port)
|
||||||
|
{
|
||||||
|
isRunningV4 = false;
|
||||||
|
isRunningV6 = false;
|
||||||
|
running = true;
|
||||||
|
_ = Task.Run(() => ListenV4(port));
|
||||||
|
while (running && !isRunningV4)
|
||||||
|
Thread.Yield();
|
||||||
|
_ = Task.Run(() => ListenV6(Port));
|
||||||
|
while (running && (!isRunningV4 || !isRunningV6))
|
||||||
|
Thread.Yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stop udp listener
|
||||||
|
/// </summary>
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
running = false;
|
||||||
|
cancellationTokenSource.Cancel();
|
||||||
|
if (serverSocketV4 != null)
|
||||||
|
serverSocketV4?.Close();
|
||||||
|
if (serverSocketV6 != null)
|
||||||
|
serverSocketV6?.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!isDisposed)
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
while (isRunningV4 || isRunningV6)
|
||||||
|
Task.Yield();
|
||||||
|
|
||||||
|
cancellationTokenSource.Dispose();
|
||||||
|
isDisposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ using Lobbies;
|
||||||
using LobbyClientTest;
|
using LobbyClientTest;
|
||||||
using LobbyServerDto;
|
using LobbyServerDto;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
|
||||||
Console.WriteLine("Starting lobby client v0.7!");
|
Console.WriteLine("Starting lobby client v0.7!");
|
||||||
var lobbyClient = new LobbyClient();
|
var lobbyClient = new LobbyClient();
|
||||||
|
|
@ -16,12 +17,11 @@ FakeGameHost fakeGameHost = new FakeGameHost();
|
||||||
int myPort = fakeGameHost.Server(0);
|
int myPort = fakeGameHost.Server(0);
|
||||||
string? myExternalIp = null;
|
string? myExternalIp = null;
|
||||||
int myExternalPort = -1;
|
int myExternalPort = -1;
|
||||||
IPEndPoint? hostInfo = null;
|
|
||||||
|
|
||||||
bool running = true;
|
bool running = true;
|
||||||
bool connected = false;
|
bool connected = false;
|
||||||
|
|
||||||
_ = Task.Run(() =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
while (running)
|
while (running)
|
||||||
{
|
{
|
||||||
|
|
@ -78,8 +78,23 @@ _ = Task.Run(() =>
|
||||||
var lobbyHostInfo = lobbyEvent.EventData as LobbyHostInfo;
|
var lobbyHostInfo = lobbyEvent.EventData as LobbyHostInfo;
|
||||||
var p = Console.GetCursorPosition();
|
var p = Console.GetCursorPosition();
|
||||||
Console.SetCursorPosition(0, p.Top);
|
Console.SetCursorPosition(0, p.Top);
|
||||||
Console.WriteLine($"Host info for lobby {lobbyHostInfo!.LobbyId} is {lobbyHostInfo.HostIp}:{lobbyHostInfo.HostPort}!");
|
Console.WriteLine($"Host info for lobby {lobbyHostInfo!.LobbyId} is {(lobbyHostInfo.HostIps != null && lobbyHostInfo.HostIps.Length > 0 ? lobbyHostInfo.HostIps[0].ToString() : "")}:{lobbyHostInfo.HostPort}!");
|
||||||
hostInfo = new IPEndPoint(IPAddress.Parse(lobbyHostInfo.HostIp!), lobbyHostInfo.HostPort);
|
|
||||||
|
//Try direct connection
|
||||||
|
if (lobbyHostInfo.HostIps != null && lobbyHostInfo.HostIps.Length > 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Trying direct connection to {string.Join<IPAddress>(",", lobbyHostInfo.HostIps)} on port {lobbyHostInfo.HostTryPort}!");
|
||||||
|
var reachableIp = await lobbyClient.TryDirectConnection(lobbyHostInfo.HostIps, lobbyHostInfo.HostTryPort);
|
||||||
|
if(reachableIp != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Direct connection to {reachableIp.ToString()} possible, using direct connection!");
|
||||||
|
Console.WriteLine($"Connecting game client!");
|
||||||
|
fakeGameHost.Send(new IPEndPoint(reachableIp, lobbyHostInfo.HostPort), "Hello from Game Client!");
|
||||||
|
Console.Write(">");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Console.WriteLine($"Requesting nat punch to me!");
|
Console.WriteLine($"Requesting nat punch to me!");
|
||||||
lobbyClient.RequestLobbyNatPunch(lobbyHostInfo.LobbyId, null, (remoteEndpoint, messageBuffer, messageLength) => {
|
lobbyClient.RequestLobbyNatPunch(lobbyHostInfo.LobbyId, null, (remoteEndpoint, messageBuffer, messageLength) => {
|
||||||
fakeGameHost.Send(remoteEndpoint, messageBuffer, messageLength);
|
fakeGameHost.Send(remoteEndpoint, messageBuffer, messageLength);
|
||||||
|
|
@ -111,7 +126,7 @@ _ = Task.Run(() =>
|
||||||
Console.SetCursorPosition(0, p.Top);
|
Console.SetCursorPosition(0, p.Top);
|
||||||
Console.WriteLine($"Nat punch requested to {lobbyRequestNatPunch!.ClientIp}:{lobbyRequestNatPunch.ClientPort}!");
|
Console.WriteLine($"Nat punch requested to {lobbyRequestNatPunch!.ClientIp}:{lobbyRequestNatPunch.ClientPort}!");
|
||||||
|
|
||||||
Task.Run(() =>
|
_ = Task.Run(() =>
|
||||||
{
|
{
|
||||||
lobbyClient.QueryExternalIpAndPort((remoteEndpoint, messageData, messageLength) => {
|
lobbyClient.QueryExternalIpAndPort((remoteEndpoint, messageData, messageLength) => {
|
||||||
fakeGameHost.Send(remoteEndpoint, messageData, messageLength);
|
fakeGameHost.Send(remoteEndpoint, messageData, messageLength);
|
||||||
|
|
@ -140,7 +155,6 @@ _ = Task.Run(() =>
|
||||||
Console.SetCursorPosition(0, p.Top);
|
Console.SetCursorPosition(0, p.Top);
|
||||||
Console.WriteLine($"Nat punch request done!");
|
Console.WriteLine($"Nat punch request done!");
|
||||||
Console.WriteLine($"Connecting game client!");
|
Console.WriteLine($"Connecting game client!");
|
||||||
|
|
||||||
fakeGameHost.Send(new IPEndPoint(IPAddress.Parse(lobbyNatPunchDone!.ExternalIp!), lobbyNatPunchDone.ExternalPort), "Hello from Game Client!");
|
fakeGameHost.Send(new IPEndPoint(IPAddress.Parse(lobbyNatPunchDone!.ExternalIp!), lobbyNatPunchDone.ExternalPort), "Hello from Game Client!");
|
||||||
Console.Write(">");
|
Console.Write(">");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|
@ -24,7 +25,8 @@ namespace LobbyServer
|
||||||
public int MaxPlayerCount { get; set; }
|
public int MaxPlayerCount { get; set; }
|
||||||
public byte[]? PasswordHash { get; set; }
|
public byte[]? PasswordHash { get; set; }
|
||||||
public byte[]? PasswordSalt { get; set; }
|
public byte[]? PasswordSalt { get; set; }
|
||||||
public required string HostIp { get; set; }
|
public required IPAddress[] HostIps { get; set; }
|
||||||
public int HostPort { get; set; }
|
public int HostPort { get; set; }
|
||||||
|
public int HostTryPort { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
using LobbyServer;
|
using LobbyServer;
|
||||||
using LobbyServerDto;
|
using LobbyServerDto;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
using var closing = new AutoResetEvent(false);
|
using var closing = new AutoResetEvent(false);
|
||||||
using var tcpServer = new TcpServer();
|
using var tcpServer = new TcpServer();
|
||||||
|
|
@ -42,6 +42,11 @@ tcpServer.DataReceived += (clientId, dataLength, data) =>
|
||||||
if (!GameGuids.ValidGuids.Contains(lobbyCreate.GameId))
|
if (!GameGuids.ValidGuids.Contains(lobbyCreate.GameId))
|
||||||
throw new Exception("Invalid game guid!");
|
throw new Exception("Invalid game guid!");
|
||||||
|
|
||||||
|
List<IPAddress> hostIpAddresses = new List<IPAddress>();
|
||||||
|
hostIpAddresses.Add(IPAddress.Parse(tcpServer.GetClientIp(clientId)!));
|
||||||
|
if(lobbyCreate.HostIps != null)
|
||||||
|
hostIpAddresses.AddRange(lobbyCreate.HostIps);
|
||||||
|
|
||||||
var lobby = new Lobby
|
var lobby = new Lobby
|
||||||
{
|
{
|
||||||
Name = lobbyCreate.Name,
|
Name = lobbyCreate.Name,
|
||||||
|
|
@ -52,8 +57,9 @@ tcpServer.DataReceived += (clientId, dataLength, data) =>
|
||||||
PasswordHash = lobbyCreate.PasswordHash,
|
PasswordHash = lobbyCreate.PasswordHash,
|
||||||
PasswordSalt = lobbyCreate.PasswordSalt,
|
PasswordSalt = lobbyCreate.PasswordSalt,
|
||||||
HostClientId = clientId,
|
HostClientId = clientId,
|
||||||
HostIp = lobbyCreate.HostIp == null ? tcpServer.GetClientIp(clientId)! : lobbyCreate.HostIp,
|
HostIps = hostIpAddresses.ToArray(),
|
||||||
HostPort = lobbyCreate.HostPort,
|
HostPort = lobbyCreate.HostPort,
|
||||||
|
HostTryPort = lobbyCreate.HostTryPort
|
||||||
};
|
};
|
||||||
|
|
||||||
if(lobbiesByClientId.TryGetValue(clientId, out var existingLobby))
|
if(lobbiesByClientId.TryGetValue(clientId, out var existingLobby))
|
||||||
|
|
@ -113,10 +119,17 @@ tcpServer.DataReceived += (clientId, dataLength, data) =>
|
||||||
existingLobby.PasswordHash = lobbyUpdate.PasswordHash;
|
existingLobby.PasswordHash = lobbyUpdate.PasswordHash;
|
||||||
existingLobby.PasswordSalt = lobbyUpdate.PasswordSalt;
|
existingLobby.PasswordSalt = lobbyUpdate.PasswordSalt;
|
||||||
|
|
||||||
if (lobbyUpdate.HostIp != null)
|
List<IPAddress> hostIpAddresses = new List<IPAddress>();
|
||||||
existingLobby.HostIp = lobbyUpdate.HostIp;
|
hostIpAddresses.Add(IPAddress.Parse(tcpServer.GetClientIp(clientId)!));
|
||||||
|
if (lobbyUpdate.HostIps != null)
|
||||||
|
hostIpAddresses.AddRange(lobbyUpdate.HostIps);
|
||||||
|
|
||||||
|
if (!Enumerable.SequenceEqual(existingLobby.HostIps, hostIpAddresses))
|
||||||
|
existingLobby.HostIps = hostIpAddresses.ToArray();
|
||||||
|
|
||||||
existingLobby.HostPort = lobbyUpdate.HostPort;
|
existingLobby.HostPort = lobbyUpdate.HostPort;
|
||||||
|
existingLobby.HostTryPort = lobbyUpdate.HostTryPort;
|
||||||
|
|
||||||
_ = Task.Run(() => SendLobbyUpdate(Lobby.LobbyUpdateType.Update, existingLobby));
|
_ = Task.Run(() => SendLobbyUpdate(Lobby.LobbyUpdateType.Update, existingLobby));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -221,7 +234,7 @@ tcpServer.DataReceived += (clientId, dataLength, data) =>
|
||||||
|
|
||||||
{
|
{
|
||||||
var messageData = bufferRental.Rent();
|
var messageData = bufferRental.Rent();
|
||||||
var lobbyHostInfo = new LobbyHostInfo() { LobbyId = lobby.Id, HostIp = lobby.HostIp, HostPort = lobby.HostPort };
|
var lobbyHostInfo = new LobbyHostInfo() { LobbyId = lobby.Id, HostIps = lobby.HostIps, HostPort = lobby.HostPort, HostTryPort = lobby.HostTryPort };
|
||||||
var messageDataLength = lobbyHostInfo.Serialize(messageData);
|
var messageDataLength = lobbyHostInfo.Serialize(messageData);
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
|
|
@ -426,7 +439,7 @@ Console.CancelKeyPress += (sender, args) =>
|
||||||
args.Cancel = true;
|
args.Cancel = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
Console.WriteLine($"{DateTime.Now}: Application started v0.7");
|
Console.WriteLine($"{DateTime.Now}: Application started v0.8");
|
||||||
|
|
||||||
udpServer.Start(8088);
|
udpServer.Start(8088);
|
||||||
tcpServer.Start(8088);
|
tcpServer.Start(8088);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
namespace LobbyServerDto
|
namespace LobbyServerDto
|
||||||
{
|
{
|
||||||
|
|
@ -41,13 +42,17 @@ namespace LobbyServerDto
|
||||||
[MaxLength(16)]
|
[MaxLength(16)]
|
||||||
public byte[]? PasswordSalt { get; set; }
|
public byte[]? PasswordSalt { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The hosts ip. Used the the host information send to clients on their request.
|
/// The hosts ip addresses locally detected. Used the the host information send to clients on their request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[MaxLength(32)]
|
[MaxLength(32)]
|
||||||
public string? HostIp { get; set; }
|
public IPAddress[]? HostIps { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The hosts port. Used the the host information send to clients on their request.
|
/// The hosts port. Used the the host information send to clients on their request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int HostPort { get; set; }
|
public int HostPort { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The hosts echo port to try if a connection is possible.
|
||||||
|
/// </summary>
|
||||||
|
public int HostTryPort { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
namespace LobbyServerDto
|
namespace LobbyServerDto
|
||||||
{
|
{
|
||||||
|
|
@ -13,13 +14,17 @@ namespace LobbyServerDto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Guid LobbyId { get; set; }
|
public Guid LobbyId { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The hosts ip, this could be an internal address
|
/// The hosts ip addresses locally detected. Used the the host information send to clients on their request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[MaxLength(32)]
|
[MaxLength(32)]
|
||||||
public string? HostIp { get; set; }
|
public IPAddress[]? HostIps { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The hosts port
|
/// The hosts port
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int HostPort { get; set; }
|
public int HostPort { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The hosts echo port to try if a connection is possible.
|
||||||
|
/// </summary>
|
||||||
|
public int HostTryPort { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
namespace LobbyServerDto
|
namespace LobbyServerDto
|
||||||
{
|
{
|
||||||
|
|
@ -34,13 +35,17 @@ namespace LobbyServerDto
|
||||||
[MaxLength(16)]
|
[MaxLength(16)]
|
||||||
public byte[]? PasswordSalt { get; set; }
|
public byte[]? PasswordSalt { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The hosts ip
|
/// The hosts ip addresses locally detected. Used the the host information send to clients on their request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[MaxLength(32)]
|
[MaxLength(32)]
|
||||||
public string? HostIp { get; set; }
|
public IPAddress[]? HostIps { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The hosts port
|
/// The hosts port. Used the the host information send to clients on their request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int HostPort { get; set; }
|
public int HostPort { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The hosts echo port to try if a connection is possible.
|
||||||
|
/// </summary>
|
||||||
|
public int HostTryPort { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -74,6 +74,7 @@ namespace LobbyServerDto
|
||||||
s.Append(@$"// <auto-generated />
|
s.Append(@$"// <auto-generated />
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
{(foundClass.Value.nameSpace is null ? null : $@"namespace {foundClass.Value.nameSpace}
|
{(foundClass.Value.nameSpace is null ? null : $@"namespace {foundClass.Value.nameSpace}
|
||||||
{{")}
|
{{")}
|
||||||
|
|
@ -113,6 +114,34 @@ using System.Text;
|
||||||
var name = p.Identifier.ToString();
|
var name = p.Identifier.ToString();
|
||||||
switch(p.Type.ToString())
|
switch(p.Type.ToString())
|
||||||
{
|
{
|
||||||
|
case "IPAddress[]":
|
||||||
|
case "IPAddress[]?":
|
||||||
|
s.Append($@"
|
||||||
|
if ({name} != null)
|
||||||
|
{{
|
||||||
|
int maxLength = Math.Min({name}.Length, {maxLength});
|
||||||
|
uint v = (uint)maxLength;
|
||||||
|
while (v >= 0x80)
|
||||||
|
{{
|
||||||
|
buffer[offset++] = (byte)(v | 0x80);
|
||||||
|
v >>= 7;
|
||||||
|
}}
|
||||||
|
buffer[offset++] = (byte)v;
|
||||||
|
|
||||||
|
for(int i = 0; i < maxLength; i++)
|
||||||
|
{{
|
||||||
|
var ipBuffer = {name}[i].GetAddressBytes();
|
||||||
|
buffer[offset++] = (byte)ipBuffer.Length;
|
||||||
|
Buffer.BlockCopy(ipBuffer, 0, buffer, offset, ipBuffer.Length);
|
||||||
|
offset += ipBuffer.Length;
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
else
|
||||||
|
{{
|
||||||
|
buffer[offset++] = 0;
|
||||||
|
}}
|
||||||
|
");
|
||||||
|
break;
|
||||||
case "bool":
|
case "bool":
|
||||||
s.Append($@"
|
s.Append($@"
|
||||||
buffer[offset++] = (byte)({name} == true ? 1 : 0);");
|
buffer[offset++] = (byte)({name} == true ? 1 : 0);");
|
||||||
|
|
@ -226,6 +255,42 @@ using System.Text;
|
||||||
var name = p.Identifier.ToString();
|
var name = p.Identifier.ToString();
|
||||||
switch (p.Type.ToString())
|
switch (p.Type.ToString())
|
||||||
{
|
{
|
||||||
|
case "IPAddress[]":
|
||||||
|
case "IPAddress[]?":
|
||||||
|
s.Append($@"
|
||||||
|
{{
|
||||||
|
int arrayLen = 0;
|
||||||
|
int shift = 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++];
|
||||||
|
arrayLen |= (b & 0x7F) << shift;
|
||||||
|
shift += 7;
|
||||||
|
}} while ((b & 0x80) != 0);
|
||||||
|
|
||||||
|
if(arrayLen > {maxLength})
|
||||||
|
throw new FormatException(""Format_IPAddressArrayToLong"");
|
||||||
|
|
||||||
|
if(arrayLen > 0)
|
||||||
|
{{
|
||||||
|
ret.{name} = new IPAddress[arrayLen];
|
||||||
|
for(int i=0; i < arrayLen; i++)
|
||||||
|
{{
|
||||||
|
var itemLen = buffer[offset++];
|
||||||
|
if(itemLen > 16)
|
||||||
|
throw new FormatException(""Format_IPAddressBytesArrayToLong"");
|
||||||
|
ret.{name}[i] = new IPAddress(buffer.Slice(offset, itemLen).ToArray());
|
||||||
|
offset += itemLen;
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}");
|
||||||
|
break;
|
||||||
case "bool":
|
case "bool":
|
||||||
s.Append($@"
|
s.Append($@"
|
||||||
ret.{name} = buffer[offset++] == 0 ? false : true;");
|
ret.{name} = buffer[offset++] == 0 ? false : true;");
|
||||||
|
|
@ -260,6 +325,9 @@ using System.Text;
|
||||||
shift += 7;
|
shift += 7;
|
||||||
}} while ((b & 0x80) != 0);
|
}} while ((b & 0x80) != 0);
|
||||||
|
|
||||||
|
if(strLen > {maxLength})
|
||||||
|
throw new FormatException(""Format_StringToLong"");
|
||||||
|
|
||||||
if(strLen > 0)
|
if(strLen > 0)
|
||||||
{{
|
{{
|
||||||
ret.{name} = Encoding.UTF8.GetString(buffer.Slice(offset, strLen).ToArray());
|
ret.{name} = Encoding.UTF8.GetString(buffer.Slice(offset, strLen).ToArray());
|
||||||
|
|
@ -271,7 +339,7 @@ using System.Text;
|
||||||
case "byte[]?":
|
case "byte[]?":
|
||||||
s.Append($@"
|
s.Append($@"
|
||||||
{{
|
{{
|
||||||
int strLen = 0;
|
int byteLen = 0;
|
||||||
int shift = 0;
|
int shift = 0;
|
||||||
byte b;
|
byte b;
|
||||||
do {{
|
do {{
|
||||||
|
|
@ -282,14 +350,17 @@ using System.Text;
|
||||||
|
|
||||||
// ReadByte handles end of stream cases for us.
|
// ReadByte handles end of stream cases for us.
|
||||||
b = buffer[offset++];
|
b = buffer[offset++];
|
||||||
strLen |= (b & 0x7F) << shift;
|
byteLen |= (b & 0x7F) << shift;
|
||||||
shift += 7;
|
shift += 7;
|
||||||
}} while ((b & 0x80) != 0);
|
}} while ((b & 0x80) != 0);
|
||||||
|
|
||||||
if(strLen > 0)
|
if(byteLen > {maxLength})
|
||||||
|
throw new FormatException(""Format_ByteArrayToLong"");
|
||||||
|
|
||||||
|
if(byteLen > 0)
|
||||||
{{
|
{{
|
||||||
ret.{name} = buffer.Slice(offset, strLen).ToArray();
|
ret.{name} = buffer.Slice(offset, byteLen).ToArray();
|
||||||
offset += strLen;
|
offset += byteLen;
|
||||||
}}
|
}}
|
||||||
}}");
|
}}");
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,6 @@
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Remove="bin\Release\netstandard2.0\\LobbyServerSourceGenerator.dll" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue