forked from cgvr/DeltaVR
		
	
		
			
				
	
	
		
			312 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			312 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using FishNet.Managing;
 | 
						|
using FishNet.Managing.Logging;
 | 
						|
using FishNet.Transporting;
 | 
						|
using System;
 | 
						|
using System.Net;
 | 
						|
using System.Net.Sockets;
 | 
						|
using System.Text;
 | 
						|
using System.Threading.Tasks;
 | 
						|
using UnityEngine;
 | 
						|
 | 
						|
namespace FishNet.Discovery
 | 
						|
{
 | 
						|
	/// <summary>
 | 
						|
	/// A component that advertises a server or searches for servers.
 | 
						|
	/// </summary>
 | 
						|
	public sealed class NetworkDiscovery : MonoBehaviour
 | 
						|
	{
 | 
						|
		/// <summary>
 | 
						|
		/// A string that differentiates your application/game from others.
 | 
						|
		/// <b>Must not</b> be null, empty, or blank.
 | 
						|
		/// </summary>
 | 
						|
		[SerializeField]
 | 
						|
		[Tooltip("A string that differentiates your application/game from others. Must not be null, empty, or blank.")]
 | 
						|
		private string secret;
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// The port number used by this <see cref="NetworkDiscovery"/> component.
 | 
						|
		/// <b>Must</b> be different from the one used by the <seealso cref="Transport"/>.
 | 
						|
		/// </summary>
 | 
						|
		[SerializeField]
 | 
						|
		[Tooltip("The port number used by this NetworkDiscovery component. Must be different from the one used by the Transport.")]
 | 
						|
		private ushort port;
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// How often does this <see cref="NetworkDiscovery"/> component advertises a server or searches for servers.
 | 
						|
		/// </summary>
 | 
						|
		[SerializeField]
 | 
						|
		[Tooltip("How often does this NetworkDiscovery component advertises a server or searches for servers.")]
 | 
						|
		private float discoveryInterval;
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// Whether this <see cref="NetworkDiscovery"/> component will automatically start/stop? <b>Setting this to true is recommended.</b>
 | 
						|
		/// </summary>
 | 
						|
		[SerializeField]
 | 
						|
		[Tooltip("Whether this NetworkDiscovery component will automatically start/stop? Setting this to true is recommended.")]
 | 
						|
		private bool automatic;
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// The <see cref="UdpClient"/> used to advertise the server.
 | 
						|
		/// </summary>
 | 
						|
		private UdpClient _serverUdpClient;
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// The <see cref="UdpClient"/> used to search for servers.
 | 
						|
		/// </summary>
 | 
						|
		private UdpClient _clientUdpClient;
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// Whether this <see cref="NetworkDiscovery"/> component is currently advertising a server or not.
 | 
						|
		/// </summary>
 | 
						|
		public bool IsAdvertising => _serverUdpClient != null;
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// Whether this <see cref="NetworkDiscovery"/> component is currently searching for servers or not.
 | 
						|
		/// </summary>
 | 
						|
		public bool IsSearching => _clientUdpClient != null;
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// An <see cref="Action"/> that is invoked by this <seealso cref="NetworkDiscovery"/> component whenever a server is found.
 | 
						|
		/// </summary>
 | 
						|
		public event Action<IPEndPoint> ServerFoundCallback;
 | 
						|
 | 
						|
		private void Start()
 | 
						|
		{
 | 
						|
			if (automatic)
 | 
						|
			{
 | 
						|
				InstanceFinder.ServerManager.OnServerConnectionState += ServerConnectionStateChangedHandler;
 | 
						|
 | 
						|
				InstanceFinder.ClientManager.OnClientConnectionState += ClientConnectionStateChangedHandler;
 | 
						|
 | 
						|
				StartSearchingForServers();
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		private void OnDisable()
 | 
						|
		{
 | 
						|
			InstanceFinder.ServerManager.OnServerConnectionState -= ServerConnectionStateChangedHandler;
 | 
						|
 | 
						|
			InstanceFinder.ClientManager.OnClientConnectionState -= ClientConnectionStateChangedHandler;
 | 
						|
 | 
						|
			StopAdvertisingServer();
 | 
						|
 | 
						|
			StopSearchingForServers();
 | 
						|
		}
 | 
						|
 | 
						|
		private void OnDestroy()
 | 
						|
		{
 | 
						|
			InstanceFinder.ServerManager.OnServerConnectionState -= ServerConnectionStateChangedHandler;
 | 
						|
 | 
						|
			InstanceFinder.ClientManager.OnClientConnectionState -= ClientConnectionStateChangedHandler;
 | 
						|
 | 
						|
			StopAdvertisingServer();
 | 
						|
 | 
						|
			StopSearchingForServers();
 | 
						|
		}
 | 
						|
 | 
						|
		private void OnApplicationQuit()
 | 
						|
		{
 | 
						|
			InstanceFinder.ServerManager.OnServerConnectionState -= ServerConnectionStateChangedHandler;
 | 
						|
 | 
						|
			InstanceFinder.ClientManager.OnClientConnectionState -= ClientConnectionStateChangedHandler;
 | 
						|
 | 
						|
			StopAdvertisingServer();
 | 
						|
 | 
						|
			StopSearchingForServers();
 | 
						|
		}
 | 
						|
 | 
						|
		#region Connection State Handlers
 | 
						|
 | 
						|
		private void ServerConnectionStateChangedHandler(ServerConnectionStateArgs args)
 | 
						|
		{
 | 
						|
			if (args.ConnectionState == LocalConnectionState.Starting)
 | 
						|
			{
 | 
						|
				StopSearchingForServers();
 | 
						|
			}
 | 
						|
			else if (args.ConnectionState == LocalConnectionState.Started)
 | 
						|
			{
 | 
						|
				StartAdvertisingServer();
 | 
						|
			}
 | 
						|
			else if (args.ConnectionState == LocalConnectionState.Stopping)
 | 
						|
			{
 | 
						|
				//StopAdvertisingServer();
 | 
						|
			}
 | 
						|
			else if (args.ConnectionState == LocalConnectionState.Stopped)
 | 
						|
			{
 | 
						|
				StartSearchingForServers();
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		private void ClientConnectionStateChangedHandler(ClientConnectionStateArgs args)
 | 
						|
		{
 | 
						|
			if (args.ConnectionState == LocalConnectionState.Starting)
 | 
						|
			{
 | 
						|
				StopSearchingForServers();
 | 
						|
			}
 | 
						|
			else if (args.ConnectionState == LocalConnectionState.Stopped)
 | 
						|
			{
 | 
						|
				StartSearchingForServers();
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		#endregion
 | 
						|
 | 
						|
		#region Server
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// Makes this <see cref="NetworkDiscovery"/> component start advertising a server.
 | 
						|
		/// </summary>
 | 
						|
		public void StartAdvertisingServer()
 | 
						|
		{
 | 
						|
			//Debug.Log("NetworkDiscovery is advertizing on port " + port);
 | 
						|
			if (!InstanceFinder.IsServer)
 | 
						|
			{
 | 
						|
				if (NetworkManager.StaticCanLog(LoggingType.Warning)) Debug.LogWarning("Unable to start advertising server. Server is inactive.", this);
 | 
						|
 | 
						|
				return;
 | 
						|
			}
 | 
						|
 | 
						|
			if (_serverUdpClient != null)
 | 
						|
			{
 | 
						|
				if (NetworkManager.StaticCanLog(LoggingType.Common)) Debug.Log("Server is already being advertised.", this);
 | 
						|
 | 
						|
				return;
 | 
						|
			}
 | 
						|
 | 
						|
			if (port == InstanceFinder.TransportManager.Transport.GetPort())
 | 
						|
			{
 | 
						|
				if (NetworkManager.StaticCanLog(LoggingType.Warning)) Debug.LogWarning("Unable to start advertising server on the same port as the transport.", this);
 | 
						|
 | 
						|
				return;
 | 
						|
			}
 | 
						|
 | 
						|
			_serverUdpClient = new UdpClient(port)
 | 
						|
			{
 | 
						|
				EnableBroadcast = true,
 | 
						|
				MulticastLoopback = false,
 | 
						|
			};
 | 
						|
			//Debug.Log("UDP = " + _serverUdpClient.ToString());
 | 
						|
			Task.Run(AdvertiseServerAsync);
 | 
						|
 | 
						|
			if (NetworkManager.StaticCanLog(LoggingType.Common)) Debug.Log("Started advertising server.", this);
 | 
						|
		}
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// Makes this <see cref="NetworkDiscovery"/> component <i>immediately</i> stop advertising the server it is currently advertising.
 | 
						|
		/// </summary>
 | 
						|
		public void StopAdvertisingServer()
 | 
						|
		{
 | 
						|
			if (_serverUdpClient == null) return;
 | 
						|
 | 
						|
			_serverUdpClient.Close();
 | 
						|
 | 
						|
			_serverUdpClient = null;
 | 
						|
 | 
						|
			if (NetworkManager.StaticCanLog(LoggingType.Common)) Debug.Log("Stopped advertising server.", this);
 | 
						|
		}
 | 
						|
 | 
						|
		private async void AdvertiseServerAsync()
 | 
						|
		{
 | 
						|
			while (_serverUdpClient != null)
 | 
						|
			{
 | 
						|
				
 | 
						|
				await Task.Delay(TimeSpan.FromSeconds(discoveryInterval));
 | 
						|
				
 | 
						|
				UdpReceiveResult result = await _serverUdpClient.ReceiveAsync();
 | 
						|
				Debug.Log("Awaiting for server");
 | 
						|
				string receivedSecret = Encoding.UTF8.GetString(result.Buffer);
 | 
						|
				Debug.Log(receivedSecret);
 | 
						|
 | 
						|
				if (receivedSecret == secret)
 | 
						|
				{
 | 
						|
					byte[] okBytes = BitConverter.GetBytes(true);
 | 
						|
 | 
						|
					await _serverUdpClient.SendAsync(okBytes, okBytes.Length, result.RemoteEndPoint);
 | 
						|
				}
 | 
						|
			}
 | 
						|
			Debug.Log("_serverUdpClient == null");
 | 
						|
		}
 | 
						|
 | 
						|
		#endregion
 | 
						|
 | 
						|
		#region Client
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// Makes this <see cref="NetworkDiscovery"/> component start searching for servers.
 | 
						|
		/// </summary>
 | 
						|
		public void StartSearchingForServers()
 | 
						|
		{
 | 
						|
			// if (InstanceFinder.IsServer)
 | 
						|
			// {
 | 
						|
			// 	if (NetworkManager.StaticCanLog(LoggingType.Warning)) Debug.LogWarning("Unable to start searching for servers. Server is active.", this);
 | 
						|
			//
 | 
						|
			// 	return;
 | 
						|
			// }
 | 
						|
			Debug.Log("NetworkDiscovery is searching");
 | 
						|
 | 
						|
			if (InstanceFinder.IsClient)
 | 
						|
			{
 | 
						|
				if (NetworkManager.StaticCanLog(LoggingType.Warning)) Debug.LogWarning("Unable to start searching for servers. Client is active.", this);
 | 
						|
 | 
						|
				return;
 | 
						|
			}
 | 
						|
 | 
						|
			if (_clientUdpClient != null)
 | 
						|
			{
 | 
						|
				if (NetworkManager.StaticCanLog(LoggingType.Common)) Debug.Log("Already searching for servers.", this);
 | 
						|
 | 
						|
				return;
 | 
						|
			}
 | 
						|
 | 
						|
			_clientUdpClient = new UdpClient()
 | 
						|
			{
 | 
						|
				EnableBroadcast = true,
 | 
						|
				MulticastLoopback = false,
 | 
						|
			};
 | 
						|
 | 
						|
			Task.Run(SearchForServersAsync);
 | 
						|
 | 
						|
			if (NetworkManager.StaticCanLog(LoggingType.Common)) Debug.Log("Started searching for servers.", this);
 | 
						|
		}
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// Makes this <see cref="NetworkDiscovery"/> component <i>immediately</i> stop searching for servers.
 | 
						|
		/// </summary>
 | 
						|
		public void StopSearchingForServers()
 | 
						|
		{
 | 
						|
			if (_clientUdpClient == null) return;
 | 
						|
 | 
						|
			_clientUdpClient.Close();
 | 
						|
 | 
						|
			_clientUdpClient = null;
 | 
						|
 | 
						|
			if (NetworkManager.StaticCanLog(LoggingType.Common)) Debug.Log("Stopped searching for servers.", this);
 | 
						|
		}
 | 
						|
 | 
						|
		private async void SearchForServersAsync()
 | 
						|
		{
 | 
						|
			byte[] secretBytes = Encoding.UTF8.GetBytes(secret);
 | 
						|
 | 
						|
			IPEndPoint endPoint = new IPEndPoint(IPAddress.Broadcast, port);
 | 
						|
 | 
						|
			while (_clientUdpClient != null)
 | 
						|
			{
 | 
						|
				await Task.Delay(TimeSpan.FromSeconds(discoveryInterval));
 | 
						|
 | 
						|
				await _clientUdpClient.SendAsync(secretBytes, secretBytes.Length, endPoint);
 | 
						|
 | 
						|
				UdpReceiveResult result = await _clientUdpClient.ReceiveAsync();
 | 
						|
 | 
						|
				if (BitConverter.ToBoolean(result.Buffer, 0))
 | 
						|
				{
 | 
						|
					ServerFoundCallback?.Invoke(result.RemoteEndPoint);
 | 
						|
 | 
						|
					StopSearchingForServers();
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		#endregion
 | 
						|
	}
 | 
						|
}
 |