Files
sbox-public/engine/Sandbox.Engine/Systems/Networking/System/NetworkSystem.Connections.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

179 lines
4.7 KiB
C#

using Sandbox.Engine;
using Sandbox.Internal;
namespace Sandbox.Network;
internal partial class NetworkSystem
{
[SkipHotload]
private readonly HashSet<Connection> _connections = [];
[SkipHotload]
private readonly Dictionary<Guid, Connection> _connectionLookup = new();
public IEnumerable<Connection> Connections => _connections;
/// <summary>
/// If true then this host is sending assemblies and other files via network tables
/// and as such, does not need to load assemblies from the package (if it even exists).
/// </summary>
public bool IsDeveloperHost { get; set; }
/// <summary>
/// Get whether there are any connections still doing the handshake process.
/// </summary>
/// <returns></returns>
internal bool IsHandshaking()
{
return _connections.Count != 0 && _connections.Any( c => c.State != Connection.ChannelState.Connected );
}
/// <summary>
/// Find a connection by its unique id.
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
internal Connection FindConnection( Guid id )
{
if ( Connection is not null && Connection.Id == id )
return Connection;
if ( _connectionLookup.TryGetValue( id, out var cached ) )
return cached;
foreach ( var c in _connections )
{
if ( c.Id == id )
{
_connectionLookup[id] = c;
return c;
}
}
return null;
}
internal void OnConnected( Connection channel )
{
Log.Trace( $"{this}: On Connected {channel}" );
_connections.Add( channel );
channel.InitializeSystem( this );
if ( IsHost )
{
// If we're the host and have a new connection then start handshaking
channel.GenerateConnectionId();
StartHandshake( channel );
}
if ( channel.Id != Guid.Empty )
{
_connectionLookup[channel.Id] = channel;
}
}
/// <summary>
/// Start the handshaking process with the specified client <see cref="Connection"/>.
/// </summary>
/// <param name="channel"></param>
void StartHandshake( Connection channel )
{
channel.HandshakeId = Guid.NewGuid();
var hello = new ServerInfo
{
ServerName = Networking.ServerName,
ServerData = Networking.ServerData,
MaxPlayers = Networking.MaxPlayers,
MapName = Networking.MapName,
EngineVersion = 234,
GamePackage = Application.GamePackage?.GetIdent( false, true ) ?? "",
MapPackage = Application.MapPackage?.GetIdent( false, true ) ?? "",
Host = new ChannelInfo { Id = Connection.Local.Id },
Assigned = new ChannelInfo { Id = channel.Id },
IsDeveloperHost = IsDeveloperHost,
HandshakeId = channel.HandshakeId
};
channel.SendMessage( hello );
channel.State = Connection.ChannelState.LoadingServerInformation;
}
/// <summary>
/// Restart the handshaking process. This could be used if the host changed
/// while we're connecting.
/// </summary>
internal void RestartHandshake()
{
var host = _connections.FirstOrDefault( c => c.IsHost );
if ( host is null )
return;
Connection.Local.State = Connection.ChannelState.Unconnected;
host.SendMessage( new RestartHandshakeMsg() );
}
/// <summary>
/// Called from the host to add a connection to the ConnectionInfo table
/// </summary>
internal void AddConnection( Connection source, UserInfo data )
{
var info = ConnectionInfo.Add( source );
info.Update( data );
OnConnectionInfoUpdated();
}
internal void OnDisconnected( Connection source )
{
if ( source.State >= Connection.ChannelState.Welcome )
{
// We only need to call this if we called OnConnected, which would only
// be the case if they got to at least the Welcome state during the handshake.
GameSystem?.OnLeave( source );
}
_connectionLookup.Remove( source.Id );
_connections.Remove( source );
source.State = Connection.ChannelState.Unconnected;
if ( !IsHost )
return;
Log.Info( $"{source.DisplayName} [{source.SteamId}] disconnected" );
ConnectionInfo.Remove( source.Id );
OnConnectionInfoUpdated();
}
/// <summary>
/// We were disconnected from the server. This will almost always be when we're on a dedicated server
/// and it has closed down.
/// </summary>
internal void OnServerDisconnection( int reasonCode, string reasonString )
{
if ( Connection.Local.State == Connection.ChannelState.Unconnected )
return;
IGameInstanceDll.Current.Disconnect();
IMenuSystem.ShowServerError( "Disconnected", reasonString );
Log.Warning( $"Disconnecting - {reasonString}" );
}
/// <summary>
/// Called any time we suspect the connection info might have changed, which
/// lets us tell the sockets/connections to update any information they hold.
/// </summary>
internal void OnConnectionInfoUpdated()
{
foreach ( var socket in sockets )
{
socket.OnConnectionInfoUpdated( this );
}
_connectionLookup.Clear();
}
}