diff --git a/Assets/Mirror/Components/NetworkDiagnosticsDebugger.cs b/Assets/Mirror/Components/NetworkDiagnosticsDebugger.cs new file mode 100644 index 000000000..021b791ff --- /dev/null +++ b/Assets/Mirror/Components/NetworkDiagnosticsDebugger.cs @@ -0,0 +1,30 @@ +using UnityEngine; + +namespace Mirror +{ + public class NetworkDiagnosticsDebugger : MonoBehaviour + { + public bool logInMessages = true; + public bool logOutMessages = true; + void OnInMessage(NetworkDiagnostics.MessageInfo msgInfo) + { + if (logInMessages) + Debug.Log(msgInfo); + } + void OnOutMessage(NetworkDiagnostics.MessageInfo msgInfo) + { + if (logOutMessages) + Debug.Log(msgInfo); + } + void OnEnable() + { + NetworkDiagnostics.InMessageEvent += OnInMessage; + NetworkDiagnostics.OutMessageEvent += OnOutMessage; + } + void OnDisable() + { + NetworkDiagnostics.InMessageEvent -= OnInMessage; + NetworkDiagnostics.OutMessageEvent -= OnOutMessage; + } + } +} diff --git a/Assets/Mirror/Components/NetworkDiagnosticsDebugger.cs.meta b/Assets/Mirror/Components/NetworkDiagnosticsDebugger.cs.meta new file mode 100644 index 000000000..5a9819e73 --- /dev/null +++ b/Assets/Mirror/Components/NetworkDiagnosticsDebugger.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bc9f0a0fe4124424b8f9d4927795ee01 +timeCreated: 1700945893 \ No newline at end of file diff --git a/Assets/Mirror/Core/NetworkClient.cs b/Assets/Mirror/Core/NetworkClient.cs index e64698d05..86549286b 100644 --- a/Assets/Mirror/Core/NetworkClient.cs +++ b/Assets/Mirror/Core/NetworkClient.cs @@ -1703,7 +1703,7 @@ public static void OnGUI() // only if in world if (!ready) return; - GUILayout.BeginArea(new Rect(10, 5, 1000, 50)); + GUILayout.BeginArea(new Rect(10, 5, 1020, 50)); GUILayout.BeginHorizontal("Box"); GUILayout.Label("Snapshot Interp.:"); diff --git a/Assets/Mirror/Core/NetworkManager.cs b/Assets/Mirror/Core/NetworkManager.cs index c075e485a..192e74a63 100644 --- a/Assets/Mirror/Core/NetworkManager.cs +++ b/Assets/Mirror/Core/NetworkManager.cs @@ -9,6 +9,7 @@ namespace Mirror { public enum PlayerSpawnMethod { Random, RoundRobin } public enum NetworkManagerMode { Offline, ServerOnly, ClientOnly, Host } + public enum HeadlessStartOptions { DoNothing, AutoStartServer, AutoStartClient } [DisallowMultipleComponent] [AddComponentMenu("Network/Network Manager")] @@ -29,18 +30,31 @@ public class NetworkManager : MonoBehaviour /// Should the server auto-start when 'Server Build' is checked in build settings [Header("Headless Builds")] - [Tooltip("Should the server auto-start when 'Dedicated Server' platform is selected, or 'Server Build' is checked in build settings.")] - [FormerlySerializedAs("startOnHeadless")] - public bool autoStartServerBuild = true; - [Tooltip("Automatically connect the client in headless builds. Useful for CCU tests with bot clients.\n\nAddress may be passed as command line argument.\n\nMake sure that only 'autostartServer' or 'autoconnectClient' is enabled, not both!")] - public bool autoConnectClientBuild; + [Tooltip("Choose whether Server or Client should auto-start in headless builds")] + public HeadlessStartOptions headlessStartMode = HeadlessStartOptions.DoNothing; /// Server Update frequency, per second. Use around 60Hz for fast paced games like Counter-Strike to minimize latency. Use around 30Hz for games like WoW to minimize computations. Use around 1-10Hz for slow paced games like EVE. [Tooltip("Server & Client send rate per second. Use 60-100Hz for fast paced games like Counter-Strike to minimize latency. Use around 30Hz for games like WoW to minimize computations. Use around 1-10Hz for slow paced games like EVE.")] [FormerlySerializedAs("serverTickRate")] public int sendRate = 60; + // Deprecated 2023-11-25 + // Using SerializeField and HideInInspector to self-correct for being + // replaced by headlessStartMode. This can be removed in the future. + // See OnValidate() for how we handle this. + [Obsolete("Deprecated - Use headlessStartMode instead.")] + [FormerlySerializedAs("autoStartServerBuild"), SerializeField, HideInInspector] + public bool autoStartServerBuild = true; + + // Deprecated 2023-11-25 + // Using SerializeField and HideInInspector to self-correct for being + // replaced by headlessStartMode. This can be removed in the future. + // See OnValidate() for how we handle this. + [Obsolete("Deprecated - Use headlessStartMode instead.")] + [FormerlySerializedAs("autoConnectClientBuild"), SerializeField, HideInInspector] + public bool autoConnectClientBuild; + // client send rate follows server send rate to avoid errors for now /// Client Update frequency, per second. Use around 60Hz for fast paced games like Counter-Strike to minimize latency. Use around 30Hz for games like WoW to minimize computations. Use around 1-10Hz for slow paced games like EVE. // [Tooltip("Client broadcasts 'sendRate' times per second. Use around 60Hz for fast paced games like Counter-Strike to minimize latency. Use around 30Hz for games like WoW to minimize computations. Use around 1-10Hz for slow paced games like EVE.")] @@ -157,6 +171,24 @@ public class NetworkManager : MonoBehaviour // virtual so that inheriting classes' OnValidate() can call base.OnValidate() too public virtual void OnValidate() { +#pragma warning disable 618 + // autoStartServerBuild and autoConnectClientBuild are now obsolete, but to avoid + // a breaking change we'll set headlessStartMode to what the user had set before. + // + // headlessStartMode defaults to DoNothing, so if the user had neither of these + // set, then it will remain as DoNothing, and if they set headlessStartMode to + // any selection in the inspector it won't get changed back. + if (autoStartServerBuild) + headlessStartMode = HeadlessStartOptions.AutoStartServer; + else if (autoConnectClientBuild) + headlessStartMode = HeadlessStartOptions.AutoStartClient; + + // Setting both to false here prevents this code from fighting with user + // selection in the inspector, and they're both SerialisedField's. + autoStartServerBuild = false; + autoConnectClientBuild = false; +#pragma warning restore 618 + // always >= 0 maxConnections = Mathf.Max(maxConnections, 0); @@ -215,25 +247,24 @@ public virtual void Awake() // virtual so that inheriting classes' Start() can call base.Start() too public virtual void Start() { - // headless mode? then start the server - // can't do this in Awake because Awake is for initialization. - // some transports might not be ready until Start. + // Auto-start headless server or client. // - // (tick rate is applied in StartServer!) + // We can't do this in Awake because Awake is for initialization + // and some transports might not be ready until Start. // // don't auto start in editor where we have a UI, only in builds. // otherwise if we switch to 'Dedicated Server' target and press // Play, it would auto start the server every time. - if (Utils.IsHeadless() && !Application.isEditor) + if (Utils.IsHeadless()) { - if (autoStartServerBuild) + switch (headlessStartMode) { - StartServer(); - } - // only start server or client, never both - else if (autoConnectClientBuild) - { - StartClient(); + case HeadlessStartOptions.AutoStartServer: + StartServer(); + break; + case HeadlessStartOptions.AutoStartClient: + StartClient(); + break; } } } @@ -398,6 +429,8 @@ void SetupClient() /// Starts the client, connects it to the server with networkAddress. public void StartClient() { + // Do checks and short circuits before setting anything up. + // If / when we retry, we won't have conflict issues. if (NetworkClient.active) { Debug.LogWarning("Client already started."); diff --git a/Assets/Mirror/Hosting/Edgegap/Editor/EdgegapWindow.cs b/Assets/Mirror/Hosting/Edgegap/Editor/EdgegapWindow.cs index a8d5ab28e..6ee0e45fc 100755 --- a/Assets/Mirror/Hosting/Edgegap/Editor/EdgegapWindow.cs +++ b/Assets/Mirror/Hosting/Edgegap/Editor/EdgegapWindow.cs @@ -378,6 +378,30 @@ async void BuildAndPushServer() string imageName = _containerImageRepo; string tag = _containerImageTag; + // MIRROR CHANGE /////////////////////////////////////////////// + // registry, repository and tag can not contain whitespaces. + // otherwise the docker command will throw an error: + // "ERROR: "docker buildx build" requires exactly 1 argument." + // catch this early and notify the user immediately. + if (registry.Contains(" ")) + { + onError($"Container Registry is not allowed to contain whitespace: '{registry}'"); + return; + } + + if (imageName.Contains(" ")) + { + onError($"Image Repository is not allowed to contain whitespace: '{imageName}'"); + return; + } + + if (tag.Contains(" ")) + { + onError($"Tag is not allowed to contain whitespace: '{tag}'"); + return; + } + // END MIRROR CHANGE /////////////////////////////////////////// + // increment tag for quicker iteration if (_autoIncrementTag) { @@ -615,9 +639,16 @@ void SyncObjectWithForm() _appName = _appNameInput.value; _appVersionName = _appVersionNameInput.value; - _containerRegistry = _containerRegistryInput.value; - _containerImageTag = _containerImageTagInput.value; - _containerImageRepo = _containerImageRepoInput.value; + // MIRROR CHANGE /////////////////////////////////////////////////// + // registry, repository and tag can not contain whitespaces. + // otherwise it'll throw an error: + // "ERROR: "docker buildx build" requires exactly 1 argument." + // trim whitespace in case users accidentally added some. + _containerRegistry = _containerRegistryInput.value.Trim(); + _containerImageTag = _containerImageTagInput.value.Trim(); + _containerImageRepo = _containerImageRepoInput.value.Trim(); + // END MIRROR CHANGE /////////////////////////////////////////////// + _autoIncrementTag = _autoIncrementTagInput.value; } diff --git a/Assets/Mirror/Transports/KCP/KcpTransport.cs b/Assets/Mirror/Transports/KCP/KcpTransport.cs index 63d47f017..016914c0b 100644 --- a/Assets/Mirror/Transports/KCP/KcpTransport.cs +++ b/Assets/Mirror/Transports/KCP/KcpTransport.cs @@ -133,7 +133,7 @@ protected virtual void Awake() if (statisticsLog) InvokeRepeating(nameof(OnLogStatistics), 1, 1); - Debug.Log("KcpTransport initialized!"); + Log.Info("KcpTransport initialized!"); } protected virtual void OnValidate() @@ -344,7 +344,7 @@ protected virtual void OnLogStatistics() log += $" ReceiveQueue: {GetTotalReceiveQueue()}\n"; log += $" SendBuffer: {GetTotalSendBuffer()}\n"; log += $" ReceiveBuffer: {GetTotalReceiveBuffer()}\n\n"; - Debug.Log(log); + Log.Info(log); } if (ClientConnected()) @@ -356,7 +356,7 @@ protected virtual void OnLogStatistics() log += $" ReceiveQueue: {client.ReceiveQueueCount}\n"; log += $" SendBuffer: {client.SendBufferCount}\n"; log += $" ReceiveBuffer: {client.ReceiveBufferCount}\n\n"; - Debug.Log(log); + Log.Info(log); } } diff --git a/Assets/Mirror/Transports/KCP/ThreadedKcpTransport.cs b/Assets/Mirror/Transports/KCP/ThreadedKcpTransport.cs index 3b9d07917..1a24bc58f 100644 --- a/Assets/Mirror/Transports/KCP/ThreadedKcpTransport.cs +++ b/Assets/Mirror/Transports/KCP/ThreadedKcpTransport.cs @@ -122,7 +122,7 @@ protected override void Awake() // it'll be used by the created thread immediately. base.Awake(); - Debug.Log("ThreadedKcpTransport initialized!"); + Log.Info("ThreadedKcpTransport initialized!"); } protected virtual void OnValidate() @@ -300,7 +300,7 @@ protected virtual void OnLogStatistics() log += $" ReceiveQueue: {GetTotalReceiveQueue()}\n"; log += $" SendBuffer: {GetTotalSendBuffer()}\n"; log += $" ReceiveBuffer: {GetTotalReceiveBuffer()}\n\n"; - Debug.Log(log); + Log.Info(log); } if (ClientConnected()) @@ -312,7 +312,7 @@ protected virtual void OnLogStatistics() log += $" ReceiveQueue: {client.peer.ReceiveQueueCount}\n"; log += $" SendBuffer: {client.peer.SendBufferCount}\n"; log += $" ReceiveBuffer: {client.peer.ReceiveBufferCount}\n\n"; - Debug.Log(log); + Log.Info(log); } */ } diff --git a/Assets/Mirror/Transports/Multiplex/MultiplexTransport.cs b/Assets/Mirror/Transports/Multiplex/MultiplexTransport.cs index fbd2abf61..7690e5970 100644 --- a/Assets/Mirror/Transports/Multiplex/MultiplexTransport.cs +++ b/Assets/Mirror/Transports/Multiplex/MultiplexTransport.cs @@ -337,9 +337,10 @@ public override void Shutdown() public override string ToString() { StringBuilder builder = new StringBuilder(); + builder.Append("Multiplexer:"); foreach (Transport transport in transports) - builder.AppendLine(transport.ToString()); + builder.Append($" {transport}"); return builder.ToString().Trim(); } diff --git a/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/ServerSslHelper.cs b/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/ServerSslHelper.cs index 9bc9d8d5c..fb45b1358 100644 --- a/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/ServerSslHelper.cs +++ b/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/ServerSslHelper.cs @@ -29,8 +29,6 @@ internal class ServerSslHelper public ServerSslHelper(SslConfig sslConfig) { - Console.Clear(); - config = sslConfig; if (config.enabled) { diff --git a/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/WebSocketServer.cs b/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/WebSocketServer.cs index 82b22d2ca..c8895a234 100644 --- a/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/WebSocketServer.cs +++ b/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/WebSocketServer.cs @@ -37,7 +37,7 @@ public void Listen(int port) listener = TcpListener.Create(port); listener.Start(); - Log.Info($"[SWT-WebSocketServer]: Server Started on {port}!", ConsoleColor.Green); + Log.Verbose($"[SWT-WebSocketServer]: Server Started on {port}"); acceptThread = new Thread(acceptLoop); acceptThread.IsBackground = true; @@ -53,7 +53,7 @@ public void Stop() listener?.Stop(); acceptThread = null; - Log.Info($"[SWT-WebSocketServer]: Server stopped...closing all connections."); + Log.Verbose($"[SWT-WebSocketServer]: Server stopped...closing all connections."); // make copy so that foreach doesn't break if values are removed Connection[] connectionsCopy = connections.Values.ToArray(); diff --git a/Assets/Mirror/Transports/SimpleWeb/SimpleWebTransport.cs b/Assets/Mirror/Transports/SimpleWeb/SimpleWebTransport.cs index 6ae388406..5990bc3e8 100644 --- a/Assets/Mirror/Transports/SimpleWeb/SimpleWebTransport.cs +++ b/Assets/Mirror/Transports/SimpleWeb/SimpleWebTransport.cs @@ -52,7 +52,31 @@ public class SimpleWebTransport : Transport, PortTransport [Tooltip("Port to use for server")] public ushort port = 7778; - public ushort Port { get => port; set => port = value; } + public ushort Port + { + get + { +#if UNITY_WEBGL + if (clientWebsocketSettings.ClientPortOption == WebsocketPortOption.SpecifyPort) + return clientWebsocketSettings.CustomClientPort; + else + return port; +#else + return port; +#endif + } + set + { +#if UNITY_WEBGL + if (clientWebsocketSettings.ClientPortOption == WebsocketPortOption.SpecifyPort) + clientWebsocketSettings.CustomClientPort = value; + else + port = value; +#else + port = value; +#endif + } + } [Tooltip("Groups messages in queue before calling Stream.Send")] public bool batchSend = true; @@ -144,7 +168,7 @@ public override void ClientConnect(string hostname) // https://github.com/MirrorNetworking/Mirror/pull/3477 break; default: // default case handles ClientWebsocketPortOption.DefaultSameAsServerPort - builder.Port = Port; + builder.Port = port; break; } @@ -176,11 +200,28 @@ public override void ClientConnect(Uri uri) client.onData += (ArraySegment data) => OnClientDataReceived.Invoke(data, Channels.Reliable); - client.onError += (Exception e) => + // We will not invoke OnClientError if minLogLevel is set to None + // We only send the full exception if minLogLevel is set to Verbose + switch (Log.minLogLevel) { - OnClientError.Invoke(TransportError.Unexpected, e.ToString()); - ClientDisconnect(); - }; + case Log.Levels.Flood: + case Log.Levels.Verbose: + client.onError += (Exception e) => + { + OnClientError.Invoke(TransportError.Unexpected, e.ToString()); + ClientDisconnect(); + }; + break; + case Log.Levels.Info: + case Log.Levels.Warn: + case Log.Levels.Error: + client.onError += (Exception e) => + { + OnClientError.Invoke(TransportError.Unexpected, e.Message); + ClientDisconnect(); + }; + break; + } client.Connect(uri); } @@ -256,7 +297,29 @@ public override void ServerStart() server.onConnect += OnServerConnected.Invoke; server.onDisconnect += OnServerDisconnected.Invoke; server.onData += (int connId, ArraySegment data) => OnServerDataReceived.Invoke(connId, data, Channels.Reliable); - server.onError += (connId, exception) => OnServerError(connId, TransportError.Unexpected, exception.ToString()); + + // We will not invoke OnServerError if minLogLevel is set to None + // We only send the full exception if minLogLevel is set to Verbose + switch (Log.minLogLevel) + { + case Log.Levels.Flood: + case Log.Levels.Verbose: + server.onError += (connId, exception) => + { + OnServerError(connId, TransportError.Unexpected, exception.ToString()); + ServerDisconnect(connId); + }; + break; + case Log.Levels.Info: + case Log.Levels.Warn: + case Log.Levels.Error: + server.onError += (connId, exception) => + { + OnServerError(connId, TransportError.Unexpected, exception.Message); + ServerDisconnect(connId); + }; + break; + } SendLoopConfig.batchSend = batchSend || waitBeforeSend; SendLoopConfig.sleepBeforeSend = waitBeforeSend; diff --git a/Assets/Mirror/Transports/Telepathy/TelepathyTransport.cs b/Assets/Mirror/Transports/Telepathy/TelepathyTransport.cs index fabc189f1..d452d665a 100644 --- a/Assets/Mirror/Transports/Telepathy/TelepathyTransport.cs +++ b/Assets/Mirror/Transports/Telepathy/TelepathyTransport.cs @@ -250,25 +250,16 @@ public override int GetMaxPacketSize(int channelId) return serverMaxMessageSize; } - public override string ToString() - { - if (server != null && server.Active && server.listener != null) - { - // printing server.listener.LocalEndpoint causes an Exception - // in UWP + Unity 2019: - // Exception thrown at 0x00007FF9755DA388 in UWF.exe: - // Microsoft C++ exception: Il2CppExceptionWrapper at memory - // location 0x000000E15A0FCDD0. SocketException: An address - // incompatible with the requested protocol was used at - // System.Net.Sockets.Socket.get_LocalEndPoint () - // so let's use the regular port instead. - return $"Telepathy Server port: {port}"; - } - else if (client != null && (client.Connecting || client.Connected)) - { - return $"Telepathy Client port: {port}"; - } - return "Telepathy (inactive/disconnected)"; - } + // Keep it short and simple so it looks nice in the HUD. + // + // printing server.listener.LocalEndpoint causes an Exception + // in UWP + Unity 2019: + // Exception thrown at 0x00007FF9755DA388 in UWF.exe: + // Microsoft C++ exception: Il2CppExceptionWrapper at memory + // location 0x000000E15A0FCDD0. SocketException: An address + // incompatible with the requested protocol was used at + // System.Net.Sockets.Socket.get_LocalEndPoint () + // so just use the regular port instead. + public override string ToString() => $"Telepathy [{port}]"; } } diff --git a/README.md b/README.md index c92d02348..679b05e3d 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,16 @@ ![Mirror Logo](https://user-images.githubusercontent.com/16416509/119120944-6db26780-ba5f-11eb-9cdd-fc8500207f4d.png) - -[![Download](https://img.shields.io/badge/asset_store-brightgreen.svg)](https://assetstore.unity.com/packages/tools/network/mirror-129321) -[![Documentation](https://img.shields.io/badge/docs-brightgreen.svg)](https://mirror-networking.gitbook.io/) -[![Showcase](https://img.shields.io/badge/showcase-brightgreen.svg)](https://mirror-networking.com/showcase/) -[![Video Tutorials](https://img.shields.io/badge/video_tutorial-brightgreen.svg)](https://mirror-networking.gitbook.io/docs/community-guides/video-tutorials) -[![Forum](https://img.shields.io/badge/forum-brightgreen.svg)](https://forum.unity.com/threads/mirror-networking-for-unity-aka-hlapi-community-edition.425437/) -[![Build](https://img.shields.io/appveyor/ci/vis2k73562/hlapi-community-edition/Mirror.svg)](https://ci.appveyor.com/project/vis2k73562/hlapi-community-edition/branch/mirror) -[![Discord](https://img.shields.io/discord/343440455738064897.svg)](https://discordapp.com/invite/xVW4nU4C34) -[![release](https://img.shields.io/github/release/vis2k/Mirror.svg)](https://github.com/vis2k/Mirror/releases/latest) -[![License: MIT](https://img.shields.io/badge/License-MIT-brightgreen.svg)](https://github.com/vis2k/Mirror/blob/master/LICENSE) -[![Roadmap](https://img.shields.io/badge/roadmap-blue.svg)](https://trello.com/b/fgAE7Tud) +

+Download +Showcase +Documentation +Forum +Roadmap +
+License: MIT +Build +release +Discord +

**It's only the dreamers who ever move mountains.**