using FishNet.Managing; using FishNet.Managing.Transporting; using System; using SynapseSocket.Core.Configuration; using UnityEngine; namespace FishNet.Transporting.Synapse { /// /// FishNet transport backed by the SynapseSocket UDP networking library. /// [DisallowMultipleComponent] [AddComponentMenu("FishNet/Transport/Synapse")] public class Synapse : Transport { ~Synapse() { Shutdown(); } /* Server. */ /// /// IPv4 address to bind the server to. Leave empty to bind to all interfaces. /// [Tooltip("IPv4 address to bind the server to. Leave empty to bind to all interfaces.")] [SerializeField] private string _ipv4BindAddress = string.Empty; /// /// IPv6 address to bind the server to. Leave empty to disable IPv6 binding. /// [Tooltip("IPv6 address to bind the server to. Leave empty to disable IPv6 binding.")] [SerializeField] private string _ipv6BindAddress = string.Empty; /// /// Port used by both server and client. /// [Tooltip("Port used by both server and client.")] [SerializeField] private ushort _port = 7770; /// /// Maximum number of clients that may be connected at once. /// [Tooltip("Maximum number of clients that may be connected at once.")] [Range(1, 9999)] [SerializeField] private int _maximumClients = 4095; /* Client. */ /// /// Address the client will connect to. /// [Tooltip("Address the client will connect to.")] [SerializeField] private string _clientAddress = "localhost"; /// /// Advanced Synapse configuration applied when starting the server or client. /// [Tooltip("Advanced Synapse configuration applied when starting the server or client.")] [SerializeField] private SynapseConfig _synapseConfig = new(); /// /// Server socket. Exposed for advanced use; access with caution outside this class. /// public Server.ServerSocket ServerSocket = new(); /// /// Client socket. Exposed for advanced use; access with caution outside this class. /// public Client.ClientSocket ClientSocket = new(); /// /// MTU returned to FishNet for packet sizing decisions. /// Synapse handles segmentation internally, so this reflects the per-segment wire limit. /// private const int MaximumTransmissionUnit = 1200; #region Initialization. /// /// Called by FishNet to initialize this transport. /// public override void Initialize(NetworkManager networkManager, int transportIndex) { base.Initialize(networkManager, transportIndex); } private void OnDestroy() { Shutdown(); } #endregion #region Connection states. /// /// Called when the local client connection state changes. /// public override event Action OnClientConnectionState; /// /// Called when the local server connection state changes. /// public override event Action OnServerConnectionState; /// /// Called when a remote client connection state changes. /// public override event Action OnRemoteConnectionState; /// /// Returns the local connection state for either the server or client. /// public override LocalConnectionState GetConnectionState(bool isServer) { if (isServer) return ServerSocket.GetConnectionState(); else return ClientSocket.GetConnectionState(); } /// /// Returns the connection state of a remote client on the server. /// public override RemoteConnectionState GetConnectionState(int connectionId) { return ServerSocket.GetConnectionState(connectionId); } /// /// Returns the remote endpoint address for a given connection ID. /// public override string GetConnectionAddress(int connectionId) { return ServerSocket.GetConnectionAddress(connectionId); } /// /// Forwards a client connection state change to subscribers. /// public override void HandleClientConnectionState(ClientConnectionStateArgs clientConnectionStateArgs) { OnClientConnectionState?.Invoke(clientConnectionStateArgs); } /// /// Forwards a server connection state change to subscribers. /// public override void HandleServerConnectionState(ServerConnectionStateArgs serverConnectionStateArgs) { OnServerConnectionState?.Invoke(serverConnectionStateArgs); } /// /// Forwards a remote client connection state change to subscribers. /// public override void HandleRemoteConnectionState(RemoteConnectionStateArgs remoteConnectionStateArgs) { OnRemoteConnectionState?.Invoke(remoteConnectionStateArgs); } #endregion #region Iterating. /// /// Processes data received by the socket. /// public override void IterateIncoming(bool isServer) { if (isServer) ServerSocket.IterateIncoming(); else ClientSocket.IterateIncoming(); } /// /// Processes data to be sent by the socket. /// public override void IterateOutgoing(bool isServer) { if (isServer) ServerSocket.IterateOutgoing(); else ClientSocket.IterateOutgoing(); } #endregion #region Sending. /// /// Sends data from the local client to the server. /// public override void SendToServer(byte channelId, ArraySegment segment) { SanitizeChannel(ref channelId); ClientSocket.SendToServer(channelId, segment); } /// /// Sends data from the server to a specific client. /// public override void SendToClient(byte channelId, ArraySegment segment, int connectionId) { SanitizeChannel(ref channelId); ServerSocket.SendToClient(channelId, segment, connectionId); } #endregion #region Receiving. /// /// Called when the client receives data. /// public override event Action OnClientReceivedData; /// /// Forwards received client data to subscribers. /// public override void HandleClientReceivedDataArgs(ClientReceivedDataArgs clientReceivedDataArgs) { OnClientReceivedData?.Invoke(clientReceivedDataArgs); } /// /// Called when the server receives data. /// public override event Action OnServerReceivedData; /// /// Forwards received server data to subscribers. /// public override void HandleServerReceivedDataArgs(ServerReceivedDataArgs serverReceivedDataArgs) { OnServerReceivedData?.Invoke(serverReceivedDataArgs); } #endregion #region Configuration. /// /// Returns how long in seconds until a connection times out. /// public override float GetTimeout(bool isServer) { return _synapseConfig.Connection.TimeoutMilliseconds / 1000f; } /// /// Sets how long in seconds until a connection times out. /// Takes effect on the next connection start. /// public override void SetTimeout(float value, bool isServer) { _synapseConfig.Connection.TimeoutMilliseconds = (uint)(Math.Max(0f, value) * 1000f); } /// /// Returns the maximum number of clients allowed to connect. /// public override int GetMaximumClients() { return ServerSocket.GetMaximumClients(); } /// /// Sets the maximum number of clients allowed to connect. /// public override void SetMaximumClients(int value) { _maximumClients = value; ServerSocket.SetMaximumClients(value); } /// /// Sets the address the client will connect to. /// public override void SetClientAddress(string address) { _clientAddress = address; } /// /// Returns the address the client will connect to. /// public override string GetClientAddress() { return _clientAddress; } /// /// Sets the address the server will bind to. /// public override void SetServerBindAddress(string address, IPAddressType addressType) { if (addressType == IPAddressType.IPv4) _ipv4BindAddress = address; else _ipv6BindAddress = address; } /// /// Returns the address the server will bind to. /// public override string GetServerBindAddress(IPAddressType addressType) { return addressType == IPAddressType.IPv4 ? _ipv4BindAddress : _ipv6BindAddress; } /// /// Sets the port used by both server and client. /// public override void SetPort(ushort port) { _port = port; } /// /// Returns the active port. Prefers the server port, then the client port, then the configured value. /// public override ushort GetPort() { ushort? serverPort = ServerSocket?.GetPort(); if (serverPort.HasValue) return serverPort.Value; ushort? clientPort = ClientSocket?.GetPort(); if (clientPort.HasValue) return clientPort.Value; return _port; } #endregion #region Start and stop. /// /// Starts the local server or client. /// public override bool StartConnection(bool isServer) { if (isServer) return StartServer(); return StartClient(_clientAddress); } /// /// Stops the local server or client. /// public override bool StopConnection(bool isServer) { if (isServer) return StopServer(); return StopClient(); } /// /// Stops a remote client from the server. /// public override bool StopConnection(int connectionId, bool immediately) { return ServerSocket.StopConnection(connectionId); } /// /// Stops both the server and client. /// public override void Shutdown() { StopConnection(false); StopConnection(true); } private bool StartServer() { ServerSocket.Initialize(this); return ServerSocket.StartConnection(_port, _maximumClients, _ipv4BindAddress, _ipv6BindAddress, _synapseConfig); } private bool StopServer() { if (ServerSocket is null) return false; return ServerSocket.StopConnection(); } private bool StartClient(string address) { ClientSocket.Initialize(this); return ClientSocket.StartConnection(address, _port, _synapseConfig); } private bool StopClient() { if (ClientSocket is null) return false; return ClientSocket.StopConnection(); } #endregion #region Channels. /// /// Clamps an invalid channel ID to reliable (0). /// private void SanitizeChannel(ref byte channelId) { if (channelId >= TransportManager.CHANNEL_COUNT) { NetworkManager.LogWarning($"Channel {channelId} is out of range. Defaulting to reliable."); channelId = 0; } } /// /// Returns the MTU for the given channel. /// Synapse handles segmentation internally; this value guides FishNet's packet sizing. /// public override int GetMTU(byte channel) { return MaximumTransmissionUnit; } #endregion } }