Files
2026-03-04 09:37:33 +08:00

396 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using FishNet.Managing;
using FishNet.Transporting;
using FishySteamworks.Client;
using Steamworks;
namespace FishySteamworks.Server
{
public class ServerSocket : CommonSocket
{
public struct ConnectionChange
{
public int ConnectionId;
public HSteamNetConnection SteamConnection;
public CSteamID SteamId;
public bool IsConnect => SteamId.IsValid();
public ConnectionChange(int id)
{
ConnectionId = id;
SteamId = CSteamID.Nil;
SteamConnection = default(HSteamNetConnection);
}
public ConnectionChange(int id, HSteamNetConnection steamConnection, CSteamID steamId)
{
ConnectionId = id;
SteamConnection = steamConnection;
SteamId = steamId;
}
}
private BidirectionalDictionary<HSteamNetConnection, int> _steamConnections = new BidirectionalDictionary<HSteamNetConnection, int>();
private BidirectionalDictionary<CSteamID, int> _steamIds = new BidirectionalDictionary<CSteamID, int>();
private int _maximumClients;
private int _nextConnectionId;
private HSteamListenSocket _socket = new HSteamListenSocket(0u);
private Queue<LocalPacket> _clientHostIncoming = new Queue<LocalPacket>();
private bool _clientHostStarted;
private Callback<SteamNetConnectionStatusChangedCallback_t> _onRemoteConnectionStateCallback;
private Queue<int> _cachedConnectionIds = new Queue<int>();
private ClientHostSocket _clientHost;
private bool _iteratingConnections;
private List<ConnectionChange> _pendingConnectionChanges = new List<ConnectionChange>();
internal RemoteConnectionState GetConnectionState(int connectionId)
{
if (_steamConnections.Second.ContainsKey(connectionId))
{
return RemoteConnectionState.Started;
}
return RemoteConnectionState.Stopped;
}
internal void ResetInvalidSocket()
{
if (_socket == HSteamListenSocket.Invalid)
{
base.SetLocalConnectionState(LocalConnectionState.Stopped, server: true);
}
}
internal bool StartConnection(string address, ushort port, int maximumClients, bool peerToPeer)
{
try
{
if (_onRemoteConnectionStateCallback == null)
{
_onRemoteConnectionStateCallback = Callback<SteamNetConnectionStatusChangedCallback_t>.Create(OnRemoteConnectionState);
}
PeerToPeer = peerToPeer;
byte[] array = ((!peerToPeer) ? GetIPBytes(address) : null);
PeerToPeer = peerToPeer;
SetMaximumClients(maximumClients);
_nextConnectionId = 0;
_cachedConnectionIds.Clear();
_iteratingConnections = false;
base.SetLocalConnectionState(LocalConnectionState.Starting, server: true);
SteamNetworkingConfigValue_t[] array2 = new SteamNetworkingConfigValue_t[0];
if (PeerToPeer)
{
_socket = SteamNetworkingSockets.CreateListenSocketP2P(0, array2.Length, array2);
}
else
{
SteamNetworkingIPAddr localAddress = default(SteamNetworkingIPAddr);
localAddress.Clear();
if (array != null)
{
localAddress.SetIPv6(array, port);
}
_socket = SteamNetworkingSockets.CreateListenSocketIP(ref localAddress, 0, array2);
}
}
catch
{
base.SetLocalConnectionState(LocalConnectionState.Stopped, server: true);
return false;
}
if (_socket == HSteamListenSocket.Invalid)
{
base.SetLocalConnectionState(LocalConnectionState.Stopped, server: true);
return false;
}
base.SetLocalConnectionState(LocalConnectionState.Started, server: true);
return true;
}
internal bool StopConnection()
{
if (_socket != HSteamListenSocket.Invalid)
{
SteamNetworkingSockets.CloseListenSocket(_socket);
if (_onRemoteConnectionStateCallback != null)
{
_onRemoteConnectionStateCallback.Dispose();
_onRemoteConnectionStateCallback = null;
}
_socket = HSteamListenSocket.Invalid;
}
_pendingConnectionChanges.Clear();
if (GetLocalConnectionState() == LocalConnectionState.Stopped)
{
return false;
}
base.SetLocalConnectionState(LocalConnectionState.Stopping, server: true);
base.SetLocalConnectionState(LocalConnectionState.Stopped, server: true);
return true;
}
internal bool StopConnection(int connectionId)
{
if (connectionId == 32767)
{
if (_clientHost != null)
{
_clientHost.StopConnection();
return true;
}
return false;
}
if (_steamConnections.Second.TryGetValue(connectionId, out var value))
{
return StopConnection(connectionId, value);
}
Transport.NetworkManager.LogError($"Steam connection not found for connectionId {connectionId}.");
return false;
}
private bool StopConnection(int connectionId, HSteamNetConnection socket)
{
SteamNetworkingSockets.CloseConnection(socket, 0, string.Empty, bEnableLinger: false);
if (!_iteratingConnections)
{
RemoveConnection(connectionId);
}
else
{
_pendingConnectionChanges.Add(new ConnectionChange(connectionId));
}
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void OnRemoteConnectionState(SteamNetConnectionStatusChangedCallback_t args)
{
ulong steamID = args.m_info.m_identityRemote.GetSteamID64();
if (args.m_info.m_eState == ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_Connecting)
{
if (_steamConnections.Count >= GetMaximumClients())
{
Transport.NetworkManager.Log($"Incoming connection {steamID} was rejected because would exceed the maximum connection count.");
SteamNetworkingSockets.CloseConnection(args.m_hConn, 0, "Max Connection Count", bEnableLinger: false);
return;
}
EResult eResult = SteamNetworkingSockets.AcceptConnection(args.m_hConn);
if (eResult == EResult.k_EResultOK)
{
Transport.NetworkManager.Log($"Accepting connection {steamID}");
}
else
{
Transport.NetworkManager.Log($"Connection {steamID} could not be accepted: {eResult.ToString()}");
}
}
else if (args.m_info.m_eState == ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_Connected)
{
int num = ((_cachedConnectionIds.Count > 0) ? _cachedConnectionIds.Dequeue() : _nextConnectionId++);
if (!_iteratingConnections)
{
AddConnection(num, args.m_hConn, args.m_info.m_identityRemote.GetSteamID());
}
else
{
_pendingConnectionChanges.Add(new ConnectionChange(num, args.m_hConn, args.m_info.m_identityRemote.GetSteamID()));
}
}
else if (args.m_info.m_eState == ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_ClosedByPeer || args.m_info.m_eState == ESteamNetworkingConnectionState.k_ESteamNetworkingConnectionState_ProblemDetectedLocally)
{
if (_steamConnections.TryGetValue(args.m_hConn, out var value))
{
StopConnection(value, args.m_hConn);
}
}
else
{
Transport.NetworkManager.Log($"Connection {steamID} state changed: {args.m_info.m_eState.ToString()}");
}
}
private void AddConnection(int connectionId, HSteamNetConnection steamConnection, CSteamID steamId)
{
_steamConnections.Add(steamConnection, connectionId);
_steamIds.Add(steamId, connectionId);
Transport.NetworkManager.Log($"Client with SteamID {steamId.m_SteamID} connected. Assigning connection id {connectionId}");
Transport.HandleRemoteConnectionState(new RemoteConnectionStateArgs(RemoteConnectionState.Started, connectionId, Transport.Index));
}
private void RemoveConnection(int connectionId)
{
_steamConnections.Remove(connectionId);
_steamIds.Remove(connectionId);
Transport.NetworkManager.Log($"Client with ConnectionID {connectionId} disconnected.");
Transport.HandleRemoteConnectionState(new RemoteConnectionStateArgs(RemoteConnectionState.Stopped, connectionId, Transport.Index));
_cachedConnectionIds.Enqueue(connectionId);
}
internal void IterateOutgoing()
{
if (GetLocalConnectionState() != LocalConnectionState.Started)
{
return;
}
_iteratingConnections = true;
foreach (HSteamNetConnection firstType in _steamConnections.FirstTypes)
{
SteamNetworkingSockets.FlushMessagesOnConnection(firstType);
}
_iteratingConnections = false;
ProcessPendingConnectionChanges();
}
internal void IterateIncoming()
{
if (GetLocalConnectionState() == LocalConnectionState.Stopped || GetLocalConnectionState() == LocalConnectionState.Stopping)
{
return;
}
_iteratingConnections = true;
while (_clientHostIncoming.Count > 0)
{
LocalPacket localPacket = _clientHostIncoming.Dequeue();
ArraySegment<byte> data = new ArraySegment<byte>(localPacket.Data, 0, localPacket.Length);
Transport.HandleServerReceivedDataArgs(new ServerReceivedDataArgs(data, (Channel)localPacket.Channel, 32767, Transport.Index));
}
foreach (KeyValuePair<HSteamNetConnection, int> item in _steamConnections.First)
{
HSteamNetConnection key = item.Key;
int value = item.Value;
int num = SteamNetworkingSockets.ReceiveMessagesOnConnection(key, MessagePointers, 256);
if (num > 0)
{
for (int i = 0; i < num; i++)
{
GetMessage(MessagePointers[i], InboundBuffer, out var segment, out var channel);
Transport.HandleServerReceivedDataArgs(new ServerReceivedDataArgs(segment, (Channel)channel, value, Transport.Index));
}
}
}
_iteratingConnections = false;
ProcessPendingConnectionChanges();
}
private void ProcessPendingConnectionChanges()
{
foreach (ConnectionChange pendingConnectionChange in _pendingConnectionChanges)
{
if (pendingConnectionChange.IsConnect)
{
AddConnection(pendingConnectionChange.ConnectionId, pendingConnectionChange.SteamConnection, pendingConnectionChange.SteamId);
}
else
{
RemoveConnection(pendingConnectionChange.ConnectionId);
}
}
_pendingConnectionChanges.Clear();
}
internal void SendToClient(byte channelId, ArraySegment<byte> segment, int connectionId)
{
if (GetLocalConnectionState() != LocalConnectionState.Started)
{
return;
}
HSteamNetConnection value;
if (connectionId == 32767)
{
if (_clientHost != null)
{
LocalPacket packet = new LocalPacket(segment, channelId);
_clientHost.ReceivedFromLocalServer(packet);
}
}
else if (_steamConnections.TryGetValue(connectionId, out value))
{
EResult eResult = Send(value, segment, channelId);
switch (eResult)
{
case EResult.k_EResultNoConnection:
case EResult.k_EResultInvalidParam:
Transport.NetworkManager.Log($"Connection to {connectionId} was lost.");
StopConnection(connectionId, value);
break;
default:
Transport.NetworkManager.LogError("Could not send: " + eResult);
break;
case EResult.k_EResultOK:
break;
}
}
else
{
Transport.NetworkManager.LogError($"ConnectionId {connectionId} does not exist, data will not be sent.");
}
}
internal string GetConnectionAddress(int connectionId)
{
if (_steamIds.TryGetValue(connectionId, out var value))
{
return value.ToString();
}
Transport.NetworkManager.LogError($"ConnectionId {connectionId} is invalid; address cannot be returned.");
return string.Empty;
}
internal void SetMaximumClients(int value)
{
_maximumClients = Math.Min(value, 32766);
}
internal int GetMaximumClients()
{
return _maximumClients;
}
internal void SetClientHostSocket(ClientHostSocket socket)
{
_clientHost = socket;
}
internal void OnClientHostState(bool started)
{
FishySteamworks fishySteamworks = (FishySteamworks)Transport;
CSteamID key = new CSteamID(fishySteamworks.LocalUserSteamID);
if (!started && _clientHostStarted)
{
ClearQueue(_clientHostIncoming);
Transport.HandleRemoteConnectionState(new RemoteConnectionStateArgs(RemoteConnectionState.Stopped, 32767, Transport.Index));
_steamIds.Remove(key);
}
else if (started)
{
_steamIds[key] = 32767;
Transport.HandleRemoteConnectionState(new RemoteConnectionStateArgs(RemoteConnectionState.Started, 32767, Transport.Index));
}
_clientHostStarted = started;
}
internal void ReceivedFromClientHost(LocalPacket packet)
{
if (_clientHostStarted)
{
_clientHostIncoming.Enqueue(packet);
}
}
}
}