From e75b45f8889478456573ea395694b4efc560ace0 Mon Sep 17 00:00:00 2001 From: Paul Pacheco Date: Wed, 29 Jan 2020 02:56:29 -0600 Subject: [PATCH] feat: LAN Network discovery (#1453) * Fix typo * Updated Changelog * first commit * Add example for discovery * NetworkDiscovery component should be added * fixed UI * Fix some warnings * refactor: network discovery reimplemented * Remove unused GUIstyle * Fix namespaces * Just send to the broadcast address * Fix indentation * Log errors in ClientListen * Code formatting cleanup, HelpURL's fixed, comments revised. (#38) * Transport can now provide server uri * work with any transport by passing uri * Move discovery initialization to start * feat: Discovery can now be easily customized per game * Use generics to simplify api * Renamed ServerInfo -> ServerResponse * Rename method * Moved up one folder * Move ServerId to NetworkDiscovery * tests now reference Mirror.Discovery * Cleaned up blank space * Disable GUID apparently fixes it * Use UnityEvents for ease of use * Remove noisy log * remove blank spaces * Process request receives the client endpoint * use consistent name for parameters * Remove white space * Keep it minimalistic, we don't need age or totalPlayers * Comment non obvious property * Don't break transports * Documentation and image * Code formatting * removed privates * Added Range attribute * Rename ActiveDiscoverySecondInterval * Revised NetworkDiscovery doc * Swapped field order (Cosmetics) * Added ScriptTemplate * Update ProjectSettings/ProjectVersion.txt * Updated ScriptTemplate * Updated xml comment and ScriptTemplate * Updated ScriptTemplate * Improve xmldocs * Improve xmldocs * Remove leftover comment * Renamed event * Moved discovery inside components * Keep parameter names consistent * Provide a guide for network discovery * XML Comments and ScriptTemplate * Moved Credits * fixed template * Removed comment * removed comment * xml comments and template * fixed method name * fixed method and template * removed semicolon * fixed template * fixed method and template * fixed template * fixed template * Fix copypasta error * Show error if no url is available Network Discovery now shows an error if the transport does not support providing Url * Grammar fix * Extended Template * fixed template * Added guide link to template * New image * Update NetworkDiscovery.md * Updated Guid Doc and Template * fixed bullets * Remove unnecessary using * Make it like Mirror's * Update ScriptTemplates Image & Zip * Removed from Deprecations * Updated ChangeLog * Updated ChangeLog * Update NetworkDiscovery.md Remove last line...this was copied to the paragraph above the code block Co-authored-by: MrGadget --- Assets/Mirror/Components/Discovery.meta | 8 + .../Components/Discovery/NetworkDiscovery.cs | 112 +++ .../Discovery/NetworkDiscovery.cs.meta | 11 + .../Discovery/NetworkDiscoveryBase.cs | 357 ++++++++ .../Discovery/NetworkDiscoveryBase.cs.meta | 11 + .../Discovery/NetworkDiscoveryHUD.cs | 95 +++ .../Discovery/NetworkDiscoveryHUD.cs.meta | 11 + .../Components/Discovery/ServerRequest.cs | 4 + .../Discovery/ServerRequest.cs.meta | 11 + .../Components/Discovery/ServerResponse.cs | 18 + .../Discovery/ServerResponse.cs.meta | 11 + Assets/Mirror/Examples/Discovery.meta | 8 + Assets/Mirror/Examples/Discovery/Prefabs.meta | 8 + .../Examples/Discovery/Prefabs/Player.prefab | 110 +++ .../Discovery/Prefabs/Player.prefab.meta | 7 + Assets/Mirror/Examples/Discovery/Scenes.meta | 8 + .../Examples/Discovery/Scenes/Scene.unity | 785 ++++++++++++++++++ .../Discovery/Scenes/Scene.unity.meta | 7 + .../Runtime/Transport/FallbackTransport.cs | 1 + ...twork Discovery-NewNetworkDiscovery.cs.txt | 84 ++ ... Discovery-NewNetworkDiscovery.cs.txt.meta | 7 + doc/Components/NetworkDiscovery.md | 15 + doc/Components/NetworkDiscovery.png | Bin 0 -> 46924 bytes doc/Components/index.md | 2 + doc/Components/toc.yml | 2 + doc/General/ChangeLog.md | 4 +- doc/General/Deprecations.md | 4 - doc/General/ScriptTemplates.png | Bin 19926 -> 22177 bytes doc/General/ScriptTemplates.zip | Bin 6541 -> 7336 bytes doc/Guides/NetworkDiscovery.md | 107 +++ doc/Guides/toc.yml | 2 + 31 files changed, 1805 insertions(+), 5 deletions(-) create mode 100644 Assets/Mirror/Components/Discovery.meta create mode 100644 Assets/Mirror/Components/Discovery/NetworkDiscovery.cs create mode 100644 Assets/Mirror/Components/Discovery/NetworkDiscovery.cs.meta create mode 100644 Assets/Mirror/Components/Discovery/NetworkDiscoveryBase.cs create mode 100644 Assets/Mirror/Components/Discovery/NetworkDiscoveryBase.cs.meta create mode 100644 Assets/Mirror/Components/Discovery/NetworkDiscoveryHUD.cs create mode 100644 Assets/Mirror/Components/Discovery/NetworkDiscoveryHUD.cs.meta create mode 100644 Assets/Mirror/Components/Discovery/ServerRequest.cs create mode 100644 Assets/Mirror/Components/Discovery/ServerRequest.cs.meta create mode 100644 Assets/Mirror/Components/Discovery/ServerResponse.cs create mode 100644 Assets/Mirror/Components/Discovery/ServerResponse.cs.meta create mode 100644 Assets/Mirror/Examples/Discovery.meta create mode 100644 Assets/Mirror/Examples/Discovery/Prefabs.meta create mode 100644 Assets/Mirror/Examples/Discovery/Prefabs/Player.prefab create mode 100644 Assets/Mirror/Examples/Discovery/Prefabs/Player.prefab.meta create mode 100644 Assets/Mirror/Examples/Discovery/Scenes.meta create mode 100644 Assets/Mirror/Examples/Discovery/Scenes/Scene.unity create mode 100644 Assets/Mirror/Examples/Discovery/Scenes/Scene.unity.meta create mode 100644 Assets/ScriptTemplates/56-Mirror__Network Discovery-NewNetworkDiscovery.cs.txt create mode 100644 Assets/ScriptTemplates/56-Mirror__Network Discovery-NewNetworkDiscovery.cs.txt.meta create mode 100644 doc/Components/NetworkDiscovery.md create mode 100644 doc/Components/NetworkDiscovery.png create mode 100644 doc/Guides/NetworkDiscovery.md diff --git a/Assets/Mirror/Components/Discovery.meta b/Assets/Mirror/Components/Discovery.meta new file mode 100644 index 000000000..d5bb0cbb2 --- /dev/null +++ b/Assets/Mirror/Components/Discovery.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b5dcf9618f5e14a4eb60bff5480284a6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Components/Discovery/NetworkDiscovery.cs b/Assets/Mirror/Components/Discovery/NetworkDiscovery.cs new file mode 100644 index 000000000..bd5714b79 --- /dev/null +++ b/Assets/Mirror/Components/Discovery/NetworkDiscovery.cs @@ -0,0 +1,112 @@ +using UnityEngine; +using System.Net; +using System; +using UnityEngine.Events; + +namespace Mirror.Discovery +{ + [Serializable] + public class ServerFoundUnityEvent : UnityEvent { }; + + [DisallowMultipleComponent] + [AddComponentMenu("Network/NetworkDiscovery")] + public class NetworkDiscovery : NetworkDiscoveryBase + { + #region Server + + public long ServerId { get; private set; } + + [Tooltip("Transport to be advertised during discovery")] + public Transport transport; + + [Tooltip("Invoked when a server is found")] + public ServerFoundUnityEvent OnServerFound; + + public void Start() + { + ServerId = RandomLong(); + + // active transport gets initialized in awake + // so make sure we set it here in Start() (after awakes) + // Or just let the user assign it in the inspector + if (transport == null) + transport = Transport.activeTransport; + } + + /// + /// Process the request from a client + /// + /// + /// Override if you wish to provide more information to the clients + /// such as the name of the host player + /// + /// Request comming from client + /// Address of the client that sent the request + /// The message to be sent back to the client or null + protected override ServerResponse ProcessRequest(ServerRequest request, IPEndPoint endpoint) + { + // In this case we don't do anything with the request + // but other discovery implementations might want to use the data + // in there, This way the client can ask for + // specific game mode or something + + try + { + // this is an example reply message, return your own + // to include whatever is relevant for your game + return new ServerResponse + { + serverId = ServerId, + uri = transport.ServerUri() + }; + } + catch (NotImplementedException) + { + Debug.LogError($"Transport {transport} does not support network discovery"); + throw; + } + } + + #endregion + + #region Client + + /// + /// Create a message that will be broadcasted on the network to discover servers + /// + /// + /// Override if you wish to include additional data in the discovery message + /// such as desired game mode, language, difficulty, etc... + /// An instance of ServerRequest with data to be broadcasted + protected override ServerRequest GetRequest() => new ServerRequest(); + + /// + /// Process the answer from a server + /// + /// + /// A client receives a reply from a server, this method processes the + /// reply and raises an event + /// + /// Response that came from the server + /// Address of the server that replied + protected override void ProcessResponse(ServerResponse response, IPEndPoint endpoint) + { + // we received a message from the remote endpoint + response.EndPoint = endpoint; + + // although we got a supposedly valid url, we may not be able to resolve + // the provided host + // However we know the real ip address of the server because we just + // received a packet from it, so use that as host. + UriBuilder realUri = new UriBuilder(response.uri) + { + Host = response.EndPoint.Address.ToString() + }; + response.uri = realUri.Uri; + + OnServerFound.Invoke(response); + } + + #endregion + } +} diff --git a/Assets/Mirror/Components/Discovery/NetworkDiscovery.cs.meta b/Assets/Mirror/Components/Discovery/NetworkDiscovery.cs.meta new file mode 100644 index 000000000..523cc52ec --- /dev/null +++ b/Assets/Mirror/Components/Discovery/NetworkDiscovery.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c761308e733c51245b2e8bb4201f46dc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Components/Discovery/NetworkDiscoveryBase.cs b/Assets/Mirror/Components/Discovery/NetworkDiscoveryBase.cs new file mode 100644 index 000000000..419627476 --- /dev/null +++ b/Assets/Mirror/Components/Discovery/NetworkDiscoveryBase.cs @@ -0,0 +1,357 @@ +using UnityEngine; +using System.Net; +using System.Net.Sockets; +using System; +using System.Threading.Tasks; + +// Based on https://github.com/EnlightenedOne/MirrorNetworkDiscovery +// forked from https://github.com/in0finite/MirrorNetworkDiscovery +// Both are MIT Licensed + +namespace Mirror.Discovery +{ + /// + /// Base implementation for Network Discovery. Extend this component + /// to provide custom discovery with game specific data + /// for a sample implementation + /// + [DisallowMultipleComponent] + [HelpURL("https://mirror-networking.com/docs/Components/NetworkDiscovery.html")] + public abstract class NetworkDiscoveryBase : MonoBehaviour + where Request : IMessageBase, new() + where Response : IMessageBase, new() + { + public static bool SupportedOnThisPlatform { get { return Application.platform != RuntimePlatform.WebGLPlayer; } } + + // each game should have a random unique handshake, this way you can tell if this is the same game or not + [HideInInspector] + public long secretHandshake; + + [SerializeField] + [Tooltip("The UDP port the server will listen for multi-cast messages")] + int serverBroadcastListenPort = 47777; + + [SerializeField] + [Tooltip("Time in seconds between multi-cast messages")] + [Range(1, 60)] + float ActiveDiscoveryInterval = 3; + + protected UdpClient serverUdpClient = null; + protected UdpClient clientUdpClient = null; + +#if UNITY_EDITOR + void OnValidate() + { + if (secretHandshake == 0) + { + secretHandshake = RandomLong(); + UnityEditor.Undo.RecordObject(this, "Set secret handshake"); + } + } +#endif + + public static long RandomLong() + { + int value1 = UnityEngine.Random.Range(int.MinValue, int.MaxValue); + int value2 = UnityEngine.Random.Range(int.MinValue, int.MaxValue); + return value1 + ((long)value2 << 32); + } + + // Ensure the ports are cleared no matter when Game/Unity UI exits + void OnApplicationQuit() + { + Shutdown(); + } + + void Shutdown() + { + if (serverUdpClient != null) + { + try + { + serverUdpClient.Close(); + } + catch (Exception) + { + // it is just close, swallow the error + } + + serverUdpClient = null; + } + + if (clientUdpClient != null) + { + try + { + clientUdpClient.Close(); + } + catch (Exception) + { + // it is just close, swallow the error + } + + clientUdpClient = null; + } + + CancelInvoke(); + } + + #region Server + + /// + /// Advertise this server in the local network + /// + /// Network Manager + public void AdvertiseServer() + { + if (!SupportedOnThisPlatform) + throw new PlatformNotSupportedException("Network discovery not supported in this platform"); + + StopDiscovery(); + + // Setup port -- may throw exception + serverUdpClient = new UdpClient(serverBroadcastListenPort) + { + EnableBroadcast = true, + MulticastLoopback = false + }; + + // listen for client pings + _ = ServerListenAsync(); + } + + public async Task ServerListenAsync() + { + while (true) + { + try + { + await ReceiveRequestAsync(serverUdpClient); + } + catch (ObjectDisposedException) + { + // socket has been closed + break; + } + catch (Exception) + { + continue; + } + } + } + + async Task ReceiveRequestAsync(UdpClient udpClient) + { + // only proceed if there is available data in network buffer, or otherwise Receive() will block + // average time for UdpClient.Available : 10 us + + UdpReceiveResult udpReceiveResult = await udpClient.ReceiveAsync(); + + NetworkReader reader = new NetworkReader(udpReceiveResult.Buffer); + + long handshake = reader.ReadInt64(); + if (handshake != secretHandshake) + { + // message is not for us + throw new ProtocolViolationException("Invalid handshake"); + } + + Request request = new Request(); + request.Deserialize(reader); + + ProcessClientRequest(request, udpReceiveResult.RemoteEndPoint); + } + + /// + /// Reply to the client to inform it of this server + /// + /// + /// Override if you wish to ignore server requests based on + /// custom criteria such as language, full server game mode or difficulty + /// + /// Request comming from client + /// Address of the client that sent the request + protected virtual void ProcessClientRequest(Request request, IPEndPoint endpoint) + { + Response info = ProcessRequest(request, endpoint); + + if (info == null) + return; + + NetworkWriter writer = NetworkWriterPool.GetWriter(); + + try + { + writer.WriteInt64(secretHandshake); + + info.Serialize(writer); + + ArraySegment data = writer.ToArraySegment(); + // signature matches + // send response + serverUdpClient.Send(data.Array, data.Count, endpoint); + } + catch (Exception ex) + { + Debug.LogException(ex, this); + } + finally + { + NetworkWriterPool.Recycle(writer); + } + } + + /// + /// Process the request from a client + /// + /// + /// Override if you wish to provide more information to the clients + /// such as the name of the host player + /// + /// Request comming from client + /// Address of the client that sent the request + /// The message to be sent back to the client or null + protected abstract Response ProcessRequest(Request request, IPEndPoint endpoint); + + #endregion + + #region Client + + /// + /// Start Active Discovery + /// + public void StartDiscovery() + { + if (!SupportedOnThisPlatform) + throw new PlatformNotSupportedException("Network discovery not supported in this platform"); + + StopDiscovery(); + + try + { + // Setup port + clientUdpClient = new UdpClient(0) + { + EnableBroadcast = true, + MulticastLoopback = false + }; + } + catch (Exception) + { + // Free the port if we took it + Shutdown(); + throw; + } + + _ = ClientListenAsync(); + + InvokeRepeating(nameof(BroadcastDiscoveryRequest), 0, ActiveDiscoveryInterval); + } + + /// + /// Start Active Discovery + /// + public void StopDiscovery() + { + Shutdown(); + } + + /// + /// Awaits for server response + /// + /// ClientListenAsync Task + public async Task ClientListenAsync() + { + while (true) + { + try + { + await ReceiveGameBroadcastAsync(clientUdpClient); + } + catch (ObjectDisposedException) + { + // socket was closed, no problem + return; + } + catch (Exception ex) + { + Debug.LogException(ex); + } + } + } + + /// + /// Sends discovery request from client + /// + public void BroadcastDiscoveryRequest() + { + if (clientUdpClient == null) + return; + + IPEndPoint endPoint = new IPEndPoint(IPAddress.Broadcast, serverBroadcastListenPort); + + NetworkWriter writer = NetworkWriterPool.GetWriter(); + + writer.WriteInt64(secretHandshake); + + try + { + Request request = GetRequest(); + + request.Serialize(writer); + + ArraySegment data = writer.ToArraySegment(); + + clientUdpClient.SendAsync(data.Array, data.Count, endPoint); + } + catch (Exception) + { + // It is ok if we can't broadcast to one of the addresses + } + finally + { + NetworkWriterPool.Recycle(writer); + } + } + + /// + /// Create a message that will be broadcasted on the network to discover servers + /// + /// + /// Override if you wish to include additional data in the discovery message + /// such as desired game mode, language, difficulty, etc... + /// An instance of ServerRequest with data to be broadcasted + protected virtual Request GetRequest() => new Request(); + + async Task ReceiveGameBroadcastAsync(UdpClient udpClient) + { + // only proceed if there is available data in network buffer, or otherwise Receive() will block + // average time for UdpClient.Available : 10 us + + UdpReceiveResult udpReceiveResult = await udpClient.ReceiveAsync(); + + NetworkReader reader = new NetworkReader(udpReceiveResult.Buffer); + + if (reader.ReadInt64() != secretHandshake) + return; + + Response response = new Response(); + response.Deserialize(reader); + + ProcessResponse(response, udpReceiveResult.RemoteEndPoint); + } + + /// + /// Process the answer from a server + /// + /// + /// A client receives a reply from a server, this method processes the + /// reply and raises an event + /// + /// Response that came from the server + /// Address of the server that replied + protected abstract void ProcessResponse(Response response, IPEndPoint endpoint); + + #endregion + } +} diff --git a/Assets/Mirror/Components/Discovery/NetworkDiscoveryBase.cs.meta b/Assets/Mirror/Components/Discovery/NetworkDiscoveryBase.cs.meta new file mode 100644 index 000000000..7dfbaf62a --- /dev/null +++ b/Assets/Mirror/Components/Discovery/NetworkDiscoveryBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b9971d60ce61f4e39b07cd9e7e0c68fa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Components/Discovery/NetworkDiscoveryHUD.cs b/Assets/Mirror/Components/Discovery/NetworkDiscoveryHUD.cs new file mode 100644 index 000000000..cfb53d686 --- /dev/null +++ b/Assets/Mirror/Components/Discovery/NetworkDiscoveryHUD.cs @@ -0,0 +1,95 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace Mirror.Discovery +{ + [DisallowMultipleComponent] + [AddComponentMenu("Network/NetworkDiscoveryHUD")] + [HelpURL("https://mirror-networking.com/docs/Components/NetworkDiscovery.html")] + [RequireComponent(typeof(NetworkDiscovery))] + public class NetworkDiscoveryHUD : MonoBehaviour + { + readonly Dictionary discoveredServers = new Dictionary(); + Vector2 scrollViewPos = Vector2.zero; + + public NetworkDiscovery networkDiscovery; + +#if UNITY_EDITOR + void OnValidate() + { + if (networkDiscovery == null) + { + networkDiscovery = GetComponent(); + UnityEditor.Events.UnityEventTools.AddPersistentListener(networkDiscovery.OnServerFound, OnDiscoveredServer); + UnityEditor.Undo.RecordObjects(new Object[] { this, networkDiscovery }, "Set NetworkDiscovery"); + } + } +#endif + + void OnGUI() + { + if (NetworkManager.singleton == null) + return; + + if (NetworkServer.active || NetworkClient.active) + return; + + if (!NetworkClient.isConnected && !NetworkServer.active && !NetworkClient.active) + DrawGUI(); + } + + void DrawGUI() + { + GUILayout.BeginHorizontal(); + + if (GUILayout.Button("Find Servers")) + { + discoveredServers.Clear(); + networkDiscovery.StartDiscovery(); + } + + // LAN Host + if (GUILayout.Button("Start Host")) + { + discoveredServers.Clear(); + NetworkManager.singleton.StartHost(); + networkDiscovery.AdvertiseServer(); + } + + // Dedicated server + if (GUILayout.Button("Start Server")) + { + discoveredServers.Clear(); + NetworkManager.singleton.StartServer(); + + networkDiscovery.AdvertiseServer(); + } + + GUILayout.EndHorizontal(); + + // show list of found server + + GUILayout.Label($"Discovered Servers [{discoveredServers.Count}]:"); + + // servers + scrollViewPos = GUILayout.BeginScrollView(scrollViewPos); + + foreach (ServerResponse info in discoveredServers.Values) + if (GUILayout.Button(info.EndPoint.Address.ToString())) + Connect(info); + + GUILayout.EndScrollView(); + } + + void Connect(ServerResponse info) + { + NetworkManager.singleton.StartClient(info.uri); + } + + public void OnDiscoveredServer(ServerResponse info) + { + // Note that you can check the versioning to decide if you can connect to the server or not using this method + discoveredServers[info.serverId] = info; + } + } +} diff --git a/Assets/Mirror/Components/Discovery/NetworkDiscoveryHUD.cs.meta b/Assets/Mirror/Components/Discovery/NetworkDiscoveryHUD.cs.meta new file mode 100644 index 000000000..5e27a2282 --- /dev/null +++ b/Assets/Mirror/Components/Discovery/NetworkDiscoveryHUD.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 88c37d3deca7a834d80cfd8d3cfcc510 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Components/Discovery/ServerRequest.cs b/Assets/Mirror/Components/Discovery/ServerRequest.cs new file mode 100644 index 000000000..3ac59cfa4 --- /dev/null +++ b/Assets/Mirror/Components/Discovery/ServerRequest.cs @@ -0,0 +1,4 @@ +namespace Mirror.Discovery +{ + public class ServerRequest : MessageBase { } +} diff --git a/Assets/Mirror/Components/Discovery/ServerRequest.cs.meta b/Assets/Mirror/Components/Discovery/ServerRequest.cs.meta new file mode 100644 index 000000000..84f3232a1 --- /dev/null +++ b/Assets/Mirror/Components/Discovery/ServerRequest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ea7254bf7b9454da4adad881d94cd141 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Components/Discovery/ServerResponse.cs b/Assets/Mirror/Components/Discovery/ServerResponse.cs new file mode 100644 index 000000000..34173e532 --- /dev/null +++ b/Assets/Mirror/Components/Discovery/ServerResponse.cs @@ -0,0 +1,18 @@ +using System; +using System.Net; + +namespace Mirror.Discovery +{ + public class ServerResponse : MessageBase + { + // The server that sent this + // this is a property so that it is not serialized, but the + // client fills this up after we receive it + public IPEndPoint EndPoint { get; set; } + + public Uri uri; + + // Prevent duplicate server appearance when a connection can be made via LAN on multiple NICs + public long serverId; + } +} \ No newline at end of file diff --git a/Assets/Mirror/Components/Discovery/ServerResponse.cs.meta b/Assets/Mirror/Components/Discovery/ServerResponse.cs.meta new file mode 100644 index 000000000..44f23bac4 --- /dev/null +++ b/Assets/Mirror/Components/Discovery/ServerResponse.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 36f97227fdf2d7a4e902db5bfc43039c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Examples/Discovery.meta b/Assets/Mirror/Examples/Discovery.meta new file mode 100644 index 000000000..85a73ff3a --- /dev/null +++ b/Assets/Mirror/Examples/Discovery.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 450d6133608b04c57a6ebd6830d455fd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Examples/Discovery/Prefabs.meta b/Assets/Mirror/Examples/Discovery/Prefabs.meta new file mode 100644 index 000000000..ef0083dc9 --- /dev/null +++ b/Assets/Mirror/Examples/Discovery/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8d8abc53a4efb4544ad9cb7a44b4840a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Examples/Discovery/Prefabs/Player.prefab b/Assets/Mirror/Examples/Discovery/Prefabs/Player.prefab new file mode 100644 index 000000000..2d917ed8e --- /dev/null +++ b/Assets/Mirror/Examples/Discovery/Prefabs/Player.prefab @@ -0,0 +1,110 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &9081919128954505657 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8463701767414927392} + - component: {fileID: 435337138507318507} + - component: {fileID: 8589595951595565844} + - component: {fileID: 8188542106662419882} + - component: {fileID: 1410032569926419539} + m_Layer: 0 + m_Name: Player + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8463701767414927392 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9081919128954505657} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &435337138507318507 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9081919128954505657} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &8589595951595565844 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9081919128954505657} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!136 &8188542106662419882 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9081919128954505657} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + m_Radius: 0.5 + m_Height: 2 + m_Direction: 1 + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &1410032569926419539 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9081919128954505657} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3} + m_Name: + m_EditorClassIdentifier: + serverOnly: 0 + m_AssetId: + m_SceneId: 0 diff --git a/Assets/Mirror/Examples/Discovery/Prefabs/Player.prefab.meta b/Assets/Mirror/Examples/Discovery/Prefabs/Player.prefab.meta new file mode 100644 index 000000000..1c818fadc --- /dev/null +++ b/Assets/Mirror/Examples/Discovery/Prefabs/Player.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ecd52c53a6ef7496693343d3e32dace1 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Examples/Discovery/Scenes.meta b/Assets/Mirror/Examples/Discovery/Scenes.meta new file mode 100644 index 000000000..ed0ba6454 --- /dev/null +++ b/Assets/Mirror/Examples/Discovery/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ceaf2344f4e6944258442667a9fbbfdf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Examples/Discovery/Scenes/Scene.unity b/Assets/Mirror/Examples/Discovery/Scenes/Scene.unity new file mode 100644 index 000000000..396aa9ee4 --- /dev/null +++ b/Assets/Mirror/Examples/Discovery/Scenes/Scene.unity @@ -0,0 +1,785 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 0 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 500 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 2 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 0 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &62199026 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 62199028} + - component: {fileID: 62199027} + m_Layer: 0 + m_Name: StartPos + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &62199027 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 62199026} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 41f84591ce72545258ea98cb7518d8b9, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!4 &62199028 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 62199026} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &441913360 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 441913362} + - component: {fileID: 441913361} + m_Layer: 0 + m_Name: StartPos + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &441913361 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 441913360} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 41f84591ce72545258ea98cb7518d8b9, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!4 &441913362 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 441913360} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -3.78, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &919124423 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 919124425} + - component: {fileID: 919124424} + m_Layer: 0 + m_Name: StartPos + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &919124424 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919124423} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 41f84591ce72545258ea98cb7518d8b9, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!4 &919124425 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919124423} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -1.09, y: 0, z: -4.03} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &970214386 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 970214388} + - component: {fileID: 970214387} + m_Layer: 0 + m_Name: StartPos + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &970214387 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 970214386} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 41f84591ce72545258ea98cb7518d8b9, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!4 &970214388 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 970214386} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 1.99, y: 0, z: -4.03} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1392889995 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1392889998} + - component: {fileID: 1392889997} + - component: {fileID: 1392889996} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &1392889996 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1392889995} + m_Enabled: 1 +--- !u!20 &1392889997 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1392889995} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0, g: 0, b: 0, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &1392889998 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1392889995} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1556883243 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1556883247} + - component: {fileID: 1556883245} + - component: {fileID: 1556883244} + - component: {fileID: 1556883248} + - component: {fileID: 1556883246} + m_Layer: 0 + m_Name: NetworkManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1556883244 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1556883243} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c7424c1070fad4ba2a7a96b02fbeb4bb, type: 3} + m_Name: + m_EditorClassIdentifier: + OnClientConnected: + m_PersistentCalls: + m_Calls: [] + OnClientDataReceived: + m_PersistentCalls: + m_Calls: [] + OnClientError: + m_PersistentCalls: + m_Calls: [] + OnClientDisconnected: + m_PersistentCalls: + m_Calls: [] + OnServerConnected: + m_PersistentCalls: + m_Calls: [] + OnServerDataReceived: + m_PersistentCalls: + m_Calls: [] + OnServerError: + m_PersistentCalls: + m_Calls: [] + OnServerDisconnected: + m_PersistentCalls: + m_Calls: [] + port: 7777 + NoDelay: 1 + serverMaxMessageSize: 16384 + clientMaxMessageSize: 16384 +--- !u!114 &1556883245 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1556883243} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8aab4c8111b7c411b9b92cf3dbc5bd4e, type: 3} + m_Name: + m_EditorClassIdentifier: + dontDestroyOnLoad: 1 + runInBackground: 1 + startOnHeadless: 1 + serverTickRate: 30 + showDebugMessages: 0 + offlineScene: + onlineScene: + transport: {fileID: 1556883244} + networkAddress: localhost + maxConnections: 4 + authenticator: {fileID: 0} + playerPrefab: {fileID: 9081919128954505657, guid: ecd52c53a6ef7496693343d3e32dace1, + type: 3} + autoCreatePlayer: 1 + playerSpawnMethod: 0 + spawnPrefabs: [] +--- !u!114 &1556883246 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1556883243} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 88c37d3deca7a834d80cfd8d3cfcc510, type: 3} + m_Name: + m_EditorClassIdentifier: + networkDiscovery: {fileID: 1556883248} +--- !u!4 &1556883247 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1556883243} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1556883248 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1556883243} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c761308e733c51245b2e8bb4201f46dc, type: 3} + m_Name: + m_EditorClassIdentifier: + secretHandshake: 1558261479176021378 + serverBroadcastListenPort: 47777 + ActiveDiscoveryInterval: 3 + transport: {fileID: 0} + OnServerFound: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1556883246} + m_MethodName: OnDiscoveredServer + m_Mode: 0 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!1 &1611696151 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1611696153} + - component: {fileID: 1611696152} + m_Layer: 0 + m_Name: StartPos + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1611696152 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1611696151} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 41f84591ce72545258ea98cb7518d8b9, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!4 &1611696153 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1611696151} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 4.6, y: 0, z: -1.43} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 9 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1730851146 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1730851148} + - component: {fileID: 1730851147} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &1730851147 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1730851146} + m_Enabled: 1 + serializedVersion: 9 + m_Type: 1 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.802082 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &1730851148 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1730851146} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &1911023976 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1911023978} + - component: {fileID: 1911023977} + m_Layer: 0 + m_Name: StartPos + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1911023977 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1911023976} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 41f84591ce72545258ea98cb7518d8b9, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!4 &1911023978 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1911023976} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -3.78, y: 0, z: -4.03} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1958729888 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1958729890} + - component: {fileID: 1958729889} + m_Layer: 0 + m_Name: StartPos + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1958729889 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1958729888} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 41f84591ce72545258ea98cb7518d8b9, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!4 &1958729890 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1958729888} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 4.6, y: 0, z: -4.08} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 10 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2054361114 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2054361116} + - component: {fileID: 2054361115} + m_Layer: 0 + m_Name: StartPos + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &2054361115 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2054361114} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 41f84591ce72545258ea98cb7518d8b9, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!4 &2054361116 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2054361114} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 1.99, y: 0, z: -1.43} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 8 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/Mirror/Examples/Discovery/Scenes/Scene.unity.meta b/Assets/Mirror/Examples/Discovery/Scenes/Scene.unity.meta new file mode 100644 index 000000000..b02433fdb --- /dev/null +++ b/Assets/Mirror/Examples/Discovery/Scenes/Scene.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 90fddc74fa21c423599167eb28b09dd1 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Runtime/Transport/FallbackTransport.cs b/Assets/Mirror/Runtime/Transport/FallbackTransport.cs index f9f4c77b9..a5194876d 100644 --- a/Assets/Mirror/Runtime/Transport/FallbackTransport.cs +++ b/Assets/Mirror/Runtime/Transport/FallbackTransport.cs @@ -175,5 +175,6 @@ public override string ToString() { return available.ToString(); } + } } diff --git a/Assets/ScriptTemplates/56-Mirror__Network Discovery-NewNetworkDiscovery.cs.txt b/Assets/ScriptTemplates/56-Mirror__Network Discovery-NewNetworkDiscovery.cs.txt new file mode 100644 index 000000000..01b8b1f31 --- /dev/null +++ b/Assets/ScriptTemplates/56-Mirror__Network Discovery-NewNetworkDiscovery.cs.txt @@ -0,0 +1,84 @@ +using System.Net; +using Mirror; +using Mirror.Discovery; + +/* + Discovery Guide: https://mirror-networking.com/docs/Guides/NetworkDiscovery.html + Documentation: https://mirror-networking.com/docs/Components/NetworkDiscovery.html + API Reference: https://mirror-networking.com/docs/api/Mirror.Discovery.NetworkDiscovery.html +*/ + +public class DiscoveryRequest : MessageBase +{ + // Add properties for whatever information you want sent by clients + // in their broadcast messages that servers will consume. +} + +public class DiscoveryResponse : MessageBase +{ + // Add properties for whatever information you want the server to return to + // clients for them to display or consume for establishing a connection. +} + +public class #SCRIPTNAME# : NetworkDiscoveryBase +{ + #region Server + + /// + /// Reply to the client to inform it of this server + /// + /// + /// Override if you wish to ignore server requests based on + /// custom criteria such as language, full server game mode or difficulty + /// + /// Request comming from client + /// Address of the client that sent the request + protected override void ProcessClientRequest(DiscoveryRequest request, IPEndPoint endpoint) + { + base.ProcessClientRequest(request, endpoint); + } + + /// + /// Process the request from a client + /// + /// + /// Override if you wish to provide more information to the clients + /// such as the name of the host player + /// + /// Request comming from client + /// Address of the client that sent the request + /// A message containing information about this server + protected override DiscoveryResponse ProcessRequest(DiscoveryRequest request, IPEndPoint endpoint) + { + return new DiscoveryResponse(); + } + + #endregion + + #region Client + + /// + /// Create a message that will be broadcasted on the network to discover servers + /// + /// + /// Override if you wish to include additional data in the discovery message + /// such as desired game mode, language, difficulty, etc... + /// An instance of ServerRequest with data to be broadcasted + protected override DiscoveryRequest GetRequest() + { + return new DiscoveryRequest(); + } + + /// + /// Process the answer from a server + /// + /// + /// A client receives a reply from a server, this method processes the + /// reply and raises an event + /// + /// Response that came from the server + /// Address of the server that replied + protected override void ProcessResponse(DiscoveryResponse response, IPEndPoint endpoint) { } + + #endregion +} diff --git a/Assets/ScriptTemplates/56-Mirror__Network Discovery-NewNetworkDiscovery.cs.txt.meta b/Assets/ScriptTemplates/56-Mirror__Network Discovery-NewNetworkDiscovery.cs.txt.meta new file mode 100644 index 000000000..a034ec865 --- /dev/null +++ b/Assets/ScriptTemplates/56-Mirror__Network Discovery-NewNetworkDiscovery.cs.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 04337367db30af3459bf9e9f3f880734 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/doc/Components/NetworkDiscovery.md b/doc/Components/NetworkDiscovery.md new file mode 100644 index 000000000..764ef9a35 --- /dev/null +++ b/doc/Components/NetworkDiscovery.md @@ -0,0 +1,15 @@ +# NetworkDiscovery + +Network Discovery uses a UDP broadcast on the LAN enabling clients to find the running server and connect to it. + +![Inspector](NetworkDiscovery.png) + +NetworkDiscovery and NetworkDiscoveryHUD components are included, or you can make your own from a [ScriptTemplate](../General/ScriptTemplates.md). + +When a server is started, it listens on the UDP Broadcast Listen Port for requests from clients and returns a connection URI that clients apply to their transport. + +You can adjust how often the clients send their requests out to find a server in seconds with the Active Discovery Interval. + +The Server Found event must be assigned to a handler method, e.g. the OnDiscoveredServer method of NetworkDiscoveryHUD. + +In the NetworkDiscoveryHUD, the NetworkDiscovery component should be assigned automatically. diff --git a/doc/Components/NetworkDiscovery.png b/doc/Components/NetworkDiscovery.png new file mode 100644 index 0000000000000000000000000000000000000000..5adbc166bfa4b8a13562288537dcba29f2afc44a GIT binary patch literal 46924 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 literal 0 HcmV?d00001 diff --git a/doc/Components/index.md b/doc/Components/index.md index e99eb8c67..f725d855c 100644 --- a/doc/Components/index.md +++ b/doc/Components/index.md @@ -10,6 +10,8 @@ These core components are included in Mirror: The Network Manager is a component for managing the networking aspects of a multiplayer game. - [NetworkManagerHUD](NetworkManagerHUD.md) The Network Manager HUD is a quick-start tool to help you start building your multiplayer game straight away, without first having to build a user interface for game creation/connection/joining. It allows you to jump straight into your gameplay programming, and means you can build your own version of these controls later in your development schedule. + [NetworkDiscovery](NetworkDiscovery.md) + Network Discovery uses a UDP broadcast on the LAN enabling clients to find the running server and connect to it. - [NetworkProximityChecker](NetworkProximityChecker.md) The Network Proximity Checker component controls the visibility of game objects for network clients, based on proximity to players. - [NetworkRoomManager](NetworkRoomManager.md) diff --git a/doc/Components/toc.yml b/doc/Components/toc.yml index 7a9f7c39a..19fc74588 100644 --- a/doc/Components/toc.yml +++ b/doc/Components/toc.yml @@ -8,6 +8,8 @@ href: NetworkManager.md - name: NetworkManagerHUD href: NetworkManagerHUD.md +- name: NetworkDiscovery + href: NetworkDiscovery.md - name: NetworkProximityChecker href: NetworkProximityChecker.md - name: NetworkSceneChecker diff --git a/doc/General/ChangeLog.md b/doc/General/ChangeLog.md index 0aba15311..7549c6798 100644 --- a/doc/General/ChangeLog.md +++ b/doc/General/ChangeLog.md @@ -4,7 +4,9 @@ - Added: NetworkAnimator now has a ResetTrigger function and server / client authority warnings - Added: NetworkTransform now has 3 new floats for Sensitivity to quiet down message traffic from micro changes. - Added: Network Observer added to [Script Templates](ScriptTemplates.md) -- See the new Mirror section in the Assets > Create menu. -- Added: [Network Scene Checker Component](../Components/NetworkSceneChecker.html) +- Added: [Network Discovery](../Components/NetworkDiscovery.md) has been reimplemented including an example and script template -- thanks to all those who contributed! +- Added: [Network Discovery](../Guides/NetworkDiscovery.md) Guide added to documentation +- Added: [Network Scene Checker Component](../Components/NetworkSceneChecker.md) - Added: Mirror Icon for all components - Added: URI added to supported data types - Fixed: NetworkTransform and NetworkAnimator now uses NetworkWriterPool diff --git a/doc/General/Deprecations.md b/doc/General/Deprecations.md index 0cde41ba9..0b7f78a0c 100644 --- a/doc/General/Deprecations.md +++ b/doc/General/Deprecations.md @@ -6,10 +6,6 @@ Certain features of Unity Networking were removed from Mirror for various reason As part of the Unity Services, this entire namespace was removed. It didn't work well to begin with, and was incredibly complex to be part of the core networking package. We expect this, along with other back-end services, will be provided through standalone apps that have integration to Mirror. -## Network Discovery - -NetworkDiscovery was a UNet component intended for UDP projects. Since Mirror was built on TCP, it was removed. Now that all [transports](../Transports/index.md) are separate components, Discovery has been re-implemented in at least one of them. - ## networkPort in Network Manager Network Manager's `networkPort` property was removed now that all transports are separate components. Not all transports use ports, but those that do have a field for it. See [Transports](../Transports/index.md) for more info. diff --git a/doc/General/ScriptTemplates.png b/doc/General/ScriptTemplates.png index 0d8062d9add0dcb6f4a11d05f3a51f843d63bf5f..0c85ad446f7994356164a1c5c2704e6f79d5be2d 100644 GIT binary patch literal 22177 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 KQI%HbfBOe>i3fB5 diff --git a/doc/General/ScriptTemplates.zip b/doc/General/ScriptTemplates.zip index 55e01354761423bd031252f8571ebed3e80307d1..c270bc9bd10f0b5669709ee061107f63acdf2ecc 100644 GIT binary patch literal 7336 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 delta 6503 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 Mirror > Network Discovery. + +This will create a script in your project with 2 empty message classes and a custom NetworkDiscovery class that inherits from NetworkDiscoveryBase and has all the override methods included and documented for you. + +The message classes define what is sent between the client and server. As long as you keep your messages simple using the [data types](DataTypes.md) that Mirror can serialize, you won't need to write custom serializers for them. + +```cs +public class DiscoveryRequest : MessageBase +{ + public string language="en"; + + // Add properties for whatever information you want sent by clients + // in their broadcast messages that servers will consume. +} + +public class DiscoveryResponse : MessageBase +{ + enum GameMode {PvP, PvE}; + + // you probably want uri so clients know how to connect to the server + public Uri uri; + + public GameMode GameMode; + public int TotalPlayers; + public int HostPlayerName; + + // Add properties for whatever information you want the server to return to + // clients for them to display or consume for establishing a connection. +} +``` + +The custom NetworkDiscovery class contains the overrides for handling the messages above. + +You may want to refer to the NetworkDiscovery.cs script in the Components/Discovery folder to see how these should be implemented. + +```cs +public class NewNetworkDiscovery: NetworkDiscoveryBase +{ + #region Server + + protected override void ProcessClientRequest(DiscoveryRequest request, IPEndPoint endpoint) + { + base.ProcessClientRequest(request, endpoint); + } + + protected override DiscoveryResponse ProcessRequest(DiscoveryRequest request, IPEndPoint endpoint) + { + // TODO: Create your response and return it + return new DiscoveryResponse(); + } + + #endregion + + #region Client + + protected override DiscoveryRequest GetRequest() + { + return new DiscoveryRequest(); + } + + protected override void ProcessResponse(DiscoveryResponse response, IPEndPoint endpoint) + { + // TODO: a server replied, do something with the response such as invoking a unityevent + } + + #endregion +} +``` diff --git a/doc/Guides/toc.yml b/doc/Guides/toc.yml index a3343a7a6..f8d99ab63 100644 --- a/doc/Guides/toc.yml +++ b/doc/Guides/toc.yml @@ -8,6 +8,8 @@ href: ClientsServers.md - name: Authentication href: Authentication.md +- name: Network Discovery + href: NetworkDiscovery.md - name: Conversion href: Conversion.md - name: Network Profiler