diff --git a/UnityProject/Assets/LRMFunctionTest.cs b/UnityProject/Assets/LRMFunctionTest.cs index 0d4141f..ca047ae 100644 --- a/UnityProject/Assets/LRMFunctionTest.cs +++ b/UnityProject/Assets/LRMFunctionTest.cs @@ -34,6 +34,12 @@ public class LRMFunctionTest : MonoBehaviour NetworkManager.singleton.StartHost(); yield return new WaitUntil(() => _LRM.serverId.Length > 4); DisplayText($"Room created! ID: {_LRM.serverId}"); + DisplayText("Requesting Server List..."); + _LRM.RequestServerList(); + yield return new WaitUntil(() => _serverListUpdated); + foreach (var server in _LRM.relayServerList) + DisplayText($"Got Server: {server.serverName}, {server.serverData}, {server.maxPlayers}"); + _serverListUpdated = false; DisplayText("Requesting Server Data Change..."); _LRM.UpdateRoomName("Updated Server Name"); _LRM.UpdateRoomData("Updated Server Data"); diff --git a/UnityProject/Assets/Mirror/Authenticators.meta b/UnityProject/Assets/Mirror/Authenticators.meta index ff0eac4..644f4ec 100644 --- a/UnityProject/Assets/Mirror/Authenticators.meta +++ b/UnityProject/Assets/Mirror/Authenticators.meta @@ -3,6 +3,6 @@ guid: 1b2f9d254154cd942ba40b06b869b8f3 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Authenticators/BasicAuthenticator.cs b/UnityProject/Assets/Mirror/Authenticators/BasicAuthenticator.cs index ef58fd1..8b1e739 100644 --- a/UnityProject/Assets/Mirror/Authenticators/BasicAuthenticator.cs +++ b/UnityProject/Assets/Mirror/Authenticators/BasicAuthenticator.cs @@ -1,20 +1,23 @@ using System; using System.Collections; +using System.Collections.Generic; using UnityEngine; namespace Mirror.Authenticators { - [AddComponentMenu("Network/Authenticators/BasicAuthenticator")] + [AddComponentMenu("Network/ Authenticators/Basic Authenticator")] + [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-authenticators/basic-authenticator")] public class BasicAuthenticator : NetworkAuthenticator { - [Header("Custom Properties")] + [Header("Server Credentials")] + public string serverUsername; + public string serverPassword; - // set these in the inspector + [Header("Client Credentials")] public string username; public string password; - // this is set if authentication fails to prevent garbage AuthRequestMessage spam - bool ServerAuthFailed; + readonly HashSet connectionsPendingDisconnect = new HashSet(); #region Messages @@ -57,10 +60,10 @@ namespace Mirror.Authenticators } /// - /// Called on server from OnServerAuthenticateInternal when a client needs to authenticate + /// Called on server from OnServerConnectInternal when a client needs to authenticate /// /// Connection to client. - public override void OnServerAuthenticate(NetworkConnection conn) + public override void OnServerAuthenticate(NetworkConnectionToClient conn) { // do nothing...wait for AuthRequestMessage from client } @@ -70,12 +73,14 @@ namespace Mirror.Authenticators /// /// Connection to client. /// The message payload - public void OnAuthRequestMessage(NetworkConnection conn, AuthRequestMessage msg) + public void OnAuthRequestMessage(NetworkConnectionToClient conn, AuthRequestMessage msg) { - // Debug.LogFormat(LogType.Log, "Authentication Request: {0} {1}", msg.authUsername, msg.authPassword); + //Debug.Log($"Authentication Request: {msg.authUsername} {msg.authPassword}"); + + if (connectionsPendingDisconnect.Contains(conn)) return; // check the credentials by calling your web server, database table, playfab api, or any method appropriate. - if (msg.authUsername == username && msg.authPassword == password) + if (msg.authUsername == serverUsername && msg.authPassword == serverPassword) { // create and send msg to client so it knows to proceed AuthResponseMessage authResponseMessage = new AuthResponseMessage @@ -91,6 +96,8 @@ namespace Mirror.Authenticators } else { + connectionsPendingDisconnect.Add(conn); + // create and send msg to client so it knows to disconnect AuthResponseMessage authResponseMessage = new AuthResponseMessage { @@ -104,22 +111,21 @@ namespace Mirror.Authenticators conn.isAuthenticated = false; // disconnect the client after 1 second so that response message gets delivered - if (!ServerAuthFailed) - { - // set this false so this coroutine can only be started once - ServerAuthFailed = true; - - StartCoroutine(DelayedDisconnect(conn, 1)); - } + StartCoroutine(DelayedDisconnect(conn, 1f)); } } - IEnumerator DelayedDisconnect(NetworkConnection conn, float waitTime) + IEnumerator DelayedDisconnect(NetworkConnectionToClient conn, float waitTime) { yield return new WaitForSeconds(waitTime); // Reject the unsuccessful authentication ServerReject(conn); + + yield return null; + + // remove conn from pending connections + connectionsPendingDisconnect.Remove(conn); } #endregion @@ -133,7 +139,7 @@ namespace Mirror.Authenticators public override void OnStartClient() { // register a handler for the authentication response we expect from server - NetworkClient.RegisterHandler((Action)OnAuthResponseMessage, false); + NetworkClient.RegisterHandler(OnAuthResponseMessage, false); } /// @@ -147,7 +153,7 @@ namespace Mirror.Authenticators } /// - /// Called on client from OnClientAuthenticateInternal when a client needs to authenticate + /// Called on client from OnClientConnectInternal when a client needs to authenticate /// public override void OnClientAuthenticate() { @@ -160,9 +166,6 @@ namespace Mirror.Authenticators NetworkClient.connection.Send(authRequestMessage); } - [Obsolete("Call OnAuthResponseMessage without the NetworkConnection parameter. It always points to NetworkClient.connection anyway.")] - public void OnAuthResponseMessage(NetworkConnection conn, AuthResponseMessage msg) => OnAuthResponseMessage(msg); - /// /// Called on client when the server's AuthResponseMessage arrives /// @@ -171,7 +174,7 @@ namespace Mirror.Authenticators { if (msg.code == 100) { - // Debug.LogFormat(LogType.Log, "Authentication Response: {0}", msg.message); + //Debug.Log($"Authentication Response: {msg.message}"); // Authentication has been accepted ClientAccept(); diff --git a/UnityProject/Assets/Mirror/Authenticators/BasicAuthenticator.cs.meta b/UnityProject/Assets/Mirror/Authenticators/BasicAuthenticator.cs.meta index 74b9134..4765013 100644 --- a/UnityProject/Assets/Mirror/Authenticators/BasicAuthenticator.cs.meta +++ b/UnityProject/Assets/Mirror/Authenticators/BasicAuthenticator.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Authenticators/DeviceAuthenticator.cs b/UnityProject/Assets/Mirror/Authenticators/DeviceAuthenticator.cs new file mode 100644 index 0000000..7074ec5 --- /dev/null +++ b/UnityProject/Assets/Mirror/Authenticators/DeviceAuthenticator.cs @@ -0,0 +1,129 @@ +using System; +using UnityEngine; + +namespace Mirror.Authenticators +{ + /// + /// An authenticator that identifies the user by their device. + /// A GUID is used as a fallback when the platform doesn't support SystemInfo.deviceUniqueIdentifier. + /// Note: deviceUniqueIdentifier can be spoofed, so security is not guaranteed. + /// See https://docs.unity3d.com/ScriptReference/SystemInfo-deviceUniqueIdentifier.html for details. + /// + [AddComponentMenu("Network/ Authenticators/Device Authenticator")] + [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-authenticators/device-authenticator")] + public class DeviceAuthenticator : NetworkAuthenticator + { + #region Messages + + public struct AuthRequestMessage : NetworkMessage + { + public string clientDeviceID; + } + + public struct AuthResponseMessage : NetworkMessage { } + + #endregion + + #region Server + + /// + /// Called on server from StartServer to initialize the Authenticator + /// Server message handlers should be registered in this method. + /// + public override void OnStartServer() + { + // register a handler for the authentication request we expect from client + NetworkServer.RegisterHandler(OnAuthRequestMessage, false); + } + + /// + /// Called on server from StopServer to reset the Authenticator + /// Server message handlers should be registered in this method. + /// + public override void OnStopServer() + { + // unregister the handler for the authentication request + NetworkServer.UnregisterHandler(); + } + + /// + /// Called on server from OnServerConnectInternal when a client needs to authenticate + /// + /// Connection to client. + public override void OnServerAuthenticate(NetworkConnectionToClient conn) + { + // do nothing, wait for client to send his id + } + + void OnAuthRequestMessage(NetworkConnectionToClient conn, AuthRequestMessage msg) + { + Debug.Log($"connection {conn.connectionId} authenticated with id {msg.clientDeviceID}"); + + // Store the device id for later reference, e.g. when spawning the player + conn.authenticationData = msg.clientDeviceID; + + // Send a response to client telling it to proceed as authenticated + conn.Send(new AuthResponseMessage()); + + // Accept the successful authentication + ServerAccept(conn); + } + + #endregion + + #region Client + + /// + /// Called on client from StartClient to initialize the Authenticator + /// Client message handlers should be registered in this method. + /// + public override void OnStartClient() + { + // register a handler for the authentication response we expect from server + NetworkClient.RegisterHandler(OnAuthResponseMessage, false); + } + + /// + /// Called on client from StopClient to reset the Authenticator + /// Client message handlers should be unregistered in this method. + /// + public override void OnStopClient() + { + // unregister the handler for the authentication response + NetworkClient.UnregisterHandler(); + } + + /// + /// Called on client from OnClientConnectInternal when a client needs to authenticate + /// + public override void OnClientAuthenticate() + { + string deviceUniqueIdentifier = SystemInfo.deviceUniqueIdentifier; + + // Not all platforms support this, so we use a GUID instead + if (deviceUniqueIdentifier == SystemInfo.unsupportedIdentifier) + { + // Get the value from PlayerPrefs if it exists, new GUID if it doesn't + deviceUniqueIdentifier = PlayerPrefs.GetString("deviceUniqueIdentifier", Guid.NewGuid().ToString()); + + // Store the deviceUniqueIdentifier to PlayerPrefs (in case we just made a new GUID) + PlayerPrefs.SetString("deviceUniqueIdentifier", deviceUniqueIdentifier); + } + + // send the deviceUniqueIdentifier to the server + NetworkClient.connection.Send(new AuthRequestMessage { clientDeviceID = deviceUniqueIdentifier } ); + } + + /// + /// Called on client when the server's AuthResponseMessage arrives + /// + /// The message payload + public void OnAuthResponseMessage(AuthResponseMessage msg) + { + Debug.Log("Authentication Success"); + ClientAccept(); + } + + #endregion + } +} diff --git a/UnityProject/Assets/Mirror/Components/Experimental/NetworkTransformChild.cs.meta b/UnityProject/Assets/Mirror/Authenticators/DeviceAuthenticator.cs.meta similarity index 66% rename from UnityProject/Assets/Mirror/Components/Experimental/NetworkTransformChild.cs.meta rename to UnityProject/Assets/Mirror/Authenticators/DeviceAuthenticator.cs.meta index 2ed4212..9ca9f64 100644 --- a/UnityProject/Assets/Mirror/Components/Experimental/NetworkTransformChild.cs.meta +++ b/UnityProject/Assets/Mirror/Authenticators/DeviceAuthenticator.cs.meta @@ -1,11 +1,11 @@ fileFormatVersion: 2 -guid: f65214da13a861f4a8ae309d3daea1c6 +guid: 60960a6ba81a842deb2fdcdc93788242 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Authenticators/Mirror.Authenticators.asmdef.meta b/UnityProject/Assets/Mirror/Authenticators/Mirror.Authenticators.asmdef.meta index 0fa1014..2731701 100644 --- a/UnityProject/Assets/Mirror/Authenticators/Mirror.Authenticators.asmdef.meta +++ b/UnityProject/Assets/Mirror/Authenticators/Mirror.Authenticators.asmdef.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: e720aa64e3f58fb4880566a322584340 AssemblyDefinitionImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Authenticators/TimeoutAuthenticator.cs b/UnityProject/Assets/Mirror/Authenticators/TimeoutAuthenticator.cs index 28f989b..711ee6e 100644 --- a/UnityProject/Assets/Mirror/Authenticators/TimeoutAuthenticator.cs +++ b/UnityProject/Assets/Mirror/Authenticators/TimeoutAuthenticator.cs @@ -7,7 +7,7 @@ namespace Mirror.Authenticators /// An authenticator that disconnects connections if they don't /// authenticate within a specified time limit. /// - [AddComponentMenu("Network/Authenticators/TimeoutAuthenticator")] + [AddComponentMenu("Network/ Authenticators/Timeout Authenticator")] public class TimeoutAuthenticator : NetworkAuthenticator { public NetworkAuthenticator authenticator; @@ -18,7 +18,7 @@ namespace Mirror.Authenticators public void Awake() { authenticator.OnServerAuthenticated.AddListener(connection => OnServerAuthenticated.Invoke(connection)); - authenticator.OnClientAuthenticated.AddListener(connection => OnClientAuthenticated.Invoke(connection)); + authenticator.OnClientAuthenticated.AddListener(OnClientAuthenticated.Invoke); } public override void OnStartServer() @@ -41,7 +41,7 @@ namespace Mirror.Authenticators authenticator.OnStopClient(); } - public override void OnServerAuthenticate(NetworkConnection conn) + public override void OnServerAuthenticate(NetworkConnectionToClient conn) { authenticator.OnServerAuthenticate(conn); if (timeout > 0) @@ -57,12 +57,12 @@ namespace Mirror.Authenticators IEnumerator BeginAuthentication(NetworkConnection conn) { - // Debug.Log($"Authentication countdown started {conn} {timeout}"); + //Debug.Log($"Authentication countdown started {conn} {timeout}"); yield return new WaitForSecondsRealtime(timeout); if (!conn.isAuthenticated) { - // Debug.Log($"Authentication Timeout {conn}"); + Debug.LogError($"Authentication Timeout - Disconnecting {conn}"); conn.Disconnect(); } } diff --git a/UnityProject/Assets/Mirror/Authenticators/TimeoutAuthenticator.cs.meta b/UnityProject/Assets/Mirror/Authenticators/TimeoutAuthenticator.cs.meta index 433d094..b19ddec 100644 --- a/UnityProject/Assets/Mirror/Authenticators/TimeoutAuthenticator.cs.meta +++ b/UnityProject/Assets/Mirror/Authenticators/TimeoutAuthenticator.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/CompilerSymbols.meta b/UnityProject/Assets/Mirror/CompilerSymbols.meta index 8d0dedc..4652ae1 100644 --- a/UnityProject/Assets/Mirror/CompilerSymbols.meta +++ b/UnityProject/Assets/Mirror/CompilerSymbols.meta @@ -3,6 +3,6 @@ guid: 1f8b918bcd89f5c488b06f5574f34760 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/CompilerSymbols/Mirror.CompilerSymbols.asmdef.meta b/UnityProject/Assets/Mirror/CompilerSymbols/Mirror.CompilerSymbols.asmdef.meta index 98ac7a0..8b23823 100644 --- a/UnityProject/Assets/Mirror/CompilerSymbols/Mirror.CompilerSymbols.asmdef.meta +++ b/UnityProject/Assets/Mirror/CompilerSymbols/Mirror.CompilerSymbols.asmdef.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 325984b52e4128546bc7558552f8b1d2 AssemblyDefinitionImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/CompilerSymbols/PreprocessorDefine.cs b/UnityProject/Assets/Mirror/CompilerSymbols/PreprocessorDefine.cs index e81d542..7106b04 100644 --- a/UnityProject/Assets/Mirror/CompilerSymbols/PreprocessorDefine.cs +++ b/UnityProject/Assets/Mirror/CompilerSymbols/PreprocessorDefine.cs @@ -31,7 +31,20 @@ namespace Mirror "MIRROR_37_0_OR_NEWER", "MIRROR_38_0_OR_NEWER", "MIRROR_39_0_OR_NEWER", - "MIRROR_40_0_OR_NEWER" + "MIRROR_40_0_OR_NEWER", + "MIRROR_41_0_OR_NEWER", + "MIRROR_42_0_OR_NEWER", + "MIRROR_43_0_OR_NEWER", + "MIRROR_44_0_OR_NEWER", + "MIRROR_46_0_OR_NEWER", + "MIRROR_47_0_OR_NEWER", + "MIRROR_53_0_OR_NEWER", + "MIRROR_55_0_OR_NEWER", + "MIRROR_57_0_OR_NEWER", + "MIRROR_58_0_OR_NEWER", + "MIRROR_65_0_OR_NEWER", + "MIRROR_66_0_OR_NEWER", + "MIRROR_2022_9_OR_NEWER" }; // only touch PlayerSettings if we actually modified it. diff --git a/UnityProject/Assets/Mirror/CompilerSymbols/PreprocessorDefine.cs.meta b/UnityProject/Assets/Mirror/CompilerSymbols/PreprocessorDefine.cs.meta index 248f00b..30806d0 100644 --- a/UnityProject/Assets/Mirror/CompilerSymbols/PreprocessorDefine.cs.meta +++ b/UnityProject/Assets/Mirror/CompilerSymbols/PreprocessorDefine.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components.meta b/UnityProject/Assets/Mirror/Components.meta index a257fe6..c2771d9 100644 --- a/UnityProject/Assets/Mirror/Components.meta +++ b/UnityProject/Assets/Mirror/Components.meta @@ -3,6 +3,6 @@ guid: 9bee879fbc8ef4b1a9a9f7088bfbf726 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/AssemblyInfo.cs b/UnityProject/Assets/Mirror/Components/AssemblyInfo.cs index 5af6857..f342716 100644 --- a/UnityProject/Assets/Mirror/Components/AssemblyInfo.cs +++ b/UnityProject/Assets/Mirror/Components/AssemblyInfo.cs @@ -2,6 +2,9 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Mirror.Tests.Common")] [assembly: InternalsVisibleTo("Mirror.Tests")] +// need to use Unity.*.CodeGen assembly name to import Unity.CompilationPipeline +// for ILPostProcessor tests. +[assembly: InternalsVisibleTo("Unity.Mirror.Tests.CodeGen")] [assembly: InternalsVisibleTo("Mirror.Tests.Generated")] [assembly: InternalsVisibleTo("Mirror.Tests.Runtime")] [assembly: InternalsVisibleTo("Mirror.Tests.Performance.Editor")] diff --git a/UnityProject/Assets/Mirror/Components/AssemblyInfo.cs.meta b/UnityProject/Assets/Mirror/Components/AssemblyInfo.cs.meta index d50ab80..f9af1fa 100644 --- a/UnityProject/Assets/Mirror/Components/AssemblyInfo.cs.meta +++ b/UnityProject/Assets/Mirror/Components/AssemblyInfo.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/Discovery.meta b/UnityProject/Assets/Mirror/Components/Discovery.meta index 28e0327..d5bb0cb 100644 --- a/UnityProject/Assets/Mirror/Components/Discovery.meta +++ b/UnityProject/Assets/Mirror/Components/Discovery.meta @@ -3,6 +3,6 @@ guid: b5dcf9618f5e14a4eb60bff5480284a6 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscovery.cs b/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscovery.cs index 5d90087..ddb38ea 100644 --- a/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscovery.cs +++ b/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscovery.cs @@ -9,7 +9,7 @@ namespace Mirror.Discovery public class ServerFoundUnityEvent : UnityEvent {}; [DisallowMultipleComponent] - [AddComponentMenu("Network/NetworkDiscovery")] + [AddComponentMenu("Network/Network Discovery")] public class NetworkDiscovery : NetworkDiscoveryBase { #region Server diff --git a/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscovery.cs.meta b/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscovery.cs.meta index f6c8bcd..c691a61 100644 --- a/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscovery.cs.meta +++ b/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscovery.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscoveryBase.cs b/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscoveryBase.cs index f83ebd2..ac57b75 100644 --- a/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscoveryBase.cs +++ b/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscoveryBase.cs @@ -93,6 +93,7 @@ namespace Mirror.Discovery void Shutdown() { + EndpMulticastLock(); if (serverUdpClient != null) { try @@ -149,6 +150,7 @@ namespace Mirror.Discovery public async Task ServerListenAsync() { + BeginMulticastLock(); while (true) { try @@ -173,7 +175,7 @@ namespace Mirror.Discovery UdpReceiveResult udpReceiveResult = await udpClient.ReceiveAsync(); - using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(udpReceiveResult.Buffer)) + using (NetworkReaderPooled networkReader = NetworkReaderPool.Get(udpReceiveResult.Buffer)) { long handshake = networkReader.ReadLong(); if (handshake != secretHandshake) @@ -204,7 +206,7 @@ namespace Mirror.Discovery if (info == null) return; - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + using (NetworkWriterPooled writer = NetworkWriterPool.Get()) { try { @@ -236,7 +238,42 @@ namespace Mirror.Discovery /// The message to be sent back to the client or null protected abstract Response ProcessRequest(Request request, IPEndPoint endpoint); - #endregion + // Android Multicast fix: https://github.com/vis2k/Mirror/pull/2887 +#if UNITY_ANDROID + AndroidJavaObject multicastLock; + bool hasMulticastLock; +#endif + void BeginMulticastLock() + { +#if UNITY_ANDROID + if (hasMulticastLock) return; + + if (Application.platform == RuntimePlatform.Android) + { + using (AndroidJavaObject activity = new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic("currentActivity")) + { + using (var wifiManager = activity.Call("getSystemService", "wifi")) + { + multicastLock = wifiManager.Call("createMulticastLock", "lock"); + multicastLock.Call("acquire"); + hasMulticastLock = true; + } + } + } +#endif + } + + void EndpMulticastLock() + { +#if UNITY_ANDROID + if (!hasMulticastLock) return; + + multicastLock?.Call("release"); + hasMulticastLock = false; +#endif + } + +#endregion #region Client @@ -287,7 +324,18 @@ namespace Mirror.Discovery /// ClientListenAsync Task public async Task ClientListenAsync() { - while (true) + // while clientUpdClient to fix: + // https://github.com/vis2k/Mirror/pull/2908 + // + // If, you cancel discovery the clientUdpClient is set to null. + // However, nothing cancels ClientListenAsync. If we change the if(true) + // to check if the client is null. You can properly cancel the discovery, + // and kill the listen thread. + // + // Prior to this fix, if you cancel the discovery search. It crashes the + // thread, and is super noisy in the output. As well as causes issues on + // the quest. + while (clientUdpClient != null) { try { @@ -321,7 +369,7 @@ namespace Mirror.Discovery IPEndPoint endPoint = new IPEndPoint(IPAddress.Broadcast, serverBroadcastListenPort); - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + using (NetworkWriterPooled writer = NetworkWriterPool.Get()) { writer.WriteLong(secretHandshake); @@ -358,7 +406,7 @@ namespace Mirror.Discovery UdpReceiveResult udpReceiveResult = await udpClient.ReceiveAsync(); - using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(udpReceiveResult.Buffer)) + using (NetworkReaderPooled networkReader = NetworkReaderPool.Get(udpReceiveResult.Buffer)) { if (networkReader.ReadLong() != secretHandshake) return; diff --git a/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscoveryBase.cs.meta b/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscoveryBase.cs.meta index e51d40a..7dfbaf6 100644 --- a/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscoveryBase.cs.meta +++ b/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscoveryBase.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscoveryHUD.cs b/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscoveryHUD.cs index 2f40d01..e43c3d7 100644 --- a/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscoveryHUD.cs +++ b/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscoveryHUD.cs @@ -4,7 +4,7 @@ using UnityEngine; namespace Mirror.Discovery { [DisallowMultipleComponent] - [AddComponentMenu("Network/NetworkDiscoveryHUD")] + [AddComponentMenu("Network/Network Discovery HUD")] [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-discovery")] [RequireComponent(typeof(NetworkDiscovery))] public class NetworkDiscoveryHUD : MonoBehaviour @@ -62,7 +62,6 @@ namespace Mirror.Discovery { discoveredServers.Clear(); NetworkManager.singleton.StartServer(); - networkDiscovery.AdvertiseServer(); } @@ -93,6 +92,7 @@ namespace Mirror.Discovery if (GUILayout.Button("Stop Host")) { NetworkManager.singleton.StopHost(); + networkDiscovery.StopDiscovery(); } } // stop client if client-only @@ -101,6 +101,7 @@ namespace Mirror.Discovery if (GUILayout.Button("Stop Client")) { NetworkManager.singleton.StopClient(); + networkDiscovery.StopDiscovery(); } } // stop server if server-only @@ -109,6 +110,7 @@ namespace Mirror.Discovery if (GUILayout.Button("Stop Server")) { NetworkManager.singleton.StopServer(); + networkDiscovery.StopDiscovery(); } } @@ -117,6 +119,7 @@ namespace Mirror.Discovery void Connect(ServerResponse info) { + networkDiscovery.StopDiscovery(); NetworkManager.singleton.StartClient(info.uri); } diff --git a/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscoveryHUD.cs.meta b/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscoveryHUD.cs.meta index 375a0d8..f93b275 100644 --- a/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscoveryHUD.cs.meta +++ b/UnityProject/Assets/Mirror/Components/Discovery/NetworkDiscoveryHUD.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/Discovery/ServerRequest.cs.meta b/UnityProject/Assets/Mirror/Components/Discovery/ServerRequest.cs.meta index 0bbe3ef..84f3232 100644 --- a/UnityProject/Assets/Mirror/Components/Discovery/ServerRequest.cs.meta +++ b/UnityProject/Assets/Mirror/Components/Discovery/ServerRequest.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/Discovery/ServerResponse.cs.meta b/UnityProject/Assets/Mirror/Components/Discovery/ServerResponse.cs.meta index 09532c4..44f23ba 100644 --- a/UnityProject/Assets/Mirror/Components/Discovery/ServerResponse.cs.meta +++ b/UnityProject/Assets/Mirror/Components/Discovery/ServerResponse.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/Experimental.meta b/UnityProject/Assets/Mirror/Components/Experimental.meta index ebf1efd..57cce38 100644 --- a/UnityProject/Assets/Mirror/Components/Experimental.meta +++ b/UnityProject/Assets/Mirror/Components/Experimental.meta @@ -3,6 +3,6 @@ guid: bfbf2a1f2b300c5489dcab219ef2846e folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/Experimental/NetworkLerpRigidbody.cs b/UnityProject/Assets/Mirror/Components/Experimental/NetworkLerpRigidbody.cs index 41a4d6a..06a87a9 100644 --- a/UnityProject/Assets/Mirror/Components/Experimental/NetworkLerpRigidbody.cs +++ b/UnityProject/Assets/Mirror/Components/Experimental/NetworkLerpRigidbody.cs @@ -2,7 +2,7 @@ using UnityEngine; namespace Mirror.Experimental { - [AddComponentMenu("Network/Experimental/NetworkLerpRigidbody")] + [AddComponentMenu("Network/ Experimental/Network Lerp Rigidbody")] [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-lerp-rigidbody")] public class NetworkLerpRigidbody : NetworkBehaviour { @@ -85,7 +85,7 @@ namespace Mirror.Experimental target.velocity = Vector3.Lerp(target.velocity, targetVelocity, lerpVelocityAmount); target.position = Vector3.Lerp(target.position, targetPosition, lerpPositionAmount); // add velocity to position as position would have moved on server at that velocity - targetPosition += target.velocity * Time.fixedDeltaTime; + target.position += target.velocity * Time.fixedDeltaTime; // TODO does this also need to sync acceleration so and update velocity? } diff --git a/UnityProject/Assets/Mirror/Components/Experimental/NetworkLerpRigidbody.cs.meta b/UnityProject/Assets/Mirror/Components/Experimental/NetworkLerpRigidbody.cs.meta index 1c006ab..35ef1fe 100644 --- a/UnityProject/Assets/Mirror/Components/Experimental/NetworkLerpRigidbody.cs.meta +++ b/UnityProject/Assets/Mirror/Components/Experimental/NetworkLerpRigidbody.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/Experimental/NetworkRigidbody.cs b/UnityProject/Assets/Mirror/Components/Experimental/NetworkRigidbody.cs index b39b0ff..4989d29 100644 --- a/UnityProject/Assets/Mirror/Components/Experimental/NetworkRigidbody.cs +++ b/UnityProject/Assets/Mirror/Components/Experimental/NetworkRigidbody.cs @@ -2,7 +2,7 @@ using UnityEngine; namespace Mirror.Experimental { - [AddComponentMenu("Network/Experimental/NetworkRigidbody")] + [AddComponentMenu("Network/ Experimental/Network Rigidbody")] [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-rigidbody")] public class NetworkRigidbody : NetworkBehaviour { @@ -10,7 +10,7 @@ namespace Mirror.Experimental [SerializeField] internal Rigidbody target = null; [Tooltip("Set to true if moves come from owner client, set to false if moves always come from server")] - [SerializeField] bool clientAuthority = false; + public bool clientAuthority = false; [Header("Velocity")] diff --git a/UnityProject/Assets/Mirror/Components/Experimental/NetworkRigidbody.cs.meta b/UnityProject/Assets/Mirror/Components/Experimental/NetworkRigidbody.cs.meta index 9a05405..1610f0a 100644 --- a/UnityProject/Assets/Mirror/Components/Experimental/NetworkRigidbody.cs.meta +++ b/UnityProject/Assets/Mirror/Components/Experimental/NetworkRigidbody.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/Experimental/NetworkRigidbody2D.cs b/UnityProject/Assets/Mirror/Components/Experimental/NetworkRigidbody2D.cs index facb773..c14b260 100644 --- a/UnityProject/Assets/Mirror/Components/Experimental/NetworkRigidbody2D.cs +++ b/UnityProject/Assets/Mirror/Components/Experimental/NetworkRigidbody2D.cs @@ -2,14 +2,14 @@ using UnityEngine; namespace Mirror.Experimental { - [AddComponentMenu("Network/Experimental/NetworkRigidbody2D")] + [AddComponentMenu("Network/ Experimental/Network Rigidbody 2D")] public class NetworkRigidbody2D : NetworkBehaviour { [Header("Settings")] [SerializeField] internal Rigidbody2D target = null; [Tooltip("Set to true if moves come from owner client, set to false if moves always come from server")] - [SerializeField] bool clientAuthority = false; + public bool clientAuthority = false; [Header("Velocity")] diff --git a/UnityProject/Assets/Mirror/Components/Experimental/NetworkRigidbody2D.cs.meta b/UnityProject/Assets/Mirror/Components/Experimental/NetworkRigidbody2D.cs.meta index f092db7..df466bd 100644 --- a/UnityProject/Assets/Mirror/Components/Experimental/NetworkRigidbody2D.cs.meta +++ b/UnityProject/Assets/Mirror/Components/Experimental/NetworkRigidbody2D.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/Experimental/NetworkTransform.cs b/UnityProject/Assets/Mirror/Components/Experimental/NetworkTransform.cs deleted file mode 100644 index 495518e..0000000 --- a/UnityProject/Assets/Mirror/Components/Experimental/NetworkTransform.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UnityEngine; - -namespace Mirror.Experimental -{ - [DisallowMultipleComponent] - [AddComponentMenu("Network/Experimental/NetworkTransformExperimental")] - [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-transform")] - public class NetworkTransform : NetworkTransformBase - { - protected override Transform targetTransform => transform; - } -} diff --git a/UnityProject/Assets/Mirror/Components/Experimental/NetworkTransformBase.cs b/UnityProject/Assets/Mirror/Components/Experimental/NetworkTransformBase.cs deleted file mode 100644 index 6e1fc9a..0000000 --- a/UnityProject/Assets/Mirror/Components/Experimental/NetworkTransformBase.cs +++ /dev/null @@ -1,529 +0,0 @@ -// vis2k: -// base class for NetworkTransform and NetworkTransformChild. -// New method is simple and stupid. No more 1500 lines of code. -// -// Server sends current data. -// Client saves it and interpolates last and latest data points. -// Update handles transform movement / rotation -// FixedUpdate handles rigidbody movement / rotation -// -// Notes: -// * Built-in Teleport detection in case of lags / teleport / obstacles -// * Quaternion > EulerAngles because gimbal lock and Quaternion.Slerp -// * Syncs XYZ. Works 3D and 2D. Saving 4 bytes isn't worth 1000 lines of code. -// * Initial delay might happen if server sends packet immediately after moving -// just 1cm, hence we move 1cm and then wait 100ms for next packet -// * Only way for smooth movement is to use a fixed movement speed during -// interpolation. interpolation over time is never that good. -// -using System; -using UnityEngine; - -namespace Mirror.Experimental -{ - public abstract class NetworkTransformBase : NetworkBehaviour - { - // target transform to sync. can be on a child. - protected abstract Transform targetTransform { get; } - - [Header("Authority")] - - [Tooltip("Set to true if moves come from owner client, set to false if moves always come from server")] - [SyncVar] - public bool clientAuthority; - - [Tooltip("Set to true if updates from server should be ignored by owner")] - [SyncVar] - public bool excludeOwnerUpdate = true; - - [Header("Synchronization")] - - [Tooltip("Set to true if position should be synchronized")] - [SyncVar] - public bool syncPosition = true; - - [Tooltip("Set to true if rotation should be synchronized")] - [SyncVar] - public bool syncRotation = true; - - [Tooltip("Set to true if scale should be synchronized")] - [SyncVar] - public bool syncScale = true; - - [Header("Interpolation")] - - [Tooltip("Set to true if position should be interpolated")] - [SyncVar] - public bool interpolatePosition = true; - - [Tooltip("Set to true if rotation should be interpolated")] - [SyncVar] - public bool interpolateRotation = true; - - [Tooltip("Set to true if scale should be interpolated")] - [SyncVar] - public bool interpolateScale = true; - - // Sensitivity is added for VR where human players tend to have micro movements so this can quiet down - // the network traffic. Additionally, rigidbody drift should send less traffic, e.g very slow sliding / rolling. - [Header("Sensitivity")] - - [Tooltip("Changes to the transform must exceed these values to be transmitted on the network.")] - [SyncVar] - public float localPositionSensitivity = .01f; - - [Tooltip("If rotation exceeds this angle, it will be transmitted on the network")] - [SyncVar] - public float localRotationSensitivity = .01f; - - [Tooltip("Changes to the transform must exceed these values to be transmitted on the network.")] - [SyncVar] - public float localScaleSensitivity = .01f; - - [Header("Diagnostics")] - - // server - public Vector3 lastPosition; - public Quaternion lastRotation; - public Vector3 lastScale; - - // client - // use local position/rotation for VR support - [Serializable] - public struct DataPoint - { - public float timeStamp; - public Vector3 localPosition; - public Quaternion localRotation; - public Vector3 localScale; - public float movementSpeed; - - public bool isValid => timeStamp != 0; - } - - // Is this a client with authority over this transform? - // This component could be on the player object or any object that has been assigned authority to this client. - bool IsOwnerWithClientAuthority => hasAuthority && clientAuthority; - - // interpolation start and goal - public DataPoint start = new DataPoint(); - public DataPoint goal = new DataPoint(); - - // We need to store this locally on the server so clients can't request Authority when ever they like - bool clientAuthorityBeforeTeleport; - - void FixedUpdate() - { - // if server then always sync to others. - // let the clients know that this has moved - if (isServer && HasEitherMovedRotatedScaled()) - { - ServerUpdate(); - } - - if (isClient) - { - // send to server if we have local authority (and aren't the server) - // -> only if connectionToServer has been initialized yet too - if (IsOwnerWithClientAuthority) - { - ClientAuthorityUpdate(); - } - else if (goal.isValid) - { - ClientRemoteUpdate(); - } - } - } - - void ServerUpdate() - { - RpcMove(targetTransform.localPosition, Compression.CompressQuaternion(targetTransform.localRotation), targetTransform.localScale); - } - - void ClientAuthorityUpdate() - { - if (!isServer && HasEitherMovedRotatedScaled()) - { - // serialize - // local position/rotation for VR support - // send to server - CmdClientToServerSync(targetTransform.localPosition, Compression.CompressQuaternion(targetTransform.localRotation), targetTransform.localScale); - } - } - - void ClientRemoteUpdate() - { - // teleport or interpolate - if (NeedsTeleport()) - { - // local position/rotation for VR support - ApplyPositionRotationScale(goal.localPosition, goal.localRotation, goal.localScale); - - // reset data points so we don't keep interpolating - start = new DataPoint(); - goal = new DataPoint(); - } - else - { - // local position/rotation for VR support - ApplyPositionRotationScale(InterpolatePosition(start, goal, targetTransform.localPosition), - InterpolateRotation(start, goal, targetTransform.localRotation), - InterpolateScale(start, goal, targetTransform.localScale)); - } - } - - // moved or rotated or scaled since last time we checked it? - bool HasEitherMovedRotatedScaled() - { - // Save last for next frame to compare only if change was detected, otherwise - // slow moving objects might never sync because of C#'s float comparison tolerance. - // See also: https://github.com/vis2k/Mirror/pull/428) - bool changed = HasMoved || HasRotated || HasScaled; - if (changed) - { - // local position/rotation for VR support - if (syncPosition) lastPosition = targetTransform.localPosition; - if (syncRotation) lastRotation = targetTransform.localRotation; - if (syncScale) lastScale = targetTransform.localScale; - } - return changed; - } - - // local position/rotation for VR support - // SqrMagnitude is faster than Distance per Unity docs - // https://docs.unity3d.com/ScriptReference/Vector3-sqrMagnitude.html - - bool HasMoved => syncPosition && Vector3.SqrMagnitude(lastPosition - targetTransform.localPosition) > localPositionSensitivity * localPositionSensitivity; - bool HasRotated => syncRotation && Quaternion.Angle(lastRotation, targetTransform.localRotation) > localRotationSensitivity; - bool HasScaled => syncScale && Vector3.SqrMagnitude(lastScale - targetTransform.localScale) > localScaleSensitivity * localScaleSensitivity; - - // teleport / lag / stuck detection - // - checking distance is not enough since there could be just a tiny fence between us and the goal - // - checking time always works, this way we just teleport if we still didn't reach the goal after too much time has elapsed - bool NeedsTeleport() - { - // calculate time between the two data points - float startTime = start.isValid ? start.timeStamp : Time.time - Time.fixedDeltaTime; - float goalTime = goal.isValid ? goal.timeStamp : Time.time; - float difference = goalTime - startTime; - float timeSinceGoalReceived = Time.time - goalTime; - return timeSinceGoalReceived > difference * 5; - } - - // local authority client sends sync message to server for broadcasting - [Command(channel = Channels.Unreliable)] - void CmdClientToServerSync(Vector3 position, uint packedRotation, Vector3 scale) - { - // Ignore messages from client if not in client authority mode - if (!clientAuthority) - return; - - // deserialize payload - SetGoal(position, Compression.DecompressQuaternion(packedRotation), scale); - - // server-only mode does no interpolation to save computations, but let's set the position directly - if (isServer && !isClient) - ApplyPositionRotationScale(goal.localPosition, goal.localRotation, goal.localScale); - - RpcMove(position, packedRotation, scale); - } - - [ClientRpc(channel = Channels.Unreliable)] - void RpcMove(Vector3 position, uint packedRotation, Vector3 scale) - { - if (hasAuthority && excludeOwnerUpdate) return; - - if (!isServer) - SetGoal(position, Compression.DecompressQuaternion(packedRotation), scale); - } - - // serialization is needed by OnSerialize and by manual sending from authority - void SetGoal(Vector3 position, Quaternion rotation, Vector3 scale) - { - // put it into a data point immediately - DataPoint temp = new DataPoint - { - // deserialize position - localPosition = position, - localRotation = rotation, - localScale = scale, - timeStamp = Time.time - }; - - // movement speed: based on how far it moved since last time has to be calculated before 'start' is overwritten - temp.movementSpeed = EstimateMovementSpeed(goal, temp, targetTransform, Time.fixedDeltaTime); - - // reassign start wisely - // first ever data point? then make something up for previous one so that we can start interpolation without waiting for next. - if (start.timeStamp == 0) - { - start = new DataPoint - { - timeStamp = Time.time - Time.fixedDeltaTime, - // local position/rotation for VR support - localPosition = targetTransform.localPosition, - localRotation = targetTransform.localRotation, - localScale = targetTransform.localScale, - movementSpeed = temp.movementSpeed - }; - } - // second or nth data point? then update previous - // but: we start at where ever we are right now, so that it's perfectly smooth and we don't jump anywhere - // - // example if we are at 'x': - // - // A--x->B - // - // and then receive a new point C: - // - // A--x--B - // | - // | - // C - // - // then we don't want to just jump to B and start interpolation: - // - // x - // | - // | - // C - // - // we stay at 'x' and interpolate from there to C: - // - // x..B - // \ . - // \. - // C - // - else - { - float oldDistance = Vector3.Distance(start.localPosition, goal.localPosition); - float newDistance = Vector3.Distance(goal.localPosition, temp.localPosition); - - start = goal; - - // local position/rotation for VR support - // teleport / lag / obstacle detection: only continue at current position if we aren't too far away - // XC < AB + BC (see comments above) - if (Vector3.Distance(targetTransform.localPosition, start.localPosition) < oldDistance + newDistance) - { - start.localPosition = targetTransform.localPosition; - start.localRotation = targetTransform.localRotation; - start.localScale = targetTransform.localScale; - } - } - - // set new destination in any case. new data is best data. - goal = temp; - } - - // try to estimate movement speed for a data point based on how far it moved since the previous one - // - if this is the first time ever then we use our best guess: - // - delta based on transform.localPosition - // - elapsed based on send interval hoping that it roughly matches - static float EstimateMovementSpeed(DataPoint from, DataPoint to, Transform transform, float sendInterval) - { - Vector3 delta = to.localPosition - (from.localPosition != transform.localPosition ? from.localPosition : transform.localPosition); - float elapsed = from.isValid ? to.timeStamp - from.timeStamp : sendInterval; - - // avoid NaN - return elapsed > 0 ? delta.magnitude / elapsed : 0; - } - - // set position carefully depending on the target component - void ApplyPositionRotationScale(Vector3 position, Quaternion rotation, Vector3 scale) - { - // local position/rotation for VR support - if (syncPosition) targetTransform.localPosition = position; - if (syncRotation) targetTransform.localRotation = rotation; - if (syncScale) targetTransform.localScale = scale; - } - - // where are we in the timeline between start and goal? [0,1] - Vector3 InterpolatePosition(DataPoint start, DataPoint goal, Vector3 currentPosition) - { - if (!interpolatePosition) - return currentPosition; - - if (start.movementSpeed != 0) - { - // Option 1: simply interpolate based on time, but stutter will happen, it's not that smooth. - // This is especially noticeable if the camera automatically follows the player - // - Tell SonarCloud this isn't really commented code but actual comments and to stfu about it - // - float t = CurrentInterpolationFactor(); - // - return Vector3.Lerp(start.position, goal.position, t); - - // Option 2: always += speed - // speed is 0 if we just started after idle, so always use max for best results - float speed = Mathf.Max(start.movementSpeed, goal.movementSpeed); - return Vector3.MoveTowards(currentPosition, goal.localPosition, speed * Time.deltaTime); - } - - return currentPosition; - } - - Quaternion InterpolateRotation(DataPoint start, DataPoint goal, Quaternion defaultRotation) - { - if (!interpolateRotation) - return defaultRotation; - - if (start.localRotation != goal.localRotation) - { - float t = CurrentInterpolationFactor(start, goal); - return Quaternion.Slerp(start.localRotation, goal.localRotation, t); - } - - return defaultRotation; - } - - Vector3 InterpolateScale(DataPoint start, DataPoint goal, Vector3 currentScale) - { - if (!interpolateScale) - return currentScale; - - if (start.localScale != goal.localScale) - { - float t = CurrentInterpolationFactor(start, goal); - return Vector3.Lerp(start.localScale, goal.localScale, t); - } - - return currentScale; - } - - static float CurrentInterpolationFactor(DataPoint start, DataPoint goal) - { - if (start.isValid) - { - float difference = goal.timeStamp - start.timeStamp; - - // the moment we get 'goal', 'start' is supposed to start, so elapsed time is based on: - float elapsed = Time.time - goal.timeStamp; - - // avoid NaN - return difference > 0 ? elapsed / difference : 1; - } - return 1; - } - - #region Server Teleport (force move player) - - /// - /// This method will override this GameObject's current Transform.localPosition to the specified Vector3 and update all clients. - /// NOTE: position must be in LOCAL space if the transform has a parent - /// - /// Where to teleport this GameObject - [Server] - public void ServerTeleport(Vector3 localPosition) - { - Quaternion localRotation = targetTransform.localRotation; - ServerTeleport(localPosition, localRotation); - } - - /// - /// This method will override this GameObject's current Transform.localPosition and Transform.localRotation - /// to the specified Vector3 and Quaternion and update all clients. - /// NOTE: localPosition must be in LOCAL space if the transform has a parent - /// NOTE: localRotation must be in LOCAL space if the transform has a parent - /// - /// Where to teleport this GameObject - /// Which rotation to set this GameObject - [Server] - public void ServerTeleport(Vector3 localPosition, Quaternion localRotation) - { - // To prevent applying the position updates received from client (if they have ClientAuth) while being teleported. - // clientAuthorityBeforeTeleport defaults to false when not teleporting, if it is true then it means that teleport - // was previously called but not finished therefore we should keep it as true so that 2nd teleport call doesn't clear authority - clientAuthorityBeforeTeleport = clientAuthority || clientAuthorityBeforeTeleport; - clientAuthority = false; - - DoTeleport(localPosition, localRotation); - - // tell all clients about new values - RpcTeleport(localPosition, Compression.CompressQuaternion(localRotation), clientAuthorityBeforeTeleport); - } - - void DoTeleport(Vector3 newLocalPosition, Quaternion newLocalRotation) - { - targetTransform.localPosition = newLocalPosition; - targetTransform.localRotation = newLocalRotation; - - // Since we are overriding the position we don't need a goal and start. - // Reset them to null for fresh start - goal = new DataPoint(); - start = new DataPoint(); - lastPosition = newLocalPosition; - lastRotation = newLocalRotation; - } - - [ClientRpc(channel = Channels.Unreliable)] - void RpcTeleport(Vector3 newPosition, uint newPackedRotation, bool isClientAuthority) - { - DoTeleport(newPosition, Compression.DecompressQuaternion(newPackedRotation)); - - // only send finished if is owner and is ClientAuthority on server - if (hasAuthority && isClientAuthority) - CmdTeleportFinished(); - } - - /// - /// This RPC will be invoked on server after client finishes overriding the position. - /// - /// - [Command(channel = Channels.Unreliable)] - void CmdTeleportFinished() - { - if (clientAuthorityBeforeTeleport) - { - clientAuthority = true; - - // reset value so doesn't effect future calls, see note in ServerTeleport - clientAuthorityBeforeTeleport = false; - } - else - { - Debug.LogWarning("Client called TeleportFinished when clientAuthority was false on server", this); - } - } - - #endregion - - #region Debug Gizmos - - // draw the data points for easier debugging - void OnDrawGizmos() - { - // draw start and goal points and a line between them - if (start.localPosition != goal.localPosition) - { - DrawDataPointGizmo(start, Color.yellow); - DrawDataPointGizmo(goal, Color.green); - DrawLineBetweenDataPoints(start, goal, Color.cyan); - } - } - - static void DrawDataPointGizmo(DataPoint data, Color color) - { - // use a little offset because transform.localPosition might be in the ground in many cases - Vector3 offset = Vector3.up * 0.01f; - - // draw position - Gizmos.color = color; - Gizmos.DrawSphere(data.localPosition + offset, 0.5f); - - // draw forward and up like unity move tool - Gizmos.color = Color.blue; - Gizmos.DrawRay(data.localPosition + offset, data.localRotation * Vector3.forward); - Gizmos.color = Color.green; - Gizmos.DrawRay(data.localPosition + offset, data.localRotation * Vector3.up); - } - - static void DrawLineBetweenDataPoints(DataPoint data1, DataPoint data2, Color color) - { - Gizmos.color = color; - Gizmos.DrawLine(data1.localPosition, data2.localPosition); - } - - #endregion - } -} diff --git a/UnityProject/Assets/Mirror/Components/Experimental/NetworkTransformBase.cs.meta b/UnityProject/Assets/Mirror/Components/Experimental/NetworkTransformBase.cs.meta deleted file mode 100644 index adf3755..0000000 --- a/UnityProject/Assets/Mirror/Components/Experimental/NetworkTransformBase.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ea7c690c4fbf8c4439726f4c62eda6d3 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Components/Experimental/NetworkTransformChild.cs b/UnityProject/Assets/Mirror/Components/Experimental/NetworkTransformChild.cs deleted file mode 100644 index 44c49bd..0000000 --- a/UnityProject/Assets/Mirror/Components/Experimental/NetworkTransformChild.cs +++ /dev/null @@ -1,18 +0,0 @@ -using UnityEngine; - -namespace Mirror.Experimental -{ - /// - /// A component to synchronize the position of child transforms of networked objects. - /// There must be a NetworkTransform on the root object of the hierarchy. There can be multiple NetworkTransformChild components on an object. This does not use physics for synchronization, it simply synchronizes the localPosition and localRotation of the child transform and lerps towards the received values. - /// - [AddComponentMenu("Network/Experimental/NetworkTransformChildExperimentalExperimental")] - [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-transform-child")] - public class NetworkTransformChild : NetworkTransformBase - { - [Header("Target")] - public Transform target; - - protected override Transform targetTransform => target; - } -} diff --git a/UnityProject/Assets/Mirror/Components/GUIConsole.cs b/UnityProject/Assets/Mirror/Components/GUIConsole.cs index c738cae..c6acbb8 100644 --- a/UnityProject/Assets/Mirror/Components/GUIConsole.cs +++ b/UnityProject/Assets/Mirror/Components/GUIConsole.cs @@ -60,14 +60,17 @@ namespace Mirror void OnLog(string message, string stackTrace, LogType type) { // is this important? - bool isImportant = type == LogType.Error || type == LogType.Exception; + // => always show exceptions & errors + // => usually a good idea to show warnings too, otherwise it's too + // easy to miss OnDeserialize warnings etc. in builds + bool isImportant = type == LogType.Error || type == LogType.Exception || type == LogType.Warning; // use stack trace only if important // (otherwise users would have to find and search the log file. // seeing it in the console directly is way easier to deal with.) // => only add \n if stack trace is available (only in debug builds) if (isImportant && !string.IsNullOrWhiteSpace(stackTrace)) - message += "\n" + stackTrace; + message += $"\n{stackTrace}"; // add to queue log.Enqueue(new LogEntry(message, type)); diff --git a/UnityProject/Assets/Mirror/Components/GUIConsole.cs.meta b/UnityProject/Assets/Mirror/Components/GUIConsole.cs.meta index 933dd05..5664216 100644 --- a/UnityProject/Assets/Mirror/Components/GUIConsole.cs.meta +++ b/UnityProject/Assets/Mirror/Components/GUIConsole.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement.meta b/UnityProject/Assets/Mirror/Components/InterestManagement.meta index 1b7a972..9b1f746 100644 --- a/UnityProject/Assets/Mirror/Components/InterestManagement.meta +++ b/UnityProject/Assets/Mirror/Components/InterestManagement.meta @@ -3,6 +3,6 @@ guid: c66f27e006ab94253b39a55a3b213651 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/Distance.meta b/UnityProject/Assets/Mirror/Components/InterestManagement/Distance.meta index 832357e..9847902 100644 --- a/UnityProject/Assets/Mirror/Components/InterestManagement/Distance.meta +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/Distance.meta @@ -1,3 +1,3 @@ -fileFormatVersion: 2 +fileFormatVersion: 2 guid: fa4cbc6b9c584db4971985cb9f369077 -timeCreated: 1613110605 +timeCreated: 1613110605 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/Distance/DistanceInterestManagement.cs b/UnityProject/Assets/Mirror/Components/InterestManagement/Distance/DistanceInterestManagement.cs index 6aa8d7d..116051b 100644 --- a/UnityProject/Assets/Mirror/Components/InterestManagement/Distance/DistanceInterestManagement.cs +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/Distance/DistanceInterestManagement.cs @@ -4,23 +4,38 @@ using UnityEngine; namespace Mirror { + [AddComponentMenu("Network/ Interest Management/ Distance/Distance Interest Management")] public class DistanceInterestManagement : InterestManagement { - [Tooltip("The maximum range that objects will be visible at.")] + [Tooltip("The maximum range that objects will be visible at. Add DistanceInterestManagementCustomRange onto NetworkIdentities for custom ranges.")] public int visRange = 10; [Tooltip("Rebuild all every 'rebuildInterval' seconds.")] public float rebuildInterval = 1; double lastRebuildTime; - public override bool OnCheckObserver(NetworkIdentity identity, NetworkConnection newObserver) + // helper function to get vis range for a given object, or default. + int GetVisRange(NetworkIdentity identity) { - return Vector3.Distance(identity.transform.position, newObserver.identity.transform.position) <= visRange; + return identity.TryGetComponent(out DistanceInterestManagementCustomRange custom) ? custom.visRange : visRange; } - public override void OnRebuildObservers(NetworkIdentity identity, HashSet newObservers, bool initialize) + [ServerCallback] + public override void Reset() { - // 'transform.' calls GetComponent, only do it once + lastRebuildTime = 0D; + } + + public override bool OnCheckObserver(NetworkIdentity identity, NetworkConnectionToClient newObserver) + { + int range = GetVisRange(identity); + return Vector3.Distance(identity.transform.position, newObserver.identity.transform.position) < range; + } + + public override void OnRebuildObservers(NetworkIdentity identity, HashSet newObservers) + { + // cache range and .transform because both call GetComponent. + int range = GetVisRange(identity); Vector3 position = identity.transform.position; // brute force distance check @@ -36,7 +51,7 @@ namespace Mirror if (conn != null && conn.isAuthenticated && conn.identity != null) { // check distance - if (Vector3.Distance(conn.identity.transform.position, position) < visRange) + if (Vector3.Distance(conn.identity.transform.position, position) < range) { newObservers.Add(conn); } @@ -44,16 +59,15 @@ namespace Mirror } } - void Update() + // internal so we can update from tests + [ServerCallback] + internal void Update() { - // only on server - if (!NetworkServer.active) return; - // rebuild all spawned NetworkIdentity's observers every interval - if (NetworkTime.time >= lastRebuildTime + rebuildInterval) + if (NetworkTime.localTime >= lastRebuildTime + rebuildInterval) { RebuildAll(); - lastRebuildTime = NetworkTime.time; + lastRebuildTime = NetworkTime.localTime; } } } diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/Distance/DistanceInterestManagement.cs.meta b/UnityProject/Assets/Mirror/Components/InterestManagement/Distance/DistanceInterestManagement.cs.meta index 6adbd65..1a575af 100644 --- a/UnityProject/Assets/Mirror/Components/InterestManagement/Distance/DistanceInterestManagement.cs.meta +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/Distance/DistanceInterestManagement.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/Distance/DistanceInterestManagementCustomRange.cs b/UnityProject/Assets/Mirror/Components/InterestManagement/Distance/DistanceInterestManagementCustomRange.cs new file mode 100644 index 0000000..25f5347 --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/Distance/DistanceInterestManagementCustomRange.cs @@ -0,0 +1,15 @@ +// add this to NetworkIdentities for custom range if needed. +// only works with DistanceInterestManagement. +using UnityEngine; + +namespace Mirror +{ + [DisallowMultipleComponent] + [AddComponentMenu("Network/ Interest Management/ Distance/Distance Custom Range")] + [HelpURL("https://mirror-networking.gitbook.io/docs/guides/interest-management")] + public class DistanceInterestManagementCustomRange : NetworkBehaviour + { + [Tooltip("The maximum range that objects will be visible at.")] + public int visRange = 20; + } +} diff --git a/UnityProject/Assets/Mirror/Components/Experimental/NetworkTransform.cs.meta b/UnityProject/Assets/Mirror/Components/InterestManagement/Distance/DistanceInterestManagementCustomRange.cs.meta similarity index 66% rename from UnityProject/Assets/Mirror/Components/Experimental/NetworkTransform.cs.meta rename to UnityProject/Assets/Mirror/Components/InterestManagement/Distance/DistanceInterestManagementCustomRange.cs.meta index 412dcbf..406ca78 100644 --- a/UnityProject/Assets/Mirror/Components/Experimental/NetworkTransform.cs.meta +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/Distance/DistanceInterestManagementCustomRange.cs.meta @@ -1,11 +1,11 @@ fileFormatVersion: 2 -guid: 741bbe11f5357b44593b15c0d11b16bd +guid: b2e242ee38a14076a39934172a19079b MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/Match.meta b/UnityProject/Assets/Mirror/Components/InterestManagement/Match.meta new file mode 100644 index 0000000..e429883 --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/Match.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5eca5245ae6bb460e9a92f7e14d5493a +timeCreated: 1622649517 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/Match/MatchInterestManagement.cs b/UnityProject/Assets/Mirror/Components/InterestManagement/Match/MatchInterestManagement.cs new file mode 100644 index 0000000..e0b7726 --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/Match/MatchInterestManagement.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Mirror +{ + [AddComponentMenu("Network/ Interest Management/ Match/Match Interest Management")] + public class MatchInterestManagement : InterestManagement + { + readonly Dictionary> matchObjects = + new Dictionary>(); + + readonly Dictionary lastObjectMatch = + new Dictionary(); + + readonly HashSet dirtyMatches = new HashSet(); + + public override void OnSpawned(NetworkIdentity identity) + { + if (!identity.TryGetComponent(out NetworkMatch networkMatch)) + return; + + Guid currentMatch = networkMatch.matchId; + lastObjectMatch[identity] = currentMatch; + + // Guid.Empty is never a valid matchId...do not add to matchObjects collection + if (currentMatch == Guid.Empty) + return; + + // Debug.Log($"MatchInterestManagement.OnSpawned({identity.name}) currentMatch: {currentMatch}"); + if (!matchObjects.TryGetValue(currentMatch, out HashSet objects)) + { + objects = new HashSet(); + matchObjects.Add(currentMatch, objects); + } + + objects.Add(identity); + } + + public override void OnDestroyed(NetworkIdentity identity) + { + lastObjectMatch.TryGetValue(identity, out Guid currentMatch); + lastObjectMatch.Remove(identity); + if (currentMatch != Guid.Empty && matchObjects.TryGetValue(currentMatch, out HashSet objects) && objects.Remove(identity)) + RebuildMatchObservers(currentMatch); + } + + // internal so we can update from tests + [ServerCallback] + internal void Update() + { + // for each spawned: + // if match changed: + // add previous to dirty + // add new to dirty + foreach (NetworkIdentity netIdentity in NetworkServer.spawned.Values) + { + // Ignore objects that don't have a NetworkMatch component + if (!netIdentity.TryGetComponent(out NetworkMatch networkMatch)) + continue; + + Guid newMatch = networkMatch.matchId; + lastObjectMatch.TryGetValue(netIdentity, out Guid currentMatch); + + // Guid.Empty is never a valid matchId + // Nothing to do if matchId hasn't changed + if (newMatch == Guid.Empty || newMatch == currentMatch) + continue; + + // Mark new/old matches as dirty so they get rebuilt + UpdateDirtyMatches(newMatch, currentMatch); + + // This object is in a new match so observers in the prior match + // and the new match need to rebuild their respective observers lists. + UpdateMatchObjects(netIdentity, newMatch, currentMatch); + } + + // rebuild all dirty matches + foreach (Guid dirtyMatch in dirtyMatches) + RebuildMatchObservers(dirtyMatch); + + dirtyMatches.Clear(); + } + + void UpdateDirtyMatches(Guid newMatch, Guid currentMatch) + { + // Guid.Empty is never a valid matchId + if (currentMatch != Guid.Empty) + dirtyMatches.Add(currentMatch); + + dirtyMatches.Add(newMatch); + } + + void UpdateMatchObjects(NetworkIdentity netIdentity, Guid newMatch, Guid currentMatch) + { + // Remove this object from the hashset of the match it just left + // Guid.Empty is never a valid matchId + if (currentMatch != Guid.Empty) + matchObjects[currentMatch].Remove(netIdentity); + + // Set this to the new match this object just entered + lastObjectMatch[netIdentity] = newMatch; + + // Make sure this new match is in the dictionary + if (!matchObjects.ContainsKey(newMatch)) + matchObjects.Add(newMatch, new HashSet()); + + // Add this object to the hashset of the new match + matchObjects[newMatch].Add(netIdentity); + } + + void RebuildMatchObservers(Guid matchId) + { + foreach (NetworkIdentity netIdentity in matchObjects[matchId]) + if (netIdentity != null) + NetworkServer.RebuildObservers(netIdentity, false); + } + + public override bool OnCheckObserver(NetworkIdentity identity, NetworkConnectionToClient newObserver) + { + // Never observed if no NetworkMatch component + if (!identity.TryGetComponent(out NetworkMatch identityNetworkMatch)) + return false; + + // Guid.Empty is never a valid matchId + if (identityNetworkMatch.matchId == Guid.Empty) + return false; + + // Never observed if no NetworkMatch component + if (!newObserver.identity.TryGetComponent(out NetworkMatch newObserverNetworkMatch)) + return false; + + // Guid.Empty is never a valid matchId + if (newObserverNetworkMatch.matchId == Guid.Empty) + return false; + + return identityNetworkMatch.matchId == newObserverNetworkMatch.matchId; + } + + public override void OnRebuildObservers(NetworkIdentity identity, HashSet newObservers) + { + if (!identity.TryGetComponent(out NetworkMatch networkMatch)) + return; + + Guid matchId = networkMatch.matchId; + + // Guid.Empty is never a valid matchId + if (matchId == Guid.Empty) + return; + + if (!matchObjects.TryGetValue(matchId, out HashSet objects)) + return; + + // Add everything in the hashset for this object's current match + foreach (NetworkIdentity networkIdentity in objects) + if (networkIdentity != null && networkIdentity.connectionToClient != null) + newObservers.Add(networkIdentity.connectionToClient); + } + } +} diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/Match/MatchInterestManagement.cs.meta b/UnityProject/Assets/Mirror/Components/InterestManagement/Match/MatchInterestManagement.cs.meta new file mode 100644 index 0000000..605d215 --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/Match/MatchInterestManagement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d09f5c8bf2f4747b7a9284ef5d9ce2a7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/Match/NetworkMatch.cs b/UnityProject/Assets/Mirror/Components/InterestManagement/Match/NetworkMatch.cs new file mode 100644 index 0000000..f6689f2 --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/Match/NetworkMatch.cs @@ -0,0 +1,15 @@ +// simple component that holds match information +using System; +using UnityEngine; + +namespace Mirror +{ + [DisallowMultipleComponent] + [AddComponentMenu("Network/ Interest Management/ Match/Network Match")] + [HelpURL("https://mirror-networking.gitbook.io/docs/guides/interest-management")] + public class NetworkMatch : NetworkBehaviour + { + ///Set this to the same value on all networked objects that belong to a given match + public Guid matchId; + } +} diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/Match/NetworkMatch.cs.meta b/UnityProject/Assets/Mirror/Components/InterestManagement/Match/NetworkMatch.cs.meta new file mode 100644 index 0000000..47fc70a --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/Match/NetworkMatch.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5d17e718851449a6879986e45c458fb7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/Scene.meta b/UnityProject/Assets/Mirror/Components/InterestManagement/Scene.meta new file mode 100644 index 0000000..28b469f --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/Scene.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7655d309a46a4bd4860edf964228b3f6 +timeCreated: 1622649517 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/Scene/SceneInterestManagement.cs b/UnityProject/Assets/Mirror/Components/InterestManagement/Scene/SceneInterestManagement.cs new file mode 100644 index 0000000..8cbfa3b --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/Scene/SceneInterestManagement.cs @@ -0,0 +1,108 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace Mirror +{ + [AddComponentMenu("Network/ Interest Management/ Scene/Scene Interest Management")] + public class SceneInterestManagement : InterestManagement + { + // Use Scene instead of string scene.name because when additively + // loading multiples of a subscene the name won't be unique + readonly Dictionary> sceneObjects = + new Dictionary>(); + + readonly Dictionary lastObjectScene = + new Dictionary(); + + HashSet dirtyScenes = new HashSet(); + + public override void OnSpawned(NetworkIdentity identity) + { + Scene currentScene = identity.gameObject.scene; + lastObjectScene[identity] = currentScene; + // Debug.Log($"SceneInterestManagement.OnSpawned({identity.name}) currentScene: {currentScene}"); + if (!sceneObjects.TryGetValue(currentScene, out HashSet objects)) + { + objects = new HashSet(); + sceneObjects.Add(currentScene, objects); + } + + objects.Add(identity); + } + + public override void OnDestroyed(NetworkIdentity identity) + { + Scene currentScene = lastObjectScene[identity]; + lastObjectScene.Remove(identity); + if (sceneObjects.TryGetValue(currentScene, out HashSet objects) && objects.Remove(identity)) + RebuildSceneObservers(currentScene); + } + + // internal so we can update from tests + [ServerCallback] + internal void Update() + { + // for each spawned: + // if scene changed: + // add previous to dirty + // add new to dirty + foreach (NetworkIdentity identity in NetworkServer.spawned.Values) + { + Scene currentScene = lastObjectScene[identity]; + Scene newScene = identity.gameObject.scene; + if (newScene == currentScene) + continue; + + // Mark new/old scenes as dirty so they get rebuilt + dirtyScenes.Add(currentScene); + dirtyScenes.Add(newScene); + + // This object is in a new scene so observers in the prior scene + // and the new scene need to rebuild their respective observers lists. + + // Remove this object from the hashset of the scene it just left + sceneObjects[currentScene].Remove(identity); + + // Set this to the new scene this object just entered + lastObjectScene[identity] = newScene; + + // Make sure this new scene is in the dictionary + if (!sceneObjects.ContainsKey(newScene)) + sceneObjects.Add(newScene, new HashSet()); + + // Add this object to the hashset of the new scene + sceneObjects[newScene].Add(identity); + } + + // rebuild all dirty scenes + foreach (Scene dirtyScene in dirtyScenes) + RebuildSceneObservers(dirtyScene); + + dirtyScenes.Clear(); + } + + void RebuildSceneObservers(Scene scene) + { + foreach (NetworkIdentity netIdentity in sceneObjects[scene]) + if (netIdentity != null) + NetworkServer.RebuildObservers(netIdentity, false); + } + + public override bool OnCheckObserver(NetworkIdentity identity, NetworkConnectionToClient newObserver) + { + return identity.gameObject.scene == newObserver.identity.gameObject.scene; + } + + public override void OnRebuildObservers(NetworkIdentity identity, HashSet newObservers) + { + if (!sceneObjects.TryGetValue(identity.gameObject.scene, out HashSet objects)) + return; + + // Add everything in the hashset for this object's current scene + foreach (NetworkIdentity networkIdentity in objects) + if (networkIdentity != null && networkIdentity.connectionToClient != null) + newObservers.Add(networkIdentity.connectionToClient); + } + } +} diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/Scene/SceneInterestManagement.cs.meta b/UnityProject/Assets/Mirror/Components/InterestManagement/Scene/SceneInterestManagement.cs.meta new file mode 100644 index 0000000..9cc1ff5 --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/Scene/SceneInterestManagement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b979f26c95d34324ba005bfacfa9c4fc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/SpatialHashing.meta b/UnityProject/Assets/Mirror/Components/InterestManagement/SpatialHashing.meta index 6a35e1e..00f5cd6 100644 --- a/UnityProject/Assets/Mirror/Components/InterestManagement/SpatialHashing.meta +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/SpatialHashing.meta @@ -1,3 +1,3 @@ -fileFormatVersion: 2 +fileFormatVersion: 2 guid: cfa12b73503344d49b398b01bcb07967 -timeCreated: 1613110634 +timeCreated: 1613110634 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/SpatialHashing/Grid2D.cs.meta b/UnityProject/Assets/Mirror/Components/InterestManagement/SpatialHashing/Grid2D.cs.meta index 4448994..f1d3cf0 100644 --- a/UnityProject/Assets/Mirror/Components/InterestManagement/SpatialHashing/Grid2D.cs.meta +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/SpatialHashing/Grid2D.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/SpatialHashing/SpatialHashingInterestManagement.cs b/UnityProject/Assets/Mirror/Components/InterestManagement/SpatialHashing/SpatialHashingInterestManagement.cs index 4761f7a..2f78e72 100644 --- a/UnityProject/Assets/Mirror/Components/InterestManagement/SpatialHashing/SpatialHashingInterestManagement.cs +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/SpatialHashing/SpatialHashingInterestManagement.cs @@ -6,13 +6,23 @@ using UnityEngine; namespace Mirror { + [AddComponentMenu("Network/ Interest Management/ Spatial Hash/Spatial Hashing Interest Management")] public class SpatialHashingInterestManagement : InterestManagement { [Tooltip("The maximum range that objects will be visible at.")] public int visRange = 30; - // if we see 8 neighbors then 1 entry is visRange/3 - public int resolution => visRange / 3; + // we use a 9 neighbour grid. + // so we always see in a distance of 2 grids. + // for example, our own grid and then one on top / below / left / right. + // + // this means that grid resolution needs to be distance / 2. + // so for example, for distance = 30 we see 2 cells = 15 * 2 distance. + // + // on first sight, it seems we need distance / 3 (we see left/us/right). + // but that's not the case. + // resolution would be 10, and we only see 1 cell far, so 10+10=20. + public int resolution => visRange / 2; [Tooltip("Rebuild all every 'rebuildInterval' seconds.")] public float rebuildInterval = 1; @@ -30,7 +40,7 @@ namespace Mirror public bool showSlider; // the grid - Grid2D grid = new Grid2D(); + Grid2D grid = new Grid2D(); // project 3d world position to grid position Vector2Int ProjectToGrid(Vector3 position) => @@ -38,7 +48,7 @@ namespace Mirror ? Vector2Int.RoundToInt(new Vector2(position.x, position.z) / resolution) : Vector2Int.RoundToInt(new Vector2(position.x, position.y) / resolution); - public override bool OnCheckObserver(NetworkIdentity identity, NetworkConnection newObserver) + public override bool OnCheckObserver(NetworkIdentity identity, NetworkConnectionToClient newObserver) { // calculate projected positions Vector2Int projected = ProjectToGrid(identity.transform.position); @@ -51,7 +61,7 @@ namespace Mirror return (projected - observerProjected).sqrMagnitude <= 2; } - public override void OnRebuildObservers(NetworkIdentity identity, HashSet newObservers, bool initialize) + public override void OnRebuildObservers(NetworkIdentity identity, HashSet newObservers) { // add everyone in 9 neighbour grid // -> pass observers to GetWithNeighbours directly to avoid allocations @@ -60,12 +70,19 @@ namespace Mirror grid.GetWithNeighbours(current, newObservers); } + [ServerCallback] + public override void Reset() + { + lastRebuildTime = 0D; + } + // update everyone's position in the grid // (internal so we can update from tests) + [ServerCallback] internal void Update() { - // only on server - if (!NetworkServer.active) return; + // NOTE: unlike Scene/MatchInterestManagement, this rebuilds ALL + // entities every INTERVAL. consider the other approach later. // IMPORTANT: refresh grid every update! // => newly spawned entities get observers assigned via @@ -103,13 +120,15 @@ namespace Mirror // rebuild all spawned entities' observers every 'interval' // this will call OnRebuildObservers which then returns the // observers at grid[position] for each entity. - if (NetworkTime.time >= lastRebuildTime + rebuildInterval) + if (NetworkTime.localTime >= lastRebuildTime + rebuildInterval) { RebuildAll(); - lastRebuildTime = NetworkTime.time; + lastRebuildTime = NetworkTime.localTime; } } +// OnGUI allocates even if it does nothing. avoid in release. +#if UNITY_EDITOR || DEVELOPMENT_BUILD // slider from dotsnet. it's nice to play around with in the benchmark // demo. void OnGUI() @@ -129,5 +148,6 @@ namespace Mirror GUILayout.EndHorizontal(); GUILayout.EndArea(); } +#endif } } diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/SpatialHashing/SpatialHashingInterestManagement.cs.meta b/UnityProject/Assets/Mirror/Components/InterestManagement/SpatialHashing/SpatialHashingInterestManagement.cs.meta index 5f5007e..271e433 100644 --- a/UnityProject/Assets/Mirror/Components/InterestManagement/SpatialHashing/SpatialHashingInterestManagement.cs.meta +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/SpatialHashing/SpatialHashingInterestManagement.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Editor.meta b/UnityProject/Assets/Mirror/Components/InterestManagement/Team.meta similarity index 77% rename from UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Editor.meta rename to UnityProject/Assets/Mirror/Components/InterestManagement/Team.meta index 40c6fd2..fe40aa4 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Editor.meta +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/Team.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 0995e08af14888348b42ecaa6eb21544 +guid: 2d418e60072433b4bbebbf5f3a7de1bb folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/Team/NetworkTeam.cs b/UnityProject/Assets/Mirror/Components/InterestManagement/Team/NetworkTeam.cs new file mode 100644 index 0000000..e6033ad --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/Team/NetworkTeam.cs @@ -0,0 +1,17 @@ +// simple component that holds team information +using UnityEngine; + +namespace Mirror +{ + [DisallowMultipleComponent] + [AddComponentMenu("Network/ Interest Management/ Team/Network Team")] + [HelpURL("https://mirror-networking.gitbook.io/docs/guides/interest-management")] + public class NetworkTeam : NetworkBehaviour + { + [Tooltip("Set this to the same value on all networked objects that belong to a given team")] + public string teamId = string.Empty; + + [Tooltip("When enabled this object is visible to all clients. Typically this would be true for player objects")] + public bool forceShown; + } +} diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/Team/NetworkTeam.cs.meta b/UnityProject/Assets/Mirror/Components/InterestManagement/Team/NetworkTeam.cs.meta new file mode 100644 index 0000000..ca75a7a --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/Team/NetworkTeam.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2576730625b1632468cbcbfe5e721f88 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/Team/TeamInterestManagement.cs b/UnityProject/Assets/Mirror/Components/InterestManagement/Team/TeamInterestManagement.cs new file mode 100644 index 0000000..22b4ace --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/Team/TeamInterestManagement.cs @@ -0,0 +1,191 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace Mirror +{ + [AddComponentMenu("Network/ Interest Management/ Team/Team Interest Management")] + public class TeamInterestManagement : InterestManagement + { + readonly Dictionary> teamObjects = + new Dictionary>(); + + readonly Dictionary lastObjectTeam = + new Dictionary(); + + readonly HashSet dirtyTeams = new HashSet(); + + public override void OnSpawned(NetworkIdentity identity) + { + if (!identity.TryGetComponent(out NetworkTeam networkTeam)) + return; + + string currentTeam = networkTeam.teamId; + lastObjectTeam[identity] = currentTeam; + + // Null / Empty string is never a valid teamId...do not add to teamObjects collection + if (string.IsNullOrWhiteSpace(currentTeam)) + return; + + // Debug.Log($"MatchInterestManagement.OnSpawned({identity.name}) currentMatch: {currentTeam}"); + if (!teamObjects.TryGetValue(currentTeam, out HashSet objects)) + { + objects = new HashSet(); + teamObjects.Add(currentTeam, objects); + } + + objects.Add(identity); + } + + public override void OnDestroyed(NetworkIdentity identity) + { + if (lastObjectTeam.TryGetValue(identity, out string currentTeam)) + { + lastObjectTeam.Remove(identity); + if (!string.IsNullOrWhiteSpace(currentTeam) + && teamObjects.TryGetValue(currentTeam, out HashSet objects) + && objects.Remove(identity)) + RebuildTeamObservers(currentTeam); + } + } + + // internal so we can update from tests + [ServerCallback] + internal void Update() + { + // for each spawned: + // if team changed: + // add previous to dirty + // add new to dirty + foreach (NetworkIdentity netIdentity in NetworkServer.spawned.Values) + { + // Ignore objects that don't have a NetworkTeam component + if (!netIdentity.TryGetComponent(out NetworkTeam networkTeam)) + continue; + + string newTeam = networkTeam.teamId; + if (!lastObjectTeam.TryGetValue(netIdentity, out string currentTeam)) + continue; + + // Null / Empty string is never a valid teamId + // Nothing to do if teamId hasn't changed + if (string.IsNullOrWhiteSpace(newTeam) || newTeam == currentTeam) + continue; + + // Mark new/old Teams as dirty so they get rebuilt + UpdateDirtyTeams(newTeam, currentTeam); + + // This object is in a new team so observers in the prior team + // and the new team need to rebuild their respective observers lists. + UpdateTeamObjects(netIdentity, newTeam, currentTeam); + } + + // rebuild all dirty teams + foreach (string dirtyTeam in dirtyTeams) + RebuildTeamObservers(dirtyTeam); + + dirtyTeams.Clear(); + } + + void UpdateDirtyTeams(string newTeam, string currentTeam) + { + // Null / Empty string is never a valid teamId + if (!string.IsNullOrWhiteSpace(currentTeam)) + dirtyTeams.Add(currentTeam); + + dirtyTeams.Add(newTeam); + } + + void UpdateTeamObjects(NetworkIdentity netIdentity, string newTeam, string currentTeam) + { + // Remove this object from the hashset of the team it just left + // string.Empty is never a valid teamId + if (!string.IsNullOrWhiteSpace(currentTeam)) + teamObjects[currentTeam].Remove(netIdentity); + + // Set this to the new team this object just entered + lastObjectTeam[netIdentity] = newTeam; + + // Make sure this new team is in the dictionary + if (!teamObjects.ContainsKey(newTeam)) + teamObjects.Add(newTeam, new HashSet()); + + // Add this object to the hashset of the new team + teamObjects[newTeam].Add(netIdentity); + } + + void RebuildTeamObservers(string teamId) + { + foreach (NetworkIdentity netIdentity in teamObjects[teamId]) + if (netIdentity != null) + NetworkServer.RebuildObservers(netIdentity, false); + } + + public override bool OnCheckObserver(NetworkIdentity identity, NetworkConnectionToClient newObserver) + { + // Always observed if no NetworkTeam component + if (!identity.TryGetComponent(out NetworkTeam identityNetworkTeam)) + return true; + + if (identityNetworkTeam.forceShown) + return true; + + // string.Empty is never a valid teamId + if (string.IsNullOrWhiteSpace(identityNetworkTeam.teamId)) + return false; + + // Always observed if no NetworkTeam component + if (!newObserver.identity.TryGetComponent(out NetworkTeam newObserverNetworkTeam)) + return true; + + if (newObserverNetworkTeam.forceShown) + return true; + + // Null / Empty string is never a valid teamId + if (string.IsNullOrWhiteSpace(newObserverNetworkTeam.teamId)) + return false; + + // Observed only if teamId's match + return identityNetworkTeam.teamId == newObserverNetworkTeam.teamId; + } + + public override void OnRebuildObservers(NetworkIdentity identity, HashSet newObservers) + { + // If this object doesn't have a NetworkTeam then it's visible to all clients + if (!identity.TryGetComponent(out NetworkTeam networkTeam)) + { + AddAllConnections(newObservers); + return; + } + + // If this object has NetworkTeam and forceShown == true then it's visible to all clients + if (networkTeam.forceShown) + { + AddAllConnections(newObservers); + return; + } + + // Null / Empty string is never a valid teamId + if (string.IsNullOrWhiteSpace(networkTeam.teamId)) + return; + + // Abort if this team hasn't been created yet by OnSpawned or UpdateTeamObjects + if (!teamObjects.TryGetValue(networkTeam.teamId, out HashSet objects)) + return; + + // Add everything in the hashset for this object's current team + foreach (NetworkIdentity networkIdentity in objects) + if (networkIdentity != null && networkIdentity.connectionToClient != null) + newObservers.Add(networkIdentity.connectionToClient); + } + + void AddAllConnections(HashSet newObservers) + { + foreach (NetworkConnectionToClient conn in NetworkServer.connections.Values) + { + // authenticated and joined world with a player? + if (conn != null && conn.isAuthenticated && conn.identity != null) + newObservers.Add(conn); + } + } + } +} diff --git a/UnityProject/Assets/Mirror/Components/InterestManagement/Team/TeamInterestManagement.cs.meta b/UnityProject/Assets/Mirror/Components/InterestManagement/Team/TeamInterestManagement.cs.meta new file mode 100644 index 0000000..6e8748a --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/InterestManagement/Team/TeamInterestManagement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dceb9a7085758fd4590419ff5b14b636 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/Mirror.Components.asmdef.meta b/UnityProject/Assets/Mirror/Components/Mirror.Components.asmdef.meta index 59c31c8..263b6f0 100644 --- a/UnityProject/Assets/Mirror/Components/Mirror.Components.asmdef.meta +++ b/UnityProject/Assets/Mirror/Components/Mirror.Components.asmdef.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 72872094b21c16e48b631b2224833d49 AssemblyDefinitionImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/NetworkAnimator.cs b/UnityProject/Assets/Mirror/Components/NetworkAnimator.cs index a1d3be5..2360fe3 100644 --- a/UnityProject/Assets/Mirror/Components/NetworkAnimator.cs +++ b/UnityProject/Assets/Mirror/Components/NetworkAnimator.cs @@ -13,7 +13,7 @@ namespace Mirror /// If the object has authority on the server, then it should be animated on the server and state information will be sent to all clients. This is common for objects not related to a specific client, such as an enemy unit. /// The NetworkAnimator synchronizes all animation parameters of the selected Animator. It does not automatically synchronize triggers. The function SetTrigger can by used by an object with authority to fire an animation trigger on other clients. /// - [AddComponentMenu("Network/NetworkAnimator")] + [AddComponentMenu("Network/Network Animator")] [RequireComponent(typeof(NetworkIdentity))] [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-animator")] public class NetworkAnimator : NetworkBehaviour @@ -30,7 +30,6 @@ namespace Mirror [Tooltip("Animator that will have parameters synchronized")] public Animator animator; - /// /// Syncs animator.speed /// @@ -48,7 +47,7 @@ namespace Mirror int[] animationHash; int[] transitionHash; float[] layerWeight; - float nextSendTime; + double nextSendTime; bool SendMessagesAllowed { @@ -107,7 +106,7 @@ namespace Mirror continue; } - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + using (NetworkWriterPooled writer = NetworkWriterPool.Get()) { WriteParameters(writer); SendAnimationMessage(stateHash, normalizedTime, i, layerWeight[i], writer.ToArray()); @@ -134,13 +133,6 @@ namespace Mirror } } - void CmdSetAnimatorSpeed(float newSpeed) - { - // set animator - animator.speed = newSpeed; - animatorSpeed = newSpeed; - } - void OnAnimatorSpeedChanged(float _, float value) { // skip if host or client with authority @@ -196,12 +188,12 @@ namespace Mirror void CheckSendRate() { - float now = Time.time; + double now = NetworkTime.localTime; if (SendMessagesAllowed && syncInterval >= 0 && now > nextSendTime) { nextSendTime = now + syncInterval; - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + using (NetworkWriterPooled writer = NetworkWriterPool.Get()) { if (WriteParameters(writer)) SendAnimationParametersMessage(writer.ToArray()); @@ -532,10 +524,10 @@ namespace Mirror if (!clientAuthority) return; - // Debug.Log("OnAnimationMessage for netId=" + netId); + //Debug.Log($"OnAnimationMessage for netId {netId}"); // handle and broadcast - using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(parameters)) + using (NetworkReaderPooled networkReader = NetworkReaderPool.Get(parameters)) { HandleAnimMsg(stateHash, normalizedTime, layerId, weight, networkReader); RpcOnAnimationClientMessage(stateHash, normalizedTime, layerId, weight, parameters); @@ -550,7 +542,7 @@ namespace Mirror return; // handle and broadcast - using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(parameters)) + using (NetworkReaderPooled networkReader = NetworkReaderPool.Get(parameters)) { HandleAnimParamsMsg(networkReader); RpcOnAnimationParametersClientMessage(parameters); @@ -593,6 +585,14 @@ namespace Mirror RpcOnAnimationResetTriggerClientMessage(hash); } + [Command] + void CmdSetAnimatorSpeed(float newSpeed) + { + // set animator + animator.speed = newSpeed; + animatorSpeed = newSpeed; + } + #endregion #region client message handlers @@ -600,14 +600,14 @@ namespace Mirror [ClientRpc] void RpcOnAnimationClientMessage(int stateHash, float normalizedTime, int layerId, float weight, byte[] parameters) { - using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(parameters)) + using (NetworkReaderPooled networkReader = NetworkReaderPool.Get(parameters)) HandleAnimMsg(stateHash, normalizedTime, layerId, weight, networkReader); } [ClientRpc] void RpcOnAnimationParametersClientMessage(byte[] parameters) { - using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(parameters)) + using (NetworkReaderPooled networkReader = NetworkReaderPool.Get(parameters)) HandleAnimParamsMsg(networkReader); } diff --git a/UnityProject/Assets/Mirror/Components/NetworkAnimator.cs.meta b/UnityProject/Assets/Mirror/Components/NetworkAnimator.cs.meta index 7b0d8d1..211ce78 100644 --- a/UnityProject/Assets/Mirror/Components/NetworkAnimator.cs.meta +++ b/UnityProject/Assets/Mirror/Components/NetworkAnimator.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/NetworkLobbyManager.cs b/UnityProject/Assets/Mirror/Components/NetworkLobbyManager.cs index 6ea069a..3dcd4ea 100644 --- a/UnityProject/Assets/Mirror/Components/NetworkLobbyManager.cs +++ b/UnityProject/Assets/Mirror/Components/NetworkLobbyManager.cs @@ -11,7 +11,7 @@ namespace Mirror /// NetworkLobbyManager is derived from NetworkManager, and so it implements many of the virtual functions provided by the NetworkManager class. To avoid accidentally replacing functionality of the NetworkLobbyManager, there are new virtual functions on the NetworkLobbyManager that begin with "OnLobby". These should be used on classes derived from NetworkLobbyManager instead of the virtual functions on NetworkManager. /// The OnLobby*() functions have empty implementations on the NetworkLobbyManager base class, so the base class functions do not have to be called. /// - [AddComponentMenu("Network/NetworkLobbyManager")] + [AddComponentMenu("Network/Network Lobby Manager")] [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-room-manager")] [Obsolete("Use / inherit from NetworkRoomManager instead")] public class NetworkLobbyManager : NetworkRoomManager {} diff --git a/UnityProject/Assets/Mirror/Components/NetworkLobbyManager.cs.meta b/UnityProject/Assets/Mirror/Components/NetworkLobbyManager.cs.meta index 7fdd9ef..a32c8c7 100644 --- a/UnityProject/Assets/Mirror/Components/NetworkLobbyManager.cs.meta +++ b/UnityProject/Assets/Mirror/Components/NetworkLobbyManager.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/NetworkLobbyPlayer.cs b/UnityProject/Assets/Mirror/Components/NetworkLobbyPlayer.cs index cf18566..95ffbbc 100644 --- a/UnityProject/Assets/Mirror/Components/NetworkLobbyPlayer.cs +++ b/UnityProject/Assets/Mirror/Components/NetworkLobbyPlayer.cs @@ -8,7 +8,7 @@ namespace Mirror /// The LobbyPrefab object of the NetworkLobbyManager must have this component on it. This component holds basic lobby player data required for the lobby to function. Game specific data for lobby players can be put in other components on the LobbyPrefab or in scripts derived from NetworkLobbyPlayer. /// [DisallowMultipleComponent] - [AddComponentMenu("Network/NetworkLobbyPlayer")] + [AddComponentMenu("Network/Network Lobby Player")] [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-room-player")] [Obsolete("Use / inherit from NetworkRoomPlayer instead")] public class NetworkLobbyPlayer : NetworkRoomPlayer {} diff --git a/UnityProject/Assets/Mirror/Components/NetworkLobbyPlayer.cs.meta b/UnityProject/Assets/Mirror/Components/NetworkLobbyPlayer.cs.meta index 7dfc205..7a21eec 100644 --- a/UnityProject/Assets/Mirror/Components/NetworkLobbyPlayer.cs.meta +++ b/UnityProject/Assets/Mirror/Components/NetworkLobbyPlayer.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/NetworkMatchChecker.cs b/UnityProject/Assets/Mirror/Components/NetworkMatchChecker.cs deleted file mode 100644 index 3092b67..0000000 --- a/UnityProject/Assets/Mirror/Components/NetworkMatchChecker.cs +++ /dev/null @@ -1,144 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; - -namespace Mirror -{ - /// - /// Component that controls visibility of networked objects based on match id. - /// Any object with this component on it will only be visible to other objects in the same match. - /// This would be used to isolate players to their respective matches within a single game server instance. - /// - [Obsolete(NetworkVisibilityObsoleteMessage.Message)] - [DisallowMultipleComponent] - [AddComponentMenu("Network/NetworkMatchChecker")] - [RequireComponent(typeof(NetworkIdentity))] - [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-match-checker")] - public class NetworkMatchChecker : NetworkVisibility - { - // internal for tests - internal static readonly Dictionary> matchPlayers = - new Dictionary>(); - - // internal for tests - internal Guid currentMatch = Guid.Empty; - - [Header("Diagnostics")] - [SyncVar] - public string currentMatchDebug; - - /// - /// Set this to the same value on all networked objects that belong to a given match - /// - public Guid matchId - { - get { return currentMatch; } - set - { - if (currentMatch == value) return; - - // cache previous match so observers in that match can be rebuilt - Guid previousMatch = currentMatch; - - // Set this to the new match this object just entered ... - currentMatch = value; - // ... and copy the string for the inspector because Unity can't show Guid directly - currentMatchDebug = currentMatch.ToString(); - - if (previousMatch != Guid.Empty) - { - // Remove this object from the hashset of the match it just left - matchPlayers[previousMatch].Remove(netIdentity); - - // RebuildObservers of all NetworkIdentity's in the match this object just left - RebuildMatchObservers(previousMatch); - } - - if (currentMatch != Guid.Empty) - { - // Make sure this new match is in the dictionary - if (!matchPlayers.ContainsKey(currentMatch)) - matchPlayers.Add(currentMatch, new HashSet()); - - // Add this object to the hashset of the new match - matchPlayers[currentMatch].Add(netIdentity); - - // RebuildObservers of all NetworkIdentity's in the match this object just entered - RebuildMatchObservers(currentMatch); - } - else - { - // Not in any match now...RebuildObservers will clear and add self - netIdentity.RebuildObservers(false); - } - } - } - - public override void OnStartServer() - { - if (currentMatch == Guid.Empty) return; - - if (!matchPlayers.ContainsKey(currentMatch)) - matchPlayers.Add(currentMatch, new HashSet()); - - matchPlayers[currentMatch].Add(netIdentity); - - // No need to rebuild anything here. - // identity.RebuildObservers is called right after this from NetworkServer.SpawnObject - } - - public override void OnStopServer() - { - if (currentMatch == Guid.Empty) return; - - if (matchPlayers.ContainsKey(currentMatch) && matchPlayers[currentMatch].Remove(netIdentity)) - RebuildMatchObservers(currentMatch); - } - - void RebuildMatchObservers(Guid specificMatch) - { - foreach (NetworkIdentity networkIdentity in matchPlayers[specificMatch]) - if (networkIdentity != null) - networkIdentity.RebuildObservers(false); - } - - #region Observers - - /// - /// Callback used by the visibility system to determine if an observer (player) can see this object. - /// If this function returns true, the network connection will be added as an observer. - /// - /// Network connection of a player. - /// True if the player can see this object. - public override bool OnCheckObserver(NetworkConnection conn) - { - // Not Visible if not in a match - if (matchId == Guid.Empty) - return false; - - NetworkMatchChecker networkMatchChecker = conn.identity.GetComponent(); - - if (networkMatchChecker == null) - return false; - - return networkMatchChecker.matchId == matchId; - } - - /// - /// Callback used by the visibility system to (re)construct the set of observers that can see this object. - /// Implementations of this callback should add network connections of players that can see this object to the observers set. - /// - /// The new set of observers for this object. - /// True if the set of observers is being built for the first time. - public override void OnRebuildObservers(HashSet observers, bool initialize) - { - if (currentMatch == Guid.Empty) return; - - foreach (NetworkIdentity networkIdentity in matchPlayers[currentMatch]) - if (networkIdentity != null && networkIdentity.connectionToClient != null) - observers.Add(networkIdentity.connectionToClient); - } - - #endregion - } -} diff --git a/UnityProject/Assets/Mirror/Components/NetworkOwnerChecker.cs b/UnityProject/Assets/Mirror/Components/NetworkOwnerChecker.cs deleted file mode 100644 index 175165a..0000000 --- a/UnityProject/Assets/Mirror/Components/NetworkOwnerChecker.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using UnityEngine; -using System.Collections.Generic; - -namespace Mirror -{ - /// - /// Component that limits visibility of networked objects to the authority client. - /// Any object with this component on it will only be visible to the client that has been assigned authority for it. - /// This would be used for spawning a non-player networked object for single client to interact with, e.g. in-game puzzles. - /// - [Obsolete(NetworkVisibilityObsoleteMessage.Message)] - [DisallowMultipleComponent] - [AddComponentMenu("Network/NetworkOwnerChecker")] - [RequireComponent(typeof(NetworkIdentity))] - [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-owner-checker")] - public class NetworkOwnerChecker : NetworkVisibility - { - /// - /// Callback used by the visibility system to determine if an observer (player) can see this object. - /// If this function returns true, the network connection will be added as an observer. - /// - /// Network connection of a player. - /// True if the client is the owner of this object. - public override bool OnCheckObserver(NetworkConnection conn) - { - // Debug.Log($"OnCheckObserver {netIdentity.connectionToClient} {conn}"); - - return (netIdentity.connectionToClient == conn); - } - - /// - /// Callback used by the visibility system to (re)construct the set of observers that can see this object. - /// - /// The new set of observers for this object. - /// True if the set of observers is being built for the first time. - public override void OnRebuildObservers(HashSet observers, bool initialize) - { - // Do nothing here because the authority client is always added as an observer internally. - } - } -} diff --git a/UnityProject/Assets/Mirror/Components/NetworkPingDisplay.cs b/UnityProject/Assets/Mirror/Components/NetworkPingDisplay.cs index ca3527b..156a48c 100644 --- a/UnityProject/Assets/Mirror/Components/NetworkPingDisplay.cs +++ b/UnityProject/Assets/Mirror/Components/NetworkPingDisplay.cs @@ -7,14 +7,14 @@ namespace Mirror /// Component that will display the clients ping in milliseconds /// [DisallowMultipleComponent] - [AddComponentMenu("Network/NetworkPingDisplay")] + [AddComponentMenu("Network/Network Ping Display")] [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-ping-display")] public class NetworkPingDisplay : MonoBehaviour { public Color color = Color.white; public int padding = 2; - int width = 150; - int height = 25; + public int width = 150; + public int height = 25; void OnGUI() { diff --git a/UnityProject/Assets/Mirror/Components/NetworkPingDisplay.cs.meta b/UnityProject/Assets/Mirror/Components/NetworkPingDisplay.cs.meta index 826d5d3..221a745 100644 --- a/UnityProject/Assets/Mirror/Components/NetworkPingDisplay.cs.meta +++ b/UnityProject/Assets/Mirror/Components/NetworkPingDisplay.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/NetworkProximityChecker.cs b/UnityProject/Assets/Mirror/Components/NetworkProximityChecker.cs deleted file mode 100644 index b83c80f..0000000 --- a/UnityProject/Assets/Mirror/Components/NetworkProximityChecker.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; - -namespace Mirror -{ - /// - /// Component that controls visibility of networked objects for players. - /// Any object with this component on it will not be visible to players more than a (configurable) distance away. - /// - [Obsolete(NetworkVisibilityObsoleteMessage.Message)] - [AddComponentMenu("Network/NetworkProximityChecker")] - [RequireComponent(typeof(NetworkIdentity))] - [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-proximity-checker")] - public class NetworkProximityChecker : NetworkVisibility - { - /// - /// The maximum range that objects will be visible at. - /// - [Tooltip("The maximum range that objects will be visible at.")] - public int visRange = 10; - - /// - /// How often (in seconds) that this object should update the list of observers that can see it. - /// - [Tooltip("How often (in seconds) that this object should update the list of observers that can see it.")] - public float visUpdateInterval = 1; - - /// - /// Flag to force this object to be hidden for players. - /// If this object is a player object, it will not be hidden for that player. - /// - // Deprecated 2021-02-17 - [Obsolete("Use NetworkIdentity.visible mode instead of forceHidden!")] - public bool forceHidden - { - get => netIdentity.visible == Visibility.ForceHidden; - set => netIdentity.visible = value ? Visibility.ForceHidden : Visibility.Default; - } - - public override void OnStartServer() - { - InvokeRepeating(nameof(RebuildObservers), 0, visUpdateInterval); - } - public override void OnStopServer() - { - CancelInvoke(nameof(RebuildObservers)); - } - - void RebuildObservers() - { - netIdentity.RebuildObservers(false); - } - - /// - /// Callback used by the visibility system to determine if an observer (player) can see this object. - /// If this function returns true, the network connection will be added as an observer. - /// - /// Network connection of a player. - /// True if the player can see this object. - public override bool OnCheckObserver(NetworkConnection conn) - { - if (forceHidden) - return false; - - return Vector3.Distance(conn.identity.transform.position, transform.position) < visRange; - } - - /// - /// Callback used by the visibility system to (re)construct the set of observers that can see this object. - /// Implementations of this callback should add network connections of players that can see this object to the observers set. - /// - /// The new set of observers for this object. - /// True if the set of observers is being built for the first time. - public override void OnRebuildObservers(HashSet observers, bool initialize) - { - // if force hidden then return without adding any observers. - if (forceHidden) - return; - - // 'transform.' calls GetComponent, only do it once - Vector3 position = transform.position; - - // brute force distance check - // -> only player connections can be observers, so it's enough if we - // go through all connections instead of all spawned identities. - // -> compared to UNET's sphere cast checking, this one is orders of - // magnitude faster. if we have 10k monsters and run a sphere - // cast 10k times, we will see a noticeable lag even with physics - // layers. but checking to every connection is fast. - foreach (NetworkConnectionToClient conn in NetworkServer.connections.Values) - { - if (conn != null && conn.identity != null) - { - // check distance - if (Vector3.Distance(conn.identity.transform.position, position) < visRange) - { - observers.Add(conn); - } - } - } - } - } -} diff --git a/UnityProject/Assets/Mirror/Components/NetworkRoomManager.cs b/UnityProject/Assets/Mirror/Components/NetworkRoomManager.cs index c730e8f..5ba4417 100644 --- a/UnityProject/Assets/Mirror/Components/NetworkRoomManager.cs +++ b/UnityProject/Assets/Mirror/Components/NetworkRoomManager.cs @@ -1,6 +1,8 @@ +using System; using System.Collections.Generic; using System.Linq; using UnityEngine; +using UnityEngine.SceneManagement; using UnityEngine.Serialization; namespace Mirror @@ -13,13 +15,13 @@ namespace Mirror /// NetworkRoomManager is derived from NetworkManager, and so it implements many of the virtual functions provided by the NetworkManager class. To avoid accidentally replacing functionality of the NetworkRoomManager, there are new virtual functions on the NetworkRoomManager that begin with "OnRoom". These should be used on classes derived from NetworkRoomManager instead of the virtual functions on NetworkManager. /// The OnRoom*() functions have empty implementations on the NetworkRoomManager base class, so the base class functions do not have to be called. /// - [AddComponentMenu("Network/NetworkRoomManager")] + [AddComponentMenu("Network/Network Room Manager")] [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-room-manager")] public class NetworkRoomManager : NetworkManager { public struct PendingPlayer { - public NetworkConnection conn; + public NetworkConnectionToClient conn; public GameObject roomPlayer; } @@ -148,9 +150,9 @@ namespace Mirror /// The default implementation of this function calls NetworkServer.SetClientReady() to continue the network setup process. /// /// Connection from client. - public override void OnServerReady(NetworkConnection conn) + public override void OnServerReady(NetworkConnectionToClient conn) { - Debug.Log("NetworkRoomManager OnServerReady"); + Debug.Log($"NetworkRoomManager OnServerReady {conn}"); base.OnServerReady(conn); if (conn != null && conn.identity != null) @@ -163,9 +165,9 @@ namespace Mirror } } - void SceneLoadedForPlayer(NetworkConnection conn, GameObject roomPlayer) + void SceneLoadedForPlayer(NetworkConnectionToClient conn, GameObject roomPlayer) { - // Debug.LogFormat(LogType.Log, "NetworkRoom SceneLoadedForPlayer scene: {0} {1}", SceneManager.GetActiveScene().path, conn); + Debug.Log($"NetworkRoom SceneLoadedForPlayer scene: {SceneManager.GetActiveScene().path} {conn}"); if (IsSceneActive(RoomScene)) { @@ -243,7 +245,7 @@ namespace Mirror /// Unity calls this on the Server when a Client connects to the Server. Use an override to tell the NetworkManager what to do when a client connects to the server. /// /// Connection from client. - public override void OnServerConnect(NetworkConnection conn) + public override void OnServerConnect(NetworkConnectionToClient conn) { if (numPlayers >= maxConnections) { @@ -267,7 +269,7 @@ namespace Mirror /// This is called on the Server when a Client disconnects from the Server. Use an override to decide what should happen when a disconnection is detected. /// /// Connection from client. - public override void OnServerDisconnect(NetworkConnection conn) + public override void OnServerDisconnect(NetworkConnectionToClient conn) { if (conn.identity != null) { @@ -312,7 +314,7 @@ namespace Mirror /// The default implementation for this function creates a new player object from the playerPrefab. /// /// Connection from client. - public override void OnServerAddPlayer(NetworkConnection conn) + public override void OnServerAddPlayer(NetworkConnectionToClient conn) { // increment the index before adding the player, so first player starts at 1 clientIndex++; @@ -324,7 +326,7 @@ namespace Mirror allPlayersReady = false; - // Debug.LogFormat(LogType.Log, "NetworkRoomManager.OnServerAddPlayer playerPrefab:{0}", roomPlayerPrefab.name); + //Debug.Log("NetworkRoomManager.OnServerAddPlayer playerPrefab: {roomPlayerPrefab.name}"); GameObject newRoomGameObject = OnRoomServerCreateRoomPlayer(conn); if (newRoomGameObject == null) @@ -403,13 +405,13 @@ namespace Mirror /// public override void OnStartServer() { - if (string.IsNullOrEmpty(RoomScene)) + if (string.IsNullOrWhiteSpace(RoomScene)) { Debug.LogError("NetworkRoomManager RoomScene is empty. Set the RoomScene in the inspector for the NetworkRoomManager"); return; } - if (string.IsNullOrEmpty(GameplayScene)) + if (string.IsNullOrWhiteSpace(GameplayScene)) { Debug.LogError("NetworkRoomManager PlayScene is empty. Set the PlayScene in the inspector for the NetworkRoomManager"); return; @@ -444,9 +446,9 @@ namespace Mirror OnRoomStopHost(); } -#endregion + #endregion -#region client handlers + #region client handlers /// /// This is invoked when the client is started. @@ -468,22 +470,20 @@ namespace Mirror /// Called on the client when connected to a server. /// The default implementation of this function sets the client as ready and adds a player. Override the function to dictate what happens when the client connects. /// - /// Connection to the server. - public override void OnClientConnect(NetworkConnection conn) + public override void OnClientConnect() { - OnRoomClientConnect(conn); - base.OnClientConnect(conn); + OnRoomClientConnect(); + base.OnClientConnect(); } /// /// Called on clients when disconnected from a server. /// This is called on the client when it disconnects from the server. Override this function to decide what happens when the client disconnects. /// - /// Connection to the server. - public override void OnClientDisconnect(NetworkConnection conn) + public override void OnClientDisconnect() { - OnRoomClientDisconnect(conn); - base.OnClientDisconnect(conn); + OnRoomClientDisconnect(); + base.OnClientDisconnect(); } /// @@ -500,8 +500,7 @@ namespace Mirror /// Called on clients when a scene has completed loaded, when the scene load was initiated by the server. /// Scene changes can cause player objects to be destroyed. The default implementation of OnClientSceneChanged in the NetworkManager is to add a player object for the connection if no player object exists. /// - /// Connection of the client - public override void OnClientSceneChanged(NetworkConnection conn) + public override void OnClientSceneChanged() { if (IsSceneActive(RoomScene)) { @@ -511,13 +510,13 @@ namespace Mirror else CallOnClientExitRoom(); - base.OnClientSceneChanged(conn); - OnRoomClientSceneChanged(conn); + base.OnClientSceneChanged(); + OnRoomClientSceneChanged(); } -#endregion + #endregion -#region room server virtuals + #region room server virtuals /// /// This is called on the host when a host is started. @@ -543,13 +542,13 @@ namespace Mirror /// This is called on the server when a new client connects to the server. /// /// The new connection. - public virtual void OnRoomServerConnect(NetworkConnection conn) {} + public virtual void OnRoomServerConnect(NetworkConnectionToClient conn) {} /// /// This is called on the server when a client disconnects. /// /// The connection that disconnected. - public virtual void OnRoomServerDisconnect(NetworkConnection conn) {} + public virtual void OnRoomServerDisconnect(NetworkConnectionToClient conn) {} /// /// This is called on the server when a networked scene finishes loading. @@ -563,7 +562,7 @@ namespace Mirror /// /// The connection the player object is for. /// The new room-player object. - public virtual GameObject OnRoomServerCreateRoomPlayer(NetworkConnection conn) + public virtual GameObject OnRoomServerCreateRoomPlayer(NetworkConnectionToClient conn) { return null; } @@ -575,7 +574,7 @@ namespace Mirror /// The connection the player object is for. /// The room player object for this connection. /// A new GamePlayer object. - public virtual GameObject OnRoomServerCreateGamePlayer(NetworkConnection conn, GameObject roomPlayer) + public virtual GameObject OnRoomServerCreateGamePlayer(NetworkConnectionToClient conn, GameObject roomPlayer) { return null; } @@ -583,10 +582,10 @@ namespace Mirror /// /// This allows customization of the creation of the GamePlayer object on the server. /// This is only called for subsequent GamePlay scenes after the first one. - /// See OnRoomServerCreateGamePlayer(NetworkConnection, GameObject) to customize the player object for the initial GamePlay scene. + /// See OnRoomServerCreateGamePlayer(NetworkConnection, GameObject) to customize the player object for the initial GamePlay scene. /// /// The connection the player object is for. - public virtual void OnRoomServerAddPlayer(NetworkConnection conn) + public virtual void OnRoomServerAddPlayer(NetworkConnectionToClient conn) { base.OnServerAddPlayer(conn); } @@ -600,7 +599,7 @@ namespace Mirror /// The room player object. /// The game player object. /// False to not allow this player to replace the room player. - public virtual bool OnRoomServerSceneLoadedForPlayer(NetworkConnection conn, GameObject roomPlayer, GameObject gamePlayer) + public virtual bool OnRoomServerSceneLoadedForPlayer(NetworkConnectionToClient conn, GameObject roomPlayer, GameObject gamePlayer) { return true; } @@ -621,9 +620,9 @@ namespace Mirror /// public virtual void OnRoomServerPlayersNotReady() {} -#endregion + #endregion -#region room client virtuals + #region room client virtuals /// /// This is a hook to allow custom behaviour when the game client enters the room. @@ -638,19 +637,16 @@ namespace Mirror /// /// This is called on the client when it connects to server. /// - /// The connection that connected. - public virtual void OnRoomClientConnect(NetworkConnection conn) {} + public virtual void OnRoomClientConnect() {} /// /// This is called on the client when disconnected from a server. /// - /// The connection that disconnected. - public virtual void OnRoomClientDisconnect(NetworkConnection conn) {} + public virtual void OnRoomClientDisconnect() {} /// /// This is called on the client when a client is started. /// - /// The connection for the room. public virtual void OnRoomStartClient() {} /// @@ -661,8 +657,7 @@ namespace Mirror /// /// This is called on the client when the client is finished loading a new networked scene. /// - /// The connection that finished loading a new networked scene. - public virtual void OnRoomClientSceneChanged(NetworkConnection conn) {} + public virtual void OnRoomClientSceneChanged() {} /// /// Called on the client when adding a player to the room fails. @@ -670,9 +665,9 @@ namespace Mirror /// public virtual void OnRoomClientAddPlayerFailed() {} -#endregion + #endregion -#region optional UI + #region optional UI /// /// virtual so inheriting classes can roll their own @@ -694,6 +689,6 @@ namespace Mirror GUI.Box(new Rect(10f, 180f, 520f, 150f), "PLAYERS"); } -#endregion + #endregion } } diff --git a/UnityProject/Assets/Mirror/Components/NetworkRoomManager.cs.meta b/UnityProject/Assets/Mirror/Components/NetworkRoomManager.cs.meta index 2c64616..76e7d42 100644 --- a/UnityProject/Assets/Mirror/Components/NetworkRoomManager.cs.meta +++ b/UnityProject/Assets/Mirror/Components/NetworkRoomManager.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/NetworkRoomPlayer.cs b/UnityProject/Assets/Mirror/Components/NetworkRoomPlayer.cs index c16052d..d2763d5 100644 --- a/UnityProject/Assets/Mirror/Components/NetworkRoomPlayer.cs +++ b/UnityProject/Assets/Mirror/Components/NetworkRoomPlayer.cs @@ -7,7 +7,7 @@ namespace Mirror /// The RoomPrefab object of the NetworkRoomManager must have this component on it. This component holds basic room player data required for the room to function. Game specific data for room players can be put in other components on the RoomPrefab or in scripts derived from NetworkRoomPlayer. /// [DisallowMultipleComponent] - [AddComponentMenu("Network/NetworkRoomPlayer")] + [AddComponentMenu("Network/Network Room Player")] [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-room-player")] public class NetworkRoomPlayer : NetworkBehaviour { @@ -126,7 +126,7 @@ namespace Mirror #region Optional UI /// - /// Render a UI for the room. Override to provide your on UI + /// Render a UI for the room. Override to provide your own UI /// public virtual void OnGUI() { diff --git a/UnityProject/Assets/Mirror/Components/NetworkRoomPlayer.cs.meta b/UnityProject/Assets/Mirror/Components/NetworkRoomPlayer.cs.meta index d355d8d..0299bea 100644 --- a/UnityProject/Assets/Mirror/Components/NetworkRoomPlayer.cs.meta +++ b/UnityProject/Assets/Mirror/Components/NetworkRoomPlayer.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/NetworkSceneChecker.cs b/UnityProject/Assets/Mirror/Components/NetworkSceneChecker.cs deleted file mode 100644 index d94400e..0000000 --- a/UnityProject/Assets/Mirror/Components/NetworkSceneChecker.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; -using UnityEngine.SceneManagement; - -namespace Mirror -{ - /// - /// Component that controls visibility of networked objects between scenes. - /// Any object with this component on it will only be visible to other objects in the same scene - /// This would be used when the server has multiple additive subscenes loaded to isolate players to their respective subscenes - /// - [Obsolete(NetworkVisibilityObsoleteMessage.Message)] - [DisallowMultipleComponent] - [AddComponentMenu("Network/NetworkSceneChecker")] - [RequireComponent(typeof(NetworkIdentity))] - [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-scene-checker")] - public class NetworkSceneChecker : NetworkVisibility - { - /// - /// Flag to force this object to be hidden from all observers. - /// If this object is a player object, it will not be hidden for that client. - /// - [Tooltip("Enable to force this object to be hidden from all observers.")] - public bool forceHidden; - - // Use Scene instead of string scene.name because when additively loading multiples of a subscene the name won't be unique - static readonly Dictionary> sceneCheckerObjects = new Dictionary>(); - - Scene currentScene; - - [ServerCallback] - void Awake() - { - currentScene = gameObject.scene; - // Debug.Log($"NetworkSceneChecker.Awake currentScene: {currentScene}"); - } - - public override void OnStartServer() - { - if (!sceneCheckerObjects.ContainsKey(currentScene)) - sceneCheckerObjects.Add(currentScene, new HashSet()); - - sceneCheckerObjects[currentScene].Add(netIdentity); - } - - public override void OnStopServer() - { - if (sceneCheckerObjects.ContainsKey(currentScene) && sceneCheckerObjects[currentScene].Remove(netIdentity)) - RebuildSceneObservers(); - } - - [ServerCallback] - void Update() - { - if (currentScene == gameObject.scene) - return; - - // This object is in a new scene so observers in the prior scene - // and the new scene need to rebuild their respective observers lists. - - // Remove this object from the hashset of the scene it just left - sceneCheckerObjects[currentScene].Remove(netIdentity); - - // RebuildObservers of all NetworkIdentity's in the scene this object just left - RebuildSceneObservers(); - - // Set this to the new scene this object just entered - currentScene = gameObject.scene; - - // Make sure this new scene is in the dictionary - if (!sceneCheckerObjects.ContainsKey(currentScene)) - sceneCheckerObjects.Add(currentScene, new HashSet()); - - // Add this object to the hashset of the new scene - sceneCheckerObjects[currentScene].Add(netIdentity); - - // RebuildObservers of all NetworkIdentity's in the scene this object just entered - RebuildSceneObservers(); - } - - void RebuildSceneObservers() - { - foreach (NetworkIdentity networkIdentity in sceneCheckerObjects[currentScene]) - if (networkIdentity != null) - networkIdentity.RebuildObservers(false); - } - - /// - /// Callback used by the visibility system to determine if an observer (player) can see this object. - /// If this function returns true, the network connection will be added as an observer. - /// - /// Network connection of a player. - /// True if the player can see this object. - public override bool OnCheckObserver(NetworkConnection conn) - { - if (forceHidden) - return false; - - return conn.identity.gameObject.scene == gameObject.scene; - } - - /// - /// Callback used by the visibility system to (re)construct the set of observers that can see this object. - /// Implementations of this callback should add network connections of players that can see this object to the observers set. - /// - /// The new set of observers for this object. - /// True if the set of observers is being built for the first time. - public override void OnRebuildObservers(HashSet observers, bool initialize) - { - // If forceHidden then return without adding any observers. - if (forceHidden) - return; - - // Add everything in the hashset for this object's current scene - foreach (NetworkIdentity networkIdentity in sceneCheckerObjects[currentScene]) - if (networkIdentity != null && networkIdentity.connectionToClient != null) - observers.Add(networkIdentity.connectionToClient); - } - } -} diff --git a/UnityProject/Assets/Mirror/Components/NetworkStatistics.cs b/UnityProject/Assets/Mirror/Components/NetworkStatistics.cs new file mode 100644 index 0000000..e1fac19 --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/NetworkStatistics.cs @@ -0,0 +1,194 @@ +using System; +using UnityEngine; + +namespace Mirror +{ + /// + /// Shows Network messages and bytes sent and received per second. + /// + /// + /// Add this component to the same object as Network Manager. + /// + [AddComponentMenu("Network/Network Statistics")] + [DisallowMultipleComponent] + [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-statistics")] + public class NetworkStatistics : MonoBehaviour + { + // update interval + double intervalStartTime; + + // --------------------------------------------------------------------- + + // CLIENT + // long bytes to support >2GB + int clientIntervalReceivedPackets; + long clientIntervalReceivedBytes; + int clientIntervalSentPackets; + long clientIntervalSentBytes; + + // results from last interval + // long bytes to support >2GB + int clientReceivedPacketsPerSecond; + long clientReceivedBytesPerSecond; + int clientSentPacketsPerSecond; + long clientSentBytesPerSecond; + + // --------------------------------------------------------------------- + + // SERVER + // capture interval + // long bytes to support >2GB + int serverIntervalReceivedPackets; + long serverIntervalReceivedBytes; + int serverIntervalSentPackets; + long serverIntervalSentBytes; + + // results from last interval + // long bytes to support >2GB + int serverReceivedPacketsPerSecond; + long serverReceivedBytesPerSecond; + int serverSentPacketsPerSecond; + long serverSentBytesPerSecond; + + // NetworkManager sets Transport.activeTransport in Awake(). + // so let's hook into it in Start(). + void Start() + { + // find available transport + Transport transport = Transport.activeTransport; + if (transport != null) + { + transport.OnClientDataReceived += OnClientReceive; + transport.OnClientDataSent += OnClientSend; + transport.OnServerDataReceived += OnServerReceive; + transport.OnServerDataSent += OnServerSend; + } + else Debug.LogError($"NetworkStatistics: no available or active Transport found on this platform: {Application.platform}"); + } + + void OnDestroy() + { + // remove transport hooks + Transport transport = Transport.activeTransport; + if (transport != null) + { + transport.OnClientDataReceived -= OnClientReceive; + transport.OnClientDataSent -= OnClientSend; + transport.OnServerDataReceived -= OnServerReceive; + transport.OnServerDataSent -= OnServerSend; + } + } + + void OnClientReceive(ArraySegment data, int channelId) + { + ++clientIntervalReceivedPackets; + clientIntervalReceivedBytes += data.Count; + } + + void OnClientSend(ArraySegment data, int channelId) + { + ++clientIntervalSentPackets; + clientIntervalSentBytes += data.Count; + } + + void OnServerReceive(int connectionId, ArraySegment data, int channelId) + { + ++serverIntervalReceivedPackets; + serverIntervalReceivedBytes += data.Count; + } + + void OnServerSend(int connectionId, ArraySegment data, int channelId) + { + ++serverIntervalSentPackets; + serverIntervalSentBytes += data.Count; + } + + void Update() + { + // calculate results every second + if (NetworkTime.localTime >= intervalStartTime + 1) + { + if (NetworkClient.active) UpdateClient(); + if (NetworkServer.active) UpdateServer(); + + intervalStartTime = NetworkTime.localTime; + } + } + + void UpdateClient() + { + clientReceivedPacketsPerSecond = clientIntervalReceivedPackets; + clientReceivedBytesPerSecond = clientIntervalReceivedBytes; + clientSentPacketsPerSecond = clientIntervalSentPackets; + clientSentBytesPerSecond = clientIntervalSentBytes; + + clientIntervalReceivedPackets = 0; + clientIntervalReceivedBytes = 0; + clientIntervalSentPackets = 0; + clientIntervalSentBytes = 0; + } + + void UpdateServer() + { + serverReceivedPacketsPerSecond = serverIntervalReceivedPackets; + serverReceivedBytesPerSecond = serverIntervalReceivedBytes; + serverSentPacketsPerSecond = serverIntervalSentPackets; + serverSentBytesPerSecond = serverIntervalSentBytes; + + serverIntervalReceivedPackets = 0; + serverIntervalReceivedBytes = 0; + serverIntervalSentPackets = 0; + serverIntervalSentBytes = 0; + } + + void OnGUI() + { + // only show if either server or client active + if (NetworkClient.active || NetworkServer.active) + { + // create main GUI area + // 105 is below NetworkManager HUD in all cases. + GUILayout.BeginArea(new Rect(10, 105, 215, 300)); + + // show client / server stats if active + if (NetworkClient.active) OnClientGUI(); + if (NetworkServer.active) OnServerGUI(); + + // end of GUI area + GUILayout.EndArea(); + } + } + + void OnClientGUI() + { + // background + GUILayout.BeginVertical("Box"); + GUILayout.Label("Client Statistics"); + + // sending ("msgs" instead of "packets" to fit larger numbers) + GUILayout.Label($"Send: {clientSentPacketsPerSecond} msgs @ {Utils.PrettyBytes(clientSentBytesPerSecond)}/s"); + + // receiving ("msgs" instead of "packets" to fit larger numbers) + GUILayout.Label($"Recv: {clientReceivedPacketsPerSecond} msgs @ {Utils.PrettyBytes(clientReceivedBytesPerSecond)}/s"); + + // end background + GUILayout.EndVertical(); + } + + void OnServerGUI() + { + // background + GUILayout.BeginVertical("Box"); + GUILayout.Label("Server Statistics"); + + // sending ("msgs" instead of "packets" to fit larger numbers) + GUILayout.Label($"Send: {serverSentPacketsPerSecond} msgs @ {Utils.PrettyBytes(serverSentBytesPerSecond)}/s"); + + // receiving ("msgs" instead of "packets" to fit larger numbers) + GUILayout.Label($"Recv: {serverReceivedPacketsPerSecond} msgs @ {Utils.PrettyBytes(serverReceivedBytesPerSecond)}/s"); + + // end background + GUILayout.EndVertical(); + } + } +} diff --git a/UnityProject/Assets/Mirror/Components/NetworkStatistics.cs.meta b/UnityProject/Assets/Mirror/Components/NetworkStatistics.cs.meta new file mode 100644 index 0000000..0bf4251 --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/NetworkStatistics.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6d7da4e566d24ea7b0e12178d934b648 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/NetworkTransform.cs b/UnityProject/Assets/Mirror/Components/NetworkTransform.cs deleted file mode 100644 index e001d1e..0000000 --- a/UnityProject/Assets/Mirror/Components/NetworkTransform.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UnityEngine; - -namespace Mirror -{ - [DisallowMultipleComponent] - [AddComponentMenu("Network/NetworkTransform")] - [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-transform")] - public class NetworkTransform : NetworkTransformBase - { - protected override Transform targetComponent => transform; - } -} diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Dependencies.meta b/UnityProject/Assets/Mirror/Components/NetworkTransform2k.meta similarity index 77% rename from UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Dependencies.meta rename to UnityProject/Assets/Mirror/Components/NetworkTransform2k.meta index 565dc3b..fe99bf0 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Dependencies.meta +++ b/UnityProject/Assets/Mirror/Components/NetworkTransform2k.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 12f903db684732e45b130ad56f7c86c1 +guid: 44e823b93c7d2477c8796766dc364c59 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransform.cs b/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransform.cs new file mode 100644 index 0000000..b7b8e81 --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransform.cs @@ -0,0 +1,17 @@ +// ʻOumuamua's light curve, assuming little systematic error, presents its +// motion as tumbling, rather than smoothly rotating, and moving sufficiently +// fast relative to the Sun. +// +// A small number of astronomers suggested that ʻOumuamua could be a product of +// alien technology, but evidence in support of this hypothesis is weak. +using UnityEngine; + +namespace Mirror +{ + [DisallowMultipleComponent] + [AddComponentMenu("Network/Network Transform")] + public class NetworkTransform : NetworkTransformBase + { + protected override Transform targetComponent => transform; + } +} diff --git a/UnityProject/Assets/Mirror/Components/NetworkTransform.cs.meta b/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransform.cs.meta similarity index 79% rename from UnityProject/Assets/Mirror/Components/NetworkTransform.cs.meta rename to UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransform.cs.meta index 4ad6860..a569990 100644 --- a/UnityProject/Assets/Mirror/Components/NetworkTransform.cs.meta +++ b/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransform.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransformBase.cs b/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransformBase.cs new file mode 100644 index 0000000..e8c510e --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransformBase.cs @@ -0,0 +1,869 @@ +// NetworkTransform V2 aka project Oumuamua by vis2k (2021-07) +// Snapshot Interpolation: https://gafferongames.com/post/snapshot_interpolation/ +// +// Base class for NetworkTransform and NetworkTransformChild. +// => simple unreliable sync without any interpolation for now. +// => which means we don't need teleport detection either +// +// NOTE: several functions are virtual in case someone needs to modify a part. +// +// Channel: uses UNRELIABLE at all times. +// -> out of order packets are dropped automatically +// -> it's better than RELIABLE for several reasons: +// * head of line blocking would add delay +// * resending is mostly pointless +// * bigger data race: +// -> if we use a Cmd() at position X over reliable +// -> client gets Cmd() and X at the same time, but buffers X for bufferTime +// -> for unreliable, it would get X before the reliable Cmd(), still +// buffer for bufferTime but end up closer to the original time +// comment out the below line to quickly revert the onlySyncOnChange feature +#define onlySyncOnChange_BANDWIDTH_SAVING +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Mirror +{ + public abstract class NetworkTransformBase : NetworkBehaviour + { + // TODO SyncDirection { CLIENT_TO_SERVER, SERVER_TO_CLIENT } is easier? + [Header("Authority")] + [Tooltip("Set to true if moves come from owner client, set to false if moves always come from server")] + public bool clientAuthority; + + // Is this a client with authority over this transform? + // This component could be on the player object or any object that has been assigned authority to this client. + protected bool IsClientWithAuthority => hasAuthority && clientAuthority; + + // target transform to sync. can be on a child. + protected abstract Transform targetComponent { get; } + + [Header("Synchronization")] + [Tooltip("Send N snapshots per second. Multiples of frame rate make sense.")] + public int sendRate = 30; // in Hz. easier to work with as int for EMA. easier to display '30' than '0.333333333' + public float sendInterval => 1f / sendRate; + + // decrease bufferTime at runtime to see the catchup effect. + // increase to see slowdown. + // 'double' so we can have very precise dynamic adjustment without rounding + [Header("Buffering")] + [Tooltip("Local simulation is behind by sendInterval * multiplier seconds.\n\nThis guarantees that we always have enough snapshots in the buffer to mitigate lags & jitter.\n\nIncrease this if the simulation isn't smooth. By default, it should be around 2.")] + public double bufferTimeMultiplier = 2; + public double bufferTime => sendInterval * bufferTimeMultiplier; + + [Tooltip("Buffer size limit to avoid ever growing list memory consumption attacks.")] + public int bufferSizeLimit = 64; + + // catchup ///////////////////////////////////////////////////////////// + // catchup thresholds in 'frames'. + // half a frame might be too aggressive. + [Header("Catchup / Slowdown")] + [Tooltip("Slowdown begins when the local timeline is moving too fast towards remote time. Threshold is in frames worth of snapshots.\n\nThis needs to be negative.\n\nDon't modify unless you know what you are doing.")] + public float catchupNegativeThreshold = -1; // careful, don't want to run out of snapshots + + [Tooltip("Catchup begins when the local timeline is moving too slow and getting too far away from remote time. Threshold is in frames worth of snapshots.\n\nThis needs to be positive.\n\nDon't modify unless you know what you are doing.")] + public float catchupPositiveThreshold = 1; + + [Tooltip("Local timeline acceleration in % while catching up.")] + [Range(0, 1)] + public double catchupSpeed = 0.01f; // 1% + + [Tooltip("Local timeline slowdown in % while slowing down.")] + [Range(0, 1)] + public double slowdownSpeed = 0.01f; // 1% + + [Tooltip("Catchup/Slowdown is adjusted over n-second exponential moving average.")] + public int driftEmaDuration = 1; // shouldn't need to modify this, but expose it anyway + + // we use EMA to average the last second worth of snapshot time diffs. + // manually averaging the last second worth of values with a for loop + // would be the same, but a moving average is faster because we only + // ever add one value. + ExponentialMovingAverage serverDriftEma; + ExponentialMovingAverage clientDriftEma; + + // dynamic buffer time adjustment ////////////////////////////////////// + // dynamically adjusts bufferTimeMultiplier for smooth results. + // to understand how this works, try this manually: + // + // - disable dynamic adjustment + // - set jitter = 0.2 (20% is a lot!) + // - notice some stuttering + // - disable interpolation to see just how much jitter this really is(!) + // - enable interpolation again + // - manually increase bufferTimeMultiplier to 3-4 + // ... the cube slows down (blue) until it's smooth + // - with dynamic adjustment enabled, it will set 4 automatically + // ... the cube slows down (blue) until it's smooth as well + // + // note that 20% jitter is extreme. + // for this to be perfectly smooth, set the safety tolerance to '2'. + // but realistically this is not necessary, and '1' is enough. + [Header("Dynamic Adjustment")] + [Tooltip("Automatically adjust bufferTimeMultiplier for smooth results.\nSets a low multiplier on stable connections, and a high multiplier on jittery connections.")] + public bool dynamicAdjustment = true; + + [Tooltip("Safety buffer that is always added to the dynamic bufferTimeMultiplier adjustment.")] + public float dynamicAdjustmentTolerance = 1; // 1 is realistically just fine, 2 is very very safe even for 20% jitter. can be half a frame too. (see above comments) + + [Tooltip("Dynamic adjustment is computed over n-second exponential moving average standard deviation.")] + public int deliveryTimeEmaDuration = 2; // 1-2s recommended to capture average delivery time + + ExponentialMovingAverage serverDeliveryTimeEma; // average delivery time (standard deviation gives average jitter) + ExponentialMovingAverage clientDeliveryTimeEma; // average delivery time (standard deviation gives average jitter) + + // buffers & time ////////////////////////////////////////////////////// + // snapshots sorted by timestamp + // in the original article, glenn fiedler drops any snapshots older than + // the last received snapshot. + // -> instead, we insert into a sorted buffer + // -> the higher the buffer information density, the better + // -> we still drop anything older than the first element in the buffer + // => internal for testing + // + // IMPORTANT: of explicit 'NTSnapshot' type instead of 'Snapshot' + // interface because List allocates through boxing + internal SortedList serverSnapshots = new SortedList(); + internal SortedList clientSnapshots = new SortedList(); + + // only convert the static Interpolation function to Func once to + // avoid allocations + Func Interpolate = NTSnapshot.Interpolate; + + // for smooth interpolation, we need to interpolate along server time. + // any other time (arrival on client, client local time, etc.) is not + // going to give smooth results. + double serverTimeline; + double serverTimescale; + + // catchup / slowdown adjustments are applied to timescale, + // to be adjusted in every update instead of when receiving messages. + double clientTimeline; + double clientTimescale; + + // only sync when changed hack ///////////////////////////////////////// +#if onlySyncOnChange_BANDWIDTH_SAVING + [Header("Sync Only If Changed")] + [Tooltip("When true, changes are not sent unless greater than sensitivity values below.")] + public bool onlySyncOnChange = true; + + // 3 was original, but testing under really bad network conditions, 2%-5% packet loss and 250-1200ms ping, 5 proved to eliminate any twitching. + [Tooltip("How much time, as a multiple of send interval, has passed before clearing buffers.")] + public float bufferResetMultiplier = 5; + + [Header("Sensitivity"), Tooltip("Sensitivity of changes needed before an updated state is sent over the network")] + public float positionSensitivity = 0.01f; + public float rotationSensitivity = 0.01f; + public float scaleSensitivity = 0.01f; + + protected bool positionChanged; + protected bool rotationChanged; + protected bool scaleChanged; + + // Used to store last sent snapshots + protected NTSnapshot lastSnapshot; + protected bool cachedSnapshotComparison; + protected bool hasSentUnchangedPosition; +#endif + // selective sync ////////////////////////////////////////////////////// + [Header("Selective Sync & interpolation")] + public bool syncPosition = true; + public bool syncRotation = true; + public bool syncScale = false; // rare. off by default. + + double lastClientSendTime; + double lastServerSendTime; + + // debugging /////////////////////////////////////////////////////////// + [Header("Debug")] + public bool showGizmos; + public bool showOverlay; + public Color overlayColor = new Color(0, 0, 0, 0.5f); + + // initialization ////////////////////////////////////////////////////// + // make sure to call this when inheriting too! + protected virtual void Awake() + { + // initialize EMA with 'emaDuration' seconds worth of history. + // 1 second holds 'sendRate' worth of values. + // multiplied by emaDuration gives n-seconds. + serverDriftEma = new ExponentialMovingAverage(sendRate * driftEmaDuration); + clientDriftEma = new ExponentialMovingAverage(sendRate * driftEmaDuration); + serverDeliveryTimeEma = new ExponentialMovingAverage(sendRate * deliveryTimeEmaDuration); + clientDeliveryTimeEma = new ExponentialMovingAverage(sendRate * deliveryTimeEmaDuration); + } + + // snapshot functions ////////////////////////////////////////////////// + // construct a snapshot of the current state + // => internal for testing + protected virtual NTSnapshot ConstructSnapshot() + { + // NetworkTime.localTime for double precision until Unity has it too + return new NTSnapshot( + // our local time is what the other end uses as remote time + NetworkTime.localTime, + // the other end fills out local time itself + 0, + targetComponent.localPosition, + targetComponent.localRotation, + targetComponent.localScale + ); + } + + // apply a snapshot to the Transform. + // -> start, end, interpolated are all passed in caes they are needed + // -> a regular game would apply the 'interpolated' snapshot + // -> a board game might want to jump to 'goal' directly + // (it's easier to always interpolate and then apply selectively, + // instead of manually interpolating x, y, z, ... depending on flags) + // => internal for testing + // + // NOTE: stuck detection is unnecessary here. + // we always set transform.position anyway, we can't get stuck. + protected virtual void ApplySnapshot(NTSnapshot interpolated) + { + // local position/rotation for VR support + // + // if syncPosition/Rotation/Scale is disabled then we received nulls + // -> current position/rotation/scale would've been added as snapshot + // -> we still interpolated + // -> but simply don't apply it. if the user doesn't want to sync + // scale, then we should not touch scale etc. + if (syncPosition) + targetComponent.localPosition = interpolated.position; + + if (syncRotation) + targetComponent.localRotation = interpolated.rotation; + + if (syncScale) + targetComponent.localScale = interpolated.scale; + } + +#if onlySyncOnChange_BANDWIDTH_SAVING + // Returns true if position, rotation AND scale are unchanged, within given sensitivity range. + protected virtual bool CompareSnapshots(NTSnapshot currentSnapshot) + { + positionChanged = Vector3.SqrMagnitude(lastSnapshot.position - currentSnapshot.position) > positionSensitivity * positionSensitivity; + rotationChanged = Quaternion.Angle(lastSnapshot.rotation, currentSnapshot.rotation) > rotationSensitivity; + scaleChanged = Vector3.SqrMagnitude(lastSnapshot.scale - currentSnapshot.scale) > scaleSensitivity * scaleSensitivity; + + return (!positionChanged && !rotationChanged && !scaleChanged); + } +#endif + // cmd ///////////////////////////////////////////////////////////////// + // only unreliable. see comment above of this file. + [Command(channel = Channels.Unreliable)] + void CmdClientToServerSync(Vector3? position, Quaternion? rotation, Vector3? scale) + { + OnClientToServerSync(position, rotation, scale); + //For client authority, immediately pass on the client snapshot to all other + //clients instead of waiting for server to send its snapshots. + if (clientAuthority) + { + RpcServerToClientSync(position, rotation, scale); + } + } + + // local authority client sends sync message to server for broadcasting + protected virtual void OnClientToServerSync(Vector3? position, Quaternion? rotation, Vector3? scale) + { + // only apply if in client authority mode + if (!clientAuthority) return; + + // protect against ever growing buffer size attacks + if (serverSnapshots.Count >= bufferSizeLimit) return; + + // only player owned objects (with a connection) can send to + // server. we can get the timestamp from the connection. + double timestamp = connectionToClient.remoteTimeStamp; +#if onlySyncOnChange_BANDWIDTH_SAVING + if (onlySyncOnChange) + { + double timeIntervalCheck = bufferResetMultiplier * sendInterval; + + if (serverSnapshots.Count > 0 && serverSnapshots.Values[serverSnapshots.Count - 1].remoteTime + timeIntervalCheck < timestamp) + { + Reset(); + } + } +#endif + // position, rotation, scale can have no value if same as last time. + // saves bandwidth. + // but we still need to feed it to snapshot interpolation. we can't + // just have gaps in there if nothing has changed. for example, if + // client sends snapshot at t=0 + // client sends nothing for 10s because not moved + // client sends snapshot at t=10 + // then the server would assume that it's one super slow move and + // replay it for 10 seconds. + if (!position.HasValue) position = serverSnapshots.Count > 0 ? serverSnapshots.Values[serverSnapshots.Count - 1].position : targetComponent.localPosition; + if (!rotation.HasValue) rotation = serverSnapshots.Count > 0 ? serverSnapshots.Values[serverSnapshots.Count - 1].rotation : targetComponent.localRotation; + if (!scale.HasValue) scale = serverSnapshots.Count > 0 ? serverSnapshots.Values[serverSnapshots.Count - 1].scale : targetComponent.localScale; + + // construct snapshot with batch timestamp to save bandwidth + NTSnapshot snapshot = new NTSnapshot( + timestamp, + NetworkTime.localTime, + position.Value, rotation.Value, scale.Value + ); + + // (optional) dynamic adjustment + if (dynamicAdjustment) + { + // set bufferTime on the fly. + // shows in inspector for easier debugging :) + bufferTimeMultiplier = SnapshotInterpolation.DynamicAdjustment( + sendInterval, + serverDeliveryTimeEma.StandardDeviation, + dynamicAdjustmentTolerance + ); + // Debug.Log($"[Server]: {name} delivery std={serverDeliveryTimeEma.StandardDeviation} bufferTimeMult := {bufferTimeMultiplier} "); + } + + // insert into the server buffer & initialize / adjust / catchup + SnapshotInterpolation.Insert( + serverSnapshots, + snapshot, + ref serverTimeline, + ref serverTimescale, + sendInterval, + bufferTime, + catchupSpeed, + slowdownSpeed, + ref serverDriftEma, + catchupNegativeThreshold, + catchupPositiveThreshold, + ref serverDeliveryTimeEma + ); + } + + // rpc ///////////////////////////////////////////////////////////////// + // only unreliable. see comment above of this file. + [ClientRpc(channel = Channels.Unreliable)] + void RpcServerToClientSync(Vector3? position, Quaternion? rotation, Vector3? scale) => + OnServerToClientSync(position, rotation, scale); + + // server broadcasts sync message to all clients + protected virtual void OnServerToClientSync(Vector3? position, Quaternion? rotation, Vector3? scale) + { + // in host mode, the server sends rpcs to all clients. + // the host client itself will receive them too. + // -> host server is always the source of truth + // -> we can ignore any rpc on the host client + // => otherwise host objects would have ever growing clientBuffers + // (rpc goes to clients. if isServer is true too then we are host) + if (isServer) return; + + // don't apply for local player with authority + if (IsClientWithAuthority) return; + + // protect against ever growing buffer size attacks + if (clientSnapshots.Count >= bufferSizeLimit) return; + + // on the client, we receive rpcs for all entities. + // not all of them have a connectionToServer. + // but all of them go through NetworkClient.connection. + // we can get the timestamp from there. + double timestamp = NetworkClient.connection.remoteTimeStamp; +#if onlySyncOnChange_BANDWIDTH_SAVING + if (onlySyncOnChange) + { + double timeIntervalCheck = bufferResetMultiplier * sendInterval; + + if (clientSnapshots.Count > 0 && clientSnapshots.Values[clientSnapshots.Count - 1].remoteTime + timeIntervalCheck < timestamp) + { + Reset(); + } + } +#endif + // position, rotation, scale can have no value if same as last time. + // saves bandwidth. + // but we still need to feed it to snapshot interpolation. we can't + // just have gaps in there if nothing has changed. for example, if + // client sends snapshot at t=0 + // client sends nothing for 10s because not moved + // client sends snapshot at t=10 + // then the server would assume that it's one super slow move and + // replay it for 10 seconds. + if (!position.HasValue) position = clientSnapshots.Count > 0 ? clientSnapshots.Values[clientSnapshots.Count - 1].position : targetComponent.localPosition; + if (!rotation.HasValue) rotation = clientSnapshots.Count > 0 ? clientSnapshots.Values[clientSnapshots.Count - 1].rotation : targetComponent.localRotation; + if (!scale.HasValue) scale = clientSnapshots.Count > 0 ? clientSnapshots.Values[clientSnapshots.Count - 1].scale : targetComponent.localScale; + + // construct snapshot with batch timestamp to save bandwidth + NTSnapshot snapshot = new NTSnapshot( + timestamp, + NetworkTime.localTime, + position.Value, rotation.Value, scale.Value + ); + + // (optional) dynamic adjustment + if (dynamicAdjustment) + { + // set bufferTime on the fly. + // shows in inspector for easier debugging :) + bufferTimeMultiplier = SnapshotInterpolation.DynamicAdjustment( + sendInterval, + clientDeliveryTimeEma.StandardDeviation, + dynamicAdjustmentTolerance + ); + // Debug.Log($"[Client]: {name} delivery std={clientDeliveryTimeEma.StandardDeviation} bufferTimeMult := {bufferTimeMultiplier} "); + } + + // insert into the client buffer & initialize / adjust / catchup + SnapshotInterpolation.Insert( + clientSnapshots, + snapshot, + ref clientTimeline, + ref clientTimescale, + sendInterval, + bufferTime, + catchupSpeed, + slowdownSpeed, + ref clientDriftEma, + catchupNegativeThreshold, + catchupPositiveThreshold, + ref clientDeliveryTimeEma + ); + } + + // update ////////////////////////////////////////////////////////////// + void UpdateServer() + { + // broadcast to all clients each 'sendInterval' + // (client with authority will drop the rpc) + // NetworkTime.localTime for double precision until Unity has it too + // + // IMPORTANT: + // snapshot interpolation requires constant sending. + // DO NOT only send if position changed. for example: + // --- + // * client sends first position at t=0 + // * ... 10s later ... + // * client moves again, sends second position at t=10 + // --- + // * server gets first position at t=0 + // * server gets second position at t=10 + // * server moves from first to second within a time of 10s + // => would be a super slow move, instead of a wait & move. + // + // IMPORTANT: + // DO NOT send nulls if not changed 'since last send' either. we + // send unreliable and don't know which 'last send' the other end + // received successfully. + // + // Checks to ensure server only sends snapshots if object is + // on server authority(!clientAuthority) mode because on client + // authority mode snapshots are broadcasted right after the authoritative + // client updates server in the command function(see above), OR, + // since host does not send anything to update the server, any client + // authoritative movement done by the host will have to be broadcasted + // here by checking IsClientWithAuthority. + if (NetworkTime.localTime >= lastServerSendTime + sendInterval && + (!clientAuthority || IsClientWithAuthority)) + { + // send snapshot without timestamp. + // receiver gets it from batch timestamp to save bandwidth. + NTSnapshot snapshot = ConstructSnapshot(); +#if onlySyncOnChange_BANDWIDTH_SAVING + cachedSnapshotComparison = CompareSnapshots(snapshot); + if (cachedSnapshotComparison && hasSentUnchangedPosition && onlySyncOnChange) { return; } +#endif + +#if onlySyncOnChange_BANDWIDTH_SAVING + RpcServerToClientSync( + // only sync what the user wants to sync + syncPosition && positionChanged ? snapshot.position : default(Vector3?), + syncRotation && rotationChanged ? snapshot.rotation : default(Quaternion?), + syncScale && scaleChanged ? snapshot.scale : default(Vector3?) + ); +#else + RpcServerToClientSync( + // only sync what the user wants to sync + syncPosition ? snapshot.position : default(Vector3?), + syncRotation ? snapshot.rotation : default(Quaternion?), + syncScale ? snapshot.scale : default(Vector3?) + ); +#endif + + lastServerSendTime = NetworkTime.localTime; +#if onlySyncOnChange_BANDWIDTH_SAVING + if (cachedSnapshotComparison) + { + hasSentUnchangedPosition = true; + } + else + { + hasSentUnchangedPosition = false; + lastSnapshot = snapshot; + } +#endif + } + + // apply buffered snapshots IF client authority + // -> in server authority, server moves the object + // so no need to apply any snapshots there. + // -> don't apply for host mode player objects either, even if in + // client authority mode. if it doesn't go over the network, + // then we don't need to do anything. + if (clientAuthority && !hasAuthority) + { + if (serverSnapshots.Count > 0) + { + // compute snapshot interpolation & apply if any was spit out + if (SnapshotInterpolation.Step( + serverSnapshots, + Time.unscaledDeltaTime, + ref serverTimeline, + serverTimescale, + Interpolate, + out NTSnapshot computed)) + { + ApplySnapshot(computed); + } + } + } + } + + void UpdateClient() + { + // client authority, and local player (= allowed to move myself)? + if (IsClientWithAuthority) + { + // https://github.com/vis2k/Mirror/pull/2992/ + if (!NetworkClient.ready) return; + + // send to server each 'sendInterval' + // NetworkTime.localTime for double precision until Unity has it too + // + // IMPORTANT: + // snapshot interpolation requires constant sending. + // DO NOT only send if position changed. for example: + // --- + // * client sends first position at t=0 + // * ... 10s later ... + // * client moves again, sends second position at t=10 + // --- + // * server gets first position at t=0 + // * server gets second position at t=10 + // * server moves from first to second within a time of 10s + // => would be a super slow move, instead of a wait & move. + // + // IMPORTANT: + // DO NOT send nulls if not changed 'since last send' either. we + // send unreliable and don't know which 'last send' the other end + // received successfully. + if (NetworkTime.localTime >= lastClientSendTime + sendInterval) + { + // send snapshot without timestamp. + // receiver gets it from batch timestamp to save bandwidth. + NTSnapshot snapshot = ConstructSnapshot(); +#if onlySyncOnChange_BANDWIDTH_SAVING + cachedSnapshotComparison = CompareSnapshots(snapshot); + if (cachedSnapshotComparison && hasSentUnchangedPosition && onlySyncOnChange) { return; } +#endif + +#if onlySyncOnChange_BANDWIDTH_SAVING + CmdClientToServerSync( + // only sync what the user wants to sync + syncPosition && positionChanged ? snapshot.position : default(Vector3?), + syncRotation && rotationChanged ? snapshot.rotation : default(Quaternion?), + syncScale && scaleChanged ? snapshot.scale : default(Vector3?) + ); +#else + CmdClientToServerSync( + // only sync what the user wants to sync + syncPosition ? snapshot.position : default(Vector3?), + syncRotation ? snapshot.rotation : default(Quaternion?), + syncScale ? snapshot.scale : default(Vector3?) + ); +#endif + + lastClientSendTime = NetworkTime.localTime; +#if onlySyncOnChange_BANDWIDTH_SAVING + if (cachedSnapshotComparison) + { + hasSentUnchangedPosition = true; + } + else + { + hasSentUnchangedPosition = false; + lastSnapshot = snapshot; + } +#endif + } + } + // for all other clients (and for local player if !authority), + // we need to apply snapshots from the buffer + else + { + if (clientSnapshots.Count > 0) + { + // compute snapshot interpolation & apply if any was spit out + if (SnapshotInterpolation.Step( + clientSnapshots, + Time.unscaledDeltaTime, + ref clientTimeline, + clientTimescale, + Interpolate, + out NTSnapshot computed)) + { + ApplySnapshot(computed); + } + } + } + } + + void Update() + { + // if server then always sync to others. + if (isServer) UpdateServer(); + // 'else if' because host mode shouldn't send anything to server. + // it is the server. don't overwrite anything there. + else if (isClient) UpdateClient(); + } + + // common Teleport code for client->server and server->client + protected virtual void OnTeleport(Vector3 destination) + { + // reset any in-progress interpolation & buffers + Reset(); + + // set the new position. + // interpolation will automatically continue. + targetComponent.position = destination; + + // TODO + // what if we still receive a snapshot from before the interpolation? + // it could easily happen over unreliable. + // -> maybe add destination as first entry? + } + + // common Teleport code for client->server and server->client + protected virtual void OnTeleport(Vector3 destination, Quaternion rotation) + { + // reset any in-progress interpolation & buffers + Reset(); + + // set the new position. + // interpolation will automatically continue. + targetComponent.position = destination; + targetComponent.rotation = rotation; + + // TODO + // what if we still receive a snapshot from before the interpolation? + // it could easily happen over unreliable. + // -> maybe add destination as first entry? + } + + // server->client teleport to force position without interpolation. + // otherwise it would interpolate to a (far away) new position. + // => manually calling Teleport is the only 100% reliable solution. + [ClientRpc] + public void RpcTeleport(Vector3 destination) + { + // NOTE: even in client authority mode, the server is always allowed + // to teleport the player. for example: + // * CmdEnterPortal() might teleport the player + // * Some people use client authority with server sided checks + // so the server should be able to reset position if needed. + + // TODO what about host mode? + OnTeleport(destination); + } + + // server->client teleport to force position and rotation without interpolation. + // otherwise it would interpolate to a (far away) new position. + // => manually calling Teleport is the only 100% reliable solution. + [ClientRpc] + public void RpcTeleport(Vector3 destination, Quaternion rotation) + { + // NOTE: even in client authority mode, the server is always allowed + // to teleport the player. for example: + // * CmdEnterPortal() might teleport the player + // * Some people use client authority with server sided checks + // so the server should be able to reset position if needed. + + // TODO what about host mode? + OnTeleport(destination, rotation); + } + + // client->server teleport to force position without interpolation. + // otherwise it would interpolate to a (far away) new position. + // => manually calling Teleport is the only 100% reliable solution. + [Command] + public void CmdTeleport(Vector3 destination) + { + // client can only teleport objects that it has authority over. + if (!clientAuthority) return; + + // TODO what about host mode? + OnTeleport(destination); + + // if a client teleports, we need to broadcast to everyone else too + // TODO the teleported client should ignore the rpc though. + // otherwise if it already moved again after teleporting, + // the rpc would come a little bit later and reset it once. + // TODO or not? if client ONLY calls Teleport(pos), the position + // would only be set after the rpc. unless the client calls + // BOTH Teleport(pos) and targetComponent.position=pos + RpcTeleport(destination); + } + + // client->server teleport to force position and rotation without interpolation. + // otherwise it would interpolate to a (far away) new position. + // => manually calling Teleport is the only 100% reliable solution. + [Command] + public void CmdTeleport(Vector3 destination, Quaternion rotation) + { + // client can only teleport objects that it has authority over. + if (!clientAuthority) return; + + // TODO what about host mode? + OnTeleport(destination, rotation); + + // if a client teleports, we need to broadcast to everyone else too + // TODO the teleported client should ignore the rpc though. + // otherwise if it already moved again after teleporting, + // the rpc would come a little bit later and reset it once. + // TODO or not? if client ONLY calls Teleport(pos), the position + // would only be set after the rpc. unless the client calls + // BOTH Teleport(pos) and targetComponent.position=pos + RpcTeleport(destination, rotation); + } + + public virtual void Reset() + { + // disabled objects aren't updated anymore. + // so let's clear the buffers. + serverSnapshots.Clear(); + clientSnapshots.Clear(); + + // reset interpolation time too so we start at t=0 next time + serverTimeline = 0; + serverTimescale = 0; + clientTimeline = 0; + clientTimescale = 0; + } + + protected virtual void OnDisable() => Reset(); + protected virtual void OnEnable() => Reset(); + + protected virtual void OnValidate() + { + // thresholds need to be <0 and >0 + catchupNegativeThreshold = Math.Min(catchupNegativeThreshold, 0); + catchupPositiveThreshold = Math.Max(catchupPositiveThreshold, 0); + + // buffer limit should be at least multiplier to have enough in there + bufferSizeLimit = Mathf.Max((int)bufferTimeMultiplier, bufferSizeLimit); + } + + public override bool OnSerialize(NetworkWriter writer, bool initialState) + { + // sync target component's position on spawn. + // fixes https://github.com/vis2k/Mirror/pull/3051/ + // (Spawn message wouldn't sync NTChild positions either) + if (initialState) + { + if (syncPosition) writer.WriteVector3(targetComponent.localPosition); + if (syncRotation) writer.WriteQuaternion(targetComponent.localRotation); + if (syncScale) writer.WriteVector3(targetComponent.localScale); + return true; + } + return false; + } + + public override void OnDeserialize(NetworkReader reader, bool initialState) + { + // sync target component's position on spawn. + // fixes https://github.com/vis2k/Mirror/pull/3051/ + // (Spawn message wouldn't sync NTChild positions either) + if (initialState) + { + if (syncPosition) targetComponent.localPosition = reader.ReadVector3(); + if (syncRotation) targetComponent.localRotation = reader.ReadQuaternion(); + if (syncScale) targetComponent.localScale = reader.ReadVector3(); + } + } + + // OnGUI allocates even if it does nothing. avoid in release. +#if UNITY_EDITOR || DEVELOPMENT_BUILD + // debug /////////////////////////////////////////////////////////////// + protected virtual void OnGUI() + { + if (!showOverlay) return; + + // show data next to player for easier debugging. this is very useful! + // IMPORTANT: this is basically an ESP hack for shooter games. + // DO NOT make this available with a hotkey in release builds + if (!Debug.isDebugBuild) return; + + // project position to screen + Vector3 point = Camera.main.WorldToScreenPoint(targetComponent.position); + + // enough alpha, in front of camera and in screen? + if (point.z >= 0 && Utils.IsPointInScreen(point)) + { + GUI.color = overlayColor; + GUILayout.BeginArea(new Rect(point.x, Screen.height - point.y, 200, 100)); + + // always show both client & server buffers so it's super + // obvious if we accidentally populate both. + if (serverSnapshots.Count > 0) + { + GUILayout.Label($"Server Buffer:{serverSnapshots.Count}"); + GUILayout.Label($"Server Timescale:{serverTimescale * 100:F2}%"); + } + + if (clientSnapshots.Count > 0) + { + GUILayout.Label($"Client Buffer:{clientSnapshots.Count}"); + GUILayout.Label($"Client Timescale:{clientTimescale * 100:F2}%"); + } + + GUILayout.EndArea(); + GUI.color = Color.white; + } + } + + protected virtual void DrawGizmos(SortedList buffer) + { + // only draw if we have at least two entries + if (buffer.Count < 2) return; + + // calculate threshold for 'old enough' snapshots + double threshold = NetworkTime.localTime - bufferTime; + Color oldEnoughColor = new Color(0, 1, 0, 0.5f); + Color notOldEnoughColor = new Color(0.5f, 0.5f, 0.5f, 0.3f); + + // draw the whole buffer for easier debugging. + // it's worth seeing how much we have buffered ahead already + for (int i = 0; i < buffer.Count; ++i) + { + // color depends on if old enough or not + NTSnapshot entry = buffer.Values[i]; + bool oldEnough = entry.localTime <= threshold; + Gizmos.color = oldEnough ? oldEnoughColor : notOldEnoughColor; + Gizmos.DrawCube(entry.position, Vector3.one); + } + + // extra: lines between start<->position<->goal + Gizmos.color = Color.green; + Gizmos.DrawLine(buffer.Values[0].position, targetComponent.position); + Gizmos.color = Color.white; + Gizmos.DrawLine(targetComponent.position, buffer.Values[1].position); + } + + protected virtual void OnDrawGizmos() + { + // This fires in edit mode but that spams NRE's so check isPlaying + if (!Application.isPlaying) return; + if (!showGizmos) return; + + if (isServer) DrawGizmos(serverSnapshots); + if (isClient) DrawGizmos(clientSnapshots); + } +#endif + } +} diff --git a/UnityProject/Assets/Mirror/Components/NetworkTransformBase.cs.meta b/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransformBase.cs.meta similarity index 79% rename from UnityProject/Assets/Mirror/Components/NetworkTransformBase.cs.meta rename to UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransformBase.cs.meta index 3ce0661..ab649d9 100644 --- a/UnityProject/Assets/Mirror/Components/NetworkTransformBase.cs.meta +++ b/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransformBase.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransformChild.cs b/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransformChild.cs new file mode 100644 index 0000000..8032506 --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransformChild.cs @@ -0,0 +1,14 @@ +// A component to synchronize the position of child transforms of networked objects. +// There must be a NetworkTransform on the root object of the hierarchy. There can be multiple NetworkTransformChild components on an object. This does not use physics for synchronization, it simply synchronizes the localPosition and localRotation of the child transform and lerps towards the recieved values. +using UnityEngine; + +namespace Mirror +{ + [AddComponentMenu("Network/Network Transform Child")] + public class NetworkTransformChild : NetworkTransformBase + { + [Header("Target")] + public Transform target; + protected override Transform targetComponent => target; + } +} diff --git a/UnityProject/Assets/Mirror/Components/NetworkTransformChild.cs.meta b/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransformChild.cs.meta similarity index 79% rename from UnityProject/Assets/Mirror/Components/NetworkTransformChild.cs.meta rename to UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransformChild.cs.meta index 4fce429..ae36756 100644 --- a/UnityProject/Assets/Mirror/Components/NetworkTransformChild.cs.meta +++ b/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransformChild.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransformSnapshot.cs b/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransformSnapshot.cs new file mode 100644 index 0000000..9935b4b --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransformSnapshot.cs @@ -0,0 +1,64 @@ +// snapshot for snapshot interpolation +// https://gafferongames.com/post/snapshot_interpolation/ +// position, rotation, scale for compatibility for now. +using UnityEngine; + +namespace Mirror +{ + // NetworkTransform Snapshot + public struct NTSnapshot : Snapshot + { + // time or sequence are needed to throw away older snapshots. + // + // glenn fiedler starts with a 16 bit sequence number. + // supposedly this is meant as a simplified example. + // in the end we need the remote timestamp for accurate interpolation + // and buffering over time. + // + // note: in theory, IF server sends exactly(!) at the same interval then + // the 16 bit ushort timestamp would be enough to calculate the + // remote time (sequence * sendInterval). but Unity's update is + // not guaranteed to run on the exact intervals / do catchup. + // => remote timestamp is better for now + // + // [REMOTE TIME, NOT LOCAL TIME] + // => DOUBLE for long term accuracy & batching gives us double anyway + public double remoteTime { get; set; } + // the local timestamp (when we received it) + // used to know if the first two snapshots are old enough to start. + public double localTime { get; set; } + + public Vector3 position; + public Quaternion rotation; + public Vector3 scale; + + public NTSnapshot(double remoteTime, double localTime, Vector3 position, Quaternion rotation, Vector3 scale) + { + this.remoteTime = remoteTime; + this.localTime = localTime; + this.position = position; + this.rotation = rotation; + this.scale = scale; + } + + public static NTSnapshot Interpolate(NTSnapshot from, NTSnapshot to, double t) + { + // NOTE: + // Vector3 & Quaternion components are float anyway, so we can + // keep using the functions with 't' as float instead of double. + return new NTSnapshot( + // interpolated snapshot is applied directly. don't need timestamps. + 0, 0, + // lerp position/rotation/scale unclamped in case we ever need + // to extrapolate. atm SnapshotInterpolation never does. + Vector3.LerpUnclamped(from.position, to.position, (float)t), + // IMPORTANT: LerpUnclamped(0, 60, 1.5) extrapolates to ~86. + // SlerpUnclamped(0, 60, 1.5) extrapolates to 90! + // (0, 90, 1.5) is even worse. for Lerp. + // => Slerp works way better for our euler angles. + Quaternion.SlerpUnclamped(from.rotation, to.rotation, (float)t), + Vector3.LerpUnclamped(from.scale, to.scale, (float)t) + ); + } + } +} diff --git a/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransformSnapshot.cs.meta b/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransformSnapshot.cs.meta new file mode 100644 index 0000000..f43458f --- /dev/null +++ b/UnityProject/Assets/Mirror/Components/NetworkTransform2k/NetworkTransformSnapshot.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d3dae77b43dc4e1dbb2012924b2da79c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Components/NetworkTransformBase.cs b/UnityProject/Assets/Mirror/Components/NetworkTransformBase.cs deleted file mode 100644 index f390111..0000000 --- a/UnityProject/Assets/Mirror/Components/NetworkTransformBase.cs +++ /dev/null @@ -1,563 +0,0 @@ -// vis2k: -// base class for NetworkTransform and NetworkTransformChild. -// New method is simple and stupid. No more 1500 lines of code. -// -// Server sends current data. -// Client saves it and interpolates last and latest data points. -// Update handles transform movement / rotation -// FixedUpdate handles rigidbody movement / rotation -// -// Notes: -// * Built-in Teleport detection in case of lags / teleport / obstacles -// * Quaternion > EulerAngles because gimbal lock and Quaternion.Slerp -// * Syncs XYZ. Works 3D and 2D. Saving 4 bytes isn't worth 1000 lines of code. -// * Initial delay might happen if server sends packet immediately after moving -// just 1cm, hence we move 1cm and then wait 100ms for next packet -// * Only way for smooth movement is to use a fixed movement speed during -// interpolation. interpolation over time is never that good. -// -using System; -using UnityEngine; - -namespace Mirror -{ - public abstract class NetworkTransformBase : NetworkBehaviour - { - [Header("Authority")] - [Tooltip("Set to true if moves come from owner client, set to false if moves always come from server")] - public bool clientAuthority; - - /// - /// We need to store this locally on the server so clients can't request Authority when ever they like - /// - bool clientAuthorityBeforeTeleport; - - // Is this a client with authority over this transform? - // This component could be on the player object or any object that has been assigned authority to this client. - bool IsClientWithAuthority => hasAuthority && clientAuthority; - - // Sensitivity is added for VR where human players tend to have micro movements so this can quiet down - // the network traffic. Additionally, rigidbody drift should send less traffic, e.g very slow sliding / rolling. - [Header("Sensitivity")] - [Tooltip("Changes to the transform must exceed these values to be transmitted on the network.")] - public float localPositionSensitivity = .01f; - [Tooltip("If rotation exceeds this angle, it will be transmitted on the network")] - public float localRotationSensitivity = .01f; - [Tooltip("Changes to the transform must exceed these values to be transmitted on the network.")] - public float localScaleSensitivity = .01f; - - [Header("Compression")] - [Tooltip("Enables smallest-three quaternion compression, which is lossy. Great for 3D, not great for 2D where minimal sprite rotations would look wobbly.")] - public bool compressRotation; // disabled by default to not break 2D projects - - [Header("Interpolation")] - [Tooltip("Set to true if scale should be interpolated, false is ideal for instant sprite flipping.")] - public bool interpolateScale = true; - [Tooltip("Set to true if rotation should be interpolated, false is ideal for instant turning, common in retro 2d style games")] - public bool interpolateRotation = true; - [Tooltip("Set to true if position should be interpolated, false is ideal for grid bassed movement")] - public bool interpolatePosition = true; - - [Header("Synchronization")] - // It should be very rare cases that people want to continuously sync scale, true by default to not break previous projects that use it - // Users in most scenarios are best to send change of scale via cmd/rpc, syncvar/hooks, only once, and when required. Saves instant 12 bytes (25% of NT bandwidth!) - [Tooltip("Set to false to not continuously send scale data, and save bandwidth.")] - public bool syncScale = true; - - // target transform to sync. can be on a child. - protected abstract Transform targetComponent { get; } - - // server - Vector3 lastPosition; - Quaternion lastRotation; - Vector3 lastScale; - - // client - public class DataPoint - { - public float timeStamp; - // use local position/rotation for VR support - public Vector3 localPosition; - public Quaternion localRotation; - public Vector3 localScale; - public float movementSpeed; - } - // interpolation start and goal - DataPoint start; - DataPoint goal; - - // local authority send time - float lastClientSendTime; - - // serialization is needed by OnSerialize and by manual sending from authority - // public only for tests - public static void SerializeIntoWriter(NetworkWriter writer, Vector3 position, Quaternion rotation, Vector3 scale, bool compressRotation, bool syncScale) - { - // serialize position, rotation, scale - // => compress rotation from 4*4=16 to 4 bytes - // => less bandwidth = better CCU tests / scale - writer.WriteVector3(position); - if (compressRotation) - { - // smalles three compression for 3D - writer.WriteUInt(Compression.CompressQuaternion(rotation)); - } - else - { - // uncompressed for 2D - writer.WriteQuaternion(rotation); - } - if (syncScale) { writer.WriteVector3(scale); } - } - - public override bool OnSerialize(NetworkWriter writer, bool initialState) - { - // use local position/rotation/scale for VR support - SerializeIntoWriter(writer, targetComponent.localPosition, targetComponent.localRotation, targetComponent.localScale, compressRotation, syncScale); - return true; - } - - // try to estimate movement speed for a data point based on how far it - // moved since the previous one - // => if this is the first time ever then we use our best guess: - // -> delta based on transform.localPosition - // -> elapsed based on send interval hoping that it roughly matches - static float EstimateMovementSpeed(DataPoint from, DataPoint to, Transform transform, float sendInterval) - { - Vector3 delta = to.localPosition - (from != null ? from.localPosition : transform.localPosition); - float elapsed = from != null ? to.timeStamp - from.timeStamp : sendInterval; - // avoid NaN - return elapsed > 0 ? delta.magnitude / elapsed : 0; - } - - // serialization is needed by OnSerialize and by manual sending from authority - void DeserializeFromReader(NetworkReader reader) - { - // put it into a data point immediately - DataPoint temp = new DataPoint - { - // deserialize position, rotation, scale - // (rotation is optionally compressed) - localPosition = reader.ReadVector3(), - localRotation = compressRotation - ? Compression.DecompressQuaternion(reader.ReadUInt()) - : reader.ReadQuaternion(), - // use current target scale, so we can check boolean and reader later, to see if the data is actually sent. - localScale = targetComponent.localScale, - timeStamp = Time.time - }; - - if (syncScale) - { - // Reader length is checked here, 12 is used as thats the current Vector3 (3 floats) amount. - // In rare cases people may do mis-matched builds, log useful warning message, and then do not process missing scale data. - if (reader.Length >= 12) { temp.localScale = reader.ReadVector3(); } - else { Debug.LogWarning("Reader length does not contain enough data for a scale, please check that both server and client builds syncScale booleans match.", this); } - } - - // movement speed: based on how far it moved since last time - // has to be calculated before 'start' is overwritten - temp.movementSpeed = EstimateMovementSpeed(goal, temp, targetComponent, syncInterval); - - // reassign start wisely - // -> first ever data point? then make something up for previous one - // so that we can start interpolation without waiting for next. - if (start == null) - { - start = new DataPoint - { - timeStamp = Time.time - syncInterval, - // local position/rotation for VR support - localPosition = targetComponent.localPosition, - localRotation = targetComponent.localRotation, - localScale = targetComponent.localScale, - movementSpeed = temp.movementSpeed - }; - } - // -> second or nth data point? then update previous, but: - // we start at where ever we are right now, so that it's - // perfectly smooth and we don't jump anywhere - // - // example if we are at 'x': - // - // A--x->B - // - // and then receive a new point C: - // - // A--x--B - // | - // | - // C - // - // then we don't want to just jump to B and start interpolation: - // - // x - // | - // | - // C - // - // we stay at 'x' and interpolate from there to C: - // - // x..B - // \ . - // \. - // C - // - else - { - float oldDistance = Vector3.Distance(start.localPosition, goal.localPosition); - float newDistance = Vector3.Distance(goal.localPosition, temp.localPosition); - - start = goal; - - // teleport / lag / obstacle detection: only continue at current - // position if we aren't too far away - // - // local position/rotation for VR support - if (Vector3.Distance(targetComponent.localPosition, start.localPosition) < oldDistance + newDistance) - { - start.localPosition = targetComponent.localPosition; - start.localRotation = targetComponent.localRotation; - start.localScale = targetComponent.localScale; - } - } - - // set new destination in any case. new data is best data. - goal = temp; - } - - public override void OnDeserialize(NetworkReader reader, bool initialState) - { - // deserialize - DeserializeFromReader(reader); - } - - // local authority client sends sync message to server for broadcasting - [Command(channel = Channels.Unreliable)] - void CmdClientToServerSync(ArraySegment payload) - { - // Ignore messages from client if not in client authority mode - if (!clientAuthority) - return; - - // deserialize payload - using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(payload)) - DeserializeFromReader(networkReader); - - // server-only mode does no interpolation to save computations, - // but let's set the position directly - if (isServer && !isClient) - ApplyPositionRotationScale(goal.localPosition, goal.localRotation, goal.localScale); - - // set dirty so that OnSerialize broadcasts it - SetDirtyBit(1UL); - } - - // where are we in the timeline between start and goal? [0,1] - static float CurrentInterpolationFactor(DataPoint start, DataPoint goal) - { - if (start != null) - { - float difference = goal.timeStamp - start.timeStamp; - - // the moment we get 'goal', 'start' is supposed to - // start, so elapsed time is based on: - float elapsed = Time.time - goal.timeStamp; - // avoid NaN - return difference > 0 ? elapsed / difference : 0; - } - return 0; - } - - Vector3 InterpolatePosition(DataPoint start, DataPoint goal, Vector3 currentPosition) - { - if (!interpolatePosition) - { - return goal.localPosition; - } - else if (start != null) - { - // Option 1: simply interpolate based on time. but stutter - // will happen, it's not that smooth. especially noticeable if - // the camera automatically follows the player - // float t = CurrentInterpolationFactor(); - // return Vector3.Lerp(start.position, goal.position, t); - - // Option 2: always += speed - // -> speed is 0 if we just started after idle, so always use max - // for best results - float speed = Mathf.Max(start.movementSpeed, goal.movementSpeed); - return Vector3.MoveTowards(currentPosition, goal.localPosition, speed * Time.deltaTime); - } - return currentPosition; - } - - Quaternion InterpolateRotation(DataPoint start, DataPoint goal, Quaternion defaultRotation) - { - if (!interpolateRotation) - { - return goal.localRotation; - } - else if (start != null) - { - float t = CurrentInterpolationFactor(start, goal); - return Quaternion.Slerp(start.localRotation, goal.localRotation, t); - } - return defaultRotation; - } - - Vector3 InterpolateScale(DataPoint start, DataPoint goal, Vector3 currentScale) - { - if (!syncScale) - { - return currentScale; - } - else if (!interpolateScale) - { - return goal.localScale; - } - else if (interpolateScale && start != null ) - { - float t = CurrentInterpolationFactor(start, goal); - return Vector3.Lerp(start.localScale, goal.localScale, t); - } - else - { - return currentScale; - } - } - - // teleport / lag / stuck detection - // -> checking distance is not enough since there could be just a tiny - // fence between us and the goal - // -> checking time always works, this way we just teleport if we still - // didn't reach the goal after too much time has elapsed - bool NeedsTeleport() - { - // calculate time between the two data points - float startTime = start != null ? start.timeStamp : Time.time - syncInterval; - float goalTime = goal != null ? goal.timeStamp : Time.time; - float difference = goalTime - startTime; - float timeSinceGoalReceived = Time.time - goalTime; - return timeSinceGoalReceived > difference * 5; - } - - // moved since last time we checked it? - bool HasEitherMovedRotatedScaled() - { - // moved or rotated or scaled? - // local position/rotation/scale for VR support - bool moved = Vector3.Distance(lastPosition, targetComponent.localPosition) > localPositionSensitivity; - bool scaled = Vector3.Distance(lastScale, targetComponent.localScale) > localScaleSensitivity; - bool rotated = Quaternion.Angle(lastRotation, targetComponent.localRotation) > localRotationSensitivity; - - // save last for next frame to compare - // (only if change was detected. otherwise slow moving objects might - // never sync because of C#'s float comparison tolerance. see also: - // https://github.com/vis2k/Mirror/pull/428) - bool change = moved || rotated || scaled; - if (change) - { - // local position/rotation for VR support - lastPosition = targetComponent.localPosition; - lastRotation = targetComponent.localRotation; - lastScale = targetComponent.localScale; - } - return change; - } - - // set position carefully depending on the target component - void ApplyPositionRotationScale(Vector3 position, Quaternion rotation, Vector3 scale) - { - // local position/rotation for VR support - targetComponent.localPosition = position; - targetComponent.localRotation = rotation; - targetComponent.localScale = scale; - } - - void Update() - { - // if server then always sync to others. - if (isServer) - { - // just use OnSerialize via SetDirtyBit only sync when position - // changed. set dirty bits 0 or 1 - SetDirtyBit(HasEitherMovedRotatedScaled() ? 1UL : 0UL); - } - - // no 'else if' since host mode would be both - if (isClient) - { - // send to server if we have local authority (and aren't the server) - // -> only if connectionToServer has been initialized yet too - if (!isServer && IsClientWithAuthority) - { - // check only each 'syncInterval' - if (Time.time - lastClientSendTime >= syncInterval) - { - if (HasEitherMovedRotatedScaled()) - { - // serialize - // local position/rotation for VR support - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - SerializeIntoWriter(writer, targetComponent.localPosition, targetComponent.localRotation, targetComponent.localScale, compressRotation, syncScale); - - // send to server - CmdClientToServerSync(writer.ToArraySegment()); - } - } - lastClientSendTime = Time.time; - } - } - - // apply interpolation on client for all players - // unless this client has authority over the object. could be - // himself or another object that he was assigned authority over - if (!IsClientWithAuthority) - { - // received one yet? (initialized?) - if (goal != null) - { - // teleport or interpolate - if (NeedsTeleport()) - { - // local position/rotation for VR support - ApplyPositionRotationScale(goal.localPosition, goal.localRotation, goal.localScale); - - // reset data points so we don't keep interpolating - start = null; - goal = null; - } - else - { - // local position/rotation for VR support - ApplyPositionRotationScale(InterpolatePosition(start, goal, targetComponent.localPosition), - InterpolateRotation(start, goal, targetComponent.localRotation), - InterpolateScale(start, goal, targetComponent.localScale)); - } - } - } - } - } - - #region Server Teleport (force move player) - /// - /// Server side teleportation. - /// This method will override this GameObject's current Transform.Position to the Vector3 you have provided - /// and send it to all other Clients to override it at their side too. - /// - /// Where to teleport this GameObject - [Server] - public void ServerTeleport(Vector3 position) - { - Quaternion rotation = transform.rotation; - ServerTeleport(position, rotation); - } - - /// - /// Server side teleportation. - /// This method will override this GameObject's current Transform.Position and Transform.Rotation - /// to the Vector3 you have provided - /// and send it to all other Clients to override it at their side too. - /// - /// Where to teleport this GameObject - /// Which rotation to set this GameObject - [Server] - public void ServerTeleport(Vector3 position, Quaternion rotation) - { - // To prevent applying the position updates received from client (if they have ClientAuth) while being teleported. - - // clientAuthorityBeforeTeleport defaults to false when not teleporting, if it is true then it means that teleport was previously called but not finished - // therefore we should keep it as true so that 2nd teleport call doesn't clear authority - clientAuthorityBeforeTeleport = clientAuthority || clientAuthorityBeforeTeleport; - clientAuthority = false; - - DoTeleport(position, rotation); - - // tell all clients about new values - RpcTeleport(position, rotation, clientAuthorityBeforeTeleport); - } - - void DoTeleport(Vector3 newPosition, Quaternion newRotation) - { - transform.position = newPosition; - transform.rotation = newRotation; - - // Since we are overriding the position we don't need a goal and start. - // Reset them to null for fresh start - goal = null; - start = null; - lastPosition = newPosition; - lastRotation = newRotation; - } - - [ClientRpc] - void RpcTeleport(Vector3 newPosition, Quaternion newRotation, bool isClientAuthority) - { - DoTeleport(newPosition, newRotation); - - // only send finished if is owner and is ClientAuthority on server - if (hasAuthority && isClientAuthority) - CmdTeleportFinished(); - } - - /// - /// This RPC will be invoked on server after client finishes overriding the position. - /// - /// - [Command] - void CmdTeleportFinished() - { - if (clientAuthorityBeforeTeleport) - { - clientAuthority = true; - - // reset value so doesn't effect future calls, see note in ServerTeleport - clientAuthorityBeforeTeleport = false; - } - else - { - Debug.LogWarning("Client called TeleportFinished when clientAuthority was false on server", this); - } - } - #endregion - - static void DrawDataPointGizmo(DataPoint data, Color color) - { - // use a little offset because transform.localPosition might be in - // the ground in many cases - Vector3 offset = Vector3.up * 0.01f; - - // draw position - Gizmos.color = color; - Gizmos.DrawSphere(data.localPosition + offset, 0.5f); - - // draw forward and up - // like unity move tool - Gizmos.color = Color.blue; - Gizmos.DrawRay(data.localPosition + offset, data.localRotation * Vector3.forward); - - // like unity move tool - Gizmos.color = Color.green; - Gizmos.DrawRay(data.localPosition + offset, data.localRotation * Vector3.up); - } - - static void DrawLineBetweenDataPoints(DataPoint data1, DataPoint data2, Color color) - { - Gizmos.color = color; - Gizmos.DrawLine(data1.localPosition, data2.localPosition); - } - - // draw the data points for easier debugging - void OnDrawGizmos() - { - // draw start and goal points - if (start != null) DrawDataPointGizmo(start, Color.gray); - if (goal != null) DrawDataPointGizmo(goal, Color.white); - - // draw line between them - if (start != null && goal != null) DrawLineBetweenDataPoints(start, goal, Color.cyan); - } - } -} diff --git a/UnityProject/Assets/Mirror/Components/NetworkTransformChild.cs b/UnityProject/Assets/Mirror/Components/NetworkTransformChild.cs deleted file mode 100644 index 3ad4287..0000000 --- a/UnityProject/Assets/Mirror/Components/NetworkTransformChild.cs +++ /dev/null @@ -1,18 +0,0 @@ -using UnityEngine; - -namespace Mirror -{ - /// - /// A component to synchronize the position of child transforms of networked objects. - /// There must be a NetworkTransform on the root object of the hierarchy. There can be multiple NetworkTransformChild components on an object. This does not use physics for synchronization, it simply synchronizes the localPosition and localRotation of the child transform and lerps towards the received values. - /// - [AddComponentMenu("Network/NetworkTransformChild")] - [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-transform-child")] - public class NetworkTransformChild : NetworkTransformBase - { - [Header("Target")] - public Transform target; - - protected override Transform targetComponent => target; - } -} diff --git a/UnityProject/Assets/Mirror/Editor.meta b/UnityProject/Assets/Mirror/Editor.meta index 0c23e8c..f679511 100644 --- a/UnityProject/Assets/Mirror/Editor.meta +++ b/UnityProject/Assets/Mirror/Editor.meta @@ -3,6 +3,6 @@ guid: 2539267b6934a4026a505690a1e1eda2 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/AndroidManifestHelper.cs b/UnityProject/Assets/Mirror/Editor/AndroidManifestHelper.cs new file mode 100644 index 0000000..c76359a --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/AndroidManifestHelper.cs @@ -0,0 +1,113 @@ +// Android NetworkDiscovery Multicast fix +// https://github.com/vis2k/Mirror/pull/2887 +using UnityEditor; +using UnityEngine; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; +using System.Xml; +using System.IO; +#if UNITY_ANDROID +using UnityEditor.Android; +#endif + + +[InitializeOnLoad] +public class AndroidManifestHelper : IPreprocessBuildWithReport, IPostprocessBuildWithReport +#if UNITY_ANDROID + , IPostGenerateGradleAndroidProject +#endif +{ + public int callbackOrder { get { return 99999; } } + +#if UNITY_ANDROID + public void OnPostGenerateGradleAndroidProject(string path) + { + string manifestFolder = Path.Combine(path, "src/main"); + string sourceFile = manifestFolder + "/AndroidManifest.xml"; + // Load android manifest file + XmlDocument doc = new XmlDocument(); + doc.Load(sourceFile); + + string androidNamepsaceURI; + XmlElement element = (XmlElement)doc.SelectSingleNode("/manifest"); + if (element == null) + { + UnityEngine.Debug.LogError("Could not find manifest tag in android manifest."); + return; + } + + // Get android namespace URI from the manifest + androidNamepsaceURI = element.GetAttribute("xmlns:android"); + if (string.IsNullOrEmpty(androidNamepsaceURI)) + { + UnityEngine.Debug.LogError("Could not find Android Namespace in manifest."); + return; + } + AddOrRemoveTag(doc, + androidNamepsaceURI, + "/manifest", + "uses-permission", + "android.permission.CHANGE_WIFI_MULTICAST_STATE", + true, + false); + AddOrRemoveTag(doc, + androidNamepsaceURI, + "/manifest", + "uses-permission", + "android.permission.INTERNET", + true, + false); + doc.Save(sourceFile); + } +#endif + + static void AddOrRemoveTag(XmlDocument doc, string @namespace, string path, string elementName, string name, bool required, bool modifyIfFound, params string[] attrs) // name, value pairs + { + var nodes = doc.SelectNodes(path + "/" + elementName); + XmlElement element = null; + foreach (XmlElement e in nodes) + { + if (name == null || name == e.GetAttribute("name", @namespace)) + { + element = e; + break; + } + } + + if (required) + { + if (element == null) + { + var parent = doc.SelectSingleNode(path); + element = doc.CreateElement(elementName); + element.SetAttribute("name", @namespace, name); + parent.AppendChild(element); + } + + for (int i = 0; i < attrs.Length; i += 2) + { + if (modifyIfFound || string.IsNullOrEmpty(element.GetAttribute(attrs[i], @namespace))) + { + if (attrs[i + 1] != null) + { + element.SetAttribute(attrs[i], @namespace, attrs[i + 1]); + } + else + { + element.RemoveAttribute(attrs[i], @namespace); + } + } + } + } + else + { + if (element != null && modifyIfFound) + { + element.ParentNode.RemoveChild(element); + } + } + } + + public void OnPostprocessBuild(BuildReport report) {} + public void OnPreprocessBuild(BuildReport report) {} +} diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Core/IgnoranceClient.cs.meta b/UnityProject/Assets/Mirror/Editor/AndroidManifestHelper.cs.meta similarity index 83% rename from UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Core/IgnoranceClient.cs.meta rename to UnityProject/Assets/Mirror/Editor/AndroidManifestHelper.cs.meta index fe965ca..1281aea 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Core/IgnoranceClient.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/AndroidManifestHelper.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b7b9e2c091c3d42439840a02fe700252 +guid: 80cc70189403d7444bbffd185ca28462 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/UnityProject/Assets/Mirror/Editor/EditorHelper.cs b/UnityProject/Assets/Mirror/Editor/EditorHelper.cs index 7870d62..b10c7b0 100644 --- a/UnityProject/Assets/Mirror/Editor/EditorHelper.cs +++ b/UnityProject/Assets/Mirror/Editor/EditorHelper.cs @@ -10,8 +10,8 @@ namespace Mirror { string typeName = typeof(T).Name; - string[] guidsFound = AssetDatabase.FindAssets($"t:Script " + typeName); - if (guidsFound.Length >= 1 && !string.IsNullOrEmpty(guidsFound[0])) + string[] guidsFound = AssetDatabase.FindAssets($"t:Script {typeName}"); + if (guidsFound.Length >= 1 && !string.IsNullOrWhiteSpace(guidsFound[0])) { if (guidsFound.Length > 1) { diff --git a/UnityProject/Assets/Mirror/Editor/EditorHelper.cs.meta b/UnityProject/Assets/Mirror/Editor/EditorHelper.cs.meta index 02aec40..a1cd814 100644 --- a/UnityProject/Assets/Mirror/Editor/EditorHelper.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/EditorHelper.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Empty.meta b/UnityProject/Assets/Mirror/Editor/Empty.meta index 2c2233e..ee87976 100644 --- a/UnityProject/Assets/Mirror/Editor/Empty.meta +++ b/UnityProject/Assets/Mirror/Editor/Empty.meta @@ -3,6 +3,6 @@ guid: 62c8dc5bb12bbc6428bb66ccbac57000 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Empty/EnterPlayModeSettingsCheck.cs b/UnityProject/Assets/Mirror/Editor/Empty/EnterPlayModeSettingsCheck.cs new file mode 100644 index 0000000..18ab111 --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Empty/EnterPlayModeSettingsCheck.cs @@ -0,0 +1 @@ +// removed 2021-12-12 diff --git a/UnityProject/Assets/Mirror/Editor/EnterPlayModeSettingsCheck.cs.meta b/UnityProject/Assets/Mirror/Editor/Empty/EnterPlayModeSettingsCheck.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Editor/EnterPlayModeSettingsCheck.cs.meta rename to UnityProject/Assets/Mirror/Editor/Empty/EnterPlayModeSettingsCheck.cs.meta index 49a4298..79a200d 100644 --- a/UnityProject/Assets/Mirror/Editor/EnterPlayModeSettingsCheck.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Empty/EnterPlayModeSettingsCheck.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Empty/LogLevelWindow.cs.meta b/UnityProject/Assets/Mirror/Editor/Empty/LogLevelWindow.cs.meta index a068358..b8cbaeb 100644 --- a/UnityProject/Assets/Mirror/Editor/Empty/LogLevelWindow.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Empty/LogLevelWindow.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Empty/Logging.meta b/UnityProject/Assets/Mirror/Editor/Empty/Logging.meta index 0919d1e..257467f 100644 --- a/UnityProject/Assets/Mirror/Editor/Empty/Logging.meta +++ b/UnityProject/Assets/Mirror/Editor/Empty/Logging.meta @@ -3,6 +3,6 @@ guid: 4d97731cd74ac8b4b8aad808548ef9cd folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Empty/Logging/LogLevelWindow.cs.meta b/UnityProject/Assets/Mirror/Editor/Empty/Logging/LogLevelWindow.cs.meta index aff9fad..832876f 100644 --- a/UnityProject/Assets/Mirror/Editor/Empty/Logging/LogLevelWindow.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Empty/Logging/LogLevelWindow.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Empty/Logging/LogLevelsGUI.cs.meta b/UnityProject/Assets/Mirror/Editor/Empty/Logging/LogLevelsGUI.cs.meta index bc9e68c..3214b08 100644 --- a/UnityProject/Assets/Mirror/Editor/Empty/Logging/LogLevelsGUI.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Empty/Logging/LogLevelsGUI.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Empty/Logging/LogSettingsEditor.cs.meta b/UnityProject/Assets/Mirror/Editor/Empty/Logging/LogSettingsEditor.cs.meta index 1a456c5..2c1fac4 100644 --- a/UnityProject/Assets/Mirror/Editor/Empty/Logging/LogSettingsEditor.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Empty/Logging/LogSettingsEditor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Empty/Logging/NetworkLogSettingsEditor.cs.meta b/UnityProject/Assets/Mirror/Editor/Empty/Logging/NetworkLogSettingsEditor.cs.meta index 21d0db6..b4c277d 100644 --- a/UnityProject/Assets/Mirror/Editor/Empty/Logging/NetworkLogSettingsEditor.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Empty/Logging/NetworkLogSettingsEditor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Empty/ScriptableObjectUtility.cs.meta b/UnityProject/Assets/Mirror/Editor/Empty/ScriptableObjectUtility.cs.meta index c1cfe35..a1a0af3 100644 --- a/UnityProject/Assets/Mirror/Editor/Empty/ScriptableObjectUtility.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Empty/ScriptableObjectUtility.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/EnterPlayModeSettingsCheck.cs b/UnityProject/Assets/Mirror/Editor/EnterPlayModeSettingsCheck.cs deleted file mode 100644 index 30b1acd..0000000 --- a/UnityProject/Assets/Mirror/Editor/EnterPlayModeSettingsCheck.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Unity 2019.3 has an experimental 'disable domain reload on play' -// feature. keeping any global state between sessions will break -// Mirror and most of our user's projects. don't allow it for now. -// https://blogs.unity3d.com/2019/11/05/enter-play-mode-faster-in-unity-2019-3/ -using UnityEditor; -using UnityEngine; - -namespace Mirror -{ - public class EnterPlayModeSettingsCheck : MonoBehaviour - { - [InitializeOnLoadMethod] - static void OnInitializeOnLoad() - { -#if UNITY_2019_3_OR_NEWER - // We can't support experimental "Enter Play Mode Options" mode - // Check immediately on load, and before entering play mode, and warn the user - CheckPlayModeOptions(); -#endif - - // Hook this event to see if we have a good weave every time - // user attempts to enter play mode or tries to do a build - EditorApplication.playModeStateChanged += OnPlayModeStateChanged; - } - - static void OnPlayModeStateChanged(PlayModeStateChange state) - { - // Per Unity docs, this fires "when exiting edit mode before the Editor is in play mode". - // This doesn't fire when closing the editor. - if (state == PlayModeStateChange.ExitingEditMode) - { - CheckSuccessfulWeave(); - -#if UNITY_2019_3_OR_NEWER - // We can't support experimental "Enter Play Mode Options" mode - // Check and prevent entering play mode if enabled - CheckPlayModeOptions(); -#endif - } - } - - static void CheckSuccessfulWeave() - { - // Check if last weave result was successful - if (!SessionState.GetBool("MIRROR_WEAVE_SUCCESS", false)) - { - // Last weave result was a failure...try to weave again - // Faults will show in the console that may have been cleared by "Clear on Play" - SessionState.SetBool("MIRROR_WEAVE_SUCCESS", true); - Weaver.CompilationFinishedHook.WeaveExistingAssemblies(); - - // Did that clear things up for us? - if (!SessionState.GetBool("MIRROR_WEAVE_SUCCESS", false)) - { - // Nope, still failed, and console has the issues logged - Debug.LogError("Can't enter play mode until weaver issues are resolved."); - EditorApplication.isPlaying = false; - } - } - } - -#if UNITY_2019_3_OR_NEWER - static void CheckPlayModeOptions() - { - // enabling the checkbox is enough. it controls all the other settings. - if (EditorSettings.enterPlayModeOptionsEnabled) - { - Debug.LogError("Enter Play Mode Options are not supported by Mirror. Please disable 'ProjectSettings -> Editor -> Enter Play Mode Settings (Experimental)'."); - EditorApplication.isPlaying = false; - } - } -#endif - } -} diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance.meta b/UnityProject/Assets/Mirror/Editor/Icon.meta similarity index 77% rename from UnityProject/Assets/Mirror/Runtime/Transport/Ignorance.meta rename to UnityProject/Assets/Mirror/Editor/Icon.meta index b56e66c..7338187 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance.meta +++ b/UnityProject/Assets/Mirror/Editor/Icon.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: cc98cd95e3fb22b4eb88082706967357 +guid: b5f1356ad059a1243910a4e82cd68c5f folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/UnityProject/Assets/Mirror/Icon/MirrorIcon.png b/UnityProject/Assets/Mirror/Editor/Icon/MirrorIcon.png similarity index 100% rename from UnityProject/Assets/Mirror/Icon/MirrorIcon.png rename to UnityProject/Assets/Mirror/Editor/Icon/MirrorIcon.png diff --git a/UnityProject/Assets/Mirror/Icon/MirrorIcon.png.meta b/UnityProject/Assets/Mirror/Editor/Icon/MirrorIcon.png.meta similarity index 95% rename from UnityProject/Assets/Mirror/Icon/MirrorIcon.png.meta rename to UnityProject/Assets/Mirror/Editor/Icon/MirrorIcon.png.meta index 4364f8a..8049382 100644 --- a/UnityProject/Assets/Mirror/Icon/MirrorIcon.png.meta +++ b/UnityProject/Assets/Mirror/Editor/Icon/MirrorIcon.png.meta @@ -97,14 +97,14 @@ TextureImporter: outline: [] physicsShape: [] bones: [] - spriteID: '' + spriteID: vertices: [] - indices: '' + indices: edges: [] weights: [] - spritePackingTag: '' + spritePackingTag: pSDRemoveMatte: 0 pSDShowRemoveMatteOption: 0 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/InspectorHelper.cs b/UnityProject/Assets/Mirror/Editor/InspectorHelper.cs index 40d2f1e..80b3a06 100644 --- a/UnityProject/Assets/Mirror/Editor/InspectorHelper.cs +++ b/UnityProject/Assets/Mirror/Editor/InspectorHelper.cs @@ -7,12 +7,8 @@ namespace Mirror { public static class InspectorHelper { - /// - /// Gets all public and private fields for a type - /// - /// - /// Stops at this base type (exclusive) - /// + /// Gets all public and private fields for a type + // deepestBaseType: Stops at this base type (exclusive) public static IEnumerable GetAllFields(Type type, Type deepestBaseType) { const BindingFlags publicFields = BindingFlags.Public | BindingFlags.Instance; @@ -49,25 +45,30 @@ namespace Mirror object[] fieldMarkers = field.GetCustomAttributes(typeof(SyncVarAttribute), true); return fieldMarkers.Length > 0; } + public static bool IsSerializeField(this FieldInfo field) { object[] fieldMarkers = field.GetCustomAttributes(typeof(SerializeField), true); return fieldMarkers.Length > 0; } + public static bool IsVisibleField(this FieldInfo field) { return field.IsPublic || IsSerializeField(field); } - public static bool IsSyncObject(this FieldInfo field) + public static bool ImplementsInterface(this FieldInfo field) { - return typeof(SyncObject).IsAssignableFrom(field.FieldType); + return typeof(T).IsAssignableFrom(field.FieldType); } + public static bool HasShowInInspector(this FieldInfo field) { object[] fieldMarkers = field.GetCustomAttributes(typeof(ShowInInspectorAttribute), true); return fieldMarkers.Length > 0; } + + // checks if SyncObject is public or has our custom [ShowInInspector] field public static bool IsVisibleSyncObject(this FieldInfo field) { return field.IsPublic || HasShowInInspector(field); diff --git a/UnityProject/Assets/Mirror/Editor/InspectorHelper.cs.meta b/UnityProject/Assets/Mirror/Editor/InspectorHelper.cs.meta index bb6cb6c..852ff71 100644 --- a/UnityProject/Assets/Mirror/Editor/InspectorHelper.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/InspectorHelper.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Mirror.Editor.asmdef b/UnityProject/Assets/Mirror/Editor/Mirror.Editor.asmdef index 3ed7e2f..0d59f9f 100644 --- a/UnityProject/Assets/Mirror/Editor/Mirror.Editor.asmdef +++ b/UnityProject/Assets/Mirror/Editor/Mirror.Editor.asmdef @@ -1,10 +1,10 @@ { "name": "Mirror.Editor", + "rootNamespace": "", "references": [ "Mirror", - "Mirror.Weaver" + "Unity.Mirror.CodeGen" ], - "optionalUnityReferences": [], "includePlatforms": [ "Editor" ], @@ -13,5 +13,7 @@ "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, - "defineConstraints": [] + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false } \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Editor/Mirror.Editor.asmdef.meta b/UnityProject/Assets/Mirror/Editor/Mirror.Editor.asmdef.meta index ba2d88c..e2e6f2a 100644 --- a/UnityProject/Assets/Mirror/Editor/Mirror.Editor.asmdef.meta +++ b/UnityProject/Assets/Mirror/Editor/Mirror.Editor.asmdef.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 1c7c33eb5480dd24c9e29a8250c1a775 AssemblyDefinitionImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/NetworkBehaviourInspector.cs b/UnityProject/Assets/Mirror/Editor/NetworkBehaviourInspector.cs index cc56210..54b3ae7 100644 --- a/UnityProject/Assets/Mirror/Editor/NetworkBehaviourInspector.cs +++ b/UnityProject/Assets/Mirror/Editor/NetworkBehaviourInspector.cs @@ -1,6 +1,4 @@ using System; -using System.Collections; -using System.Collections.Generic; using System.Reflection; using UnityEditor; using UnityEngine; @@ -11,12 +9,8 @@ namespace Mirror [CanEditMultipleObjects] public class NetworkBehaviourInspector : Editor { - /// - /// List of all visible syncVars in target class - /// - protected List syncVarNames = new List(); bool syncsAnything; - SyncListDrawer syncListDrawer; + SyncObjectCollectionsDrawer syncObjectCollectionsDrawer; // does this type sync anything? otherwise we don't need to show syncInterval bool SyncsAnything(Type scriptClass) @@ -44,10 +38,7 @@ namespace Mirror // search for SyncObjects manually. // Any SyncObject should be added to syncObjects when unity creates an // object so we can check length of list so see if sync objects exists - FieldInfo syncObjectsField = scriptClass.GetField("syncObjects", BindingFlags.NonPublic | BindingFlags.Instance); - List syncObjects = (List)syncObjectsField.GetValue(serializedObject.targetObject); - - return syncObjects.Count > 0; + return ((NetworkBehaviour)serializedObject.targetObject).HasSyncObjects(); } void OnEnable() @@ -60,16 +51,7 @@ namespace Mirror Type scriptClass = target.GetType(); - syncVarNames = new List(); - foreach (FieldInfo field in InspectorHelper.GetAllFields(scriptClass, typeof(NetworkBehaviour))) - { - if (field.IsSyncVar() && field.IsVisibleField()) - { - syncVarNames.Add(field.Name); - } - } - - syncListDrawer = new SyncListDrawer(serializedObject.targetObject); + syncObjectCollectionsDrawer = new SyncObjectCollectionsDrawer(serializedObject.targetObject); syncsAnything = SyncsAnything(scriptClass); } @@ -77,24 +59,20 @@ namespace Mirror public override void OnInspectorGUI() { DrawDefaultInspector(); - DrawDefaultSyncLists(); + DrawSyncObjectCollections(); DrawDefaultSyncSettings(); } - /// - /// Draws Sync Objects that are IEnumerable - /// - protected void DrawDefaultSyncLists() + // Draws Sync Objects that are IEnumerable + protected void DrawSyncObjectCollections() { // Need this check in case OnEnable returns early - if (syncListDrawer == null) { return; } + if (syncObjectCollectionsDrawer == null) return; - syncListDrawer.Draw(); + syncObjectCollectionsDrawer.Draw(); } - /// - /// Draws SyncSettings if the NetworkBehaviour has anything to sync - /// + // Draws SyncSettings if the NetworkBehaviour has anything to sync protected void DrawDefaultSyncSettings() { // does it sync anything? then show extra properties @@ -114,73 +92,4 @@ namespace Mirror serializedObject.ApplyModifiedProperties(); } } - public class SyncListDrawer - { - readonly UnityEngine.Object targetObject; - readonly List syncListFields; - - public SyncListDrawer(UnityEngine.Object targetObject) - { - this.targetObject = targetObject; - syncListFields = new List(); - foreach (FieldInfo field in InspectorHelper.GetAllFields(targetObject.GetType(), typeof(NetworkBehaviour))) - { - if (field.IsSyncObject() && field.IsVisibleSyncObject()) - { - syncListFields.Add(new SyncListField(field)); - } - } - } - - public void Draw() - { - if (syncListFields.Count == 0) { return; } - - EditorGUILayout.Space(); - EditorGUILayout.LabelField("Sync Lists", EditorStyles.boldLabel); - - for (int i = 0; i < syncListFields.Count; i++) - { - DrawSyncList(syncListFields[i]); - } - } - - void DrawSyncList(SyncListField syncListField) - { - syncListField.visible = EditorGUILayout.Foldout(syncListField.visible, syncListField.label); - if (syncListField.visible) - { - using (new EditorGUI.IndentLevelScope()) - { - object fieldValue = syncListField.field.GetValue(targetObject); - if (fieldValue is IEnumerable synclist) - { - int index = 0; - foreach (object item in synclist) - { - string itemValue = item != null ? item.ToString() : "NULL"; - string itemLabel = "Element " + index; - EditorGUILayout.LabelField(itemLabel, itemValue); - - index++; - } - } - } - } - } - - class SyncListField - { - public bool visible; - public readonly FieldInfo field; - public readonly string label; - - public SyncListField(FieldInfo field) - { - this.field = field; - visible = false; - label = field.Name + " [" + field.FieldType.Name + "]"; - } - } - } } diff --git a/UnityProject/Assets/Mirror/Editor/NetworkBehaviourInspector.cs.meta b/UnityProject/Assets/Mirror/Editor/NetworkBehaviourInspector.cs.meta index 2a4a45a..78d9fa8 100644 --- a/UnityProject/Assets/Mirror/Editor/NetworkBehaviourInspector.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/NetworkBehaviourInspector.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/NetworkInformationPreview.cs b/UnityProject/Assets/Mirror/Editor/NetworkInformationPreview.cs index 0c30298..2c8874b 100644 --- a/UnityProject/Assets/Mirror/Editor/NetworkInformationPreview.cs +++ b/UnityProject/Assets/Mirror/Editor/NetworkInformationPreview.cs @@ -73,7 +73,7 @@ namespace Mirror public override bool HasPreviewGUI() { - // need to check if target is null to stop MissingReferenceException + // need to check if target is null to stop MissingReferenceException return target != null && target is GameObject gameObject && gameObject.GetComponent() != null; } @@ -180,9 +180,9 @@ namespace Mirror observerRect.x += 20; observerRect.y += observerRect.height; - foreach (KeyValuePair kvp in identity.observers) + foreach (KeyValuePair kvp in identity.observers) { - GUI.Label(observerRect, kvp.Value.address + ":" + kvp.Value, styles.componentName); + GUI.Label(observerRect, $"{kvp.Value.address}:{kvp.Value}", styles.componentName); observerRect.y += observerRect.height; Y = observerRect.y; } @@ -196,7 +196,7 @@ namespace Mirror if (identity.connectionToClient != null) { Rect ownerRect = new Rect(initialX, Y + 10, 400, 20); - GUI.Label(ownerRect, new GUIContent("Client Authority: " + identity.connectionToClient), styles.labelStyle); + GUI.Label(ownerRect, new GUIContent($"Client Authority: {identity.connectionToClient}"), styles.labelStyle); Y += ownerRect.height; } return Y; @@ -277,7 +277,7 @@ namespace Mirror NetworkIdentityInfo GetAssetId(NetworkIdentity identity) { string assetId = identity.assetId.ToString(); - if (string.IsNullOrEmpty(assetId)) + if (string.IsNullOrWhiteSpace(assetId)) { assetId = ""; } diff --git a/UnityProject/Assets/Mirror/Editor/NetworkInformationPreview.cs.meta b/UnityProject/Assets/Mirror/Editor/NetworkInformationPreview.cs.meta index c93e6c0..9bf2de4 100644 --- a/UnityProject/Assets/Mirror/Editor/NetworkInformationPreview.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/NetworkInformationPreview.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/NetworkManagerEditor.cs b/UnityProject/Assets/Mirror/Editor/NetworkManagerEditor.cs index 4f80de1..94b0844 100644 --- a/UnityProject/Assets/Mirror/Editor/NetworkManagerEditor.cs +++ b/UnityProject/Assets/Mirror/Editor/NetworkManagerEditor.cs @@ -62,7 +62,7 @@ namespace Mirror else { NetworkIdentity identity = go.GetComponent(); - label = new GUIContent(go.name, identity != null ? "AssetId: [" + identity.assetId + "]" : "No Network Identity"); + label = new GUIContent(go.name, identity != null ? $"AssetId: [{identity.assetId}]" : "No Network Identity"); } GameObject newGameObject = (GameObject)EditorGUI.ObjectField(r, label, go, typeof(GameObject), false); @@ -71,7 +71,7 @@ namespace Mirror { if (newGameObject != null && !newGameObject.GetComponent()) { - Debug.LogError("Prefab " + newGameObject + " cannot be added as spawnable as it doesn't have a NetworkIdentity."); + Debug.LogError($"Prefab {newGameObject} cannot be added as spawnable as it doesn't have a NetworkIdentity."); return; } prefab.objectReferenceValue = newGameObject; diff --git a/UnityProject/Assets/Mirror/Editor/NetworkManagerEditor.cs.meta b/UnityProject/Assets/Mirror/Editor/NetworkManagerEditor.cs.meta index 6fb657e..7fe8dbc 100644 --- a/UnityProject/Assets/Mirror/Editor/NetworkManagerEditor.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/NetworkManagerEditor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/NetworkScenePostProcess.cs b/UnityProject/Assets/Mirror/Editor/NetworkScenePostProcess.cs index c60493d..6dcd65d 100644 --- a/UnityProject/Assets/Mirror/Editor/NetworkScenePostProcess.cs +++ b/UnityProject/Assets/Mirror/Editor/NetworkScenePostProcess.cs @@ -80,7 +80,9 @@ namespace Mirror // set scene hash identity.SetSceneIdSceneHashPartInternal(); - // disable it + // spawnable scene objects are force disabled on scene load to + // ensure Start/Update/etc. aren't called until actually spawned. + // // note: NetworkIdentity.OnDisable adds itself to the // spawnableObjects dictionary (only if sceneId != 0) identity.gameObject.SetActive(false); diff --git a/UnityProject/Assets/Mirror/Editor/NetworkScenePostProcess.cs.meta b/UnityProject/Assets/Mirror/Editor/NetworkScenePostProcess.cs.meta index 53b148f..b567cc9 100644 --- a/UnityProject/Assets/Mirror/Editor/NetworkScenePostProcess.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/NetworkScenePostProcess.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/SceneDrawer.cs b/UnityProject/Assets/Mirror/Editor/SceneDrawer.cs index cb3b731..f391684 100644 --- a/UnityProject/Assets/Mirror/Editor/SceneDrawer.cs +++ b/UnityProject/Assets/Mirror/Editor/SceneDrawer.cs @@ -12,12 +12,12 @@ namespace Mirror { SceneAsset sceneObject = AssetDatabase.LoadAssetAtPath(property.stringValue); - if (sceneObject == null && !string.IsNullOrEmpty(property.stringValue)) + if (sceneObject == null && !string.IsNullOrWhiteSpace(property.stringValue)) { // try to load it from the build settings for legacy compatibility sceneObject = GetBuildSettingsSceneObject(property.stringValue); } - if (sceneObject == null && !string.IsNullOrEmpty(property.stringValue)) + if (sceneObject == null && !string.IsNullOrWhiteSpace(property.stringValue)) { Debug.LogError($"Could not find scene {property.stringValue} in {property.propertyPath}, assign the proper scenes in your NetworkManager"); } diff --git a/UnityProject/Assets/Mirror/Editor/SceneDrawer.cs.meta b/UnityProject/Assets/Mirror/Editor/SceneDrawer.cs.meta index e37ff9c..6a996dc 100644 --- a/UnityProject/Assets/Mirror/Editor/SceneDrawer.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/SceneDrawer.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/SyncObjectCollectionsDrawer.cs b/UnityProject/Assets/Mirror/Editor/SyncObjectCollectionsDrawer.cs new file mode 100644 index 0000000..2c95bcf --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/SyncObjectCollectionsDrawer.cs @@ -0,0 +1,83 @@ +// helper class for NetworkBehaviourInspector to draw all enumerable SyncObjects +// (SyncList/Set/Dictionary) +// 'SyncObjectCollectionsDrawer' is a nicer name than 'IEnumerableSyncObjectsDrawer' +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using UnityEditor; + +namespace Mirror +{ + class SyncObjectCollectionField + { + public bool visible; + public readonly FieldInfo field; + public readonly string label; + + public SyncObjectCollectionField(FieldInfo field) + { + this.field = field; + visible = false; + label = $"{field.Name} [{field.FieldType.Name}]"; + } + } + + public class SyncObjectCollectionsDrawer + { + readonly UnityEngine.Object targetObject; + readonly List syncObjectCollectionFields; + + public SyncObjectCollectionsDrawer(UnityEngine.Object targetObject) + { + this.targetObject = targetObject; + syncObjectCollectionFields = new List(); + foreach (FieldInfo field in InspectorHelper.GetAllFields(targetObject.GetType(), typeof(NetworkBehaviour))) + { + // only draw SyncObjects that are IEnumerable (SyncList/Set/Dictionary) + if (field.IsVisibleSyncObject() && + field.ImplementsInterface() && + field.ImplementsInterface()) + { + syncObjectCollectionFields.Add(new SyncObjectCollectionField(field)); + } + } + } + + public void Draw() + { + if (syncObjectCollectionFields.Count == 0) { return; } + + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Sync Collections", EditorStyles.boldLabel); + + for (int i = 0; i < syncObjectCollectionFields.Count; i++) + { + DrawSyncObjectCollection(syncObjectCollectionFields[i]); + } + } + + void DrawSyncObjectCollection(SyncObjectCollectionField syncObjectCollectionField) + { + syncObjectCollectionField.visible = EditorGUILayout.Foldout(syncObjectCollectionField.visible, syncObjectCollectionField.label); + if (syncObjectCollectionField.visible) + { + using (new EditorGUI.IndentLevelScope()) + { + object fieldValue = syncObjectCollectionField.field.GetValue(targetObject); + if (fieldValue is IEnumerable syncObject) + { + int index = 0; + foreach (object item in syncObject) + { + string itemValue = item != null ? item.ToString() : "NULL"; + string itemLabel = $"Element {index}"; + EditorGUILayout.LabelField(itemLabel, itemValue); + + index++; + } + } + } + } + } + } +} diff --git a/UnityProject/Assets/Mirror/Editor/SyncObjectCollectionsDrawer.cs.meta b/UnityProject/Assets/Mirror/Editor/SyncObjectCollectionsDrawer.cs.meta new file mode 100644 index 0000000..44ba75d --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/SyncObjectCollectionsDrawer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6f90afab12e04f0e945d83e9d38308a3 +timeCreated: 1632556645 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Editor/SyncVarAttributeDrawer.cs.meta b/UnityProject/Assets/Mirror/Editor/SyncVarAttributeDrawer.cs.meta index 1cb1250..6311f1d 100644 --- a/UnityProject/Assets/Mirror/Editor/SyncVarAttributeDrawer.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/SyncVarAttributeDrawer.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/SyncVarDrawer.cs b/UnityProject/Assets/Mirror/Editor/SyncVarDrawer.cs new file mode 100644 index 0000000..b0532ae --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/SyncVarDrawer.cs @@ -0,0 +1,35 @@ +// SyncVar looks like this in the Inspector: +// Health +// Value: 42 +// instead, let's draw ._Value directly so it looks like this: +// Health: 42 +// +// BUG: Unity also doesn't show custom drawer for readonly fields (#1368395) +using UnityEditor; +using UnityEngine; + +namespace Mirror +{ + [CustomPropertyDrawer(typeof(SyncVar<>))] + public class SyncVarDrawer : PropertyDrawer + { + static readonly GUIContent syncVarIndicatorContent = new GUIContent("SyncVar", "This variable is a SyncVar."); + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + Vector2 syncVarIndicatorRect = EditorStyles.miniLabel.CalcSize(syncVarIndicatorContent); + float valueWidth = position.width - syncVarIndicatorRect.x; + + Rect valueRect = new Rect(position.x, position.y, valueWidth, position.height); + Rect labelRect = new Rect(position.x + valueWidth, position.y, syncVarIndicatorRect.x, position.height); + + EditorGUI.PropertyField(valueRect, property.FindPropertyRelative("_Value"), label, true); + GUI.Label(labelRect, syncVarIndicatorContent, EditorStyles.miniLabel); + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + return EditorGUI.GetPropertyHeight(property.FindPropertyRelative("_Value")); + } + } +} diff --git a/UnityProject/Assets/Mirror/Editor/SyncVarDrawer.cs.meta b/UnityProject/Assets/Mirror/Editor/SyncVarDrawer.cs.meta new file mode 100644 index 0000000..0ee91aa --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/SyncVarDrawer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 874812594431423b84f763b987ff9681 +timeCreated: 1632553007 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Editor/Weaver.meta b/UnityProject/Assets/Mirror/Editor/Weaver.meta index ed8a67a..121fbf4 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver.meta @@ -3,6 +3,6 @@ guid: d9f8e6274119b4ce29e498cfb8aca8a4 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/AssemblyInfo.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/AssemblyInfo.cs.meta index f31bae2..d356af8 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/AssemblyInfo.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/AssemblyInfo.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Empty.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Empty.meta index 71a3491..6e29ee7 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Empty.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Empty.meta @@ -3,6 +3,6 @@ guid: 30fc290f2ff9c29498f54f63de12ca6f folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Empty/GenericArgumentResolver.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Empty/GenericArgumentResolver.cs.meta index 3fcfa0c..685f914 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Empty/GenericArgumentResolver.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Empty/GenericArgumentResolver.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Empty/MessageClassProcessor.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Empty/MessageClassProcessor.cs.meta index d7b6208..cbea4d6 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Empty/MessageClassProcessor.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Empty/MessageClassProcessor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Empty/Program.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Empty/Program.cs.meta index bde63d2..0a14018 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Empty/Program.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Empty/Program.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Empty/SyncDictionaryProcessor.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Empty/SyncDictionaryProcessor.cs.meta index 8ca8566..0a7c2aa 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Empty/SyncDictionaryProcessor.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Empty/SyncDictionaryProcessor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Empty/SyncEventProcessor.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Empty/SyncEventProcessor.cs.meta index 2a2b518..81b9576 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Empty/SyncEventProcessor.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Empty/SyncEventProcessor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Empty/SyncListProcessor.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Empty/SyncListProcessor.cs.meta index 16b8097..b73b047 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Empty/SyncListProcessor.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Empty/SyncListProcessor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint.meta b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint.meta new file mode 100644 index 0000000..81827c5 --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 251338e67afb4cefa38da924f8c50a6e +timeCreated: 1628851818 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/CompilationFinishedHook.cs b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint/CompilationFinishedHook.cs similarity index 60% rename from UnityProject/Assets/Mirror/Editor/Weaver/CompilationFinishedHook.cs rename to UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint/CompilationFinishedHook.cs index 5c272b5..9016949 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/CompilationFinishedHook.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint/CompilationFinishedHook.cs @@ -1,7 +1,11 @@ +// for Unity 2020+ we use ILPostProcessor. +// only automatically invoke it for older versions. +#if !UNITY_2020_3_OR_NEWER using System; using System.Collections.Generic; using System.IO; using System.Linq; +using Mono.CecilX; using UnityEditor; using UnityEditor.Compilation; using UnityEngine; @@ -11,31 +15,21 @@ namespace Mirror.Weaver { public static class CompilationFinishedHook { + // needs to be the same as Weaver.MirrorAssemblyName! const string MirrorRuntimeAssemblyName = "Mirror"; const string MirrorWeaverAssemblyName = "Mirror.Weaver"; + // global weaver define so that tests can use it + internal static Weaver weaver; + // delegate for subscription to Weaver warning messages public static Action OnWeaverWarning; // delete for subscription to Weaver error messages public static Action OnWeaverError; - // controls weather Weaver errors are reported direct to the Unity console (tests enable this) + // controls whether Weaver errors are reported direct to the Unity console (tests enable this) public static bool UnityLogEnabled = true; - // warning message handler that also calls OnWarningMethod delegate - static void HandleWarning(string msg) - { - if (UnityLogEnabled) Debug.LogWarning(msg); - OnWeaverWarning?.Invoke(msg); - } - - // error message handler that also calls OnErrorMethod delegate - static void HandleError(string msg) - { - if (UnityLogEnabled) Debug.LogError(msg); - OnWeaverError?.Invoke(msg); - } - [InitializeOnLoadMethod] public static void OnInitializeOnLoad() { @@ -70,24 +64,13 @@ namespace Mirror.Weaver #endif } - static string FindMirrorRuntime() - { - foreach (UnityAssembly assembly in CompilationPipeline.GetAssemblies()) - { - if (assembly.name == MirrorRuntimeAssemblyName) - { - return assembly.outputPath; - } - } - return ""; - } + static Assembly FindCompilationPipelineAssembly(string assemblyName) => + CompilationPipeline.GetAssemblies().First(assembly => assembly.name == assemblyName); - static bool CompilerMessagesContainError(CompilerMessage[] messages) - { - return messages.Any(msg => msg.type == CompilerMessageType.Error); - } + static bool CompilerMessagesContainError(CompilerMessage[] messages) => + messages.Any(msg => msg.type == CompilerMessageType.Error); - static void OnCompilationFinished(string assemblyPath, CompilerMessage[] messages) + public static void OnCompilationFinished(string assemblyPath, CompilerMessage[] messages) { // Do nothing if there were compile errors on the target if (CompilerMessagesContainError(messages)) @@ -110,12 +93,14 @@ namespace Mirror.Weaver } // find Mirror.dll - string mirrorRuntimeDll = FindMirrorRuntime(); - if (string.IsNullOrEmpty(mirrorRuntimeDll)) + Assembly mirrorAssembly = FindCompilationPipelineAssembly(MirrorRuntimeAssemblyName); + if (mirrorAssembly == null) { Debug.LogError("Failed to find Mirror runtime assembly"); return; } + + string mirrorRuntimeDll = mirrorAssembly.outputPath; if (!File.Exists(mirrorRuntimeDll)) { // this is normal, it happens with any assembly that is built before mirror @@ -133,39 +118,72 @@ namespace Mirror.Weaver return; } - HashSet dependencyPaths = GetDependecyPaths(assemblyPath); + HashSet dependencyPaths = GetDependencyPaths(assemblyPath); dependencyPaths.Add(Path.GetDirectoryName(mirrorRuntimeDll)); dependencyPaths.Add(Path.GetDirectoryName(unityEngineCoreModuleDLL)); - Log.Warning = HandleWarning; - Log.Error = HandleError; - if (!Weaver.WeaveAssembly(assemblyPath, dependencyPaths.ToArray())) + if (!WeaveFromFile(assemblyPath, dependencyPaths.ToArray())) { // Set false...will be checked in \Editor\EnterPlayModeSettingsCheck.CheckSuccessfulWeave() SessionState.SetBool("MIRROR_WEAVE_SUCCESS", false); - if (UnityLogEnabled) Debug.LogError("Weaving failed for: " + assemblyPath); + if (UnityLogEnabled) Debug.LogError($"Weaving failed for {assemblyPath}"); } } - static HashSet GetDependecyPaths(string assemblyPath) + static HashSet GetDependencyPaths(string assemblyPath) { // build directory list for later asm/symbol resolving using CompilationPipeline refs HashSet dependencyPaths = new HashSet { Path.GetDirectoryName(assemblyPath) }; - foreach (UnityAssembly unityAsm in CompilationPipeline.GetAssemblies()) + foreach (Assembly assembly in CompilationPipeline.GetAssemblies()) { - if (unityAsm.outputPath == assemblyPath) + if (assembly.outputPath == assemblyPath) { - foreach (string unityAsmRef in unityAsm.compiledAssemblyReferences) + foreach (string reference in assembly.compiledAssemblyReferences) { - dependencyPaths.Add(Path.GetDirectoryName(unityAsmRef)); + dependencyPaths.Add(Path.GetDirectoryName(reference)); } } } return dependencyPaths; } + // helper function to invoke Weaver with an AssemblyDefinition from a + // file path, with dependencies added. + static bool WeaveFromFile(string assemblyPath, string[] dependencies) + { + // resolve assembly from stream + using (DefaultAssemblyResolver asmResolver = new DefaultAssemblyResolver()) + using (AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(assemblyPath, new ReaderParameters{ ReadWrite = true, ReadSymbols = true, AssemblyResolver = asmResolver })) + { + // add this assembly's path and unity's assembly path + asmResolver.AddSearchDirectory(Path.GetDirectoryName(assemblyPath)); + asmResolver.AddSearchDirectory(Helpers.UnityEngineDllDirectoryName()); + + // add dependencies + if (dependencies != null) + { + foreach (string path in dependencies) + { + asmResolver.AddSearchDirectory(path); + } + } + + // create weaver with logger + weaver = new Weaver(new CompilationFinishedLogger()); + if (weaver.Weave(assembly, asmResolver, out bool modified)) + { + // write changes to file if modified + if (modified) + assembly.Write(new WriterParameters{WriteSymbols = true}); + + return true; + } + return false; + } + } } } +#endif diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/CompilationFinishedHook.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint/CompilationFinishedHook.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Editor/Weaver/CompilationFinishedHook.cs.meta rename to UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint/CompilationFinishedHook.cs.meta index 004ab69..ed537ab 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/CompilationFinishedHook.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint/CompilationFinishedHook.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint/CompilationFinishedLogger.cs b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint/CompilationFinishedLogger.cs new file mode 100644 index 0000000..e053404 --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint/CompilationFinishedLogger.cs @@ -0,0 +1,31 @@ +// logger for compilation finished hook. +// where we need a callback and Debug.Log. +// for Unity 2020+ we use ILPostProcessor. +#if !UNITY_2020_3_OR_NEWER +using Mono.CecilX; +using UnityEngine; + +namespace Mirror.Weaver +{ + public class CompilationFinishedLogger : Logger + { + public void Warning(string message) => Warning(message, null); + public void Warning(string message, MemberReference mr) + { + if (mr != null) message = $"{message} (at {mr})"; + + if (CompilationFinishedHook.UnityLogEnabled) Debug.LogWarning(message); + CompilationFinishedHook.OnWeaverWarning?.Invoke(message); + } + + public void Error(string message) => Error(message, null); + public void Error(string message, MemberReference mr) + { + if (mr != null) message = $"{message} (at {mr})"; + + if (CompilationFinishedHook.UnityLogEnabled) Debug.LogError(message); + CompilationFinishedHook.OnWeaverError?.Invoke(message); + } + } +} +#endif diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint/CompilationFinishedLogger.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint/CompilationFinishedLogger.cs.meta new file mode 100644 index 0000000..f8c7139 --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint/CompilationFinishedLogger.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 47026732f0fa475c94bd1dd41f1de559 +timeCreated: 1629379868 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint/EnterPlayModeHook.cs b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint/EnterPlayModeHook.cs new file mode 100644 index 0000000..5dffa97 --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint/EnterPlayModeHook.cs @@ -0,0 +1,44 @@ +#if !UNITY_2020_3_OR_NEWER +// make sure we weaved successfully when entering play mode. +using UnityEditor; +using UnityEngine; + +namespace Mirror +{ + public class EnterPlayModeSettingsCheck : MonoBehaviour + { + [InitializeOnLoadMethod] + static void OnInitializeOnLoad() + { + // Hook this event to see if we have a good weave every time + // user attempts to enter play mode or tries to do a build + EditorApplication.playModeStateChanged += OnPlayModeStateChanged; + } + + static void OnPlayModeStateChanged(PlayModeStateChange state) + { + // Per Unity docs, this fires "when exiting edit mode before the Editor is in play mode". + // This doesn't fire when closing the editor. + if (state == PlayModeStateChange.ExitingEditMode) + { + // Check if last weave result was successful + if (!SessionState.GetBool("MIRROR_WEAVE_SUCCESS", false)) + { + // Last weave result was a failure...try to weave again + // Faults will show in the console that may have been cleared by "Clear on Play" + SessionState.SetBool("MIRROR_WEAVE_SUCCESS", true); + Weaver.CompilationFinishedHook.WeaveExistingAssemblies(); + + // Did that clear things up for us? + if (!SessionState.GetBool("MIRROR_WEAVE_SUCCESS", false)) + { + // Nope, still failed, and console has the issues logged + Debug.LogError("Can't enter play mode until weaver issues are resolved."); + EditorApplication.isPlaying = false; + } + } + } + } + } +} +#endif diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint/EnterPlayModeHook.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint/EnterPlayModeHook.cs.meta new file mode 100644 index 0000000..eca31e3 --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPoint/EnterPlayModeHook.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b73d0f106ba84aa983baa5142b08a0a9 +timeCreated: 1628851346 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor.meta b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor.meta new file mode 100644 index 0000000..6ef7bf3 --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 09082db63d1d48d9ab91320165c1b684 +timeCreated: 1628859005 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/CompiledAssemblyFromFile.cs b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/CompiledAssemblyFromFile.cs new file mode 100644 index 0000000..e4d9de2 --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/CompiledAssemblyFromFile.cs @@ -0,0 +1,31 @@ +// tests use WeaveAssembler, which uses AssemblyBuilder to Build(). +// afterwards ILPostProcessor weaves the build. +// this works on windows, but build() does not run ILPP on mac atm. +// we need to manually invoke ILPP with an assembly from file. +// +// this is in Weaver folder becuase CompilationPipeline can only be accessed +// from assemblies with the name "Unity.*.CodeGen" +using System.IO; +using Unity.CompilationPipeline.Common.ILPostProcessing; + +namespace Mirror.Weaver +{ + public class CompiledAssemblyFromFile : ICompiledAssembly + { + readonly string assemblyPath; + + public string Name => Path.GetFileNameWithoutExtension(assemblyPath); + public string[] References { get; set; } + public string[] Defines { get; set; } + public InMemoryAssembly InMemoryAssembly { get; } + + public CompiledAssemblyFromFile(string assemblyPath) + { + this.assemblyPath = assemblyPath; + byte[] peData = File.ReadAllBytes(assemblyPath); + string pdbFileName = Path.GetFileNameWithoutExtension(assemblyPath) + ".pdb"; + byte[] pdbData = File.ReadAllBytes(Path.Combine(Path.GetDirectoryName(assemblyPath), pdbFileName)); + InMemoryAssembly = new InMemoryAssembly(peData, pdbData); + } + } +} diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/CompiledAssemblyFromFile.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/CompiledAssemblyFromFile.cs.meta new file mode 100644 index 0000000..1e5091e --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/CompiledAssemblyFromFile.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9009d1db4ed44f6694a92bf8ad7738e9 +timeCreated: 1630129423 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorAssemblyResolver.cs b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorAssemblyResolver.cs new file mode 100644 index 0000000..cbc8e41 --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorAssemblyResolver.cs @@ -0,0 +1,167 @@ +// based on paul's resolver from +// https://github.com/MirageNet/Mirage/commit/def64cd1db525398738f057b3d1eb1fe8afc540c?branch=def64cd1db525398738f057b3d1eb1fe8afc540c&diff=split +// +// an assembly resolver's job is to open an assembly in case we want to resolve +// a type from it. +// +// for example, while weaving MyGame.dll: if we want to resolve ArraySegment, +// then we need to open and resolve from another assembly (CoreLib). +// +// using DefaultAssemblyResolver with ILPostProcessor throws Exceptions in +// WeaverTypes.cs when resolving anything, for example: +// ArraySegment in Mirror.Tests.Dll. +// +// we need a custom resolver for ILPostProcessor. +#if UNITY_2020_3_OR_NEWER +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using Mono.CecilX; +using Unity.CompilationPipeline.Common.ILPostProcessing; + +namespace Mirror.Weaver +{ + class ILPostProcessorAssemblyResolver : IAssemblyResolver + { + readonly string[] assemblyReferences; + readonly Dictionary assemblyCache = + new Dictionary(); + readonly ICompiledAssembly compiledAssembly; + AssemblyDefinition selfAssembly; + + Logger Log; + + public ILPostProcessorAssemblyResolver(ICompiledAssembly compiledAssembly, Logger Log) + { + this.compiledAssembly = compiledAssembly; + assemblyReferences = compiledAssembly.References; + this.Log = Log; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + // Cleanup + } + + public AssemblyDefinition Resolve(AssemblyNameReference name) => + Resolve(name, new ReaderParameters(ReadingMode.Deferred)); + + public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) + { + lock (assemblyCache) + { + if (name.Name == compiledAssembly.Name) + return selfAssembly; + + string fileName = FindFile(name); + if (fileName == null) + { + // returning null will throw exceptions in our weaver where. + // let's make it obvious why we returned null for easier debugging. + // NOTE: if this fails for "System.Private.CoreLib": + // ILPostProcessorReflectionImporter fixes it! + Log.Warning($"ILPostProcessorAssemblyResolver.Resolve: Failed to find file for {name}"); + return null; + } + + DateTime lastWriteTime = File.GetLastWriteTime(fileName); + + string cacheKey = fileName + lastWriteTime; + + if (assemblyCache.TryGetValue(cacheKey, out AssemblyDefinition result)) + return result; + + parameters.AssemblyResolver = this; + + MemoryStream ms = MemoryStreamFor(fileName); + + string pdb = fileName + ".pdb"; + if (File.Exists(pdb)) + parameters.SymbolStream = MemoryStreamFor(pdb); + + AssemblyDefinition assemblyDefinition = AssemblyDefinition.ReadAssembly(ms, parameters); + assemblyCache.Add(cacheKey, assemblyDefinition); + return assemblyDefinition; + } + } + + // find assemblyname in assembly's references + string FindFile(AssemblyNameReference name) + { + string fileName = assemblyReferences.FirstOrDefault(r => Path.GetFileName(r) == name.Name + ".dll"); + if (fileName != null) + return fileName; + + // perhaps the type comes from an exe instead + fileName = assemblyReferences.FirstOrDefault(r => Path.GetFileName(r) == name.Name + ".exe"); + if (fileName != null) + return fileName; + + // Unfortunately the current ICompiledAssembly API only provides direct references. + // It is very much possible that a postprocessor ends up investigating a type in a directly + // referenced assembly, that contains a field that is not in a directly referenced assembly. + // if we don't do anything special for that situation, it will fail to resolve. We should fix this + // in the ILPostProcessing API. As a workaround, we rely on the fact here that the indirect references + // are always located next to direct references, so we search in all directories of direct references we + // got passed, and if we find the file in there, we resolve to it. + foreach (string parentDir in assemblyReferences.Select(Path.GetDirectoryName).Distinct()) + { + string candidate = Path.Combine(parentDir, name.Name + ".dll"); + if (File.Exists(candidate)) + return candidate; + } + + return null; + } + + // open file as MemoryStream + // attempts multiple times, not sure why.. + static MemoryStream MemoryStreamFor(string fileName) + { + return Retry(10, TimeSpan.FromSeconds(1), () => + { + byte[] byteArray; + using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + byteArray = new byte[fs.Length]; + int readLength = fs.Read(byteArray, 0, (int)fs.Length); + if (readLength != fs.Length) + throw new InvalidOperationException("File read length is not full length of file."); + } + + return new MemoryStream(byteArray); + }); + } + + static MemoryStream Retry(int retryCount, TimeSpan waitTime, Func func) + { + try + { + return func(); + } + catch (IOException) + { + if (retryCount == 0) + throw; + Console.WriteLine($"Caught IO Exception, trying {retryCount} more times"); + Thread.Sleep(waitTime); + return Retry(retryCount - 1, waitTime, func); + } + } + + // if the CompiledAssembly's AssemblyDefinition is known, we can add it + public void SetAssemblyDefinitionForCompiledAssembly(AssemblyDefinition assemblyDefinition) + { + selfAssembly = assemblyDefinition; + } + } +} +#endif diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorAssemblyResolver.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorAssemblyResolver.cs.meta new file mode 100644 index 0000000..07289dd --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorAssemblyResolver.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0b3e94696e22440ead0b3a42411bbe14 +timeCreated: 1629693784 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorFromFile.cs b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorFromFile.cs new file mode 100644 index 0000000..6a70641 --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorFromFile.cs @@ -0,0 +1,53 @@ +// helper function to use ILPostProcessor for an assembly from file. +// we keep this in Weaver folder because we can access CompilationPipleine here. +// in tests folder we can't, unless we rename to "Unity.*.CodeGen", +// but then tests wouldn't be weaved anymore. +#if UNITY_2020_3_OR_NEWER +using System; +using System.IO; +using Unity.CompilationPipeline.Common.Diagnostics; +using Unity.CompilationPipeline.Common.ILPostProcessing; + +namespace Mirror.Weaver +{ + public static class ILPostProcessorFromFile + { + // read, weave, write file via ILPostProcessor + public static void ILPostProcessFile(string assemblyPath, string[] references, Action OnWarning, Action OnError) + { + // we COULD Weave() with a test logger manually. + // but for test result consistency on all platforms, + // let's invoke the ILPostProcessor here too. + CompiledAssemblyFromFile assembly = new CompiledAssemblyFromFile(assemblyPath); + assembly.References = references; + + // create ILPP and check WillProcess like Unity would. + ILPostProcessorHook ilpp = new ILPostProcessorHook(); + if (ilpp.WillProcess(assembly)) + { + //Debug.Log($"Will Process: {assembly.Name}"); + + // process it like Unity would + ILPostProcessResult result = ilpp.Process(assembly); + + // handle the error messages like Unity would + foreach (DiagnosticMessage message in result.Diagnostics) + { + if (message.DiagnosticType == DiagnosticType.Warning) + { + OnWarning(message.MessageData); + } + else if (message.DiagnosticType == DiagnosticType.Error) + { + OnError(message.MessageData); + } + } + + // save the weaved assembly to file. + // some tests open it and check for certain IL code. + File.WriteAllBytes(assemblyPath, result.InMemoryAssembly.PeData); + } + } + } +} +#endif diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorFromFile.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorFromFile.cs.meta new file mode 100644 index 0000000..e06dfa7 --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorFromFile.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2a4b115486b74d27a9540f3c39ae2d46 +timeCreated: 1630152191 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorHook.cs b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorHook.cs new file mode 100644 index 0000000..0cfb433 --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorHook.cs @@ -0,0 +1,143 @@ +// hook via ILPostProcessor from Unity 2020.3+ +// (2020.1 has errors https://github.com/vis2k/Mirror/issues/2912) +#if UNITY_2020_3_OR_NEWER +// Unity.CompilationPipeline reference is only resolved if assembly name is +// Unity.*.CodeGen: +// https://forum.unity.com/threads/how-does-unity-do-codegen-and-why-cant-i-do-it-myself.853867/#post-5646937 +using System.IO; +using System.Linq; +// to use Mono.CecilX here, we need to 'override references' in the +// Unity.Mirror.CodeGen assembly definition file in the Editor, and add CecilX. +// otherwise we get a reflection exception with 'file not found: CecilX'. +using Mono.CecilX; +using Mono.CecilX.Cil; +using Unity.CompilationPipeline.Common.ILPostProcessing; +// IMPORTANT: 'using UnityEngine' does not work in here. +// Unity gives "(0,0): error System.Security.SecurityException: ECall methods must be packaged into a system module." +//using UnityEngine; + +namespace Mirror.Weaver +{ + public class ILPostProcessorHook : ILPostProcessor + { + // from CompilationFinishedHook + const string MirrorRuntimeAssemblyName = "Mirror"; + + // ILPostProcessor is invoked by Unity. + // we can not tell it to ignore certain assemblies before processing. + // add a 'ignore' define for convenience. + // => WeaverTests/WeaverAssembler need it to avoid Unity running it + public const string IgnoreDefine = "ILPP_IGNORE"; + + // we can't use Debug.Log in ILPP, so we need a custom logger + ILPostProcessorLogger Log = new ILPostProcessorLogger(); + + // ??? + public override ILPostProcessor GetInstance() => this; + + // check if assembly has the 'ignore' define + static bool HasDefine(ICompiledAssembly assembly, string define) => + assembly.Defines != null && + assembly.Defines.Contains(define); + + // process Mirror, or anything that references Mirror + public override bool WillProcess(ICompiledAssembly compiledAssembly) + { + // compiledAssembly.References are file paths: + // Library/Bee/artifacts/200b0aE.dag/Mirror.CompilerSymbols.dll + // Assets/Mirror/Plugins/Mono.Cecil/Mono.CecilX.dll + // /Applications/Unity/Hub/Editor/2021.2.0b6_apple_silicon/Unity.app/Contents/NetStandard/ref/2.1.0/netstandard.dll + // + // log them to see: + // foreach (string reference in compiledAssembly.References) + // LogDiagnostics($"{compiledAssembly.Name} references {reference}"); + bool relevant = compiledAssembly.Name == MirrorRuntimeAssemblyName || + compiledAssembly.References.Any(filePath => Path.GetFileNameWithoutExtension(filePath) == MirrorRuntimeAssemblyName); + bool ignore = HasDefine(compiledAssembly, IgnoreDefine); + return relevant && !ignore; + } + + public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) + { + //Log.Warning($"Processing {compiledAssembly.Name}"); + + // load the InMemoryAssembly peData into a MemoryStream + byte[] peData = compiledAssembly.InMemoryAssembly.PeData; + //LogDiagnostics($" peData.Length={peData.Length} bytes"); + using (MemoryStream stream = new MemoryStream(peData)) + using (ILPostProcessorAssemblyResolver asmResolver = new ILPostProcessorAssemblyResolver(compiledAssembly, Log)) + { + // we need to load symbols. otherwise we get: + // "(0,0): error Mono.CecilX.Cil.SymbolsNotFoundException: No symbol found for file: " + using (MemoryStream symbols = new MemoryStream(compiledAssembly.InMemoryAssembly.PdbData)) + { + ReaderParameters readerParameters = new ReaderParameters{ + SymbolStream = symbols, + ReadWrite = true, + ReadSymbols = true, + AssemblyResolver = asmResolver, + // custom reflection importer to fix System.Private.CoreLib + // not being found in custom assembly resolver above. + ReflectionImporterProvider = new ILPostProcessorReflectionImporterProvider() + }; + using (AssemblyDefinition asmDef = AssemblyDefinition.ReadAssembly(stream, readerParameters)) + { + // resolving a Mirror.dll type like NetworkServer while + // weaving Mirror.dll does not work. it throws a + // NullReferenceException in WeaverTypes.ctor + // when Resolve() is called on the first Mirror type. + // need to add the AssemblyDefinition itself to use. + asmResolver.SetAssemblyDefinitionForCompiledAssembly(asmDef); + + // weave this assembly. + Weaver weaver = new Weaver(Log); + if (weaver.Weave(asmDef, asmResolver, out bool modified)) + { + //Log.Warning($"Weaving succeeded for: {compiledAssembly.Name}"); + + // write if modified + if (modified) + { + // when weaving Mirror.dll with ILPostProcessor, + // Weave() -> WeaverTypes -> resolving the first + // type in Mirror.dll adds a reference to + // Mirror.dll even though we are in Mirror.dll. + // -> this would throw an exception: + // "Mirror references itself" and not compile + // -> need to detect and fix manually here + if (asmDef.MainModule.AssemblyReferences.Any(r => r.Name == asmDef.Name.Name)) + { + asmDef.MainModule.AssemblyReferences.Remove(asmDef.MainModule.AssemblyReferences.First(r => r.Name == asmDef.Name.Name)); + //Log.Warning($"fixed self referencing Assembly: {asmDef.Name.Name}"); + } + + MemoryStream peOut = new MemoryStream(); + MemoryStream pdbOut = new MemoryStream(); + WriterParameters writerParameters = new WriterParameters + { + SymbolWriterProvider = new PortablePdbWriterProvider(), + SymbolStream = pdbOut, + WriteSymbols = true + }; + + asmDef.Write(peOut, writerParameters); + + InMemoryAssembly inMemory = new InMemoryAssembly(peOut.ToArray(), pdbOut.ToArray()); + return new ILPostProcessResult(inMemory, Log.Logs); + } + } + // if anything during Weave() fails, we log an error. + // don't need to indicate 'weaving failed' again. + // in fact, this would break tests only expecting certain errors. + //else Log.Error($"Weaving failed for: {compiledAssembly.Name}"); + } + } + } + + // always return an ILPostProcessResult with Logs. + // otherwise we won't see Logs if weaving failed. + return new ILPostProcessResult(compiledAssembly.InMemoryAssembly, Log.Logs); + } + } +} +#endif diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorHook.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorHook.cs.meta new file mode 100644 index 0000000..9d7e0a2 --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorHook.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5f113eb695b348b5b28cd85358c8959a +timeCreated: 1628859074 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorLogger.cs b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorLogger.cs new file mode 100644 index 0000000..2c070cc --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorLogger.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; +using Mono.CecilX; +using Unity.CompilationPipeline.Common.Diagnostics; + +namespace Mirror.Weaver +{ + public class ILPostProcessorLogger : Logger + { + // can't Debug.Log in ILPostProcessor. need to add to this list. + internal List Logs = new List(); + + void Add(string message, DiagnosticType logType) + { + Logs.Add(new DiagnosticMessage + { + // TODO add file etc. for double click opening later? + DiagnosticType = logType, // doesn't have .Log + File = null, + Line = 0, + Column = 0, + MessageData = message + }); + } + + public void LogDiagnostics(string message, DiagnosticType logType = DiagnosticType.Warning) + { + // DiagnosticMessage can't display \n for some reason. + // it just cuts it off and we don't see any stack trace. + // so let's replace all line breaks so we get the stack trace. + // (Unity 2021.2.0b6 apple silicon) + //message = message.Replace("\n", "/"); + + // lets break it into several messages instead so it's easier readable + string[] lines = message.Split('\n'); + + // if it's just one line, simply log it + if (lines.Length == 1) + { + // tests assume exact message log. + // don't include 'Weaver: ...' or similar. + Add($"{message}", logType); + } + // for multiple lines, log each line separately with start/end indicators + else + { + // first line with Weaver: ... first + Add("----------------------------------------------", logType); + foreach (string line in lines) Add(line, logType); + Add("----------------------------------------------", logType); + } + } + + public void Warning(string message) => Warning(message, null); + public void Warning(string message, MemberReference mr) + { + if (mr != null) message = $"{message} (at {mr})"; + LogDiagnostics(message, DiagnosticType.Warning); + } + + public void Error(string message) => Error(message, null); + public void Error(string message, MemberReference mr) + { + if (mr != null) message = $"{message} (at {mr})"; + LogDiagnostics(message, DiagnosticType.Error); + } + } +} diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorLogger.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorLogger.cs.meta new file mode 100644 index 0000000..8bb72e0 --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorLogger.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e7b56e7826664e34a415e4b70d958f2a +timeCreated: 1629533154 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorReflectionImporter.cs b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorReflectionImporter.cs new file mode 100644 index 0000000..e15c103 --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorReflectionImporter.cs @@ -0,0 +1,36 @@ +// based on paul's resolver from +// https://github.com/MirageNet/Mirage/commit/def64cd1db525398738f057b3d1eb1fe8afc540c?branch=def64cd1db525398738f057b3d1eb1fe8afc540c&diff=split +// +// ILPostProcessorAssemblyRESOLVER does not find the .dll file for: +// "System.Private.CoreLib" +// we need this custom reflection importer to fix that. +using System.Linq; +using System.Reflection; +using Mono.CecilX; + +namespace Mirror.Weaver +{ + internal class ILPostProcessorReflectionImporter : DefaultReflectionImporter + { + const string SystemPrivateCoreLib = "System.Private.CoreLib"; + readonly AssemblyNameReference fixedCoreLib; + + public ILPostProcessorReflectionImporter(ModuleDefinition module) : base(module) + { + // find the correct library for "System.Private.CoreLib". + // either mscorlib or netstandard. + // defaults to System.Private.CoreLib if not found. + fixedCoreLib = module.AssemblyReferences.FirstOrDefault(a => a.Name == "mscorlib" || a.Name == "netstandard" || a.Name == SystemPrivateCoreLib); + } + + public override AssemblyNameReference ImportReference(AssemblyName name) + { + // System.Private.CoreLib? + if (name.Name == SystemPrivateCoreLib && fixedCoreLib != null) + return fixedCoreLib; + + // otherwise import as usual + return base.ImportReference(name); + } + } +} diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorReflectionImporter.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorReflectionImporter.cs.meta new file mode 100644 index 0000000..d361e21 --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorReflectionImporter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6403a7e3b3ae4e009ae282f111d266e0 +timeCreated: 1629709256 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorReflectionImporterProvider.cs b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorReflectionImporterProvider.cs new file mode 100644 index 0000000..7358e1b --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorReflectionImporterProvider.cs @@ -0,0 +1,16 @@ +// based on paul's resolver from +// https://github.com/MirageNet/Mirage/commit/def64cd1db525398738f057b3d1eb1fe8afc540c?branch=def64cd1db525398738f057b3d1eb1fe8afc540c&diff=split +// +// ILPostProcessorAssemblyRESOLVER does not find the .dll file for: +// "System.Private.CoreLib" +// we need this custom reflection importer to fix that. +using Mono.CecilX; + +namespace Mirror.Weaver +{ + internal class ILPostProcessorReflectionImporterProvider : IReflectionImporterProvider + { + public IReflectionImporter GetReflectionImporter(ModuleDefinition module) => + new ILPostProcessorReflectionImporter(module); + } +} diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorReflectionImporterProvider.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorReflectionImporterProvider.cs.meta new file mode 100644 index 0000000..d9b6f6b --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/EntryPointILPostProcessor/ILPostProcessorReflectionImporterProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a1003b568bad4e69b961c4c81d5afd96 +timeCreated: 1629709223 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Extensions.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Extensions.cs index b8982a9..e5ddb1f 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Extensions.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Extensions.cs @@ -7,14 +7,10 @@ namespace Mirror.Weaver { public static class Extensions { - public static bool Is(this TypeReference td, Type t) - { - if (t.IsGenericType) - { - return td.GetElementType().FullName == t.FullName; - } - return td.FullName == t.FullName; - } + public static bool Is(this TypeReference td, Type type) => + type.IsGenericType + ? td.GetElementType().FullName == type.FullName + : td.FullName == type.FullName; public static bool Is(this TypeReference td) => Is(td, typeof(T)); @@ -76,20 +72,14 @@ namespace Mirror.Weaver return false; } - public static bool IsMultidimensionalArray(this TypeReference tr) - { - return tr is ArrayType arrayType && arrayType.Rank > 1; - } + public static bool IsMultidimensionalArray(this TypeReference tr) => + tr is ArrayType arrayType && arrayType.Rank > 1; - /// - /// Does type use netId as backing field - /// - public static bool IsNetworkIdentityField(this TypeReference tr) - { - return tr.Is() - || tr.Is() - || tr.IsDerivedFrom(); - } + // Does type use netId as backing field + public static bool IsNetworkIdentityField(this TypeReference tr) => + tr.Is() || + tr.Is() || + tr.IsDerivedFrom(); public static bool CanBeResolved(this TypeReference parent) { @@ -118,31 +108,21 @@ namespace Mirror.Weaver return true; } - /// - /// Makes T => Variable and imports function - /// - /// - /// - /// - public static MethodReference MakeGeneric(this MethodReference generic, TypeReference variableReference) + // Makes T => Variable and imports function + public static MethodReference MakeGeneric(this MethodReference generic, ModuleDefinition module, TypeReference variableReference) { GenericInstanceMethod instance = new GenericInstanceMethod(generic); instance.GenericArguments.Add(variableReference); - MethodReference readFunc = Weaver.CurrentAssembly.MainModule.ImportReference(instance); + MethodReference readFunc = module.ImportReference(instance); return readFunc; } - /// - /// Given a method of a generic class such as ArraySegment`T.get_Count, - /// and a generic instance such as ArraySegment`int - /// Creates a reference to the specialized method ArraySegment`int`.get_Count - /// Note that calling ArraySegment`T.get_Count directly gives an invalid IL error - /// - /// - /// - /// - public static MethodReference MakeHostInstanceGeneric(this MethodReference self, GenericInstanceType instanceType) + // Given a method of a generic class such as ArraySegment`T.get_Count, + // and a generic instance such as ArraySegment`int + // Creates a reference to the specialized method ArraySegment`int`.get_Count + // Note that calling ArraySegment`T.get_Count directly gives an invalid IL error + public static MethodReference MakeHostInstanceGeneric(this MethodReference self, ModuleDefinition module, GenericInstanceType instanceType) { MethodReference reference = new MethodReference(self.Name, self.ReturnType, instanceType) { @@ -157,22 +137,29 @@ namespace Mirror.Weaver foreach (GenericParameter generic_parameter in self.GenericParameters) reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference)); - return Weaver.CurrentAssembly.MainModule.ImportReference(reference); + return module.ImportReference(reference); } - /// - /// Given a field of a generic class such as Writer.write, - /// and a generic instance such as ArraySegment`int - /// Creates a reference to the specialized method ArraySegment`int`.get_Count - /// Note that calling ArraySegment`T.get_Count directly gives an invalid IL error - /// - /// - /// Generic Instance e.g. Writer - /// - public static FieldReference SpecializeField(this FieldReference self, GenericInstanceType instanceType) + // needed for NetworkBehaviour support + // https://github.com/vis2k/Mirror/pull/3073/ + public static FieldReference MakeHostInstanceGeneric(this FieldReference self) + { + var declaringType = new GenericInstanceType(self.DeclaringType); + foreach (var parameter in self.DeclaringType.GenericParameters) + { + declaringType.GenericArguments.Add(parameter); + } + return new FieldReference(self.Name, self.FieldType, declaringType); + } + + // Given a field of a generic class such as Writer.write, + // and a generic instance such as ArraySegment`int + // Creates a reference to the specialized method ArraySegment`int`.get_Count + // Note that calling ArraySegment`T.get_Count directly gives an invalid IL error + public static FieldReference SpecializeField(this FieldReference self, ModuleDefinition module, GenericInstanceType instanceType) { FieldReference reference = new FieldReference(self.Name, self.FieldType, instanceType); - return Weaver.CurrentAssembly.MainModule.ImportReference(reference); + return module.ImportReference(reference); } public static CustomAttribute GetCustomAttribute(this ICustomAttributeProvider method) @@ -229,21 +216,13 @@ namespace Mirror.Weaver return null; } - /// - /// Finds public fields in type and base type - /// - /// - /// + // Finds public fields in type and base type public static IEnumerable FindAllPublicFields(this TypeReference variable) { return FindAllPublicFields(variable.Resolve()); } - /// - /// Finds public fields in type and base type - /// - /// - /// + // Finds public fields in type and base type public static IEnumerable FindAllPublicFields(this TypeDefinition typeDefinition) { while (typeDefinition != null) @@ -269,5 +248,90 @@ namespace Mirror.Weaver } } } + + public static bool ContainsClass(this ModuleDefinition module, string nameSpace, string className) => + module.GetTypes().Any(td => td.Namespace == nameSpace && + td.Name == className); + + + public static AssemblyNameReference FindReference(this ModuleDefinition module, string referenceName) + { + foreach (AssemblyNameReference reference in module.AssemblyReferences) + { + if (reference.Name == referenceName) + return reference; + } + return null; + } + + // Takes generic arguments from child class and applies them to parent reference, if possible + // eg makes `Base` in Child : Base have `int` instead of `T` + // Originally by James-Frowen under MIT + // https://github.com/MirageNet/Mirage/commit/cf91e1d54796866d2cf87f8e919bb5c681977e45 + public static TypeReference ApplyGenericParameters(this TypeReference parentReference, + TypeReference childReference) + { + // If the parent is not generic, we got nothing to apply + if (!parentReference.IsGenericInstance) + return parentReference; + + GenericInstanceType parentGeneric = (GenericInstanceType)parentReference; + // make new type so we can replace the args on it + // resolve it so we have non-generic instance (eg just instance with instead of ) + // if we don't cecil will make it double generic (eg INVALID IL) + GenericInstanceType generic = new GenericInstanceType(parentReference.Resolve()); + foreach (TypeReference arg in parentGeneric.GenericArguments) + generic.GenericArguments.Add(arg); + + for (int i = 0; i < generic.GenericArguments.Count; i++) + { + // if arg is not generic + // eg List would be int so not generic. + // But List would be T so is generic + if (!generic.GenericArguments[i].IsGenericParameter) + continue; + + // get the generic name, eg T + string name = generic.GenericArguments[i].Name; + // find what type T is, eg turn it into `int` if `List` + TypeReference arg = FindMatchingGenericArgument(childReference, name); + + // import just to be safe + TypeReference imported = parentReference.Module.ImportReference(arg); + // set arg on generic, parent ref will be Base instead of just Base + generic.GenericArguments[i] = imported; + } + + return generic; + } + + // Finds the type reference for a generic parameter with the provided name in the child reference + // Originally by James-Frowen under MIT + // https://github.com/MirageNet/Mirage/commit/cf91e1d54796866d2cf87f8e919bb5c681977e45 + static TypeReference FindMatchingGenericArgument(TypeReference childReference, string paramName) + { + TypeDefinition def = childReference.Resolve(); + // child class must be generic if we are in this part of the code + // eg Child : Base <--- child must have generic if Base has T + // vs Child : Base <--- wont be here if Base has int (we check if T exists before calling this) + if (!def.HasGenericParameters) + throw new InvalidOperationException( + "Base class had generic parameters, but could not find them in child class"); + + // go through parameters in child class, and find the generic that matches the name + for (int i = 0; i < def.GenericParameters.Count; i++) + { + GenericParameter param = def.GenericParameters[i]; + if (param.Name == paramName) + { + GenericInstanceType generic = (GenericInstanceType)childReference; + // return generic arg with same index + return generic.GenericArguments[i]; + } + } + + // this should never happen, if it does it means that this code is bugged + throw new InvalidOperationException("Did not find matching generic"); + } } } diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Extensions.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Extensions.cs.meta index a5b8aa4..78660f9 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Extensions.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Extensions.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Helpers.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Helpers.cs index d864ee7..56b7385 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Helpers.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Helpers.cs @@ -1,16 +1,26 @@ using System.IO; +using System.Linq; using System.Reflection; +using Mono.CecilX; namespace Mirror.Weaver { static class Helpers { // This code is taken from SerializationWeaver - public static string UnityEngineDllDirectoryName() { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase); return directoryName?.Replace(@"file:\", ""); } + + public static bool IsEditorAssembly(AssemblyDefinition currentAssembly) + { + // we want to add the [InitializeOnLoad] attribute if it's available + // -> usually either 'UnityEditor' or 'UnityEditor.CoreModule' + return currentAssembly.MainModule.AssemblyReferences.Any(assemblyReference => + assemblyReference.Name.StartsWith(nameof(UnityEditor)) + ); + } } } diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Helpers.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Helpers.cs.meta index f49ae98..231f539 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Helpers.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Helpers.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Log.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Log.cs deleted file mode 100644 index fb28be4..0000000 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Log.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace Mirror.Weaver -{ - public static class Log - { - public static Action Warning; - public static Action Error; - } -} diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Log.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Log.cs.meta deleted file mode 100644 index ed584d5..0000000 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Log.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2a21c60c40a4c4d679c2b71a7c40882e -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Logger.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Logger.cs new file mode 100644 index 0000000..8be978f --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Logger.cs @@ -0,0 +1,13 @@ +using Mono.CecilX; + +namespace Mirror.Weaver +{ + // not static, because ILPostProcessor is multithreaded + public interface Logger + { + void Warning(string message); + void Warning(string message, MemberReference mr); + void Error(string message); + void Error(string message, MemberReference mr); + } +} diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Editor/AddScriptingDefine.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Logger.cs.meta similarity index 83% rename from UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Editor/AddScriptingDefine.cs.meta rename to UnityProject/Assets/Mirror/Editor/Weaver/Logger.cs.meta index dbff870..3f62978 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Editor/AddScriptingDefine.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Logger.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ed8acbde141f2d8469baf2142712de9e +guid: 2a21c60c40a4c4d679c2b71a7c40882e MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Mirror.Weaver.asmdef b/UnityProject/Assets/Mirror/Editor/Weaver/Mirror.Weaver.asmdef deleted file mode 100644 index 673d347..0000000 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Mirror.Weaver.asmdef +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "Mirror.Weaver", - "references": [ - "Mirror" - ], - "optionalUnityReferences": [], - "includePlatforms": [ - "Editor" - ], - "excludePlatforms": [], - "allowUnsafeCode": true, - "overrideReferences": false, - "precompiledReferences": [], - "autoReferenced": true, - "defineConstraints": [] -} \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Processors.meta index f3a01c1..eb719b4 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors.meta @@ -3,6 +3,6 @@ guid: e538d627280d2471b8c72fdea822ca49 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/CommandProcessor.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/CommandProcessor.cs index 1f46e01..55893f7 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/CommandProcessor.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/CommandProcessor.cs @@ -3,9 +3,7 @@ using Mono.CecilX.Cil; namespace Mirror.Weaver { - /// - /// Processes [Command] methods in NetworkBehaviour - /// + // Processes [Command] methods in NetworkBehaviour public static class CommandProcessor { /* @@ -15,7 +13,7 @@ namespace Mirror.Weaver NetworkWriter networkWriter = new NetworkWriter(); networkWriter.Write(thrusting); networkWriter.WritePackedUInt32((uint)spin); - base.SendCommandInternal(cmdName, networkWriter, cmdName); + base.SendCommandInternal(cmdName, networkWriter, channel); } public void CallCmdThrust(float thrusting, int spin) @@ -31,40 +29,37 @@ namespace Mirror.Weaver This way we do not need to modify the code anywhere else, and this works correctly in dependent assemblies */ - public static MethodDefinition ProcessCommandCall(TypeDefinition td, MethodDefinition md, CustomAttribute commandAttr) + public static MethodDefinition ProcessCommandCall(WeaverTypes weaverTypes, Writers writers, Logger Log, TypeDefinition td, MethodDefinition md, CustomAttribute commandAttr, ref bool WeavingFailed) { - MethodDefinition cmd = MethodProcessor.SubstituteMethod(td, md); + MethodDefinition cmd = MethodProcessor.SubstituteMethod(Log, td, md, ref WeavingFailed); ILProcessor worker = md.Body.GetILProcessor(); - NetworkBehaviourProcessor.WriteSetupLocals(worker); + NetworkBehaviourProcessor.WriteSetupLocals(worker, weaverTypes); // NetworkWriter writer = new NetworkWriter(); - NetworkBehaviourProcessor.WriteCreateWriter(worker); + NetworkBehaviourProcessor.WriteGetWriter(worker, weaverTypes); // write all the arguments that the user passed to the Cmd call - if (!NetworkBehaviourProcessor.WriteArguments(worker, md, RemoteCallType.Command)) + if (!NetworkBehaviourProcessor.WriteArguments(worker, writers, Log, md, RemoteCallType.Command, ref WeavingFailed)) return null; - string cmdName = md.Name; int channel = commandAttr.GetField("channel", 0); bool requiresAuthority = commandAttr.GetField("requiresAuthority", true); // invoke internal send and return // load 'base.' to call the SendCommand function with worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldtoken, td); - // invokerClass - worker.Emit(OpCodes.Call, WeaverTypes.getTypeFromHandleReference); - worker.Emit(OpCodes.Ldstr, cmdName); + // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions + worker.Emit(OpCodes.Ldstr, md.FullName); // writer worker.Emit(OpCodes.Ldloc_0); worker.Emit(OpCodes.Ldc_I4, channel); // requiresAuthority ? 1 : 0 worker.Emit(requiresAuthority ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); - worker.Emit(OpCodes.Call, WeaverTypes.sendCommandInternal); + worker.Emit(OpCodes.Call, weaverTypes.sendCommandInternal); - NetworkBehaviourProcessor.WriteRecycleWriter(worker); + NetworkBehaviourProcessor.WriteReturnWriter(worker, weaverTypes); worker.Emit(OpCodes.Ret); return cmd; @@ -81,22 +76,24 @@ namespace Mirror.Weaver ((ShipControl)obj).CmdThrust(reader.ReadSingle(), (int)reader.ReadPackedUInt32()); } */ - public static MethodDefinition ProcessCommandInvoke(TypeDefinition td, MethodDefinition method, MethodDefinition cmdCallFunc) + public static MethodDefinition ProcessCommandInvoke(WeaverTypes weaverTypes, Readers readers, Logger Log, TypeDefinition td, MethodDefinition method, MethodDefinition cmdCallFunc, ref bool WeavingFailed) { - MethodDefinition cmd = new MethodDefinition(Weaver.InvokeRpcPrefix + method.Name, + string cmdName = Weaver.GenerateMethodName(Weaver.InvokeRpcPrefix, method); + + MethodDefinition cmd = new MethodDefinition(cmdName, MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig, - WeaverTypes.Import(typeof(void))); + weaverTypes.Import(typeof(void))); ILProcessor worker = cmd.Body.GetILProcessor(); Instruction label = worker.Create(OpCodes.Nop); - NetworkBehaviourProcessor.WriteServerActiveCheck(worker, method.Name, label, "Command"); + NetworkBehaviourProcessor.WriteServerActiveCheck(worker, weaverTypes, method.Name, label, "Command"); // setup for reader worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Castclass, td); - if (!NetworkBehaviourProcessor.ReadArguments(method, worker, RemoteCallType.Command)) + if (!NetworkBehaviourProcessor.ReadArguments(method, readers, Log, worker, RemoteCallType.Command, ref WeavingFailed)) return null; AddSenderConnection(method, worker); @@ -105,7 +102,7 @@ namespace Mirror.Weaver worker.Emit(OpCodes.Callvirt, cmdCallFunc); worker.Emit(OpCodes.Ret); - NetworkBehaviourProcessor.AddInvokeParameters(cmd.Parameters); + NetworkBehaviourProcessor.AddInvokeParameters(weaverTypes, cmd.Parameters); td.Methods.Add(cmd); return cmd; diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/CommandProcessor.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/CommandProcessor.cs.meta index 4596f69..20c3e15 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/CommandProcessor.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/CommandProcessor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/MethodProcessor.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/MethodProcessor.cs index 455f9b2..8a4c581 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/MethodProcessor.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/MethodProcessor.cs @@ -7,34 +7,29 @@ namespace Mirror.Weaver { const string RpcPrefix = "UserCode_"; - // creates a method substitute - // For example, if we have this: - // public void CmdThrust(float thrusting, int spin) - // { - // xxxxx - // } + // For a function like + // [ClientRpc] void RpcTest(int value), + // Weaver substitutes the method and moves the code to a new method: + // UserCode_RpcTest(int value) <- contains original code + // RpcTest(int value) <- serializes parameters, sends the message // - // it will substitute the method and move the code to a new method with a provided name - // for example: - // - // public void CmdTrust(float thrusting, int spin) - // { - // } - // - // public void (float thrusting, int spin) - // { - // xxxxx - // } - // - // Note that all the calls to the method remain untouched - // - // the original method definition loses all code - // this returns the newly created method with all the user provided code - public static MethodDefinition SubstituteMethod(TypeDefinition td, MethodDefinition md) + // Note that all the calls to the method remain untouched. + // FixRemoteCallToBaseMethod replaces them afterwards. + public static MethodDefinition SubstituteMethod(Logger Log, TypeDefinition td, MethodDefinition md, ref bool WeavingFailed) { - string newName = RpcPrefix + md.Name; + string newName = Weaver.GenerateMethodName(RpcPrefix, md); + MethodDefinition cmd = new MethodDefinition(newName, md.Attributes, md.ReturnType); + // force the substitute method to be protected. + // -> public would show in the Inspector for UnityEvents as + // User_CmdUsePotion() etc. but the user shouldn't use those. + // -> private would not allow inheriting classes to call it, see + // OverrideVirtualWithBaseCallsBothVirtualAndBase test. + // -> IL has no concept of 'protected', it's called IsFamily there. + cmd.IsPublic = false; + cmd.IsFamily = true; + // add parameters foreach (ParameterDefinition pd in md.Parameters) { @@ -57,17 +52,21 @@ namespace Mirror.Weaver td.Methods.Add(cmd); - FixRemoteCallToBaseMethod(td, cmd); + FixRemoteCallToBaseMethod(Log, td, cmd, ref WeavingFailed); return cmd; } - /// - /// Finds and fixes call to base methods within remote calls - /// For example, changes `base.CmdDoSomething` to `base.CallCmdDoSomething` within `this.CallCmdDoSomething` - /// - /// - /// - public static void FixRemoteCallToBaseMethod(TypeDefinition type, MethodDefinition method) + // For a function like + // [ClientRpc] void RpcTest(int value), + // Weaver substitutes the method and moves the code to a new method: + // UserCode_RpcTest(int value) <- contains original code + // RpcTest(int value) <- serializes parameters, sends the message + // + // FixRemoteCallToBaseMethod replaces all calls to + // RpcTest(value) + // with + // UserCode_RpcTest(value) + public static void FixRemoteCallToBaseMethod(Logger Log, TypeDefinition type, MethodDefinition method, ref bool WeavingFailed) { string callName = method.Name; @@ -81,28 +80,43 @@ namespace Mirror.Weaver foreach (Instruction instruction in method.Body.Instructions) { - // if call to base.CmdDoSomething within this.CallCmdDoSomething - if (IsCallToMethod(instruction, out MethodDefinition calledMethod) && - calledMethod.Name == baseRemoteCallName) + // is this instruction a Call to a method? + // if yes, output the method so we can check it. + if (IsCallToMethod(instruction, out MethodDefinition calledMethod)) { - TypeDefinition baseType = type.BaseType.Resolve(); - MethodDefinition baseMethod = baseType.GetMethodInBaseType(callName); - - if (baseMethod == null) + // when considering if 'calledMethod' is a call to 'method', + // we originally compared .Name. + // + // to fix IL2CPP build bugs with overloaded Rpcs, we need to + // generated rpc names like + // RpcTest(string value) => RpcTestString(strig value) + // RpcTest(int value) => RpcTestInt(int value) + // to make them unique. + // + // calledMethod.Name is still "RpcTest", so we need to + // convert this to the generated name as well before comparing. + string calledMethodName_Generated = Weaver.GenerateMethodName("", calledMethod); + if (calledMethodName_Generated == baseRemoteCallName) { - Weaver.Error($"Could not find base method for {callName}", method); - return; + TypeDefinition baseType = type.BaseType.Resolve(); + MethodDefinition baseMethod = baseType.GetMethodInBaseType(callName); + + if (baseMethod == null) + { + Log.Error($"Could not find base method for {callName}", method); + WeavingFailed = true; + return; + } + + if (!baseMethod.IsVirtual) + { + Log.Error($"Could not find base method that was virtual {callName}", method); + WeavingFailed = true; + return; + } + + instruction.Operand = baseMethod; } - - if (!baseMethod.IsVirtual) - { - Weaver.Error($"Could not find base method that was virutal {callName}", method); - return; - } - - instruction.Operand = baseMethod; - - Weaver.DLog(type, "Replacing call to '{0}' with '{1}' inside '{2}'", calledMethod.FullName, baseMethod.FullName, method.FullName); } } } diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/MethodProcessor.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/MethodProcessor.cs.meta index b40023d..3c81894 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/MethodProcessor.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/MethodProcessor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/MonoBehaviourProcessor.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/MonoBehaviourProcessor.cs index 5f24487..e88c5d6 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/MonoBehaviourProcessor.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/MonoBehaviourProcessor.cs @@ -2,43 +2,54 @@ using Mono.CecilX; namespace Mirror.Weaver { - /// - /// only shows warnings in case we use SyncVars etc. for MonoBehaviour. - /// + // only shows warnings in case we use SyncVars etc. for MonoBehaviour. static class MonoBehaviourProcessor { - public static void Process(TypeDefinition td) + public static void Process(Logger Log, TypeDefinition td, ref bool WeavingFailed) { - ProcessSyncVars(td); - ProcessMethods(td); + ProcessSyncVars(Log, td, ref WeavingFailed); + ProcessMethods(Log, td, ref WeavingFailed); } - static void ProcessSyncVars(TypeDefinition td) + static void ProcessSyncVars(Logger Log, TypeDefinition td, ref bool WeavingFailed) { // find syncvars foreach (FieldDefinition fd in td.Fields) { if (fd.HasCustomAttribute()) - Weaver.Error($"SyncVar {fd.Name} must be inside a NetworkBehaviour. {td.Name} is not a NetworkBehaviour", fd); + { + Log.Error($"SyncVar {fd.Name} must be inside a NetworkBehaviour. {td.Name} is not a NetworkBehaviour", fd); + WeavingFailed = true; + } if (SyncObjectInitializer.ImplementsSyncObject(fd.FieldType)) { - Weaver.Error($"{fd.Name} is a SyncObject and must be inside a NetworkBehaviour. {td.Name} is not a NetworkBehaviour", fd); + Log.Error($"{fd.Name} is a SyncObject and must be inside a NetworkBehaviour. {td.Name} is not a NetworkBehaviour", fd); + WeavingFailed = true; } } } - static void ProcessMethods(TypeDefinition td) + static void ProcessMethods(Logger Log, TypeDefinition td, ref bool WeavingFailed) { // find command and RPC functions foreach (MethodDefinition md in td.Methods) { if (md.HasCustomAttribute()) - Weaver.Error($"Command {md.Name} must be declared inside a NetworkBehaviour", md); + { + Log.Error($"Command {md.Name} must be declared inside a NetworkBehaviour", md); + WeavingFailed = true; + } if (md.HasCustomAttribute()) - Weaver.Error($"ClientRpc {md.Name} must be declared inside a NetworkBehaviour", md); + { + Log.Error($"ClientRpc {md.Name} must be declared inside a NetworkBehaviour", md); + WeavingFailed = true; + } if (md.HasCustomAttribute()) - Weaver.Error($"TargetRpc {md.Name} must be declared inside a NetworkBehaviour", md); + { + Log.Error($"TargetRpc {md.Name} must be declared inside a NetworkBehaviour", md); + WeavingFailed = true; + } } } } diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/MonoBehaviourProcessor.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/MonoBehaviourProcessor.cs.meta index 4eb8840..ef3f5f4 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/MonoBehaviourProcessor.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/MonoBehaviourProcessor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs index 7b8fbdd..ac00f65 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs @@ -11,12 +11,17 @@ namespace Mirror.Weaver TargetRpc } - - /// - /// processes SyncVars, Cmds, Rpcs, etc. of NetworkBehaviours - /// + // processes SyncVars, Cmds, Rpcs, etc. of NetworkBehaviours class NetworkBehaviourProcessor { + AssemblyDefinition assembly; + WeaverTypes weaverTypes; + SyncVarAccessLists syncVarAccessLists; + SyncVarAttributeProcessor syncVarAttributeProcessor; + Writers writers; + Readers readers; + Logger Log; + List syncVars = new List(); List syncObjects = new List(); // @@ -42,56 +47,55 @@ namespace Mirror.Weaver public bool includeOwner; } - public NetworkBehaviourProcessor(TypeDefinition td) + public NetworkBehaviourProcessor(AssemblyDefinition assembly, WeaverTypes weaverTypes, SyncVarAccessLists syncVarAccessLists, Writers writers, Readers readers, Logger Log, TypeDefinition td) { - Weaver.DLog(td, "NetworkBehaviourProcessor"); + this.assembly = assembly; + this.weaverTypes = weaverTypes; + this.syncVarAccessLists = syncVarAccessLists; + this.writers = writers; + this.readers = readers; + this.Log = Log; + syncVarAttributeProcessor = new SyncVarAttributeProcessor(assembly, weaverTypes, syncVarAccessLists, Log); netBehaviourSubclass = td; } // return true if modified - public bool Process() + public bool Process(ref bool WeavingFailed) { // only process once if (WasProcessed(netBehaviourSubclass)) { return false; } - Weaver.DLog(netBehaviourSubclass, "Found NetworkBehaviour " + netBehaviourSubclass.FullName); - if (netBehaviourSubclass.HasGenericParameters) - { - Weaver.Error($"{netBehaviourSubclass.Name} cannot have generic parameters", netBehaviourSubclass); - // originally Process returned true in every case, except if already processed. - // maybe return false here in the future. - return true; - } - Weaver.DLog(netBehaviourSubclass, "Process Start"); MarkAsProcessed(netBehaviourSubclass); // deconstruct tuple and set fields - (syncVars, syncVarNetIds) = SyncVarProcessor.ProcessSyncVars(netBehaviourSubclass); + (syncVars, syncVarNetIds) = syncVarAttributeProcessor.ProcessSyncVars(netBehaviourSubclass, ref WeavingFailed); - syncObjects = SyncObjectProcessor.FindSyncObjectsFields(netBehaviourSubclass); + syncObjects = SyncObjectProcessor.FindSyncObjectsFields(writers, readers, Log, netBehaviourSubclass, ref WeavingFailed); - ProcessMethods(); - if (Weaver.WeavingFailed) - { - // originally Process returned true in every case, except if already processed. - // maybe return false here in the future. - return true; - } - GenerateConstants(); - - GenerateSerialization(); - if (Weaver.WeavingFailed) + ProcessMethods(ref WeavingFailed); + if (WeavingFailed) { // originally Process returned true in every case, except if already processed. // maybe return false here in the future. return true; } - GenerateDeSerialization(); - Weaver.DLog(netBehaviourSubclass, "Process Done"); + // inject initializations into static & instance constructor + InjectIntoStaticConstructor(ref WeavingFailed); + InjectIntoInstanceConstructor(ref WeavingFailed); + + GenerateSerialization(ref WeavingFailed); + if (WeavingFailed) + { + // originally Process returned true in every case, except if already processed. + // maybe return false here in the future. + return true; + } + + GenerateDeSerialization(ref WeavingFailed); return true; } @@ -102,14 +106,14 @@ namespace Mirror.Weaver which is used in InvokeCmd, InvokeRpc, etc. */ - public static void WriteClientActiveCheck(ILProcessor worker, string mdName, Instruction label, string errString) + public static void WriteClientActiveCheck(ILProcessor worker, WeaverTypes weaverTypes, string mdName, Instruction label, string errString) { // client active check - worker.Emit(OpCodes.Call, WeaverTypes.NetworkClientGetActive); + worker.Emit(OpCodes.Call, weaverTypes.NetworkClientGetActive); worker.Emit(OpCodes.Brtrue, label); - worker.Emit(OpCodes.Ldstr, errString + " " + mdName + " called on server."); - worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference); + worker.Emit(OpCodes.Ldstr, $"{errString} {mdName} called on server."); + worker.Emit(OpCodes.Call, weaverTypes.logErrorReference); worker.Emit(OpCodes.Ret); worker.Append(label); } @@ -118,39 +122,39 @@ namespace Mirror.Weaver if (!NetworkServer.active) Debug.LogError((object) "Command CmdMsgWhisper called on client."); */ - public static void WriteServerActiveCheck(ILProcessor worker, string mdName, Instruction label, string errString) + public static void WriteServerActiveCheck(ILProcessor worker, WeaverTypes weaverTypes, string mdName, Instruction label, string errString) { // server active check - worker.Emit(OpCodes.Call, WeaverTypes.NetworkServerGetActive); + worker.Emit(OpCodes.Call, weaverTypes.NetworkServerGetActive); worker.Emit(OpCodes.Brtrue, label); - worker.Emit(OpCodes.Ldstr, errString + " " + mdName + " called on client."); - worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference); + worker.Emit(OpCodes.Ldstr, $"{errString} {mdName} called on client."); + worker.Emit(OpCodes.Call, weaverTypes.logErrorReference); worker.Emit(OpCodes.Ret); worker.Append(label); } - public static void WriteSetupLocals(ILProcessor worker) + public static void WriteSetupLocals(ILProcessor worker, WeaverTypes weaverTypes) { worker.Body.InitLocals = true; - worker.Body.Variables.Add(new VariableDefinition(WeaverTypes.Import())); + worker.Body.Variables.Add(new VariableDefinition(weaverTypes.Import())); } - public static void WriteCreateWriter(ILProcessor worker) + public static void WriteGetWriter(ILProcessor worker, WeaverTypes weaverTypes) { // create writer - worker.Emit(OpCodes.Call, WeaverTypes.GetPooledWriterReference); + worker.Emit(OpCodes.Call, weaverTypes.GetWriterReference); worker.Emit(OpCodes.Stloc_0); } - public static void WriteRecycleWriter(ILProcessor worker) + public static void WriteReturnWriter(ILProcessor worker, WeaverTypes weaverTypes) { // NetworkWriterPool.Recycle(writer); worker.Emit(OpCodes.Ldloc_0); - worker.Emit(OpCodes.Call, WeaverTypes.RecycleWriterReference); + worker.Emit(OpCodes.Call, weaverTypes.ReturnWriterReference); } - public static bool WriteArguments(ILProcessor worker, MethodDefinition method, RemoteCallType callType) + public static bool WriteArguments(ILProcessor worker, Writers writers, Logger Log, MethodDefinition method, RemoteCallType callType, ref bool WeavingFailed) { // write each argument // example result @@ -180,10 +184,11 @@ namespace Mirror.Weaver continue; } - MethodReference writeFunc = Writers.GetWriteFunc(param.ParameterType); + MethodReference writeFunc = writers.GetWriteFunc(param.ParameterType, ref WeavingFailed); if (writeFunc == null) { - Weaver.Error($"{method.Name} has invalid parameter {param}", method); + Log.Error($"{method.Name} has invalid parameter {param}", method); + WeavingFailed = true; return false; } @@ -208,11 +213,11 @@ namespace Mirror.Weaver return td.GetMethod(ProcessedFunctionName) != null; } - public static void MarkAsProcessed(TypeDefinition td) + public void MarkAsProcessed(TypeDefinition td) { if (!WasProcessed(td)) { - MethodDefinition versionMethod = new MethodDefinition(ProcessedFunctionName, MethodAttributes.Private, WeaverTypes.Import(typeof(void))); + MethodDefinition versionMethod = new MethodDefinition(ProcessedFunctionName, MethodAttributes.Private, weaverTypes.Import(typeof(void))); ILProcessor worker = versionMethod.Body.GetILProcessor(); worker.Emit(OpCodes.Ret); td.Methods.Add(versionMethod); @@ -220,12 +225,32 @@ namespace Mirror.Weaver } #endregion - void GenerateConstants() + // helper function to remove 'Ret' from the end of the method if 'Ret' + // is the last instruction. + // returns false if there was an issue + static bool RemoveFinalRetInstruction(MethodDefinition method) { - if (commands.Count == 0 && clientRpcs.Count == 0 && targetRpcs.Count == 0 && syncObjects.Count == 0) - return; + // remove the return opcode from end of function. will add our own later. + if (method.Body.Instructions.Count != 0) + { + Instruction retInstr = method.Body.Instructions[method.Body.Instructions.Count - 1]; + if (retInstr.OpCode == OpCodes.Ret) + { + method.Body.Instructions.RemoveAt(method.Body.Instructions.Count - 1); + return true; + } + return false; + } - Weaver.DLog(netBehaviourSubclass, " GenerateConstants "); + // we did nothing, but there was no error. + return true; + } + + // we need to inject several initializations into NetworkBehaviour cctor + void InjectIntoStaticConstructor(ref bool WeavingFailed) + { + if (commands.Count == 0 && clientRpcs.Count == 0 && targetRpcs.Count == 0) + return; // find static constructor MethodDefinition cctor = netBehaviourSubclass.GetMethod(".cctor"); @@ -233,18 +258,11 @@ namespace Mirror.Weaver if (cctor != null) { // remove the return opcode from end of function. will add our own later. - if (cctor.Body.Instructions.Count != 0) + if (!RemoveFinalRetInstruction(cctor)) { - Instruction retInstr = cctor.Body.Instructions[cctor.Body.Instructions.Count - 1]; - if (retInstr.OpCode == OpCodes.Ret) - { - cctor.Body.Instructions.RemoveAt(cctor.Body.Instructions.Count - 1); - } - else - { - Weaver.Error($"{netBehaviourSubclass.Name} has invalid class constructor", cctor); - return; - } + Log.Error($"{netBehaviourSubclass.Name} has invalid class constructor", cctor); + WeavingFailed = true; + return; } } else @@ -255,97 +273,109 @@ namespace Mirror.Weaver MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Static, - WeaverTypes.Import(typeof(void))); + weaverTypes.Import(typeof(void))); } - // find instance constructor - MethodDefinition ctor = netBehaviourSubclass.GetMethod(".ctor"); - - if (ctor == null) - { - Weaver.Error($"{netBehaviourSubclass.Name} has invalid constructor", netBehaviourSubclass); - return; - } - - Instruction ret = ctor.Body.Instructions[ctor.Body.Instructions.Count - 1]; - if (ret.OpCode == OpCodes.Ret) - { - ctor.Body.Instructions.RemoveAt(ctor.Body.Instructions.Count - 1); - } - else - { - Weaver.Error($"{netBehaviourSubclass.Name} has invalid constructor", ctor); - return; - } - - // TODO: find out if the order below matters. If it doesn't split code below into 2 functions - ILProcessor ctorWorker = ctor.Body.GetILProcessor(); ILProcessor cctorWorker = cctor.Body.GetILProcessor(); + // register all commands in cctor for (int i = 0; i < commands.Count; ++i) { CmdResult cmdResult = commands[i]; - GenerateRegisterCommandDelegate(cctorWorker, WeaverTypes.registerCommandDelegateReference, commandInvocationFuncs[i], cmdResult); + GenerateRegisterCommandDelegate(cctorWorker, weaverTypes.registerCommandReference, commandInvocationFuncs[i], cmdResult); } + // register all client rpcs in cctor for (int i = 0; i < clientRpcs.Count; ++i) { ClientRpcResult clientRpcResult = clientRpcs[i]; - GenerateRegisterRemoteDelegate(cctorWorker, WeaverTypes.registerRpcDelegateReference, clientRpcInvocationFuncs[i], clientRpcResult.method.Name); + GenerateRegisterRemoteDelegate(cctorWorker, weaverTypes.registerRpcReference, clientRpcInvocationFuncs[i], clientRpcResult.method.FullName); } + // register all target rpcs in cctor for (int i = 0; i < targetRpcs.Count; ++i) { - GenerateRegisterRemoteDelegate(cctorWorker, WeaverTypes.registerRpcDelegateReference, targetRpcInvocationFuncs[i], targetRpcs[i].Name); - } - - foreach (FieldDefinition fd in syncObjects) - { - SyncObjectInitializer.GenerateSyncObjectInitializer(ctorWorker, fd); + GenerateRegisterRemoteDelegate(cctorWorker, weaverTypes.registerRpcReference, targetRpcInvocationFuncs[i], targetRpcs[i].FullName); } + // add final 'Ret' instruction to cctor cctorWorker.Append(cctorWorker.Create(OpCodes.Ret)); if (!cctorFound) { netBehaviourSubclass.Methods.Add(cctor); } - // finish ctor - ctorWorker.Append(ctorWorker.Create(OpCodes.Ret)); - // in case class had no cctor, it might have BeforeFieldInit, so injected cctor would be called too late netBehaviourSubclass.Attributes &= ~TypeAttributes.BeforeFieldInit; } + // we need to inject several initializations into NetworkBehaviour ctor + void InjectIntoInstanceConstructor(ref bool WeavingFailed) + { + if (syncObjects.Count == 0) + return; + + // find instance constructor + MethodDefinition ctor = netBehaviourSubclass.GetMethod(".ctor"); + if (ctor == null) + { + Log.Error($"{netBehaviourSubclass.Name} has invalid constructor", netBehaviourSubclass); + WeavingFailed = true; + return; + } + + // remove the return opcode from end of function. will add our own later. + if (!RemoveFinalRetInstruction(ctor)) + { + Log.Error($"{netBehaviourSubclass.Name} has invalid constructor", ctor); + WeavingFailed = true; + return; + } + + ILProcessor ctorWorker = ctor.Body.GetILProcessor(); + + // initialize all sync objects in ctor + foreach (FieldDefinition fd in syncObjects) + { + SyncObjectInitializer.GenerateSyncObjectInitializer(ctorWorker, weaverTypes, fd); + } + + // add final 'Ret' instruction to ctor + ctorWorker.Append(ctorWorker.Create(OpCodes.Ret)); + } + /* // This generates code like: NetworkBehaviour.RegisterCommandDelegate(base.GetType(), "CmdThrust", new NetworkBehaviour.CmdDelegate(ShipControl.InvokeCmdCmdThrust)); */ - void GenerateRegisterRemoteDelegate(ILProcessor worker, MethodReference registerMethod, MethodDefinition func, string cmdName) + + // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions + void GenerateRegisterRemoteDelegate(ILProcessor worker, MethodReference registerMethod, MethodDefinition func, string functionFullName) { worker.Emit(OpCodes.Ldtoken, netBehaviourSubclass); - worker.Emit(OpCodes.Call, WeaverTypes.getTypeFromHandleReference); - worker.Emit(OpCodes.Ldstr, cmdName); + worker.Emit(OpCodes.Call, weaverTypes.getTypeFromHandleReference); + worker.Emit(OpCodes.Ldstr, functionFullName); worker.Emit(OpCodes.Ldnull); worker.Emit(OpCodes.Ldftn, func); - worker.Emit(OpCodes.Newobj, WeaverTypes.CmdDelegateConstructor); + worker.Emit(OpCodes.Newobj, weaverTypes.RemoteCallDelegateConstructor); // worker.Emit(OpCodes.Call, registerMethod); } void GenerateRegisterCommandDelegate(ILProcessor worker, MethodReference registerMethod, MethodDefinition func, CmdResult cmdResult) { - string cmdName = cmdResult.method.Name; + // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions + string cmdName = cmdResult.method.FullName; bool requiresAuthority = cmdResult.requiresAuthority; worker.Emit(OpCodes.Ldtoken, netBehaviourSubclass); - worker.Emit(OpCodes.Call, WeaverTypes.getTypeFromHandleReference); + worker.Emit(OpCodes.Call, weaverTypes.getTypeFromHandleReference); worker.Emit(OpCodes.Ldstr, cmdName); worker.Emit(OpCodes.Ldnull); worker.Emit(OpCodes.Ldftn, func); - worker.Emit(OpCodes.Newobj, WeaverTypes.CmdDelegateConstructor); + worker.Emit(OpCodes.Newobj, weaverTypes.RemoteCallDelegateConstructor); // requiresAuthority ? 1 : 0 worker.Emit(requiresAuthority ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); @@ -353,10 +383,8 @@ namespace Mirror.Weaver worker.Emit(OpCodes.Call, registerMethod); } - void GenerateSerialization() + void GenerateSerialization(ref bool WeavingFailed) { - Weaver.DLog(netBehaviourSubclass, " GenerateSerialization"); - const string SerializeMethodName = "SerializeSyncVars"; if (netBehaviourSubclass.GetMethod(SerializeMethodName) != null) return; @@ -369,19 +397,19 @@ namespace Mirror.Weaver MethodDefinition serialize = new MethodDefinition(SerializeMethodName, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, - WeaverTypes.Import()); + weaverTypes.Import()); - serialize.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, WeaverTypes.Import())); - serialize.Parameters.Add(new ParameterDefinition("forceAll", ParameterAttributes.None, WeaverTypes.Import())); + serialize.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, weaverTypes.Import())); + serialize.Parameters.Add(new ParameterDefinition("forceAll", ParameterAttributes.None, weaverTypes.Import())); ILProcessor worker = serialize.Body.GetILProcessor(); serialize.Body.InitLocals = true; // loc_0, this local variable is to determine if any variable was dirty - VariableDefinition dirtyLocal = new VariableDefinition(WeaverTypes.Import()); + VariableDefinition dirtyLocal = new VariableDefinition(weaverTypes.Import()); serialize.Body.Variables.Add(dirtyLocal); - MethodReference baseSerialize = Resolvers.TryResolveMethodInParents(netBehaviourSubclass.BaseType, Weaver.CurrentAssembly, SerializeMethodName); + MethodReference baseSerialize = Resolvers.TryResolveMethodInParents(netBehaviourSubclass.BaseType, assembly, SerializeMethodName); if (baseSerialize != null) { // base @@ -401,22 +429,28 @@ namespace Mirror.Weaver worker.Emit(OpCodes.Ldarg_2); worker.Emit(OpCodes.Brfalse, initialStateLabel); - foreach (FieldDefinition syncVar in syncVars) + foreach (FieldDefinition syncVarDef in syncVars) { + FieldReference syncVar = syncVarDef; + if (netBehaviourSubclass.HasGenericParameters) + { + syncVar = syncVarDef.MakeHostInstanceGeneric(); + } // Generates a writer call for each sync variable // writer worker.Emit(OpCodes.Ldarg_1); // this worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldfld, syncVar); - MethodReference writeFunc = Writers.GetWriteFunc(syncVar.FieldType); + MethodReference writeFunc = writers.GetWriteFunc(syncVar.FieldType, ref WeavingFailed); if (writeFunc != null) { worker.Emit(OpCodes.Call, writeFunc); } else { - Weaver.Error($"{syncVar.Name} has unsupported type. Use a supported Mirror type instead", syncVar); + Log.Error($"{syncVar.Name} has unsupported type. Use a supported Mirror type instead", syncVar); + WeavingFailed = true; return; } } @@ -436,22 +470,28 @@ namespace Mirror.Weaver worker.Emit(OpCodes.Ldarg_1); // base worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Call, WeaverTypes.NetworkBehaviourDirtyBitsReference); - MethodReference writeUint64Func = Writers.GetWriteFunc(WeaverTypes.Import()); + worker.Emit(OpCodes.Call, weaverTypes.NetworkBehaviourDirtyBitsReference); + MethodReference writeUint64Func = writers.GetWriteFunc(weaverTypes.Import(), ref WeavingFailed); worker.Emit(OpCodes.Call, writeUint64Func); // generate a writer call for any dirty variable in this class // start at number of syncvars in parent - int dirtyBit = Weaver.WeaveLists.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); - foreach (FieldDefinition syncVar in syncVars) + int dirtyBit = syncVarAccessLists.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); + foreach (FieldDefinition syncVarDef in syncVars) { + + FieldReference syncVar = syncVarDef; + if (netBehaviourSubclass.HasGenericParameters) + { + syncVar = syncVarDef.MakeHostInstanceGeneric(); + } Instruction varLabel = worker.Create(OpCodes.Nop); // Generates: if ((base.get_syncVarDirtyBits() & 1uL) != 0uL) // base worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Call, WeaverTypes.NetworkBehaviourDirtyBitsReference); + worker.Emit(OpCodes.Call, weaverTypes.NetworkBehaviourDirtyBitsReference); // 8 bytes = long worker.Emit(OpCodes.Ldc_I8, 1L << dirtyBit); worker.Emit(OpCodes.And); @@ -464,14 +504,15 @@ namespace Mirror.Weaver worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldfld, syncVar); - MethodReference writeFunc = Writers.GetWriteFunc(syncVar.FieldType); + MethodReference writeFunc = writers.GetWriteFunc(syncVar.FieldType, ref WeavingFailed); if (writeFunc != null) { worker.Emit(OpCodes.Call, writeFunc); } else { - Weaver.Error($"{syncVar.Name} has unsupported type. Use a supported Mirror type instead", syncVar); + Log.Error($"{syncVar.Name} has unsupported type. Use a supported Mirror type instead", syncVar); + WeavingFailed = true; return; } @@ -484,11 +525,9 @@ namespace Mirror.Weaver dirtyBit += 1; } - if (Weaver.GenerateLogErrors) - { - worker.Emit(OpCodes.Ldstr, "Injected Serialize " + netBehaviourSubclass.Name); - worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference); - } + // add a log message if needed for debugging + //worker.Emit(OpCodes.Ldstr, $"Injected Serialize {netBehaviourSubclass.Name}"); + //worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference); // generate: return dirtyLocal worker.Emit(OpCodes.Ldloc_0); @@ -496,331 +535,103 @@ namespace Mirror.Weaver netBehaviourSubclass.Methods.Add(serialize); } - void DeserializeField(FieldDefinition syncVar, ILProcessor worker, MethodDefinition deserialize) + void DeserializeField(FieldDefinition syncVar, ILProcessor worker, ref bool WeavingFailed) { - // check for Hook function - MethodDefinition hookMethod = SyncVarProcessor.GetHookMethod(netBehaviourSubclass, syncVar); + // put 'this.' onto stack for 'this.syncvar' below + worker.Append(worker.Create(OpCodes.Ldarg_0)); - if (syncVar.FieldType.IsDerivedFrom()) + // push 'ref T this.field' + worker.Emit(OpCodes.Ldarg_0); + // if the netbehaviour class is generic, we need to make the field reference generic as well for correct IL + if (netBehaviourSubclass.HasGenericParameters) { - DeserializeNetworkBehaviourField(syncVar, worker, deserialize, hookMethod); - } - else if (syncVar.FieldType.IsNetworkIdentityField()) - { - DeserializeNetworkIdentityField(syncVar, worker, deserialize, hookMethod); + worker.Emit(OpCodes.Ldflda, syncVar.MakeHostInstanceGeneric()); } else { - DeserializeNormalField(syncVar, worker, deserialize, hookMethod); + worker.Emit(OpCodes.Ldflda, syncVar); } - } - - /// - /// [SyncVar] GameObject/NetworkIdentity? - /// - /// - /// - /// - /// - /// - void DeserializeNetworkIdentityField(FieldDefinition syncVar, ILProcessor worker, MethodDefinition deserialize, MethodDefinition hookMethod) - { - /* - Generates code like: - uint oldNetId = ___qNetId; - // returns GetSyncVarGameObject(___qNetId) - GameObject oldSyncVar = syncvar.getter; - ___qNetId = reader.ReadPackedUInt32(); - if (!SyncVarEqual(oldNetId, ref ___goNetId)) - { - // getter returns GetSyncVarGameObject(___qNetId) - OnSetQ(oldSyncVar, syncvar.getter); - } - */ - - // GameObject/NetworkIdentity SyncVar: - // OnSerialize sends writer.Write(go); - // OnDeserialize reads to __netId manually so we can use - // lookups in the getter (so it still works if objects - // move in and out of range repeatedly) - FieldDefinition netIdField = syncVarNetIds[syncVar]; - - // uint oldNetId = ___qNetId; - VariableDefinition oldNetId = new VariableDefinition(WeaverTypes.Import()); - deserialize.Body.Variables.Add(oldNetId); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, netIdField); - worker.Emit(OpCodes.Stloc, oldNetId); - - // GameObject/NetworkIdentity oldSyncVar = syncvar.getter; - VariableDefinition oldSyncVar = new VariableDefinition(syncVar.FieldType); - deserialize.Body.Variables.Add(oldSyncVar); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, syncVar); - worker.Emit(OpCodes.Stloc, oldSyncVar); - - // read id and store in netId field BEFORE calling the hook - // -> this makes way more sense. by definition, the hook is - // supposed to be called after it was changed. not before. - // -> setting it BEFORE calling the hook fixes the following bug: - // https://github.com/vis2k/Mirror/issues/1151 in host mode - // where the value during the Hook call would call Cmds on - // the host server, and they would all happen and compare - // values BEFORE the hook even returned and hence BEFORE the - // actual value was even set. - // put 'this.' onto stack for 'this.netId' below - worker.Emit(OpCodes.Ldarg_0); - // reader. for 'reader.Read()' below - worker.Emit(OpCodes.Ldarg_1); - // Read() - worker.Emit(OpCodes.Call, Readers.GetReadFunc(WeaverTypes.Import())); - // netId - worker.Emit(OpCodes.Stfld, netIdField); + // hook? then push 'new Action(Hook)' onto stack + MethodDefinition hookMethod = syncVarAttributeProcessor.GetHookMethod(netBehaviourSubclass, syncVar, ref WeavingFailed); if (hookMethod != null) { - // call Hook(this.GetSyncVarGameObject/NetworkIdentity(reader.ReadPackedUInt32())) - // because we send/receive the netID, not the GameObject/NetworkIdentity - // but only if SyncVar changed. otherwise a client would - // get hook calls for all initial values, even if they - // didn't change from the default values on the client. - // see also: https://github.com/vis2k/Mirror/issues/1278 - - // IMPORTANT: for GameObjects/NetworkIdentities we usually - // use SyncVarGameObjectEqual to compare equality. - // in this case however, we can just use - // SyncVarEqual with the two uint netIds. - // => this is easier weaver code because we don't - // have to get the GameObject/NetworkIdentity - // from the uint netId - // => this is faster because we void one - // GetComponent call for GameObjects to get - // their NetworkIdentity when comparing. - - // Generates: if (!SyncVarEqual); - Instruction syncVarEqualLabel = worker.Create(OpCodes.Nop); - - // 'this.' for 'this.SyncVarEqual' - worker.Emit(OpCodes.Ldarg_0); - // 'oldNetId' - worker.Emit(OpCodes.Ldloc, oldNetId); - // 'ref this.__netId' - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldflda, netIdField); - // call the function - GenericInstanceMethod syncVarEqualGm = new GenericInstanceMethod(WeaverTypes.syncVarEqualReference); - syncVarEqualGm.GenericArguments.Add(netIdField.FieldType); - worker.Emit(OpCodes.Call, syncVarEqualGm); - worker.Emit(OpCodes.Brtrue, syncVarEqualLabel); - - // call the hook - // Generates: OnValueChanged(oldValue, this.syncVar); - SyncVarProcessor.WriteCallHookMethodUsingField(worker, hookMethod, oldSyncVar, syncVar); - - // Generates: end if (!SyncVarEqual); - worker.Append(syncVarEqualLabel); + syncVarAttributeProcessor.GenerateNewActionFromHookMethod(syncVar, worker, hookMethod); } - } - - /// - /// [SyncVar] NetworkBehaviour - /// - /// - /// - /// - /// - /// - void DeserializeNetworkBehaviourField(FieldDefinition syncVar, ILProcessor worker, MethodDefinition deserialize, MethodDefinition hookMethod) - { - /* - Generates code like: - uint oldNetId = ___qNetId.netId; - byte oldCompIndex = ___qNetId.componentIndex; - T oldSyncVar = syncvar.getter; - ___qNetId.netId = reader.ReadPackedUInt32(); - ___qNetId.componentIndex = reader.ReadByte(); - if (!SyncVarEqual(oldNetId, ref ___goNetId)) - { - // getter returns GetSyncVarGameObject(___qNetId) - OnSetQ(oldSyncVar, syncvar.getter); - } - */ - - // GameObject/NetworkIdentity SyncVar: - // OnSerialize sends writer.Write(go); - // OnDeserialize reads to __netId manually so we can use - // lookups in the getter (so it still works if objects - // move in and out of range repeatedly) - FieldDefinition netIdField = syncVarNetIds[syncVar]; - - // uint oldNetId = ___qNetId; - VariableDefinition oldNetId = new VariableDefinition(WeaverTypes.Import()); - deserialize.Body.Variables.Add(oldNetId); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, netIdField); - worker.Emit(OpCodes.Stloc, oldNetId); - - // GameObject/NetworkIdentity oldSyncVar = syncvar.getter; - VariableDefinition oldSyncVar = new VariableDefinition(syncVar.FieldType); - deserialize.Body.Variables.Add(oldSyncVar); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, syncVar); - worker.Emit(OpCodes.Stloc, oldSyncVar); - - // read id and store in netId field BEFORE calling the hook - // -> this makes way more sense. by definition, the hook is - // supposed to be called after it was changed. not before. - // -> setting it BEFORE calling the hook fixes the following bug: - // https://github.com/vis2k/Mirror/issues/1151 in host mode - // where the value during the Hook call would call Cmds on - // the host server, and they would all happen and compare - // values BEFORE the hook even returned and hence BEFORE the - // actual value was even set. - // put 'this.' onto stack for 'this.netId' below - worker.Emit(OpCodes.Ldarg_0); - // reader. for 'reader.Read()' below - worker.Emit(OpCodes.Ldarg_1); - // Read() - worker.Emit(OpCodes.Call, Readers.GetReadFunc(WeaverTypes.Import())); - // netId - worker.Emit(OpCodes.Stfld, netIdField); - - if (hookMethod != null) + // otherwise push 'null' as hook + else { - // call Hook(this.GetSyncVarGameObject/NetworkIdentity(reader.ReadPackedUInt32())) - // because we send/receive the netID, not the GameObject/NetworkIdentity - // but only if SyncVar changed. otherwise a client would - // get hook calls for all initial values, even if they - // didn't change from the default values on the client. - // see also: https://github.com/vis2k/Mirror/issues/1278 + worker.Emit(OpCodes.Ldnull); + } - // IMPORTANT: for GameObjects/NetworkIdentities we usually - // use SyncVarGameObjectEqual to compare equality. - // in this case however, we can just use - // SyncVarEqual with the two uint netIds. - // => this is easier weaver code because we don't - // have to get the GameObject/NetworkIdentity - // from the uint netId - // => this is faster because we void one - // GetComponent call for GameObjects to get - // their NetworkIdentity when comparing. + // call GeneratedSyncVarDeserialize. + // special cases for GameObject/NetworkIdentity/NetworkBehaviour + // passing netId too for persistence. + if (syncVar.FieldType.Is()) + { + // reader + worker.Emit(OpCodes.Ldarg_1); - // Generates: if (!SyncVarEqual); - Instruction syncVarEqualLabel = worker.Create(OpCodes.Nop); - - // 'this.' for 'this.SyncVarEqual' - worker.Emit(OpCodes.Ldarg_0); - // 'oldNetId' - worker.Emit(OpCodes.Ldloc, oldNetId); - // 'ref this.__netId' + // GameObject setter needs one more parameter: netId field ref + FieldDefinition netIdField = syncVarNetIds[syncVar]; worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldflda, netIdField); - // call the function - GenericInstanceMethod syncVarEqualGm = new GenericInstanceMethod(WeaverTypes.syncVarEqualReference); - syncVarEqualGm.GenericArguments.Add(netIdField.FieldType); - worker.Emit(OpCodes.Call, syncVarEqualGm); - worker.Emit(OpCodes.Brtrue, syncVarEqualLabel); - - // call the hook - // Generates: OnValueChanged(oldValue, this.syncVar); - SyncVarProcessor.WriteCallHookMethodUsingField(worker, hookMethod, oldSyncVar, syncVar); - - // Generates: end if (!SyncVarEqual); - worker.Append(syncVarEqualLabel); + worker.Emit(OpCodes.Call, weaverTypes.generatedSyncVarDeserialize_GameObject); } - } + else if (syncVar.FieldType.Is()) + { + // reader + worker.Emit(OpCodes.Ldarg_1); + // NetworkIdentity deserialize needs one more parameter: netId field ref + FieldDefinition netIdField = syncVarNetIds[syncVar]; + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldflda, netIdField); + worker.Emit(OpCodes.Call, weaverTypes.generatedSyncVarDeserialize_NetworkIdentity); + } + // TODO this only uses the persistent netId for types DERIVED FROM NB. + // not if the type is just 'NetworkBehaviour'. + // this is what original implementation did too. fix it after. + else if (syncVar.FieldType.IsDerivedFrom()) + { + // reader + worker.Emit(OpCodes.Ldarg_1); - /// - /// [SyncVar] int/float/struct/etc.? - /// - /// - /// - /// - /// - /// - void DeserializeNormalField(FieldDefinition syncVar, ILProcessor serWorker, MethodDefinition deserialize, MethodDefinition hookMethod) - { - /* - Generates code like: - // for hook - int oldValue = a; - Networka = reader.ReadPackedInt32(); - if (!SyncVarEqual(oldValue, ref a)) + // NetworkIdentity deserialize needs one more parameter: netId field ref + // (actually its a NetworkBehaviourSyncVar type) + FieldDefinition netIdField = syncVarNetIds[syncVar]; + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldflda, netIdField); + // make generic version of GeneratedSyncVarSetter_NetworkBehaviour + MethodReference getFunc = weaverTypes.generatedSyncVarDeserialize_NetworkBehaviour_T.MakeGeneric(assembly.MainModule, syncVar.FieldType); + worker.Emit(OpCodes.Call, getFunc); + } + else + { + // T value = reader.ReadT(); + // this is still in IL because otherwise weaver generated + // readers/writers don't seem to work in tests. + // besides, this also avoids reader.Read overhead. + MethodReference readFunc = readers.GetReadFunc(syncVar.FieldType, ref WeavingFailed); + if (readFunc == null) { - OnSetA(oldValue, Networka); + Log.Error($"{syncVar.Name} has unsupported type. Use a supported Mirror type instead", syncVar); + WeavingFailed = true; + return; } - */ + // reader. for 'reader.Read()' below + worker.Emit(OpCodes.Ldarg_1); + // reader.Read() + worker.Emit(OpCodes.Call, readFunc); - MethodReference readFunc = Readers.GetReadFunc(syncVar.FieldType); - if (readFunc == null) - { - Weaver.Error($"{syncVar.Name} has unsupported type. Use a supported Mirror type instead", syncVar); - return; - } - - // T oldValue = value; - VariableDefinition oldValue = new VariableDefinition(syncVar.FieldType); - deserialize.Body.Variables.Add(oldValue); - serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); - serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar)); - serWorker.Append(serWorker.Create(OpCodes.Stloc, oldValue)); - - // read value and store in syncvar BEFORE calling the hook - // -> this makes way more sense. by definition, the hook is - // supposed to be called after it was changed. not before. - // -> setting it BEFORE calling the hook fixes the following bug: - // https://github.com/vis2k/Mirror/issues/1151 in host mode - // where the value during the Hook call would call Cmds on - // the host server, and they would all happen and compare - // values BEFORE the hook even returned and hence BEFORE the - // actual value was even set. - // put 'this.' onto stack for 'this.syncvar' below - serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); - // reader. for 'reader.Read()' below - serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); - // reader.Read() - serWorker.Append(serWorker.Create(OpCodes.Call, readFunc)); - // syncvar - serWorker.Append(serWorker.Create(OpCodes.Stfld, syncVar)); - - if (hookMethod != null) - { - // call hook - // but only if SyncVar changed. otherwise a client would - // get hook calls for all initial values, even if they - // didn't change from the default values on the client. - // see also: https://github.com/vis2k/Mirror/issues/1278 - - // Generates: if (!SyncVarEqual); - Instruction syncVarEqualLabel = serWorker.Create(OpCodes.Nop); - - // 'this.' for 'this.SyncVarEqual' - serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); - // 'oldValue' - serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldValue)); - // 'ref this.syncVar' - serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); - serWorker.Append(serWorker.Create(OpCodes.Ldflda, syncVar)); - // call the function - GenericInstanceMethod syncVarEqualGm = new GenericInstanceMethod(WeaverTypes.syncVarEqualReference); - syncVarEqualGm.GenericArguments.Add(syncVar.FieldType); - serWorker.Append(serWorker.Create(OpCodes.Call, syncVarEqualGm)); - serWorker.Append(serWorker.Create(OpCodes.Brtrue, syncVarEqualLabel)); - - // call the hook - // Generates: OnValueChanged(oldValue, this.syncVar); - SyncVarProcessor.WriteCallHookMethodUsingField(serWorker, hookMethod, oldValue, syncVar); - - // Generates: end if (!SyncVarEqual); - serWorker.Append(syncVarEqualLabel); + // make generic version of GeneratedSyncVarDeserialize + MethodReference generic = weaverTypes.generatedSyncVarDeserialize.MakeGeneric(assembly.MainModule, syncVar.FieldType); + worker.Emit(OpCodes.Call, generic); } } - void GenerateDeSerialization() + void GenerateDeSerialization(ref bool WeavingFailed) { - Weaver.DLog(netBehaviourSubclass, " GenerateDeSerialization"); - const string DeserializeMethodName = "DeserializeSyncVars"; if (netBehaviourSubclass.GetMethod(DeserializeMethodName) != null) return; @@ -833,17 +644,17 @@ namespace Mirror.Weaver MethodDefinition serialize = new MethodDefinition(DeserializeMethodName, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, - WeaverTypes.Import(typeof(void))); + weaverTypes.Import(typeof(void))); - serialize.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, WeaverTypes.Import())); - serialize.Parameters.Add(new ParameterDefinition("initialState", ParameterAttributes.None, WeaverTypes.Import())); + serialize.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, weaverTypes.Import())); + serialize.Parameters.Add(new ParameterDefinition("initialState", ParameterAttributes.None, weaverTypes.Import())); ILProcessor serWorker = serialize.Body.GetILProcessor(); // setup local for dirty bits serialize.Body.InitLocals = true; - VariableDefinition dirtyBitsLocal = new VariableDefinition(WeaverTypes.Import()); + VariableDefinition dirtyBitsLocal = new VariableDefinition(weaverTypes.Import()); serialize.Body.Variables.Add(dirtyBitsLocal); - MethodReference baseDeserialize = Resolvers.TryResolveMethodInParents(netBehaviourSubclass.BaseType, Weaver.CurrentAssembly, DeserializeMethodName); + MethodReference baseDeserialize = Resolvers.TryResolveMethodInParents(netBehaviourSubclass.BaseType, assembly, DeserializeMethodName); if (baseDeserialize != null) { // base @@ -863,7 +674,7 @@ namespace Mirror.Weaver foreach (FieldDefinition syncVar in syncVars) { - DeserializeField(syncVar, serWorker, serialize); + DeserializeField(syncVar, serWorker, ref WeavingFailed); } serWorker.Append(serWorker.Create(OpCodes.Ret)); @@ -873,12 +684,12 @@ namespace Mirror.Weaver // get dirty bits serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); - serWorker.Append(serWorker.Create(OpCodes.Call, Readers.GetReadFunc(WeaverTypes.Import()))); + serWorker.Append(serWorker.Create(OpCodes.Call, readers.GetReadFunc(weaverTypes.Import(), ref WeavingFailed))); serWorker.Append(serWorker.Create(OpCodes.Stloc_0)); // conditionally read each syncvar // start at number of syncvars in parent - int dirtyBit = Weaver.WeaveLists.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); + int dirtyBit = syncVarAccessLists.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); foreach (FieldDefinition syncVar in syncVars) { Instruction varLabel = serWorker.Create(OpCodes.Nop); @@ -889,23 +700,21 @@ namespace Mirror.Weaver serWorker.Append(serWorker.Create(OpCodes.And)); serWorker.Append(serWorker.Create(OpCodes.Brfalse, varLabel)); - DeserializeField(syncVar, serWorker, serialize); + DeserializeField(syncVar, serWorker, ref WeavingFailed); serWorker.Append(varLabel); dirtyBit += 1; } - if (Weaver.GenerateLogErrors) - { - serWorker.Append(serWorker.Create(OpCodes.Ldstr, "Injected Deserialize " + netBehaviourSubclass.Name)); - serWorker.Append(serWorker.Create(OpCodes.Call, WeaverTypes.logErrorReference)); - } + // add a log message if needed for debugging + //serWorker.Append(serWorker.Create(OpCodes.Ldstr, $"Injected Deserialize {netBehaviourSubclass.Name}")); + //serWorker.Append(serWorker.Create(OpCodes.Call, WeaverTypes.logErrorReference)); serWorker.Append(serWorker.Create(OpCodes.Ret)); netBehaviourSubclass.Methods.Add(serialize); } - public static bool ReadArguments(MethodDefinition method, ILProcessor worker, RemoteCallType callType) + public static bool ReadArguments(MethodDefinition method, Readers readers, Logger Log, ILProcessor worker, RemoteCallType callType, ref bool WeavingFailed) { // read each argument // example result @@ -935,11 +744,12 @@ namespace Mirror.Weaver } - MethodReference readFunc = Readers.GetReadFunc(param.ParameterType); + MethodReference readFunc = readers.GetReadFunc(param.ParameterType, ref WeavingFailed); if (readFunc == null) { - Weaver.Error($"{method.Name} has invalid parameter {param}. Unsupported type {param.ParameterType}, use a supported Mirror type instead", method); + Log.Error($"{method.Name} has invalid parameter {param}. Unsupported type {param.ParameterType}, use a supported Mirror type instead", method); + WeavingFailed = true; return false; } @@ -959,55 +769,59 @@ namespace Mirror.Weaver return true; } - public static void AddInvokeParameters(ICollection collection) + public static void AddInvokeParameters(WeaverTypes weaverTypes, ICollection collection) { - collection.Add(new ParameterDefinition("obj", ParameterAttributes.None, WeaverTypes.Import())); - collection.Add(new ParameterDefinition("reader", ParameterAttributes.None, WeaverTypes.Import())); + collection.Add(new ParameterDefinition("obj", ParameterAttributes.None, weaverTypes.Import())); + collection.Add(new ParameterDefinition("reader", ParameterAttributes.None, weaverTypes.Import())); // senderConnection is only used for commands but NetworkBehaviour.CmdDelegate is used for all remote calls - collection.Add(new ParameterDefinition("senderConnection", ParameterAttributes.None, WeaverTypes.Import())); + collection.Add(new ParameterDefinition("senderConnection", ParameterAttributes.None, weaverTypes.Import())); } // check if a Command/TargetRpc/Rpc function & parameters are valid for weaving - public static bool ValidateRemoteCallAndParameters(MethodDefinition method, RemoteCallType callType) + public bool ValidateRemoteCallAndParameters(MethodDefinition method, RemoteCallType callType, ref bool WeavingFailed) { if (method.IsStatic) { - Weaver.Error($"{method.Name} must not be static", method); + Log.Error($"{method.Name} must not be static", method); + WeavingFailed = true; return false; } - return ValidateFunction(method) && - ValidateParameters(method, callType); + return ValidateFunction(method, ref WeavingFailed) && + ValidateParameters(method, callType, ref WeavingFailed); } // check if a Command/TargetRpc/Rpc function is valid for weaving - static bool ValidateFunction(MethodReference md) + bool ValidateFunction(MethodReference md, ref bool WeavingFailed) { if (md.ReturnType.Is()) { - Weaver.Error($"{md.Name} cannot be a coroutine", md); + Log.Error($"{md.Name} cannot be a coroutine", md); + WeavingFailed = true; return false; } if (!md.ReturnType.Is(typeof(void))) { - Weaver.Error($"{md.Name} cannot return a value. Make it void instead", md); + Log.Error($"{md.Name} cannot return a value. Make it void instead", md); + WeavingFailed = true; return false; } if (md.HasGenericParameters) { - Weaver.Error($"{md.Name} cannot have generic parameters", md); + Log.Error($"{md.Name} cannot have generic parameters", md); + WeavingFailed = true; return false; } return true; } // check if all Command/TargetRpc/Rpc function's parameters are valid for weaving - static bool ValidateParameters(MethodReference method, RemoteCallType callType) + bool ValidateParameters(MethodReference method, RemoteCallType callType, ref bool WeavingFailed) { for (int i = 0; i < method.Parameters.Count; ++i) { ParameterDefinition param = method.Parameters[i]; - if (!ValidateParameter(method, param, callType, i == 0)) + if (!ValidateParameter(method, param, callType, i == 0, ref WeavingFailed)) { return false; } @@ -1016,36 +830,46 @@ namespace Mirror.Weaver } // validate parameters for a remote function call like Rpc/Cmd - static bool ValidateParameter(MethodReference method, ParameterDefinition param, RemoteCallType callType, bool firstParam) + bool ValidateParameter(MethodReference method, ParameterDefinition param, RemoteCallType callType, bool firstParam, ref bool WeavingFailed) { + // need to check this before any type lookups since those will fail since generic types don't resolve + if (param.ParameterType.IsGenericParameter) + { + Log.Error($"{method.Name} cannot have generic parameters", method); + WeavingFailed = true; + return false; + } + bool isNetworkConnection = param.ParameterType.Is(); bool isSenderConnection = IsSenderConnection(param, callType); if (param.IsOut) { - Weaver.Error($"{method.Name} cannot have out parameters", method); + Log.Error($"{method.Name} cannot have out parameters", method); + WeavingFailed = true; return false; } - // if not SenderConnection And not TargetRpc NetworkConnection first param if (!isSenderConnection && isNetworkConnection && !(callType == RemoteCallType.TargetRpc && firstParam)) { if (callType == RemoteCallType.Command) { - Weaver.Error($"{method.Name} has invalid parameter {param}, Cannot pass NetworkConnections. Instead use 'NetworkConnectionToClient conn = null' to get the sender's connection on the server", method); + Log.Error($"{method.Name} has invalid parameter {param}, Cannot pass NetworkConnections. Instead use 'NetworkConnectionToClient conn = null' to get the sender's connection on the server", method); } else { - Weaver.Error($"{method.Name} has invalid parameter {param}. Cannot pass NetworkConnections", method); + Log.Error($"{method.Name} has invalid parameter {param}. Cannot pass NetworkConnections", method); } + WeavingFailed = true; return false; } // sender connection can be optional if (param.IsOptional && !isSenderConnection) { - Weaver.Error($"{method.Name} cannot have optional parameters", method); + Log.Error($"{method.Name} cannot have optional parameters", method); + WeavingFailed = true; return false; } @@ -1065,7 +889,7 @@ namespace Mirror.Weaver || type.Resolve().IsDerivedFrom(); } - void ProcessMethods() + void ProcessMethods(ref bool WeavingFailed) { HashSet names = new HashSet(); @@ -1078,44 +902,39 @@ namespace Mirror.Weaver { if (ca.AttributeType.Is()) { - ProcessCommand(names, md, ca); + ProcessCommand(names, md, ca, ref WeavingFailed); break; } if (ca.AttributeType.Is()) { - ProcessTargetRpc(names, md, ca); + ProcessTargetRpc(names, md, ca, ref WeavingFailed); break; } if (ca.AttributeType.Is()) { - ProcessClientRpc(names, md, ca); + ProcessClientRpc(names, md, ca, ref WeavingFailed); break; } } } } - void ProcessClientRpc(HashSet names, MethodDefinition md, CustomAttribute clientRpcAttr) + void ProcessClientRpc(HashSet names, MethodDefinition md, CustomAttribute clientRpcAttr, ref bool WeavingFailed) { if (md.IsAbstract) { - Weaver.Error("Abstract ClientRpc are currently not supported, use virtual method instead", md); + Log.Error("Abstract ClientRpc are currently not supported, use virtual method instead", md); + WeavingFailed = true; return; } - if (!ValidateRemoteCallAndParameters(md, RemoteCallType.ClientRpc)) + if (!ValidateRemoteCallAndParameters(md, RemoteCallType.ClientRpc, ref WeavingFailed)) { return; } - if (names.Contains(md.Name)) - { - Weaver.Error($"Duplicate ClientRpc name {md.Name}", md); - return; - } - bool includeOwner = clientRpcAttr.GetField("includeOwner", true); names.Add(md.Name); @@ -1125,62 +944,53 @@ namespace Mirror.Weaver includeOwner = includeOwner }); - MethodDefinition rpcCallFunc = RpcProcessor.ProcessRpcCall(netBehaviourSubclass, md, clientRpcAttr); + MethodDefinition rpcCallFunc = RpcProcessor.ProcessRpcCall(weaverTypes, writers, Log, netBehaviourSubclass, md, clientRpcAttr, ref WeavingFailed); // need null check here because ProcessRpcCall returns null if it can't write all the args if (rpcCallFunc == null) { return; } - MethodDefinition rpcFunc = RpcProcessor.ProcessRpcInvoke(netBehaviourSubclass, md, rpcCallFunc); + MethodDefinition rpcFunc = RpcProcessor.ProcessRpcInvoke(weaverTypes, writers, readers, Log, netBehaviourSubclass, md, rpcCallFunc, ref WeavingFailed); if (rpcFunc != null) { clientRpcInvocationFuncs.Add(rpcFunc); } } - void ProcessTargetRpc(HashSet names, MethodDefinition md, CustomAttribute targetRpcAttr) + void ProcessTargetRpc(HashSet names, MethodDefinition md, CustomAttribute targetRpcAttr, ref bool WeavingFailed) { if (md.IsAbstract) { - Weaver.Error("Abstract TargetRpc are currently not supported, use virtual method instead", md); + Log.Error("Abstract TargetRpc are currently not supported, use virtual method instead", md); + WeavingFailed = true; return; } - if (!ValidateRemoteCallAndParameters(md, RemoteCallType.TargetRpc)) + if (!ValidateRemoteCallAndParameters(md, RemoteCallType.TargetRpc, ref WeavingFailed)) return; - if (names.Contains(md.Name)) - { - Weaver.Error($"Duplicate Target Rpc name {md.Name}", md); - return; - } names.Add(md.Name); targetRpcs.Add(md); - MethodDefinition rpcCallFunc = TargetRpcProcessor.ProcessTargetRpcCall(netBehaviourSubclass, md, targetRpcAttr); + MethodDefinition rpcCallFunc = TargetRpcProcessor.ProcessTargetRpcCall(weaverTypes, writers, Log, netBehaviourSubclass, md, targetRpcAttr, ref WeavingFailed); - MethodDefinition rpcFunc = TargetRpcProcessor.ProcessTargetRpcInvoke(netBehaviourSubclass, md, rpcCallFunc); + MethodDefinition rpcFunc = TargetRpcProcessor.ProcessTargetRpcInvoke(weaverTypes, readers, Log, netBehaviourSubclass, md, rpcCallFunc, ref WeavingFailed); if (rpcFunc != null) { targetRpcInvocationFuncs.Add(rpcFunc); } } - void ProcessCommand(HashSet names, MethodDefinition md, CustomAttribute commandAttr) + void ProcessCommand(HashSet names, MethodDefinition md, CustomAttribute commandAttr, ref bool WeavingFailed) { if (md.IsAbstract) { - Weaver.Error("Abstract Commands are currently not supported, use virtual method instead", md); + Log.Error("Abstract Commands are currently not supported, use virtual method instead", md); + WeavingFailed = true; return; } - if (!ValidateRemoteCallAndParameters(md, RemoteCallType.Command)) + if (!ValidateRemoteCallAndParameters(md, RemoteCallType.Command, ref WeavingFailed)) return; - if (names.Contains(md.Name)) - { - Weaver.Error($"Duplicate Command name {md.Name}", md); - return; - } - bool requiresAuthority = commandAttr.GetField("requiresAuthority", true); names.Add(md.Name); @@ -1190,9 +1000,9 @@ namespace Mirror.Weaver requiresAuthority = requiresAuthority }); - MethodDefinition cmdCallFunc = CommandProcessor.ProcessCommandCall(netBehaviourSubclass, md, commandAttr); + MethodDefinition cmdCallFunc = CommandProcessor.ProcessCommandCall(weaverTypes, writers, Log, netBehaviourSubclass, md, commandAttr, ref WeavingFailed); - MethodDefinition cmdFunc = CommandProcessor.ProcessCommandInvoke(netBehaviourSubclass, md, cmdCallFunc); + MethodDefinition cmdFunc = CommandProcessor.ProcessCommandInvoke(weaverTypes, readers, Log, netBehaviourSubclass, md, cmdCallFunc, ref WeavingFailed); if (cmdFunc != null) { commandInvocationFuncs.Add(cmdFunc); diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs.meta index 78a67bf..67c27dc 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/NetworkBehaviourProcessor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/PropertySiteProcessor.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/PropertySiteProcessor.cs.meta deleted file mode 100644 index b0fea9a..0000000 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/PropertySiteProcessor.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: d48f1ab125e9940a995603796bccc59e -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/ReaderWriterProcessor.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/ReaderWriterProcessor.cs index 9f59d07..280240c 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/ReaderWriterProcessor.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/ReaderWriterProcessor.cs @@ -1,36 +1,49 @@ // finds all readers and writers and register them -using System; using System.Linq; using Mono.CecilX; using Mono.CecilX.Cil; -using UnityEditor; -using UnityEditor.Compilation; +using Mono.CecilX.Rocks; using UnityEngine; namespace Mirror.Weaver { public static class ReaderWriterProcessor { - public static bool Process(AssemblyDefinition CurrentAssembly) + public static bool Process(AssemblyDefinition CurrentAssembly, IAssemblyResolver resolver, Logger Log, Writers writers, Readers readers, ref bool WeavingFailed) { - Readers.Init(); - Writers.Init(); - foreach (Assembly unityAsm in CompilationPipeline.GetAssemblies()) - { - if (unityAsm.name == "Mirror") - { - using (DefaultAssemblyResolver asmResolver = new DefaultAssemblyResolver()) - using (AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(unityAsm.outputPath, new ReaderParameters { ReadWrite = false, ReadSymbols = false, AssemblyResolver = asmResolver })) - { - ProcessAssemblyClasses(CurrentAssembly, assembly); - } - } - } + // find NetworkReader/Writer extensions from Mirror.dll first. + // and NetworkMessage custom writer/reader extensions. + // NOTE: do not include this result in our 'modified' return value, + // otherwise Unity crashes when running tests + ProcessMirrorAssemblyClasses(CurrentAssembly, resolver, Log, writers, readers, ref WeavingFailed); - return ProcessAssemblyClasses(CurrentAssembly, CurrentAssembly); + // find readers/writers in the assembly we are in right now. + return ProcessAssemblyClasses(CurrentAssembly, CurrentAssembly, writers, readers, ref WeavingFailed); } - static bool ProcessAssemblyClasses(AssemblyDefinition CurrentAssembly, AssemblyDefinition assembly) + static void ProcessMirrorAssemblyClasses(AssemblyDefinition CurrentAssembly, IAssemblyResolver resolver, Logger Log, Writers writers, Readers readers, ref bool WeavingFailed) + { + // find Mirror.dll in assembly's references. + // those are guaranteed to be resolvable and correct. + // after all, it references them :) + AssemblyNameReference mirrorAssemblyReference = CurrentAssembly.MainModule.FindReference(Weaver.MirrorAssemblyName); + if (mirrorAssemblyReference != null) + { + // resolve the assembly to load the AssemblyDefinition. + // we need to search all types in it. + // if we only were to resolve one known type like in WeaverTypes, + // then we wouldn't need it. + AssemblyDefinition mirrorAssembly = resolver.Resolve(mirrorAssemblyReference); + if (mirrorAssembly != null) + { + ProcessAssemblyClasses(CurrentAssembly, mirrorAssembly, writers, readers, ref WeavingFailed); + } + else Log.Error($"Failed to resolve {mirrorAssemblyReference}"); + } + else Log.Error("Failed to find Mirror AssemblyNameReference. Can't register Mirror.dll readers/writers."); + } + + static bool ProcessAssemblyClasses(AssemblyDefinition CurrentAssembly, AssemblyDefinition assembly, Writers writers, Readers readers, ref bool WeavingFailed) { bool modified = false; foreach (TypeDefinition klass in assembly.MainModule.Types) @@ -40,37 +53,37 @@ namespace Mirror.Weaver if (klass.IsAbstract && klass.IsSealed) { // if assembly has any declared writers then it is "modified" - modified |= LoadDeclaredWriters(CurrentAssembly, klass); - modified |= LoadDeclaredReaders(CurrentAssembly, klass); + modified |= LoadDeclaredWriters(CurrentAssembly, klass, writers); + modified |= LoadDeclaredReaders(CurrentAssembly, klass, readers); } } foreach (TypeDefinition klass in assembly.MainModule.Types) { // if assembly has any network message then it is modified - modified |= LoadMessageReadWriter(CurrentAssembly.MainModule, klass); + modified |= LoadMessageReadWriter(CurrentAssembly.MainModule, writers, readers, klass, ref WeavingFailed); } return modified; } - static bool LoadMessageReadWriter(ModuleDefinition module, TypeDefinition klass) + static bool LoadMessageReadWriter(ModuleDefinition module, Writers writers, Readers readers, TypeDefinition klass, ref bool WeavingFailed) { bool modified = false; if (!klass.IsAbstract && !klass.IsInterface && klass.ImplementsInterface()) { - Readers.GetReadFunc(module.ImportReference(klass)); - Writers.GetWriteFunc(module.ImportReference(klass)); + readers.GetReadFunc(module.ImportReference(klass), ref WeavingFailed); + writers.GetWriteFunc(module.ImportReference(klass), ref WeavingFailed); modified = true; } foreach (TypeDefinition td in klass.NestedTypes) { - modified |= LoadMessageReadWriter(module, td); + modified |= LoadMessageReadWriter(module, writers, readers, td, ref WeavingFailed); } return modified; } - static bool LoadDeclaredWriters(AssemblyDefinition currentAssembly, TypeDefinition klass) + static bool LoadDeclaredWriters(AssemblyDefinition currentAssembly, TypeDefinition klass, Writers writers) { // register all the writers in this class. Skip the ones with wrong signature bool modified = false; @@ -92,13 +105,13 @@ namespace Mirror.Weaver continue; TypeReference dataType = method.Parameters[1].ParameterType; - Writers.Register(dataType, currentAssembly.MainModule.ImportReference(method)); + writers.Register(dataType, currentAssembly.MainModule.ImportReference(method)); modified = true; } return modified; } - static bool LoadDeclaredReaders(AssemblyDefinition currentAssembly, TypeDefinition klass) + static bool LoadDeclaredReaders(AssemblyDefinition currentAssembly, TypeDefinition klass, Readers readers) { // register all the reader in this class. Skip the ones with wrong signature bool modified = false; @@ -119,19 +132,51 @@ namespace Mirror.Weaver if (method.HasGenericParameters) continue; - Readers.Register(method.ReturnType, currentAssembly.MainModule.ImportReference(method)); + readers.Register(method.ReturnType, currentAssembly.MainModule.ImportReference(method)); modified = true; } return modified; } - static bool IsEditorAssembly(AssemblyDefinition currentAssembly) + // helper function to add [RuntimeInitializeOnLoad] attribute to method + static void AddRuntimeInitializeOnLoadAttribute(AssemblyDefinition assembly, WeaverTypes weaverTypes, MethodDefinition method) { - // we want to add the [InitializeOnLoad] attribute if it's available - // -> usually either 'UnityEditor' or 'UnityEditor.CoreModule' - return currentAssembly.MainModule.AssemblyReferences.Any(assemblyReference => - assemblyReference.Name.StartsWith(nameof(UnityEditor)) - ); + // NOTE: previously we used reflection because according paul, + // 'weaving Mirror.dll caused unity to rebuild all dlls but in wrong + // order, which breaks rewired' + // it's not obvious why importing an attribute via reflection instead + // of cecil would break anything. let's use cecil. + + // to add a CustomAttribute, we need the attribute's constructor. + // in this case, there are two: empty, and RuntimeInitializeOnLoadType. + // we want the last one, with the type parameter. + MethodDefinition ctor = weaverTypes.runtimeInitializeOnLoadMethodAttribute.GetConstructors().Last(); + //MethodDefinition ctor = weaverTypes.runtimeInitializeOnLoadMethodAttribute.GetConstructors().First(); + // using ctor directly throws: ArgumentException: Member 'System.Void UnityEditor.InitializeOnLoadMethodAttribute::.ctor()' is declared in another module and needs to be imported + // we need to import it first. + CustomAttribute attribute = new CustomAttribute(assembly.MainModule.ImportReference(ctor)); + // add the RuntimeInitializeLoadType.BeforeSceneLoad argument to ctor + attribute.ConstructorArguments.Add(new CustomAttributeArgument(weaverTypes.Import(), RuntimeInitializeLoadType.BeforeSceneLoad)); + method.CustomAttributes.Add(attribute); + } + + // helper function to add [InitializeOnLoad] attribute to method + // (only works in Editor assemblies. check IsEditorAssembly first.) + static void AddInitializeOnLoadAttribute(AssemblyDefinition assembly, WeaverTypes weaverTypes, MethodDefinition method) + { + // NOTE: previously we used reflection because according paul, + // 'weaving Mirror.dll caused unity to rebuild all dlls but in wrong + // order, which breaks rewired' + // it's not obvious why importing an attribute via reflection instead + // of cecil would break anything. let's use cecil. + + // to add a CustomAttribute, we need the attribute's constructor. + // in this case, there's only one - and it's an empty constructor. + MethodDefinition ctor = weaverTypes.initializeOnLoadMethodAttribute.GetConstructors().First(); + // using ctor directly throws: ArgumentException: Member 'System.Void UnityEditor.InitializeOnLoadMethodAttribute::.ctor()' is declared in another module and needs to be imported + // we need to import it first. + CustomAttribute attribute = new CustomAttribute(assembly.MainModule.ImportReference(ctor)); + method.CustomAttributes.Add(attribute); } // adds Mirror.GeneratedNetworkCode.InitReadWriters() method that @@ -141,39 +186,31 @@ namespace Mirror.Weaver // in Editor and in tests too // // use ILSpy to see the result (it's in the DLL's 'Mirror' namespace) - public static void InitializeReaderAndWriters(AssemblyDefinition currentAssembly) + public static void InitializeReaderAndWriters(AssemblyDefinition currentAssembly, WeaverTypes weaverTypes, Writers writers, Readers readers, TypeDefinition GeneratedCodeClass) { - MethodDefinition rwInitializer = new MethodDefinition("InitReadWriters", MethodAttributes.Public | + MethodDefinition initReadWriters = new MethodDefinition("InitReadWriters", MethodAttributes.Public | MethodAttributes.Static, - WeaverTypes.Import(typeof(void))); + weaverTypes.Import(typeof(void))); // add [RuntimeInitializeOnLoad] in any case - System.Reflection.ConstructorInfo attributeconstructor = typeof(RuntimeInitializeOnLoadMethodAttribute).GetConstructor(new[] { typeof(RuntimeInitializeLoadType) }); - CustomAttribute customAttributeRef = new CustomAttribute(currentAssembly.MainModule.ImportReference(attributeconstructor)); - customAttributeRef.ConstructorArguments.Add(new CustomAttributeArgument(WeaverTypes.Import(), RuntimeInitializeLoadType.BeforeSceneLoad)); - rwInitializer.CustomAttributes.Add(customAttributeRef); + AddRuntimeInitializeOnLoadAttribute(currentAssembly, weaverTypes, initReadWriters); // add [InitializeOnLoad] if UnityEditor is referenced - if (IsEditorAssembly(currentAssembly)) + if (Helpers.IsEditorAssembly(currentAssembly)) { - System.Reflection.ConstructorInfo initializeOnLoadConstructor = typeof(InitializeOnLoadMethodAttribute).GetConstructor(new Type[0]); - CustomAttribute initializeCustomConstructorRef = new CustomAttribute(currentAssembly.MainModule.ImportReference(initializeOnLoadConstructor)); - rwInitializer.CustomAttributes.Add(initializeCustomConstructorRef); + AddInitializeOnLoadAttribute(currentAssembly, weaverTypes, initReadWriters); } // fill function body with reader/writer initializers - ILProcessor worker = rwInitializer.Body.GetILProcessor(); - + ILProcessor worker = initReadWriters.Body.GetILProcessor(); // for debugging: add a log to see if initialized on load //worker.Emit(OpCodes.Ldstr, $"[InitReadWriters] called!"); - //worker.Emit(OpCodes.Call, WeaverTypes.logWarningReference); - - Writers.InitializeWriters(worker); - Readers.InitializeReaders(worker); - + //worker.Emit(OpCodes.Call, Weaver.weaverTypes.logWarningReference); + writers.InitializeWriters(worker); + readers.InitializeReaders(worker); worker.Emit(OpCodes.Ret); - Weaver.GeneratedCodeClass.Methods.Add(rwInitializer); + GeneratedCodeClass.Methods.Add(initReadWriters); } } } diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/ReaderWriterProcessor.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/ReaderWriterProcessor.cs.meta index 8d8b30a..c14d6fa 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/ReaderWriterProcessor.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/ReaderWriterProcessor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/RpcProcessor.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/RpcProcessor.cs index 81f0d4b..df44f20 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/RpcProcessor.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/RpcProcessor.cs @@ -3,35 +3,33 @@ using Mono.CecilX.Cil; namespace Mirror.Weaver { - /// - /// Processes [Rpc] methods in NetworkBehaviour - /// + // Processes [Rpc] methods in NetworkBehaviour public static class RpcProcessor { - public static MethodDefinition ProcessRpcInvoke(TypeDefinition td, MethodDefinition md, MethodDefinition rpcCallFunc) + public static MethodDefinition ProcessRpcInvoke(WeaverTypes weaverTypes, Writers writers, Readers readers, Logger Log, TypeDefinition td, MethodDefinition md, MethodDefinition rpcCallFunc, ref bool WeavingFailed) { - MethodDefinition rpc = new MethodDefinition( - Weaver.InvokeRpcPrefix + md.Name, - MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig, - WeaverTypes.Import(typeof(void))); + string rpcName = Weaver.GenerateMethodName(Weaver.InvokeRpcPrefix, md); + + MethodDefinition rpc = new MethodDefinition(rpcName, MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig, + weaverTypes.Import(typeof(void))); ILProcessor worker = rpc.Body.GetILProcessor(); Instruction label = worker.Create(OpCodes.Nop); - NetworkBehaviourProcessor.WriteClientActiveCheck(worker, md.Name, label, "RPC"); + NetworkBehaviourProcessor.WriteClientActiveCheck(worker, weaverTypes, md.Name, label, "RPC"); // setup for reader worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Castclass, td); - if (!NetworkBehaviourProcessor.ReadArguments(md, worker, RemoteCallType.ClientRpc)) + if (!NetworkBehaviourProcessor.ReadArguments(md, readers, Log, worker, RemoteCallType.ClientRpc, ref WeavingFailed)) return null; // invoke actual command function worker.Emit(OpCodes.Callvirt, rpcCallFunc); worker.Emit(OpCodes.Ret); - NetworkBehaviourProcessor.AddInvokeParameters(rpc.Parameters); + NetworkBehaviourProcessor.AddInvokeParameters(weaverTypes, rpc.Parameters); td.Methods.Add(rpc); return rpc; } @@ -58,45 +56,40 @@ namespace Mirror.Weaver This way we do not need to modify the code anywhere else, and this works correctly in dependent assemblies */ - public static MethodDefinition ProcessRpcCall(TypeDefinition td, MethodDefinition md, CustomAttribute clientRpcAttr) + public static MethodDefinition ProcessRpcCall(WeaverTypes weaverTypes, Writers writers, Logger Log, TypeDefinition td, MethodDefinition md, CustomAttribute clientRpcAttr, ref bool WeavingFailed) { - MethodDefinition rpc = MethodProcessor.SubstituteMethod(td, md); + MethodDefinition rpc = MethodProcessor.SubstituteMethod(Log, td, md, ref WeavingFailed); ILProcessor worker = md.Body.GetILProcessor(); - NetworkBehaviourProcessor.WriteSetupLocals(worker); + NetworkBehaviourProcessor.WriteSetupLocals(worker, weaverTypes); - if (Weaver.GenerateLogErrors) - { - worker.Emit(OpCodes.Ldstr, "Call ClientRpc function " + md.Name); - worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference); - } + // add a log message if needed for debugging + //worker.Emit(OpCodes.Ldstr, $"Call ClientRpc function {md.Name}"); + //worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference); - NetworkBehaviourProcessor.WriteCreateWriter(worker); + NetworkBehaviourProcessor.WriteGetWriter(worker, weaverTypes); // write all the arguments that the user passed to the Rpc call - if (!NetworkBehaviourProcessor.WriteArguments(worker, md, RemoteCallType.ClientRpc)) + if (!NetworkBehaviourProcessor.WriteArguments(worker, writers, Log, md, RemoteCallType.ClientRpc, ref WeavingFailed)) return null; - string rpcName = md.Name; int channel = clientRpcAttr.GetField("channel", 0); bool includeOwner = clientRpcAttr.GetField("includeOwner", true); // invoke SendInternal and return // this worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldtoken, td); - // invokerClass - worker.Emit(OpCodes.Call, WeaverTypes.getTypeFromHandleReference); - worker.Emit(OpCodes.Ldstr, rpcName); + // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions + worker.Emit(OpCodes.Ldstr, md.FullName); // writer worker.Emit(OpCodes.Ldloc_0); worker.Emit(OpCodes.Ldc_I4, channel); // includeOwner ? 1 : 0 worker.Emit(includeOwner ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); - worker.Emit(OpCodes.Callvirt, WeaverTypes.sendRpcInternal); + worker.Emit(OpCodes.Callvirt, weaverTypes.sendRpcInternal); - NetworkBehaviourProcessor.WriteRecycleWriter(worker); + NetworkBehaviourProcessor.WriteReturnWriter(worker, weaverTypes); worker.Emit(OpCodes.Ret); diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/RpcProcessor.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/RpcProcessor.cs.meta index 5f94aa7..22375ba 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/RpcProcessor.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/RpcProcessor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/ServerClientAttributeProcessor.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/ServerClientAttributeProcessor.cs index c653f93..50df598 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/ServerClientAttributeProcessor.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/ServerClientAttributeProcessor.cs @@ -6,22 +6,22 @@ namespace Mirror.Weaver { static class ServerClientAttributeProcessor { - public static bool Process(TypeDefinition td) + public static bool Process(WeaverTypes weaverTypes, Logger Log, TypeDefinition td, ref bool WeavingFailed) { bool modified = false; foreach (MethodDefinition md in td.Methods) { - modified |= ProcessSiteMethod(md); + modified |= ProcessSiteMethod(weaverTypes, Log, md, ref WeavingFailed); } foreach (TypeDefinition nested in td.NestedTypes) { - modified |= Process(nested); + modified |= Process(weaverTypes, Log, nested, ref WeavingFailed); } return modified; } - static bool ProcessSiteMethod(MethodDefinition md) + static bool ProcessSiteMethod(WeaverTypes weaverTypes, Logger Log, MethodDefinition md, ref bool WeavingFailed) { if (md.Name == ".cctor" || md.Name == NetworkBehaviourProcessor.ProcessedFunctionName || @@ -32,14 +32,15 @@ namespace Mirror.Weaver { if (HasServerClientAttribute(md)) { - Weaver.Error("Server or Client Attributes can't be added to abstract method. Server and Client Attributes are not inherited so they need to be applied to the override methods instead.", md); + Log.Error("Server or Client Attributes can't be added to abstract method. Server and Client Attributes are not inherited so they need to be applied to the override methods instead.", md); + WeavingFailed = true; } return false; } if (md.Body != null && md.Body.Instructions != null) { - return ProcessMethodAttributes(md); + return ProcessMethodAttributes(weaverTypes, md); } return false; } @@ -62,50 +63,50 @@ namespace Mirror.Weaver return false; } - public static bool ProcessMethodAttributes(MethodDefinition md) + public static bool ProcessMethodAttributes(WeaverTypes weaverTypes, MethodDefinition md) { if (md.HasCustomAttribute()) - InjectServerGuard(md, true); + InjectServerGuard(weaverTypes, md, true); else if (md.HasCustomAttribute()) - InjectServerGuard(md, false); + InjectServerGuard(weaverTypes, md, false); else if (md.HasCustomAttribute()) - InjectClientGuard(md, true); + InjectClientGuard(weaverTypes, md, true); else if (md.HasCustomAttribute()) - InjectClientGuard(md, false); + InjectClientGuard(weaverTypes, md, false); else return false; return true; } - static void InjectServerGuard(MethodDefinition md, bool logWarning) + static void InjectServerGuard(WeaverTypes weaverTypes, MethodDefinition md, bool logWarning) { ILProcessor worker = md.Body.GetILProcessor(); Instruction top = md.Body.Instructions[0]; - worker.InsertBefore(top, worker.Create(OpCodes.Call, WeaverTypes.NetworkServerGetActive)); + worker.InsertBefore(top, worker.Create(OpCodes.Call, weaverTypes.NetworkServerGetActive)); worker.InsertBefore(top, worker.Create(OpCodes.Brtrue, top)); if (logWarning) { worker.InsertBefore(top, worker.Create(OpCodes.Ldstr, $"[Server] function '{md.FullName}' called when server was not active")); - worker.InsertBefore(top, worker.Create(OpCodes.Call, WeaverTypes.logWarningReference)); + worker.InsertBefore(top, worker.Create(OpCodes.Call, weaverTypes.logWarningReference)); } InjectGuardParameters(md, worker, top); InjectGuardReturnValue(md, worker, top); worker.InsertBefore(top, worker.Create(OpCodes.Ret)); } - static void InjectClientGuard(MethodDefinition md, bool logWarning) + static void InjectClientGuard(WeaverTypes weaverTypes, MethodDefinition md, bool logWarning) { ILProcessor worker = md.Body.GetILProcessor(); Instruction top = md.Body.Instructions[0]; - worker.InsertBefore(top, worker.Create(OpCodes.Call, WeaverTypes.NetworkClientGetActive)); + worker.InsertBefore(top, worker.Create(OpCodes.Call, weaverTypes.NetworkClientGetActive)); worker.InsertBefore(top, worker.Create(OpCodes.Brtrue, top)); if (logWarning) { worker.InsertBefore(top, worker.Create(OpCodes.Ldstr, $"[Client] function '{md.FullName}' called when client was not active")); - worker.InsertBefore(top, worker.Create(OpCodes.Call, WeaverTypes.logWarningReference)); + worker.InsertBefore(top, worker.Create(OpCodes.Call, weaverTypes.logWarningReference)); } InjectGuardParameters(md, worker, top); diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/ServerClientAttributeProcessor.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/ServerClientAttributeProcessor.cs.meta index ff09ae9..5a5451d 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/ServerClientAttributeProcessor.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/ServerClientAttributeProcessor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncObjectInitializer.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncObjectInitializer.cs index 3bb4b20..a174403 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncObjectInitializer.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncObjectInitializer.cs @@ -5,10 +5,15 @@ namespace Mirror.Weaver { public static class SyncObjectInitializer { - public static void GenerateSyncObjectInitializer(ILProcessor worker, FieldDefinition fd) + // generates code like: + // this.InitSyncObject(m_sizes); + public static void GenerateSyncObjectInitializer(ILProcessor worker, WeaverTypes weaverTypes, FieldDefinition fd) { // register syncobject in network behaviour - GenerateSyncObjectRegistration(worker, fd); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, fd); + worker.Emit(OpCodes.Call, weaverTypes.InitSyncObjectReference); } public static bool ImplementsSyncObject(TypeReference typeRef) @@ -21,7 +26,7 @@ namespace Mirror.Weaver return false; } - return typeRef.Resolve().ImplementsInterface(); + return typeRef.Resolve().IsDerivedFrom(); } catch { @@ -30,18 +35,5 @@ namespace Mirror.Weaver return false; } - - /* - // generates code like: - this.InitSyncObject(m_sizes); - */ - static void GenerateSyncObjectRegistration(ILProcessor worker, FieldDefinition fd) - { - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, fd); - - worker.Emit(OpCodes.Call, WeaverTypes.InitSyncObjectReference); - } } } diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncObjectInitializer.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncObjectInitializer.cs.meta index 1a0c3cf..22f976e 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncObjectInitializer.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncObjectInitializer.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncObjectProcessor.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncObjectProcessor.cs index c5b3a11..2cec8a4 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncObjectProcessor.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncObjectProcessor.cs @@ -5,42 +5,69 @@ namespace Mirror.Weaver { public static class SyncObjectProcessor { - /// - /// Finds SyncObjects fields in a type - /// Type should be a NetworkBehaviour - /// - /// - /// - public static List FindSyncObjectsFields(TypeDefinition td) + // ulong = 64 bytes + const int SyncObjectsLimit = 64; + + // Finds SyncObjects fields in a type + // Type should be a NetworkBehaviour + public static List FindSyncObjectsFields(Writers writers, Readers readers, Logger Log, TypeDefinition td, ref bool WeavingFailed) { List syncObjects = new List(); foreach (FieldDefinition fd in td.Fields) { - if (fd.FieldType.Resolve().ImplementsInterface()) + if (fd.FieldType.IsGenericParameter) + { + // can't call .Resolve on generic ones + continue; + } + + if (fd.FieldType.Resolve().IsDerivedFrom()) { if (fd.IsStatic) { - Weaver.Error($"{fd.Name} cannot be static", fd); + Log.Error($"{fd.Name} cannot be static", fd); + WeavingFailed = true; continue; } - GenerateReadersAndWriters(fd.FieldType); + // SyncObjects always needs to be readonly to guarantee. + // Weaver calls InitSyncObject on them for dirty bits etc. + // Reassigning at runtime would cause undefined behaviour. + // (C# 'readonly' is called 'initonly' in IL code.) + // + // NOTE: instead of forcing readonly, we could also scan all + // instructions for SyncObject assignments. this would + // make unit tests very difficult though. + if (!fd.IsInitOnly) + { + // just a warning for now. + // many people might still use non-readonly SyncObjects. + Log.Warning($"{fd.Name} should have a 'readonly' keyword in front of the variable because {typeof(SyncObject)}s always need to be initialized by the Weaver.", fd); + + // only log, but keep weaving. no need to break projects. + //WeavingFailed = true; + } + + GenerateReadersAndWriters(writers, readers, fd.FieldType, ref WeavingFailed); syncObjects.Add(fd); } } + // SyncObjects dirty mask is 64 bit. can't sync more than 64. + if (syncObjects.Count > 64) + { + Log.Error($"{td.Name} has > {SyncObjectsLimit} SyncObjects (SyncLists etc). Consider refactoring your class into multiple components", td); + WeavingFailed = true; + } + return syncObjects; } - /// - /// Generates serialization methods for synclists - /// - /// The synclist class - /// the base SyncObject td inherits from - static void GenerateReadersAndWriters(TypeReference tr) + // Generates serialization methods for synclists + static void GenerateReadersAndWriters(Writers writers, Readers readers, TypeReference tr, ref bool WeavingFailed) { if (tr is GenericInstanceType genericInstance) { @@ -48,15 +75,15 @@ namespace Mirror.Weaver { if (!argument.IsGenericParameter) { - Readers.GetReadFunc(argument); - Writers.GetWriteFunc(argument); + readers.GetReadFunc(argument, ref WeavingFailed); + writers.GetWriteFunc(argument, ref WeavingFailed); } } } if (tr != null) { - GenerateReadersAndWriters(tr.Resolve().BaseType); + GenerateReadersAndWriters(writers, readers, tr.Resolve().BaseType, ref WeavingFailed); } } } diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncObjectProcessor.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncObjectProcessor.cs.meta index 82696b6..0efe434 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncObjectProcessor.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncObjectProcessor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/PropertySiteProcessor.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncVarAttributeAccessReplacer.cs similarity index 53% rename from UnityProject/Assets/Mirror/Editor/Weaver/Processors/PropertySiteProcessor.cs rename to UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncVarAttributeAccessReplacer.cs index 20397e9..0a6f376 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/PropertySiteProcessor.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncVarAttributeAccessReplacer.cs @@ -1,134 +1,148 @@ +// [SyncVar] int health; +// is replaced with: +// public int Networkhealth { get; set; } properties. +// this class processes all access to 'health' and replaces it with 'Networkhealth' using System; using Mono.CecilX; using Mono.CecilX.Cil; namespace Mirror.Weaver { - public static class PropertySiteProcessor + public static class SyncVarAttributeAccessReplacer { - public static void Process(ModuleDefinition moduleDef) + // process the module + public static void Process(ModuleDefinition moduleDef, SyncVarAccessLists syncVarAccessLists) { DateTime startTime = DateTime.Now; - //Search through the types + // process all classes in this module foreach (TypeDefinition td in moduleDef.Types) { if (td.IsClass) { - ProcessSiteClass(td); + ProcessClass(syncVarAccessLists, td); } } - Console.WriteLine(" ProcessSitesModule " + moduleDef.Name + " elapsed time:" + (DateTime.Now - startTime)); + Console.WriteLine($" ProcessSitesModule {moduleDef.Name} elapsed time:{(DateTime.Now - startTime)}"); } - static void ProcessSiteClass(TypeDefinition td) + static void ProcessClass(SyncVarAccessLists syncVarAccessLists, TypeDefinition td) { - //Console.WriteLine(" ProcessSiteClass " + td); + //Console.WriteLine($" ProcessClass {td}"); + + // process all methods in this class foreach (MethodDefinition md in td.Methods) { - ProcessSiteMethod(md); + ProcessMethod(syncVarAccessLists, md); } + // processes all nested classes in this class recursively foreach (TypeDefinition nested in td.NestedTypes) { - ProcessSiteClass(nested); + ProcessClass(syncVarAccessLists, nested); } } - static void ProcessSiteMethod(MethodDefinition md) + static void ProcessMethod(SyncVarAccessLists syncVarAccessLists, MethodDefinition md) { // process all references to replaced members with properties - //Weaver.DLog(td, " ProcessSiteMethod " + md); + //Log.Warning($" ProcessSiteMethod {md}"); + // skip static constructor, "MirrorProcessed", "InvokeUserCode_" if (md.Name == ".cctor" || md.Name == NetworkBehaviourProcessor.ProcessedFunctionName || md.Name.StartsWith(Weaver.InvokeRpcPrefix)) return; + // skip abstract if (md.IsAbstract) { return; } + // go through all instructions of this method if (md.Body != null && md.Body.Instructions != null) { - for (int iCount = 0; iCount < md.Body.Instructions.Count;) + for (int i = 0; i < md.Body.Instructions.Count;) { - Instruction instr = md.Body.Instructions[iCount]; - iCount += ProcessInstruction(md, instr, iCount); + Instruction instr = md.Body.Instructions[i]; + i += ProcessInstruction(syncVarAccessLists, md, instr, i); } } } - // replaces syncvar write access with the NetworkXYZ.get property calls - static void ProcessInstructionSetterField(MethodDefinition md, Instruction i, FieldDefinition opField) + static int ProcessInstruction(SyncVarAccessLists syncVarAccessLists, MethodDefinition md, Instruction instr, int iCount) + { + // stfld (sets value of a field)? + if (instr.OpCode == OpCodes.Stfld && instr.Operand is FieldDefinition opFieldst) + { + ProcessSetInstruction(syncVarAccessLists, md, instr, opFieldst); + } + + // ldfld (load value of a field)? + if (instr.OpCode == OpCodes.Ldfld && instr.Operand is FieldDefinition opFieldld) + { + // this instruction gets the value of a field. cache the field reference. + ProcessGetInstruction(syncVarAccessLists, md, instr, opFieldld); + } + + // ldflda (load field address aka reference) + if (instr.OpCode == OpCodes.Ldflda && instr.Operand is FieldDefinition opFieldlda) + { + // watch out for initobj instruction + // see https://github.com/vis2k/Mirror/issues/696 + return ProcessLoadAddressInstruction(syncVarAccessLists, md, instr, opFieldlda, iCount); + } + + // we processed one instruction (instr) + return 1; + } + + // replaces syncvar write access with the NetworkXYZ.set property calls + static void ProcessSetInstruction(SyncVarAccessLists syncVarAccessLists, MethodDefinition md, Instruction i, FieldDefinition opField) { // don't replace property call sites in constructors if (md.Name == ".ctor") return; // does it set a field that we replaced? - if (Weaver.WeaveLists.replacementSetterProperties.TryGetValue(opField, out MethodDefinition replacement)) + if (syncVarAccessLists.replacementSetterProperties.TryGetValue(opField, out MethodDefinition replacement)) { //replace with property - //DLog(td, " replacing " + md.Name + ":" + i); + //Log.Warning($" replacing {md.Name}:{i}", opField); i.OpCode = OpCodes.Call; i.Operand = replacement; - //DLog(td, " replaced " + md.Name + ":" + i); + //Log.Warning($" replaced {md.Name}:{i}", opField); } } // replaces syncvar read access with the NetworkXYZ.get property calls - static void ProcessInstructionGetterField(MethodDefinition md, Instruction i, FieldDefinition opField) + static void ProcessGetInstruction(SyncVarAccessLists syncVarAccessLists, MethodDefinition md, Instruction i, FieldDefinition opField) { // don't replace property call sites in constructors if (md.Name == ".ctor") return; // does it set a field that we replaced? - if (Weaver.WeaveLists.replacementGetterProperties.TryGetValue(opField, out MethodDefinition replacement)) + if (syncVarAccessLists.replacementGetterProperties.TryGetValue(opField, out MethodDefinition replacement)) { //replace with property - //DLog(td, " replacing " + md.Name + ":" + i); + //Log.Warning($" replacing {md.Name}:{i}"); i.OpCode = OpCodes.Call; i.Operand = replacement; - //DLog(td, " replaced " + md.Name + ":" + i); + //Log.Warning($" replaced {md.Name}:{i}"); } } - static int ProcessInstruction(MethodDefinition md, Instruction instr, int iCount) - { - if (instr.OpCode == OpCodes.Stfld && instr.Operand is FieldDefinition opFieldst) - { - // this instruction sets the value of a field. cache the field reference. - ProcessInstructionSetterField(md, instr, opFieldst); - } - - if (instr.OpCode == OpCodes.Ldfld && instr.Operand is FieldDefinition opFieldld) - { - // this instruction gets the value of a field. cache the field reference. - ProcessInstructionGetterField(md, instr, opFieldld); - } - - if (instr.OpCode == OpCodes.Ldflda && instr.Operand is FieldDefinition opFieldlda) - { - // loading a field by reference, watch out for initobj instruction - // see https://github.com/vis2k/Mirror/issues/696 - return ProcessInstructionLoadAddress(md, instr, opFieldlda, iCount); - } - - return 1; - } - - static int ProcessInstructionLoadAddress(MethodDefinition md, Instruction instr, FieldDefinition opField, int iCount) + static int ProcessLoadAddressInstruction(SyncVarAccessLists syncVarAccessLists, MethodDefinition md, Instruction instr, FieldDefinition opField, int iCount) { // don't replace property call sites in constructors if (md.Name == ".ctor") return 1; // does it set a field that we replaced? - if (Weaver.WeaveLists.replacementSetterProperties.TryGetValue(opField, out MethodDefinition replacement)) + if (syncVarAccessLists.replacementSetterProperties.TryGetValue(opField, out MethodDefinition replacement)) { // we have a replacement for this property // is the next instruction a initobj? diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Core/IgnoranceServer.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncVarAttributeAccessReplacer.cs.meta similarity index 83% rename from UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Core/IgnoranceServer.cs.meta rename to UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncVarAttributeAccessReplacer.cs.meta index 8b3212b..e8c2500 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Core/IgnoranceServer.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncVarAttributeAccessReplacer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 1069f42b88a4adb4ab1990cec4949343 +guid: d48f1ab125e9940a995603796bccc59e MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncVarAttributeProcessor.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncVarAttributeProcessor.cs new file mode 100644 index 0000000..a5f95cd --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncVarAttributeProcessor.cs @@ -0,0 +1,483 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Mono.CecilX; +using Mono.CecilX.Cil; +using Mono.CecilX.Rocks; + +namespace Mirror.Weaver +{ + // Processes [SyncVar] attribute fields in NetworkBehaviour + // not static, because ILPostProcessor is multithreaded + public class SyncVarAttributeProcessor + { + // ulong = 64 bytes + const int SyncVarLimit = 64; + + AssemblyDefinition assembly; + WeaverTypes weaverTypes; + SyncVarAccessLists syncVarAccessLists; + Logger Log; + + string HookParameterMessage(string hookName, TypeReference ValueType) => + $"void {hookName}({ValueType} oldValue, {ValueType} newValue)"; + + public SyncVarAttributeProcessor(AssemblyDefinition assembly, WeaverTypes weaverTypes, SyncVarAccessLists syncVarAccessLists, Logger Log) + { + this.assembly = assembly; + this.weaverTypes = weaverTypes; + this.syncVarAccessLists = syncVarAccessLists; + this.Log = Log; + } + + // Get hook method if any + public MethodDefinition GetHookMethod(TypeDefinition td, FieldDefinition syncVar, ref bool WeavingFailed) + { + CustomAttribute syncVarAttr = syncVar.GetCustomAttribute(); + + if (syncVarAttr == null) + return null; + + string hookFunctionName = syncVarAttr.GetField("hook", null); + + if (hookFunctionName == null) + return null; + + return FindHookMethod(td, syncVar, hookFunctionName, ref WeavingFailed); + } + + // push hook from GetHookMethod() onto the stack as a new Action. + // allows for reuse without handling static/virtual cases every time. + public void GenerateNewActionFromHookMethod(FieldDefinition syncVar, ILProcessor worker, MethodDefinition hookMethod) + { + // IL_000a: ldarg.0 + // IL_000b: ldftn instance void Mirror.Examples.Tanks.Tank::ExampleHook(int32, int32) + // IL_0011: newobj instance void class [netstandard]System.Action`2::.ctor(object, native int) + + // we support static hook sand instance hooks. + if (hookMethod.IsStatic) + { + // for static hooks, we need to push 'null' first. + // we can't just push nothing. + // stack would get out of balance because we already pushed + // other stuff above. + worker.Emit(OpCodes.Ldnull); + } + else + { + // for instance hooks, we need to push 'this.' first. + worker.Emit(OpCodes.Ldarg_0); + } + + MethodReference hookMethodReference; + // if the network behaviour class is generic, we need to make the method reference generic for correct IL + if (hookMethod.DeclaringType.HasGenericParameters) + { + hookMethodReference = hookMethod.MakeHostInstanceGeneric(hookMethod.Module, hookMethod.DeclaringType.MakeGenericInstanceType(hookMethod.DeclaringType.GenericParameters.ToArray())); + } + else + { + hookMethodReference = hookMethod; + } + + // we support regular and virtual hook functions. + if (hookMethod.IsVirtual) + { + // for virtual / overwritten hooks, we need different IL. + // this is from simply testing Action = VirtualHook; in C#. + worker.Emit(OpCodes.Dup); + worker.Emit(OpCodes.Ldvirtftn, hookMethodReference); + } + else + { + worker.Emit(OpCodes.Ldftn, hookMethodReference); + } + + // call 'new Action()' constructor to convert the function to an action + // we need to make an instance of the generic Action. + // + // TODO this allocates a new 'Action' for every SyncVar hook call. + // we should allocate it once and store it somewhere in the future. + // hooks are only called on the client though, so it's not too bad for now. + TypeReference actionRef = assembly.MainModule.ImportReference(typeof(Action<,>)); + GenericInstanceType genericInstance = actionRef.MakeGenericInstanceType(syncVar.FieldType, syncVar.FieldType); + worker.Emit(OpCodes.Newobj, weaverTypes.ActionT_T.MakeHostInstanceGeneric(assembly.MainModule, genericInstance)); + } + + MethodDefinition FindHookMethod(TypeDefinition td, FieldDefinition syncVar, string hookFunctionName, ref bool WeavingFailed) + { + List methods = td.GetMethods(hookFunctionName); + + List methodsWith2Param = new List(methods.Where(m => m.Parameters.Count == 2)); + + if (methodsWith2Param.Count == 0) + { + Log.Error($"Could not find hook for '{syncVar.Name}', hook name '{hookFunctionName}'. " + + $"Method signature should be {HookParameterMessage(hookFunctionName, syncVar.FieldType)}", + syncVar); + WeavingFailed = true; + + return null; + } + + foreach (MethodDefinition method in methodsWith2Param) + { + if (MatchesParameters(syncVar, method)) + { + return method; + } + } + + Log.Error($"Wrong type for Parameter in hook for '{syncVar.Name}', hook name '{hookFunctionName}'. " + + $"Method signature should be {HookParameterMessage(hookFunctionName, syncVar.FieldType)}", + syncVar); + WeavingFailed = true; + + return null; + } + + bool MatchesParameters(FieldDefinition syncVar, MethodDefinition method) + { + // matches void onValueChange(T oldValue, T newValue) + return method.Parameters[0].ParameterType.FullName == syncVar.FieldType.FullName && + method.Parameters[1].ParameterType.FullName == syncVar.FieldType.FullName; + } + + public MethodDefinition GenerateSyncVarGetter(FieldDefinition fd, string originalName, FieldDefinition netFieldId) + { + //Create the get method + MethodDefinition get = new MethodDefinition( + $"get_Network{originalName}", MethodAttributes.Public | + MethodAttributes.SpecialName | + MethodAttributes.HideBySig, + fd.FieldType); + + ILProcessor worker = get.Body.GetILProcessor(); + + FieldReference fr; + if (fd.DeclaringType.HasGenericParameters) + { + fr = fd.MakeHostInstanceGeneric(); + } + else + { + fr = fd; + } + + FieldReference netIdFieldReference = null; + if (netFieldId != null) + { + if (netFieldId.DeclaringType.HasGenericParameters) + { + netIdFieldReference = netFieldId.MakeHostInstanceGeneric(); + } + else + { + netIdFieldReference = netFieldId; + } + } + + // [SyncVar] GameObject? + if (fd.FieldType.Is()) + { + // return this.GetSyncVarGameObject(ref field, uint netId); + // this. + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, netIdFieldReference); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldflda, fr); + worker.Emit(OpCodes.Call, weaverTypes.getSyncVarGameObjectReference); + worker.Emit(OpCodes.Ret); + } + // [SyncVar] NetworkIdentity? + else if (fd.FieldType.Is()) + { + // return this.GetSyncVarNetworkIdentity(ref field, uint netId); + // this. + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, netIdFieldReference); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldflda, fr); + worker.Emit(OpCodes.Call, weaverTypes.getSyncVarNetworkIdentityReference); + worker.Emit(OpCodes.Ret); + } + else if (fd.FieldType.IsDerivedFrom()) + { + // return this.GetSyncVarNetworkBehaviour(ref field, uint netId); + // this. + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, netIdFieldReference); + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldflda, fr); + MethodReference getFunc = weaverTypes.getSyncVarNetworkBehaviourReference.MakeGeneric(assembly.MainModule, fd.FieldType); + worker.Emit(OpCodes.Call, getFunc); + worker.Emit(OpCodes.Ret); + } + // [SyncVar] int, string, etc. + else + { + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldfld, fr); + worker.Emit(OpCodes.Ret); + } + + get.Body.Variables.Add(new VariableDefinition(fd.FieldType)); + get.Body.InitLocals = true; + get.SemanticsAttributes = MethodSemanticsAttributes.Getter; + + return get; + } + + // for [SyncVar] health, weaver generates + // + // NetworkHealth + // { + // get => health; + // set => GeneratedSyncVarSetter(...) + // } + // + // the setter used to be manually IL generated, but we moved it to C# :) + public MethodDefinition GenerateSyncVarSetter(TypeDefinition td, FieldDefinition fd, string originalName, long dirtyBit, FieldDefinition netFieldId, ref bool WeavingFailed) + { + //Create the set method + MethodDefinition set = new MethodDefinition($"set_Network{originalName}", MethodAttributes.Public | + MethodAttributes.SpecialName | + MethodAttributes.HideBySig, + weaverTypes.Import(typeof(void))); + + ILProcessor worker = set.Body.GetILProcessor(); + FieldReference fr; + if (fd.DeclaringType.HasGenericParameters) + { + fr = fd.MakeHostInstanceGeneric(); + } + else + { + fr = fd; + } + + FieldReference netIdFieldReference = null; + if (netFieldId != null) + { + if (netFieldId.DeclaringType.HasGenericParameters) + { + netIdFieldReference = netFieldId.MakeHostInstanceGeneric(); + } + else + { + netIdFieldReference = netFieldId; + } + } + + // if (!SyncVarEqual(value, ref playerData)) + Instruction endOfMethod = worker.Create(OpCodes.Nop); + + // NOTE: SyncVar...Equal functions are static. + // don't Emit Ldarg_0 aka 'this'. + + // call WeaverSyncVarSetter(T value, ref T field, ulong dirtyBit, Action OnChanged = null) + // IL_0000: ldarg.0 + // IL_0001: ldarg.1 + // IL_0002: ldarg.0 + // IL_0003: ldflda int32 Mirror.Examples.Tanks.Tank::health + // IL_0008: ldc.i4.1 + // IL_0009: conv.i8 + // IL_000a: ldnull + // IL_000b: call instance void [Mirror]Mirror.NetworkBehaviour::GeneratedSyncVarSetter(!!0, !!0&, uint64, class [netstandard]System.Action`2) + // IL_0010: ret + + // 'this.' for the call + worker.Emit(OpCodes.Ldarg_0); + + // first push 'value' + worker.Emit(OpCodes.Ldarg_1); + + // push 'ref T this.field' + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldflda, fr); + + // push the dirty bit for this SyncVar + worker.Emit(OpCodes.Ldc_I8, dirtyBit); + + // hook? then push 'new Action(Hook)' onto stack + MethodDefinition hookMethod = GetHookMethod(td, fd, ref WeavingFailed); + if (hookMethod != null) + { + GenerateNewActionFromHookMethod(fd, worker, hookMethod); + } + // otherwise push 'null' as hook + else + { + worker.Emit(OpCodes.Ldnull); + } + + // call GeneratedSyncVarSetter. + // special cases for GameObject/NetworkIdentity/NetworkBehaviour + // passing netId too for persistence. + if (fd.FieldType.Is()) + { + // GameObject setter needs one more parameter: netId field ref + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldflda, netIdFieldReference); + worker.Emit(OpCodes.Call, weaverTypes.generatedSyncVarSetter_GameObject); + } + else if (fd.FieldType.Is()) + { + // NetworkIdentity setter needs one more parameter: netId field ref + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldflda, netIdFieldReference); + worker.Emit(OpCodes.Call, weaverTypes.generatedSyncVarSetter_NetworkIdentity); + } + // TODO this only uses the persistent netId for types DERIVED FROM NB. + // not if the type is just 'NetworkBehaviour'. + // this is what original implementation did too. fix it after. + else if (fd.FieldType.IsDerivedFrom()) + { + // NetworkIdentity setter needs one more parameter: netId field ref + // (actually its a NetworkBehaviourSyncVar type) + worker.Emit(OpCodes.Ldarg_0); + worker.Emit(OpCodes.Ldflda, netIdFieldReference); + // make generic version of GeneratedSyncVarSetter_NetworkBehaviour + MethodReference getFunc = weaverTypes.generatedSyncVarSetter_NetworkBehaviour_T.MakeGeneric(assembly.MainModule, fd.FieldType); + worker.Emit(OpCodes.Call, getFunc); + } + else + { + // make generic version of GeneratedSyncVarSetter + MethodReference generic = weaverTypes.generatedSyncVarSetter.MakeGeneric(assembly.MainModule, fd.FieldType); + worker.Emit(OpCodes.Call, generic); + } + + worker.Append(endOfMethod); + + worker.Emit(OpCodes.Ret); + + set.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.In, fd.FieldType)); + set.SemanticsAttributes = MethodSemanticsAttributes.Setter; + + return set; + } + + public void ProcessSyncVar(TypeDefinition td, FieldDefinition fd, Dictionary syncVarNetIds, long dirtyBit, ref bool WeavingFailed) + { + string originalName = fd.Name; + + // GameObject/NetworkIdentity SyncVars have a new field for netId + FieldDefinition netIdField = null; + // NetworkBehaviour has different field type than other NetworkIdentityFields + if (fd.FieldType.IsDerivedFrom()) + { + netIdField = new FieldDefinition($"___{fd.Name}NetId", + FieldAttributes.Family, // needs to be protected for generic classes, otherwise access isn't allowed + weaverTypes.Import()); + netIdField.DeclaringType = td; + + syncVarNetIds[fd] = netIdField; + } + else if (fd.FieldType.IsNetworkIdentityField()) + { + netIdField = new FieldDefinition($"___{fd.Name}NetId", + FieldAttributes.Family, // needs to be protected for generic classes, otherwise access isn't allowed + weaverTypes.Import()); + netIdField.DeclaringType = td; + + syncVarNetIds[fd] = netIdField; + } + + MethodDefinition get = GenerateSyncVarGetter(fd, originalName, netIdField); + MethodDefinition set = GenerateSyncVarSetter(td, fd, originalName, dirtyBit, netIdField, ref WeavingFailed); + + //NOTE: is property even needed? Could just use a setter function? + //create the property + PropertyDefinition propertyDefinition = new PropertyDefinition($"Network{originalName}", PropertyAttributes.None, fd.FieldType) + { + GetMethod = get, + SetMethod = set + }; + + //add the methods and property to the type. + td.Methods.Add(get); + td.Methods.Add(set); + td.Properties.Add(propertyDefinition); + syncVarAccessLists.replacementSetterProperties[fd] = set; + + // replace getter field if GameObject/NetworkIdentity so it uses + // netId instead + // -> only for GameObjects, otherwise an int syncvar's getter would + // end up in recursion. + if (fd.FieldType.IsNetworkIdentityField()) + { + syncVarAccessLists.replacementGetterProperties[fd] = get; + } + } + + public (List syncVars, Dictionary syncVarNetIds) ProcessSyncVars(TypeDefinition td, ref bool WeavingFailed) + { + List syncVars = new List(); + Dictionary syncVarNetIds = new Dictionary(); + + // the mapping of dirtybits to sync-vars is implicit in the order of the fields here. this order is recorded in m_replacementProperties. + // start assigning syncvars at the place the base class stopped, if any + int dirtyBitCounter = syncVarAccessLists.GetSyncVarStart(td.BaseType.FullName); + + // find syncvars + foreach (FieldDefinition fd in td.Fields) + { + if (fd.HasCustomAttribute()) + { + if ((fd.Attributes & FieldAttributes.Static) != 0) + { + Log.Error($"{fd.Name} cannot be static", fd); + WeavingFailed = true; + continue; + } + + if (fd.FieldType.IsGenericParameter) + { + Log.Error($"{fd.Name} has generic type. Generic SyncVars are not supported", fd); + WeavingFailed = true; + continue; + } + + if (fd.FieldType.IsArray) + { + Log.Error($"{fd.Name} has invalid type. Use SyncLists instead of arrays", fd); + WeavingFailed = true; + continue; + } + + if (SyncObjectInitializer.ImplementsSyncObject(fd.FieldType)) + { + Log.Warning($"{fd.Name} has [SyncVar] attribute. SyncLists should not be marked with SyncVar", fd); + } + else + { + syncVars.Add(fd); + + ProcessSyncVar(td, fd, syncVarNetIds, 1L << dirtyBitCounter, ref WeavingFailed); + dirtyBitCounter += 1; + + if (dirtyBitCounter > SyncVarLimit) + { + Log.Error($"{td.Name} has > {SyncVarLimit} SyncVars. Consider refactoring your class into multiple components", td); + WeavingFailed = true; + continue; + } + } + } + } + + // add all the new SyncVar __netId fields + foreach (FieldDefinition fd in syncVarNetIds.Values) + { + td.Fields.Add(fd); + } + syncVarAccessLists.SetNumSyncVars(td.FullName, syncVars.Count); + + return (syncVars, syncVarNetIds); + } + } +} diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Dependencies/ENet.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncVarAttributeProcessor.cs.meta similarity index 83% rename from UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Dependencies/ENet.cs.meta rename to UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncVarAttributeProcessor.cs.meta index d58413a..982f768 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Dependencies/ENet.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncVarAttributeProcessor.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 12a7875e95f5ebb4a9b58390441dd933 +guid: f52c39bddd95d42b88f9cd554dfd9198 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncVarProcessor.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncVarProcessor.cs deleted file mode 100644 index 4c179c7..0000000 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncVarProcessor.cs +++ /dev/null @@ -1,472 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Mono.CecilX; -using Mono.CecilX.Cil; - -namespace Mirror.Weaver -{ - /// - /// Processes [SyncVar] in NetworkBehaviour - /// - public static class SyncVarProcessor - { - // ulong = 64 bytes - const int SyncVarLimit = 64; - - - static string HookParameterMessage(string hookName, TypeReference ValueType) - => string.Format("void {0}({1} oldValue, {1} newValue)", hookName, ValueType); - - // Get hook method if any - public static MethodDefinition GetHookMethod(TypeDefinition td, FieldDefinition syncVar) - { - CustomAttribute syncVarAttr = syncVar.GetCustomAttribute(); - - if (syncVarAttr == null) - return null; - - string hookFunctionName = syncVarAttr.GetField("hook", null); - - if (hookFunctionName == null) - return null; - - return FindHookMethod(td, syncVar, hookFunctionName); - } - - static MethodDefinition FindHookMethod(TypeDefinition td, FieldDefinition syncVar, string hookFunctionName) - { - List methods = td.GetMethods(hookFunctionName); - - List methodsWith2Param = new List(methods.Where(m => m.Parameters.Count == 2)); - - if (methodsWith2Param.Count == 0) - { - Weaver.Error($"Could not find hook for '{syncVar.Name}', hook name '{hookFunctionName}'. " + - $"Method signature should be {HookParameterMessage(hookFunctionName, syncVar.FieldType)}", - syncVar); - - return null; - } - - foreach (MethodDefinition method in methodsWith2Param) - { - if (MatchesParameters(syncVar, method)) - { - return method; - } - } - - Weaver.Error($"Wrong type for Parameter in hook for '{syncVar.Name}', hook name '{hookFunctionName}'. " + - $"Method signature should be {HookParameterMessage(hookFunctionName, syncVar.FieldType)}", - syncVar); - - return null; - } - - static bool MatchesParameters(FieldDefinition syncVar, MethodDefinition method) - { - // matches void onValueChange(T oldValue, T newValue) - return method.Parameters[0].ParameterType.FullName == syncVar.FieldType.FullName && - method.Parameters[1].ParameterType.FullName == syncVar.FieldType.FullName; - } - - public static MethodDefinition GenerateSyncVarGetter(FieldDefinition fd, string originalName, FieldDefinition netFieldId) - { - //Create the get method - MethodDefinition get = new MethodDefinition( - "get_Network" + originalName, MethodAttributes.Public | - MethodAttributes.SpecialName | - MethodAttributes.HideBySig, - fd.FieldType); - - ILProcessor worker = get.Body.GetILProcessor(); - - // [SyncVar] GameObject? - if (fd.FieldType.Is()) - { - // return this.GetSyncVarGameObject(ref field, uint netId); - // this. - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, netFieldId); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldflda, fd); - worker.Emit(OpCodes.Call, WeaverTypes.getSyncVarGameObjectReference); - worker.Emit(OpCodes.Ret); - } - // [SyncVar] NetworkIdentity? - else if (fd.FieldType.Is()) - { - // return this.GetSyncVarNetworkIdentity(ref field, uint netId); - // this. - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, netFieldId); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldflda, fd); - worker.Emit(OpCodes.Call, WeaverTypes.getSyncVarNetworkIdentityReference); - worker.Emit(OpCodes.Ret); - } - else if (fd.FieldType.IsDerivedFrom()) - { - // return this.GetSyncVarNetworkBehaviour(ref field, uint netId); - // this. - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, netFieldId); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldflda, fd); - MethodReference getFunc = WeaverTypes.getSyncVarNetworkBehaviourReference.MakeGeneric(fd.FieldType); - worker.Emit(OpCodes.Call, getFunc); - worker.Emit(OpCodes.Ret); - } - // [SyncVar] int, string, etc. - else - { - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, fd); - worker.Emit(OpCodes.Ret); - } - - get.Body.Variables.Add(new VariableDefinition(fd.FieldType)); - get.Body.InitLocals = true; - get.SemanticsAttributes = MethodSemanticsAttributes.Getter; - - return get; - } - - public static MethodDefinition GenerateSyncVarSetter(TypeDefinition td, FieldDefinition fd, string originalName, long dirtyBit, FieldDefinition netFieldId) - { - //Create the set method - MethodDefinition set = new MethodDefinition("set_Network" + originalName, MethodAttributes.Public | - MethodAttributes.SpecialName | - MethodAttributes.HideBySig, - WeaverTypes.Import(typeof(void))); - - ILProcessor worker = set.Body.GetILProcessor(); - - // if (!SyncVarEqual(value, ref playerData)) - Instruction endOfMethod = worker.Create(OpCodes.Nop); - - // this - worker.Emit(OpCodes.Ldarg_0); - // new value to set - worker.Emit(OpCodes.Ldarg_1); - // reference to field to set - // make generic version of SetSyncVar with field type - if (fd.FieldType.Is()) - { - // reference to netId Field to set - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, netFieldId); - - worker.Emit(OpCodes.Call, WeaverTypes.syncVarGameObjectEqualReference); - } - else if (fd.FieldType.Is()) - { - // reference to netId Field to set - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, netFieldId); - - worker.Emit(OpCodes.Call, WeaverTypes.syncVarNetworkIdentityEqualReference); - } - else if (fd.FieldType.IsDerivedFrom()) - { - // reference to netId Field to set - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, netFieldId); - - MethodReference getFunc = WeaverTypes.syncVarNetworkBehaviourEqualReference.MakeGeneric(fd.FieldType); - worker.Emit(OpCodes.Call, getFunc); - } - else - { - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldflda, fd); - - GenericInstanceMethod syncVarEqualGm = new GenericInstanceMethod(WeaverTypes.syncVarEqualReference); - syncVarEqualGm.GenericArguments.Add(fd.FieldType); - worker.Emit(OpCodes.Call, syncVarEqualGm); - } - - worker.Emit(OpCodes.Brtrue, endOfMethod); - - // T oldValue = value; - // TODO for GO/NI we need to backup the netId don't we? - VariableDefinition oldValue = new VariableDefinition(fd.FieldType); - set.Body.Variables.Add(oldValue); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldfld, fd); - worker.Emit(OpCodes.Stloc, oldValue); - - // this - worker.Emit(OpCodes.Ldarg_0); - - // new value to set - worker.Emit(OpCodes.Ldarg_1); - - // reference to field to set - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldflda, fd); - - // dirty bit - // 8 byte integer aka long - worker.Emit(OpCodes.Ldc_I8, dirtyBit); - - if (fd.FieldType.Is()) - { - // reference to netId Field to set - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldflda, netFieldId); - - worker.Emit(OpCodes.Call, WeaverTypes.setSyncVarGameObjectReference); - } - else if (fd.FieldType.Is()) - { - // reference to netId Field to set - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldflda, netFieldId); - - worker.Emit(OpCodes.Call, WeaverTypes.setSyncVarNetworkIdentityReference); - } - else if (fd.FieldType.IsDerivedFrom()) - { - // reference to netId Field to set - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldflda, netFieldId); - - MethodReference getFunc = WeaverTypes.setSyncVarNetworkBehaviourReference.MakeGeneric(fd.FieldType); - worker.Emit(OpCodes.Call, getFunc); - } - else - { - // make generic version of SetSyncVar with field type - GenericInstanceMethod gm = new GenericInstanceMethod(WeaverTypes.setSyncVarReference); - gm.GenericArguments.Add(fd.FieldType); - - // invoke SetSyncVar - worker.Emit(OpCodes.Call, gm); - } - - MethodDefinition hookMethod = GetHookMethod(td, fd); - - if (hookMethod != null) - { - //if (NetworkServer.localClientActive && !getSyncVarHookGuard(dirtyBit)) - Instruction label = worker.Create(OpCodes.Nop); - worker.Emit(OpCodes.Call, WeaverTypes.NetworkServerGetLocalClientActive); - worker.Emit(OpCodes.Brfalse, label); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldc_I8, dirtyBit); - worker.Emit(OpCodes.Call, WeaverTypes.getSyncVarHookGuard); - worker.Emit(OpCodes.Brtrue, label); - - // setSyncVarHookGuard(dirtyBit, true); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldc_I8, dirtyBit); - worker.Emit(OpCodes.Ldc_I4_1); - worker.Emit(OpCodes.Call, WeaverTypes.setSyncVarHookGuard); - - // call hook (oldValue, newValue) - // Generates: OnValueChanged(oldValue, value); - WriteCallHookMethodUsingArgument(worker, hookMethod, oldValue); - - // setSyncVarHookGuard(dirtyBit, false); - worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Ldc_I8, dirtyBit); - worker.Emit(OpCodes.Ldc_I4_0); - worker.Emit(OpCodes.Call, WeaverTypes.setSyncVarHookGuard); - - worker.Append(label); - } - - worker.Append(endOfMethod); - - worker.Emit(OpCodes.Ret); - - set.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.In, fd.FieldType)); - set.SemanticsAttributes = MethodSemanticsAttributes.Setter; - - return set; - } - - public static void ProcessSyncVar(TypeDefinition td, FieldDefinition fd, Dictionary syncVarNetIds, long dirtyBit) - { - string originalName = fd.Name; - Weaver.DLog(td, "Sync Var " + fd.Name + " " + fd.FieldType); - - // GameObject/NetworkIdentity SyncVars have a new field for netId - FieldDefinition netIdField = null; - // NetworkBehaviour has different field type than other NetworkIdentityFields - if (fd.FieldType.IsDerivedFrom()) - { - netIdField = new FieldDefinition("___" + fd.Name + "NetId", - FieldAttributes.Private, - WeaverTypes.Import()); - - syncVarNetIds[fd] = netIdField; - } - else if (fd.FieldType.IsNetworkIdentityField()) - { - netIdField = new FieldDefinition("___" + fd.Name + "NetId", - FieldAttributes.Private, - WeaverTypes.Import()); - - syncVarNetIds[fd] = netIdField; - } - - MethodDefinition get = GenerateSyncVarGetter(fd, originalName, netIdField); - MethodDefinition set = GenerateSyncVarSetter(td, fd, originalName, dirtyBit, netIdField); - - //NOTE: is property even needed? Could just use a setter function? - //create the property - PropertyDefinition propertyDefinition = new PropertyDefinition("Network" + originalName, PropertyAttributes.None, fd.FieldType) - { - GetMethod = get, - SetMethod = set - }; - - //add the methods and property to the type. - td.Methods.Add(get); - td.Methods.Add(set); - td.Properties.Add(propertyDefinition); - Weaver.WeaveLists.replacementSetterProperties[fd] = set; - - // replace getter field if GameObject/NetworkIdentity so it uses - // netId instead - // -> only for GameObjects, otherwise an int syncvar's getter would - // end up in recursion. - if (fd.FieldType.IsNetworkIdentityField()) - { - Weaver.WeaveLists.replacementGetterProperties[fd] = get; - } - } - - public static (List syncVars, Dictionary syncVarNetIds) ProcessSyncVars(TypeDefinition td) - { - List syncVars = new List(); - Dictionary syncVarNetIds = new Dictionary(); - - // the mapping of dirtybits to sync-vars is implicit in the order of the fields here. this order is recorded in m_replacementProperties. - // start assigning syncvars at the place the base class stopped, if any - int dirtyBitCounter = Weaver.WeaveLists.GetSyncVarStart(td.BaseType.FullName); - - // find syncvars - foreach (FieldDefinition fd in td.Fields) - { - if (fd.HasCustomAttribute()) - { - if ((fd.Attributes & FieldAttributes.Static) != 0) - { - Weaver.Error($"{fd.Name} cannot be static", fd); - continue; - } - - if (fd.FieldType.IsArray) - { - Weaver.Error($"{fd.Name} has invalid type. Use SyncLists instead of arrays", fd); - continue; - } - - if (SyncObjectInitializer.ImplementsSyncObject(fd.FieldType)) - { - Weaver.Warning($"{fd.Name} has [SyncVar] attribute. SyncLists should not be marked with SyncVar", fd); - } - else - { - syncVars.Add(fd); - - ProcessSyncVar(td, fd, syncVarNetIds, 1L << dirtyBitCounter); - dirtyBitCounter += 1; - - if (dirtyBitCounter == SyncVarLimit) - { - Weaver.Error($"{td.Name} has too many SyncVars. Consider refactoring your class into multiple components", td); - continue; - } - } - } - } - - // add all the new SyncVar __netId fields - foreach (FieldDefinition fd in syncVarNetIds.Values) - { - td.Fields.Add(fd); - } - Weaver.WeaveLists.SetNumSyncVars(td.FullName, syncVars.Count); - - return (syncVars, syncVarNetIds); - } - - public static void WriteCallHookMethodUsingArgument(ILProcessor worker, MethodDefinition hookMethod, VariableDefinition oldValue) - { - WriteCallHookMethod(worker, hookMethod, oldValue, null); - } - - public static void WriteCallHookMethodUsingField(ILProcessor worker, MethodDefinition hookMethod, VariableDefinition oldValue, FieldDefinition newValue) - { - if (newValue == null) - { - Weaver.Error("NewValue field was null when writing SyncVar hook"); - } - - WriteCallHookMethod(worker, hookMethod, oldValue, newValue); - } - - static void WriteCallHookMethod(ILProcessor worker, MethodDefinition hookMethod, VariableDefinition oldValue, FieldDefinition newValue) - { - WriteStartFunctionCall(); - - // write args - WriteOldValue(); - WriteNewValue(); - - WriteEndFunctionCall(); - - - // *** Local functions used to write OpCodes *** - // Local functions have access to function variables, no need to pass in args - - void WriteOldValue() - { - worker.Emit(OpCodes.Ldloc, oldValue); - } - - void WriteNewValue() - { - // write arg1 or this.field - if (newValue == null) - { - worker.Emit(OpCodes.Ldarg_1); - } - else - { - // this. - worker.Emit(OpCodes.Ldarg_0); - // syncvar.get - worker.Emit(OpCodes.Ldfld, newValue); - } - } - - // Writes this before method if it is not static - void WriteStartFunctionCall() - { - // don't add this (Ldarg_0) if method is static - if (!hookMethod.IsStatic) - { - // this before method call - // e.g. this.onValueChanged - worker.Emit(OpCodes.Ldarg_0); - } - } - - // Calls method - void WriteEndFunctionCall() - { - // only use Callvirt when not static - OpCode opcode = hookMethod.IsStatic ? OpCodes.Call : OpCodes.Callvirt; - worker.Emit(opcode, hookMethod); - } - } - } -} diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncVarProcessor.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncVarProcessor.cs.meta deleted file mode 100644 index 31c4586..0000000 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/SyncVarProcessor.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f52c39bddd95d42b88f9cd554dfd9198 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/TargetRpcProcessor.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/TargetRpcProcessor.cs index c67a86a..8afba94 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/TargetRpcProcessor.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/TargetRpcProcessor.cs @@ -3,9 +3,7 @@ using Mono.CecilX.Cil; namespace Mirror.Weaver { - /// - /// Processes [TargetRpc] methods in NetworkBehaviour - /// + // Processes [TargetRpc] methods in NetworkBehaviour public static class TargetRpcProcessor { // helper functions to check if the method has a NetworkConnection parameter @@ -15,17 +13,19 @@ namespace Mirror.Weaver md.Parameters[0].ParameterType.Is(); } - public static MethodDefinition ProcessTargetRpcInvoke(TypeDefinition td, MethodDefinition md, MethodDefinition rpcCallFunc) + public static MethodDefinition ProcessTargetRpcInvoke(WeaverTypes weaverTypes, Readers readers, Logger Log, TypeDefinition td, MethodDefinition md, MethodDefinition rpcCallFunc, ref bool WeavingFailed) { - MethodDefinition rpc = new MethodDefinition(Weaver.InvokeRpcPrefix + md.Name, MethodAttributes.Family | + string trgName = Weaver.GenerateMethodName(Weaver.InvokeRpcPrefix, md); + + MethodDefinition rpc = new MethodDefinition(trgName, MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig, - WeaverTypes.Import(typeof(void))); + weaverTypes.Import(typeof(void))); ILProcessor worker = rpc.Body.GetILProcessor(); Instruction label = worker.Create(OpCodes.Nop); - NetworkBehaviourProcessor.WriteClientActiveCheck(worker, md.Name, label, "TargetRPC"); + NetworkBehaviourProcessor.WriteClientActiveCheck(worker, weaverTypes, md.Name, label, "TargetRPC"); // setup for reader worker.Emit(OpCodes.Ldarg_0); @@ -43,18 +43,18 @@ namespace Mirror.Weaver // TODO // a) .connectionToServer = best solution. no doubt. // b) NetworkClient.connection for now. add TODO to not use static later. - worker.Emit(OpCodes.Call, WeaverTypes.ReadyConnectionReference); + worker.Emit(OpCodes.Call, weaverTypes.NetworkClientConnectionReference); } // process reader parameters and skip first one if first one is NetworkConnection - if (!NetworkBehaviourProcessor.ReadArguments(md, worker, RemoteCallType.TargetRpc)) + if (!NetworkBehaviourProcessor.ReadArguments(md, readers, Log, worker, RemoteCallType.TargetRpc, ref WeavingFailed)) return null; // invoke actual command function worker.Emit(OpCodes.Callvirt, rpcCallFunc); worker.Emit(OpCodes.Ret); - NetworkBehaviourProcessor.AddInvokeParameters(rpc.Parameters); + NetworkBehaviourProcessor.AddInvokeParameters(weaverTypes, rpc.Parameters); td.Methods.Add(rpc); return rpc; } @@ -92,23 +92,21 @@ namespace Mirror.Weaver correctly in dependent assemblies */ - public static MethodDefinition ProcessTargetRpcCall(TypeDefinition td, MethodDefinition md, CustomAttribute targetRpcAttr) + public static MethodDefinition ProcessTargetRpcCall(WeaverTypes weaverTypes, Writers writers, Logger Log, TypeDefinition td, MethodDefinition md, CustomAttribute targetRpcAttr, ref bool WeavingFailed) { - MethodDefinition rpc = MethodProcessor.SubstituteMethod(td, md); + MethodDefinition rpc = MethodProcessor.SubstituteMethod(Log, td, md, ref WeavingFailed); ILProcessor worker = md.Body.GetILProcessor(); - NetworkBehaviourProcessor.WriteSetupLocals(worker); + NetworkBehaviourProcessor.WriteSetupLocals(worker, weaverTypes); - NetworkBehaviourProcessor.WriteCreateWriter(worker); + NetworkBehaviourProcessor.WriteGetWriter(worker, weaverTypes); // write all the arguments that the user passed to the TargetRpc call // (skip first one if first one is NetworkConnection) - if (!NetworkBehaviourProcessor.WriteArguments(worker, md, RemoteCallType.TargetRpc)) + if (!NetworkBehaviourProcessor.WriteArguments(worker, writers, Log, md, RemoteCallType.TargetRpc, ref WeavingFailed)) return null; - string rpcName = md.Name; - // invoke SendInternal and return // this worker.Emit(OpCodes.Ldarg_0); @@ -122,16 +120,14 @@ namespace Mirror.Weaver // null worker.Emit(OpCodes.Ldnull); } - worker.Emit(OpCodes.Ldtoken, td); - // invokerClass - worker.Emit(OpCodes.Call, WeaverTypes.getTypeFromHandleReference); - worker.Emit(OpCodes.Ldstr, rpcName); + // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions + worker.Emit(OpCodes.Ldstr, md.FullName); // writer worker.Emit(OpCodes.Ldloc_0); worker.Emit(OpCodes.Ldc_I4, targetRpcAttr.GetField("channel", 0)); - worker.Emit(OpCodes.Callvirt, WeaverTypes.sendTargetRpcInternal); + worker.Emit(OpCodes.Callvirt, weaverTypes.sendTargetRpcInternal); - NetworkBehaviourProcessor.WriteRecycleWriter(worker); + NetworkBehaviourProcessor.WriteReturnWriter(worker, weaverTypes); worker.Emit(OpCodes.Ret); diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/TargetRpcProcessor.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/TargetRpcProcessor.cs.meta index c315349..0ff7cc5 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Processors/TargetRpcProcessor.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Processors/TargetRpcProcessor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Readers.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Readers.cs index dcb5cd2..fa888c9 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Readers.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Readers.cs @@ -2,61 +2,67 @@ using System; using System.Collections.Generic; using Mono.CecilX; using Mono.CecilX.Cil; +// to use Mono.CecilX.Rocks here, we need to 'override references' in the +// Unity.Mirror.CodeGen assembly definition file in the Editor, and add CecilX.Rocks. +// otherwise we get an unknown import exception. using Mono.CecilX.Rocks; namespace Mirror.Weaver { - public static class Readers + // not static, because ILPostProcessor is multithreaded + public class Readers { - static Dictionary readFuncs; + // Readers are only for this assembly. + // can't be used from another assembly, otherwise we will get: + // "System.ArgumentException: Member ... is declared in another module and needs to be imported" + AssemblyDefinition assembly; + WeaverTypes weaverTypes; + TypeDefinition GeneratedCodeClass; + Logger Log; - public static void Init() + Dictionary readFuncs = + new Dictionary(new TypeReferenceComparer()); + + public Readers(AssemblyDefinition assembly, WeaverTypes weaverTypes, TypeDefinition GeneratedCodeClass, Logger Log) { - readFuncs = new Dictionary(new TypeReferenceComparer()); + this.assembly = assembly; + this.weaverTypes = weaverTypes; + this.GeneratedCodeClass = GeneratedCodeClass; + this.Log = Log; } - internal static void Register(TypeReference dataType, MethodReference methodReference) + internal void Register(TypeReference dataType, MethodReference methodReference) { if (readFuncs.ContainsKey(dataType)) { // TODO enable this again later. // Reader has some obsolete functions that were renamed. // Don't want weaver warnings for all of them. - //Weaver.Warning($"Registering a Read method for {dataType.FullName} when one already exists", methodReference); + //Log.Warning($"Registering a Read method for {dataType.FullName} when one already exists", methodReference); } // we need to import type when we Initialize Readers so import here in case it is used anywhere else - TypeReference imported = Weaver.CurrentAssembly.MainModule.ImportReference(dataType); + TypeReference imported = assembly.MainModule.ImportReference(dataType); readFuncs[imported] = methodReference; } - static void RegisterReadFunc(TypeReference typeReference, MethodDefinition newReaderFunc) + void RegisterReadFunc(TypeReference typeReference, MethodDefinition newReaderFunc) { Register(typeReference, newReaderFunc); - - Weaver.GeneratedCodeClass.Methods.Add(newReaderFunc); + GeneratedCodeClass.Methods.Add(newReaderFunc); } - /// - /// Finds existing reader for type, if non exists trys to create one - /// This method is recursive - /// - /// - /// Returns or null - public static MethodReference GetReadFunc(TypeReference variable) + // Finds existing reader for type, if non exists trys to create one + public MethodReference GetReadFunc(TypeReference variable, ref bool WeavingFailed) { if (readFuncs.TryGetValue(variable, out MethodReference foundFunc)) - { return foundFunc; - } - else - { - TypeReference importedVariable = Weaver.CurrentAssembly.MainModule.ImportReference(variable); - return GenerateReader(importedVariable); - } + + TypeReference importedVariable = assembly.MainModule.ImportReference(variable); + return GenerateReader(importedVariable, ref WeavingFailed); } - static MethodReference GenerateReader(TypeReference variableReference) + MethodReference GenerateReader(TypeReference variableReference, ref bool WeavingFailed) { // Arrays are special, if we resolve them, we get the element type, // so the following ifs might choke on it for scriptable objects @@ -66,86 +72,99 @@ namespace Mirror.Weaver { if (variableReference.IsMultidimensionalArray()) { - Weaver.Error($"{variableReference.Name} is an unsupported type. Multidimensional arrays are not supported", variableReference); + Log.Error($"{variableReference.Name} is an unsupported type. Multidimensional arrays are not supported", variableReference); + WeavingFailed = true; return null; } - return GenerateReadCollection(variableReference, variableReference.GetElementType(), nameof(NetworkReaderExtensions.ReadArray)); + return GenerateReadCollection(variableReference, variableReference.GetElementType(), nameof(NetworkReaderExtensions.ReadArray), ref WeavingFailed); } TypeDefinition variableDefinition = variableReference.Resolve(); + + // check if the type is completely invalid if (variableDefinition == null) { - Weaver.Error($"{variableReference.Name} is not a supported type", variableReference); + Log.Error($"{variableReference.Name} is not a supported type", variableReference); + WeavingFailed = true; return null; } - if (variableDefinition.IsDerivedFrom() && - !variableReference.IsDerivedFrom()) - { - Weaver.Error($"Cannot generate reader for component type {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); - return null; - } - if (variableReference.Is()) - { - Weaver.Error($"Cannot generate reader for {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); - return null; - } - if (variableReference.Is()) - { - Weaver.Error($"Cannot generate reader for {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); - return null; - } - if (variableReference.IsByReference) + else if (variableReference.IsByReference) { // error?? - Weaver.Error($"Cannot pass type {variableReference.Name} by reference", variableReference); - return null; - } - if (variableDefinition.HasGenericParameters && !variableDefinition.Is(typeof(ArraySegment<>)) && !variableDefinition.Is(typeof(List<>))) - { - Weaver.Error($"Cannot generate reader for generic variable {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); - return null; - } - if (variableDefinition.IsInterface) - { - Weaver.Error($"Cannot generate reader for interface {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); - return null; - } - if (variableDefinition.IsAbstract) - { - Weaver.Error($"Cannot generate reader for abstract class {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); + Log.Error($"Cannot pass type {variableReference.Name} by reference", variableReference); + WeavingFailed = true; return null; } + // use existing func for known types if (variableDefinition.IsEnum) { - return GenerateEnumReadFunc(variableReference); + return GenerateEnumReadFunc(variableReference, ref WeavingFailed); } else if (variableDefinition.Is(typeof(ArraySegment<>))) { - return GenerateArraySegmentReadFunc(variableReference); + return GenerateArraySegmentReadFunc(variableReference, ref WeavingFailed); } else if (variableDefinition.Is(typeof(List<>))) { GenericInstanceType genericInstance = (GenericInstanceType)variableReference; TypeReference elementType = genericInstance.GenericArguments[0]; - return GenerateReadCollection(variableReference, elementType, nameof(NetworkReaderExtensions.ReadList)); + return GenerateReadCollection(variableReference, elementType, nameof(NetworkReaderExtensions.ReadList), ref WeavingFailed); } else if (variableReference.IsDerivedFrom()) { return GetNetworkBehaviourReader(variableReference); } - return GenerateClassOrStructReadFunction(variableReference); + // check if reader generation is applicable on this type + if (variableDefinition.IsDerivedFrom()) + { + Log.Error($"Cannot generate reader for component type {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); + WeavingFailed = true; + return null; + } + if (variableReference.Is()) + { + Log.Error($"Cannot generate reader for {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); + WeavingFailed = true; + return null; + } + if (variableReference.Is()) + { + Log.Error($"Cannot generate reader for {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); + WeavingFailed = true; + return null; + } + if (variableDefinition.HasGenericParameters) + { + Log.Error($"Cannot generate reader for generic variable {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); + WeavingFailed = true; + return null; + } + if (variableDefinition.IsInterface) + { + Log.Error($"Cannot generate reader for interface {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); + WeavingFailed = true; + return null; + } + if (variableDefinition.IsAbstract) + { + Log.Error($"Cannot generate reader for abstract class {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); + WeavingFailed = true; + return null; + } + + return GenerateClassOrStructReadFunction(variableReference, ref WeavingFailed); } - static MethodReference GetNetworkBehaviourReader(TypeReference variableReference) + MethodReference GetNetworkBehaviourReader(TypeReference variableReference) { // uses generic ReadNetworkBehaviour rather than having weaver create one for each NB - MethodReference generic = WeaverTypes.readNetworkBehaviourGeneric; + MethodReference generic = weaverTypes.readNetworkBehaviourGeneric; - MethodReference readFunc = generic.MakeGeneric(variableReference); + MethodReference readFunc = generic.MakeGeneric(assembly.MainModule, variableReference); // register function so it is added to Reader // use Register instead of RegisterWriteFunc because this is not a generated function @@ -154,7 +173,7 @@ namespace Mirror.Weaver return readFunc; } - static MethodDefinition GenerateEnumReadFunc(TypeReference variable) + MethodDefinition GenerateEnumReadFunc(TypeReference variable, ref bool WeavingFailed) { MethodDefinition readerFunc = GenerateReaderFunction(variable); @@ -163,14 +182,14 @@ namespace Mirror.Weaver worker.Emit(OpCodes.Ldarg_0); TypeReference underlyingType = variable.Resolve().GetEnumUnderlyingType(); - MethodReference underlyingFunc = GetReadFunc(underlyingType); + MethodReference underlyingFunc = GetReadFunc(underlyingType, ref WeavingFailed); worker.Emit(OpCodes.Call, underlyingFunc); worker.Emit(OpCodes.Ret); return readerFunc; } - static MethodDefinition GenerateArraySegmentReadFunc(TypeReference variable) + MethodDefinition GenerateArraySegmentReadFunc(TypeReference variable, ref bool WeavingFailed) { GenericInstanceType genericInstance = (GenericInstanceType)variable; TypeReference elementType = genericInstance.GenericArguments[0]; @@ -182,17 +201,17 @@ namespace Mirror.Weaver // $array = reader.Read<[T]>() ArrayType arrayType = elementType.MakeArrayType(); worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Call, GetReadFunc(arrayType)); + worker.Emit(OpCodes.Call, GetReadFunc(arrayType, ref WeavingFailed)); // return new ArraySegment($array); - worker.Emit(OpCodes.Newobj, WeaverTypes.ArraySegmentConstructorReference.MakeHostInstanceGeneric(genericInstance)); + worker.Emit(OpCodes.Newobj, weaverTypes.ArraySegmentConstructorReference.MakeHostInstanceGeneric(assembly.MainModule, genericInstance)); worker.Emit(OpCodes.Ret); return readerFunc; } - static MethodDefinition GenerateReaderFunction(TypeReference variable) + MethodDefinition GenerateReaderFunction(TypeReference variable) { - string functionName = "_Read_" + variable.FullName; + string functionName = $"_Read_{variable.FullName}"; // create new reader for this type MethodDefinition readerFunc = new MethodDefinition(functionName, @@ -201,22 +220,22 @@ namespace Mirror.Weaver MethodAttributes.HideBySig, variable); - readerFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, WeaverTypes.Import())); + readerFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, weaverTypes.Import())); readerFunc.Body.InitLocals = true; RegisterReadFunc(variable, readerFunc); return readerFunc; } - static MethodDefinition GenerateReadCollection(TypeReference variable, TypeReference elementType, string readerFunction) + MethodDefinition GenerateReadCollection(TypeReference variable, TypeReference elementType, string readerFunction, ref bool WeavingFailed) { MethodDefinition readerFunc = GenerateReaderFunction(variable); // generate readers for the element - GetReadFunc(elementType); + GetReadFunc(elementType, ref WeavingFailed); - ModuleDefinition module = Weaver.CurrentAssembly.MainModule; + ModuleDefinition module = assembly.MainModule; TypeReference readerExtensions = module.ImportReference(typeof(NetworkReaderExtensions)); - MethodReference listReader = Resolvers.ResolveMethod(readerExtensions, Weaver.CurrentAssembly, readerFunction); + MethodReference listReader = Resolvers.ResolveMethod(readerExtensions, assembly, Log, readerFunction, ref WeavingFailed); GenericInstanceMethod methodRef = new GenericInstanceMethod(listReader); methodRef.GenericArguments.Add(elementType); @@ -233,7 +252,7 @@ namespace Mirror.Weaver return readerFunc; } - static MethodDefinition GenerateClassOrStructReadFunction(TypeReference variable) + MethodDefinition GenerateClassOrStructReadFunction(TypeReference variable, ref bool WeavingFailed) { MethodDefinition readerFunc = GenerateReaderFunction(variable); @@ -245,23 +264,23 @@ namespace Mirror.Weaver TypeDefinition td = variable.Resolve(); if (!td.IsValueType) - GenerateNullCheck(worker); + GenerateNullCheck(worker, ref WeavingFailed); - CreateNew(variable, worker, td); - ReadAllFields(variable, worker); + CreateNew(variable, worker, td, ref WeavingFailed); + ReadAllFields(variable, worker, ref WeavingFailed); worker.Emit(OpCodes.Ldloc_0); worker.Emit(OpCodes.Ret); return readerFunc; } - static void GenerateNullCheck(ILProcessor worker) + void GenerateNullCheck(ILProcessor worker, ref bool WeavingFailed) { // if (!reader.ReadBoolean()) { // return null; // } worker.Emit(OpCodes.Ldarg_0); - worker.Emit(OpCodes.Call, GetReadFunc(WeaverTypes.Import())); + worker.Emit(OpCodes.Call, GetReadFunc(weaverTypes.Import(), ref WeavingFailed)); Instruction labelEmptyArray = worker.Create(OpCodes.Nop); worker.Emit(OpCodes.Brtrue, labelEmptyArray); @@ -272,7 +291,7 @@ namespace Mirror.Weaver } // Initialize the local variable with a new instance - static void CreateNew(TypeReference variable, ILProcessor worker, TypeDefinition td) + void CreateNew(TypeReference variable, ILProcessor worker, TypeDefinition td, ref bool WeavingFailed) { if (variable.IsValueType) { @@ -282,7 +301,7 @@ namespace Mirror.Weaver } else if (td.IsDerivedFrom()) { - GenericInstanceMethod genericInstanceMethod = new GenericInstanceMethod(WeaverTypes.ScriptableObjectCreateInstanceMethod); + GenericInstanceMethod genericInstanceMethod = new GenericInstanceMethod(weaverTypes.ScriptableObjectCreateInstanceMethod); genericInstanceMethod.GenericArguments.Add(variable); worker.Emit(OpCodes.Call, genericInstanceMethod); worker.Emit(OpCodes.Stloc_0); @@ -293,25 +312,26 @@ namespace Mirror.Weaver MethodDefinition ctor = Resolvers.ResolveDefaultPublicCtor(variable); if (ctor == null) { - Weaver.Error($"{variable.Name} can't be deserialized because it has no default constructor", variable); + Log.Error($"{variable.Name} can't be deserialized because it has no default constructor. Don't use {variable.Name} in [SyncVar]s, Rpcs, Cmds, etc.", variable); + WeavingFailed = true; return; } - MethodReference ctorRef = Weaver.CurrentAssembly.MainModule.ImportReference(ctor); + MethodReference ctorRef = assembly.MainModule.ImportReference(ctor); worker.Emit(OpCodes.Newobj, ctorRef); worker.Emit(OpCodes.Stloc_0); } } - static void ReadAllFields(TypeReference variable, ILProcessor worker) + void ReadAllFields(TypeReference variable, ILProcessor worker, ref bool WeavingFailed) { foreach (FieldDefinition field in variable.FindAllPublicFields()) { // mismatched ldloca/ldloc for struct/class combinations is invalid IL, which causes crash at runtime OpCode opcode = variable.IsValueType ? OpCodes.Ldloca : OpCodes.Ldloc; worker.Emit(opcode, 0); - MethodReference readFunc = GetReadFunc(field.FieldType); + MethodReference readFunc = GetReadFunc(field.FieldType, ref WeavingFailed); if (readFunc != null) { worker.Emit(OpCodes.Ldarg_0); @@ -319,21 +339,19 @@ namespace Mirror.Weaver } else { - Weaver.Error($"{field.Name} has an unsupported type", field); + Log.Error($"{field.Name} has an unsupported type", field); + WeavingFailed = true; } - FieldReference fieldRef = Weaver.CurrentAssembly.MainModule.ImportReference(field); + FieldReference fieldRef = assembly.MainModule.ImportReference(field); worker.Emit(OpCodes.Stfld, fieldRef); } } - /// - /// Save a delegate for each one of the readers into - /// - /// - internal static void InitializeReaders(ILProcessor worker) + // Save a delegate for each one of the readers into Reader.read + internal void InitializeReaders(ILProcessor worker) { - ModuleDefinition module = Weaver.CurrentAssembly.MainModule; + ModuleDefinition module = assembly.MainModule; TypeReference genericReaderClassRef = module.ImportReference(typeof(Reader<>)); @@ -352,15 +370,14 @@ namespace Mirror.Weaver worker.Emit(OpCodes.Ldnull); worker.Emit(OpCodes.Ldftn, readFunc); GenericInstanceType funcGenericInstance = funcRef.MakeGenericInstanceType(networkReaderRef, targetType); - MethodReference funcConstructorInstance = funcConstructorRef.MakeHostInstanceGeneric(funcGenericInstance); + MethodReference funcConstructorInstance = funcConstructorRef.MakeHostInstanceGeneric(assembly.MainModule, funcGenericInstance); worker.Emit(OpCodes.Newobj, funcConstructorInstance); // save it in Reader.read GenericInstanceType genericInstance = genericReaderClassRef.MakeGenericInstanceType(targetType); - FieldReference specializedField = fieldRef.SpecializeField(genericInstance); + FieldReference specializedField = fieldRef.SpecializeField(assembly.MainModule, genericInstance); worker.Emit(OpCodes.Stsfld, specializedField); } - } } } diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Readers.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Readers.cs.meta index e119669..838ff59 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Readers.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Readers.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Resolvers.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Resolvers.cs index 9bfbb3c..a9d551b 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Resolvers.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Resolvers.cs @@ -10,51 +10,59 @@ namespace Mirror.Weaver { public static class Resolvers { - public static MethodReference ResolveMethod(TypeReference tr, AssemblyDefinition scriptDef, string name) + public static MethodReference ResolveMethod(TypeReference tr, AssemblyDefinition assembly, Logger Log, string name, ref bool WeavingFailed) { if (tr == null) { - Weaver.Error($"Cannot resolve method {name} without a class"); + Log.Error($"Cannot resolve method {name} without a class"); + WeavingFailed = true; return null; } - MethodReference method = ResolveMethod(tr, scriptDef, m => m.Name == name); + MethodReference method = ResolveMethod(tr, assembly, Log, m => m.Name == name, ref WeavingFailed); if (method == null) { - Weaver.Error($"Method not found with name {name} in type {tr.Name}", tr); + Log.Error($"Method not found with name {name} in type {tr.Name}", tr); + WeavingFailed = true; } return method; } - public static MethodReference ResolveMethod(TypeReference t, AssemblyDefinition scriptDef, System.Func predicate) + public static MethodReference ResolveMethod(TypeReference t, AssemblyDefinition assembly, Logger Log, System.Func predicate, ref bool WeavingFailed) { foreach (MethodDefinition methodRef in t.Resolve().Methods) { if (predicate(methodRef)) { - return scriptDef.MainModule.ImportReference(methodRef); + return assembly.MainModule.ImportReference(methodRef); } } - Weaver.Error($"Method not found in type {t.Name}", t); + Log.Error($"Method not found in type {t.Name}", t); + WeavingFailed = true; return null; } - public static MethodReference TryResolveMethodInParents(TypeReference tr, AssemblyDefinition scriptDef, string name) + public static MethodReference TryResolveMethodInParents(TypeReference tr, AssemblyDefinition assembly, string name) { if (tr == null) { return null; } - foreach (MethodDefinition methodRef in tr.Resolve().Methods) + foreach (MethodDefinition methodDef in tr.Resolve().Methods) { - if (methodRef.Name == name) + if (methodDef.Name == name) { - return scriptDef.MainModule.ImportReference(methodRef); + MethodReference methodRef = methodDef; + if (tr.IsGenericInstance) + { + methodRef = methodRef.MakeHostInstanceGeneric(tr.Module, (GenericInstanceType)tr); + } + return assembly.MainModule.ImportReference(methodRef); } } // Could not find the method in this class, try the parent - return TryResolveMethodInParents(tr.Resolve().BaseType, scriptDef, name); + return TryResolveMethodInParents(tr.Resolve().BaseType.ApplyGenericParameters(tr), assembly, name); } public static MethodDefinition ResolveDefaultPublicCtor(TypeReference variable) @@ -71,13 +79,13 @@ namespace Mirror.Weaver return null; } - public static MethodReference ResolveProperty(TypeReference tr, AssemblyDefinition scriptDef, string name) + public static MethodReference ResolveProperty(TypeReference tr, AssemblyDefinition assembly, string name) { foreach (PropertyDefinition pd in tr.Resolve().Properties) { if (pd.Name == name) { - return scriptDef.MainModule.ImportReference(pd.GetMethod); + return assembly.MainModule.ImportReference(pd.GetMethod); } } return null; diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Resolvers.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Resolvers.cs.meta index 38680aa..f4f6602 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Resolvers.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Resolvers.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/SyncVarAccessLists.cs b/UnityProject/Assets/Mirror/Editor/Weaver/SyncVarAccessLists.cs new file mode 100644 index 0000000..fa7a682 --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/SyncVarAccessLists.cs @@ -0,0 +1,32 @@ +// tracks SyncVar read/write access when processing NetworkBehaviour, +// to later be replaced by SyncVarAccessReplacer. +using System.Collections.Generic; +using Mono.CecilX; + +namespace Mirror.Weaver +{ + // This data is flushed each time - if we are run multiple times in the same process/domain + public class SyncVarAccessLists + { + // setter functions that replace [SyncVar] member variable references. dict + public Dictionary replacementSetterProperties = + new Dictionary(); + + // getter functions that replace [SyncVar] member variable references. dict + public Dictionary replacementGetterProperties = + new Dictionary(); + + // amount of SyncVars per class. dict + // necessary for SyncVar dirty bits, where inheriting classes start + // their dirty bits at base class SyncVar amount. + public Dictionary numSyncVars = new Dictionary(); + + public int GetSyncVarStart(string className) => + numSyncVars.TryGetValue(className, out int value) ? value : 0; + + public void SetNumSyncVars(string className, int num) + { + numSyncVars[className] = num; + } + } +} diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/SyncVarAccessLists.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/SyncVarAccessLists.cs.meta new file mode 100644 index 0000000..9a4da44 --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/SyncVarAccessLists.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6905230c3c4c4e158760065a93380e83 +timeCreated: 1629348618 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/TypeReferenceComparer.cs b/UnityProject/Assets/Mirror/Editor/Weaver/TypeReferenceComparer.cs index 804c494..e3c31d5 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/TypeReferenceComparer.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/TypeReferenceComparer.cs @@ -3,19 +3,13 @@ using Mono.CecilX; namespace Mirror.Weaver { - /// - /// Compares TypeReference using FullName - /// + // Compares TypeReference using FullName public class TypeReferenceComparer : IEqualityComparer { - public bool Equals(TypeReference x, TypeReference y) - { - return x.FullName == y.FullName; - } + public bool Equals(TypeReference x, TypeReference y) => + x.FullName == y.FullName; - public int GetHashCode(TypeReference obj) - { - return obj.FullName.GetHashCode(); - } + public int GetHashCode(TypeReference obj) => + obj.FullName.GetHashCode(); } } diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/TypeReferenceComparer.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/TypeReferenceComparer.cs.meta index 1aed28f..890b4dc 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/TypeReferenceComparer.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/TypeReferenceComparer.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Unity.Mirror.CodeGen.asmdef b/UnityProject/Assets/Mirror/Editor/Weaver/Unity.Mirror.CodeGen.asmdef new file mode 100644 index 0000000..4566bb2 --- /dev/null +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Unity.Mirror.CodeGen.asmdef @@ -0,0 +1,21 @@ +{ + "name": "Unity.Mirror.CodeGen", + "rootNamespace": "", + "references": [ + "Mirror" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": true, + "precompiledReferences": [ + "Mono.CecilX.dll", + "Mono.CecilX.Rocks.dll" + ], + "autoReferenced": false, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Mirror.Weaver.asmdef.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Unity.Mirror.CodeGen.asmdef.meta similarity index 63% rename from UnityProject/Assets/Mirror/Editor/Weaver/Mirror.Weaver.asmdef.meta rename to UnityProject/Assets/Mirror/Editor/Weaver/Unity.Mirror.CodeGen.asmdef.meta index 2ee4754..b65a0cd 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Mirror.Weaver.asmdef.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Unity.Mirror.CodeGen.asmdef.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 1d0b9d21c3ff546a4aa32399dfd33474 AssemblyDefinitionImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Weaver.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Weaver.cs index 4e9c08c..2644e68 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Weaver.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Weaver.cs @@ -1,96 +1,80 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Linq; +using System.Diagnostics; using Mono.CecilX; namespace Mirror.Weaver { - // This data is flushed each time - if we are run multiple times in the same process/domain - class WeaverLists + // not static, because ILPostProcessor is multithreaded + internal class Weaver { - // setter functions that replace [SyncVar] member variable references. dict - public Dictionary replacementSetterProperties = new Dictionary(); - // getter functions that replace [SyncVar] member variable references. dict - public Dictionary replacementGetterProperties = new Dictionary(); - - // amount of SyncVars per class. dict - public Dictionary numSyncVars = new Dictionary(); - - public int GetSyncVarStart(string className) - { - return numSyncVars.ContainsKey(className) - ? numSyncVars[className] - : 0; - } - - public void SetNumSyncVars(string className, int num) - { - numSyncVars[className] = num; - } - } - - internal static class Weaver - { - public static string InvokeRpcPrefix => "InvokeUserCode_"; + public const string InvokeRpcPrefix = "InvokeUserCode_"; // generated code class public const string GeneratedCodeNamespace = "Mirror"; public const string GeneratedCodeClassName = "GeneratedNetworkCode"; - public static TypeDefinition GeneratedCodeClass; + TypeDefinition GeneratedCodeClass; - public static WeaverLists WeaveLists { get; private set; } - public static AssemblyDefinition CurrentAssembly { get; private set; } - public static bool WeavingFailed { get; private set; } - public static bool GenerateLogErrors; + // for resolving Mirror.dll in ReaderWriterProcessor, we need to know + // Mirror.dll name + public const string MirrorAssemblyName = "Mirror"; - // private properties - static readonly bool DebugLogEnabled = true; + WeaverTypes weaverTypes; + SyncVarAccessLists syncVarAccessLists; + AssemblyDefinition CurrentAssembly; + Writers writers; + Readers readers; - public static void DLog(TypeDefinition td, string fmt, params object[] args) + // in case of weaver errors, we don't stop immediately. + // we log all errors and then eventually return false if + // weaving has failed. + // this way the user can fix multiple errors at once, instead of having + // to fix -> recompile -> fix -> recompile for one error at a time. + bool WeavingFailed; + + // logger functions can be set from the outside. + // for example, Debug.Log or ILPostProcessor Diagnostics log for + // multi threaded logging. + public Logger Log; + + // remote actions now support overloads, + // -> but IL2CPP doesnt like it when two generated methods + // -> have the same signature, + // -> so, append the signature to the generated method name, + // -> to create a unique name + // Example: + // RpcTeleport(Vector3 position) -> InvokeUserCode_RpcTeleport__Vector3() + // RpcTeleport(Vector3 position, Quaternion rotation) -> InvokeUserCode_RpcTeleport__Vector3Quaternion() + // fixes https://github.com/vis2k/Mirror/issues/3060 + public static string GenerateMethodName(string initialPrefix, MethodDefinition md) { - if (!DebugLogEnabled) - return; + initialPrefix += md.Name; - Console.WriteLine("[" + td.Name + "] " + string.Format(fmt, args)); - } - - // display weaver error - // and mark process as failed - public static void Error(string message) - { - Log.Error(message); - WeavingFailed = true; - } - - public static void Error(string message, MemberReference mr) - { - Log.Error($"{message} (at {mr})"); - WeavingFailed = true; - } - - public static void Warning(string message, MemberReference mr) - { - Log.Warning($"{message} (at {mr})"); - } - - - static void CheckMonoBehaviour(TypeDefinition td) - { - if (td.IsDerivedFrom()) + for (int i = 0; i < md.Parameters.Count; ++i) { - MonoBehaviourProcessor.Process(td); + // with __ so it's more obvious that this is the parameter suffix. + // otherwise RpcTest(int) => RpcTestInt(int) which is not obvious. + initialPrefix += $"__{md.Parameters[i].ParameterType.Name}"; } + + return initialPrefix; } - static bool WeaveNetworkBehavior(TypeDefinition td) + public Weaver(Logger Log) + { + this.Log = Log; + } + + // returns 'true' if modified (=if we did anything) + bool WeaveNetworkBehavior(TypeDefinition td) { if (!td.IsClass) return false; if (!td.IsDerivedFrom()) { - CheckMonoBehaviour(td); + if (td.IsDerivedFrom()) + MonoBehaviourProcessor.Process(Log, td, ref WeavingFailed); return false; } @@ -122,68 +106,67 @@ namespace Mirror.Weaver bool modified = false; foreach (TypeDefinition behaviour in behaviourClasses) { - modified |= new NetworkBehaviourProcessor(behaviour).Process(); + modified |= new NetworkBehaviourProcessor(CurrentAssembly, weaverTypes, syncVarAccessLists, writers, readers, Log, behaviour).Process(ref WeavingFailed); } return modified; } - static bool WeaveModule(ModuleDefinition moduleDefinition) + bool WeaveModule(ModuleDefinition moduleDefinition) { - try + bool modified = false; + + Stopwatch watch = Stopwatch.StartNew(); + watch.Start(); + + foreach (TypeDefinition td in moduleDefinition.Types) { - bool modified = false; - - System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew(); - - watch.Start(); - foreach (TypeDefinition td in moduleDefinition.Types) + if (td.IsClass && td.BaseType.CanBeResolved()) { - if (td.IsClass && td.BaseType.CanBeResolved()) - { - modified |= WeaveNetworkBehavior(td); - modified |= ServerClientAttributeProcessor.Process(td); - } + modified |= WeaveNetworkBehavior(td); + modified |= ServerClientAttributeProcessor.Process(weaverTypes, Log, td, ref WeavingFailed); } - watch.Stop(); - Console.WriteLine("Weave behaviours and messages took " + watch.ElapsedMilliseconds + " milliseconds"); + } - return modified; - } - catch (Exception ex) - { - Error(ex.ToString()); - throw new Exception(ex.Message, ex); - } + watch.Stop(); + Console.WriteLine($"Weave behaviours and messages took {watch.ElapsedMilliseconds} milliseconds"); + + return modified; } - static void CreateGeneratedCodeClass() + void CreateGeneratedCodeClass() { - // create "Mirror.GeneratedNetworkCode" class + // create "Mirror.GeneratedNetworkCode" class which holds all + // Readers and Writers GeneratedCodeClass = new TypeDefinition(GeneratedCodeNamespace, GeneratedCodeClassName, TypeAttributes.BeforeFieldInit | TypeAttributes.Class | TypeAttributes.AnsiClass | TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.Abstract | TypeAttributes.Sealed, - WeaverTypes.Import()); + weaverTypes.Import()); } - static bool ContainsGeneratedCodeClass(ModuleDefinition module) + // Weave takes an AssemblyDefinition to be compatible with both old and + // new weavers: + // * old takes a filepath, new takes a in-memory byte[] + // * old uses DefaultAssemblyResolver with added dependencies paths, + // new uses ...? + // + // => assembly: the one we are currently weaving (MyGame.dll) + // => resolver: useful in case we need to resolve any of the assembly's + // assembly.MainModule.AssemblyReferences. + // -> we can resolve ANY of them given that the resolver + // works properly (need custom one for ILPostProcessor) + // -> IMPORTANT: .Resolve() takes an AssemblyNameReference. + // those from assembly.MainModule.AssemblyReferences are + // guaranteed to be resolve-able. + // Parsing from a string for Library/.../Mirror.dll + // would not be guaranteed to be resolve-able because + // for ILPostProcessor we can't assume where Mirror.dll + // is etc. + public bool Weave(AssemblyDefinition assembly, IAssemblyResolver resolver, out bool modified) { - return module.GetTypes().Any(td => td.Namespace == GeneratedCodeNamespace && - td.Name == GeneratedCodeClassName); - } - - static bool Weave(string assName, IEnumerable dependencies) - { - using (DefaultAssemblyResolver asmResolver = new DefaultAssemblyResolver()) - using (CurrentAssembly = AssemblyDefinition.ReadAssembly(assName, new ReaderParameters { ReadWrite = true, ReadSymbols = true, AssemblyResolver = asmResolver })) + WeavingFailed = false; + modified = false; + try { - asmResolver.AddSearchDirectory(Path.GetDirectoryName(assName)); - asmResolver.AddSearchDirectory(Helpers.UnityEngineDllDirectoryName()); - if (dependencies != null) - { - foreach (string path in dependencies) - { - asmResolver.AddSearchDirectory(path); - } - } + CurrentAssembly = assembly; // fix "No writer found for ..." error // https://github.com/vis2k/Mirror/issues/2579 @@ -191,22 +174,31 @@ namespace Mirror.Weaver // again // -> resulting in two GeneratedNetworkCode classes (see ILSpy) // -> the second one wouldn't have all the writer types setup - if (ContainsGeneratedCodeClass(CurrentAssembly.MainModule)) + if (CurrentAssembly.MainModule.ContainsClass(GeneratedCodeNamespace, GeneratedCodeClassName)) { //Log.Warning($"Weaver: skipping {CurrentAssembly.Name} because already weaved"); return true; } - WeaverTypes.SetupTargetTypes(CurrentAssembly); + weaverTypes = new WeaverTypes(CurrentAssembly, Log, ref WeavingFailed); + // weaverTypes are needed for CreateGeneratedCodeClass CreateGeneratedCodeClass(); // WeaverList depends on WeaverTypes setup because it uses Import - WeaveLists = new WeaverLists(); + syncVarAccessLists = new SyncVarAccessLists(); - System.Diagnostics.Stopwatch rwstopwatch = System.Diagnostics.Stopwatch.StartNew(); + // initialize readers & writers with this assembly. + // we need to do this in every Process() call. + // otherwise we would get + // "System.ArgumentException: Member ... is declared in another module and needs to be imported" + // errors when still using the previous module's reader/writer funcs. + writers = new Writers(CurrentAssembly, weaverTypes, GeneratedCodeClass, Log); + readers = new Readers(CurrentAssembly, weaverTypes, GeneratedCodeClass, Log); + + Stopwatch rwstopwatch = Stopwatch.StartNew(); // Need to track modified from ReaderWriterProcessor too because it could find custom read/write functions or create functions for NetworkMessages - bool modified = ReaderWriterProcessor.Process(CurrentAssembly); + modified = ReaderWriterProcessor.Process(CurrentAssembly, resolver, Log, writers, readers, ref WeavingFailed); rwstopwatch.Stop(); Console.WriteLine($"Find all reader and writers took {rwstopwatch.ElapsedMilliseconds} milliseconds"); @@ -222,36 +214,28 @@ namespace Mirror.Weaver if (modified) { - PropertySiteProcessor.Process(moduleDefinition); + SyncVarAttributeAccessReplacer.Process(moduleDefinition, syncVarAccessLists); // add class that holds read/write functions moduleDefinition.Types.Add(GeneratedCodeClass); - ReaderWriterProcessor.InitializeReaderAndWriters(CurrentAssembly); + ReaderWriterProcessor.InitializeReaderAndWriters(CurrentAssembly, weaverTypes, writers, readers, GeneratedCodeClass); - // write to outputDir if specified, otherwise perform in-place write - WriterParameters writeParams = new WriterParameters { WriteSymbols = true }; - CurrentAssembly.Write(writeParams); + // DO NOT WRITE here. + // CompilationFinishedHook writes to the file. + // ILPostProcessor writes to in-memory assembly. + // it depends on the caller. + //CurrentAssembly.Write(new WriterParameters{ WriteSymbols = true }); } - } - return true; - } - - public static bool WeaveAssembly(string assembly, IEnumerable dependencies) - { - WeavingFailed = false; - - try - { - return Weave(assembly, dependencies); + return true; } catch (Exception e) { - Log.Error("Exception :" + e); + Log.Error($"Exception :{e}"); + WeavingFailed = true; return false; } } - } } diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Weaver.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Weaver.cs.meta index ec2cb3e..0ea2dfe 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Weaver.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Weaver.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/WeaverExceptions.cs b/UnityProject/Assets/Mirror/Editor/Weaver/WeaverExceptions.cs index 55556b6..efedd27 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/WeaverExceptions.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/WeaverExceptions.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.Serialization; using Mono.CecilX; namespace Mirror.Weaver @@ -13,13 +14,13 @@ namespace Mirror.Weaver MemberReference = member; } - protected WeaverException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) : base(serializationInfo, streamingContext) {} + protected WeaverException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) {} } [Serializable] public class GenerateWriterException : WeaverException { public GenerateWriterException(string message, MemberReference member) : base(message, member) {} - protected GenerateWriterException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) : base(serializationInfo, streamingContext) {} + protected GenerateWriterException(SerializationInfo serializationInfo, StreamingContext streamingContext) : base(serializationInfo, streamingContext) {} } } diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/WeaverExceptions.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/WeaverExceptions.cs.meta index 4c11527..68643b2 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/WeaverExceptions.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/WeaverExceptions.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/WeaverTypes.cs b/UnityProject/Assets/Mirror/Editor/Weaver/WeaverTypes.cs index 6a13005..173af58 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/WeaverTypes.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/WeaverTypes.cs @@ -1,144 +1,164 @@ using System; using Mono.CecilX; +using UnityEditor; +using UnityEngine; namespace Mirror.Weaver { - public static class WeaverTypes + // not static, because ILPostProcessor is multithreaded + public class WeaverTypes { - public static MethodReference ScriptableObjectCreateInstanceMethod; + public MethodReference ScriptableObjectCreateInstanceMethod; - public static MethodReference NetworkBehaviourDirtyBitsReference; - public static MethodReference GetPooledWriterReference; - public static MethodReference RecycleWriterReference; + public MethodReference NetworkBehaviourDirtyBitsReference; + public MethodReference GetWriterReference; + public MethodReference ReturnWriterReference; - public static MethodReference ReadyConnectionReference; + public MethodReference NetworkClientConnectionReference; - public static MethodReference CmdDelegateConstructor; + public MethodReference RemoteCallDelegateConstructor; - public static MethodReference NetworkServerGetActive; - public static MethodReference NetworkServerGetLocalClientActive; - public static MethodReference NetworkClientGetActive; + public MethodReference NetworkServerGetActive; + public MethodReference NetworkClientGetActive; // custom attribute types - public static MethodReference InitSyncObjectReference; + public MethodReference InitSyncObjectReference; // array segment - public static MethodReference ArraySegmentConstructorReference; + public MethodReference ArraySegmentConstructorReference; + + // Action for SyncVar Hooks + public MethodReference ActionT_T; // syncvar - public static MethodReference syncVarEqualReference; - public static MethodReference syncVarNetworkIdentityEqualReference; - public static MethodReference syncVarGameObjectEqualReference; - public static MethodReference setSyncVarReference; - public static MethodReference setSyncVarHookGuard; - public static MethodReference getSyncVarHookGuard; - public static MethodReference setSyncVarGameObjectReference; - public static MethodReference getSyncVarGameObjectReference; - public static MethodReference setSyncVarNetworkIdentityReference; - public static MethodReference getSyncVarNetworkIdentityReference; - public static MethodReference syncVarNetworkBehaviourEqualReference; - public static MethodReference setSyncVarNetworkBehaviourReference; - public static MethodReference getSyncVarNetworkBehaviourReference; - public static MethodReference registerCommandDelegateReference; - public static MethodReference registerRpcDelegateReference; - public static MethodReference getTypeFromHandleReference; - public static MethodReference logErrorReference; - public static MethodReference logWarningReference; - public static MethodReference sendCommandInternal; - public static MethodReference sendRpcInternal; - public static MethodReference sendTargetRpcInternal; + public MethodReference generatedSyncVarSetter; + public MethodReference generatedSyncVarSetter_GameObject; + public MethodReference generatedSyncVarSetter_NetworkIdentity; + public MethodReference generatedSyncVarSetter_NetworkBehaviour_T; + public MethodReference generatedSyncVarDeserialize; + public MethodReference generatedSyncVarDeserialize_GameObject; + public MethodReference generatedSyncVarDeserialize_NetworkIdentity; + public MethodReference generatedSyncVarDeserialize_NetworkBehaviour_T; + public MethodReference getSyncVarGameObjectReference; + public MethodReference getSyncVarNetworkIdentityReference; + public MethodReference getSyncVarNetworkBehaviourReference; + public MethodReference registerCommandReference; + public MethodReference registerRpcReference; + public MethodReference getTypeFromHandleReference; + public MethodReference logErrorReference; + public MethodReference logWarningReference; + public MethodReference sendCommandInternal; + public MethodReference sendRpcInternal; + public MethodReference sendTargetRpcInternal; - public static MethodReference readNetworkBehaviourGeneric; + public MethodReference readNetworkBehaviourGeneric; - static AssemblyDefinition currentAssembly; + // attributes + public TypeDefinition initializeOnLoadMethodAttribute; + public TypeDefinition runtimeInitializeOnLoadMethodAttribute; - public static TypeReference Import() => Import(typeof(T)); + AssemblyDefinition assembly; - public static TypeReference Import(Type t) => currentAssembly.MainModule.ImportReference(t); + public TypeReference Import() => Import(typeof(T)); - public static void SetupTargetTypes(AssemblyDefinition currentAssembly) + public TypeReference Import(Type t) => assembly.MainModule.ImportReference(t); + + // constructor resolves the types and stores them in fields + public WeaverTypes(AssemblyDefinition assembly, Logger Log, ref bool WeavingFailed) { // system types - WeaverTypes.currentAssembly = currentAssembly; + this.assembly = assembly; TypeReference ArraySegmentType = Import(typeof(ArraySegment<>)); - ArraySegmentConstructorReference = Resolvers.ResolveMethod(ArraySegmentType, currentAssembly, ".ctor"); + ArraySegmentConstructorReference = Resolvers.ResolveMethod(ArraySegmentType, assembly, Log, ".ctor", ref WeavingFailed); - TypeReference ListType = Import(typeof(System.Collections.Generic.List<>)); + TypeReference ActionType = Import(typeof(Action<,>)); + ActionT_T = Resolvers.ResolveMethod(ActionType, assembly, Log, ".ctor", ref WeavingFailed); TypeReference NetworkServerType = Import(typeof(NetworkServer)); - NetworkServerGetActive = Resolvers.ResolveMethod(NetworkServerType, currentAssembly, "get_active"); - NetworkServerGetLocalClientActive = Resolvers.ResolveMethod(NetworkServerType, currentAssembly, "get_localClientActive"); + NetworkServerGetActive = Resolvers.ResolveMethod(NetworkServerType, assembly, Log, "get_active", ref WeavingFailed); TypeReference NetworkClientType = Import(typeof(NetworkClient)); - NetworkClientGetActive = Resolvers.ResolveMethod(NetworkClientType, currentAssembly, "get_active"); + NetworkClientGetActive = Resolvers.ResolveMethod(NetworkClientType, assembly, Log, "get_active", ref WeavingFailed); - TypeReference cmdDelegateReference = Import(); - CmdDelegateConstructor = Resolvers.ResolveMethod(cmdDelegateReference, currentAssembly, ".ctor"); + TypeReference RemoteCallDelegateType = Import(); + RemoteCallDelegateConstructor = Resolvers.ResolveMethod(RemoteCallDelegateType, assembly, Log, ".ctor", ref WeavingFailed); TypeReference NetworkBehaviourType = Import(); - TypeReference RemoteCallHelperType = Import(typeof(RemoteCalls.RemoteCallHelper)); + TypeReference RemoteProcedureCallsType = Import(typeof(RemoteCalls.RemoteProcedureCalls)); - TypeReference ScriptableObjectType = Import(); + TypeReference ScriptableObjectType = Import(); ScriptableObjectCreateInstanceMethod = Resolvers.ResolveMethod( - ScriptableObjectType, currentAssembly, - md => md.Name == "CreateInstance" && md.HasGenericParameters); + ScriptableObjectType, assembly, Log, + md => md.Name == "CreateInstance" && md.HasGenericParameters, + ref WeavingFailed); - NetworkBehaviourDirtyBitsReference = Resolvers.ResolveProperty(NetworkBehaviourType, currentAssembly, "syncVarDirtyBits"); + NetworkBehaviourDirtyBitsReference = Resolvers.ResolveProperty(NetworkBehaviourType, assembly, "syncVarDirtyBits"); TypeReference NetworkWriterPoolType = Import(typeof(NetworkWriterPool)); - GetPooledWriterReference = Resolvers.ResolveMethod(NetworkWriterPoolType, currentAssembly, "GetWriter"); - RecycleWriterReference = Resolvers.ResolveMethod(NetworkWriterPoolType, currentAssembly, "Recycle"); + GetWriterReference = Resolvers.ResolveMethod(NetworkWriterPoolType, assembly, Log, "Get", ref WeavingFailed); + ReturnWriterReference = Resolvers.ResolveMethod(NetworkWriterPoolType, assembly, Log, "Return", ref WeavingFailed); - ReadyConnectionReference = Resolvers.ResolveMethod(NetworkClientType, currentAssembly, "get_readyConnection"); + NetworkClientConnectionReference = Resolvers.ResolveMethod(NetworkClientType, assembly, Log, "get_connection", ref WeavingFailed); - syncVarEqualReference = Resolvers.ResolveMethod(NetworkBehaviourType, currentAssembly, "SyncVarEqual"); - syncVarNetworkIdentityEqualReference = Resolvers.ResolveMethod(NetworkBehaviourType, currentAssembly, "SyncVarNetworkIdentityEqual"); - syncVarGameObjectEqualReference = Resolvers.ResolveMethod(NetworkBehaviourType, currentAssembly, "SyncVarGameObjectEqual"); - setSyncVarReference = Resolvers.ResolveMethod(NetworkBehaviourType, currentAssembly, "SetSyncVar"); - setSyncVarHookGuard = Resolvers.ResolveMethod(NetworkBehaviourType, currentAssembly, "setSyncVarHookGuard"); - getSyncVarHookGuard = Resolvers.ResolveMethod(NetworkBehaviourType, currentAssembly, "getSyncVarHookGuard"); + generatedSyncVarSetter = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GeneratedSyncVarSetter", ref WeavingFailed); + generatedSyncVarSetter_GameObject = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GeneratedSyncVarSetter_GameObject", ref WeavingFailed); + generatedSyncVarSetter_NetworkIdentity = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GeneratedSyncVarSetter_NetworkIdentity", ref WeavingFailed); + generatedSyncVarSetter_NetworkBehaviour_T = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GeneratedSyncVarSetter_NetworkBehaviour", ref WeavingFailed); - setSyncVarGameObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, currentAssembly, "SetSyncVarGameObject"); - getSyncVarGameObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, currentAssembly, "GetSyncVarGameObject"); - setSyncVarNetworkIdentityReference = Resolvers.ResolveMethod(NetworkBehaviourType, currentAssembly, "SetSyncVarNetworkIdentity"); - getSyncVarNetworkIdentityReference = Resolvers.ResolveMethod(NetworkBehaviourType, currentAssembly, "GetSyncVarNetworkIdentity"); - syncVarNetworkBehaviourEqualReference = Resolvers.ResolveMethod(NetworkBehaviourType, currentAssembly, "SyncVarNetworkBehaviourEqual"); - setSyncVarNetworkBehaviourReference = Resolvers.ResolveMethod(NetworkBehaviourType, currentAssembly, "SetSyncVarNetworkBehaviour"); - getSyncVarNetworkBehaviourReference = Resolvers.ResolveMethod(NetworkBehaviourType, currentAssembly, "GetSyncVarNetworkBehaviour"); + generatedSyncVarDeserialize_GameObject = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GeneratedSyncVarDeserialize_GameObject", ref WeavingFailed); + generatedSyncVarDeserialize = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GeneratedSyncVarDeserialize", ref WeavingFailed); + generatedSyncVarDeserialize_NetworkIdentity = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GeneratedSyncVarDeserialize_NetworkIdentity", ref WeavingFailed); + generatedSyncVarDeserialize_NetworkBehaviour_T = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GeneratedSyncVarDeserialize_NetworkBehaviour", ref WeavingFailed); - registerCommandDelegateReference = Resolvers.ResolveMethod(RemoteCallHelperType, currentAssembly, "RegisterCommandDelegate"); - registerRpcDelegateReference = Resolvers.ResolveMethod(RemoteCallHelperType, currentAssembly, "RegisterRpcDelegate"); + getSyncVarGameObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GetSyncVarGameObject", ref WeavingFailed); + getSyncVarNetworkIdentityReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GetSyncVarNetworkIdentity", ref WeavingFailed); + getSyncVarNetworkBehaviourReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "GetSyncVarNetworkBehaviour", ref WeavingFailed); + + registerCommandReference = Resolvers.ResolveMethod(RemoteProcedureCallsType, assembly, Log, "RegisterCommand", ref WeavingFailed); + registerRpcReference = Resolvers.ResolveMethod(RemoteProcedureCallsType, assembly, Log, "RegisterRpc", ref WeavingFailed); TypeReference unityDebug = Import(typeof(UnityEngine.Debug)); // these have multiple methods with same name, so need to check parameters too - logErrorReference = Resolvers.ResolveMethod(unityDebug, currentAssembly, (md) => - { - return md.Name == "LogError" && - md.Parameters.Count == 1 && - md.Parameters[0].ParameterType.FullName == typeof(object).FullName; - }); - logWarningReference = Resolvers.ResolveMethod(unityDebug, currentAssembly, (md) => - { - return md.Name == "LogWarning" && - md.Parameters.Count == 1 && - md.Parameters[0].ParameterType.FullName == typeof(object).FullName; + logErrorReference = Resolvers.ResolveMethod(unityDebug, assembly, Log, md => + md.Name == "LogError" && + md.Parameters.Count == 1 && + md.Parameters[0].ParameterType.FullName == typeof(object).FullName, + ref WeavingFailed); - }); + logWarningReference = Resolvers.ResolveMethod(unityDebug, assembly, Log, md => + md.Name == "LogWarning" && + md.Parameters.Count == 1 && + md.Parameters[0].ParameterType.FullName == typeof(object).FullName, + ref WeavingFailed); TypeReference typeType = Import(typeof(Type)); - getTypeFromHandleReference = Resolvers.ResolveMethod(typeType, currentAssembly, "GetTypeFromHandle"); - sendCommandInternal = Resolvers.ResolveMethod(NetworkBehaviourType, currentAssembly, "SendCommandInternal"); - sendRpcInternal = Resolvers.ResolveMethod(NetworkBehaviourType, currentAssembly, "SendRPCInternal"); - sendTargetRpcInternal = Resolvers.ResolveMethod(NetworkBehaviourType, currentAssembly, "SendTargetRPCInternal"); + getTypeFromHandleReference = Resolvers.ResolveMethod(typeType, assembly, Log, "GetTypeFromHandle", ref WeavingFailed); + sendCommandInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendCommandInternal", ref WeavingFailed); + sendRpcInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendRPCInternal", ref WeavingFailed); + sendTargetRpcInternal = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "SendTargetRPCInternal", ref WeavingFailed); - InitSyncObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, currentAssembly, "InitSyncObject"); + InitSyncObjectReference = Resolvers.ResolveMethod(NetworkBehaviourType, assembly, Log, "InitSyncObject", ref WeavingFailed); TypeReference readerExtensions = Import(typeof(NetworkReaderExtensions)); - readNetworkBehaviourGeneric = Resolvers.ResolveMethod(readerExtensions, currentAssembly, (md => + readNetworkBehaviourGeneric = Resolvers.ResolveMethod(readerExtensions, assembly, Log, (md => { return md.Name == nameof(NetworkReaderExtensions.ReadNetworkBehaviour) && - md.HasGenericParameters; - })); + md.HasGenericParameters; + }), + ref WeavingFailed); + + // [InitializeOnLoadMethod] + // 'UnityEditor' is not available in builds. + // we can only import this attribute if we are in an Editor assembly. + if (Helpers.IsEditorAssembly(assembly)) + { + TypeReference initializeOnLoadMethodAttributeRef = Import(typeof(InitializeOnLoadMethodAttribute)); + initializeOnLoadMethodAttribute = initializeOnLoadMethodAttributeRef.Resolve(); + } + + // [RuntimeInitializeOnLoadMethod] + TypeReference runtimeInitializeOnLoadMethodAttributeRef = Import(typeof(RuntimeInitializeOnLoadMethodAttribute)); + runtimeInitializeOnLoadMethodAttribute = runtimeInitializeOnLoadMethodAttributeRef.Resolve(); } } } diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/WeaverTypes.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/WeaverTypes.cs.meta index e15e589..d71c33f 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/WeaverTypes.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/WeaverTypes.cs.meta @@ -1,3 +1,3 @@ -fileFormatVersion: 2 +fileFormatVersion: 2 guid: 2585961bf7fe4c10a9143f4087efdf6f -timeCreated: 1596486854 +timeCreated: 1596486854 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Writers.cs b/UnityProject/Assets/Mirror/Editor/Weaver/Writers.cs index c813b66..51d514e 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Writers.cs +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Writers.cs @@ -2,71 +2,78 @@ using System; using System.Collections.Generic; using Mono.CecilX; using Mono.CecilX.Cil; +// to use Mono.CecilX.Rocks here, we need to 'override references' in the +// Unity.Mirror.CodeGen assembly definition file in the Editor, and add CecilX.Rocks. +// otherwise we get an unknown import exception. using Mono.CecilX.Rocks; namespace Mirror.Weaver { - public static class Writers + // not static, because ILPostProcessor is multithreaded + public class Writers { - static Dictionary writeFuncs; + // Writers are only for this assembly. + // can't be used from another assembly, otherwise we will get: + // "System.ArgumentException: Member ... is declared in another module and needs to be imported" + AssemblyDefinition assembly; + WeaverTypes weaverTypes; + TypeDefinition GeneratedCodeClass; + Logger Log; - public static void Init() + Dictionary writeFuncs = + new Dictionary(new TypeReferenceComparer()); + + public Writers(AssemblyDefinition assembly, WeaverTypes weaverTypes, TypeDefinition GeneratedCodeClass, Logger Log) { - writeFuncs = new Dictionary(new TypeReferenceComparer()); + this.assembly = assembly; + this.weaverTypes = weaverTypes; + this.GeneratedCodeClass = GeneratedCodeClass; + this.Log = Log; } - public static void Register(TypeReference dataType, MethodReference methodReference) + public void Register(TypeReference dataType, MethodReference methodReference) { if (writeFuncs.ContainsKey(dataType)) { // TODO enable this again later. // Writer has some obsolete functions that were renamed. // Don't want weaver warnings for all of them. - //Weaver.Warning($"Registering a Write method for {dataType.FullName} when one already exists", methodReference); + //Log.Warning($"Registering a Write method for {dataType.FullName} when one already exists", methodReference); } // we need to import type when we Initialize Writers so import here in case it is used anywhere else - TypeReference imported = Weaver.CurrentAssembly.MainModule.ImportReference(dataType); + TypeReference imported = assembly.MainModule.ImportReference(dataType); writeFuncs[imported] = methodReference; } - static void RegisterWriteFunc(TypeReference typeReference, MethodDefinition newWriterFunc) + void RegisterWriteFunc(TypeReference typeReference, MethodDefinition newWriterFunc) { Register(typeReference, newWriterFunc); - - Weaver.GeneratedCodeClass.Methods.Add(newWriterFunc); + GeneratedCodeClass.Methods.Add(newWriterFunc); } - /// - /// Finds existing writer for type, if non exists trys to create one - /// This method is recursive - /// - /// - /// Returns or null - public static MethodReference GetWriteFunc(TypeReference variable) + // Finds existing writer for type, if non exists trys to create one + public MethodReference GetWriteFunc(TypeReference variable, ref bool WeavingFailed) { if (writeFuncs.TryGetValue(variable, out MethodReference foundFunc)) - { return foundFunc; - } - else + + // this try/catch will be removed in future PR and make `GetWriteFunc` throw instead + try { - // this try/catch will be removed in future PR and make `GetWriteFunc` throw instead - try - { - TypeReference importedVariable = Weaver.CurrentAssembly.MainModule.ImportReference(variable); - return GenerateWriter(importedVariable); - } - catch (GenerateWriterException e) - { - Weaver.Error(e.Message, e.MemberReference); - return null; - } + TypeReference importedVariable = assembly.MainModule.ImportReference(variable); + return GenerateWriter(importedVariable, ref WeavingFailed); + } + catch (GenerateWriterException e) + { + Log.Error(e.Message, e.MemberReference); + WeavingFailed = true; + return null; } } - /// Throws when writer could not be generated for type - static MethodReference GenerateWriter(TypeReference variableReference) + //Throws GenerateWriterException when writer could not be generated for type + MethodReference GenerateWriter(TypeReference variableReference, ref bool WeavingFailed) { if (variableReference.IsByReference) { @@ -83,13 +90,13 @@ namespace Mirror.Weaver throw new GenerateWriterException($"{variableReference.Name} is an unsupported type. Multidimensional arrays are not supported", variableReference); } TypeReference elementType = variableReference.GetElementType(); - return GenerateCollectionWriter(variableReference, elementType, nameof(NetworkWriterExtensions.WriteArray)); + return GenerateCollectionWriter(variableReference, elementType, nameof(NetworkWriterExtensions.WriteArray), ref WeavingFailed); } if (variableReference.Resolve()?.IsEnum ?? false) { // serialize enum as their base type - return GenerateEnumWriteFunc(variableReference); + return GenerateEnumWriteFunc(variableReference, ref WeavingFailed); } // check for collections @@ -98,14 +105,14 @@ namespace Mirror.Weaver GenericInstanceType genericInstance = (GenericInstanceType)variableReference; TypeReference elementType = genericInstance.GenericArguments[0]; - return GenerateCollectionWriter(variableReference, elementType, nameof(NetworkWriterExtensions.WriteArraySegment)); + return GenerateCollectionWriter(variableReference, elementType, nameof(NetworkWriterExtensions.WriteArraySegment), ref WeavingFailed); } if (variableReference.Is(typeof(List<>))) { GenericInstanceType genericInstance = (GenericInstanceType)variableReference; TypeReference elementType = genericInstance.GenericArguments[0]; - return GenerateCollectionWriter(variableReference, elementType, nameof(NetworkWriterExtensions.WriteList)); + return GenerateCollectionWriter(variableReference, elementType, nameof(NetworkWriterExtensions.WriteList), ref WeavingFailed); } if (variableReference.IsDerivedFrom()) @@ -145,13 +152,13 @@ namespace Mirror.Weaver } // generate writer for class/struct - return GenerateClassOrStructWriterFunction(variableReference); + return GenerateClassOrStructWriterFunction(variableReference, ref WeavingFailed); } - static MethodReference GetNetworkBehaviourWriter(TypeReference variableReference) + MethodReference GetNetworkBehaviourWriter(TypeReference variableReference) { // all NetworkBehaviours can use the same write function - if (writeFuncs.TryGetValue(WeaverTypes.Import(), out MethodReference func)) + if (writeFuncs.TryGetValue(weaverTypes.Import(), out MethodReference func)) { // register function so it is added to writer // use Register instead of RegisterWriteFunc because this is not a generated function @@ -166,13 +173,13 @@ namespace Mirror.Weaver } } - static MethodDefinition GenerateEnumWriteFunc(TypeReference variable) + MethodDefinition GenerateEnumWriteFunc(TypeReference variable, ref bool WeavingFailed) { MethodDefinition writerFunc = GenerateWriterFunc(variable); ILProcessor worker = writerFunc.Body.GetILProcessor(); - MethodReference underlyingWriter = GetWriteFunc(variable.Resolve().GetEnumUnderlyingType()); + MethodReference underlyingWriter = GetWriteFunc(variable.Resolve().GetEnumUnderlyingType(), ref WeavingFailed); worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldarg_1); @@ -182,17 +189,17 @@ namespace Mirror.Weaver return writerFunc; } - static MethodDefinition GenerateWriterFunc(TypeReference variable) + MethodDefinition GenerateWriterFunc(TypeReference variable) { - string functionName = "_Write_" + variable.FullName; + string functionName = $"_Write_{variable.FullName}"; // create new writer for this type MethodDefinition writerFunc = new MethodDefinition(functionName, MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, - WeaverTypes.Import(typeof(void))); + weaverTypes.Import(typeof(void))); - writerFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, WeaverTypes.Import())); + writerFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, weaverTypes.Import())); writerFunc.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, variable)); writerFunc.Body.InitLocals = true; @@ -200,23 +207,23 @@ namespace Mirror.Weaver return writerFunc; } - static MethodDefinition GenerateClassOrStructWriterFunction(TypeReference variable) + MethodDefinition GenerateClassOrStructWriterFunction(TypeReference variable, ref bool WeavingFailed) { MethodDefinition writerFunc = GenerateWriterFunc(variable); ILProcessor worker = writerFunc.Body.GetILProcessor(); if (!variable.Resolve().IsValueType) - WriteNullCheck(worker); + WriteNullCheck(worker, ref WeavingFailed); - if (!WriteAllFields(variable, worker)) + if (!WriteAllFields(variable, worker, ref WeavingFailed)) return null; worker.Emit(OpCodes.Ret); return writerFunc; } - static void WriteNullCheck(ILProcessor worker) + void WriteNullCheck(ILProcessor worker, ref bool WeavingFailed) { // if (value == null) // { @@ -230,34 +237,27 @@ namespace Mirror.Weaver worker.Emit(OpCodes.Brtrue, labelNotNull); worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldc_I4_0); - worker.Emit(OpCodes.Call, GetWriteFunc(WeaverTypes.Import())); + worker.Emit(OpCodes.Call, GetWriteFunc(weaverTypes.Import(), ref WeavingFailed)); worker.Emit(OpCodes.Ret); worker.Append(labelNotNull); // write.WriteBoolean(true); worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldc_I4_1); - worker.Emit(OpCodes.Call, GetWriteFunc(WeaverTypes.Import())); + worker.Emit(OpCodes.Call, GetWriteFunc(weaverTypes.Import(), ref WeavingFailed)); } - /// - /// Find all fields in type and write them - /// - /// - /// - /// false if fail - static bool WriteAllFields(TypeReference variable, ILProcessor worker) + // Find all fields in type and write them + bool WriteAllFields(TypeReference variable, ILProcessor worker, ref bool WeavingFailed) { - uint fields = 0; foreach (FieldDefinition field in variable.FindAllPublicFields()) { - MethodReference writeFunc = GetWriteFunc(field.FieldType); + MethodReference writeFunc = GetWriteFunc(field.FieldType, ref WeavingFailed); // need this null check till later PR when GetWriteFunc throws exception instead if (writeFunc == null) { return false; } - FieldReference fieldRef = Weaver.CurrentAssembly.MainModule.ImportReference(field); + FieldReference fieldRef = assembly.MainModule.ImportReference(field); - fields++; worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldarg_1); worker.Emit(OpCodes.Ldfld, fieldRef); @@ -267,24 +267,25 @@ namespace Mirror.Weaver return true; } - static MethodDefinition GenerateCollectionWriter(TypeReference variable, TypeReference elementType, string writerFunction) + MethodDefinition GenerateCollectionWriter(TypeReference variable, TypeReference elementType, string writerFunction, ref bool WeavingFailed) { MethodDefinition writerFunc = GenerateWriterFunc(variable); - MethodReference elementWriteFunc = GetWriteFunc(elementType); - MethodReference intWriterFunc = GetWriteFunc(WeaverTypes.Import()); + MethodReference elementWriteFunc = GetWriteFunc(elementType, ref WeavingFailed); + MethodReference intWriterFunc = GetWriteFunc(weaverTypes.Import(), ref WeavingFailed); // need this null check till later PR when GetWriteFunc throws exception instead if (elementWriteFunc == null) { - Weaver.Error($"Cannot generate writer for {variable}. Use a supported type or provide a custom writer", variable); + Log.Error($"Cannot generate writer for {variable}. Use a supported type or provide a custom writer", variable); + WeavingFailed = true; return writerFunc; } - ModuleDefinition module = Weaver.CurrentAssembly.MainModule; + ModuleDefinition module = assembly.MainModule; TypeReference readerExtensions = module.ImportReference(typeof(NetworkWriterExtensions)); - MethodReference collectionWriter = Resolvers.ResolveMethod(readerExtensions, Weaver.CurrentAssembly, writerFunction); + MethodReference collectionWriter = Resolvers.ResolveMethod(readerExtensions, assembly, Log, writerFunction, ref WeavingFailed); GenericInstanceMethod methodRef = new GenericInstanceMethod(collectionWriter); methodRef.GenericArguments.Add(elementType); @@ -303,13 +304,10 @@ namespace Mirror.Weaver return writerFunc; } - /// - /// Save a delegate for each one of the writers into - /// - /// - internal static void InitializeWriters(ILProcessor worker) + // Save a delegate for each one of the writers into Writer{T}.write + internal void InitializeWriters(ILProcessor worker) { - ModuleDefinition module = Weaver.CurrentAssembly.MainModule; + ModuleDefinition module = assembly.MainModule; TypeReference genericWriterClassRef = module.ImportReference(typeof(Writer<>)); @@ -328,15 +326,14 @@ namespace Mirror.Weaver worker.Emit(OpCodes.Ldnull); worker.Emit(OpCodes.Ldftn, writeFunc); GenericInstanceType actionGenericInstance = actionRef.MakeGenericInstanceType(networkWriterRef, targetType); - MethodReference actionRefInstance = actionConstructorRef.MakeHostInstanceGeneric(actionGenericInstance); + MethodReference actionRefInstance = actionConstructorRef.MakeHostInstanceGeneric(assembly.MainModule, actionGenericInstance); worker.Emit(OpCodes.Newobj, actionRefInstance); // save it in Writer.write GenericInstanceType genericInstance = genericWriterClassRef.MakeGenericInstanceType(targetType); - FieldReference specializedField = fieldRef.SpecializeField(genericInstance); + FieldReference specializedField = fieldRef.SpecializeField(assembly.MainModule, genericInstance); worker.Emit(OpCodes.Stsfld, specializedField); } } - } } diff --git a/UnityProject/Assets/Mirror/Editor/Weaver/Writers.cs.meta b/UnityProject/Assets/Mirror/Editor/Weaver/Writers.cs.meta index a2f931f..3769f7f 100644 --- a/UnityProject/Assets/Mirror/Editor/Weaver/Writers.cs.meta +++ b/UnityProject/Assets/Mirror/Editor/Weaver/Writers.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples.meta b/UnityProject/Assets/Mirror/Examples.meta index f02aca2..b594a81 100644 --- a/UnityProject/Assets/Mirror/Examples.meta +++ b/UnityProject/Assets/Mirror/Examples.meta @@ -3,6 +3,6 @@ guid: a08b1f591326642d1b140fc818c9c6b1 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Core.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels.meta similarity index 77% rename from UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Core.meta rename to UnityProject/Assets/Mirror/Examples/AdditiveLevels.meta index 2b3246b..d8d6382 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Core.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e03829cb9e647274db0f6a7b8b1b757b +guid: c2da9e7acc6ad1648b0ff0849ef4c283 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials.meta new file mode 100644 index 0000000..7a5d3cd --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 60c31680133898d43822d2d9eae56494 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/CubeSphere.mat b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/CubeSphere.mat new file mode 100644 index 0000000..6531132 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/CubeSphere.mat @@ -0,0 +1,77 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: CubeSphere + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: _GLOSSYREFLECTIONS_OFF _SPECULARHIGHLIGHTS_OFF + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 1 + - _GlossyReflections: 0 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 0 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0, g: 0, b: 0, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/CubeSphere.mat.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/CubeSphere.mat.meta new file mode 100644 index 0000000..1bfa547 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/CubeSphere.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 38950807c6bf6454dbef567827b770b6 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Ground.mat b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Ground.mat new file mode 100644 index 0000000..7dd07ac --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Ground.mat @@ -0,0 +1,77 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Ground + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0.027055884, g: 0.09279272, b: 0.3018868, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Ground.mat.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Ground.mat.meta new file mode 100644 index 0000000..9541526 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Ground.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 20d755ab53045e545ab0b6e59c710ed9 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Player.mat b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Player.mat new file mode 100644 index 0000000..a394fe1 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Player.mat @@ -0,0 +1,77 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Player + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: _GLOSSYREFLECTIONS_OFF _SPECULARHIGHLIGHTS_OFF + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 1 + - _GlossyReflections: 0 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 0 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0, g: 0, b: 0, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Player.mat.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Player.mat.meta new file mode 100644 index 0000000..281bb03 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Player.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6b7039502d1528a4db29c4d43d159b01 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Portal.mat b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Portal.mat new file mode 100644 index 0000000..f0d5fce --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Portal.mat @@ -0,0 +1,78 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Portal + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: _ALPHAPREMULTIPLY_ON _GLOSSYREFLECTIONS_OFF _SPECULARHIGHLIGHTS_OFF + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: 3000 + stringTagMap: + RenderType: Transparent + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 10 + - _GlossMapScale: 1 + - _Glossiness: 1 + - _GlossyReflections: 0 + - _Metallic: 0 + - _Mode: 3 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 0 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 0 + m_Colors: + - _Color: {r: 0.54901963, g: 0.54901963, b: 1, a: 0.23529412} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Portal.mat.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Portal.mat.meta new file mode 100644 index 0000000..989054f --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Portal.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3500dce637708024da2f2e45e4f71cd3 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Skybox.mat b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Skybox.mat new file mode 100644 index 0000000..7d59ae9 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Skybox.mat @@ -0,0 +1,104 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Skybox + m_Shader: {fileID: 104, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BackTex: + m_Texture: {fileID: 2800000, guid: 1566af6e663684dbd85aa5fb32fd9148, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DownTex: + m_Texture: {fileID: 2800000, guid: 9ffeeee1accdc4b4faf2b3e27b226340, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _FrontTex: + m_Texture: {fileID: 2800000, guid: ca928ef0e269448ba82388eb41d48544, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _LeftTex: + m_Texture: {fileID: 2800000, guid: e5a0eeef6a2514a78b267405d686fe09, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _RightTex: + m_Texture: {fileID: 2800000, guid: 4c8911efa34c845f28fbac86385a1b41, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _UpTex: + m_Texture: {fileID: 2800000, guid: bd9ad886b003944169e7ce1286756940, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _Exposure: 1 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _Rotation: 0 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _Tint: {r: 0.5, g: 0.5, b: 0.5, a: 0.5} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Skybox.mat.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Skybox.mat.meta new file mode 100644 index 0000000..53c5d15 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/Skybox.mat.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 81f714daf7144784b8a2f42f1cd30927 +timeCreated: 1497127647 +licenseType: Store +NativeFormatImporter: + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/StartPoint.mat b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/StartPoint.mat new file mode 100644 index 0000000..7ec88f5 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/StartPoint.mat @@ -0,0 +1,78 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: StartPoint + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: _ALPHAPREMULTIPLY_ON _GLOSSYREFLECTIONS_OFF _SPECULARHIGHLIGHTS_OFF + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: 3000 + stringTagMap: + RenderType: Transparent + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 10 + - _GlossMapScale: 1 + - _Glossiness: 1 + - _GlossyReflections: 0 + - _Metallic: 0 + - _Mode: 3 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 0 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 0 + m_Colors: + - _Color: {r: 1, g: 0.6236605, b: 0, a: 0.5372549} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/StartPoint.mat.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/StartPoint.mat.meta new file mode 100644 index 0000000..5f7c126 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Materials/StartPoint.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ec3b6ac9651c31248bcfbf695e6f597a +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs.meta new file mode 100644 index 0000000..0bb6a54 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d9d4b9adc9613a04fb715c64d3d2dcf8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Cube.prefab b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Cube.prefab new file mode 100644 index 0000000..a250054 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Cube.prefab @@ -0,0 +1,131 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &3181899219042528418 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3181899219042528419} + - component: {fileID: 3181899219042528430} + - component: {fileID: 3181899219042528417} + - component: {fileID: 3181899219042528416} + - component: {fileID: -1828993248307539358} + - component: {fileID: 96969086124788804} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3181899219042528419 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3181899219042528418} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.5, 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 &3181899219042528430 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3181899219042528418} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &3181899219042528417 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3181899219042528418} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 38950807c6bf6454dbef567827b770b6, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!65 &3181899219042528416 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3181899219042528418} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &-1828993248307539358 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3181899219042528418} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3} + m_Name: + m_EditorClassIdentifier: + sceneId: 0 + serverOnly: 0 + visible: 0 + m_AssetId: 7c6a0a278eba11e44b9a86cd4da603df + hasSpawned: 0 +--- !u!114 &96969086124788804 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3181899219042528418} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a91a718a70d01b347b75cb768a6f1a92, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 + color: + serializedVersion: 2 + rgba: 4278190080 diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Cube.prefab.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Cube.prefab.meta new file mode 100644 index 0000000..e5705b1 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Cube.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7c6a0a278eba11e44b9a86cd4da603df +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Plane.prefab b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Plane.prefab new file mode 100644 index 0000000..9270e1f --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Plane.prefab @@ -0,0 +1,152 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &7349408984269008055 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7349408984269008052} + - component: {fileID: 7349408984269008059} + - component: {fileID: 7349408984269008058} + - component: {fileID: 7349408984269008053} + - component: {fileID: 7349408984269008063} + - component: {fileID: 7349408984269008062} + - component: {fileID: 7349408984269008057} + - component: {fileID: 7349408984269008056} + m_Layer: 0 + m_Name: Plane + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7349408984269008052 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7349408984269008055} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 50, y: 1, z: 50} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7349408984269008059 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7349408984269008055} + m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7349408984269008058 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7349408984269008055} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 20d755ab53045e545ab0b6e59c710ed9, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!64 &7349408984269008053 +MeshCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7349408984269008055} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 4 + m_Convex: 0 + m_CookingOptions: 30 + m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} +--- !u!65 &7349408984269008063 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7349408984269008055} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 20, z: 0.05} + m_Center: {x: 0, y: 10, z: -0.5} +--- !u!65 &7349408984269008062 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7349408984269008055} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 0.05, y: 20, z: 1} + m_Center: {x: -0.5, y: 10, z: 0} +--- !u!65 &7349408984269008057 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7349408984269008055} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 0.05, y: 20, z: 1} + m_Center: {x: 0.5, y: 10, z: 0} +--- !u!65 &7349408984269008056 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7349408984269008055} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 20, z: 0.05} + m_Center: {x: 0, y: 10, z: 0.5} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Plane.prefab.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Plane.prefab.meta new file mode 100644 index 0000000..98f04d3 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Plane.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4fdcc4aa5c669b348a5954a92f5e8e89 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Player.prefab b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Player.prefab new file mode 100644 index 0000000..340e315 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Player.prefab @@ -0,0 +1,355 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &1724664263041697580 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7513987664611104733} + - component: {fileID: 3866048407219963700} + - component: {fileID: 6667391912482377964} + m_Layer: 0 + m_Name: Visor + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7513987664611104733 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1724664263041697580} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0.39999998, z: 0.5} + m_LocalScale: {x: 0.5, y: 0.1, z: 0.2} + m_Children: [] + m_Father: {fileID: 962190737825349125} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &3866048407219963700 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1724664263041697580} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &6667391912482377964 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1724664263041697580} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 4294967295 + 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_ReceiveGI: 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!1 &5620029719931856626 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 962190737825349125} + - component: {fileID: 8168211270351413936} + - component: {fileID: 5601443051240418659} + m_Layer: 0 + m_Name: Capsule + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &962190737825349125 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5620029719931856626} + 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: + - {fileID: 7513987664611104733} + m_Father: {fileID: 464867598898769114} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &8168211270351413936 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5620029719931856626} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &5601443051240418659 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5620029719931856626} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 6b7039502d1528a4db29c4d43d159b01, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!1 &7619140271685878370 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 464867598898769114} + - component: {fileID: 5674344380471455553} + - component: {fileID: -4914236621144254103} + - component: {fileID: -903079073849018483} + - component: {fileID: -1734969889957956087} + - component: {fileID: 5462452979215896872} + - component: {fileID: -2422551836510166228} + - component: {fileID: 1763152038191860132} + - component: {fileID: 1747637013460943425} + m_Layer: 8 + m_Name: Player + m_TagString: Player + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &464867598898769114 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7619140271685878370} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1.08, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 962190737825349125} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &5674344380471455553 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7619140271685878370} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3} + m_Name: + m_EditorClassIdentifier: + sceneId: 0 + serverOnly: 0 + visible: 0 + m_AssetId: 9f0094c1325091d42a558274b947221f + hasSpawned: 0 +--- !u!114 &-4914236621144254103 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7619140271685878370} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2f74aedd71d9a4f55b3ce499326d45fb, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 + clientAuthority: 1 + sendInterval: 0.05 + syncPosition: 1 + syncRotation: 1 + syncScale: 0 + interpolatePosition: 1 + interpolateRotation: 1 + interpolateScale: 0 + bufferTimeMultiplier: 1 + bufferSizeLimit: 64 + catchupThreshold: 4 + catchupMultiplier: 0.1 + onlySyncOnChange: 1 + bufferResetMultiplier: 5 + positionSensitivity: 0.01 + rotationSensitivity: 0.01 + scaleSensitivity: 0.01 + showGizmos: 0 + showOverlay: 0 + overlayColor: {r: 0, g: 0, b: 0, a: 0.5} +--- !u!114 &-903079073849018483 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7619140271685878370} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 22976424f775a0f4a8531e6713ff6de2, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 +--- !u!114 &-1734969889957956087 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7619140271685878370} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 05e10150710dde14b83d3c8f5aa853c2, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 + characterController: {fileID: 5462452979215896872} + moveSpeed: 8 + turnSensitivity: 5 + maxTurnSpeed: 100 + horizontal: 0 + vertical: 0 + turn: 0 + jumpSpeed: 0 + isGrounded: 1 + isFalling: 0 + velocity: {x: 0, y: 0, z: 0} +--- !u!143 &5462452979215896872 +CharacterController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7619140271685878370} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 0 + serializedVersion: 2 + m_Height: 2 + m_Radius: 0.5 + m_SlopeLimit: 45 + m_StepOffset: 0.3 + m_SkinWidth: 0.08 + m_MinMoveDistance: 0.001 + m_Center: {x: 0, y: 0, z: 0} +--- !u!136 &-2422551836510166228 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7619140271685878370} + 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!54 &1763152038191860132 +Rigidbody: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7619140271685878370} + serializedVersion: 2 + m_Mass: 1 + m_Drag: 0 + m_AngularDrag: 0.05 + m_UseGravity: 1 + m_IsKinematic: 1 + m_Interpolate: 0 + m_Constraints: 0 + m_CollisionDetection: 0 +--- !u!114 &1747637013460943425 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7619140271685878370} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a91a718a70d01b347b75cb768a6f1a92, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 + color: + serializedVersion: 2 + rgba: 4278190080 diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Player.prefab.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Player.prefab.meta new file mode 100644 index 0000000..9ec1c9e --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Player.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9f0094c1325091d42a558274b947221f +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Portal.prefab b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Portal.prefab new file mode 100644 index 0000000..36fc9be --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Portal.prefab @@ -0,0 +1,319 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &1098173225717622925 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1098173225717622921} + - component: {fileID: 1098173225717622922} + - component: {fileID: 1098173225717622923} + - component: {fileID: 1098173225717622924} + - component: {fileID: 3141292696673982546} + - component: {fileID: 5948271423698091598} + m_Layer: 9 + m_Name: Portal + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1098173225717622921 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1098173225717622925} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: 0} + m_LocalScale: {x: 2, y: 2, z: 2} + m_Children: + - {fileID: 1355348187805494562} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &1098173225717622922 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1098173225717622925} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &1098173225717622923 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1098173225717622925} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 3500dce637708024da2f2e45e4f71cd3, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!136 &1098173225717622924 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1098173225717622925} + m_Material: {fileID: 0} + m_IsTrigger: 1 + m_Enabled: 1 + m_Radius: 0.2 + m_Height: 1 + m_Direction: 1 + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &3141292696673982546 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1098173225717622925} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3} + m_Name: + m_EditorClassIdentifier: + sceneId: 0 + serverOnly: 0 + visible: 0 + m_AssetId: c624c75494b4d7d4086b9212f897e56a + hasSpawned: 0 +--- !u!114 &5948271423698091598 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1098173225717622925} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0e680878006965146a8f9d85834c4d1c, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 + destinationScene: + startPosition: {x: 0, y: 0, z: 0} + label: {fileID: 5446595135713311426} + labelText: +--- !u!1 &5961932215084527574 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1355348187805494562} + - component: {fileID: 5428053421152709616} + - component: {fileID: 5446595135713311426} + - component: {fileID: 3243959486819493908} + m_Layer: 9 + m_Name: Label_TMP + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1355348187805494562 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5961932215084527574} + 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: 1098173225717622921} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 1.5} + m_SizeDelta: {x: 20, y: 5} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &5428053421152709616 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5961932215084527574} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!114 &5446595135713311426 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5961932215084527574} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Sub Level 2 + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4288619804 + m_fontColor: {r: 0.108668566, g: 0.14359462, b: 0.6226415, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 4 + m_fontSizeBase: 4 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 5 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 1 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 5428053421152709616} + m_maskType: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 +--- !u!114 &3243959486819493908 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5961932215084527574} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: cc58300ee45438a418d9e32957fdc0c0, type: 3} + m_Name: + m_EditorClassIdentifier: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Portal.prefab.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Portal.prefab.meta new file mode 100644 index 0000000..d43ce42 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Portal.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c624c75494b4d7d4086b9212f897e56a +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Sphere.prefab b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Sphere.prefab new file mode 100644 index 0000000..fdc50b4 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Sphere.prefab @@ -0,0 +1,131 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &2188792038427236743 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2188792038427236742} + - component: {fileID: 2188792038427236747} + - component: {fileID: 2188792038427236744} + - component: {fileID: 2188792038427236745} + - component: {fileID: -5172514306435102607} + - component: {fileID: 1758063572567377699} + m_Layer: 0 + m_Name: Sphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2188792038427236742 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2188792038427236743} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.5, 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 &2188792038427236747 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2188792038427236743} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &2188792038427236744 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2188792038427236743} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 38950807c6bf6454dbef567827b770b6, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!135 &2188792038427236745 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2188792038427236743} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &-5172514306435102607 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2188792038427236743} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3} + m_Name: + m_EditorClassIdentifier: + sceneId: 0 + serverOnly: 0 + visible: 0 + m_AssetId: e588080aa542be54d9ca9d5c734dc9ee + hasSpawned: 0 +--- !u!114 &1758063572567377699 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2188792038427236743} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a91a718a70d01b347b75cb768a6f1a92, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 + color: + serializedVersion: 2 + rgba: 4278190080 diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Sphere.prefab.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Sphere.prefab.meta new file mode 100644 index 0000000..95d16da --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/Sphere.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e588080aa542be54d9ca9d5c734dc9ee +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/StartPoint.prefab b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/StartPoint.prefab new file mode 100644 index 0000000..dc890d6 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/StartPoint.prefab @@ -0,0 +1,81 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &7582855647636897216 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7582855647636897223} + - component: {fileID: 7582855647636897221} + - component: {fileID: 7582855647636897222} + m_Layer: 0 + m_Name: StartPoint + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7582855647636897223 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7582855647636897216} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 2, y: 0.05, z: 2} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7582855647636897221 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7582855647636897216} + m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7582855647636897222 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7582855647636897216} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: ec3b6ac9651c31248bcfbf695e6f597a, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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 diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/StartPoint.prefab.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/StartPoint.prefab.meta new file mode 100644 index 0000000..ceaf231 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Prefabs/StartPoint.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 849c125c9d8094249b3c664da1cd143a +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/ReadMe.txt b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/ReadMe.txt new file mode 100644 index 0000000..9f5f0f1 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/ReadMe.txt @@ -0,0 +1 @@ +Setup details at https://mirror-networking.gitbook.io/docs/examples/additive-levels \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/readme.txt.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/ReadMe.txt.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/readme.txt.meta rename to UnityProject/Assets/Mirror/Examples/AdditiveLevels/ReadMe.txt.meta index 968eafe..c883438 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/readme.txt.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/ReadMe.txt.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: a28193472bc84d341ab4aee18c471a93 +guid: e0b006e68a5390c42a2ac1413c98e72f TextScriptImporter: externalObjects: {} userData: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes.meta new file mode 100644 index 0000000..2817b8a --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7a6d39d823db54d43925eb65b4ffec55 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Offline.unity b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Offline.unity new file mode 100644 index 0000000..cc0e17a --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Offline.unity @@ -0,0 +1,495 @@ +%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, g: 0, b: 0, 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: 3 + 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: 1 + 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: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + 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: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + 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_LightProbeSampleCountMultiplier: 4 + 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 &185921126 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 185921129} + - component: {fileID: 185921128} + - component: {fileID: 185921127} + 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 &185921127 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 185921126} + m_Enabled: 1 +--- !u!20 &185921128 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 185921126} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 2 + 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 &185921129 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 185921126} + 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 &1040404844 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1040404847} + - component: {fileID: 1040404846} + - component: {fileID: 1040404845} + m_Layer: 5 + m_Name: Panel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1040404845 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1040404844} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1040404846 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1040404844} + m_CullTransparentMesh: 0 +--- !u!224 &1040404847 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1040404844} + 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: 1300359894} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &1074858613 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1074858617} + - component: {fileID: 1074858616} + - component: {fileID: 1074858615} + - component: {fileID: 1074858614} + - component: {fileID: 1074858618} + m_Layer: 0 + m_Name: Network + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1074858614 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1074858613} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6442dc8070ceb41f094e44de0bf87274, type: 3} + m_Name: + m_EditorClassIdentifier: + offsetX: 0 + offsetY: 0 +--- !u!114 &1074858615 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1074858613} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6b0fecffa3f624585964b0d0eb21b18e, type: 3} + m_Name: + m_EditorClassIdentifier: + Port: 7777 + DualMode: 1 + NoDelay: 1 + Interval: 10 + Timeout: 10000 + FastResend: 2 + CongestionWindow: 0 + SendWindowSize: 4096 + ReceiveWindowSize: 4096 + NonAlloc: 1 + debugLog: 0 + statisticsGUI: 0 + statisticsLog: 0 +--- !u!114 &1074858616 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1074858613} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 01eafa8309a0894479f0f87ae1a9c30f, type: 3} + m_Name: + m_EditorClassIdentifier: + dontDestroyOnLoad: 1 + PersistNetworkManagerToOfflineScene: 0 + runInBackground: 1 + autoStartServerBuild: 1 + serverTickRate: 30 + offlineScene: Assets/Mirror/Examples/AdditiveLevels/Scenes/Offline.unity + onlineScene: Assets/Mirror/Examples/AdditiveLevels/Scenes/Online.unity + transport: {fileID: 1074858615} + networkAddress: localhost + maxConnections: 100 + authenticator: {fileID: 0} + playerPrefab: {fileID: 7619140271685878370, guid: 9f0094c1325091d42a558274b947221f, + type: 3} + autoCreatePlayer: 0 + playerSpawnMethod: 1 + spawnPrefabs: [] + additiveScenes: + - Assets/Mirror/Examples/AdditiveLevels/Scenes/SubLevel1.unity + - Assets/Mirror/Examples/AdditiveLevels/Scenes/SubLevel2.unity + fadeInOut: {fileID: 1300359890} +--- !u!4 &1074858617 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1074858613} + 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: + - {fileID: 1300359894} + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1074858618 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1074858613} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b979f26c95d34324ba005bfacfa9c4fc, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!1 &1300359889 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300359894} + - component: {fileID: 1300359893} + - component: {fileID: 1300359892} + - component: {fileID: 1300359890} + m_Layer: 5 + m_Name: FadeCanvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1300359890 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1300359889} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 363a8867bb9c7b845a73233566df8c1e, type: 3} + m_Name: + m_EditorClassIdentifier: + speed: 1 + fadeImage: {fileID: 1040404845} + fadeColor: {r: 0, g: 0, b: 0, a: 1} +--- !u!114 &1300359892 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1300359889} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 +--- !u!223 &1300359893 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1300359889} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &1300359894 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1300359889} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 1040404847} + m_Father: {fileID: 1074858617} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Offline.unity.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Offline.unity.meta new file mode 100644 index 0000000..13352b4 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Offline.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9959d9d8e637be64dab77006a85135c1 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online.meta new file mode 100644 index 0000000..133371f --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e73939c6462fca44d9c1fa826ff5c072 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online.unity b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online.unity new file mode 100644 index 0000000..3b49a89 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online.unity @@ -0,0 +1,646 @@ +%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: 1 + m_FogColor: {r: 0.0627451, g: 0.08235294, b: 0.18431373, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0, g: 0, b: 0, 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: 2100000, guid: 81f714daf7144784b8a2f42f1cd30927, type: 2} + 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.2312071, g: 0.2344121, b: 0.27077314, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + 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: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + 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_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 112000002, guid: 7868b86bff1943140826320e66c66468, type: 2} + m_LightingSettings: {fileID: 4890085278179872738, guid: b14d22a581510774fa4c3d6017f61944, type: 2} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &203151409 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 203151411} + - component: {fileID: 203151410} + m_Layer: 0 + m_Name: Directional Light (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &203151410 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 203151409} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 0.5 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 0 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 0.5 + 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: 0 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &203151411 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 203151409} + m_LocalRotation: {x: 0.55403227, y: -0.21201213, z: 0.14845248, w: 0.79124016} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 70, y: -30, z: 0} +--- !u!1 &413994573 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 413994576} + - component: {fileID: 413994575} + - component: {fileID: 413994574} + 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 &413994574 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 413994573} + m_Enabled: 1 +--- !u!20 &413994575 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 413994573} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, 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 &413994576 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 413994573} + m_LocalRotation: {x: 0, y: -0.2658926, z: 0, w: 0.9640027} + m_LocalPosition: {x: 3.84, y: 1.91, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: -30.84, z: 0} +--- !u!1 &1110529627 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1110529629} + - component: {fileID: 1110529628} + m_Layer: 0 + m_Name: Directional Light (3) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &1110529628 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1110529627} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 0.5 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 0 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 0.5 + 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: 0 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &1110529629 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1110529627} + m_LocalRotation: {x: 0.9848078, y: -0, z: -0, w: -0.17364809} + 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: 200, y: 0, z: 0} +--- !u!1 &1309024824 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1309024826} + - component: {fileID: 1309024825} + m_Layer: 0 + m_Name: Directional Light (4) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &1309024825 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1309024824} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 0.5 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 0 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 0.5 + 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: 0 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &1309024826 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1309024824} + m_LocalRotation: {x: 0.28678825, y: -0.70940644, z: -0.4967319, w: -0.4095759} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 290, y: 120, z: 0} +--- !u!1 &1606864533 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1606864535} + - component: {fileID: 1606864534} + m_Layer: 0 + m_Name: Directional Light (2) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &1606864534 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1606864533} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 0.5 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 0 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 0.5 + 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: 0 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &1606864535 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1606864533} + m_LocalRotation: {x: 0.9063079, y: 0, z: 0, w: 0.42261827} + 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: 130, y: 0, z: 0} +--- !u!1 &1841150988 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1841150991} + - component: {fileID: 1841150990} + - component: {fileID: 1841150989} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1841150989 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1841150988} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} + m_Name: + m_EditorClassIdentifier: + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &1841150990 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1841150988} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!4 &1841150991 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1841150988} + 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: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online.unity.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online.unity.meta new file mode 100644 index 0000000..5713db8 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3c63960543989ea41ba3d3fb04d6dcf3 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online/LightingData.asset b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online/LightingData.asset new file mode 100644 index 0000000..ce15e19 Binary files /dev/null and b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online/LightingData.asset differ diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online/LightingData.asset.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online/LightingData.asset.meta new file mode 100644 index 0000000..341be03 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online/LightingData.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7868b86bff1943140826320e66c66468 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 6475696996188705980 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online/ReflectionProbe-0.exr b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online/ReflectionProbe-0.exr new file mode 100644 index 0000000..4e35126 Binary files /dev/null and b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online/ReflectionProbe-0.exr differ diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online/ReflectionProbe-0.exr.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online/ReflectionProbe-0.exr.meta new file mode 100644 index 0000000..ebde034 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/Online/ReflectionProbe-0.exr.meta @@ -0,0 +1,92 @@ +fileFormatVersion: 2 +guid: 433b69cd38057f84bb82796761c1be49 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 1 + seamlessCubemap: 1 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 0 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 2 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 100 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/OnlineSettings.lighting b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/OnlineSettings.lighting new file mode 100644 index 0000000..fd67916 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/OnlineSettings.lighting @@ -0,0 +1,63 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!850595691 &4890085278179872738 +LightingSettings: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: OnlineSettings + serializedVersion: 3 + m_GIWorkflowMode: 1 + m_EnableBakedLightmaps: 0 + m_EnableRealtimeLightmaps: 0 + m_RealtimeEnvironmentLighting: 1 + m_BounceScale: 1 + m_AlbedoBoost: 1 + m_IndirectOutputScale: 1 + m_UsingShadowmask: 1 + m_BakeBackend: 1 + m_LightmapMaxSize: 1024 + m_BakeResolution: 40 + m_Padding: 2 + m_TextureCompression: 1 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAO: 0 + m_MixedBakeMode: 2 + m_LightmapsBakeMode: 1 + m_FilterMode: 1 + m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_RealtimeResolution: 2 + m_ForceWhiteAlbedo: 0 + m_ForceUpdates: 0 + m_FinalGather: 0 + m_FinalGatherRayCount: 256 + m_FinalGatherFiltering: 1 + m_PVRCulling: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_LightProbeSampleCountMultiplier: 4 + m_PVRBounces: 2 + m_PVRMinBounces: 2 + m_PVREnvironmentMIS: 1 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/OnlineSettings.lighting.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/OnlineSettings.lighting.meta new file mode 100644 index 0000000..96b73eb --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/OnlineSettings.lighting.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b14d22a581510774fa4c3d6017f61944 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 4890085278179872738 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/SubLevel1.unity b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/SubLevel1.unity new file mode 100644 index 0000000..95a98ef --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/SubLevel1.unity @@ -0,0 +1,1021 @@ +%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: 10304, guid: 0000000000000000f000000000000000, type: 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.18028378, g: 0.22571412, b: 0.30692285, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + 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: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + 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_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 112000006, guid: 7868b86bff1943140826320e66c66468, + type: 2} + 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 &126437272 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 126437274} + - component: {fileID: 126437273} + m_Layer: 0 + m_Name: Environment + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &126437273 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 126437272} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3} + m_Name: + m_EditorClassIdentifier: + sceneId: 1317994002 + serverOnly: 0 + visible: 0 + m_AssetId: + hasSpawned: 0 +--- !u!4 &126437274 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 126437272} + 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: + - {fileID: 1373387508} + - {fileID: 1445828407} + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &472411619 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 472411620} + - component: {fileID: 472411621} + m_Layer: 0 + m_Name: StartPosition + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &472411620 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 472411619} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -6, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1518259679} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &472411621 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 472411619} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 41f84591ce72545258ea98cb7518d8b9, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!1 &493146027 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 493146028} + - component: {fileID: 493146029} + m_Layer: 0 + m_Name: StartPosition + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &493146028 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 493146027} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -9, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1518259679} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &493146029 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 493146027} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 41f84591ce72545258ea98cb7518d8b9, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!1 &939597736 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 939597737} + m_Layer: 0 + m_Name: Cubes + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &939597737 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 939597736} + 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: + - {fileID: 3181899219847070119} + - {fileID: 1294796099} + - {fileID: 2120764046} + - {fileID: 1071886464} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1001 &1071886463 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 939597737} + m_Modifications: + - target: {fileID: -1828993248307539358, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: sceneId + value: 656062201 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528418, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_Name + value: Cube + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_RootOrder + value: 3 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalPosition.x + value: 7 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalPosition.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 7c6a0a278eba11e44b9a86cd4da603df, type: 3} +--- !u!4 &1071886464 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + m_PrefabInstance: {fileID: 1071886463} + m_PrefabAsset: {fileID: 0} +--- !u!1 &1224119056 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1224119057} + - component: {fileID: 1224119058} + m_Layer: 0 + m_Name: StartPosition + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1224119057 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1224119056} + 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: 1518259679} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1224119058 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1224119056} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 41f84591ce72545258ea98cb7518d8b9, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!1001 &1294796098 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 939597737} + m_Modifications: + - target: {fileID: -1828993248307539358, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: sceneId + value: 3113436280 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528418, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_Name + value: Cube + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_RootOrder + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalPosition.x + value: -1 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalPosition.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 7c6a0a278eba11e44b9a86cd4da603df, type: 3} +--- !u!4 &1294796099 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + m_PrefabInstance: {fileID: 1294796098} + m_PrefabAsset: {fileID: 0} +--- !u!4 &1373387508 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + m_PrefabInstance: {fileID: 7582855648999245619} + m_PrefabAsset: {fileID: 0} +--- !u!1001 &1445828406 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 126437274} + m_Modifications: + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_RootOrder + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008055, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_Name + value: Plane + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, type: 3} +--- !u!4 &1445828407 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + m_PrefabInstance: {fileID: 1445828406} + m_PrefabAsset: {fileID: 0} +--- !u!1 &1513281386 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1513281387} + - component: {fileID: 1513281388} + m_Layer: 0 + m_Name: StartPosition + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1513281387 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1513281386} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -3, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1518259679} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1513281388 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1513281386} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 41f84591ce72545258ea98cb7518d8b9, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!1 &1518259678 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1518259679} + m_Layer: 0 + m_Name: StartPosttions + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1518259679 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1518259678} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1.1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 493146028} + - {fileID: 472411620} + - {fileID: 1513281387} + - {fileID: 1224119057} + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1842934413 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1842934415} + - component: {fileID: 1842934414} + m_Layer: 0 + m_Name: PhysicsSimulator + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1842934414 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1842934413} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 472efbe031bc80a499ea2a1003ac7452, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!4 &1842934415 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1842934413} + 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!1001 &2120764045 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 939597737} + m_Modifications: + - target: {fileID: -1828993248307539358, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: sceneId + value: 3424948259 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528418, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_Name + value: Cube + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_RootOrder + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalPosition.x + value: 3 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalPosition.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 7c6a0a278eba11e44b9a86cd4da603df, type: 3} +--- !u!4 &2120764046 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + m_PrefabInstance: {fileID: 2120764045} + m_PrefabAsset: {fileID: 0} +--- !u!1001 &1098173225757504847 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_RootOrder + value: 3 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalPosition.x + value: -10 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalPosition.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalPosition.z + value: 10 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622925, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_Name + value: Portal + objectReference: {fileID: 0} + - target: {fileID: 3141292696673982546, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: sceneId + value: 1143465467 + objectReference: {fileID: 0} + - target: {fileID: 5948271423698091598, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: startPosition.x + value: -5 + objectReference: {fileID: 0} + - target: {fileID: 5948271423698091598, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: startPosition.y + value: 1.1 + objectReference: {fileID: 0} + - target: {fileID: 5948271423698091598, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: startPosition.z + value: 10 + objectReference: {fileID: 0} + - target: {fileID: 5948271423698091598, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: destinationScene + value: Assets/Mirror/Examples/AdditiveLevels/Scenes/SubLevel2.unity + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: c624c75494b4d7d4086b9212f897e56a, type: 3} +--- !u!1001 &3181899219847070118 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 939597737} + m_Modifications: + - target: {fileID: -1828993248307539358, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: sceneId + value: 3043478025 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528418, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_Name + value: Cube + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalPosition.x + value: -5 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalPosition.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 7c6a0a278eba11e44b9a86cd4da603df, type: 3} +--- !u!4 &3181899219847070119 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 3181899219042528419, guid: 7c6a0a278eba11e44b9a86cd4da603df, + type: 3} + m_PrefabInstance: {fileID: 3181899219847070118} + m_PrefabAsset: {fileID: 0} +--- !u!1001 &7582855648999245619 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 126437274} + m_Modifications: + - target: {fileID: 7582855647636897216, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_Name + value: StartPoint + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_RootOrder + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalPosition.x + value: 5 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalPosition.z + value: -10 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 849c125c9d8094249b3c664da1cd143a, type: 3} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/SubLevel1.unity.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/SubLevel1.unity.meta new file mode 100644 index 0000000..0b2651c --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/SubLevel1.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 91c0fa5f4542be14dbad5c838292056b +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/SubLevel2.unity b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/SubLevel2.unity new file mode 100644 index 0000000..de14d14 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/SubLevel2.unity @@ -0,0 +1,820 @@ +%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: 10304, guid: 0000000000000000f000000000000000, type: 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.18028378, g: 0.22571412, b: 0.30692285, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + 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: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + 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_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 112000004, guid: 7868b86bff1943140826320e66c66468, + type: 2} + 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!1001 &571924470 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_RootOrder + value: 3 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalPosition.x + value: 10 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalPosition.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalPosition.z + value: -10 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622921, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1098173225717622925, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: m_Name + value: Portal + objectReference: {fileID: 0} + - target: {fileID: 3141292696673982546, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: sceneId + value: 1073849646 + objectReference: {fileID: 0} + - target: {fileID: 5948271423698091598, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: originScene + value: Assets/DungeonTest/Scenes/Level2.unity + objectReference: {fileID: 0} + - target: {fileID: 5948271423698091598, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: startPosition.x + value: 5 + objectReference: {fileID: 0} + - target: {fileID: 5948271423698091598, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: startPosition.y + value: 1.1 + objectReference: {fileID: 0} + - target: {fileID: 5948271423698091598, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: startPosition.z + value: -10 + objectReference: {fileID: 0} + - target: {fileID: 5948271423698091598, guid: c624c75494b4d7d4086b9212f897e56a, + type: 3} + propertyPath: destinationScene + value: Assets/Mirror/Examples/AdditiveLevels/Scenes/SubLevel1.unity + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: c624c75494b4d7d4086b9212f897e56a, type: 3} +--- !u!1001 &1071886463 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 1514693545} + m_Modifications: + - target: {fileID: -5172514306435102607, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: sceneId + value: 2068572849 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_RootOrder + value: 3 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalPosition.x + value: 5 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalPosition.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236743, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_Name + value: Sphere + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: e588080aa542be54d9ca9d5c734dc9ee, type: 3} +--- !u!4 &1071886464 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + m_PrefabInstance: {fileID: 1071886463} + m_PrefabAsset: {fileID: 0} +--- !u!1001 &1294796098 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 1514693545} + m_Modifications: + - target: {fileID: -5172514306435102607, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: sceneId + value: 131067959 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_RootOrder + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalPosition.x + value: -3 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalPosition.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236743, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_Name + value: Sphere + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: e588080aa542be54d9ca9d5c734dc9ee, type: 3} +--- !u!4 &1294796099 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + m_PrefabInstance: {fileID: 1294796098} + m_PrefabAsset: {fileID: 0} +--- !u!1 &1514693544 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1514693545} + m_Layer: 0 + m_Name: Spheres + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1514693545 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1514693544} + 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: + - {fileID: 2188792038635513490} + - {fileID: 1294796099} + - {fileID: 2120764046} + - {fileID: 1071886464} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1842934413 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1842934415} + - component: {fileID: 1842934414} + m_Layer: 0 + m_Name: PhysicsSimulator + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1842934414 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1842934413} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 472efbe031bc80a499ea2a1003ac7452, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!4 &1842934415 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1842934413} + 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!1 &1932534637 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1932534639} + - component: {fileID: 1932534638} + m_Layer: 0 + m_Name: Environment + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1932534638 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1932534637} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3} + m_Name: + m_EditorClassIdentifier: + sceneId: 159404139 + serverOnly: 0 + visible: 0 + m_AssetId: + hasSpawned: 0 +--- !u!4 &1932534639 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1932534637} + 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: + - {fileID: 1999985658} + - {fileID: 6358973573665600640} + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1001 &1999985657 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 1932534639} + m_Modifications: + - target: {fileID: 7582855647636897216, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_Name + value: StartPoint + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_RootOrder + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalPosition.x + value: -5 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalPosition.z + value: 10 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 849c125c9d8094249b3c664da1cd143a, type: 3} +--- !u!4 &1999985658 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 7582855647636897223, guid: 849c125c9d8094249b3c664da1cd143a, + type: 3} + m_PrefabInstance: {fileID: 1999985657} + m_PrefabAsset: {fileID: 0} +--- !u!1001 &2120764045 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 1514693545} + m_Modifications: + - target: {fileID: -5172514306435102607, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: sceneId + value: 1581864539 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_RootOrder + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalPosition.x + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalPosition.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236743, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_Name + value: Sphere + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: e588080aa542be54d9ca9d5c734dc9ee, type: 3} +--- !u!4 &2120764046 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + m_PrefabInstance: {fileID: 2120764045} + m_PrefabAsset: {fileID: 0} +--- !u!1001 &2188792038635513489 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 1514693545} + m_Modifications: + - target: {fileID: -5172514306435102607, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: sceneId + value: 3873206969 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalPosition.x + value: -7 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalPosition.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2188792038427236743, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + propertyPath: m_Name + value: Sphere + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: e588080aa542be54d9ca9d5c734dc9ee, type: 3} +--- !u!4 &2188792038635513490 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 2188792038427236742, guid: e588080aa542be54d9ca9d5c734dc9ee, + type: 3} + m_PrefabInstance: {fileID: 2188792038635513489} + m_PrefabAsset: {fileID: 0} +--- !u!1001 &6358973573665600639 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 1932534639} + m_Modifications: + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_RootOrder + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7349408984269008055, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + propertyPath: m_Name + value: Plane + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, type: 3} +--- !u!4 &6358973573665600640 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 7349408984269008052, guid: 4fdcc4aa5c669b348a5954a92f5e8e89, + type: 3} + m_PrefabInstance: {fileID: 6358973573665600639} + m_PrefabAsset: {fileID: 0} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/SubLevel2.unity.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/SubLevel2.unity.meta new file mode 100644 index 0000000..6296895 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scenes/SubLevel2.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e8e2dc8c3ef824b468c6f6e1980a4b27 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts.meta new file mode 100644 index 0000000..4e40c32 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: daf19edd647d8ae4ead9cb46f24f5424 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/AdditiveLevelsNetworkManager.cs b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/AdditiveLevelsNetworkManager.cs new file mode 100644 index 0000000..7dffafe --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/AdditiveLevelsNetworkManager.cs @@ -0,0 +1,187 @@ +using System.Collections; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace Mirror.Examples.AdditiveLevels +{ + [AddComponentMenu("")] + public class AdditiveLevelsNetworkManager : NetworkManager + { + [Header("Additive Scenes - First is start scene")] + + [Scene, Tooltip("Add additive scenes here.\nFirst entry will be players' start scene")] + public string[] additiveScenes; + + [Header("Fade Control - See child FadeCanvas")] + + [Tooltip("Reference to FadeInOut script on child FadeCanvas")] + public FadeInOut fadeInOut; + + // This is set true after server loads all subscene instances + bool subscenesLoaded; + + // This is managed in LoadAdditive, UnloadAdditive, and checked in OnClientSceneChanged + bool isInTransition; + + #region Scene Management + + /// + /// Called on the server when a scene is completed loaded, when the scene load was initiated by the server with ServerChangeScene(). + /// + /// The name of the new scene. + public override void OnServerSceneChanged(string sceneName) + { + // This fires after server fully changes scenes, e.g. offline to online + // If server has just loaded the Container (online) scene, load the subscenes on server + if (sceneName == onlineScene) + StartCoroutine(ServerLoadSubScenes()); + } + + IEnumerator ServerLoadSubScenes() + { + foreach (string additiveScene in additiveScenes) + yield return SceneManager.LoadSceneAsync(additiveScene, new LoadSceneParameters + { + loadSceneMode = LoadSceneMode.Additive, + localPhysicsMode = LocalPhysicsMode.Physics3D // change this to .Physics2D for a 2D game + }); + + subscenesLoaded = true; + } + + /// + /// Called from ClientChangeScene immediately before SceneManager.LoadSceneAsync is executed + /// This allows client to do work / cleanup / prep before the scene changes. + /// + /// Name of the scene that's about to be loaded + /// Scene operation that's about to happen + /// true to indicate that scene loading will be handled through overrides + public override void OnClientChangeScene(string sceneName, SceneOperation sceneOperation, bool customHandling) + { + //Debug.Log($"{System.DateTime.Now:HH:mm:ss:fff} OnClientChangeScene {sceneName} {sceneOperation}"); + + if (sceneOperation == SceneOperation.UnloadAdditive) + StartCoroutine(UnloadAdditive(sceneName)); + + if (sceneOperation == SceneOperation.LoadAdditive) + StartCoroutine(LoadAdditive(sceneName)); + } + + IEnumerator LoadAdditive(string sceneName) + { + isInTransition = true; + + // This will return immediately if already faded in + // e.g. by UnloadAdditive above or by default startup state + yield return fadeInOut.FadeIn(); + + // host client is on server...don't load the additive scene again + if (mode == NetworkManagerMode.ClientOnly) + { + // Start loading the additive subscene + loadingSceneAsync = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive); + + while (loadingSceneAsync != null && !loadingSceneAsync.isDone) + yield return null; + } + + // Reset these to false when ready to proceed + NetworkClient.isLoadingScene = false; + isInTransition = false; + + OnClientSceneChanged(); + + yield return fadeInOut.FadeOut(); + } + + IEnumerator UnloadAdditive(string sceneName) + { + isInTransition = true; + + // This will return immediately if already faded in + // e.g. by LoadAdditive above or by default startup state + yield return fadeInOut.FadeIn(); + + if (mode == NetworkManagerMode.ClientOnly) + { + yield return SceneManager.UnloadSceneAsync(sceneName); + yield return Resources.UnloadUnusedAssets(); + } + + // Reset these to false when ready to proceed + NetworkClient.isLoadingScene = false; + isInTransition = false; + + OnClientSceneChanged(); + + // There is no call to FadeOut here on purpose. + // Expectation is that a LoadAdditive will follow + // that will call FadeOut after that scene loads. + } + + /// + /// Called on clients when a scene has completed loaded, when the scene load was initiated by the server. + /// Scene changes can cause player objects to be destroyed. The default implementation of OnClientSceneChanged in the NetworkManager is to add a player object for the connection if no player object exists. + /// + /// The network connection that the scene change message arrived on. + public override void OnClientSceneChanged() + { + //Debug.Log($"{System.DateTime.Now:HH:mm:ss:fff} OnClientSceneChanged {isInTransition}"); + + // Only call the base method if not in a transition. + // This will be called from DoTransition after setting doingTransition to false + // but will also be called first by Mirror when the scene loading finishes. + if (!isInTransition) + base.OnClientSceneChanged(); + } + + #endregion + + #region Server System Callbacks + + /// + /// Called on the server when a client is ready. + /// The default implementation of this function calls NetworkServer.SetClientReady() to continue the network setup process. + /// + /// Connection from client. + public override void OnServerReady(NetworkConnectionToClient conn) + { + //Debug.Log($"OnServerReady {conn} {conn.identity}"); + + // This fires from a Ready message client sends to server after loading the online scene + base.OnServerReady(conn); + + if (conn.identity == null) + StartCoroutine(AddPlayerDelayed(conn)); + } + + // This delay is mostly for the host player that loads too fast for the + // server to have subscenes async loaded from OnServerSceneChanged ahead of it. + IEnumerator AddPlayerDelayed(NetworkConnectionToClient conn) + { + // Wait for server to async load all subscenes for game instances + while (!subscenesLoaded) + yield return null; + + // Send Scene msg to client telling it to load the first additive scene + conn.Send(new SceneMessage { sceneName = additiveScenes[0], sceneOperation = SceneOperation.LoadAdditive, customHandling = true }); + + // We have Network Start Positions in first additive scene...pick one + Transform start = GetStartPosition(); + + // Instantiate player as child of start position - this will place it in the additive scene + // This also lets player object "inherit" pos and rot from start position transform + GameObject player = Instantiate(playerPrefab, start); + // now set parent null to get it out from under the Start Position object + player.transform.SetParent(null); + + // Wait for end of frame before adding the player to ensure Scene Message goes first + yield return new WaitForEndOfFrame(); + + // Finally spawn the player object for this connection + NetworkServer.AddPlayerForConnection(conn, player); + } + + #endregion + } +} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/AdditiveLevelsNetworkManager.cs.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/AdditiveLevelsNetworkManager.cs.meta new file mode 100644 index 0000000..af10a3c --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/AdditiveLevelsNetworkManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 01eafa8309a0894479f0f87ae1a9c30f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/FadeInOut.cs b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/FadeInOut.cs new file mode 100644 index 0000000..c378980 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/FadeInOut.cs @@ -0,0 +1,60 @@ +using System.Collections; +using UnityEngine; +using UnityEngine.UI; + +namespace Mirror.Examples.AdditiveLevels +{ + public class FadeInOut : MonoBehaviour + { + // set these in the inspector + [Range(1, 100), Tooltip("Speed of fade in / out: lower is slower")] + public byte speed = 1; + + [Tooltip("Reference to Image component on child panel")] + public Image fadeImage; + + [Tooltip("Color to use during scene transition")] + public Color fadeColor = Color.black; + + WaitForSeconds waitForSeconds; + + void Awake() + { + waitForSeconds = new WaitForSeconds(speed * 0.01f); + } + + public IEnumerator FadeIn() + { + //Debug.Log($"{System.DateTime.Now:HH:mm:ss:fff} FadeIn - fading image in {fadeImage.color.a}"); + + float alpha = fadeImage.color.a; + + while (alpha < 1) + { + yield return waitForSeconds; + alpha += 0.01f; + fadeColor.a = alpha; + fadeImage.color = fadeColor; + } + + //Debug.Log($"{System.DateTime.Now:HH:mm:ss:fff} FadeIn - done fading"); + } + + public IEnumerator FadeOut() + { + //Debug.Log($"{System.DateTime.Now:HH:mm:ss:fff} FadeOut - fading image out {fadeImage.color.a}"); + + float alpha = fadeImage.color.a; + + while (alpha > 0) + { + yield return waitForSeconds; + alpha -= 0.01f; + fadeColor.a = alpha; + fadeImage.color = fadeColor; + } + + //Debug.Log($"{System.DateTime.Now:HH:mm:ss:fff} FadeOut - done fading"); + } + } +} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/FadeInOut.cs.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/FadeInOut.cs.meta new file mode 100644 index 0000000..c54328f --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/FadeInOut.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 363a8867bb9c7b845a73233566df8c1e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/LookAtMainCamera.cs b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/LookAtMainCamera.cs new file mode 100644 index 0000000..2295416 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/LookAtMainCamera.cs @@ -0,0 +1,14 @@ +using UnityEngine; + +namespace Mirror.Examples.AdditiveLevels +{ + // This script is attached to portal labels to keep them facing the camera + public class LookAtMainCamera : MonoBehaviour + { + // LateUpdate so that all camera updates are finished. + void LateUpdate() + { + transform.forward = Camera.main.transform.forward; + } + } +} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/LookAtMainCamera.cs.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/LookAtMainCamera.cs.meta new file mode 100644 index 0000000..34a9978 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/LookAtMainCamera.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cc58300ee45438a418d9e32957fdc0c0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/PhysicsSimulator.cs b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/PhysicsSimulator.cs new file mode 100644 index 0000000..7b0da7a --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/PhysicsSimulator.cs @@ -0,0 +1,40 @@ +using UnityEngine; + +namespace Mirror.Examples.AdditiveLevels +{ + public class PhysicsSimulator : MonoBehaviour + { + PhysicsScene physicsScene; + PhysicsScene2D physicsScene2D; + + bool simulatePhysicsScene; + bool simulatePhysicsScene2D; + + void Awake() + { + if (NetworkServer.active) + { + physicsScene = gameObject.scene.GetPhysicsScene(); + simulatePhysicsScene = physicsScene.IsValid() && physicsScene != Physics.defaultPhysicsScene; + + physicsScene2D = gameObject.scene.GetPhysicsScene2D(); + simulatePhysicsScene2D = physicsScene2D.IsValid() && physicsScene2D != Physics2D.defaultPhysicsScene; + } + else + { + enabled = false; + } + } + + void FixedUpdate() + { + if (!NetworkServer.active) return; + + if (simulatePhysicsScene) + physicsScene.Simulate(Time.fixedDeltaTime); + + if (simulatePhysicsScene2D) + physicsScene2D.Simulate(Time.fixedDeltaTime); + } + } +} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/PhysicsSimulator.cs.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/PhysicsSimulator.cs.meta new file mode 100644 index 0000000..b683f9a --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/PhysicsSimulator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 472efbe031bc80a499ea2a1003ac7452 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/PlayerCamera.cs b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/PlayerCamera.cs new file mode 100644 index 0000000..ed9ad58 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/PlayerCamera.cs @@ -0,0 +1,41 @@ +using UnityEngine; +using UnityEngine.SceneManagement; + +// This sets up the scene camera for the local player + +namespace Mirror.Examples.AdditiveLevels +{ + public class PlayerCamera : NetworkBehaviour + { + Camera mainCam; + + void Awake() + { + mainCam = Camera.main; + } + + public override void OnStartLocalPlayer() + { + if (mainCam != null) + { + // configure and make camera a child of player with 3rd person offset + mainCam.orthographic = false; + mainCam.transform.SetParent(transform); + mainCam.transform.localPosition = new Vector3(0f, 3f, -8f); + mainCam.transform.localEulerAngles = new Vector3(10f, 0f, 0f); + } + } + + public override void OnStopLocalPlayer() + { + if (mainCam != null) + { + mainCam.transform.SetParent(null); + SceneManager.MoveGameObjectToScene(mainCam.gameObject, SceneManager.GetActiveScene()); + mainCam.orthographic = true; + mainCam.transform.localPosition = new Vector3(0f, 70f, 0f); + mainCam.transform.localEulerAngles = new Vector3(90f, 0f, 0f); + } + } + } +} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/PlayerCamera.cs.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/PlayerCamera.cs.meta new file mode 100644 index 0000000..c70cb63 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/PlayerCamera.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 22976424f775a0f4a8531e6713ff6de2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/PlayerController.cs b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/PlayerController.cs new file mode 100644 index 0000000..8cabd66 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/PlayerController.cs @@ -0,0 +1,95 @@ +using UnityEngine; + +namespace Mirror.Examples.AdditiveLevels +{ + [RequireComponent(typeof(CapsuleCollider))] + [RequireComponent(typeof(CharacterController))] + [RequireComponent(typeof(NetworkTransform))] + [RequireComponent(typeof(Rigidbody))] + public class PlayerController : NetworkBehaviour + { + public CharacterController characterController; + + [Header("Movement Settings")] + public float moveSpeed = 8f; + public float turnSensitivity = 5f; + public float maxTurnSpeed = 100f; + + [Header("Diagnostics")] + public float horizontal; + public float vertical; + public float turn; + public float jumpSpeed; + public bool isGrounded = true; + public bool isFalling; + public Vector3 velocity; + + void OnValidate() + { + if (characterController == null) + characterController = GetComponent(); + + characterController.enabled = false; + GetComponent().isKinematic = true; + GetComponent().clientAuthority = true; + } + + public override void OnStartLocalPlayer() + { + characterController.enabled = true; + } + + void Update() + { + if (!isLocalPlayer || characterController == null || !characterController.enabled) + return; + + horizontal = Input.GetAxis("Horizontal"); + vertical = Input.GetAxis("Vertical"); + + // Q and E cancel each other out, reducing the turn to zero + if (Input.GetKey(KeyCode.Q)) + turn = Mathf.MoveTowards(turn, -maxTurnSpeed, turnSensitivity); + if (Input.GetKey(KeyCode.E)) + turn = Mathf.MoveTowards(turn, maxTurnSpeed, turnSensitivity); + if (Input.GetKey(KeyCode.Q) && Input.GetKey(KeyCode.E)) + turn = Mathf.MoveTowards(turn, 0, turnSensitivity); + if (!Input.GetKey(KeyCode.Q) && !Input.GetKey(KeyCode.E)) + turn = Mathf.MoveTowards(turn, 0, turnSensitivity); + + if (isGrounded) + isFalling = false; + + if ((isGrounded || !isFalling) && jumpSpeed < 1f && Input.GetKey(KeyCode.Space)) + { + jumpSpeed = Mathf.Lerp(jumpSpeed, 1f, 0.5f); + } + else if (!isGrounded) + { + isFalling = true; + jumpSpeed = 0; + } + } + + void FixedUpdate() + { + if (!isLocalPlayer || characterController == null || !characterController.enabled) + return; + + transform.Rotate(0f, turn * Time.fixedDeltaTime, 0f); + + Vector3 direction = new Vector3(horizontal, jumpSpeed, vertical); + direction = Vector3.ClampMagnitude(direction, 1f); + direction = transform.TransformDirection(direction); + direction *= moveSpeed; + + if (jumpSpeed > 0) + characterController.Move(direction * Time.fixedDeltaTime); + else + characterController.SimpleMove(direction); + + isGrounded = characterController.isGrounded; + velocity = characterController.velocity; + } + } +} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/PlayerController.cs.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/PlayerController.cs.meta new file mode 100644 index 0000000..eebb2a3 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/PlayerController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 05e10150710dde14b83d3c8f5aa853c2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/Portal.cs b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/Portal.cs new file mode 100644 index 0000000..e0df00c --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/Portal.cs @@ -0,0 +1,91 @@ +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace Mirror.Examples.AdditiveLevels +{ + public class Portal : NetworkBehaviour + { + [Scene, Tooltip("Which scene to send player from here")] + public string destinationScene; + + [Tooltip("Where to spawn player in Destination Scene")] + public Vector3 startPosition; + + [Tooltip("Reference to child TMP label")] + public TMPro.TextMeshPro label; + + [SyncVar(hook = nameof(OnLabelTextChanged))] + public string labelText; + + public void OnLabelTextChanged(string _, string newValue) + { + label.text = labelText; + } + + // This is approximately the fade time + WaitForSeconds waitForFade = new WaitForSeconds(2f); + + public override void OnStartServer() + { + labelText = Path.GetFileNameWithoutExtension(destinationScene); + + // Simple Regex to insert spaces before capitals, numbers + labelText = Regex.Replace(labelText, @"\B[A-Z0-9]+", " $0"); + } + + // Note that I have created layers called Player(8) and Portal(9) and set them + // up in the Physics collision matrix so only Player collides with Portal. + void OnTriggerEnter(Collider other) + { + // tag check in case you didn't set up the layers and matrix as noted above + if (!other.CompareTag("Player")) return; + + //Debug.Log($"{System.DateTime.Now:HH:mm:ss:fff} Portal::OnTriggerEnter {gameObject.name} in {gameObject.scene.name}"); + + // applies to host client on server and remote clients + if (other.TryGetComponent(out PlayerController playerController)) + playerController.enabled = false; + + if (isServer) + StartCoroutine(SendPlayerToNewScene(other.gameObject)); + } + + [ServerCallback] + IEnumerator SendPlayerToNewScene(GameObject player) + { + if (player.TryGetComponent(out NetworkIdentity identity)) + { + NetworkConnectionToClient conn = identity.connectionToClient; + if (conn == null) yield break; + + // Tell client to unload previous subscene. No custom handling for this. + conn.Send(new SceneMessage { sceneName = gameObject.scene.path, sceneOperation = SceneOperation.UnloadAdditive, customHandling = true }); + + yield return waitForFade; + + //Debug.Log($"SendPlayerToNewScene RemovePlayerForConnection {conn} netId:{conn.identity.netId}"); + NetworkServer.RemovePlayerForConnection(conn, false); + + // reposition player on server and client + player.transform.position = startPosition; + player.transform.LookAt(Vector3.up); + + // Move player to new subscene. + SceneManager.MoveGameObjectToScene(player, SceneManager.GetSceneByPath(destinationScene)); + + // Tell client to load the new subscene with custom handling (see NetworkManager::OnClientChangeScene). + conn.Send(new SceneMessage { sceneName = destinationScene, sceneOperation = SceneOperation.LoadAdditive, customHandling = true }); + + //Debug.Log($"SendPlayerToNewScene AddPlayerForConnection {conn} netId:{conn.identity.netId}"); + NetworkServer.AddPlayerForConnection(conn, player); + + // host client would have been disabled by OnTriggerEnter above + if (NetworkClient.localPlayer != null && NetworkClient.localPlayer.TryGetComponent(out PlayerController playerController)) + playerController.enabled = true; + } + } + } +} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/Portal.cs.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/Portal.cs.meta new file mode 100644 index 0000000..92289a9 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/Portal.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0e680878006965146a8f9d85834c4d1c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/RandomColor.cs b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/RandomColor.cs new file mode 100644 index 0000000..e82613e --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/RandomColor.cs @@ -0,0 +1,36 @@ +using UnityEngine; + +namespace Mirror.Examples.AdditiveLevels +{ + public class RandomColor : NetworkBehaviour + { + // Color32 packs to 4 bytes + [SyncVar(hook = nameof(SetColor))] + public Color32 color = Color.black; + + // Unity clones the material when GetComponent().material is called + // Cache it here and destroy it in OnDestroy to prevent a memory leak + Material cachedMaterial; + + void SetColor(Color32 _, Color32 newColor) + { + if (cachedMaterial == null) cachedMaterial = GetComponentInChildren().material; + cachedMaterial.color = newColor; + } + + void OnDestroy() + { + Destroy(cachedMaterial); + } + + public override void OnStartServer() + { + base.OnStartServer(); + + // This script is on players that are respawned repeatedly + // so once the color has been set, don't change it. + if (color == Color.black) + color = Random.ColorHSV(0f, 1f, 1f, 1f, 0.5f, 1f); + } + } +} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/RandomColor.cs.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/RandomColor.cs.meta new file mode 100644 index 0000000..516f255 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Scripts/RandomColor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a91a718a70d01b347b75cb768a6f1a92 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures.meta new file mode 100644 index 0000000..9335d0d --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: a1ac2873c4eb8d64db33d7df8acb9188 +folderAsset: yes +timeCreated: 1497127550 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Back_Tex.jpeg b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Back_Tex.jpeg new file mode 100644 index 0000000..f5d4c11 Binary files /dev/null and b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Back_Tex.jpeg differ diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Back_Tex.jpeg.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Back_Tex.jpeg.meta new file mode 100644 index 0000000..4d9209f --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Back_Tex.jpeg.meta @@ -0,0 +1,92 @@ +fileFormatVersion: 2 +guid: 1566af6e663684dbd85aa5fb32fd9148 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Down_Tex.jpeg b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Down_Tex.jpeg new file mode 100644 index 0000000..5491bd0 Binary files /dev/null and b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Down_Tex.jpeg differ diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Down_Tex.jpeg.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Down_Tex.jpeg.meta new file mode 100644 index 0000000..d04aca2 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Down_Tex.jpeg.meta @@ -0,0 +1,92 @@ +fileFormatVersion: 2 +guid: 9ffeeee1accdc4b4faf2b3e27b226340 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Front_Tex.jpeg b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Front_Tex.jpeg new file mode 100644 index 0000000..5e91f3f Binary files /dev/null and b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Front_Tex.jpeg differ diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Front_Tex.jpeg.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Front_Tex.jpeg.meta new file mode 100644 index 0000000..36658d0 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Front_Tex.jpeg.meta @@ -0,0 +1,92 @@ +fileFormatVersion: 2 +guid: ca928ef0e269448ba82388eb41d48544 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Left_Tex.jpeg b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Left_Tex.jpeg new file mode 100644 index 0000000..5f7de24 Binary files /dev/null and b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Left_Tex.jpeg differ diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Left_Tex.jpeg.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Left_Tex.jpeg.meta new file mode 100644 index 0000000..11c98dd --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Left_Tex.jpeg.meta @@ -0,0 +1,92 @@ +fileFormatVersion: 2 +guid: e5a0eeef6a2514a78b267405d686fe09 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Right_Tex.jpeg b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Right_Tex.jpeg new file mode 100644 index 0000000..d7e87f9 Binary files /dev/null and b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Right_Tex.jpeg differ diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Right_Tex.jpeg.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Right_Tex.jpeg.meta new file mode 100644 index 0000000..07cdb09 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Right_Tex.jpeg.meta @@ -0,0 +1,92 @@ +fileFormatVersion: 2 +guid: 4c8911efa34c845f28fbac86385a1b41 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Up_Tex.jpeg b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Up_Tex.jpeg new file mode 100644 index 0000000..f45b6af Binary files /dev/null and b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Up_Tex.jpeg differ diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Up_Tex.jpeg.meta b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Up_Tex.jpeg.meta new file mode 100644 index 0000000..4a84217 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveLevels/Textures/Up_Tex.jpeg.meta @@ -0,0 +1,92 @@ +fileFormatVersion: 2 +guid: bd9ad886b003944169e7ce1286756940 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes.meta index 518df51..bf61013 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes.meta @@ -3,6 +3,6 @@ guid: b84b2a39b3027c747b21ad714a439214 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/AnimationControllers.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/AnimationControllers.meta index ec709da..c9cd79f 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/AnimationControllers.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/AnimationControllers.meta @@ -3,6 +3,6 @@ guid: 4969918300bfa9a4a8c733975df74016 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/AnimationControllers/Tank.controller.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/AnimationControllers/Tank.controller.meta index 8ad6c71..b4df009 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/AnimationControllers/Tank.controller.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/AnimationControllers/Tank.controller.meta @@ -3,6 +3,6 @@ guid: e0dbc8b2f2711a54f9b7ed1358a4c6af NativeFormatImporter: externalObjects: {} mainObjectFileID: 9100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials.meta index ebd0798..d6629fb 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials.meta @@ -3,6 +3,6 @@ guid: 7ef7a74859259a546b73ed820e449ae8 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Capsule.mat.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Capsule.mat.meta index 9089181..b202aa9 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Capsule.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Capsule.mat.meta @@ -3,6 +3,6 @@ guid: 3ec90781e3720544da7fc86055e6cbe6 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Cube.mat.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Cube.mat.meta index a8af065..9c096c8 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Cube.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Cube.mat.meta @@ -3,6 +3,6 @@ guid: 1d5f3015968dad04780bf9d2113cc772 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Cylinder.mat.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Cylinder.mat.meta index d77fac5..00cc85e 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Cylinder.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Cylinder.mat.meta @@ -3,6 +3,6 @@ guid: 439a10ee8f8d14040be9003239449741 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Player.mat.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Player.mat.meta index d0b34f0..56884a4 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Player.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Player.mat.meta @@ -3,6 +3,6 @@ guid: 792117fe9a386a8489e8010bec746339 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Quad.mat.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Quad.mat.meta index cc7fb6e..be7d0db 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Quad.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Quad.mat.meta @@ -3,6 +3,6 @@ guid: 6eb3f3ba66756364d8b94e662e7e8af5 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Shelter.mat.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Shelter.mat.meta index dc0786b..c261931 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Shelter.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Shelter.mat.meta @@ -3,6 +3,6 @@ guid: aef230244d219314fb8453f0365b8176 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Sphere.mat.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Sphere.mat.meta index 8556d78..8822215 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Sphere.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Sphere.mat.meta @@ -3,6 +3,6 @@ guid: 58936713efca1ec488624ee297b5d687 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Zone.mat.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Zone.mat.meta index 88474b2..4cfbe95 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Zone.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Materials/Zone.mat.meta @@ -3,6 +3,6 @@ guid: a7c679cf124f7ae46a0291ea35848554 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs.meta index ff4ccaf..37601bf 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs.meta @@ -3,6 +3,6 @@ guid: 2be4f78570b2a1e4cae84466d35d606c folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Capsule.prefab b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Capsule.prefab index 301aef4..383cb51 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Capsule.prefab +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Capsule.prefab @@ -54,6 +54,7 @@ MeshRenderer: m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -65,6 +66,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -87,7 +89,6 @@ GameObject: m_Component: - component: {fileID: 1076878374699499735} - component: {fileID: 2648107611936813301} - - component: {fileID: 5697694911122891659} - component: {fileID: 1076878374699499734} m_Layer: 0 m_Name: Capsule @@ -104,7 +105,7 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1076878374699499732} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 20, y: 1, z: -20} + m_LocalPosition: {x: 0, y: 1, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - {fileID: 6907979021268419569} @@ -125,28 +126,9 @@ MonoBehaviour: m_EditorClassIdentifier: sceneId: 0 serverOnly: 0 - m_AssetId: ---- !u!114 &5697694911122891659 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1076878374699499732} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 1731d8de2d0c84333b08ebe1e79f4118, type: 3} - m_Name: - m_EditorClassIdentifier: - syncMode: 0 - syncInterval: 0.1 - visRange: 5 - visUpdateInterval: 0.1 - checkMethod: 0 - forceHidden: 0 - castLayers: - serializedVersion: 2 - m_Bits: 256 + visible: 0 + m_AssetId: e1971f4a8c7661546bc509b44bd91b80 + hasSpawned: 0 --- !u!136 &1076878374699499734 CapsuleCollider: m_ObjectHideFlags: 0 diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Capsule.prefab.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Capsule.prefab.meta index 31b37d0..99fb5c0 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Capsule.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Capsule.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: e1971f4a8c7661546bc509b44bd91b80 PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Cube.prefab b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Cube.prefab index 83433af..f7b66a8 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Cube.prefab +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Cube.prefab @@ -54,6 +54,7 @@ MeshRenderer: m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -65,6 +66,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -87,7 +89,6 @@ GameObject: m_Component: - component: {fileID: 5623359707189648426} - component: {fileID: 5623359707189648404} - - component: {fileID: 5623359707189648405} - component: {fileID: 963943828455949898} m_Layer: 0 m_Name: Cube @@ -125,28 +126,9 @@ MonoBehaviour: m_EditorClassIdentifier: sceneId: 0 serverOnly: 0 - m_AssetId: ---- !u!114 &5623359707189648405 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5623359707189648430} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 1731d8de2d0c84333b08ebe1e79f4118, type: 3} - m_Name: - m_EditorClassIdentifier: - syncMode: 0 - syncInterval: 0.1 - visRange: 5 - visUpdateInterval: 0.1 - checkMethod: 0 - forceHidden: 0 - castLayers: - serializedVersion: 2 - m_Bits: 256 + visible: 0 + m_AssetId: 4ff300cf6bb3c6342a9552c4f18788c8 + hasSpawned: 0 --- !u!65 &963943828455949898 BoxCollider: m_ObjectHideFlags: 0 diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Cube.prefab.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Cube.prefab.meta index 913306b..8b53f72 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Cube.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Cube.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 4ff300cf6bb3c6342a9552c4f18788c8 PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Cylinder.prefab b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Cylinder.prefab index 57a3e41..d0eb27d 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Cylinder.prefab +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Cylinder.prefab @@ -10,7 +10,6 @@ GameObject: m_Component: - component: {fileID: 6852530814182375316} - component: {fileID: 6852530814182375318} - - component: {fileID: 6852530814182375317} - component: {fileID: 6852530814182375313} m_Layer: 0 m_Name: Cylinder @@ -27,7 +26,7 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6852530814182375312} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: -2, y: 1, z: 0} + m_LocalPosition: {x: 0, y: 1, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - {fileID: 5318406868242510088} @@ -48,28 +47,9 @@ MonoBehaviour: m_EditorClassIdentifier: sceneId: 0 serverOnly: 0 - m_AssetId: ---- !u!114 &6852530814182375317 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6852530814182375312} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 1731d8de2d0c84333b08ebe1e79f4118, type: 3} - m_Name: - m_EditorClassIdentifier: - syncMode: 0 - syncInterval: 0.1 - visRange: 5 - visUpdateInterval: 0.1 - checkMethod: 0 - forceHidden: 0 - castLayers: - serializedVersion: 2 - m_Bits: 256 + visible: 0 + m_AssetId: 12a4c14e672c00b4b840f937d824b890 + hasSpawned: 0 --- !u!136 &6852530814182375313 CapsuleCollider: m_ObjectHideFlags: 0 @@ -138,6 +118,7 @@ MeshRenderer: m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -149,6 +130,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Cylinder.prefab.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Cylinder.prefab.meta index 391948a..113674d 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Cylinder.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Cylinder.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 12a4c14e672c00b4b840f937d824b890 PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Player.prefab b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Player.prefab index 5f9117d..176b7bd 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Player.prefab +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Player.prefab @@ -170,11 +170,13 @@ GameObject: - component: {fileID: 5328458565928408179} - component: {fileID: 8537344390966522168} - component: {fileID: 887491563423388292} + - component: {fileID: -2082299755652640335} + - component: {fileID: 8704659178864205755} - component: {fileID: 8993127209816276930} - component: {fileID: 1143206540915927667} - component: {fileID: 3175779197224890082} - - component: {fileID: 8704659178864205755} - component: {fileID: 3086414693581178039} + - component: {fileID: -4778368485878020104} m_Layer: 8 m_Name: Player m_TagString: Untagged @@ -190,7 +192,7 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8872462076811691049} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 1.08, z: -20} + m_LocalPosition: {x: 0, y: 1.08, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - {fileID: 9057824595171805708} @@ -212,7 +214,7 @@ MonoBehaviour: sceneId: 0 serverOnly: 0 visible: 0 - m_AssetId: + m_AssetId: a5bdca0a2315d43499be7dcef473fbc7 hasSpawned: 0 --- !u!114 &887491563423388292 MonoBehaviour: @@ -227,14 +229,66 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: syncMode: 0 - syncInterval: 0 + syncInterval: 0.1 clientAuthority: 1 - localPositionSensitivity: 0.01 - localRotationSensitivity: 0.01 - localScaleSensitivity: 0.01 - compressRotation: 0 - interpolateScale: 0 + sendInterval: 0.05 + syncPosition: 1 + syncRotation: 1 syncScale: 0 + interpolatePosition: 1 + interpolateRotation: 1 + interpolateScale: 0 + bufferTimeMultiplier: 1 + bufferSizeLimit: 64 + catchupThreshold: 4 + catchupMultiplier: 0.1 + onlySyncOnChange: 1 + bufferResetMultiplier: 5 + positionSensitivity: 0.01 + rotationSensitivity: 0.01 + scaleSensitivity: 0.01 + showGizmos: 0 + showOverlay: 0 + overlayColor: {r: 0, g: 0, b: 0, a: 0.5} +--- !u!114 &-2082299755652640335 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8872462076811691049} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 437e9ed1a55931845bef07e2f5ef57e0, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 +--- !u!114 &8704659178864205755 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8872462076811691049} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e8f68561248aaca4fb96847ce24742ee, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 + characterController: {fileID: 8993127209816276930} + moveSpeed: 8 + turnSensitivity: 5 + maxTurnSpeed: 100 + horizontal: 0 + vertical: 0 + turn: 0 + jumpSpeed: 0 + isGrounded: 1 + isFalling: 0 + velocity: {x: 0, y: 0, z: 0} --- !u!143 &8993127209816276930 CharacterController: m_ObjectHideFlags: 0 @@ -283,31 +337,6 @@ Rigidbody: m_Interpolate: 0 m_Constraints: 0 m_CollisionDetection: 0 ---- !u!114 &8704659178864205755 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8872462076811691049} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: e8f68561248aaca4fb96847ce24742ee, type: 3} - m_Name: - m_EditorClassIdentifier: - syncMode: 0 - syncInterval: 0 - characterController: {fileID: 8993127209816276930} - moveSpeed: 8 - turnSensitivity: 5 - maxTurnSpeed: 150 - horizontal: 0 - vertical: 0 - turn: 0 - jumpSpeed: 0 - isGrounded: 1 - isFalling: 0 - velocity: {x: 0, y: 0, z: 0} --- !u!114 &3086414693581178039 MonoBehaviour: m_ObjectHideFlags: 0 @@ -325,3 +354,18 @@ MonoBehaviour: color: serializedVersion: 2 rgba: 4278190080 +--- !u!114 &-4778368485878020104 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8872462076811691049} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b2e242ee38a14076a39934172a19079b, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 + visRange: 40 diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Player.prefab.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Player.prefab.meta index 7d6d9c0..b91d19d 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Player.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Player.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: a5bdca0a2315d43499be7dcef473fbc7 PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Sphere.prefab b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Sphere.prefab index 0384f0d..434ced3 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Sphere.prefab +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Sphere.prefab @@ -10,7 +10,6 @@ GameObject: m_Component: - component: {fileID: 855244094988030909} - component: {fileID: 855244094988030911} - - component: {fileID: 855244094988030908} - component: {fileID: 855244094988030904} m_Layer: 0 m_Name: Sphere @@ -27,7 +26,7 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 855244094988030905} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 2, y: 1, z: 0} + m_LocalPosition: {x: 0, y: 1, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - {fileID: 1367021456138387611} @@ -48,28 +47,9 @@ MonoBehaviour: m_EditorClassIdentifier: sceneId: 0 serverOnly: 0 - m_AssetId: ---- !u!114 &855244094988030908 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 855244094988030905} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 1731d8de2d0c84333b08ebe1e79f4118, type: 3} - m_Name: - m_EditorClassIdentifier: - syncMode: 0 - syncInterval: 0.1 - visRange: 5 - visUpdateInterval: 0.1 - checkMethod: 0 - forceHidden: 0 - castLayers: - serializedVersion: 2 - m_Bits: 256 + visible: 0 + m_AssetId: f6d08eb9a8e35d84fa30a7e3ae64181a + hasSpawned: 0 --- !u!135 &855244094988030904 SphereCollider: m_ObjectHideFlags: 0 @@ -137,6 +117,7 @@ MeshRenderer: m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -148,6 +129,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Sphere.prefab.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Sphere.prefab.meta index bfe2367..0fb6a41 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Sphere.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Sphere.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: f6d08eb9a8e35d84fa30a7e3ae64181a PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Tank.prefab b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Tank.prefab index 560c37c..47f5856 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Tank.prefab +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Tank.prefab @@ -10,7 +10,6 @@ GameObject: m_Component: - component: {fileID: 160176456} - component: {fileID: 160176459} - - component: {fileID: 160176458} - component: {fileID: 160176461} - component: {fileID: 160176460} - component: {fileID: 160176462} @@ -29,7 +28,7 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 160176457} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: -20, y: 0, z: -20} + m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 5, y: 5, z: 5} m_Children: - {fileID: 3234001708628876000} @@ -51,28 +50,9 @@ MonoBehaviour: m_EditorClassIdentifier: sceneId: 0 serverOnly: 0 - m_AssetId: ---- !u!114 &160176458 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 160176457} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 1731d8de2d0c84333b08ebe1e79f4118, type: 3} - m_Name: - m_EditorClassIdentifier: - syncMode: 0 - syncInterval: 0.1 - visRange: 10 - visUpdateInterval: 0.1 - checkMethod: 0 - forceHidden: 0 - castLayers: - serializedVersion: 2 - m_Bits: 256 + visible: 0 + m_AssetId: ab222ed73ada1ac4ba2f61e843d7627c + hasSpawned: 0 --- !u!114 &160176461 MonoBehaviour: m_ObjectHideFlags: 0 @@ -323,6 +303,7 @@ SkinnedMeshRenderer: m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -334,6 +315,7 @@ SkinnedMeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -375,9 +357,9 @@ MeshCollider: m_Material: {fileID: 0} m_IsTrigger: 0 m_Enabled: 1 - serializedVersion: 3 + serializedVersion: 4 m_Convex: 0 - m_CookingOptions: 14 + m_CookingOptions: 30 m_Mesh: {fileID: 4300000, guid: 38b49695fc0a4418bbc350f2366660c5, type: 3} --- !u!1 &4728827432125738153 GameObject: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Tank.prefab.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Tank.prefab.meta index e16f65c..9c53dff 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Tank.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Tank.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: ab222ed73ada1ac4ba2f61e843d7627c PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Zone.prefab.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Zone.prefab.meta index c079324..7099571 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Zone.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Prefabs/Zone.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: de939020b5e2aa5489ebcc4002d75d54 PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/README.md.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/README.md.meta index 53021c1..470c34f 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/README.md.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/README.md.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 0a023e0d7315ac74094703ab69348733 TextScriptImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes.meta index 06714e8..79b44f1 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes.meta @@ -3,6 +3,6 @@ guid: e989860f377e7764bb7787086ef44ea4 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes/MainScene.unity b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes/MainScene.unity index 568b594..c230c5b 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes/MainScene.unity +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes/MainScene.unity @@ -147,13 +147,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 34755345} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalRotation: {x: 0, y: 0.3826836, z: -0, w: -0.92387944} m_LocalPosition: {x: 20, y: 1, z: -20} m_LocalScale: {x: 10, y: 10, z: 10} m_Children: [] m_Father: {fileID: 47225731} - m_RootOrder: 2 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 315, z: 0} --- !u!23 &34755347 MeshRenderer: m_ObjectHideFlags: 0 @@ -230,8 +230,8 @@ Transform: m_Children: - {fileID: 1727677799} - {fileID: 62078680} - - {fileID: 34755346} - {fileID: 589935541} + - {fileID: 34755346} m_Father: {fileID: 909502395} m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -260,13 +260,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 62078679} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalRotation: {x: 0, y: 0.9238796, z: -0, w: -0.38268325} m_LocalPosition: {x: 20, y: 1, z: 20} m_LocalScale: {x: 10, y: 10, z: 10} m_Children: [] m_Father: {fileID: 47225731} m_RootOrder: 1 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_LocalEulerAnglesHint: {x: 0, y: 225, z: 0} --- !u!23 &62078681 MeshRenderer: m_ObjectHideFlags: 0 @@ -323,7 +323,7 @@ PrefabInstance: m_Modifications: - target: {fileID: 160176456, guid: ab222ed73ada1ac4ba2f61e843d7627c, type: 3} propertyPath: m_RootOrder - value: 0 + value: 2 objectReference: {fileID: 0} - target: {fileID: 160176456, guid: ab222ed73ada1ac4ba2f61e843d7627c, type: 3} propertyPath: m_LocalPosition.x @@ -367,15 +367,11 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 160176457, guid: ab222ed73ada1ac4ba2f61e843d7627c, type: 3} propertyPath: m_Name - value: reconTank - objectReference: {fileID: 0} - - target: {fileID: 160176458, guid: ab222ed73ada1ac4ba2f61e843d7627c, type: 3} - propertyPath: forceHidden - value: 0 + value: Tank objectReference: {fileID: 0} - target: {fileID: 160176459, guid: ab222ed73ada1ac4ba2f61e843d7627c, type: 3} propertyPath: sceneId - value: 1521238664 + value: 1579907432 objectReference: {fileID: 0} - target: {fileID: 160176459, guid: ab222ed73ada1ac4ba2f61e843d7627c, type: 3} propertyPath: m_AssetId @@ -539,13 +535,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 589935540} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalRotation: {x: 0, y: 0.38268343, z: 0, w: 0.92387956} m_LocalPosition: {x: -20, y: 1, z: -20} m_LocalScale: {x: 20, y: 20, z: 20} m_Children: [] m_Father: {fileID: 47225731} - m_RootOrder: 3 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 45, z: 0} --- !u!23 &589935542 MeshRenderer: m_ObjectHideFlags: 0 @@ -785,15 +781,10 @@ PrefabInstance: propertyPath: m_Name value: Sphere objectReference: {fileID: 0} - - target: {fileID: 855244094988030908, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, - type: 3} - propertyPath: forceHidden - value: 0 - objectReference: {fileID: 0} - target: {fileID: 855244094988030909, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, type: 3} propertyPath: m_RootOrder - value: 2 + value: 0 objectReference: {fileID: 0} - target: {fileID: 855244094988030909, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, type: 3} @@ -1122,15 +1113,10 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 6852530814182375317, guid: 12a4c14e672c00b4b840f937d824b890, - type: 3} - propertyPath: forceHidden - value: 0 - objectReference: {fileID: 0} - target: {fileID: 6852530814182375318, guid: 12a4c14e672c00b4b840f937d824b890, type: 3} propertyPath: sceneId - value: 1418438611 + value: 4277306991 objectReference: {fileID: 0} - target: {fileID: 6852530814182375318, guid: 12a4c14e672c00b4b840f937d824b890, type: 3} @@ -1172,9 +1158,9 @@ Transform: m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - - {fileID: 160176456} - - {fileID: 901271863} - {fileID: 748207075} + - {fileID: 901271863} + - {fileID: 160176456} - {fileID: 1284471874} - {fileID: 47225731} m_Father: {fileID: 0} @@ -1274,6 +1260,25 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1047741290} m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1072006166 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: 160176457, guid: ab222ed73ada1ac4ba2f61e843d7627c, + type: 3} + m_PrefabInstance: {fileID: 160176455} + m_PrefabAsset: {fileID: 0} +--- !u!114 &1072006167 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1072006166} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b2e242ee38a14076a39934172a19079b, type: 3} + m_Name: + m_EditorClassIdentifier: + visRange: 10 --- !u!1 &1172568541 GameObject: m_ObjectHideFlags: 0 @@ -1776,8 +1781,9 @@ GameObject: - component: {fileID: 1661834281} - component: {fileID: 1661834278} - component: {fileID: 1661834280} + - component: {fileID: 1661834282} m_Layer: 0 - m_Name: Network + m_Name: NetworkManager m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -1800,8 +1806,6 @@ MonoBehaviour: runInBackground: 1 autoStartServerBuild: 1 serverTickRate: 30 - serverBatching: 0 - serverBatchInterval: 0 offlineScene: onlineScene: transport: {fileID: 1661834280} @@ -1845,12 +1849,15 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Port: 7777 + DualMode: 1 NoDelay: 1 Interval: 10 + Timeout: 10000 FastResend: 2 CongestionWindow: 0 SendWindowSize: 4096 ReceiveWindowSize: 4096 + NonAlloc: 1 debugLog: 0 statisticsGUI: 0 statisticsLog: 0 @@ -1869,6 +1876,20 @@ MonoBehaviour: showGUI: 1 offsetX: 0 offsetY: 0 +--- !u!114 &1661834282 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1661834277} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8f60becab051427fbdd3c8ac9ab4712b, type: 3} + m_Name: + m_EditorClassIdentifier: + visRange: 5 + rebuildInterval: 0.1 --- !u!1 &1727677796 GameObject: m_ObjectHideFlags: 0 @@ -1941,13 +1962,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1727677796} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalRotation: {x: 0, y: 0.92387956, z: 0, w: 0.38268343} m_LocalPosition: {x: -20, y: 1, z: 20} m_LocalScale: {x: 10, y: 10, z: 10} m_Children: [] m_Father: {fileID: 47225731} m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_LocalEulerAnglesHint: {x: 0, y: 135, z: 0} --- !u!1 &1816951099 GameObject: m_ObjectHideFlags: 0 @@ -2073,10 +2094,5 @@ PrefabInstance: propertyPath: m_SceneId value: 2061538488 objectReference: {fileID: 0} - - target: {fileID: 5697694911122891659, guid: e1971f4a8c7661546bc509b44bd91b80, - type: 3} - propertyPath: forceHidden - value: 0 - objectReference: {fileID: 0} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: e1971f4a8c7661546bc509b44bd91b80, type: 3} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes/MainScene.unity.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes/MainScene.unity.meta index 8c5b47b..a231750 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes/MainScene.unity.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes/MainScene.unity.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 7a0eee2f518e9dc4fb628d96dc452faf DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes/SubScene.unity b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes/SubScene.unity index f66f697..37161b3 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes/SubScene.unity +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes/SubScene.unity @@ -38,7 +38,7 @@ RenderSettings: m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 0} m_Sun: {fileID: 0} - m_IndirectSpecularColor: {r: 0.37311953, g: 0.38074014, b: 0.3587274, a: 1} + m_IndirectSpecularColor: {r: 0.12731749, g: 0.13414757, b: 0.1210787, a: 1} m_UseRadianceAmbientProbe: 0 --- !u!157 &3 LightmapSettings: @@ -54,7 +54,7 @@ LightmapSettings: m_EnableBakedLightmaps: 0 m_EnableRealtimeLightmaps: 0 m_LightmapEditorSettings: - serializedVersion: 10 + serializedVersion: 12 m_Resolution: 2 m_BakeResolution: 40 m_AtlasSize: 1024 @@ -62,6 +62,7 @@ LightmapSettings: m_AOMaxDistance: 1 m_CompAOExponent: 1 m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 m_Padding: 2 m_LightmapParameters: {fileID: 0} m_LightmapsBakeMode: 1 @@ -76,10 +77,16 @@ LightmapSettings: 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_PVRFilteringMode: 2 + m_PVREnvironmentMIS: 0 m_PVRCulling: 1 m_PVRFilteringGaussRadiusDirect: 1 m_PVRFilteringGaussRadiusIndirect: 5 @@ -87,7 +94,9 @@ LightmapSettings: m_PVRFilteringAtrousPositionSigmaDirect: 0.5 m_PVRFilteringAtrousPositionSigmaIndirect: 2 m_PVRFilteringAtrousPositionSigmaAO: 1 - m_ShowResolutionOverlay: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 m_LightingDataAsset: {fileID: 112000002, guid: b287b2046ddc6af4b9ddc48ab35ca3cb, type: 2} m_UseShadowmask: 1 @@ -125,7 +134,7 @@ GameObject: - component: {fileID: 21610636} - component: {fileID: 21610635} m_Layer: 0 - m_Name: VisibleRange + m_Name: VisibleRangeCapsule m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -138,13 +147,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 21610633} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalRotation: {x: 0, y: 0.92387956, z: 0, w: 0.38268343} m_LocalPosition: {x: 3, y: 1, z: -3} m_LocalScale: {x: 10, y: 10, z: 10} m_Children: [] m_Father: {fileID: 1690140971} - m_RootOrder: 3 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 135, z: 0} --- !u!23 &21610635 MeshRenderer: m_ObjectHideFlags: 0 @@ -159,6 +168,7 @@ MeshRenderer: m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -170,6 +180,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -207,6 +218,11 @@ PrefabInstance: propertyPath: m_StaticEditorFlags value: 4294967295 objectReference: {fileID: 0} + - target: {fileID: 1076878374699499735, guid: e1971f4a8c7661546bc509b44bd91b80, + type: 3} + propertyPath: m_RootOrder + value: 1 + objectReference: {fileID: 0} - target: {fileID: 1076878374699499735, guid: e1971f4a8c7661546bc509b44bd91b80, type: 3} propertyPath: m_LocalPosition.x @@ -222,6 +238,11 @@ PrefabInstance: propertyPath: m_LocalPosition.z value: -3 objectReference: {fileID: 0} + - target: {fileID: 1076878374699499735, guid: e1971f4a8c7661546bc509b44bd91b80, + type: 3} + propertyPath: m_LocalRotation.w + value: 0.38268343 + objectReference: {fileID: 0} - target: {fileID: 1076878374699499735, guid: e1971f4a8c7661546bc509b44bd91b80, type: 3} propertyPath: m_LocalRotation.x @@ -237,16 +258,6 @@ PrefabInstance: propertyPath: m_LocalRotation.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 1076878374699499735, guid: e1971f4a8c7661546bc509b44bd91b80, - type: 3} - propertyPath: m_LocalRotation.w - value: 0.38268343 - objectReference: {fileID: 0} - - target: {fileID: 1076878374699499735, guid: e1971f4a8c7661546bc509b44bd91b80, - type: 3} - propertyPath: m_RootOrder - value: 3 - objectReference: {fileID: 0} - target: {fileID: 1076878374699499735, guid: e1971f4a8c7661546bc509b44bd91b80, type: 3} propertyPath: m_LocalEulerAnglesHint.x @@ -262,26 +273,24 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 2648107611936813301, guid: e1971f4a8c7661546bc509b44bd91b80, - type: 3} - propertyPath: m_SceneId - value: 15452677 - objectReference: {fileID: 0} - target: {fileID: 2648107611936813301, guid: e1971f4a8c7661546bc509b44bd91b80, type: 3} propertyPath: sceneId value: 3231330715 objectReference: {fileID: 0} - - target: {fileID: 5697694911122891659, guid: e1971f4a8c7661546bc509b44bd91b80, + - target: {fileID: 2648107611936813301, guid: e1971f4a8c7661546bc509b44bd91b80, type: 3} - propertyPath: castLayers.m_Bits - value: 256 + propertyPath: m_SceneId + value: 15452677 objectReference: {fileID: 0} - - target: {fileID: 5697694911122891659, guid: e1971f4a8c7661546bc509b44bd91b80, - type: 3} + - target: {fileID: 0} propertyPath: forceHidden value: 0 objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: castLayers.m_Bits + value: 256 + objectReference: {fileID: 0} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: e1971f4a8c7661546bc509b44bd91b80, type: 3} --- !u!1 &222935521 @@ -296,7 +305,7 @@ GameObject: - component: {fileID: 222935524} - component: {fileID: 222935523} m_Layer: 0 - m_Name: VisibleRange + m_Name: VisibleRangeSphere m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -309,13 +318,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 222935521} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalRotation: {x: 0, y: 0.3826836, z: -0, w: -0.92387944} m_LocalPosition: {x: -3, y: 1, z: 3} m_LocalScale: {x: 10, y: 10, z: 10} m_Children: [] m_Father: {fileID: 1690140971} - m_RootOrder: 1 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 315, z: 0} --- !u!23 &222935523 MeshRenderer: m_ObjectHideFlags: 0 @@ -330,6 +339,7 @@ MeshRenderer: m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -341,6 +351,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -373,7 +384,7 @@ GameObject: - component: {fileID: 507729672} - component: {fileID: 507729671} m_Layer: 0 - m_Name: VisibleRange + m_Name: VisibleRangeCylinder m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -386,13 +397,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 507729669} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalRotation: {x: 0, y: 0.38268343, z: 0, w: 0.92387956} m_LocalPosition: {x: 3, y: 1, z: 3} m_LocalScale: {x: 10, y: 10, z: 10} m_Children: [] m_Father: {fileID: 1690140971} - m_RootOrder: 2 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 45, z: 0} --- !u!23 &507729671 MeshRenderer: m_ObjectHideFlags: 0 @@ -407,6 +418,7 @@ MeshRenderer: m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -418,6 +430,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -466,9 +479,9 @@ Transform: m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - {fileID: 1722279649} + - {fileID: 21610634} - {fileID: 222935522} - {fileID: 507729670} - - {fileID: 21610634} m_Father: {fileID: 0} m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -484,7 +497,7 @@ GameObject: - component: {fileID: 1722279651} - component: {fileID: 1722279650} m_Layer: 0 - m_Name: VisibleRange + m_Name: VisibleRangeCube m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -497,13 +510,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1722279648} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalRotation: {x: 0, y: 0.9238796, z: -0, w: -0.38268325} m_LocalPosition: {x: -3, y: 1, z: -3} m_LocalScale: {x: 10, y: 10, z: 10} m_Children: [] m_Father: {fileID: 1690140971} m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_LocalEulerAnglesHint: {x: 0, y: 225, z: 0} --- !u!23 &1722279650 MeshRenderer: m_ObjectHideFlags: 0 @@ -518,6 +531,7 @@ MeshRenderer: m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -529,6 +543,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -561,15 +576,48 @@ PrefabInstance: propertyPath: m_Name value: Sphere objectReference: {fileID: 0} - - target: {fileID: 855244094988030908, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, + - target: {fileID: 855244094988030909, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, type: 3} + propertyPath: m_LocalPosition.x + value: -3 + objectReference: {fileID: 0} + - target: {fileID: 855244094988030909, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, + type: 3} + propertyPath: m_LocalPosition.z + value: 3 + objectReference: {fileID: 0} + - target: {fileID: 855244094988030909, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, + type: 3} + propertyPath: m_LocalRotation.w + value: -0.92387944 + objectReference: {fileID: 0} + - target: {fileID: 855244094988030909, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, + type: 3} + propertyPath: m_LocalRotation.y + value: 0.3826836 + objectReference: {fileID: 0} + - target: {fileID: 855244094988030909, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, + type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 855244094988030909, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 315 + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: forceHidden + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 0} propertyPath: castLayers.m_Bits value: 256 objectReference: {fileID: 0} - - target: {fileID: 855244094988030908, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, + - target: {fileID: 855244094988030909, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, type: 3} - propertyPath: forceHidden - value: 0 + propertyPath: m_RootOrder + value: 2 objectReference: {fileID: 0} - target: {fileID: 855244094988030909, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, type: 3} @@ -586,6 +634,11 @@ PrefabInstance: propertyPath: m_LocalPosition.z value: 3 objectReference: {fileID: 0} + - target: {fileID: 855244094988030909, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, + type: 3} + propertyPath: m_LocalRotation.w + value: -0.92387944 + objectReference: {fileID: 0} - target: {fileID: 855244094988030909, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, type: 3} propertyPath: m_LocalRotation.x @@ -601,16 +654,6 @@ PrefabInstance: propertyPath: m_LocalRotation.z value: -0 objectReference: {fileID: 0} - - target: {fileID: 855244094988030909, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, - type: 3} - propertyPath: m_LocalRotation.w - value: -0.92387944 - objectReference: {fileID: 0} - - target: {fileID: 855244094988030909, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, - type: 3} - propertyPath: m_RootOrder - value: 1 - objectReference: {fileID: 0} - target: {fileID: 855244094988030909, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, type: 3} propertyPath: m_LocalEulerAnglesHint.x @@ -628,13 +671,13 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 855244094988030911, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, type: 3} - propertyPath: m_SceneId - value: 11893259 + propertyPath: sceneId + value: 1396685688 objectReference: {fileID: 0} - target: {fileID: 855244094988030911, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, type: 3} - propertyPath: sceneId - value: 1396685688 + propertyPath: m_SceneId + value: 11893259 objectReference: {fileID: 0} - target: {fileID: 855244096231524078, guid: f6d08eb9a8e35d84fa30a7e3ae64181a, type: 3} @@ -675,25 +718,15 @@ PrefabInstance: propertyPath: m_ReflectionProbeUsage value: 0 objectReference: {fileID: 0} - - target: {fileID: 5623359707189648404, guid: 4ff300cf6bb3c6342a9552c4f18788c8, - type: 3} - propertyPath: m_SceneId - value: 4733130 - objectReference: {fileID: 0} - target: {fileID: 5623359707189648404, guid: 4ff300cf6bb3c6342a9552c4f18788c8, type: 3} propertyPath: sceneId value: 86995073 objectReference: {fileID: 0} - - target: {fileID: 5623359707189648405, guid: 4ff300cf6bb3c6342a9552c4f18788c8, + - target: {fileID: 5623359707189648404, guid: 4ff300cf6bb3c6342a9552c4f18788c8, type: 3} - propertyPath: castLayers.m_Bits - value: 256 - objectReference: {fileID: 0} - - target: {fileID: 5623359707189648405, guid: 4ff300cf6bb3c6342a9552c4f18788c8, - type: 3} - propertyPath: forceHidden - value: 0 + propertyPath: m_SceneId + value: 4733130 objectReference: {fileID: 0} - target: {fileID: 5623359707189648426, guid: 4ff300cf6bb3c6342a9552c4f18788c8, type: 3} @@ -702,13 +735,13 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 5623359707189648426, guid: 4ff300cf6bb3c6342a9552c4f18788c8, type: 3} - propertyPath: m_LocalPosition.y - value: 1 + propertyPath: m_LocalPosition.z + value: -3 objectReference: {fileID: 0} - target: {fileID: 5623359707189648426, guid: 4ff300cf6bb3c6342a9552c4f18788c8, type: 3} - propertyPath: m_LocalPosition.z - value: -3 + propertyPath: m_LocalRotation.w + value: -0.38268325 objectReference: {fileID: 0} - target: {fileID: 5623359707189648426, guid: 4ff300cf6bb3c6342a9552c4f18788c8, type: 3} @@ -727,14 +760,57 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 5623359707189648426, guid: 4ff300cf6bb3c6342a9552c4f18788c8, type: 3} - propertyPath: m_LocalRotation.w - value: -0.38268325 + propertyPath: m_LocalEulerAnglesHint.y + value: 225 + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: forceHidden + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: castLayers.m_Bits + value: 256 objectReference: {fileID: 0} - target: {fileID: 5623359707189648426, guid: 4ff300cf6bb3c6342a9552c4f18788c8, type: 3} propertyPath: m_RootOrder value: 0 objectReference: {fileID: 0} + - target: {fileID: 5623359707189648426, guid: 4ff300cf6bb3c6342a9552c4f18788c8, + type: 3} + propertyPath: m_LocalPosition.x + value: -3 + objectReference: {fileID: 0} + - target: {fileID: 5623359707189648426, guid: 4ff300cf6bb3c6342a9552c4f18788c8, + type: 3} + propertyPath: m_LocalPosition.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 5623359707189648426, guid: 4ff300cf6bb3c6342a9552c4f18788c8, + type: 3} + propertyPath: m_LocalPosition.z + value: -3 + objectReference: {fileID: 0} + - target: {fileID: 5623359707189648426, guid: 4ff300cf6bb3c6342a9552c4f18788c8, + type: 3} + propertyPath: m_LocalRotation.w + value: -0.38268325 + objectReference: {fileID: 0} + - target: {fileID: 5623359707189648426, guid: 4ff300cf6bb3c6342a9552c4f18788c8, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5623359707189648426, guid: 4ff300cf6bb3c6342a9552c4f18788c8, + type: 3} + propertyPath: m_LocalRotation.y + value: 0.9238796 + objectReference: {fileID: 0} + - target: {fileID: 5623359707189648426, guid: 4ff300cf6bb3c6342a9552c4f18788c8, + type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} - target: {fileID: 5623359707189648426, guid: 4ff300cf6bb3c6342a9552c4f18788c8, type: 3} propertyPath: m_LocalEulerAnglesHint.x @@ -752,13 +828,13 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 5623359707189648430, guid: 4ff300cf6bb3c6342a9552c4f18788c8, type: 3} - propertyPath: m_Name - value: Cube + propertyPath: m_Icon + value: objectReference: {fileID: 0} - target: {fileID: 5623359707189648430, guid: 4ff300cf6bb3c6342a9552c4f18788c8, type: 3} - propertyPath: m_Icon - value: + propertyPath: m_Name + value: Cube objectReference: {fileID: 0} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 4ff300cf6bb3c6342a9552c4f18788c8, type: 3} @@ -774,6 +850,11 @@ PrefabInstance: propertyPath: m_Name value: Cylinder objectReference: {fileID: 0} + - target: {fileID: 6852530814182375316, guid: 12a4c14e672c00b4b840f937d824b890, + type: 3} + propertyPath: m_RootOrder + value: 3 + objectReference: {fileID: 0} - target: {fileID: 6852530814182375316, guid: 12a4c14e672c00b4b840f937d824b890, type: 3} propertyPath: m_LocalPosition.x @@ -789,6 +870,11 @@ PrefabInstance: propertyPath: m_LocalPosition.z value: 3 objectReference: {fileID: 0} + - target: {fileID: 6852530814182375316, guid: 12a4c14e672c00b4b840f937d824b890, + type: 3} + propertyPath: m_LocalRotation.w + value: 0.92387956 + objectReference: {fileID: 0} - target: {fileID: 6852530814182375316, guid: 12a4c14e672c00b4b840f937d824b890, type: 3} propertyPath: m_LocalRotation.x @@ -804,16 +890,6 @@ PrefabInstance: propertyPath: m_LocalRotation.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 6852530814182375316, guid: 12a4c14e672c00b4b840f937d824b890, - type: 3} - propertyPath: m_LocalRotation.w - value: 0.92387956 - objectReference: {fileID: 0} - - target: {fileID: 6852530814182375316, guid: 12a4c14e672c00b4b840f937d824b890, - type: 3} - propertyPath: m_RootOrder - value: 2 - objectReference: {fileID: 0} - target: {fileID: 6852530814182375316, guid: 12a4c14e672c00b4b840f937d824b890, type: 3} propertyPath: m_LocalEulerAnglesHint.x @@ -829,15 +905,23 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 6852530814182375317, guid: 12a4c14e672c00b4b840f937d824b890, + - target: {fileID: 6852530814182375318, guid: 12a4c14e672c00b4b840f937d824b890, type: 3} + propertyPath: sceneId + value: 3262924414 + objectReference: {fileID: 0} + - target: {fileID: 0} + propertyPath: forceHidden + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 0} propertyPath: castLayers.m_Bits value: 256 objectReference: {fileID: 0} - - target: {fileID: 6852530814182375317, guid: 12a4c14e672c00b4b840f937d824b890, + - target: {fileID: 6852530814182375318, guid: 12a4c14e672c00b4b840f937d824b890, type: 3} - propertyPath: forceHidden - value: 0 + propertyPath: sceneId + value: 3262924414 objectReference: {fileID: 0} - target: {fileID: 6852530814182375318, guid: 12a4c14e672c00b4b840f937d824b890, type: 3} @@ -849,10 +933,5 @@ PrefabInstance: propertyPath: m_SceneId value: 4633990 objectReference: {fileID: 0} - - target: {fileID: 6852530814182375318, guid: 12a4c14e672c00b4b840f937d824b890, - type: 3} - propertyPath: sceneId - value: 1889713496 - objectReference: {fileID: 0} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 12a4c14e672c00b4b840f937d824b890, type: 3} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes/SubScene.unity.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes/SubScene.unity.meta index 25341d3..94a2050 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes/SubScene.unity.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scenes/SubScene.unity.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 7f4fd683fc6d866418c95f99977533a6 DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts.meta index 6aebeb5..cb97b32 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts.meta @@ -3,6 +3,6 @@ guid: f9c36b0deb5d9b245b7c97e3d6eeed29 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/AdditiveNetworkManager.cs b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/AdditiveNetworkManager.cs index a022c2c..756a5dc 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/AdditiveNetworkManager.cs +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/AdditiveNetworkManager.cs @@ -2,7 +2,12 @@ using System.Collections; using UnityEngine; using UnityEngine.SceneManagement; -namespace Mirror.Examples.Additive +/* + Documentation: https://mirror-networking.gitbook.io/docs/components/network-manager + API Reference: https://mirror-networking.com/docs/api/Mirror.NetworkManager.html +*/ + +namespace Mirror.Examples.AdditiveScenes { [AddComponentMenu("")] public class AdditiveNetworkManager : NetworkManager @@ -25,6 +30,16 @@ namespace Mirror.Examples.Additive Instantiate(Zone); } + public override void OnStopServer() + { + StartCoroutine(UnloadScenes()); + } + + public override void OnStopClient() + { + StartCoroutine(UnloadScenes()); + } + IEnumerator LoadSubScenes() { Debug.Log("Loading Scenes"); @@ -36,16 +51,6 @@ namespace Mirror.Examples.Additive } } - public override void OnStopServer() - { - StartCoroutine(UnloadScenes()); - } - - public override void OnStopClient() - { - StartCoroutine(UnloadScenes()); - } - IEnumerator UnloadScenes() { Debug.Log("Unloading Subscenes"); diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/AdditiveNetworkManager.cs.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/AdditiveNetworkManager.cs.meta index 7b92eb6..0df3fb3 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/AdditiveNetworkManager.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/AdditiveNetworkManager.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/PlayerCamera.cs b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/PlayerCamera.cs new file mode 100644 index 0000000..95a52f5 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/PlayerCamera.cs @@ -0,0 +1,41 @@ +using UnityEngine; +using UnityEngine.SceneManagement; + +// This sets up the scene camera for the local player + +namespace Mirror.Examples.AdditiveScenes +{ + public class PlayerCamera : NetworkBehaviour + { + Camera mainCam; + + void Awake() + { + mainCam = Camera.main; + } + + public override void OnStartLocalPlayer() + { + if (mainCam != null) + { + // configure and make camera a child of player with 3rd person offset + mainCam.orthographic = false; + mainCam.transform.SetParent(transform); + mainCam.transform.localPosition = new Vector3(0f, 3f, -8f); + mainCam.transform.localEulerAngles = new Vector3(10f, 0f, 0f); + } + } + + public override void OnStopLocalPlayer() + { + if (mainCam != null) + { + mainCam.transform.SetParent(null); + SceneManager.MoveGameObjectToScene(mainCam.gameObject, SceneManager.GetActiveScene()); + mainCam.orthographic = true; + mainCam.transform.localPosition = new Vector3(0f, 70f, 0f); + mainCam.transform.localEulerAngles = new Vector3(90f, 0f, 0f); + } + } + } +} diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/PlayerCamera.cs.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/PlayerCamera.cs.meta new file mode 100644 index 0000000..36a243f --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/PlayerCamera.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 437e9ed1a55931845bef07e2f5ef57e0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/PlayerController.cs b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/PlayerController.cs index de38b81..f27163e 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/PlayerController.cs +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/PlayerController.cs @@ -1,6 +1,6 @@ using UnityEngine; -namespace Mirror.Examples.Additive +namespace Mirror.Examples.AdditiveScenes { [RequireComponent(typeof(CapsuleCollider))] [RequireComponent(typeof(CharacterController))] @@ -14,36 +14,21 @@ namespace Mirror.Examples.Additive { if (characterController == null) characterController = GetComponent(); - } - void Start() - { - characterController.enabled = isLocalPlayer; + characterController.enabled = false; + GetComponent().isKinematic = true; + GetComponent().clientAuthority = true; } public override void OnStartLocalPlayer() { - Camera.main.orthographic = false; - Camera.main.transform.SetParent(transform); - Camera.main.transform.localPosition = new Vector3(0f, 3f, -8f); - Camera.main.transform.localEulerAngles = new Vector3(10f, 0f, 0f); - } - - void OnDisable() - { - if (isLocalPlayer && Camera.main != null) - { - Camera.main.orthographic = true; - Camera.main.transform.SetParent(null); - Camera.main.transform.localPosition = new Vector3(0f, 70f, 0f); - Camera.main.transform.localEulerAngles = new Vector3(90f, 0f, 0f); - } + characterController.enabled = true; } [Header("Movement Settings")] public float moveSpeed = 8f; public float turnSensitivity = 5f; - public float maxTurnSpeed = 150f; + public float maxTurnSpeed = 100f; [Header("Diagnostics")] public float horizontal; @@ -56,7 +41,7 @@ namespace Mirror.Examples.Additive void Update() { - if (!isLocalPlayer || !characterController.enabled) + if (!isLocalPlayer || characterController == null || !characterController.enabled) return; horizontal = Input.GetAxis("Horizontal"); @@ -88,7 +73,7 @@ namespace Mirror.Examples.Additive void FixedUpdate() { - if (!isLocalPlayer || characterController == null) + if (!isLocalPlayer || characterController == null || !characterController.enabled) return; transform.Rotate(0f, turn * Time.fixedDeltaTime, 0f); diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/PlayerController.cs.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/PlayerController.cs.meta index 98c0e27..5deced6 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/PlayerController.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/PlayerController.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/RandomColor.cs b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/RandomColor.cs index 34d650a..42f1b58 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/RandomColor.cs +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/RandomColor.cs @@ -1,6 +1,6 @@ using UnityEngine; -namespace Mirror.Examples.Additive +namespace Mirror.Examples.AdditiveScenes { public class RandomColor : NetworkBehaviour { diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/RandomColor.cs.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/RandomColor.cs.meta index d3c401f..2c0008b 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/RandomColor.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/RandomColor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/ShootingTankBehaviour.cs b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/ShootingTankBehaviour.cs index 155d7ce..4492324 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/ShootingTankBehaviour.cs +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/ShootingTankBehaviour.cs @@ -1,6 +1,6 @@ using UnityEngine; -namespace Mirror.Examples.Additive +namespace Mirror.Examples.AdditiveScenes { // This script demonstrates the NetworkAnimator and how to leverage // the built-in observers system to track players. diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/ShootingTankBehaviour.cs.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/ShootingTankBehaviour.cs.meta index 315914a..90771ab 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/ShootingTankBehaviour.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/ShootingTankBehaviour.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/ZoneHandler.cs b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/ZoneHandler.cs index 894ec7b..180b271 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/ZoneHandler.cs +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/ZoneHandler.cs @@ -1,34 +1,33 @@ using UnityEngine; -namespace Mirror.Examples.Additive +namespace Mirror.Examples.AdditiveScenes { - // This script is attached to a scene object called Zone that is on the Player layer and has: - // - Sphere Collider with isTrigger = true - // - Network Identity with Server Only checked - // These OnTrigger events only run on the server and will only send a message to the player - // that entered the Zone to load the subscene assigned to the subscene property. + // This script is attached to a prefab called Zone that is on the Player layer + // AdditiveNetworkManager, in OnStartServer, instantiates the prefab only on the server. + // It never exists for clients (other than host client if there is one). + // The prefab has a Sphere Collider with isTrigger = true. + // These OnTrigger events only run on the server and will only send a message to the + // client that entered the Zone to load the subscene assigned to the subscene property. public class ZoneHandler : MonoBehaviour { [Scene] [Tooltip("Assign the sub-scene to load for this zone")] public string subScene; + [ServerCallback] void OnTriggerEnter(Collider other) { - if (!NetworkServer.active) return; - - // Debug.LogFormat(LogType.Log, "Loading {0}", subScene); + // Debug.Log($"Loading {subScene}"); NetworkIdentity networkIdentity = other.gameObject.GetComponent(); SceneMessage message = new SceneMessage{ sceneName = subScene, sceneOperation = SceneOperation.LoadAdditive }; networkIdentity.connectionToClient.Send(message); } + [ServerCallback] void OnTriggerExit(Collider other) { - if (!NetworkServer.active) return; - - // Debug.LogFormat(LogType.Log, "Unloading {0}", subScene); + // Debug.Log($"Unloading {subScene}"); NetworkIdentity networkIdentity = other.gameObject.GetComponent(); SceneMessage message = new SceneMessage{ sceneName = subScene, sceneOperation = SceneOperation.UnloadAdditive }; diff --git a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/ZoneHandler.cs.meta b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/ZoneHandler.cs.meta index 88adef5..126d072 100644 --- a/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/ZoneHandler.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/AdditiveScenes/Scripts/ZoneHandler.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Basic.meta b/UnityProject/Assets/Mirror/Examples/Basic.meta index b61f55f..653ea78 100644 --- a/UnityProject/Assets/Mirror/Examples/Basic.meta +++ b/UnityProject/Assets/Mirror/Examples/Basic.meta @@ -3,6 +3,6 @@ guid: 0ea49fcefbc864e19a94091a170fc06c folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Basic/Prefabs.meta b/UnityProject/Assets/Mirror/Examples/Basic/Prefabs.meta index 261c187..234c22a 100644 --- a/UnityProject/Assets/Mirror/Examples/Basic/Prefabs.meta +++ b/UnityProject/Assets/Mirror/Examples/Basic/Prefabs.meta @@ -3,6 +3,6 @@ guid: 4f821a97809492a479cac0843442e245 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Basic/Prefabs/Player.prefab b/UnityProject/Assets/Mirror/Examples/Basic/Prefabs/Player.prefab index 2f6dd94..b9a33ea 100644 --- a/UnityProject/Assets/Mirror/Examples/Basic/Prefabs/Player.prefab +++ b/UnityProject/Assets/Mirror/Examples/Basic/Prefabs/Player.prefab @@ -26,7 +26,7 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 897184729387425976} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 562, y: 368, z: 0} + m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 0} @@ -46,7 +46,8 @@ MonoBehaviour: m_EditorClassIdentifier: sceneId: 0 serverOnly: 0 - m_AssetId: + visible: 0 + m_AssetId: dc2c4328591bef748abb8df795c17202 hasSpawned: 0 --- !u!114 &8550999602067651493 MonoBehaviour: diff --git a/UnityProject/Assets/Mirror/Examples/Basic/Prefabs/Player.prefab.meta b/UnityProject/Assets/Mirror/Examples/Basic/Prefabs/Player.prefab.meta index 07fac0c..a943869 100644 --- a/UnityProject/Assets/Mirror/Examples/Basic/Prefabs/Player.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/Basic/Prefabs/Player.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: dc2c4328591bef748abb8df795c17202 PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Basic/Prefabs/PlayerUI.prefab.meta b/UnityProject/Assets/Mirror/Examples/Basic/Prefabs/PlayerUI.prefab.meta index c522c6f..c6a8284 100644 --- a/UnityProject/Assets/Mirror/Examples/Basic/Prefabs/PlayerUI.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/Basic/Prefabs/PlayerUI.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 22f1fa3a0aff72b46a371f667bb4fb30 PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Basic/README.md.meta b/UnityProject/Assets/Mirror/Examples/Basic/README.md.meta index ce85eed..d4e1715 100644 --- a/UnityProject/Assets/Mirror/Examples/Basic/README.md.meta +++ b/UnityProject/Assets/Mirror/Examples/Basic/README.md.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 67177defd4d334a549e535f10506cc66 TextScriptImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Basic/Scenes.meta b/UnityProject/Assets/Mirror/Examples/Basic/Scenes.meta index 56c0473..0c751c6 100644 --- a/UnityProject/Assets/Mirror/Examples/Basic/Scenes.meta +++ b/UnityProject/Assets/Mirror/Examples/Basic/Scenes.meta @@ -3,6 +3,6 @@ guid: 16f46473489d3364badc2f37c4db8634 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Basic/Scenes/Example.unity b/UnityProject/Assets/Mirror/Examples/Basic/Scenes/Example.unity index d3cbbce..66697db 100644 --- a/UnityProject/Assets/Mirror/Examples/Basic/Scenes/Example.unity +++ b/UnityProject/Assets/Mirror/Examples/Basic/Scenes/Example.unity @@ -54,7 +54,7 @@ LightmapSettings: m_EnableBakedLightmaps: 0 m_EnableRealtimeLightmaps: 0 m_LightmapEditorSettings: - serializedVersion: 10 + serializedVersion: 12 m_Resolution: 2 m_BakeResolution: 40 m_AtlasSize: 1024 @@ -62,6 +62,7 @@ LightmapSettings: m_AOMaxDistance: 1 m_CompAOExponent: 1 m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 m_Padding: 2 m_LightmapParameters: {fileID: 0} m_LightmapsBakeMode: 1 @@ -76,10 +77,16 @@ LightmapSettings: 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_PVRFilteringMode: 2 + m_PVREnvironmentMIS: 0 m_PVRCulling: 1 m_PVRFilteringGaussRadiusDirect: 1 m_PVRFilteringGaussRadiusIndirect: 5 @@ -87,7 +94,9 @@ LightmapSettings: m_PVRFilteringAtrousPositionSigmaDirect: 0.5 m_PVRFilteringAtrousPositionSigmaIndirect: 2 m_PVRFilteringAtrousPositionSigmaAO: 1 - m_ShowResolutionOverlay: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 m_LightingDataAsset: {fileID: 0} m_UseShadowmask: 1 --- !u!196 &4 @@ -143,7 +152,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 6442dc8070ceb41f094e44de0bf87274, type: 3} m_Name: m_EditorClassIdentifier: - showGUI: 1 offsetX: 0 offsetY: 0 --- !u!114 &249891955 @@ -159,12 +167,17 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Port: 7777 + DualMode: 1 NoDelay: 1 Interval: 10 + Timeout: 10000 FastResend: 2 CongestionWindow: 0 SendWindowSize: 4096 ReceiveWindowSize: 4096 + NonAlloc: 0 + ReliableMaxMessageSize: 4811624 + UnreliableMaxMessageSize: 1199 debugLog: 0 statisticsGUI: 0 statisticsLog: 0 @@ -183,25 +196,18 @@ MonoBehaviour: dontDestroyOnLoad: 0 runInBackground: 1 autoStartServerBuild: 1 - showDebugMessages: 0 serverTickRate: 30 - serverBatching: 0 - serverBatchInterval: 0 offlineScene: onlineScene: transport: {fileID: 249891955} networkAddress: localhost maxConnections: 100 - disconnectInactiveConnections: 0 - disconnectInactiveTimeout: 60 authenticator: {fileID: 0} playerPrefab: {fileID: 897184729387425976, guid: dc2c4328591bef748abb8df795c17202, type: 3} autoCreatePlayer: 1 playerSpawnMethod: 1 spawnPrefabs: [] - mainPanel: {fileID: 1712119861} - playersPanel: {fileID: 379082679} --- !u!4 &249891957 Transform: m_ObjectHideFlags: 0 @@ -245,9 +251,10 @@ Camera: m_ClearFlags: 2 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_GateFitMode: 2 m_FocalLength: 50 m_NormalizedViewPortRect: serializedVersion: 2 @@ -337,7 +344,7 @@ MonoBehaviour: m_GameObject: {fileID: 379082678} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -2095666955, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 8a8695521f0d02e499659fee002a26c2, type: 3} m_Name: m_EditorClassIdentifier: m_Padding: @@ -369,12 +376,13 @@ MonoBehaviour: m_GameObject: {fileID: 379082678} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0, g: 0, b: 0, a: 0.039215688} m_RaycastTarget: 0 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -387,6 +395,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!114 &379082683 MonoBehaviour: m_ObjectHideFlags: 0 @@ -396,7 +405,7 @@ MonoBehaviour: m_GameObject: {fileID: 379082678} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -1200242548, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3} m_Name: m_EditorClassIdentifier: m_ShowMaskGraphic: 0 @@ -428,14 +437,11 @@ MonoBehaviour: m_GameObject: {fileID: 533055200} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1301386320, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 266fac335be17a243af86e88de84766d, type: 3} m_Name: m_EditorClassIdentifier: - m_IgnoreReversedGraphics: 1 - m_BlockingObjects: 0 - m_BlockingMask: - serializedVersion: 2 - m_Bits: 4294967295 + mainPanel: {fileID: 1712119861} + playersPanel: {fileID: 379082679} --- !u!114 &533055202 MonoBehaviour: m_ObjectHideFlags: 0 @@ -445,7 +451,7 @@ MonoBehaviour: m_GameObject: {fileID: 533055200} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1980459831, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} m_Name: m_EditorClassIdentifier: m_UiScaleMode: 0 @@ -546,12 +552,13 @@ MonoBehaviour: m_GameObject: {fileID: 864730912} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 0 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -564,6 +571,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &864730916 CanvasRenderer: m_ObjectHideFlags: 0 @@ -599,7 +607,7 @@ MonoBehaviour: m_GameObject: {fileID: 1356257340} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1077351063, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} m_Name: m_EditorClassIdentifier: m_HorizontalAxis: Horizontal @@ -618,7 +626,7 @@ MonoBehaviour: m_GameObject: {fileID: 1356257340} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -619905303, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} m_Name: m_EditorClassIdentifier: m_FirstSelected: {fileID: 0} @@ -685,12 +693,13 @@ MonoBehaviour: m_GameObject: {fileID: 1712119860} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0, g: 0, b: 0, a: 0} m_RaycastTarget: 0 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -703,6 +712,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &1712119863 CanvasRenderer: m_ObjectHideFlags: 0 diff --git a/UnityProject/Assets/Mirror/Examples/Basic/Scenes/Example.unity.meta b/UnityProject/Assets/Mirror/Examples/Basic/Scenes/Example.unity.meta index 7d2a8de..25c602e 100644 --- a/UnityProject/Assets/Mirror/Examples/Basic/Scenes/Example.unity.meta +++ b/UnityProject/Assets/Mirror/Examples/Basic/Scenes/Example.unity.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: b30904751905d3f4dacde62ac85ec7c2 DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Basic/Scripts.meta b/UnityProject/Assets/Mirror/Examples/Basic/Scripts.meta index b5807e6..5cc0800 100644 --- a/UnityProject/Assets/Mirror/Examples/Basic/Scripts.meta +++ b/UnityProject/Assets/Mirror/Examples/Basic/Scripts.meta @@ -3,6 +3,6 @@ guid: 9c5291659f25af9409bbc25a2d37d628 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Basic/Scripts/BasicNetManager.cs b/UnityProject/Assets/Mirror/Examples/Basic/Scripts/BasicNetManager.cs index e9c9d57..4da39b5 100644 --- a/UnityProject/Assets/Mirror/Examples/Basic/Scripts/BasicNetManager.cs +++ b/UnityProject/Assets/Mirror/Examples/Basic/Scripts/BasicNetManager.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using UnityEngine; /* @@ -11,20 +10,12 @@ namespace Mirror.Examples.Basic [AddComponentMenu("")] public class BasicNetManager : NetworkManager { - [Header("Canvas UI")] - - [Tooltip("Assign Main Panel so it can be turned on from Player:OnStartClient")] - public RectTransform mainPanel; - - [Tooltip("Assign Players Panel for instantiating PlayerUI as child")] - public RectTransform playersPanel; - /// /// Called on the server when a client adds a new player with NetworkClient.AddPlayer. /// The default implementation for this function creates a new player object from the playerPrefab. /// /// Connection from client. - public override void OnServerAddPlayer(NetworkConnection conn) + public override void OnServerAddPlayer(NetworkConnectionToClient conn) { base.OnServerAddPlayer(conn); Player.ResetPlayerNumbers(); @@ -35,11 +26,10 @@ namespace Mirror.Examples.Basic /// This is called on the Server when a Client disconnects from the Server. Use an override to decide what should happen when a disconnection is detected. /// /// Connection from client. - public override void OnServerDisconnect(NetworkConnection conn) + public override void OnServerDisconnect(NetworkConnectionToClient conn) { base.OnServerDisconnect(conn); Player.ResetPlayerNumbers(); } - } } diff --git a/UnityProject/Assets/Mirror/Examples/Basic/Scripts/BasicNetManager.cs.meta b/UnityProject/Assets/Mirror/Examples/Basic/Scripts/BasicNetManager.cs.meta index d249881..35918e9 100644 --- a/UnityProject/Assets/Mirror/Examples/Basic/Scripts/BasicNetManager.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Basic/Scripts/BasicNetManager.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Basic/Scripts/CanvasUI.cs b/UnityProject/Assets/Mirror/Examples/Basic/Scripts/CanvasUI.cs new file mode 100644 index 0000000..d5c8987 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Basic/Scripts/CanvasUI.cs @@ -0,0 +1,21 @@ +using UnityEngine; + +namespace Mirror.Examples.Basic +{ + public class CanvasUI : MonoBehaviour + { + [Tooltip("Assign Main Panel so it can be turned on from Player:OnStartClient")] + public RectTransform mainPanel; + + [Tooltip("Assign Players Panel for instantiating PlayerUI as child")] + public RectTransform playersPanel; + + // static instance that can be referenced directly from Player script + public static CanvasUI instance; + + void Awake() + { + instance = this; + } + } +} diff --git a/UnityProject/Assets/Mirror/Examples/Basic/Scripts/CanvasUI.cs.meta b/UnityProject/Assets/Mirror/Examples/Basic/Scripts/CanvasUI.cs.meta new file mode 100644 index 0000000..30ad077 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Basic/Scripts/CanvasUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 266fac335be17a243af86e88de84766d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Basic/Scripts/Player.cs b/UnityProject/Assets/Mirror/Examples/Basic/Scripts/Player.cs index 31949dc..f62bc69 100644 --- a/UnityProject/Assets/Mirror/Examples/Basic/Scripts/Player.cs +++ b/UnityProject/Assets/Mirror/Examples/Basic/Scripts/Player.cs @@ -5,26 +5,21 @@ namespace Mirror.Examples.Basic { public class Player : NetworkBehaviour { - // Events that the UI will subscribe to - public event System.Action OnPlayerNumberChanged; + // Events that the PlayerUI will subscribe to + public event System.Action OnPlayerNumberChanged; public event System.Action OnPlayerColorChanged; - public event System.Action OnPlayerDataChanged; + public event System.Action OnPlayerDataChanged; // Players List to manage playerNumber - internal static readonly List playersList = new List(); - - internal static void ResetPlayerNumbers() - { - int playerNumber = 0; - foreach (Player player in playersList) - { - player.playerNumber = playerNumber++; - } - } + static readonly List playersList = new List(); [Header("Player UI")] public GameObject playerUIPrefab; - GameObject playerUI; + + GameObject playerUIObject; + PlayerUI playerUI = null; + + #region SyncVars [Header("SyncVars")] @@ -32,13 +27,7 @@ namespace Mirror.Examples.Basic /// This is appended to the player name text, e.g. "Player 01" /// [SyncVar(hook = nameof(PlayerNumberChanged))] - public int playerNumber = 0; - - /// - /// This is updated by UpdateData which is called from OnStartServer via InvokeRepeating - /// - [SyncVar(hook = nameof(PlayerDataChanged))] - public int playerData = 0; + public byte playerNumber = 0; /// /// Random color for the playerData text, assigned in OnStartServer @@ -46,24 +35,34 @@ namespace Mirror.Examples.Basic [SyncVar(hook = nameof(PlayerColorChanged))] public Color32 playerColor = Color.white; + /// + /// This is updated by UpdateData which is called from OnStartServer via InvokeRepeating + /// + [SyncVar(hook = nameof(PlayerDataChanged))] + public ushort playerData = 0; + // This is called by the hook of playerNumber SyncVar above - void PlayerNumberChanged(int _, int newPlayerNumber) + void PlayerNumberChanged(byte _, byte newPlayerNumber) { OnPlayerNumberChanged?.Invoke(newPlayerNumber); } - // This is called by the hook of playerData SyncVar above - void PlayerDataChanged(int _, int newPlayerData) - { - OnPlayerDataChanged?.Invoke(newPlayerData); - } - // This is called by the hook of playerColor SyncVar above void PlayerColorChanged(Color32 _, Color32 newPlayerColor) { OnPlayerColorChanged?.Invoke(newPlayerColor); } + // This is called by the hook of playerData SyncVar above + void PlayerDataChanged(ushort _, ushort newPlayerData) + { + OnPlayerDataChanged?.Invoke(newPlayerData); + } + + #endregion + + #region Server + /// /// This is invoked for NetworkBehaviour objects when they become active on the server. /// This could be triggered by NetworkServer.Listen() for objects in the scene, or by NetworkServer.Spawn() for objects that are dynamically created. @@ -79,10 +78,30 @@ namespace Mirror.Examples.Basic // set the Player Color SyncVar playerColor = Random.ColorHSV(0f, 1f, 0.9f, 0.9f, 1f, 1f); + // set the initial player data + playerData = (ushort)Random.Range(100, 1000); + // Start generating updates InvokeRepeating(nameof(UpdateData), 1, 1); } + // This is called from BasicNetManager OnServerAddPlayer and OnServerDisconnect + // Player numbers are reset whenever a player joins / leaves + [ServerCallback] + internal static void ResetPlayerNumbers() + { + byte playerNumber = 0; + foreach (Player player in playersList) + player.playerNumber = playerNumber++; + } + + // This only runs on the server, called from OnStartServer via InvokeRepeating + [ServerCallback] + void UpdateData() + { + playerData = (ushort)Random.Range(100, 1000); + } + /// /// Invoked on the server when the object is unspawned /// Useful for saving object data in persistent storage @@ -93,12 +112,9 @@ namespace Mirror.Examples.Basic playersList.Remove(this); } - // This only runs on the server, called from OnStartServer via InvokeRepeating - [ServerCallback] - void UpdateData() - { - playerData = Random.Range(100, 1000); - } + #endregion + + #region Client /// /// Called on every NetworkBehaviour when it is activated on a client. @@ -106,33 +122,63 @@ namespace Mirror.Examples.Basic /// public override void OnStartClient() { - // Activate the main panel - ((BasicNetManager)NetworkManager.singleton).mainPanel.gameObject.SetActive(true); + Debug.Log("OnStartClient"); // Instantiate the player UI as child of the Players Panel - playerUI = Instantiate(playerUIPrefab, ((BasicNetManager)NetworkManager.singleton).playersPanel); + playerUIObject = Instantiate(playerUIPrefab, CanvasUI.instance.playersPanel); + playerUI = playerUIObject.GetComponent(); - // Set this player object in PlayerUI to wire up event handlers - playerUI.GetComponent().SetPlayer(this, isLocalPlayer); + // wire up all events to handlers in PlayerUI + OnPlayerNumberChanged = playerUI.OnPlayerNumberChanged; + OnPlayerColorChanged = playerUI.OnPlayerColorChanged; + OnPlayerDataChanged = playerUI.OnPlayerDataChanged; - // Invoke all event handlers with the current data + // Invoke all event handlers with the initial data from spawn payload OnPlayerNumberChanged.Invoke(playerNumber); OnPlayerColorChanged.Invoke(playerColor); OnPlayerDataChanged.Invoke(playerData); } + /// + /// Called when the local player object has been set up. + /// This happens after OnStartClient(), as it is triggered by an ownership message from the server. This is an appropriate place to activate components or functionality that should only be active for the local player, such as cameras and input. + /// + public override void OnStartLocalPlayer() + { + Debug.Log("OnStartLocalPlayer"); + + // Set isLocalPlayer for this Player in UI for background shading + playerUI.SetLocalPlayer(); + + // Activate the main panel + CanvasUI.instance.mainPanel.gameObject.SetActive(true); + } + + /// + /// Called when the local player object is being stopped. + /// This happens before OnStopClient(), as it may be triggered by an ownership message from the server, or because the player object is being destroyed. This is an appropriate place to deactivate components or functionality that should only be active for the local player, such as cameras and input. + /// + public override void OnStopLocalPlayer() + { + // Disable the main panel for local player + CanvasUI.instance.mainPanel.gameObject.SetActive(false); + } + /// /// This is invoked on clients when the server has caused this object to be destroyed. /// This can be used as a hook to invoke effects or do client specific cleanup. /// public override void OnStopClient() { - // Remove this player's UI object - Destroy(playerUI); + // disconnect event handlers + OnPlayerNumberChanged = null; + OnPlayerColorChanged = null; + OnPlayerDataChanged = null; - // Disable the main panel for local player - if (isLocalPlayer) - ((BasicNetManager)NetworkManager.singleton).mainPanel.gameObject.SetActive(false); + // Remove this player's UI object + Destroy(playerUIObject); } + + #endregion } } diff --git a/UnityProject/Assets/Mirror/Examples/Basic/Scripts/Player.cs.meta b/UnityProject/Assets/Mirror/Examples/Basic/Scripts/Player.cs.meta index 3be1823..cf15827 100644 --- a/UnityProject/Assets/Mirror/Examples/Basic/Scripts/Player.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Basic/Scripts/Player.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Basic/Scripts/PlayerUI.cs b/UnityProject/Assets/Mirror/Examples/Basic/Scripts/PlayerUI.cs index b11be79..e3fe6c9 100644 --- a/UnityProject/Assets/Mirror/Examples/Basic/Scripts/PlayerUI.cs +++ b/UnityProject/Assets/Mirror/Examples/Basic/Scripts/PlayerUI.cs @@ -12,53 +12,30 @@ namespace Mirror.Examples.Basic public Text playerNameText; public Text playerDataText; - Player player; - - /// - /// Caches the controlling Player object, subscribes to its events - /// - /// Player object that controls this UI - /// true if the Player object is the Local Player - public void SetPlayer(Player player, bool isLocalPlayer) + // Sets a highlight color for the local player + public void SetLocalPlayer() { - // cache reference to the player that controls this UI object - this.player = player; - - // subscribe to the events raised by SyncVar Hooks on the Player object - player.OnPlayerNumberChanged += OnPlayerNumberChanged; - player.OnPlayerColorChanged += OnPlayerColorChanged; - player.OnPlayerDataChanged += OnPlayerDataChanged; - // add a visual background for the local player in the UI - if (isLocalPlayer) - image.color = new Color(1f, 1f, 1f, 0.1f); - } - - void OnDisable() - { - player.OnPlayerNumberChanged -= OnPlayerNumberChanged; - player.OnPlayerColorChanged -= OnPlayerColorChanged; - player.OnPlayerDataChanged -= OnPlayerDataChanged; + image.color = new Color(1f, 1f, 1f, 0.1f); } // This value can change as clients leave and join - void OnPlayerNumberChanged(int newPlayerNumber) + public void OnPlayerNumberChanged(byte newPlayerNumber) { playerNameText.text = string.Format("Player {0:00}", newPlayerNumber); } // Random color set by Player::OnStartServer - void OnPlayerColorChanged(Color32 newPlayerColor) + public void OnPlayerColorChanged(Color32 newPlayerColor) { playerNameText.color = newPlayerColor; } // This updates from Player::UpdateData via InvokeRepeating on server - void OnPlayerDataChanged(int newPlayerData) + public void OnPlayerDataChanged(ushort newPlayerData) { // Show the data in the UI playerDataText.text = string.Format("Data: {0:000}", newPlayerData); } - } } diff --git a/UnityProject/Assets/Mirror/Examples/Basic/Scripts/PlayerUI.cs.meta b/UnityProject/Assets/Mirror/Examples/Basic/Scripts/PlayerUI.cs.meta index f5eaa15..b7c07b7 100644 --- a/UnityProject/Assets/Mirror/Examples/Basic/Scripts/PlayerUI.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Basic/Scripts/PlayerUI.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Benchmark.meta b/UnityProject/Assets/Mirror/Examples/Benchmark.meta index 5e1f04c..73801df 100644 --- a/UnityProject/Assets/Mirror/Examples/Benchmark.meta +++ b/UnityProject/Assets/Mirror/Examples/Benchmark.meta @@ -3,6 +3,6 @@ guid: 1a0b6db5b77ec4177a6e47b68ea7d064 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Benchmark/Materials.meta b/UnityProject/Assets/Mirror/Examples/Benchmark/Materials.meta index 1ac3c41..12a3156 100644 --- a/UnityProject/Assets/Mirror/Examples/Benchmark/Materials.meta +++ b/UnityProject/Assets/Mirror/Examples/Benchmark/Materials.meta @@ -3,6 +3,6 @@ guid: ba4e4f7749e6b437aac187bd7625cf28 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Benchmark/Materials/Red.mat.meta b/UnityProject/Assets/Mirror/Examples/Benchmark/Materials/Red.mat.meta index a976752..9e65288 100644 --- a/UnityProject/Assets/Mirror/Examples/Benchmark/Materials/Red.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/Benchmark/Materials/Red.mat.meta @@ -3,6 +3,6 @@ guid: 451c5da2c5056496297cffba02216286 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Benchmark/Materials/White.mat.meta b/UnityProject/Assets/Mirror/Examples/Benchmark/Materials/White.mat.meta index 2c7446d..0a9db55 100644 --- a/UnityProject/Assets/Mirror/Examples/Benchmark/Materials/White.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/Benchmark/Materials/White.mat.meta @@ -3,6 +3,6 @@ guid: ff8f106e5c9e34da28ad9cee6edb2255 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs.meta b/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs.meta index 21e533a..2cdde64 100644 --- a/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs.meta +++ b/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs.meta @@ -3,6 +3,6 @@ guid: 29276b4f741904266bb3eb6331bee4ab folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs/Monster.prefab b/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs/Monster.prefab index cf862c9..60f96ce 100644 --- a/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs/Monster.prefab +++ b/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs/Monster.prefab @@ -31,6 +31,7 @@ Transform: 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_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 @@ -54,10 +55,12 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -82,6 +85,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!114 &1078519278818213949 MonoBehaviour: m_ObjectHideFlags: 0 @@ -97,7 +101,7 @@ MonoBehaviour: sceneId: 0 serverOnly: 0 visible: 0 - m_AssetId: + m_AssetId: 30b8f251d03d84284b70601e691d474f hasSpawned: 0 --- !u!114 &3679374677650722848 MonoBehaviour: @@ -114,12 +118,28 @@ MonoBehaviour: syncMode: 0 syncInterval: 0.1 clientAuthority: 0 - localPositionSensitivity: 0.01 - localRotationSensitivity: 0.01 - localScaleSensitivity: 0.01 - compressRotation: 1 - interpolateScale: 0 + sendRate: 30 + bufferTimeMultiplier: 1 + bufferSizeLimit: 64 + catchupNegativeThreshold: -1 + catchupPositiveThreshold: 1 + catchupSpeed: 0.009999999776482582 + slowdownSpeed: 0.009999999776482582 + driftEmaDuration: 1 + dynamicAdjustment: 1 + dynamicAdjustmentTolerance: 1 + deliveryTimeEmaDuration: 2 + onlySyncOnChange: 0 + bufferResetMultiplier: 5 + positionSensitivity: 0.01 + rotationSensitivity: 0.01 + scaleSensitivity: 0.01 + syncPosition: 1 + syncRotation: 0 syncScale: 0 + showGizmos: 0 + showOverlay: 0 + overlayColor: {r: 0, g: 0, b: 0, a: 0.5} --- !u!114 &8309506939003697769 MonoBehaviour: m_ObjectHideFlags: 0 diff --git a/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs/Monster.prefab.meta b/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs/Monster.prefab.meta index af6f683..766ed77 100644 --- a/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs/Monster.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs/Monster.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 30b8f251d03d84284b70601e691d474f PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs/Player.prefab b/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs/Player.prefab index 32fccb2..1e51d8b 100644 --- a/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs/Player.prefab +++ b/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs/Player.prefab @@ -31,6 +31,7 @@ Transform: 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_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 @@ -54,10 +55,12 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -82,6 +85,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!114 &1078519278818213949 MonoBehaviour: m_ObjectHideFlags: 0 @@ -97,7 +101,7 @@ MonoBehaviour: sceneId: 0 serverOnly: 0 visible: 0 - m_AssetId: + m_AssetId: e1299008405d14b17b1ca459a6cd44a2 hasSpawned: 0 --- !u!114 &3679374677650722848 MonoBehaviour: @@ -114,12 +118,28 @@ MonoBehaviour: syncMode: 0 syncInterval: 0.1 clientAuthority: 1 - localPositionSensitivity: 0.01 - localRotationSensitivity: 0.01 - localScaleSensitivity: 0.01 - compressRotation: 1 - interpolateScale: 0 + sendRate: 30 + bufferTimeMultiplier: 1 + bufferSizeLimit: 64 + catchupNegativeThreshold: -1 + catchupPositiveThreshold: 1 + catchupSpeed: 0.009999999776482582 + slowdownSpeed: 0.009999999776482582 + driftEmaDuration: 1 + dynamicAdjustment: 1 + dynamicAdjustmentTolerance: 1 + deliveryTimeEmaDuration: 2 + onlySyncOnChange: 0 + bufferResetMultiplier: 5 + positionSensitivity: 0.01 + rotationSensitivity: 0.01 + scaleSensitivity: 0.01 + syncPosition: 1 + syncRotation: 0 syncScale: 0 + showGizmos: 0 + showOverlay: 0 + overlayColor: {r: 0, g: 0, b: 0, a: 0.5} --- !u!114 &644305951047116972 MonoBehaviour: m_ObjectHideFlags: 0 @@ -134,4 +154,4 @@ MonoBehaviour: m_EditorClassIdentifier: syncMode: 0 syncInterval: 0.1 - speed: 5 + speed: 20 diff --git a/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs/Player.prefab.meta b/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs/Player.prefab.meta index 36bbff1..cb898f8 100644 --- a/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs/Player.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/Benchmark/Prefabs/Player.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: e1299008405d14b17b1ca459a6cd44a2 PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Benchmark/Scenes.meta b/UnityProject/Assets/Mirror/Examples/Benchmark/Scenes.meta index 5af04cf..f73c636 100644 --- a/UnityProject/Assets/Mirror/Examples/Benchmark/Scenes.meta +++ b/UnityProject/Assets/Mirror/Examples/Benchmark/Scenes.meta @@ -3,6 +3,6 @@ guid: 1507ce547cd6a42ddb4ba60c3552dc48 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Benchmark/Scenes/Scene.unity b/UnityProject/Assets/Mirror/Examples/Benchmark/Scenes/Scene.unity index 8a09733..d4e504a 100644 --- a/UnityProject/Assets/Mirror/Examples/Benchmark/Scenes/Scene.unity +++ b/UnityProject/Assets/Mirror/Examples/Benchmark/Scenes/Scene.unity @@ -43,7 +43,7 @@ RenderSettings: --- !u!157 &3 LightmapSettings: m_ObjectHideFlags: 0 - serializedVersion: 11 + serializedVersion: 12 m_GIWorkflowMode: 1 m_GISettings: serializedVersion: 2 @@ -54,7 +54,7 @@ LightmapSettings: m_EnableBakedLightmaps: 0 m_EnableRealtimeLightmaps: 0 m_LightmapEditorSettings: - serializedVersion: 10 + serializedVersion: 12 m_Resolution: 2 m_BakeResolution: 40 m_AtlasSize: 1024 @@ -62,6 +62,7 @@ LightmapSettings: m_AOMaxDistance: 1 m_CompAOExponent: 1 m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 m_Padding: 2 m_LightmapParameters: {fileID: 0} m_LightmapsBakeMode: 1 @@ -76,10 +77,16 @@ LightmapSettings: 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_PVRFilteringMode: 1 + m_PVREnvironmentMIS: 0 m_PVRCulling: 1 m_PVRFilteringGaussRadiusDirect: 1 m_PVRFilteringGaussRadiusIndirect: 5 @@ -87,7 +94,9 @@ LightmapSettings: m_PVRFilteringAtrousPositionSigmaDirect: 0.5 m_PVRFilteringAtrousPositionSigmaIndirect: 2 m_PVRFilteringAtrousPositionSigmaAO: 1 - m_ShowResolutionOverlay: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 m_LightingDataAsset: {fileID: 0} m_UseShadowmask: 1 --- !u!196 &4 @@ -123,6 +132,7 @@ GameObject: - component: {fileID: 88936777} - component: {fileID: 88936776} - component: {fileID: 88936774} + - component: {fileID: 88936778} m_Layer: 0 m_Name: Main Camera m_TagString: MainCamera @@ -157,9 +167,10 @@ Camera: m_ClearFlags: 2 m_BackGroundColor: {r: 0.39215687, g: 0.58431375, b: 0.92941177, a: 1} m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 m_SensorSize: {x: 36, y: 24} m_LensShift: {x: 0, y: 0} - m_GateFitMode: 2 m_FocalLength: 50 m_NormalizedViewPortRect: serializedVersion: 2 @@ -197,10 +208,25 @@ Transform: m_LocalRotation: {x: 0.38268343, y: 0, z: 0, w: 0.92387956} m_LocalPosition: {x: 0, y: 50, z: -80} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 45, y: 0, z: 0} +--- !u!114 &88936778 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 88936773} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6635375fbc6be456ea640b75add6378e, type: 3} + m_Name: + m_EditorClassIdentifier: + showGUI: 1 + showLog: 0 --- !u!1 &535739935 GameObject: m_ObjectHideFlags: 0 @@ -290,7 +316,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 6442dc8070ceb41f094e44de0bf87274, type: 3} m_Name: m_EditorClassIdentifier: - showGUI: 1 offsetX: 0 offsetY: 0 --- !u!114 &1282001520 @@ -306,19 +331,15 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: dontDestroyOnLoad: 1 + PersistNetworkManagerToOfflineScene: 0 runInBackground: 1 autoStartServerBuild: 1 - showDebugMessages: 0 serverTickRate: 30 - serverBatching: 1 - serverBatchInterval: 0 offlineScene: onlineScene: transport: {fileID: 1282001521} networkAddress: localhost maxConnections: 1000 - disconnectInactiveConnections: 0 - disconnectInactiveTimeout: 60 authenticator: {fileID: 0} playerPrefab: {fileID: 449802645721213856, guid: e1299008405d14b17b1ca459a6cd44a2, type: 3} @@ -343,12 +364,15 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Port: 7777 + DualMode: 1 NoDelay: 1 Interval: 10 + Timeout: 10000 FastResend: 2 CongestionWindow: 0 SendWindowSize: 4096 ReceiveWindowSize: 4096 + NonAlloc: 1 debugLog: 0 statisticsGUI: 0 statisticsLog: 0 @@ -364,7 +388,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 39adc6e09d5544ed955a50ce8600355a, type: 3} m_Name: m_EditorClassIdentifier: - visRange: 999 + visRange: 200 rebuildInterval: 1 checkMethod: 0 showSlider: 1 @@ -393,12 +417,14 @@ Light: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2054208274} m_Enabled: 1 - serializedVersion: 8 + serializedVersion: 10 m_Type: 1 + m_Shape: 0 m_Color: {r: 1, g: 1, b: 1, a: 1} m_Intensity: 0.8 m_Range: 10 m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 m_CookieSize: 10 m_Shadows: m_Type: 2 @@ -408,6 +434,24 @@ Light: 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} @@ -415,12 +459,15 @@ Light: 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 &2054208276 diff --git a/UnityProject/Assets/Mirror/Examples/Benchmark/Scenes/Scene.unity.meta b/UnityProject/Assets/Mirror/Examples/Benchmark/Scenes/Scene.unity.meta index 5f0f907..4a3c47e 100644 --- a/UnityProject/Assets/Mirror/Examples/Benchmark/Scenes/Scene.unity.meta +++ b/UnityProject/Assets/Mirror/Examples/Benchmark/Scenes/Scene.unity.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 3b956c7d68b6144dd8e6c36636e25b52 DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts.meta b/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts.meta index 868debf..7247026 100644 --- a/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts.meta +++ b/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts.meta @@ -3,6 +3,6 @@ guid: 56ec73164c7f24072b822ed0d1e4d03e folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/BenchmarkNetworkManager.cs.meta b/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/BenchmarkNetworkManager.cs.meta index 34f34b7..6355efe 100644 --- a/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/BenchmarkNetworkManager.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/BenchmarkNetworkManager.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/FPS.cs b/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/FPS.cs new file mode 100644 index 0000000..2b3fb58 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/FPS.cs @@ -0,0 +1,37 @@ +using UnityEngine; + +namespace Mirror.Examples.Cubes +{ + public class FPS : MonoBehaviour + { + // fps accessible to the outside + public int framesPerSecond { get; private set; } + + // configuration + public bool showGUI = true; + public bool showLog = false; + + // helpers + int count; + double startTime; + + protected void Update() + { + ++count; + if (Time.time >= startTime + 1) + { + framesPerSecond = count; + startTime = Time.time; + count = 0; + if (showLog) Debug.Log($"FPS: {framesPerSecond}"); + } + } + + protected void OnGUI() + { + if (!showGUI) return; + + GUI.Label(new Rect(Screen.width - 70, 0, 70, 25), $"FPS: {framesPerSecond}"); + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/FPS.cs.meta b/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/FPS.cs.meta new file mode 100644 index 0000000..b7d2ac4 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/FPS.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6635375fbc6be456ea640b75add6378e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/MonsterMovement.cs b/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/MonsterMovement.cs index 2ef7fae..fd7d377 100644 --- a/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/MonsterMovement.cs +++ b/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/MonsterMovement.cs @@ -38,15 +38,11 @@ namespace Mirror.Examples.Benchmark { Vector2 circlePos = Random.insideUnitCircle; Vector3 dir = new Vector3(circlePos.x, 0, circlePos.y); - Vector3 dest = transform.position + dir * movementDistance; - // within move dist around start? + // set destination on random pos in a circle around start. // (don't want to wander off) - if (Vector3.Distance(start, dest) <= movementDistance) - { - destination = dest; - moving = true; - } + destination = start + dir * movementDistance; + moving = true; } } } diff --git a/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/MonsterMovement.cs.meta b/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/MonsterMovement.cs.meta index 23d46c4..e4aea03 100644 --- a/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/MonsterMovement.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/MonsterMovement.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/PlayerMovement.cs.meta b/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/PlayerMovement.cs.meta index 7eb5367..a520ac7 100644 --- a/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/PlayerMovement.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Benchmark/Scripts/PlayerMovement.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Chat.meta b/UnityProject/Assets/Mirror/Examples/Chat.meta index d9b8551..1b51dde 100644 --- a/UnityProject/Assets/Mirror/Examples/Chat.meta +++ b/UnityProject/Assets/Mirror/Examples/Chat.meta @@ -3,6 +3,6 @@ guid: 92165d23a248449f58d0be75d794a127 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Chat/Prefabs.meta b/UnityProject/Assets/Mirror/Examples/Chat/Prefabs.meta index ea5ec30..f3f82c4 100644 --- a/UnityProject/Assets/Mirror/Examples/Chat/Prefabs.meta +++ b/UnityProject/Assets/Mirror/Examples/Chat/Prefabs.meta @@ -3,6 +3,6 @@ guid: 55a4e4e8824ec4e329adf12e2cfb02a4 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Chat/Prefabs/Player.prefab.meta b/UnityProject/Assets/Mirror/Examples/Chat/Prefabs/Player.prefab.meta index c1264aa..0429ce4 100644 --- a/UnityProject/Assets/Mirror/Examples/Chat/Prefabs/Player.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/Chat/Prefabs/Player.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: e5905ffa27de84009b346b49d518ba03 PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Chat/Scenes.meta b/UnityProject/Assets/Mirror/Examples/Chat/Scenes.meta index 63a8fe5..ac2b33e 100644 --- a/UnityProject/Assets/Mirror/Examples/Chat/Scenes.meta +++ b/UnityProject/Assets/Mirror/Examples/Chat/Scenes.meta @@ -3,6 +3,6 @@ guid: 71f6f21bb51d14dc0b231a8488826aac folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Chat/Scenes/Main.unity b/UnityProject/Assets/Mirror/Examples/Chat/Scenes/Main.unity index d00c47a..b138484 100644 --- a/UnityProject/Assets/Mirror/Examples/Chat/Scenes/Main.unity +++ b/UnityProject/Assets/Mirror/Examples/Chat/Scenes/Main.unity @@ -20,30 +20,30 @@ RenderSettings: m_FogDensity: 0.01 m_LinearFogStart: 0 m_LinearFogEnd: 300 - m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientSkyColor: {r: 0.012745098, g: 0.07450981, b: 0.19901961, 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_AmbientMode: 3 m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} - m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + 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_DefaultReflectionMode: 1 m_DefaultReflectionResolution: 128 m_ReflectionBounces: 1 m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 0} m_Sun: {fileID: 0} - m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} m_UseRadianceAmbientProbe: 0 --- !u!157 &3 LightmapSettings: m_ObjectHideFlags: 0 - serializedVersion: 11 + serializedVersion: 12 m_GIWorkflowMode: 0 m_GISettings: serializedVersion: 2 @@ -54,7 +54,7 @@ LightmapSettings: m_EnableBakedLightmaps: 0 m_EnableRealtimeLightmaps: 0 m_LightmapEditorSettings: - serializedVersion: 10 + serializedVersion: 12 m_Resolution: 2 m_BakeResolution: 40 m_AtlasSize: 1024 @@ -62,6 +62,7 @@ LightmapSettings: m_AOMaxDistance: 1 m_CompAOExponent: 1 m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 m_Padding: 2 m_LightmapParameters: {fileID: 0} m_LightmapsBakeMode: 1 @@ -76,10 +77,16 @@ LightmapSettings: 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_PVRFilteringMode: 1 + m_PVREnvironmentMIS: 0 m_PVRCulling: 1 m_PVRFilteringGaussRadiusDirect: 1 m_PVRFilteringGaussRadiusIndirect: 5 @@ -87,9 +94,11 @@ LightmapSettings: m_PVRFilteringAtrousPositionSigmaDirect: 0.5 m_PVRFilteringAtrousPositionSigmaIndirect: 2 m_PVRFilteringAtrousPositionSigmaAO: 1 - m_ShowResolutionOverlay: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 m_LightingDataAsset: {fileID: 0} - m_UseShadowmask: 1 + m_LightingSettings: {fileID: 212571282} --- !u!196 &4 NavMeshSettings: serializedVersion: 2 @@ -109,6 +118,8 @@ NavMeshSettings: manualTileSize: 0 tileSize: 256 accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 debug: m_Flags: 0 m_NavMeshData: {fileID: 0} @@ -146,7 +157,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 0, y: -0.5000019} + m_AnchoredPosition: {x: 0, y: -0.5} m_SizeDelta: {x: -20, y: -13} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &20782997 @@ -158,12 +169,14 @@ MonoBehaviour: m_GameObject: {fileID: 20782995} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} - m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 0.5} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 0.39215687} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -201,15 +214,13 @@ GameObject: - component: {fileID: 75860999} - component: {fileID: 75860998} - component: {fileID: 75860997} - - component: {fileID: 75861000} - - component: {fileID: 75861001} m_Layer: 5 - m_Name: Chat + m_Name: ChatPanel m_TagString: ChatWindow m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!224 &75860996 RectTransform: m_ObjectHideFlags: 0 @@ -217,21 +228,22 @@ RectTransform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 75860995} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + 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: - {fileID: 762534976} + - {fileID: 1731300362} + - {fileID: 1863915625} - {fileID: 1231350850} - {fileID: 1286463573} - - {fileID: 1863915625} - m_Father: {fileID: 1453327788} - m_RootOrder: 2 + m_Father: {fileID: 719573003} + m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: -40, y: -40} + m_SizeDelta: {x: -40.000008, y: -40} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &75860997 MonoBehaviour: @@ -242,10 +254,10 @@ MonoBehaviour: m_GameObject: {fileID: 75860995} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1573420865, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: cfabb0440166ab443bba8876756fdfa9, type: 3} m_Name: m_EditorClassIdentifier: - m_EffectColor: {r: 0, g: 0, b: 0, a: 0.19607843} + m_EffectColor: {r: 0, g: 0, b: 0, a: 0.5019608} m_EffectDistance: {x: 10, y: -10} m_UseGraphicAlpha: 1 --- !u!114 &75860998 @@ -257,12 +269,14 @@ MonoBehaviour: m_GameObject: {fileID: 75860995} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 0.92941177} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -275,6 +289,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &75860999 CanvasRenderer: m_ObjectHideFlags: 0 @@ -283,38 +298,6 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 75860995} m_CullTransparentMesh: 0 ---- !u!114 &75861000 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 75860995} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 2c102f62d739545269250f48327d4429, type: 3} - m_Name: - m_EditorClassIdentifier: - chatMessage: {fileID: 1231350851} - chatHistory: {fileID: 827598817} - scrollbar: {fileID: 423302021} ---- !u!114 &75861001 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 75860995} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3} - m_Name: - m_EditorClassIdentifier: - sceneId: 1581524575 - serverOnly: 0 - visible: 0 - m_AssetId: - hasSpawned: 0 --- !u!1 &90143746 GameObject: m_ObjectHideFlags: 0 @@ -361,12 +344,14 @@ MonoBehaviour: m_GameObject: {fileID: 90143746} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -438,18 +423,20 @@ MonoBehaviour: m_GameObject: {fileID: 107824418} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} - m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_Color: {r: 0, g: 0.5019608, b: 0, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] m_FontData: m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} - m_FontSize: 40 + m_FontSize: 30 m_FontStyle: 1 m_BestFit: 0 m_MinSize: 2 @@ -460,7 +447,7 @@ MonoBehaviour: m_HorizontalOverflow: 0 m_VerticalOverflow: 0 m_LineSpacing: 1 - m_Text: Connect + m_Text: Start Client --- !u!222 &107824421 CanvasRenderer: m_ObjectHideFlags: 0 @@ -469,98 +456,67 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 107824418} m_CullTransparentMesh: 0 ---- !u!1 &293460942 -GameObject: +--- !u!850595691 &212571282 +LightingSettings: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 293460943} - - component: {fileID: 293460945} - - component: {fileID: 293460944} - - component: {fileID: 293460946} - m_Layer: 5 - m_Name: Username - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &293460943 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 293460942} - 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: - - {fileID: 1143498589} - - {fileID: 1481045373} - - {fileID: 851154180} - - {fileID: 1523854170} - m_Father: {fileID: 1453327788} - m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 600, y: 400} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!114 &293460944 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 293460942} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} - m_Name: - m_EditorClassIdentifier: - m_Material: {fileID: 0} - m_Color: {r: 1, g: 1, b: 1, a: 0.92941177} - m_RaycastTarget: 1 - m_OnCullStateChanged: - m_PersistentCalls: - m_Calls: [] - m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} - m_Type: 1 - m_PreserveAspect: 0 - m_FillCenter: 1 - m_FillMethod: 4 - m_FillAmount: 1 - m_FillClockwise: 1 - m_FillOrigin: 0 - m_UseSpriteMesh: 0 ---- !u!222 &293460945 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 293460942} - m_CullTransparentMesh: 0 ---- !u!114 &293460946 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 293460942} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 1573420865, guid: f70555f144d8491a825f0804e09c671c, type: 3} - m_Name: - m_EditorClassIdentifier: - m_EffectColor: {r: 0, g: 0, b: 0, a: 0.5} - m_EffectDistance: {x: 20, y: -20} - m_UseGraphicAlpha: 1 + m_Name: Settings.lighting + serializedVersion: 3 + m_GIWorkflowMode: 0 + m_EnableBakedLightmaps: 0 + m_EnableRealtimeLightmaps: 0 + m_RealtimeEnvironmentLighting: 1 + m_BounceScale: 1 + m_AlbedoBoost: 1 + m_IndirectOutputScale: 1 + m_UsingShadowmask: 1 + m_BakeBackend: 1 + m_LightmapMaxSize: 1024 + m_BakeResolution: 40 + m_Padding: 2 + m_TextureCompression: 1 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAO: 0 + m_MixedBakeMode: 2 + m_LightmapsBakeMode: 1 + m_FilterMode: 1 + m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_RealtimeResolution: 2 + m_ForceWhiteAlbedo: 0 + m_ForceUpdates: 0 + m_FinalGather: 0 + m_FinalGatherRayCount: 256 + m_FinalGatherFiltering: 1 + m_PVRCulling: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVREnvironmentSampleCount: 500 + m_PVREnvironmentReferencePointCount: 2048 + m_LightProbeSampleCountMultiplier: 4 + m_PVRBounces: 2 + m_PVRMinBounces: 2 + m_PVREnvironmentMIS: 0 + m_PVRFilteringMode: 2 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 --- !u!1 &423302019 GameObject: m_ObjectHideFlags: 0 @@ -609,11 +565,12 @@ MonoBehaviour: m_GameObject: {fileID: 423302019} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -2061169968, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 2a4db7a114972834c8e4117be1d82ba3, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: m_Mode: 3 + m_WrapAround: 0 m_SelectOnUp: {fileID: 0} m_SelectOnDown: {fileID: 0} m_SelectOnLeft: {fileID: 0} @@ -623,17 +580,20 @@ MonoBehaviour: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled m_Interactable: 1 m_TargetGraphic: {fileID: 1616857743} @@ -654,12 +614,14 @@ MonoBehaviour: m_GameObject: {fileID: 423302019} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -672,6 +634,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &423302023 CanvasRenderer: m_ObjectHideFlags: 0 @@ -714,7 +677,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 0, y: -0.5000019} + m_AnchoredPosition: {x: 0, y: -0.5} m_SizeDelta: {x: -20, y: -13} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &576238263 @@ -726,12 +689,14 @@ MonoBehaviour: m_GameObject: {fileID: 576238261} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -791,7 +756,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 0, y: -0.5000019} + m_AnchoredPosition: {x: 0, y: -0.5} m_SizeDelta: {x: -20, y: -13} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &591385425 @@ -803,12 +768,14 @@ MonoBehaviour: m_GameObject: {fileID: 591385423} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -834,6 +801,222 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 591385423} m_CullTransparentMesh: 0 +--- !u!1 &637644698 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 637644699} + - component: {fileID: 637644701} + - component: {fileID: 637644700} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &637644699 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 637644698} + 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: 1731300362} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &637644700 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 637644698} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 1 + m_BestFit: 0 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: X +--- !u!222 &637644701 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 637644698} + m_CullTransparentMesh: 1 +--- !u!1 &719572997 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 719573003} + - component: {fileID: 719573002} + - component: {fileID: 719573001} + - component: {fileID: 719573000} + - component: {fileID: 719572999} + - component: {fileID: 719572998} + m_Layer: 5 + m_Name: ChatUI + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &719572998 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 719572997} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2c102f62d739545269250f48327d4429, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 + chatHistory: {fileID: 827598817} + scrollbar: {fileID: 423302021} + chatMessage: {fileID: 1231350851} + sendButton: {fileID: 1286463574} +--- !u!114 &719572999 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 719572997} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3} + m_Name: + m_EditorClassIdentifier: + sceneId: 2564614208 + serverOnly: 0 + visible: 0 + m_AssetId: + hasSpawned: 0 +--- !u!114 &719573000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 719572997} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 823 +--- !u!114 &719573001 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 719572997} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!223 &719573002 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 719572997} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &719573003 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 719572997} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 75860996} + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} --- !u!1 &719610385 GameObject: m_ObjectHideFlags: 0 @@ -880,12 +1063,14 @@ MonoBehaviour: m_GameObject: {fileID: 719610385} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} - m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 0.5} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 0.39215687} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -902,7 +1087,7 @@ MonoBehaviour: m_HorizontalOverflow: 0 m_VerticalOverflow: 0 m_LineSpacing: 1 - m_Text: Enter text... + m_Text: Enter text and press Enter or click Send... --- !u!222 &719610388 CanvasRenderer: m_ObjectHideFlags: 0 @@ -981,8 +1166,8 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 1} m_AnchorMax: {x: 0.5, y: 1} - m_AnchoredPosition: {x: 0.000048637, y: -30} - m_SizeDelta: {x: 300, y: 40} + m_AnchoredPosition: {x: 0, y: -30} + m_SizeDelta: {x: 1211, y: 40} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &762534977 MonoBehaviour: @@ -993,23 +1178,25 @@ MonoBehaviour: m_GameObject: {fileID: 762534975} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} - m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_Color: {r: 0, g: 0, b: 1, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] m_FontData: m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} m_FontSize: 30 - m_FontStyle: 0 + m_FontStyle: 1 m_BestFit: 0 m_MinSize: 3 m_MaxSize: 40 - m_Alignment: 1 + m_Alignment: 4 m_AlignByGeometry: 0 m_RichText: 1 m_HorizontalOverflow: 0 @@ -1059,9 +1246,9 @@ RectTransform: m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 1, y: 1} + m_AnchorMax: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: -17, y: 0} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0, y: 1} --- !u!114 &780870087 MonoBehaviour: @@ -1072,12 +1259,14 @@ MonoBehaviour: m_GameObject: {fileID: 780870085} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -1090,6 +1279,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &780870088 CanvasRenderer: m_ObjectHideFlags: 0 @@ -1107,7 +1297,7 @@ MonoBehaviour: m_GameObject: {fileID: 780870085} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -1200242548, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3} m_Name: m_EditorClassIdentifier: m_ShowMaskGraphic: 0 @@ -1143,10 +1333,10 @@ RectTransform: m_Father: {fileID: 1335915325} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 440, y: -5} - m_SizeDelta: {x: 870, y: 137} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 1} --- !u!114 &827598817 MonoBehaviour: @@ -1157,12 +1347,14 @@ MonoBehaviour: m_GameObject: {fileID: 827598815} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -1220,13 +1412,13 @@ RectTransform: m_Children: - {fileID: 20782996} - {fileID: 576238262} - m_Father: {fileID: 293460943} - m_RootOrder: 2 + m_Father: {fileID: 1499096249} + m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 102, y: 37} - m_SizeDelta: {x: 300, y: 40} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 381, y: -175} + m_SizeDelta: {x: 250, y: 40} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &851154181 MonoBehaviour: @@ -1237,11 +1429,12 @@ MonoBehaviour: m_GameObject: {fileID: 851154179} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 575553740, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: d199490a83bb2b844b9695cbf13b01ef, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: m_Mode: 3 + m_WrapAround: 0 m_SelectOnUp: {fileID: 0} m_SelectOnDown: {fileID: 0} m_SelectOnLeft: {fileID: 0} @@ -1251,17 +1444,20 @@ MonoBehaviour: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled m_Interactable: 1 m_TargetGraphic: {fileID: 851154182} @@ -1281,8 +1477,21 @@ MonoBehaviour: m_OnValueChanged: m_PersistentCalls: m_Calls: - - m_Target: {fileID: 1783103025} - m_MethodName: set_PlayerName + - m_Target: {fileID: 1783103024} + m_TargetAssemblyTypeName: + m_MethodName: SetPlayername + 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 + - m_Target: {fileID: 1453327789} + m_TargetAssemblyTypeName: + m_MethodName: ToggleButtons m_Mode: 0 m_Arguments: m_ObjectArgument: {fileID: 0} @@ -1299,6 +1508,7 @@ MonoBehaviour: m_CaretBlinkRate: 0.85 m_CaretWidth: 1 m_ReadOnly: 0 + m_ShouldActivateOnSelect: 1 --- !u!114 &851154182 MonoBehaviour: m_ObjectHideFlags: 0 @@ -1308,12 +1518,14 @@ MonoBehaviour: m_GameObject: {fileID: 851154179} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -1326,6 +1538,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &851154183 CanvasRenderer: m_ObjectHideFlags: 0 @@ -1380,12 +1593,14 @@ MonoBehaviour: m_GameObject: {fileID: 1018203013} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} - m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_Color: {r: 0, g: 0.5019608, b: 0, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -1448,10 +1663,10 @@ RectTransform: m_Father: {fileID: 1499096249} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 102, y: 37} - m_SizeDelta: {x: 300, y: 40} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 381, y: -126} + m_SizeDelta: {x: 250, y: 40} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1027272349 MonoBehaviour: @@ -1462,11 +1677,12 @@ MonoBehaviour: m_GameObject: {fileID: 1027272347} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 575553740, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: d199490a83bb2b844b9695cbf13b01ef, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: m_Mode: 3 + m_WrapAround: 0 m_SelectOnUp: {fileID: 0} m_SelectOnDown: {fileID: 0} m_SelectOnLeft: {fileID: 0} @@ -1476,17 +1692,20 @@ MonoBehaviour: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled m_Interactable: 1 m_TargetGraphic: {fileID: 1027272350} @@ -1507,6 +1726,7 @@ MonoBehaviour: m_PersistentCalls: m_Calls: - m_Target: {fileID: 1783103025} + m_TargetAssemblyTypeName: m_MethodName: SetHostname m_Mode: 0 m_Arguments: @@ -1524,6 +1744,7 @@ MonoBehaviour: m_CaretBlinkRate: 0.85 m_CaretWidth: 1 m_ReadOnly: 0 + m_ShouldActivateOnSelect: 1 --- !u!114 &1027272350 MonoBehaviour: m_ObjectHideFlags: 0 @@ -1533,12 +1754,14 @@ MonoBehaviour: m_GameObject: {fileID: 1027272347} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -1551,6 +1774,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &1027272351 CanvasRenderer: m_ObjectHideFlags: 0 @@ -1591,12 +1815,12 @@ RectTransform: m_Children: - {fileID: 107824419} m_Father: {fileID: 1499096249} - m_RootOrder: 4 + m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 187, y: -102} - m_SizeDelta: {x: 300, y: 73.2} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 415, y: -254} + m_SizeDelta: {x: 220, y: 60} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1063265580 MonoBehaviour: @@ -1607,11 +1831,12 @@ MonoBehaviour: m_GameObject: {fileID: 1063265578} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: m_Mode: 3 + m_WrapAround: 0 m_SelectOnUp: {fileID: 0} m_SelectOnDown: {fileID: 0} m_SelectOnLeft: {fileID: 0} @@ -1621,24 +1846,28 @@ MonoBehaviour: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled - m_Interactable: 1 + m_Interactable: 0 m_TargetGraphic: {fileID: 1063265581} m_OnClick: m_PersistentCalls: m_Calls: - - m_Target: {fileID: 1499096248} + - m_Target: {fileID: 1453327784} + m_TargetAssemblyTypeName: m_MethodName: SetActive m_Mode: 6 m_Arguments: @@ -1650,6 +1879,7 @@ MonoBehaviour: m_BoolArgument: 0 m_CallState: 2 - m_Target: {fileID: 1783103025} + m_TargetAssemblyTypeName: m_MethodName: StartClient m_Mode: 1 m_Arguments: @@ -1669,12 +1899,14 @@ MonoBehaviour: m_GameObject: {fileID: 1063265578} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -1687,6 +1919,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &1063265582 CanvasRenderer: m_ObjectHideFlags: 0 @@ -1695,83 +1928,6 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1063265578} m_CullTransparentMesh: 0 ---- !u!1 &1143498588 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1143498589} - - component: {fileID: 1143498591} - - component: {fileID: 1143498590} - m_Layer: 5 - m_Name: Header - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &1143498589 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1143498588} - 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: 293460943} - m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: -0.000012398, y: 141} - m_SizeDelta: {x: 300, y: 40} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!114 &1143498590 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1143498588} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} - m_Name: - m_EditorClassIdentifier: - m_Material: {fileID: 0} - m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} - m_RaycastTarget: 1 - m_OnCullStateChanged: - m_PersistentCalls: - m_Calls: [] - m_FontData: - m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} - m_FontSize: 30 - m_FontStyle: 0 - m_BestFit: 0 - m_MinSize: 3 - m_MaxSize: 40 - m_Alignment: 1 - m_AlignByGeometry: 0 - m_RichText: 1 - m_HorizontalOverflow: 0 - m_VerticalOverflow: 0 - m_LineSpacing: 1 - m_Text: Mirror Chat Example ---- !u!222 &1143498591 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1143498588} - m_CullTransparentMesh: 0 --- !u!1 &1170876674 GameObject: m_ObjectHideFlags: 0 @@ -1806,7 +1962,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 0, y: -0.5000019} + m_AnchoredPosition: {x: 0, y: -0.5} m_SizeDelta: {x: -20, y: -13} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1170876676 @@ -1818,12 +1974,14 @@ MonoBehaviour: m_GameObject: {fileID: 1170876674} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} - m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 0.5} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 0.39215687} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -1882,7 +2040,7 @@ RectTransform: - {fileID: 719610386} - {fileID: 90143747} m_Father: {fileID: 75860996} - m_RootOrder: 1 + m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 0} @@ -1898,11 +2056,12 @@ MonoBehaviour: m_GameObject: {fileID: 1231350849} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 575553740, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: d199490a83bb2b844b9695cbf13b01ef, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: m_Mode: 3 + m_WrapAround: 0 m_SelectOnUp: {fileID: 0} m_SelectOnDown: {fileID: 0} m_SelectOnLeft: {fileID: 0} @@ -1912,17 +2071,20 @@ MonoBehaviour: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled m_Interactable: 1 m_TargetGraphic: {fileID: 1231350852} @@ -1938,10 +2100,34 @@ MonoBehaviour: m_CharacterLimit: 0 m_OnEndEdit: m_PersistentCalls: - m_Calls: [] + m_Calls: + - m_Target: {fileID: 719572998} + m_TargetAssemblyTypeName: + m_MethodName: OnEndEdit + 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 m_OnValueChanged: m_PersistentCalls: - m_Calls: [] + m_Calls: + - m_Target: {fileID: 719572998} + m_TargetAssemblyTypeName: Mirror.Examples.Chat.ChatUI, Mirror.Examples + m_MethodName: ToggleButton + 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 m_CaretColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_CustomCaretColor: 0 m_SelectionColor: {r: 0.65882355, g: 0.80784315, b: 1, a: 0.7529412} @@ -1949,6 +2135,7 @@ MonoBehaviour: m_CaretBlinkRate: 0.85 m_CaretWidth: 1 m_ReadOnly: 0 + m_ShouldActivateOnSelect: 1 --- !u!114 &1231350852 MonoBehaviour: m_ObjectHideFlags: 0 @@ -1958,12 +2145,14 @@ MonoBehaviour: m_GameObject: {fileID: 1231350849} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -1976,6 +2165,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &1231350853 CanvasRenderer: m_ObjectHideFlags: 0 @@ -1984,83 +2174,6 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1231350849} m_CullTransparentMesh: 0 ---- !u!1 &1264446057 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1264446058} - - component: {fileID: 1264446060} - - component: {fileID: 1264446059} - m_Layer: 5 - m_Name: Text - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &1264446058 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1264446057} - 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: 1523854170} - m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 0} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!114 &1264446059 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1264446057} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} - m_Name: - m_EditorClassIdentifier: - m_Material: {fileID: 0} - m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} - m_RaycastTarget: 1 - m_OnCullStateChanged: - m_PersistentCalls: - m_Calls: [] - m_FontData: - m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} - m_FontSize: 40 - m_FontStyle: 1 - m_BestFit: 0 - m_MinSize: 2 - m_MaxSize: 40 - m_Alignment: 4 - m_AlignByGeometry: 0 - m_RichText: 1 - m_HorizontalOverflow: 0 - m_VerticalOverflow: 0 - m_LineSpacing: 1 - m_Text: Next ---- !u!222 &1264446060 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1264446057} - m_CullTransparentMesh: 0 --- !u!1 &1286463572 GameObject: m_ObjectHideFlags: 0 @@ -2093,7 +2206,7 @@ RectTransform: m_Children: - {fileID: 1018203014} m_Father: {fileID: 75860996} - m_RootOrder: 2 + m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 1, y: 0} m_AnchorMax: {x: 1, y: 0} @@ -2109,11 +2222,12 @@ MonoBehaviour: m_GameObject: {fileID: 1286463572} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: m_Mode: 3 + m_WrapAround: 0 m_SelectOnUp: {fileID: 0} m_SelectOnDown: {fileID: 0} m_SelectOnLeft: {fileID: 0} @@ -2123,25 +2237,29 @@ MonoBehaviour: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled - m_Interactable: 1 + m_Interactable: 0 m_TargetGraphic: {fileID: 1286463575} m_OnClick: m_PersistentCalls: m_Calls: - - m_Target: {fileID: 75861000} - m_MethodName: OnSend + - m_Target: {fileID: 719572998} + m_TargetAssemblyTypeName: + m_MethodName: SendMessage m_Mode: 1 m_Arguments: m_ObjectArgument: {fileID: 0} @@ -2160,12 +2278,14 @@ MonoBehaviour: m_GameObject: {fileID: 1286463572} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -2178,6 +2298,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &1286463576 CanvasRenderer: m_ObjectHideFlags: 0 @@ -2222,7 +2343,7 @@ RectTransform: m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} m_AnchoredPosition: {x: 1, y: 0} - m_SizeDelta: {x: -1, y: -220.9} + m_SizeDelta: {x: -1, y: 0} m_Pivot: {x: 0, y: 1} --- !u!114 &1335915326 MonoBehaviour: @@ -2233,7 +2354,7 @@ MonoBehaviour: m_GameObject: {fileID: 1335915324} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1741964061, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3} m_Name: m_EditorClassIdentifier: m_HorizontalFit: 0 @@ -2247,7 +2368,7 @@ MonoBehaviour: m_GameObject: {fileID: 1335915324} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1297475563, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3} m_Name: m_EditorClassIdentifier: m_Padding: @@ -2261,6 +2382,9 @@ MonoBehaviour: m_ChildForceExpandHeight: 1 m_ChildControlWidth: 1 m_ChildControlHeight: 1 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 --- !u!1 &1453327784 GameObject: m_ObjectHideFlags: 0 @@ -2273,8 +2397,9 @@ GameObject: - component: {fileID: 1453327787} - component: {fileID: 1453327786} - component: {fileID: 1453327785} + - component: {fileID: 1453327789} m_Layer: 5 - m_Name: Canvas + m_Name: LoginUI m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -2289,14 +2414,14 @@ MonoBehaviour: m_GameObject: {fileID: 1453327784} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1301386320, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} m_Name: m_EditorClassIdentifier: m_IgnoreReversedGraphics: 1 m_BlockingObjects: 0 m_BlockingMask: serializedVersion: 2 - m_Bits: 4294967295 + m_Bits: 823 --- !u!114 &1453327786 MonoBehaviour: m_ObjectHideFlags: 0 @@ -2306,7 +2431,7 @@ MonoBehaviour: m_GameObject: {fileID: 1453327784} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1980459831, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} m_Name: m_EditorClassIdentifier: m_UiScaleMode: 0 @@ -2319,6 +2444,7 @@ MonoBehaviour: m_FallbackScreenDPI: 96 m_DefaultSpriteDPI: 96 m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 --- !u!223 &1453327787 Canvas: m_ObjectHideFlags: 0 @@ -2351,9 +2477,7 @@ RectTransform: m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0, y: 0, z: 0} m_Children: - - {fileID: 293460943} - {fileID: 1499096249} - - {fileID: 75860996} m_Father: {fileID: 0} m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -2362,6 +2486,22 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0, y: 0} +--- !u!114 &1453327789 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1453327784} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7a77ca56c9d91af4b81b73a9907d6112, type: 3} + m_Name: + m_EditorClassIdentifier: + usernameInput: {fileID: 851154181} + hostButton: {fileID: 1904406266} + clientButton: {fileID: 1063265580} + errorText: {fileID: 1580038555} --- !u!1 &1481045372 GameObject: m_ObjectHideFlags: 0 @@ -2374,7 +2514,7 @@ GameObject: - component: {fileID: 1481045375} - component: {fileID: 1481045374} m_Layer: 5 - m_Name: Text + m_Name: UsernameLabel m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -2391,12 +2531,12 @@ RectTransform: m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] - m_Father: {fileID: 293460943} - m_RootOrder: 1 + m_Father: {fileID: 1499096249} + m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: -128, y: 37} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 172, y: -175} m_SizeDelta: {x: 160, y: 30} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1481045374 @@ -2408,12 +2548,14 @@ MonoBehaviour: m_GameObject: {fileID: 1481045372} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -2452,12 +2594,12 @@ GameObject: - component: {fileID: 1499096251} - component: {fileID: 1499096250} m_Layer: 5 - m_Name: Server + m_Name: LoginPanel m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!224 &1499096249 RectTransform: m_ObjectHideFlags: 0 @@ -2472,15 +2614,18 @@ RectTransform: - {fileID: 1909588651} - {fileID: 1995652016} - {fileID: 1027272348} + - {fileID: 1481045373} + - {fileID: 851154180} - {fileID: 1904406265} - {fileID: 1063265579} + - {fileID: 1580038557} m_Father: {fileID: 1453327788} - m_RootOrder: 1 + m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 750, y: 400} + m_SizeDelta: {x: 600, y: 400} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1499096250 MonoBehaviour: @@ -2491,11 +2636,11 @@ MonoBehaviour: m_GameObject: {fileID: 1499096248} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1573420865, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: cfabb0440166ab443bba8876756fdfa9, type: 3} m_Name: m_EditorClassIdentifier: m_EffectColor: {r: 0, g: 0, b: 0, a: 0.5} - m_EffectDistance: {x: 20, y: -20} + m_EffectDistance: {x: 10, y: -10} m_UseGraphicAlpha: 1 --- !u!114 &1499096251 MonoBehaviour: @@ -2506,12 +2651,14 @@ MonoBehaviour: m_GameObject: {fileID: 1499096248} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 0.92941177} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -2524,6 +2671,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &1499096252 CanvasRenderer: m_ObjectHideFlags: 0 @@ -2532,142 +2680,6 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1499096248} m_CullTransparentMesh: 0 ---- !u!1 &1523854169 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1523854170} - - component: {fileID: 1523854173} - - component: {fileID: 1523854172} - - component: {fileID: 1523854171} - m_Layer: 5 - m_Name: NextButton - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &1523854170 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1523854169} - 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: - - {fileID: 1264446058} - m_Father: {fileID: 293460943} - m_RootOrder: 3 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.000011444, y: -102} - m_SizeDelta: {x: 183.6, y: 73.2} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!114 &1523854171 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1523854169} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3} - m_Name: - m_EditorClassIdentifier: - m_Navigation: - m_Mode: 3 - m_SelectOnUp: {fileID: 0} - m_SelectOnDown: {fileID: 0} - m_SelectOnLeft: {fileID: 0} - m_SelectOnRight: {fileID: 0} - m_Transition: 1 - m_Colors: - m_NormalColor: {r: 1, g: 1, b: 1, a: 1} - m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} - m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} - m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} - m_ColorMultiplier: 1 - m_FadeDuration: 0.1 - m_SpriteState: - m_HighlightedSprite: {fileID: 0} - m_PressedSprite: {fileID: 0} - m_DisabledSprite: {fileID: 0} - m_AnimationTriggers: - m_NormalTrigger: Normal - m_HighlightedTrigger: Highlighted - m_PressedTrigger: Pressed - m_DisabledTrigger: Disabled - m_Interactable: 1 - m_TargetGraphic: {fileID: 1523854172} - m_OnClick: - m_PersistentCalls: - m_Calls: - - m_Target: {fileID: 293460942} - m_MethodName: SetActive - m_Mode: 6 - 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 - - m_Target: {fileID: 1499096248} - m_MethodName: SetActive - m_Mode: 6 - m_Arguments: - m_ObjectArgument: {fileID: 0} - m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine - m_IntArgument: 0 - m_FloatArgument: 0 - m_StringArgument: - m_BoolArgument: 1 - m_CallState: 2 ---- !u!114 &1523854172 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1523854169} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} - m_Name: - m_EditorClassIdentifier: - m_Material: {fileID: 0} - m_Color: {r: 1, g: 1, b: 1, a: 1} - m_RaycastTarget: 1 - m_OnCullStateChanged: - m_PersistentCalls: - m_Calls: [] - m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} - m_Type: 1 - m_PreserveAspect: 0 - m_FillCenter: 1 - m_FillMethod: 4 - m_FillAmount: 1 - m_FillClockwise: 1 - m_FillOrigin: 0 - m_UseSpriteMesh: 0 ---- !u!222 &1523854173 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1523854169} - m_CullTransparentMesh: 0 --- !u!1 &1569758148 GameObject: m_ObjectHideFlags: 0 @@ -2714,18 +2726,20 @@ MonoBehaviour: m_GameObject: {fileID: 1569758148} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} - m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_Color: {r: 0, g: 0.5019608, b: 0, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] m_FontData: m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} - m_FontSize: 40 + m_FontSize: 30 m_FontStyle: 1 m_BestFit: 0 m_MinSize: 2 @@ -2736,7 +2750,7 @@ MonoBehaviour: m_HorizontalOverflow: 0 m_VerticalOverflow: 0 m_LineSpacing: 1 - m_Text: Start Server + m_Text: Start Host --- !u!222 &1569758151 CanvasRenderer: m_ObjectHideFlags: 0 @@ -2745,6 +2759,85 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1569758148} m_CullTransparentMesh: 0 +--- !u!1 &1580038554 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1580038557} + - component: {fileID: 1580038556} + - component: {fileID: 1580038555} + m_Layer: 5 + m_Name: ErrorText + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!114 &1580038555 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1580038554} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 20 + m_FontStyle: 1 + m_BestFit: 0 + m_MinSize: 0 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: +--- !u!222 &1580038556 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1580038554} + m_CullTransparentMesh: 0 +--- !u!224 &1580038557 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1580038554} + 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: 1499096249} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 1} + m_AnchorMax: {x: 0.5, y: 1} + m_AnchoredPosition: {x: 0, y: -340} + m_SizeDelta: {x: 440, y: 40} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &1616857741 GameObject: m_ObjectHideFlags: 0 @@ -2778,7 +2871,7 @@ RectTransform: m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 1, y: 1} + m_AnchorMax: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 20, y: 20} m_Pivot: {x: 0.5, y: 0.5} @@ -2791,12 +2884,14 @@ MonoBehaviour: m_GameObject: {fileID: 1616857741} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -2809,6 +2904,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &1616857744 CanvasRenderer: m_ObjectHideFlags: 0 @@ -2833,7 +2929,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!108 &1667679450 Light: m_ObjectHideFlags: 0 @@ -2842,12 +2938,14 @@ Light: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1667679449} m_Enabled: 1 - serializedVersion: 8 + serializedVersion: 10 m_Type: 1 + m_Shape: 0 m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} m_Intensity: 1 m_Range: 10 m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 m_CookieSize: 10 m_Shadows: m_Type: 2 @@ -2857,6 +2955,24 @@ Light: 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} @@ -2864,12 +2980,16 @@ Light: 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_UseViewFrustumForShadowCasterCull: 1 m_ShadowRadius: 0 m_ShadowAngle: 0 --- !u!4 &1667679451 @@ -2886,6 +3006,139 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &1731300361 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1731300362} + - component: {fileID: 1731300365} + - component: {fileID: 1731300364} + - component: {fileID: 1731300363} + m_Layer: 5 + m_Name: ExitButton + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1731300362 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1731300361} + 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: + - {fileID: 637644699} + m_Father: {fileID: 75860996} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: -20, y: -20} + m_SizeDelta: {x: 25, y: 25} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1731300363 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1731300361} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1731300364} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 719572998} + m_TargetAssemblyTypeName: Mirror.Examples.Chat.ChatUI, Mirror.Examples + m_MethodName: ExitButtonOnClick + m_Mode: 1 + 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!114 &1731300364 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1731300361} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1731300365 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1731300361} + m_CullTransparentMesh: 1 --- !u!1 &1783103022 GameObject: m_ObjectHideFlags: 0 @@ -2897,6 +3150,7 @@ GameObject: - component: {fileID: 1783103026} - component: {fileID: 1783103025} - component: {fileID: 1783103023} + - component: {fileID: 1783103024} m_Layer: 0 m_Name: NetworkManager m_TagString: Untagged @@ -2917,15 +3171,41 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Port: 7777 + DualMode: 1 NoDelay: 1 Interval: 10 + Timeout: 10000 FastResend: 2 CongestionWindow: 0 SendWindowSize: 4096 ReceiveWindowSize: 4096 + MaxRetransmit: 40 + NonAlloc: 1 + MaximizeSendReceiveBuffersToOSLimit: 1 + ReliableMaxMessageSize: 298449 + UnreliableMaxMessageSize: 1199 debugLog: 0 statisticsGUI: 0 statisticsLog: 0 +--- !u!114 &1783103024 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1783103022} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6e2e6b40604520d408bef0a5243a58cd, type: 3} + m_Name: + m_EditorClassIdentifier: + OnServerAuthenticated: + m_PersistentCalls: + m_Calls: [] + OnClientAuthenticated: + m_PersistentCalls: + m_Calls: [] + playerName: --- !u!114 &1783103025 MonoBehaviour: m_ObjectHideFlags: 0 @@ -2938,27 +3218,20 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: d0cd72391a563461f88eb3ddf120efef, type: 3} m_Name: m_EditorClassIdentifier: - dontDestroyOnLoad: 1 + dontDestroyOnLoad: 0 runInBackground: 1 autoStartServerBuild: 1 - showDebugMessages: 0 serverTickRate: 30 - serverBatching: 0 - serverBatchInterval: 0 offlineScene: onlineScene: transport: {fileID: 1783103023} networkAddress: localhost maxConnections: 100 - disconnectInactiveConnections: 0 - disconnectInactiveTimeout: 60 - authenticator: {fileID: 0} - playerPrefab: {fileID: 5075528875289742095, guid: e5905ffa27de84009b346b49d518ba03, - type: 3} - autoCreatePlayer: 0 + authenticator: {fileID: 1783103024} + playerPrefab: {fileID: 5075528875289742095, guid: e5905ffa27de84009b346b49d518ba03, type: 3} + autoCreatePlayer: 1 playerSpawnMethod: 0 spawnPrefabs: [] - chatWindow: {fileID: 75861000} --- !u!4 &1783103026 Transform: m_ObjectHideFlags: 0 @@ -3006,12 +3279,12 @@ RectTransform: - {fileID: 780870086} - {fileID: 423302020} m_Father: {fileID: 75860996} - m_RootOrder: 3 + m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 0.000061035156, y: 5.5} - m_SizeDelta: {x: -52, y: -111.1} + m_AnchoredPosition: {x: 0, y: 5} + m_SizeDelta: {x: -51.999992, y: -110} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1863915626 MonoBehaviour: @@ -3022,12 +3295,14 @@ MonoBehaviour: m_GameObject: {fileID: 1863915624} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 0.392} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -3040,6 +3315,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &1863915627 CanvasRenderer: m_ObjectHideFlags: 0 @@ -3057,17 +3333,17 @@ MonoBehaviour: m_GameObject: {fileID: 1863915624} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1367256648, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 1aa08ab6e0800fa44ae55d278d1423e3, type: 3} m_Name: m_EditorClassIdentifier: m_Content: {fileID: 1335915325} m_Horizontal: 0 m_Vertical: 1 m_MovementType: 1 - m_Elasticity: 0.1 + m_Elasticity: 0.01 m_Inertia: 1 m_DecelerationRate: 0.135 - m_ScrollSensitivity: 1 + m_ScrollSensitivity: 100 m_Viewport: {fileID: 780870086} m_HorizontalScrollbar: {fileID: 0} m_VerticalScrollbar: {fileID: 423302021} @@ -3104,12 +3380,13 @@ Camera: m_GameObject: {fileID: 1897504366} m_Enabled: 1 serializedVersion: 2 - m_ClearFlags: 1 - m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_ClearFlags: 2 + m_BackGroundColor: {r: 0.19215688, g: 0.3019608, b: 0.47450984, a: 1} m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 m_SensorSize: {x: 36, y: 24} m_LensShift: {x: 0, y: 0} - m_GateFitMode: 2 m_FocalLength: 50 m_NormalizedViewPortRect: serializedVersion: 2 @@ -3183,12 +3460,12 @@ RectTransform: m_Children: - {fileID: 1569758149} m_Father: {fileID: 1499096249} - m_RootOrder: 3 + m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: -187, y: -102} - m_SizeDelta: {x: 300, y: 73.2} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 185, y: -254} + m_SizeDelta: {x: 220, y: 60} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1904406266 MonoBehaviour: @@ -3199,11 +3476,12 @@ MonoBehaviour: m_GameObject: {fileID: 1904406264} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: m_Mode: 3 + m_WrapAround: 0 m_SelectOnUp: {fileID: 0} m_SelectOnDown: {fileID: 0} m_SelectOnLeft: {fileID: 0} @@ -3213,24 +3491,28 @@ MonoBehaviour: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled - m_Interactable: 1 + m_Interactable: 0 m_TargetGraphic: {fileID: 1904406267} m_OnClick: m_PersistentCalls: m_Calls: - - m_Target: {fileID: 1499096248} + - m_Target: {fileID: 1453327784} + m_TargetAssemblyTypeName: m_MethodName: SetActive m_Mode: 6 m_Arguments: @@ -3242,6 +3524,7 @@ MonoBehaviour: m_BoolArgument: 0 m_CallState: 2 - m_Target: {fileID: 1783103025} + m_TargetAssemblyTypeName: m_MethodName: StartHost m_Mode: 1 m_Arguments: @@ -3261,12 +3544,14 @@ MonoBehaviour: m_GameObject: {fileID: 1904406264} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -3279,6 +3564,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &1904406268 CanvasRenderer: m_ObjectHideFlags: 0 @@ -3321,8 +3607,8 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 1} m_AnchorMax: {x: 0.5, y: 1} - m_AnchoredPosition: {x: -0.000012398, y: -59} - m_SizeDelta: {x: 300, y: 40} + m_AnchoredPosition: {x: 0, y: -55} + m_SizeDelta: {x: 350, y: 40} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1909588652 MonoBehaviour: @@ -3333,19 +3619,21 @@ MonoBehaviour: m_GameObject: {fileID: 1909588650} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} - m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_Color: {r: 0, g: 0, b: 1, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] m_FontData: m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} - m_FontSize: 30 - m_FontStyle: 0 + m_FontSize: 35 + m_FontStyle: 1 m_BestFit: 0 m_MinSize: 3 m_MaxSize: 40 @@ -3391,7 +3679,7 @@ MonoBehaviour: m_GameObject: {fileID: 1923358029} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1077351063, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} m_Name: m_EditorClassIdentifier: m_HorizontalAxis: Horizontal @@ -3410,7 +3698,7 @@ MonoBehaviour: m_GameObject: {fileID: 1923358029} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -619905303, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} m_Name: m_EditorClassIdentifier: m_FirstSelected: {fileID: 0} @@ -3428,7 +3716,7 @@ Transform: m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 4 + m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1995652015 GameObject: @@ -3442,7 +3730,7 @@ GameObject: - component: {fileID: 1995652018} - component: {fileID: 1995652017} m_Layer: 5 - m_Name: Text + m_Name: ServerLabel m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -3462,9 +3750,9 @@ RectTransform: m_Father: {fileID: 1499096249} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: -128, y: 37} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 172, y: -126} m_SizeDelta: {x: 160, y: 30} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1995652017 @@ -3476,12 +3764,14 @@ MonoBehaviour: m_GameObject: {fileID: 1995652015} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] diff --git a/UnityProject/Assets/Mirror/Examples/Chat/Scenes/Main.unity.meta b/UnityProject/Assets/Mirror/Examples/Chat/Scenes/Main.unity.meta index cf81526..d381d8c 100644 --- a/UnityProject/Assets/Mirror/Examples/Chat/Scenes/Main.unity.meta +++ b/UnityProject/Assets/Mirror/Examples/Chat/Scenes/Main.unity.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: f4e8d4de4484e44bba666f2d1f66c73e DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Chat/Scripts.meta b/UnityProject/Assets/Mirror/Examples/Chat/Scripts.meta index e1c9c15..71858f4 100644 --- a/UnityProject/Assets/Mirror/Examples/Chat/Scripts.meta +++ b/UnityProject/Assets/Mirror/Examples/Chat/Scripts.meta @@ -3,6 +3,6 @@ guid: 81da49d71176c41169a24259df78e50a folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatAuthenticator.cs b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatAuthenticator.cs new file mode 100644 index 0000000..c00f33c --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatAuthenticator.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +/* + Documentation: https://mirror-networking.gitbook.io/docs/components/network-authenticators + API Reference: https://mirror-networking.com/docs/api/Mirror.NetworkAuthenticator.html +*/ + +namespace Mirror.Examples.Chat +{ + [AddComponentMenu("")] + public class ChatAuthenticator : NetworkAuthenticator + { + readonly HashSet connectionsPendingDisconnect = new HashSet(); + + [Header("Client Username")] + public string playerName; + + #region Messages + + public struct AuthRequestMessage : NetworkMessage + { + // use whatever credentials make sense for your game + // for example, you might want to pass the accessToken if using oauth + public string authUsername; + } + + public struct AuthResponseMessage : NetworkMessage + { + public byte code; + public string message; + } + + #endregion + + #region Server + + /// + /// Called on server from StartServer to initialize the Authenticator + /// Server message handlers should be registered in this method. + /// + public override void OnStartServer() + { + // register a handler for the authentication request we expect from client + NetworkServer.RegisterHandler(OnAuthRequestMessage, false); + } + + /// + /// Called on server from StopServer to reset the Authenticator + /// Server message handlers should be registered in this method. + /// + public override void OnStopServer() + { + // unregister the handler for the authentication request + NetworkServer.UnregisterHandler(); + } + + /// + /// Called on server from OnServerConnectInternal when a client needs to authenticate + /// + /// Connection to client. + public override void OnServerAuthenticate(NetworkConnectionToClient conn) + { + // do nothing...wait for AuthRequestMessage from client + } + + /// + /// Called on server when the client's AuthRequestMessage arrives + /// + /// Connection to client. + /// The message payload + public void OnAuthRequestMessage(NetworkConnectionToClient conn, AuthRequestMessage msg) + { + Debug.Log($"Authentication Request: {msg.authUsername}"); + + if (connectionsPendingDisconnect.Contains(conn)) return; + + // check the credentials by calling your web server, database table, playfab api, or any method appropriate. + if (!Player.playerNames.Contains(msg.authUsername)) + { + // Add the name to the HashSet + Player.playerNames.Add(msg.authUsername); + + // Store username in authenticationData + // This will be read in Player.OnStartServer + // to set the playerName SyncVar. + conn.authenticationData = msg.authUsername; + + // create and send msg to client so it knows to proceed + AuthResponseMessage authResponseMessage = new AuthResponseMessage + { + code = 100, + message = "Success" + }; + + conn.Send(authResponseMessage); + + // Accept the successful authentication + ServerAccept(conn); + } + else + { + connectionsPendingDisconnect.Add(conn); + + // create and send msg to client so it knows to disconnect + AuthResponseMessage authResponseMessage = new AuthResponseMessage + { + code = 200, + message = "Username already in use...try again" + }; + + conn.Send(authResponseMessage); + + // must set NetworkConnection isAuthenticated = false + conn.isAuthenticated = false; + + // disconnect the client after 1 second so that response message gets delivered + StartCoroutine(DelayedDisconnect(conn, 1f)); + } + } + + IEnumerator DelayedDisconnect(NetworkConnectionToClient conn, float waitTime) + { + yield return new WaitForSeconds(waitTime); + + // Reject the unsuccessful authentication + ServerReject(conn); + + yield return null; + + // remove conn from pending connections + connectionsPendingDisconnect.Remove(conn); + } + + #endregion + + #region Client + + // Called by UI element UsernameInput.OnValueChanged + public void SetPlayername(string username) + { + playerName = username; + LoginUI.instance.errorText.text = string.Empty; + LoginUI.instance.errorText.gameObject.SetActive(false); + } + + /// + /// Called on client from StartClient to initialize the Authenticator + /// Client message handlers should be registered in this method. + /// + public override void OnStartClient() + { + // register a handler for the authentication response we expect from server + NetworkClient.RegisterHandler(OnAuthResponseMessage, false); + } + + /// + /// Called on client from StopClient to reset the Authenticator + /// Client message handlers should be unregistered in this method. + /// + public override void OnStopClient() + { + // unregister the handler for the authentication response + NetworkClient.UnregisterHandler(); + } + + /// + /// Called on client from OnClientConnectInternal when a client needs to authenticate + /// + public override void OnClientAuthenticate() + { + AuthRequestMessage authRequestMessage = new AuthRequestMessage + { + authUsername = playerName, + }; + + NetworkClient.connection.Send(authRequestMessage); + } + + /// + /// Called on client when the server's AuthResponseMessage arrives + /// + /// The message payload + public void OnAuthResponseMessage(AuthResponseMessage msg) + { + if (msg.code == 100) + { + Debug.Log($"Authentication Response: {msg.message}"); + + // Authentication has been accepted + ClientAccept(); + } + else + { + Debug.LogError($"Authentication Response: {msg.message}"); + + // Authentication has been rejected + // StopHost works for both host client and remote clients + NetworkManager.singleton.StopHost(); + + // Do this AFTER StopHost so it doesn't get cleared / hidden by OnClientDisconnect + LoginUI.instance.errorText.text = msg.message; + LoginUI.instance.errorText.gameObject.SetActive(true); + } + } + + #endregion + } +} diff --git a/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatAuthenticator.cs.meta b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatAuthenticator.cs.meta new file mode 100644 index 0000000..eb06fd1 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatAuthenticator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6e2e6b40604520d408bef0a5243a58cd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatNetworkManager.cs b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatNetworkManager.cs index 34bbceb..be9d43a 100644 --- a/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatNetworkManager.cs +++ b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatNetworkManager.cs @@ -1,51 +1,39 @@ using UnityEngine; +/* + Documentation: https://mirror-networking.gitbook.io/docs/components/network-manager + API Reference: https://mirror-networking.com/docs/api/Mirror.NetworkManager.html +*/ + namespace Mirror.Examples.Chat { [AddComponentMenu("")] public class ChatNetworkManager : NetworkManager { - [Header("Chat GUI")] - public ChatWindow chatWindow; - - // Set by UI element UsernameInput OnValueChanged - public string PlayerName { get; set; } - // Called by UI element NetworkAddressInput.OnValueChanged public void SetHostname(string hostname) { networkAddress = hostname; } - public struct CreatePlayerMessage : NetworkMessage + public override void OnServerDisconnect(NetworkConnectionToClient conn) { - public string name; + // remove player name from the HashSet + if (conn.authenticationData != null) + Player.playerNames.Remove((string)conn.authenticationData); + + // remove connection from Dictionary of conn > names + ChatUI.connNames.Remove(conn); + + base.OnServerDisconnect(conn); } - public override void OnStartServer() + public override void OnClientDisconnect() { - base.OnStartServer(); - NetworkServer.RegisterHandler(OnCreatePlayer); - } - - public override void OnClientConnect(NetworkConnection conn) - { - base.OnClientConnect(conn); - - // tell the server to create a player with this name - conn.Send(new CreatePlayerMessage { name = PlayerName }); - } - - void OnCreatePlayer(NetworkConnection connection, CreatePlayerMessage createPlayerMessage) - { - // create a gameobject using the name supplied by client - GameObject playergo = Instantiate(playerPrefab); - playergo.GetComponent().playerName = createPlayerMessage.name; - - // set it as the player - NetworkServer.AddPlayerForConnection(connection, playergo); - - chatWindow.gameObject.SetActive(true); + base.OnClientDisconnect(); + LoginUI.instance.gameObject.SetActive(true); + LoginUI.instance.usernameInput.text = ""; + LoginUI.instance.usernameInput.ActivateInputField(); } } } diff --git a/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatNetworkManager.cs.meta b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatNetworkManager.cs.meta index fd00d18..cbc4ca7 100644 --- a/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatNetworkManager.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatNetworkManager.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatUI.cs b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatUI.cs new file mode 100644 index 0000000..e8c8d0f --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatUI.cs @@ -0,0 +1,100 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +namespace Mirror.Examples.Chat +{ + public class ChatUI : NetworkBehaviour + { + [Header("UI Elements")] + [SerializeField] Text chatHistory; + [SerializeField] Scrollbar scrollbar; + [SerializeField] InputField chatMessage; + [SerializeField] Button sendButton; + + // This is only set on client to the name of the local player + internal static string localPlayerName; + + // Server-only cross-reference of connections to player names + internal static readonly Dictionary connNames = new Dictionary(); + + public override void OnStartServer() + { + connNames.Clear(); + } + + public override void OnStartClient() + { + chatHistory.text = ""; + } + + [Command(requiresAuthority = false)] + void CmdSend(string message, NetworkConnectionToClient sender = null) + { + if (!connNames.ContainsKey(sender)) + connNames.Add(sender, sender.identity.GetComponent().playerName); + + if (!string.IsNullOrWhiteSpace(message)) + RpcReceive(connNames[sender], message.Trim()); + } + + [ClientRpc] + void RpcReceive(string playerName, string message) + { + string prettyMessage = playerName == localPlayerName ? + $"{playerName}: {message}" : + $"{playerName}: {message}"; + AppendMessage(prettyMessage); + } + + void AppendMessage(string message) + { + StartCoroutine(AppendAndScroll(message)); + } + + IEnumerator AppendAndScroll(string message) + { + chatHistory.text += message + "\n"; + + // it takes 2 frames for the UI to update ?!?! + yield return null; + yield return null; + + // slam the scrollbar down + scrollbar.value = 0; + } + + // Called by UI element ExitButton.OnClick + public void ExitButtonOnClick() + { + // StopHost calls both StopClient and StopServer + // StopServer does nothing on remote clients + NetworkManager.singleton.StopHost(); + } + + // Called by UI element MessageField.OnValueChanged + public void ToggleButton(string input) + { + sendButton.interactable = !string.IsNullOrWhiteSpace(input); + } + + // Called by UI element MessageField.OnEndEdit + public void OnEndEdit(string input) + { + if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter) || Input.GetButtonDown("Submit")) + SendMessage(); + } + + // Called by OnEndEdit above and UI element SendButton.OnClick + public void SendMessage() + { + if (!string.IsNullOrWhiteSpace(chatMessage.text)) + { + CmdSend(chatMessage.text.Trim()); + chatMessage.text = string.Empty; + chatMessage.ActivateInputField(); + } + } + } +} diff --git a/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatUI.cs.meta b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatUI.cs.meta new file mode 100644 index 0000000..9b9bd1f --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2c102f62d739545269250f48327d4429 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatWindow.cs b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatWindow.cs deleted file mode 100644 index 821d01c..0000000 --- a/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatWindow.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.Collections; -using UnityEngine; -using UnityEngine.UI; - -namespace Mirror.Examples.Chat -{ - public class ChatWindow : MonoBehaviour - { - public InputField chatMessage; - public Text chatHistory; - public Scrollbar scrollbar; - - public void Awake() - { - Player.OnMessage += OnPlayerMessage; - } - - void OnPlayerMessage(Player player, string message) - { - string prettyMessage = player.isLocalPlayer ? - $"{player.playerName}: {message}" : - $"{player.playerName}: {message}"; - AppendMessage(prettyMessage); - - Debug.Log(message); - } - - // Called by UI element SendButton.OnClick - public void OnSend() - { - if (chatMessage.text.Trim() == "") - return; - - // get our player - Player player = NetworkClient.connection.identity.GetComponent(); - - // send a message - player.CmdSend(chatMessage.text.Trim()); - - chatMessage.text = ""; - } - - internal void AppendMessage(string message) - { - StartCoroutine(AppendAndScroll(message)); - } - - IEnumerator AppendAndScroll(string message) - { - chatHistory.text += message + "\n"; - - // it takes 2 frames for the UI to update ?!?! - yield return null; - yield return null; - - // slam the scrollbar down - scrollbar.value = 0; - } - } -} diff --git a/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatWindow.cs.meta b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatWindow.cs.meta deleted file mode 100644 index 6b6dedb..0000000 --- a/UnityProject/Assets/Mirror/Examples/Chat/Scripts/ChatWindow.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2c102f62d739545269250f48327d4429 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Examples/Chat/Scripts/LoginUI.cs b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/LoginUI.cs new file mode 100644 index 0000000..a42f984 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/LoginUI.cs @@ -0,0 +1,28 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace Mirror.Examples.Chat +{ + public class LoginUI : MonoBehaviour + { + [Header("UI Elements")] + [SerializeField] internal InputField usernameInput; + [SerializeField] internal Button hostButton; + [SerializeField] internal Button clientButton; + [SerializeField] internal Text errorText; + + public static LoginUI instance; + + void Awake() + { + instance = this; + } + + // Called by UI element UsernameInput.OnValueChanged + public void ToggleButtons(string username) + { + hostButton.interactable = !string.IsNullOrWhiteSpace(username); + clientButton.interactable = !string.IsNullOrWhiteSpace(username); + } + } +} diff --git a/UnityProject/Assets/Mirror/Examples/Chat/Scripts/LoginUI.cs.meta b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/LoginUI.cs.meta new file mode 100644 index 0000000..920dda4 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/LoginUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7a77ca56c9d91af4b81b73a9907d6112 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Chat/Scripts/Player.cs b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/Player.cs index 6f75aed..253c004 100644 --- a/UnityProject/Assets/Mirror/Examples/Chat/Scripts/Player.cs +++ b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/Player.cs @@ -1,26 +1,30 @@ -using System; +using System.Collections.Generic; using UnityEngine; namespace Mirror.Examples.Chat { public class Player : NetworkBehaviour { - [SyncVar] - public string playerName; + internal static readonly HashSet playerNames = new HashSet(); - public static event Action OnMessage; + [SerializeField, SyncVar] + internal string playerName; - [Command] - public void CmdSend(string message) + // RuntimeInitializeOnLoadMethod -> fast playmode without domain reload + [UnityEngine.RuntimeInitializeOnLoadMethod] + static void ResetStatics() { - if (message.Trim() != "") - RpcReceive(message.Trim()); + playerNames.Clear(); } - [ClientRpc] - public void RpcReceive(string message) + public override void OnStartServer() { - OnMessage?.Invoke(this, message); + playerName = (string)connectionToClient.authenticationData; + } + + public override void OnStartLocalPlayer() + { + ChatUI.localPlayerName = playerName; } } } diff --git a/UnityProject/Assets/Mirror/Examples/Chat/Scripts/Player.cs.meta b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/Player.cs.meta index 4a7ac91..610ee4c 100644 --- a/UnityProject/Assets/Mirror/Examples/Chat/Scripts/Player.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Chat/Scripts/Player.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Discovery.meta b/UnityProject/Assets/Mirror/Examples/Discovery.meta index ba76865..85a73ff 100644 --- a/UnityProject/Assets/Mirror/Examples/Discovery.meta +++ b/UnityProject/Assets/Mirror/Examples/Discovery.meta @@ -3,6 +3,6 @@ guid: 450d6133608b04c57a6ebd6830d455fd folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Discovery/Prefabs.meta b/UnityProject/Assets/Mirror/Examples/Discovery/Prefabs.meta index b9080f8..ef0083d 100644 --- a/UnityProject/Assets/Mirror/Examples/Discovery/Prefabs.meta +++ b/UnityProject/Assets/Mirror/Examples/Discovery/Prefabs.meta @@ -3,6 +3,6 @@ guid: 8d8abc53a4efb4544ad9cb7a44b4840a folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Discovery/Prefabs/Player.prefab.meta b/UnityProject/Assets/Mirror/Examples/Discovery/Prefabs/Player.prefab.meta index c40645c..1c818fa 100644 --- a/UnityProject/Assets/Mirror/Examples/Discovery/Prefabs/Player.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/Discovery/Prefabs/Player.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: ecd52c53a6ef7496693343d3e32dace1 PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Discovery/Scenes.meta b/UnityProject/Assets/Mirror/Examples/Discovery/Scenes.meta index 4cf810a..ed0ba64 100644 --- a/UnityProject/Assets/Mirror/Examples/Discovery/Scenes.meta +++ b/UnityProject/Assets/Mirror/Examples/Discovery/Scenes.meta @@ -3,6 +3,6 @@ guid: ceaf2344f4e6944258442667a9fbbfdf folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Discovery/Scenes/Scene.unity.meta b/UnityProject/Assets/Mirror/Examples/Discovery/Scenes/Scene.unity.meta index 1f4f24e..b02433f 100644 --- a/UnityProject/Assets/Mirror/Examples/Discovery/Scenes/Scene.unity.meta +++ b/UnityProject/Assets/Mirror/Examples/Discovery/Scenes/Scene.unity.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 90fddc74fa21c423599167eb28b09dd1 DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Mirror.Examples.asmdef b/UnityProject/Assets/Mirror/Examples/Mirror.Examples.asmdef index c4f942e..67d9ce5 100644 --- a/UnityProject/Assets/Mirror/Examples/Mirror.Examples.asmdef +++ b/UnityProject/Assets/Mirror/Examples/Mirror.Examples.asmdef @@ -2,14 +2,16 @@ "name": "Mirror.Examples", "references": [ "Mirror", - "Mirror.Components" + "Mirror.Components", + "Unity.TextMeshPro" ], - "optionalUnityReferences": [], "includePlatforms": [], "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, - "defineConstraints": [] + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false } \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Examples/Mirror.Examples.asmdef.meta b/UnityProject/Assets/Mirror/Examples/Mirror.Examples.asmdef.meta index b68d67b..2b545b5 100644 --- a/UnityProject/Assets/Mirror/Examples/Mirror.Examples.asmdef.meta +++ b/UnityProject/Assets/Mirror/Examples/Mirror.Examples.asmdef.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: fecf25954bb196642ab50657689761d6 AssemblyDefinitionImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes.meta index d9d9280..bbe9ba2 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes.meta @@ -3,6 +3,6 @@ guid: 792b2d05e371c3c47ac7c4b1fa0dbfe2 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials.meta index edd3710..7a4a337 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials.meta @@ -3,6 +3,6 @@ guid: ef54d3fc8c3b6c845bb29f2d04ea7edb folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Physics.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Physics.meta index 13d0938..a372565 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Physics.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Physics.meta @@ -3,6 +3,6 @@ guid: b5ae92b6f97224e418115c9f16c50fd8 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Physics/Icosphere.physicMaterial.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Physics/Icosphere.physicMaterial.meta index 7bb76bd..0403873 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Physics/Icosphere.physicMaterial.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Physics/Icosphere.physicMaterial.meta @@ -3,6 +3,6 @@ guid: 47163bc0301c1a146bbaa4d539a6ac36 NativeFormatImporter: externalObjects: {} mainObjectFileID: 13400000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Physics/Player.physicMaterial.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Physics/Player.physicMaterial.meta index 2016cde..40322e3 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Physics/Player.physicMaterial.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Physics/Player.physicMaterial.meta @@ -3,6 +3,6 @@ guid: 2debad4ac21a6644faf4fc93bd5b5869 NativeFormatImporter: externalObjects: {} mainObjectFileID: 13400000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Physics/RoomBounce.physicMaterial.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Physics/RoomBounce.physicMaterial.meta index 9a6653e..02a172b 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Physics/RoomBounce.physicMaterial.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Physics/RoomBounce.physicMaterial.meta @@ -3,6 +3,6 @@ guid: 2e179c076d5d0924dbf5a2de0630bdb1 NativeFormatImporter: externalObjects: {} mainObjectFileID: 13400000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Render.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Render.meta index 26b8d43..0953148 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Render.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Render.meta @@ -3,6 +3,6 @@ guid: e79e44ac19c0d9244bb54a0e960210e3 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Render/PlayArea.mat.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Render/PlayArea.mat.meta index dcdfd42..ab00bc8 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Render/PlayArea.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Render/PlayArea.mat.meta @@ -3,6 +3,6 @@ guid: 42fe0bcfbb65da3429ae2c289686e024 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Render/Player.mat.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Render/Player.mat.meta index 1c930eb..d890edb 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Render/Player.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Render/Player.mat.meta @@ -3,6 +3,6 @@ guid: 2089070a3452e6f4d866c53e51aae8f2 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Render/Prize.mat.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Render/Prize.mat.meta index 551120b..3df4a27 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Render/Prize.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Materials/Render/Prize.mat.meta @@ -3,6 +3,6 @@ guid: 2becd2014627a774e9e8f668f281f1d2 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models.meta index 0cf8693..fbeb501 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models.meta @@ -3,6 +3,6 @@ guid: 758bdb1e6d29abf4e96198a11d34f313 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models/Icosphere.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models/Icosphere.meta index 45126aa..5a41527 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models/Icosphere.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models/Icosphere.meta @@ -3,6 +3,6 @@ guid: ea3fb2e0d8b9abc43b8b628e3e550872 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models/Icosphere/Icosphere.obj.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models/Icosphere/Icosphere.obj.meta index 26d5bc8..e72ff2c 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models/Icosphere/Icosphere.obj.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models/Icosphere/Icosphere.obj.meta @@ -27,12 +27,12 @@ ModelImporter: bakeSimulation: 0 resampleCurves: 1 optimizeGameObjects: 0 - motionNodeName: '' - rigImportErrors: '' - rigImportWarnings: '' - animationImportErrors: '' - animationImportWarnings: '' - animationRetargetingWarnings: '' + motionNodeName: + rigImportErrors: + rigImportWarnings: + animationImportErrors: + animationImportWarnings: + animationRetargetingWarnings: animationDoRetargetingWarnings: 0 importAnimatedCustomProperties: 0 importConstraints: 0 @@ -91,7 +91,7 @@ ModelImporter: armStretch: 0.05 legStretch: 0.05 feetSpacing: 0 - rootMotionBoneName: '' + rootMotionBoneName: hasTranslationDoF: 0 hasExtraRoot: 0 skeletonHasParents: 1 @@ -99,6 +99,6 @@ ModelImporter: animationType: 0 humanoidOversampling: 1 additionalBone: 0 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models/Icosphere/Materials.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models/Icosphere/Materials.meta index a9904b2..1eb00d1 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models/Icosphere/Materials.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models/Icosphere/Materials.meta @@ -3,6 +3,6 @@ guid: 2c07f54121eb4534e85f72041ec0f196 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models/Icosphere/Materials/Icosphere.mat.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models/Icosphere/Materials/Icosphere.mat.meta index 4fb049f..3e1c3b8 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models/Icosphere/Materials/Icosphere.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Models/Icosphere/Materials/Icosphere.mat.meta @@ -3,6 +3,6 @@ guid: 7e6bf26596c6f564097734c7cc319e15 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs.meta index 41ae6a3..6d235af 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs.meta @@ -3,6 +3,6 @@ guid: 90a1d98ef5d99304095438cdf9cbdc10 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Icosphere.prefab b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Icosphere.prefab index e2dc34b..16b861d 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Icosphere.prefab +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Icosphere.prefab @@ -105,7 +105,6 @@ GameObject: - component: {fileID: 5513112217680897778} - component: {fileID: -7012348765844800875} - component: {fileID: -5073764247860119520} - - component: {fileID: -8786580539857106334} - component: {fileID: 8774992865005872063} - component: {fileID: -73998256042230442} - component: {fileID: -2850352209440038129} @@ -168,22 +167,9 @@ MonoBehaviour: localScaleSensitivity: 0.01 compressRotation: 0 interpolateScale: 0 + interpolateRotation: 1 + interpolatePosition: 1 syncScale: 0 ---- !u!114 &-8786580539857106334 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5513112217680870098} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: b7fdb599e1359924bad6255660370252, type: 3} - m_Name: - m_EditorClassIdentifier: - syncMode: 0 - syncInterval: 0.1 - forceHidden: 0 --- !u!114 &8774992865005872063 MonoBehaviour: m_ObjectHideFlags: 0 diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Icosphere.prefab.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Icosphere.prefab.meta index aad8827..2d5d382 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Icosphere.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Icosphere.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: a104de86221e66a48832c222471d4f1e PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Player.prefab b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Player.prefab index 2e82643..a4ee312 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Player.prefab +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Player.prefab @@ -89,14 +89,14 @@ GameObject: m_Component: - component: {fileID: 4822224316094678} - component: {fileID: 114402732107420660} - - component: {fileID: 114720308987319626} - component: {fileID: 114265392388239132} + - component: {fileID: 8715117357206038899} + - component: {fileID: 114892629901890886} + - component: {fileID: 115187108610643062} - component: {fileID: 143011667059871024} - component: {fileID: 4839740653866577337} - component: {fileID: 1849877933717427647} - - component: {fileID: 114892629901890886} - component: {fileID: 6261579163786439309} - - component: {fileID: 115187108610643062} m_Layer: 0 m_Name: Player m_TagString: Player @@ -112,7 +112,7 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1480027675339556} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 1, z: 0} + m_LocalPosition: {x: 0, y: 1.08, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - {fileID: 3138541494209382947} @@ -134,23 +134,8 @@ MonoBehaviour: sceneId: 0 serverOnly: 0 visible: 0 - m_AssetId: + m_AssetId: 1f4d376d8ca693049abd1744e4c79fad hasSpawned: 0 ---- !u!114 &114720308987319626 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1480027675339556} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: b7fdb599e1359924bad6255660370252, type: 3} - m_Name: - m_EditorClassIdentifier: - syncMode: 0 - syncInterval: 0.1 - forceHidden: 0 --- !u!114 &114265392388239132 MonoBehaviour: m_ObjectHideFlags: 0 @@ -164,14 +149,85 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: syncMode: 0 - syncInterval: 0 + syncInterval: 0.1 clientAuthority: 1 - localPositionSensitivity: 0.01 - localRotationSensitivity: 0.01 - localScaleSensitivity: 0.01 - compressRotation: 0 - interpolateScale: 0 + sendInterval: 0.05 + syncPosition: 1 + syncRotation: 1 syncScale: 0 + interpolatePosition: 1 + interpolateRotation: 1 + interpolateScale: 0 + bufferTimeMultiplier: 1 + bufferSizeLimit: 64 + catchupThreshold: 4 + catchupMultiplier: 0.1 + onlySyncOnChange: 1 + bufferResetMultiplier: 5 + positionSensitivity: 0.01 + rotationSensitivity: 0.01 + scaleSensitivity: 0.01 + showGizmos: 0 + showOverlay: 0 + overlayColor: {r: 0, g: 0, b: 0, a: 0.5} +--- !u!114 &8715117357206038899 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1480027675339556} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6cac48dc20f866148b44d0f9b5850761, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 +--- !u!114 &114892629901890886 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1480027675339556} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 479a5196564ede84791870b414a13645, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 + characterController: {fileID: 143011667059871024} + moveSpeed: 8 + turnSensitivity: 5 + maxTurnSpeed: 100 + horizontal: 0 + vertical: 0 + turn: 0 + jumpSpeed: 0 + isGrounded: 1 + isFalling: 0 + velocity: {x: 0, y: 0, z: 0} +--- !u!114 &115187108610643062 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1480027675339556} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8be750efa9df50f47b65ae156053d149, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0 + playerNumber: 0 + scoreIndex: 0 + matchIndex: 0 + score: 0 + clientMatchIndex: -1 --- !u!143 &143011667059871024 CharacterController: m_ObjectHideFlags: 0 @@ -220,31 +276,6 @@ Rigidbody: m_Interpolate: 0 m_Constraints: 0 m_CollisionDetection: 0 ---- !u!114 &114892629901890886 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1480027675339556} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 479a5196564ede84791870b414a13645, type: 3} - m_Name: - m_EditorClassIdentifier: - syncMode: 0 - syncInterval: 0 - characterController: {fileID: 143011667059871024} - moveSpeed: 8 - turnSensitivity: 5 - maxTurnSpeed: 150 - horizontal: 0 - vertical: 0 - turn: 0 - jumpSpeed: 0 - isGrounded: 1 - isFalling: 0 - velocity: {x: 0, y: 0, z: 0} --- !u!114 &6261579163786439309 MonoBehaviour: m_ObjectHideFlags: 0 @@ -262,25 +293,6 @@ MonoBehaviour: color: serializedVersion: 2 rgba: 4278190080 ---- !u!114 &115187108610643062 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1480027675339556} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 8be750efa9df50f47b65ae156053d149, type: 3} - m_Name: - m_EditorClassIdentifier: - syncMode: 0 - syncInterval: 0 - playerNumber: 0 - scoreIndex: 0 - matchIndex: 0 - score: 0 - clientMatchIndex: -1 --- !u!1 &4926068573968176962 GameObject: m_ObjectHideFlags: 0 diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Player.prefab.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Player.prefab.meta index 16c9602..2aa9fe4 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Player.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Player.prefab.meta @@ -3,6 +3,6 @@ guid: 1f4d376d8ca693049abd1744e4c79fad NativeFormatImporter: externalObjects: {} mainObjectFileID: 100100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Prize.prefab b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Prize.prefab index c09f4aa..c08171f 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Prize.prefab +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Prize.prefab @@ -12,7 +12,6 @@ GameObject: - component: {fileID: 135606878775227198} - component: {fileID: 6909319328281960030} - component: {fileID: 114251241889735402} - - component: {fileID: 114426876133629542} - component: {fileID: 114048121767222990} - component: {fileID: 7669440687796875101} m_Layer: 0 @@ -80,23 +79,9 @@ MonoBehaviour: m_EditorClassIdentifier: sceneId: 0 serverOnly: 0 + visible: 0 m_AssetId: hasSpawned: 0 ---- !u!114 &114426876133629542 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1139254171913846} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: b7fdb599e1359924bad6255660370252, type: 3} - m_Name: - m_EditorClassIdentifier: - syncMode: 0 - syncInterval: 0.1 - forceHidden: 0 --- !u!114 &114048121767222990 MonoBehaviour: m_ObjectHideFlags: 0 @@ -112,7 +97,6 @@ MonoBehaviour: syncMode: 0 syncInterval: 0.1 available: 1 - spawner: {fileID: 0} randomColor: {fileID: 7669440687796875101} --- !u!114 &7669440687796875101 MonoBehaviour: @@ -185,6 +169,7 @@ MeshRenderer: m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Prize.prefab.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Prize.prefab.meta index 8bdf5f1..b0e7226 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Prize.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Prefabs/Prize.prefab.meta @@ -3,6 +3,6 @@ guid: 8cec47ed46e0eff45966a5173d3aa0d3 NativeFormatImporter: externalObjects: {} mainObjectFileID: 100100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/README.md.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/README.md.meta index dbf036c..668fabd 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/README.md.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/README.md.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 915d7b115a88c7c409dadf5bfc543737 TextScriptImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes.meta index 01b8f9d..d749d32 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes.meta @@ -3,6 +3,6 @@ guid: 060de58cc46acdf4b92e21c43400aa58 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes/Game.unity b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes/Game.unity index bfc4384..0254722 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes/Game.unity +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes/Game.unity @@ -243,7 +243,8 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - m_RemovedComponents: [] + m_RemovedComponents: + - {fileID: -8786580539857106334, guid: a104de86221e66a48832c222471d4f1e, type: 3} m_SourcePrefab: {fileID: 100100000, guid: a104de86221e66a48832c222471d4f1e, type: 3} --- !u!4 &535961556 stripped Transform: @@ -338,7 +339,8 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - m_RemovedComponents: [] + m_RemovedComponents: + - {fileID: -8786580539857106334, guid: a104de86221e66a48832c222471d4f1e, type: 3} m_SourcePrefab: {fileID: 100100000, guid: a104de86221e66a48832c222471d4f1e, type: 3} --- !u!4 &1069065321 stripped Transform: @@ -433,7 +435,8 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - m_RemovedComponents: [] + m_RemovedComponents: + - {fileID: -8786580539857106334, guid: a104de86221e66a48832c222471d4f1e, type: 3} m_SourcePrefab: {fileID: 100100000, guid: a104de86221e66a48832c222471d4f1e, type: 3} --- !u!4 &1072549450 stripped Transform: @@ -457,6 +460,7 @@ GameObject: - component: {fileID: 1305256740} - component: {fileID: 1305256739} - component: {fileID: 1305256738} + - component: {fileID: 1305256746} m_Layer: 0 m_Name: PlayArea m_TagString: Untagged @@ -591,6 +595,23 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1305256746 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1305256737} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3} + m_Name: + m_EditorClassIdentifier: + sceneId: 3504801306 + serverOnly: 0 + visible: 0 + m_AssetId: + hasSpawned: 0 --- !u!1 &1713236906 GameObject: m_ObjectHideFlags: 0 @@ -721,7 +742,8 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - m_RemovedComponents: [] + m_RemovedComponents: + - {fileID: -8786580539857106334, guid: a104de86221e66a48832c222471d4f1e, type: 3} m_SourcePrefab: {fileID: 100100000, guid: a104de86221e66a48832c222471d4f1e, type: 3} --- !u!4 &2061474489 stripped Transform: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes/Game.unity.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes/Game.unity.meta index 274c183..dae050d 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes/Game.unity.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes/Game.unity.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: d45ed07e5475d4740812c97ae565255c DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes/Main.unity b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes/Main.unity index b34c652..b01ced7 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes/Main.unity +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes/Main.unity @@ -216,8 +216,9 @@ GameObject: - component: {fileID: 69965669} - component: {fileID: 69965667} - component: {fileID: 69965671} + - component: {fileID: 69965668} m_Layer: 0 - m_Name: Network + m_Name: NetworkManager m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -238,6 +239,18 @@ MonoBehaviour: showGUI: 1 offsetX: 0 offsetY: 0 +--- !u!114 &69965668 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 69965666} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b979f26c95d34324ba005bfacfa9c4fc, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!114 &69965669 MonoBehaviour: m_ObjectHideFlags: 0 @@ -255,8 +268,6 @@ MonoBehaviour: runInBackground: 1 autoStartServerBuild: 1 serverTickRate: 30 - serverBatching: 0 - serverBatchInterval: 0 offlineScene: onlineScene: transport: {fileID: 69965671} @@ -302,12 +313,15 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Port: 7777 + DualMode: 1 NoDelay: 1 Interval: 10 + Timeout: 10000 FastResend: 2 CongestionWindow: 0 SendWindowSize: 4096 ReceiveWindowSize: 4096 + NonAlloc: 1 debugLog: 0 statisticsGUI: 0 statisticsLog: 0 diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes/Main.unity.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes/Main.unity.meta index 365d1c5..036bc0d 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes/Main.unity.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scenes/Main.unity.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 0fa8b7965660de64f8aefd6b64f18a08 DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts.meta index 2b4112f..bcb2ac2 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts.meta @@ -3,6 +3,6 @@ guid: 6d930bed284ca5040b2743524031cc13 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/MultiSceneNetManager.cs b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/MultiSceneNetManager.cs index b296e31..116b59b 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/MultiSceneNetManager.cs +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/MultiSceneNetManager.cs @@ -3,6 +3,11 @@ using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; +/* + Documentation: https://mirror-networking.gitbook.io/docs/components/network-manager + API Reference: https://mirror-networking.com/docs/api/Mirror.NetworkManager.html +*/ + namespace Mirror.Examples.MultipleAdditiveScenes { [AddComponentMenu("")] @@ -34,14 +39,14 @@ namespace Mirror.Examples.MultipleAdditiveScenes /// The default implementation for this function creates a new player object from the playerPrefab. /// /// Connection from client. - public override void OnServerAddPlayer(NetworkConnection conn) + public override void OnServerAddPlayer(NetworkConnectionToClient conn) { StartCoroutine(OnServerAddPlayerDelayed(conn)); } // This delay is mostly for the host player that loads too fast for the // server to have subscenes async loaded from OnStartServer ahead of it. - IEnumerator OnServerAddPlayerDelayed(NetworkConnection conn) + IEnumerator OnServerAddPlayerDelayed(NetworkConnectionToClient conn) { // wait for server to async load all subscenes for game instances while (!subscenesLoaded) @@ -60,13 +65,13 @@ namespace Mirror.Examples.MultipleAdditiveScenes playerScore.scoreIndex = clientIndex / subScenes.Count; playerScore.matchIndex = clientIndex % subScenes.Count; - clientIndex++; - // Do this only on server, not on clients // This is what allows the NetworkSceneChecker on player and scene objects // to isolate matches per scene instance on server. if (subScenes.Count > 0) SceneManager.MoveGameObjectToScene(conn.identity.gameObject, subScenes[clientIndex % subScenes.Count]); + + clientIndex++; } #endregion diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/MultiSceneNetManager.cs.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/MultiSceneNetManager.cs.meta index fc86894..dbca140 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/MultiSceneNetManager.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/MultiSceneNetManager.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PhysicsCollision.cs.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PhysicsCollision.cs.meta index 0aae7a1..2343563 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PhysicsCollision.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PhysicsCollision.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PhysicsSimulator.cs.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PhysicsSimulator.cs.meta index fa7c2b5..a48884d 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PhysicsSimulator.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PhysicsSimulator.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PlayerCamera.cs b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PlayerCamera.cs new file mode 100644 index 0000000..b61c18a --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PlayerCamera.cs @@ -0,0 +1,41 @@ +using UnityEngine; +using UnityEngine.SceneManagement; + +// This sets up the scene camera for the local player + +namespace Mirror.Examples.MultipleAdditiveScenes +{ + public class PlayerCamera : NetworkBehaviour + { + Camera mainCam; + + void Awake() + { + mainCam = Camera.main; + } + + public override void OnStartLocalPlayer() + { + if (mainCam != null) + { + // configure and make camera a child of player with 3rd person offset + mainCam.orthographic = false; + mainCam.transform.SetParent(transform); + mainCam.transform.localPosition = new Vector3(0f, 3f, -8f); + mainCam.transform.localEulerAngles = new Vector3(10f, 0f, 0f); + } + } + + public override void OnStopLocalPlayer() + { + if (mainCam != null) + { + mainCam.transform.SetParent(null); + SceneManager.MoveGameObjectToScene(mainCam.gameObject, SceneManager.GetActiveScene()); + mainCam.orthographic = true; + mainCam.transform.localPosition = new Vector3(0f, 70f, 0f); + mainCam.transform.localEulerAngles = new Vector3(90f, 0f, 0f); + } + } + } +} diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PlayerCamera.cs.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PlayerCamera.cs.meta new file mode 100644 index 0000000..6d4a9c9 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PlayerCamera.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6cac48dc20f866148b44d0f9b5850761 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PlayerController.cs b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PlayerController.cs index 4d869d1..b8a1c00 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PlayerController.cs +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PlayerController.cs @@ -15,37 +15,21 @@ namespace Mirror.Examples.MultipleAdditiveScenes { if (characterController == null) characterController = GetComponent(); - } - void Start() - { - characterController.enabled = isLocalPlayer; + characterController.enabled = false; + GetComponent().isKinematic = true; + GetComponent().clientAuthority = true; } public override void OnStartLocalPlayer() { - Camera.main.orthographic = false; - Camera.main.transform.SetParent(transform); - Camera.main.transform.localPosition = new Vector3(0f, 3f, -8f); - Camera.main.transform.localEulerAngles = new Vector3(10f, 0f, 0f); - } - - void OnDisable() - { - if (isLocalPlayer && Camera.main != null) - { - Camera.main.orthographic = true; - Camera.main.transform.SetParent(null); - SceneManager.MoveGameObjectToScene(Camera.main.gameObject, SceneManager.GetActiveScene()); - Camera.main.transform.localPosition = new Vector3(0f, 70f, 0f); - Camera.main.transform.localEulerAngles = new Vector3(90f, 0f, 0f); - } + characterController.enabled = true; } [Header("Movement Settings")] public float moveSpeed = 8f; public float turnSensitivity = 5f; - public float maxTurnSpeed = 150f; + public float maxTurnSpeed = 100f; [Header("Diagnostics")] public float horizontal; @@ -58,7 +42,7 @@ namespace Mirror.Examples.MultipleAdditiveScenes void Update() { - if (!isLocalPlayer || !characterController.enabled) + if (!isLocalPlayer || characterController == null || !characterController.enabled) return; horizontal = Input.GetAxis("Horizontal"); @@ -90,7 +74,7 @@ namespace Mirror.Examples.MultipleAdditiveScenes void FixedUpdate() { - if (!isLocalPlayer || characterController == null) + if (!isLocalPlayer || characterController == null || !characterController.enabled) return; transform.Rotate(0f, turn * Time.fixedDeltaTime, 0f); diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PlayerController.cs.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PlayerController.cs.meta index 9d5e109..c3bad11 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PlayerController.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PlayerController.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PlayerScore.cs.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PlayerScore.cs.meta index a20c7ca..3b9c9dd 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PlayerScore.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/PlayerScore.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/RandomColor.cs.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/RandomColor.cs.meta index 7a9c6f1..2bef2a6 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/RandomColor.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/RandomColor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/Reward.cs b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/Reward.cs index ea7516d..e3d2bc0 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/Reward.cs +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/Reward.cs @@ -36,7 +36,7 @@ namespace Mirror.Examples.MultipleAdditiveScenes // calculate the points from the color ... lighter scores higher as the average approaches 255 // UnityEngine.Color RGB values are float fractions of 255 uint points = (uint)(((color.r) + (color.g) + (color.b)) / 3); - // Debug.LogFormat(LogType.Log, "Scored {0} points R:{1} G:{2} B:{3}", points, color.r, color.g, color.b); + //Debug.Log($"Scored {points} points R:{color.r} G:{color.g} B:{color.b}"); // award the points via SyncVar on the PlayerController player.GetComponent().score += points; diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/Reward.cs.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/Reward.cs.meta index 931aa47..1bb7ea3 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/Reward.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/Reward.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/Spawner.cs.meta b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/Spawner.cs.meta index 602d86a..9d9b2b1 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/Spawner.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleAdditiveScenes/Scripts/Spawner.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches.meta b/UnityProject/Assets/Mirror/Examples/MultipleMatches.meta index 10786b5..c8788e5 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches.meta @@ -3,6 +3,6 @@ guid: 8e1be7be6da8e2448ba40eba0daa44b9 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs.meta b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs.meta index c61a3fb..7d19e91 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs.meta @@ -3,6 +3,6 @@ guid: ad2253a5702ed854995bf034be09a1ae folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/CellGUI.prefab.meta b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/CellGUI.prefab.meta index a4b23e0..8ddee88 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/CellGUI.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/CellGUI.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 5ab8c221183f0094eb04b7f6eb569e7b PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchController.prefab b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchController.prefab index 2ecba6c..cba01f5 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchController.prefab +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchController.prefab @@ -63,7 +63,7 @@ MonoBehaviour: m_GameObject: {fileID: 2555295541931690112} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -2095666955, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 8a8695521f0d02e499659fee002a26c2, type: 3} m_Name: m_EditorClassIdentifier: m_Padding: @@ -92,7 +92,7 @@ GameObject: - component: {fileID: 4694292283357099176} - component: {fileID: 1964986598517419573} - component: {fileID: 400263754679705305} - - component: {fileID: 7350382998445798922} + - component: {fileID: 4082668978785527835} - component: {fileID: 6069480080749678769} m_Layer: 5 m_Name: MatchController @@ -156,7 +156,7 @@ MonoBehaviour: m_GameObject: {fileID: 2923784255186993310} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1980459831, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} m_Name: m_EditorClassIdentifier: m_UiScaleMode: 0 @@ -190,7 +190,7 @@ MonoBehaviour: m_GameObject: {fileID: 2923784255186993310} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1301386320, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} m_Name: m_EditorClassIdentifier: m_IgnoreReversedGraphics: 1 @@ -212,9 +212,10 @@ MonoBehaviour: m_EditorClassIdentifier: sceneId: 0 serverOnly: 0 - m_AssetId: + visible: 0 + m_AssetId: e101d385946c91b4c81f318efc723a88 hasSpawned: 0 ---- !u!114 &7350382998445798922 +--- !u!114 &4082668978785527835 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -223,12 +224,11 @@ MonoBehaviour: m_GameObject: {fileID: 2923784255186993310} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 1020a74962faada4b807ac5dc053a4cf, type: 3} + m_Script: {fileID: 11500000, guid: 5d17e718851449a6879986e45c458fb7, type: 3} m_Name: m_EditorClassIdentifier: syncMode: 0 syncInterval: 0.1 - currentMatchDebug: --- !u!114 &6069480080749678769 MonoBehaviour: m_ObjectHideFlags: 0 @@ -254,7 +254,6 @@ MonoBehaviour: player2: {fileID: 0} startingPlayer: {fileID: 0} currentPlayer: {fileID: 0} - boardScore: 0 --- !u!1 &3002539098384373397 GameObject: m_ObjectHideFlags: 0 @@ -311,12 +310,13 @@ MonoBehaviour: m_GameObject: {fileID: 3002539098384373397} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -329,6 +329,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!114 &3578082239447023245 MonoBehaviour: m_ObjectHideFlags: 0 @@ -338,7 +339,7 @@ MonoBehaviour: m_GameObject: {fileID: 3002539098384373397} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: @@ -352,17 +353,20 @@ MonoBehaviour: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled m_Interactable: 1 m_TargetGraphic: {fileID: 5971814406204202327} @@ -436,12 +440,13 @@ MonoBehaviour: m_GameObject: {fileID: 3827598301957981835} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -454,6 +459,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!114 &505721148531914274 MonoBehaviour: m_ObjectHideFlags: 0 @@ -463,7 +469,7 @@ MonoBehaviour: m_GameObject: {fileID: 3827598301957981835} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: @@ -477,17 +483,20 @@ MonoBehaviour: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled m_Interactable: 1 m_TargetGraphic: {fileID: 3285613769939000379} @@ -560,12 +569,13 @@ MonoBehaviour: m_GameObject: {fileID: 4626758344199994040} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 0, b: 0, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -592,7 +602,7 @@ MonoBehaviour: m_GameObject: {fileID: 4626758344199994040} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -900027084, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: e19747de3f5aca642ab2be37e372fb86, type: 3} m_Name: m_EditorClassIdentifier: m_EffectColor: {r: 1, g: 1, b: 1, a: 0.5} @@ -653,12 +663,13 @@ MonoBehaviour: m_GameObject: {fileID: 4990186583940393891} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -685,7 +696,7 @@ MonoBehaviour: m_GameObject: {fileID: 4990186583940393891} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -900027084, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: e19747de3f5aca642ab2be37e372fb86, type: 3} m_Name: m_EditorClassIdentifier: m_EffectColor: {r: 1, g: 1, b: 1, a: 0.5} @@ -746,12 +757,13 @@ MonoBehaviour: m_GameObject: {fileID: 6520692455078726128} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0, g: 0, b: 1, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -778,7 +790,7 @@ MonoBehaviour: m_GameObject: {fileID: 6520692455078726128} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -900027084, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: e19747de3f5aca642ab2be37e372fb86, type: 3} m_Name: m_EditorClassIdentifier: m_EffectColor: {r: 1, g: 1, b: 1, a: 0.5} @@ -838,12 +850,13 @@ MonoBehaviour: m_GameObject: {fileID: 7074721236105841979} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -915,12 +928,13 @@ MonoBehaviour: m_GameObject: {fileID: 8221244113287092374} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -945,21 +959,66 @@ PrefabInstance: m_Modification: m_TransformParent: {fileID: 9152319602512786093} m_Modifications: - - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: matchController - value: - objectReference: {fileID: 6069480080749678769} - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: cellValue value: 128 objectReference: {fileID: 0} + - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: matchController + value: + objectReference: {fileID: 6069480080749678769} - target: {fileID: 4903779300334215825, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_Name value: B3 objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_RootOrder + value: 7 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMax.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_SizeDelta.y + value: 0 + objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_LocalPosition.x @@ -975,6 +1034,11 @@ PrefabInstance: propertyPath: m_LocalPosition.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_LocalRotation.x @@ -992,13 +1056,13 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} - propertyPath: m_LocalRotation.w - value: 1 + propertyPath: m_AnchoredPosition.x + value: 0 objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} - propertyPath: m_RootOrder - value: 7 + propertyPath: m_AnchoredPosition.y + value: 0 objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} @@ -1015,56 +1079,6 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchoredPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchoredPosition.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_SizeDelta.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_SizeDelta.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMin.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMin.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMax.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMax.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_Pivot.x - value: 0.5 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_Pivot.y - value: 0.5 - objectReference: {fileID: 0} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} --- !u!224 &8092575970934441626 stripped @@ -1080,21 +1094,66 @@ PrefabInstance: m_Modification: m_TransformParent: {fileID: 9152319602512786093} m_Modifications: - - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: matchController - value: - objectReference: {fileID: 6069480080749678769} - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: cellValue value: 1 objectReference: {fileID: 0} + - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: matchController + value: + objectReference: {fileID: 6069480080749678769} - target: {fileID: 4903779300334215825, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_Name value: A1 objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMax.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_SizeDelta.y + value: 0 + objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_LocalPosition.x @@ -1110,6 +1169,11 @@ PrefabInstance: propertyPath: m_LocalPosition.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_LocalRotation.x @@ -1127,12 +1191,12 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} - propertyPath: m_LocalRotation.w - value: 1 + propertyPath: m_AnchoredPosition.x + value: 0 objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} - propertyPath: m_RootOrder + propertyPath: m_AnchoredPosition.y value: 0 objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, @@ -1150,56 +1214,6 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchoredPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchoredPosition.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_SizeDelta.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_SizeDelta.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMin.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMin.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMax.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMax.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_Pivot.x - value: 0.5 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_Pivot.y - value: 0.5 - objectReference: {fileID: 0} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} --- !u!224 &7845801290156398150 stripped @@ -1215,21 +1229,66 @@ PrefabInstance: m_Modification: m_TransformParent: {fileID: 9152319602512786093} m_Modifications: - - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: matchController - value: - objectReference: {fileID: 6069480080749678769} - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: cellValue value: 16 objectReference: {fileID: 0} + - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: matchController + value: + objectReference: {fileID: 6069480080749678769} - target: {fileID: 4903779300334215825, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_Name value: B2 objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_RootOrder + value: 4 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMax.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_SizeDelta.y + value: 0 + objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_LocalPosition.x @@ -1245,6 +1304,11 @@ PrefabInstance: propertyPath: m_LocalPosition.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_LocalRotation.x @@ -1262,13 +1326,13 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} - propertyPath: m_LocalRotation.w - value: 1 + propertyPath: m_AnchoredPosition.x + value: 0 objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} - propertyPath: m_RootOrder - value: 4 + propertyPath: m_AnchoredPosition.y + value: 0 objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} @@ -1285,56 +1349,6 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchoredPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchoredPosition.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_SizeDelta.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_SizeDelta.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMin.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMin.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMax.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMax.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_Pivot.x - value: 0.5 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_Pivot.y - value: 0.5 - objectReference: {fileID: 0} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} --- !u!224 &5768719360570285027 stripped @@ -1350,21 +1364,66 @@ PrefabInstance: m_Modification: m_TransformParent: {fileID: 9152319602512786093} m_Modifications: - - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: matchController - value: - objectReference: {fileID: 6069480080749678769} - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: cellValue value: 2 objectReference: {fileID: 0} + - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: matchController + value: + objectReference: {fileID: 6069480080749678769} - target: {fileID: 4903779300334215825, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_Name value: B1 objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_RootOrder + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMax.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_SizeDelta.y + value: 0 + objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_LocalPosition.x @@ -1380,6 +1439,11 @@ PrefabInstance: propertyPath: m_LocalPosition.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_LocalRotation.x @@ -1397,13 +1461,13 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} - propertyPath: m_LocalRotation.w - value: 1 + propertyPath: m_AnchoredPosition.x + value: 0 objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} - propertyPath: m_RootOrder - value: 1 + propertyPath: m_AnchoredPosition.y + value: 0 objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} @@ -1420,56 +1484,6 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchoredPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchoredPosition.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_SizeDelta.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_SizeDelta.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMin.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMin.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMax.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMax.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_Pivot.x - value: 0.5 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_Pivot.y - value: 0.5 - objectReference: {fileID: 0} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} --- !u!224 &6007826401523109089 stripped @@ -1485,21 +1499,66 @@ PrefabInstance: m_Modification: m_TransformParent: {fileID: 9152319602512786093} m_Modifications: - - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: matchController - value: - objectReference: {fileID: 6069480080749678769} - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: cellValue value: 8 objectReference: {fileID: 0} + - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: matchController + value: + objectReference: {fileID: 6069480080749678769} - target: {fileID: 4903779300334215825, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_Name value: A2 objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_RootOrder + value: 3 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMax.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_SizeDelta.y + value: 0 + objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_LocalPosition.x @@ -1515,6 +1574,11 @@ PrefabInstance: propertyPath: m_LocalPosition.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_LocalRotation.x @@ -1532,13 +1596,13 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} - propertyPath: m_LocalRotation.w - value: 1 + propertyPath: m_AnchoredPosition.x + value: 0 objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} - propertyPath: m_RootOrder - value: 3 + propertyPath: m_AnchoredPosition.y + value: 0 objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} @@ -1555,56 +1619,6 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchoredPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchoredPosition.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_SizeDelta.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_SizeDelta.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMin.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMin.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMax.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMax.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_Pivot.x - value: 0.5 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_Pivot.y - value: 0.5 - objectReference: {fileID: 0} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} --- !u!224 &5012654960586055004 stripped @@ -1620,21 +1634,66 @@ PrefabInstance: m_Modification: m_TransformParent: {fileID: 9152319602512786093} m_Modifications: - - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: matchController - value: - objectReference: {fileID: 6069480080749678769} - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: cellValue value: 32 objectReference: {fileID: 0} + - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: matchController + value: + objectReference: {fileID: 6069480080749678769} - target: {fileID: 4903779300334215825, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_Name value: C2 objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_RootOrder + value: 5 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMax.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_SizeDelta.y + value: 0 + objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_LocalPosition.x @@ -1650,6 +1709,11 @@ PrefabInstance: propertyPath: m_LocalPosition.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_LocalRotation.x @@ -1667,13 +1731,13 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} - propertyPath: m_LocalRotation.w - value: 1 + propertyPath: m_AnchoredPosition.x + value: 0 objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} - propertyPath: m_RootOrder - value: 5 + propertyPath: m_AnchoredPosition.y + value: 0 objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} @@ -1690,56 +1754,6 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchoredPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchoredPosition.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_SizeDelta.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_SizeDelta.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMin.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMin.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMax.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMax.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_Pivot.x - value: 0.5 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_Pivot.y - value: 0.5 - objectReference: {fileID: 0} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} --- !u!224 &3581888124560234741 stripped @@ -1755,21 +1769,66 @@ PrefabInstance: m_Modification: m_TransformParent: {fileID: 9152319602512786093} m_Modifications: - - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: matchController - value: - objectReference: {fileID: 6069480080749678769} - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: cellValue value: 4 objectReference: {fileID: 0} + - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: matchController + value: + objectReference: {fileID: 6069480080749678769} - target: {fileID: 4903779300334215825, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_Name value: C1 objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_RootOrder + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMax.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_SizeDelta.y + value: 0 + objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_LocalPosition.x @@ -1785,6 +1844,11 @@ PrefabInstance: propertyPath: m_LocalPosition.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_LocalRotation.x @@ -1802,13 +1866,13 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} - propertyPath: m_LocalRotation.w - value: 1 + propertyPath: m_AnchoredPosition.x + value: 0 objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} - propertyPath: m_RootOrder - value: 2 + propertyPath: m_AnchoredPosition.y + value: 0 objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} @@ -1825,56 +1889,6 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchoredPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchoredPosition.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_SizeDelta.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_SizeDelta.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMin.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMin.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMax.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMax.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_Pivot.x - value: 0.5 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_Pivot.y - value: 0.5 - objectReference: {fileID: 0} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} --- !u!224 &2045714052640889548 stripped @@ -1890,21 +1904,66 @@ PrefabInstance: m_Modification: m_TransformParent: {fileID: 9152319602512786093} m_Modifications: - - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: matchController - value: - objectReference: {fileID: 6069480080749678769} - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: cellValue value: 64 objectReference: {fileID: 0} + - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: matchController + value: + objectReference: {fileID: 6069480080749678769} - target: {fileID: 4903779300334215825, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_Name value: A3 objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_RootOrder + value: 6 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMax.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_SizeDelta.y + value: 0 + objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_LocalPosition.x @@ -1920,6 +1979,11 @@ PrefabInstance: propertyPath: m_LocalPosition.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_LocalRotation.x @@ -1937,13 +2001,13 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} - propertyPath: m_LocalRotation.w - value: 1 + propertyPath: m_AnchoredPosition.x + value: 0 objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} - propertyPath: m_RootOrder - value: 6 + propertyPath: m_AnchoredPosition.y + value: 0 objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} @@ -1960,56 +2024,6 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchoredPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchoredPosition.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_SizeDelta.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_SizeDelta.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMin.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMin.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMax.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMax.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_Pivot.x - value: 0.5 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_Pivot.y - value: 0.5 - objectReference: {fileID: 0} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} --- !u!224 &1213482271452262600 stripped @@ -2025,21 +2039,66 @@ PrefabInstance: m_Modification: m_TransformParent: {fileID: 9152319602512786093} m_Modifications: - - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: matchController - value: - objectReference: {fileID: 6069480080749678769} - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: cellValue value: 256 objectReference: {fileID: 0} + - target: {fileID: 2439948753870522382, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: matchController + value: + objectReference: {fileID: 6069480080749678769} - target: {fileID: 4903779300334215825, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_Name value: C3 objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_RootOrder + value: 8 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMax.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_SizeDelta.y + value: 0 + objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_LocalPosition.x @@ -2055,6 +2114,11 @@ PrefabInstance: propertyPath: m_LocalPosition.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} propertyPath: m_LocalRotation.x @@ -2072,13 +2136,13 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} - propertyPath: m_LocalRotation.w - value: 1 + propertyPath: m_AnchoredPosition.x + value: 0 objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} - propertyPath: m_RootOrder - value: 8 + propertyPath: m_AnchoredPosition.y + value: 0 objectReference: {fileID: 0} - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} @@ -2095,56 +2159,6 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchoredPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchoredPosition.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_SizeDelta.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_SizeDelta.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMin.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMin.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMax.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_AnchorMax.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_Pivot.x - value: 0.5 - objectReference: {fileID: 0} - - target: {fileID: 8667093524748208693, guid: 5ab8c221183f0094eb04b7f6eb569e7b, - type: 3} - propertyPath: m_Pivot.y - value: 0.5 - objectReference: {fileID: 0} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 5ab8c221183f0094eb04b7f6eb569e7b, type: 3} --- !u!224 &134186364329385301 stripped diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchController.prefab.meta b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchController.prefab.meta index e1cda8d..e358fcc 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchController.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchController.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: e101d385946c91b4c81f318efc723a88 PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchGUI.prefab.meta b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchGUI.prefab.meta index 3096ce4..3308e2c 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchGUI.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchGUI.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: df3727c2222378b4ca786485a8b09981 PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchPlayer.prefab b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchPlayer.prefab index acc9298..dd41ad6 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchPlayer.prefab +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchPlayer.prefab @@ -10,7 +10,7 @@ GameObject: m_Component: - component: {fileID: 1204658229548937417} - component: {fileID: 7660209086417859164} - - component: {fileID: -3043620344775994659} + - component: {fileID: -3992624706284245286} m_Layer: 0 m_Name: MatchPlayer m_TagString: Untagged @@ -46,9 +46,10 @@ MonoBehaviour: m_EditorClassIdentifier: sceneId: 0 serverOnly: 0 - m_AssetId: + visible: 0 + m_AssetId: 11d2414f1e120a14c98cba6866e5348f hasSpawned: 0 ---- !u!114 &-3043620344775994659 +--- !u!114 &-3992624706284245286 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -57,9 +58,8 @@ MonoBehaviour: m_GameObject: {fileID: 3559083313143303799} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 1020a74962faada4b807ac5dc053a4cf, type: 3} + m_Script: {fileID: 11500000, guid: 5d17e718851449a6879986e45c458fb7, type: 3} m_Name: m_EditorClassIdentifier: syncMode: 0 syncInterval: 0.1 - currentMatchDebug: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchPlayer.prefab.meta b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchPlayer.prefab.meta index 1c8503f..66f3795 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchPlayer.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/MatchPlayer.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 11d2414f1e120a14c98cba6866e5348f PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/PlayerGUI.prefab.meta b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/PlayerGUI.prefab.meta index 7dbc934..27639dc 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/PlayerGUI.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Prefabs/PlayerGUI.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 504f86497324bc040be44f6f88b6cc73 PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/README.md.meta b/UnityProject/Assets/Mirror/Examples/MultipleMatches/README.md.meta index 3c529de..fdee840 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/README.md.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/README.md.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 9749e23e4e90e0646afc81061710a927 TextScriptImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scenes.meta b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scenes.meta index 085483e..af27542 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scenes.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scenes.meta @@ -3,6 +3,6 @@ guid: a92fd9255da5fe9449e612566195a3aa folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scenes/Main.unity b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scenes/Main.unity index 13a5d18..993ff0b 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scenes/Main.unity +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scenes/Main.unity @@ -54,7 +54,7 @@ LightmapSettings: m_EnableBakedLightmaps: 0 m_EnableRealtimeLightmaps: 0 m_LightmapEditorSettings: - serializedVersion: 10 + serializedVersion: 12 m_Resolution: 2 m_BakeResolution: 40 m_AtlasSize: 1024 @@ -62,6 +62,7 @@ LightmapSettings: m_AOMaxDistance: 1 m_CompAOExponent: 1 m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 m_Padding: 2 m_LightmapParameters: {fileID: 0} m_LightmapsBakeMode: 1 @@ -76,10 +77,16 @@ LightmapSettings: m_PVRDirectSampleCount: 32 m_PVRSampleCount: 512 m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 512 + 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_PVRFilteringMode: 2 + m_PVREnvironmentMIS: 0 m_PVRCulling: 1 m_PVRFilteringGaussRadiusDirect: 1 m_PVRFilteringGaussRadiusIndirect: 5 @@ -87,7 +94,9 @@ LightmapSettings: m_PVRFilteringAtrousPositionSigmaDirect: 0.5 m_PVRFilteringAtrousPositionSigmaIndirect: 2 m_PVRFilteringAtrousPositionSigmaAO: 1 - m_ShowResolutionOverlay: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 m_LightingDataAsset: {fileID: 0} m_UseShadowmask: 1 --- !u!196 &4 @@ -160,7 +169,7 @@ MonoBehaviour: m_GameObject: {fileID: 8446050} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: @@ -174,17 +183,20 @@ MonoBehaviour: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled m_Interactable: 1 m_TargetGraphic: {fileID: 8446053} @@ -211,12 +223,13 @@ MonoBehaviour: m_GameObject: {fileID: 8446050} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -229,6 +242,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &8446054 CanvasRenderer: m_ObjectHideFlags: 0 @@ -283,12 +297,13 @@ MonoBehaviour: m_GameObject: {fileID: 18198962} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -360,12 +375,13 @@ MonoBehaviour: m_GameObject: {fileID: 73998977} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -437,12 +453,13 @@ MonoBehaviour: m_GameObject: {fileID: 239920241} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -455,6 +472,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &239920244 CanvasRenderer: m_ObjectHideFlags: 0 @@ -509,12 +527,13 @@ MonoBehaviour: m_GameObject: {fileID: 346368339} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -624,12 +643,13 @@ MonoBehaviour: m_GameObject: {fileID: 492832111} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -642,6 +662,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &492832114 CanvasRenderer: m_ObjectHideFlags: 0 @@ -696,12 +717,13 @@ MonoBehaviour: m_GameObject: {fileID: 647085297} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -810,7 +832,7 @@ MonoBehaviour: m_GameObject: {fileID: 777606420} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -900027084, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: e19747de3f5aca642ab2be37e372fb86, type: 3} m_Name: m_EditorClassIdentifier: m_EffectColor: {r: 0, g: 0, b: 0, a: 0.5} @@ -825,12 +847,13 @@ MonoBehaviour: m_GameObject: {fileID: 777606420} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 0, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -894,9 +917,10 @@ Camera: m_ClearFlags: 1 m_BackGroundColor: {r: 0.21698111, g: 0.21698111, b: 0.21698111, a: 0} m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 m_SensorSize: {x: 36, y: 24} m_LensShift: {x: 0, y: 0} - m_GateFitMode: 2 m_FocalLength: 50 m_NormalizedViewPortRect: serializedVersion: 2 @@ -986,7 +1010,7 @@ MonoBehaviour: m_GameObject: {fileID: 816951367} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -2061169968, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 2a4db7a114972834c8e4117be1d82ba3, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: @@ -1000,17 +1024,20 @@ MonoBehaviour: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled m_Interactable: 1 m_TargetGraphic: {fileID: 239920243} @@ -1031,12 +1058,13 @@ MonoBehaviour: m_GameObject: {fileID: 816951367} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -1049,6 +1077,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &816951371 CanvasRenderer: m_ObjectHideFlags: 0 @@ -1069,8 +1098,9 @@ GameObject: - component: {fileID: 874070009} - component: {fileID: 874070008} - component: {fileID: 874070007} + - component: {fileID: 874070011} m_Layer: 0 - m_Name: Network + m_Name: NetworkManager m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -1100,15 +1130,22 @@ MonoBehaviour: m_GameObject: {fileID: 874070006} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: c7424c1070fad4ba2a7a96b02fbeb4bb, type: 3} + m_Script: {fileID: 11500000, guid: 6b0fecffa3f624585964b0d0eb21b18e, type: 3} m_Name: m_EditorClassIdentifier: - port: 7777 + Port: 7777 + DualMode: 1 NoDelay: 1 - serverMaxMessageSize: 16384 - serverMaxReceivesPerTick: 10000 - clientMaxMessageSize: 16384 - clientMaxReceivesPerTick: 1000 + Interval: 10 + Timeout: 10000 + FastResend: 2 + CongestionWindow: 0 + SendWindowSize: 4096 + ReceiveWindowSize: 4096 + NonAlloc: 1 + debugLog: 0 + statisticsGUI: 0 + statisticsLog: 0 --- !u!114 &874070009 MonoBehaviour: m_ObjectHideFlags: 0 @@ -1122,9 +1159,9 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: dontDestroyOnLoad: 0 + PersistNetworkManagerToOfflineScene: 0 runInBackground: 1 autoStartServerBuild: 1 - showDebugMessages: 0 serverTickRate: 30 offlineScene: onlineScene: @@ -1156,6 +1193,18 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &874070011 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 874070006} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d09f5c8bf2f4747b7a9284ef5d9ce2a7, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1 &1005098733 GameObject: m_ObjectHideFlags: 0 @@ -1205,12 +1254,13 @@ MonoBehaviour: m_GameObject: {fileID: 1005098733} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0, g: 0, b: 0, a: 0.19607843} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -1223,6 +1273,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &1005098736 CanvasRenderer: m_ObjectHideFlags: 0 @@ -1240,7 +1291,7 @@ MonoBehaviour: m_GameObject: {fileID: 1005098733} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1367256648, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 1aa08ab6e0800fa44ae55d278d1423e3, type: 3} m_Name: m_EditorClassIdentifier: m_Content: {fileID: 1386671669} @@ -1309,12 +1360,13 @@ MonoBehaviour: m_GameObject: {fileID: 1100846360} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -1327,6 +1379,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!114 &1100846363 MonoBehaviour: m_ObjectHideFlags: 0 @@ -1336,7 +1389,7 @@ MonoBehaviour: m_GameObject: {fileID: 1100846360} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: @@ -1350,17 +1403,20 @@ MonoBehaviour: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled m_Interactable: 1 m_TargetGraphic: {fileID: 1100846362} @@ -1434,7 +1490,7 @@ MonoBehaviour: m_GameObject: {fileID: 1108207369} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: @@ -1448,17 +1504,20 @@ MonoBehaviour: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled m_Interactable: 1 m_TargetGraphic: {fileID: 1108207372} @@ -1485,12 +1544,13 @@ MonoBehaviour: m_GameObject: {fileID: 1108207369} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -1503,6 +1563,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &1108207373 CanvasRenderer: m_ObjectHideFlags: 0 @@ -1559,7 +1620,7 @@ MonoBehaviour: m_GameObject: {fileID: 1132002899} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -2061169968, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 2a4db7a114972834c8e4117be1d82ba3, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: @@ -1573,17 +1634,20 @@ MonoBehaviour: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled m_Interactable: 1 m_TargetGraphic: {fileID: 492832113} @@ -1604,12 +1668,13 @@ MonoBehaviour: m_GameObject: {fileID: 1132002899} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -1622,6 +1687,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &1132002903 CanvasRenderer: m_ObjectHideFlags: 0 @@ -1772,7 +1838,7 @@ MonoBehaviour: m_GameObject: {fileID: 1295888617} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1741964061, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3} m_Name: m_EditorClassIdentifier: m_HorizontalFit: 0 @@ -1786,7 +1852,7 @@ MonoBehaviour: m_GameObject: {fileID: 1295888617} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1297475563, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3} m_Name: m_EditorClassIdentifier: m_Padding: @@ -1800,6 +1866,8 @@ MonoBehaviour: m_ChildForceExpandHeight: 0 m_ChildControlWidth: 1 m_ChildControlHeight: 0 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 --- !u!1 &1348255963 GameObject: m_ObjectHideFlags: 0 @@ -1848,12 +1916,13 @@ MonoBehaviour: m_GameObject: {fileID: 1348255963} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -1866,6 +1935,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &1348255966 CanvasRenderer: m_ObjectHideFlags: 0 @@ -1883,7 +1953,7 @@ MonoBehaviour: m_GameObject: {fileID: 1348255963} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -1200242548, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3} m_Name: m_EditorClassIdentifier: m_ShowMaskGraphic: 0 @@ -1934,7 +2004,7 @@ MonoBehaviour: m_GameObject: {fileID: 1386671668} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1741964061, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3} m_Name: m_EditorClassIdentifier: m_HorizontalFit: 0 @@ -1948,7 +2018,7 @@ MonoBehaviour: m_GameObject: {fileID: 1386671668} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1297475563, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3} m_Name: m_EditorClassIdentifier: m_Padding: @@ -1962,6 +2032,8 @@ MonoBehaviour: m_ChildForceExpandHeight: 0 m_ChildControlWidth: 1 m_ChildControlHeight: 0 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 --- !u!114 &1386671672 MonoBehaviour: m_ObjectHideFlags: 0 @@ -1971,7 +2043,7 @@ MonoBehaviour: m_GameObject: {fileID: 1386671668} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -1184210157, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 2fafe2cfe61f6974895a912c3755e8f1, type: 3} m_Name: m_EditorClassIdentifier: m_AllowSwitchOff: 1 @@ -2024,12 +2096,13 @@ MonoBehaviour: m_GameObject: {fileID: 1538073290} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0, g: 0, b: 0, a: 0.19607843} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -2042,6 +2115,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &1538073293 CanvasRenderer: m_ObjectHideFlags: 0 @@ -2059,7 +2133,7 @@ MonoBehaviour: m_GameObject: {fileID: 1538073290} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1367256648, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 1aa08ab6e0800fa44ae55d278d1423e3, type: 3} m_Name: m_EditorClassIdentifier: m_Content: {fileID: 1295888618} @@ -2126,12 +2200,13 @@ MonoBehaviour: m_GameObject: {fileID: 1582884914} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -2205,7 +2280,7 @@ MonoBehaviour: m_GameObject: {fileID: 1613178152} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: @@ -2219,17 +2294,20 @@ MonoBehaviour: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled m_Interactable: 1 m_TargetGraphic: {fileID: 1613178155} @@ -2256,12 +2334,13 @@ MonoBehaviour: m_GameObject: {fileID: 1613178152} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -2274,6 +2353,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &1613178156 CanvasRenderer: m_ObjectHideFlags: 0 @@ -2330,12 +2410,13 @@ MonoBehaviour: m_GameObject: {fileID: 1623056865} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -2348,6 +2429,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &1623056868 CanvasRenderer: m_ObjectHideFlags: 0 @@ -2365,7 +2447,7 @@ MonoBehaviour: m_GameObject: {fileID: 1623056865} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -1200242548, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3} m_Name: m_EditorClassIdentifier: m_ShowMaskGraphic: 0 @@ -2396,7 +2478,7 @@ MonoBehaviour: m_GameObject: {fileID: 1902615731} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1077351063, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} m_Name: m_EditorClassIdentifier: m_HorizontalAxis: Horizontal @@ -2415,7 +2497,7 @@ MonoBehaviour: m_GameObject: {fileID: 1902615731} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -619905303, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} m_Name: m_EditorClassIdentifier: m_FirstSelected: {fileID: 0} @@ -2483,7 +2565,7 @@ MonoBehaviour: m_GameObject: {fileID: 1986814488} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: @@ -2497,17 +2579,20 @@ MonoBehaviour: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled m_Interactable: 1 m_TargetGraphic: {fileID: 1986814491} @@ -2534,12 +2619,13 @@ MonoBehaviour: m_GameObject: {fileID: 1986814488} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -2552,6 +2638,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &1986814492 CanvasRenderer: m_ObjectHideFlags: 0 @@ -2589,7 +2676,7 @@ MonoBehaviour: m_GameObject: {fileID: 2050110693} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1301386320, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} m_Name: m_EditorClassIdentifier: m_IgnoreReversedGraphics: 1 @@ -2606,7 +2693,7 @@ MonoBehaviour: m_GameObject: {fileID: 2050110693} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1980459831, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} m_Name: m_EditorClassIdentifier: m_UiScaleMode: 0 @@ -2733,7 +2820,7 @@ MonoBehaviour: m_GameObject: {fileID: 2105646338} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} m_Name: m_EditorClassIdentifier: m_Navigation: @@ -2747,17 +2834,20 @@ MonoBehaviour: m_NormalColor: {r: 1, g: 1, b: 1, a: 1} m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} m_ColorMultiplier: 1 m_FadeDuration: 0.1 m_SpriteState: m_HighlightedSprite: {fileID: 0} m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} m_DisabledSprite: {fileID: 0} m_AnimationTriggers: m_NormalTrigger: Normal m_HighlightedTrigger: Highlighted m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted m_DisabledTrigger: Disabled m_Interactable: 1 m_TargetGraphic: {fileID: 2105646341} @@ -2784,12 +2874,13 @@ MonoBehaviour: m_GameObject: {fileID: 2105646338} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -2802,6 +2893,7 @@ MonoBehaviour: m_FillClockwise: 1 m_FillOrigin: 0 m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!222 &2105646342 CanvasRenderer: m_ObjectHideFlags: 0 @@ -2856,12 +2948,13 @@ MonoBehaviour: m_GameObject: {fileID: 2133592451} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_RaycastTarget: 1 + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scenes/Main.unity.meta b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scenes/Main.unity.meta index 15b2968..234a173 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scenes/Main.unity.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scenes/Main.unity.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: aa1f875396fa7d348a782cebc2f75d7c DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts.meta b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts.meta index 6e44c51..bc1a9c0 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts.meta @@ -3,6 +3,6 @@ guid: d418be768b6d32e4d9c3b8828929c3eb folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/CanvasController.cs b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/CanvasController.cs index db3c1e6..872892b 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/CanvasController.cs +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/CanvasController.cs @@ -11,12 +11,12 @@ namespace Mirror.Examples.MultipleMatch /// /// Match Controllers listen for this to terminate their match and clean up /// - public event Action OnPlayerDisconnected; + public event Action OnPlayerDisconnected; /// /// Cross-reference of client that created the corresponding match in openMatches below /// - internal static readonly Dictionary playerMatches = new Dictionary(); + internal static readonly Dictionary playerMatches = new Dictionary(); /// /// Open matches that are available for joining @@ -26,7 +26,7 @@ namespace Mirror.Examples.MultipleMatch /// /// Network Connections of all players in a match /// - internal static readonly Dictionary> matchConnections = new Dictionary>(); + internal static readonly Dictionary> matchConnections = new Dictionary>(); /// /// Player informations by Network Connection @@ -36,7 +36,7 @@ namespace Mirror.Examples.MultipleMatch /// /// Network Connections that have neither started nor joined a match yet /// - internal static readonly List waitingConnections = new List(); + internal static readonly List waitingConnections = new List(); /// /// GUID of a match the local player has created @@ -67,6 +67,17 @@ namespace Mirror.Examples.MultipleMatch public RoomGUI roomGUI; public ToggleGroup toggleGroup; + // RuntimeInitializeOnLoadMethod -> fast playmode without domain reload + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + static void ResetStatics() + { + playerMatches.Clear(); + openMatches.Clear(); + matchConnections.Clear(); + playerInfos.Clear(); + waitingConnections.Clear(); + } + #region UI Functions // Called from several places to ensure a clean reset @@ -203,7 +214,7 @@ namespace Mirror.Examples.MultipleMatch /// Sends updated match list to all waiting connections or just one if specified /// /// - internal void SendMatchList(NetworkConnection conn = null) + internal void SendMatchList(NetworkConnectionToClient conn = null) { if (!NetworkServer.active) return; @@ -213,7 +224,7 @@ namespace Mirror.Examples.MultipleMatch } else { - foreach (var waiter in waitingConnections) + foreach (NetworkConnectionToClient waiter in waitingConnections) { waiter.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.List, matchInfos = openMatches.Values.ToArray() }); } @@ -234,7 +245,7 @@ namespace Mirror.Examples.MultipleMatch NetworkServer.RegisterHandler(OnServerMatchMessage); } - internal void OnServerReady(NetworkConnection conn) + internal void OnServerReady(NetworkConnectionToClient conn) { if (!NetworkServer.active) return; @@ -245,7 +256,7 @@ namespace Mirror.Examples.MultipleMatch SendMatchList(); } - internal void OnServerDisconnect(NetworkConnection conn) + internal void OnServerDisconnect(NetworkConnectionToClient conn) { if (!NetworkServer.active) return; @@ -258,7 +269,7 @@ namespace Mirror.Examples.MultipleMatch playerMatches.Remove(conn); openMatches.Remove(matchId); - foreach (NetworkConnection playerConn in matchConnections[matchId]) + foreach (NetworkConnectionToClient playerConn in matchConnections[matchId]) { PlayerInfo _playerInfo = playerInfos[playerConn]; _playerInfo.ready = false; @@ -268,7 +279,7 @@ namespace Mirror.Examples.MultipleMatch } } - foreach (KeyValuePair> kvp in matchConnections) + foreach (KeyValuePair> kvp in matchConnections) { kvp.Value.Remove(conn); } @@ -283,12 +294,12 @@ namespace Mirror.Examples.MultipleMatch openMatches[playerInfo.matchId] = matchInfo; } - HashSet connections; + HashSet connections; if (matchConnections.TryGetValue(playerInfo.matchId, out connections)) { PlayerInfo[] infos = connections.Select(playerConn => playerInfos[playerConn]).ToArray(); - foreach (NetworkConnection playerConn in matchConnections[playerInfo.matchId]) + foreach (NetworkConnectionToClient playerConn in matchConnections[playerInfo.matchId]) { if (playerConn != conn) { @@ -306,9 +317,9 @@ namespace Mirror.Examples.MultipleMatch ResetCanvas(); } - internal void OnClientConnect(NetworkConnection conn) + internal void OnClientConnect() { - playerInfos.Add(conn, new PlayerInfo { playerIndex = this.playerIndex, ready = false }); + playerInfos.Add(NetworkClient.connection, new PlayerInfo { playerIndex = this.playerIndex, ready = false }); } internal void OnStartClient() @@ -338,7 +349,7 @@ namespace Mirror.Examples.MultipleMatch #region Server Match Message Handlers - void OnServerMatchMessage(NetworkConnection conn, ServerMatchMessage msg) + void OnServerMatchMessage(NetworkConnectionToClient conn, ServerMatchMessage msg) { if (!NetworkServer.active) return; @@ -382,7 +393,7 @@ namespace Mirror.Examples.MultipleMatch } } - void OnServerPlayerReady(NetworkConnection conn, Guid matchId) + void OnServerPlayerReady(NetworkConnectionToClient conn, Guid matchId) { if (!NetworkServer.active) return; @@ -390,16 +401,16 @@ namespace Mirror.Examples.MultipleMatch playerInfo.ready = !playerInfo.ready; playerInfos[conn] = playerInfo; - HashSet connections = matchConnections[matchId]; + HashSet connections = matchConnections[matchId]; PlayerInfo[] infos = connections.Select(playerConn => playerInfos[playerConn]).ToArray(); - foreach (NetworkConnection playerConn in matchConnections[matchId]) + foreach (NetworkConnectionToClient playerConn in matchConnections[matchId]) { playerConn.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.UpdateRoom, playerInfos = infos }); } } - void OnServerLeaveMatch(NetworkConnection conn, Guid matchId) + void OnServerLeaveMatch(NetworkConnectionToClient conn, Guid matchId) { if (!NetworkServer.active) return; @@ -412,15 +423,15 @@ namespace Mirror.Examples.MultipleMatch playerInfo.matchId = Guid.Empty; playerInfos[conn] = playerInfo; - foreach (KeyValuePair> kvp in matchConnections) + foreach (KeyValuePair> kvp in matchConnections) { kvp.Value.Remove(conn); } - HashSet connections = matchConnections[matchId]; + HashSet connections = matchConnections[matchId]; PlayerInfo[] infos = connections.Select(playerConn => playerInfos[playerConn]).ToArray(); - foreach (NetworkConnection playerConn in matchConnections[matchId]) + foreach (NetworkConnectionToClient playerConn in matchConnections[matchId]) { playerConn.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.UpdateRoom, playerInfos = infos }); } @@ -430,12 +441,12 @@ namespace Mirror.Examples.MultipleMatch conn.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.Departed }); } - void OnServerCreateMatch(NetworkConnection conn) + void OnServerCreateMatch(NetworkConnectionToClient conn) { if (!NetworkServer.active || playerMatches.ContainsKey(conn)) return; Guid newMatchId = Guid.NewGuid(); - matchConnections.Add(newMatchId, new HashSet()); + matchConnections.Add(newMatchId, new HashSet()); matchConnections[newMatchId].Add(conn); playerMatches.Add(conn, newMatchId); openMatches.Add(newMatchId, new MatchInfo { matchId = newMatchId, maxPlayers = 2, players = 1 }); @@ -452,7 +463,7 @@ namespace Mirror.Examples.MultipleMatch SendMatchList(); } - void OnServerCancelMatch(NetworkConnection conn) + void OnServerCancelMatch(NetworkConnectionToClient conn) { if (!NetworkServer.active || !playerMatches.ContainsKey(conn)) return; @@ -464,7 +475,7 @@ namespace Mirror.Examples.MultipleMatch playerMatches.Remove(conn); openMatches.Remove(matchId); - foreach (NetworkConnection playerConn in matchConnections[matchId]) + foreach (NetworkConnectionToClient playerConn in matchConnections[matchId]) { PlayerInfo playerInfo = playerInfos[playerConn]; playerInfo.ready = false; @@ -477,7 +488,7 @@ namespace Mirror.Examples.MultipleMatch } } - void OnServerStartMatch(NetworkConnection conn) + void OnServerStartMatch(NetworkConnectionToClient conn) { if (!NetworkServer.active || !playerMatches.ContainsKey(conn)) return; @@ -485,21 +496,17 @@ namespace Mirror.Examples.MultipleMatch if (playerMatches.TryGetValue(conn, out matchId)) { GameObject matchControllerObject = Instantiate(matchControllerPrefab); -#pragma warning disable 618 - matchControllerObject.GetComponent().matchId = matchId; -#pragma warning restore 618 + matchControllerObject.GetComponent().matchId = matchId; NetworkServer.Spawn(matchControllerObject); MatchController matchController = matchControllerObject.GetComponent(); - foreach (NetworkConnection playerConn in matchConnections[matchId]) + foreach (NetworkConnectionToClient playerConn in matchConnections[matchId]) { playerConn.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.Started }); GameObject player = Instantiate(NetworkManager.singleton.playerPrefab); -#pragma warning disable 618 - player.GetComponent().matchId = matchId; -#pragma warning restore 618 + player.GetComponent().matchId = matchId; NetworkServer.AddPlayerForConnection(playerConn, player); if (matchController.player1 == null) @@ -529,7 +536,7 @@ namespace Mirror.Examples.MultipleMatch } } - void OnServerJoinMatch(NetworkConnection conn, Guid matchId) + void OnServerJoinMatch(NetworkConnectionToClient conn, Guid matchId) { if (!NetworkServer.active || !matchConnections.ContainsKey(matchId) || !openMatches.ContainsKey(matchId)) return; @@ -548,7 +555,7 @@ namespace Mirror.Examples.MultipleMatch conn.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.Joined, matchId = matchId, playerInfos = infos }); - foreach (NetworkConnection playerConn in matchConnections[matchId]) + foreach (NetworkConnectionToClient playerConn in matchConnections[matchId]) { playerConn.Send(new ClientMatchMessage { clientMatchOperation = ClientMatchOperation.UpdateRoom, playerInfos = infos }); } diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/CanvasController.cs.meta b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/CanvasController.cs.meta index 99b4f9a..9379f5c 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/CanvasController.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/CanvasController.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/CellGUI.cs.meta b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/CellGUI.cs.meta index 6a7568a..2ab1003 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/CellGUI.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/CellGUI.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchController.cs b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchController.cs index 95bf470..6b99ceb 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchController.cs +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchController.cs @@ -5,9 +5,7 @@ using UnityEngine.UI; namespace Mirror.Examples.MultipleMatch { -#pragma warning disable 618 - [RequireComponent(typeof(NetworkMatchChecker))] -#pragma warning restore 618 + [RequireComponent(typeof(NetworkMatch))] public class MatchController : NetworkBehaviour { internal readonly SyncDictionary matchPlayerData = new SyncDictionary(); @@ -255,7 +253,7 @@ namespace Mirror.Examples.MultipleMatch StartCoroutine(ServerEndMatch(sender, false)); } - public void OnPlayerDisconnected(NetworkConnection conn) + public void OnPlayerDisconnected(NetworkConnectionToClient conn) { // Check that the disconnecting client is a player in this match if (player1 == conn.identity || player2 == conn.identity) @@ -264,7 +262,7 @@ namespace Mirror.Examples.MultipleMatch } } - public IEnumerator ServerEndMatch(NetworkConnection conn, bool disconnected) + public IEnumerator ServerEndMatch(NetworkConnectionToClient conn, bool disconnected) { canvasController.OnPlayerDisconnected -= OnPlayerDisconnected; diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchController.cs.meta b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchController.cs.meta index 9383900..d6b6a8b 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchController.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchController.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchGUI.cs b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchGUI.cs index 125754b..ff60f29 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchGUI.cs +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchGUI.cs @@ -37,8 +37,8 @@ namespace Mirror.Examples.MultipleMatch public void SetMatchInfo(MatchInfo infos) { matchId = infos.matchId; - matchName.text = "Match " + infos.matchId.ToString().Substring(0, 8); - playerCount.text = infos.players + " / " + infos.maxPlayers; + matchName.text = $"Match {infos.matchId.ToString().Substring(0, 8)}"; + playerCount.text = $"{infos.players} / {infos.maxPlayers}"; } } } diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchGUI.cs.meta b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchGUI.cs.meta index eb3f301..8a036f1 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchGUI.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchGUI.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchMessages.cs.meta b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchMessages.cs.meta index cdc9baa..bf41030 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchMessages.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchMessages.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchNetworkManager.cs b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchNetworkManager.cs index 59bd579..71f7be2 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchNetworkManager.cs +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchNetworkManager.cs @@ -35,7 +35,7 @@ namespace Mirror.Examples.MultipleMatch /// The default implementation of this function calls NetworkServer.SetClientReady() to continue the network setup process. /// /// Connection from client. - public override void OnServerReady(NetworkConnection conn) + public override void OnServerReady(NetworkConnectionToClient conn) { base.OnServerReady(conn); canvasController.OnServerReady(conn); @@ -46,7 +46,7 @@ namespace Mirror.Examples.MultipleMatch /// This is called on the Server when a Client disconnects from the Server. Use an override to decide what should happen when a disconnection is detected. /// /// Connection from client. - public override void OnServerDisconnect(NetworkConnection conn) + public override void OnServerDisconnect(NetworkConnectionToClient conn) { canvasController.OnServerDisconnect(conn); base.OnServerDisconnect(conn); @@ -60,22 +60,20 @@ namespace Mirror.Examples.MultipleMatch /// Called on the client when connected to a server. /// The default implementation of this function sets the client as ready and adds a player. Override the function to dictate what happens when the client connects. /// - /// Connection to the server. - public override void OnClientConnect(NetworkConnection conn) + public override void OnClientConnect() { - base.OnClientConnect(conn); - canvasController.OnClientConnect(conn); + base.OnClientConnect(); + canvasController.OnClientConnect(); } /// /// Called on clients when disconnected from a server. /// This is called on the client when it disconnects from the server. Override this function to decide what happens when the client disconnects. /// - /// Connection to the server. - public override void OnClientDisconnect(NetworkConnection conn) + public override void OnClientDisconnect() { canvasController.OnClientDisconnect(); - base.OnClientDisconnect(conn); + base.OnClientDisconnect(); } #endregion @@ -88,7 +86,7 @@ namespace Mirror.Examples.MultipleMatch /// public override void OnStartServer() { - if (mode == NetworkManagerMode.ServerOnly) + if (mode == NetworkManagerMode.ServerOnly) canvas.SetActive(true); canvasController.OnStartServer(); @@ -122,4 +120,4 @@ namespace Mirror.Examples.MultipleMatch #endregion } -} \ No newline at end of file +} diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchNetworkManager.cs.meta b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchNetworkManager.cs.meta index e73e281..95f851c 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchNetworkManager.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/MatchNetworkManager.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/PlayerGUI.cs b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/PlayerGUI.cs index 44215fb..57d1ad2 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/PlayerGUI.cs +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/PlayerGUI.cs @@ -9,7 +9,7 @@ namespace Mirror.Examples.MultipleMatch public void SetPlayerInfo(PlayerInfo info) { - playerName.text = "Player " + info.playerIndex; + playerName.text = $"Player {info.playerIndex}"; playerName.color = info.ready ? Color.green : Color.red; } } diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/PlayerGUI.cs.meta b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/PlayerGUI.cs.meta index 42425cd..7da4f20 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/PlayerGUI.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/PlayerGUI.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/RoomGUI.cs.meta b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/RoomGUI.cs.meta index 082026b..0e970ab 100644 --- a/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/RoomGUI.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/MultipleMatches/Scripts/RoomGUI.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Pong.meta b/UnityProject/Assets/Mirror/Examples/Pong.meta index fea47b9..ac8a6e2 100644 --- a/UnityProject/Assets/Mirror/Examples/Pong.meta +++ b/UnityProject/Assets/Mirror/Examples/Pong.meta @@ -3,6 +3,6 @@ guid: 2220cb06641bd40be8c8330b3b4dd0ee folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Pong/PhysicsMaterials.meta b/UnityProject/Assets/Mirror/Examples/Pong/PhysicsMaterials.meta index 31e934c..3ba9985 100644 --- a/UnityProject/Assets/Mirror/Examples/Pong/PhysicsMaterials.meta +++ b/UnityProject/Assets/Mirror/Examples/Pong/PhysicsMaterials.meta @@ -3,6 +3,6 @@ guid: 7f122364b93b24938b3f50539de4d746 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Pong/Prefabs.meta b/UnityProject/Assets/Mirror/Examples/Pong/Prefabs.meta index 34f632a..7e90f0d 100644 --- a/UnityProject/Assets/Mirror/Examples/Pong/Prefabs.meta +++ b/UnityProject/Assets/Mirror/Examples/Pong/Prefabs.meta @@ -3,6 +3,6 @@ guid: db1c7e1499ee84bfba33fa7f2cb1bc57 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Pong/Prefabs/Ball.prefab b/UnityProject/Assets/Mirror/Examples/Pong/Prefabs/Ball.prefab index f0fe6d8..a889e87 100644 --- a/UnityProject/Assets/Mirror/Examples/Pong/Prefabs/Ball.prefab +++ b/UnityProject/Assets/Mirror/Examples/Pong/Prefabs/Ball.prefab @@ -128,7 +128,7 @@ Rigidbody2D: m_LinearDrag: 0 m_AngularDrag: 0.05 m_GravityScale: 0 - m_Material: {fileID: 0} + m_Material: {fileID: 6200000, guid: 97a3e4cddb8635c4eba1265f44d106bf, type: 2} m_Interpolate: 0 m_SleepingMode: 1 m_CollisionDetection: 0 @@ -148,7 +148,7 @@ MonoBehaviour: sceneId: 0 serverOnly: 0 visible: 0 - m_AssetId: + m_AssetId: 5f7a7f34494ed40268eff49dbf9168bf hasSpawned: 0 --- !u!114 &114692463781779748 MonoBehaviour: @@ -181,9 +181,17 @@ MonoBehaviour: syncMode: 0 syncInterval: 0 clientAuthority: 0 - localPositionSensitivity: 0.01 - localRotationSensitivity: 0.01 - localScaleSensitivity: 0.01 - compressRotation: 1 - interpolateScale: 0 + sendInterval: 0.05 + syncPosition: 1 + syncRotation: 0 syncScale: 0 + interpolatePosition: 1 + interpolateRotation: 0 + interpolateScale: 0 + bufferTimeMultiplier: 1 + bufferSizeLimit: 64 + catchupThreshold: 4 + catchupMultiplier: 0.1 + showGizmos: 0 + showOverlay: 0 + overlayColor: {r: 0, g: 0, b: 0, a: 0.5} diff --git a/UnityProject/Assets/Mirror/Examples/Pong/Prefabs/Ball.prefab.meta b/UnityProject/Assets/Mirror/Examples/Pong/Prefabs/Ball.prefab.meta index be6d784..6322417 100644 --- a/UnityProject/Assets/Mirror/Examples/Pong/Prefabs/Ball.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/Pong/Prefabs/Ball.prefab.meta @@ -3,6 +3,6 @@ guid: 5f7a7f34494ed40268eff49dbf9168bf NativeFormatImporter: externalObjects: {} mainObjectFileID: 100100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Pong/Prefabs/Racket.prefab b/UnityProject/Assets/Mirror/Examples/Pong/Prefabs/Racket.prefab index a0401b5..14e0cc0 100644 --- a/UnityProject/Assets/Mirror/Examples/Pong/Prefabs/Racket.prefab +++ b/UnityProject/Assets/Mirror/Examples/Pong/Prefabs/Racket.prefab @@ -148,7 +148,7 @@ MonoBehaviour: sceneId: 0 serverOnly: 0 visible: 0 - m_AssetId: + m_AssetId: b1651eaf8c7564a1c86031dfbb8a7b28 hasSpawned: 0 --- !u!114 &114626868563338794 MonoBehaviour: @@ -181,9 +181,17 @@ MonoBehaviour: syncMode: 0 syncInterval: 0 clientAuthority: 1 - localPositionSensitivity: 0.01 - localRotationSensitivity: 0.01 - localScaleSensitivity: 0.01 - compressRotation: 1 - interpolateScale: 0 + sendInterval: 0.05 + syncPosition: 1 + syncRotation: 0 syncScale: 0 + interpolatePosition: 1 + interpolateRotation: 0 + interpolateScale: 0 + bufferTimeMultiplier: 1 + bufferSizeLimit: 64 + catchupThreshold: 4 + catchupMultiplier: 0.1 + showGizmos: 0 + showOverlay: 0 + overlayColor: {r: 0, g: 0, b: 0, a: 0.5} diff --git a/UnityProject/Assets/Mirror/Examples/Pong/Prefabs/Racket.prefab.meta b/UnityProject/Assets/Mirror/Examples/Pong/Prefabs/Racket.prefab.meta index 87eb142..c67ac8f 100644 --- a/UnityProject/Assets/Mirror/Examples/Pong/Prefabs/Racket.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/Pong/Prefabs/Racket.prefab.meta @@ -3,6 +3,6 @@ guid: b1651eaf8c7564a1c86031dfbb8a7b28 NativeFormatImporter: externalObjects: {} mainObjectFileID: 100100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Pong/Scenes.meta b/UnityProject/Assets/Mirror/Examples/Pong/Scenes.meta index 5a8969b..ba5b4d4 100644 --- a/UnityProject/Assets/Mirror/Examples/Pong/Scenes.meta +++ b/UnityProject/Assets/Mirror/Examples/Pong/Scenes.meta @@ -3,6 +3,6 @@ guid: 00c56c45628954f44aa4d944cbd9bfca folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Pong/Scenes/Scene.unity.meta b/UnityProject/Assets/Mirror/Examples/Pong/Scenes/Scene.unity.meta index 9418efe..f4267c6 100644 --- a/UnityProject/Assets/Mirror/Examples/Pong/Scenes/Scene.unity.meta +++ b/UnityProject/Assets/Mirror/Examples/Pong/Scenes/Scene.unity.meta @@ -1,8 +1,8 @@ fileFormatVersion: 2 guid: 89a84548859e3b643b4fb27984dc2b0d timeCreated: 1426587410 -licenseType: Free +licenseType: Store DefaultImporter: - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Pong/Scripts.meta b/UnityProject/Assets/Mirror/Examples/Pong/Scripts.meta index 9387865..b482c47 100644 --- a/UnityProject/Assets/Mirror/Examples/Pong/Scripts.meta +++ b/UnityProject/Assets/Mirror/Examples/Pong/Scripts.meta @@ -3,6 +3,6 @@ guid: 55e7bd9b7212f4318909cbca3bdb1284 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Pong/Scripts/Ball.cs.meta b/UnityProject/Assets/Mirror/Examples/Pong/Scripts/Ball.cs.meta index e433070..14a37a0 100644 --- a/UnityProject/Assets/Mirror/Examples/Pong/Scripts/Ball.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Pong/Scripts/Ball.cs.meta @@ -1,12 +1,12 @@ fileFormatVersion: 2 guid: 38b5c2f743cd8034a8beeebf277c92c1 timeCreated: 1426602353 -licenseType: Free +licenseType: Store MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Pong/Scripts/NetworkManagerPong.cs b/UnityProject/Assets/Mirror/Examples/Pong/Scripts/NetworkManagerPong.cs index f3e300a..667e372 100644 --- a/UnityProject/Assets/Mirror/Examples/Pong/Scripts/NetworkManagerPong.cs +++ b/UnityProject/Assets/Mirror/Examples/Pong/Scripts/NetworkManagerPong.cs @@ -1,5 +1,10 @@ using UnityEngine; +/* + Documentation: https://mirror-networking.gitbook.io/docs/components/network-manager + API Reference: https://mirror-networking.com/docs/api/Mirror.NetworkManager.html +*/ + namespace Mirror.Examples.Pong { // Custom NetworkManager that simply assigns the correct racket positions when @@ -12,7 +17,7 @@ namespace Mirror.Examples.Pong public Transform rightRacketSpawn; GameObject ball; - public override void OnServerAddPlayer(NetworkConnection conn) + public override void OnServerAddPlayer(NetworkConnectionToClient conn) { // add player at correct spawn position Transform start = numPlayers == 0 ? leftRacketSpawn : rightRacketSpawn; @@ -27,7 +32,7 @@ namespace Mirror.Examples.Pong } } - public override void OnServerDisconnect(NetworkConnection conn) + public override void OnServerDisconnect(NetworkConnectionToClient conn) { // destroy ball if (ball != null) diff --git a/UnityProject/Assets/Mirror/Examples/Pong/Scripts/NetworkManagerPong.cs.meta b/UnityProject/Assets/Mirror/Examples/Pong/Scripts/NetworkManagerPong.cs.meta index 0d4cbff..4b191ea 100644 --- a/UnityProject/Assets/Mirror/Examples/Pong/Scripts/NetworkManagerPong.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Pong/Scripts/NetworkManagerPong.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Pong/Scripts/Player.cs.meta b/UnityProject/Assets/Mirror/Examples/Pong/Scripts/Player.cs.meta index f1a1550..f0de41d 100644 --- a/UnityProject/Assets/Mirror/Examples/Pong/Scripts/Player.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Pong/Scripts/Player.cs.meta @@ -1,12 +1,12 @@ fileFormatVersion: 2 guid: 0748c7eda22b19845b9ce0e4d23d1021 timeCreated: 1426597826 -licenseType: Free +licenseType: Store MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Pong/Sprites.meta b/UnityProject/Assets/Mirror/Examples/Pong/Sprites.meta index e7b1178..42350a3 100644 --- a/UnityProject/Assets/Mirror/Examples/Pong/Sprites.meta +++ b/UnityProject/Assets/Mirror/Examples/Pong/Sprites.meta @@ -3,6 +3,6 @@ guid: 9d39b68545c2e43cfa4a37f6703a983b folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Pong/Sprites/Ball.png.meta b/UnityProject/Assets/Mirror/Examples/Pong/Sprites/Ball.png.meta index 3d91cf7..040bba3 100644 --- a/UnityProject/Assets/Mirror/Examples/Pong/Sprites/Ball.png.meta +++ b/UnityProject/Assets/Mirror/Examples/Pong/Sprites/Ball.png.meta @@ -77,12 +77,12 @@ TextureImporter: bones: [] spriteID: c5a291323e0d5f34883a55625f66ca70 vertices: [] - indices: '' + indices: edges: [] weights: [] - spritePackingTag: '' + spritePackingTag: pSDRemoveMatte: 0 pSDShowRemoveMatteOption: 0 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Pong/Sprites/DottedLine.png.meta b/UnityProject/Assets/Mirror/Examples/Pong/Sprites/DottedLine.png.meta index 48e8eef..993c831 100644 --- a/UnityProject/Assets/Mirror/Examples/Pong/Sprites/DottedLine.png.meta +++ b/UnityProject/Assets/Mirror/Examples/Pong/Sprites/DottedLine.png.meta @@ -77,12 +77,12 @@ TextureImporter: bones: [] spriteID: 98b4e2aa86aa3d843821adfe71dbbac0 vertices: [] - indices: '' + indices: edges: [] weights: [] - spritePackingTag: '' + spritePackingTag: pSDRemoveMatte: 0 pSDShowRemoveMatteOption: 0 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Pong/Sprites/WallHorizontal.png.meta b/UnityProject/Assets/Mirror/Examples/Pong/Sprites/WallHorizontal.png.meta index fc57b86..10afcfc 100644 --- a/UnityProject/Assets/Mirror/Examples/Pong/Sprites/WallHorizontal.png.meta +++ b/UnityProject/Assets/Mirror/Examples/Pong/Sprites/WallHorizontal.png.meta @@ -77,12 +77,12 @@ TextureImporter: bones: [] spriteID: 74c5541eed52f67428025c83260d8bec vertices: [] - indices: '' + indices: edges: [] weights: [] - spritePackingTag: '' + spritePackingTag: pSDRemoveMatte: 0 pSDShowRemoveMatteOption: 0 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Pong/Sprites/WallVertical.png.meta b/UnityProject/Assets/Mirror/Examples/Pong/Sprites/WallVertical.png.meta index fa3f7e0..9b191ee 100644 --- a/UnityProject/Assets/Mirror/Examples/Pong/Sprites/WallVertical.png.meta +++ b/UnityProject/Assets/Mirror/Examples/Pong/Sprites/WallVertical.png.meta @@ -77,12 +77,12 @@ TextureImporter: bones: [] spriteID: 3a92f998f14389948aa928ac64e8e426 vertices: [] - indices: '' + indices: edges: [] weights: [] - spritePackingTag: '' + spritePackingTag: pSDRemoveMatte: 0 pSDShowRemoveMatteOption: 0 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics.meta b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics.meta index 2cf49d2..854a7cd 100644 --- a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics.meta +++ b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics.meta @@ -3,6 +3,6 @@ guid: 703869fabc2e0e241962909920029b0b folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials.meta b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials.meta index e63d9d6..48b3a17 100644 --- a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials.meta +++ b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials.meta @@ -3,6 +3,6 @@ guid: d871cab4e65c6e542a384f695eac5c75 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Blue.mat.meta b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Blue.mat.meta index 34991da..2b1c4f6 100644 --- a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Blue.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Blue.mat.meta @@ -3,6 +3,6 @@ guid: 84335f003ad8f42408d13787e76d4f94 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Floor.mat b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Floor.mat new file mode 100644 index 0000000..990f778 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Floor.mat @@ -0,0 +1,77 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Floor + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.192 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 0.99504846, b: 0.5518868, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Floor.mat.meta b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Floor.mat.meta new file mode 100644 index 0000000..09dd281 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Floor.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2082151ae101a9445990e8a0fe9142da +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Green.mat b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Green.mat index bccc96b..dd7a2b8 100644 --- a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Green.mat +++ b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Green.mat @@ -61,7 +61,7 @@ Material: - _DetailNormalMapScale: 1 - _DstBlend: 0 - _GlossMapScale: 1 - - _Glossiness: 0.192 + - _Glossiness: 0.816 - _GlossyReflections: 1 - _Metallic: 0 - _Mode: 0 diff --git a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Green.mat.meta b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Green.mat.meta index 2764f01..f9ebf7b 100644 --- a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Green.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Green.mat.meta @@ -3,6 +3,6 @@ guid: bbe2c7293af197640a2b40018e79b9a6 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Red.mat.meta b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Red.mat.meta index f30b839..f66bd07 100644 --- a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Red.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Materials/Red.mat.meta @@ -3,6 +3,6 @@ guid: 6ec777c59ba249a4aa16956e9dd9a0ee NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/PhysicMaterials.meta b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/PhysicMaterials.meta index 8824c45..8564ffe 100644 --- a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/PhysicMaterials.meta +++ b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/PhysicMaterials.meta @@ -3,6 +3,6 @@ guid: e271b04d72e58af47ad61009045f6779 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/PhysicMaterials/Ball.physicMaterial.meta b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/PhysicMaterials/Ball.physicMaterial.meta index 2efdbad..4526222 100644 --- a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/PhysicMaterials/Ball.physicMaterial.meta +++ b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/PhysicMaterials/Ball.physicMaterial.meta @@ -3,6 +3,6 @@ guid: f2bb9a8ecc30457468aea7dacef6f5de NativeFormatImporter: externalObjects: {} mainObjectFileID: 13400000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/PhysicMaterials/Floor.physicMaterial.meta b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/PhysicMaterials/Floor.physicMaterial.meta index 6fd1cc0..aef94c6 100644 --- a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/PhysicMaterials/Floor.physicMaterial.meta +++ b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/PhysicMaterials/Floor.physicMaterial.meta @@ -3,6 +3,6 @@ guid: df195ce493ed09b4c929f832263ba617 NativeFormatImporter: externalObjects: {} mainObjectFileID: 13400000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Prefabs.meta b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Prefabs.meta index 34067f9..b257679 100644 --- a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Prefabs.meta +++ b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Prefabs.meta @@ -3,6 +3,6 @@ guid: dd49d852d6231e6418c72870c7491fd7 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Prefabs/Empty Player.prefab.meta b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Prefabs/Empty Player.prefab.meta index ad28c9a..28b4b2b 100644 --- a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Prefabs/Empty Player.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Prefabs/Empty Player.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 4ab0a427bdc13244499c6e044ad7eb40 PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scenes.meta b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scenes.meta index 73e4cd4..276beb5 100644 --- a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scenes.meta +++ b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scenes.meta @@ -3,6 +3,6 @@ guid: c11c778c869824342b9edc9a95de5a19 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scenes/BounceScene.unity b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scenes/BounceScene.unity index bc10d1e..54f821c 100644 --- a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scenes/BounceScene.unity +++ b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scenes/BounceScene.unity @@ -172,7 +172,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: bbe2c7293af197640a2b40018e79b9a6, type: 2} + - {fileID: 2100000, guid: 2082151ae101a9445990e8a0fe9142da, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -247,12 +247,15 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Port: 7777 + DualMode: 1 NoDelay: 1 Interval: 10 + Timeout: 10000 FastResend: 2 CongestionWindow: 0 SendWindowSize: 4096 ReceiveWindowSize: 4096 + NonAlloc: 1 debugLog: 0 statisticsGUI: 0 statisticsLog: 0 @@ -273,15 +276,11 @@ MonoBehaviour: runInBackground: 1 autoStartServerBuild: 1 serverTickRate: 30 - serverBatching: 0 - serverBatchInterval: 0 offlineScene: onlineScene: transport: {fileID: 492096634} networkAddress: localhost maxConnections: 100 - disconnectInactiveConnections: 0 - disconnectInactiveTimeout: 60 authenticator: {fileID: 0} playerPrefab: {fileID: 844717362685181648, guid: 4ab0a427bdc13244499c6e044ad7eb40, type: 3} @@ -300,7 +299,7 @@ Transform: m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 5 + m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &492096637 MonoBehaviour: @@ -314,7 +313,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 6442dc8070ceb41f094e44de0bf87274, type: 3} m_Name: m_EditorClassIdentifier: - showGUI: 1 offsetX: 0 offsetY: 0 --- !u!1 &746548597 @@ -329,7 +327,7 @@ GameObject: - component: {fileID: 746548600} - component: {fileID: 746548599} m_Layer: 5 - m_Name: Image (2) + m_Name: Image m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -429,7 +427,7 @@ MonoBehaviour: m_EditorClassIdentifier: syncMode: 0 syncInterval: 0.1 - rigidbody3d: {fileID: 0} + rigidbody3d: {fileID: 1019217379} force: 500 --- !u!114 &1019217377 MonoBehaviour: @@ -444,14 +442,22 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: syncMode: 0 - syncInterval: 0 + syncInterval: 0.1 clientAuthority: 0 - localPositionSensitivity: 0.01 - localRotationSensitivity: 0.01 - localScaleSensitivity: 0.01 - compressRotation: 1 - interpolateScale: 0 + sendInterval: 0.05 + syncPosition: 1 + syncRotation: 1 syncScale: 0 + interpolatePosition: 1 + interpolateRotation: 1 + interpolateScale: 0 + bufferTimeMultiplier: 1 + bufferSizeLimit: 64 + catchupThreshold: 4 + catchupMultiplier: 0.1 + showGizmos: 0 + showOverlay: 0 + overlayColor: {r: 0, g: 0, b: 0, a: 0.5} --- !u!114 &1019217378 MonoBehaviour: m_ObjectHideFlags: 0 @@ -483,7 +489,7 @@ Rigidbody: m_UseGravity: 1 m_IsKinematic: 1 m_Interpolate: 0 - m_Constraints: 0 + m_Constraints: 122 m_CollisionDetection: 0 --- !u!135 &1019217380 SphereCollider: @@ -553,7 +559,7 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1019217375} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: -1.5, y: 0.5, z: 0} + m_LocalPosition: {x: -3, y: 0.5, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 0} @@ -689,7 +695,7 @@ MonoBehaviour: m_EditorClassIdentifier: syncMode: 0 syncInterval: 0.1 - rigidbody3d: {fileID: 0} + rigidbody3d: {fileID: 1200292995} force: 500 --- !u!114 &1200292992 MonoBehaviour: @@ -704,7 +710,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: syncMode: 0 - syncInterval: 0 + syncInterval: 0.1 target: {fileID: 1200292995} clientAuthority: 0 syncVelocity: 1 @@ -726,14 +732,22 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: syncMode: 0 - syncInterval: 0 + syncInterval: 0.1 clientAuthority: 0 - localPositionSensitivity: 0.01 - localRotationSensitivity: 0.01 - localScaleSensitivity: 0.01 - compressRotation: 1 - interpolateScale: 0 + sendInterval: 0.05 + syncPosition: 1 + syncRotation: 1 syncScale: 0 + interpolatePosition: 1 + interpolateRotation: 1 + interpolateScale: 0 + bufferTimeMultiplier: 1 + bufferSizeLimit: 64 + catchupThreshold: 4 + catchupMultiplier: 0.1 + showGizmos: 0 + showOverlay: 0 + overlayColor: {r: 0, g: 0, b: 0, a: 0.5} --- !u!114 &1200292994 MonoBehaviour: m_ObjectHideFlags: 0 @@ -765,7 +779,7 @@ Rigidbody: m_UseGravity: 1 m_IsKinematic: 1 m_Interpolate: 0 - m_Constraints: 0 + m_Constraints: 122 m_CollisionDetection: 0 --- !u!135 &1200292996 SphereCollider: @@ -835,7 +849,7 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1200292990} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 1.5, y: 0.5, z: 0} + m_LocalPosition: {x: 0, y: 0.5, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 0} @@ -853,7 +867,7 @@ GameObject: - component: {fileID: 1282619066} - component: {fileID: 1282619065} m_Layer: 5 - m_Name: Image (1) + m_Name: Image m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -928,7 +942,7 @@ GameObject: - component: {fileID: 1592570047} - component: {fileID: 1592570046} m_Layer: 5 - m_Name: Text (2) + m_Name: Text m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -973,7 +987,7 @@ MonoBehaviour: m_PersistentCalls: m_Calls: [] m_FontData: - m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 3} + m_Font: {fileID: 12800000, guid: e3265ab4bf004d28a9537516768c1c75, type: 3} m_FontSize: 24 m_FontStyle: 0 m_BestFit: 0 @@ -1051,7 +1065,7 @@ MonoBehaviour: m_PersistentCalls: m_Calls: [] m_FontData: - m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 3} + m_Font: {fileID: 12800000, guid: e3265ab4bf004d28a9537516768c1c75, type: 3} m_FontSize: 24 m_FontStyle: 0 m_BestFit: 0 @@ -1240,13 +1254,177 @@ RectTransform: - {fileID: 1282619064} - {fileID: 746548598} m_Father: {fileID: 0} - m_RootOrder: 6 + m_RootOrder: 7 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0, y: 0} +--- !u!1 &1945586487 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1945586496} + - component: {fileID: 1945586495} + - component: {fileID: 1945586494} + - component: {fileID: 1945586493} + - component: {fileID: 1945586492} + - component: {fileID: 1945586491} + - component: {fileID: 1945586489} + - component: {fileID: 1945586488} + m_Layer: 0 + m_Name: Ball Green + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1945586488 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1945586487} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c1b468aa0f3779f4b950b7651891cb8c, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 + rigidbody3d: {fileID: 1945586492} + force: 500 +--- !u!114 &1945586489 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1945586487} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7f032128052c95a46afb0ddd97d994cc, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 + target: {fileID: 1945586492} + lerpVelocityAmount: 0.5 + lerpPositionAmount: 0.5 + clientAuthority: 0 +--- !u!114 &1945586491 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1945586487} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3} + m_Name: + m_EditorClassIdentifier: + sceneId: 4068402964 + serverOnly: 0 + visible: 0 + m_AssetId: + hasSpawned: 0 +--- !u!54 &1945586492 +Rigidbody: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1945586487} + serializedVersion: 2 + m_Mass: 1 + m_Drag: 0 + m_AngularDrag: 0.05 + m_UseGravity: 1 + m_IsKinematic: 1 + m_Interpolate: 0 + m_Constraints: 122 + m_CollisionDetection: 0 +--- !u!135 &1945586493 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1945586487} + m_Material: {fileID: 13400000, guid: f2bb9a8ecc30457468aea7dacef6f5de, type: 2} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1945586494 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1945586487} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: bbe2c7293af197640a2b40018e79b9a6, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 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!33 &1945586495 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1945586487} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1945586496 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1945586487} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 3, y: 0.5, z: 0} + 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 &2029351939 GameObject: m_ObjectHideFlags: 0 @@ -1259,7 +1437,7 @@ GameObject: - component: {fileID: 2029351942} - component: {fileID: 2029351941} m_Layer: 5 - m_Name: Text (1) + m_Name: Text m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -1304,7 +1482,7 @@ MonoBehaviour: m_PersistentCalls: m_Calls: [] m_FontData: - m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 3} + m_Font: {fileID: 12800000, guid: e3265ab4bf004d28a9537516768c1c75, type: 3} m_FontSize: 24 m_FontStyle: 0 m_BestFit: 0 diff --git a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scenes/BounceScene.unity.meta b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scenes/BounceScene.unity.meta index 9a6b2b0..2e53449 100644 --- a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scenes/BounceScene.unity.meta +++ b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scenes/BounceScene.unity.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: b42449e4a64f03a42b999b0c118df11b DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scripts.meta b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scripts.meta index 7f88d55..f7469ea 100644 --- a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scripts.meta +++ b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scripts.meta @@ -3,6 +3,6 @@ guid: 8a0b4c0f86764874481e949b6190940e folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scripts/AddForce.cs b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scripts/AddForce.cs index 36030be..2242bb2 100644 --- a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scripts/AddForce.cs +++ b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scripts/AddForce.cs @@ -2,22 +2,28 @@ using UnityEngine; namespace Mirror.Examples.RigidbodyPhysics { + [RequireComponent(typeof(Rigidbody))] public class AddForce : NetworkBehaviour { public Rigidbody rigidbody3d; public float force = 500f; - void Start() + void OnValidate() { - rigidbody3d.isKinematic = !isServer; + rigidbody3d = GetComponent(); + rigidbody3d.isKinematic = true; } + public override void OnStartServer() + { + rigidbody3d.isKinematic = false; + } + + [ServerCallback] void Update() { - if (isServer && Input.GetKeyDown(KeyCode.Space)) - { + if (Input.GetKeyDown(KeyCode.Space)) rigidbody3d.AddForce(Vector3.up * force); - } } } } diff --git a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scripts/AddForce.cs.meta b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scripts/AddForce.cs.meta index cebd3c6..b985a20 100644 --- a/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scripts/AddForce.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/RigidbodyPhysics/Scripts/AddForce.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room.meta b/UnityProject/Assets/Mirror/Examples/Room.meta index a4ba161..51c8ec2 100644 --- a/UnityProject/Assets/Mirror/Examples/Room.meta +++ b/UnityProject/Assets/Mirror/Examples/Room.meta @@ -3,6 +3,6 @@ guid: ba0822b68f209a743bc575c6f2cc78f0 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Materials.meta b/UnityProject/Assets/Mirror/Examples/Room/Materials.meta index b604b3f..1593571 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Materials.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Materials.meta @@ -3,6 +3,6 @@ guid: 177a490b498134246b6eeddfeb608b94 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Materials/PlayArea.mat.meta b/UnityProject/Assets/Mirror/Examples/Room/Materials/PlayArea.mat.meta index 98ae304..093bbc4 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Materials/PlayArea.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Materials/PlayArea.mat.meta @@ -3,6 +3,6 @@ guid: 3201636fa507dad448e9a36d66a80825 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Materials/Player.mat.meta b/UnityProject/Assets/Mirror/Examples/Room/Materials/Player.mat.meta index d710b35..46963af 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Materials/Player.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Materials/Player.mat.meta @@ -3,6 +3,6 @@ guid: ac64a68d9ea8fa9459ff2f158065c1d0 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Materials/Prize.mat.meta b/UnityProject/Assets/Mirror/Examples/Room/Materials/Prize.mat.meta index f2e19fc..18935a5 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Materials/Prize.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Materials/Prize.mat.meta @@ -3,6 +3,6 @@ guid: a1d7c9f39b41d414d86e64f7761cd545 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Materials/Textures.meta b/UnityProject/Assets/Mirror/Examples/Room/Materials/Textures.meta index 480567c..0ab7902 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Materials/Textures.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Materials/Textures.meta @@ -3,6 +3,6 @@ guid: 047a5014adf04914f9ffded62a715e39 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Materials/Textures/Wall01.tga.meta b/UnityProject/Assets/Mirror/Examples/Room/Materials/Textures/Wall01.tga.meta index e4b2df2..e48bd95 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Materials/Textures/Wall01.tga.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Materials/Textures/Wall01.tga.meta @@ -75,14 +75,14 @@ TextureImporter: outline: [] physicsShape: [] bones: [] - spriteID: '' + spriteID: vertices: [] - indices: '' + indices: edges: [] weights: [] - spritePackingTag: '' + spritePackingTag: pSDRemoveMatte: 0 pSDShowRemoveMatteOption: 0 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Materials/Textures/Wall01_n.tga.meta b/UnityProject/Assets/Mirror/Examples/Room/Materials/Textures/Wall01_n.tga.meta index 86f5d88..1e69eda 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Materials/Textures/Wall01_n.tga.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Materials/Textures/Wall01_n.tga.meta @@ -75,14 +75,14 @@ TextureImporter: outline: [] physicsShape: [] bones: [] - spriteID: '' + spriteID: vertices: [] - indices: '' + indices: edges: [] weights: [] - spritePackingTag: '' + spritePackingTag: pSDRemoveMatte: 0 pSDShowRemoveMatteOption: 0 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Prefabs.meta b/UnityProject/Assets/Mirror/Examples/Room/Prefabs.meta index f6046b7..78f4e75 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Prefabs.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Prefabs.meta @@ -3,6 +3,6 @@ guid: 9a6e603a7f407ec4aa25ac2c2799f71b folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Prefabs/GamePlayer.prefab b/UnityProject/Assets/Mirror/Examples/Room/Prefabs/GamePlayer.prefab index 9c9a5cb..efd58f9 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Prefabs/GamePlayer.prefab +++ b/UnityProject/Assets/Mirror/Examples/Room/Prefabs/GamePlayer.prefab @@ -90,12 +90,13 @@ GameObject: - component: {fileID: 4822224316094678} - component: {fileID: 114402732107420660} - component: {fileID: 114265392388239132} - - component: {fileID: 143011667059871024} - - component: {fileID: 6799120071495980942} - - component: {fileID: -6233809968765690729} - component: {fileID: 114892629901890886} - - component: {fileID: 6261579163786439309} + - component: {fileID: -6736841464767829722} - component: {fileID: 115187108610643062} + - component: {fileID: 6799120071495980942} + - component: {fileID: 143011667059871024} + - component: {fileID: -6233809968765690729} + - component: {fileID: 6261579163786439309} m_Layer: 0 m_Name: GamePlayer m_TagString: Player @@ -133,7 +134,7 @@ MonoBehaviour: sceneId: 0 serverOnly: 0 visible: 0 - m_AssetId: + m_AssetId: 21daf89214c6ee443ad6875b73083c60 hasSpawned: 0 --- !u!114 &114265392388239132 MonoBehaviour: @@ -148,14 +149,96 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: syncMode: 0 - syncInterval: 0 + syncInterval: 0.1 clientAuthority: 1 - localPositionSensitivity: 0.01 - localRotationSensitivity: 0.01 - localScaleSensitivity: 0.01 - compressRotation: 0 - interpolateScale: 0 + sendInterval: 0.05 + syncPosition: 1 + syncRotation: 1 syncScale: 0 + interpolatePosition: 1 + interpolateRotation: 1 + interpolateScale: 0 + bufferTimeMultiplier: 1 + bufferSizeLimit: 64 + catchupThreshold: 4 + catchupMultiplier: 0.1 + onlySyncOnChange: 1 + bufferResetMultiplier: 5 + positionSensitivity: 0.01 + rotationSensitivity: 0.01 + scaleSensitivity: 0.01 + showGizmos: 0 + showOverlay: 0 + overlayColor: {r: 0, g: 0, b: 0, a: 0.5} +--- !u!114 &114892629901890886 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1480027675339556} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 24fd13686a451ad498101a604d134e39, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 + characterController: {fileID: 143011667059871024} + moveSpeed: 8 + turnSensitivity: 5 + maxTurnSpeed: 100 + horizontal: 0 + vertical: 0 + turn: 0 + jumpSpeed: 0 + isGrounded: 1 + isFalling: 0 + velocity: {x: 0, y: 0, z: 0} +--- !u!114 &-6736841464767829722 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1480027675339556} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 71ac1e35462ffad469e77d1c2fe6c9f3, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 +--- !u!114 &115187108610643062 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1480027675339556} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1ba998ee2eff92a419f4377519caf095, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0 + index: 0 + score: 0 +--- !u!136 &6799120071495980942 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1480027675339556} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + m_Radius: 0.5 + m_Height: 1 + m_Direction: 1 + m_Center: {x: 0, y: 0, z: 0} --- !u!143 &143011667059871024 CharacterController: m_ObjectHideFlags: 0 @@ -174,20 +257,6 @@ CharacterController: m_SkinWidth: 0.08 m_MinMoveDistance: 0.001 m_Center: {x: 0, y: 0, z: 0} ---- !u!136 &6799120071495980942 -CapsuleCollider: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1480027675339556} - m_Material: {fileID: 0} - m_IsTrigger: 0 - m_Enabled: 1 - m_Radius: 0.5 - m_Height: 1 - m_Direction: 1 - m_Center: {x: 0, y: 0, z: 0} --- !u!54 &-6233809968765690729 Rigidbody: m_ObjectHideFlags: 0 @@ -204,31 +273,6 @@ Rigidbody: m_Interpolate: 0 m_Constraints: 0 m_CollisionDetection: 0 ---- !u!114 &114892629901890886 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1480027675339556} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 24fd13686a451ad498101a604d134e39, type: 3} - m_Name: - m_EditorClassIdentifier: - syncMode: 0 - syncInterval: 0 - characterController: {fileID: 143011667059871024} - moveSpeed: 8 - turnSensitivity: 5 - maxTurnSpeed: 150 - horizontal: 0 - vertical: 0 - turn: 6.57 - jumpSpeed: 0 - isGrounded: 1 - isFalling: 0 - velocity: {x: 0, y: 0, z: 0} --- !u!114 &6261579163786439309 MonoBehaviour: m_ObjectHideFlags: 0 @@ -246,22 +290,6 @@ MonoBehaviour: color: serializedVersion: 2 rgba: 4278190080 ---- !u!114 &115187108610643062 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1480027675339556} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 1ba998ee2eff92a419f4377519caf095, type: 3} - m_Name: - m_EditorClassIdentifier: - syncMode: 0 - syncInterval: 0 - index: 0 - score: 0 --- !u!1 &4926068573968176962 GameObject: m_ObjectHideFlags: 0 diff --git a/UnityProject/Assets/Mirror/Examples/Room/Prefabs/GamePlayer.prefab.meta b/UnityProject/Assets/Mirror/Examples/Room/Prefabs/GamePlayer.prefab.meta index 5aa120d..0df6ef0 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Prefabs/GamePlayer.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Prefabs/GamePlayer.prefab.meta @@ -3,6 +3,6 @@ guid: 21daf89214c6ee443ad6875b73083c60 NativeFormatImporter: externalObjects: {} mainObjectFileID: 100100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Prefabs/Prize.prefab.meta b/UnityProject/Assets/Mirror/Examples/Room/Prefabs/Prize.prefab.meta index 9835e43..87ab623 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Prefabs/Prize.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Prefabs/Prize.prefab.meta @@ -3,6 +3,6 @@ guid: 52f1c9ea06cfd154cb68ff9d1b66fc13 NativeFormatImporter: externalObjects: {} mainObjectFileID: 100100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Prefabs/RoomPlayer.prefab.meta b/UnityProject/Assets/Mirror/Examples/Room/Prefabs/RoomPlayer.prefab.meta index ecf0f17..153c825 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Prefabs/RoomPlayer.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Prefabs/RoomPlayer.prefab.meta @@ -3,6 +3,6 @@ guid: deae2134a1d77704b9c595efe69767dd NativeFormatImporter: externalObjects: {} mainObjectFileID: 100100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/README.md.meta b/UnityProject/Assets/Mirror/Examples/Room/README.md.meta index 7f760e8..eb4961d 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/README.md.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/README.md.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 9110f04bd1e8468479f6625342d311c5 TextScriptImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scenes.meta b/UnityProject/Assets/Mirror/Examples/Room/Scenes.meta index cc97df2..17072cf 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Scenes.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Scenes.meta @@ -3,6 +3,6 @@ guid: 4f704ae4b4f98ae41a0bce26658850c1 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scenes/OfflineScene.unity b/UnityProject/Assets/Mirror/Examples/Room/Scenes/OfflineScene.unity index 94aedc6..006c382 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Scenes/OfflineScene.unity +++ b/UnityProject/Assets/Mirror/Examples/Room/Scenes/OfflineScene.unity @@ -54,7 +54,7 @@ LightmapSettings: m_EnableBakedLightmaps: 0 m_EnableRealtimeLightmaps: 0 m_LightmapEditorSettings: - serializedVersion: 10 + serializedVersion: 12 m_Resolution: 2 m_BakeResolution: 40 m_AtlasSize: 1024 @@ -62,6 +62,7 @@ LightmapSettings: m_AOMaxDistance: 1 m_CompAOExponent: 1 m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 m_Padding: 2 m_LightmapParameters: {fileID: 0} m_LightmapsBakeMode: 1 @@ -76,10 +77,16 @@ LightmapSettings: 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_PVRFilteringMode: 2 + m_PVREnvironmentMIS: 0 m_PVRCulling: 1 m_PVRFilteringGaussRadiusDirect: 1 m_PVRFilteringGaussRadiusIndirect: 5 @@ -87,7 +94,9 @@ LightmapSettings: m_PVRFilteringAtrousPositionSigmaDirect: 0.5 m_PVRFilteringAtrousPositionSigmaIndirect: 2 m_PVRFilteringAtrousPositionSigmaAO: 1 - m_ShowResolutionOverlay: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 m_LightingDataAsset: {fileID: 0} m_UseShadowmask: 1 --- !u!196 &4 @@ -141,9 +150,10 @@ Camera: m_ClearFlags: 1 m_BackGroundColor: {r: 0.23429157, g: 0.254717, b: 0.23546094, a: 0} m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 m_SensorSize: {x: 36, y: 24} m_LensShift: {x: 0, y: 0} - m_GateFitMode: 2 m_FocalLength: 50 m_NormalizedViewPortRect: serializedVersion: 2 @@ -198,7 +208,7 @@ GameObject: - component: {fileID: 2008127831} - component: {fileID: 2008127830} m_Layer: 0 - m_Name: RoomManager + m_Name: NetworkRoomManager m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -217,12 +227,15 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Port: 7777 + DualMode: 1 NoDelay: 1 Interval: 10 + Timeout: 10000 FastResend: 2 CongestionWindow: 0 SendWindowSize: 4096 ReceiveWindowSize: 4096 + NonAlloc: 1 debugLog: 0 statisticsGUI: 0 statisticsLog: 0 @@ -239,12 +252,10 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: dontDestroyOnLoad: 1 + PersistNetworkManagerToOfflineScene: 0 runInBackground: 1 autoStartServerBuild: 1 - showDebugMessages: 0 serverTickRate: 30 - serverBatching: 0 - serverBatchInterval: 0 offlineScene: Assets/Mirror/Examples/Room/Scenes/OfflineScene.unity onlineScene: Assets/Mirror/Examples/Room/Scenes/RoomScene.unity transport: {fileID: 2008127830} diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scenes/OfflineScene.unity.meta b/UnityProject/Assets/Mirror/Examples/Room/Scenes/OfflineScene.unity.meta index 9e52d86..45332ad 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Scenes/OfflineScene.unity.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Scenes/OfflineScene.unity.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 9e6f14982412b2545a0911ebaaa46a23 DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scenes/OnlineScene.unity.meta b/UnityProject/Assets/Mirror/Examples/Room/Scenes/OnlineScene.unity.meta index 1c77276..8a990fe 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Scenes/OnlineScene.unity.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Scenes/OnlineScene.unity.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: d7a6763559b31854586c9e49916273ef DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scenes/RoomScene.unity.meta b/UnityProject/Assets/Mirror/Examples/Room/Scenes/RoomScene.unity.meta index 1015cbb..8749026 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Scenes/RoomScene.unity.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Scenes/RoomScene.unity.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 48478f4d522f96945b8396f376299d3a DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scripts.meta b/UnityProject/Assets/Mirror/Examples/Room/Scripts.meta index b9fd528..b15256a 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Scripts.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Scripts.meta @@ -3,6 +3,6 @@ guid: 03401915dd450454e88f0a839d3346f1 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scripts/NetworkRoomManagerExt.cs b/UnityProject/Assets/Mirror/Examples/Room/Scripts/NetworkRoomManagerExt.cs index c89f7da..a85dffa 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Scripts/NetworkRoomManagerExt.cs +++ b/UnityProject/Assets/Mirror/Examples/Room/Scripts/NetworkRoomManagerExt.cs @@ -1,5 +1,9 @@ using UnityEngine; -using UnityEngine.SceneManagement; + +/* + Documentation: https://mirror-networking.gitbook.io/docs/components/network-manager + API Reference: https://mirror-networking.com/docs/api/Mirror.NetworkManager.html +*/ namespace Mirror.Examples.NetworkRoom { @@ -18,9 +22,7 @@ namespace Mirror.Examples.NetworkRoom { // spawn the initial batch of Rewards if (sceneName == GameplayScene) - { Spawner.InitialSpawn(); - } } /// @@ -31,7 +33,7 @@ namespace Mirror.Examples.NetworkRoom /// /// /// true unless some code in here decides it needs to abort the replacement - public override bool OnRoomServerSceneLoadedForPlayer(NetworkConnection conn, GameObject roomPlayer, GameObject gamePlayer) + public override bool OnRoomServerSceneLoadedForPlayer(NetworkConnectionToClient conn, GameObject roomPlayer, GameObject gamePlayer) { PlayerScore playerScore = gamePlayer.GetComponent(); playerScore.index = roomPlayer.GetComponent().index; diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scripts/NetworkRoomManagerExt.cs.meta b/UnityProject/Assets/Mirror/Examples/Room/Scripts/NetworkRoomManagerExt.cs.meta index a4f65c8..d8f5760 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Scripts/NetworkRoomManagerExt.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Scripts/NetworkRoomManagerExt.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scripts/NetworkRoomPlayerExt.cs b/UnityProject/Assets/Mirror/Examples/Room/Scripts/NetworkRoomPlayerExt.cs index d59e554..2706622 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Scripts/NetworkRoomPlayerExt.cs +++ b/UnityProject/Assets/Mirror/Examples/Room/Scripts/NetworkRoomPlayerExt.cs @@ -1,4 +1,5 @@ using UnityEngine; +using UnityEngine.SceneManagement; namespace Mirror.Examples.NetworkRoom { @@ -7,24 +8,32 @@ namespace Mirror.Examples.NetworkRoom { public override void OnStartClient() { - // Debug.LogFormat(LogType.Log, "OnStartClient {0}", SceneManager.GetActiveScene().path); - - base.OnStartClient(); + //Debug.Log($"OnStartClient {gameObject}"); } public override void OnClientEnterRoom() { - // Debug.LogFormat(LogType.Log, "OnClientEnterRoom {0}", SceneManager.GetActiveScene().path); + //Debug.Log($"OnClientEnterRoom {SceneManager.GetActiveScene().path}"); } public override void OnClientExitRoom() { - // Debug.LogFormat(LogType.Log, "OnClientExitRoom {0}", SceneManager.GetActiveScene().path); + //Debug.Log($"OnClientExitRoom {SceneManager.GetActiveScene().path}"); + } + + public override void IndexChanged(int oldIndex, int newIndex) + { + //Debug.Log($"IndexChanged {newIndex}"); } public override void ReadyStateChanged(bool oldReadyState, bool newReadyState) { - // Debug.LogFormat(LogType.Log, "ReadyStateChanged {0}", newReadyState); + //Debug.Log($"ReadyStateChanged {newReadyState}"); + } + + public override void OnGUI() + { + base.OnGUI(); } } } diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scripts/NetworkRoomPlayerExt.cs.meta b/UnityProject/Assets/Mirror/Examples/Room/Scripts/NetworkRoomPlayerExt.cs.meta index 773e162..8e4d135 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Scripts/NetworkRoomPlayerExt.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Scripts/NetworkRoomPlayerExt.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerCamera.cs b/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerCamera.cs new file mode 100644 index 0000000..46b84ca --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerCamera.cs @@ -0,0 +1,41 @@ +using UnityEngine; +using UnityEngine.SceneManagement; + +// This sets up the scene camera for the local player + +namespace Mirror.Examples.NetworkRoom +{ + public class PlayerCamera : NetworkBehaviour + { + Camera mainCam; + + void Awake() + { + mainCam = Camera.main; + } + + public override void OnStartLocalPlayer() + { + if (mainCam != null) + { + // configure and make camera a child of player with 3rd person offset + mainCam.orthographic = false; + mainCam.transform.SetParent(transform); + mainCam.transform.localPosition = new Vector3(0f, 3f, -8f); + mainCam.transform.localEulerAngles = new Vector3(10f, 0f, 0f); + } + } + + public override void OnStopLocalPlayer() + { + if (mainCam != null) + { + mainCam.transform.SetParent(null); + SceneManager.MoveGameObjectToScene(mainCam.gameObject, SceneManager.GetActiveScene()); + mainCam.orthographic = true; + mainCam.transform.localPosition = new Vector3(0f, 70f, 0f); + mainCam.transform.localEulerAngles = new Vector3(90f, 0f, 0f); + } + } + } +} diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerCamera.cs.meta b/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerCamera.cs.meta new file mode 100644 index 0000000..8c21f56 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerCamera.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 71ac1e35462ffad469e77d1c2fe6c9f3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerController.cs b/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerController.cs index 3455519..2346c58 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerController.cs +++ b/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerController.cs @@ -14,36 +14,21 @@ namespace Mirror.Examples.NetworkRoom { if (characterController == null) characterController = GetComponent(); - } - void Start() - { - characterController.enabled = isLocalPlayer; + characterController.enabled = false; + GetComponent().isKinematic = true; + GetComponent().clientAuthority = true; } public override void OnStartLocalPlayer() { - Camera.main.orthographic = false; - Camera.main.transform.SetParent(transform); - Camera.main.transform.localPosition = new Vector3(0f, 3f, -8f); - Camera.main.transform.localEulerAngles = new Vector3(10f, 0f, 0f); - } - - void OnDisable() - { - if (isLocalPlayer && Camera.main != null) - { - Camera.main.orthographic = true; - Camera.main.transform.SetParent(null); - Camera.main.transform.localPosition = new Vector3(0f, 70f, 0f); - Camera.main.transform.localEulerAngles = new Vector3(90f, 0f, 0f); - } + characterController.enabled = true; } [Header("Movement Settings")] public float moveSpeed = 8f; public float turnSensitivity = 5f; - public float maxTurnSpeed = 150f; + public float maxTurnSpeed = 100f; [Header("Diagnostics")] public float horizontal; @@ -56,7 +41,7 @@ namespace Mirror.Examples.NetworkRoom void Update() { - if (!isLocalPlayer) + if (!isLocalPlayer || characterController == null || !characterController.enabled) return; horizontal = Input.GetAxis("Horizontal"); @@ -88,7 +73,7 @@ namespace Mirror.Examples.NetworkRoom void FixedUpdate() { - if (!isLocalPlayer || characterController == null) + if (!isLocalPlayer || characterController == null || !characterController.enabled) return; transform.Rotate(0f, turn * Time.fixedDeltaTime, 0f); diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerController.cs.meta b/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerController.cs.meta index 2dad690..74f8f95 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerController.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerController.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerScore.cs b/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerScore.cs index fdd57de..80afd78 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerScore.cs +++ b/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerScore.cs @@ -12,7 +12,7 @@ namespace Mirror.Examples.NetworkRoom void OnGUI() { - GUI.Box(new Rect(10f + (index * 110), 10f, 100f, 25f), $"P{index}: {score.ToString("0000000")}"); + GUI.Box(new Rect(10f + (index * 110), 10f, 100f, 25f), $"P{index}: {score:0000000}"); } } } diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerScore.cs.meta b/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerScore.cs.meta index 2dab38c..7b1502f 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerScore.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Scripts/PlayerScore.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scripts/RandomColor.cs.meta b/UnityProject/Assets/Mirror/Examples/Room/Scripts/RandomColor.cs.meta index 192269f..be310a0 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Scripts/RandomColor.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Scripts/RandomColor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scripts/Reward.cs b/UnityProject/Assets/Mirror/Examples/Room/Scripts/Reward.cs index b21e245..550ebf6 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Scripts/Reward.cs +++ b/UnityProject/Assets/Mirror/Examples/Room/Scripts/Reward.cs @@ -38,7 +38,7 @@ namespace Mirror.Examples.NetworkRoom // calculate the points from the color ... lighter scores higher as the average approaches 255 // UnityEngine.Color RGB values are float fractions of 255 uint points = (uint)(((color.r) + (color.g) + (color.b)) / 3); - // Debug.LogFormat(LogType.Log, "Scored {0} points R:{1} G:{2} B:{3}", points, color.r, color.g, color.b); + //Debug.Log($"Scored {points} points R:{color.r} G:{color.g} B:{color.b}"); // award the points via SyncVar on the PlayerController player.GetComponent().score += points; diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scripts/Reward.cs.meta b/UnityProject/Assets/Mirror/Examples/Room/Scripts/Reward.cs.meta index 487ff18..879bc7d 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Scripts/Reward.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Scripts/Reward.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Room/Scripts/Spawner.cs.meta b/UnityProject/Assets/Mirror/Examples/Room/Scripts/Spawner.cs.meta index 9bd2df8..d4103d6 100644 --- a/UnityProject/Assets/Mirror/Examples/Room/Scripts/Spawner.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Room/Scripts/Spawner.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation.meta b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation.meta new file mode 100644 index 0000000..fd92c28 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 51718c4f05a6a4125967a38f5df2eb8c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ClientCube.cs b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ClientCube.cs new file mode 100644 index 0000000..60e879a --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ClientCube.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Mirror.Examples.SnapshotInterpolationDemo +{ + public class ClientCube : MonoBehaviour + { + [Header("Components")] + public ServerCube server; + public Renderer render; + + [Header("Toggle")] + public bool interpolate = true; + + // decrease bufferTime at runtime to see the catchup effect. + // increase to see slowdown. + // 'double' so we can have very precise dynamic adjustment without rounding + [Header("Buffering")] + [Tooltip("Local simulation is behind by sendInterval * multiplier seconds.\n\nThis guarantees that we always have enough snapshots in the buffer to mitigate lags & jitter.\n\nIncrease this if the simulation isn't smooth. By default, it should be around 2.")] + public double bufferTimeMultiplier = 2; + public double bufferTime => server.sendInterval * bufferTimeMultiplier; + + // + public SortedList snapshots = new SortedList(); + Func Interpolate = Snapshot3D.Interpolate; + + // for smooth interpolation, we need to interpolate along server time. + // any other time (arrival on client, client local time, etc.) is not + // going to give smooth results. + double localTimeline; + + // catchup / slowdown adjustments are applied to timescale, + // to be adjusted in every update instead of when receiving messages. + double localTimescale = 1; + + // catchup ///////////////////////////////////////////////////////////// + // catchup thresholds in 'frames'. + // half a frame might be too aggressive. + [Header("Catchup / Slowdown")] + [Tooltip("Slowdown begins when the local timeline is moving too fast towards remote time. Threshold is in frames worth of snapshots.\n\nThis needs to be negative.\n\nDon't modify unless you know what you are doing.")] + public float catchupNegativeThreshold = -1; // careful, don't want to run out of snapshots + + [Tooltip("Catchup begins when the local timeline is moving too slow and getting too far away from remote time. Threshold is in frames worth of snapshots.\n\nThis needs to be positive.\n\nDon't modify unless you know what you are doing.")] + public float catchupPositiveThreshold = 1; + + [Tooltip("Local timeline acceleration in % while catching up.")] + [Range(0, 1)] + public double catchupSpeed = 0.01f; // 1% + + [Tooltip("Local timeline slowdown in % while slowing down.")] + [Range(0, 1)] + public double slowdownSpeed = 0.01f; // 1% + + [Tooltip("Catchup/Slowdown is adjusted over n-second exponential moving average.")] + public int driftEmaDuration = 1; // shouldn't need to modify this, but expose it anyway + + // we use EMA to average the last second worth of snapshot time diffs. + // manually averaging the last second worth of values with a for loop + // would be the same, but a moving average is faster because we only + // ever add one value. + ExponentialMovingAverage driftEma; + + // dynamic buffer time adjustment ////////////////////////////////////// + // dynamically adjusts bufferTimeMultiplier for smooth results. + // to understand how this works, try this manually: + // + // - disable dynamic adjustment + // - set jitter = 0.2 (20% is a lot!) + // - notice some stuttering + // - disable interpolation to see just how much jitter this really is(!) + // - enable interpolation again + // - manually increase bufferTimeMultiplier to 3-4 + // ... the cube slows down (blue) until it's smooth + // - with dynamic adjustment enabled, it will set 4 automatically + // ... the cube slows down (blue) until it's smooth as well + // + // note that 20% jitter is extreme. + // for this to be perfectly smooth, set the safety tolerance to '2'. + // but realistically this is not necessary, and '1' is enough. + [Header("Dynamic Adjustment")] + [Tooltip("Automatically adjust bufferTimeMultiplier for smooth results.\nSets a low multiplier on stable connections, and a high multiplier on jittery connections.")] + public bool dynamicAdjustment = true; + + [Tooltip("Safety buffer that is always added to the dynamic bufferTimeMultiplier adjustment.")] + public float dynamicAdjustmentTolerance = 1; // 1 is realistically just fine, 2 is very very safe even for 20% jitter. can be half a frame too. (see above comments) + + [Tooltip("Dynamic adjustment is computed over n-second exponential moving average standard deviation.")] + public int deliveryTimeEmaDuration = 2; // 1-2s recommended to capture average delivery time + ExponentialMovingAverage deliveryTimeEma; // average delivery time (standard deviation gives average jitter) + + // debugging /////////////////////////////////////////////////////////// + [Header("Debug")] + public Color catchupColor = Color.red; + public Color slowdownColor = Color.blue; + Color defaultColor; + + void Awake() + { + defaultColor = render.sharedMaterial.color; + + // initialize EMA with 'emaDuration' seconds worth of history. + // 1 second holds 'sendRate' worth of values. + // multiplied by emaDuration gives n-seconds. + driftEma = new ExponentialMovingAverage(server.sendRate * driftEmaDuration); + deliveryTimeEma = new ExponentialMovingAverage(server.sendRate * deliveryTimeEmaDuration); + } + + // add snapshot & initialize client interpolation time if needed + public void OnMessage(Snapshot3D snap) + { + // set local timestamp (= when it was received on our end) + snap.localTime = Time.timeAsDouble; + + // (optional) dynamic adjustment + if (dynamicAdjustment) + { + // set bufferTime on the fly. + // shows in inspector for easier debugging :) + bufferTimeMultiplier = SnapshotInterpolation.DynamicAdjustment( + server.sendInterval, + deliveryTimeEma.StandardDeviation, + dynamicAdjustmentTolerance + ); + } + + // insert into the buffer & initialize / adjust / catchup + SnapshotInterpolation.Insert( + snapshots, + snap, + ref localTimeline, + ref localTimescale, + server.sendInterval, + bufferTime, + catchupSpeed, + slowdownSpeed, + ref driftEma, + catchupNegativeThreshold, + catchupPositiveThreshold, + ref deliveryTimeEma); + } + + void Update() + { + if (snapshots.Count > 0) + { + // snapshot interpolation + if (interpolate) + { + if (SnapshotInterpolation.Step( + snapshots, + Time.unscaledDeltaTime, + ref localTimeline, + localTimescale, + Interpolate, + out Snapshot3D computed)) + { + transform.position = computed.position; + } + } + // apply raw + else + { + Snapshot3D snap = snapshots.Values[0]; + transform.position = snap.position; + snapshots.RemoveAt(0); + } + } + + // color material while catching up / slowing down + if (localTimescale < 1) + render.material.color = slowdownColor; + else if (localTimescale > 1) + render.material.color = catchupColor; + else + render.material.color = defaultColor; + } + + void OnGUI() + { + // display buffer size as number for easier debugging. + // catchup is displayed as color state in Update() already. + const int width = 30; // fit 3 digits + const int height = 20; + Vector2 screen = Camera.main.WorldToScreenPoint(transform.position); + string str = $"{snapshots.Count}"; + GUI.Label(new Rect(screen.x - width / 2, screen.y - height / 2, width, height), str); + } + + void OnValidate() + { + // thresholds need to be <0 and >0 + catchupNegativeThreshold = Math.Min(catchupNegativeThreshold, 0); + catchupPositiveThreshold = Math.Max(catchupPositiveThreshold, 0); + } + } +} diff --git a/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ClientCube.cs.meta b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ClientCube.cs.meta new file mode 100644 index 0000000..f3ca428 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ClientCube.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 51b244c3535d474aaf0a7a679f86185f +timeCreated: 1654065994 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ClientMaterial.mat b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ClientMaterial.mat new file mode 100644 index 0000000..558d872 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ClientMaterial.mat @@ -0,0 +1,80 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: ClientMaterial + m_Shader: {fileID: 10755, guid: 0000000000000000f000000000000000, type: 0} + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0, g: 0, b: 0, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ClientMaterial.mat.meta b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ClientMaterial.mat.meta new file mode 100644 index 0000000..3caa45c --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ClientMaterial.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f17cbcb3229954975ab0818845a2c17f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ServerCube.cs b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ServerCube.cs new file mode 100644 index 0000000..2d54698 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ServerCube.cs @@ -0,0 +1,104 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace Mirror.Examples.SnapshotInterpolationDemo +{ + public class ServerCube : MonoBehaviour + { + [Header("Components")] + public ClientCube client; + + [Header("Movement")] + public float distance = 10; + public float speed = 3; + Vector3 start; + + [Header("Snapshot Interpolation")] + [Tooltip("Send N snapshots per second. Multiples of frame rate make sense.")] + public int sendRate = 30; // in Hz. easier to work with as int for EMA. easier to display '30' than '0.333333333' + public float sendInterval => 1f / sendRate; + float lastSendTime; + + [Header("Latency Simulation")] + [Tooltip("Latency in seconds")] + public float latency = 0.05f; // 50 ms + [Tooltip("Latency jitter, randomly added to latency.")] + [Range(0, 1)] public float jitter = 0.05f; + [Tooltip("Packet loss in %")] + [Range(0, 1)] public float loss = 0.1f; + [Tooltip("Scramble % of unreliable messages, just like over the real network. Mirror unreliable is unordered.")] + [Range(0, 1)] public float scramble = 0.1f; + + // random + // UnityEngine.Random.value is [0, 1] with both upper and lower bounds inclusive + // but we need the upper bound to be exclusive, so using System.Random instead. + // => NextDouble() is NEVER < 0 so loss=0 never drops! + // => NextDouble() is ALWAYS < 1 so loss=1 always drops! + System.Random random = new System.Random(); + + // hold on to snapshots for a little while before delivering + // + List<(double, Snapshot3D)> queue = new List<(double, Snapshot3D)>(); + + // latency simulation: + // always a fixed value + some jitter. + float SimulateLatency() => latency + Random.value * jitter; + + void Start() + { + start = transform.position; + } + + void Update() + { + // move on XY plane + float x = Mathf.PingPong(Time.time * speed, distance); + transform.position = new Vector3(start.x + x, start.y, start.z); + + // broadcast snapshots every interval + if (Time.time >= lastSendTime + sendInterval) + { + Send(transform.position); + lastSendTime = Time.time; + } + + Flush(); + } + + void Send(Vector3 position) + { + // create snapshot + Snapshot3D snap = new Snapshot3D(Time.timeAsDouble, 0, position); + + // simulate packet loss + bool drop = random.NextDouble() < loss; + if (!drop) + { + // simulate scramble (Random.Next is < max, so +1) + bool doScramble = random.NextDouble() < scramble; + int last = queue.Count; + int index = doScramble ? random.Next(0, last + 1) : last; + + // simulate latency + float simulatedLatency = SimulateLatency(); + double deliveryTime = Time.timeAsDouble + simulatedLatency; + queue.Insert(index, (deliveryTime, snap)); + } + } + + void Flush() + { + // flush ready snapshots to client + for (int i = 0; i < queue.Count; ++i) + { + (double deliveryTime, Snapshot3D snap) = queue[i]; + if (Time.timeAsDouble >= deliveryTime) + { + client.OnMessage(snap); + queue.RemoveAt(i); + --i; + } + } + } + } +} diff --git a/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ServerCube.cs.meta b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ServerCube.cs.meta new file mode 100644 index 0000000..6c5aad8 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ServerCube.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9a53730695f274a8aaa7ffdcf50d1008 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ServerMaterial.mat b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ServerMaterial.mat new file mode 100644 index 0000000..2046f0c --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ServerMaterial.mat @@ -0,0 +1,80 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: ServerMaterial + m_Shader: {fileID: 10755, guid: 0000000000000000f000000000000000, type: 0} + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ServerMaterial.mat.meta b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ServerMaterial.mat.meta new file mode 100644 index 0000000..9c6d761 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/ServerMaterial.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 163b909ba60cc435a95bb35396edda15 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/Snapshot3D.cs b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/Snapshot3D.cs new file mode 100644 index 0000000..7f14b29 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/Snapshot3D.cs @@ -0,0 +1,26 @@ +// a simple snapshot with timestamp & interpolation +using UnityEngine; + +namespace Mirror.Examples.SnapshotInterpolationDemo +{ + public struct Snapshot3D : Snapshot + { + public double remoteTime { get; set; } + public double localTime { get; set; } + public Vector3 position; + + public Snapshot3D(double remoteTime, double localTime, Vector3 position) + { + this.remoteTime = remoteTime; + this.localTime = localTime; + this.position = position; + } + + public static Snapshot3D Interpolate(Snapshot3D from, Snapshot3D to, double t) => + new Snapshot3D( + // interpolated snapshot is applied directly. don't need timestamps. + 0, 0, + // lerp unclamped in case we ever need to extrapolate. + Vector3.LerpUnclamped(from.position, to.position, (float)t)); + } +} diff --git a/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/Snapshot3D.cs.meta b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/Snapshot3D.cs.meta new file mode 100644 index 0000000..e8b0206 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/Snapshot3D.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a2c9bdffb0b934b52b2c337a5c8a5698 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/SnapshotInterpolation.unity b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/SnapshotInterpolation.unity new file mode 100644 index 0000000..2a218ca --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/SnapshotInterpolation.unity @@ -0,0 +1,455 @@ +%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: 10304, guid: 0000000000000000f000000000000000, type: 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.37311915, g: 0.3807396, b: 0.35872662, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + 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: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + 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_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &89338751 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 89338755} + - component: {fileID: 89338756} + m_Layer: 0 + m_Name: Client Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &89338755 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 89338751} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -5, y: 0.5, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1292704308} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &89338756 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 89338751} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 51b244c3535d474aaf0a7a679f86185f, type: 3} + m_Name: + m_EditorClassIdentifier: + server: {fileID: 474480122} + render: {fileID: 1292704310} + interpolate: 1 + bufferTimeMultiplier: 1 + catchupNegativeThreshold: -1 + catchupPositiveThreshold: 1 + catchupSpeed: 0.009999999776482582 + slowdownSpeed: 0.009999999776482582 + driftEmaDuration: 1 + dynamicAdjustment: 1 + dynamicAdjustmentTolerance: 1 + deliveryTimeEmaDuration: 2 + catchupColor: {r: 1, g: 0, b: 0, a: 1} + slowdownColor: {r: 0, g: 0, b: 1, a: 1} +--- !u!1 &474480117 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 474480121} + - component: {fileID: 474480120} + - component: {fileID: 474480119} + - component: {fileID: 474480122} + m_Layer: 0 + m_Name: Server Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!23 &474480119 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 474480117} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 163b909ba60cc435a95bb35396edda15, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &474480120 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 474480117} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &474480121 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 474480117} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -5, y: 0.5, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &474480122 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 474480117} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9a53730695f274a8aaa7ffdcf50d1008, type: 3} + m_Name: + m_EditorClassIdentifier: + client: {fileID: 89338756} + distance: 10 + speed: 3 + sendRate: 30 + latency: 0.05 + jitter: 0.05 + loss: 0.05 + scramble: 0.05 +--- !u!1 &1292704307 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1292704308} + - component: {fileID: 1292704311} + - component: {fileID: 1292704310} + m_Layer: 0 + m_Name: Visual Offset + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1292704308 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1292704307} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: -1, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 89338755} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &1292704310 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1292704307} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: f17cbcb3229954975ab0818845a2c17f, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1292704311 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1292704307} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1961486736 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1961486739} + - component: {fileID: 1961486738} + - component: {fileID: 1961486737} + m_Layer: 0 + m_Name: Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &1961486737 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1961486736} + m_Enabled: 1 +--- !u!20 &1961486738 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1961486736} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 2 + m_BackGroundColor: {r: 0.26415092, g: 0.26415092, b: 0.26415092, 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: 1 + orthographic size: 7 + m_Depth: 0 + 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 &1961486739 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1961486736} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -11.22} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/SnapshotInterpolation.unity.meta b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/SnapshotInterpolation.unity.meta new file mode 100644 index 0000000..3ca6255 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/SnapshotInterpolation.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 612a705077c16479db7b167ab1599ae8 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/_DISABLE VSYNC_ b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/_DISABLE VSYNC_ new file mode 100644 index 0000000..c1a879d --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/_DISABLE VSYNC_ @@ -0,0 +1,2 @@ +otherwise it may not look entirely smooth. +even on 120 hz. \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/_DISABLE VSYNC_.meta b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/_DISABLE VSYNC_.meta new file mode 100644 index 0000000..ee6b19c --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Snapshot Interpolation/_DISABLE VSYNC_.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7db50a51601fe4b3c8c929f6868bffbc +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks.meta b/UnityProject/Assets/Mirror/Examples/Tanks.meta index 0864886..bee4222 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks.meta @@ -3,6 +3,6 @@ guid: a6dc5104926d44c9296aec7699989ac3 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Models.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Models.meta index 709c608..c94259a 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Models.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Models.meta @@ -3,6 +3,6 @@ guid: 6878aacf12b204d03a94f71e49f9ad60 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank.meta index fdade2c..93b0075 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank.meta @@ -3,6 +3,6 @@ guid: d8b3e43538fc240feb28e07816c7e733 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/BaseColor.png.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/BaseColor.png.meta index 8daf1fd..6e069a1 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/BaseColor.png.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/BaseColor.png.meta @@ -75,14 +75,14 @@ TextureImporter: outline: [] physicsShape: [] bones: [] - spriteID: '' + spriteID: vertices: [] - indices: '' + indices: edges: [] weights: [] - spritePackingTag: '' + spritePackingTag: pSDRemoveMatte: 0 pSDShowRemoveMatteOption: 0 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Controller.controller.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Controller.controller.meta index 498813a..7e1061c 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Controller.controller.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Controller.controller.meta @@ -3,6 +3,6 @@ guid: a7211483bbd794b6d85ed88576e7d85c NativeFormatImporter: externalObjects: {} mainObjectFileID: 9100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Emissive.png.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Emissive.png.meta index d867e7d..333174c 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Emissive.png.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Emissive.png.meta @@ -75,14 +75,14 @@ TextureImporter: outline: [] physicsShape: [] bones: [] - spriteID: '' + spriteID: vertices: [] - indices: '' + indices: edges: [] weights: [] - spritePackingTag: '' + spritePackingTag: pSDRemoveMatte: 0 pSDShowRemoveMatteOption: 0 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Metallic.png.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Metallic.png.meta index cf1ba03..91006e4 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Metallic.png.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Metallic.png.meta @@ -75,14 +75,14 @@ TextureImporter: outline: [] physicsShape: [] bones: [] - spriteID: '' + spriteID: vertices: [] - indices: '' + indices: edges: [] weights: [] - spritePackingTag: '' + spritePackingTag: pSDRemoveMatte: 0 pSDShowRemoveMatteOption: 0 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Normal.png.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Normal.png.meta index 2d023b0..acc128a 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Normal.png.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Normal.png.meta @@ -75,14 +75,14 @@ TextureImporter: outline: [] physicsShape: [] bones: [] - spriteID: '' + spriteID: vertices: [] - indices: '' + indices: edges: [] weights: [] - spritePackingTag: '' + spritePackingTag: pSDRemoveMatte: 0 pSDShowRemoveMatteOption: 0 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Recon_Tank - License.txt.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Recon_Tank - License.txt.meta index 0a73a61..2168518 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Recon_Tank - License.txt.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/Recon_Tank - License.txt.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 6e745106dbbc3412bbe43eaf25dabd4f TextScriptImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/TankMaterial.mat.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/TankMaterial.mat.meta index b99569a..080f534 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/TankMaterial.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/TankMaterial.mat.meta @@ -3,6 +3,6 @@ guid: 2e67e42170aa64aa9a33424f8045ac89 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/reconTank.fbx.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/reconTank.fbx.meta index 350ac4b..1ac1ee0 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/reconTank.fbx.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Models/(Public Domain) Recon_Tank/reconTank.fbx.meta @@ -50,12 +50,12 @@ ModelImporter: bakeSimulation: 0 resampleCurves: 1 optimizeGameObjects: 0 - motionNodeName: '' - rigImportErrors: '' - rigImportWarnings: '' - animationImportErrors: '' - animationImportWarnings: '' - animationRetargetingWarnings: '' + motionNodeName: + rigImportErrors: + rigImportWarnings: + animationImportErrors: + animationImportWarnings: + animationRetargetingWarnings: animationDoRetargetingWarnings: 0 importAnimatedCustomProperties: 0 importConstraints: 0 @@ -226,7 +226,7 @@ ModelImporter: armStretch: 0.05 legStretch: 0.05 feetSpacing: 0 - rootMotionBoneName: '' + rootMotionBoneName: hasTranslationDoF: 0 hasExtraRoot: 0 skeletonHasParents: 1 @@ -234,6 +234,6 @@ ModelImporter: animationType: 2 humanoidOversampling: 1 additionalBone: 0 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs.meta index 5de9ad3..6c92485 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs.meta @@ -3,6 +3,6 @@ guid: 2baeb281f5e8e421582aa9f72e7f1702 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs/Projectile.prefab b/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs/Projectile.prefab index 938c99d..1f66d0b 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs/Projectile.prefab +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs/Projectile.prefab @@ -54,6 +54,7 @@ MeshRenderer: m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -65,6 +66,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -87,9 +89,9 @@ GameObject: m_Component: - component: {fileID: 24373266488650541} - component: {fileID: 1713098107664522388} + - component: {fileID: 7082621516996595528} - component: {fileID: 2355290524794870353} - component: {fileID: 4629190479245867726} - - component: {fileID: 7082621516996595528} m_Layer: 0 m_Name: Projectile m_TagString: Untagged @@ -125,10 +127,28 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3} m_Name: m_EditorClassIdentifier: - m_ServerOnly: 0 - m_LocalPlayerAuthority: 0 + sceneId: 0 + serverOnly: 0 + visible: 0 m_AssetId: b7dd46dbf38c643f09e206f9fa4be008 - m_SceneId: 0 + hasSpawned: 0 +--- !u!114 &7082621516996595528 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5890560936853567077} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8f49b83f111a64bc7a5275af4f6f930b, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 + destroyAfter: 2 + rigidBody: {fileID: 4629190479245867726} + force: 1000 --- !u!136 &2355290524794870353 CapsuleCollider: m_ObjectHideFlags: 0 @@ -159,22 +179,6 @@ Rigidbody: m_Interpolate: 1 m_Constraints: 0 m_CollisionDetection: 1 ---- !u!114 &7082621516996595528 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5890560936853567077} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 8f49b83f111a64bc7a5275af4f6f930b, type: 3} - m_Name: - m_EditorClassIdentifier: - syncInterval: 0.1 - destroyAfter: 5 - rigidBody: {fileID: 4629190479245867726} - force: 1000 --- !u!1 &9126921595194253319 GameObject: m_ObjectHideFlags: 0 @@ -214,12 +218,14 @@ Light: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 9126921595194253319} m_Enabled: 1 - serializedVersion: 8 + serializedVersion: 10 m_Type: 2 + m_Shape: 0 m_Color: {r: 1, g: 1, b: 1, a: 1} m_Intensity: 5 m_Range: 2 m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 m_CookieSize: 10 m_Shadows: m_Type: 0 @@ -229,6 +235,24 @@ Light: 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} @@ -236,11 +260,14 @@ Light: m_CullingMask: serializedVersion: 2 m_Bits: 4294967295 + m_RenderingLayerMask: 1 m_Lightmapping: 4 m_LightShadowCasterMode: 0 m_AreaSize: {x: 1, y: 1} m_BounceIntensity: 3 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 diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs/Projectile.prefab.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs/Projectile.prefab.meta index f7ff699..f6d4068 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs/Projectile.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs/Projectile.prefab.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: b7dd46dbf38c643f09e206f9fa4be008 PrefabImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs/Tank.prefab b/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs/Tank.prefab index 7682a90..daa80ff 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs/Tank.prefab +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs/Tank.prefab @@ -10,11 +10,12 @@ GameObject: m_Component: - component: {fileID: 4492442352427800} - component: {fileID: 114118589361100106} - - component: {fileID: 2240606817507776182} - component: {fileID: 114250499875391520} + - component: {fileID: 7144377311613369288} + - component: {fileID: 114654712548978148} + - component: {fileID: 2240606817507776182} - component: {fileID: 6900008319038825817} - component: {fileID: 5194388907919410155} - - component: {fileID: 114654712548978148} m_Layer: 0 m_Name: Tank m_TagString: Untagged @@ -34,8 +35,7 @@ Transform: m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - {fileID: 7831918942946891954} - - {fileID: 6564220120147636086} - - {fileID: 5718089106632469514} + - {fileID: 4116800716706440423} m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -54,8 +54,101 @@ MonoBehaviour: sceneId: 0 serverOnly: 0 visible: 0 - m_AssetId: + m_AssetId: 6f43bf5488a7443d19ab2a83c6b91f35 hasSpawned: 0 +--- !u!114 &114250499875391520 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1916082411674582} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2f74aedd71d9a4f55b3ce499326d45fb, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 + clientAuthority: 1 + sendInterval: 0.05 + syncPosition: 1 + syncRotation: 1 + syncScale: 0 + interpolatePosition: 1 + interpolateRotation: 1 + interpolateScale: 0 + bufferTimeMultiplier: 1 + bufferSizeLimit: 64 + catchupThreshold: 4 + catchupMultiplier: 0.1 + onlySyncOnChange: 1 + bufferResetMultiplier: 5 + positionSensitivity: 0.01 + rotationSensitivity: 0.01 + scaleSensitivity: 0.01 + showGizmos: 0 + showOverlay: 0 + overlayColor: {r: 0, g: 0, b: 0, a: 0.5} +--- !u!114 &7144377311613369288 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1916082411674582} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 734b48bea0b204338958ee3d885e11f0, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 + clientAuthority: 1 + sendInterval: 0.05 + syncPosition: 0 + syncRotation: 1 + syncScale: 0 + interpolatePosition: 0 + interpolateRotation: 1 + interpolateScale: 0 + bufferTimeMultiplier: 0 + bufferSizeLimit: 64 + catchupThreshold: 3 + catchupMultiplier: 0.1 + onlySyncOnChange: 1 + bufferResetMultiplier: 5 + positionSensitivity: 0.01 + rotationSensitivity: 0.01 + scaleSensitivity: 0.01 + showGizmos: 0 + showOverlay: 0 + overlayColor: {r: 0, g: 0, b: 0, a: 0.5} + target: {fileID: 7831918942946891958} +--- !u!114 &114654712548978148 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1916082411674582} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7deadf756194d461e9140e42d651693b, type: 3} + m_Name: + m_EditorClassIdentifier: + syncMode: 0 + syncInterval: 0.1 + agent: {fileID: 6900008319038825817} + animator: {fileID: 2240606817507776182} + healthBar: {fileID: 1985504562751981867} + turret: {fileID: 7831918942946891958} + rotationSpeed: 80 + shootKey: 32 + projectilePrefab: {fileID: 5890560936853567077, guid: b7dd46dbf38c643f09e206f9fa4be008, + type: 3} + projectileMount: {fileID: 5718089106632469514} + health: 4 --- !u!95 &2240606817507776182 Animator: serializedVersion: 3 @@ -75,25 +168,6 @@ Animator: m_HasTransformHierarchy: 1 m_AllowConstantClipSamplingOptimization: 1 m_KeepAnimatorControllerStateOnDisable: 0 ---- !u!114 &114250499875391520 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1916082411674582} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 2f74aedd71d9a4f55b3ce499326d45fb, type: 3} - m_Name: - m_EditorClassIdentifier: - syncMode: 0 - syncInterval: 0.1 - clientAuthority: 1 - localPositionSensitivity: 0.01 - localRotationSensitivity: 0.01 - localScaleSensitivity: 0.01 - compressRotation: 1 --- !u!195 &6900008319038825817 NavMeshAgent: m_ObjectHideFlags: 0 @@ -129,27 +203,6 @@ SphereCollider: serializedVersion: 2 m_Radius: 0.5 m_Center: {x: 0, y: 0.25, z: 0} ---- !u!114 &114654712548978148 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1916082411674582} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 7deadf756194d461e9140e42d651693b, type: 3} - m_Name: - m_EditorClassIdentifier: - syncMode: 0 - syncInterval: 0.1 - agent: {fileID: 6900008319038825817} - animator: {fileID: 2240606817507776182} - rotationSpeed: 80 - shootKey: 32 - projectilePrefab: {fileID: 5890560936853567077, guid: b7dd46dbf38c643f09e206f9fa4be008, - type: 3} - projectileMount: {fileID: 5718089106632469514} --- !u!1 &4426914200102054949 GameObject: m_ObjectHideFlags: 0 @@ -174,12 +227,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4426914200102054949} - m_LocalRotation: {x: 0.02281505, y: -0, z: -0, w: 0.9997397} - m_LocalPosition: {x: 0.07, y: 0.46, z: 0.126} - m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalRotation: {x: 0.022814991, y: 0.000000119178246, z: -0.0000000027197586, + w: 0.9997397} + m_LocalPosition: {x: 0.00070000027, y: 0.002070647, z: 0.0012600002} + m_LocalScale: {x: 0.01, y: 0.010000003, z: 0.010000002} m_Children: [] - m_Father: {fileID: 4492442352427800} - m_RootOrder: 1 + m_Father: {fileID: 7831918942946891958} + m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 2.615, y: 0, z: 0} --- !u!108 &7604806193092689376 Light: @@ -265,13 +319,119 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4730779867780281009} + m_LocalRotation: {x: -0, y: 0.000000119209275, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0.0015906466, z: 0.009359999} + m_LocalScale: {x: 0.01, y: 0.010000003, z: 0.010000002} + m_Children: [] + m_Father: {fileID: 7831918942946891958} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &6425560216547760105 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4116800716706440423} + - component: {fileID: 1346539668293290578} + - component: {fileID: 1985504562751981867} + - component: {fileID: 4969305952766559653} + m_Layer: 0 + m_Name: HealthBar + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4116800716706440423 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6425560216547760105} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0.412, z: 0.936} + m_LocalPosition: {x: 0, y: 0.8, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 4492442352427800} - m_RootOrder: 2 + m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &1346539668293290578 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6425560216547760105} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10100, guid: 0000000000000000e000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + 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!102 &1985504562751981867 +TextMesh: + serializedVersion: 3 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6425560216547760105} + m_Text: ---- + m_OffsetZ: 0 + m_CharacterSize: 0.02 + m_LineSpacing: 1 + m_Anchor: 4 + m_Alignment: 1 + m_TabSize: 4 + m_FontSize: 200 + m_FontStyle: 1 + m_RichText: 1 + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_Color: + serializedVersion: 2 + rgba: 4285887999 +--- !u!114 &4969305952766559653 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6425560216547760105} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: afa2d590c474413d9fc183551385ed85, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1001 &7831918942947279416 PrefabInstance: m_ObjectHideFlags: 0 @@ -340,3 +500,9 @@ Transform: type: 3} m_PrefabInstance: {fileID: 7831918942947279416} m_PrefabAsset: {fileID: 0} +--- !u!4 &7831918942946891958 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 400014, guid: 38b49695fc0a4418bbc350f2366660c5, + type: 3} + m_PrefabInstance: {fileID: 7831918942947279416} + m_PrefabAsset: {fileID: 0} diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs/Tank.prefab.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs/Tank.prefab.meta index 67bcb0d..4454818 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs/Tank.prefab.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Prefabs/Tank.prefab.meta @@ -3,6 +3,6 @@ guid: 6f43bf5488a7443d19ab2a83c6b91f35 NativeFormatImporter: externalObjects: {} mainObjectFileID: 100100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Scenes.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Scenes.meta index 74874ff..5665d39 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Scenes.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Scenes.meta @@ -3,6 +3,6 @@ guid: 91225a3fc58fe4a7f9e10718cd3bdc61 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Scenes/Scene.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Scenes/Scene.meta index 533d9ce..01af532 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Scenes/Scene.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Scenes/Scene.meta @@ -3,6 +3,6 @@ guid: 81cfd31f234d94a0985fe946d2ce699f folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Scenes/Scene.unity b/UnityProject/Assets/Mirror/Examples/Tanks/Scenes/Scene.unity index 804c241..15a9630 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Scenes/Scene.unity +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Scenes/Scene.unity @@ -122,6 +122,7 @@ GameObject: m_Component: - component: {fileID: 88936777} - component: {fileID: 88936776} + - component: {fileID: 88936778} m_Layer: 0 m_Name: Main Camera m_TagString: MainCamera @@ -181,10 +182,26 @@ Transform: m_LocalRotation: {x: 0, y: 0.92387956, z: -0.38268343, w: 0} m_LocalPosition: {x: 0, y: 6.5, z: 8} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 45, y: 180, z: 0} +--- !u!114 &88936778 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 88936773} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9021b6cc314944290986ab6feb48db79, type: 3} + m_Name: + m_EditorClassIdentifier: + height: 150 + maxLogCount: 50 + hotKey: 293 --- !u!1 &251893064 GameObject: m_ObjectHideFlags: 0 diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Scenes/Scene.unity.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Scenes/Scene.unity.meta index 55dfff7..c7f922e 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Scenes/Scene.unity.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Scenes/Scene.unity.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 83f9d2fb76f5742448c6e51f258327a2 DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Scenes/Scene/NavMesh.asset.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Scenes/Scene/NavMesh.asset.meta index 6f229a8..1997fe9 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Scenes/Scene/NavMesh.asset.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Scenes/Scene/NavMesh.asset.meta @@ -3,6 +3,6 @@ guid: 0bc607fa2e315482ebe98797e844e11f NativeFormatImporter: externalObjects: {} mainObjectFileID: 23800000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Scripts.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Scripts.meta index 5cba8e5..e6be7b6 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Scripts.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Scripts.meta @@ -3,6 +3,6 @@ guid: 3815d4126f2934af187ce078c648bbc8 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/FaceCamera.cs b/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/FaceCamera.cs new file mode 100644 index 0000000..1de7c09 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/FaceCamera.cs @@ -0,0 +1,14 @@ +// Useful for Text Meshes that should face the camera. +using UnityEngine; + +namespace Mirror.Examples.Tanks +{ + public class FaceCamera : MonoBehaviour + { + // LateUpdate so that all camera updates are finished. + void LateUpdate() + { + transform.forward = Camera.main.transform.forward; + } + } +} diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/FaceCamera.cs.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/FaceCamera.cs.meta new file mode 100644 index 0000000..f4f9616 --- /dev/null +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/FaceCamera.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: afa2d590c474413d9fc183551385ed85 +timeCreated: 1632052051 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/Projectile.cs b/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/Projectile.cs index e75e156..be7b3ed 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/Projectile.cs +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/Projectile.cs @@ -4,7 +4,7 @@ namespace Mirror.Examples.Tanks { public class Projectile : NetworkBehaviour { - public float destroyAfter = 5; + public float destroyAfter = 2; public Rigidbody rigidBody; public float force = 1000; @@ -27,12 +27,9 @@ namespace Mirror.Examples.Tanks NetworkServer.Destroy(gameObject); } - // ServerCallback because we don't want a warning if OnTriggerEnter is - // called on the client + // ServerCallback because we don't want a warning + // if OnTriggerEnter is called on the client [ServerCallback] - void OnTriggerEnter(Collider co) - { - NetworkServer.Destroy(gameObject); - } + void OnTriggerEnter(Collider co) => DestroySelf(); } } diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/Projectile.cs.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/Projectile.cs.meta index 55c8d37..387ece6 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/Projectile.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/Projectile.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/Tank.cs b/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/Tank.cs index 878388f..1b856f9 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/Tank.cs +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/Tank.cs @@ -1,4 +1,3 @@ -using System; using UnityEngine; using UnityEngine.AI; @@ -9,6 +8,8 @@ namespace Mirror.Examples.Tanks [Header("Components")] public NavMeshAgent agent; public Animator animator; + public TextMesh healthBar; + public Transform turret; [Header("Movement")] public float rotationSpeed = 100; @@ -18,25 +19,35 @@ namespace Mirror.Examples.Tanks public GameObject projectilePrefab; public Transform projectileMount; + [Header("Stats")] + [SyncVar] public int health = 4; + void Update() { + // always update health bar. + // (SyncVar hook would only update on clients, not on server) + healthBar.text = new string('-', health); + // movement for local player - if (!isLocalPlayer) return; - - // rotate - float horizontal = Input.GetAxis("Horizontal"); - transform.Rotate(0, horizontal * rotationSpeed * Time.deltaTime, 0); - - // move - float vertical = Input.GetAxis("Vertical"); - Vector3 forward = transform.TransformDirection(Vector3.forward); - agent.velocity = forward * Mathf.Max(vertical, 0) * agent.speed; - animator.SetBool("Moving", agent.velocity != Vector3.zero); - - // shoot - if (Input.GetKeyDown(shootKey)) + if (isLocalPlayer) { - CmdFire(); + // rotate + float horizontal = Input.GetAxis("Horizontal"); + transform.Rotate(0, horizontal * rotationSpeed * Time.deltaTime, 0); + + // move + float vertical = Input.GetAxis("Vertical"); + Vector3 forward = transform.TransformDirection(Vector3.forward); + agent.velocity = forward * Mathf.Max(vertical, 0) * agent.speed; + animator.SetBool("Moving", agent.velocity != Vector3.zero); + + // shoot + if (Input.GetKeyDown(shootKey)) + { + CmdFire(); + } + + RotateTurret(); } } @@ -44,7 +55,7 @@ namespace Mirror.Examples.Tanks [Command] void CmdFire() { - GameObject projectile = Instantiate(projectilePrefab, projectileMount.position, transform.rotation); + GameObject projectile = Instantiate(projectilePrefab, projectileMount.position, projectileMount.rotation); NetworkServer.Spawn(projectile); RpcOnFire(); } @@ -55,5 +66,28 @@ namespace Mirror.Examples.Tanks { animator.SetTrigger("Shoot"); } + + [ServerCallback] + void OnTriggerEnter(Collider other) + { + if (other.GetComponent() != null) + { + --health; + if (health == 0) + NetworkServer.Destroy(gameObject); + } + } + + void RotateTurret() + { + Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); + RaycastHit hit; + if (Physics.Raycast(ray, out hit, 100)) + { + Debug.DrawLine(ray.origin, hit.point); + Vector3 lookRotation = new Vector3(hit.point.x, turret.transform.position.y, hit.point.z); + turret.transform.LookAt(lookRotation); + } + } } } diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/Tank.cs.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/Tank.cs.meta index 2e38a39..b7874c4 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/Tank.cs.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Scripts/Tank.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Textures.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Textures.meta index c920492..fad816b 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Textures.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Textures.meta @@ -3,6 +3,6 @@ guid: 8ce6821cc1c644d5595ce9fc1f61debc folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Textures/(Public Domain) Dirt Hand Painted Texture.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Textures/(Public Domain) Dirt Hand Painted Texture.meta index 5e1ff15..a77c1ef 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Textures/(Public Domain) Dirt Hand Painted Texture.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Textures/(Public Domain) Dirt Hand Painted Texture.meta @@ -3,6 +3,6 @@ guid: 4e08c21c1034445fc834851f9e4202fe folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Textures/(Public Domain) Dirt Hand Painted Texture/Dirt Hand Painted Texture - License.txt.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Textures/(Public Domain) Dirt Hand Painted Texture/Dirt Hand Painted Texture - License.txt.meta index fa90a4f..4682e6c 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Textures/(Public Domain) Dirt Hand Painted Texture/Dirt Hand Painted Texture - License.txt.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Textures/(Public Domain) Dirt Hand Painted Texture/Dirt Hand Painted Texture - License.txt.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 422e9f56a29ae488e836a23d1e0f21d0 TextScriptImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Textures/(Public Domain) Dirt Hand Painted Texture/Dirt.mat.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Textures/(Public Domain) Dirt Hand Painted Texture/Dirt.mat.meta index 14ca3d0..f571c8b 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Textures/(Public Domain) Dirt Hand Painted Texture/Dirt.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Textures/(Public Domain) Dirt Hand Painted Texture/Dirt.mat.meta @@ -3,6 +3,6 @@ guid: 29b49c27a74f145918356859bd7af511 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Textures/(Public Domain) Dirt Hand Painted Texture/dirt.png.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Textures/(Public Domain) Dirt Hand Painted Texture/dirt.png.meta index 626e1d6..b00a626 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Textures/(Public Domain) Dirt Hand Painted Texture/dirt.png.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Textures/(Public Domain) Dirt Hand Painted Texture/dirt.png.meta @@ -75,14 +75,14 @@ TextureImporter: outline: [] physicsShape: [] bones: [] - spriteID: '' + spriteID: vertices: [] - indices: '' + indices: edges: [] weights: [] - spritePackingTag: '' + spritePackingTag: pSDRemoveMatte: 0 pSDShowRemoveMatteOption: 0 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Examples/Tanks/Textures/ProjectileMaterial.mat.meta b/UnityProject/Assets/Mirror/Examples/Tanks/Textures/ProjectileMaterial.mat.meta index 17cce74..84c957e 100644 --- a/UnityProject/Assets/Mirror/Examples/Tanks/Textures/ProjectileMaterial.mat.meta +++ b/UnityProject/Assets/Mirror/Examples/Tanks/Textures/ProjectileMaterial.mat.meta @@ -3,6 +3,6 @@ guid: cba1b63a0bccc4b12ac25f05d0ae2dd1 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Icon.meta b/UnityProject/Assets/Mirror/Icon.meta deleted file mode 100644 index eeeb74e..0000000 --- a/UnityProject/Assets/Mirror/Icon.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: b5f1356ad059a1243910a4e82cd68c5f -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Notice.txt b/UnityProject/Assets/Mirror/Notice.txt new file mode 100644 index 0000000..5eeaf88 --- /dev/null +++ b/UnityProject/Assets/Mirror/Notice.txt @@ -0,0 +1,123 @@ +This asset is governed by the Asset Store EULA; however, the following +components are governed by the licenses indicated below: + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +Mirror +MIT License + +Copyright (c) 2015, Unity Technologies +Copyright (c) 2019, vis2k, Paul and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +kcp2k +MIT License + +Copyright (c) 2016 limpo1989 +Copyright (c) 2020 Paul Pacheco +Copyright (c) 2020 Lymdun +Copyright (c) 2020 vis2k + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +Mono.Cecil +MIT License + +Copyright (c) 2008 - 2015 Jb Evain +Copyright (c) 2008 - 2011 Novell, Inc. + +https://github.com/jbevain/cecil + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +Telepathy +MIT License + +Copyright (c) 2018, vis2k + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/version.txt.meta b/UnityProject/Assets/Mirror/Notice.txt.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/version.txt.meta rename to UnityProject/Assets/Mirror/Notice.txt.meta index bd4c745..3281bd8 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/version.txt.meta +++ b/UnityProject/Assets/Mirror/Notice.txt.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ad80075449f17c548877161f32a9841a +guid: 1a7b49ad188074707b004e7bb8824857 TextScriptImporter: externalObjects: {} userData: diff --git a/UnityProject/Assets/Mirror/Plugins.meta b/UnityProject/Assets/Mirror/Plugins.meta index 47adc9e..9504239 100644 --- a/UnityProject/Assets/Mirror/Plugins.meta +++ b/UnityProject/Assets/Mirror/Plugins.meta @@ -3,6 +3,6 @@ guid: 05eb4061e2eb94061b9a08c918fff99b folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Plugins/Mono.Cecil.meta b/UnityProject/Assets/Mirror/Plugins/Mono.Cecil.meta index bf53b8c..a104e2e 100644 --- a/UnityProject/Assets/Mirror/Plugins/Mono.Cecil.meta +++ b/UnityProject/Assets/Mirror/Plugins/Mono.Cecil.meta @@ -3,6 +3,6 @@ guid: ce126b4e1a7d13b4c865cd92929f13c3 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/License.txt.meta b/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/License.txt.meta index aa7a929..9477cb6 100644 --- a/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/License.txt.meta +++ b/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/License.txt.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: ab858db5ebbb0d542a9acd197669cb5a TextScriptImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/Mono.CecilX.Mdb.dll.meta b/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/Mono.CecilX.Mdb.dll.meta index bcec14a..d5555bf 100644 --- a/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/Mono.CecilX.Mdb.dll.meta +++ b/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/Mono.CecilX.Mdb.dll.meta @@ -23,7 +23,7 @@ PluginImporter: Exclude Win: 1 Exclude Win64: 1 - first: - Any: '' + Any: second: enabled: 0 settings: {} @@ -87,6 +87,6 @@ PluginImporter: enabled: 0 settings: CPU: AnyCPU - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/Mono.CecilX.Pdb.dll.meta b/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/Mono.CecilX.Pdb.dll.meta index bfcb622..3ab420f 100644 --- a/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/Mono.CecilX.Pdb.dll.meta +++ b/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/Mono.CecilX.Pdb.dll.meta @@ -23,7 +23,7 @@ PluginImporter: Exclude Win: 1 Exclude Win64: 1 - first: - Any: '' + Any: second: enabled: 0 settings: {} @@ -87,6 +87,6 @@ PluginImporter: enabled: 0 settings: CPU: AnyCPU - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/Mono.CecilX.Rocks.dll.meta b/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/Mono.CecilX.Rocks.dll.meta index 15e34ef..aff0237 100644 --- a/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/Mono.CecilX.Rocks.dll.meta +++ b/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/Mono.CecilX.Rocks.dll.meta @@ -23,7 +23,7 @@ PluginImporter: Exclude Win: 1 Exclude Win64: 1 - first: - Any: '' + Any: second: enabled: 0 settings: {} @@ -87,6 +87,6 @@ PluginImporter: enabled: 0 settings: CPU: AnyCPU - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/Mono.CecilX.dll.meta b/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/Mono.CecilX.dll.meta index 439068d..f87dc69 100644 --- a/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/Mono.CecilX.dll.meta +++ b/UnityProject/Assets/Mirror/Plugins/Mono.Cecil/Mono.CecilX.dll.meta @@ -23,7 +23,7 @@ PluginImporter: Exclude Win: 1 Exclude Win64: 1 - first: - Any: '' + Any: second: enabled: 0 settings: {} @@ -89,6 +89,6 @@ PluginImporter: enabled: 0 settings: CPU: AnyCPU - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Readme.txt.meta b/UnityProject/Assets/Mirror/Readme.txt.meta index 255f2e5..d52ccce 100644 --- a/UnityProject/Assets/Mirror/Readme.txt.meta +++ b/UnityProject/Assets/Mirror/Readme.txt.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: f6d84e019c68446f28415a923b460a03 TextScriptImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/AssemblyInfo.cs b/UnityProject/Assets/Mirror/Runtime/AssemblyInfo.cs index 5af6857..f342716 100644 --- a/UnityProject/Assets/Mirror/Runtime/AssemblyInfo.cs +++ b/UnityProject/Assets/Mirror/Runtime/AssemblyInfo.cs @@ -2,6 +2,9 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Mirror.Tests.Common")] [assembly: InternalsVisibleTo("Mirror.Tests")] +// need to use Unity.*.CodeGen assembly name to import Unity.CompilationPipeline +// for ILPostProcessor tests. +[assembly: InternalsVisibleTo("Unity.Mirror.Tests.CodeGen")] [assembly: InternalsVisibleTo("Mirror.Tests.Generated")] [assembly: InternalsVisibleTo("Mirror.Tests.Runtime")] [assembly: InternalsVisibleTo("Mirror.Tests.Performance.Editor")] diff --git a/UnityProject/Assets/Mirror/Runtime/AssemblyInfo.cs.meta b/UnityProject/Assets/Mirror/Runtime/AssemblyInfo.cs.meta index 728d533..50cc028 100644 --- a/UnityProject/Assets/Mirror/Runtime/AssemblyInfo.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/AssemblyInfo.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Attributes.cs.meta b/UnityProject/Assets/Mirror/Runtime/Attributes.cs.meta index 0a07632..c50a489 100644 --- a/UnityProject/Assets/Mirror/Runtime/Attributes.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Attributes.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Batching.meta b/UnityProject/Assets/Mirror/Runtime/Batching.meta new file mode 100644 index 0000000..bf23600 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Batching.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1c38e1bebe9947f8b842a8a57aa2b71c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Batching/Batcher.cs b/UnityProject/Assets/Mirror/Runtime/Batching/Batcher.cs new file mode 100644 index 0000000..3a8d457 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Batching/Batcher.cs @@ -0,0 +1,127 @@ +// batching functionality encapsulated into one class. +// -> less complexity +// -> easy to test +// +// IMPORTANT: we use THRESHOLD batching, not MAXED SIZE batching. +// see threshold comments below. +// +// includes timestamp for tick batching. +// -> allows NetworkTransform etc. to use timestamp without including it in +// every single message +using System; +using System.Collections.Generic; + +namespace Mirror +{ + public class Batcher + { + // batching threshold instead of max size. + // -> small messages are fit into threshold sized batches + // -> messages larger than threshold are single batches + // + // in other words, we fit up to 'threshold' but still allow larger ones + // for two reasons: + // 1.) data races: skipping batching for larger messages would send a + // large spawn message immediately, while others are batched and + // only flushed at the end of the frame + // 2) timestamp batching: if each batch is expected to contain a + // timestamp, then large messages have to be a batch too. otherwise + // they would not contain a timestamp + readonly int threshold; + + // TimeStamp header size for those who need it + public const int HeaderSize = sizeof(double); + + // full batches ready to be sent. + // DO NOT queue NetworkMessage, it would box. + // DO NOT queue each serialization separately. + // it would allocate too many writers. + // https://github.com/vis2k/Mirror/pull/3127 + // => best to build batches on the fly. + Queue batches = new Queue(); + + // current batch in progress + NetworkWriterPooled batch; + + public Batcher(int threshold) + { + this.threshold = threshold; + } + + // add a message for batching + // we allow any sized messages. + // caller needs to make sure they are within max packet size. + public void AddMessage(ArraySegment message, double timeStamp) + { + // when appending to a batch in progress, check final size. + // if it expands beyond threshold, then we should finalize it first. + // => less than or exactly threshold is fine. + // GetBatch() will finalize it. + // => see unit tests. + if (batch != null && + batch.Position + message.Count > threshold) + { + batches.Enqueue(batch); + batch = null; + } + + // initialize a new batch if necessary + if (batch == null) + { + // borrow from pool. we return it in GetBatch. + batch = NetworkWriterPool.Get(); + + // write timestamp first. + // -> double precision for accuracy over long periods of time + // -> batches are per-frame, it doesn't matter which message's + // timestamp we use. + batch.WriteDouble(timeStamp); + } + + // add serialization to current batch. even if > threshold. + // -> we do allow > threshold sized messages as single batch + // -> WriteBytes instead of WriteSegment because the latter + // would add a size header. we want to write directly. + batch.WriteBytes(message.Array, message.Offset, message.Count); + } + + // helper function to copy a batch to writer and return it to pool + static void CopyAndReturn(NetworkWriterPooled batch, NetworkWriter writer) + { + // make sure the writer is fresh to avoid uncertain situations + if (writer.Position != 0) + throw new ArgumentException($"GetBatch needs a fresh writer!"); + + // copy to the target writer + ArraySegment segment = batch.ToArraySegment(); + writer.WriteBytes(segment.Array, segment.Offset, segment.Count); + + // return batch to pool for reuse + NetworkWriterPool.Return(batch); + } + + // get the next batch which is available for sending (if any). + // TODO safely get & return a batch instead of copying to writer? + // TODO could return pooled writer & use GetBatch in a 'using' statement! + public bool GetBatch(NetworkWriter writer) + { + // get first batch from queue (if any) + if (batches.TryDequeue(out NetworkWriterPooled first)) + { + CopyAndReturn(first, writer); + return true; + } + + // if queue was empty, we can send the batch in progress. + if (batch != null) + { + CopyAndReturn(batch, writer); + batch = null; + return true; + } + + // nothing was written + return false; + } + } +} diff --git a/UnityProject/Assets/Mirror/Runtime/Batching/Batcher.cs.meta b/UnityProject/Assets/Mirror/Runtime/Batching/Batcher.cs.meta new file mode 100644 index 0000000..a774908 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Batching/Batcher.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0afaaa611a2142d48a07bdd03b68b2b3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Batching/Unbatcher.cs b/UnityProject/Assets/Mirror/Runtime/Batching/Unbatcher.cs new file mode 100644 index 0000000..495ada9 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Batching/Unbatcher.cs @@ -0,0 +1,142 @@ +// un-batching functionality encapsulated into one class. +// -> less complexity +// -> easy to test +// +// includes timestamp for tick batching. +// -> allows NetworkTransform etc. to use timestamp without including it in +// every single message +using System; +using System.Collections.Generic; + +namespace Mirror +{ + public class Unbatcher + { + // supporting adding multiple batches before GetNextMessage is called. + // just in case. + Queue batches = new Queue(); + + public int BatchesCount => batches.Count; + + // NetworkReader is only created once, + // then pointed to the first batch. + NetworkReader reader = new NetworkReader(new byte[0]); + + // timestamp that was written into the batch remotely. + // for the batch that our reader is currently pointed at. + double readerRemoteTimeStamp; + + // helper function to start reading a batch. + void StartReadingBatch(NetworkWriterPooled batch) + { + // point reader to it + reader.SetBuffer(batch.ToArraySegment()); + + // read remote timestamp (double) + // -> AddBatch quarantees that we have at least 8 bytes to read + readerRemoteTimeStamp = reader.ReadDouble(); + } + + // add a new batch. + // returns true if valid. + // returns false if not, in which case the connection should be disconnected. + public bool AddBatch(ArraySegment batch) + { + // IMPORTANT: ArraySegment is only valid until returning. we copy it! + // + // NOTE: it's not possible to create empty ArraySegments, so we + // don't need to check against that. + + // make sure we have at least 8 bytes to read for tick timestamp + if (batch.Count < Batcher.HeaderSize) + return false; + + // put into a (pooled) writer + // -> WriteBytes instead of WriteSegment because the latter + // would add a size header. we want to write directly. + // -> will be returned to pool when sending! + NetworkWriterPooled writer = NetworkWriterPool.Get(); + writer.WriteBytes(batch.Array, batch.Offset, batch.Count); + + // first batch? then point reader there + if (batches.Count == 0) + StartReadingBatch(writer); + + // add batch + batches.Enqueue(writer); + //Debug.Log($"Adding Batch {BitConverter.ToString(batch.Array, batch.Offset, batch.Count)} => batches={batches.Count} reader={reader}"); + return true; + } + + // get next message, unpacked from batch (if any) + // timestamp is the REMOTE time when the batch was created remotely. + public bool GetNextMessage(out NetworkReader message, out double remoteTimeStamp) + { + // getting messages would be easy via + // <> + // but to save A LOT of bandwidth, we use + // < + // in other words, we don't know where the current message ends + // + // BUT: it doesn't matter! + // -> we simply return the reader + // * if we have one yet + // * and if there's more to read + // -> the caller can then read one message from it + // -> when the end is reached, we retire the batch! + // + // for example: + // while (GetNextMessage(out message)) + // ProcessMessage(message); + // + message = null; + + // do nothing if we don't have any batches. + // otherwise the below queue.Dequeue() would throw an + // InvalidOperationException if operating on empty queue. + if (batches.Count == 0) + { + remoteTimeStamp = 0; + return false; + } + + // was our reader pointed to anything yet? + if (reader.Length == 0) + { + remoteTimeStamp = 0; + return false; + } + + // no more data to read? + if (reader.Remaining == 0) + { + // retire the batch + NetworkWriterPooled writer = batches.Dequeue(); + NetworkWriterPool.Return(writer); + + // do we have another batch? + if (batches.Count > 0) + { + // point reader to the next batch. + // we'll return the reader below. + NetworkWriterPooled next = batches.Peek(); + StartReadingBatch(next); + } + // otherwise there's nothing more to read + else + { + remoteTimeStamp = 0; + return false; + } + } + + // use the current batch's remote timestamp + // AFTER potentially moving to the next batch ABOVE! + remoteTimeStamp = readerRemoteTimeStamp; + + // if we got here, then we have more data to read. + message = reader; + return true; + } + } +} diff --git a/UnityProject/Assets/Mirror/Runtime/Batching/Unbatcher.cs.meta b/UnityProject/Assets/Mirror/Runtime/Batching/Unbatcher.cs.meta new file mode 100644 index 0000000..26038b0 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Batching/Unbatcher.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 328562d71e1c45c58581b958845aa7a4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/ClientScene.cs b/UnityProject/Assets/Mirror/Runtime/ClientScene.cs deleted file mode 100644 index 6970e11..0000000 --- a/UnityProject/Assets/Mirror/Runtime/ClientScene.cs +++ /dev/null @@ -1,95 +0,0 @@ -// moved into NetworkClient on 2021-03-07 -using System; -using System.Collections.Generic; -using UnityEngine; -using Guid = System.Guid; - -namespace Mirror -{ - // Deprecated 2021-07-03 - [Obsolete("Use NetworkClient instead")] - public static class ClientScene - { - [Obsolete("ClientScene.localPlayer was moved to NetworkClient.localPlayer")] - public static NetworkIdentity localPlayer - { - get { return NetworkClient.localPlayer; } - set { NetworkClient.localPlayer = value; } - } - - [Obsolete("ClientScene.ready was moved to NetworkClient.ready")] - public static bool ready - { - get { return NetworkClient.ready; } - set { NetworkClient.ready = value; } - } - - [Obsolete("ClientScene.readyConnection was moved to NetworkClient.readyConnection")] - public static NetworkConnection readyConnection - { - get { return NetworkClient.readyConnection; } - set { NetworkClient.connection = value; } - } - - [Obsolete("ClientScene.prefabs was moved to NetworkClient.prefabs")] - public static Dictionary prefabs => NetworkClient.prefabs; - - // add player ////////////////////////////////////////////////////////// - [Obsolete("ClientScene.AddPlayer was moved to NetworkClient.AddPlayer")] - public static bool AddPlayer(NetworkConnection readyConn) => NetworkClient.AddPlayer(readyConn); - - // ready /////////////////////////////////////////////////////////////// - [Obsolete("ClientScene.Ready was moved to NetworkClient.Ready")] - public static bool Ready(NetworkConnection conn) => NetworkClient.Ready(conn); - - [Obsolete("ClientScene.PrepareToSpawnSceneObjects was moved to NetworkClient.PrepareToSpawnSceneObjects")] - public static void PrepareToSpawnSceneObjects() => NetworkClient.PrepareToSpawnSceneObjects(); - - // spawnable prefabs /////////////////////////////////////////////////// - [Obsolete("ClientScene.GetPrefab was moved to NetworkClient.GetPrefab")] - public static bool GetPrefab(Guid assetId, out GameObject prefab) => NetworkClient.GetPrefab(assetId, out prefab); - - [Obsolete("ClientScene.RegisterPrefab was moved to NetworkClient.RegisterPrefab")] - public static void RegisterPrefab(GameObject prefab, Guid newAssetId) => NetworkClient.RegisterPrefab(prefab, newAssetId); - - [Obsolete("ClientScene.RegisterPrefab was moved to NetworkClient.RegisterPrefab")] - public static void RegisterPrefab(GameObject prefab) => NetworkClient.RegisterPrefab(prefab); - - [Obsolete("ClientScene.RegisterPrefab was moved to NetworkClient.RegisterPrefab")] - public static void RegisterPrefab(GameObject prefab, Guid newAssetId, SpawnDelegate spawnHandler, UnSpawnDelegate unspawnHandler) => - NetworkClient.RegisterPrefab(prefab, newAssetId, spawnHandler, unspawnHandler); - - [Obsolete("ClientScene.RegisterPrefab was moved to NetworkClient.RegisterPrefab")] - public static void RegisterPrefab(GameObject prefab, SpawnDelegate spawnHandler, UnSpawnDelegate unspawnHandler) => - NetworkClient.RegisterPrefab(prefab, spawnHandler, unspawnHandler); - - [Obsolete("ClientScene.RegisterPrefab was moved to NetworkClient.RegisterPrefab")] - public static void RegisterPrefab(GameObject prefab, Guid newAssetId, SpawnHandlerDelegate spawnHandler, UnSpawnDelegate unspawnHandler) => - NetworkClient.RegisterPrefab(prefab, newAssetId, spawnHandler, unspawnHandler); - - [Obsolete("ClientScene.RegisterPrefab was moved to NetworkClient.RegisterPrefab")] - public static void RegisterPrefab(GameObject prefab, SpawnHandlerDelegate spawnHandler, UnSpawnDelegate unspawnHandler) => - NetworkClient.RegisterPrefab(prefab, spawnHandler, unspawnHandler); - - [Obsolete("ClientScene.UnregisterPrefab was moved to NetworkClient.UnregisterPrefab")] - public static void UnregisterPrefab(GameObject prefab) => NetworkClient.UnregisterPrefab(prefab); - - // spawn handlers ////////////////////////////////////////////////////// - [Obsolete("ClientScene.RegisterSpawnHandler was moved to NetworkClient.RegisterSpawnHandler")] - public static void RegisterSpawnHandler(Guid assetId, SpawnDelegate spawnHandler, UnSpawnDelegate unspawnHandler) => - NetworkClient.RegisterSpawnHandler(assetId, spawnHandler, unspawnHandler); - - [Obsolete("ClientScene.RegisterSpawnHandler was moved to NetworkClient.RegisterSpawnHandler")] - public static void RegisterSpawnHandler(Guid assetId, SpawnHandlerDelegate spawnHandler, UnSpawnDelegate unspawnHandler) => - NetworkClient.RegisterSpawnHandler(assetId, spawnHandler, unspawnHandler); - - [Obsolete("ClientScene.UnregisterSpawnHandler was moved to NetworkClient.UnregisterSpawnHandler")] - public static void UnregisterSpawnHandler(Guid assetId) => NetworkClient.UnregisterSpawnHandler(assetId); - - [Obsolete("ClientScene.ClearSpawners was moved to NetworkClient.ClearSpawners")] - public static void ClearSpawners() => NetworkClient.ClearSpawners(); - - [Obsolete("ClientScene.DestroyAllClientObjects was moved to NetworkClient.DestroyAllClientObjects")] - public static void DestroyAllClientObjects() => NetworkClient.DestroyAllClientObjects(); - } -} diff --git a/UnityProject/Assets/Mirror/Runtime/Compression.cs b/UnityProject/Assets/Mirror/Runtime/Compression.cs index 0077686..3e4b0f6 100644 --- a/UnityProject/Assets/Mirror/Runtime/Compression.cs +++ b/UnityProject/Assets/Mirror/Runtime/Compression.cs @@ -1,5 +1,6 @@ // Quaternion compression from DOTSNET - +using System; +using System.Runtime.CompilerServices; using UnityEngine; namespace Mirror @@ -13,40 +14,40 @@ namespace Mirror // helper function to find largest absolute element // returns the index of the largest one - public static int LargestAbsoluteComponentIndex(Vector4 value, out float largest, out Vector3 withoutLargest) + public static int LargestAbsoluteComponentIndex(Vector4 value, out float largestAbs, out Vector3 withoutLargest) { // convert to abs Vector4 abs = new Vector4(Mathf.Abs(value.x), Mathf.Abs(value.y), Mathf.Abs(value.z), Mathf.Abs(value.w)); - // set largest to first value (x) - largest = value.x; + // set largest to first abs (x) + largestAbs = abs.x; withoutLargest = new Vector3(value.y, value.z, value.w); - int index = 0; + int largestIndex = 0; // compare to the others, starting at second value // performance for 100k calls // for-loop: 25ms // manual checks: 22ms - if (abs.y > largest) + if (abs.y > largestAbs) { - index = 1; - largest = abs.y; + largestIndex = 1; + largestAbs = abs.y; withoutLargest = new Vector3(value.x, value.z, value.w); } - if (abs.z > largest) + if (abs.z > largestAbs) { - index = 2; - largest = abs.z; + largestIndex = 2; + largestAbs = abs.z; withoutLargest = new Vector3(value.x, value.y, value.w); } - if (abs.w > largest) + if (abs.w > largestAbs) { - index = 3; - largest = abs.w; + largestIndex = 3; + largestAbs = abs.w; withoutLargest = new Vector3(value.x, value.y, value.z); } - return index; + return largestIndex; } // scale a float within min/max range to an ushort between min/max range @@ -77,6 +78,7 @@ namespace Mirror const ushort TenBitsMax = 0x3FF; // helper function to access 'nth' component of quaternion + [MethodImpl(MethodImplOptions.AggressiveInlining)] static float QuaternionElement(Quaternion q, int element) { switch (element) @@ -137,6 +139,7 @@ namespace Mirror // Quaternion normalizeSAFE from ECS math.normalizesafe() // => useful to produce valid quaternions even if client sends invalid // data + [MethodImpl(MethodImplOptions.AggressiveInlining)] static Quaternion QuaternionNormalizeSafe(Quaternion value) { // The smallest positive normal number representable in a float. @@ -189,5 +192,170 @@ namespace Mirror // in NaN from deserializing invalid values! return QuaternionNormalizeSafe(new Quaternion(value.x, value.y, value.z, value.w)); } + + // varint compression ////////////////////////////////////////////////// + // compress ulong varint. + // same result for int, short and byte. only need one function. + // NOT an extension. otherwise weaver might accidentally use it. + public static void CompressVarUInt(NetworkWriter writer, ulong value) + { + if (value <= 240) + { + writer.WriteByte((byte)value); + return; + } + if (value <= 2287) + { + writer.WriteByte((byte)(((value - 240) >> 8) + 241)); + writer.WriteByte((byte)((value - 240) & 0xFF)); + return; + } + if (value <= 67823) + { + writer.WriteByte((byte)249); + writer.WriteByte((byte)((value - 2288) >> 8)); + writer.WriteByte((byte)((value - 2288) & 0xFF)); + return; + } + if (value <= 16777215) + { + writer.WriteByte((byte)250); + writer.WriteByte((byte)(value & 0xFF)); + writer.WriteByte((byte)((value >> 8) & 0xFF)); + writer.WriteByte((byte)((value >> 16) & 0xFF)); + return; + } + if (value <= 4294967295) + { + writer.WriteByte((byte)251); + writer.WriteByte((byte)(value & 0xFF)); + writer.WriteByte((byte)((value >> 8) & 0xFF)); + writer.WriteByte((byte)((value >> 16) & 0xFF)); + writer.WriteByte((byte)((value >> 24) & 0xFF)); + return; + } + if (value <= 1099511627775) + { + writer.WriteByte((byte)252); + writer.WriteByte((byte)(value & 0xFF)); + writer.WriteByte((byte)((value >> 8) & 0xFF)); + writer.WriteByte((byte)((value >> 16) & 0xFF)); + writer.WriteByte((byte)((value >> 24) & 0xFF)); + writer.WriteByte((byte)((value >> 32) & 0xFF)); + return; + } + if (value <= 281474976710655) + { + writer.WriteByte((byte)253); + writer.WriteByte((byte)(value & 0xFF)); + writer.WriteByte((byte)((value >> 8) & 0xFF)); + writer.WriteByte((byte)((value >> 16) & 0xFF)); + writer.WriteByte((byte)((value >> 24) & 0xFF)); + writer.WriteByte((byte)((value >> 32) & 0xFF)); + writer.WriteByte((byte)((value >> 40) & 0xFF)); + return; + } + if (value <= 72057594037927935) + { + writer.WriteByte((byte)254); + writer.WriteByte((byte)(value & 0xFF)); + writer.WriteByte((byte)((value >> 8) & 0xFF)); + writer.WriteByte((byte)((value >> 16) & 0xFF)); + writer.WriteByte((byte)((value >> 24) & 0xFF)); + writer.WriteByte((byte)((value >> 32) & 0xFF)); + writer.WriteByte((byte)((value >> 40) & 0xFF)); + writer.WriteByte((byte)((value >> 48) & 0xFF)); + return; + } + + // all others + { + writer.WriteByte((byte)255); + writer.WriteByte((byte)(value & 0xFF)); + writer.WriteByte((byte)((value >> 8) & 0xFF)); + writer.WriteByte((byte)((value >> 16) & 0xFF)); + writer.WriteByte((byte)((value >> 24) & 0xFF)); + writer.WriteByte((byte)((value >> 32) & 0xFF)); + writer.WriteByte((byte)((value >> 40) & 0xFF)); + writer.WriteByte((byte)((value >> 48) & 0xFF)); + writer.WriteByte((byte)((value >> 56) & 0xFF)); + } + } + + // zigzag encoding https://gist.github.com/mfuerstenau/ba870a29e16536fdbaba + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CompressVarInt(NetworkWriter writer, long i) + { + ulong zigzagged = (ulong)((i >> 63) ^ (i << 1)); + CompressVarUInt(writer, zigzagged); + } + + // NOT an extension. otherwise weaver might accidentally use it. + public static ulong DecompressVarUInt(NetworkReader reader) + { + byte a0 = reader.ReadByte(); + if (a0 < 241) + { + return a0; + } + + byte a1 = reader.ReadByte(); + if (a0 <= 248) + { + return 240 + ((a0 - (ulong)241) << 8) + a1; + } + + byte a2 = reader.ReadByte(); + if (a0 == 249) + { + return 2288 + ((ulong)a1 << 8) + a2; + } + + byte a3 = reader.ReadByte(); + if (a0 == 250) + { + return a1 + (((ulong)a2) << 8) + (((ulong)a3) << 16); + } + + byte a4 = reader.ReadByte(); + if (a0 == 251) + { + return a1 + (((ulong)a2) << 8) + (((ulong)a3) << 16) + (((ulong)a4) << 24); + } + + byte a5 = reader.ReadByte(); + if (a0 == 252) + { + return a1 + (((ulong)a2) << 8) + (((ulong)a3) << 16) + (((ulong)a4) << 24) + (((ulong)a5) << 32); + } + + byte a6 = reader.ReadByte(); + if (a0 == 253) + { + return a1 + (((ulong)a2) << 8) + (((ulong)a3) << 16) + (((ulong)a4) << 24) + (((ulong)a5) << 32) + (((ulong)a6) << 40); + } + + byte a7 = reader.ReadByte(); + if (a0 == 254) + { + return a1 + (((ulong)a2) << 8) + (((ulong)a3) << 16) + (((ulong)a4) << 24) + (((ulong)a5) << 32) + (((ulong)a6) << 40) + (((ulong)a7) << 48); + } + + byte a8 = reader.ReadByte(); + if (a0 == 255) + { + return a1 + (((ulong)a2) << 8) + (((ulong)a3) << 16) + (((ulong)a4) << 24) + (((ulong)a5) << 32) + (((ulong)a6) << 40) + (((ulong)a7) << 48) + (((ulong)a8) << 56); + } + + throw new IndexOutOfRangeException($"DecompressVarInt failure: {a0}"); + } + + // zigzag decoding https://gist.github.com/mfuerstenau/ba870a29e16536fdbaba + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long DecompressVarInt(NetworkReader reader) + { + ulong data = DecompressVarUInt(reader); + return ((long)(data >> 1)) ^ -((long)data & 1); + } } } diff --git a/UnityProject/Assets/Mirror/Runtime/Compression.cs.meta b/UnityProject/Assets/Mirror/Runtime/Compression.cs.meta index c24622d..e35474b 100644 --- a/UnityProject/Assets/Mirror/Runtime/Compression.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Compression.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty.meta b/UnityProject/Assets/Mirror/Runtime/Empty.meta index 43901a4..e702402 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty.meta @@ -1,3 +1,3 @@ -fileFormatVersion: 2 +fileFormatVersion: 2 guid: a99666a026b14cf6ba1a2b65946b1b27 -timeCreated: 1615288671 +timeCreated: 1615288671 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/ClientScene.cs b/UnityProject/Assets/Mirror/Runtime/Empty/ClientScene.cs new file mode 100644 index 0000000..0d1b96e --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Empty/ClientScene.cs @@ -0,0 +1 @@ +// moved into NetworkClient on 2021-03-07 diff --git a/UnityProject/Assets/Mirror/Runtime/ClientScene.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/ClientScene.cs.meta similarity index 79% rename from UnityProject/Assets/Mirror/Runtime/ClientScene.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Empty/ClientScene.cs.meta index 0da7dbc..82b617e 100644 --- a/UnityProject/Assets/Mirror/Runtime/ClientScene.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/ClientScene.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud.meta index f2c04e4..e2c44de 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud.meta @@ -3,6 +3,6 @@ guid: 73a9bb2dacafa8141bce8feef34e33a7 folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ApiConnector.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ApiConnector.cs.meta index 27dc6c9..9279c0c 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ApiConnector.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ApiConnector.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ApiUpdater.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ApiUpdater.cs.meta index 5c4e761..98a4c11 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ApiUpdater.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ApiUpdater.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Ball.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Ball.cs.meta index dd62ea2..a6fc272 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Ball.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Ball.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/BallManager.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/BallManager.cs.meta index dbb2967..b914a33 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/BallManager.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/BallManager.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/BaseApi.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/BaseApi.cs.meta index 9f10766..f66b84e 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/BaseApi.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/BaseApi.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Events.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Events.cs.meta index e2da2f9..150d85b 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Events.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Events.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Extensions.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Extensions.cs.meta index d22d7c6..6bf6291 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Extensions.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Extensions.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ICoroutineRunner.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ICoroutineRunner.cs.meta index 9ee9c8a..f1149a9 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ICoroutineRunner.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ICoroutineRunner.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/IRequestCreator.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/IRequestCreator.cs.meta index 61c4658..966c503 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/IRequestCreator.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/IRequestCreator.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/IUnityEqualCheck.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/IUnityEqualCheck.cs.meta index be2f996..7cb2a59 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/IUnityEqualCheck.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/IUnityEqualCheck.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/InstantiateNetworkManager.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/InstantiateNetworkManager.cs.meta index 52832d3..4b7219b 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/InstantiateNetworkManager.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/InstantiateNetworkManager.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/JsonStructs.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/JsonStructs.cs.meta index beab7e7..2c04009 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/JsonStructs.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/JsonStructs.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServer.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServer.cs.meta index f79ac5b..519876d 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServer.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServer.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServerBaseApi.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServerBaseApi.cs.meta index 55dd322..a9d32ea 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServerBaseApi.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServerBaseApi.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServerClientApi.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServerClientApi.cs.meta index 43c3e4d..306bf7c 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServerClientApi.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServerClientApi.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServerJson.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServerJson.cs.meta index a863c65..7e206f1 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServerJson.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServerJson.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServerServerApi.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServerServerApi.cs.meta index a8db619..82e23fd 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServerServerApi.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ListServerServerApi.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Logger.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Logger.cs.meta index 4141280..5984ce3 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Logger.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Logger.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/NetworkManagerListServer.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/NetworkManagerListServer.cs.meta index 1a4791f..86775df 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/NetworkManagerListServer.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/NetworkManagerListServer.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/NetworkManagerListServerPong.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/NetworkManagerListServerPong.cs.meta index ca3708c..5c4294f 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/NetworkManagerListServerPong.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/NetworkManagerListServerPong.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Player.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Player.cs.meta index a8ada01..1c85828 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Player.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/Player.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/QuickListServerDebug.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/QuickListServerDebug.cs.meta index 3db6d15..4a22565 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/QuickListServerDebug.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/QuickListServerDebug.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/QuitButtonHUD.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/QuitButtonHUD.cs.meta index 96ea79b..67341ea 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/QuitButtonHUD.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/QuitButtonHUD.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/RequestCreator.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/RequestCreator.cs.meta index 654cb83..eb139af 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/RequestCreator.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/RequestCreator.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ServerListManager.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ServerListManager.cs.meta index 77644bd..74c6a0f 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ServerListManager.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ServerListManager.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ServerListUI.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ServerListUI.cs.meta index 438be38..f7fe4f2 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ServerListUI.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ServerListUI.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ServerListUIItem.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ServerListUIItem.cs.meta index cd43714..d8857e8 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ServerListUIItem.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Cloud/ServerListUIItem.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/DotNetCompatibility.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/DotNetCompatibility.cs.meta index b1bda33..8742197 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/DotNetCompatibility.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/DotNetCompatibility.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/FallbackTransport.cs b/UnityProject/Assets/Mirror/Runtime/Empty/FallbackTransport.cs new file mode 100644 index 0000000..57f3344 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Empty/FallbackTransport.cs @@ -0,0 +1 @@ +// removed 2021-05-13 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/FallbackTransport.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/FallbackTransport.cs.meta similarity index 79% rename from UnityProject/Assets/Mirror/Runtime/Transport/FallbackTransport.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Empty/FallbackTransport.cs.meta index f4f199e..509a58f 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/FallbackTransport.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/FallbackTransport.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/LogFactory.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/LogFactory.cs.meta index 741b217..0715501 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/LogFactory.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/LogFactory.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/LogFilter.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/LogFilter.cs.meta index b34efa6..aab4fa0 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/LogFilter.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/LogFilter.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Logging.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Logging.meta index 927da1d..867da74 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Logging.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Logging.meta @@ -3,6 +3,6 @@ guid: 63d647500ca1bfa4a845bc1f4cff9dcc folderAsset: yes DefaultImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Logging/ConsoleColorLogHandler.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Logging/ConsoleColorLogHandler.cs.meta index cff4b2d..329c6eb 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Logging/ConsoleColorLogHandler.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Logging/ConsoleColorLogHandler.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Logging/EditorLogSettingsLoader.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Logging/EditorLogSettingsLoader.cs.meta index 4db0d88..81b33e9 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Logging/EditorLogSettingsLoader.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Logging/EditorLogSettingsLoader.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Logging/LogFactory.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Logging/LogFactory.cs.meta index 884703c..acf3b63 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Logging/LogFactory.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Logging/LogFactory.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Logging/LogSettings.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Logging/LogSettings.cs.meta index c8169a8..90c4e4d 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Logging/LogSettings.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Logging/LogSettings.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Logging/NetworkHeadlessLogger.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Logging/NetworkHeadlessLogger.cs.meta index d855af4..221a61b 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Logging/NetworkHeadlessLogger.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Logging/NetworkHeadlessLogger.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/Logging/NetworkLogSettings.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/Logging/NetworkLogSettings.cs.meta index 48f84eb..2f7ecdf 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/Logging/NetworkLogSettings.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/Logging/NetworkLogSettings.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/NetworkMatchChecker.cs b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkMatchChecker.cs new file mode 100644 index 0000000..3797620 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkMatchChecker.cs @@ -0,0 +1 @@ +// removed 2022-01-06 diff --git a/UnityProject/Assets/Mirror/Components/NetworkMatchChecker.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkMatchChecker.cs.meta similarity index 79% rename from UnityProject/Assets/Mirror/Components/NetworkMatchChecker.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Empty/NetworkMatchChecker.cs.meta index adf2a61..7c7d6cf 100644 --- a/UnityProject/Assets/Mirror/Components/NetworkMatchChecker.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkMatchChecker.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/NetworkOwnerChecker.cs b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkOwnerChecker.cs new file mode 100644 index 0000000..712833c --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkOwnerChecker.cs @@ -0,0 +1 @@ +// removed 2022-01-06 diff --git a/UnityProject/Assets/Mirror/Components/NetworkOwnerChecker.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkOwnerChecker.cs.meta similarity index 79% rename from UnityProject/Assets/Mirror/Components/NetworkOwnerChecker.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Empty/NetworkOwnerChecker.cs.meta index 060154e..fee7725 100644 --- a/UnityProject/Assets/Mirror/Components/NetworkOwnerChecker.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkOwnerChecker.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/NetworkProximityChecker.cs b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkProximityChecker.cs new file mode 100644 index 0000000..3797620 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkProximityChecker.cs @@ -0,0 +1 @@ +// removed 2022-01-06 diff --git a/UnityProject/Assets/Mirror/Components/NetworkProximityChecker.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkProximityChecker.cs.meta similarity index 79% rename from UnityProject/Assets/Mirror/Components/NetworkProximityChecker.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Empty/NetworkProximityChecker.cs.meta index 0131b73..c5aa112 100644 --- a/UnityProject/Assets/Mirror/Components/NetworkProximityChecker.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkProximityChecker.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/NetworkSceneChecker.cs b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkSceneChecker.cs new file mode 100644 index 0000000..3797620 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkSceneChecker.cs @@ -0,0 +1 @@ +// removed 2022-01-06 diff --git a/UnityProject/Assets/Mirror/Components/NetworkSceneChecker.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkSceneChecker.cs.meta similarity index 79% rename from UnityProject/Assets/Mirror/Components/NetworkSceneChecker.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Empty/NetworkSceneChecker.cs.meta index f81ad4f..b451655 100644 --- a/UnityProject/Assets/Mirror/Components/NetworkSceneChecker.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkSceneChecker.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/NetworkVisibility.cs b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkVisibility.cs new file mode 100644 index 0000000..3797620 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkVisibility.cs @@ -0,0 +1 @@ +// removed 2022-01-06 diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkVisibility.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkVisibility.cs.meta similarity index 79% rename from UnityProject/Assets/Mirror/Runtime/NetworkVisibility.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Empty/NetworkVisibility.cs.meta index 9e3d280..f71b7be 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkVisibility.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/NetworkVisibility.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Empty/StringHash.cs.meta b/UnityProject/Assets/Mirror/Runtime/Empty/StringHash.cs.meta index b9757b4..6198581 100644 --- a/UnityProject/Assets/Mirror/Runtime/Empty/StringHash.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Empty/StringHash.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/ExponentialMovingAverage.cs b/UnityProject/Assets/Mirror/Runtime/ExponentialMovingAverage.cs index 9463168..a6f6849 100644 --- a/UnityProject/Assets/Mirror/Runtime/ExponentialMovingAverage.cs +++ b/UnityProject/Assets/Mirror/Runtime/ExponentialMovingAverage.cs @@ -1,20 +1,29 @@ +// N-day EMA implementation from Mirror with a few changes (struct etc.) +// it calculates an exponential moving average roughly equivalent to the last n observations +// https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average +using System; + namespace Mirror { - // implementation of N-day EMA - // it calculates an exponential moving average roughly equivalent to the last n observations - // https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average - public class ExponentialMovingAverage + public struct ExponentialMovingAverage { readonly float alpha; bool initialized; - public double Value { get; private set; } - public double Var { get; private set; } + public double Value; + public double Variance; + [Obsolete("Var was renamed to Variance")] // 2022-06-17 + public double Var => Variance; + public double StandardDeviation; // absolute value, see test public ExponentialMovingAverage(int n) { // standard N-day EMA alpha calculation alpha = 2.0f / (n + 1); + initialized = false; + Value = 0; + Variance = 0; + StandardDeviation = 0; } public void Add(double newValue) @@ -25,7 +34,8 @@ namespace Mirror { double delta = newValue - Value; Value += alpha * delta; - Var = (1 - alpha) * (Var + alpha * delta * delta); + Variance = (1 - alpha) * (Variance + alpha * delta * delta); + StandardDeviation = Math.Sqrt(Variance); } else { diff --git a/UnityProject/Assets/Mirror/Runtime/ExponentialMovingAverage.cs.meta b/UnityProject/Assets/Mirror/Runtime/ExponentialMovingAverage.cs.meta index 3354532..d0d8210 100644 --- a/UnityProject/Assets/Mirror/Runtime/ExponentialMovingAverage.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/ExponentialMovingAverage.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Extensions.cs b/UnityProject/Assets/Mirror/Runtime/Extensions.cs index 6f384d1..0f6f62d 100644 --- a/UnityProject/Assets/Mirror/Runtime/Extensions.cs +++ b/UnityProject/Assets/Mirror/Runtime/Extensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; namespace Mirror { @@ -31,10 +32,26 @@ namespace Mirror // helper function to copy to List // C# only provides CopyTo(T[]) + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CopyTo(this IEnumerable source, List destination) { // foreach allocates. use AddRange. destination.AddRange(source); } + +#if !UNITY_2021_OR_NEWER + // Unity 2020 and earlier doesn't have Queue.TryDequeue which we need for batching. + public static bool TryDequeue(this Queue source, out T element) + { + if (source.Count > 0) + { + element = source.Dequeue(); + return true; + } + + element = default; + return false; + } +#endif } } diff --git a/UnityProject/Assets/Mirror/Runtime/Extensions.cs.meta b/UnityProject/Assets/Mirror/Runtime/Extensions.cs.meta index dc46fce..c2a18b7 100644 --- a/UnityProject/Assets/Mirror/Runtime/Extensions.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Extensions.cs.meta @@ -1,4 +1,4 @@ -fileFormatVersion: 2 +fileFormatVersion: 2 guid: decf32fd053744d18f35712b7a6f5116 MonoImporter: externalObjects: {} @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/InterestManagement.cs b/UnityProject/Assets/Mirror/Runtime/InterestManagement.cs index a2d36aa..ab149c3 100644 --- a/UnityProject/Assets/Mirror/Runtime/InterestManagement.cs +++ b/UnityProject/Assets/Mirror/Runtime/InterestManagement.cs @@ -6,9 +6,12 @@ using UnityEngine; namespace Mirror { [DisallowMultipleComponent] + [HelpURL("https://mirror-networking.gitbook.io/docs/guides/interest-management")] public abstract class InterestManagement : MonoBehaviour { - // Awake configures InterestManagement in NetworkServer + // Awake configures InterestManagement in NetworkServer/Client + // Do NOT check for active server or client here. + // Awake must always set the static aoi references. void Awake() { if (NetworkServer.aoi == null) @@ -16,14 +19,23 @@ namespace Mirror NetworkServer.aoi = this; } else Debug.LogError($"Only one InterestManagement component allowed. {NetworkServer.aoi.GetType()} has been set up already."); + + if (NetworkClient.aoi == null) + { + NetworkClient.aoi = this; + } + else Debug.LogError($"Only one InterestManagement component allowed. {NetworkClient.aoi.GetType()} has been set up already."); } + [ServerCallback] + public virtual void Reset() {} + // Callback used by the visibility system to determine if an observer // (player) can see the NetworkIdentity. If this function returns true, // the network connection will be added as an observer. // conn: Network connection of a player. // returns True if the player can see this object. - public abstract bool OnCheckObserver(NetworkIdentity identity, NetworkConnection newObserver); + public abstract bool OnCheckObserver(NetworkIdentity identity, NetworkConnectionToClient newObserver); // rebuild observers for the given NetworkIdentity. // Server will automatically spawn/despawn added/removed ones. @@ -42,7 +54,7 @@ namespace Mirror // // Mirror maintains .observing automatically in the background. best of // both worlds without any worrying now! - public abstract void OnRebuildObservers(NetworkIdentity identity, HashSet newObservers, bool initialize); + public abstract void OnRebuildObservers(NetworkIdentity identity, HashSet newObservers); // helper function to trigger a full rebuild. // most implementations should call this in a certain interval. @@ -50,12 +62,38 @@ namespace Mirror // scene changes and so on. // // IMPORTANT: check if NetworkServer.active when using Update()! + [ServerCallback] protected void RebuildAll() { - foreach (NetworkIdentity identity in NetworkIdentity.spawned.Values) + foreach (NetworkIdentity identity in NetworkServer.spawned.Values) { NetworkServer.RebuildObservers(identity, false); } } + + // Callback used by the visibility system for objects on a host. + // Objects on a host (with a local client) cannot be disabled or + // destroyed when they are not visible to the local client. So this + // function is called to allow custom code to hide these objects. A + // typical implementation will disable renderer components on the + // object. This is only called on local clients on a host. + // => need the function in here and virtual so people can overwrite! + // => not everyone wants to hide renderers! + [ServerCallback] + public virtual void SetHostVisibility(NetworkIdentity identity, bool visible) + { + foreach (Renderer rend in identity.GetComponentsInChildren()) + rend.enabled = visible; + } + + /// Called on the server when a new networked object is spawned. + // (useful for 'only rebuild if changed' interest management algorithms) + [ServerCallback] + public virtual void OnSpawned(NetworkIdentity identity) {} + + /// Called on the server when a networked object is destroyed. + // (useful for 'only rebuild if changed' interest management algorithms) + [ServerCallback] + public virtual void OnDestroyed(NetworkIdentity identity) {} } } diff --git a/UnityProject/Assets/Mirror/Runtime/InterestManagement.cs.meta b/UnityProject/Assets/Mirror/Runtime/InterestManagement.cs.meta index 849cedb..bfabf6b 100644 --- a/UnityProject/Assets/Mirror/Runtime/InterestManagement.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/InterestManagement.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/LocalConnectionToClient.cs b/UnityProject/Assets/Mirror/Runtime/LocalConnectionToClient.cs new file mode 100644 index 0000000..67c9649 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/LocalConnectionToClient.cs @@ -0,0 +1,47 @@ +using System; + +namespace Mirror +{ + // a server's connection TO a LocalClient. + // sending messages on this connection causes the client's handler function to be invoked directly + public class LocalConnectionToClient : NetworkConnectionToClient + { + internal LocalConnectionToServer connectionToServer; + + public LocalConnectionToClient() : base(LocalConnectionId) {} + + public override string address => "localhost"; + + // Send stage two: serialized NetworkMessage as ArraySegment + internal override void Send(ArraySegment segment, int channelId = Channels.Reliable) + { + // get a writer to copy the message into since the segment is only + // valid until returning. + // => pooled writer will be returned to pool when dequeuing. + // => WriteBytes instead of WriteArraySegment because the latter + // includes a 4 bytes header. we just want to write raw. + //Debug.Log($"Enqueue {BitConverter.ToString(segment.Array, segment.Offset, segment.Count)}"); + NetworkWriterPooled writer = NetworkWriterPool.Get(); + writer.WriteBytes(segment.Array, segment.Offset, segment.Count); + connectionToServer.queue.Enqueue(writer); + } + + // true because local connections never timeout + internal override bool IsAlive(float timeout) => true; + + internal void DisconnectInternal() + { + // set not ready and handle clientscene disconnect in any case + // (might be client or host mode here) + isReady = false; + RemoveFromObservingsObservers(); + } + + /// Disconnects this connection. + public override void Disconnect() + { + DisconnectInternal(); + connectionToServer.DisconnectInternal(); + } + } +} diff --git a/UnityProject/Assets/Mirror/Runtime/LocalConnections.cs.meta b/UnityProject/Assets/Mirror/Runtime/LocalConnectionToClient.cs.meta similarity index 79% rename from UnityProject/Assets/Mirror/Runtime/LocalConnections.cs.meta rename to UnityProject/Assets/Mirror/Runtime/LocalConnectionToClient.cs.meta index ef61c3d..a4f2d2c 100644 --- a/UnityProject/Assets/Mirror/Runtime/LocalConnections.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/LocalConnectionToClient.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/LocalConnections.cs b/UnityProject/Assets/Mirror/Runtime/LocalConnectionToServer.cs similarity index 50% rename from UnityProject/Assets/Mirror/Runtime/LocalConnections.cs rename to UnityProject/Assets/Mirror/Runtime/LocalConnectionToServer.cs index 0f06b68..378ffdb 100644 --- a/UnityProject/Assets/Mirror/Runtime/LocalConnections.cs +++ b/UnityProject/Assets/Mirror/Runtime/LocalConnectionToServer.cs @@ -4,56 +4,14 @@ using UnityEngine; namespace Mirror { - // a server's connection TO a LocalClient. - // sending messages on this connection causes the client's handler function to be invoked directly - class LocalConnectionToClient : NetworkConnectionToClient - { - internal LocalConnectionToServer connectionToServer; - - public LocalConnectionToClient() : base(LocalConnectionId, false) {} - - public override string address => "localhost"; - - internal override void Send(ArraySegment segment, int channelId = Channels.Reliable) - { - // get a writer to copy the message into since the segment is only - // valid until returning. - // => pooled writer will be returned to pool when dequeuing. - // => WriteBytes instead of WriteArraySegment because the latter - // includes a 4 bytes header. we just want to write raw. - //Debug.Log("Enqueue " + BitConverter.ToString(segment.Array, segment.Offset, segment.Count)); - PooledNetworkWriter writer = NetworkWriterPool.GetWriter(); - writer.WriteBytes(segment.Array, segment.Offset, segment.Count); - connectionToServer.queue.Enqueue(writer); - } - - // true because local connections never timeout - internal override bool IsAlive(float timeout) => true; - - internal void DisconnectInternal() - { - // set not ready and handle clientscene disconnect in any case - // (might be client or host mode here) - isReady = false; - RemoveFromObservingsObservers(); - } - - /// Disconnects this connection. - public override void Disconnect() - { - DisconnectInternal(); - connectionToServer.DisconnectInternal(); - } - } - // a localClient's connection TO a server. // send messages on this connection causes the server's handler function to be invoked directly. - internal class LocalConnectionToServer : NetworkConnectionToServer + public class LocalConnectionToServer : NetworkConnectionToServer { internal LocalConnectionToClient connectionToClient; // packet queue - internal readonly Queue queue = new Queue(); + internal readonly Queue queue = new Queue(); public override string address => "localhost"; @@ -63,6 +21,7 @@ namespace Mirror internal void QueueConnectedEvent() => connectedEventPending = true; internal void QueueDisconnectedEvent() => disconnectedEventPending = true; + // Send stage two: serialized NetworkMessage as ArraySegment internal override void Send(ArraySegment segment, int channelId = Channels.Reliable) { if (segment.Count == 0) @@ -71,12 +30,28 @@ namespace Mirror return; } - // handle the server's message directly - NetworkServer.OnTransportData(connectionId, segment, channelId); + // OnTransportData assumes batching. + // so let's make a batch with proper timestamp prefix. + Batcher batcher = GetBatchForChannelId(channelId); + batcher.AddMessage(segment, NetworkTime.localTime); + + // flush it to the server's OnTransportData immediately. + // local connection to server always invokes immediately. + using (NetworkWriterPooled writer = NetworkWriterPool.Get()) + { + // make a batch with our local time (double precision) + if (batcher.GetBatch(writer)) + { + NetworkServer.OnTransportData(connectionId, writer.ToArraySegment(), channelId); + } + else Debug.LogError("Local connection failed to make batch. This should never happen."); + } } - internal void Update() + internal override void Update() { + base.Update(); + // should we still process a connected event? if (connectedEventPending) { @@ -88,11 +63,24 @@ namespace Mirror while (queue.Count > 0) { // call receive on queued writer's content, return to pool - PooledNetworkWriter writer = queue.Dequeue(); - ArraySegment segment = writer.ToArraySegment(); - //Debug.Log("Dequeue " + BitConverter.ToString(segment.Array, segment.Offset, segment.Count)); - NetworkClient.OnTransportData(segment, Channels.Reliable); - NetworkWriterPool.Recycle(writer); + NetworkWriterPooled writer = queue.Dequeue(); + ArraySegment message = writer.ToArraySegment(); + + // OnTransportData assumes a proper batch with timestamp etc. + // let's make a proper batch and pass it to OnTransportData. + Batcher batcher = GetBatchForChannelId(Channels.Reliable); + batcher.AddMessage(message, NetworkTime.localTime); + + using (NetworkWriterPooled batchWriter = NetworkWriterPool.Get()) + { + // make a batch with our local time (double precision) + if (batcher.GetBatch(batchWriter)) + { + NetworkClient.OnTransportData(batchWriter.ToArraySegment(), Channels.Reliable); + } + } + + NetworkWriterPool.Return(writer); } // should we still process a disconnected event? @@ -119,11 +107,17 @@ namespace Mirror connectionToClient.DisconnectInternal(); DisconnectInternal(); - // this was in NetworkClient.Disconnect 'if isLocalConnection' before - // but it's clearly local connection related, so put it in here. + // simulate what a true remote connection would do: + // first, the server should remove it: // TODO should probably be in connectionToClient.DisconnectInternal // because that's the NetworkServer's connection! NetworkServer.RemoveLocalConnection(); + + // then call OnTransportDisconnected for proper disconnect handling, + // callbacks & cleanups. + // => otherwise OnClientDisconnected() is never called! + // => see NetworkClientTests.DisconnectCallsOnClientDisconnect_HostMode() + NetworkClient.OnTransportDisconnected(); } // true because local connections never timeout diff --git a/UnityProject/Assets/Mirror/Runtime/LocalConnectionToServer.cs.meta b/UnityProject/Assets/Mirror/Runtime/LocalConnectionToServer.cs.meta new file mode 100644 index 0000000..856b255 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/LocalConnectionToServer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cdfff390c3504158a269e8b8662e2a40 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Mathd.cs b/UnityProject/Assets/Mirror/Runtime/Mathd.cs new file mode 100644 index 0000000..2dfa2f9 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Mathd.cs @@ -0,0 +1,28 @@ +// 'double' precision variants for some of Unity's Mathf functions. + +using System.Runtime.CompilerServices; + +namespace Mirror +{ + public static class Mathd + { + /// Linearly interpolates between a and b by t with no limit to t. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double LerpUnclamped(double a, double b, double t) => + a + (b - a) * t; + + /// Clamps value between 0 and 1 and returns value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Clamp01(double value) + { + if (value < 0.0) + return 0; + return value > 1 ? 1 : value; + } + + /// Calculates the linear parameter t that produces the interpolant value within the range [a, b]. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double InverseLerp(double a, double b, double value) => + a != b ? Clamp01((value - a) / (b - a)) : 0; + } +} diff --git a/UnityProject/Assets/Mirror/Runtime/Mathd.cs.meta b/UnityProject/Assets/Mirror/Runtime/Mathd.cs.meta new file mode 100644 index 0000000..927c55a --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Mathd.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5f74084b91c74df2839b426c4a381373 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/MessagePacking.cs b/UnityProject/Assets/Mirror/Runtime/MessagePacking.cs index fc6e105..af7fca6 100644 --- a/UnityProject/Assets/Mirror/Runtime/MessagePacking.cs +++ b/UnityProject/Assets/Mirror/Runtime/MessagePacking.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using UnityEngine; namespace Mirror @@ -6,30 +7,36 @@ namespace Mirror // message packing all in one place, instead of constructing headers in all // kinds of different places // - // MsgType (1-n bytes) + // MsgType (2 bytes) // Content (ContentSize bytes) - // - // -> we use varint for headers because most messages will result in 1 byte - // type/size headers then instead of always - // using 2 bytes for shorts. - // -> this reduces bandwidth by 10% if average message size is 20 bytes - // (probably even shorter) public static class MessagePacking { // message header size - internal const int HeaderSize = sizeof(ushort); + public const int HeaderSize = sizeof(ushort); - public static ushort GetId() where T : struct, NetworkMessage + // max message content size (without header) calculation for convenience + // -> Transport.GetMaxPacketSize is the raw maximum + // -> Every message gets serialized into <> + // -> Every serialized message get put into a batch with a header + public static int MaxContentSize { - // paul: 16 bits is enough to avoid collisions - // - keeps the message size small - // - in case of collisions, Mirror will display an error - return (ushort)(typeof(T).FullName.GetStableHashCode() & 0xFFFF); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Transport.activeTransport.GetMaxPacketSize() + - HeaderSize + - Batcher.HeaderSize; } + // paul: 16 bits is enough to avoid collisions + // - keeps the message size small + // - in case of collisions, Mirror will display an error + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort GetId() where T : struct, NetworkMessage => + (ushort)(typeof(T).FullName.GetStableHashCode() & 0xFFFF); + // pack message before sending // -> NetworkWriter passed as arg so that we can use .ToArraySegment // and do an allocation free send before recycling it. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Pack(T message, NetworkWriter writer) where T : struct, NetworkMessage { @@ -44,9 +51,10 @@ namespace Mirror // -> pass NetworkReader so it's less strange if we create it in here // and pass it upwards. // -> NetworkReader will point at content afterwards! + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Unpack(NetworkReader messageReader, out ushort msgType) { - // read message type (varint) + // read message type try { msgType = messageReader.ReadUShort(); @@ -59,7 +67,10 @@ namespace Mirror } } - internal static NetworkMessageDelegate WrapHandler(Action handler, bool requireAuthentication) + // version for handlers with channelId + // inline! only exists for 20-30 messages and they call it all the time. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static NetworkMessageDelegate WrapHandler(Action handler, bool requireAuthentication) where T : struct, NetworkMessage where C : NetworkConnection => (conn, reader, channelId) => @@ -112,13 +123,26 @@ namespace Mirror try { // user implemented handler - handler((C)conn, message); + handler((C)conn, message, channelId); } catch (Exception e) { - Debug.LogError($"Exception in MessageHandler: {e.GetType().Name} {e.Message}\n{e.StackTrace}"); + Debug.LogError($"Disconnecting connId={conn.connectionId} to prevent exploits from an Exception in MessageHandler: {e.GetType().Name} {e.Message}\n{e.StackTrace}"); conn.Disconnect(); } }; + + // version for handlers without channelId + // TODO obsolete this some day to always use the channelId version. + // all handlers in this version are wrapped with 1 extra action. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static NetworkMessageDelegate WrapHandler(Action handler, bool requireAuthentication) + where T : struct, NetworkMessage + where C : NetworkConnection + { + // wrap action as channelId version, call original + void Wrapped(C conn, T msg, int _) => handler(conn, msg); + return WrapHandler((Action) Wrapped, requireAuthentication); + } } } diff --git a/UnityProject/Assets/Mirror/Runtime/MessagePacking.cs.meta b/UnityProject/Assets/Mirror/Runtime/MessagePacking.cs.meta index de16013..910b75c 100644 --- a/UnityProject/Assets/Mirror/Runtime/MessagePacking.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/MessagePacking.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Messages.cs b/UnityProject/Assets/Mirror/Runtime/Messages.cs index eb3cbef..bd194c8 100644 --- a/UnityProject/Assets/Mirror/Runtime/Messages.cs +++ b/UnityProject/Assets/Mirror/Runtime/Messages.cs @@ -3,14 +3,6 @@ using UnityEngine; namespace Mirror { - // Deprecated 2020-10-06 - [Obsolete("Implement NetworkMessage instead. Use extension methods instead of Serialize/Deserialize, see https://github.com/vis2k/Mirror/pull/2317", true)] - public interface IMessageBase {} - - // Deprecated 2020-10-06 - [Obsolete("Implement NetworkMessage instead. Use extension methods instead of Serialize/Deserialize, see https://github.com/vis2k/Mirror/pull/2317", true)] - public class MessageBase : IMessageBase {} - public struct ReadyMessage : NetworkMessage {} public struct NotReadyMessage : NetworkMessage {} @@ -35,8 +27,8 @@ namespace Mirror public struct CommandMessage : NetworkMessage { public uint netId; - public int componentIndex; - public int functionHash; + public byte componentIndex; + public ushort functionHash; // the parameters for the Cmd function // -> ArraySegment to avoid unnecessary allocations public ArraySegment payload; @@ -45,8 +37,8 @@ namespace Mirror public struct RpcMessage : NetworkMessage { public uint netId; - public int componentIndex; - public int functionHash; + public byte componentIndex; + public ushort functionHash; // the parameters for the Cmd function // -> ArraySegment to avoid unnecessary allocations public ArraySegment payload; @@ -73,6 +65,13 @@ namespace Mirror public ArraySegment payload; } + public struct ChangeOwnerMessage : NetworkMessage + { + public uint netId; + public bool isOwner; + public bool isLocalPlayer; + } + public struct ObjectSpawnStartedMessage : NetworkMessage {} public struct ObjectSpawnFinishedMessage : NetworkMessage {} diff --git a/UnityProject/Assets/Mirror/Runtime/Messages.cs.meta b/UnityProject/Assets/Mirror/Runtime/Messages.cs.meta index 56cb95f..5d119e2 100644 --- a/UnityProject/Assets/Mirror/Runtime/Messages.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Messages.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Mirror.asmdef b/UnityProject/Assets/Mirror/Runtime/Mirror.asmdef index 1dedc5a..0f38055 100644 --- a/UnityProject/Assets/Mirror/Runtime/Mirror.asmdef +++ b/UnityProject/Assets/Mirror/Runtime/Mirror.asmdef @@ -8,7 +8,7 @@ "optionalUnityReferences": [], "includePlatforms": [], "excludePlatforms": [], - "allowUnsafeCode": false, + "allowUnsafeCode": true, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, diff --git a/UnityProject/Assets/Mirror/Runtime/Mirror.asmdef.meta b/UnityProject/Assets/Mirror/Runtime/Mirror.asmdef.meta index 93af53d..202009b 100644 --- a/UnityProject/Assets/Mirror/Runtime/Mirror.asmdef.meta +++ b/UnityProject/Assets/Mirror/Runtime/Mirror.asmdef.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 30817c1a0e6d646d99c048fc403f5979 AssemblyDefinitionImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkAuthenticator.cs b/UnityProject/Assets/Mirror/Runtime/NetworkAuthenticator.cs index e980a89..aa1e7f7 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkAuthenticator.cs +++ b/UnityProject/Assets/Mirror/Runtime/NetworkAuthenticator.cs @@ -4,7 +4,7 @@ using UnityEngine.Events; namespace Mirror { - [Serializable] public class UnityEventNetworkConnection : UnityEvent {} + [Serializable] public class UnityEventNetworkConnection : UnityEvent {} /// Base class for implementing component-based authentication during the Connect phase [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-authenticators")] @@ -17,7 +17,7 @@ namespace Mirror /// Notify subscribers on the client when the client is authenticated [Tooltip("Mirror has an internal subscriber to this event. You can add your own here.")] - public UnityEventNetworkConnection OnClientAuthenticated = new UnityEventNetworkConnection(); + public UnityEvent OnClientAuthenticated = new UnityEvent(); /// Called when server starts, used to register message handlers if needed. public virtual void OnStartServer() {} @@ -25,15 +25,15 @@ namespace Mirror /// Called when server stops, used to unregister message handlers if needed. public virtual void OnStopServer() {} - /// Called on server from OnServerAuthenticateInternal when a client needs to authenticate - public abstract void OnServerAuthenticate(NetworkConnection conn); + /// Called on server from OnServerConnectInternal when a client needs to authenticate + public virtual void OnServerAuthenticate(NetworkConnectionToClient conn) {} - protected void ServerAccept(NetworkConnection conn) + protected void ServerAccept(NetworkConnectionToClient conn) { OnServerAuthenticated.Invoke(conn); } - protected void ServerReject(NetworkConnection conn) + protected void ServerReject(NetworkConnectionToClient conn) { conn.Disconnect(); } @@ -44,26 +44,14 @@ namespace Mirror /// Called when client stops, used to unregister message handlers if needed. public virtual void OnStopClient() {} - // Deprecated 2021-03-13 - [Obsolete("Remove the NetworkConnection parameter from your override and use NetworkClient.connection instead")] - public virtual void OnClientAuthenticate(NetworkConnection conn) => OnClientAuthenticate(); - - /// Called on client from OnClientAuthenticateInternal when a client needs to authenticate - public abstract void OnClientAuthenticate(); - - // Deprecated 2021-03-13 - [Obsolete("Remove the NetworkConnection parameter from your override and use NetworkClient.connection instead")] - protected void ClientAccept(NetworkConnection conn) => ClientAccept(); + /// Called on client from OnClientConnectInternal when a client needs to authenticate + public virtual void OnClientAuthenticate() {} protected void ClientAccept() { - OnClientAuthenticated.Invoke(NetworkClient.connection); + OnClientAuthenticated.Invoke(); } - // Deprecated 2021-03-13 - [Obsolete("Remove the NetworkConnection parameter from your override and use NetworkClient.connection instead")] - protected void ClientReject(NetworkConnection conn) => ClientReject(); - protected void ClientReject() { // Set this on the client for local reference @@ -72,16 +60,23 @@ namespace Mirror // disconnect the client NetworkClient.connection.Disconnect(); } - - void OnValidate() + + // Reset() instead of OnValidate(): + // Any NetworkAuthenticator assigns itself to the NetworkManager, this is fine on first adding it, + // but if someone intentionally sets Authenticator to null on the NetworkManager again then the + // Authenticator will reassign itself if a value in the inspector is changed. + // My change switches OnValidate to Reset since Reset is only called when the component is first + // added (or reset is pressed). + void Reset() { #if UNITY_EDITOR // automatically assign authenticator field if we add this to NetworkManager NetworkManager manager = GetComponent(); if (manager != null && manager.authenticator == null) { + // undo has to be called before the change happens + UnityEditor.Undo.RecordObject(manager, "Assigned NetworkManager authenticator"); manager.authenticator = this; - UnityEditor.Undo.RecordObject(gameObject, "Assigned NetworkManager authenticator"); } #endif } diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkAuthenticator.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkAuthenticator.cs.meta index e12482b..d37db68 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkAuthenticator.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/NetworkAuthenticator.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkBehaviour.cs b/UnityProject/Assets/Mirror/Runtime/NetworkBehaviour.cs index afaebc3..d58bf39 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkBehaviour.cs +++ b/UnityProject/Assets/Mirror/Runtime/NetworkBehaviour.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; -using Mirror.RemoteCalls; +using System.ComponentModel; +using System.Runtime.CompilerServices; using UnityEngine; namespace Mirror @@ -13,8 +14,6 @@ namespace Mirror [HelpURL("https://mirror-networking.gitbook.io/docs/guides/networkbehaviour")] public abstract class NetworkBehaviour : MonoBehaviour { - internal float lastSyncTime; - /// sync mode for OnSerialize // hidden because NetworkBehaviourInspector shows it only if has OnSerialize. [Tooltip("By default synced data is sent from the server to all Observers of the object.\nChange this to Owner to only have the server update the client that has ownership authority for this object")] @@ -26,6 +25,7 @@ namespace Mirror [Tooltip("Time in seconds until next change is synchronized to the client. '0' means send immediately if changed. '0.5' means only send changes every 500ms.\n(This is for state synchronization like SyncVars, SyncLists, OnSerialize. Not for Cmds, Rpcs, etc.)")] [Range(0, 2)] [HideInInspector] public float syncInterval = 0.1f; + internal double lastSyncTime; /// True if this object is on the server and has been spawned. // This is different from NetworkServer.active, which is true if the @@ -44,40 +44,25 @@ namespace Mirror /// True if this object is on the client-only, not host. public bool isClientOnly => netIdentity.isClientOnly; - /// This returns true if this object is the authoritative version of the object in the distributed network application. - // keeping this ridiculous summary as a reminder of a time long gone... + /// True on client if that component has been assigned to the client. E.g. player, pets, henchmen. public bool hasAuthority => netIdentity.hasAuthority; /// The unique network Id of this object (unique at runtime). public uint netId => netIdentity.netId; /// Client's network connection to the server. This is only valid for player objects on the client. + // TODO change to NetworkConnectionToServer, but might cause some breaking public NetworkConnection connectionToServer => netIdentity.connectionToServer; /// Server's network connection to the client. This is only valid for player objects on the server. - public NetworkConnection connectionToClient => netIdentity.connectionToClient; - - protected ulong syncVarDirtyBits { get; private set; } - ulong syncVarHookGuard; - - // USED BY WEAVER to set syncvars in host mode without deadlocking - protected bool getSyncVarHookGuard(ulong dirtyBit) - { - return (syncVarHookGuard & dirtyBit) != 0UL; - } - - // USED BY WEAVER to set syncvars in host mode without deadlocking - protected void setSyncVarHookGuard(ulong dirtyBit, bool value) - { - if (value) - syncVarHookGuard |= dirtyBit; - else - syncVarHookGuard &= ~dirtyBit; - } + public NetworkConnectionToClient connectionToClient => netIdentity.connectionToClient; // SyncLists, SyncSets, etc. protected readonly List syncObjects = new List(); + // NetworkBehaviourInspector needs to know if we have SyncObjects + internal bool HasSyncObjects() => syncObjects.Count > 0; + // NetworkIdentity based values set from NetworkIdentity.Awake(), // which is way more simple and way faster than trying to figure out // component index from in here by searching all NetworkComponents. @@ -88,40 +73,136 @@ namespace Mirror /// Returns the index of the component on this object public int ComponentIndex { get; internal set; } + // to avoid fully serializing entities every time, we have two options: + // * run a delta compression algorithm + // -> for fixed size types this is as easy as varint(b-a) for all + // -> for dynamically sized types like strings this is not easy. + // algorithms need to detect inserts/deletions, i.e. Myers Diff. + // those are very cpu intensive and barely fast enough for large + // scale multiplayer games (in Unity) + // * or we use dirty bits as meta data about which fields have changed + // -> spares us from running delta algorithms + // -> still supports dynamically sized types + // + // 64 bit mask, tracking up to 64 SyncVars. + protected ulong syncVarDirtyBits { get; private set; } + // 64 bit mask, tracking up to 64 sync collections (internal for tests). + // internal for tests, field for faster access (instead of property) + // TODO 64 SyncLists are too much. consider smaller mask later. + internal ulong syncObjectDirtyBits; + + // Weaver replaces '[SyncVar] int health' with 'Networkhealth' property. + // setter calls the hook if value changed. + // if we then modify the [SyncVar] from inside the setter, + // the setter would call the hook and we deadlock. + // hook guard prevents that. + ulong syncVarHookGuard; + + // USED BY WEAVER to set syncvars in host mode without deadlocking + protected bool GetSyncVarHookGuard(ulong dirtyBit) => + (syncVarHookGuard & dirtyBit) != 0UL; + + // USED BY WEAVER to set syncvars in host mode without deadlocking + protected void SetSyncVarHookGuard(ulong dirtyBit, bool value) + { + // set the bit + if (value) + syncVarHookGuard |= dirtyBit; + // clear the bit + else + syncVarHookGuard &= ~dirtyBit; + } + + /// Set as dirty so that it's synced to clients again. + // these are masks, not bit numbers, ie. 110011b not '2' for 2nd bit. + public void SetSyncVarDirtyBit(ulong dirtyBit) + { + syncVarDirtyBits |= dirtyBit; + } + + // true if syncInterval elapsed and any SyncVar or SyncObject is dirty + public bool IsDirty() + { + if (NetworkTime.localTime - lastSyncTime >= syncInterval) + { + // OR both bitmasks. != 0 if either was dirty. + return (syncVarDirtyBits | syncObjectDirtyBits) != 0UL; + } + return false; + } + + /// Clears all the dirty bits that were set by SetDirtyBits() + // automatically invoked when an update is sent for this object, but can + // be called manually as well. + public void ClearAllDirtyBits() + { + lastSyncTime = NetworkTime.localTime; + syncVarDirtyBits = 0L; + syncObjectDirtyBits = 0L; + + // clear all unsynchronized changes in syncobjects + // (Linq allocates, use for instead) + for (int i = 0; i < syncObjects.Count; ++i) + { + syncObjects[i].ClearChanges(); + } + } + // this gets called in the constructor by the weaver // for every SyncObject in the component (e.g. SyncLists). // We collect all of them and we synchronize them with OnSerialize/OnDeserialize protected void InitSyncObject(SyncObject syncObject) { if (syncObject == null) - Debug.LogError("Uninitialized SyncObject. Manually call the constructor on your SyncList, SyncSet or SyncDictionary"); - else - syncObjects.Add(syncObject); + { + Debug.LogError("Uninitialized SyncObject. Manually call the constructor on your SyncList, SyncSet, SyncDictionary or SyncField"); + return; + } + + // add it, remember the index in list (if Count=0, index=0 etc.) + int index = syncObjects.Count; + syncObjects.Add(syncObject); + + // OnDirty needs to set nth bit in our dirty mask + ulong nthBit = 1UL << index; + syncObject.OnDirty = () => syncObjectDirtyBits |= nthBit; + + // only record changes while we have observers. + // prevents ever growing .changes lists: + // if a monster has no observers but we keep modifing a SyncObject, + // then the changes would never be flushed and keep growing, + // because OnSerialize isn't called without observers. + syncObject.IsRecording = () => netIdentity.observers?.Count > 0; } - protected void SendCommandInternal(Type invokeClass, string cmdName, NetworkWriter writer, int channelId, bool requiresAuthority = true) + // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions + protected void SendCommandInternal(string functionFullName, NetworkWriter writer, int channelId, bool requiresAuthority = true) { // this was in Weaver before // NOTE: we could remove this later to allow calling Cmds on Server // to avoid Wrapper functions. a lot of people requested this. if (!NetworkClient.active) { - Debug.LogError($"Command Function {cmdName} called without an active client."); + Debug.LogError($"Command Function {functionFullName} called on {name} without an active client.", gameObject); + return; + } + + // previously we used NetworkClient.readyConnection. + // now we check .ready separately. + if (!NetworkClient.ready) + { + // Unreliable Cmds from NetworkTransform may be generated, + // or client may have been set NotReady intentionally, so + // only warn if on the reliable channel. + if (channelId == Channels.Reliable) + Debug.LogWarning($"Command Function {functionFullName} called on {name} while NetworkClient is not ready.\nThis may be ignored if client intentionally set NotReady.", gameObject); return; } // local players can always send commands, regardless of authority, other objects must have authority. if (!(!requiresAuthority || isLocalPlayer || hasAuthority)) { - Debug.LogWarning($"Trying to send command for object without authority. {invokeClass}.{cmdName}"); - return; - } - - // previously we used NetworkClient.readyConnection. - // now we check .ready separately and use .connection instead. - if (!NetworkClient.ready) - { - Debug.LogError("Send command attempted while NetworkClient is not ready."); + Debug.LogWarning($"Command Function {functionFullName} called on {name} without authority.", gameObject); return; } @@ -132,7 +213,7 @@ namespace Mirror // => see also: https://github.com/vis2k/Mirror/issues/2629 if (NetworkClient.connection == null) { - Debug.LogError("Send command attempted with no client running."); + Debug.LogError($"Command Function {functionFullName} called on {name} with no client running.", gameObject); return; } @@ -140,9 +221,9 @@ namespace Mirror CommandMessage message = new CommandMessage { netId = netId, - componentIndex = ComponentIndex, + componentIndex = (byte)ComponentIndex, // type+func so Inventory.RpcUse != Equipment.RpcUse - functionHash = RemoteCallHelper.GetMethodHash(invokeClass, cmdName), + functionHash = (ushort)functionFullName.GetStableHashCode(), // segment to avoid reader allocations payload = writer.ToArraySegment() }; @@ -155,19 +236,20 @@ namespace Mirror NetworkClient.connection.Send(message, channelId); } - protected void SendRPCInternal(Type invokeClass, string rpcName, NetworkWriter writer, int channelId, bool includeOwner) + // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions + protected void SendRPCInternal(string functionFullName, NetworkWriter writer, int channelId, bool includeOwner) { // this was in Weaver before if (!NetworkServer.active) { - Debug.LogError("RPC Function " + rpcName + " called on Client."); + Debug.LogError($"RPC Function {functionFullName} called on Client.", gameObject); return; } // This cannot use NetworkServer.active, as that is not specific to this object. if (!isServer) { - Debug.LogWarning("ClientRpc " + rpcName + " called on un-spawned object: " + name); + Debug.LogWarning($"ClientRpc {functionFullName} called on un-spawned object: {name}", gameObject); return; } @@ -175,27 +257,28 @@ namespace Mirror RpcMessage message = new RpcMessage { netId = netId, - componentIndex = ComponentIndex, + componentIndex = (byte)ComponentIndex, // type+func so Inventory.RpcUse != Equipment.RpcUse - functionHash = RemoteCallHelper.GetMethodHash(invokeClass, rpcName), + functionHash = (ushort)functionFullName.GetStableHashCode(), // segment to avoid reader allocations payload = writer.ToArraySegment() }; - NetworkServer.SendToReady(netIdentity, message, includeOwner, channelId); + NetworkServer.SendToReadyObservers(netIdentity, message, includeOwner, channelId); } - protected void SendTargetRPCInternal(NetworkConnection conn, Type invokeClass, string rpcName, NetworkWriter writer, int channelId) + // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions + protected void SendTargetRPCInternal(NetworkConnection conn, string functionFullName, NetworkWriter writer, int channelId) { if (!NetworkServer.active) { - Debug.LogError($"TargetRPC {rpcName} called when server not active"); + Debug.LogError($"TargetRPC {functionFullName} called on {name} when server not active", gameObject); return; } if (!isServer) { - Debug.LogWarning($"TargetRpc {rpcName} called on {name} but that object has not been spawned or has been unspawned"); + Debug.LogWarning($"TargetRpc {functionFullName} called on {name} but that object has not been spawned or has been unspawned", gameObject); return; } @@ -208,13 +291,13 @@ namespace Mirror // if still null if (conn is null) { - Debug.LogError($"TargetRPC {rpcName} was given a null connection, make sure the object has an owner or you pass in the target connection"); + Debug.LogError($"TargetRPC {functionFullName} was given a null connection, make sure the object {name} has an owner or you pass in the target connection", gameObject); return; } if (!(conn is NetworkConnectionToClient)) { - Debug.LogError($"TargetRPC {rpcName} requires a NetworkConnectionToClient but was given {conn.GetType().Name}"); + Debug.LogError($"TargetRPC {functionFullName} called on {name} requires a NetworkConnectionToClient but was given {conn.GetType().Name}", gameObject); return; } @@ -222,9 +305,9 @@ namespace Mirror RpcMessage message = new RpcMessage { netId = netId, - componentIndex = ComponentIndex, + componentIndex = (byte)ComponentIndex, // type+func so Inventory.RpcUse != Equipment.RpcUse - functionHash = RemoteCallHelper.GetMethodHash(invokeClass, rpcName), + functionHash = (ushort)functionFullName.GetStableHashCode(), // segment to avoid reader allocations payload = writer.ToArraySegment() }; @@ -232,11 +315,144 @@ namespace Mirror conn.Send(message, channelId); } + // move the [SyncVar] generated property's .set into C# to avoid much IL + // + // public int health = 42; + // + // public int Networkhealth + // { + // get + // { + // return health; + // } + // [param: In] + // set + // { + // if (!NetworkBehaviour.SyncVarEqual(value, ref health)) + // { + // int oldValue = health; + // SetSyncVar(value, ref health, 1uL); + // if (NetworkServer.localClientActive && !GetSyncVarHookGuard(1uL)) + // { + // SetSyncVarHookGuard(1uL, value: true); + // OnChanged(oldValue, value); + // SetSyncVarHookGuard(1uL, value: false); + // } + // } + // } + // } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void GeneratedSyncVarSetter(T value, ref T field, ulong dirtyBit, Action OnChanged) + { + if (!SyncVarEqual(value, ref field)) + { + T oldValue = field; + SetSyncVar(value, ref field, dirtyBit); + + // call hook (if any) + if (OnChanged != null) + { + // in host mode, setting a SyncVar calls the hook directly. + // in client-only mode, OnDeserialize would call it. + // we use hook guard to protect against deadlock where hook + // changes syncvar, calling hook again. + if (NetworkServer.localClientActive && !GetSyncVarHookGuard(dirtyBit)) + { + SetSyncVarHookGuard(dirtyBit, true); + OnChanged(oldValue, value); + SetSyncVarHookGuard(dirtyBit, false); + } + } + } + } + + // GameObject needs custom handling for persistence via netId. + // has one extra parameter. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void GeneratedSyncVarSetter_GameObject(GameObject value, ref GameObject field, ulong dirtyBit, Action OnChanged, ref uint netIdField) + { + if (!SyncVarGameObjectEqual(value, netIdField)) + { + GameObject oldValue = field; + SetSyncVarGameObject(value, ref field, dirtyBit, ref netIdField); + + // call hook (if any) + if (OnChanged != null) + { + // in host mode, setting a SyncVar calls the hook directly. + // in client-only mode, OnDeserialize would call it. + // we use hook guard to protect against deadlock where hook + // changes syncvar, calling hook again. + if (NetworkServer.localClientActive && !GetSyncVarHookGuard(dirtyBit)) + { + SetSyncVarHookGuard(dirtyBit, true); + OnChanged(oldValue, value); + SetSyncVarHookGuard(dirtyBit, false); + } + } + } + } + + // NetworkIdentity needs custom handling for persistence via netId. + // has one extra parameter. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void GeneratedSyncVarSetter_NetworkIdentity(NetworkIdentity value, ref NetworkIdentity field, ulong dirtyBit, Action OnChanged, ref uint netIdField) + { + if (!SyncVarNetworkIdentityEqual(value, netIdField)) + { + NetworkIdentity oldValue = field; + SetSyncVarNetworkIdentity(value, ref field, dirtyBit, ref netIdField); + + // call hook (if any) + if (OnChanged != null) + { + // in host mode, setting a SyncVar calls the hook directly. + // in client-only mode, OnDeserialize would call it. + // we use hook guard to protect against deadlock where hook + // changes syncvar, calling hook again. + if (NetworkServer.localClientActive && !GetSyncVarHookGuard(dirtyBit)) + { + SetSyncVarHookGuard(dirtyBit, true); + OnChanged(oldValue, value); + SetSyncVarHookGuard(dirtyBit, false); + } + } + } + } + + // NetworkBehaviour needs custom handling for persistence via netId. + // has one extra parameter. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void GeneratedSyncVarSetter_NetworkBehaviour(T value, ref T field, ulong dirtyBit, Action OnChanged, ref NetworkBehaviourSyncVar netIdField) + where T : NetworkBehaviour + { + if (!SyncVarNetworkBehaviourEqual(value, netIdField)) + { + T oldValue = field; + SetSyncVarNetworkBehaviour(value, ref field, dirtyBit, ref netIdField); + + // call hook (if any) + if (OnChanged != null) + { + // in host mode, setting a SyncVar calls the hook directly. + // in client-only mode, OnDeserialize would call it. + // we use hook guard to protect against deadlock where hook + // changes syncvar, calling hook again. + if (NetworkServer.localClientActive && !GetSyncVarHookGuard(dirtyBit)) + { + SetSyncVarHookGuard(dirtyBit, true); + OnChanged(oldValue, value); + SetSyncVarHookGuard(dirtyBit, false); + } + } + } + } + // helper function for [SyncVar] GameObjects. - // IMPORTANT: keep as 'protected', not 'internal', otherwise Weaver - // can't resolve it - // TODO make this static and adjust weaver to find it - protected bool SyncVarGameObjectEqual(GameObject newGameObject, uint netIdField) + // needs to be public so that tests & NetworkBehaviours from other + // assemblies both find it + [EditorBrowsable(EditorBrowsableState.Never)] + public static bool SyncVarGameObjectEqual(GameObject newGameObject, uint netIdField) { uint newNetId = 0; if (newGameObject != null) @@ -247,7 +463,7 @@ namespace Mirror newNetId = identity.netId; if (newNetId == 0) { - Debug.LogWarning("SetSyncVarGameObject GameObject " + newGameObject + " has a zero netId. Maybe it is not spawned yet?"); + Debug.LogWarning($"SetSyncVarGameObject GameObject {newGameObject} has a zero netId. Maybe it is not spawned yet?"); } } } @@ -256,9 +472,10 @@ namespace Mirror } // helper function for [SyncVar] GameObjects. + // dirtyBit is a mask like 00010 protected void SetSyncVarGameObject(GameObject newGameObject, ref GameObject gameObjectField, ulong dirtyBit, ref uint netIdField) { - if (getSyncVarHookGuard(dirtyBit)) + if (GetSyncVarHookGuard(dirtyBit)) return; uint newNetId = 0; @@ -270,13 +487,13 @@ namespace Mirror newNetId = identity.netId; if (newNetId == 0) { - Debug.LogWarning("SetSyncVarGameObject GameObject " + newGameObject + " has a zero netId. Maybe it is not spawned yet?"); + Debug.LogWarning($"SetSyncVarGameObject GameObject {newGameObject} has a zero netId. Maybe it is not spawned yet?"); } } } - // Debug.Log("SetSyncVar GameObject " + GetType().Name + " bit [" + dirtyBit + "] netfieldId:" + netIdField + "->" + newNetId); - SetDirtyBit(dirtyBit); + //Debug.Log($"SetSyncVar GameObject {GetType().Name} bit:{dirtyBit} netfieldId:{netIdField} -> {newNetId}"); + SetSyncVarDirtyBit(dirtyBit); // assign new one on the server, and in case we ever need it on client too gameObjectField = newGameObject; netIdField = newNetId; @@ -294,15 +511,16 @@ namespace Mirror // client always looks up based on netId because objects might get in and out of range // over and over again, which shouldn't null them forever - if (NetworkIdentity.spawned.TryGetValue(netId, out NetworkIdentity identity) && identity != null) + if (NetworkClient.spawned.TryGetValue(netId, out NetworkIdentity identity) && identity != null) return gameObjectField = identity.gameObject; return null; } // helper function for [SyncVar] NetworkIdentities. - // IMPORTANT: keep as 'protected', not 'internal', otherwise Weaver - // can't resolve it - protected bool SyncVarNetworkIdentityEqual(NetworkIdentity newIdentity, uint netIdField) + // needs to be public so that tests & NetworkBehaviours from other + // assemblies both find it + [EditorBrowsable(EditorBrowsableState.Never)] + public static bool SyncVarNetworkIdentityEqual(NetworkIdentity newIdentity, uint netIdField) { uint newNetId = 0; if (newIdentity != null) @@ -310,7 +528,7 @@ namespace Mirror newNetId = newIdentity.netId; if (newNetId == 0) { - Debug.LogWarning("SetSyncVarNetworkIdentity NetworkIdentity " + newIdentity + " has a zero netId. Maybe it is not spawned yet?"); + Debug.LogWarning($"SetSyncVarNetworkIdentity NetworkIdentity {newIdentity} has a zero netId. Maybe it is not spawned yet?"); } } @@ -318,10 +536,258 @@ namespace Mirror return newNetId == netIdField; } + // move the [SyncVar] generated OnDeserialize C# to avoid much IL. + // + // before: + // public override void DeserializeSyncVars(NetworkReader reader, bool initialState) + // { + // base.DeserializeSyncVars(reader, initialState); + // if (initialState) + // { + // int num = health; + // Networkhealth = reader.ReadInt(); + // if (!NetworkBehaviour.SyncVarEqual(num, ref health)) + // { + // OnChanged(num, health); + // } + // return; + // } + // long num2 = (long)reader.ReadULong(); + // if ((num2 & 1L) != 0L) + // { + // int num3 = health; + // Networkhealth = reader.ReadInt(); + // if (!NetworkBehaviour.SyncVarEqual(num3, ref health)) + // { + // OnChanged(num3, health); + // } + // } + // } + // + // after: + // + // public override void DeserializeSyncVars(NetworkReader reader, bool initialState) + // { + // base.DeserializeSyncVars(reader, initialState); + // if (initialState) + // { + // GeneratedSyncVarDeserialize(reader, ref health, null, reader.ReadInt()); + // return; + // } + // long num = (long)reader.ReadULong(); + // if ((num & 1L) != 0L) + // { + // GeneratedSyncVarDeserialize(reader, ref health, null, reader.ReadInt()); + // } + // } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void GeneratedSyncVarDeserialize(ref T field, Action OnChanged, T value) + { + T previous = field; + field = value; + + // any hook? then call if changed. + if (OnChanged != null && !SyncVarEqual(previous, ref field)) + { + OnChanged(previous, field); + } + } + + // move the [SyncVar] generated OnDeserialize C# to avoid much IL. + // + // before: + // public override void DeserializeSyncVars(NetworkReader reader, bool initialState) + // { + // base.DeserializeSyncVars(reader, initialState); + // if (initialState) + // { + // uint __targetNetId = ___targetNetId; + // GameObject networktarget = Networktarget; + // ___targetNetId = reader.ReadUInt(); + // if (!NetworkBehaviour.SyncVarEqual(__targetNetId, ref ___targetNetId)) + // { + // OnChangedNB(networktarget, Networktarget); + // } + // return; + // } + // long num = (long)reader.ReadULong(); + // if ((num & 1L) != 0L) + // { + // uint __targetNetId2 = ___targetNetId; + // GameObject networktarget2 = Networktarget; + // ___targetNetId = reader.ReadUInt(); + // if (!NetworkBehaviour.SyncVarEqual(__targetNetId2, ref ___targetNetId)) + // { + // OnChangedNB(networktarget2, Networktarget); + // } + // } + // } + // + // after: + // public override void DeserializeSyncVars(NetworkReader reader, bool initialState) + // { + // base.DeserializeSyncVars(reader, initialState); + // if (initialState) + // { + // GeneratedSyncVarDeserialize_GameObject(reader, ref target, OnChangedNB, ref ___targetNetId); + // return; + // } + // long num = (long)reader.ReadULong(); + // if ((num & 1L) != 0L) + // { + // GeneratedSyncVarDeserialize_GameObject(reader, ref target, OnChangedNB, ref ___targetNetId); + // } + // } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void GeneratedSyncVarDeserialize_GameObject(ref GameObject field, Action OnChanged, NetworkReader reader, ref uint netIdField) + { + uint previousNetId = netIdField; + GameObject previousGameObject = field; + netIdField = reader.ReadUInt(); + + // get the new GameObject now that netId field is set + field = GetSyncVarGameObject(netIdField, ref field); + + // any hook? then call if changed. + if (OnChanged != null && !SyncVarEqual(previousNetId, ref netIdField)) + { + OnChanged(previousGameObject, field); + } + } + + // move the [SyncVar] generated OnDeserialize C# to avoid much IL. + // + // before: + // public override void DeserializeSyncVars(NetworkReader reader, bool initialState) + // { + // base.DeserializeSyncVars(reader, initialState); + // if (initialState) + // { + // uint __targetNetId = ___targetNetId; + // NetworkIdentity networktarget = Networktarget; + // ___targetNetId = reader.ReadUInt(); + // if (!NetworkBehaviour.SyncVarEqual(__targetNetId, ref ___targetNetId)) + // { + // OnChangedNI(networktarget, Networktarget); + // } + // return; + // } + // long num = (long)reader.ReadULong(); + // if ((num & 1L) != 0L) + // { + // uint __targetNetId2 = ___targetNetId; + // NetworkIdentity networktarget2 = Networktarget; + // ___targetNetId = reader.ReadUInt(); + // if (!NetworkBehaviour.SyncVarEqual(__targetNetId2, ref ___targetNetId)) + // { + // OnChangedNI(networktarget2, Networktarget); + // } + // } + // } + // + // after: + // + // public override void DeserializeSyncVars(NetworkReader reader, bool initialState) + // { + // base.DeserializeSyncVars(reader, initialState); + // if (initialState) + // { + // GeneratedSyncVarDeserialize_NetworkIdentity(reader, ref target, OnChangedNI, ref ___targetNetId); + // return; + // } + // long num = (long)reader.ReadULong(); + // if ((num & 1L) != 0L) + // { + // GeneratedSyncVarDeserialize_NetworkIdentity(reader, ref target, OnChangedNI, ref ___targetNetId); + // } + // } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void GeneratedSyncVarDeserialize_NetworkIdentity(ref NetworkIdentity field, Action OnChanged, NetworkReader reader, ref uint netIdField) + { + uint previousNetId = netIdField; + NetworkIdentity previousIdentity = field; + netIdField = reader.ReadUInt(); + + // get the new NetworkIdentity now that netId field is set + field = GetSyncVarNetworkIdentity(netIdField, ref field); + + // any hook? then call if changed. + if (OnChanged != null && !SyncVarEqual(previousNetId, ref netIdField)) + { + OnChanged(previousIdentity, field); + } + } + + // move the [SyncVar] generated OnDeserialize C# to avoid much IL. + // + // before: + // + // public override void DeserializeSyncVars(NetworkReader reader, bool initialState) + // { + // base.DeserializeSyncVars(reader, initialState); + // if (initialState) + // { + // NetworkBehaviourSyncVar __targetNetId = ___targetNetId; + // Tank networktarget = Networktarget; + // ___targetNetId = reader.ReadNetworkBehaviourSyncVar(); + // if (!NetworkBehaviour.SyncVarEqual(__targetNetId, ref ___targetNetId)) + // { + // OnChangedNB(networktarget, Networktarget); + // } + // return; + // } + // long num = (long)reader.ReadULong(); + // if ((num & 1L) != 0L) + // { + // NetworkBehaviourSyncVar __targetNetId2 = ___targetNetId; + // Tank networktarget2 = Networktarget; + // ___targetNetId = reader.ReadNetworkBehaviourSyncVar(); + // if (!NetworkBehaviour.SyncVarEqual(__targetNetId2, ref ___targetNetId)) + // { + // OnChangedNB(networktarget2, Networktarget); + // } + // } + // } + // + // after: + // + // public override void DeserializeSyncVars(NetworkReader reader, bool initialState) + // { + // base.DeserializeSyncVars(reader, initialState); + // if (initialState) + // { + // GeneratedSyncVarDeserialize_NetworkBehaviour(reader, ref target, OnChangedNB, ref ___targetNetId); + // return; + // } + // long num = (long)reader.ReadULong(); + // if ((num & 1L) != 0L) + // { + // GeneratedSyncVarDeserialize_NetworkBehaviour(reader, ref target, OnChangedNB, ref ___targetNetId); + // } + // } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void GeneratedSyncVarDeserialize_NetworkBehaviour(ref T field, Action OnChanged, NetworkReader reader, ref NetworkBehaviourSyncVar netIdField) + where T : NetworkBehaviour + { + NetworkBehaviourSyncVar previousNetId = netIdField; + T previousBehaviour = field; + netIdField = reader.ReadNetworkBehaviourSyncVar(); + + // get the new NetworkBehaviour now that netId field is set + field = GetSyncVarNetworkBehaviour(netIdField, ref field); + + // any hook? then call if changed. + if (OnChanged != null && !SyncVarEqual(previousNetId, ref netIdField)) + { + OnChanged(previousBehaviour, field); + } + } + // helper function for [SyncVar] NetworkIdentities. + // dirtyBit is a mask like 00010 protected void SetSyncVarNetworkIdentity(NetworkIdentity newIdentity, ref NetworkIdentity identityField, ulong dirtyBit, ref uint netIdField) { - if (getSyncVarHookGuard(dirtyBit)) + if (GetSyncVarHookGuard(dirtyBit)) return; uint newNetId = 0; @@ -330,12 +796,12 @@ namespace Mirror newNetId = newIdentity.netId; if (newNetId == 0) { - Debug.LogWarning("SetSyncVarNetworkIdentity NetworkIdentity " + newIdentity + " has a zero netId. Maybe it is not spawned yet?"); + Debug.LogWarning($"SetSyncVarNetworkIdentity NetworkIdentity {newIdentity} has a zero netId. Maybe it is not spawned yet?"); } } - // Debug.Log("SetSyncVarNetworkIdentity NetworkIdentity " + GetType().Name + " bit [" + dirtyBit + "] netIdField:" + netIdField + "->" + newNetId); - SetDirtyBit(dirtyBit); + //Debug.Log($"SetSyncVarNetworkIdentity NetworkIdentity {GetType().Name} bit:{dirtyBit} netIdField:{netIdField} -> {newNetId}"); + SetSyncVarDirtyBit(dirtyBit); netIdField = newNetId; // assign new one on the server, and in case we ever need it on client too identityField = newIdentity; @@ -353,11 +819,11 @@ namespace Mirror // client always looks up based on netId because objects might get in and out of range // over and over again, which shouldn't null them forever - NetworkIdentity.spawned.TryGetValue(netId, out identityField); + NetworkClient.spawned.TryGetValue(netId, out identityField); return identityField; } - protected bool SyncVarNetworkBehaviourEqual(T newBehaviour, NetworkBehaviourSyncVar syncField) where T : NetworkBehaviour + protected static bool SyncVarNetworkBehaviourEqual(T newBehaviour, NetworkBehaviourSyncVar syncField) where T : NetworkBehaviour { uint newNetId = 0; int newComponentIndex = 0; @@ -367,7 +833,7 @@ namespace Mirror newComponentIndex = newBehaviour.ComponentIndex; if (newNetId == 0) { - Debug.LogWarning("SetSyncVarNetworkIdentity NetworkIdentity " + newBehaviour + " has a zero netId. Maybe it is not spawned yet?"); + Debug.LogWarning($"SetSyncVarNetworkIdentity NetworkIdentity {newBehaviour} has a zero netId. Maybe it is not spawned yet?"); } } @@ -376,9 +842,10 @@ namespace Mirror } // helper function for [SyncVar] NetworkIdentities. + // dirtyBit is a mask like 00010 protected void SetSyncVarNetworkBehaviour(T newBehaviour, ref T behaviourField, ulong dirtyBit, ref NetworkBehaviourSyncVar syncField) where T : NetworkBehaviour { - if (getSyncVarHookGuard(dirtyBit)) + if (GetSyncVarHookGuard(dirtyBit)) return; uint newNetId = 0; @@ -389,13 +856,13 @@ namespace Mirror componentIndex = newBehaviour.ComponentIndex; if (newNetId == 0) { - Debug.LogWarning($"{nameof(SetSyncVarNetworkBehaviour)} NetworkIdentity " + newBehaviour + " has a zero netId. Maybe it is not spawned yet?"); + Debug.LogWarning($"{nameof(SetSyncVarNetworkBehaviour)} NetworkIdentity {newBehaviour} has a zero netId. Maybe it is not spawned yet?"); } } syncField = new NetworkBehaviourSyncVar(newNetId, componentIndex); - SetDirtyBit(dirtyBit); + SetSyncVarDirtyBit(dirtyBit); // assign new one on the server, and in case we ever need it on client too behaviourField = newBehaviour; @@ -415,7 +882,7 @@ namespace Mirror // client always looks up based on netId because objects might get in and out of range // over and over again, which shouldn't null them forever - if (!NetworkIdentity.spawned.TryGetValue(syncNetBehaviour.netId, out NetworkIdentity identity)) + if (!NetworkClient.spawned.TryGetValue(syncNetBehaviour.netId, out NetworkIdentity identity)) { return null; } @@ -453,69 +920,23 @@ namespace Mirror } } - protected bool SyncVarEqual(T value, ref T fieldValue) + protected static bool SyncVarEqual(T value, ref T fieldValue) { // newly initialized or changed value? + // value.Equals(fieldValue) allocates without 'where T : IEquatable' + // seems like we use EqualityComparer to avoid allocations, + // because not all SyncVars are IEquatable return EqualityComparer.Default.Equals(value, fieldValue); } + // dirtyBit is a mask like 00010 protected void SetSyncVar(T value, ref T fieldValue, ulong dirtyBit) { - // Debug.Log("SetSyncVar " + GetType().Name + " bit [" + dirtyBit + "] " + fieldValue + "->" + value); - SetDirtyBit(dirtyBit); + //Debug.Log($"SetSyncVar {GetType().Name} bit:{dirtyBit} fieldValue:{value}"); + SetSyncVarDirtyBit(dirtyBit); fieldValue = value; } - /// Set as dirty so that it's synced to clients again. - // these are masks, not bit numbers, ie. 0x004 not 2 - public void SetDirtyBit(ulong dirtyBit) - { - syncVarDirtyBits |= dirtyBit; - } - - /// Clears all the dirty bits that were set by SetDirtyBits() - // automatically invoked when an update is sent for this object, but can - // be called manually as well. - public void ClearAllDirtyBits() - { - lastSyncTime = Time.time; - syncVarDirtyBits = 0L; - - // flush all unsynchronized changes in syncobjects - // note: don't use List.ForEach here, this is a hot path - // List.ForEach: 432b/frame - // for: 231b/frame - for (int i = 0; i < syncObjects.Count; ++i) - { - syncObjects[i].Flush(); - } - } - - bool AnySyncObjectDirty() - { - // note: don't use Linq here. 1200 networked objects: - // Linq: 187KB GC/frame;, 2.66ms time - // for: 8KB GC/frame; 1.28ms time - for (int i = 0; i < syncObjects.Count; ++i) - { - if (syncObjects[i].IsDirty) - { - return true; - } - } - return false; - } - - // true if syncInterval elapsed and any SyncVar or SyncObject is dirty - public bool IsDirty() - { - if (Time.time - lastSyncTime >= syncInterval) - { - return syncVarDirtyBits != 0L || AnySyncObjectDirty(); - } - return false; - } - /// Override to do custom serialization (instead of SyncVars/SyncLists). Use OnDeserialize too. // if a class has syncvars, then OnSerialize/OnDeserialize are added // automatically. @@ -524,20 +945,10 @@ namespace Mirror // note: SyncVar hooks are only called when inital=false public virtual bool OnSerialize(NetworkWriter writer, bool initialState) { - bool objectWritten = false; // if initialState: write all SyncVars. // otherwise write dirtyBits+dirty SyncVars - if (initialState) - { - objectWritten = SerializeObjectsAll(writer); - } - else - { - objectWritten = SerializeObjectsDelta(writer); - } - + bool objectWritten = initialState ? SerializeObjectsAll(writer) : SerializeObjectsDelta(writer); bool syncVarWritten = SerializeSyncVars(writer, initialState); - return objectWritten || syncVarWritten; } @@ -582,20 +993,6 @@ namespace Mirror // read dirty SyncVars } - internal ulong DirtyObjectBits() - { - ulong dirtyObjects = 0; - for (int i = 0; i < syncObjects.Count; i++) - { - SyncObject syncObject = syncObjects[i]; - if (syncObject.IsDirty) - { - dirtyObjects |= 1UL << i; - } - } - return dirtyObjects; - } - public bool SerializeObjectsAll(NetworkWriter writer) { bool dirty = false; @@ -612,12 +1009,13 @@ namespace Mirror { bool dirty = false; // write the mask - writer.WriteULong(DirtyObjectBits()); + writer.WriteULong(syncObjectDirtyBits); // serializable objects, such as synclists for (int i = 0; i < syncObjects.Count; i++) { + // check dirty mask at nth bit SyncObject syncObject = syncObjects[i]; - if (syncObject.IsDirty) + if ((syncObjectDirtyBits & (1UL << i)) != 0) { syncObject.OnSerializeDelta(writer); dirty = true; @@ -640,6 +1038,7 @@ namespace Mirror ulong dirty = reader.ReadULong(); for (int i = 0; i < syncObjects.Count; i++) { + // check dirty mask at nth bit SyncObject syncObject = syncObjects[i]; if ((dirty & (1UL << i)) != 0) { @@ -671,6 +1070,9 @@ namespace Mirror /// Like Start(), but only called on client and host for the local player object. public virtual void OnStartLocalPlayer() {} + /// Stop event, but only called on client and host for the local player object. + public virtual void OnStopLocalPlayer() {} + /// Like Start(), but only called for objects the client has authority over. public virtual void OnStartAuthority() {} diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkBehaviour.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkBehaviour.cs.meta index 980859a..f0bc195 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkBehaviour.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/NetworkBehaviour.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkClient.cs b/UnityProject/Assets/Mirror/Runtime/NetworkClient.cs index ce12b07..b6a1bd7 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkClient.cs +++ b/UnityProject/Assets/Mirror/Runtime/NetworkClient.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Mirror.RemoteCalls; using UnityEngine; namespace Mirror @@ -8,8 +9,11 @@ namespace Mirror public enum ConnectState { None, + // connecting between Connect() and OnTransportConnected() Connecting, Connected, + // disconnecting between Disconnect() and OnTransportDisconnected() + Disconnecting, Disconnected } @@ -20,6 +24,11 @@ namespace Mirror internal static readonly Dictionary handlers = new Dictionary(); + /// All spawned NetworkIdentities by netId. + // client sees OBSERVED spawned ones. + public static readonly Dictionary spawned = + new Dictionary(); + /// Client's NetworkConnection to server. public static NetworkConnection connection { get; internal set; } @@ -31,11 +40,6 @@ namespace Mirror // way better for security if we can check states in callbacks public static bool ready; - /// The NetworkConnection object that is currently "ready". - // TODO this is from UNET. it's redundant and we should probably obsolete it. - [Obsolete("NetworkClient.readyConnection is redundant. Use NetworkClient.connection and use NetworkClient.ready to check if it's ready.")] - public static NetworkConnection readyConnection => ready ? connection : null; - /// NetworkIdentity of the localPlayer public static NetworkIdentity localPlayer { get; internal set; } @@ -59,60 +63,81 @@ namespace Mirror /// True if client is running in host mode. public static bool isHostClient => connection is LocalConnectionToServer; - // Deprecated 2021-05-26 - [Obsolete("isLocalClient was renamed to isHostClient because that's what it actually means.")] - public static bool isLocalClient => isHostClient; // OnConnected / OnDisconnected used to be NetworkMessages that were // invoked. this introduced a bug where external clients could send // Connected/Disconnected messages over the network causing undefined // behaviour. - internal static Action OnConnectedEvent; - internal static Action OnDisconnectedEvent; + // => public so that custom NetworkManagers can hook into it + public static Action OnConnectedEvent; + public static Action OnDisconnectedEvent; + public static Action OnErrorEvent; /// Registered spawnable prefabs by assetId. public static readonly Dictionary prefabs = new Dictionary(); - // spawn handlers + // custom spawn / unspawn handlers. + // useful to support prefab pooling etc.: + // https://mirror-networking.gitbook.io/docs/guides/gameobjects/custom-spawnfunctions internal static readonly Dictionary spawnHandlers = new Dictionary(); internal static readonly Dictionary unspawnHandlers = new Dictionary(); // spawning - static bool isSpawnFinished; + // internal for tests + internal static bool isSpawnFinished; // Disabled scene objects that can be spawned again, by sceneId. internal static readonly Dictionary spawnableObjects = new Dictionary(); + static Unbatcher unbatcher = new Unbatcher(); + + // interest management component (optional) + // only needed for SetHostVisibility + public static InterestManagement aoi; + + // scene loading + public static bool isLoadingScene; + // initialization ////////////////////////////////////////////////////// static void AddTransportHandlers() { - Transport.activeTransport.OnClientConnected = OnTransportConnected; - Transport.activeTransport.OnClientDataReceived = OnTransportData; - Transport.activeTransport.OnClientDisconnected = OnTransportDisconnected; - Transport.activeTransport.OnClientError = OnError; + // += so that other systems can also hook into it (i.e. statistics) + Transport.activeTransport.OnClientConnected += OnTransportConnected; + Transport.activeTransport.OnClientDataReceived += OnTransportData; + Transport.activeTransport.OnClientDisconnected += OnTransportDisconnected; + Transport.activeTransport.OnClientError += OnTransportError; + } + + static void RemoveTransportHandlers() + { + // -= so that other systems can also hook into it (i.e. statistics) + Transport.activeTransport.OnClientConnected -= OnTransportConnected; + Transport.activeTransport.OnClientDataReceived -= OnTransportData; + Transport.activeTransport.OnClientDisconnected -= OnTransportDisconnected; + Transport.activeTransport.OnClientError -= OnTransportError; } internal static void RegisterSystemHandlers(bool hostMode) { - // host mode client / regular client react to some messages differently. + // host mode client / remote client react to some messages differently. // but we still need to add handlers for all of them to avoid // 'message id not found' errors. if (hostMode) { RegisterHandler(OnHostClientObjectDestroy); RegisterHandler(OnHostClientObjectHide); - RegisterHandler(msg => {}, false); + RegisterHandler(_ => {}, false); RegisterHandler(OnHostClientSpawn); // host mode doesn't need spawning - RegisterHandler(msg => {}); + RegisterHandler(_ => {}); // host mode doesn't need spawning - RegisterHandler(msg => {}); + RegisterHandler(_ => {}); // host mode doesn't need state updates - RegisterHandler(msg => {}); + RegisterHandler(_ => {}); } else { @@ -124,6 +149,9 @@ namespace Mirror RegisterHandler(OnObjectSpawnFinished); RegisterHandler(OnEntityStateMessage); } + + // These handlers are the same for host and remote clients + RegisterHandler(OnChangeOwner); RegisterHandler(OnRPCMessage); } @@ -131,7 +159,7 @@ namespace Mirror /// Connect client to a NetworkServer by address. public static void Connect(string address) { - // Debug.Log("Client Connect: " + address); + // Debug.Log($"Client Connect: {address}"); Debug.Assert(Transport.activeTransport != null, "There was no active transport when calling NetworkClient.Connect, If you are calling Connect manually then make sure to set 'Transport.activeTransport' first"); RegisterSystemHandlers(false); @@ -147,7 +175,7 @@ namespace Mirror /// Connect client to a NetworkServer by Uri. public static void Connect(Uri uri) { - // Debug.Log("Client Connect: " + uri); + // Debug.Log($"Client Connect: {uri}"); Debug.Assert(Transport.activeTransport != null, "There was no active transport when calling NetworkClient.Connect, If you are calling Connect manually then make sure to set 'Transport.activeTransport' first"); RegisterSystemHandlers(false); @@ -205,35 +233,27 @@ namespace Mirror /// Disconnect from server. public static void Disconnect() { - // only if connected or connecting - if (connectState == ConnectState.Disconnected) return; + // only if connected or connecting. + // don't disconnect() again if already in the process of + // disconnecting or fully disconnected. + if (connectState != ConnectState.Connecting && + connectState != ConnectState.Connected) + return; + // we are disconnecting until OnTransportDisconnected is called. + // setting state to Disconnected would stop OnTransportDisconnected + // from calling cleanup code because it would think we are already + // disconnected fully. // TODO move to 'cleanup' code below if safe - connectState = ConnectState.Disconnected; + connectState = ConnectState.Disconnecting; ready = false; // call Disconnect on the NetworkConnection connection?.Disconnect(); - // clean up - // (previously only for remote connection, not for local) - connection = null; - } - - /// Disconnect host mode. - // this is needed to call DisconnectMessage for the host client too. - [Obsolete("Call NetworkClient.Disconnect() instead. Nobody should use DisconnectLocalServer.")] - public static void DisconnectLocalServer() - { - // only if host connection is running - if (NetworkServer.localConnection != null) - { - // TODO ConnectLocalServer manually sends a ConnectMessage to the - // local connection. should we send a DisconnectMessage here too? - // (if we do then we get an Unknown Message ID log) - //NetworkServer.localConnection.Send(new DisconnectMessage()); - NetworkServer.OnTransportDisconnected(NetworkServer.localConnection.connectionId); - } + // IMPORTANT: do NOT clear connection here yet. + // we still need it in OnTransportDisconnected for callbacks. + // connection = null; } // transport events //////////////////////////////////////////////////// @@ -243,7 +263,10 @@ namespace Mirror if (connection != null) { // reset network time stats - NetworkTime.Reset(); + NetworkTime.ResetStatics(); + + // reset unbatcher in case any batches from last session remain. + unbatcher = new Unbatcher(); // the handler may want to send messages to the client // thus we should set the connected state before calling the handler @@ -263,19 +286,33 @@ namespace Mirror if (handlers.TryGetValue(msgType, out NetworkMessageDelegate handler)) { handler.Invoke(connection, reader, channelId); - connection.lastMessageTime = Time.time; + + // message handler may disconnect client, making connection = null + // therefore must check for null to avoid NRE. + if (connection != null) + connection.lastMessageTime = Time.time; + return true; } else { - // Debug.Log("Unknown message ID " + msgType + " " + this + ". May be due to no existing RegisterHandler for this message."); + // message in a batch are NOT length prefixed to save bandwidth. + // every message needs to be handled and read until the end. + // otherwise it would overlap into the next message. + // => need to warn and disconnect to avoid undefined behaviour. + // => WARNING, not error. can happen if attacker sends random data. + Debug.LogWarning($"Unknown message id: {msgType}. This can happen if no handler was registered for this message."); + // simply return false. caller is responsible for disconnecting. + //connection.Disconnect(); return false; } } else { - Debug.LogError("Closed connection: " + connection + ". Invalid message header."); - connection.Disconnect(); + // => WARNING, not error. can happen if attacker sends random data. + Debug.LogWarning("Invalid message header."); + // simply return false. caller is responsible for disconnecting. + //connection.Disconnect(); return false; } } @@ -285,23 +322,81 @@ namespace Mirror { if (connection != null) { - if (data.Count < MessagePacking.HeaderSize) + // server might batch multiple messages into one packet. + // feed it to the Unbatcher. + // NOTE: we don't need to associate a channelId because we + // always process all messages in the batch. + if (!unbatcher.AddBatch(data)) { - Debug.LogError($"NetworkClient: received Message was too short (messages should start with message id)"); + Debug.LogWarning($"NetworkClient: failed to add batch, disconnecting."); connection.Disconnect(); return; } - // unpack message - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(data)) + // process all messages in the batch. + // only while NOT loading a scene. + // if we get a scene change message, then we need to stop + // processing. otherwise we might apply them to the old scene. + // => fixes https://github.com/vis2k/Mirror/issues/2651 + // + // NOTE: is scene starts loading, then the rest of the batch + // would only be processed when OnTransportData is called + // the next time. + // => consider moving processing to NetworkEarlyUpdate. + while (!isLoadingScene && + unbatcher.GetNextMessage(out NetworkReader reader, out double remoteTimestamp)) { - // server might batch multiple messages into one packet. - // we need to try to unpack multiple times. - while (reader.Position < reader.Length) + // enough to read at least header size? + if (reader.Remaining >= MessagePacking.HeaderSize) { + // make remoteTimeStamp available to the user + connection.remoteTimeStamp = remoteTimestamp; + + // handle message if (!UnpackAndInvoke(reader, channelId)) - break; + { + // warn, disconnect and return if failed + // -> warning because attackers might send random data + // -> messages in a batch aren't length prefixed. + // failing to read one would cause undefined + // behaviour for every message afterwards. + // so we need to disconnect. + // -> return to avoid the below unbatches.count error. + // we already disconnected and handled it. + Debug.LogWarning($"NetworkClient: failed to unpack and invoke message. Disconnecting."); + connection.Disconnect(); + return; + } } + // otherwise disconnect + else + { + // WARNING, not error. can happen if attacker sends random data. + Debug.LogWarning($"NetworkClient: received Message was too short (messages should start with message id)"); + connection.Disconnect(); + return; + } + } + + // if we weren't interrupted by a scene change, + // then all batched messages should have been processed now. + // if not, we need to log an error to avoid debugging hell. + // otherwise batches would silently grow. + // we need to log an error to avoid debugging hell. + // + // EXAMPLE: https://github.com/vis2k/Mirror/issues/2882 + // -> UnpackAndInvoke silently returned because no handler for id + // -> Reader would never be read past the end + // -> Batch would never be retired because end is never reached + // + // NOTE: prefixing every message in a batch with a length would + // avoid ever not reading to the end. for extra bandwidth. + // + // IMPORTANT: always keep this check to detect memory leaks. + // this took half a day to debug last time. + if (!isLoadingScene && unbatcher.BatchesCount > 0) + { + Debug.LogError($"Still had {unbatcher.BatchesCount} batches remaining after processing, even though processing was not interrupted by a scene change. This should never happen, as it would cause ever growing batches.\nPossible reasons:\n* A message didn't deserialize as much as it serialized\n*There was no message handler for a message id, so the reader wasn't read until the end."); } } else Debug.LogError("Skipped Data message handling because connection is null."); @@ -313,20 +408,38 @@ namespace Mirror // the disconnect immediately. // => which is fine as long as we guarantee it only runs once // => which we do by setting the state to Disconnected! - static void OnTransportDisconnected() + internal static void OnTransportDisconnected() { // StopClient called from user code triggers Disconnected event // from transport which calls StopClient again, so check here // and short circuit running the Shutdown process twice. if (connectState == ConnectState.Disconnected) return; + // Raise the event before changing ConnectState + // because 'active' depends on this during shutdown + if (connection != null) OnDisconnectedEvent?.Invoke(); + connectState = ConnectState.Disconnected; ready = false; - if (connection != null) OnDisconnectedEvent?.Invoke(); + // now that everything was handled, clear the connection. + // previously this was done in Disconnect() already, but we still + // need it for the above OnDisconnectedEvent. + connection = null; + + // transport handlers are only added when connecting. + // so only remove when actually disconnecting. + RemoveTransportHandlers(); } - static void OnError(Exception exception) => Debug.LogException(exception); + // transport errors are forwarded to high level + static void OnTransportError(TransportError error, string reason) + { + // transport errors will happen. logging a warning is enough. + // make sure the user does not panic. + Debug.LogWarning($"Client Transport Error: {error}: {reason}. This is fine."); + OnErrorEvent?.Invoke(error, reason); + } // send //////////////////////////////////////////////////////////////// /// Send a NetworkMessage to the server over the given channel. @@ -345,19 +458,6 @@ namespace Mirror } // message handlers //////////////////////////////////////////////////// - /// Register a handler for a message type T. Most should require authentication. - [Obsolete("Use RegisterHandler version without NetworkConnection parameter. It always points to NetworkClient.connection anyway.")] - public static void RegisterHandler(Action handler, bool requireAuthentication = true) - where T : struct, NetworkMessage - { - ushort msgType = MessagePacking.GetId(); - if (handlers.ContainsKey(msgType)) - { - Debug.LogWarning($"NetworkClient.RegisterHandler replacing handler for {typeof(T).FullName}, id={msgType}. If replacement is intentional, use ReplaceHandler instead to avoid this warning."); - } - handlers[msgType] = MessagePacking.WrapHandler(handler, requireAuthentication); - } - /// Register a handler for a message type T. Most should require authentication. public static void RegisterHandler(Action handler, bool requireAuthentication = true) where T : struct, NetworkMessage @@ -375,7 +475,8 @@ namespace Mirror } /// Replace a handler for a particular message type. Should require authentication by default. - // TODO does anyone even use that? consider removing + // RegisterHandler throws a warning (as it should) if a handler is assigned twice + // Use of ReplaceHandler makes it clear the user intended to replace the handler public static void ReplaceHandler(Action handler, bool requireAuthentication = true) where T : struct, NetworkMessage { @@ -384,7 +485,8 @@ namespace Mirror } /// Replace a handler for a particular message type. Should require authentication by default. - // TODO does anyone even use that? consider removing + // RegisterHandler throws a warning (as it should) if a handler is assigned twice + // Use of ReplaceHandler makes it clear the user intended to replace the handler public static void ReplaceHandler(Action handler, bool requireAuthentication = true) where T : struct, NetworkMessage { @@ -428,7 +530,7 @@ namespace Mirror NetworkIdentity[] identities = prefab.GetComponentsInChildren(); if (identities.Length > 1) { - Debug.LogWarning($"Prefab '{prefab.name}' has multiple NetworkIdentity components. There should only be one NetworkIdentity on a prefab, and it must be on the root object."); + Debug.LogError($"Prefab '{prefab.name}' has multiple NetworkIdentity components. There should only be one NetworkIdentity on a prefab, and it must be on the root object."); } if (prefabs.ContainsKey(prefab.assetId)) @@ -439,7 +541,7 @@ namespace Mirror if (spawnHandlers.ContainsKey(prefab.assetId) || unspawnHandlers.ContainsKey(prefab.assetId)) { - Debug.LogWarning($"Adding prefab '{prefab.name}' with assetId '{prefab.assetId}' when spawnHandlers with same assetId already exists."); + Debug.LogWarning($"Adding prefab '{prefab.name}' with assetId '{prefab.assetId}' when spawnHandlers with same assetId already exists. If you want to use custom spawn handling, then remove the prefab from NetworkManager's registered prefabs first."); } // Debug.Log($"Registering prefab '{prefab.name}' as asset:{prefab.assetId}"); @@ -532,7 +634,7 @@ namespace Mirror NetworkIdentity identity = prefab.GetComponent(); if (identity == null) { - Debug.LogError("Could not register handler for '" + prefab.name + "' since it contains no NetworkIdentity component"); + Debug.LogError($"Could not register handler for '{prefab.name}' since it contains no NetworkIdentity component"); return; } @@ -582,7 +684,7 @@ namespace Mirror NetworkIdentity identity = prefab.GetComponent(); if (identity == null) { - Debug.LogError("Could not register handler for '" + prefab.name + "' since it contains no NetworkIdentity component"); + Debug.LogError($"Could not register handler for '{prefab.name}' since it contains no NetworkIdentity component"); return; } @@ -627,10 +729,10 @@ namespace Mirror NetworkIdentity[] identities = prefab.GetComponentsInChildren(); if (identities.Length > 1) { - Debug.LogWarning($"Prefab '{prefab.name}' has multiple NetworkIdentity components. There should only be one NetworkIdentity on a prefab, and it must be on the root object."); + Debug.LogError($"Prefab '{prefab.name}' has multiple NetworkIdentity components. There should only be one NetworkIdentity on a prefab, and it must be on the root object."); } - // Debug.Log("Registering custom prefab '" + prefab.name + "' as asset:" + assetId + " " + spawnHandler.GetMethodName() + "/" + unspawnHandler.GetMethodName()); + //Debug.Log($"Registering custom prefab {prefab.name} as asset:{assetId} {spawnHandler.GetMethodName()}/{unspawnHandler.GetMethodName()}"); spawnHandlers[assetId] = spawnHandler; unspawnHandlers[assetId] = unspawnHandler; @@ -649,7 +751,7 @@ namespace Mirror NetworkIdentity identity = prefab.GetComponent(); if (identity == null) { - Debug.LogError("Could not register handler for '" + prefab.name + "' since it contains no NetworkIdentity component"); + Debug.LogError($"Could not register handler for '{prefab.name}' since it contains no NetworkIdentity component"); return; } @@ -693,10 +795,10 @@ namespace Mirror NetworkIdentity[] identities = prefab.GetComponentsInChildren(); if (identities.Length > 1) { - Debug.LogWarning($"Prefab '{prefab.name}' has multiple NetworkIdentity components. There should only be one NetworkIdentity on a prefab, and it must be on the root object."); + Debug.LogError($"Prefab '{prefab.name}' has multiple NetworkIdentity components. There should only be one NetworkIdentity on a prefab, and it must be on the root object."); } - // Debug.Log("Registering custom prefab '" + prefab.name + "' as asset:" + assetId + " " + spawnHandler.GetMethodName() + "/" + unspawnHandler.GetMethodName()); + //Debug.Log($"Registering custom prefab {prefab.name} as asset:{assetId} {spawnHandler.GetMethodName()}/{unspawnHandler.GetMethodName()}"); spawnHandlers[assetId] = spawnHandler; unspawnHandlers[assetId] = unspawnHandler; @@ -714,7 +816,7 @@ namespace Mirror NetworkIdentity identity = prefab.GetComponent(); if (identity == null) { - Debug.LogError("Could not unregister '" + prefab.name + "' since it contains no NetworkIdentity component"); + Debug.LogError($"Could not unregister '{prefab.name}' since it contains no NetworkIdentity component"); return; } @@ -781,7 +883,7 @@ namespace Mirror Debug.LogError($"assetId '{assetId}' is already used by prefab '{prefabs[assetId].name}'"); } - // Debug.Log("RegisterSpawnHandler asset '" + assetId + "' " + spawnHandler.GetMethodName() + "/" + unspawnHandler.GetMethodName()); + // Debug.Log("RegisterSpawnHandler asset {assetId} {spawnHandler.GetMethodName()}/{unspawnHandler.GetMethodName()}"); spawnHandlers[assetId] = spawnHandler; unspawnHandlers[assetId] = unspawnHandler; @@ -820,7 +922,7 @@ namespace Mirror // the players object for example. public static bool Ready() { - // Debug.Log("NetworkClient.Ready() called with connection [" + conn + "]"); + // Debug.Log($"NetworkClient.Ready() called with connection {conn}"); if (ready) { Debug.LogError("NetworkClient is already ready. It shouldn't be called twice."); @@ -845,9 +947,6 @@ namespace Mirror return true; } - [Obsolete("NetworkClient.Ready doesn't need a NetworkConnection parameter anymore. It always uses NetworkClient.connection anyway.")] - public static bool Ready(NetworkConnection conn) => Ready(); - // add player ////////////////////////////////////////////////////////// // called from message handler for Owner message internal static void InternalAddPlayer(NetworkIdentity identity) @@ -895,14 +994,11 @@ namespace Mirror return false; } - // Debug.Log("NetworkClient.AddPlayer() called with connection [" + readyConnection + "]"); + // Debug.Log($"NetworkClient.AddPlayer() called with connection {readyConnection}"); connection.Send(new AddPlayerMessage()); return true; } - [Obsolete("NetworkClient.AddPlayer doesn't need a NetworkConnection parameter anymore. It always uses NetworkClient.connection anyway.")] - public static bool AddPlayer(NetworkConnection readyConn) => AddPlayer(); - // spawning //////////////////////////////////////////////////////////// internal static void ApplySpawnPayload(NetworkIdentity identity, SpawnMessage message) { @@ -928,15 +1024,20 @@ namespace Mirror // (Count is 0 if there were no components) if (message.payload.Count > 0) { - using (PooledNetworkReader payloadReader = NetworkReaderPool.GetReader(message.payload)) + using (NetworkReaderPooled payloadReader = NetworkReaderPool.Get(message.payload)) { identity.OnDeserializeAllSafely(payloadReader, true); } } - NetworkIdentity.spawned[message.netId] = identity; + spawned[message.netId] = identity; - // objects spawned as part of initial state are started on a second pass + // the initial spawn with OnObjectSpawnStarted/Finished calls all + // object's OnStartClient/OnStartLocalPlayer after they were all + // spawned. + // this only happens once though. + // for all future spawns, we need to call OnStartClient/LocalPlayer + // here immediately since there won't be another OnObjectSpawnFinished. if (isSpawnFinished) { identity.NotifyAuthority(); @@ -963,7 +1064,7 @@ namespace Mirror return false; } - identity = message.sceneId == 0 ? SpawnPrefab(message) : SpawnSceneObject(message); + identity = message.sceneId == 0 ? SpawnPrefab(message) : SpawnSceneObject(message.sceneId); if (identity == null) { @@ -976,18 +1077,19 @@ namespace Mirror static NetworkIdentity GetExistingObject(uint netid) { - NetworkIdentity.spawned.TryGetValue(netid, out NetworkIdentity localObject); + spawned.TryGetValue(netid, out NetworkIdentity localObject); return localObject; } static NetworkIdentity SpawnPrefab(SpawnMessage message) { - if (GetPrefab(message.assetId, out GameObject prefab)) - { - GameObject obj = GameObject.Instantiate(prefab, message.position, message.rotation); - //Debug.Log("Client spawn handler instantiating [netId:" + msg.netId + " asset ID:" + msg.assetId + " pos:" + msg.position + " rotation: " + msg.rotation + "]"); - return obj.GetComponent(); - } + // custom spawn handler for this prefab? (for prefab pools etc.) + // + // IMPORTANT: look for spawn handlers BEFORE looking for registered + // prefabs. Unspawning also looks for unspawn handlers + // before falling back to regular Destroy. this needs to + // be consistent. + // https://github.com/vis2k/Mirror/issues/2705 if (spawnHandlers.TryGetValue(message.assetId, out SpawnHandlerDelegate handler)) { GameObject obj = handler(message); @@ -1004,16 +1106,25 @@ namespace Mirror } return identity; } + + // otherwise look in NetworkManager registered prefabs + if (GetPrefab(message.assetId, out GameObject prefab)) + { + GameObject obj = GameObject.Instantiate(prefab, message.position, message.rotation); + //Debug.Log($"Client spawn handler instantiating [netId{message.netId} asset ID:{message.assetId} pos:{message.position} rotation:{message.rotation}]"); + return obj.GetComponent(); + } + Debug.LogError($"Failed to spawn server object, did you forget to add it to the NetworkManager? assetId={message.assetId} netId={message.netId}"); return null; } - static NetworkIdentity SpawnSceneObject(SpawnMessage message) + static NetworkIdentity SpawnSceneObject(ulong sceneId) { - NetworkIdentity identity = GetAndRemoveSceneObject(message.sceneId); + NetworkIdentity identity = GetAndRemoveSceneObject(sceneId); if (identity == null) { - Debug.LogError($"Spawn scene object not found for {message.sceneId:X}. Make sure that client and server use exactly the same project. This only happens if the hierarchy gets out of sync."); + Debug.LogError($"Spawn scene object not found for {sceneId:X}. Make sure that client and server use exactly the same project. This only happens if the hierarchy gets out of sync."); // dump the whole spawnable objects dict for easier debugging //foreach (KeyValuePair kvp in spawnableObjects) @@ -1034,9 +1145,14 @@ namespace Mirror } // Checks if identity is not spawned yet, not hidden and has sceneId + // TODO merge with ValidateSceneObject on server static bool ConsiderForSpawning(NetworkIdentity identity) { // not spawned yet, not hidden, etc.? + + // need to ensure it's not active yet because + // PrepareToSpawnSceneObjects may be called multiple times in case + // the ObjectSpawnStarted message is received multiple times. return !identity.gameObject.activeSelf && identity.gameObject.hideFlags != HideFlags.NotEditable && identity.gameObject.hideFlags != HideFlags.HideAndDontSave && @@ -1056,7 +1172,17 @@ namespace Mirror // add all unspawned NetworkIdentities to spawnable objects if (ConsiderForSpawning(identity)) { - spawnableObjects.Add(identity.sceneId, identity); + if (spawnableObjects.TryGetValue(identity.sceneId, out NetworkIdentity existingIdentity)) + { + string msg = $"NetworkClient: Duplicate sceneId {identity.sceneId} detected on {identity.gameObject.name} and {existingIdentity.gameObject.name}\n" + + $"This can happen if a networked object is persisted in DontDestroyOnLoad through loading / changing to the scene where it originated,\n" + + $"otherwise you may need to open and re-save the {identity.gameObject.scene} to reset scene id's."; + Debug.LogWarning(msg, identity.gameObject); + } + else + { + spawnableObjects.Add(identity.sceneId, identity); + } } } } @@ -1076,7 +1202,7 @@ namespace Mirror // paul: Initialize the objects in the same order as they were // initialized in the server. This is important if spawned objects // use data from scene objects - foreach (NetworkIdentity identity in NetworkIdentity.spawned.Values.OrderBy(uv => uv.netId)) + foreach (NetworkIdentity identity in spawned.Values.OrderBy(uv => uv.netId)) { identity.NotifyAuthority(); identity.OnStartClient(); @@ -1093,7 +1219,7 @@ namespace Mirror // loop below does not get NullReferenceException // see https://github.com/vis2k/Mirror/pull/2240 // TODO fix scene logic so that client scene doesn't have null objects - foreach (KeyValuePair kvp in NetworkIdentity.spawned) + foreach (KeyValuePair kvp in spawned) { if (kvp.Value == null) { @@ -1104,7 +1230,7 @@ namespace Mirror // can't modify NetworkIdentity.spawned inside foreach so need 2nd loop to remove foreach (uint id in removeFromSpawned) { - NetworkIdentity.spawned.Remove(id); + spawned.Remove(id); } removeFromSpawned.Clear(); } @@ -1112,37 +1238,40 @@ namespace Mirror // host mode callbacks ///////////////////////////////////////////////// static void OnHostClientObjectDestroy(ObjectDestroyMessage message) { - // Debug.Log("NetworkClient.OnLocalObjectObjDestroy netId:" + msg.netId); - - // TODO why do we do this? - // in host mode, .spawned is shared between server and client. - // removing it on client would remove it on server. - // huh. - NetworkIdentity.spawned.Remove(message.netId); + //Debug.Log($"NetworkClient.OnLocalObjectObjDestroy netId:{message.netId}"); + spawned.Remove(message.netId); } static void OnHostClientObjectHide(ObjectHideMessage message) { - // Debug.Log("ClientScene::OnLocalObjectObjHide netId:" + msg.netId); - if (NetworkIdentity.spawned.TryGetValue(message.netId, out NetworkIdentity localObject) && + //Debug.Log($"ClientScene::OnLocalObjectObjHide netId:{message.netId}"); + if (spawned.TryGetValue(message.netId, out NetworkIdentity localObject) && localObject != null) { - localObject.OnSetHostVisibility(false); + if (aoi != null) + aoi.SetHostVisibility(localObject, false); } } internal static void OnHostClientSpawn(SpawnMessage message) { - if (NetworkIdentity.spawned.TryGetValue(message.netId, out NetworkIdentity localObject) && - localObject != null) + // on host mode, the object already exist in NetworkServer.spawned. + // simply add it to NetworkClient.spawned too. + if (NetworkServer.spawned.TryGetValue(message.netId, out NetworkIdentity localObject) && localObject != null) { + spawned[message.netId] = localObject; + + // now do the actual 'spawning' on host mode if (message.isLocalPlayer) InternalAddPlayer(localObject); localObject.hasAuthority = message.isOwner; localObject.NotifyAuthority(); localObject.OnStartClient(); - localObject.OnSetHostVisibility(true); + + if (aoi != null) + aoi.SetHostVisibility(localObject, true); + CheckForLocalPlayer(localObject); } } @@ -1150,22 +1279,22 @@ namespace Mirror // client-only mode callbacks ////////////////////////////////////////// static void OnEntityStateMessage(EntityStateMessage message) { - // Debug.Log("NetworkClient.OnUpdateVarsMessage " + msg.netId); - if (NetworkIdentity.spawned.TryGetValue(message.netId, out NetworkIdentity localObject) && localObject != null) + // Debug.Log($"NetworkClient.OnUpdateVarsMessage {msg.netId}"); + if (spawned.TryGetValue(message.netId, out NetworkIdentity localObject) && localObject != null) { - using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(message.payload)) + using (NetworkReaderPooled networkReader = NetworkReaderPool.Get(message.payload)) localObject.OnDeserializeAllSafely(networkReader, false); } - else Debug.LogWarning("Did not find target for sync message for " + message.netId + " . Note: this can be completely normal because UDP messages may arrive out of order, so this message might have arrived after a Destroy message."); + else Debug.LogWarning($"Did not find target for sync message for {message.netId} . Note: this can be completely normal because UDP messages may arrive out of order, so this message might have arrived after a Destroy message."); } static void OnRPCMessage(RpcMessage message) { - // Debug.Log("NetworkClient.OnRPCMessage hash:" + msg.functionHash + " netId:" + msg.netId); - if (NetworkIdentity.spawned.TryGetValue(message.netId, out NetworkIdentity identity)) + // Debug.Log($"NetworkClient.OnRPCMessage hash:{msg.functionHash} netId:{msg.netId}"); + if (spawned.TryGetValue(message.netId, out NetworkIdentity identity)) { - using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(message.payload)) - identity.HandleRemoteCall(message.componentIndex, message.functionHash, MirrorInvokeType.ClientRpc, networkReader); + using (NetworkReaderPooled networkReader = NetworkReaderPool.Get(message.payload)) + identity.HandleRemoteCall(message.componentIndex, message.functionHash, RemoteCallType.ClientRpc, networkReader); } } @@ -1182,6 +1311,50 @@ namespace Mirror } } + internal static void OnChangeOwner(ChangeOwnerMessage message) + { + NetworkIdentity identity = GetExistingObject(message.netId); + + if (identity != null) + ChangeOwner(identity, message); + else + Debug.LogError($"OnChangeOwner: Could not find object with netId {message.netId}"); + } + + // ChangeOwnerMessage contains new 'owned' and new 'localPlayer' + // that we need to apply to the identity. + internal static void ChangeOwner(NetworkIdentity identity, ChangeOwnerMessage message) + { + // local player before, but not anymore? + // call OnStopLocalPlayer before setting new values. + if (identity.isLocalPlayer && !message.isLocalPlayer) + { + identity.OnStopLocalPlayer(); + } + + // set ownership flag (aka authority) + identity.hasAuthority = message.isOwner; + identity.NotifyAuthority(); + + // set localPlayer flag + identity.isLocalPlayer = message.isLocalPlayer; + + // identity is now local player. set our static helper field to it. + if (identity.isLocalPlayer) + { + localPlayer = identity; + } + // identity's isLocalPlayer was set to false. + // clear our static localPlayer IF (and only IF) it was that one before. + else if (localPlayer == identity) + { + localPlayer = null; + } + + // call OnStartLocalPlayer if it's the local player now. + CheckForLocalPlayer(identity); + } + internal static void CheckForLocalPlayer(NetworkIdentity identity) { if (identity == localPlayer) @@ -1190,25 +1363,28 @@ namespace Mirror // OnStartLocalPlayer in all scripts on the same GO identity.connectionToServer = connection; identity.OnStartLocalPlayer(); - // Debug.Log("NetworkClient.OnOwnerMessage - player=" + identity.name); + // Debug.Log($"NetworkClient.OnOwnerMessage player:{identity.name}"); } } // destroy ///////////////////////////////////////////////////////////// static void DestroyObject(uint netId) { - // Debug.Log("NetworkClient.OnObjDestroy netId:" + netId); - if (NetworkIdentity.spawned.TryGetValue(netId, out NetworkIdentity localObject) && localObject != null) + // Debug.Log($"NetworkClient.OnObjDestroy netId: {netId}"); + if (spawned.TryGetValue(netId, out NetworkIdentity localObject) && localObject != null) { + if (localObject.isLocalPlayer) + localObject.OnStopLocalPlayer(); + localObject.OnStopClient(); - // user handling + // custom unspawn handler for this prefab? (for prefab pools etc.) if (InvokeUnSpawnHandler(localObject.assetId, localObject.gameObject)) { // reset object after user's handler localObject.Reset(); } - // default handling + // otherwise fall back to default Destroy else if (localObject.sceneId == 0) { // don't call reset before destroy so that values are still set in OnDestroy @@ -1224,9 +1400,9 @@ namespace Mirror } // remove from dictionary no matter how it is unspawned - NetworkIdentity.spawned.Remove(netId); + spawned.Remove(netId); } - //else Debug.LogWarning("Did not find target for destroy message for " + netId); + //else Debug.LogWarning($"Did not find target for destroy message for {netId}"); } // update ////////////////////////////////////////////////////////////// @@ -1249,12 +1425,16 @@ namespace Mirror localConnection.Update(); } // remote connection? - else + else if (connection is NetworkConnectionToServer remoteConnection) { // only update things while connected if (active && connectState == ConnectState.Connected) { + // update NetworkTime NetworkTime.UpdateClient(); + + // update connection to flush out batched messages + remoteConnection.Update(); } } @@ -1263,10 +1443,6 @@ namespace Mirror Transport.activeTransport.ClientLateUpdate(); } - // obsolete to not break people's projects. Update was public. - [Obsolete("NetworkClient.Update is now called internally from our custom update loop. No need to call Update manually anymore.")] - public static void Update() => NetworkLateUpdate(); - // shutdown //////////////////////////////////////////////////////////// /// Destroys all networked objects on the client. // Note: NetworkServer.CleanupNetworkIdentities does the same on server. @@ -1277,30 +1453,53 @@ namespace Mirror // we need the Try/Catch so that the rest of the shutdown does not get stopped try { - foreach (NetworkIdentity identity in NetworkIdentity.spawned.Values) + foreach (NetworkIdentity identity in spawned.Values) { if (identity != null && identity.gameObject != null) { + if (identity.isLocalPlayer) + identity.OnStopLocalPlayer(); + identity.OnStopClient(); - bool wasUnspawned = InvokeUnSpawnHandler(identity.assetId, identity.gameObject); - if (!wasUnspawned) + + // NetworkClient.Shutdown calls DestroyAllClientObjects. + // which destroys all objects in NetworkClient.spawned. + // => NC.spawned contains owned & observed objects + // => in host mode, we CAN NOT destroy observed objects. + // => that would destroy them other connection's objects + // on the host server, making them disconnect. + // https://github.com/vis2k/Mirror/issues/2954 + bool hostOwned = identity.connectionToServer is LocalConnectionToServer; + bool shouldDestroy = !identity.isServer || hostOwned; + if (shouldDestroy) { - // scene objects are reset and disabled. - // they always stay in the scene, we don't destroy them. - if (identity.sceneId != 0) + bool wasUnspawned = InvokeUnSpawnHandler(identity.assetId, identity.gameObject); + + // unspawned objects should be reset for reuse later. + if (wasUnspawned) { identity.Reset(); - identity.gameObject.SetActive(false); } - // spawned objects are destroyed + // without unspawn handler, we need to disable/destroy. else { - GameObject.Destroy(identity.gameObject); + // scene objects are reset and disabled. + // they always stay in the scene, we don't destroy them. + if (identity.sceneId != 0) + { + identity.Reset(); + identity.gameObject.SetActive(false); + } + // spawned objects are destroyed + else + { + GameObject.Destroy(identity.gameObject); + } } } } } - NetworkIdentity.spawned.Clear(); + spawned.Clear(); } catch (InvalidOperationException e) { @@ -1310,23 +1509,54 @@ namespace Mirror } /// Shutdown the client. + // RuntimeInitializeOnLoadMethod -> fast playmode without domain reload + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] public static void Shutdown() { - Debug.Log("Shutting down client."); + //Debug.Log("Shutting down client."); + + // calls prefabs.Clear(); + // calls spawnHandlers.Clear(); + // calls unspawnHandlers.Clear(); ClearSpawners(); - spawnableObjects.Clear(); - ready = false; - isSpawnFinished = false; + + // calls spawned.Clear() if no exception occurs DestroyAllClientObjects(); - connectState = ConnectState.None; + + spawned.Clear(); handlers.Clear(); + spawnableObjects.Clear(); + + // IMPORTANT: do NOT call NetworkIdentity.ResetStatics() here! + // calling StopClient() in host mode would reset nextNetId to 1, + // causing next connection to have a duplicate netId accidentally. + // => see also: https://github.com/vis2k/Mirror/issues/2954 + //NetworkIdentity.ResetStatics(); + // => instead, reset only the client sided statics. + NetworkIdentity.ResetClientStatics(); + // disconnect the client connection. // we do NOT call Transport.Shutdown, because someone only called // NetworkClient.Shutdown. we can't assume that the server is // supposed to be shut down too! if (Transport.activeTransport != null) Transport.activeTransport.ClientDisconnect(); + + // reset statics + connectState = ConnectState.None; connection = null; + localPlayer = null; + ready = false; + isSpawnFinished = false; + isLoadingScene = false; + + unbatcher = new Unbatcher(); + + // clear events. someone might have hooked into them before, but + // we don't want to use those hooks after Shutdown anymore. + OnConnectedEvent = null; + OnDisconnectedEvent = null; + OnErrorEvent = null; } } } diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkClient.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkClient.cs.meta index bbf1a4d..20cb211 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkClient.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/NetworkClient.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkConnection.cs b/UnityProject/Assets/Mirror/Runtime/NetworkConnection.cs index 36da18a..14729c6 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkConnection.cs +++ b/UnityProject/Assets/Mirror/Runtime/NetworkConnection.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using UnityEngine; namespace Mirror @@ -9,9 +10,10 @@ namespace Mirror { public const int LocalConnectionId = 0; - // NetworkIdentities that this connection can see - // TODO move to server's NetworkConnectionToClient? - internal readonly HashSet observing = new HashSet(); + /// NetworkIdentities that this connection can see + // DEPRECATED 2022-02-05 + [Obsolete("Cast to NetworkConnectionToClient to access .observing")] + public HashSet observing => ((NetworkConnectionToClient)this).observing; /// Unique identifier for this connection that is assigned by the transport layer. // assigned by transport, this id is unique for every connection on server. @@ -44,7 +46,29 @@ namespace Mirror // fixes a bug where DestroyOwnedObjects wouldn't find the // netId anymore: https://github.com/vis2k/Mirror/issues/1380 // Works fine with NetworkIdentity pointers though. - public readonly HashSet clientOwnedObjects = new HashSet(); + // DEPRECATED 2022-02-05 + [Obsolete("Cast to NetworkConnectionToClient to access .clientOwnedObjects")] + public HashSet clientOwnedObjects => ((NetworkConnectionToClient)this).clientOwnedObjects; + + // batching from server to client & client to server. + // fewer transport calls give us significantly better performance/scale. + // + // for a 64KB max message transport and 64 bytes/message on average, we + // reduce transport calls by a factor of 1000. + // + // depending on the transport, this can give 10x performance. + // + // Dictionary because we have multiple channels. + protected Dictionary batches = new Dictionary(); + + /// last batch's remote timestamp. not interpolated. useful for NetworkTransform etc. + // for any given NetworkMessage/Rpc/Cmd/OnSerialize, this was the time + // on the REMOTE END when it was sent. + // + // NOTE: this is NOT in NetworkTime, it needs to be per-connection + // because the server receives different batch timestamps from + // different connections. + public double remoteTimeStamp { get; internal set; } internal NetworkConnection() { @@ -56,9 +80,134 @@ namespace Mirror internal NetworkConnection(int networkConnectionId) : this() { connectionId = networkConnectionId; - // TODO why isn't lastMessageTime set in here like in the other ctor? } + // TODO if we only have Reliable/Unreliable, then we could initialize + // two batches and avoid this code + protected Batcher GetBatchForChannelId(int channelId) + { + // get existing or create new writer for the channelId + Batcher batch; + if (!batches.TryGetValue(channelId, out batch)) + { + // get max batch size for this channel + int threshold = Transport.activeTransport.GetBatchThreshold(channelId); + + // create batcher + batch = new Batcher(threshold); + batches[channelId] = batch; + } + return batch; + } + + // validate packet size before sending. show errors if too big/small. + // => it's best to check this here, we can't assume that all transports + // would check max size and show errors internally. best to do it + // in one place in Mirror. + // => it's important to log errors, so the user knows what went wrong. + protected static bool ValidatePacketSize(ArraySegment segment, int channelId) + { + int max = Transport.activeTransport.GetMaxPacketSize(channelId); + if (segment.Count > max) + { + Debug.LogError($"NetworkConnection.ValidatePacketSize: cannot send packet larger than {max} bytes, was {segment.Count} bytes"); + return false; + } + + if (segment.Count == 0) + { + // zero length packets getting into the packet queues are bad. + Debug.LogError("NetworkConnection.ValidatePacketSize: cannot send zero bytes"); + return false; + } + + // good size + return true; + } + + // Send stage one: NetworkMessage + /// Send a NetworkMessage to this connection over the given channel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Send(T message, int channelId = Channels.Reliable) + where T : struct, NetworkMessage + { + using (NetworkWriterPooled writer = NetworkWriterPool.Get()) + { + // pack message and send allocation free + MessagePacking.Pack(message, writer); + NetworkDiagnostics.OnSend(message, channelId, writer.Position, 1); + Send(writer.ToArraySegment(), channelId); + } + } + + // Send stage two: serialized NetworkMessage as ArraySegment + // internal because no one except Mirror should send bytes directly to + // the client. they would be detected as a message. send messages instead. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal virtual void Send(ArraySegment segment, int channelId = Channels.Reliable) + { + //Debug.Log($"ConnectionSend {this} bytes:{BitConverter.ToString(segment.Array, segment.Offset, segment.Count)}"); + + // add to batch no matter what. + // batching will try to fit as many as possible into MTU. + // but we still allow > MTU, e.g. kcp max packet size 144kb. + // those are simply sent as single batches. + // + // IMPORTANT: do NOT send > batch sized messages directly: + // - data race: large messages would be sent directly. small + // messages would be sent in the batch at the end of frame + // - timestamps: if batching assumes a timestamp, then large + // messages need that too. + // + // NOTE: we ALWAYS batch. it's not optional, because the + // receiver needs timestamps for NT etc. + // + // NOTE: we do NOT ValidatePacketSize here yet. the final packet + // will be the full batch, including timestamp. + GetBatchForChannelId(channelId).AddMessage(segment, NetworkTime.localTime); + } + + // Send stage three: hand off to transport + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected abstract void SendToTransport(ArraySegment segment, int channelId = Channels.Reliable); + + // flush batched messages at the end of every Update. + internal virtual void Update() + { + // go through batches for all channels + foreach (KeyValuePair kvp in batches) + { + // make and send as many batches as necessary from the stored + // messages. + Batcher batcher = kvp.Value; + using (NetworkWriterPooled writer = NetworkWriterPool.Get()) + { + // make a batch with our local time (double precision) + while (batcher.GetBatch(writer)) + { + // validate packet before handing the batch to the + // transport. this guarantees that we always stay + // within transport's max message size limit. + // => just in case transport forgets to check it + // => just in case mirror miscalulated it etc. + ArraySegment segment = writer.ToArraySegment(); + if (ValidatePacketSize(segment, kvp.Key)) + { + // send to transport + SendToTransport(segment, kvp.Key); + //UnityEngine.Debug.Log($"sending batch of {writer.Position} bytes for channel={kvp.Key} connId={connectionId}"); + + // reset writer for each new batch + writer.Position = 0; + } + } + } + } + } + + /// Check if we received a message within the last 'timeout' seconds. + internal virtual bool IsAlive(float timeout) => Time.time - lastMessageTime < timeout; + /// Disconnects this connection. // for future reference, here is how Disconnects work in Mirror. // @@ -79,107 +228,6 @@ namespace Mirror // then later the transport events will do the clean up. public abstract void Disconnect(); - /// Send a NetworkMessage to this connection over the given channel. - public void Send(T msg, int channelId = Channels.Reliable) - where T : struct, NetworkMessage - { - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // pack message and send allocation free - MessagePacking.Pack(msg, writer); - NetworkDiagnostics.OnSend(msg, channelId, writer.Position, 1); - Send(writer.ToArraySegment(), channelId); - } - } - - // validate packet size before sending. show errors if too big/small. - // => it's best to check this here, we can't assume that all transports - // would check max size and show errors internally. best to do it - // in one place in hlapi. - // => it's important to log errors, so the user knows what went wrong. - protected static bool ValidatePacketSize(ArraySegment segment, int channelId) - { - if (segment.Count > Transport.activeTransport.GetMaxPacketSize(channelId)) - { - Debug.LogError($"NetworkConnection.ValidatePacketSize: cannot send packet larger than {Transport.activeTransport.GetMaxPacketSize(channelId)} bytes, was {segment.Count} bytes"); - return false; - } - - if (segment.Count == 0) - { - // zero length packets getting into the packet queues are bad. - Debug.LogError("NetworkConnection.ValidatePacketSize: cannot send zero bytes"); - return false; - } - - // good size - return true; - } - - // internal because no one except Mirror should send bytes directly to - // the client. they would be detected as a message. send messages instead. - internal abstract void Send(ArraySegment segment, int channelId = Channels.Reliable); - public override string ToString() => $"connection({connectionId})"; - - // TODO move to server's NetworkConnectionToClient? - internal void AddToObserving(NetworkIdentity netIdentity) - { - observing.Add(netIdentity); - - // spawn identity for this conn - NetworkServer.ShowForConnection(netIdentity, this); - } - - // TODO move to server's NetworkConnectionToClient? - internal void RemoveFromObserving(NetworkIdentity netIdentity, bool isDestroyed) - { - observing.Remove(netIdentity); - - if (!isDestroyed) - { - // hide identity for this conn - NetworkServer.HideForConnection(netIdentity, this); - } - } - - // TODO move to server's NetworkConnectionToClient? - internal void RemoveFromObservingsObservers() - { - foreach (NetworkIdentity netIdentity in observing) - { - netIdentity.RemoveObserverInternal(this); - } - observing.Clear(); - } - - /// Check if we received a message within the last 'timeout' seconds. - internal virtual bool IsAlive(float timeout) => Time.time - lastMessageTime < timeout; - - internal void AddOwnedObject(NetworkIdentity obj) - { - clientOwnedObjects.Add(obj); - } - - internal void RemoveOwnedObject(NetworkIdentity obj) - { - clientOwnedObjects.Remove(obj); - } - - internal void DestroyOwnedObjects() - { - // create a copy because the list might be modified when destroying - HashSet tmp = new HashSet(clientOwnedObjects); - foreach (NetworkIdentity netIdentity in tmp) - { - if (netIdentity != null) - { - NetworkServer.Destroy(netIdentity.gameObject); - } - } - - // clear the hashset because we destroyed them all - clientOwnedObjects.Clear(); - } } } diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkConnection.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkConnection.cs.meta index 2ece7b6..32c4ba2 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkConnection.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/NetworkConnection.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkConnectionToClient.cs b/UnityProject/Assets/Mirror/Runtime/NetworkConnectionToClient.cs index eb3ddca..4cb56f5 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkConnectionToClient.cs +++ b/UnityProject/Assets/Mirror/Runtime/NetworkConnectionToClient.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; namespace Mirror { @@ -8,148 +9,27 @@ namespace Mirror public override string address => Transport.activeTransport.ServerGetClientAddress(connectionId); - // batching from server to client. - // fewer transport calls give us significantly better performance/scale. - // - // for a 64KB max message transport and 64 bytes/message on average, we - // reduce transport calls by a factor of 1000. - // - // depending on the transport, this can give 10x performance. - // - // Dictionary because we have multiple channels. - internal class Batch - { - // batched messages - // IMPORTANT: we queue the serialized messages! - // queueing NetworkMessage would box and allocate! - internal Queue messages = new Queue(); - } - Dictionary batches = new Dictionary(); + /// NetworkIdentities that this connection can see + // TODO move to server's NetworkConnectionToClient? + public new readonly HashSet observing = new HashSet(); - // batch messages and send them out in LateUpdate (or after batchInterval) - bool batching; + /// All NetworkIdentities owned by this connection. Can be main player, pets, etc. + // IMPORTANT: this needs to be , not . + // fixes a bug where DestroyOwnedObjects wouldn't find the + // netId anymore: https://github.com/vis2k/Mirror/issues/1380 + // Works fine with NetworkIdentity pointers though. + public new readonly HashSet clientOwnedObjects = new HashSet(); - public NetworkConnectionToClient(int networkConnectionId, bool batching) - : base(networkConnectionId) - { - this.batching = batching; - } + // unbatcher + public Unbatcher unbatcher = new Unbatcher(); - // TODO if we only have Reliable/Unreliable, then we could initialize - // two batches and avoid this code - Batch GetBatchForChannelId(int channelId) - { - // get existing or create new writer for the channelId - Batch batch; - if (!batches.TryGetValue(channelId, out batch)) - { - batch = new Batch(); - batches[channelId] = batch; - } - return batch; - } + public NetworkConnectionToClient(int networkConnectionId) + : base(networkConnectionId) {} - // send a batch. internal so we can test it. - internal void SendBatch(int channelId, Batch batch) - { - // get max batch size for this channel - int max = Transport.activeTransport.GetMaxBatchSize(channelId); - - // we need a writer to merge queued messages into a batch - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // for each queued message - while (batch.messages.Count > 0) - { - // get it - PooledNetworkWriter message = batch.messages.Dequeue(); - ArraySegment segment = message.ToArraySegment(); - - // IF adding to writer would end up >= MTU then we should - // flush first. the goal is to always flush < MTU packets. - // - // IMPORTANT: if writer is empty and segment is > MTU - // (which can happen for large max sized message) - // then we would send an empty previous writer. - // => don't do that. - // => only send if not empty. - if (writer.Position > 0 && - writer.Position + segment.Count >= max) - { - // flush & reset writer - Transport.activeTransport.ServerSend(connectionId, writer.ToArraySegment(), channelId); - writer.Position = 0; - } - - // now add to writer in any case - // -> WriteBytes instead of WriteSegment because the latter - // would add a size header. we want to write directly. - // - // NOTE: it's very possible that we add > MTU to writer if - // message size is > MTU. - // which is fine. next iteration will just flush it. - writer.WriteBytes(segment.Array, segment.Offset, segment.Count); - - // return queued message to pool - NetworkWriterPool.Recycle(message); - } - - // done iterating queued messages. - // batch might still contain the last message. - // send it. - if (writer.Position > 0) - { - Transport.activeTransport.ServerSend(connectionId, writer.ToArraySegment(), channelId); - writer.Position = 0; - } - } - } - - internal override void Send(ArraySegment segment, int channelId = Channels.Reliable) - { - //Debug.Log("ConnectionSend " + this + " bytes:" + BitConverter.ToString(segment.Array, segment.Offset, segment.Count)); - - // validate packet size first. - if (ValidatePacketSize(segment, channelId)) - { - // batching? then add to queued messages - if (batching) - { - // put into a (pooled) writer - // -> WriteBytes instead of WriteSegment because the latter - // would add a size header. we want to write directly. - // -> will be returned to pool when sending! - PooledNetworkWriter writer = NetworkWriterPool.GetWriter(); - writer.WriteBytes(segment.Array, segment.Offset, segment.Count); - - // add to batch queue - Batch batch = GetBatchForChannelId(channelId); - batch.messages.Enqueue(writer); - } - // otherwise send directly to minimize latency - else Transport.activeTransport.ServerSend(connectionId, segment, channelId); - } - } - - // flush batched messages at the end of every Update. - internal void Update() - { - // batching? - if (batching) - { - // go through batches for all channels - foreach (KeyValuePair kvp in batches) - { - // is this channel's batch not empty? - if (kvp.Value.messages.Count > 0) - { - // send the batch. - //Debug.Log($"sending batch of {kvp.Value.writer.Position} bytes for channel={kvp.Key} connId={connectionId}"); - SendBatch(kvp.Key, kvp.Value); - } - } - } - } + // Send stage three: hand off to transport + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override void SendToTransport(ArraySegment segment, int channelId = Channels.Reliable) => + Transport.activeTransport.ServerSend(connectionId, segment, channelId); /// Disconnects this connection. public override void Disconnect() @@ -164,7 +44,60 @@ namespace Mirror // -> so all 'on disconnect' cleanup code needs to be in // OnTransportDisconnect, where it's called for both voluntary // and involuntary disconnects! - RemoveFromObservingsObservers(); + } + + internal void AddToObserving(NetworkIdentity netIdentity) + { + observing.Add(netIdentity); + + // spawn identity for this conn + NetworkServer.ShowForConnection(netIdentity, this); + } + + internal void RemoveFromObserving(NetworkIdentity netIdentity, bool isDestroyed) + { + observing.Remove(netIdentity); + + if (!isDestroyed) + { + // hide identity for this conn + NetworkServer.HideForConnection(netIdentity, this); + } + } + + internal void RemoveFromObservingsObservers() + { + foreach (NetworkIdentity netIdentity in observing) + { + netIdentity.RemoveObserver(this); + } + observing.Clear(); + } + + internal void AddOwnedObject(NetworkIdentity obj) + { + clientOwnedObjects.Add(obj); + } + + internal void RemoveOwnedObject(NetworkIdentity obj) + { + clientOwnedObjects.Remove(obj); + } + + internal void DestroyOwnedObjects() + { + // create a copy because the list might be modified when destroying + HashSet tmp = new HashSet(clientOwnedObjects); + foreach (NetworkIdentity netIdentity in tmp) + { + if (netIdentity != null) + { + NetworkServer.Destroy(netIdentity.gameObject); + } + } + + // clear the hashset because we destroyed them all + clientOwnedObjects.Clear(); } } } diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkConnectionToClient.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkConnectionToClient.cs.meta index 8ec6133..6001a71 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkConnectionToClient.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/NetworkConnectionToClient.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkConnectionToServer.cs b/UnityProject/Assets/Mirror/Runtime/NetworkConnectionToServer.cs index 434147c..a1ebc5f 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkConnectionToServer.cs +++ b/UnityProject/Assets/Mirror/Runtime/NetworkConnectionToServer.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; namespace Mirror { @@ -6,16 +7,10 @@ namespace Mirror { public override string address => ""; - internal override void Send(ArraySegment segment, int channelId = Channels.Reliable) - { - // Debug.Log("ConnectionSend " + this + " bytes:" + BitConverter.ToString(segment.Array, segment.Offset, segment.Count)); - - // validate packet size first. - if (ValidatePacketSize(segment, channelId)) - { - Transport.activeTransport.ClientSend(segment, channelId); - } - } + // Send stage three: hand off to transport + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override void SendToTransport(ArraySegment segment, int channelId = Channels.Reliable) => + Transport.activeTransport.ClientSend(segment, channelId); /// Disconnects this connection. public override void Disconnect() diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkConnectionToServer.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkConnectionToServer.cs.meta index 12d66c9..3424b58 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkConnectionToServer.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/NetworkConnectionToServer.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkDiagnostics.cs b/UnityProject/Assets/Mirror/Runtime/NetworkDiagnostics.cs index 5b72961..1cdc96f 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkDiagnostics.cs +++ b/UnityProject/Assets/Mirror/Runtime/NetworkDiagnostics.cs @@ -29,6 +29,17 @@ namespace Mirror /// Event for when Mirror sends a message. Can be subscribed to. public static event Action OutMessageEvent; + /// Event for when Mirror receives a message. Can be subscribed to. + public static event Action InMessageEvent; + + // RuntimeInitializeOnLoadMethod -> fast playmode without domain reload + [UnityEngine.RuntimeInitializeOnLoadMethod] + static void ResetStatics() + { + InMessageEvent = null; + OutMessageEvent = null; + } + internal static void OnSend(T message, int channel, int bytes, int count) where T : struct, NetworkMessage { @@ -39,9 +50,6 @@ namespace Mirror } } - /// Event for when Mirror receives a message. Can be subscribed to. - public static event Action InMessageEvent; - internal static void OnReceive(T message, int channel, int bytes) where T : struct, NetworkMessage { diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkDiagnostics.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkDiagnostics.cs.meta index 6979333..fe37316 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkDiagnostics.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/NetworkDiagnostics.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkIdentity.cs b/UnityProject/Assets/Mirror/Runtime/NetworkIdentity.cs index b796580..f42e311 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkIdentity.cs +++ b/UnityProject/Assets/Mirror/Runtime/NetworkIdentity.cs @@ -5,10 +5,13 @@ using UnityEngine; using UnityEngine.Serialization; #if UNITY_EDITOR -using UnityEditor; -#if UNITY_2018_3_OR_NEWER -using UnityEditor.Experimental.SceneManagement; -#endif + using UnityEditor; + + #if UNITY_2021_2_OR_NEWER + using UnityEditor.SceneManagement; + #elif UNITY_2018_3_OR_NEWER + using UnityEditor.Experimental.SceneManagement; + #endif #endif namespace Mirror @@ -21,12 +24,10 @@ namespace Mirror public struct NetworkIdentitySerialization { - public float tickTimeStamp; + // IMPORTANT: int tick avoids floating point inaccuracy over days/weeks + public int tick; public NetworkWriter ownerWriter; public NetworkWriter observersWriter; - // TODO there is probably a more simple way later - public int ownerWritten; - public int observersWritten; } /// NetworkIdentity identifies objects across the network. @@ -34,7 +35,7 @@ namespace Mirror // NetworkIdentity.Awake initializes all NetworkComponents. // let's make sure it's always called before their Awake's. [DefaultExecutionOrder(-1)] - [AddComponentMenu("Network/NetworkIdentity")] + [AddComponentMenu("Network/Network Identity")] [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-identity")] public sealed class NetworkIdentity : MonoBehaviour { @@ -87,17 +88,13 @@ namespace Mirror /// True if this object exists on a client that is not also acting as a server. public bool isClientOnly => isClient && !isServer; - /// True if this object is the authoritative player object on the client. - // Determined at runtime. For most objects, authority is held by the server. - // For objects that had their authority set by AssignClientAuthority on - // the server, this will be true on the client that owns the object. NOT - // on other clients. + /// True on client if that component has been assigned to the client. E.g. player, pets, henchmen. public bool hasAuthority { get; internal set; } /// The set of network connections (players) that can see this object. // note: null until OnStartServer was called. this is necessary for // SendTo* to work properly in server-only mode. - public Dictionary observers; + public Dictionary observers; /// The unique network Id of this object (unique at runtime). public uint netId { get; internal set; } @@ -117,9 +114,10 @@ namespace Mirror internal bool destroyCalled; /// Client's network connection to the server. This is only valid for player objects on the client. + // TODO change to NetworkConnectionToServer, but might cause some breaking public NetworkConnection connectionToServer { get; internal set; } - /// Server's network connection to the client. This is only valid for player objects on the server. + /// Server's network connection to the client. This is only valid for client-owned objects (including the Player object) on the server. public NetworkConnectionToClient connectionToClient { get => _connectionToClient; @@ -135,17 +133,32 @@ namespace Mirror /// All spawned NetworkIdentities by netId. Available on server and client. // server sees ALL spawned ones. // client sees OBSERVED spawned ones. - public static readonly Dictionary spawned = - new Dictionary(); + // => split into NetworkServer.spawned and NetworkClient.spawned to + // reduce shared state between server & client. + // => prepares for NetworkServer/Client as component & better host mode. + [Obsolete("NetworkIdentity.spawned is now NetworkServer.spawned on server, NetworkClient.spawned on client.\nPrepares for NetworkServer/Client as component, better host mode, better testing.")] + public static Dictionary spawned + { + get + { + // server / host mode: use the one from server. + // host mode has access to all spawned. + if (NetworkServer.active) return NetworkServer.spawned; + + // client + if (NetworkClient.active) return NetworkClient.spawned; + + // neither: then we are testing. + // we could default to NetworkServer.spawned. + // but from the outside, that's not obvious. + // better to throw an exception to make it obvious. + throw new Exception("NetworkIdentity.spawned was accessed before NetworkServer/NetworkClient were active."); + } + } // get all NetworkBehaviour components public NetworkBehaviour[] NetworkBehaviours { get; private set; } -#pragma warning disable 618 - [Obsolete(NetworkVisibilityObsoleteMessage.Message)] - public NetworkVisibility visibility { get; private set; } -#pragma warning restore 618 - // current visibility // // Default = use interest management @@ -190,35 +203,35 @@ namespace Mirror { #if UNITY_EDITOR // This is important because sometimes OnValidate does not run (like when adding view to prefab with no child links) - if (string.IsNullOrEmpty(m_AssetId)) + if (string.IsNullOrWhiteSpace(m_AssetId)) SetupIDs(); #endif // convert string to Guid and use .Empty to avoid exception if // we would use 'new Guid("")' - return string.IsNullOrEmpty(m_AssetId) ? Guid.Empty : new Guid(m_AssetId); + return string.IsNullOrWhiteSpace(m_AssetId) ? Guid.Empty : new Guid(m_AssetId); } internal set { string newAssetIdString = value == Guid.Empty ? string.Empty : value.ToString("N"); - string oldAssetIdSrting = m_AssetId; + string oldAssetIdString = m_AssetId; // they are the same, do nothing - if (oldAssetIdSrting == newAssetIdString) + if (oldAssetIdString == newAssetIdString) { return; } // new is empty - if (string.IsNullOrEmpty(newAssetIdString)) + if (string.IsNullOrWhiteSpace(newAssetIdString)) { - Debug.LogError($"Can not set AssetId to empty guid on NetworkIdentity '{name}', old assetId '{oldAssetIdSrting}'"); + Debug.LogError($"Can not set AssetId to empty guid on NetworkIdentity '{name}', old assetId '{oldAssetIdString}'"); return; } // old not empty - if (!string.IsNullOrEmpty(oldAssetIdSrting)) + if (!string.IsNullOrWhiteSpace(oldAssetIdString)) { - Debug.LogError($"Can not Set AssetId on NetworkIdentity '{name}' because it already had an assetId, current assetId '{oldAssetIdSrting}', attempted new assetId '{newAssetIdString}'"); + Debug.LogError($"Can not Set AssetId on NetworkIdentity '{name}' because it already had an assetId, current assetId '{oldAssetIdString}', attempted new assetId '{newAssetIdString}'"); return; } @@ -233,11 +246,35 @@ namespace Mirror static readonly Dictionary sceneIds = new Dictionary(); + // reset only client sided statics. + // don't touch server statics when calling StopClient in host mode. + // https://github.com/vis2k/Mirror/issues/2954 + internal static void ResetClientStatics() + { + previousLocalPlayer = null; + clientAuthorityCallback = null; + } + + internal static void ResetServerStatics() + { + nextNetworkId = 1; + } + + // RuntimeInitializeOnLoadMethod -> fast playmode without domain reload + // internal so it can be called from NetworkServer & NetworkClient + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + internal static void ResetStatics() + { + // reset ALL statics + ResetClientStatics(); + ResetServerStatics(); + } + /// Gets the NetworkIdentity from the sceneIds dictionary with the corresponding id public static NetworkIdentity GetSceneIdentity(ulong id) => sceneIds[id]; // used when adding players - internal void SetClientOwner(NetworkConnection conn) + internal void SetClientOwner(NetworkConnectionToClient conn) { // do nothing if it already has an owner if (connectionToClient != null && conn != connectionToClient) @@ -247,7 +284,7 @@ namespace Mirror } // otherwise set the owner connection - connectionToClient = (NetworkConnectionToClient)conn; + connectionToClient = conn; } static uint nextNetworkId = 1; @@ -257,17 +294,11 @@ namespace Mirror public static void ResetNextNetworkId() => nextNetworkId = 1; /// The delegate type for the clientAuthorityCallback. - public delegate void ClientAuthorityCallback(NetworkConnection conn, NetworkIdentity identity, bool authorityState); + public delegate void ClientAuthorityCallback(NetworkConnectionToClient conn, NetworkIdentity identity, bool authorityState); /// A callback that can be populated to be notified when the client-authority state of objects changes. public static event ClientAuthorityCallback clientAuthorityCallback; - // this is used when a connection is destroyed, since the "observers" property is read-only - internal void RemoveObserverInternal(NetworkConnection conn) - { - observers?.Remove(conn.connectionId); - } - // hasSpawned should always be false before runtime [SerializeField, HideInInspector] bool hasSpawned; public bool SpawnedFromInstantiate { get; private set; } @@ -302,11 +333,6 @@ namespace Mirror // => doing it here is the fastest and easiest solution. InitializeNetworkBehaviours(); - // initialize visibility component. only call GetComponent once. -#pragma warning disable 618 - visibility = GetComponent(); -#pragma warning restore 618 - if (hasSpawned) { Debug.LogError($"{name} has already spawned. Don't call Instantiate for NetworkIdentities that were in the scene since the beginning (aka scene objects). Otherwise the client won't know which object to use for a SpawnSceneObject message."); @@ -328,7 +354,13 @@ namespace Mirror } #if UNITY_EDITOR - void AssignAssetID(string path) => m_AssetId = AssetDatabase.AssetPathToGUID(path); + void AssignAssetID(string path) + { + // only set if not empty. fixes https://github.com/vis2k/Mirror/issues/2765 + if (!string.IsNullOrWhiteSpace(path)) + m_AssetId = AssetDatabase.AssetPathToGUID(path); + } + void AssignAssetID(GameObject prefab) => AssignAssetID(AssetDatabase.GetAssetPath(prefab)); // persistent sceneId assignment @@ -398,7 +430,7 @@ namespace Mirror // => throw an exception to cancel the build and let the user // know how to fix it! if (BuildPipeline.isBuildingPlayer) - throw new InvalidOperationException("Scene " + gameObject.scene.path + " needs to be opened and resaved before building, because the scene object " + name + " has no valid sceneId yet."); + throw new InvalidOperationException($"Scene {gameObject.scene.path} needs to be opened and resaved before building, because the scene object {name} has no valid sceneId yet."); // if we generate the sceneId then we MUST be sure to set dirty // in order to save the scene object properly. otherwise it @@ -417,7 +449,7 @@ namespace Mirror if (!duplicate) { sceneId = randomId; - //Debug.Log(name + " in scene=" + gameObject.scene.name + " sceneId assigned to: " + m_SceneId.ToString("X")); + //Debug.Log($"{name} in scene {gameObject.scene.name} sceneId assigned to:{sceneId:X}"); } } @@ -458,7 +490,7 @@ namespace Mirror sceneId = (sceneId & 0xFFFFFFFF) | shiftedHash; // log it. this is incredibly useful to debug sceneId issues. - // Debug.Log(name + " in scene=" + gameObject.scene.name + " scene index hash(" + pathHash.ToString("X") + ") copied into sceneId: " + sceneId.ToString("X")); + //Debug.Log($"{name} in scene {gameObject.scene.name} scene index hash {pathHash:X} copied into sceneId {sceneId:X}"); } void SetupIDs() @@ -492,13 +524,13 @@ namespace Mirror { // force 0 for prefabs sceneId = 0; - //Debug.Log(name + " @ scene: " + gameObject.scene.name + " sceneid reset to 0 because CurrentPrefabStage=" + PrefabStageUtility.GetCurrentPrefabStage() + " PrefabStage=" + PrefabStageUtility.GetPrefabStage(gameObject)); - // NOTE: might make sense to use GetPrefabStage for asset - // path, but let's not touch it while it works. + //Debug.Log($"{name} scene:{gameObject.scene.name} sceneid reset to 0 because CurrentPrefabStage={PrefabStageUtility.GetCurrentPrefabStage()} PrefabStage={PrefabStageUtility.GetPrefabStage(gameObject)}"); + + // get path from PrefabStage for this prefab #if UNITY_2020_1_OR_NEWER - string path = PrefabStageUtility.GetCurrentPrefabStage().assetPath; + string path = PrefabStageUtility.GetPrefabStage(gameObject).assetPath; #else - string path = PrefabStageUtility.GetCurrentPrefabStage().prefabAssetPath; + string path = PrefabStageUtility.GetPrefabStage(gameObject).prefabAssetPath; #endif AssignAssetID(path); @@ -614,13 +646,13 @@ namespace Mirror } netId = GetNextNetworkId(); - observers = new Dictionary(); + observers = new Dictionary(); - // Debug.Log("OnStartServer " + this + " NetId:" + netId + " SceneId:" + sceneId.ToString("X")); + //Debug.Log($"OnStartServer {this} NetId:{netId} SceneId:{sceneId:X}"); // add to spawned (note: the original EnableIsServer isn't needed // because we already set m_isServer=true above) - spawned[netId] = this; + NetworkServer.spawned[netId] = this; // in host mode we set isClient true before calling OnStartServer, // otherwise isClient is false in OnStartServer. @@ -642,7 +674,7 @@ namespace Mirror } catch (Exception e) { - Debug.LogError("Exception in OnStartServer:" + e.Message + " " + e.StackTrace); + Debug.LogException(e, comp); } } } @@ -662,7 +694,7 @@ namespace Mirror } catch (Exception e) { - Debug.LogError("Exception in OnStopServer:" + e.Message + " " + e.StackTrace); + Debug.LogException(e, comp); } } } @@ -685,7 +717,7 @@ namespace Mirror isLocalPlayer = true; } - // Debug.Log("OnStartClient " + gameObject + " netId:" + netId); + // Debug.Log($"OnStartClient {gameObject} netId:{netId}"); foreach (NetworkBehaviour comp in NetworkBehaviours) { // an exception in OnStartClient should be caught, so that one @@ -700,7 +732,7 @@ namespace Mirror } catch (Exception e) { - Debug.LogError("Exception in OnStartClient:" + e.Message + " " + e.StackTrace); + Debug.LogException(e, comp); } } } @@ -720,7 +752,7 @@ namespace Mirror } catch (Exception e) { - Debug.LogError("Exception in OnStopClient:" + e.Message + " " + e.StackTrace); + Debug.LogException(e, comp); } } } @@ -736,7 +768,7 @@ namespace Mirror // ownership change due to sync to owner feature. // Without this static, the second time we get the spawn message we // would call OnStartLocalPlayer again on the same object - static NetworkIdentity previousLocalPlayer = null; + internal static NetworkIdentity previousLocalPlayer = null; internal void OnStartLocalPlayer() { if (previousLocalPlayer == this) @@ -758,7 +790,27 @@ namespace Mirror } catch (Exception e) { - Debug.LogError("Exception in OnStartLocalPlayer:" + e.Message + " " + e.StackTrace); + Debug.LogException(e, comp); + } + } + } + + internal void OnStopLocalPlayer() + { + foreach (NetworkBehaviour comp in NetworkBehaviours) + { + // an exception in OnStopLocalPlayer should be caught, so that + // one component's exception doesn't stop all other components + // from being initialized + // => this is what Unity does for Start() etc. too. + // one exception doesn't stop all the other Start() calls! + try + { + comp.OnStopLocalPlayer(); + } + catch (Exception e) + { + Debug.LogException(e, comp); } } } @@ -788,7 +840,7 @@ namespace Mirror } catch (Exception e) { - Debug.LogError("Exception in OnStartAuthority:" + e.Message + " " + e.StackTrace); + Debug.LogException(e, comp); } } } @@ -808,32 +860,11 @@ namespace Mirror } catch (Exception e) { - Debug.LogError("Exception in OnStopAuthority:" + e.Message + " " + e.StackTrace); + Debug.LogException(e, comp); } } } - // interest management ///////////////////////////////////////////////// - // obsoletes to still support ProximityChecker while transitioning to - // global Interest Management - [Obsolete("Use NetworkServer.RebuildObservers(identity, initialize) instead.")] - public void RebuildObservers(bool initialize) => NetworkServer.RebuildObservers(this, initialize); - - // Callback used by the visibility system for objects on a host. - // Objects on a host (with a local client) cannot be disabled or - // destroyed when they are not visible to the local client. So this - // function is called to allow custom code to hide these objects. A - // typical implementation will disable renderer components on the - // object. This is only called on local clients on a host. - // => this used to be in proximitychecker, but since day one everyone - // used the same function without any modifications. so let's keep it - // directly in NetworkIdentity. - internal void OnSetHostVisibility(bool visible) - { - foreach (Renderer rend in GetComponentsInChildren()) - rend.enabled = visible; - } - // vis2k: readstring bug prevention: https://github.com/vis2k/Mirror/issues/2617 // -> OnSerialize writes length,componentData,length,componentData,... // -> OnDeserialize carefully extracts each data, then deserializes each component with separate readers @@ -845,6 +876,7 @@ namespace Mirror // (jumping back later is WAY faster than allocating a temporary // writer for the payload, then writing payload.size, payload) int headerPosition = writer.Position; + // no varint because we don't know the final size yet writer.WriteInt(0); int contentPosition = writer.Position; @@ -857,7 +889,7 @@ namespace Mirror catch (Exception e) { // show a detailed error and let the user know what went wrong - Debug.LogError("OnSerialize failed for: object=" + name + " component=" + comp.GetType() + " sceneId=" + sceneId.ToString("X") + "\n\n" + e); + Debug.LogError($"OnSerialize failed for: object={name} component={comp.GetType()} sceneId={sceneId:X}\n\n{e}"); } int endPosition = writer.Position; @@ -866,7 +898,7 @@ namespace Mirror writer.WriteInt(endPosition - contentPosition); writer.Position = endPosition; - // Debug.Log("OnSerializeSafely written for object=" + comp.name + " component=" + comp.GetType() + " sceneId=" + sceneId.ToString("X") + "header@" + headerPosition + " content@" + contentPosition + " end@" + endPosition + " contentSize=" + (endPosition - contentPosition)); + //Debug.Log($"OnSerializeSafely written for object {comp.name} component:{comp.GetType()} sceneId:{sceneId:X} header:{headerPosition} content:{contentPosition} end:{endPosition} contentSize:{endPosition - contentPosition}"); return result; } @@ -875,11 +907,8 @@ namespace Mirror // check ownerWritten/observersWritten to know if anything was written // We pass dirtyComponentsMask into this function so that we can check // if any Components are dirty before creating writers - internal void OnSerializeAllSafely(bool initialState, NetworkWriter ownerWriter, out int ownerWritten, NetworkWriter observersWriter, out int observersWritten) + internal void OnSerializeAllSafely(bool initialState, NetworkWriter ownerWriter, NetworkWriter observersWriter) { - // clear 'written' variables - ownerWritten = observersWritten = 0; - // check if components are in byte.MaxRange just to be 100% sure // that we avoid overflows NetworkBehaviour[] components = NetworkBehaviours; @@ -895,19 +924,20 @@ namespace Mirror NetworkBehaviour comp = components[i]; if (initialState || comp.IsDirty()) { - // Debug.Log("OnSerializeAllSafely: " + name + " -> " + comp.GetType() + " initial=" + initialState); + //Debug.Log($"OnSerializeAllSafely: {name} -> {comp.GetType()} initial:{ initialState}"); // remember start position in case we need to copy it into // observers writer too int startPosition = ownerWriter.Position; - // write index as byte [0..255] + // write index as byte [0..255]. + // necessary because deserialize may only get data for some + // components because not dirty, not owner, etc. ownerWriter.WriteByte((byte)i); // serialize into ownerWriter first // (owner always gets everything!) OnSerializeSafely(comp, ownerWriter, initialState); - ++ownerWritten; // copy into observersWriter too if SyncMode.Observers // -> we copy instead of calling OnSerialize again because @@ -923,17 +953,26 @@ namespace Mirror ArraySegment segment = ownerWriter.ToArraySegment(); int length = ownerWriter.Position - startPosition; observersWriter.WriteBytes(segment.Array, startPosition, length); - ++observersWritten; } } } } // get cached serialization for this tick (or serialize if none yet) - internal NetworkIdentitySerialization GetSerializationAtTick(float tickTimeStamp) + // IMPORTANT: int tick avoids floating point inaccuracy over days/weeks + internal NetworkIdentitySerialization GetSerializationAtTick(int tick) { - // serialize fresh if tick is newer than last one - if (lastSerialization.tickTimeStamp < tickTimeStamp) + // only rebuild serialization once per tick. reuse otherwise. + // except for tests, where Time.frameCount never increases. + // so during tests, we always rebuild. + // (otherwise [SyncVar] changes would never be serialized in tests) + // + // NOTE: != instead of < because int.max+1 overflows at some point. + if (lastSerialization.tick != tick +#if UNITY_EDITOR + || !Application.isPlaying +#endif + ) { // reset lastSerialization.ownerWriter.Position = 0; @@ -942,12 +981,31 @@ namespace Mirror // serialize OnSerializeAllSafely(false, lastSerialization.ownerWriter, - out lastSerialization.ownerWritten, - lastSerialization.observersWriter, - out lastSerialization.observersWritten); + lastSerialization.observersWriter); - // set timestamp - lastSerialization.tickTimeStamp = tickTimeStamp; + // clear dirty bits for the components that we serialized. + // previously we did this in NetworkServer.BroadcastToConnection + // for every connection, for every entity. + // but we only serialize each entity once, right here in this + // 'lastSerialization.tick != tick' scope. + // so only do it once. + // + // NOTE: not in OnSerializeAllSafely as that should only do one + // thing: serialize data. + // + // + // NOTE: DO NOT clear ALL component's dirty bits, because + // components can have different syncIntervals and we + // don't want to reset dirty bits for the ones that were + // not synced yet. + // + // NOTE: this used to be very important to avoid ever growing + // SyncList changes if they had no observers, but we've + // added SyncObject.isRecording since. + ClearDirtyComponentsDirtyBits(); + + // set tick + lastSerialization.tick = tick; //Debug.Log($"{name} (netId={netId}) serialized for tick={tickTimeStamp}"); } @@ -966,7 +1024,7 @@ namespace Mirror // way to mess up another component's deserialization try { - // Debug.Log("OnDeserializeSafely: " + comp.name + " component=" + comp.GetType() + " sceneId=" + sceneId.ToString("X") + " length=" + contentSize); + //Debug.Log($"OnDeserializeSafely: {comp.name} component:{comp.GetType()} sceneId:{sceneId:X} length:{contentSize}"); comp.OnDeserialize(reader, initialState); } catch (Exception e) @@ -986,7 +1044,7 @@ namespace Mirror { // warn the user int bytesRead = reader.Position - chunkStart; - Debug.LogWarning("OnDeserialize was expected to read " + contentSize + " instead of " + bytesRead + " bytes for object:" + name + " component=" + comp.GetType() + " sceneId=" + sceneId.ToString("X") + ". Make sure that OnSerialize and OnDeserialize write/read the same amount of data in all cases."); + Debug.LogWarning($"OnDeserialize was expected to read {contentSize} instead of {bytesRead} bytes for object:{name} component={comp.GetType()} sceneId={sceneId:X}. Make sure that OnSerialize and OnDeserialize write/read the same amount of data in all cases."); // fix the position, so the following components don't all fail reader.Position = chunkEnd; @@ -995,9 +1053,18 @@ namespace Mirror internal void OnDeserializeAllSafely(NetworkReader reader, bool initialState) { + if (NetworkBehaviours == null) + { + Debug.LogError($"NetworkBehaviours array is null on {gameObject.name}!\n" + + $"Typically this can happen when a networked object is a child of a " + + $"non-networked parent that's disabled, preventing Awake on the networked object " + + $"from being invoked, where the NetworkBehaviours array is initialized.", gameObject); + return; + } + // deserialize all components that were received NetworkBehaviour[] components = NetworkBehaviours; - while (reader.Position < reader.Length) + while (reader.Remaining > 0) { // read & check index [0..255] byte index = reader.ReadByte(); @@ -1010,70 +1077,34 @@ namespace Mirror } // Helper function to handle Command/Rpc - internal void HandleRemoteCall(int componentIndex, int functionHash, MirrorInvokeType invokeType, NetworkReader reader, NetworkConnectionToClient senderConnection = null) + internal void HandleRemoteCall(byte componentIndex, ushort functionHash, RemoteCallType remoteCallType, NetworkReader reader, NetworkConnectionToClient senderConnection = null) { // check if unity object has been destroyed if (this == null) { - Debug.LogWarning($"{invokeType} [{functionHash}] received for deleted object [netId={netId}]"); + Debug.LogWarning($"{remoteCallType} [{functionHash}] received for deleted object [netId={netId}]"); return; } // find the right component to invoke the function on - if (componentIndex < 0 || componentIndex >= NetworkBehaviours.Length) + if (componentIndex >= NetworkBehaviours.Length) { Debug.LogWarning($"Component [{componentIndex}] not found for [netId={netId}]"); return; } NetworkBehaviour invokeComponent = NetworkBehaviours[componentIndex]; - if (!RemoteCallHelper.InvokeHandlerDelegate(functionHash, invokeType, reader, invokeComponent, senderConnection)) + if (!RemoteProcedureCalls.Invoke(functionHash, remoteCallType, reader, invokeComponent, senderConnection)) { - Debug.LogError($"Found no receiver for incoming {invokeType} [{functionHash}] on {gameObject.name}, the server and client should have the same NetworkBehaviour instances [netId={netId}]."); + Debug.LogError($"Found no receiver for incoming {remoteCallType} [{functionHash}] on {gameObject.name}, the server and client should have the same NetworkBehaviour instances [netId={netId}]."); } } - // Runs on server - internal CommandInfo GetCommandInfo(int componentIndex, int cmdHash) - { - // check if unity object has been destroyed - if (this == null) - { - // error can be logged later - return default; - } - - // find the right component to invoke the function on - if (0 <= componentIndex && componentIndex < NetworkBehaviours.Length) - { - NetworkBehaviour invokeComponent = NetworkBehaviours[componentIndex]; - return RemoteCallHelper.GetCommandInfo(cmdHash, invokeComponent); - } - else - { - // error can be logged later - return default; - } - } - - // Called when NetworkIdentity is destroyed - internal void ClearObservers() - { - if (observers != null) - { - foreach (NetworkConnection conn in observers.Values) - { - conn.RemoveFromObserving(this, true); - } - observers.Clear(); - } - } - - internal void AddObserver(NetworkConnection conn) + internal void AddObserver(NetworkConnectionToClient conn) { if (observers == null) { - Debug.LogError("AddObserver for " + gameObject + " observer list is null"); + Debug.LogError($"AddObserver for {gameObject} observer list is null"); return; } @@ -1084,12 +1115,55 @@ namespace Mirror return; } - // Debug.Log("Added observer " + conn.address + " added for " + gameObject); + // Debug.Log($"Added observer: {conn.address} added for {gameObject}"); + + // if we previously had no observers, then clear all dirty bits once. + // a monster's health may have changed while it had no observers. + // but that change (= the dirty bits) don't matter as soon as the + // first observer comes. + // -> first observer gets full spawn packet + // -> afterwards it gets delta packet + // => if we don't clear previous dirty bits, observer would get + // the health change because the bit was still set. + // => ultimately this happens because spawn doesn't reset dirty + // bits + // => which happens because spawn happens separately, instead of + // in Broadcast() (which will be changed in the future) + // + // NOTE that NetworkServer.Broadcast previously cleared dirty bits + // for ALL SPAWNED that don't have observers. that was super + // expensive. doing it when adding the first observer has the + // same result, without the O(N) iteration in Broadcast(). + // + // TODO remove this after moving spawning into Broadcast()! + if (observers.Count == 0) + { + ClearAllComponentsDirtyBits(); + } observers[conn.connectionId] = conn; conn.AddToObserving(this); } + // this is used when a connection is destroyed, since the "observers" property is read-only + internal void RemoveObserver(NetworkConnection conn) + { + observers?.Remove(conn.connectionId); + } + + // Called when NetworkIdentity is destroyed + internal void ClearObservers() + { + if (observers != null) + { + foreach (NetworkConnectionToClient conn in observers.Values) + { + conn.RemoveFromObserving(this, true); + } + observers.Clear(); + } + } + /// Assign control of an object to a client via the client's NetworkConnection. // This causes hasAuthority to be set on the client that owns the object, // and NetworkBehaviour.OnStartAuthority will be called on that client. @@ -1099,7 +1173,7 @@ namespace Mirror // Authority can be removed with RemoveClientAuthority. Only one client // can own an object at any time. This does not need to be called for // player objects, as their authority is setup automatically. - public bool AssignClientAuthority(NetworkConnection conn) + public bool AssignClientAuthority(NetworkConnectionToClient conn) { if (!isServer) { @@ -1109,21 +1183,20 @@ namespace Mirror if (conn == null) { - Debug.LogError("AssignClientAuthority for " + gameObject + " owner cannot be null. Use RemoveClientAuthority() instead."); + Debug.LogError($"AssignClientAuthority for {gameObject} owner cannot be null. Use RemoveClientAuthority() instead."); return false; } if (connectionToClient != null && conn != connectionToClient) { - Debug.LogError("AssignClientAuthority for " + gameObject + " already has an owner. Use RemoveClientAuthority() first."); + Debug.LogError($"AssignClientAuthority for {gameObject} already has an owner. Use RemoveClientAuthority() first."); return false; } SetClientOwner(conn); // The client will match to the existing object - // update all variables and assign authority - NetworkServer.SendSpawnMessage(this, conn); + NetworkServer.SendChangeOwnerMessage(this, conn); clientAuthorityCallback?.Invoke(conn, this, true); @@ -1151,20 +1224,9 @@ namespace Mirror if (connectionToClient != null) { clientAuthorityCallback?.Invoke(connectionToClient, this, false); - NetworkConnectionToClient previousOwner = connectionToClient; - - // TODO why do we clear this twice? - connectionToClient = null; - - // we need to resynchronize the entire object - // so just spawn it again, - // the client will not create a new instance, it will simply - // reset all variables and remove authority - NetworkServer.SendSpawnMessage(this, previousOwner); - - // TODO why do we clear this twice? connectionToClient = null; + NetworkServer.SendChangeOwnerMessage(this, previousOwner); } } @@ -1192,6 +1254,10 @@ namespace Mirror isServer = false; //isLocalPlayer = false; <- cleared AFTER ClearLocalPlayer below! + // remove authority flag. This object may be unspawned, not destroyed, on client. + hasAuthority = false; + NotifyAuthority(); + netId = 0; connectionToServer = null; connectionToClient = null; @@ -1208,6 +1274,8 @@ namespace Mirror if (NetworkClient.localPlayer == this) NetworkClient.localPlayer = null; } + + previousLocalPlayer = null; isLocalPlayer = false; } @@ -1222,6 +1290,10 @@ namespace Mirror // Clear only dirty component's dirty bits. ignores components which // may be dirty but not ready to be synced yet (because of syncInterval) + // + // NOTE: this used to be very important to avoid ever + // growing SyncList changes if they had no observers, + // but we've added SyncObject.isRecording since. internal void ClearDirtyComponentsDirtyBits() { foreach (NetworkBehaviour comp in NetworkBehaviours) diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkIdentity.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkIdentity.cs.meta index 45dacb6..7b96521 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkIdentity.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/NetworkIdentity.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkLoop.cs b/UnityProject/Assets/Mirror/Runtime/NetworkLoop.cs index a9398a8..50d9e95 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkLoop.cs +++ b/UnityProject/Assets/Mirror/Runtime/NetworkLoop.cs @@ -1,4 +1,4 @@ -// our ideal update looks like this: +// our ideal update looks like this: // transport.process_incoming() // update_world() // transport.process_outgoing() @@ -40,11 +40,23 @@ using UnityEngine.Experimental.PlayerLoop; namespace Mirror { - internal static class NetworkLoop + public static class NetworkLoop { // helper enum to add loop to begin/end of subSystemList internal enum AddMode { Beginning, End } + // callbacks in case someone needs to use early/lateupdate too. + public static Action OnEarlyUpdate; + public static Action OnLateUpdate; + + // RuntimeInitializeOnLoadMethod -> fast playmode without domain reload + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + static void ResetStatics() + { + OnEarlyUpdate = null; + OnLateUpdate = null; + } + // helper function to find an update function's index in a player loop // type. this is used for testing to guarantee our functions are added // at the beginning/end properly. @@ -146,10 +158,10 @@ namespace Mirror } // hook into Unity runtime to actually add our custom functions - [RuntimeInitializeOnLoadMethod] + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void RuntimeInitializeOnLoad() { - Debug.Log("Mirror: adding Network[Early/Late]Update to Unity..."); + //Debug.Log("Mirror: adding Network[Early/Late]Update to Unity..."); // get loop // 2019 has GetCURRENTPlayerLoop which is safe to use without @@ -177,14 +189,18 @@ namespace Mirror static void NetworkEarlyUpdate() { - //Debug.Log("NetworkEarlyUpdate @ " + Time.time); + //Debug.Log($"NetworkEarlyUpdate {Time.time}"); NetworkServer.NetworkEarlyUpdate(); NetworkClient.NetworkEarlyUpdate(); + // invoke event after mirror has done it's early updating. + OnEarlyUpdate?.Invoke(); } static void NetworkLateUpdate() { - //Debug.Log("NetworkLateUpdate @ " + Time.time); + //Debug.Log($"NetworkLateUpdate {Time.time}"); + // invoke event before mirror does its final late updating. + OnLateUpdate?.Invoke(); NetworkServer.NetworkLateUpdate(); NetworkClient.NetworkLateUpdate(); } diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkLoop.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkLoop.cs.meta index 3432200..52b6e6a 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkLoop.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/NetworkLoop.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkManager.cs b/UnityProject/Assets/Mirror/Runtime/NetworkManager.cs index 04d80ed..32fd3e4 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkManager.cs +++ b/UnityProject/Assets/Mirror/Runtime/NetworkManager.cs @@ -12,7 +12,7 @@ namespace Mirror public enum NetworkManagerMode { Offline, ServerOnly, ClientOnly, Host } [DisallowMultipleComponent] - [AddComponentMenu("Network/NetworkManager")] + [AddComponentMenu("Network/Network Manager")] [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-manager")] public class NetworkManager : MonoBehaviour { @@ -23,14 +23,6 @@ namespace Mirror [Tooltip("Should the Network Manager object be persisted through scene changes?")] public bool dontDestroyOnLoad = true; - // Deprecated 2021-03-10 - // Temporary bool to allow Network Manager to persist to offline scene - // Based on Discord convo, BigBox is invoking StopHost in startup sequence, bouncing the server and clients back to offline scene, which resets Network Manager. - // Request is for a checkbox to persist Network Manager to offline scene, despite the collision and warning. - [Obsolete("This was added temporarily and will be removed in a future release.")] - [Tooltip("Should the Network Manager object be persisted through scene change to the offline scene?")] - public bool PersistNetworkManagerToOfflineScene; - /// Multiplayer games should always run in the background so the network doesn't time out. [FormerlySerializedAs("m_RunInBackground")] [Tooltip("Multiplayer games should always run in the background so the network doesn't time out.")] @@ -45,10 +37,6 @@ namespace Mirror [Tooltip("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.")] public int serverTickRate = 30; - /// batch messages and send them out in LateUpdate (or after batchInterval) - [Tooltip("Batch message and send them out in LateUpdate (or after batchInterval). This is pretty much always a good idea.")] - public bool serverBatching = true; - /// Automatically switch to this scene upon going offline (on start / on disconnect / on shutdown). [Header("Scene Management")] [Scene] @@ -78,12 +66,6 @@ namespace Mirror [Tooltip("Maximum number of concurrent connections.")] public int maxConnections = 100; - [Obsolete("Transport is responsible for timeouts.")] - public bool disconnectInactiveConnections; - - [Obsolete("Transport is responsible for timeouts. Configure the Transport's timeout setting instead.")] - public float disconnectInactiveTimeout = 60f; - [Header("Authentication")] [Tooltip("Authentication component attached to this object")] public NetworkAuthenticator authenticator; @@ -115,23 +97,22 @@ namespace Mirror public static int startPositionIndex; /// The one and only NetworkManager - public static NetworkManager singleton { get; private set; } + public static NetworkManager singleton { get; internal set; } /// Number of active player objects across all connections on the server. public int numPlayers => NetworkServer.connections.Count(kv => kv.Value.identity != null); /// True if the server is running or client is connected/connecting. - [NonSerialized] - public bool isNetworkActive; + public bool isNetworkActive => NetworkServer.active || NetworkClient.active; // TODO remove this - static NetworkConnection clientReadyConnection; + // internal for tests + internal static NetworkConnection clientReadyConnection; /// True if the client loaded a new scene when connecting to the server. // This is set before OnClientConnect is called, so it can be checked // there to perform different logic if a scene load occurred. - [NonSerialized] - public bool clientLoadedScene; + protected bool clientLoadedScene; // helper enum to know if we started the networkmanager as server/client/host. // -> this is necessary because when StartHost changes server scene to @@ -144,23 +125,6 @@ namespace Mirror // virtual so that inheriting classes' OnValidate() can call base.OnValidate() too public virtual void OnValidate() { - // add transport if there is none yet. makes upgrading easier. - if (transport == null) - { - // was a transport added yet? if not, add one - transport = GetComponent(); - if (transport == null) - { - transport = gameObject.AddComponent(); - Debug.Log("NetworkManager: added default Transport because there was none yet."); - } -#if UNITY_EDITOR - // For some insane reason, this line fails when building unless wrapped in this define. Stupid but true. - // error CS0234: The type or namespace name 'Undo' does not exist in the namespace 'UnityEditor' (are you missing an assembly reference?) - UnityEditor.Undo.RecordObject(gameObject, "Added default Transport"); -#endif - } - // always >= 0 maxConnections = Mathf.Max(maxConnections, 0); @@ -178,6 +142,45 @@ namespace Mirror } } + // virtual so that inheriting classes' Reset() can call base.Reset() too + // Reset only gets called when the component is added or the user resets the component + // Thats why we validate these things that only need to be validated on adding the NetworkManager here + // If we would do it in OnValidate() then it would run this everytime a value changes + public virtual void Reset() + { + // make sure someone doesn't accidentally add another NetworkManager + // need transform.root because when adding to a child, the parent's + // Reset isn't called. + foreach (NetworkManager manager in transform.root.GetComponentsInChildren()) + { + if (manager != this) + { + Debug.LogError($"{name} detected another component of type {typeof(NetworkManager)} in its hierarchy on {manager.name}. There can only be one, please remove one of them."); + // return early so that transport component isn't auto-added + // to the duplicate NetworkManager. + return; + } + } + + // add transport if there is none yet. makes upgrading easier. + if (transport == null) + { +#if UNITY_EDITOR + // RecordObject needs to be called before we make the change + UnityEditor.Undo.RecordObject(gameObject, "Added default Transport"); +#endif + + transport = GetComponent(); + + // was a transport added yet? if not, add one + if (transport == null) + { + transport = gameObject.AddComponent(); + Debug.Log("NetworkManager: added default Transport because there was none yet."); + } + } + } + // virtual so that inheriting classes' Awake() can call base.Awake() too public virtual void Awake() { @@ -220,7 +223,7 @@ namespace Mirror bool IsServerOnlineSceneChangeNeeded() { // Only change scene if the requested online scene is not blank, and is not already loaded - return !string.IsNullOrEmpty(onlineScene) && !IsSceneActive(onlineScene) && onlineScene != offlineScene; + return !string.IsNullOrWhiteSpace(onlineScene) && !IsSceneActive(onlineScene) && onlineScene != offlineScene; } public static bool IsSceneActive(string scene) @@ -244,16 +247,7 @@ namespace Mirror authenticator.OnServerAuthenticated.AddListener(OnServerAuthenticated); } - ConfigureServerFrameRate(); - - // batching - NetworkServer.batching = serverBatching; - - // Copy auto-disconnect settings to NetworkServer -#pragma warning disable 618 - NetworkServer.disconnectInactiveTimeout = disconnectInactiveTimeout; - NetworkServer.disconnectInactiveConnections = disconnectInactiveConnections; -#pragma warning restore 618 + ConfigureHeadlessFrameRate(); // start listening to network connections NetworkServer.Listen(maxConnections); @@ -270,8 +264,6 @@ namespace Mirror // this must be after Listen(), since that registers the default message handlers RegisterServerMessages(); - - isNetworkActive = true; } /// Starts the server, listening for incoming connections. @@ -337,19 +329,17 @@ namespace Mirror authenticator.OnClientAuthenticated.AddListener(OnClientAuthenticated); } - isNetworkActive = true; - // In case this is a headless client... - ConfigureServerFrameRate(); + ConfigureHeadlessFrameRate(); RegisterClientMessages(); - if (string.IsNullOrEmpty(networkAddress)) + if (string.IsNullOrWhiteSpace(networkAddress)) { Debug.LogError("Must set the Network Address field in the manager"); return; } - // Debug.Log("NetworkManager StartClient address:" + networkAddress); + // Debug.Log($"NetworkManager StartClient address:{networkAddress}"); NetworkClient.Connect(networkAddress); @@ -378,11 +368,9 @@ namespace Mirror authenticator.OnClientAuthenticated.AddListener(OnClientAuthenticated); } - isNetworkActive = true; - RegisterClientMessages(); - // Debug.Log("NetworkManager StartClient address:" + uri); + // Debug.Log($"NetworkManager StartClient address:{uri}"); networkAddress = uri.Host; NetworkClient.Connect(uri); @@ -537,6 +525,7 @@ namespace Mirror /// Stops the server from listening and simulating the game. public void StopServer() { + // return if already stopped to avoid recursion deadlock if (!NetworkServer.active) return; @@ -550,25 +539,22 @@ namespace Mirror // to avoid collision and let a fresh Network Manager be created. // IMPORTANT: .gameObject can be null if StopClient is called from // OnApplicationQuit or from tests! -#pragma warning disable 618 - if (gameObject != null && !PersistNetworkManagerToOfflineScene && - gameObject.scene.name == "DontDestroyOnLoad" - && !string.IsNullOrEmpty(offlineScene) + if (gameObject != null + && gameObject.scene.name == "DontDestroyOnLoad" + && !string.IsNullOrWhiteSpace(offlineScene) && SceneManager.GetActiveScene().path != offlineScene) SceneManager.MoveGameObjectToScene(gameObject, SceneManager.GetActiveScene()); -#pragma warning restore 618 OnStopServer(); //Debug.Log("NetworkManager StopServer"); - isNetworkActive = false; NetworkServer.Shutdown(); // set offline mode BEFORE changing scene so that FinishStartScene // doesn't think we need initialize anything. mode = NetworkManagerMode.Offline; - if (!string.IsNullOrEmpty(offlineScene)) + if (!string.IsNullOrWhiteSpace(offlineScene)) { ServerChangeScene(offlineScene); } @@ -581,6 +567,9 @@ namespace Mirror /// Stops and disconnects the client. public void StopClient() { + if (mode == NetworkManagerMode.Offline) + return; + if (authenticator != null) { authenticator.OnClientAuthenticated.RemoveListener(OnClientAuthenticated); @@ -591,31 +580,30 @@ namespace Mirror // to avoid collision and let a fresh Network Manager be created. // IMPORTANT: .gameObject can be null if StopClient is called from // OnApplicationQuit or from tests! -#pragma warning disable 618 - if (gameObject != null && !PersistNetworkManagerToOfflineScene && - gameObject.scene.name == "DontDestroyOnLoad" - && !string.IsNullOrEmpty(offlineScene) + if (gameObject != null + && gameObject.scene.name == "DontDestroyOnLoad" + && !string.IsNullOrWhiteSpace(offlineScene) && SceneManager.GetActiveScene().path != offlineScene) SceneManager.MoveGameObjectToScene(gameObject, SceneManager.GetActiveScene()); -#pragma warning restore 618 OnStopClient(); //Debug.Log("NetworkManager StopClient"); - isNetworkActive = false; + + // set offline mode BEFORE changing scene so that FinishStartScene + // doesn't think we need initialize anything. + // set offline mode BEFORE NetworkClient.Disconnect so StopClient + // only runs once. + mode = NetworkManagerMode.Offline; // shutdown client NetworkClient.Disconnect(); NetworkClient.Shutdown(); - // set offline mode BEFORE changing scene so that FinishStartScene - // doesn't think we need initialize anything. - mode = NetworkManagerMode.Offline; - // If this is the host player, StopServer will already be changing scenes. // Check loadingSceneAsync to ensure we don't double-invoke the scene change. // Check if NetworkServer.active because we can get here via Disconnect before server has started to change scenes. - if (!string.IsNullOrEmpty(offlineScene) && !IsSceneActive(offlineScene) && loadingSceneAsync == null && !NetworkServer.active) + if (!string.IsNullOrWhiteSpace(offlineScene) && !IsSceneActive(offlineScene) && loadingSceneAsync == null && !NetworkServer.active) { ClientChangeScene(offlineScene, SceneOperation.Normal); } @@ -643,15 +631,19 @@ namespace Mirror StopServer(); //Debug.Log("OnApplicationQuit: stopped server"); } + + // Call ResetStatics to reset statics and singleton + ResetStatics(); } - /// Set the frame rate for a headless server. Override to disable or modify. - public virtual void ConfigureServerFrameRate() + /// Set the frame rate for a headless builds. Override to disable or modify. + // useful for dedicated servers. + // useful for headless benchmark clients. + public virtual void ConfigureHeadlessFrameRate() { - // only set framerate for server build #if UNITY_SERVER Application.targetFrameRate = serverTickRate; - // Debug.Log("Server Tick Rate set to: " + Application.targetFrameRate + " Hz."); + // Debug.Log($"Server Tick Rate set to {Application.targetFrameRate} Hz."); #endif } @@ -670,13 +662,19 @@ namespace Mirror // Return false to not allow collision-destroyed second instance to continue. return false; } - Debug.Log("NetworkManager created singleton (DontDestroyOnLoad)"); + //Debug.Log("NetworkManager created singleton (DontDestroyOnLoad)"); singleton = this; - if (Application.isPlaying) DontDestroyOnLoad(gameObject); + if (Application.isPlaying) + { + // Force the object to scene root, in case user made it a child of something + // in the scene since DDOL is only allowed for scene root objects + transform.SetParent(null); + DontDestroyOnLoad(gameObject); + } } else { - Debug.Log("NetworkManager created singleton (ForScene)"); + //Debug.Log("NetworkManager created singleton (ForScene)"); singleton = this; } @@ -690,6 +688,7 @@ namespace Mirror { NetworkServer.OnConnectedEvent = OnServerConnectInternal; NetworkServer.OnDisconnectedEvent = OnServerDisconnect; + NetworkServer.OnErrorEvent = OnServerError; NetworkServer.RegisterHandler(OnServerAddPlayerInternal); // Network Server initially registers its own handler for this, so we replace it here. @@ -700,6 +699,7 @@ namespace Mirror { NetworkClient.OnConnectedEvent = OnClientConnectInternal; NetworkClient.OnDisconnectedEvent = OnClientDisconnectInternal; + NetworkClient.OnErrorEvent = OnClientError; NetworkClient.RegisterHandler(OnClientNotReadyMessageInternal); NetworkClient.RegisterHandler(OnClientSceneInternal, false); @@ -711,16 +711,22 @@ namespace Mirror } // This is the only way to clear the singleton, so another instance can be created. - public static void Shutdown() + // RuntimeInitializeOnLoadMethod -> fast playmode without domain reload + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + public static void ResetStatics() { - if (singleton == null) - return; + // call StopHost if we have a singleton + if (singleton) + singleton.StopHost(); + // reset all statics startPositions.Clear(); startPositionIndex = 0; clientReadyConnection = null; + loadingSceneAsync = null; + networkSceneName = string.Empty; - singleton.StopHost(); + // and finally (in case it isn't null already)... singleton = null; } @@ -745,22 +751,28 @@ namespace Mirror // the change and ready again to participate in the new scene. public virtual void ServerChangeScene(string newSceneName) { - if (string.IsNullOrEmpty(newSceneName)) + if (string.IsNullOrWhiteSpace(newSceneName)) { Debug.LogError("ServerChangeScene empty scene name"); return; } - // Debug.Log("ServerChangeScene " + newSceneName); + if (NetworkServer.isLoadingScene && newSceneName == networkSceneName) + { + Debug.LogError($"Scene change is already in progress for {newSceneName}"); + return; + } + + // Debug.Log($"ServerChangeScene {newSceneName}"); NetworkServer.SetAllClientsNotReady(); networkSceneName = newSceneName; // Let server prepare for scene change OnServerChangeScene(newSceneName); - // Suspend the server's transport while changing scenes - // It will be re-enabled in FinishLoadScene. - Transport.activeTransport.enabled = false; + // set server flag to stop processing messages while changing scenes + // it will be re-enabled in FinishLoadScene. + NetworkServer.isLoadingScene = true; loadingSceneAsync = SceneManager.LoadSceneAsync(newSceneName); @@ -783,33 +795,39 @@ namespace Mirror internal void ClientChangeScene(string newSceneName, SceneOperation sceneOperation = SceneOperation.Normal, bool customHandling = false) { - if (string.IsNullOrEmpty(newSceneName)) + if (string.IsNullOrWhiteSpace(newSceneName)) { Debug.LogError("ClientChangeScene empty scene name"); return; } - // Debug.Log("ClientChangeScene newSceneName:" + newSceneName + " networkSceneName:" + networkSceneName); + //Debug.Log($"ClientChangeScene newSceneName: {newSceneName} networkSceneName{networkSceneName}"); - // vis2k: pause message handling while loading scene. otherwise we will process messages and then lose all - // the state as soon as the load is finishing, causing all kinds of bugs because of missing state. + // Let client prepare for scene change + OnClientChangeScene(newSceneName, sceneOperation, customHandling); + + // After calling OnClientChangeScene, exit if server since server is already doing + // the actual scene change, and we don't need to do it for the host client + if (NetworkServer.active) + return; + + // set client flag to stop processing messages while loading scenes. + // otherwise we would process messages and then lose all the state + // as soon as the load is finishing, causing all kinds of bugs + // because of missing state. // (client may be null after StopClient etc.) // Debug.Log("ClientChangeScene: pausing handlers while scene is loading to avoid data loss after scene was loaded."); - Transport.activeTransport.enabled = false; + NetworkClient.isLoadingScene = true; // Cache sceneOperation so we know what was requested by the // Scene message in OnClientChangeScene and OnClientSceneChanged clientSceneOperation = sceneOperation; - // Let client prepare for scene change - OnClientChangeScene(newSceneName, sceneOperation, customHandling); - // scene handling will happen in overrides of OnClientChangeScene and/or OnClientSceneChanged + // Do not call FinishLoadScene here. Custom handler will assign loadingSceneAsync and we need + // to wait for that to finish. UpdateScene already checks for that to be not null and isDone. if (customHandling) - { - FinishLoadScene(); return; - } switch (sceneOperation) { @@ -825,8 +843,8 @@ namespace Mirror { Debug.LogWarning($"Scene {newSceneName} is already loaded"); - // Re-enable the transport that we disabled before entering this switch - Transport.activeTransport.enabled = true; + // Reset the flag that we disabled before entering this switch + NetworkClient.isLoadingScene = false; } break; case SceneOperation.UnloadAdditive: @@ -838,8 +856,8 @@ namespace Mirror { Debug.LogWarning($"Cannot unload {newSceneName} with UnloadAdditive operation"); - // Re-enable the transport that we disabled before entering this switch - Transport.activeTransport.enabled = true; + // Reset the flag that we disabled before entering this switch + NetworkClient.isLoadingScene = false; } break; } @@ -866,12 +884,12 @@ namespace Mirror { // TODO only respawn the server objects from that scene later! NetworkServer.SpawnObjects(); - // Debug.Log("Respawned Server objects after additive scene load: " + scene.name); + // Debug.Log($"Respawned Server objects after additive scene load: {scene.name}"); } if (NetworkClient.active) { NetworkClient.PrepareToSpawnSceneObjects(); - // Debug.Log("Rebuild Client spawnableObjects after additive scene load: " + scene.name); + // Debug.Log($"Rebuild Client spawnableObjects after additive scene load: {scene.name}"); } } } @@ -880,7 +898,7 @@ namespace Mirror { if (loadingSceneAsync != null && loadingSceneAsync.isDone) { - // Debug.Log("ClientChangeScene done readyCon:" + clientReadyConnection); + //Debug.Log($"ClientChangeScene done readyConn {clientReadyConnection}"); // try-finally to guarantee loadingSceneAsync being cleared. // fixes https://github.com/vis2k/Mirror/issues/2517 where if @@ -903,8 +921,9 @@ namespace Mirror // NOTE: this cannot use NetworkClient.allClients[0] - that client may be for a completely different purpose. // process queued messages that we received while loading the scene - Debug.Log("FinishLoadScene: resuming handlers after scene was loading."); - Transport.activeTransport.enabled = true; + //Debug.Log("FinishLoadScene: resuming handlers after scene was loading."); + NetworkServer.isLoadingScene = false; + NetworkClient.isLoadingScene = false; // host mode? if (mode == NetworkManagerMode.Host) @@ -932,11 +951,11 @@ namespace Mirror { // debug message is very important. if we ever break anything then // it's very obvious to notice. - Debug.Log("Finished loading scene in host mode."); + //Debug.Log("Finished loading scene in host mode."); if (clientReadyConnection != null) { - OnClientConnect(clientReadyConnection); + OnClientConnect(); clientLoadedScene = true; clientReadyConnection = null; } @@ -970,10 +989,7 @@ namespace Mirror OnServerSceneChanged(networkSceneName); if (NetworkClient.isConnected) - { - // let client know that we changed scene - OnClientSceneChanged(NetworkClient.connection); - } + OnClientSceneChanged(); } } @@ -983,7 +999,7 @@ namespace Mirror { // debug message is very important. if we ever break anything then // it's very obvious to notice. - Debug.Log("Finished loading scene in server-only mode."); + //Debug.Log("Finished loading scene in server-only mode."); NetworkServer.SpawnObjects(); OnServerSceneChanged(networkSceneName); @@ -995,19 +1011,17 @@ namespace Mirror { // debug message is very important. if we ever break anything then // it's very obvious to notice. - Debug.Log("Finished loading scene in client-only mode."); + //Debug.Log("Finished loading scene in client-only mode."); if (clientReadyConnection != null) { - OnClientConnect(clientReadyConnection); + OnClientConnect(); clientLoadedScene = true; clientReadyConnection = null; } if (NetworkClient.isConnected) - { - OnClientSceneChanged(NetworkClient.connection); - } + OnClientSceneChanged(); } /// @@ -1015,9 +1029,11 @@ namespace Mirror /// This is done automatically by NetworkStartPosition components, but can be done manually from user script code. /// /// Transform to register. + // Static because it's called from NetworkStartPosition::Awake + // and singleton may not exist yet public static void RegisterStartPosition(Transform start) { - // Debug.Log("RegisterStartPosition: (" + start.gameObject.name + ") " + start.position); + // Debug.Log($"RegisterStartPosition: {start.gameObject.name} {start.position}"); startPositions.Add(start); // reorder the list so that round-robin spawning uses the start positions @@ -1028,15 +1044,16 @@ namespace Mirror } /// Unregister a Transform from start positions. - // TODO why is this static? + // Static because it's called from NetworkStartPosition::OnDestroy + // and singleton may not exist yet public static void UnRegisterStartPosition(Transform start) { - // Debug.Log("UnRegisterStartPosition: (" + start.gameObject.name + ") " + start.position); + //Debug.Log($"UnRegisterStartPosition: {start.name} {start.position}"); startPositions.Remove(start); } /// Get the next NetworkStartPosition based on the selected PlayerSpawnMethod. - public Transform GetStartPosition() + public virtual Transform GetStartPosition() { // first remove any dead transforms startPositions.RemoveAll(t => t == null); @@ -1056,7 +1073,7 @@ namespace Mirror } } - void OnServerConnectInternal(NetworkConnection conn) + void OnServerConnectInternal(NetworkConnectionToClient conn) { //Debug.Log("NetworkManager.OnServerConnectInternal"); @@ -1074,7 +1091,7 @@ namespace Mirror // called after successful authentication // TODO do the NetworkServer.OnAuthenticated thing from x branch - void OnServerAuthenticated(NetworkConnection conn) + void OnServerAuthenticated(NetworkConnectionToClient conn) { //Debug.Log("NetworkManager.OnServerAuthenticated"); @@ -1091,13 +1108,13 @@ namespace Mirror OnServerConnect(conn); } - void OnServerReadyMessageInternal(NetworkConnection conn, ReadyMessage msg) + void OnServerReadyMessageInternal(NetworkConnectionToClient conn, ReadyMessage msg) { //Debug.Log("NetworkManager.OnServerReadyMessageInternal"); OnServerReady(conn); } - void OnServerAddPlayerInternal(NetworkConnection conn, AddPlayerMessage msg) + void OnServerAddPlayerInternal(NetworkConnectionToClient conn, AddPlayerMessage msg) { //Debug.Log("NetworkManager.OnServerAddPlayer"); @@ -1134,62 +1151,64 @@ namespace Mirror else { // authenticate immediately - OnClientAuthenticated(NetworkClient.connection); + OnClientAuthenticated(); } } // called after successful authentication - void OnClientAuthenticated(NetworkConnection conn) + void OnClientAuthenticated() { //Debug.Log("NetworkManager.OnClientAuthenticated"); // set connection to authenticated - conn.isAuthenticated = true; + NetworkClient.connection.isAuthenticated = true; // proceed with the login handshake by calling OnClientConnect - if (string.IsNullOrEmpty(onlineScene) || onlineScene == offlineScene || IsSceneActive(onlineScene)) + if (string.IsNullOrWhiteSpace(onlineScene) || onlineScene == offlineScene || IsSceneActive(onlineScene)) { clientLoadedScene = false; - OnClientConnect(conn); + OnClientConnect(); } else { // will wait for scene id to come from the server. clientLoadedScene = true; - clientReadyConnection = conn; + clientReadyConnection = NetworkClient.connection; } } - // TODO call OnClientDisconnect directly, don't pass the connection void OnClientDisconnectInternal() { //Debug.Log("NetworkManager.OnClientDisconnectInternal"); - OnClientDisconnect(NetworkClient.connection); + OnClientDisconnect(); } void OnClientNotReadyMessageInternal(NotReadyMessage msg) { //Debug.Log("NetworkManager.OnClientNotReadyMessageInternal"); NetworkClient.ready = false; - OnClientNotReady(NetworkClient.connection); + OnClientNotReady(); + // NOTE: clientReadyConnection is not set here! don't want OnClientConnect to be invoked again after scene changes. } void OnClientSceneInternal(SceneMessage msg) { //Debug.Log("NetworkManager.OnClientSceneInternal"); - if (NetworkClient.isConnected && !NetworkServer.active) + + // This needs to run for host client too. NetworkServer.active is checked there + if (NetworkClient.isConnected) { ClientChangeScene(msg.sceneName, msg.sceneOperation, msg.customHandling); } } /// Called on the server when a new client connects. - public virtual void OnServerConnect(NetworkConnection conn) {} + public virtual void OnServerConnect(NetworkConnectionToClient conn) {} /// Called on the server when a client disconnects. // Called by NetworkServer.OnTransportDisconnect! - public virtual void OnServerDisconnect(NetworkConnection conn) + public virtual void OnServerDisconnect(NetworkConnectionToClient conn) { // by default, this function destroys the connection's player. // can be overwritten for cases like delayed logouts in MMOs to @@ -1199,7 +1218,7 @@ namespace Mirror } /// Called on the server when a client is ready (= loaded the scene) - public virtual void OnServerReady(NetworkConnection conn) + public virtual void OnServerReady(NetworkConnectionToClient conn) { if (conn.identity == null) { @@ -1211,7 +1230,7 @@ namespace Mirror /// Called on server when a client requests to add the player. Adds playerPrefab by default. Can be overwritten. // The default implementation for this function creates a new player object from the playerPrefab. - public virtual void OnServerAddPlayer(NetworkConnection conn) + public virtual void OnServerAddPlayer(NetworkConnectionToClient conn) { Transform startPos = GetStartPosition(); GameObject player = startPos != null @@ -1224,8 +1243,16 @@ namespace Mirror NetworkServer.AddPlayerForConnection(conn, player); } - [Obsolete("OnServerError was removed because it hasn't been used in a long time.")] - public virtual void OnServerError(NetworkConnection conn, int errorCode) {} + // DEPRECATED 2022-05-12 + [Obsolete("OnServerError(conn, Exception) was changed to OnServerError(conn, TransportError, string)")] + public virtual void OnServerError(NetworkConnectionToClient conn, Exception exception) {} + /// Called on server when transport raises an exception. NetworkConnection may be null. + public virtual void OnServerError(NetworkConnectionToClient conn, TransportError error, string reason) + { +#pragma warning disable CS0618 + OnServerError(conn, new Exception(reason)); +#pragma warning restore CS0618 + } /// Called from ServerChangeScene immediately before SceneManager.LoadSceneAsync is executed public virtual void OnServerChangeScene(string newSceneName) {} @@ -1234,38 +1261,45 @@ namespace Mirror public virtual void OnServerSceneChanged(string sceneName) {} /// Called on the client when connected to a server. By default it sets client as ready and adds a player. - // TODO client only ever uses NetworkClient.connection. this parameter is redundant. - public virtual void OnClientConnect(NetworkConnection conn) + public virtual void OnClientConnect() { // OnClientConnect by default calls AddPlayer but it should not do // that when we have online/offline scenes. so we need the // clientLoadedScene flag to prevent it. if (!clientLoadedScene) { - // Ready/AddPlayer is usually triggered by a scene load - // completing. if no scene was loaded, then Ready/AddPlayer it - // here instead. - if (!NetworkClient.ready) NetworkClient.Ready(); + // Ready/AddPlayer is usually triggered by a scene load completing. + // if no scene was loaded, then Ready/AddPlayer it here instead. + if (!NetworkClient.ready) + NetworkClient.Ready(); + if (autoCreatePlayer) - { NetworkClient.AddPlayer(); - } } } /// Called on clients when disconnected from a server. - // TODO client only ever uses NetworkClient.connection. this parameter is redundant. - public virtual void OnClientDisconnect(NetworkConnection conn) + public virtual void OnClientDisconnect() { + if (mode == NetworkManagerMode.Offline) + return; + StopClient(); } - [Obsolete("OnClientError was removed because it hasn't been used in a long time.")] - public virtual void OnClientError(NetworkConnection conn, int errorCode) {} + // DEPRECATED 2022-05-12 + [Obsolete("OnClientError(Exception) was changed to OnClientError(TransportError, string)")] + public virtual void OnClientError(Exception exception) {} + /// Called on client when transport raises an exception. + public virtual void OnClientError(TransportError error, string reason) + { +#pragma warning disable CS0618 + OnClientError(new Exception(reason)); +#pragma warning restore CS0618 + } /// Called on clients when a servers tells the client it is no longer ready, e.g. when switching scenes. - // TODO client only ever uses NetworkClient.connection. this parameter is redundant. - public virtual void OnClientNotReady(NetworkConnection conn) {} + public virtual void OnClientNotReady() {} /// Called from ClientChangeScene immediately before SceneManager.LoadSceneAsync is executed // customHandling: indicates if scene loading will be handled through overrides @@ -1275,8 +1309,7 @@ namespace Mirror // Scene changes can cause player objects to be destroyed. The default // implementation of OnClientSceneChanged in the NetworkManager is to // add a player object for the connection if no player object exists. - // TODO client only ever uses NetworkClient.connection. this parameter is redundant. - public virtual void OnClientSceneChanged(NetworkConnection conn) + public virtual void OnClientSceneChanged() { // always become ready. if (!NetworkClient.ready) NetworkClient.Ready(); diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkManager.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkManager.cs.meta index 4a0c1ca..0a7564a 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkManager.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/NetworkManager.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkManagerHUD.cs b/UnityProject/Assets/Mirror/Runtime/NetworkManagerHUD.cs index 7bb2fad..cba968d 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkManagerHUD.cs +++ b/UnityProject/Assets/Mirror/Runtime/NetworkManagerHUD.cs @@ -1,22 +1,18 @@ // vis2k: GUILayout instead of spacey += ...; removed Update hotkeys to avoid // confusion if someone accidentally presses one. -using System; using UnityEngine; namespace Mirror { /// Shows NetworkManager controls in a GUI at runtime. [DisallowMultipleComponent] - [AddComponentMenu("Network/NetworkManagerHUD")] + [AddComponentMenu("Network/Network Manager HUD")] [RequireComponent(typeof(NetworkManager))] [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-manager-hud")] public class NetworkManagerHUD : MonoBehaviour { NetworkManager manager; - [Obsolete("showGUI will be removed unless someone has a valid use case. Simply use or don't use the HUD component.")] - public bool showGUI = true; - public int offsetX; public int offsetY; @@ -27,10 +23,6 @@ namespace Mirror void OnGUI() { -#pragma warning disable 618 - if (!showGUI) return; -#pragma warning restore 618 - GUILayout.BeginArea(new Rect(10 + offsetX, 40 + offsetY, 215, 9999)); if (!NetworkClient.isConnected && !NetworkServer.active) { @@ -78,6 +70,7 @@ namespace Mirror { manager.StartClient(); } + // This updates networkAddress every frame from the TextField manager.networkAddress = GUILayout.TextField(manager.networkAddress); GUILayout.EndHorizontal(); @@ -95,7 +88,7 @@ namespace Mirror else { // Connecting - GUILayout.Label("Connecting to " + manager.networkAddress + ".."); + GUILayout.Label($"Connecting to {manager.networkAddress}.."); if (GUILayout.Button("Cancel Connection Attempt")) { manager.StopClient(); diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkManagerHUD.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkManagerHUD.cs.meta index c1437a7..a720b9c 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkManagerHUD.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/NetworkManagerHUD.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkMessage.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkMessage.cs.meta index ce49c4e..73d3d8f 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkMessage.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/NetworkMessage.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkReader.cs b/UnityProject/Assets/Mirror/Runtime/NetworkReader.cs index d2e52e8..eac889d 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkReader.cs +++ b/UnityProject/Assets/Mirror/Runtime/NetworkReader.cs @@ -1,19 +1,11 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Text; +using System.Runtime.CompilerServices; +using Unity.Collections.LowLevel.Unsafe; using UnityEngine; namespace Mirror { - /// Helper class that weaver populates with all reader types. - // Note that c# creates a different static variable for each type - // -> Weaver.ReaderWriterProcessor.InitializeReaderAndWriters() populates it - public static class Reader - { - public static Func read; - } - /// Network Reader for most simple types like floats, ints, buffers, structs, etc. Use NetworkReaderPool.GetReader() to avoid allocations. // Note: This class is intended to be extremely pedantic, // and throw exceptions whenever stuff is going slightly wrong. @@ -31,7 +23,18 @@ namespace Mirror public int Position; /// Total number of bytes to read from buffer - public int Length => buffer.Count; + public int Length + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => buffer.Count; + } + + /// Remaining bytes that can be read, for convenience. + public int Remaining + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Length - Position; + } public NetworkReader(byte[] bytes) { @@ -43,27 +46,133 @@ namespace Mirror buffer = segment; } - public byte ReadByte() + // sometimes it's useful to point a reader on another buffer instead of + // allocating a new reader (e.g. NetworkReaderPool) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetBuffer(byte[] bytes) { - if (Position + 1 > buffer.Count) - { - throw new EndOfStreamException("ReadByte out of range:" + ToString()); - } - return buffer.Array[buffer.Offset + Position++]; + buffer = new ArraySegment(bytes); + Position = 0; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetBuffer(ArraySegment segment) + { + buffer = segment; + Position = 0; + } + + // ReadBlittable from DOTSNET + // this is extremely fast, but only works for blittable types. + // => private to make sure nobody accidentally uses it for non-blittable + // + // Benchmark: see NetworkWriter.WriteBlittable! + // + // Note: + // ReadBlittable assumes same endianness for server & client. + // All Unity 2018+ platforms are little endian. + // + // This is not safe to expose to random structs. + // * StructLayout.Sequential is the default, which is safe. + // if the struct contains a reference type, it is converted to Auto. + // but since all structs here are unmanaged blittable, it's safe. + // see also: https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.layoutkind?view=netframework-4.8#system-runtime-interopservices-layoutkind-sequential + // * StructLayout.Pack depends on CPU word size. + // this may be different 4 or 8 on some ARM systems, etc. + // this is not safe, and would cause bytes/shorts etc. to be padded. + // see also: https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.structlayoutattribute.pack?view=net-6.0 + // * If we force pack all to '1', they would have no padding which is + // great for bandwidth. but on some android systems, CPU can't read + // unaligned memory. + // see also: https://github.com/vis2k/Mirror/issues/3044 + // * The only option would be to force explicit layout with multiples + // of word size. but this requires lots of weaver checking and is + // still questionable (IL2CPP etc.). + // + // Note: inlining ReadBlittable is enough. don't inline ReadInt etc. + // we don't want ReadBlittable to be copied in place everywhere. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal unsafe T ReadBlittable() + where T : unmanaged + { + // check if blittable for safety +#if UNITY_EDITOR + if (!UnsafeUtility.IsBlittable(typeof(T))) + { + throw new ArgumentException($"{typeof(T)} is not blittable!"); + } +#endif + + // calculate size + // sizeof(T) gets the managed size at compile time. + // Marshal.SizeOf gets the unmanaged size at runtime (slow). + // => our 1mio writes benchmark is 6x slower with Marshal.SizeOf + // => for blittable types, sizeof(T) is even recommended: + // https://docs.microsoft.com/en-us/dotnet/standard/native-interop/best-practices + int size = sizeof(T); + + // enough data to read? + if (Position + size > buffer.Count) + { + throw new EndOfStreamException($"ReadBlittable<{typeof(T)}> out of range: {ToString()}"); + } + + // read blittable + T value; + fixed (byte* ptr = &buffer.Array[buffer.Offset + Position]) + { +#if UNITY_ANDROID + // on some android systems, reading *(T*)ptr throws a NRE if + // the ptr isn't aligned (i.e. if Position is 1,2,3,5, etc.). + // here we have to use memcpy. + // + // => we can't get a pointer of a struct in C# without + // marshalling allocations + // => instead, we stack allocate an array of type T and use that + // => stackalloc avoids GC and is very fast. it only works for + // value types, but all blittable types are anyway. + // + // this way, we can still support blittable reads on android. + // see also: https://github.com/vis2k/Mirror/issues/3044 + // (solution discovered by AIIO, FakeByte, mischa) + T* valueBuffer = stackalloc T[1]; + UnsafeUtility.MemCpy(valueBuffer, ptr, size); + value = valueBuffer[0]; +#else + // cast buffer to a T* pointer and then read from it. + value = *(T*)ptr; +#endif + } + Position += size; + return value; + } + + // blittable'?' template for code reuse + // note: bool isn't blittable. need to read as byte. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal T? ReadBlittableNullable() + where T : unmanaged => + ReadByte() != 0 ? ReadBlittable() : default(T?); + + public byte ReadByte() => ReadBlittable(); + /// Read 'count' bytes into the bytes array - // TODO why does this also return bytes[]??? + // NOTE: returns byte[] because all reader functions return something. public byte[] ReadBytes(byte[] bytes, int count) { // check if passed byte array is big enough if (count > bytes.Length) { - throw new EndOfStreamException("ReadBytes can't read " + count + " + bytes because the passed byte[] only has length " + bytes.Length); + throw new EndOfStreamException($"ReadBytes can't read {count} + bytes because the passed byte[] only has length {bytes.Length}"); + } + // check if within buffer limits + if (Position + count > buffer.Count) + { + throw new EndOfStreamException($"ReadBytesSegment can't read {count} bytes because it would read past the end of the stream. {ToString()}"); } - ArraySegment data = ReadBytesSegment(count); - Array.Copy(data.Array, data.Offset, bytes, 0, count); + Array.Copy(buffer.Array, buffer.Offset + Position, bytes, 0, count); + Position += count; return bytes; } @@ -73,7 +182,7 @@ namespace Mirror // check if within buffer limits if (Position + count > buffer.Count) { - throw new EndOfStreamException("ReadBytesSegment can't read " + count + " bytes because it would read past the end of the stream. " + ToString()); + throw new EndOfStreamException($"ReadBytesSegment can't read {count} bytes because it would read past the end of the stream. {ToString()}"); } // return the segment @@ -82,12 +191,12 @@ namespace Mirror return result; } - public override string ToString() - { - return $"NetworkReader pos={Position} len={Length} buffer={BitConverter.ToString(buffer.Array, buffer.Offset, buffer.Count)}"; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override string ToString() => + $"NetworkReader pos={Position} len={Length} buffer={BitConverter.ToString(buffer.Array, buffer.Offset, buffer.Count)}"; /// Reads any data type that mirror supports. Uses weaver populated Reader(T).read + [MethodImpl(MethodImplOptions.AggressiveInlining)] public T Read() { Func readerDelegate = Reader.read; @@ -100,303 +209,11 @@ namespace Mirror } } - // Mirror's Weaver automatically detects all NetworkReader function types, - // but they do all need to be extensions. - public static class NetworkReaderExtensions + /// Helper class that weaver populates with all reader types. + // Note that c# creates a different static variable for each type + // -> Weaver.ReaderWriterProcessor.InitializeReaderAndWriters() populates it + public static class Reader { - // cache encoding instead of creating it each time - // 1000 readers before: 1MB GC, 30ms - // 1000 readers after: 0.8MB GC, 18ms - static readonly UTF8Encoding encoding = new UTF8Encoding(false, true); - - public static byte ReadByte(this NetworkReader reader) => reader.ReadByte(); - public static sbyte ReadSByte(this NetworkReader reader) => (sbyte)reader.ReadByte(); - public static char ReadChar(this NetworkReader reader) => (char)reader.ReadUShort(); - - // Deprecated 2021-05-18 - [Obsolete("We've cleaned up the API. Use ReadBool instead.")] - public static bool ReadBoolean(this NetworkReader reader) => reader.ReadBool(); - public static bool ReadBool(this NetworkReader reader) => reader.ReadByte() != 0; - - // Deprecated 2021-05-18 - [Obsolete("We've cleaned up the API. Use ReadShort instead.")] - public static short ReadInt16(this NetworkReader reader) => reader.ReadShort(); - public static short ReadShort(this NetworkReader reader) => (short)reader.ReadUShort(); - - // Deprecated 2021-05-18 - [Obsolete("We've cleaned up the API. Use ReadUShort instead.")] - public static ushort ReadUInt16(this NetworkReader reader) => reader.ReadUShort(); - public static ushort ReadUShort(this NetworkReader reader) - { - ushort value = 0; - value |= reader.ReadByte(); - value |= (ushort)(reader.ReadByte() << 8); - return value; - } - - // Deprecated 2021-05-18 - [Obsolete("We've cleaned up the API. Use ReadInt instead.")] - public static int ReadInt32(this NetworkReader reader) => reader.ReadInt(); - public static int ReadInt(this NetworkReader reader) => (int)reader.ReadUInt(); - - // Deprecated 2021-05-18 - [Obsolete("We've cleaned up the API. Use ReadUInt instead.")] - public static uint ReadUInt32(this NetworkReader reader) => reader.ReadUInt(); - public static uint ReadUInt(this NetworkReader reader) - { - uint value = 0; - value |= reader.ReadByte(); - value |= (uint)(reader.ReadByte() << 8); - value |= (uint)(reader.ReadByte() << 16); - value |= (uint)(reader.ReadByte() << 24); - return value; - } - - // Deprecated 2021-05-18 - [Obsolete("We've cleaned up the API. Use ReadLong instead.")] - public static long ReadInt64(this NetworkReader reader) => reader.ReadLong(); - public static long ReadLong(this NetworkReader reader) => (long)reader.ReadULong(); - - // Deprecated 2021-05-18 - [Obsolete("We've cleaned up the API. Use ReadULong instead.")] - public static ulong ReadUInt64(this NetworkReader reader) => reader.ReadULong(); - public static ulong ReadULong(this NetworkReader reader) - { - ulong value = 0; - value |= reader.ReadByte(); - value |= ((ulong)reader.ReadByte()) << 8; - value |= ((ulong)reader.ReadByte()) << 16; - value |= ((ulong)reader.ReadByte()) << 24; - value |= ((ulong)reader.ReadByte()) << 32; - value |= ((ulong)reader.ReadByte()) << 40; - value |= ((ulong)reader.ReadByte()) << 48; - value |= ((ulong)reader.ReadByte()) << 56; - return value; - } - - // Deprecated 2021-05-18 - [Obsolete("We've cleaned up the API. Use ReadFloat instead.")] - public static float ReadSingle(this NetworkReader reader) => reader.ReadFloat(); - public static float ReadFloat(this NetworkReader reader) - { - UIntFloat converter = new UIntFloat(); - converter.intValue = reader.ReadUInt(); - return converter.floatValue; - } - - public static double ReadDouble(this NetworkReader reader) - { - UIntDouble converter = new UIntDouble(); - converter.longValue = reader.ReadULong(); - return converter.doubleValue; - } - public static decimal ReadDecimal(this NetworkReader reader) - { - UIntDecimal converter = new UIntDecimal(); - converter.longValue1 = reader.ReadULong(); - converter.longValue2 = reader.ReadULong(); - return converter.decimalValue; - } - - /// if an invalid utf8 string is sent - public static string ReadString(this NetworkReader reader) - { - // read number of bytes - ushort size = reader.ReadUShort(); - - // null support, see NetworkWriter - if (size == 0) - return null; - - int realSize = size - 1; - - // make sure it's within limits to avoid allocation attacks etc. - if (realSize >= NetworkWriter.MaxStringLength) - { - throw new EndOfStreamException("ReadString too long: " + realSize + ". Limit is: " + NetworkWriter.MaxStringLength); - } - - ArraySegment data = reader.ReadBytesSegment(realSize); - - // convert directly from buffer to string via encoding - return encoding.GetString(data.Array, data.Offset, data.Count); - } - - /// if count is invalid - public static byte[] ReadBytesAndSize(this NetworkReader reader) - { - // count = 0 means the array was null - // otherwise count -1 is the length of the array - uint count = reader.ReadUInt(); - // Use checked() to force it to throw OverflowException if data is invalid - return count == 0 ? null : reader.ReadBytes(checked((int)(count - 1u))); - } - - /// if count is invalid - public static ArraySegment ReadBytesAndSizeSegment(this NetworkReader reader) - { - // count = 0 means the array was null - // otherwise count - 1 is the length of the array - uint count = reader.ReadUInt(); - // Use checked() to force it to throw OverflowException if data is invalid - return count == 0 ? default : reader.ReadBytesSegment(checked((int)(count - 1u))); - } - - public static Vector2 ReadVector2(this NetworkReader reader) => new Vector2(reader.ReadFloat(), reader.ReadFloat()); - public static Vector3 ReadVector3(this NetworkReader reader) => new Vector3(reader.ReadFloat(), reader.ReadFloat(), reader.ReadFloat()); - public static Vector4 ReadVector4(this NetworkReader reader) => new Vector4(reader.ReadFloat(), reader.ReadFloat(), reader.ReadFloat(), reader.ReadFloat()); - public static Vector2Int ReadVector2Int(this NetworkReader reader) => new Vector2Int(reader.ReadInt(), reader.ReadInt()); - public static Vector3Int ReadVector3Int(this NetworkReader reader) => new Vector3Int(reader.ReadInt(), reader.ReadInt(), reader.ReadInt()); - public static Color ReadColor(this NetworkReader reader) => new Color(reader.ReadFloat(), reader.ReadFloat(), reader.ReadFloat(), reader.ReadFloat()); - public static Color32 ReadColor32(this NetworkReader reader) => new Color32(reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); - public static Quaternion ReadQuaternion(this NetworkReader reader) => new Quaternion(reader.ReadFloat(), reader.ReadFloat(), reader.ReadFloat(), reader.ReadFloat()); - public static Rect ReadRect(this NetworkReader reader) => new Rect(reader.ReadFloat(), reader.ReadFloat(), reader.ReadFloat(), reader.ReadFloat()); - public static Plane ReadPlane(this NetworkReader reader) => new Plane(reader.ReadVector3(), reader.ReadFloat()); - public static Ray ReadRay(this NetworkReader reader) => new Ray(reader.ReadVector3(), reader.ReadVector3()); - public static Matrix4x4 ReadMatrix4x4(this NetworkReader reader) - { - return new Matrix4x4 - { - m00 = reader.ReadFloat(), - m01 = reader.ReadFloat(), - m02 = reader.ReadFloat(), - m03 = reader.ReadFloat(), - m10 = reader.ReadFloat(), - m11 = reader.ReadFloat(), - m12 = reader.ReadFloat(), - m13 = reader.ReadFloat(), - m20 = reader.ReadFloat(), - m21 = reader.ReadFloat(), - m22 = reader.ReadFloat(), - m23 = reader.ReadFloat(), - m30 = reader.ReadFloat(), - m31 = reader.ReadFloat(), - m32 = reader.ReadFloat(), - m33 = reader.ReadFloat() - }; - } - public static byte[] ReadBytes(this NetworkReader reader, int count) - { - byte[] bytes = new byte[count]; - reader.ReadBytes(bytes, count); - return bytes; - } - public static Guid ReadGuid(this NetworkReader reader) => new Guid(reader.ReadBytes(16)); - - public static Transform ReadTransform(this NetworkReader reader) - { - // Don't use null propagation here as it could lead to MissingReferenceException - NetworkIdentity networkIdentity = reader.ReadNetworkIdentity(); - return networkIdentity != null ? networkIdentity.transform : null; - } - - public static GameObject ReadGameObject(this NetworkReader reader) - { - // Don't use null propagation here as it could lead to MissingReferenceException - NetworkIdentity networkIdentity = reader.ReadNetworkIdentity(); - return networkIdentity != null ? networkIdentity.gameObject : null; - } - - public static NetworkIdentity ReadNetworkIdentity(this NetworkReader reader) - { - uint netId = reader.ReadUInt(); - if (netId == 0) - return null; - - if (NetworkIdentity.spawned.TryGetValue(netId, out NetworkIdentity identity)) - { - return identity; - } - - // a netId not being in spawned is common. - // for example, "[SyncVar] NetworkIdentity target" netId would not - // be known on client if the monster walks out of proximity for a - // moment. no need to log any error or warning here. - return null; - } - - public static NetworkBehaviour ReadNetworkBehaviour(this NetworkReader reader) - { - uint netId = reader.ReadUInt(); - if (netId == 0) - return null; - - // if netId is not 0, then index is also sent to read before returning - byte componentIndex = reader.ReadByte(); - - if (NetworkIdentity.spawned.TryGetValue(netId, out NetworkIdentity identity)) - { - return identity.NetworkBehaviours[componentIndex]; - } - - // a netId not being in spawned is common. - // for example, "[SyncVar] NetworkBehaviour target" netId would not - // be known on client if the monster walks out of proximity for a - // moment. no need to log any error or warning here. - return null; - } - - public static T ReadNetworkBehaviour(this NetworkReader reader) where T : NetworkBehaviour - { - return reader.ReadNetworkBehaviour() as T; - } - - public static NetworkBehaviour.NetworkBehaviourSyncVar ReadNetworkBehaviourSyncVar(this NetworkReader reader) - { - uint netId = reader.ReadUInt(); - byte componentIndex = default; - - // if netId is not 0, then index is also sent to read before returning - if (netId != 0) - { - componentIndex = reader.ReadByte(); - } - - return new NetworkBehaviour.NetworkBehaviourSyncVar(netId, componentIndex); - } - - public static List ReadList(this NetworkReader reader) - { - int length = reader.ReadInt(); - if (length < 0) - return null; - List result = new List(length); - for (int i = 0; i < length; i++) - { - result.Add(reader.Read()); - } - return result; - } - - public static T[] ReadArray(this NetworkReader reader) - { - int length = reader.ReadInt(); - - // we write -1 for null - if (length < 0) - return null; - - // todo throw an exception for other negative values (we never write them, likely to be attacker) - - // this assumes that a reader for T reads at least 1 bytes - // we can't know the exact size of T because it could have a user created reader - // NOTE: don't add to length as it could overflow if value is int.max - if (length > reader.Length - reader.Position) - { - throw new EndOfStreamException($"Received array that is too large: {length}"); - } - - T[] result = new T[length]; - for (int i = 0; i < length; i++) - { - result[i] = reader.Read(); - } - return result; - } - - public static Uri ReadUri(this NetworkReader reader) - { - return new Uri(reader.ReadString()); - } + public static Func read; } } diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkReader.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkReader.cs.meta index 4957f80..65ad3f0 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkReader.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/NetworkReader.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkReaderExtensions.cs b/UnityProject/Assets/Mirror/Runtime/NetworkReaderExtensions.cs new file mode 100644 index 0000000..fe7288f --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/NetworkReaderExtensions.cs @@ -0,0 +1,315 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using UnityEngine; + +namespace Mirror +{ + // Mirror's Weaver automatically detects all NetworkReader function types, + // but they do all need to be extensions. + public static class NetworkReaderExtensions + { + // cache encoding instead of creating it each time + // 1000 readers before: 1MB GC, 30ms + // 1000 readers after: 0.8MB GC, 18ms + static readonly UTF8Encoding encoding = new UTF8Encoding(false, true); + + public static byte ReadByte(this NetworkReader reader) => reader.ReadBlittable(); + public static byte? ReadByteNullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static sbyte ReadSByte(this NetworkReader reader) => reader.ReadBlittable(); + public static sbyte? ReadSByteNullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + // bool is not blittable. read as ushort. + public static char ReadChar(this NetworkReader reader) => (char)reader.ReadBlittable(); + public static char? ReadCharNullable(this NetworkReader reader) => (char?)reader.ReadBlittableNullable(); + + // bool is not blittable. read as byte. + public static bool ReadBool(this NetworkReader reader) => reader.ReadBlittable() != 0; + public static bool? ReadBoolNullable(this NetworkReader reader) + { + byte? value = reader.ReadBlittableNullable(); + return value.HasValue ? (value.Value != 0) : default(bool?); + } + + public static short ReadShort(this NetworkReader reader) => (short)reader.ReadUShort(); + public static short? ReadShortNullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static ushort ReadUShort(this NetworkReader reader) => reader.ReadBlittable(); + public static ushort? ReadUShortNullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static int ReadInt(this NetworkReader reader) => reader.ReadBlittable(); + public static int? ReadIntNullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static uint ReadUInt(this NetworkReader reader) => reader.ReadBlittable(); + public static uint? ReadUIntNullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static long ReadLong(this NetworkReader reader) => reader.ReadBlittable(); + public static long? ReadLongNullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static ulong ReadULong(this NetworkReader reader) => reader.ReadBlittable(); + public static ulong? ReadULongNullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static float ReadFloat(this NetworkReader reader) => reader.ReadBlittable(); + public static float? ReadFloatNullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static double ReadDouble(this NetworkReader reader) => reader.ReadBlittable(); + public static double? ReadDoubleNullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static decimal ReadDecimal(this NetworkReader reader) => reader.ReadBlittable(); + public static decimal? ReadDecimalNullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + /// if an invalid utf8 string is sent + public static string ReadString(this NetworkReader reader) + { + // read number of bytes + ushort size = reader.ReadUShort(); + + // null support, see NetworkWriter + if (size == 0) + return null; + + int realSize = size - 1; + + // make sure it's within limits to avoid allocation attacks etc. + if (realSize >= NetworkWriter.MaxStringLength) + { + throw new EndOfStreamException($"ReadString too long: {realSize}. Limit is: {NetworkWriter.MaxStringLength}"); + } + + ArraySegment data = reader.ReadBytesSegment(realSize); + + // convert directly from buffer to string via encoding + return encoding.GetString(data.Array, data.Offset, data.Count); + } + + /// if count is invalid + public static byte[] ReadBytesAndSize(this NetworkReader reader) + { + // count = 0 means the array was null + // otherwise count -1 is the length of the array + uint count = reader.ReadUInt(); + // Use checked() to force it to throw OverflowException if data is invalid + return count == 0 ? null : reader.ReadBytes(checked((int)(count - 1u))); + } + + public static byte[] ReadBytes(this NetworkReader reader, int count) + { + byte[] bytes = new byte[count]; + reader.ReadBytes(bytes, count); + return bytes; + } + + /// if count is invalid + public static ArraySegment ReadBytesAndSizeSegment(this NetworkReader reader) + { + // count = 0 means the array was null + // otherwise count - 1 is the length of the array + uint count = reader.ReadUInt(); + // Use checked() to force it to throw OverflowException if data is invalid + return count == 0 ? default : reader.ReadBytesSegment(checked((int)(count - 1u))); + } + + public static Vector2 ReadVector2(this NetworkReader reader) => reader.ReadBlittable(); + public static Vector2? ReadVector2Nullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static Vector3 ReadVector3(this NetworkReader reader) => reader.ReadBlittable(); + public static Vector3? ReadVector3Nullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static Vector4 ReadVector4(this NetworkReader reader) => reader.ReadBlittable(); + public static Vector4? ReadVector4Nullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static Vector2Int ReadVector2Int(this NetworkReader reader) => reader.ReadBlittable(); + public static Vector2Int? ReadVector2IntNullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static Vector3Int ReadVector3Int(this NetworkReader reader) => reader.ReadBlittable(); + public static Vector3Int? ReadVector3IntNullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static Color ReadColor(this NetworkReader reader) => reader.ReadBlittable(); + public static Color? ReadColorNullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static Color32 ReadColor32(this NetworkReader reader) => reader.ReadBlittable(); + public static Color32? ReadColor32Nullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static Quaternion ReadQuaternion(this NetworkReader reader) => reader.ReadBlittable(); + public static Quaternion? ReadQuaternionNullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static Rect ReadRect(this NetworkReader reader) => reader.ReadBlittable(); + public static Rect? ReadRectNullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static Plane ReadPlane(this NetworkReader reader) => reader.ReadBlittable(); + public static Plane? ReadPlaneNullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static Ray ReadRay(this NetworkReader reader) => reader.ReadBlittable(); + public static Ray? ReadRayNullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static Matrix4x4 ReadMatrix4x4(this NetworkReader reader)=> reader.ReadBlittable(); + public static Matrix4x4? ReadMatrix4x4Nullable(this NetworkReader reader) => reader.ReadBlittableNullable(); + + public static Guid ReadGuid(this NetworkReader reader) + { + // ReadBlittable(Guid) isn't safe. see ReadBlittable comments. + // Guid is Sequential, but we can't guarantee packing. + if (reader.Remaining >= 16) + { + ReadOnlySpan span = new ReadOnlySpan(reader.buffer.Array, reader.buffer.Offset + reader.Position, 16); + reader.Position += 16; + return new Guid(span); + } + throw new EndOfStreamException($"ReadGuid out of range: {reader}"); + } + public static Guid? ReadGuidNullable(this NetworkReader reader) => reader.ReadBool() ? ReadGuid(reader) : default(Guid?); + + public static NetworkIdentity ReadNetworkIdentity(this NetworkReader reader) + { + uint netId = reader.ReadUInt(); + if (netId == 0) + return null; + + // NOTE: a netId not being in spawned is common. + // for example, "[SyncVar] NetworkIdentity target" netId would not + // be known on client if the monster walks out of proximity for a + // moment. no need to log any error or warning here. + return Utils.GetSpawnedInServerOrClient(netId); + } + + public static NetworkBehaviour ReadNetworkBehaviour(this NetworkReader reader) + { + // read netId first. + // + // IMPORTANT: if netId != 0, writer always writes componentIndex. + // reusing ReadNetworkIdentity() might return a null NetworkIdentity + // even if netId was != 0 but the identity disappeared on the client, + // resulting in unequal amounts of data being written / read. + // https://github.com/vis2k/Mirror/issues/2972 + uint netId = reader.ReadUInt(); + if (netId == 0) + return null; + + // read component index in any case, BEFORE searching the spawned + // NetworkIdentity by netId. + byte componentIndex = reader.ReadByte(); + + // NOTE: a netId not being in spawned is common. + // for example, "[SyncVar] NetworkIdentity target" netId would not + // be known on client if the monster walks out of proximity for a + // moment. no need to log any error or warning here. + NetworkIdentity identity = Utils.GetSpawnedInServerOrClient(netId); + + return identity != null + ? identity.NetworkBehaviours[componentIndex] + : null; + } + + public static T ReadNetworkBehaviour(this NetworkReader reader) where T : NetworkBehaviour + { + return reader.ReadNetworkBehaviour() as T; + } + + public static NetworkBehaviour.NetworkBehaviourSyncVar ReadNetworkBehaviourSyncVar(this NetworkReader reader) + { + uint netId = reader.ReadUInt(); + byte componentIndex = default; + + // if netId is not 0, then index is also sent to read before returning + if (netId != 0) + { + componentIndex = reader.ReadByte(); + } + + return new NetworkBehaviour.NetworkBehaviourSyncVar(netId, componentIndex); + } + + public static Transform ReadTransform(this NetworkReader reader) + { + // Don't use null propagation here as it could lead to MissingReferenceException + NetworkIdentity networkIdentity = reader.ReadNetworkIdentity(); + return networkIdentity != null ? networkIdentity.transform : null; + } + + public static GameObject ReadGameObject(this NetworkReader reader) + { + // Don't use null propagation here as it could lead to MissingReferenceException + NetworkIdentity networkIdentity = reader.ReadNetworkIdentity(); + return networkIdentity != null ? networkIdentity.gameObject : null; + } + + public static List ReadList(this NetworkReader reader) + { + int length = reader.ReadInt(); + if (length < 0) + return null; + List result = new List(length); + for (int i = 0; i < length; i++) + { + result.Add(reader.Read()); + } + return result; + } + + public static T[] ReadArray(this NetworkReader reader) + { + int length = reader.ReadInt(); + + // we write -1 for null + if (length < 0) + return null; + + // todo throw an exception for other negative values (we never write them, likely to be attacker) + + // this assumes that a reader for T reads at least 1 bytes + // we can't know the exact size of T because it could have a user created reader + // NOTE: don't add to length as it could overflow if value is int.max + if (length > reader.Length - reader.Position) + { + throw new EndOfStreamException($"Received array that is too large: {length}"); + } + + T[] result = new T[length]; + for (int i = 0; i < length; i++) + { + result[i] = reader.Read(); + } + return result; + } + + public static Uri ReadUri(this NetworkReader reader) + { + string uriString = reader.ReadString(); + return (string.IsNullOrWhiteSpace(uriString) ? null : new Uri(uriString)); + } + + public static Texture2D ReadTexture2D(this NetworkReader reader) + { + // TODO allocation protection when sending textures to server. + // currently can allocate 32k x 32k x 4 byte = 3.8 GB + + // support 'null' textures for [SyncVar]s etc. + // https://github.com/vis2k/Mirror/issues/3144 + short width = reader.ReadShort(); + if (width == -1) return null; + + // read height + short height = reader.ReadShort(); + Texture2D texture2D = new Texture2D(width, height); + + // read pixel content + Color32[] pixels = reader.ReadArray(); + texture2D.SetPixels32(pixels); + texture2D.Apply(); + return texture2D; + } + + public static Sprite ReadSprite(this NetworkReader reader) + { + // support 'null' textures for [SyncVar]s etc. + // https://github.com/vis2k/Mirror/issues/3144 + Texture2D texture = reader.ReadTexture2D(); + if (texture == null) return null; + + // otherwise create a valid sprite + return Sprite.Create(texture, reader.ReadRect(), reader.ReadVector2()); + } + } +} diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkReaderExtensions.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkReaderExtensions.cs.meta new file mode 100644 index 0000000..66536c9 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/NetworkReaderExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 364a9f7ccd5541e19aa2ae0b81f0b3cf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkReaderPool.cs b/UnityProject/Assets/Mirror/Runtime/NetworkReaderPool.cs index b5591ba..ebbfac5 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkReaderPool.cs +++ b/UnityProject/Assets/Mirror/Runtime/NetworkReaderPool.cs @@ -1,50 +1,57 @@ +// API consistent with Microsoft's ObjectPool. using System; +using System.Runtime.CompilerServices; namespace Mirror { - /// Pooled NetworkReader, automatically returned to pool when using 'using' - public sealed class PooledNetworkReader : NetworkReader, IDisposable - { - internal PooledNetworkReader(byte[] bytes) : base(bytes) {} - internal PooledNetworkReader(ArraySegment segment) : base(segment) {} - public void Dispose() => NetworkReaderPool.Recycle(this); - } - /// Pool of NetworkReaders to avoid allocations. public static class NetworkReaderPool { // reuse Pool // we still wrap it in NetworkReaderPool.Get/Recyle so we can reset the // position and array before reusing. - static readonly Pool Pool = new Pool( + static readonly Pool Pool = new Pool( // byte[] will be assigned in GetReader - () => new PooledNetworkReader(new byte[]{}), + () => new NetworkReaderPooled(new byte[]{}), // initial capacity to avoid allocations in the first few frames 1000 ); + // DEPRECATED 2022-03-10 + [Obsolete("GetReader() was renamed to Get()")] + public static NetworkReaderPooled GetReader(byte[] bytes) => Get(bytes); + /// Get the next reader in the pool. If pool is empty, creates a new Reader - public static PooledNetworkReader GetReader(byte[] bytes) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NetworkReaderPooled Get(byte[] bytes) { // grab from pool & set buffer - PooledNetworkReader reader = Pool.Take(); - reader.buffer = new ArraySegment(bytes); - reader.Position = 0; + NetworkReaderPooled reader = Pool.Get(); + reader.SetBuffer(bytes); return reader; } + // DEPRECATED 2022-03-10 + [Obsolete("GetReader() was renamed to Get()")] + public static NetworkReaderPooled GetReader(ArraySegment segment) => Get(segment); + /// Get the next reader in the pool. If pool is empty, creates a new Reader - public static PooledNetworkReader GetReader(ArraySegment segment) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NetworkReaderPooled Get(ArraySegment segment) { // grab from pool & set buffer - PooledNetworkReader reader = Pool.Take(); - reader.buffer = segment; - reader.Position = 0; + NetworkReaderPooled reader = Pool.Get(); + reader.SetBuffer(segment); return reader; } + // DEPRECATED 2022-03-10 + [Obsolete("Recycle() was renamed to Return()")] + public static void Recycle(NetworkReaderPooled reader) => Return(reader); + /// Returns a reader to the pool. - public static void Recycle(PooledNetworkReader reader) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Return(NetworkReaderPooled reader) { Pool.Return(reader); } diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkReaderPool.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkReaderPool.cs.meta index 7db6ef0..2c94768 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkReaderPool.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/NetworkReaderPool.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkReaderPooled.cs b/UnityProject/Assets/Mirror/Runtime/NetworkReaderPooled.cs new file mode 100644 index 0000000..fcfa792 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/NetworkReaderPooled.cs @@ -0,0 +1,22 @@ +// "NetworkReaderPooled" instead of "PooledNetworkReader" to group files, for +// easier IDE workflow and more elegant code. +using System; + +namespace Mirror +{ + [Obsolete("PooledNetworkReader was renamed to NetworkReaderPooled. It's cleaner & slightly easier to use.")] + public sealed class PooledNetworkReader : NetworkReaderPooled + { + internal PooledNetworkReader(byte[] bytes) : base(bytes) {} + internal PooledNetworkReader(ArraySegment segment) : base(segment) {} + } + + /// Pooled NetworkReader, automatically returned to pool when using 'using' + // TODO make sealed again after removing obsolete NetworkReaderPooled! + public class NetworkReaderPooled : NetworkReader, IDisposable + { + internal NetworkReaderPooled(byte[] bytes) : base(bytes) {} + internal NetworkReaderPooled(ArraySegment segment) : base(segment) {} + public void Dispose() => NetworkReaderPool.Return(this); + } +} diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkReaderPooled.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkReaderPooled.cs.meta new file mode 100644 index 0000000..4eb6e9d --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/NetworkReaderPooled.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: faafa97c32e44adf8e8888de817a370a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkServer.cs b/UnityProject/Assets/Mirror/Runtime/NetworkServer.cs index 6bad781..bdc3b97 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkServer.cs +++ b/UnityProject/Assets/Mirror/Runtime/NetworkServer.cs @@ -22,10 +22,15 @@ namespace Mirror public static Dictionary connections = new Dictionary(); - /// Message Handlers dictionary, with mesageId as key + /// Message Handlers dictionary, with messageId as key internal static Dictionary handlers = new Dictionary(); + /// All spawned NetworkIdentities by netId. + // server sees ALL spawned ones. + public static readonly Dictionary spawned = + new Dictionary(); + /// Single player mode can use dontListen to not accept incoming connections // see also: https://github.com/vis2k/Mirror/pull/2595 public static bool dontListen; @@ -33,26 +38,21 @@ namespace Mirror /// active checks if the server has been started public static bool active { get; internal set; } - /// batch messages and send them out in LateUpdate (or after batchInterval) - // (this is pretty much always a good idea) - public static bool batching = true; + // scene loading + public static bool isLoadingScene; // interest management component (optional) // by default, everyone observes everyone public static InterestManagement aoi; - [Obsolete("Transport is responsible for timeouts.")] - public static bool disconnectInactiveConnections; - - [Obsolete("Transport is responsible for timeouts. Configure the Transport's timeout setting instead.")] - public static float disconnectInactiveTimeout = 60f; - // OnConnected / OnDisconnected used to be NetworkMessages that were // invoked. this introduced a bug where external clients could send // Connected/Disconnected messages over the network causing undefined // behaviour. - internal static Action OnConnectedEvent; - internal static Action OnDisconnectedEvent; + // => public so that custom NetworkManagers can hook into it + public static Action OnConnectedEvent; + public static Action OnDisconnectedEvent; + public static Action OnErrorEvent; // initialization / shutdown /////////////////////////////////////////// static void Initialize() @@ -60,36 +60,51 @@ namespace Mirror if (initialized) return; - initialized = true; - // Debug.Log("NetworkServer Created version " + Version.Current); + // Debug.Log($"NetworkServer Created version {Version.Current}"); //Make sure connections are cleared in case any old connections references exist from previous sessions connections.Clear(); + // reset Interest Management so that rebuild intervals + // start at 0 when starting again. + if (aoi != null) aoi.Reset(); + // reset NetworkTime - NetworkTime.Reset(); + NetworkTime.ResetStatics(); Debug.Assert(Transport.activeTransport != null, "There was no active transport when calling NetworkServer.Listen, If you are calling Listen manually then make sure to set 'Transport.activeTransport' first"); AddTransportHandlers(); + + initialized = true; } static void AddTransportHandlers() { - Transport.activeTransport.OnServerConnected = OnTransportConnected; - Transport.activeTransport.OnServerDataReceived = OnTransportData; - Transport.activeTransport.OnServerDisconnected = OnTransportDisconnected; - Transport.activeTransport.OnServerError = OnError; + // += so that other systems can also hook into it (i.e. statistics) + Transport.activeTransport.OnServerConnected += OnTransportConnected; + Transport.activeTransport.OnServerDataReceived += OnTransportData; + Transport.activeTransport.OnServerDisconnected += OnTransportDisconnected; + Transport.activeTransport.OnServerError += OnTransportError; + } + + static void RemoveTransportHandlers() + { + // -= so that other systems can also hook into it (i.e. statistics) + Transport.activeTransport.OnServerConnected -= OnTransportConnected; + Transport.activeTransport.OnServerDataReceived -= OnTransportData; + Transport.activeTransport.OnServerDisconnected -= OnTransportDisconnected; + Transport.activeTransport.OnServerError -= OnTransportError; } // calls OnStartClient for all SERVER objects in host mode once. // client doesn't get spawn messages for those, so need to call manually. public static void ActivateHostScene() { - foreach (NetworkIdentity identity in NetworkIdentity.spawned.Values) + foreach (NetworkIdentity identity in spawned.Values) { if (!identity.isClient) { - // Debug.Log("ActivateHostScene " + identity.netId + " " + identity); + // Debug.Log($"ActivateHostScene {identity.netId} {identity}"); identity.OnStartClient(); } } @@ -112,7 +127,7 @@ namespace Mirror if (!dontListen) { Transport.activeTransport.ServerStart(); - Debug.Log("Server started listening"); + //Debug.Log("Server started listening"); } active = true; @@ -120,54 +135,90 @@ namespace Mirror } // Note: NetworkClient.DestroyAllClientObjects does the same on client. - static void CleanupNetworkIdentities() + static void CleanupSpawned() { - foreach (NetworkIdentity identity in NetworkIdentity.spawned.Values) + // iterate a COPY of spawned. + // DestroyObject removes them from the original collection. + // removing while iterating is not allowed. + foreach (NetworkIdentity identity in spawned.Values.ToList()) { if (identity != null) { - // scene objects are reset and disabled. - // they always stay in the scene, we don't destroy them. + // scene object if (identity.sceneId != 0) { - identity.Reset(); + // spawned scene objects are unspawned and reset. + // afterwards we disable them again. + // (they always stay in the scene, we don't destroy them) + DestroyObject(identity, DestroyMode.Reset); identity.gameObject.SetActive(false); } - // spawned objects are destroyed + // spawned prefabs else { - GameObject.Destroy(identity.gameObject); + // spawned prefabs are unspawned and destroyed. + DestroyObject(identity, DestroyMode.Destroy); } } } - NetworkIdentity.spawned.Clear(); + spawned.Clear(); } /// Shuts down the server and disconnects all clients + // RuntimeInitializeOnLoadMethod -> fast playmode without domain reload + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] public static void Shutdown() { if (initialized) { DisconnectAll(); - if (!dontListen) - { - // stop the server. - // we do NOT call Transport.Shutdown, because someone only - // called NetworkServer.Shutdown. we can't assume that the - // client is supposed to be shut down too! - Transport.activeTransport.ServerStop(); - } + // stop the server. + // we do NOT call Transport.Shutdown, because someone only + // called NetworkServer.Shutdown. we can't assume that the + // client is supposed to be shut down too! + // + // NOTE: stop no matter what, even if 'dontListen': + // someone might enabled dontListen at runtime. + // but we still need to stop the server. + // fixes https://github.com/vis2k/Mirror/issues/2536 + Transport.activeTransport.ServerStop(); + + // transport handlers are hooked into when initializing. + // so only remove them when shutting down. + RemoveTransportHandlers(); initialized = false; } + + // Reset all statics here.... dontListen = false; active = false; - handlers.Clear(); + isLoadingScene = false; - CleanupNetworkIdentities(); - NetworkIdentity.ResetNextNetworkId(); + localConnection = null; + + connections.Clear(); + connectionsCopy.Clear(); + handlers.Clear(); + newObservers.Clear(); + + // this calls spawned.Clear() + CleanupSpawned(); + + // sets nextNetworkId to 1 + // sets clientAuthorityCallback to null + // sets previousLocalPlayer to null + NetworkIdentity.ResetStatics(); + + // clear events. someone might have hooked into them before, but + // we don't want to use those hooks after Shutdown anymore. + OnConnectedEvent = null; + OnDisconnectedEvent = null; + OnErrorEvent = null; + + if (aoi != null) aoi.Reset(); } // connections ///////////////////////////////////////////////////////// @@ -186,12 +237,11 @@ namespace Mirror } /// Removes a connection by connectionId. Returns true if removed. - public static bool RemoveConnection(int connectionId) - { - return connections.Remove(connectionId); - } + public static bool RemoveConnection(int connectionId) => + connections.Remove(connectionId); // called by LocalClient to add itself. don't call directly. + // TODO consider internal setter instead? internal static void SetLocalConnection(LocalConnectionToClient conn) { if (localConnection != null) @@ -203,6 +253,7 @@ namespace Mirror localConnection = conn; } + // removes local connection to client internal static void RemoveLocalConnection() { if (localConnection != null) @@ -213,14 +264,21 @@ namespace Mirror RemoveConnection(0); } - /// True if we have no external connections (host is allowed) - public static bool NoExternalConnections() + /// True if we have external connections (that are not host) + public static bool HasExternalConnections() { - return connections.Count == 0 || - (connections.Count == 1 && localConnection != null); + // any connections? + if (connections.Count > 0) + { + // only host connection? + if (connections.Count == 1 && localConnection != null) + return false; + + // otherwise we have real external connections + return true; + } + return false; } - [Obsolete("NoConnections was renamed to NoExternalConnections because that's what it checks for.")] - public static bool NoConnections() => NoExternalConnections(); // send //////////////////////////////////////////////////////////////// /// Send a message to all clients, even those that haven't joined the world yet (non ready) @@ -233,8 +291,8 @@ namespace Mirror return; } - // Debug.Log("Server.SendToAll id:" + typeof(T)); - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + // Debug.Log($"Server.SendToAll {typeof(T)}"); + using (NetworkWriterPooled writer = NetworkWriterPool.Get()) { // pack message only once MessagePacking.Pack(message, writer); @@ -271,16 +329,40 @@ namespace Mirror SendToAll(message, channelId, true); } - /// Send a message to only clients which are ready with option to include the owner of the object identity - // TODO put rpcs into NetworkServer.Update WorldState packet, then finally remove SendToReady! - public static void SendToReady(NetworkIdentity identity, T message, bool includeOwner = true, int channelId = Channels.Reliable) + // this is like SendToReadyObservers - but it doesn't check the ready flag on the connection. + // this is used for ObjectDestroy messages. + static void SendToObservers(NetworkIdentity identity, T message, int channelId = Channels.Reliable) where T : struct, NetworkMessage { - // Debug.Log("Server.SendToReady msgType:" + typeof(T)); + // Debug.Log($"Server.SendToObservers {typeof(T)}"); if (identity == null || identity.observers == null || identity.observers.Count == 0) return; - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) + using (NetworkWriterPooled writer = NetworkWriterPool.Get()) + { + // pack message into byte[] once + MessagePacking.Pack(message, writer); + ArraySegment segment = writer.ToArraySegment(); + + foreach (NetworkConnectionToClient conn in identity.observers.Values) + { + conn.Send(segment, channelId); + } + + NetworkDiagnostics.OnSend(message, channelId, segment.Count, identity.observers.Count); + } + } + + /// Send a message to only clients which are ready with option to include the owner of the object identity + // TODO put rpcs into NetworkServer.Update WorldState packet, then finally remove SendToReady! + public static void SendToReadyObservers(NetworkIdentity identity, T message, bool includeOwner = true, int channelId = Channels.Reliable) + where T : struct, NetworkMessage + { + // Debug.Log($"Server.SendToReady {typeof(T)}"); + if (identity == null || identity.observers == null || identity.observers.Count == 0) + return; + + using (NetworkWriterPooled writer = NetworkWriterPool.Get()) { // pack message only once MessagePacking.Pack(message, writer); @@ -303,63 +385,24 @@ namespace Mirror /// Send a message to only clients which are ready including the owner of the NetworkIdentity // TODO put rpcs into NetworkServer.Update WorldState packet, then finally remove SendToReady! - public static void SendToReady(NetworkIdentity identity, T message, int channelId) + public static void SendToReadyObservers(NetworkIdentity identity, T message, int channelId) where T : struct, NetworkMessage { - SendToReady(identity, message, true, channelId); - } - - // this is like SendToReady - but it doesn't check the ready flag on the connection. - // this is used for ObjectDestroy messages. - static void SendToObservers(NetworkIdentity identity, T message, int channelId = Channels.Reliable) - where T : struct, NetworkMessage - { - // Debug.Log("Server.SendToObservers id:" + typeof(T)); - if (identity == null || identity.observers == null || identity.observers.Count == 0) - return; - - using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) - { - // pack message into byte[] once - MessagePacking.Pack(message, writer); - ArraySegment segment = writer.ToArraySegment(); - - foreach (NetworkConnection conn in identity.observers.Values) - { - conn.Send(segment, channelId); - } - - NetworkDiagnostics.OnSend(message, channelId, segment.Count, identity.observers.Count); - } - } - - /// Send this message to the player only - [Obsolete("Use identity.connectionToClient.Send() instead! Previously Mirror needed this function internally, but not anymore.")] - public static void SendToClientOfPlayer(NetworkIdentity identity, T msg, int channelId = Channels.Reliable) - where T : struct, NetworkMessage - { - if (identity != null) - { - identity.connectionToClient.Send(msg, channelId); - } - else - { - Debug.LogError("SendToClientOfPlayer: player has no NetworkIdentity: " + identity); - } + SendToReadyObservers(identity, message, true, channelId); } // transport events //////////////////////////////////////////////////// // called by transport static void OnTransportConnected(int connectionId) { - // Debug.Log("Server accepted client:" + connectionId); + // Debug.Log($"Server accepted client:{connectionId}"); // connectionId needs to be != 0 because 0 is reserved for local player // note that some transports like kcp generate connectionId by // hashing which can be < 0 as well, so we need to allow < 0! if (connectionId == 0) { - Debug.LogError("Server.HandleConnect: invalid connectionId: " + connectionId + " . Needs to be != 0, because 0 is reserved for local player."); + Debug.LogError($"Server.HandleConnect: invalid connectionId: {connectionId} . Needs to be != 0, because 0 is reserved for local player."); Transport.activeTransport.ServerDisconnect(connectionId); return; } @@ -368,7 +411,7 @@ namespace Mirror if (connections.ContainsKey(connectionId)) { Transport.activeTransport.ServerDisconnect(connectionId); - // Debug.Log("Server connectionId " + connectionId + " already in use. kicked client:" + connectionId); + // Debug.Log($"Server connectionId {connectionId} already in use...kicked client"); return; } @@ -380,27 +423,27 @@ namespace Mirror if (connections.Count < maxConnections) { // add connection - NetworkConnectionToClient conn = new NetworkConnectionToClient(connectionId, batching); + NetworkConnectionToClient conn = new NetworkConnectionToClient(connectionId); OnConnected(conn); } else { // kick Transport.activeTransport.ServerDisconnect(connectionId); - // Debug.Log("Server full, kicked client:" + connectionId); + // Debug.Log($"Server full, kicked client {connectionId}"); } } internal static void OnConnected(NetworkConnectionToClient conn) { - // Debug.Log("Server accepted client:" + conn); + // Debug.Log($"Server accepted client:{conn}"); // add connection and invoke connected event AddConnection(conn); OnConnectedEvent?.Invoke(conn); } - static void UnpackAndInvoke(NetworkConnectionToClient connection, NetworkReader reader, int channelId) + static bool UnpackAndInvoke(NetworkConnectionToClient connection, NetworkReader reader, int channelId) { if (MessagePacking.Unpack(reader, out ushort msgType)) { @@ -409,16 +452,28 @@ namespace Mirror { handler.Invoke(connection, reader, channelId); connection.lastMessageTime = Time.time; + return true; } else { - // Debug.Log("Unknown message ID " + msgType + " " + this + ". May be due to no existing RegisterHandler for this message."); + // message in a batch are NOT length prefixed to save bandwidth. + // every message needs to be handled and read until the end. + // otherwise it would overlap into the next message. + // => need to warn and disconnect to avoid undefined behaviour. + // => WARNING, not error. can happen if attacker sends random data. + Debug.LogWarning($"Unknown message id: {msgType} for connection: {connection}. This can happen if no handler was registered for this message."); + // simply return false. caller is responsible for disconnecting. + //connection.Disconnect(); + return false; } } else { - Debug.LogError("Closed connection: " + connection + ". Invalid message header."); - connection.Disconnect(); + // => WARNING, not error. can happen if attacker sends random data. + Debug.LogWarning($"Invalid message header for connection: {connection}."); + // simply return false. caller is responsible for disconnecting. + //connection.Disconnect(); + return false; } } @@ -427,20 +482,83 @@ namespace Mirror { if (connections.TryGetValue(connectionId, out NetworkConnectionToClient connection)) { - if (data.Count < MessagePacking.HeaderSize) + // client might batch multiple messages into one packet. + // feed it to the Unbatcher. + // NOTE: we don't need to associate a channelId because we + // always process all messages in the batch. + if (!connection.unbatcher.AddBatch(data)) { - Debug.LogError($"NetworkServer: received Message was too short (messages should start with message id)"); + Debug.LogWarning($"NetworkServer: received Message was too short (messages should start with message id)"); connection.Disconnect(); return; } - // unpack message - using (PooledNetworkReader reader = NetworkReaderPool.GetReader(data)) + // process all messages in the batch. + // only while NOT loading a scene. + // if we get a scene change message, then we need to stop + // processing. otherwise we might apply them to the old scene. + // => fixes https://github.com/vis2k/Mirror/issues/2651 + // + // NOTE: if scene starts loading, then the rest of the batch + // would only be processed when OnTransportData is called + // the next time. + // => consider moving processing to NetworkEarlyUpdate. + while (!isLoadingScene && + connection.unbatcher.GetNextMessage(out NetworkReader reader, out double remoteTimestamp)) { - UnpackAndInvoke(connection, reader, channelId); + // enough to read at least header size? + if (reader.Remaining >= MessagePacking.HeaderSize) + { + // make remoteTimeStamp available to the user + connection.remoteTimeStamp = remoteTimestamp; + + // handle message + if (!UnpackAndInvoke(connection, reader, channelId)) + { + // warn, disconnect and return if failed + // -> warning because attackers might send random data + // -> messages in a batch aren't length prefixed. + // failing to read one would cause undefined + // behaviour for every message afterwards. + // so we need to disconnect. + // -> return to avoid the below unbatches.count error. + // we already disconnected and handled it. + Debug.LogWarning($"NetworkServer: failed to unpack and invoke message. Disconnecting {connectionId}."); + connection.Disconnect(); + return; + } + } + // otherwise disconnect + else + { + // WARNING, not error. can happen if attacker sends random data. + Debug.LogWarning($"NetworkServer: received Message was too short (messages should start with message id). Disconnecting {connectionId}"); + connection.Disconnect(); + return; + } + } + + // if we weren't interrupted by a scene change, + // then all batched messages should have been processed now. + // otherwise batches would silently grow. + // we need to log an error to avoid debugging hell. + // + // EXAMPLE: https://github.com/vis2k/Mirror/issues/2882 + // -> UnpackAndInvoke silently returned because no handler for id + // -> Reader would never be read past the end + // -> Batch would never be retired because end is never reached + // + // NOTE: prefixing every message in a batch with a length would + // avoid ever not reading to the end. for extra bandwidth. + // + // IMPORTANT: always keep this check to detect memory leaks. + // this took half a day to debug last time. + if (!isLoadingScene && connection.unbatcher.BatchesCount > 0) + { + Debug.LogError($"Still had {connection.unbatcher.BatchesCount} batches remaining after processing, even though processing was not interrupted by a scene change. This should never happen, as it would cause ever growing batches.\nPossible reasons:\n* A message didn't deserialize as much as it serialized\n*There was no message handler for a message id, so the reader wasn't read until the end."); } } - else Debug.LogError("HandleData Unknown connectionId:" + connectionId); + else Debug.LogError($"HandleData Unknown connectionId:{connectionId}"); } // called by transport @@ -451,11 +569,11 @@ namespace Mirror // => which we do by removing the connection! internal static void OnTransportDisconnected(int connectionId) { - // Debug.Log("Server disconnect client:" + connectionId); + // Debug.Log($"Server disconnect client:{connectionId}"); if (connections.TryGetValue(connectionId, out NetworkConnectionToClient conn)) { RemoveConnection(connectionId); - // Debug.Log("Server lost client:" + connectionId); + // Debug.Log($"Server lost client:{connectionId}"); // NetworkManager hooks into OnDisconnectedEvent to make // DestroyPlayerForConnection(conn) optional, e.g. for PvP MMOs @@ -472,15 +590,22 @@ namespace Mirror } } - static void OnError(int connectionId, Exception exception) + // transport errors are forwarded to high level + static void OnTransportError(int connectionId, TransportError error, string reason) { - // TODO Let's discuss how we will handle errors - Debug.LogException(exception); + // transport errors will happen. logging a warning is enough. + // make sure the user does not panic. + Debug.LogWarning($"Server Transport Error for connId={connectionId}: {error}: {reason}. This is fine."); + // try get connection. passes null otherwise. + connections.TryGetValue(connectionId, out NetworkConnectionToClient conn); + OnErrorEvent?.Invoke(conn, error, reason); } // message handlers //////////////////////////////////////////////////// /// Register a handler for message type T. Most should require authentication. - public static void RegisterHandler(Action handler, bool requireAuthentication = true) + // TODO obsolete this some day to always use the channelId version. + // all handlers in this version are wrapped with 1 extra action. + public static void RegisterHandler(Action handler, bool requireAuthentication = true) where T : struct, NetworkMessage { ushort msgType = MessagePacking.GetId(); @@ -492,15 +617,20 @@ namespace Mirror } /// Register a handler for message type T. Most should require authentication. - [Obsolete("Use RegisterHandler(Action(Action handler, bool requireAuthentication = true) + // This version passes channelId to the handler. + public static void RegisterHandler(Action handler, bool requireAuthentication = true) where T : struct, NetworkMessage { - RegisterHandler((_, value) => { handler(value); }, requireAuthentication); + ushort msgType = MessagePacking.GetId(); + if (handlers.ContainsKey(msgType)) + { + Debug.LogWarning($"NetworkServer.RegisterHandler replacing handler for {typeof(T).FullName}, id={msgType}. If replacement is intentional, use ReplaceHandler instead to avoid this warning."); + } + handlers[msgType] = MessagePacking.WrapHandler(handler, requireAuthentication); } /// Replace a handler for message type T. Most should require authentication. - public static void ReplaceHandler(Action handler, bool requireAuthentication = true) + public static void ReplaceHandler(Action handler, bool requireAuthentication = true) where T : struct, NetworkMessage { ushort msgType = MessagePacking.GetId(); @@ -549,7 +679,7 @@ namespace Mirror // we are iterating here. // see also: https://github.com/vis2k/Mirror/issues/2357 // this whole process should be simplified some day. - // until then, let's copy .Values to avoid InvalidOperatinException. + // until then, let's copy .Values to avoid InvalidOperationException. // note that this is only called when stopping the server, so the // copy is no performance problem. foreach (NetworkConnectionToClient conn in connections.Values.ToList()) @@ -575,13 +705,6 @@ namespace Mirror active = false; } - /// Disconnect all currently connected clients except the local connection. - [Obsolete("Call NetworkClient.DisconnectAll() instead")] - public static void DisconnectAllExternalConnections() => DisconnectAll(); - - [Obsolete("Call NetworkClient.DisconnectAll() instead")] - public static void DisconnectAllConnections() => DisconnectAll(); - // add/remove/replace player /////////////////////////////////////////// /// Called by server after AddPlayer message to add the player for the connection. // When a player is added for a connection, the client for that @@ -590,12 +713,12 @@ namespace Mirror // for that object. This function is used for "adding" a player, not for // "replacing" the player on a connection. If there is already a player // on this playerControllerId for this connection, this will fail. - public static bool AddPlayerForConnection(NetworkConnection conn, GameObject player) + public static bool AddPlayerForConnection(NetworkConnectionToClient conn, GameObject player) { NetworkIdentity identity = player.GetComponent(); if (identity == null) { - Debug.LogWarning("AddPlayer: playerGameObject has no NetworkIdentity. Please add a NetworkIdentity to " + player); + Debug.LogWarning($"AddPlayer: playerGameObject has no NetworkIdentity. Please add a NetworkIdentity to {player}"); return false; } @@ -623,7 +746,7 @@ namespace Mirror // set ready if not set yet SetClientReady(conn); - // Debug.Log("Adding new playerGameObject object netId: " + identity.netId + " asset ID " + identity.assetId); + // Debug.Log($"Adding new playerGameObject object netId: {identity.netId} asset ID: {identity.assetId}"); Respawn(identity); return true; @@ -636,7 +759,7 @@ namespace Mirror // for that object. This function is used for "adding" a player, not for // "replacing" the player on a connection. If there is already a player // on this playerControllerId for this connection, this will fail. - public static bool AddPlayerForConnection(NetworkConnection conn, GameObject player, Guid assetId) + public static bool AddPlayerForConnection(NetworkConnectionToClient conn, GameObject player, Guid assetId) { if (GetNetworkIdentity(player, out NetworkIdentity identity)) { @@ -648,18 +771,18 @@ namespace Mirror /// Replaces connection's player object. The old object is not destroyed. // This does NOT change the ready state of the connection, so it can // safely be used while changing scenes. - public static bool ReplacePlayerForConnection(NetworkConnection conn, GameObject player, bool keepAuthority = false) + public static bool ReplacePlayerForConnection(NetworkConnectionToClient conn, GameObject player, bool keepAuthority = false) { NetworkIdentity identity = player.GetComponent(); if (identity == null) { - Debug.LogError("ReplacePlayer: playerGameObject has no NetworkIdentity. Please add a NetworkIdentity to " + player); + Debug.LogError($"ReplacePlayer: playerGameObject has no NetworkIdentity. Please add a NetworkIdentity to {player}"); return false; } if (identity.connectionToClient != null && identity.connectionToClient != conn) { - Debug.LogError("Cannot replace player for connection. New player is already owned by a different connection" + player); + Debug.LogError($"Cannot replace player for connection. New player is already owned by a different connection{player}"); return false; } @@ -687,12 +810,21 @@ namespace Mirror // IMPORTANT: do this in AddPlayerForConnection & ReplacePlayerForConnection! SpawnObserversForConnection(conn); - // Debug.Log("Replacing playerGameObject object netId: " + player.GetComponent().netId + " asset ID " + player.GetComponent().assetId); + //Debug.Log($"Replacing playerGameObject object netId:{player.GetComponent().netId} asset ID {player.GetComponent().assetId}"); Respawn(identity); - if (!keepAuthority) + if (keepAuthority) + { + // This needs to be sent to clear isLocalPlayer on + // client while keeping hasAuthority true + SendChangeOwnerMessage(previousPlayer, conn); + } + else + { + // This clears both isLocalPlayer and hasAuthority on client previousPlayer.RemoveClientAuthority(); + } return true; } @@ -700,7 +832,7 @@ namespace Mirror /// Replaces connection's player object. The old object is not destroyed. // This does NOT change the ready state of the connection, so it can // safely be used while changing scenes. - public static bool ReplacePlayerForConnection(NetworkConnection conn, GameObject player, Guid assetId, bool keepAuthority = false) + public static bool ReplacePlayerForConnection(NetworkConnectionToClient conn, GameObject player, Guid assetId, bool keepAuthority = false) { if (GetNetworkIdentity(player, out NetworkIdentity identity)) { @@ -717,9 +849,9 @@ namespace Mirror // SYSTEM_READY message. If there is not specific action a game needs to // take for this message, relying on the default ready handler function // is probably fine, so this call wont be needed. - public static void SetClientReady(NetworkConnection conn) + public static void SetClientReady(NetworkConnectionToClient conn) { - // Debug.Log("SetClientReadyInternal for conn:" + conn); + // Debug.Log($"SetClientReadyInternal for conn:{conn}"); // set ready conn.isReady = true; @@ -733,16 +865,11 @@ namespace Mirror // Clients that are not ready do not receive spawned objects or state // synchronization updates. They client can be made ready again by // calling SetClientReady(). - public static void SetClientNotReady(NetworkConnection conn) + public static void SetClientNotReady(NetworkConnectionToClient conn) { - if (conn.isReady) - { - // Debug.Log("PlayerNotReady " + conn); - conn.isReady = false; - conn.RemoveFromObservingsObservers(); - - conn.Send(new NotReadyMessage()); - } + conn.isReady = false; + conn.RemoveFromObservingsObservers(); + conn.Send(new NotReadyMessage()); } /// Marks all connected clients as no longer ready. @@ -758,9 +885,9 @@ namespace Mirror } // default ready handler. - static void OnClientReadyMessage(NetworkConnection conn, ReadyMessage msg) + static void OnClientReadyMessage(NetworkConnectionToClient conn, ReadyMessage msg) { - // Debug.Log("Default handler for ready message from " + conn); + // Debug.Log($"Default handler for ready message from {conn}"); SetClientReady(conn); } @@ -799,34 +926,47 @@ namespace Mirror // remote calls //////////////////////////////////////////////////////// // Handle command from specific player, this could be one of multiple // players on a single client - static void OnCommandMessage(NetworkConnection conn, CommandMessage msg) + static void OnCommandMessage(NetworkConnectionToClient conn, CommandMessage msg, int channelId) { - if (!NetworkIdentity.spawned.TryGetValue(msg.netId, out NetworkIdentity identity)) + if (!conn.isReady) { - Debug.LogWarning("Spawned object not found when handling Command message [netId=" + msg.netId + "]"); + // Clients may be set NotReady due to scene change or other game logic by user, e.g. respawning. + // Ignore commands that may have been in flight before client received NotReadyMessage message. + // Unreliable messages may be out of order, so don't spam warnings for those. + if (channelId == Channels.Reliable) + Debug.LogWarning("Command received while client is not ready.\nThis may be ignored if client intentionally set NotReady."); return; } - CommandInfo commandInfo = identity.GetCommandInfo(msg.componentIndex, msg.functionHash); + if (!spawned.TryGetValue(msg.netId, out NetworkIdentity identity)) + { + // over reliable channel, commands should always come after spawn. + // over unreliable, they might come in before the object was spawned. + // for example, NetworkTransform. + // let's not spam the console for unreliable out of order messages. + if (channelId == Channels.Reliable) + Debug.LogWarning($"Spawned object not found when handling Command message [netId={msg.netId}]"); + return; + } // Commands can be for player objects, OR other objects with client-authority // -> so if this connection's controller has a different netId then // only allow the command if clientAuthorityOwner - bool requiresAuthority = commandInfo.requiresAuthority; + bool requiresAuthority = RemoteProcedureCalls.CommandRequiresAuthority(msg.functionHash); if (requiresAuthority && identity.connectionToClient != conn) { - Debug.LogWarning("Command for object without authority [netId=" + msg.netId + "]"); + Debug.LogWarning($"Command for object without authority [netId={msg.netId}]"); return; } - // Debug.Log("OnCommandMessage for netId=" + msg.netId + " conn=" + conn); + // Debug.Log($"OnCommandMessage for netId:{msg.netId} conn:{conn}"); - using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(msg.payload)) - identity.HandleRemoteCall(msg.componentIndex, msg.functionHash, MirrorInvokeType.Command, networkReader, conn as NetworkConnectionToClient); + using (NetworkReaderPooled networkReader = NetworkReaderPool.Get(msg.payload)) + identity.HandleRemoteCall(msg.componentIndex, msg.functionHash, RemoteCallType.Command, networkReader, conn as NetworkConnectionToClient); } // spawning //////////////////////////////////////////////////////////// - static ArraySegment CreateSpawnMessagePayload(bool isOwner, NetworkIdentity identity, PooledNetworkWriter ownerWriter, PooledNetworkWriter observersWriter) + static ArraySegment CreateSpawnMessagePayload(bool isOwner, NetworkIdentity identity, NetworkWriterPooled ownerWriter, NetworkWriterPooled observersWriter) { // Only call OnSerializeAllSafely if there are NetworkBehaviours if (identity.NetworkBehaviours.Length == 0) @@ -836,12 +976,12 @@ namespace Mirror // serialize all components with initialState = true // (can be null if has none) - identity.OnSerializeAllSafely(true, ownerWriter, out int ownerWritten, observersWriter, out int observersWritten); + identity.OnSerializeAllSafely(true, ownerWriter, observersWriter); // convert to ArraySegment to avoid reader allocations - // (need to handle null case too) - ArraySegment ownerSegment = ownerWritten > 0 ? ownerWriter.ToArraySegment() : default; - ArraySegment observersSegment = observersWritten > 0 ? observersWriter.ToArraySegment() : default; + // if nothing was written, .ToArraySegment returns an empty segment. + ArraySegment ownerSegment = ownerWriter.ToArraySegment(); + ArraySegment observersSegment = observersWriter.ToArraySegment(); // use owner segment if 'conn' owns this identity, otherwise // use observers segment @@ -854,10 +994,10 @@ namespace Mirror { if (identity.serverOnly) return; - // Debug.Log("Server SendSpawnMessage: name=" + identity.name + " sceneId=" + identity.sceneId.ToString("X") + " netid=" + identity.netId); + //Debug.Log($"Server SendSpawnMessage: name:{identity.name} sceneId:{identity.sceneId:X} netid:{identity.netId}"); // one writer for owner, one for observers - using (PooledNetworkWriter ownerWriter = NetworkWriterPool.GetWriter(), observersWriter = NetworkWriterPool.GetWriter()) + using (NetworkWriterPooled ownerWriter = NetworkWriterPool.Get(), observersWriter = NetworkWriterPool.Get()) { bool isOwner = identity.connectionToClient == conn; ArraySegment payload = CreateSpawnMessagePayload(isOwner, identity, ownerWriter, observersWriter); @@ -872,15 +1012,34 @@ namespace Mirror position = identity.transform.localPosition, rotation = identity.transform.localRotation, scale = identity.transform.localScale, - payload = payload, + payload = payload }; conn.Send(message); } } + internal static void SendChangeOwnerMessage(NetworkIdentity identity, NetworkConnectionToClient conn) + { + // Don't send if identity isn't spawned or only exists on server + if (identity.netId == 0 || identity.serverOnly) return; + + // Don't send if conn doesn't have the identity spawned yet + // May be excluded from the client by interest management + if (!conn.observing.Contains(identity)) return; + + //Debug.Log($"Server SendChangeOwnerMessage: name={identity.name} netid={identity.netId}"); + + conn.Send(new ChangeOwnerMessage + { + netId = identity.netId, + isOwner = identity.connectionToClient == conn, + isLocalPlayer = conn.identity == identity + }); + } + static void SpawnObject(GameObject obj, NetworkConnection ownerConnection) { - // verify if we an spawn this + // verify if we can spawn this if (Utils.IsPrefab(obj)) { Debug.LogError($"GameObject {obj.name} is a prefab, it can't be spawned. Instantiate it first."); @@ -889,14 +1048,14 @@ namespace Mirror if (!active) { - Debug.LogError("SpawnObject for " + obj + ", NetworkServer is not active. Cannot spawn objects without an active server."); + Debug.LogError($"SpawnObject for {obj}, NetworkServer is not active. Cannot spawn objects without an active server."); return; } NetworkIdentity identity = obj.GetComponent(); if (identity == null) { - Debug.LogError("SpawnObject " + obj + " has no NetworkIdentity. Please add a NetworkIdentity to " + obj); + Debug.LogError($"SpawnObject {obj} has no NetworkIdentity. Please add a NetworkIdentity to {obj}"); return; } @@ -907,6 +1066,13 @@ namespace Mirror return; } + // Spawn should only be called once per netId + if (spawned.ContainsKey(identity.netId)) + { + Debug.LogWarning($"{identity} with netId={identity.netId} was already spawned."); + return; + } + identity.connectionToClient = (NetworkConnectionToClient)ownerConnection; // special case to make sure hasAuthority is set @@ -916,7 +1082,21 @@ namespace Mirror identity.OnStartServer(); - // Debug.Log("SpawnObject instance ID " + identity.netId + " asset ID " + identity.assetId); + // Debug.Log($"SpawnObject instance ID {identity.netId} asset ID {identity.assetId}"); + + if (aoi) + { + // This calls user code which might throw exceptions + // We don't want this to leave us in bad state + try + { + aoi.OnSpawned(identity); + } + catch (Exception e) + { + Debug.LogException(e); + } + } RebuildObservers(identity, true); } @@ -960,6 +1140,7 @@ namespace Mirror SpawnObject(obj, ownerConnection); } + // TODO merge with ConsiderForSpawning on client internal static bool ValidateSceneObject(NetworkIdentity identity) { if (identity.gameObject.hideFlags == HideFlags.NotEditable || @@ -967,6 +1148,8 @@ namespace Mirror return false; #if UNITY_EDITOR + // this never seems to trigger. + // even if a prefab is dragged into the scene. if (UnityEditor.EditorUtility.IsPersistent(identity.gameObject)) return false; #endif @@ -992,8 +1175,18 @@ namespace Mirror { if (ValidateSceneObject(identity)) { - // Debug.Log("SpawnObjects sceneId:" + identity.sceneId.ToString("X") + " name:" + identity.gameObject.name); + // Debug.Log($"SpawnObjects sceneId:{identity.sceneId:X} name:{identity.gameObject.name}"); identity.gameObject.SetActive(true); + + // fix https://github.com/vis2k/Mirror/issues/2778: + // -> SetActive(true) does NOT call Awake() if the parent + // is inactive + // -> we need Awake() to initialize NetworkBehaviours[] etc. + // because our second pass below spawns and works with it + // => detect this situation and manually call Awake for + // proper initialization + if (!identity.gameObject.activeInHierarchy) + identity.Awake(); } } @@ -1001,8 +1194,11 @@ namespace Mirror foreach (NetworkIdentity identity in identities) { if (ValidateSceneObject(identity)) - Spawn(identity.gameObject); + // pass connection so that authority is not lost when server loads a scene + // https://github.com/vis2k/Mirror/pull/2987 + Spawn(identity.gameObject, identity.connectionToClient); } + return true; } @@ -1020,9 +1216,9 @@ namespace Mirror } } - static void SpawnObserversForConnection(NetworkConnection conn) + static void SpawnObserversForConnection(NetworkConnectionToClient conn) { - // Debug.Log("Spawning " + NetworkIdentity.spawned.Count + " objects for conn " + conn); + //Debug.Log($"Spawning {spawned.Count} objects for conn {conn}"); if (!conn.isReady) { @@ -1036,12 +1232,12 @@ namespace Mirror // add connection to each nearby NetworkIdentity's observers, which // internally sends a spawn message for each one to the connection. - foreach (NetworkIdentity identity in NetworkIdentity.spawned.Values) + foreach (NetworkIdentity identity in spawned.Values) { // try with far away ones in ummorpg! if (identity.gameObject.activeSelf) //TODO this is different { - // Debug.Log("Sending spawn message for current server objects name='" + identity.name + "' netId=" + identity.netId + " sceneId=" + identity.sceneId.ToString("X")); + //Debug.Log($"Sending spawn message for current server objects name:{identity.name} netId:{identity.netId} sceneId:{identity.sceneId:X}"); // we need to support three cases: // - legacy system (identity has .visibility) @@ -1065,17 +1261,8 @@ namespace Mirror // default: legacy system / new system / no system support else if (identity.visible == Visibility.Default) { - // legacy system -#pragma warning disable 618 - if (identity.visibility != null) - { - // call OnCheckObserver - if (identity.visibility.OnCheckObserver(conn)) - identity.AddObserver(conn); - } -#pragma warning restore 618 - // new system - else if (aoi != null) + // aoi system + if (aoi != null) { // call OnCheckObserver if (aoi.OnCheckObserver(identity, conn)) @@ -1103,14 +1290,14 @@ namespace Mirror // Unlike when calling NetworkServer.Destroy(), on the server the object // will NOT be destroyed. This allows the server to re-use the object, // even spawn it again later. - public static void UnSpawn(GameObject obj) => DestroyObject(obj, false); + public static void UnSpawn(GameObject obj) => DestroyObject(obj, DestroyMode.Reset); // destroy ///////////////////////////////////////////////////////////// /// Destroys all of the connection's owned objects on the server. // This is used when a client disconnects, to remove the players for // that client. This also destroys non-player objects that have client // authority set for this connection. - public static void DestroyPlayerForConnection(NetworkConnection conn) + public static void DestroyPlayerForConnection(NetworkConnectionToClient conn) { // destroy all objects owned by this connection, including the player object conn.DestroyOwnedObjects(); @@ -1118,47 +1305,90 @@ namespace Mirror // fixes https://github.com/vis2k/Mirror/issues/2737 // -> cleaning those up in NetworkConnection.Disconnect is NOT enough // because voluntary disconnects from the other end don't call - // NetworkConnectionn.Disconnect() + // NetworkConnection.Disconnect() conn.RemoveFromObservingsObservers(); conn.identity = null; } - static void DestroyObject(NetworkIdentity identity, bool destroyServerObject) + // sometimes we want to GameObject.Destroy it. + // sometimes we want to just unspawn on clients and .Reset() it on server. + // => 'bool destroy' isn't obvious enough. it's really destroy OR reset! + enum DestroyMode { Destroy, Reset } + + static void DestroyObject(NetworkIdentity identity, DestroyMode mode) { - // Debug.Log("DestroyObject instance:" + identity.netId); - NetworkIdentity.spawned.Remove(identity.netId); + // Debug.Log($"DestroyObject instance:{identity.netId}"); + + // only call OnRebuildObservers while active, + // not while shutting down + // (https://github.com/vis2k/Mirror/issues/2977) + if (active && aoi) + { + // This calls user code which might throw exceptions + // We don't want this to leave us in bad state + try + { + aoi.OnDestroyed(identity); + } + catch (Exception e) + { + Debug.LogException(e); + } + } + + // remove from NetworkServer (this) dictionary + spawned.Remove(identity.netId); identity.connectionToClient?.RemoveOwnedObject(identity); - ObjectDestroyMessage message = new ObjectDestroyMessage - { - netId = identity.netId - }; - SendToObservers(identity, message); - + // send object destroy message to all observers, clear observers + SendToObservers(identity, new ObjectDestroyMessage{netId = identity.netId}); identity.ClearObservers(); + + // in host mode, call OnStopClient/OnStopLocalPlayer manually if (NetworkClient.active && localClientActive) { + if (identity.isLocalPlayer) + identity.OnStopLocalPlayer(); + identity.OnStopClient(); + // The object may have been spawned with host client ownership, + // e.g. a pet so we need to clear hasAuthority and call + // NotifyAuthority which invokes OnStopAuthority if hasAuthority. + identity.hasAuthority = false; + identity.NotifyAuthority(); + + // remove from NetworkClient dictionary + NetworkClient.spawned.Remove(identity.netId); } + // we are on the server. call OnStopServer. identity.OnStopServer(); - // when unspawning, don't destroy the server's object - if (destroyServerObject) + // are we supposed to GameObject.Destroy() it completely? + if (mode == DestroyMode.Destroy) { identity.destroyCalled = true; - UnityEngine.Object.Destroy(identity.gameObject); + + // Destroy if application is running + if (Application.isPlaying) + { + UnityEngine.Object.Destroy(identity.gameObject); + } + // Destroy can't be used in Editor during tests. use DestroyImmediate. + else + { + GameObject.DestroyImmediate(identity.gameObject); + } } - // if we are destroying the server object we don't need to reset the identity - // reseting it will cause isClient/isServer to be false in the OnDestroy call - else + // otherwise simply .Reset() and set inactive again + else if (mode == DestroyMode.Reset) { identity.Reset(); } } - static void DestroyObject(GameObject obj, bool destroyServerObject) + static void DestroyObject(GameObject obj, DestroyMode mode) { if (obj == null) { @@ -1168,7 +1398,7 @@ namespace Mirror if (GetNetworkIdentity(obj, out NetworkIdentity identity)) { - DestroyObject(identity, destroyServerObject); + DestroyObject(identity, mode); } } @@ -1176,7 +1406,7 @@ namespace Mirror // In some cases it is useful to remove an object but not delete it on // the server. For that, use NetworkServer.UnSpawn() instead of // NetworkServer.Destroy(). - public static void Destroy(GameObject obj) => DestroyObject(obj, true); + public static void Destroy(GameObject obj) => DestroyObject(obj, DestroyMode.Destroy); // interest management ///////////////////////////////////////////////// // Helper function to add all server connections as observers. @@ -1200,7 +1430,9 @@ namespace Mirror } // allocate newObservers helper HashSet only once - static readonly HashSet newObservers = new HashSet(); + // internal for tests + internal static readonly HashSet newObservers = + new HashSet(); // rebuild observers default method (no AOI) - adds all connections static void RebuildObserversDefault(NetworkIdentity identity, bool initialize) @@ -1226,13 +1458,7 @@ namespace Mirror // not force hidden? if (identity.visible != Visibility.ForceHidden) { - // obsolete legacy system support (for now) -#pragma warning disable 618 - if (identity.visibility != null) - identity.visibility.OnRebuildObservers(newObservers, initialize); -#pragma warning restore 618 - else - aoi.OnRebuildObservers(identity, newObservers, initialize); + aoi.OnRebuildObservers(identity, newObservers); } // IMPORTANT: AFTER rebuilding add own player connection in any case @@ -1250,7 +1476,7 @@ namespace Mirror bool changed = false; // add all newObservers that aren't in .observers yet - foreach (NetworkConnection conn in newObservers) + foreach (NetworkConnectionToClient conn in newObservers) { // only add ready connections. // otherwise the player might not be in the world yet or anymore @@ -1260,20 +1486,20 @@ namespace Mirror { // new observer conn.AddToObserving(identity); - // Debug.Log("New Observer for " + gameObject + " " + conn); + // Debug.Log($"New Observer for {gameObject} {conn}"); changed = true; } } } // remove all old .observers that aren't in newObservers anymore - foreach (NetworkConnection conn in identity.observers.Values) + foreach (NetworkConnectionToClient conn in identity.observers.Values) { if (!newObservers.Contains(conn)) { // removed observer conn.RemoveFromObserving(identity, false); - // Debug.Log("Removed Observer for " + gameObject + " " + conn); + // Debug.Log($"Removed Observer for {gameObject} {conn}"); changed = true; } } @@ -1282,7 +1508,7 @@ namespace Mirror if (changed) { identity.observers.Clear(); - foreach (NetworkConnection conn in newObservers) + foreach (NetworkConnectionToClient conn in newObservers) { if (conn != null && conn.isReady) identity.observers.Add(conn.connectionId, conn); @@ -1314,13 +1540,8 @@ namespace Mirror { if (!newObservers.Contains(localConnection)) { - // obsolete legacy system support (for now) -#pragma warning disable 618 - if (identity.visibility != null) - identity.visibility.OnSetHostVisibility(false); -#pragma warning restore 618 - else - identity.OnSetHostVisibility(false); + if (aoi != null) + aoi.SetHostVisibility(identity, false); } } } @@ -1346,22 +1567,9 @@ namespace Mirror if (identity.observers == null) return; - // legacy proximitychecker support: - // make sure user doesn't use both new and old system. -#pragma warning disable 618 - if (aoi != null & identity.visibility != null) - { - Debug.LogError($"RebuildObservers: {identity.name} has {identity.visibility.GetType()} component but there is also a global {aoi.GetType()} component. Can't use both systems at the same time!"); - return; - } -#pragma warning restore 618 - // if there is no interest management system, // or if 'force shown' then add all connections -#pragma warning disable 618 - if ((aoi == null && identity.visibility == null) || - identity.visible == Visibility.ForceShown) -#pragma warning restore 618 + if (aoi == null || identity.visible == Visibility.ForceShown) { RebuildObserversDefault(identity, initialize); } @@ -1377,7 +1585,8 @@ namespace Mirror static NetworkWriter GetEntitySerializationForConnection(NetworkIdentity identity, NetworkConnectionToClient connection) { // get serialization for this entity (cached) - NetworkIdentitySerialization serialization = identity.GetSerializationAtTick(Time.time); + // IMPORTANT: int tick avoids floating point inaccuracy over days/weeks + NetworkIdentitySerialization serialization = identity.GetSerializationAtTick(Time.frameCount); // is this entity owned by this connection? bool owned = identity.connectionToClient == connection; @@ -1387,14 +1596,14 @@ namespace Mirror if (owned) { // was it dirty / did we actually serialize anything? - if (serialization.ownerWritten > 0) + if (serialization.ownerWriter.Position > 0) return serialization.ownerWriter; } // observers writer if not owned else { // was it dirty / did we actually serialize anything? - if (serialization.observersWritten > 0) + if (serialization.observersWriter.Position > 0) return serialization.observersWriter; } @@ -1402,27 +1611,6 @@ namespace Mirror return null; } - // helper function to clear dirty bits of all spawned entities - static void ClearSpawnedDirtyBits() - { - // TODO clear dirty bits when removing the last observer instead! - // no need to do it for ALL entities ALL the time. - // - // for each spawned: - // clear dirty bits if it has no observers. - // we did this before push->pull broadcasting so let's keep - // doing this for now. - foreach (NetworkIdentity identity in NetworkIdentity.spawned.Values) - { - if (identity.observers == null || identity.observers.Count == 0) - { - // clear all component's dirty bits. - // it would be spawned on new observers anyway. - identity.ClearAllComponentsDirtyBits(); - } - } - } - // helper function to broadcast the world to a connection static void BroadcastToConnection(NetworkConnectionToClient connection) { @@ -1447,50 +1635,19 @@ namespace Mirror }; connection.Send(message); } - - // clear dirty bits only for the components that we serialized - // DO NOT clean ALL component's dirty bits, because - // components can have different syncIntervals and we don't - // want to reset dirty bits for the ones that were not - // synced yet. - // (we serialized only the IsDirty() components, or all of - // them if initialState. clearing the dirty ones is enough.) - // - // NOTE: this is what we did before push->pull - // broadcasting. let's keep doing this for - // feature parity to not break anyone's project. - // TODO make this more simple / unnecessary later. - identity.ClearDirtyComponentsDirtyBits(); } // spawned list should have no null entries because we // always call Remove in OnObjectDestroy everywhere. // if it does have null then someone used // GameObject.Destroy instead of NetworkServer.Destroy. - else Debug.LogWarning("Found 'null' entry in observing list for connectionId=" + connection.connectionId + ". Please call NetworkServer.Destroy to destroy networked objects. Don't use GameObject.Destroy."); + else Debug.LogWarning($"Found 'null' entry in observing list for connectionId={connection.connectionId}. Please call NetworkServer.Destroy to destroy networked objects. Don't use GameObject.Destroy."); } } - // helper function to check a connection for inactivity - // and disconnect if necessary - // => returns true if disconnected - static bool DisconnectIfInactive(NetworkConnectionToClient connection) - { - // check for inactivity -#pragma warning disable 618 - if (disconnectInactiveConnections && - !connection.IsAlive(disconnectInactiveTimeout)) - { - Debug.LogWarning($"Disconnecting {connection} for inactivity!"); - connection.Disconnect(); - return true; - } -#pragma warning restore 618 - return false; - } - // NetworkLateUpdate called after any Update/FixedUpdate/LateUpdate // (we add this to the UnityEngine in NetworkLoop) - static readonly List connectionsCopy = + // internal for tests + internal static readonly List connectionsCopy = new List(); static void Broadcast() @@ -1509,10 +1666,6 @@ namespace Mirror // go through all connections foreach (NetworkConnectionToClient connection in connectionsCopy) { - // check for inactivity. disconnects if necessary. - if (DisconnectIfInactive(connection)) - continue; - // has this connection joined the world yet? // for each READY connection: // pull in UpdateVarsMessage for each entity it observes @@ -1526,17 +1679,29 @@ namespace Mirror connection.Update(); } - // TODO we already clear the serialized component's dirty bits above - // might as well clear everything??? + // TODO this is way too slow because we iterate ALL spawned :/ + // TODO this is way too complicated :/ + // to understand what this tries to prevent, consider this example: + // monster has health=100 + // we change health=200, dirty bit is set + // player comes in range, gets full serialization spawn packet. + // next Broadcast(), player gets the health=200 change because dirty bit was set. // - // TODO this unfortunately means we still need to iterate ALL - // spawned and not just the ones with observers. figure - // out a way to get rid of this. + // this code clears all dirty bits if no players are around to prevent it. + // BUT there are two issues: + // 1. what if a playerB was around the whole time? + // 2. why don't we handle broadcast and spawn packets both HERE? + // handling spawn separately is why we need this complex magic // - // TODO clear dirty bits when removing the last observer instead! - // no need to do it for ALL entities ALL the time. + // see test: DirtyBitsAreClearedForSpawnedWithoutObservers() + // see test: SyncObjectChanges_DontGrowWithoutObservers() // - ClearSpawnedDirtyBits(); + // PAUL: we also do this to avoid ever growing SyncList .changes + //ClearSpawnedDirtyBits(); + // + // this was moved to NetworkIdentity.AddObserver! + // same result, but no more O(N) loop in here! + // TODO remove this comment after moving spawning into Broadcast()! } // update ////////////////////////////////////////////////////////////// @@ -1560,9 +1725,5 @@ namespace Mirror if (Transport.activeTransport != null) Transport.activeTransport.ServerLateUpdate(); } - - // obsolete to not break people's projects. Update was public. - [Obsolete("NetworkServer.Update is now called internally from our custom update loop. No need to call Update manually anymore.")] - public static void Update() => NetworkLateUpdate(); } } diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkServer.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkServer.cs.meta index af1a068..9861342 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkServer.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/NetworkServer.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkStartPosition.cs b/UnityProject/Assets/Mirror/Runtime/NetworkStartPosition.cs index 91f2623..e11029b 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkStartPosition.cs +++ b/UnityProject/Assets/Mirror/Runtime/NetworkStartPosition.cs @@ -4,7 +4,7 @@ namespace Mirror { /// Start position for player spawning, automatically registers itself in the NetworkManager. [DisallowMultipleComponent] - [AddComponentMenu("Network/NetworkStartPosition")] + [AddComponentMenu("Network/Network Start Position")] [HelpURL("https://mirror-networking.gitbook.io/docs/components/network-start-position")] public class NetworkStartPosition : MonoBehaviour { diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkStartPosition.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkStartPosition.cs.meta index 286d5f7..ae9ab89 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkStartPosition.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/NetworkStartPosition.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkTime.cs b/UnityProject/Assets/Mirror/Runtime/NetworkTime.cs index 9154794..f655f24 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkTime.cs +++ b/UnityProject/Assets/Mirror/Runtime/NetworkTime.cs @@ -1,6 +1,9 @@ using System; +using System.Runtime.CompilerServices; using UnityEngine; +#if !UNITY_2020_3_OR_NEWER using Stopwatch = System.Diagnostics.Stopwatch; +#endif namespace Mirror { @@ -15,15 +18,6 @@ namespace Mirror static double lastPingTime; - // Date and time when the application started - // TODO Unity 2020 / 2021 supposedly has double Time.time now? - static readonly Stopwatch stopwatch = new Stopwatch(); - - static NetworkTime() - { - stopwatch.Start(); - } - static ExponentialMovingAverage _rtt = new ExponentialMovingAverage(10); static ExponentialMovingAverage _offset = new ExponentialMovingAverage(10); @@ -31,38 +25,99 @@ namespace Mirror static double offsetMin = double.MinValue; static double offsetMax = double.MaxValue; - // returns the clock time _in this system_ - static double LocalTime() => stopwatch.Elapsed.TotalSeconds; - - public static void Reset() + /// Returns double precision clock time _in this system_, unaffected by the network. +#if UNITY_2020_3_OR_NEWER + public static double localTime { - stopwatch.Restart(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Time.timeAsDouble; + } +#else + // need stopwatch for older Unity versions, but it's quite slow. + // CAREFUL: unlike Time.time, this is not a FRAME time. + // it changes during the frame too. + static readonly Stopwatch stopwatch = new Stopwatch(); + static NetworkTime() => stopwatch.Start(); + public static double localTime => stopwatch.Elapsed.TotalSeconds; +#endif + + /// The time in seconds since the server started. + // + // I measured the accuracy of float and I got this: + // for the same day, accuracy is better than 1 ms + // after 1 day, accuracy goes down to 7 ms + // after 10 days, accuracy is 61 ms + // after 30 days , accuracy is 238 ms + // after 60 days, accuracy is 454 ms + // in other words, if the server is running for 2 months, + // and you cast down to float, then the time will jump in 0.4s intervals. + // + // TODO consider using Unbatcher's remoteTime for NetworkTime + public static double time + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => localTime - _offset.Value; + } + + /// Time measurement variance. The higher, the less accurate the time is. + // TODO does this need to be public? user should only need NetworkTime.time + public static double timeVariance => _offset.Variance; + + /// Time standard deviation. The highe, the less accurate the time is. + // TODO does this need to be public? user should only need NetworkTime.time + public static double timeStandardDeviation => Math.Sqrt(timeVariance); + + /// Clock difference in seconds between the client and the server. Always 0 on server. + public static double offset => _offset.Value; + + /// Round trip time (in seconds) that it takes a message to go client->server->client. + public static double rtt => _rtt.Value; + + /// Round trip time variance. The higher, the less accurate the rtt is. + // TODO does this need to be public? user should only need NetworkTime.time + public static double rttVariance => _rtt.Variance; + + /// Round trip time standard deviation. The higher, the less accurate the rtt is. + // TODO does this need to be public? user should only need NetworkTime.time + public static double rttStandardDeviation => Math.Sqrt(rttVariance); + + // RuntimeInitializeOnLoadMethod -> fast playmode without domain reload + [UnityEngine.RuntimeInitializeOnLoadMethod] + public static void ResetStatics() + { + PingFrequency = 2.0f; + PingWindowSize = 10; + lastPingTime = 0; _rtt = new ExponentialMovingAverage(PingWindowSize); _offset = new ExponentialMovingAverage(PingWindowSize); offsetMin = double.MinValue; offsetMax = double.MaxValue; +#if !UNITY_2020_3_OR_NEWER + stopwatch.Restart(); +#endif } internal static void UpdateClient() { - if (Time.time - lastPingTime >= PingFrequency) + // localTime (double) instead of Time.time for accuracy over days + if (localTime - lastPingTime >= PingFrequency) { - NetworkPingMessage pingMessage = new NetworkPingMessage(LocalTime()); + NetworkPingMessage pingMessage = new NetworkPingMessage(localTime); NetworkClient.Send(pingMessage, Channels.Unreliable); - lastPingTime = Time.time; + lastPingTime = localTime; } } // executed at the server when we receive a ping message // reply with a pong containing the time from the client // and time from the server - internal static void OnServerPing(NetworkConnection conn, NetworkPingMessage message) + internal static void OnServerPing(NetworkConnectionToClient conn, NetworkPingMessage message) { - // Debug.Log("OnPingServerMessage conn=" + conn); + // Debug.Log($"OnPingServerMessage conn:{conn}"); NetworkPongMessage pongMessage = new NetworkPongMessage { clientTime = message.clientTime, - serverTime = LocalTime() + serverTime = localTime }; conn.Send(pongMessage, Channels.Unreliable); } @@ -72,7 +127,7 @@ namespace Mirror // and update time offset internal static void OnClientPong(NetworkPongMessage message) { - double now = LocalTime(); + double now = localTime; // how long did this message take to come back double newRtt = now - message.clientTime; @@ -100,47 +155,5 @@ namespace Mirror _offset.Add(newOffset); } } - - /// The time in seconds since the server started. - // - // I measured the accuracy of float and I got this: - // for the same day, accuracy is better than 1 ms - // after 1 day, accuracy goes down to 7 ms - // after 10 days, accuracy is 61 ms - // after 30 days , accuracy is 238 ms - // after 60 days, accuracy is 454 ms - // in other words, if the server is running for 2 months, - // and you cast down to float, then the time will jump in 0.4s intervals. - public static double time => LocalTime() - _offset.Value; - - /// Time measurement variance. The higher, the less accurate the time is. - // TODO does this need to be public? user should only need NetworkTime.time - public static double timeVariance => _offset.Var; - [Obsolete("NetworkTime.timeVar was renamed to timeVariance")] - public static double timeVar => timeVariance; - - /// Time standard deviation. The highe, the less accurate the time is. - // TODO does this need to be public? user should only need NetworkTime.time - public static double timeStandardDeviation => Math.Sqrt(timeVariance); - [Obsolete("NetworkTime.timeSd was renamed to timeStandardDeviation")] - public static double timeSd => timeStandardDeviation; - - /// Clock difference in seconds between the client and the server. Always 0 on server. - public static double offset => _offset.Value; - - /// Round trip time (in seconds) that it takes a message to go client->server->client. - public static double rtt => _rtt.Value; - - /// Round trip time variance. The higher, the less accurate the rtt is. - // TODO does this need to be public? user should only need NetworkTime.time - public static double rttVariance => _rtt.Var; - [Obsolete("NetworkTime.rttVar was renamed to rttVariance")] - public static double rttVar => rttVariance; - - /// Round trip time standard deviation. The higher, the less accurate the rtt is. - // TODO does this need to be public? user should only need NetworkTime.time - public static double rttStandardDeviation => Math.Sqrt(rttVariance); - [Obsolete("NetworkTime.rttSd was renamed to rttStandardDeviation")] - public static double rttSd => rttStandardDeviation; } } diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkTime.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkTime.cs.meta index ee55ad5..1dc9e0a 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkTime.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/NetworkTime.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkVisibility.cs b/UnityProject/Assets/Mirror/Runtime/NetworkVisibility.cs deleted file mode 100644 index 6dcb0ab..0000000 --- a/UnityProject/Assets/Mirror/Runtime/NetworkVisibility.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; - -namespace Mirror -{ - // the name NetworkProximityCheck implies that it's only about objects in - // proximity to the player. But we might have room based, guild based, - // instanced based checks too, so NetworkVisibility is more fitting. - // - // note: we inherit from NetworkBehaviour so we can reuse .netIdentity, etc. - // note: unlike UNET, we only allow 1 proximity checker per NetworkIdentity. - - // obsolete message in a not-obsolete class to avoid obsolete warnings when - // obsoleting with the obsolete message. got it? - public static class NetworkVisibilityObsoleteMessage - { - // obsolete message in one place. show them everywhere. - public const string Message = "Per-NetworkIdentity Interest Management is being replaced by global Interest Management.\n\nWe already converted some components to the new system. For those, please remove Proximity checkers from NetworkIdentity prefabs and add one global InterestManagement component to your NetworkManager instead. If we didn't convert this one yet, then simply wait. See our Benchmark example and our Mirror/Components/InterestManagement for available implementations.\n\nIf you need to port a custom solution, move your code into a new class that inherits from InterestManagement and add one global update method instead of using NetworkBehaviour.Update.\n\nDon't panic. The whole change mostly moved code from NetworkVisibility components into one global place on NetworkManager. Allows for Spatial Hashing which is ~30x faster.\n\n(╯°□°)╯︵ ┻━┻"; - } - - [Obsolete(NetworkVisibilityObsoleteMessage.Message)] - [DisallowMultipleComponent] - public abstract class NetworkVisibility : NetworkBehaviour - { - /// Callback used by the visibility system to determine if an observer (player) can see this object. - // Called from NetworkServer.SpawnObserversForConnection the first time - // a NetworkIdentity is spawned. - public abstract bool OnCheckObserver(NetworkConnection conn); - - /// Callback used by the visibility system to (re)construct the set of observers that can see this object. - // Implementations of this callback should add network connections of - // players that can see this object to the observers set. - public abstract void OnRebuildObservers(HashSet observers, bool initialize); - - /// Callback used by the visibility system for objects on a host. - public virtual void OnSetHostVisibility(bool visible) - { - foreach (Renderer rend in GetComponentsInChildren()) - rend.enabled = visible; - } - } -} diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkWriter.cs b/UnityProject/Assets/Mirror/Runtime/NetworkWriter.cs index 9881bdb..adbdf0d 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkWriter.cs +++ b/UnityProject/Assets/Mirror/Runtime/NetworkWriter.cs @@ -1,19 +1,10 @@ using System; -using System.Collections.Generic; using System.Runtime.CompilerServices; -using System.Text; +using Unity.Collections.LowLevel.Unsafe; using UnityEngine; namespace Mirror { - /// Helper class that weaver populates with all writer types. - // Note that c# creates a different static variable for each type - // -> Weaver.ReaderWriterProcessor.InitializeReaderAndWriters() populates it - public static class Writer - { - public static Action write; - } - /// Network Writer for most simple types like floats, ints, buffers, structs, etc. Use NetworkWriterPool.GetReader() to avoid allocations. public class NetworkWriter { @@ -22,7 +13,7 @@ namespace Mirror // create writer immediately with it's own buffer so no one can mess with it and so that we can resize it. // note: BinaryWriter allocates too much, so we only use a MemoryStream // => 1500 bytes by default because on average, most packets will be <= MTU - byte[] buffer = new byte[1500]; + internal byte[] buffer = new byte[1500]; /// Next position to write to the buffer public int Position; @@ -30,13 +21,17 @@ namespace Mirror /// Reset both the position and length of the stream // Leaves the capacity the same so that we can reuse this writer without // extra allocations + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Reset() { Position = 0; } + // NOTE that our runtime resizing comes at no extra cost because: + // 1. 'has space' checks are necessary even for fixed sized writers. + // 2. all writers will eventually be large enough to stop resizing. [MethodImpl(MethodImplOptions.AggressiveInlining)] - void EnsureCapacity(int value) + internal void EnsureCapacity(int value) { if (buffer.Length < value) { @@ -46,6 +41,7 @@ namespace Mirror } /// Copies buffer until 'Position' to a new array. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public byte[] ToArray() { byte[] data = new byte[Position]; @@ -54,17 +50,126 @@ namespace Mirror } /// Returns allocation-free ArraySegment until 'Position'. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public ArraySegment ToArraySegment() { return new ArraySegment(buffer, 0, Position); } - public void WriteByte(byte value) + // WriteBlittable from DOTSNET. + // this is extremely fast, but only works for blittable types. + // + // Benchmark: + // WriteQuaternion x 100k, Macbook Pro 2015 @ 2.2Ghz, Unity 2018 LTS (debug mode) + // + // | Median | Min | Max | Avg | Std | (ms) + // before | 30.35 | 29.86 | 48.99 | 32.54 | 4.93 | + // blittable* | 5.69 | 5.52 | 27.51 | 7.78 | 5.65 | + // + // * without IsBlittable check + // => 4-6x faster! + // + // WriteQuaternion x 100k, Macbook Pro 2015 @ 2.2Ghz, Unity 2020.1 (release mode) + // + // | Median | Min | Max | Avg | Std | (ms) + // before | 9.41 | 8.90 | 23.02 | 10.72 | 3.07 | + // blittable* | 1.48 | 1.40 | 16.03 | 2.60 | 2.71 | + // + // * without IsBlittable check + // => 6x faster! + // + // Note: + // WriteBlittable assumes same endianness for server & client. + // All Unity 2018+ platforms are little endian. + // => run NetworkWriterTests.BlittableOnThisPlatform() to verify! + // + // This is not safe to expose to random structs. + // * StructLayout.Sequential is the default, which is safe. + // if the struct contains a reference type, it is converted to Auto. + // but since all structs here are unmanaged blittable, it's safe. + // see also: https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.layoutkind?view=netframework-4.8#system-runtime-interopservices-layoutkind-sequential + // * StructLayout.Pack depends on CPU word size. + // this may be different 4 or 8 on some ARM systems, etc. + // this is not safe, and would cause bytes/shorts etc. to be padded. + // see also: https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.structlayoutattribute.pack?view=net-6.0 + // * If we force pack all to '1', they would have no padding which is + // great for bandwidth. but on some android systems, CPU can't read + // unaligned memory. + // see also: https://github.com/vis2k/Mirror/issues/3044 + // * The only option would be to force explicit layout with multiples + // of word size. but this requires lots of weaver checking and is + // still questionable (IL2CPP etc.). + // + // Note: inlining WriteBlittable is enough. don't inline WriteInt etc. + // we don't want WriteBlittable to be copied in place everywhere. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal unsafe void WriteBlittable(T value) + where T : unmanaged { - EnsureCapacity(Position + 1); - buffer[Position++] = value; + // check if blittable for safety +#if UNITY_EDITOR + if (!UnsafeUtility.IsBlittable(typeof(T))) + { + Debug.LogError($"{typeof(T)} is not blittable!"); + return; + } +#endif + // calculate size + // sizeof(T) gets the managed size at compile time. + // Marshal.SizeOf gets the unmanaged size at runtime (slow). + // => our 1mio writes benchmark is 6x slower with Marshal.SizeOf + // => for blittable types, sizeof(T) is even recommended: + // https://docs.microsoft.com/en-us/dotnet/standard/native-interop/best-practices + int size = sizeof(T); + + // ensure capacity + // NOTE that our runtime resizing comes at no extra cost because: + // 1. 'has space' checks are necessary even for fixed sized writers. + // 2. all writers will eventually be large enough to stop resizing. + EnsureCapacity(Position + size); + + // write blittable + fixed (byte* ptr = &buffer[Position]) + { +#if UNITY_ANDROID + // on some android systems, assigning *(T*)ptr throws a NRE if + // the ptr isn't aligned (i.e. if Position is 1,2,3,5, etc.). + // here we have to use memcpy. + // + // => we can't get a pointer of a struct in C# without + // marshalling allocations + // => instead, we stack allocate an array of type T and use that + // => stackalloc avoids GC and is very fast. it only works for + // value types, but all blittable types are anyway. + // + // this way, we can still support blittable reads on android. + // see also: https://github.com/vis2k/Mirror/issues/3044 + // (solution discovered by AIIO, FakeByte, mischa) + T* valueBuffer = stackalloc T[1]{value}; + UnsafeUtility.MemCpy(ptr, valueBuffer, size); +#else + // cast buffer to T* pointer, then assign value to the area + *(T*)ptr = value; +#endif + } + Position += size; } + // blittable'?' template for code reuse + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void WriteBlittableNullable(T? value) + where T : unmanaged + { + // bool isn't blittable. write as byte. + WriteByte((byte)(value.HasValue ? 0x01 : 0x00)); + + // only write value if exists. saves bandwidth. + if (value.HasValue) + WriteBlittable(value.Value); + } + + public void WriteByte(byte value) => WriteBlittable(value); + // for byte arrays with consistent size, where the reader knows how many to read // (like a packet opcode that's always the same) public void WriteBytes(byte[] buffer, int offset, int count) @@ -75,12 +180,13 @@ namespace Mirror } /// Writes any type that mirror supports. Uses weaver populated Writer(T).write. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(T value) { Action writeDelegate = Writer.write; if (writeDelegate == null) { - Debug.LogError($"No writer found for {typeof(T)}. Use a type supported by Mirror or define a custom writer"); + Debug.LogError($"No writer found for {typeof(T)}. This happens either if you are missing a NetworkWriter extension for your custom type, or if weaving failed. Try to reimport a script to weave again."); } else { @@ -89,367 +195,11 @@ namespace Mirror } } - // Mirror's Weaver automatically detects all NetworkWriter function types, - // but they do all need to be extensions. - public static class NetworkWriterExtensions + /// Helper class that weaver populates with all writer types. + // Note that c# creates a different static variable for each type + // -> Weaver.ReaderWriterProcessor.InitializeReaderAndWriters() populates it + public static class Writer { - // cache encoding instead of creating it with BinaryWriter each time - // 1000 readers before: 1MB GC, 30ms - // 1000 readers after: 0.8MB GC, 18ms - static readonly UTF8Encoding encoding = new UTF8Encoding(false, true); - static readonly byte[] stringBuffer = new byte[NetworkWriter.MaxStringLength]; - - public static void WriteByte(this NetworkWriter writer, byte value) => writer.WriteByte(value); - - public static void WriteSByte(this NetworkWriter writer, sbyte value) => writer.WriteByte((byte)value); - - public static void WriteChar(this NetworkWriter writer, char value) => writer.WriteUShort(value); - - // Deprecated 2021-05-18 - [Obsolete("We've cleaned up the API. Use WriteBool instead.")] - public static void WriteBoolean(this NetworkWriter writer, bool value) => writer.WriteBool(value); - public static void WriteBool(this NetworkWriter writer, bool value) => writer.WriteByte((byte)(value ? 1 : 0)); - - // Deprecated 2021-05-18 - [Obsolete("We've cleaned up the API. Use WriteUShort instead.")] - public static void WriteUInt16(this NetworkWriter writer, ushort value) => writer.WriteUShort(value); - public static void WriteUShort(this NetworkWriter writer, ushort value) - { - writer.WriteByte((byte)value); - writer.WriteByte((byte)(value >> 8)); - } - - // Deprecated 2021-05-18 - [Obsolete("We've cleaned up the API. Use WriteShort instead.")] - public static void WriteInt16(this NetworkWriter writer, short value) => writer.WriteShort(value); - public static void WriteShort(this NetworkWriter writer, short value) => writer.WriteUShort((ushort)value); - - // Deprecated 2021-05-18 - [Obsolete("We've cleaned up the API. Use WriteUInt instead.")] - public static void WriteUInt32(this NetworkWriter writer, uint value) => writer.WriteUInt(value); - public static void WriteUInt(this NetworkWriter writer, uint value) - { - writer.WriteByte((byte)value); - writer.WriteByte((byte)(value >> 8)); - writer.WriteByte((byte)(value >> 16)); - writer.WriteByte((byte)(value >> 24)); - } - - // Deprecated 2021-05-18 - [Obsolete("We've cleaned up the API. Use WriteInt instead.")] - public static void WriteInt32(this NetworkWriter writer, int value) => writer.WriteInt(value); - public static void WriteInt(this NetworkWriter writer, int value) => writer.WriteUInt((uint)value); - - // Deprecated 2021-05-18 - [Obsolete("We've cleaned up the API. Use WriteULong instead.")] - public static void WriteUInt64(this NetworkWriter writer, ulong value) => writer.WriteULong(value); - public static void WriteULong(this NetworkWriter writer, ulong value) - { - writer.WriteByte((byte)value); - writer.WriteByte((byte)(value >> 8)); - writer.WriteByte((byte)(value >> 16)); - writer.WriteByte((byte)(value >> 24)); - writer.WriteByte((byte)(value >> 32)); - writer.WriteByte((byte)(value >> 40)); - writer.WriteByte((byte)(value >> 48)); - writer.WriteByte((byte)(value >> 56)); - } - - // Deprecated 2021-05-18 - [Obsolete("We've cleaned up the API. Use WriteLong instead.")] - public static void WriteInt64(this NetworkWriter writer, long value) => writer.WriteLong(value); - public static void WriteLong(this NetworkWriter writer, long value) => writer.WriteULong((ulong)value); - - // Deprecated 2021-05-18 - [Obsolete("We've cleaned up the API. Use WriteFloat instead.")] - public static void WriteSingle(this NetworkWriter writer, float value) => writer.WriteFloat(value); - public static void WriteFloat(this NetworkWriter writer, float value) - { - UIntFloat converter = new UIntFloat - { - floatValue = value - }; - writer.WriteUInt(converter.intValue); - } - - public static void WriteDouble(this NetworkWriter writer, double value) - { - UIntDouble converter = new UIntDouble - { - doubleValue = value - }; - writer.WriteULong(converter.longValue); - } - - public static void WriteDecimal(this NetworkWriter writer, decimal value) - { - // the only way to read it without allocations is to both read and - // write it with the FloatConverter (which is not binary compatible - // to writer.Write(decimal), hence why we use it here too) - UIntDecimal converter = new UIntDecimal - { - decimalValue = value - }; - writer.WriteULong(converter.longValue1); - writer.WriteULong(converter.longValue2); - } - - public static void WriteString(this NetworkWriter writer, string value) - { - // write 0 for null support, increment real size by 1 - // (note: original HLAPI would write "" for null strings, but if a - // string is null on the server then it should also be null - // on the client) - if (value == null) - { - writer.WriteUShort(0); - return; - } - - // write string with same method as NetworkReader - // convert to byte[] - int size = encoding.GetBytes(value, 0, value.Length, stringBuffer, 0); - - // check if within max size - if (size >= NetworkWriter.MaxStringLength) - { - throw new IndexOutOfRangeException("NetworkWriter.Write(string) too long: " + size + ". Limit: " + NetworkWriter.MaxStringLength); - } - - // write size and bytes - writer.WriteUShort(checked((ushort)(size + 1))); - writer.WriteBytes(stringBuffer, 0, size); - } - - // for byte arrays with dynamic size, where the reader doesn't know how many will come - // (like an inventory with different items etc.) - public static void WriteBytesAndSize(this NetworkWriter writer, byte[] buffer, int offset, int count) - { - // null is supported because [SyncVar]s might be structs with null byte[] arrays - // write 0 for null array, increment normal size by 1 to save bandwidth - // (using size=-1 for null would limit max size to 32kb instead of 64kb) - if (buffer == null) - { - writer.WriteUInt(0u); - return; - } - writer.WriteUInt(checked((uint)count) + 1u); - writer.WriteBytes(buffer, offset, count); - } - - // Weaver needs a write function with just one byte[] parameter - // (we don't name it .Write(byte[]) because it's really a WriteBytesAndSize since we write size / null info too) - public static void WriteBytesAndSize(this NetworkWriter writer, byte[] buffer) - { - // buffer might be null, so we can't use .Length in that case - writer.WriteBytesAndSize(buffer, 0, buffer != null ? buffer.Length : 0); - } - - public static void WriteBytesAndSizeSegment(this NetworkWriter writer, ArraySegment buffer) - { - writer.WriteBytesAndSize(buffer.Array, buffer.Offset, buffer.Count); - } - - public static void WriteVector2(this NetworkWriter writer, Vector2 value) - { - writer.WriteFloat(value.x); - writer.WriteFloat(value.y); - } - - public static void WriteVector3(this NetworkWriter writer, Vector3 value) - { - writer.WriteFloat(value.x); - writer.WriteFloat(value.y); - writer.WriteFloat(value.z); - } - - public static void WriteVector4(this NetworkWriter writer, Vector4 value) - { - writer.WriteFloat(value.x); - writer.WriteFloat(value.y); - writer.WriteFloat(value.z); - writer.WriteFloat(value.w); - } - - public static void WriteVector2Int(this NetworkWriter writer, Vector2Int value) - { - writer.WriteInt(value.x); - writer.WriteInt(value.y); - } - - public static void WriteVector3Int(this NetworkWriter writer, Vector3Int value) - { - writer.WriteInt(value.x); - writer.WriteInt(value.y); - writer.WriteInt(value.z); - } - - public static void WriteColor(this NetworkWriter writer, Color value) - { - writer.WriteFloat(value.r); - writer.WriteFloat(value.g); - writer.WriteFloat(value.b); - writer.WriteFloat(value.a); - } - - public static void WriteColor32(this NetworkWriter writer, Color32 value) - { - writer.WriteByte(value.r); - writer.WriteByte(value.g); - writer.WriteByte(value.b); - writer.WriteByte(value.a); - } - - public static void WriteQuaternion(this NetworkWriter writer, Quaternion value) - { - writer.WriteFloat(value.x); - writer.WriteFloat(value.y); - writer.WriteFloat(value.z); - writer.WriteFloat(value.w); - } - - public static void WriteRect(this NetworkWriter writer, Rect value) - { - writer.WriteFloat(value.xMin); - writer.WriteFloat(value.yMin); - writer.WriteFloat(value.width); - writer.WriteFloat(value.height); - } - - public static void WritePlane(this NetworkWriter writer, Plane value) - { - writer.WriteVector3(value.normal); - writer.WriteFloat(value.distance); - } - - public static void WriteRay(this NetworkWriter writer, Ray value) - { - writer.WriteVector3(value.origin); - writer.WriteVector3(value.direction); - } - - public static void WriteMatrix4x4(this NetworkWriter writer, Matrix4x4 value) - { - writer.WriteFloat(value.m00); - writer.WriteFloat(value.m01); - writer.WriteFloat(value.m02); - writer.WriteFloat(value.m03); - writer.WriteFloat(value.m10); - writer.WriteFloat(value.m11); - writer.WriteFloat(value.m12); - writer.WriteFloat(value.m13); - writer.WriteFloat(value.m20); - writer.WriteFloat(value.m21); - writer.WriteFloat(value.m22); - writer.WriteFloat(value.m23); - writer.WriteFloat(value.m30); - writer.WriteFloat(value.m31); - writer.WriteFloat(value.m32); - writer.WriteFloat(value.m33); - } - - public static void WriteGuid(this NetworkWriter writer, Guid value) - { - byte[] data = value.ToByteArray(); - writer.WriteBytes(data, 0, data.Length); - } - - public static void WriteNetworkIdentity(this NetworkWriter writer, NetworkIdentity value) - { - if (value == null) - { - writer.WriteUInt(0); - return; - } - writer.WriteUInt(value.netId); - } - - public static void WriteNetworkBehaviour(this NetworkWriter writer, NetworkBehaviour value) - { - if (value == null) - { - writer.WriteUInt(0); - return; - } - writer.WriteUInt(value.netId); - writer.WriteByte((byte)value.ComponentIndex); - } - - public static void WriteTransform(this NetworkWriter writer, Transform value) - { - if (value == null) - { - writer.WriteUInt(0); - return; - } - NetworkIdentity identity = value.GetComponent(); - if (identity != null) - { - writer.WriteUInt(identity.netId); - } - else - { - Debug.LogWarning("NetworkWriter " + value + " has no NetworkIdentity"); - writer.WriteUInt(0); - } - } - - public static void WriteGameObject(this NetworkWriter writer, GameObject value) - { - if (value == null) - { - writer.WriteUInt(0); - return; - } - NetworkIdentity identity = value.GetComponent(); - if (identity != null) - { - writer.WriteUInt(identity.netId); - } - else - { - Debug.LogWarning("NetworkWriter " + value + " has no NetworkIdentity"); - writer.WriteUInt(0); - } - } - - public static void WriteUri(this NetworkWriter writer, Uri uri) - { - writer.WriteString(uri.ToString()); - } - - public static void WriteList(this NetworkWriter writer, List list) - { - if (list is null) - { - writer.WriteInt(-1); - return; - } - writer.WriteInt(list.Count); - for (int i = 0; i < list.Count; i++) - writer.Write(list[i]); - } - - public static void WriteArray(this NetworkWriter writer, T[] array) - { - if (array is null) - { - writer.WriteInt(-1); - return; - } - writer.WriteInt(array.Length); - for (int i = 0; i < array.Length; i++) - writer.Write(array[i]); - } - - public static void WriteArraySegment(this NetworkWriter writer, ArraySegment segment) - { - int length = segment.Count; - writer.WriteInt(length); - for (int i = 0; i < length; i++) - { - writer.Write(segment.Array[segment.Offset + i]); - } - } + public static Action write; } } diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkWriter.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkWriter.cs.meta index 0ffc0fc..c938496 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkWriter.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/NetworkWriter.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkWriterExtensions.cs b/UnityProject/Assets/Mirror/Runtime/NetworkWriterExtensions.cs new file mode 100644 index 0000000..dfabd27 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/NetworkWriterExtensions.cs @@ -0,0 +1,333 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using UnityEngine; + +namespace Mirror +{ + // Mirror's Weaver automatically detects all NetworkWriter function types, + // but they do all need to be extensions. + public static class NetworkWriterExtensions + { + // cache encoding instead of creating it with BinaryWriter each time + // 1000 readers before: 1MB GC, 30ms + // 1000 readers after: 0.8MB GC, 18ms + static readonly UTF8Encoding encoding = new UTF8Encoding(false, true); + static readonly byte[] stringBuffer = new byte[NetworkWriter.MaxStringLength]; + + public static void WriteByte(this NetworkWriter writer, byte value) => writer.WriteBlittable(value); + public static void WriteByteNullable(this NetworkWriter writer, byte? value) => writer.WriteBlittableNullable(value); + + public static void WriteSByte(this NetworkWriter writer, sbyte value) => writer.WriteBlittable(value); + public static void WriteSByteNullable(this NetworkWriter writer, sbyte? value) => writer.WriteBlittableNullable(value); + + // char is not blittable. convert to ushort. + public static void WriteChar(this NetworkWriter writer, char value) => writer.WriteBlittable((ushort)value); + public static void WriteCharNullable(this NetworkWriter writer, char? value) => writer.WriteBlittableNullable((ushort?)value); + + // bool is not blittable. convert to byte. + public static void WriteBool(this NetworkWriter writer, bool value) => writer.WriteBlittable((byte)(value ? 1 : 0)); + public static void WriteBoolNullable(this NetworkWriter writer, bool? value) => writer.WriteBlittableNullable(value.HasValue ? ((byte)(value.Value ? 1 : 0)) : new byte?()); + + public static void WriteShort(this NetworkWriter writer, short value) => writer.WriteBlittable(value); + public static void WriteShortNullable(this NetworkWriter writer, short? value) => writer.WriteBlittableNullable(value); + + public static void WriteUShort(this NetworkWriter writer, ushort value) => writer.WriteBlittable(value); + public static void WriteUShortNullable(this NetworkWriter writer, ushort? value) => writer.WriteBlittableNullable(value); + + public static void WriteInt(this NetworkWriter writer, int value) => writer.WriteBlittable(value); + public static void WriteIntNullable(this NetworkWriter writer, int? value) => writer.WriteBlittableNullable(value); + + public static void WriteUInt(this NetworkWriter writer, uint value) => writer.WriteBlittable(value); + public static void WriteUIntNullable(this NetworkWriter writer, uint? value) => writer.WriteBlittableNullable(value); + + public static void WriteLong(this NetworkWriter writer, long value) => writer.WriteBlittable(value); + public static void WriteLongNullable(this NetworkWriter writer, long? value) => writer.WriteBlittableNullable(value); + + public static void WriteULong(this NetworkWriter writer, ulong value) => writer.WriteBlittable(value); + public static void WriteULongNullable(this NetworkWriter writer, ulong? value) => writer.WriteBlittableNullable(value); + + public static void WriteFloat(this NetworkWriter writer, float value) => writer.WriteBlittable(value); + public static void WriteFloatNullable(this NetworkWriter writer, float? value) => writer.WriteBlittableNullable(value); + + [StructLayout(LayoutKind.Explicit)] + internal struct UIntDouble + { + [FieldOffset(0)] + public double doubleValue; + + [FieldOffset(0)] + public ulong longValue; + } + + public static void WriteDouble(this NetworkWriter writer, double value) + { + // DEBUG: try to find the exact value that fails. + //UIntDouble convert = new UIntDouble{doubleValue = value}; + //Debug.Log($"=> NetworkWriter.WriteDouble: {value} => 0x{convert.longValue:X8}"); + + + writer.WriteBlittable(value); + } + public static void WriteDoubleNullable(this NetworkWriter writer, double? value) => writer.WriteBlittableNullable(value); + + public static void WriteDecimal(this NetworkWriter writer, decimal value) => writer.WriteBlittable(value); + public static void WriteDecimalNullable(this NetworkWriter writer, decimal? value) => writer.WriteBlittableNullable(value); + + public static void WriteString(this NetworkWriter writer, string value) + { + // write 0 for null support, increment real size by 1 + // (note: original HLAPI would write "" for null strings, but if a + // string is null on the server then it should also be null + // on the client) + if (value == null) + { + writer.WriteUShort(0); + return; + } + + // write string with same method as NetworkReader + // convert to byte[] + int size = encoding.GetBytes(value, 0, value.Length, stringBuffer, 0); + + // check if within max size + if (size >= NetworkWriter.MaxStringLength) + { + throw new IndexOutOfRangeException($"NetworkWriter.Write(string) too long: {size}. Limit: {NetworkWriter.MaxStringLength}"); + } + + // write size and bytes + writer.WriteUShort(checked((ushort)(size + 1))); + writer.WriteBytes(stringBuffer, 0, size); + } + + public static void WriteBytesAndSizeSegment(this NetworkWriter writer, ArraySegment buffer) + { + writer.WriteBytesAndSize(buffer.Array, buffer.Offset, buffer.Count); + } + + // Weaver needs a write function with just one byte[] parameter + // (we don't name it .Write(byte[]) because it's really a WriteBytesAndSize since we write size / null info too) + + public static void WriteBytesAndSize(this NetworkWriter writer, byte[] buffer) + { + // buffer might be null, so we can't use .Length in that case + writer.WriteBytesAndSize(buffer, 0, buffer != null ? buffer.Length : 0); + } + + // for byte arrays with dynamic size, where the reader doesn't know how many will come + // (like an inventory with different items etc.) + + public static void WriteBytesAndSize(this NetworkWriter writer, byte[] buffer, int offset, int count) + { + // null is supported because [SyncVar]s might be structs with null byte[] arrays + // write 0 for null array, increment normal size by 1 to save bandwidth + // (using size=-1 for null would limit max size to 32kb instead of 64kb) + if (buffer == null) + { + writer.WriteUInt(0u); + return; + } + writer.WriteUInt(checked((uint)count) + 1u); + writer.WriteBytes(buffer, offset, count); + } + + public static void WriteArraySegment(this NetworkWriter writer, ArraySegment segment) + { + int length = segment.Count; + writer.WriteInt(length); + for (int i = 0; i < length; i++) + { + writer.Write(segment.Array[segment.Offset + i]); + } + } + + public static void WriteVector2(this NetworkWriter writer, Vector2 value) => writer.WriteBlittable(value); + public static void WriteVector2Nullable(this NetworkWriter writer, Vector2? value) => writer.WriteBlittableNullable(value); + + public static void WriteVector3(this NetworkWriter writer, Vector3 value) => writer.WriteBlittable(value); + public static void WriteVector3Nullable(this NetworkWriter writer, Vector3? value) => writer.WriteBlittableNullable(value); + + public static void WriteVector4(this NetworkWriter writer, Vector4 value) => writer.WriteBlittable(value); + public static void WriteVector4Nullable(this NetworkWriter writer, Vector4? value) => writer.WriteBlittableNullable(value); + + public static void WriteVector2Int(this NetworkWriter writer, Vector2Int value) => writer.WriteBlittable(value); + public static void WriteVector2IntNullable(this NetworkWriter writer, Vector2Int? value) => writer.WriteBlittableNullable(value); + + public static void WriteVector3Int(this NetworkWriter writer, Vector3Int value) => writer.WriteBlittable(value); + public static void WriteVector3IntNullable(this NetworkWriter writer, Vector3Int? value) => writer.WriteBlittableNullable(value); + + public static void WriteColor(this NetworkWriter writer, Color value) => writer.WriteBlittable(value); + public static void WriteColorNullable(this NetworkWriter writer, Color? value) => writer.WriteBlittableNullable(value); + + public static void WriteColor32(this NetworkWriter writer, Color32 value) => writer.WriteBlittable(value); + public static void WriteColor32Nullable(this NetworkWriter writer, Color32? value) => writer.WriteBlittableNullable(value); + + public static void WriteQuaternion(this NetworkWriter writer, Quaternion value) => writer.WriteBlittable(value); + public static void WriteQuaternionNullable(this NetworkWriter writer, Quaternion? value) => writer.WriteBlittableNullable(value); + + public static void WriteRect(this NetworkWriter writer, Rect value) => writer.WriteBlittable(value); + public static void WriteRectNullable(this NetworkWriter writer, Rect? value) => writer.WriteBlittableNullable(value); + + public static void WritePlane(this NetworkWriter writer, Plane value) => writer.WriteBlittable(value); + public static void WritePlaneNullable(this NetworkWriter writer, Plane? value) => writer.WriteBlittableNullable(value); + + public static void WriteRay(this NetworkWriter writer, Ray value) => writer.WriteBlittable(value); + public static void WriteRayNullable(this NetworkWriter writer, Ray? value) => writer.WriteBlittableNullable(value); + + public static void WriteMatrix4x4(this NetworkWriter writer, Matrix4x4 value) => writer.WriteBlittable(value); + public static void WriteMatrix4x4Nullable(this NetworkWriter writer, Matrix4x4? value) => writer.WriteBlittableNullable(value); + + public static void WriteGuid(this NetworkWriter writer, Guid value) + { + // WriteBlittable(Guid) isn't safe. see WriteBlittable comments. + // Guid is Sequential, but we can't guarantee packing. + // TryWriteBytes is safe and allocation free. + writer.EnsureCapacity(writer.Position + 16); + value.TryWriteBytes(new Span(writer.buffer, writer.Position, 16)); + writer.Position += 16; + } + public static void WriteGuidNullable(this NetworkWriter writer, Guid? value) + { + writer.WriteBool(value.HasValue); + if (value.HasValue) + writer.WriteGuid(value.Value); + } + + public static void WriteNetworkIdentity(this NetworkWriter writer, NetworkIdentity value) + { + if (value == null) + { + writer.WriteUInt(0); + return; + } + + // users might try to use unspawned / prefab GameObjects in + // rpcs/cmds/syncvars/messages. they would be null on the other + // end, and it might not be obvious why. let's make it obvious. + // https://github.com/vis2k/Mirror/issues/2060 + // + // => warning (instead of exception) because we also use a warning + // if a GameObject doesn't have a NetworkIdentity component etc. + if (value.netId == 0) + Debug.LogWarning($"Attempted to serialize unspawned GameObject: {value.name}. Prefabs and unspawned GameObjects would always be null on the other side. Please spawn it before using it in [SyncVar]s/Rpcs/Cmds/NetworkMessages etc."); + + writer.WriteUInt(value.netId); + } + + public static void WriteNetworkBehaviour(this NetworkWriter writer, NetworkBehaviour value) + { + if (value == null) + { + writer.WriteUInt(0); + return; + } + writer.WriteUInt(value.netId); + writer.WriteByte((byte)value.ComponentIndex); + } + + public static void WriteTransform(this NetworkWriter writer, Transform value) + { + if (value == null) + { + writer.WriteUInt(0); + return; + } + NetworkIdentity identity = value.GetComponent(); + if (identity != null) + { + writer.WriteUInt(identity.netId); + } + else + { + Debug.LogWarning($"NetworkWriter {value} has no NetworkIdentity"); + writer.WriteUInt(0); + } + } + + public static void WriteGameObject(this NetworkWriter writer, GameObject value) + { + if (value == null) + { + writer.WriteUInt(0); + return; + } + + // warn if the GameObject doesn't have a NetworkIdentity, + NetworkIdentity identity = value.GetComponent(); + if (identity == null) + Debug.LogWarning($"NetworkWriter {value} has no NetworkIdentity"); + + // serialize the correct amount of data in any case to make sure + // that the other end can read the expected amount of data too. + writer.WriteNetworkIdentity(identity); + } + + public static void WriteList(this NetworkWriter writer, List list) + { + if (list is null) + { + writer.WriteInt(-1); + return; + } + writer.WriteInt(list.Count); + for (int i = 0; i < list.Count; i++) + writer.Write(list[i]); + } + + public static void WriteArray(this NetworkWriter writer, T[] array) + { + if (array is null) + { + writer.WriteInt(-1); + return; + } + writer.WriteInt(array.Length); + for (int i = 0; i < array.Length; i++) + writer.Write(array[i]); + } + + public static void WriteUri(this NetworkWriter writer, Uri uri) + { + writer.WriteString(uri?.ToString()); + } + + public static void WriteTexture2D(this NetworkWriter writer, Texture2D texture2D) + { + // TODO allocation protection when sending textures to server. + // currently can allocate 32k x 32k x 4 byte = 3.8 GB + + // support 'null' textures for [SyncVar]s etc. + // https://github.com/vis2k/Mirror/issues/3144 + // simply send -1 for width. + if (texture2D == null) + { + writer.WriteShort(-1); + return; + } + + // write dimensions first so reader can create the texture with size + // 32k x 32k short is more than enough + writer.WriteShort((short)texture2D.width); + writer.WriteShort((short)texture2D.height); + writer.WriteArray(texture2D.GetPixels32()); + } + + public static void WriteSprite(this NetworkWriter writer, Sprite sprite) + { + // support 'null' textures for [SyncVar]s etc. + // https://github.com/vis2k/Mirror/issues/3144 + // simply send a 'null' for texture content. + if (sprite == null) + { + writer.WriteTexture2D(null); + return; + } + + writer.WriteTexture2D(sprite.texture); + writer.WriteRect(sprite.rect); + writer.WriteVector2(sprite.pivot); + } + } +} diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkWriterExtensions.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkWriterExtensions.cs.meta new file mode 100644 index 0000000..9bbdaf0 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/NetworkWriterExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 94259792df2a404892c3e2377f58d0cb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkWriterPool.cs b/UnityProject/Assets/Mirror/Runtime/NetworkWriterPool.cs index 785d4e7..c63323d 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkWriterPool.cs +++ b/UnityProject/Assets/Mirror/Runtime/NetworkWriterPool.cs @@ -1,13 +1,9 @@ +// API consistent with Microsoft's ObjectPool. using System; +using System.Runtime.CompilerServices; namespace Mirror { - /// Pooled NetworkWriter, automatically returned to pool when using 'using' - public sealed class PooledNetworkWriter : NetworkWriter, IDisposable - { - public void Dispose() => NetworkWriterPool.Recycle(this); - } - /// Pool of NetworkWriters to avoid allocations. public static class NetworkWriterPool { @@ -16,24 +12,34 @@ namespace Mirror // position before reusing. // this is also more consistent with NetworkReaderPool where we need to // assign the internal buffer before reusing. - static readonly Pool Pool = new Pool( - () => new PooledNetworkWriter(), + static readonly Pool Pool = new Pool( + () => new NetworkWriterPooled(), // initial capacity to avoid allocations in the first few frames // 1000 * 1200 bytes = around 1 MB. 1000 ); + // DEPRECATED 2022-03-10 + [Obsolete("GetWriter() was renamed to Get()")] + public static NetworkWriterPooled GetWriter() => Get(); + /// Get a writer from the pool. Creates new one if pool is empty. - public static PooledNetworkWriter GetWriter() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NetworkWriterPooled Get() { // grab from pool & reset position - PooledNetworkWriter writer = Pool.Take(); + NetworkWriterPooled writer = Pool.Get(); writer.Reset(); return writer; } + // DEPRECATED 2022-03-10 + [Obsolete("Recycle() was renamed to Return()")] + public static void Recycle(NetworkWriterPooled writer) => Return(writer); + /// Return a writer to the pool. - public static void Recycle(PooledNetworkWriter writer) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Return(NetworkWriterPooled writer) { Pool.Return(writer); } diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkWriterPool.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkWriterPool.cs.meta index 593bfa8..19d2bb7 100644 --- a/UnityProject/Assets/Mirror/Runtime/NetworkWriterPool.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/NetworkWriterPool.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkWriterPooled.cs b/UnityProject/Assets/Mirror/Runtime/NetworkWriterPooled.cs new file mode 100644 index 0000000..ce113bc --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/NetworkWriterPooled.cs @@ -0,0 +1,17 @@ +// "NetworkWriterPooled" instead of "PooledNetworkWriter" to group files, for +// easier IDE workflow and more elegant code. +using System; + +namespace Mirror +{ + // DEPRECATED 2022-03-10 + [Obsolete("PooledNetworkWriter was renamed to NetworkWriterPooled. It's cleaner & slightly easier to use.")] + public sealed class PooledNetworkWriter : NetworkWriterPooled {} + + /// Pooled NetworkWriter, automatically returned to pool when using 'using' + // TODO make sealed again after removing obsolete NetworkWriterPooled! + public class NetworkWriterPooled : NetworkWriter, IDisposable + { + public void Dispose() => NetworkWriterPool.Return(this); + } +} diff --git a/UnityProject/Assets/Mirror/Runtime/NetworkWriterPooled.cs.meta b/UnityProject/Assets/Mirror/Runtime/NetworkWriterPooled.cs.meta new file mode 100644 index 0000000..5571d6f --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/NetworkWriterPooled.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a9fab936bf3c4716a452d94ad5ecbebe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Pool.cs b/UnityProject/Assets/Mirror/Runtime/Pool.cs index 5e078f7..e526139 100644 --- a/UnityProject/Assets/Mirror/Runtime/Pool.cs +++ b/UnityProject/Assets/Mirror/Runtime/Pool.cs @@ -1,6 +1,8 @@ // Pool to avoid allocations (from libuv2k) +// API consistent with Microsoft's ObjectPool. using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; namespace Mirror { @@ -23,10 +25,16 @@ namespace Mirror objects.Push(objectGenerator()); } + // DEPRECATED 2022-03-10 + [Obsolete("Take() was renamed to Get()")] + public T Take() => Get(); + // take an element from the pool, or create a new one if empty - public T Take() => objects.Count > 0 ? objects.Pop() : objectGenerator(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Get() => objects.Count > 0 ? objects.Pop() : objectGenerator(); // return an element to the pool + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Return(T item) => objects.Push(item); // count to see how many objects are in the pool. useful for tests. diff --git a/UnityProject/Assets/Mirror/Runtime/Pool.cs.meta b/UnityProject/Assets/Mirror/Runtime/Pool.cs.meta index 66db3cc..7d12a20 100644 --- a/UnityProject/Assets/Mirror/Runtime/Pool.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Pool.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/RemoteCallHelper.cs b/UnityProject/Assets/Mirror/Runtime/RemoteCallHelper.cs deleted file mode 100644 index dad6cbb..0000000 --- a/UnityProject/Assets/Mirror/Runtime/RemoteCallHelper.cs +++ /dev/null @@ -1,152 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; - -namespace Mirror.RemoteCalls -{ - // command function delegate - public delegate void CmdDelegate(NetworkBehaviour obj, NetworkReader reader, NetworkConnectionToClient senderConnection); - - class Invoker - { - public Type invokeClass; - public MirrorInvokeType invokeType; - public CmdDelegate invokeFunction; - public bool cmdRequiresAuthority; - - public bool AreEqual(Type invokeClass, MirrorInvokeType invokeType, CmdDelegate invokeFunction) - { - return (this.invokeClass == invokeClass && - this.invokeType == invokeType && - this.invokeFunction == invokeFunction); - } - } - - public struct CommandInfo - { - public bool requiresAuthority; - } - - /// Used to help manage remote calls for NetworkBehaviours - public static class RemoteCallHelper - { - static readonly Dictionary cmdHandlerDelegates = new Dictionary(); - - internal static int GetMethodHash(Type invokeClass, string methodName) - { - // (invokeClass + ":" + cmdName).GetStableHashCode() would cause allocations. - // so hash1 + hash2 is better. - unchecked - { - int hash = invokeClass.FullName.GetStableHashCode(); - return hash * 503 + methodName.GetStableHashCode(); - } - } - - internal static int RegisterDelegate(Type invokeClass, string cmdName, MirrorInvokeType invokerType, CmdDelegate func, bool cmdRequiresAuthority = true) - { - // type+func so Inventory.RpcUse != Equipment.RpcUse - int cmdHash = GetMethodHash(invokeClass, cmdName); - - if (CheckIfDeligateExists(invokeClass, invokerType, func, cmdHash)) - return cmdHash; - - Invoker invoker = new Invoker - { - invokeType = invokerType, - invokeClass = invokeClass, - invokeFunction = func, - cmdRequiresAuthority = cmdRequiresAuthority, - }; - - cmdHandlerDelegates[cmdHash] = invoker; - - //string ingoreAuthorityMessage = invokerType == MirrorInvokeType.Command ? $" requiresAuthority:{cmdRequiresAuthority}" : ""; - //Debug.Log($"RegisterDelegate hash: {cmdHash} invokerType: {invokerType} method: {func.GetMethodName()}{ingoreAuthorityMessage}"); - - return cmdHash; - } - - static bool CheckIfDeligateExists(Type invokeClass, MirrorInvokeType invokerType, CmdDelegate func, int cmdHash) - { - if (cmdHandlerDelegates.ContainsKey(cmdHash)) - { - // something already registered this hash - Invoker oldInvoker = cmdHandlerDelegates[cmdHash]; - if (oldInvoker.AreEqual(invokeClass, invokerType, func)) - { - // it's all right, it was the same function - return true; - } - - Debug.LogError($"Function {oldInvoker.invokeClass}.{oldInvoker.invokeFunction.GetMethodName()} and {invokeClass}.{func.GetMethodName()} have the same hash. Please rename one of them"); - } - - return false; - } - - public static void RegisterCommandDelegate(Type invokeClass, string cmdName, CmdDelegate func, bool requiresAuthority) - { - RegisterDelegate(invokeClass, cmdName, MirrorInvokeType.Command, func, requiresAuthority); - } - - public static void RegisterRpcDelegate(Type invokeClass, string rpcName, CmdDelegate func) - { - RegisterDelegate(invokeClass, rpcName, MirrorInvokeType.ClientRpc, func); - } - - // We need this in order to clean up tests - internal static void RemoveDelegate(int hash) - { - cmdHandlerDelegates.Remove(hash); - } - - static bool GetInvokerForHash(int cmdHash, MirrorInvokeType invokeType, out Invoker invoker) - { - if (cmdHandlerDelegates.TryGetValue(cmdHash, out invoker) && invoker != null && invoker.invokeType == invokeType) - { - return true; - } - - // debug message if not found, or null, or mismatched type - // (no need to throw an error, an attacker might just be trying to - // call an cmd with an rpc's hash) - // Debug.Log("GetInvokerForHash hash:" + cmdHash + " not found"); - return false; - } - - // InvokeCmd/Rpc Delegate can all use the same function here - internal static bool InvokeHandlerDelegate(int cmdHash, MirrorInvokeType invokeType, NetworkReader reader, NetworkBehaviour invokingType, NetworkConnectionToClient senderConnection = null) - { - if (GetInvokerForHash(cmdHash, invokeType, out Invoker invoker) && invoker.invokeClass.IsInstanceOfType(invokingType)) - { - invoker.invokeFunction(invokingType, reader, senderConnection); - return true; - } - return false; - } - - internal static CommandInfo GetCommandInfo(int cmdHash, NetworkBehaviour invokingType) - { - if (GetInvokerForHash(cmdHash, MirrorInvokeType.Command, out Invoker invoker) && invoker.invokeClass.IsInstanceOfType(invokingType)) - { - return new CommandInfo - { - requiresAuthority = invoker.cmdRequiresAuthority - }; - } - return default; - } - - /// Gets the handler function by hash. Useful for profilers and debuggers. - public static CmdDelegate GetDelegate(int cmdHash) - { - if (cmdHandlerDelegates.TryGetValue(cmdHash, out Invoker invoker)) - { - return invoker.invokeFunction; - } - return null; - } - } -} - diff --git a/UnityProject/Assets/Mirror/Runtime/RemoteCalls.cs b/UnityProject/Assets/Mirror/Runtime/RemoteCalls.cs new file mode 100644 index 0000000..c3f9235 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/RemoteCalls.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Mirror.RemoteCalls +{ + // invoke type for Cmd/Rpc + public enum RemoteCallType { Command, ClientRpc } + + // remote call function delegate + public delegate void RemoteCallDelegate(NetworkBehaviour obj, NetworkReader reader, NetworkConnectionToClient senderConnection); + + class Invoker + { + // GameObjects might have multiple components of TypeA.CommandA(). + // when invoking, we check if 'TypeA' is an instance of the type. + // the hash itself isn't enough because we wouldn't know which component + // to invoke it on if there are multiple of the same type. + public Type componentType; + public RemoteCallType callType; + public RemoteCallDelegate function; + public bool cmdRequiresAuthority; + + public bool AreEqual(Type componentType, RemoteCallType remoteCallType, RemoteCallDelegate invokeFunction) => + this.componentType == componentType && + this.callType == remoteCallType && + this.function == invokeFunction; + } + + /// Used to help manage remote calls for NetworkBehaviours + public static class RemoteProcedureCalls + { + // one lookup for all remote calls. + // allows us to easily add more remote call types without duplicating code. + // note: do not clear those with [RuntimeInitializeOnLoad] + // + // IMPORTANT: cmd/rpc functions are identified via **HASHES**. + // an index would requires half the bandwidth, but introduces issues + // where static constructors are lazily called, so index order isn't + // guaranteed. keep hashes to avoid: + // https://github.com/vis2k/Mirror/pull/3135 + // https://github.com/vis2k/Mirror/issues/3138 + // BUT: 2 byte hash is enough if we check for collisions. that's what we + // do for NetworkMessage as well. + static readonly Dictionary remoteCallDelegates = new Dictionary(); + + static bool CheckIfDelegateExists(Type componentType, RemoteCallType remoteCallType, RemoteCallDelegate func, ushort functionHash) + { + if (remoteCallDelegates.ContainsKey(functionHash)) + { + // something already registered this hash. + // it's okay if it was the same function. + Invoker oldInvoker = remoteCallDelegates[functionHash]; + if (oldInvoker.AreEqual(componentType, remoteCallType, func)) + { + return true; + } + + // otherwise notify user. there is a rare chance of string + // hash collisions. + Debug.LogError($"Function {oldInvoker.componentType}.{oldInvoker.function.GetMethodName()} and {componentType}.{func.GetMethodName()} have the same hash. Please rename one of them"); + } + + return false; + } + + // pass full function name to avoid ClassA.Func & ClassB.Func collisions + internal static ushort RegisterDelegate(Type componentType, string functionFullName, RemoteCallType remoteCallType, RemoteCallDelegate func, bool cmdRequiresAuthority = true) + { + // type+func so Inventory.RpcUse != Equipment.RpcUse + ushort hash = (ushort)(functionFullName.GetStableHashCode() & 0xFFFF); + + if (CheckIfDelegateExists(componentType, remoteCallType, func, hash)) + return hash; + + remoteCallDelegates[hash] = new Invoker + { + callType = remoteCallType, + componentType = componentType, + function = func, + cmdRequiresAuthority = cmdRequiresAuthority + }; + return hash; + } + + // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions + // need to pass componentType to support invoking on GameObjects with + // multiple components of same type with same remote call. + public static void RegisterCommand(Type componentType, string functionFullName, RemoteCallDelegate func, bool requiresAuthority) => + RegisterDelegate(componentType, functionFullName, RemoteCallType.Command, func, requiresAuthority); + + // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions + // need to pass componentType to support invoking on GameObjects with + // multiple components of same type with same remote call. + public static void RegisterRpc(Type componentType, string functionFullName, RemoteCallDelegate func) => + RegisterDelegate(componentType, functionFullName, RemoteCallType.ClientRpc, func); + + // to clean up tests + internal static void RemoveDelegate(ushort hash) => + remoteCallDelegates.Remove(hash); + + // note: no need to throw an error if not found. + // an attacker might just try to call a cmd with an rpc's hash etc. + // returning false is enough. + static bool GetInvokerForHash(ushort functionHash, RemoteCallType remoteCallType, out Invoker invoker) => + remoteCallDelegates.TryGetValue(functionHash, out invoker) && + invoker != null && + invoker.callType == remoteCallType; + + // InvokeCmd/Rpc Delegate can all use the same function here + internal static bool Invoke(ushort functionHash, RemoteCallType remoteCallType, NetworkReader reader, NetworkBehaviour component, NetworkConnectionToClient senderConnection = null) + { + // IMPORTANT: we check if the message's componentIndex component is + // actually of the right type. prevents attackers trying + // to invoke remote calls on wrong components. + if (GetInvokerForHash(functionHash, remoteCallType, out Invoker invoker) && + invoker.componentType.IsInstanceOfType(component)) + { + // invoke function on this component + invoker.function(component, reader, senderConnection); + return true; + } + return false; + } + + // check if the command 'requiresAuthority' which is set in the attribute + internal static bool CommandRequiresAuthority(ushort cmdHash) => + GetInvokerForHash(cmdHash, RemoteCallType.Command, out Invoker invoker) && + invoker.cmdRequiresAuthority; + + /// Gets the handler function by hash. Useful for profilers and debuggers. + public static RemoteCallDelegate GetDelegate(ushort functionHash) => + remoteCallDelegates.TryGetValue(functionHash, out Invoker invoker) + ? invoker.function + : null; + } +} + diff --git a/UnityProject/Assets/Mirror/Runtime/RemoteCallHelper.cs.meta b/UnityProject/Assets/Mirror/Runtime/RemoteCalls.cs.meta similarity index 79% rename from UnityProject/Assets/Mirror/Runtime/RemoteCallHelper.cs.meta rename to UnityProject/Assets/Mirror/Runtime/RemoteCalls.cs.meta index bece114..62b3288 100644 --- a/UnityProject/Assets/Mirror/Runtime/RemoteCallHelper.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/RemoteCalls.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/SnapshotInterpolation.meta b/UnityProject/Assets/Mirror/Runtime/SnapshotInterpolation.meta new file mode 100644 index 0000000..85ac9a9 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/SnapshotInterpolation.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4468e736f87964eaebb9d55fc3e132f7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/SnapshotInterpolation/Snapshot.cs b/UnityProject/Assets/Mirror/Runtime/SnapshotInterpolation/Snapshot.cs new file mode 100644 index 0000000..6b8ba8a --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/SnapshotInterpolation/Snapshot.cs @@ -0,0 +1,17 @@ +// Snapshot interface so we can reuse it for all kinds of systems. +// for example, NetworkTransform, NetworkRigidbody, CharacterController etc. +// NOTE: we use '' and 'where T : Snapshot' to avoid boxing. +// List would cause allocations through boxing. +namespace Mirror +{ + public interface Snapshot + { + // the remote timestamp (when it was sent by the remote) + double remoteTime { get; set; } + + // the local timestamp (when it was received on our end) + // technically not needed for basic snapshot interpolation. + // only for dynamic buffer time adjustment. + double localTime { get; set; } + } +} diff --git a/UnityProject/Assets/Mirror/Runtime/SnapshotInterpolation/Snapshot.cs.meta b/UnityProject/Assets/Mirror/Runtime/SnapshotInterpolation/Snapshot.cs.meta new file mode 100644 index 0000000..24eedd7 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/SnapshotInterpolation/Snapshot.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 12afea28fdb94154868a0a3b7a9df55b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/SnapshotInterpolation/SnapshotInterpolation.cs b/UnityProject/Assets/Mirror/Runtime/SnapshotInterpolation/SnapshotInterpolation.cs new file mode 100644 index 0000000..e526351 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/SnapshotInterpolation/SnapshotInterpolation.cs @@ -0,0 +1,291 @@ +// snapshot interpolation V2 by mischa +// +// Unity independent to be engine agnostic & easy to test. +// boxing: in C#, uses does not box! passing the interface would box! +// +// credits: +// glenn fiedler: https://gafferongames.com/post/snapshot_interpolation/ +// fholm: netcode streams +// fakebyte: standard deviation for dynamic adjustment +// ninjakicka: math & debugging +using System; +using System.Collections.Generic; + +namespace Mirror +{ + public static class SortedListExtensions + { + // removes the first 'amount' elements from the sorted list + public static void RemoveRange(this SortedList list, int amount) + { + // remove the first element 'amount' times. + // handles -1 and > count safely. + for (int i = 0; i < amount && i < list.Count; ++i) + list.RemoveAt(0); + } + } + + public static class SnapshotInterpolation + { + // calculate timescale for catch-up / slow-down + // note that negative threshold should be <0. + // caller should verify (i.e. Unity OnValidate). + // improves branch prediction. + public static double Timescale( + double drift, // how far we are off from bufferTime + double catchupSpeed, // in % [0,1] + double slowdownSpeed, // in % [0,1] + double catchupNegativeThreshold, // in % of sendInteral (careful, we may run out of snapshots) + double catchupPositiveThreshold) // in % of sendInterval) + { + // if the drift time is too large, it means we are behind more time. + // so we need to speed up the timescale. + // note the threshold should be sendInterval * catchupThreshold. + if (drift > catchupPositiveThreshold) + { + // localTimeline += 0.001; // too simple, this would ping pong + return 1 + catchupSpeed; // n% faster + } + + // if the drift time is too small, it means we are ahead of time. + // so we need to slow down the timescale. + // note the threshold should be sendInterval * catchupThreshold. + if (drift < catchupNegativeThreshold) + { + // localTimeline -= 0.001; // too simple, this would ping pong + return 1 - slowdownSpeed; // n% slower + } + + // keep constant timescale while within threshold. + // this way we have perfectly smooth speed most of the time. + return 1; + } + + // calculate dynamic buffer time adjustment + public static double DynamicAdjustment( + double sendInterval, + double jitterStandardDeviation, + double dynamicAdjustmentTolerance) + { + // jitter is equal to delivery time standard variation. + // delivery time is made up of 'sendInterval+jitter'. + // .Average would be dampened by the constant sendInterval + // .StandardDeviation is the changes in 'jitter' that we want + // so add it to send interval again. + double intervalWithJitter = sendInterval + jitterStandardDeviation; + + // how many multiples of sendInterval is that? + // we want to convert to bufferTimeMultiplier later. + double multiples = intervalWithJitter / sendInterval; + + // add the tolerance + double safezone = multiples + dynamicAdjustmentTolerance; + // UnityEngine.Debug.Log($"sendInterval={sendInterval:F3} jitter std={jitterStandardDeviation:F3} => that is ~{multiples:F1} x sendInterval + {dynamicAdjustmentTolerance} => dynamic bufferTimeMultiplier={safezone}"); + return safezone; + } + + // call this for every received snapshot. + // adds / inserts it to the list & initializes local time if needed. + public static void Insert( + SortedList buffer, // snapshot buffer + T snapshot, // the newly received snapshot + ref double localTimeline, // local interpolation time based on server time + ref double localTimescale, // timeline multiplier to apply catchup / slowdown over time + float sendInterval, // for debugging + double bufferTime, // offset for buffering + double catchupSpeed, // in % [0,1] + double slowdownSpeed, // in % [0,1] + ref ExponentialMovingAverage driftEma, // for catchup / slowdown + float catchupNegativeThreshold, // in % of sendInteral (careful, we may run out of snapshots) + float catchupPositiveThreshold, // in % of sendInterval + ref ExponentialMovingAverage deliveryTimeEma) // for dynamic buffer time adjustment + where T : Snapshot + { + // first snapshot? + // initialize local timeline. + // we want it to be behind by 'offset'. + // + // note that the first snapshot may be a lagging packet. + // so we would always be behind by that lag. + // this requires catchup later. + if (buffer.Count == 0) + localTimeline = snapshot.remoteTime - bufferTime; + + // insert into the buffer. + // + // note that we might insert it between our current interpolation + // which is fine, it adds another data point for accuracy. + // + // note that insert may be called twice for the same key. + // by default, this would throw. + // need to handle it silently. + if (!buffer.ContainsKey(snapshot.remoteTime)) + { + buffer.Add(snapshot.remoteTime, snapshot); + + // dynamic buffer adjustment needs delivery interval jitter + if (buffer.Count >= 2) + { + // note that this is not entirely accurate for scrambled inserts. + // + // we always use the last two, not what we just inserted + // even if we were to use the diff for what we just inserted, + // a scrambled insert would still not be 100% accurate: + // => assume a buffer of AC, with delivery time C-A + // => we then insert B, with delivery time B-A + // => but then technically the first C-A wasn't correct, + // as it would have to be C-B + // + // in practice, scramble is rare and won't make much difference + double previousLocalTime = buffer.Values[buffer.Count - 2].localTime; + double lastestLocalTime = buffer.Values[buffer.Count - 1].localTime; + + // this is the delivery time since last snapshot + double localDeliveryTime = lastestLocalTime - previousLocalTime; + + // feed the local delivery time to the EMA. + // this is what the original stream did too. + // our final dynamic buffer adjustment is different though. + // we use standard deviation instead of average. + deliveryTimeEma.Add(localDeliveryTime); + } + + // adjust timescale to catch up / slow down after each insertion + // because that is when we add new values to our EMA. + + // we want localTimeline to be about 'bufferTime' behind. + // for that, we need the delivery time EMA. + // snapshots may arrive out of order, we can not use last-timeline. + // we need to use the inserted snapshot's time - timeline. + double latestRemoteTime = snapshot.remoteTime; + double timeDiff = latestRemoteTime - localTimeline; + + // next, calculate average of a few seconds worth of timediffs. + // this gives smoother results. + // + // to calculate the average, we could simply loop through the + // last 'n' seconds worth of timediffs, but: + // - our buffer may only store a few snapshots (bufferTime) + // - looping through seconds worth of snapshots every time is + // expensive + // + // to solve this, we use an exponential moving average. + // https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average + // which is basically fancy math to do the same but faster. + // additionally, it allows us to look at more timeDiff values + // than we sould have access to in our buffer :) + driftEma.Add(timeDiff); + + // next up, calculate how far we are currently away from bufferTime + double drift = driftEma.Value - bufferTime; + + // convert relative thresholds to absolute values based on sendInterval + double absoluteNegativeThreshold = sendInterval * catchupNegativeThreshold; + double absolutePositiveThreshold = sendInterval * catchupPositiveThreshold; + + // next, set localTimescale to catchup consistently in Update(). + // we quantize between default/catchup/slowdown, + // this way we have 'default' speed most of the time(!). + // and only catch up / slow down for a little bit occasionally. + // a consistent multiplier would never be exactly 1.0. + localTimescale = Timescale(drift, catchupSpeed, slowdownSpeed, absoluteNegativeThreshold, absolutePositiveThreshold); + + // debug logging + // UnityEngine.Debug.Log($"sendInterval={sendInterval:F3} bufferTime={bufferTime:F3} drift={drift:F3} driftEma={driftEma.Value:F3} timescale={localTimescale:F3} deliveryIntervalEma={deliveryTimeEma.Value:F3}"); + } + } + + // sample snapshot buffer to find the pair around the given time. + // returns indices so we can use it with RemoveRange to clear old snaps. + // make sure to use use buffer.Values[from/to], not buffer[from/to]. + // make sure to only call this is we have > 0 snapshots. + public static void Sample( + SortedList buffer, // snapshot buffer + double localTimeline, // local interpolation time based on server time + out int from, // the snapshot <= time + out int to, // the snapshot >= time + out double t) // interpolation factor + where T : Snapshot + { + from = -1; + to = -1; + t = 0; + + // sample from [0,count-1] so we always have two at 'i' and 'i+1'. + for (int i = 0; i < buffer.Count - 1; ++i) + { + // is local time between these two? + T first = buffer.Values[i]; + T second = buffer.Values[i + 1]; + if (localTimeline >= first.remoteTime && + localTimeline <= second.remoteTime) + { + // use these two snapshots + from = i; + to = i + 1; + t = Mathd.InverseLerp(first.remoteTime, second.remoteTime, localTimeline); + return; + } + } + + // didn't find two snapshots around local time. + // so pick either the first or last, depending on which is closer. + + // oldest snapshot ahead of local time? + if (buffer.Values[0].remoteTime > localTimeline) + { + from = to = 0; + t = 0; + } + // otherwise initialize both to the last one + else + { + from = to = buffer.Count - 1; + t = 0; + } + } + + // update time, sample, clear old. + // call this every update. + // returns true if there is anything to apply (requires at least 1 snap) + public static bool Step( + SortedList buffer, // snapshot buffer + double deltaTime, // engine delta time (unscaled) + ref double localTimeline, // local interpolation time based on server time + double localTimescale, // catchup / slowdown is applied to time every update + Func Interpolate, // interpolates snapshot between two snapshots + out T computed) + where T : Snapshot + { + computed = default; + + // nothing to do if there are no snapshots at all yet + if (buffer.Count == 0) + return false; + + // move local forward in time, scaled with catchup / slowdown applied + localTimeline += deltaTime * localTimescale; + + // sample snapshot buffer at local interpolation time + Sample(buffer, localTimeline, out int from, out int to, out double t); + + // now interpolate between from & to (clamped) + T fromSnap = buffer.Values[from]; + T toSnap = buffer.Values[to]; + computed = Interpolate(fromSnap, toSnap, t); + // UnityEngine.Debug.Log($"step from: {from} to {to}"); + + // remove older snapshots that we definitely don't need anymore. + // after(!) using the indices. + // + // if we have 3 snapshots and we are between 2nd and 3rd: + // from = 1, to = 2 + // then we need to remove the first one, which is exactly 'from'. + // because 'from-1' = 0 would remove none. + buffer.RemoveRange(from); + + // return the interpolated snapshot + return true; + } + } +} diff --git a/UnityProject/Assets/Mirror/Runtime/SnapshotInterpolation/SnapshotInterpolation.cs.meta b/UnityProject/Assets/Mirror/Runtime/SnapshotInterpolation/SnapshotInterpolation.cs.meta new file mode 100644 index 0000000..244c5fb --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/SnapshotInterpolation/SnapshotInterpolation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 72c16070d85334011853813488ab1431 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/SyncDictionary.cs b/UnityProject/Assets/Mirror/Runtime/SyncDictionary.cs index f3a0b5a..c63077c 100644 --- a/UnityProject/Assets/Mirror/Runtime/SyncDictionary.cs +++ b/UnityProject/Assets/Mirror/Runtime/SyncDictionary.cs @@ -1,10 +1,9 @@ using System.Collections; using System.Collections.Generic; -using JetBrains.Annotations; namespace Mirror { - public class SyncIDictionary : IDictionary, SyncObject, IReadOnlyDictionary + public class SyncIDictionary : SyncObject, IDictionary, IReadOnlyDictionary { public delegate void SyncDictionaryChanged(Operation op, TKey key, TValue item); @@ -29,14 +28,20 @@ namespace Mirror internal TValue item; } + // list of changes. + // -> insert/delete/clear is only ONE change + // -> changing the same slot 10x caues 10 changes. + // -> note that this grows until next sync(!) + // TODO Dictionary to avoid ever growing changes / redundant changes! readonly List changes = new List(); + // how many changes we need to ignore // this is needed because when we initialize the list, // we might later receive changes that have already been applied // so we need to skip them int changesAhead; - public void Reset() + public override void Reset() { IsReadOnly = false; changes.Clear(); @@ -44,8 +49,6 @@ namespace Mirror objects.Clear(); } - public bool IsDirty => changes.Count > 0; - public ICollection Keys => objects.Keys; public ICollection Values => objects.Values; @@ -56,7 +59,7 @@ namespace Mirror // throw away all the changes // this should be called after a successful sync - public void Flush() => changes.Clear(); + public override void ClearChanges() => changes.Clear(); public SyncIDictionary(IDictionary objects) { @@ -77,12 +80,16 @@ namespace Mirror item = item }; - changes.Add(change); + if (IsRecording()) + { + changes.Add(change); + OnDirty?.Invoke(); + } Callback?.Invoke(op, key, item); } - public void OnSerializeAll(NetworkWriter writer) + public override void OnSerializeAll(NetworkWriter writer) { // if init, write the full list content writer.WriteUInt((uint)objects.Count); @@ -100,7 +107,7 @@ namespace Mirror writer.WriteUInt((uint)changes.Count); } - public void OnSerializeDelta(NetworkWriter writer) + public override void OnSerializeDelta(NetworkWriter writer) { // write all the queued up changes writer.WriteUInt((uint)changes.Count); @@ -124,7 +131,7 @@ namespace Mirror } } - public void OnDeserializeAll(NetworkReader reader) + public override void OnDeserializeAll(NetworkReader reader) { // This list can now only be modified by synchronization IsReadOnly = true; @@ -148,7 +155,7 @@ namespace Mirror changesAhead = (int)reader.ReadUInt(); } - public void OnDeserializeDelta(NetworkReader reader) + public override void OnDeserializeDelta(NetworkReader reader) { // This list can now only be modified by synchronization IsReadOnly = true; @@ -257,7 +264,7 @@ namespace Mirror return TryGetValue(item.Key, out TValue val) && EqualityComparer.Default.Equals(val, item.Value); } - public void CopyTo([NotNull] KeyValuePair[] array, int arrayIndex) + public void CopyTo(KeyValuePair[] array, int arrayIndex) { if (arrayIndex < 0 || arrayIndex > array.Length) { @@ -295,9 +302,9 @@ namespace Mirror { public SyncDictionary() : base(new Dictionary()) {} public SyncDictionary(IEqualityComparer eq) : base(new Dictionary(eq)) {} + public SyncDictionary(IDictionary d) : base(new Dictionary(d)) {} public new Dictionary.ValueCollection Values => ((Dictionary)objects).Values; public new Dictionary.KeyCollection Keys => ((Dictionary)objects).Keys; public new Dictionary.Enumerator GetEnumerator() => ((Dictionary)objects).GetEnumerator(); - } } diff --git a/UnityProject/Assets/Mirror/Runtime/SyncDictionary.cs.meta b/UnityProject/Assets/Mirror/Runtime/SyncDictionary.cs.meta index 9c5728b..1c20b57 100644 --- a/UnityProject/Assets/Mirror/Runtime/SyncDictionary.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/SyncDictionary.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/SyncList.cs b/UnityProject/Assets/Mirror/Runtime/SyncList.cs index 1ac230e..9eb0a59 100644 --- a/UnityProject/Assets/Mirror/Runtime/SyncList.cs +++ b/UnityProject/Assets/Mirror/Runtime/SyncList.cs @@ -4,27 +4,7 @@ using System.Collections.Generic; namespace Mirror { - // Deprecated 2020-10-02 - [Obsolete("Use SyncList instead")] - public class SyncListString : SyncList {} - - // Deprecated 2020-10-02 - [Obsolete("Use SyncList instead")] - public class SyncListFloat : SyncList {} - - // Deprecated 2020-10-02 - [Obsolete("Use SyncList instead")] - public class SyncListInt : SyncList {} - - // Deprecated 2020-10-02 - [Obsolete("Use SyncList instead")] - public class SyncListUInt : SyncList {} - - // Deprecated 2020-10-02 - [Obsolete("Use SyncList instead")] - public class SyncListBool : SyncList {} - - public class SyncList : IList, IReadOnlyList, SyncObject + public class SyncList : SyncObject, IList, IReadOnlyList { public delegate void SyncListChanged(Operation op, int itemIndex, T oldItem, T newItem); @@ -51,7 +31,12 @@ namespace Mirror internal T item; } + // list of changes. + // -> insert/delete/clear is only ONE change + // -> changing the same slot 10x caues 10 changes. + // -> note that this grows until next sync(!) readonly List changes = new List(); + // how many changes we need to ignore // this is needed because when we initialize the list, // we might later receive changes that have already been applied @@ -72,13 +57,11 @@ namespace Mirror this.objects = objects; } - public bool IsDirty => changes.Count > 0; - // throw away all the changes // this should be called after a successful sync - public void Flush() => changes.Clear(); + public override void ClearChanges() => changes.Clear(); - public void Reset() + public override void Reset() { IsReadOnly = false; changes.Clear(); @@ -100,12 +83,16 @@ namespace Mirror item = newItem }; - changes.Add(change); + if (IsRecording()) + { + changes.Add(change); + OnDirty?.Invoke(); + } Callback?.Invoke(op, itemIndex, oldItem, newItem); } - public void OnSerializeAll(NetworkWriter writer) + public override void OnSerializeAll(NetworkWriter writer) { // if init, write the full list content writer.WriteUInt((uint)objects.Count); @@ -123,7 +110,7 @@ namespace Mirror writer.WriteUInt((uint)changes.Count); } - public void OnSerializeDelta(NetworkWriter writer) + public override void OnSerializeDelta(NetworkWriter writer) { // write all the queued up changes writer.WriteUInt((uint)changes.Count); @@ -155,7 +142,7 @@ namespace Mirror } } - public void OnDeserializeAll(NetworkReader reader) + public override void OnDeserializeAll(NetworkReader reader) { // This list can now only be modified by synchronization IsReadOnly = true; @@ -178,7 +165,7 @@ namespace Mirror changesAhead = (int)reader.ReadUInt(); } - public void OnDeserializeDelta(NetworkReader reader) + public override void OnDeserializeDelta(NetworkReader reader) { // This list can now only be modified by synchronization IsReadOnly = true; diff --git a/UnityProject/Assets/Mirror/Runtime/SyncList.cs.meta b/UnityProject/Assets/Mirror/Runtime/SyncList.cs.meta index a1bffbc..088ef1e 100644 --- a/UnityProject/Assets/Mirror/Runtime/SyncList.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/SyncList.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/SyncObject.cs b/UnityProject/Assets/Mirror/Runtime/SyncObject.cs index 48bd5f8..4210b52 100644 --- a/UnityProject/Assets/Mirror/Runtime/SyncObject.cs +++ b/UnityProject/Assets/Mirror/Runtime/SyncObject.cs @@ -1,28 +1,47 @@ +using System; + namespace Mirror { /// SyncObjects sync state between server and client. E.g. SyncLists. - public interface SyncObject + // SyncObject should be a class (instead of an interface) for a few reasons: + // * NetworkBehaviour stores SyncObjects in a list. structs would be a copy + // and OnSerialize would use the copy instead of the original struct. + // * Obsolete functions like Flush() don't need to be defined by each type + // * OnDirty/IsRecording etc. default functions can be defined once here + // for example, handling 'OnDirty wasn't initialized' with a default + // function that throws an exception will be useful for SyncVar + public abstract class SyncObject { - /// True if there are changes since the last flush - bool IsDirty { get; } + /// Used internally to set owner NetworkBehaviour's dirty mask bit when changed. + public Action OnDirty; + + /// Used internally to check if we are currently tracking changes. + // prevents ever growing .changes lists: + // if a monster has no observers but we keep modifying a SyncObject, + // then the changes would never be flushed and keep growing, + // because OnSerialize isn't called without observers. + // => Func so we can set it to () => observers.Count > 0 + // without depending on NetworkComponent/NetworkIdentity here. + // => virtual so it simply always records by default + public Func IsRecording = () => true; /// Discard all the queued changes // Consider the object fully synchronized with clients - void Flush(); + public abstract void ClearChanges(); /// Write a full copy of the object - void OnSerializeAll(NetworkWriter writer); + public abstract void OnSerializeAll(NetworkWriter writer); /// Write the changes made to the object since last sync - void OnSerializeDelta(NetworkWriter writer); + public abstract void OnSerializeDelta(NetworkWriter writer); /// Reads a full copy of the object - void OnDeserializeAll(NetworkReader reader); + public abstract void OnDeserializeAll(NetworkReader reader); /// Reads the changes made to the object since last sync - void OnDeserializeDelta(NetworkReader reader); + public abstract void OnDeserializeDelta(NetworkReader reader); /// Resets the SyncObject so that it can be re-used - void Reset(); + public abstract void Reset(); } } diff --git a/UnityProject/Assets/Mirror/Runtime/SyncObject.cs.meta b/UnityProject/Assets/Mirror/Runtime/SyncObject.cs.meta index 20fc035..736c651 100644 --- a/UnityProject/Assets/Mirror/Runtime/SyncObject.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/SyncObject.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/SyncSet.cs b/UnityProject/Assets/Mirror/Runtime/SyncSet.cs index 29dc2ed..94e353f 100644 --- a/UnityProject/Assets/Mirror/Runtime/SyncSet.cs +++ b/UnityProject/Assets/Mirror/Runtime/SyncSet.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; namespace Mirror { - public class SyncSet : ISet, SyncObject + public class SyncSet : SyncObject, ISet { public delegate void SyncSetChanged(Operation op, T item); @@ -27,7 +27,13 @@ namespace Mirror internal T item; } + // list of changes. + // -> insert/delete/clear is only ONE change + // -> changing the same slot 10x caues 10 changes. + // -> note that this grows until next sync(!) + // TODO Dictionary to avoid ever growing changes / redundant changes! readonly List changes = new List(); + // how many changes we need to ignore // this is needed because when we initialize the list, // we might later receive changes that have already been applied @@ -39,7 +45,7 @@ namespace Mirror this.objects = objects; } - public void Reset() + public override void Reset() { IsReadOnly = false; changes.Clear(); @@ -47,11 +53,9 @@ namespace Mirror objects.Clear(); } - public bool IsDirty => changes.Count > 0; - // throw away all the changes // this should be called after a successful sync - public void Flush() => changes.Clear(); + public override void ClearChanges() => changes.Clear(); void AddOperation(Operation op, T item) { @@ -66,14 +70,18 @@ namespace Mirror item = item }; - changes.Add(change); + if (IsRecording()) + { + changes.Add(change); + OnDirty?.Invoke(); + } Callback?.Invoke(op, item); } void AddOperation(Operation op) => AddOperation(op, default); - public void OnSerializeAll(NetworkWriter writer) + public override void OnSerializeAll(NetworkWriter writer) { // if init, write the full list content writer.WriteUInt((uint)objects.Count); @@ -90,7 +98,7 @@ namespace Mirror writer.WriteUInt((uint)changes.Count); } - public void OnSerializeDelta(NetworkWriter writer) + public override void OnSerializeDelta(NetworkWriter writer) { // write all the queued up changes writer.WriteUInt((uint)changes.Count); @@ -116,7 +124,7 @@ namespace Mirror } } - public void OnDeserializeAll(NetworkReader reader) + public override void OnDeserializeAll(NetworkReader reader) { // This list can now only be modified by synchronization IsReadOnly = true; @@ -139,7 +147,7 @@ namespace Mirror changesAhead = (int)reader.ReadUInt(); } - public void OnDeserializeDelta(NetworkReader reader) + public override void OnDeserializeDelta(NetworkReader reader) { // This list can now only be modified by synchronization IsReadOnly = true; diff --git a/UnityProject/Assets/Mirror/Runtime/SyncSet.cs.meta b/UnityProject/Assets/Mirror/Runtime/SyncSet.cs.meta index 8f15ffc..6eeef1c 100644 --- a/UnityProject/Assets/Mirror/Runtime/SyncSet.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/SyncSet.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/SyncVar.cs b/UnityProject/Assets/Mirror/Runtime/SyncVar.cs new file mode 100644 index 0000000..5c1790b --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/SyncVar.cs @@ -0,0 +1,148 @@ +// SyncVar to make [SyncVar] weaving easier. +// +// we can possibly move a lot of complex logic out of weaver: +// * set dirty bit +// * calling the hook +// * hook guard in host mode +// * GameObject/NetworkIdentity internal netId storage +// +// here is the plan: +// 1. develop SyncVar along side [SyncVar] +// 2. internally replace [SyncVar]s with SyncVar +// 3. eventually obsolete [SyncVar] +// +// downsides: +// - generic types don't show in Unity Inspector +// +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace Mirror +{ + // 'class' so that we can track it in SyncObjects list, and iterate it for + // de/serialization. + [Serializable] + public class SyncVar : SyncObject, IEquatable + { + // Unity 2020+ can show [SerializeField] in inspector. + // (only if SyncVar isn't readonly though) + [SerializeField] T _Value; + + // Value property with hooks + // virtual for SyncFieldNetworkIdentity netId trick etc. + public virtual T Value + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _Value; + set + { + // only if value changed. otherwise don't dirty/hook. + // we have .Equals(T), simply reuse it here. + if (!Equals(value)) + { + // set value, set dirty bit + T old = _Value; + _Value = value; + OnDirty(); + + // Value.set calls the hook if changed. + // calling Value.set from within the hook would call the + // hook again and deadlock. prevent it with hookGuard. + // (see test: Hook_Set_DoesntDeadlock) + if (!hookGuard && + // original [SyncVar] only calls hook on clients. + // let's keep it for consistency for now + // TODO remove check & dependency in the future. + // use isClient/isServer in the hook instead. + NetworkClient.active) + { + hookGuard = true; + InvokeCallback(old, value); + hookGuard = false; + } + } + } + } + + // OnChanged Callback. + // named 'Callback' for consistency with SyncList etc. + // needs to be public so we can assign it in OnStartClient. + // (ctor passing doesn't work, it can only take static functions) + // assign via: field.Callback += ...! + public event Action Callback; + + // OnCallback is responsible for calling the callback. + // this is necessary for inheriting classes like SyncVarGameObject, + // where the netIds should be converted to GOs and call the GO hook. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected virtual void InvokeCallback(T oldValue, T newValue) => + Callback?.Invoke(oldValue, newValue); + + // Value.set calls the hook if changed. + // calling Value.set from within the hook would call the hook again and + // deadlock. prevent it with a simple 'are we inside the hook' bool. + bool hookGuard; + + public override void ClearChanges() {} + public override void Reset() {} + + // ctor from value and OnChanged hook. + // it was always called 'hook'. let's keep naming for convenience. + public SyncVar(T value) + { + // recommend explicit GameObject, NetworkIdentity, NetworkBehaviour + // with persistent netId method + if (this is SyncVar) + Debug.LogWarning($"Use explicit {nameof(SyncVarGameObject)} class instead of {nameof(SyncVar)}. It stores netId internally for persistence."); + + if (this is SyncVar) + Debug.LogWarning($"Use explicit {nameof(SyncVarNetworkIdentity)} class instead of {nameof(SyncVar)}. It stores netId internally for persistence."); + + if (this is SyncVar) + Debug.LogWarning($"Use explicit SyncVarNetworkBehaviour class instead of {nameof(SyncVar)}. It stores netId internally for persistence."); + + _Value = value; + } + + // NOTE: copy ctor is unnecessary. + // SyncVars are readonly and only initialized by 'Value' once. + + // implicit conversion: int value = SyncVar + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator T(SyncVar field) => field.Value; + + // implicit conversion: SyncVar = value + // even if SyncVar is readonly, it's still useful: SyncVar = 1; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator SyncVar(T value) => new SyncVar(value); + + // serialization (use .Value instead of _Value so hook is called!) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void OnSerializeAll(NetworkWriter writer) => writer.Write(Value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void OnSerializeDelta(NetworkWriter writer) => writer.Write(Value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void OnDeserializeAll(NetworkReader reader) => Value = reader.Read(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void OnDeserializeDelta(NetworkReader reader) => Value = reader.Read(); + + // IEquatable should compare Value. + // SyncVar should act invisibly like [SyncVar] before. + // this way we can do SyncVar health == 0 etc. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(T other) => + // from NetworkBehaviour.SyncVarEquals: + // EqualityComparer method avoids allocations. + // otherwise would have to be :IEquatable (not all structs are) + EqualityComparer.Default.Equals(Value, other); + + // ToString should show Value. + // SyncVar should act invisibly like [SyncVar] before. + public override string ToString() => Value.ToString(); + } +} diff --git a/UnityProject/Assets/Mirror/Runtime/SyncVar.cs.meta b/UnityProject/Assets/Mirror/Runtime/SyncVar.cs.meta new file mode 100644 index 0000000..fffb472 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/SyncVar.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5e87cb681af8459fbbb1f467e1c7632c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/SyncVarGameObject.cs b/UnityProject/Assets/Mirror/Runtime/SyncVarGameObject.cs new file mode 100644 index 0000000..81961c2 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/SyncVarGameObject.cs @@ -0,0 +1,145 @@ +// persistent GameObject SyncField which stores .netId internally. +// this is necessary for cases like a player's target. +// the target might run in and out of visibility range and become 'null'. +// but the 'netId' remains and will always point to the monster if around. +// +// NOTE that SyncFieldNetworkIdentity is faster (no .gameObject/GetComponent<>)! +// +// original Weaver code with netId workaround: +/* + // USER: + [SyncVar(hook = "OnTargetChanged")] + public GameObject target; + + // WEAVER: + private uint ___targetNetId; + + public GameObject Networktarget + { + get + { + return GetSyncVarGameObject(___targetNetId, ref target); + } + [param: In] + set + { + if (!NetworkBehaviour.SyncVarGameObjectEqual(value, ___targetNetId)) + { + GameObject networktarget = Networktarget; + SetSyncVarGameObject(value, ref target, 1uL, ref ___targetNetId); + if (NetworkServer.localClientActive && !GetSyncVarHookGuard(1uL)) + { + SetSyncVarHookGuard(1uL, value: true); + OnTargetChanged(networktarget, value); + SetSyncVarHookGuard(1uL, value: false); + } + } + } + } + + private void OnTargetChanged(GameObject old, GameObject value) + { + } +*/ +using System; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace Mirror +{ + // SyncField only stores an uint netId. + // while providing .spawned lookup for convenience. + // NOTE: server always knows all spawned. consider caching the field again. + public class SyncVarGameObject : SyncVar + { + // .spawned lookup from netId overwrites base uint .Value + public new GameObject Value + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetGameObject(base.Value); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => base.Value = GetNetId(value); + } + + // OnChanged Callback is for . + // Let's also have one for + public new event Action Callback; + + // overwrite CallCallback to use the GameObject version instead + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override void InvokeCallback(uint oldValue, uint newValue) => + Callback?.Invoke(GetGameObject(oldValue), GetGameObject(newValue)); + + // ctor + // 'value = null' so we can do: + // SyncVarGameObject = new SyncVarGameObject() + // instead of + // SyncVarGameObject = new SyncVarGameObject(null); + public SyncVarGameObject(GameObject value = null) + : base(GetNetId(value)) {} + + // helper function to get netId from GameObject (if any) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static uint GetNetId(GameObject go) + { + if (go != null) + { + NetworkIdentity identity = go.GetComponent(); + return identity != null ? identity.netId : 0; + } + return 0; + } + + // helper function to get GameObject from netId (if spawned) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static GameObject GetGameObject(uint netId) + { + NetworkIdentity spawned = Utils.GetSpawnedInServerOrClient(netId); + return spawned != null ? spawned.gameObject : null; + } + + // implicit conversion: GameObject value = SyncFieldGameObject + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator GameObject(SyncVarGameObject field) => field.Value; + + // implicit conversion: SyncFieldGameObject = value + // even if SyncField is readonly, it's still useful: SyncFieldGameObject = target; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator SyncVarGameObject(GameObject value) => new SyncVarGameObject(value); + + // == operator for comparisons like Player.target==monster + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(SyncVarGameObject a, SyncVarGameObject b) => + a.Value == b.Value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(SyncVarGameObject a, SyncVarGameObject b) => !(a == b); + + // NOTE: overloading all == operators blocks '== null' checks with an + // "ambiguous invocation" error. that's good. this way user code like + // "player.target == null" won't compile instead of silently failing! + + // == operator for comparisons like Player.target==monster + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(SyncVarGameObject a, GameObject b) => + a.Value == b; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(SyncVarGameObject a, GameObject b) => !(a == b); + + // == operator for comparisons like Player.target==monster + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(GameObject a, SyncVarGameObject b) => + a == b.Value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(GameObject a, SyncVarGameObject b) => !(a == b); + + // if we overwrite == operators, we also need to overwrite .Equals. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) => obj is SyncVarGameObject value && this == value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() => Value.GetHashCode(); + } +} diff --git a/UnityProject/Assets/Mirror/Runtime/SyncVarGameObject.cs.meta b/UnityProject/Assets/Mirror/Runtime/SyncVarGameObject.cs.meta new file mode 100644 index 0000000..4e924f0 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/SyncVarGameObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 84da90dae05442e3a149753c9b25ae98 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/SyncVarNetworkBehaviour.cs b/UnityProject/Assets/Mirror/Runtime/SyncVarNetworkBehaviour.cs new file mode 100644 index 0000000..623b890 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/SyncVarNetworkBehaviour.cs @@ -0,0 +1,168 @@ +// persistent NetworkBehaviour SyncField which stores netId and component index. +// this is necessary for cases like a player's target. +// the target might run in and out of visibility range and become 'null'. +// but the 'netId' remains and will always point to the monster if around. +// (we also store the component index because GameObject can have multiple +// NetworkBehaviours of same type) +// +// original Weaver code was broken because it didn't store by netId. +using System; +using System.Runtime.CompilerServices; + +namespace Mirror +{ + // SyncField needs an uint netId and a byte componentIndex. + // we use an ulong SyncField internally to store both. + // while providing .spawned lookup for convenience. + // NOTE: server always knows all spawned. consider caching the field again. + // to support abstract NetworkBehaviour and classes inheriting from it. + // => hooks can be OnHook(Monster, Monster) instead of OnHook(NB, NB) + // => implicit cast can be to/from Monster instead of only NetworkBehaviour + // => Weaver needs explicit types for hooks too, not just OnHook(NB, NB) + public class SyncVarNetworkBehaviour : SyncVar + where T : NetworkBehaviour + { + // .spawned lookup from netId overwrites base uint .Value + public new T Value + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ULongToNetworkBehaviour(base.Value); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => base.Value = NetworkBehaviourToULong(value); + } + + // OnChanged Callback is for . + // Let's also have one for + public new event Action Callback; + + // overwrite CallCallback to use the NetworkIdentity version instead + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override void InvokeCallback(ulong oldValue, ulong newValue) => + Callback?.Invoke(ULongToNetworkBehaviour(oldValue), ULongToNetworkBehaviour(newValue)); + + // ctor + // 'value = null' so we can do: + // SyncVarNetworkBehaviour = new SyncVarNetworkBehaviour() + // instead of + // SyncVarNetworkBehaviour = new SyncVarNetworkBehaviour(null); + public SyncVarNetworkBehaviour(T value = null) + : base(NetworkBehaviourToULong(value)) {} + + // implicit conversion: NetworkBehaviour value = SyncFieldNetworkBehaviour + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator T(SyncVarNetworkBehaviour field) => field.Value; + + // implicit conversion: SyncFieldNetworkBehaviour = value + // even if SyncField is readonly, it's still useful: SyncFieldNetworkBehaviour = target; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator SyncVarNetworkBehaviour(T value) => new SyncVarNetworkBehaviour(value); + + // NOTE: overloading all == operators blocks '== null' checks with an + // "ambiguous invocation" error. that's good. this way user code like + // "player.target == null" won't compile instead of silently failing! + + // == operator for comparisons like Player.target==monster + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(SyncVarNetworkBehaviour a, SyncVarNetworkBehaviour b) => + a.Value == b.Value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(SyncVarNetworkBehaviour a, SyncVarNetworkBehaviour b) => !(a == b); + + // == operator for comparisons like Player.target==monster + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(SyncVarNetworkBehaviour a, NetworkBehaviour b) => + a.Value == b; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(SyncVarNetworkBehaviour a, NetworkBehaviour b) => !(a == b); + + // == operator for comparisons like Player.target==monster + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(SyncVarNetworkBehaviour a, T b) => + a.Value == b; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(SyncVarNetworkBehaviour a, T b) => !(a == b); + + // == operator for comparisons like Player.target==monster + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(NetworkBehaviour a, SyncVarNetworkBehaviour b) => + a == b.Value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(NetworkBehaviour a, SyncVarNetworkBehaviour b) => !(a == b); + + // == operator for comparisons like Player.target==monster + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(T a, SyncVarNetworkBehaviour b) => + a == b.Value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(T a, SyncVarNetworkBehaviour b) => !(a == b); + + // if we overwrite == operators, we also need to overwrite .Equals. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) => obj is SyncVarNetworkBehaviour value && this == value; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() => Value.GetHashCode(); + + // helper functions to get/set netId, componentIndex from ulong + // netId on the 4 left bytes. compIndex on the right most byte. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ulong Pack(uint netId, byte componentIndex) => + (ulong)netId << 32 | componentIndex; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void Unpack(ulong value, out uint netId, out byte componentIndex) + { + netId = (uint)(value >> 32); + componentIndex = (byte)(value & 0xFF); + } + + // helper function to find/get NetworkBehaviour to ulong (netId/compIndex) + static T ULongToNetworkBehaviour(ulong value) + { + // unpack ulong to netId, componentIndex + Unpack(value, out uint netId, out byte componentIndex); + + // find spawned NetworkIdentity by netId + NetworkIdentity identity = Utils.GetSpawnedInServerOrClient(netId); + + // get the nth component + return identity != null ? (T)identity.NetworkBehaviours[componentIndex] : null; + } + + static ulong NetworkBehaviourToULong(T value) + { + // pack netId, componentIndex to ulong + return value != null ? Pack(value.netId, (byte)value.ComponentIndex) : 0; + } + + // Serialize should only write 4+1 bytes, not 8 bytes ulong + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void OnSerializeAll(NetworkWriter writer) + { + Unpack(base.Value, out uint netId, out byte componentIndex); + writer.WriteUInt(netId); + writer.WriteByte(componentIndex); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void OnSerializeDelta(NetworkWriter writer) => + OnSerializeAll(writer); + + // Deserialize should only write 4+1 bytes, not 8 bytes ulong + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void OnDeserializeAll(NetworkReader reader) + { + uint netId = reader.ReadUInt(); + byte componentIndex = reader.ReadByte(); + base.Value = Pack(netId, componentIndex); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void OnDeserializeDelta(NetworkReader reader) => + OnDeserializeAll(reader); + } +} diff --git a/UnityProject/Assets/Mirror/Runtime/SyncVarNetworkBehaviour.cs.meta b/UnityProject/Assets/Mirror/Runtime/SyncVarNetworkBehaviour.cs.meta new file mode 100644 index 0000000..0ceab50 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/SyncVarNetworkBehaviour.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c0fff77f1a624ba8ad6e4bdef6c14a8b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/SyncVarNetworkIdentity.cs b/UnityProject/Assets/Mirror/Runtime/SyncVarNetworkIdentity.cs new file mode 100644 index 0000000..0f3fdf2 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/SyncVarNetworkIdentity.cs @@ -0,0 +1,118 @@ +// persistent NetworkIdentity SyncField which stores .netId internally. +// this is necessary for cases like a player's target. +// the target might run in and out of visibility range and become 'null'. +// but the 'netId' remains and will always point to the monster if around. +// +// original Weaver code with netId workaround: +/* + // USER: + [SyncVar(hook = "OnTargetChanged")] + public NetworkIdentity target; + + // WEAVER GENERATED: + private uint ___targetNetId; + + public NetworkIdentity Networktarget + { + get + { + return GetSyncVarNetworkIdentity(___targetNetId, ref target); + } + [param: In] + set + { + if (!SyncVarNetworkIdentityEqual(value, ___targetNetId)) + { + NetworkIdentity networktarget = Networktarget; + SetSyncVarNetworkIdentity(value, ref target, 1uL, ref ___targetNetId); + if (NetworkServer.localClientActive && !GetSyncVarHookGuard(1uL)) + { + SetSyncVarHookGuard(1uL, value: true); + OnTargetChanged(networktarget, value); + SetSyncVarHookGuard(1uL, value: false); + } + } + } + } +*/ +using System; +using System.Runtime.CompilerServices; + +namespace Mirror +{ + // SyncField only stores an uint netId. + // while providing .spawned lookup for convenience. + // NOTE: server always knows all spawned. consider caching the field again. + public class SyncVarNetworkIdentity : SyncVar + { + // .spawned lookup from netId overwrites base uint .Value + public new NetworkIdentity Value + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Utils.GetSpawnedInServerOrClient(base.Value); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => base.Value = value != null ? value.netId : 0; + } + + // OnChanged Callback is for . + // Let's also have one for + public new event Action Callback; + + // overwrite CallCallback to use the NetworkIdentity version instead + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override void InvokeCallback(uint oldValue, uint newValue) => + Callback?.Invoke(Utils.GetSpawnedInServerOrClient(oldValue), Utils.GetSpawnedInServerOrClient(newValue)); + + // ctor + // 'value = null' so we can do: + // SyncVarNetworkIdentity = new SyncVarNetworkIdentity() + // instead of + // SyncVarNetworkIdentity = new SyncVarNetworkIdentity(null); + public SyncVarNetworkIdentity(NetworkIdentity value = null) + : base(value != null ? value.netId : 0) {} + + // implicit conversion: NetworkIdentity value = SyncFieldNetworkIdentity + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator NetworkIdentity(SyncVarNetworkIdentity field) => field.Value; + + // implicit conversion: SyncFieldNetworkIdentity = value + // even if SyncField is readonly, it's still useful: SyncFieldNetworkIdentity = target; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator SyncVarNetworkIdentity(NetworkIdentity value) => new SyncVarNetworkIdentity(value); + + // NOTE: overloading all == operators blocks '== null' checks with an + // "ambiguous invocation" error. that's good. this way user code like + // "player.target == null" won't compile instead of silently failing! + + // == operator for comparisons like Player.target==monster + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(SyncVarNetworkIdentity a, SyncVarNetworkIdentity b) => + a.Value == b.Value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(SyncVarNetworkIdentity a, SyncVarNetworkIdentity b) => !(a == b); + + // == operator for comparisons like Player.target==monster + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(SyncVarNetworkIdentity a, NetworkIdentity b) => + a.Value == b; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(SyncVarNetworkIdentity a, NetworkIdentity b) => !(a == b); + + // == operator for comparisons like Player.target==monster + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(NetworkIdentity a, SyncVarNetworkIdentity b) => + a == b.Value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(NetworkIdentity a, SyncVarNetworkIdentity b) => !(a == b); + + // if we overwrite == operators, we also need to overwrite .Equals. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) => obj is SyncVarNetworkIdentity value && this == value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() => Value.GetHashCode(); + } +} diff --git a/UnityProject/Assets/Mirror/Runtime/SyncVarNetworkIdentity.cs.meta b/UnityProject/Assets/Mirror/Runtime/SyncVarNetworkIdentity.cs.meta new file mode 100644 index 0000000..53271d4 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/SyncVarNetworkIdentity.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1f9a6d4d2741477999ad9588261870fe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Transport.cs b/UnityProject/Assets/Mirror/Runtime/Transport.cs similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/Transport.cs rename to UnityProject/Assets/Mirror/Runtime/Transport.cs index 66ccdf8..22b0873 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Transport.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transport.cs @@ -30,24 +30,55 @@ namespace Mirror /// Abstract transport layer component public abstract class Transport : MonoBehaviour { + // common ////////////////////////////////////////////////////////////// /// The current transport used by Mirror. public static Transport activeTransport; /// Is this transport available in the current platform? public abstract bool Available(); + // client ////////////////////////////////////////////////////////////// /// Called by Transport when the client connected to the server. - public Action OnClientConnected = () => Debug.LogWarning("OnClientConnected called with no handler"); + public Action OnClientConnected; /// Called by Transport when the client received a message from the server. - public Action, int> OnClientDataReceived = (data, channel) => Debug.LogWarning("OnClientDataReceived called with no handler"); + public Action, int> OnClientDataReceived; + + /// Called by Transport when the client sent a message to the server. + // Transports are responsible for calling it because: + // - groups it together with OnReceived responsibility + // - allows transports to decide if anything was sent or not + // - allows transports to decide the actual used channel (i.e. tcp always sending reliable) + public Action, int> OnClientDataSent; /// Called by Transport when the client encountered an error. - public Action OnClientError = (error) => Debug.LogWarning("OnClientError called with no handler"); + public Action OnClientError; /// Called by Transport when the client disconnected from the server. - public Action OnClientDisconnected = () => Debug.LogWarning("OnClientDisconnected called with no handler"); + public Action OnClientDisconnected; + // server ////////////////////////////////////////////////////////////// + /// Called by Transport when a new client connected to the server. + public Action OnServerConnected; + + /// Called by Transport when the server received a message from a client. + public Action, int> OnServerDataReceived; + + /// Called by Transport when the server sent a message to a client. + // Transports are responsible for calling it because: + // - groups it together with OnReceived responsibility + // - allows transports to decide if anything was sent or not + // - allows transports to decide the actual used channel (i.e. tcp always sending reliable) + public Action, int> OnServerDataSent; + + /// Called by Transport when a server's connection encountered a problem. + /// If a Disconnect will also be raised, raise the Error first. + public Action OnServerError; + + /// Called by Transport when a client disconnected from the server. + public Action OnServerDisconnected; + + // client functions //////////////////////////////////////////////////// /// True if the client is currently connected to the server. public abstract bool ClientConnected(); @@ -64,36 +95,16 @@ namespace Mirror /// Sends a message to the server over the given channel. // The ArraySegment is only valid until returning. Copy if needed. - // TODO make second version abstract after removing the obsolete version - [Obsolete("Use ClientSend(segment, channelId) instead. channelId is now the last parameter.")] - public virtual void ClientSend(int channelId, ArraySegment segment) {} - public virtual void ClientSend(ArraySegment segment, int channelId) - { - // defaults to obsolete version to not force break transports. -#pragma warning disable 618 - ClientSend(channelId, segment); -#pragma warning restore 618 - } + public abstract void ClientSend(ArraySegment segment, int channelId = Channels.Reliable); /// Disconnects the client from the server public abstract void ClientDisconnect(); + // server functions //////////////////////////////////////////////////// /// Returns server address as Uri. // Useful for NetworkDiscovery. public abstract Uri ServerUri(); - /// Called by Transport when a new client connected to the server. - public Action OnServerConnected = (connId) => Debug.LogWarning("OnServerConnected called with no handler"); - - /// Called by Transport when the server received a message from a client. - public Action, int> OnServerDataReceived = (connId, data, channel) => Debug.LogWarning("OnServerDataReceived called with no handler"); - - /// Called by Transport when a server's connection encountered a problem. - public Action OnServerError = (connId, error) => Debug.LogWarning("OnServerError called with no handler"); - - /// Called by Transport when a client disconnected from the server. - public Action OnServerDisconnected = (connId) => Debug.LogWarning("OnServerDisconnected called with no handler"); - /// True if the server is currently listening for connections. public abstract bool ServerActive(); @@ -101,16 +112,7 @@ namespace Mirror public abstract void ServerStart(); /// Send a message to a client over the given channel. - // TODO make second version abstract after removing the obsolete version - [Obsolete("Use ServerSend(connectionId, segment, channelId) instead. channelId is now the last parameter.")] - public virtual void ServerSend(int connectionId, int channelId, ArraySegment segment) {} - public virtual void ServerSend(int connectionId, ArraySegment segment, int channelId) - { - // defaults to obsolete version to not force break transports. -#pragma warning disable 618 - ServerSend(connectionId, channelId, segment); -#pragma warning restore 618 - } + public abstract void ServerSend(int connectionId, ArraySegment segment, int channelId = Channels.Reliable); /// Disconnect a client from the server. public abstract void ServerDisconnect(int connectionId); @@ -130,13 +132,15 @@ namespace Mirror // running or available because it's needed for initializations. public abstract int GetMaxPacketSize(int channelId = Channels.Reliable); - /// Maximum batch(!) size for the given c hannel. + /// Recommended Batching threshold for this transport. // Uses GetMaxPacketSize by default. // Some transports like kcp support large max packet sizes which should // not be used for batching all the time because they end up being too // slow (head of line blocking etc.). - public virtual int GetMaxBatchSize(int channelId) => - GetMaxPacketSize(channelId); + public virtual int GetBatchThreshold(int channelId = Channels.Reliable) + { + return GetMaxPacketSize(channelId); + } // block Update & LateUpdate to show warnings if Transports still use // them instead of using diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Transport.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport.cs.meta similarity index 79% rename from UnityProject/Assets/Mirror/Runtime/Transport/Transport.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transport.cs.meta index 4508192..55072e1 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Transport.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transport.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/FallbackTransport.cs b/UnityProject/Assets/Mirror/Runtime/Transport/FallbackTransport.cs deleted file mode 100644 index 4d784fe..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/FallbackTransport.cs +++ /dev/null @@ -1,172 +0,0 @@ -// uses the first available transport for server and client. -// example: to use Apathy if on Windows/Mac/Linux and fall back to Telepathy -// otherwise. -using System; -using UnityEngine; - -namespace Mirror -{ - [HelpURL("https://mirror-networking.gitbook.io/docs/transports/fallback-transport")] - [DisallowMultipleComponent] - [Obsolete("Fallback Transport will be retired. It was only needed for Apathy/Libuv. Use kcp or Telepathy instead, those run everywhere.")] - public class FallbackTransport : Transport - { - public Transport[] transports; - - // the first transport that is available on this platform - Transport available; - - public void Awake() - { - if (transports == null || transports.Length == 0) - { - throw new Exception("FallbackTransport requires at least 1 underlying transport"); - } - available = GetAvailableTransport(); - Debug.Log("FallbackTransport available: " + available.GetType()); - } - - void OnEnable() - { - available.enabled = true; - } - - void OnDisable() - { - available.enabled = false; - } - - // The client just uses the first transport available - Transport GetAvailableTransport() - { - foreach (Transport transport in transports) - { - if (transport.Available()) - { - return transport; - } - } - throw new Exception("No transport suitable for this platform"); - } - - public override bool Available() - { - return available.Available(); - } - - public override void ClientConnect(string address) - { - available.OnClientConnected = OnClientConnected; - available.OnClientDataReceived = OnClientDataReceived; - available.OnClientError = OnClientError; - available.OnClientDisconnected = OnClientDisconnected; - available.ClientConnect(address); - } - - public override void ClientConnect(Uri uri) - { - foreach (Transport transport in transports) - { - if (transport.Available()) - { - try - { - transport.ClientConnect(uri); - available = transport; - } - catch (ArgumentException) - { - // transport does not support the schema, just move on to the next one - } - } - } - throw new Exception("No transport suitable for this platform"); - } - - public override bool ClientConnected() - { - return available.ClientConnected(); - } - - public override void ClientDisconnect() - { - available.ClientDisconnect(); - } - - public override void ClientSend(ArraySegment segment, int channelId) - { - available.ClientSend(segment, channelId); - } - - // right now this just returns the first available uri, - // should we return the list of all available uri? - public override Uri ServerUri() => available.ServerUri(); - - public override bool ServerActive() - { - return available.ServerActive(); - } - - public override string ServerGetClientAddress(int connectionId) - { - return available.ServerGetClientAddress(connectionId); - } - - public override void ServerDisconnect(int connectionId) - { - available.ServerDisconnect(connectionId); - } - - public override void ServerSend(int connectionId, ArraySegment segment, int channelId) - { - available.ServerSend(connectionId, segment, channelId); - } - - public override void ServerStart() - { - available.OnServerConnected = OnServerConnected; - available.OnServerDataReceived = OnServerDataReceived; - available.OnServerError = OnServerError; - available.OnServerDisconnected = OnServerDisconnected; - available.ServerStart(); - } - - public override void ServerStop() - { - available.ServerStop(); - } - - public override void Shutdown() - { - available.Shutdown(); - } - - public override int GetMaxPacketSize(int channelId = 0) - { - // finding the max packet size in a fallback environment has to be - // done very carefully: - // * servers and clients might run different transports depending on - // which platform they are on. - // * there should only ever be ONE true max packet size for everyone, - // otherwise a spawn message might be sent to all tcp sockets, but - // be too big for some udp sockets. that would be a debugging - // nightmare and allow for possible exploits and players on - // different platforms seeing a different game state. - // => the safest solution is to use the smallest max size for all - // transports. that will never fail. - int mininumAllowedSize = int.MaxValue; - foreach (Transport transport in transports) - { - int size = transport.GetMaxPacketSize(channelId); - mininumAllowedSize = Mathf.Min(size, mininumAllowedSize); - } - return mininumAllowedSize; - } - - public override string ToString() - { - return available.ToString(); - } - - } -} diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Core/IgnoranceClient.cs b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Core/IgnoranceClient.cs deleted file mode 100644 index 2542fd4..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Core/IgnoranceClient.cs +++ /dev/null @@ -1,296 +0,0 @@ -// Ignorance 1.4.x -// Ignorance. It really kicks the Unity LLAPIs ass. -// https://github.com/SoftwareGuy/Ignorance -// ----------------- -// Copyright (c) 2019 - 2020 Matt Coburn (SoftwareGuy/Coburn64) -// Ignorance Transport is licensed under the MIT license. Refer -// to the LICENSE file for more information. - -using ENet; -// using NetStack.Buffers; -using System; -using System.Collections.Concurrent; -using System.Threading; -using UnityEngine; -using Event = ENet.Event; // fixes CS0104 ambigous reference between the same thing in UnityEngine -using EventType = ENet.EventType; // fixes CS0104 ambigous reference between the same thing in UnityEngine -using Object = System.Object; // fixes CS0104 ambigous reference between the same thing in UnityEngine - -namespace IgnoranceTransport -{ - public class IgnoranceClient - { - // Client connection address and port - public string ConnectAddress = "127.0.0.1"; - public int ConnectPort = 7777; - // How many channels are expected - public int ExpectedChannels = 2; - // Native poll waiting time - public int PollTime = 1; - // Maximum Packet Size - public int MaximumPacketSize = 33554432; - // General Verbosity by default. - public int Verbosity = 1; - - // Queues - public ConcurrentQueue Incoming = new ConcurrentQueue(); - public ConcurrentQueue Outgoing = new ConcurrentQueue(); - public ConcurrentQueue Commands = new ConcurrentQueue(); - public ConcurrentQueue ConnectionEvents = new ConcurrentQueue(); - public ConcurrentQueue StatusUpdates = new ConcurrentQueue(); - - public bool IsAlive => WorkerThread != null && WorkerThread.IsAlive; - - private volatile bool CeaseOperation = false; - private Thread WorkerThread; - - public void Start() - { - Debug.Log("IgnoranceClient.Start()"); - - if (WorkerThread != null && WorkerThread.IsAlive) - { - // Cannot do that. - Debug.LogError("A worker thread is already running. Cannot start another."); - return; - } - - CeaseOperation = false; - ThreadParamInfo threadParams = new ThreadParamInfo() - { - Address = ConnectAddress, - Port = ConnectPort, - Channels = ExpectedChannels, - PollTime = PollTime, - PacketSizeLimit = MaximumPacketSize, - Verbosity = Verbosity - }; - - // Drain queues. - if (Incoming != null) while (Incoming.TryDequeue(out _)) ; - if (Outgoing != null) while (Outgoing.TryDequeue(out _)) ; - if (Commands != null) while (Commands.TryDequeue(out _)) ; - if (ConnectionEvents != null) while (ConnectionEvents.TryDequeue(out _)) ; - if (StatusUpdates != null) while (StatusUpdates.TryDequeue(out _)) ; - - WorkerThread = new Thread(ThreadWorker); - WorkerThread.Start(threadParams); - - Debug.Log("Client has dispatched worker thread."); - } - - public void Stop() - { - Debug.Log("Telling client thread to stop, this may take a while depending on network load"); - CeaseOperation = true; - } - - // This runs in a seperate thread, be careful accessing anything outside of it's thread - // or you may get an AccessViolation/crash. - private void ThreadWorker(Object parameters) - { - if (Verbosity > 0) - Debug.Log("Ignorance Client: Initializing. Please stand by..."); - - ThreadParamInfo setupInfo; - Address clientAddress = new Address(); - Peer clientPeer; - Host clientENetHost; - Event clientENetEvent; - IgnoranceClientStats icsu = default; - - // Grab the setup information. - if (parameters.GetType() == typeof(ThreadParamInfo)) - { - setupInfo = (ThreadParamInfo)parameters; - } - else - { - Debug.LogError("Ignorance Client: Startup failure: Invalid thread parameters. Aborting."); - return; - } - - // Attempt to initialize ENet inside the thread. - if (Library.Initialize()) - { - Debug.Log("Ignorance Client: ENet initialized."); - } - else - { - Debug.LogError("Ignorance Client: Failed to initialize ENet. This threads' fucked."); - return; - } - - // Attempt to connect to our target. - clientAddress.SetHost(setupInfo.Address); - clientAddress.Port = (ushort)setupInfo.Port; - - using (clientENetHost = new Host()) - { - // TODO: Maybe try catch this - clientENetHost.Create(); - clientPeer = clientENetHost.Connect(clientAddress, setupInfo.Channels); - - while (!CeaseOperation) - { - bool pollComplete = false; - - // Step 0: Handle commands. - while (Commands.TryDequeue(out IgnoranceCommandPacket commandPacket)) - { - switch (commandPacket.Type) - { - default: - break; - - case IgnoranceCommandType.ClientWantsToStop: - CeaseOperation = true; - break; - - case IgnoranceCommandType.ClientRequestsStatusUpdate: - // Respond with statistics so far. - if (!clientPeer.IsSet) - break; - - icsu.RTT = clientPeer.RoundTripTime; - - icsu.BytesReceived = clientPeer.BytesReceived; - icsu.BytesSent = clientPeer.BytesSent; - - icsu.PacketsReceived = clientENetHost.PacketsReceived; - icsu.PacketsSent = clientPeer.PacketsSent; - icsu.PacketsLost = clientPeer.PacketsLost; - - StatusUpdates.Enqueue(icsu); - break; - } - } - // Step 1: Send out data. - // ---> Sending to Server - while (Outgoing.TryDequeue(out IgnoranceOutgoingPacket outgoingPacket)) - { - // TODO: Revise this, could we tell the Peer to disconnect right here? - // Stop early if we get a client stop packet. - // if (outgoingPacket.Type == IgnorancePacketType.ClientWantsToStop) break; - - int ret = clientPeer.Send(outgoingPacket.Channel, ref outgoingPacket.Payload); - - if (ret < 0 && setupInfo.Verbosity > 0) - Debug.LogWarning($"Ignorance Client: ENet error code {ret} while sending packet to Peer {outgoingPacket.NativePeerId}."); - } - - // Step 2: - // <----- Receive Data packets - // This loops until polling is completed. It may take a while, if it's - // a slow networking day. - while (!pollComplete) - { - Packet incomingPacket; - Peer incomingPeer; - int incomingPacketLength; - - // Any events worth checking out? - if (clientENetHost.CheckEvents(out clientENetEvent) <= 0) - { - // If service time is met, break out of it. - if (clientENetHost.Service(setupInfo.PollTime, out clientENetEvent) <= 0) break; - - // Poll is done. - pollComplete = true; - } - - // Setup the packet references. - incomingPeer = clientENetEvent.Peer; - - // Now, let's handle those events. - switch (clientENetEvent.Type) - { - case EventType.None: - default: - break; - - case EventType.Connect: - ConnectionEvents.Enqueue(new IgnoranceConnectionEvent() - { - NativePeerId = incomingPeer.ID, - IP = incomingPeer.IP, - Port = incomingPeer.Port - }); - break; - - case EventType.Disconnect: - case EventType.Timeout: - ConnectionEvents.Enqueue(new IgnoranceConnectionEvent() - { - WasDisconnect = true - }); - break; - - - case EventType.Receive: - // Receive event type usually includes a packet; so cache its reference. - incomingPacket = clientENetEvent.Packet; - - if (!incomingPacket.IsSet) - { - if (setupInfo.Verbosity > 0) - Debug.LogWarning($"Ignorance Client: A receive event did not supply us with a packet to work with. This should never happen."); - break; - } - - incomingPacketLength = incomingPacket.Length; - - // Never consume more than we can have capacity for. - if (incomingPacketLength > setupInfo.PacketSizeLimit) - { - if (setupInfo.Verbosity > 0) - Debug.LogWarning($"Ignorance Client: Incoming packet is too big. My limit is {setupInfo.PacketSizeLimit} byte(s) whilest this packet is {incomingPacketLength} bytes."); - - incomingPacket.Dispose(); - break; - } - - IgnoranceIncomingPacket incomingQueuePacket = new IgnoranceIncomingPacket - { - Channel = clientENetEvent.ChannelID, - NativePeerId = incomingPeer.ID, - Payload = incomingPacket - }; - - Incoming.Enqueue(incomingQueuePacket); - break; - } - } - } - - Debug.Log("Ignorance Server: Shutdown commencing, disconnecting and flushing connection."); - - // Flush the client and disconnect. - clientPeer.Disconnect(0); - clientENetHost.Flush(); - - // Fix for client stuck in limbo, since the disconnection event may not be fired until next loop. - ConnectionEvents.Enqueue(new IgnoranceConnectionEvent() - { - WasDisconnect = true - }); - } - - // Deinitialize - Library.Deinitialize(); - if (setupInfo.Verbosity > 0) - Debug.Log("Ignorance Client: Shutdown complete."); - } - - - private struct ThreadParamInfo - { - public int Channels; - public int PollTime; - public int Port; - public int PacketSizeLimit; - public int Verbosity; - public string Address; - } - } -} diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Core/IgnoranceServer.cs b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Core/IgnoranceServer.cs deleted file mode 100644 index 4f71947..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Core/IgnoranceServer.cs +++ /dev/null @@ -1,328 +0,0 @@ -// Ignorance 1.4.x -// Ignorance. It really kicks the Unity LLAPIs ass. -// https://github.com/SoftwareGuy/Ignorance -// ----------------- -// Copyright (c) 2019 - 2020 Matt Coburn (SoftwareGuy/Coburn64) -// Ignorance Transport is licensed under the MIT license. Refer -// to the LICENSE file for more information. - -using ENet; -// using NetStack.Buffers; -using System.Collections.Concurrent; -using System.Threading; -using UnityEngine; -using Event = ENet.Event; // fixes CS0104 ambigous reference between the same thing in UnityEngine -using EventType = ENet.EventType; // fixes CS0104 ambigous reference between the same thing in UnityEngine -using Object = System.Object; // fixes CS0104 ambigous reference between the same thing in UnityEngine - -namespace IgnoranceTransport -{ - public class IgnoranceServer - { - // Server Properties - // - Bind Settings - public string BindAddress = "127.0.0.1"; - public int BindPort = 7777; - // - Maximum allowed channels, peers, etc. - public int MaximumChannels = 2; - public int MaximumPeers = 100; - public int MaximumPacketSize = 33554432; // ENet.cs: uint maxPacketSize = 32 * 1024 * 1024 = 33554432 - // - Native poll waiting time - public int PollTime = 1; - public int Verbosity = 1; - - public bool IsAlive => WorkerThread != null && WorkerThread.IsAlive; - - private volatile bool CeaseOperation = false; - - // Queues - public ConcurrentQueue Incoming = new ConcurrentQueue(); - public ConcurrentQueue Outgoing = new ConcurrentQueue(); - public ConcurrentQueue Commands = new ConcurrentQueue(); - public ConcurrentQueue ConnectionEvents = new ConcurrentQueue(); - public ConcurrentQueue DisconnectionEvents = new ConcurrentQueue(); - - // Thread - private Thread WorkerThread; - - public void Start() - { - if (WorkerThread != null && WorkerThread.IsAlive) - { - // Cannot do that. - Debug.LogError("A worker thread is already running. Cannot start another."); - return; - } - - CeaseOperation = false; - ThreadParamInfo threadParams = new ThreadParamInfo() - { - Address = BindAddress, - Port = BindPort, - Peers = MaximumPeers, - Channels = MaximumChannels, - PollTime = PollTime, - PacketSizeLimit = MaximumPacketSize, - Verbosity = Verbosity - }; - - // Drain queues. - if (Incoming != null) while (Incoming.TryDequeue(out _)) ; - if (Outgoing != null) while (Outgoing.TryDequeue(out _)) ; - if (Commands != null) while (Commands.TryDequeue(out _)) ; - if (ConnectionEvents != null) while (ConnectionEvents.TryDequeue(out _)) ; - if (DisconnectionEvents != null) while (DisconnectionEvents.TryDequeue(out _)) ; - - WorkerThread = new Thread(ThreadWorker); - WorkerThread.Start(threadParams); - - // Announce - if (Verbosity > 0) - Debug.Log("Server has dispatched worker thread."); - } - - public void Stop() - { - if (Verbosity > 0) - Debug.Log("Telling server thread to stop, this may take a while depending on network load"); - CeaseOperation = true; - } - - private void ThreadWorker(Object parameters) - { - if (Verbosity > 0) - Debug.Log("Ignorance Server: Initializing. Please stand by..."); - - // Thread cache items - ThreadParamInfo setupInfo; - Address serverAddress = new Address(); - Host serverENetHost; - Event serverENetEvent; - - Peer[] serverPeerArray; - - // Grab the setup information. - if (parameters.GetType() == typeof(ThreadParamInfo)) - { - setupInfo = (ThreadParamInfo)parameters; - } - else - { - Debug.LogError("Ignorance Server: Startup failure: Invalid thread parameters. Aborting."); - return; - } - - // Attempt to initialize ENet inside the thread. - if (Library.Initialize()) - { - Debug.Log("Ignorance Server: ENet initialized."); - } - else - { - Debug.LogError("Ignorance Server: Failed to initialize ENet. This threads' fucked."); - return; - } - - // Configure the server address. - serverAddress.SetHost(setupInfo.Address); - serverAddress.Port = (ushort)setupInfo.Port; - serverPeerArray = new Peer[setupInfo.Peers]; - - using (serverENetHost = new Host()) - { - // Create the server object. - serverENetHost.Create(serverAddress, setupInfo.Peers, setupInfo.Channels); - - // Loop until we're told to cease operations. - while (!CeaseOperation) - { - // Intermission: Command Handling - while (Commands.TryDequeue(out IgnoranceCommandPacket commandPacket)) - { - switch (commandPacket.Type) - { - default: - break; - - // Boot a Peer off the Server. - case IgnoranceCommandType.ServerKickPeer: - uint targetPeer = commandPacket.PeerId; - - if (!serverPeerArray[targetPeer].IsSet) continue; - if (setupInfo.Verbosity > 0) - Debug.Log($"Ignorance Server: Booting Peer {targetPeer} off this server instance."); - - IgnoranceConnectionEvent iced = new IgnoranceConnectionEvent() - { - WasDisconnect = true, - NativePeerId = targetPeer - }; - - DisconnectionEvents.Enqueue(iced); - - // Disconnect and reset the peer array's entry for that peer. - serverPeerArray[targetPeer].DisconnectNow(0); - serverPeerArray[targetPeer] = default; - break; - } - } - - // Step One: - // ---> Sending to peers - while (Outgoing.TryDequeue(out IgnoranceOutgoingPacket outgoingPacket)) - { - // Only create a packet if the server knows the peer. - if (serverPeerArray[outgoingPacket.NativePeerId].IsSet) - { - int ret = serverPeerArray[outgoingPacket.NativePeerId].Send(outgoingPacket.Channel, ref outgoingPacket.Payload); - - if (ret < 0 && setupInfo.Verbosity > 0) - Debug.LogWarning($"Ignorance Server: ENet error code {ret} while sending packet to Peer {outgoingPacket.NativePeerId}."); - } - else - { - // A peer might have disconnected, this is OK - just log the packet if set to paranoid. - if (setupInfo.Verbosity > 1) - Debug.LogWarning("Ignorance Server: Can't send packet, a native peer object is not set. This may be normal if the Peer has disconnected before this send cycle."); - } - - } - - // Step 2 - // <--- Receiving from peers - bool pollComplete = false; - - while (!pollComplete) - { - Packet incomingPacket; - Peer incomingPeer; - int incomingPacketLength; - - // Any events happening? - if (serverENetHost.CheckEvents(out serverENetEvent) <= 0) - { - // If service time is met, break out of it. - if (serverENetHost.Service(setupInfo.PollTime, out serverENetEvent) <= 0) break; - - pollComplete = true; - } - - // Setup the packet references. - incomingPeer = serverENetEvent.Peer; - - switch (serverENetEvent.Type) - { - // Idle. - case EventType.None: - default: - break; - - // Connection Event. - case EventType.Connect: - if (setupInfo.Verbosity > 1) - Debug.Log("Ignorance Server: Here comes a new Peer connection."); - - IgnoranceConnectionEvent ice = new IgnoranceConnectionEvent() - { - NativePeerId = incomingPeer.ID, - IP = incomingPeer.IP, - Port = incomingPeer.Port - }; - - ConnectionEvents.Enqueue(ice); - - // Assign a reference to the Peer. - serverPeerArray[incomingPeer.ID] = incomingPeer; - break; - - // Disconnect/Timeout. Mirror doesn't care if it's either, so we lump them together. - case EventType.Disconnect: - case EventType.Timeout: - if (!serverPeerArray[incomingPeer.ID].IsSet) break; - - if (setupInfo.Verbosity > 1) - Debug.Log("Ignorance Server: Peer disconnection."); - - IgnoranceConnectionEvent iced = new IgnoranceConnectionEvent() - { - WasDisconnect = true, - NativePeerId = incomingPeer.ID - }; - - DisconnectionEvents.Enqueue(iced); - - // Reset the peer array's entry for that peer. - serverPeerArray[incomingPeer.ID] = default; - break; - - case EventType.Receive: - // Receive event type usually includes a packet; so cache its reference. - incomingPacket = serverENetEvent.Packet; - if (!incomingPacket.IsSet) - { - if (setupInfo.Verbosity > 0) - Debug.LogWarning($"Ignorance Server: A receive event did not supply us with a packet to work with. This should never happen."); - break; - } - - incomingPacketLength = incomingPacket.Length; - - // Firstly check if the packet is too big. If it is, do not process it - drop it. - if (incomingPacketLength > setupInfo.PacketSizeLimit) - { - if (setupInfo.Verbosity > 0) - Debug.LogWarning($"Ignorance Server: Incoming packet is too big. My limit is {setupInfo.PacketSizeLimit} byte(s) whilest this packet is {incomingPacketLength} bytes."); - - incomingPacket.Dispose(); - break; - } - - IgnoranceIncomingPacket incomingQueuePacket = new IgnoranceIncomingPacket - { - Channel = serverENetEvent.ChannelID, - NativePeerId = incomingPeer.ID, - Payload = incomingPacket, - }; - - // Enqueue. - Incoming.Enqueue(incomingQueuePacket); - break; - } - } - } - - if (Verbosity > 0) - Debug.Log("Ignorance Server: Shutdown commencing, flushing connections."); - - // Cleanup and flush everything. - serverENetHost.Flush(); - - // Kick everyone. - for (int i = 0; i < serverPeerArray.Length; i++) - { - if (!serverPeerArray[i].IsSet) continue; - serverPeerArray[i].DisconnectNow(0); - } - } - - // Flush again to ensure ENet gets those Disconnection stuff out. - // May not be needed; better to err on side of caution - - if (setupInfo.Verbosity > 0) - Debug.Log("Ignorance Server: Shutdown complete."); - - Library.Deinitialize(); - } - - private struct ThreadParamInfo - { - public int Channels; - public int Peers; - public int PollTime; - public int Port; - public int PacketSizeLimit; - public int Verbosity; - public string Address; - } - } -} diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Dependencies/ENet.cs b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Dependencies/ENet.cs deleted file mode 100644 index cf4336c..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Dependencies/ENet.cs +++ /dev/null @@ -1,1411 +0,0 @@ -/* - * Managed C# wrapper for an extended version of ENet - * This is a fork from upstream and is available at http://github.com/SoftwareGuy/ENet-CSharp - * - * Copyright (c) 2019 Matt Coburn (SoftwareGuy/Coburn64), Chris Burns (c6burns) - * Copyright (c) 2013 James Bellinger, 2016 Nate Shoffner, 2018 Stanislav Denisov - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -using System; -using System.Runtime.InteropServices; -using System.Security; -using System.Text; - -namespace ENet -{ - [Flags] - public enum PacketFlags - { - None = 0, - Reliable = 1 << 0, - Unsequenced = 1 << 1, - NoAllocate = 1 << 2, - UnreliableFragmented = 1 << 3, - Instant = 1 << 4, - Unthrottled = 1 << 5, - Sent = 1 << 8 - } - - public enum EventType - { - None = 0, - Connect = 1, - Disconnect = 2, - Receive = 3, - Timeout = 4 - } - - public enum PeerState - { - Uninitialized = -1, - Disconnected = 0, - Connecting = 1, - AcknowledgingConnect = 2, - ConnectionPending = 3, - ConnectionSucceeded = 4, - Connected = 5, - DisconnectLater = 6, - Disconnecting = 7, - AcknowledgingDisconnect = 8, - Zombie = 9 - } - - [StructLayout(LayoutKind.Explicit, Size = 18)] - internal struct ENetAddress - { - [FieldOffset(16)] - public ushort port; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct ENetEvent - { - public EventType type; - public IntPtr peer; - public byte channelID; - public uint data; - public IntPtr packet; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct ENetCallbacks - { - public AllocCallback malloc; - public FreeCallback free; - public NoMemoryCallback noMemory; - } - - public delegate IntPtr AllocCallback(IntPtr size); - public delegate void FreeCallback(IntPtr memory); - public delegate void NoMemoryCallback(); - public delegate void PacketFreeCallback(Packet packet); - public delegate int InterceptCallback(ref Event @event, ref Address address, IntPtr receivedData, int receivedDataLength); - public delegate ulong ChecksumCallback(IntPtr buffers, int bufferCount); - - internal static class ArrayPool - { - [ThreadStatic] - private static byte[] byteBuffer; - [ThreadStatic] - private static IntPtr[] pointerBuffer; - - public static byte[] GetByteBuffer() - { - if (byteBuffer == null) - byteBuffer = new byte[64]; - - return byteBuffer; - } - - public static IntPtr[] GetPointerBuffer() - { - if (pointerBuffer == null) - pointerBuffer = new IntPtr[Library.maxPeers]; - - return pointerBuffer; - } - } - - public struct Address - { - private ENetAddress nativeAddress; - - internal ENetAddress NativeData - { - get - { - return nativeAddress; - } - - set - { - nativeAddress = value; - } - } - - internal Address(ENetAddress address) - { - nativeAddress = address; - } - - public ushort Port - { - get - { - return nativeAddress.port; - } - - set - { - nativeAddress.port = value; - } - } - - public string GetIP() - { - StringBuilder ip = new StringBuilder(1025); - - if (Native.enet_address_get_ip(ref nativeAddress, ip, (IntPtr)ip.Capacity) != 0) - return String.Empty; - - return ip.ToString(); - } - - public bool SetIP(string ip) - { - if (ip == null) - throw new ArgumentNullException("ip"); - - return Native.enet_address_set_ip(ref nativeAddress, ip) == 0; - } - - public string GetHost() - { - StringBuilder hostName = new StringBuilder(1025); - - if (Native.enet_address_get_hostname(ref nativeAddress, hostName, (IntPtr)hostName.Capacity) != 0) - return String.Empty; - - return hostName.ToString(); - } - - public bool SetHost(string hostName) - { - if (hostName == null) - throw new ArgumentNullException("hostName"); - - return Native.enet_address_set_hostname(ref nativeAddress, hostName) == 0; - } - } - - public struct Event - { - private ENetEvent nativeEvent; - - internal ENetEvent NativeData - { - get - { - return nativeEvent; - } - - set - { - nativeEvent = value; - } - } - - internal Event(ENetEvent @event) - { - nativeEvent = @event; - } - - public EventType Type - { - get - { - return nativeEvent.type; - } - } - - public Peer Peer - { - get - { - return new Peer(nativeEvent.peer); - } - } - - public byte ChannelID - { - get - { - return nativeEvent.channelID; - } - } - - public uint Data - { - get - { - return nativeEvent.data; - } - } - - public Packet Packet - { - get - { - return new Packet(nativeEvent.packet); - } - } - } - - public class Callbacks - { - private ENetCallbacks nativeCallbacks; - - internal ENetCallbacks NativeData - { - get - { - return nativeCallbacks; - } - - set - { - nativeCallbacks = value; - } - } - - public Callbacks(AllocCallback allocCallback, FreeCallback freeCallback, NoMemoryCallback noMemoryCallback) - { - nativeCallbacks.malloc = allocCallback; - nativeCallbacks.free = freeCallback; - nativeCallbacks.noMemory = noMemoryCallback; - } - } - - public struct Packet : IDisposable - { - private IntPtr nativePacket; - - internal IntPtr NativeData - { - get - { - return nativePacket; - } - - set - { - nativePacket = value; - } - } - - internal Packet(IntPtr packet) - { - nativePacket = packet; - } - - public void Dispose() - { - if (nativePacket != IntPtr.Zero) - { - Native.enet_packet_dispose(nativePacket); - nativePacket = IntPtr.Zero; - } - } - - public bool IsSet - { - get - { - return nativePacket != IntPtr.Zero; - } - } - - public IntPtr Data - { - get - { - ThrowIfNotCreated(); - - return Native.enet_packet_get_data(nativePacket); - } - } - - public IntPtr UserData - { - get - { - ThrowIfNotCreated(); - - return Native.enet_packet_get_user_data(nativePacket); - } - - set - { - ThrowIfNotCreated(); - - Native.enet_packet_set_user_data(nativePacket, value); - } - } - - public int Length - { - get - { - ThrowIfNotCreated(); - - return Native.enet_packet_get_length(nativePacket); - } - } - - public bool HasReferences - { - get - { - ThrowIfNotCreated(); - - return Native.enet_packet_check_references(nativePacket) != 0; - } - } - - internal void ThrowIfNotCreated() - { - if (nativePacket == IntPtr.Zero) - throw new InvalidOperationException("Packet not created"); - } - - public void SetFreeCallback(IntPtr callback) - { - ThrowIfNotCreated(); - - Native.enet_packet_set_free_callback(nativePacket, callback); - } - - public void SetFreeCallback(PacketFreeCallback callback) - { - ThrowIfNotCreated(); - - Native.enet_packet_set_free_callback(nativePacket, Marshal.GetFunctionPointerForDelegate(callback)); - } - - public void Create(byte[] data) - { - if (data == null) - throw new ArgumentNullException("data"); - - Create(data, data.Length); - } - - public void Create(byte[] data, int length) - { - Create(data, length, PacketFlags.None); - } - - public void Create(byte[] data, PacketFlags flags) - { - Create(data, data.Length, flags); - } - - public void Create(byte[] data, int length, PacketFlags flags) - { - if (data == null) - throw new ArgumentNullException("data"); - - if (length < 0 || length > data.Length) - throw new ArgumentOutOfRangeException("length"); - - nativePacket = Native.enet_packet_create(data, (IntPtr)length, flags); - } - - public void Create(IntPtr data, int length, PacketFlags flags) - { - if (data == IntPtr.Zero) - throw new ArgumentNullException("data"); - - if (length < 0) - throw new ArgumentOutOfRangeException("length"); - - nativePacket = Native.enet_packet_create(data, (IntPtr)length, flags); - } - - public void Create(byte[] data, int offset, int length, PacketFlags flags) - { - if (data == null) - throw new ArgumentNullException("data"); - - if (offset < 0) - throw new ArgumentOutOfRangeException("offset"); - - if (length < 0 || length > data.Length) - throw new ArgumentOutOfRangeException("length"); - - nativePacket = Native.enet_packet_create_offset(data, (IntPtr)length, (IntPtr)offset, flags); - } - - public void Create(IntPtr data, int offset, int length, PacketFlags flags) - { - if (data == IntPtr.Zero) - throw new ArgumentNullException("data"); - - if (offset < 0) - throw new ArgumentOutOfRangeException("offset"); - - if (length < 0) - throw new ArgumentOutOfRangeException("length"); - - nativePacket = Native.enet_packet_create_offset(data, (IntPtr)length, (IntPtr)offset, flags); - } - - public void CopyTo(byte[] destination, int startPos = 0) - { - if (destination == null) - throw new ArgumentNullException("destination"); - - // Fix by katori, prevents trying to copy a NULL - // from native world (ie. disconnect a client) - if (Data == null) - { - return; - } - - Marshal.Copy(Data, destination, startPos, Length); - } - } - - public class Host : IDisposable - { - private IntPtr nativeHost; - - internal IntPtr NativeData - { - get - { - return nativeHost; - } - - set - { - nativeHost = value; - } - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (nativeHost != IntPtr.Zero) - { - Native.enet_host_destroy(nativeHost); - nativeHost = IntPtr.Zero; - } - } - - ~Host() - { - Dispose(false); - } - - public bool IsSet - { - get - { - return nativeHost != IntPtr.Zero; - } - } - - public uint PeersCount - { - get - { - ThrowIfNotCreated(); - - return Native.enet_host_get_peers_count(nativeHost); - } - } - - public uint PacketsSent - { - get - { - ThrowIfNotCreated(); - - return Native.enet_host_get_packets_sent(nativeHost); - } - } - - public uint PacketsReceived - { - get - { - ThrowIfNotCreated(); - - return Native.enet_host_get_packets_received(nativeHost); - } - } - - public uint BytesSent - { - get - { - ThrowIfNotCreated(); - - return Native.enet_host_get_bytes_sent(nativeHost); - } - } - - public uint BytesReceived - { - get - { - ThrowIfNotCreated(); - - return Native.enet_host_get_bytes_received(nativeHost); - } - } - - internal void ThrowIfNotCreated() - { - if (nativeHost == IntPtr.Zero) - throw new InvalidOperationException("Host not created"); - } - - private static void ThrowIfChannelsExceeded(int channelLimit) - { - if (channelLimit < 0 || channelLimit > Library.maxChannelCount) - throw new ArgumentOutOfRangeException("channelLimit"); - } - - public void Create() - { - Create(null, 1, 0); - } - - public void Create(int bufferSize) - { - Create(null, 1, 0, 0, 0, bufferSize); - } - - public void Create(Address? address, int peerLimit) - { - Create(address, peerLimit, 0); - } - - public void Create(Address? address, int peerLimit, int channelLimit) - { - Create(address, peerLimit, channelLimit, 0, 0, 0); - } - - public void Create(int peerLimit, int channelLimit) - { - Create(null, peerLimit, channelLimit, 0, 0, 0); - } - - public void Create(int peerLimit, int channelLimit, uint incomingBandwidth, uint outgoingBandwidth) - { - Create(null, peerLimit, channelLimit, incomingBandwidth, outgoingBandwidth, 0); - } - - public void Create(Address? address, int peerLimit, int channelLimit, uint incomingBandwidth, uint outgoingBandwidth) - { - Create(address, peerLimit, channelLimit, incomingBandwidth, outgoingBandwidth, 0); - } - - public void Create(Address? address, int peerLimit, int channelLimit, uint incomingBandwidth, uint outgoingBandwidth, int bufferSize) - { - if (nativeHost != IntPtr.Zero) - throw new InvalidOperationException("Host already created"); - - if (peerLimit < 0 || peerLimit > Library.maxPeers) - throw new ArgumentOutOfRangeException("peerLimit"); - - ThrowIfChannelsExceeded(channelLimit); - - if (address != null) - { - var nativeAddress = address.Value.NativeData; - - nativeHost = Native.enet_host_create(ref nativeAddress, (IntPtr)peerLimit, (IntPtr)channelLimit, incomingBandwidth, outgoingBandwidth, bufferSize); - } - else - { - nativeHost = Native.enet_host_create(IntPtr.Zero, (IntPtr)peerLimit, (IntPtr)channelLimit, incomingBandwidth, outgoingBandwidth, bufferSize); - } - - if (nativeHost == IntPtr.Zero) - throw new InvalidOperationException("Host creation call failed"); - } - - public void PreventConnections(bool state) - { - ThrowIfNotCreated(); - - Native.enet_host_prevent_connections(nativeHost, (byte)(state ? 1 : 0)); - } - - public void Broadcast(byte channelID, ref Packet packet) - { - ThrowIfNotCreated(); - - packet.ThrowIfNotCreated(); - Native.enet_host_broadcast(nativeHost, channelID, packet.NativeData); - packet.NativeData = IntPtr.Zero; - } - - public void Broadcast(byte channelID, ref Packet packet, Peer excludedPeer) - { - ThrowIfNotCreated(); - - packet.ThrowIfNotCreated(); - Native.enet_host_broadcast_exclude(nativeHost, channelID, packet.NativeData, excludedPeer.NativeData); - packet.NativeData = IntPtr.Zero; - } - - public void Broadcast(byte channelID, ref Packet packet, Peer[] peers) - { - if (peers == null) - throw new ArgumentNullException("peers"); - - ThrowIfNotCreated(); - - packet.ThrowIfNotCreated(); - - if (peers.Length > 0) - { - IntPtr[] nativePeers = ArrayPool.GetPointerBuffer(); - int nativeCount = 0; - - for (int i = 0; i < peers.Length; i++) - { - if (peers[i].NativeData != IntPtr.Zero) - { - nativePeers[nativeCount] = peers[i].NativeData; - nativeCount++; - } - } - - Native.enet_host_broadcast_selective(nativeHost, channelID, packet.NativeData, nativePeers, (IntPtr)nativeCount); - } - - packet.NativeData = IntPtr.Zero; - } - - public int CheckEvents(out Event @event) - { - ThrowIfNotCreated(); - - ENetEvent nativeEvent; - - var result = Native.enet_host_check_events(nativeHost, out nativeEvent); - - if (result <= 0) - { - @event = default(Event); - - return result; - } - - @event = new Event(nativeEvent); - - return result; - } - - public Peer Connect(Address address) - { - return Connect(address, 0, 0); - } - - public Peer Connect(Address address, int channelLimit) - { - return Connect(address, channelLimit, 0); - } - - public Peer Connect(Address address, int channelLimit, uint data) - { - ThrowIfNotCreated(); - ThrowIfChannelsExceeded(channelLimit); - - var nativeAddress = address.NativeData; - var peer = new Peer(Native.enet_host_connect(nativeHost, ref nativeAddress, (IntPtr)channelLimit, data)); - - if (peer.NativeData == IntPtr.Zero) - throw new InvalidOperationException("Host connect call failed"); - - return peer; - } - - public int Service(int timeout, out Event @event) - { - if (timeout < 0) - throw new ArgumentOutOfRangeException("timeout"); - - ThrowIfNotCreated(); - - ENetEvent nativeEvent; - - var result = Native.enet_host_service(nativeHost, out nativeEvent, (uint)timeout); - - if (result <= 0) - { - @event = default(Event); - - return result; - } - - @event = new Event(nativeEvent); - - return result; - } - - public void SetBandwidthLimit(uint incomingBandwidth, uint outgoingBandwidth) - { - ThrowIfNotCreated(); - - Native.enet_host_bandwidth_limit(nativeHost, incomingBandwidth, outgoingBandwidth); - } - - public void SetChannelLimit(int channelLimit) - { - ThrowIfNotCreated(); - ThrowIfChannelsExceeded(channelLimit); - - Native.enet_host_channel_limit(nativeHost, (IntPtr)channelLimit); - } - - public void SetMaxDuplicatePeers(ushort number) - { - ThrowIfNotCreated(); - - Native.enet_host_set_max_duplicate_peers(nativeHost, number); - } - - public void SetInterceptCallback(IntPtr callback) - { - ThrowIfNotCreated(); - - Native.enet_host_set_intercept_callback(nativeHost, callback); - } - - public void SetInterceptCallback(InterceptCallback callback) - { - ThrowIfNotCreated(); - - Native.enet_host_set_intercept_callback(nativeHost, Marshal.GetFunctionPointerForDelegate(callback)); - } - - public void SetChecksumCallback(IntPtr callback) - { - ThrowIfNotCreated(); - - Native.enet_host_set_checksum_callback(nativeHost, callback); - } - - public void SetChecksumCallback(ChecksumCallback callback) - { - ThrowIfNotCreated(); - - Native.enet_host_set_checksum_callback(nativeHost, Marshal.GetFunctionPointerForDelegate(callback)); - } - - public void Flush() - { - ThrowIfNotCreated(); - - Native.enet_host_flush(nativeHost); - } - } - - public struct Peer - { - private IntPtr nativePeer; - private uint nativeID; - - internal IntPtr NativeData - { - get - { - return nativePeer; - } - - set - { - nativePeer = value; - } - } - - internal Peer(IntPtr peer) - { - nativePeer = peer; - nativeID = nativePeer != IntPtr.Zero ? Native.enet_peer_get_id(nativePeer) : 0; - } - - public bool IsSet - { - get - { - return nativePeer != IntPtr.Zero; - } - } - - public uint ID - { - get - { - return nativeID; - } - } - - public string IP - { - get - { - ThrowIfNotCreated(); - - byte[] ip = ArrayPool.GetByteBuffer(); - - if (Native.enet_peer_get_ip(nativePeer, ip, (IntPtr)ip.Length) == 0) - return Encoding.ASCII.GetString(ip, 0, ip.StringLength()); - else - return String.Empty; - } - } - - public ushort Port - { - get - { - ThrowIfNotCreated(); - - return Native.enet_peer_get_port(nativePeer); - } - } - - public uint MTU - { - get - { - ThrowIfNotCreated(); - - return Native.enet_peer_get_mtu(nativePeer); - } - } - - public PeerState State - { - get - { - return nativePeer == IntPtr.Zero ? PeerState.Uninitialized : Native.enet_peer_get_state(nativePeer); - } - } - - public uint RoundTripTime - { - get - { - ThrowIfNotCreated(); - - return Native.enet_peer_get_rtt(nativePeer); - } - } - - public uint LastRoundTripTime - { - get - { - ThrowIfNotCreated(); - - return Native.enet_peer_get_last_rtt(nativePeer); - } - } - - public uint LastSendTime - { - get - { - ThrowIfNotCreated(); - - return Native.enet_peer_get_lastsendtime(nativePeer); - } - } - - public uint LastReceiveTime - { - get - { - ThrowIfNotCreated(); - - return Native.enet_peer_get_lastreceivetime(nativePeer); - } - } - - public ulong PacketsSent - { - get - { - ThrowIfNotCreated(); - - return Native.enet_peer_get_packets_sent(nativePeer); - } - } - - public ulong PacketsLost - { - get - { - ThrowIfNotCreated(); - - return Native.enet_peer_get_packets_lost(nativePeer); - } - } - - public float PacketsThrottle - { - get - { - ThrowIfNotCreated(); - - return Native.enet_peer_get_packets_throttle(nativePeer); - } - } - - public ulong BytesSent - { - get - { - ThrowIfNotCreated(); - - return Native.enet_peer_get_bytes_sent(nativePeer); - } - } - - public ulong BytesReceived - { - get - { - ThrowIfNotCreated(); - - return Native.enet_peer_get_bytes_received(nativePeer); - } - } - - public IntPtr Data - { - get - { - ThrowIfNotCreated(); - - return Native.enet_peer_get_data(nativePeer); - } - - set - { - ThrowIfNotCreated(); - - Native.enet_peer_set_data(nativePeer, value); - } - } - - internal void ThrowIfNotCreated() - { - if (nativePeer == IntPtr.Zero) - throw new InvalidOperationException("Peer not created"); - } - - public void ConfigureThrottle(uint interval, uint acceleration, uint deceleration, uint threshold) - { - ThrowIfNotCreated(); - - Native.enet_peer_throttle_configure(nativePeer, interval, acceleration, deceleration, threshold); - } - - public int Send(byte channelID, ref Packet packet) - { - ThrowIfNotCreated(); - - packet.ThrowIfNotCreated(); - - return Native.enet_peer_send(nativePeer, channelID, packet.NativeData); - } - - public bool Receive(out byte channelID, out Packet packet) - { - ThrowIfNotCreated(); - - IntPtr nativePacket = Native.enet_peer_receive(nativePeer, out channelID); - - if (nativePacket != IntPtr.Zero) - { - packet = new Packet(nativePacket); - - return true; - } - - packet = default(Packet); - - return false; - } - - public void Ping() - { - ThrowIfNotCreated(); - - Native.enet_peer_ping(nativePeer); - } - - public void PingInterval(uint interval) - { - ThrowIfNotCreated(); - - Native.enet_peer_ping_interval(nativePeer, interval); - } - - public void Timeout(uint timeoutLimit, uint timeoutMinimum, uint timeoutMaximum) - { - ThrowIfNotCreated(); - - Native.enet_peer_timeout(nativePeer, timeoutLimit, timeoutMinimum, timeoutMaximum); - } - - public void Disconnect(uint data) - { - ThrowIfNotCreated(); - - Native.enet_peer_disconnect(nativePeer, data); - } - - public void DisconnectNow(uint data) - { - ThrowIfNotCreated(); - - Native.enet_peer_disconnect_now(nativePeer, data); - } - - public void DisconnectLater(uint data) - { - ThrowIfNotCreated(); - - Native.enet_peer_disconnect_later(nativePeer, data); - } - - public void Reset() - { - ThrowIfNotCreated(); - - Native.enet_peer_reset(nativePeer); - } - } - - public static class Extensions - { - public static int StringLength(this byte[] data) - { - if (data == null) - throw new ArgumentNullException("data"); - - int i; - - for (i = 0; i < data.Length && data[i] != 0; i++) ; - - return i; - } - } - - public static class Library - { - public const uint maxChannelCount = 0xFF; - public const uint maxPeers = 0xFFF; - public const uint maxPacketSize = 32 * 1024 * 1024; - public const uint throttleThreshold = 40; - public const uint throttleScale = 32; - public const uint throttleAcceleration = 2; - public const uint throttleDeceleration = 2; - public const uint throttleInterval = 5000; - public const uint timeoutLimit = 32; - public const uint timeoutMinimum = 5000; - public const uint timeoutMaximum = 30000; - public const uint version = (2 << 16) | (4 << 8) | (7); - - public static uint Time - { - get - { - return Native.enet_time_get(); - } - } - - public static bool Initialize() - { - if (Native.enet_linked_version() != version) - throw new InvalidOperationException("ENet native is out of date. Download the latest release from https://github.com/SoftwareGuy/ENet-CSharp/releases"); - - return Native.enet_initialize() == 0; - } - - public static bool Initialize(Callbacks callbacks) - { - if (callbacks == null) - throw new ArgumentNullException("callbacks"); - - if (Native.enet_linked_version() != version) - throw new InvalidOperationException("ENet native is out of date. Download the latest release from https://github.com/SoftwareGuy/ENet-CSharp/releases"); - - ENetCallbacks nativeCallbacks = callbacks.NativeData; - - return Native.enet_initialize_with_callbacks(version, ref nativeCallbacks) == 0; - } - - public static void Deinitialize() - { - Native.enet_deinitialize(); - } - - public static ulong CRC64(IntPtr buffers, int bufferCount) - { - return Native.enet_crc64(buffers, bufferCount); - } - } - - [SuppressUnmanagedCodeSecurity] - internal static class Native - { - // This should address Unity usage and bug #66: Platform specific Enet / libenet - // https://github.com/SoftwareGuy/Ignorance/issues/66 -#if UNITY_EDITOR - // We are inside the Unity Editor. -#if UNITY_EDITOR_OSX - // Unity Editor on macOS needs to use libenet. - private const string nativeLibrary = "libenet"; -#else - private const string nativeLibrary = "enet"; -#endif -#endif - -#if !UNITY_EDITOR - // We're not inside the Unity Editor. -#if __APPLE__ && !(__IOS__ || UNITY_IOS) - // Use libenet on macOS. - private const string nativeLibrary = "libenet"; -#elif __IOS__ || UNITY_IOS - // We're building for a certain mobile fruity OS. - private const string nativeLibrary = "__Internal"; -#else - // Assume everything else, Windows et al. - private const string nativeLibrary = "enet"; -#endif -#endif - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern int enet_initialize(); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern int enet_initialize_with_callbacks(uint version, ref ENetCallbacks inits); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_deinitialize(); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern uint enet_linked_version(); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern uint enet_time_get(); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern ulong enet_crc64(IntPtr buffers, int bufferCount); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern int enet_address_set_ip(ref ENetAddress address, string ip); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern int enet_address_set_hostname(ref ENetAddress address, string hostName); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern int enet_address_get_ip(ref ENetAddress address, StringBuilder ip, IntPtr ipLength); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern int enet_address_get_hostname(ref ENetAddress address, StringBuilder hostName, IntPtr nameLength); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr enet_packet_create(byte[] data, IntPtr dataLength, PacketFlags flags); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr enet_packet_create(IntPtr data, IntPtr dataLength, PacketFlags flags); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr enet_packet_create_offset(byte[] data, IntPtr dataLength, IntPtr dataOffset, PacketFlags flags); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr enet_packet_create_offset(IntPtr data, IntPtr dataLength, IntPtr dataOffset, PacketFlags flags); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern int enet_packet_check_references(IntPtr packet); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr enet_packet_get_data(IntPtr packet); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr enet_packet_get_user_data(IntPtr packet); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr enet_packet_set_user_data(IntPtr packet, IntPtr userData); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern int enet_packet_get_length(IntPtr packet); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_packet_set_free_callback(IntPtr packet, IntPtr callback); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_packet_dispose(IntPtr packet); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr enet_host_create(ref ENetAddress address, IntPtr peerLimit, IntPtr channelLimit, uint incomingBandwidth, uint outgoingBandwidth, int bufferSize); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr enet_host_create(IntPtr address, IntPtr peerLimit, IntPtr channelLimit, uint incomingBandwidth, uint outgoingBandwidth, int bufferSize); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr enet_host_connect(IntPtr host, ref ENetAddress address, IntPtr channelCount, uint data); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_host_broadcast(IntPtr host, byte channelID, IntPtr packet); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_host_broadcast_exclude(IntPtr host, byte channelID, IntPtr packet, IntPtr excludedPeer); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_host_broadcast_selective(IntPtr host, byte channelID, IntPtr packet, IntPtr[] peers, IntPtr peersLength); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern int enet_host_service(IntPtr host, out ENetEvent @event, uint timeout); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern int enet_host_check_events(IntPtr host, out ENetEvent @event); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_host_channel_limit(IntPtr host, IntPtr channelLimit); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_host_bandwidth_limit(IntPtr host, uint incomingBandwidth, uint outgoingBandwidth); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern uint enet_host_get_peers_count(IntPtr host); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern uint enet_host_get_packets_sent(IntPtr host); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern uint enet_host_get_packets_received(IntPtr host); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern uint enet_host_get_bytes_sent(IntPtr host); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern uint enet_host_get_bytes_received(IntPtr host); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_host_set_max_duplicate_peers(IntPtr host, ushort number); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_host_set_intercept_callback(IntPtr host, IntPtr callback); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_host_set_checksum_callback(IntPtr host, IntPtr callback); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_host_flush(IntPtr host); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_host_destroy(IntPtr host); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_host_prevent_connections(IntPtr host, byte state); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_peer_throttle_configure(IntPtr peer, uint interval, uint acceleration, uint deceleration, uint threshold); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern uint enet_peer_get_id(IntPtr peer); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern int enet_peer_get_ip(IntPtr peer, byte[] ip, IntPtr ipLength); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern ushort enet_peer_get_port(IntPtr peer); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern uint enet_peer_get_mtu(IntPtr peer); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern PeerState enet_peer_get_state(IntPtr peer); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern uint enet_peer_get_rtt(IntPtr peer); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern uint enet_peer_get_last_rtt(IntPtr peer); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern uint enet_peer_get_lastsendtime(IntPtr peer); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern uint enet_peer_get_lastreceivetime(IntPtr peer); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern ulong enet_peer_get_packets_sent(IntPtr peer); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern ulong enet_peer_get_packets_lost(IntPtr peer); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern float enet_peer_get_packets_throttle(IntPtr peer); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern ulong enet_peer_get_bytes_sent(IntPtr peer); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern ulong enet_peer_get_bytes_received(IntPtr peer); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr enet_peer_get_data(IntPtr peer); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_peer_set_data(IntPtr peer, IntPtr data); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern int enet_peer_send(IntPtr peer, byte channelID, IntPtr packet); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr enet_peer_receive(IntPtr peer, out byte channelID); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_peer_ping(IntPtr peer); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_peer_ping_interval(IntPtr peer, uint pingInterval); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_peer_timeout(IntPtr peer, uint timeoutLimit, uint timeoutMinimum, uint timeoutMaximum); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_peer_disconnect(IntPtr peer, uint data); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_peer_disconnect_now(IntPtr peer, uint data); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_peer_disconnect_later(IntPtr peer, uint data); - - [DllImport(nativeLibrary, CallingConvention = CallingConvention.Cdecl)] - internal static extern void enet_peer_reset(IntPtr peer); - -#if UNITY_EDITOR - public static string nativeLibraryName { get { return nativeLibrary; } } -#endif - - } -} diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Editor/AddScriptingDefine.cs b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Editor/AddScriptingDefine.cs deleted file mode 100644 index 0ff3186..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Editor/AddScriptingDefine.cs +++ /dev/null @@ -1,83 +0,0 @@ -#if UNITY_EDITOR -using System.Collections.Generic; -using System.Linq; -using UnityEditor; - -namespace IgnoranceTransport -{ - /// - /// Adds the given define symbols to PlayerSettings define symbols. - /// Just add your own define symbols to the Symbols property at the below. - /// - [InitializeOnLoad] - public class AddIgnoranceDefine : Editor - { - private static string existingDefines = string.Empty; - - /// - /// Symbols that will be added to the editor - /// - public static readonly string[] Symbols = new string[] { - "IGNORANCE", // Ignorance exists - "IGNORANCE_1", // Major version - "IGNORANCE_1_4" // Major and minor version - }; - - /// - /// Do not remove these symbols - /// - public static readonly string[] DoNotRemoveTheseSymbols = new string[] - { - "IGNORANCE_NO_UPNP", - "IGNORANCE_MIRROR_POLLING" - }; - - /// - /// Add define symbols as soon as Unity gets done compiling. - /// - static AddIgnoranceDefine() - { - // Get the current scripting defines - string definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup); - if (existingDefines == definesString) - { - // 1.2.6: There is no need to apply the changes, return. - return; - } - - // Convert the string to a list - List allDefines = definesString.Split(';').ToList(); - // Remove any old version defines from previous installs - allDefines.RemoveAll(IsSafeToRemove); - // x => x.StartsWith("IGNORANCE") && !DoesSymbolExistInBlacklist(x)); - // Add any symbols that weren't already in the list - allDefines.AddRange(Symbols.Except(allDefines)); - - string newDefines = string.Join(";", allDefines.ToArray()); - PlayerSettings.SetScriptingDefineSymbolsForGroup( - EditorUserBuildSettings.selectedBuildTargetGroup, - newDefines - ); - - existingDefines = newDefines; - } - - // 1.2.4: Workaround to stop things from eating custom IGNORANCE_ symbols - static bool DoesSymbolExistInBlacklist(string symbol) - { - foreach(string s in DoNotRemoveTheseSymbols) - { - if (s == symbol.Trim()) return true; - } - - return false; - } - - static bool IsSafeToRemove (string input) - { - if (input.StartsWith("IGNORANCE") && !DoesSymbolExistInBlacklist(input)) return true; - return false; - } - } -} -#endif diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Editor/IgnoranceToolbox.cs b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Editor/IgnoranceToolbox.cs deleted file mode 100644 index dee6b62..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Editor/IgnoranceToolbox.cs +++ /dev/null @@ -1,44 +0,0 @@ -#if UNITY_EDITOR -using System.Collections.Generic; -using UnityEditor; - -namespace IgnoranceTransport -{ - public class IgnoranceToolbox - { -#pragma warning disable IDE0051 - [MenuItem("Ignorance/Mirror/Switch Update Method")] - public static void SwitchIgnoranceUpdateMethod () - { - - } - - [MenuItem("Ignorance/Debug/Reveal ENet Native Library Name")] - public static void RevealEnetLibraryName() - { - EditorUtility.DisplayDialog("Enet Library Name", $"Use this for debugging.\nYour platform expects the native Enet library to be called: {ENet.Native.nativeLibraryName}", "Got it"); - } - - [MenuItem("Ignorance/RTFM/Github Repository")] - private static void LaunchGithubRepo() - { - UnityEngine.Application.OpenURL("https://github.com/SoftwareGuy/Ignorance"); - } - - [MenuItem("Ignorance/RTFM/Github Issue Tracker")] - private static void LaunchGithubIssueTracker() - { - UnityEngine.Application.OpenURL("https://github.com/SoftwareGuy/Ignorance/issues"); - } - - [MenuItem("Ignorance/RTFM/ENet-CSharp Fork")] - private static void LaunchENetCSharpForkRepo() - { - UnityEngine.Application.OpenURL("https://github.com/SoftwareGuy/ENet-CSharp"); - } - - -#pragma warning restore - } -} -#endif diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Editor/IgnoranceToolbox.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Editor/IgnoranceToolbox.cs.meta deleted file mode 100644 index cf61ba3..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Editor/IgnoranceToolbox.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 1fdecc996313d614ca16214d4e2b9162 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Ignorance.cs b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Ignorance.cs deleted file mode 100644 index 28dbf7a..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Ignorance.cs +++ /dev/null @@ -1,746 +0,0 @@ -// Ignorance 1.4.x -// Ignorance. It really kicks the Unity LLAPIs ass. -// https://github.com/SoftwareGuy/Ignorance -// ----------------- -// Copyright (c) 2019 - 2020 Matt Coburn (SoftwareGuy/Coburn64) -// Ignorance Transport is licensed under the MIT license. Refer -// to the LICENSE file for more information. -// ----------------- -// Ignorance Experimental (New) Version -// ----------------- -using ENet; -using Mirror; -using System; -using System.Collections.Generic; -using UnityEngine; - -namespace IgnoranceTransport -{ - [DisallowMultipleComponent] - public class Ignorance : Transport - { - #region Inspector options - public int port = 7777; - - [Header("Debug & Logging Configuration")] - [Tooltip("How verbose do you want Ignorance to be?")] - public IgnoranceLogType LogType = IgnoranceLogType.Standard; - [Tooltip("Uses OnGUI to present you with statistics for Server and Client backend instances.")] - public bool DebugDisplay = false; - - [Header("Server Configuration")] - [Tooltip("Should the server bind to all interfaces?")] - public bool serverBindsAll = true; - [Tooltip("This is only used if Server Binds All is unticked.")] - public string serverBindAddress = string.Empty; - [Tooltip("This tells ENet how many Peer slots to create. Helps performance, avoids looping over huge native arrays. Recommended: Max Mirror players, rounded to nearest 10. (Example: 16 -> 20).")] - public int serverMaxPeerCapacity = 50; - [Tooltip("How long ENet waits in native world. The higher this value, the more CPU usage. Lower values may/may not impact performance at high packet load.")] - public int serverMaxNativeWaitTime = 1; - - [Header("Client Configuration")] - [Tooltip("How long ENet waits in native world. The higher this value, the more CPU usage used. This is for the client, unlike the one above. Higher value probably trades CPU for more responsive networking.")] - public int clientMaxNativeWaitTime = 3; - [Tooltip("Interval between asking ENet for client status updates. Set to -1 to disable.")] - public int clientStatusUpdateInterval = -1; - - [Header("Channel Configuration")] - [Tooltip("You must define your channels in the array shown here, otherwise ENet will not know what channel delivery type to use.")] - public IgnoranceChannelTypes[] Channels; - - [Header("Low-level Tweaking")] - [Tooltip("Used internally to keep allocations to a minimum. This is how much memory will be consumed by the packet buffer on startup, and then reused.")] - public int PacketBufferCapacity = 4096; - - [Tooltip("For UDP based protocols, it's best to keep your data under the safe MTU of 1200 bytes. You can increase this, however beware this may open you up to allocation attacks.")] - public int MaxAllowedPacketSize = 33554432; - #endregion - - #region Public Statistics - public IgnoranceClientStats ClientStatistics; - #endregion - -#if MIRROR_26_0_OR_NEWER - public override bool Available() - { - // Ignorance is not available for Unity WebGL, the PS4 (no dev kit to confirm) or Switch (port exists but I have no access to said code). - // Ignorance is available for most other operating systems. -#if (UNITY_WEBGL || UNITY_PS4 || UNITY_SWITCH) - return false; -#else - return true; -#endif - } - - public void Awake() - { - if (LogType != IgnoranceLogType.Nothing) - Debug.Log($"Thanks for using Ignorance {IgnoranceInternals.Version}. Keep up to date, report bugs and support the developer at https://github.com/SoftwareGuy/Ignorance!"); - } - - public override string ToString() - { - return $"Ignorance v{IgnoranceInternals.Version}"; - } - - public override void ClientConnect(string address) - { - ClientState = ConnectionState.Connecting; - cachedConnectionAddress = address; - - // Initialize. - InitializeClientBackend(); - - // Get going. - ignoreDataPackets = false; - - // Start! - Client.Start(); - } - - public override void ClientConnect(Uri uri) - { - if (uri.Scheme != IgnoranceInternals.Scheme) - throw new ArgumentException($"You used an invalid URI: {uri}. Please use {IgnoranceInternals.Scheme}://host:port instead", nameof(uri)); - - if (!uri.IsDefaultPort) - // Set the communication port to the one specified. - port = uri.Port; - - // Pass onwards to the proper handler. - ClientConnect(uri.Host); - } - - public override bool ClientConnected() => ClientState == ConnectionState.Connected; - - public override void ClientDisconnect() - { - if (Client != null) - Client.Stop(); - - // TODO: Figure this one out to see if it's related to a race condition. - // Maybe experiment with a while loop to pause main thread when disconnecting, - // since client might not stop on a dime. - // while(Client.IsAlive) ; - // v1.4.0b1: Probably fixed in IgnoranceClient.cs; need further testing. - - // ignoreDataPackets = true; - ClientState = ConnectionState.Disconnected; - } - -#if !MIRROR_37_0_OR_NEWER - public override void ClientSend(int channelId, ArraySegment segment) -#else - // v1.4.0b6: Mirror rearranged the ClientSend params, so we need to apply a fix for that or - // we end up using the obsoleted version. The obsolete version isn't a fatal error, but - // it's best to stick with the new structures. - public override void ClientSend(ArraySegment segment, int channelId) -#endif - { - if (Client == null) - { - Debug.LogError("Client object is null, this shouldn't really happen but it did..."); - return; - } - - if (channelId < 0 || channelId > Channels.Length) - { - Debug.LogError("Channel ID is out of bounds."); - return; - } - - // Create our struct... - Packet clientOutgoingPacket = default; - int byteCount = segment.Count; - int byteOffset = segment.Offset; - // Set our desired flags... - PacketFlags desiredFlags = (PacketFlags)Channels[channelId]; - - // Warn if over recommended MTU... - bool flagsSet = (desiredFlags & ReliableOrUnreliableFragmented) > 0; - - if (LogType != IgnoranceLogType.Nothing && byteCount > 1200 && !flagsSet) - Debug.LogWarning($"Warning: Client trying to send a Unreliable packet bigger than the recommended ENet 1200 byte MTU ({byteCount} > 1200). ENet will force Reliable Fragmented delivery."); - - // Create the packet. - clientOutgoingPacket.Create(segment.Array, byteOffset, byteCount + byteOffset, desiredFlags); - // byteCount - - // Enqueue the packet. - IgnoranceOutgoingPacket dispatchPacket = new IgnoranceOutgoingPacket - { - Channel = (byte)channelId, - Payload = clientOutgoingPacket - }; - - // Pass the packet onto the thread for dispatch. - Client.Outgoing.Enqueue(dispatchPacket); - } - - public override bool ServerActive() - { - // Very simple check. - return Server != null && Server.IsAlive; - } - -#if !MIRROR_37_0_OR_NEWER - // Workaround for legacy Mirror versions. - public override bool ServerDisconnect(int connectionId) => ServerDisconnectLegacy(connectionId); -#else - public override void ServerDisconnect(int connectionId) - { - if (Server == null) - { - Debug.LogError("Cannot enqueue kick packet; our Server object is null. Something has gone wrong."); - // Return here because otherwise we will get a NRE when trying to enqueue the kick packet. - return; - } - - IgnoranceCommandPacket kickPacket = new IgnoranceCommandPacket - { - Type = IgnoranceCommandType.ServerKickPeer, - PeerId = (uint)connectionId - 1 // ENet's native peer ID will be ConnID - 1 - }; - - // Pass the packet onto the thread for dispatch. - Server.Commands.Enqueue(kickPacket); - } -#endif - - public override string ServerGetClientAddress(int connectionId) - { - if (ConnectionLookupDict.TryGetValue(connectionId, out PeerConnectionData details)) - return $"{details.IP}:{details.Port}"; - - return "(unavailable)"; - } - -#if !MIRROR_37_0_OR_NEWER - public override void ServerSend(int connectionId, int channelId, ArraySegment segment) -#else - // v1.4.0b6: Mirror rearranged the ServerSend params, so we need to apply a fix for that or - // we end up using the obsoleted version. The obsolete version isn't a fatal error, but - // it's best to stick with the new structures. - public override void ServerSend(int connectionId, ArraySegment segment, int channelId) -#endif - { - // Debug.Log($"ServerSend({connectionId}, {channelId}, <{segment.Count} byte segment>)"); - - if (Server == null) - { - Debug.LogError("Cannot enqueue data packet; our Server object is null. Something has gone wrong."); - return; - } - - if (channelId < 0 || channelId > Channels.Length) - { - Debug.LogError("Channel ID is out of bounds."); - return; - } - - // Packet Struct - Packet serverOutgoingPacket = default; - int byteCount = segment.Count; - int byteOffset = segment.Offset; - PacketFlags desiredFlags = (PacketFlags)Channels[channelId]; - - // Warn if over recommended MTU - bool flagsSet = (desiredFlags & ReliableOrUnreliableFragmented) > 0; - - if (LogType != IgnoranceLogType.Nothing && byteCount > 1200 && !flagsSet) - Debug.LogWarning($"Warning: Server trying to send a Unreliable packet bigger than the recommended ENet 1200 byte MTU ({byteCount} > 1200). ENet will force Reliable Fragmented delivery."); - - // Create the packet. - serverOutgoingPacket.Create(segment.Array, byteOffset, byteCount + byteOffset, (PacketFlags)Channels[channelId]); - - // Enqueue the packet. - IgnoranceOutgoingPacket dispatchPacket = new IgnoranceOutgoingPacket - { - Channel = (byte)channelId, - NativePeerId = (uint)connectionId - 1, // ENet's native peer ID will be ConnID - 1 - Payload = serverOutgoingPacket - }; - - Server.Outgoing.Enqueue(dispatchPacket); - - } - - public override void ServerStart() - { - if (LogType != IgnoranceLogType.Nothing) - Debug.Log("Ignorance Server Instance starting up..."); - - InitializeServerBackend(); - - Server.Start(); - } - - public override void ServerStop() - { - if (Server != null) - { - if (LogType != IgnoranceLogType.Nothing) - Debug.Log("Ignorance Server Instance shutting down..."); - - Server.Stop(); - } - - ConnectionLookupDict.Clear(); - } - - public override Uri ServerUri() - { - UriBuilder builder = new UriBuilder - { - Scheme = IgnoranceInternals.Scheme, - Host = serverBindAddress, - Port = port - }; - - return builder.Uri; - } - - public override void Shutdown() - { - // TODO: Nothing needed here? - } - - // Check to ensure channels 0 and 1 mimic LLAPI. Override this at your own risk. - private void OnValidate() - { - if (Channels != null && Channels.Length >= 2) - { - // Check to make sure that Channel 0 and 1 are correct. - if (Channels[0] != IgnoranceChannelTypes.Reliable) - { - Debug.LogWarning("Please do not modify Ignorance Channel 0. The channel will be reset to Reliable delivery. If you need a channel with a different delivery, define and use it instead."); - Channels[0] = IgnoranceChannelTypes.Reliable; - } - if (Channels[1] != IgnoranceChannelTypes.Unreliable) - { - Debug.LogWarning("Please do not modify Ignorance Channel 1. The channel will be reset to Unreliable delivery. If you need a channel with a different delivery, define and use it instead."); - Channels[1] = IgnoranceChannelTypes.Unreliable; - } - } - else - { - Debug.LogWarning("Invalid Channels setting, fixing. If you've just added Ignorance to your NetworkManager GameObject, seeing this message is normal."); - Channels = new IgnoranceChannelTypes[2] - { - - IgnoranceChannelTypes.Reliable, - IgnoranceChannelTypes.Unreliable - }; - } - - // ENet only supports a maximum of 32MB packet size. - if (MaxAllowedPacketSize > 33554432) - MaxAllowedPacketSize = 33554432; - } - - private void InitializeServerBackend() - { - if (Server == null) - { - Debug.LogWarning("IgnoranceServer reference for Server mode was null. This shouldn't happen, but to be safe we'll reinitialize it."); - Server = new IgnoranceServer(); - } - - // Set up the new IgnoranceServer reference. - if (serverBindsAll) - // MacOS is special. It's also a massive thorn in my backside. - Server.BindAddress = IgnoranceInternals.BindAllMacs; - else - // Use the supplied bind address. - Server.BindAddress = serverBindAddress; - - // Sets port, maximum peers, max channels, the server poll time, maximum packet size and verbosity. - Server.BindPort = port; - Server.MaximumPeers = serverMaxPeerCapacity; - Server.MaximumChannels = Channels.Length; - Server.PollTime = serverMaxNativeWaitTime; - Server.MaximumPacketSize = MaxAllowedPacketSize; - Server.Verbosity = (int)LogType; - - // Initializes the packet buffer. - // Allocates once, that's it. - if (InternalPacketBuffer == null) - InternalPacketBuffer = new byte[PacketBufferCapacity]; - } - - private void InitializeClientBackend() - { - if (Client == null) - { - Debug.LogWarning("Ignorance: IgnoranceClient reference for Client mode was null. This shouldn't happen, but to be safe we'll reinitialize it."); - Client = new IgnoranceClient(); - } - - // Sets address, port, channels to expect, verbosity, the server poll time and maximum packet size. - Client.ConnectAddress = cachedConnectionAddress; - Client.ConnectPort = port; - Client.ExpectedChannels = Channels.Length; - Client.PollTime = clientMaxNativeWaitTime; - Client.MaximumPacketSize = MaxAllowedPacketSize; - Client.Verbosity = (int)LogType; - - // Initializes the packet buffer. - // Allocates once, that's it. - if (InternalPacketBuffer == null) - InternalPacketBuffer = new byte[PacketBufferCapacity]; - } - - private void ProcessServerPackets() - { - IgnoranceIncomingPacket incomingPacket; - IgnoranceConnectionEvent connectionEvent; - int adjustedConnectionId; - Packet payload; - - // Incoming connection events. - while (Server.ConnectionEvents.TryDequeue(out connectionEvent)) - { - adjustedConnectionId = (int)connectionEvent.NativePeerId + 1; - - if (LogType == IgnoranceLogType.Verbose) - Debug.Log($"Processing a server connection event from ENet native peer {connectionEvent.NativePeerId}. This peer would be Mirror ConnID {adjustedConnectionId}."); - - // TODO: Investigate ArgumentException: An item with the same key has already been added. Key: - ConnectionLookupDict.Add(adjustedConnectionId, new PeerConnectionData - { - NativePeerId = connectionEvent.NativePeerId, - IP = connectionEvent.IP, - Port = connectionEvent.Port - }); - - OnServerConnected?.Invoke(adjustedConnectionId); - } - - // Handle incoming data packets. - // Console.WriteLine($"Server Incoming Queue is {Server.Incoming.Count}"); - while (Server.Incoming.TryDequeue(out incomingPacket)) - { - adjustedConnectionId = (int)incomingPacket.NativePeerId + 1; - payload = incomingPacket.Payload; - - int length = payload.Length; - ArraySegment dataSegment; - - // Copy to working buffer and dispose of it. - if (length > InternalPacketBuffer.Length) - { - byte[] oneFreshNTastyGcAlloc = new byte[length]; - - payload.CopyTo(oneFreshNTastyGcAlloc); - dataSegment = new ArraySegment(oneFreshNTastyGcAlloc, 0, length); - } - else - { - payload.CopyTo(InternalPacketBuffer); - dataSegment = new ArraySegment(InternalPacketBuffer, 0, length); - } - - payload.Dispose(); - - OnServerDataReceived?.Invoke(adjustedConnectionId, dataSegment, incomingPacket.Channel); - - // Some messages can disable the transport - // If the transport was disabled by any of the messages, we have to break out of the loop and wait until we've been re-enabled. - if (!enabled) - break; - } - - // Disconnection events. - while (Server.DisconnectionEvents.TryDequeue(out IgnoranceConnectionEvent disconnectionEvent)) - { - adjustedConnectionId = (int)disconnectionEvent.NativePeerId + 1; - - if (LogType == IgnoranceLogType.Verbose) - Debug.Log($"ProcessServerPackets fired; handling disconnection event from native peer {disconnectionEvent.NativePeerId}."); - - ConnectionLookupDict.Remove(adjustedConnectionId); - - // Invoke Mirror handler. - OnServerDisconnected?.Invoke(adjustedConnectionId); - } - } - - private void ProcessClientPackets() - { - IgnoranceIncomingPacket incomingPacket; - IgnoranceCommandPacket commandPacket; - IgnoranceClientStats clientStats; - Packet payload; - IgnoranceConnectionEvent connectionEvent; - - // Handle connection events. - while (Client.ConnectionEvents.TryDequeue(out connectionEvent)) - { - if (LogType == IgnoranceLogType.Verbose) - Debug.Log($"ProcessClientConnectionEvents fired: processing a client ConnectionEvents queue item."); - - if (connectionEvent.WasDisconnect) - { - // Disconnected from server. - ClientState = ConnectionState.Disconnected; - - if (LogType != IgnoranceLogType.Nothing) - Debug.Log($"Ignorance Client has been disconnected from server."); - - ignoreDataPackets = true; - OnClientDisconnected?.Invoke(); - } - else - { - // Connected to server. - ClientState = ConnectionState.Connected; - - if (LogType != IgnoranceLogType.Nothing) - Debug.Log($"Ignorance Client successfully connected to server at address {connectionEvent.IP}:{connectionEvent.Port}"); - - ignoreDataPackets = false; - OnClientConnected?.Invoke(); - } - } - - // Now handle the incoming messages. - while (Client.Incoming.TryDequeue(out incomingPacket)) - { - // Temporary fix: if ENet thread is too fast for Mirror, then ignore the packet. - // This is seen sometimes if you stop the client and there's still stuff in the queue. - if (ignoreDataPackets) - { - if (LogType == IgnoranceLogType.Verbose) - Debug.Log("ProcessClientPackets cycle skipped; ignoring data packet"); - break; - } - - - // Otherwise client recieved data, advise Mirror. - // print($"Byte array: {incomingPacket.RentedByteArray.Length}. Packet Length: {incomingPacket.Length}"); - payload = incomingPacket.Payload; - int length = payload.Length; - ArraySegment dataSegment; - - // Copy to working buffer and dispose of it. - if (length > InternalPacketBuffer.Length) - { - // Unity's favourite: A fresh 'n' tasty GC Allocation! - byte[] oneFreshNTastyGcAlloc = new byte[length]; - - payload.CopyTo(oneFreshNTastyGcAlloc); - dataSegment = new ArraySegment(oneFreshNTastyGcAlloc, 0, length); - } - else - { - payload.CopyTo(InternalPacketBuffer); - dataSegment = new ArraySegment(InternalPacketBuffer, 0, length); - } - - payload.Dispose(); - - OnClientDataReceived?.Invoke(dataSegment, incomingPacket.Channel); - - // Some messages can disable the transport - // If the transport was disabled by any of the messages, we have to break out of the loop and wait until we've been re-enabled. - if (!enabled) - break; - } - - // Step 3: Handle other commands. - while (Client.Commands.TryDequeue(out commandPacket)) - { - switch (commandPacket.Type) - { - // ... - default: - break; - } - } - - // Step 4: Handle status updates. - if (Client.StatusUpdates.TryDequeue(out clientStats)) - { - ClientStatistics = clientStats; - } - } - - #region Main Thread Processing and Polling - // Ignorance 1.4.0b5: To use Mirror's polling or not use Mirror's polling, that is up to the developer to decide -#if !IGNORANCE_MIRROR_POLLING - // IMPORTANT: Set Ignorance' execution order before everything else. Yes, that's -32000 !! - // This ensures it has priority over other things. - - // FixedUpdate can be called many times per frame. - // Once we've handled stuff, we set a flag so that we don't poll again for this frame. - - private bool fixedUpdateCompletedWork; - public void FixedUpdate() - { - if (!enabled) return; - if (fixedUpdateCompletedWork) return; - - ProcessAndExecuteAllPackets(); - - // Flip the bool to signal we've done our work. - fixedUpdateCompletedWork = true; - } - - // Normally, Mirror blocks Update() due to poor design decisions... - // But thanks to Vincenzo, we've found a way to bypass that block. - // Update is called once per frame. We don't have to worry about this shit now. - public new void Update() - { - if (!enabled) return; - - // Process what FixedUpdate missed, only if the boolean is not set. - if (!fixedUpdateCompletedWork) - ProcessAndExecuteAllPackets(); - - // Flip back the bool, so it can be reset. - fixedUpdateCompletedWork = false; - } - - // Processes and Executes All Packets. - private void ProcessAndExecuteAllPackets() - { - // Process Server Events... - if (Server.IsAlive) - ProcessServerPackets(); - - // Process Client Events... - if (Client.IsAlive) - { - ProcessClientPackets(); - - if (ClientState == ConnectionState.Connected && clientStatusUpdateInterval > -1) - { - statusUpdateTimer += Time.deltaTime; - - if (statusUpdateTimer >= clientStatusUpdateInterval) - { - Client.Commands.Enqueue(new IgnoranceCommandPacket { Type = IgnoranceCommandType.ClientRequestsStatusUpdate }); - statusUpdateTimer = 0f; - } - } - } - } -#else - // This section will be compiled in instead if you enable IGNORANCE_MIRROR_POLLING. - - public override void ServerEarlyUpdate() { - // This is used by Mirror to consume the incoming server packets. - if (!enabled) return; - - // Process Server Events... - if (Server.IsAlive) - ProcessServerPackets(); - } - - public override void ClientEarlyUpdate() { - // This is used by Mirror to consume the incoming client packets. - if(!enabled) return; - - if(Client.IsAlive) - { - ProcessClientPackets(); - - if (ClientState == ConnectionState.Connected && clientStatusUpdateInterval > -1) - { - statusUpdateTimer += Time.deltaTime; - - if (statusUpdateTimer >= clientStatusUpdateInterval) - { - Client.Commands.Enqueue(new IgnoranceCommandPacket { Type = IgnoranceCommandType.ClientRequestsStatusUpdate }); - statusUpdateTimer = 0f; - } - } - } - - } - - /* - public override void ClientLateUpdate() { - // This is used by Mirror to send out the outgoing client packets. - if (!enabled) return; - } - - public override void ServerLateUpdate() { - // This is used by Mirror to send out the outgoing server packets. - if (!enabled) return; - } - */ -#endif - #endregion - - #region Debug - private void OnGUI() - { - if (DebugDisplay) - GUI.Box(new Rect( - new Vector2(32, Screen.height - 240), new Vector2(200, 160)), - - "-- CLIENT --\n" + - $"State: {ClientState}\n" + - $"Incoming Queue: {Client.Incoming.Count}\n" + - $"Outgoing Queue: {Client.Outgoing.Count}\n\n" + - - "-- SERVER --\n" + - $"Incoming Queue: {Server.Incoming.Count}\n" + - $"Outgoing Queue: {Server.Outgoing.Count}\n" + - $"ConnEvent Queue: {Server.ConnectionEvents.Count}" - ); - } - #endregion - - public override int GetMaxPacketSize(int channelId = 0) => MaxAllowedPacketSize; - - // UDP Recommended Max MTU = 1200. - public override int GetMaxBatchSize(int channelId) { - bool isFragmentedAlready = ((PacketFlags)Channels[channelId] & ReliableOrUnreliableFragmented) > 0; - return isFragmentedAlready ? MaxAllowedPacketSize : 1200; - } - - #region Internals - private bool ignoreDataPackets; - private string cachedConnectionAddress = string.Empty; - private IgnoranceServer Server = new IgnoranceServer(); - private IgnoranceClient Client = new IgnoranceClient(); - private Dictionary ConnectionLookupDict = new Dictionary(); - - private enum ConnectionState { Connecting, Connected, Disconnecting, Disconnected } - private ConnectionState ClientState = ConnectionState.Disconnected; - private byte[] InternalPacketBuffer; - - private const PacketFlags ReliableOrUnreliableFragmented = PacketFlags.Reliable | PacketFlags.UnreliableFragmented; - - private float statusUpdateTimer = 0f; - #endregion - - #region Legacy Overrides -#if !MIRROR_37_0_OR_NEWER - public bool ServerDisconnectLegacy(int connectionId) - { - if (Server == null) - { - Debug.LogError("Cannot enqueue kick packet; our Server object is null. Something has gone wrong."); - // Return here because otherwise we will get a NRE when trying to enqueue the kick packet. - return false; - } - - IgnoranceCommandPacket kickPacket = new IgnoranceCommandPacket - { - Type = IgnoranceCommandType.ServerKickPeer, - PeerId = (uint)connectionId - 1 // ENet's native peer ID will be ConnID - 1 - }; - - // Pass the packet onto the thread for dispatch. - Server.Commands.Enqueue(kickPacket); - return true; - } -#endif - #endregion -#endif - - } -} diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Ignorance.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Ignorance.cs.meta deleted file mode 100644 index 9703c34..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Ignorance.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 872fa23ef6e77334ca452ce16f6cd091 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: -32000 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/IgnoranceDefinitions.cs b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/IgnoranceDefinitions.cs deleted file mode 100644 index ca677ac..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/IgnoranceDefinitions.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using ENet; - -namespace IgnoranceTransport -{ - // Snipped from the transport files, as this will help - // me keep things up to date. - [Serializable] - public enum IgnoranceChannelTypes - { - Reliable = PacketFlags.Reliable, // TCP Emulation. - ReliableUnsequenced = PacketFlags.Reliable | PacketFlags.Unsequenced, // TCP Emulation, but no sequencing. - Unreliable = PacketFlags.Unsequenced, // Pure UDP. - UnreliableFragmented = PacketFlags.UnreliableFragmented, // Pure UDP, but fragmented. - UnreliableSequenced = PacketFlags.None, // Pure UDP, but sequenced. - Unthrottled = PacketFlags.Unthrottled, // Apparently ENet's version of Taco Bell. - } - - public class IgnoranceInternals - { - public const string Version = "1.4.0b6"; - public const string Scheme = "enet"; - public const string BindAllIPv4 = "0.0.0.0"; - public const string BindAllMacs = "::0"; - } - - public enum IgnoranceLogType - { - Nothing, - Standard, - Verbose - } - - // Struct optimized for cache efficiency. (Thanks Vincenzo!) - public struct IgnoranceIncomingPacket - { - public byte Channel; - public uint NativePeerId; - public Packet Payload; - } - - // Struct optimized for cache efficiency. (Thanks Vincenzo!) - public struct IgnoranceOutgoingPacket - { - public byte Channel; - public uint NativePeerId; - public Packet Payload; - } - - // Struct optimized for cache efficiency. (Thanks Vincenzo!) - public struct IgnoranceConnectionEvent - { - public bool WasDisconnect; - public ushort Port; - public uint NativePeerId; - public string IP; - } - - public struct IgnoranceCommandPacket - { - public IgnoranceCommandType Type; - public uint PeerId; - } - - public struct IgnoranceClientStats - { - // Stats only - may not always be used! - public uint RTT; - public ulong BytesReceived; - public ulong BytesSent; - public ulong PacketsReceived; - public ulong PacketsSent; - public ulong PacketsLost; - } - - public enum IgnoranceCommandType - { - // Client - ClientWantsToStop, - ClientRequestsStatusUpdate, - // ENet internal - ResponseToClientStatusRequest, - // Server - ServerKickPeer - } - - // TODO: Optimize struct for Cache performance. - public struct PeerConnectionData - { - public ushort Port; - public uint NativePeerId; - public string IP; - } -} diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/IgnoranceDefinitions.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/IgnoranceDefinitions.cs.meta deleted file mode 100644 index 444855c..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/IgnoranceDefinitions.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 3b2d1f7f7d9d3d64297755419f6ab925 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins.meta deleted file mode 100644 index def60ed..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: e8bd04b9965420e40ac911fbf4294e1c -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android.meta deleted file mode 100644 index f51f451..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: c90c88052305a054d87177362f63dea8 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/arm64-v8a.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/arm64-v8a.meta deleted file mode 100644 index 738b943..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/arm64-v8a.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: e4a42b5855436864693138f74335c094 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/arm64-v8a/libenet.so b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/arm64-v8a/libenet.so deleted file mode 100644 index 5a48a3b..0000000 Binary files a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/arm64-v8a/libenet.so and /dev/null differ diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/arm64-v8a/libenet.so.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/arm64-v8a/libenet.so.meta deleted file mode 100644 index 63cd799..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/arm64-v8a/libenet.so.meta +++ /dev/null @@ -1,111 +0,0 @@ -fileFormatVersion: 2 -guid: 4b3abd2268dea254da11190b00d16051 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Android: 0 - Exclude Editor: 1 - Exclude Linux: 1 - Exclude Linux64: 1 - Exclude LinuxUniversal: 1 - Exclude OSXUniversal: 1 - Exclude WebGL: 1 - Exclude Win: 1 - Exclude Win64: 1 - Exclude iOS: 1 - - first: - Android: Android - second: - enabled: 1 - settings: - CPU: ARM64 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - CPU: AnyCPU - DefaultValueInitialized: true - OS: AnyOS - - first: - Facebook: Win - second: - enabled: 0 - settings: - CPU: AnyCPU - - first: - Facebook: Win64 - second: - enabled: 0 - settings: - CPU: AnyCPU - - first: - Standalone: Linux - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: LinuxUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: AnyCPU - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: AnyCPU - - first: - WebGL: WebGL - second: - enabled: 0 - settings: {} - - first: - iPhone: iOS - second: - enabled: 0 - settings: - AddToEmbeddedBinaries: false - CompileFlags: - FrameworkDependencies: - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/armeabi-v7a.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/armeabi-v7a.meta deleted file mode 100644 index bbb65c6..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/armeabi-v7a.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: bfbaf4fbcf8987e4585ca666e28f4871 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/armeabi-v7a/libenet.so b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/armeabi-v7a/libenet.so deleted file mode 100644 index e8be734..0000000 Binary files a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/armeabi-v7a/libenet.so and /dev/null differ diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/armeabi-v7a/libenet.so.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/armeabi-v7a/libenet.so.meta deleted file mode 100644 index 07ea880..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/armeabi-v7a/libenet.so.meta +++ /dev/null @@ -1,106 +0,0 @@ -fileFormatVersion: 2 -guid: e3ce4b8600e09d74ead46137ba184d01 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Android: 0 - Exclude Editor: 1 - Exclude Linux: 1 - Exclude Linux64: 1 - Exclude LinuxUniversal: 1 - Exclude OSXUniversal: 1 - Exclude WebGL: 1 - Exclude Win: 1 - Exclude Win64: 1 - Exclude iOS: 1 - - first: - Android: Android - second: - enabled: 1 - settings: - CPU: ARMv7 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - CPU: AnyCPU - DefaultValueInitialized: true - OS: AnyOS - - first: - Facebook: Win - second: - enabled: 0 - settings: - CPU: AnyCPU - - first: - Facebook: Win64 - second: - enabled: 0 - settings: - CPU: AnyCPU - - first: - Standalone: Linux - second: - enabled: 0 - settings: - CPU: x86 - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: AnyCPU - - first: - Standalone: LinuxUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: AnyCPU - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: AnyCPU - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: AnyCPU - - first: - iPhone: iOS - second: - enabled: 0 - settings: - AddToEmbeddedBinaries: false - CompileFlags: - FrameworkDependencies: - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/x86.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/x86.meta deleted file mode 100644 index 4491214..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/x86.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: b3486c84aaebff4489e2e11f5bd3030f -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/x86/libenet.so b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/x86/libenet.so deleted file mode 100644 index 391f83e..0000000 Binary files a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/x86/libenet.so and /dev/null differ diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/x86/libenet.so.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/x86/libenet.so.meta deleted file mode 100644 index c57aeaa..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Android/x86/libenet.so.meta +++ /dev/null @@ -1,80 +0,0 @@ -fileFormatVersion: 2 -guid: ba78b07719f786c4cae4c52b9528903f -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Android: 0 - Exclude Editor: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude Win: 1 - Exclude Win64: 1 - Exclude iOS: 1 - - first: - Android: Android - second: - enabled: 1 - settings: - CPU: x86 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - CPU: AnyCPU - DefaultValueInitialized: true - OS: AnyOS - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - - first: - iPhone: iOS - second: - enabled: 0 - settings: - AddToEmbeddedBinaries: false - CPU: AnyCPU - CompileFlags: - FrameworkDependencies: - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Linux.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Linux.meta deleted file mode 100644 index f1aae5d..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Linux.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 8e3ca9b41c9f4064581a6b56ae549a9c -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Linux/libenet.so b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Linux/libenet.so deleted file mode 100644 index c64bfb7..0000000 Binary files a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Linux/libenet.so and /dev/null differ diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Linux/libenet.so.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Linux/libenet.so.meta deleted file mode 100644 index d60a26a..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Linux/libenet.so.meta +++ /dev/null @@ -1,97 +0,0 @@ -fileFormatVersion: 2 -guid: f2bd341c8082ddd47b4b5cc9bfdf2332 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - '': Any - second: - enabled: 0 - settings: - Exclude Android: 1 - Exclude Editor: 0 - Exclude Linux: 1 - Exclude Linux64: 0 - Exclude LinuxUniversal: 0 - Exclude OSXUniversal: 1 - Exclude WebGL: 1 - Exclude Win: 0 - Exclude Win64: 0 - - first: - Android: Android - second: - enabled: 0 - settings: - CPU: ARMv7 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 1 - settings: - CPU: x86_64 - DefaultValueInitialized: true - OS: Linux - - first: - Facebook: Win - second: - enabled: 0 - settings: - CPU: AnyCPU - - first: - Facebook: Win64 - second: - enabled: 0 - settings: - CPU: AnyCPU - - first: - Standalone: Linux - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Linux64 - second: - enabled: 1 - settings: - CPU: x86_64 - - first: - Standalone: LinuxUniversal - second: - enabled: 1 - settings: - CPU: x86_64 - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 1 - settings: - CPU: AnyCPU - - first: - Standalone: Win64 - second: - enabled: 1 - settings: - CPU: AnyCPU - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Windows.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Windows.meta deleted file mode 100644 index 06c1b96..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Windows.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 8837cb1a7320b8b4f824a02e23f4a545 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Windows/README.txt b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Windows/README.txt deleted file mode 100644 index 1bb2563..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Windows/README.txt +++ /dev/null @@ -1,3 +0,0 @@ -This is a Windows x64 build of ENet-CSharp. - -If you require a version of ENet for 32 Bit computer systems (ie. Windows 7/8/10 32Bit) then please get in touch, or you can install the requirements yourself and compile it using ENet-CSharp's MSBuild-based build system. Get in touch if you want me to compile them for you, but keep in mind that I do not support them when reporting bugs. \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Windows/enet.dll b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Windows/enet.dll deleted file mode 100644 index f75351b..0000000 Binary files a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Windows/enet.dll and /dev/null differ diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Windows/enet.dll.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Windows/enet.dll.meta deleted file mode 100644 index 99fefbb..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Windows/enet.dll.meta +++ /dev/null @@ -1,111 +0,0 @@ -fileFormatVersion: 2 -guid: cc98033c6bc234a419ccd053e7173781 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - '': Any - second: - enabled: 0 - settings: - Exclude Android: 1 - Exclude Editor: 0 - Exclude Linux: 0 - Exclude Linux64: 0 - Exclude LinuxUniversal: 0 - Exclude OSXUniversal: 0 - Exclude WebGL: 1 - Exclude Win: 1 - Exclude Win64: 0 - Exclude iOS: 1 - - first: - Android: Android - second: - enabled: 0 - settings: - CPU: ARMv7 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 1 - settings: - CPU: AnyCPU - DefaultValueInitialized: true - OS: AnyOS - - first: - Facebook: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Facebook: Win64 - second: - enabled: 0 - settings: - CPU: AnyCPU - - first: - Standalone: Linux - second: - enabled: 1 - settings: - CPU: x86 - - first: - Standalone: Linux64 - second: - enabled: 1 - settings: - CPU: x86_64 - - first: - Standalone: LinuxUniversal - second: - enabled: 1 - settings: - CPU: AnyCPU - - first: - Standalone: OSXUniversal - second: - enabled: 1 - settings: - CPU: AnyCPU - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 1 - settings: - CPU: AnyCPU - - first: - WebGL: WebGL - second: - enabled: 0 - settings: {} - - first: - iPhone: iOS - second: - enabled: 0 - settings: - AddToEmbeddedBinaries: false - CompileFlags: - FrameworkDependencies: - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS.meta deleted file mode 100644 index b6beac0..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 7edc275f3670f4251b90301cbb9f7413 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS/libenet-release-arm64.a b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS/libenet-release-arm64.a deleted file mode 100644 index 0d88385..0000000 Binary files a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS/libenet-release-arm64.a and /dev/null differ diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS/libenet-release-arm64.a.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS/libenet-release-arm64.a.meta deleted file mode 100644 index 1504880..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS/libenet-release-arm64.a.meta +++ /dev/null @@ -1,80 +0,0 @@ -fileFormatVersion: 2 -guid: 1485de7d76884a64bac00df01454081e -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Android: 1 - Exclude Editor: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude Win: 1 - Exclude Win64: 1 - Exclude iOS: 0 - - first: - Android: Android - second: - enabled: 0 - settings: - CPU: ARMv7 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - CPU: AnyCPU - DefaultValueInitialized: true - OS: AnyOS - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - - first: - iPhone: iOS - second: - enabled: 1 - settings: - AddToEmbeddedBinaries: false - CPU: ARM64 - CompileFlags: - FrameworkDependencies: - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS/libenet-release-armv7.a b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS/libenet-release-armv7.a deleted file mode 100644 index d8b77ba..0000000 Binary files a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS/libenet-release-armv7.a and /dev/null differ diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS/libenet-release-armv7.a.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS/libenet-release-armv7.a.meta deleted file mode 100644 index aa56177..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS/libenet-release-armv7.a.meta +++ /dev/null @@ -1,80 +0,0 @@ -fileFormatVersion: 2 -guid: fa239cb4c47fc9447816815f80667fea -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Android: 1 - Exclude Editor: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude Win: 1 - Exclude Win64: 1 - Exclude iOS: 0 - - first: - Android: Android - second: - enabled: 0 - settings: - CPU: ARMv7 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - CPU: AnyCPU - DefaultValueInitialized: true - OS: AnyOS - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - - first: - iPhone: iOS - second: - enabled: 1 - settings: - AddToEmbeddedBinaries: false - CPU: ARMv7 - CompileFlags: - FrameworkDependencies: - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS/libenet-release-simulator64.a b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS/libenet-release-simulator64.a deleted file mode 100644 index e6051c3..0000000 Binary files a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS/libenet-release-simulator64.a and /dev/null differ diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS/libenet-release-simulator64.a.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS/libenet-release-simulator64.a.meta deleted file mode 100644 index 7106afb..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/iOS/libenet-release-simulator64.a.meta +++ /dev/null @@ -1,80 +0,0 @@ -fileFormatVersion: 2 -guid: 385bb48fb124b5f40a79bdecf421671f -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Android: 1 - Exclude Editor: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude Win: 1 - Exclude Win64: 1 - Exclude iOS: 0 - - first: - Android: Android - second: - enabled: 0 - settings: - CPU: ARMv7 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - CPU: AnyCPU - DefaultValueInitialized: true - OS: AnyOS - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - - first: - iPhone: iOS - second: - enabled: 1 - settings: - AddToEmbeddedBinaries: false - CPU: X64 - CompileFlags: - FrameworkDependencies: - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/macOS.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/macOS.meta deleted file mode 100644 index b4c703f..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/macOS.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 957ba76da095cd64aaa658f034ab78e5 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/macOS/libenet.dylib b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/macOS/libenet.dylib deleted file mode 100644 index da377b7..0000000 Binary files a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/macOS/libenet.dylib and /dev/null differ diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/macOS/libenet.dylib.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/macOS/libenet.dylib.meta deleted file mode 100644 index 92fe418..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/macOS/libenet.dylib.meta +++ /dev/null @@ -1,32 +0,0 @@ -fileFormatVersion: 2 -guid: 5317859893ad2cf48a6df1d585ebdd2c -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 1 - settings: - DefaultValueInitialized: true - - first: - Standalone: OSXUniversal - second: - enabled: 1 - settings: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/readme.txt b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/readme.txt deleted file mode 100644 index 66f084a..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/readme.txt +++ /dev/null @@ -1,35 +0,0 @@ -ENET Pre-compiled Binary Library Blobs -========================== -This folder contains pre-compiled binaries for a variety of different platforms. - -A brief summary of these folders are as follows: - -- Windows, Mac, Linux --- 64bit (x64) - -- Android (Kitkat 4.4 minimum target OS) --- ARMv7 (armeabi-v7a), ARMv8/AArch64 (arm64-v8a) - -- iOS --- FAT Library (armv7 + arm64). Targeted for iOS 8 minimum. Unsigned library. - -DEBUG VERSIONS -=============== -Debug versions of the libraries can be obtained at https://github.com/SoftwareGuy/ENet-CSharp/releases. -Otherwise you can also compile the library yourself with Debug enabled. - -DOT POINTS -=========== -1. 32bit Support for Ignorance has been removed. Originally, I did not want to support 32bit operating systems, -however due to some countries in the world still stuck in the 32bit era (Brasil, some Russian areas, etc) I added them as a -goodwill gesture. However, since those who needed the libraries have now vanished, I have stopped building 32bit versions of ENet. - -COMPILE THE CODE YOURSELF -========================= -If you don't trust the above binaries then git clone the ENET-CSharp repository (http://github.com/SoftwareGuy/ENet-CSharp) and read the readme. - -EXCLUSION INSTRUCTIONS -====================== -No need, the meta data will cover that for you. - -Still don't know what to do with these? Drop by the Mirror discord and post in the Ignorance channel. \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/version.txt b/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/version.txt deleted file mode 100644 index e14bdc8..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/version.txt +++ /dev/null @@ -1 +0,0 @@ -1.4.0b6 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP.meta deleted file mode 100644 index 3577324..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 953bb5ec5ab2346a092f58061e01ba65 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/MirrorTransport.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/MirrorTransport.meta deleted file mode 100644 index 8fbdc54..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/MirrorTransport.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 7bdb797750d0a490684410110bf48192 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k.meta deleted file mode 100644 index 95f3484..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 71a1c8e8c022d4731a481c1808f37e5d -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/LICENSE.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/LICENSE.meta deleted file mode 100644 index 70f4468..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/LICENSE.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 9a3e8369060cf4e94ac117603de47aa6 -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/VERSION.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/VERSION.meta deleted file mode 100644 index cd9d20d..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/VERSION.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: ed3f2cf1bbf1b4d53a6f2c103d311f71 -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel.meta deleted file mode 100644 index a9e212c..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 5a54d18b954cb4407a28b633fc32ea6d -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpChannel.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpChannel.cs.meta deleted file mode 100644 index 1889c6c..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpChannel.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 9e852b2532fb248d19715cfebe371db3 -timeCreated: 1610081248 diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpClient.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpClient.cs.meta deleted file mode 100644 index b972397..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpClient.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 6aa069a28ed24fedb533c102d9742b36 -timeCreated: 1603786960 diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpClientConnection.cs b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpClientConnection.cs deleted file mode 100644 index 914ca48..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpClientConnection.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Net; -using System.Net.Sockets; -using WhereAllocation; - -namespace kcp2k -{ - public class KcpClientConnection : KcpConnection - { - // IMPORTANT: raw receive buffer always needs to be of 'MTU' size, even - // if MaxMessageSize is larger. kcp always sends in MTU - // segments and having a buffer smaller than MTU would - // silently drop excess data. - // => we need the MTU to fit channel + message! - readonly byte[] rawReceiveBuffer = new byte[Kcp.MTU_DEF]; - - // where-allocation - IPEndPointNonAlloc reusableEP; - - public void Connect(string host, ushort port, bool noDelay, uint interval = Kcp.INTERVAL, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = Kcp.WND_SND, uint receiveWindowSize = Kcp.WND_RCV, int timeout = DEFAULT_TIMEOUT) - { - Log.Info($"KcpClient: connect to {host}:{port}"); - IPAddress[] ipAddress = Dns.GetHostAddresses(host); - if (ipAddress.Length < 1) - throw new SocketException((int)SocketError.HostNotFound); - - remoteEndpoint = new IPEndPoint(ipAddress[0], port); - socket = new Socket(remoteEndpoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); - - // create reusableEP with same address family as remoteEndPoint. - // otherwise ReceiveFrom_NonAlloc couldn't use it. - reusableEP = new IPEndPointNonAlloc(ipAddress[0], port); - - socket.Connect(remoteEndpoint); - SetupKcp(noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout); - - // client should send handshake to server as very first message - SendHandshake(); - - RawReceive(); - } - - // call from transport update - public void RawReceive() - { - try - { - if (socket != null) - { - while (socket.Poll(0, SelectMode.SelectRead)) - { - // where-allocation: receive nonalloc. - int msgLength = socket.ReceiveFrom_NonAlloc(rawReceiveBuffer, reusableEP); - // IMPORTANT: detect if buffer was too small for the - // received msgLength. otherwise the excess - // data would be silently lost. - // (see ReceiveFrom documentation) - if (msgLength <= rawReceiveBuffer.Length) - { - //Log.Debug($"KCP: client raw recv {msgLength} bytes = {BitConverter.ToString(buffer, 0, msgLength)}"); - RawInput(rawReceiveBuffer, msgLength); - } - else - { - Log.Error($"KCP ClientConnection: message of size {msgLength} does not fit into buffer of size {rawReceiveBuffer.Length}. The excess was silently dropped. Disconnecting."); - Disconnect(); - } - } - } - } - // this is fine, the socket might have been closed in the other end - catch (SocketException) {} - } - - protected override void Dispose() - { - socket.Close(); - socket = null; - } - - protected override void RawSend(byte[] data, int length) - { - socket.Send(data, length, SocketFlags.None); - } - } -} diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpClientConnection.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpClientConnection.cs.meta deleted file mode 100644 index 7995890..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpClientConnection.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 96512e74aa8214a6faa8a412a7a07877 -timeCreated: 1602601237 diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpConnection.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpConnection.cs.meta deleted file mode 100644 index 9c41eb5..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpConnection.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 3915c7c62b72d4dc2a9e4e76c94fc484 -timeCreated: 1602600432 diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpHeader.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpHeader.cs.meta deleted file mode 100644 index 6d12b55..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpHeader.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 91b5edac31224a49bd76f960ae018942 -timeCreated: 1610081248 diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpServer.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpServer.cs.meta deleted file mode 100644 index 509aca2..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpServer.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 9759159c6589494a9037f5e130a867ed -timeCreated: 1603787747 diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpServerConnection.cs b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpServerConnection.cs deleted file mode 100644 index 295845e..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpServerConnection.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Net; -using System.Net.Sockets; -using WhereAllocation; - -namespace kcp2k -{ - public class KcpServerConnection : KcpConnection - { - IPEndPointNonAlloc reusableSendEndPoint; - - public KcpServerConnection(Socket socket, EndPoint remoteEndPoint, IPEndPointNonAlloc reusableSendEndPoint, bool noDelay, uint interval = Kcp.INTERVAL, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = Kcp.WND_SND, uint receiveWindowSize = Kcp.WND_RCV, int timeout = DEFAULT_TIMEOUT) - { - this.socket = socket; - this.remoteEndpoint = remoteEndPoint; - this.reusableSendEndPoint = reusableSendEndPoint; - SetupKcp(noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout); - } - - protected override void RawSend(byte[] data, int length) - { - // where-allocation nonalloc - socket.SendTo_NonAlloc(data, 0, length, SocketFlags.None, reusableSendEndPoint); - } - } -} diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpServerConnection.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpServerConnection.cs.meta deleted file mode 100644 index 32b97c6..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpServerConnection.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 80a9b1ce9a6f14abeb32bfa9921d097b -timeCreated: 1602601483 diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/Log.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/Log.cs.meta deleted file mode 100644 index bdc1fa5..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/Log.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 7b5e1de98d6d84c3793a61cf7d8da9a4 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp.meta deleted file mode 100644 index 32121b3..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 5cafb8851a0084f3e94a580c207b3923 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/AssemblyInfo.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/AssemblyInfo.cs.meta deleted file mode 100644 index 1f0cec5..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/AssemblyInfo.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: aec6a15ac7bd43129317ea1f01f19782 -timeCreated: 1602665988 diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Pool.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Pool.cs.meta deleted file mode 100644 index 0f4bd57..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Pool.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 35c07818fc4784bb4ba472c8e5029002 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Utils.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Utils.cs.meta deleted file mode 100644 index 0b5a45b..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Utils.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ef959eb716205bd48b050f010a9a35ae -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation.meta deleted file mode 100644 index 229506b..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: e9de45e025f26411bbb52d1aefc8d5a5 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/LICENSE.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/LICENSE.meta deleted file mode 100644 index f8b7f34..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/LICENSE.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: a857d4e863bbf4a7dba70bc2cd1b5949 -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts.meta deleted file mode 100644 index 91f78ee..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 6b7f3f8e8fa16475bbe48a8e9fbe800b -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/AssemblyInfo.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/AssemblyInfo.cs.meta deleted file mode 100644 index f7bc149..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/AssemblyInfo.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 158a96a7489b450485a8b06a13328871 -timeCreated: 1622356221 diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/Extensions.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/Extensions.cs.meta deleted file mode 100644 index 9ac6767..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/Extensions.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 9e801942544d44d65808fb250623fe25 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/VERSION.meta b/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/VERSION.meta deleted file mode 100644 index 5e5fd6a..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/VERSION.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: f1256cadc037546ccb66071784fce137 -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/LRM/LRMDirectConnectModule.cs b/UnityProject/Assets/Mirror/Runtime/Transport/LRM/LRMDirectConnectModule.cs index e89fbe3..e82f89e 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/LRM/LRMDirectConnectModule.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transport/LRM/LRMDirectConnectModule.cs @@ -166,10 +166,10 @@ public class LRMDirectConnectModule : MonoBehaviour lightMirrorTransport.DirectRemoveClient(clientID); } - void OnServerError(int client, Exception error) + void OnServerError(int connectionId, TransportError transportError, string reason) { if (showDebugLogs) - Debug.Log("Direct Server Error: " + error); + Debug.Log($"Direct Server Error: {transportError} Reason: {reason}"); } void OnClientConnected() @@ -190,10 +190,10 @@ public class LRMDirectConnectModule : MonoBehaviour lightMirrorTransport.DirectReceiveData(data, channel); } - void OnClientError(Exception error) + void OnClientError(TransportError transportError,string reason) { if (showDebugLogs) - Debug.Log("Direct Client Error: " + error); + Debug.Log($"Direct Client Error: {transportError} Reason: {reason}"); } #endregion } \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/LRM/LRMTransport/LightReflectiveMirrorTransport.cs b/UnityProject/Assets/Mirror/Runtime/Transport/LRM/LRMTransport/LightReflectiveMirrorTransport.cs index 5706909..b4c5ed8 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/LRM/LRMTransport/LightReflectiveMirrorTransport.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transport/LRM/LRMTransport/LightReflectiveMirrorTransport.cs @@ -50,7 +50,7 @@ namespace LightReflectiveMirror clientToServerTransport.OnClientConnected = OnConnectedToRelay; clientToServerTransport.OnClientDataReceived = DataReceived; clientToServerTransport.OnClientDisconnected = Disconnected; - clientToServerTransport.OnClientError = (e) => Debug.LogException(e); + clientToServerTransport.OnClientError = (transportError, reason) => Debug.LogError($"OnClientError: {transportError} Reason: {reason}"); } private void Disconnected() diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport.meta b/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport.meta deleted file mode 100644 index a503f86..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: a3ba68af305d809418d6c6a804939290 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/.cert.example.Json b/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/.cert.example.Json deleted file mode 100644 index e303974..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/.cert.example.Json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "_readme_1": "Make a copy of this file and update the fields below. (readme fields should be deleted)", - "_readme_2": "Include the json file and cert with your server build ONLY (put them outside of asset folder)", - "_readme_path": "path is relative from cwd not this json file", - "_readme_password": "password can be empty or left out", - "path": "./certs/MirrorLocal.pfx", - "password": "" -} \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/.cert.example.Json.meta b/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/.cert.example.Json.meta deleted file mode 100644 index af864e4..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/.cert.example.Json.meta +++ /dev/null @@ -1,2 +0,0 @@ -guid: 2F50A381F3D112C3F81A7CCFAA732196 -fileFormatVersion: 2 diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/AssemblyInfo.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/AssemblyInfo.cs.meta deleted file mode 100644 index e3739f6..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/AssemblyInfo.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ee9e76201f7665244bd6ab8ea343a83f -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client.meta b/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client.meta deleted file mode 100644 index 90fa667..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 5faa957b8d9fc314ab7596ccf14750d9 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone.meta b/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone.meta deleted file mode 100644 index 2dc5fc0..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: a9c19d05220a87c4cbbe4d1e422da0aa -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl.meta b/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl.meta deleted file mode 100644 index c7ae4ea..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 7142349d566213c4abc763afaf4d91a1 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/plugin.meta b/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/plugin.meta deleted file mode 100644 index 94a9918..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/plugin.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 1999985791b91b9458059e88404885a7 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common.meta b/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common.meta deleted file mode 100644 index d14f3d5..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 564d2cd3eee5b21419553c0528739d1b -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/EventType.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/EventType.cs.meta deleted file mode 100644 index 4d3948c..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/EventType.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2d9cd7d2b5229ab42a12e82ae17d0347 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Log.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Log.cs.meta deleted file mode 100644 index 5a25a92..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Log.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 3cf1521098e04f74fbea0fe2aa0439f8 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Message.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Message.cs.meta deleted file mode 100644 index 12b39e8..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Message.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f5d05d71b09d2714b96ffe80bc3d2a77 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Utils.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Utils.cs.meta deleted file mode 100644 index a84f8d7..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Utils.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 4643ffb4cb0562847b1ae925d07e15b6 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/README.txt b/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/README.txt deleted file mode 100644 index 58aa246..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/README.txt +++ /dev/null @@ -1,22 +0,0 @@ -SimpleWebTransport is a Transport that implements websocket for Webgl builds of -mirror. This transport can also work on standalone builds and has support for -encryption with websocket secure. - -How to use: - Replace your existing Transport with SimpleWebTransport on your NetworkManager - -Requirements: - Unity 2018.4 LTS - Mirror v18.0.0 - -Documentation: - https://mirror-networking.gitbook.io/docs/ - https://github.com/MirrorNetworking/SimpleWebTransport/blob/master/README.md - -Support: - Discord: https://discordapp.com/invite/N9QVxbM - Bug Reports: https://github.com/MirrorNetworking/SimpleWebTransport/issues - - -**To get most recent updates and fixes download from github** -https://github.com/MirrorNetworking/SimpleWebTransport/releases diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/README.txt.meta b/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/README.txt.meta deleted file mode 100644 index c0e5d04..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/README.txt.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 0e3971d5783109f4d9ce93c7a689d701 -TextScriptImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server.meta b/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server.meta deleted file mode 100644 index e1cd136..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 0e599e92544d43344a9a9060052add28 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy.meta deleted file mode 100644 index 11de68f..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 552b3d8382916438d81fe7f39e18db72 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy.meta deleted file mode 100644 index 4f6d619..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: a1233408bc4b145fb8f6f5a8e95790e0 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty.meta deleted file mode 100644 index 4f249a6..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 885e89897e3a03241827ab7a14fe5fa0 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/Logger.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/Logger.cs.meta deleted file mode 100644 index 0c4898d..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/Logger.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: aa8d703f0b73f4d6398b76812719b68b -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/Message.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/Message.cs.meta deleted file mode 100644 index 4734aff..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/Message.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: aedf812e9637b4f92a35db1aedca8c92 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/EventType.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/EventType.cs.meta deleted file mode 100644 index 30fb575..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/EventType.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 49f1a330755814803be5f27f493e1910 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/LICENSE.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/LICENSE.meta deleted file mode 100644 index b83b89b..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/LICENSE.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 0ba11103b95fd4721bffbb08440d5b8e -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Log.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Log.cs.meta deleted file mode 100644 index 9f8fb16..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Log.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 0a123d054bef34d059057ac2ce936605 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Pool.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Pool.cs.meta deleted file mode 100644 index 269e5f4..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Pool.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 6d3e530f6872642ec81e9b8b76277c93 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Utils.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Utils.cs.meta deleted file mode 100644 index b1b79b1..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Utils.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 951d08c05297f4b3e8feb5bfcab86531 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/VERSION.meta b/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/VERSION.meta deleted file mode 100644 index 8709787..0000000 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/VERSION.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: d942af06608be434dbeeaa58207d20bd -DefaultImporter: - externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' diff --git a/UnityProject/Assets/Mirror/Runtime/TransportError.cs b/UnityProject/Assets/Mirror/Runtime/TransportError.cs new file mode 100644 index 0000000..b452015 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/TransportError.cs @@ -0,0 +1,17 @@ +// Mirror transport error code enum. +// most transport implementations should use a subset of this, +// and then translate the transport error codes to mirror error codes. +namespace Mirror +{ + public enum TransportError : byte + { + DnsResolve, // failed to resolve a host name + Refused, // connection refused by other end. server full etc. + Timeout, // ping timeout or dead link + Congestion, // more messages than transport / network can process + InvalidReceive, // recv invalid packet (possibly intentional attack) + InvalidSend, // user tried to send invalid data + ConnectionClosed, // connection closed voluntarily or lost involuntarily + Unexpected // unexpected error / exception, requires fix. + } +} diff --git a/UnityProject/Assets/Mirror/Runtime/TransportError.cs.meta b/UnityProject/Assets/Mirror/Runtime/TransportError.cs.meta new file mode 100644 index 0000000..3aa7db1 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/TransportError.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ce162bdedd704db9b8c35d163f0c1d54 +timeCreated: 1652330240 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transports.meta b/UnityProject/Assets/Mirror/Runtime/Transports.meta new file mode 100644 index 0000000..fc29442 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7825d46cd73fe47938869eb5427b40fa +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP.meta new file mode 100644 index 0000000..ba9d190 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 953bb5ec5ab2346a092f58061e01ba65 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/MirrorTransport.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/MirrorTransport.meta new file mode 100644 index 0000000..dedea2f --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/MirrorTransport.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7bdb797750d0a490684410110bf48192 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/MirrorTransport/KcpTransport.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/MirrorTransport/KcpTransport.cs similarity index 56% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/MirrorTransport/KcpTransport.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/MirrorTransport/KcpTransport.cs index 4e77ff2..08120cc 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/MirrorTransport/KcpTransport.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/MirrorTransport/KcpTransport.cs @@ -4,9 +4,11 @@ using System.Linq; using System.Net; using UnityEngine; using Mirror; +using Unity.Collections; namespace kcp2k { + [HelpURL("https://mirror-networking.gitbook.io/docs/transports/kcp-transport")] [DisallowMultipleComponent] public class KcpTransport : Transport { @@ -16,6 +18,8 @@ namespace kcp2k // common [Header("Transport Configuration")] public ushort Port = 7777; + [Tooltip("DualMode listens to IPv6 and IPv4 simultaneously. Disable if the platform only supports IPv4.")] + public bool DualMode = true; [Tooltip("NoDelay is recommended to reduce latency. This also scales better without buffers getting full.")] public bool NoDelay = true; [Tooltip("KCP internal update interval. 100ms is KCP default, but a lower interval is recommended to minimize latency and to scale to more networked entities.")] @@ -30,10 +34,22 @@ namespace kcp2k public bool CongestionWindow = false; // KCP 'NoCongestionWindow' is false by default. here we negate it for ease of use. [Tooltip("KCP window size can be modified to support higher loads.")] public uint SendWindowSize = 4096; //Kcp.WND_SND; 32 by default. Mirror sends a lot, so we need a lot more. - [Tooltip("KCP window size can be modified to support higher loads.")] + [Tooltip("KCP window size can be modified to support higher loads. This also increases max message size.")] public uint ReceiveWindowSize = 4096; //Kcp.WND_RCV; 128 by default. Mirror sends a lot, so we need a lot more. + [Tooltip("KCP will try to retransmit lost messages up to MaxRetransmit (aka dead_link) before disconnecting.")] + public uint MaxRetransmit = Kcp.DEADLINK * 2; // default prematurely disconnects a lot of people (#3022). use 2x. + [Tooltip("Enable to use where-allocation NonAlloc KcpServer/Client/Connection versions. Highly recommended on all Unity platforms.")] + public bool NonAlloc = true; + [Tooltip("Enable to automatically set client & server send/recv buffers to OS limit. Avoids issues with too small buffers under heavy load, potentially dropping connections. Increase the OS limit if this is still too small.")] + public bool MaximizeSendReceiveBuffersToOSLimit = true; - // server & client + [Header("Calculated Max (based on Receive Window Size)")] + [Tooltip("KCP reliable max message size shown for convenience. Can be changed via ReceiveWindowSize.")] + [ReadOnly] public int ReliableMaxMessageSize = 0; // readonly, displayed from OnValidate + [Tooltip("KCP unreliable channel max message size for convenience. Not changeable.")] + [ReadOnly] public int UnreliableMaxMessageSize = 0; // readonly, displayed from OnValidate + + // server & client (where-allocation NonAlloc versions) KcpServer server; KcpClient client; @@ -45,6 +61,28 @@ namespace kcp2k // log statistics for headless servers that can't show them in GUI public bool statisticsLog; + // translate Kcp <-> Mirror channels + static int FromKcpChannel(KcpChannel channel) => + channel == KcpChannel.Reliable ? Channels.Reliable : Channels.Unreliable; + + static KcpChannel ToKcpChannel(int channel) => + channel == Channels.Reliable ? KcpChannel.Reliable : KcpChannel.Unreliable; + + TransportError ToTransportError(ErrorCode error) + { + switch(error) + { + case ErrorCode.DnsResolve: return TransportError.DnsResolve; + case ErrorCode.Timeout: return TransportError.Timeout; + case ErrorCode.Congestion: return TransportError.Congestion; + case ErrorCode.InvalidReceive: return TransportError.InvalidReceive; + case ErrorCode.InvalidSend: return TransportError.InvalidSend; + case ErrorCode.ConnectionClosed: return TransportError.ConnectionClosed; + case ErrorCode.Unexpected: return TransportError.Unexpected; + default: throw new InvalidCastException($"KCP: missing error translation for {error}"); + } + } + void Awake() { // logging @@ -57,26 +95,56 @@ namespace kcp2k Log.Warning = Debug.LogWarning; Log.Error = Debug.LogError; +#if ENABLE_IL2CPP + // NonAlloc doesn't work with IL2CPP builds + NonAlloc = false; +#endif + // client - client = new KcpClient( - () => OnClientConnected.Invoke(), - (message) => OnClientDataReceived.Invoke(message, Channels.Reliable), - () => OnClientDisconnected.Invoke() - ); + client = NonAlloc + ? new KcpClientNonAlloc( + () => OnClientConnected.Invoke(), + (message, channel) => OnClientDataReceived.Invoke(message, FromKcpChannel(channel)), + () => OnClientDisconnected.Invoke(), + (error, reason) => OnClientError.Invoke(ToTransportError(error), reason)) + : new KcpClient( + () => OnClientConnected.Invoke(), + (message, channel) => OnClientDataReceived.Invoke(message, FromKcpChannel(channel)), + () => OnClientDisconnected.Invoke(), + (error, reason) => OnClientError.Invoke(ToTransportError(error), reason)); // server - server = new KcpServer( - (connectionId) => OnServerConnected.Invoke(connectionId), - (connectionId, message) => OnServerDataReceived.Invoke(connectionId, message, Channels.Reliable), - (connectionId) => OnServerDisconnected.Invoke(connectionId), - NoDelay, - Interval, - FastResend, - CongestionWindow, - SendWindowSize, - ReceiveWindowSize, - Timeout - ); + server = NonAlloc + ? new KcpServerNonAlloc( + (connectionId) => OnServerConnected.Invoke(connectionId), + (connectionId, message, channel) => OnServerDataReceived.Invoke(connectionId, message, FromKcpChannel(channel)), + (connectionId) => OnServerDisconnected.Invoke(connectionId), + (connectionId, error, reason) => OnServerError.Invoke(connectionId, ToTransportError(error), reason), + DualMode, + NoDelay, + Interval, + FastResend, + CongestionWindow, + SendWindowSize, + ReceiveWindowSize, + Timeout, + MaxRetransmit, + MaximizeSendReceiveBuffersToOSLimit) + : new KcpServer( + (connectionId) => OnServerConnected.Invoke(connectionId), + (connectionId, message, channel) => OnServerDataReceived.Invoke(connectionId, message, FromKcpChannel(channel)), + (connectionId) => OnServerDisconnected.Invoke(connectionId), + (connectionId, error, reason) => OnServerError.Invoke(connectionId, ToTransportError(error), reason), + DualMode, + NoDelay, + Interval, + FastResend, + CongestionWindow, + SendWindowSize, + ReceiveWindowSize, + Timeout, + MaxRetransmit, + MaximizeSendReceiveBuffersToOSLimit); if (statisticsLog) InvokeRepeating(nameof(OnLogStatistics), 1, 1); @@ -84,6 +152,13 @@ namespace kcp2k Debug.Log("KcpTransport initialized!"); } + void OnValidate() + { + // show max message sizes in inspector for convenience + ReliableMaxMessageSize = KcpConnection.ReliableMaxMessageSize(ReceiveWindowSize); + UnreliableMaxMessageSize = KcpConnection.UnreliableMaxMessageSize; + } + // all except WebGL public override bool Available() => Application.platform != RuntimePlatform.WebGLPlayer; @@ -92,56 +167,35 @@ namespace kcp2k public override bool ClientConnected() => client.connected; public override void ClientConnect(string address) { - client.Connect(address, Port, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout); + client.Connect(address, Port, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout, MaxRetransmit, MaximizeSendReceiveBuffersToOSLimit); + } + public override void ClientConnect(Uri uri) + { + if (uri.Scheme != Scheme) + throw new ArgumentException($"Invalid url {uri}, use {Scheme}://host:port instead", nameof(uri)); + + int serverPort = uri.IsDefaultPort ? Port : uri.Port; + client.Connect(uri.Host, (ushort)serverPort, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout, MaxRetransmit, MaximizeSendReceiveBuffersToOSLimit); } public override void ClientSend(ArraySegment segment, int channelId) { - // switch to kcp channel. - // unreliable or reliable. - // default to reliable just to be sure. - switch (channelId) - { - case Channels.Unreliable: - client.Send(segment, KcpChannel.Unreliable); - break; - default: - client.Send(segment, KcpChannel.Reliable); - break; - } + client.Send(segment, ToKcpChannel(channelId)); + + // call event. might be null if no statistics are listening etc. + OnClientDataSent?.Invoke(segment, channelId); } public override void ClientDisconnect() => client.Disconnect(); // process incoming in early update public override void ClientEarlyUpdate() { - // scene change messages disable transports to stop them from - // processing while changing the scene. - // -> we need to check enabled here - // -> and in kcp's internal loops, see Awake() OnCheckEnabled setup! + // only process messages while transport is enabled. + // scene change messsages disable it to stop processing. // (see also: https://github.com/vis2k/Mirror/pull/379) if (enabled) client.TickIncoming(); } // process outgoing in late update public override void ClientLateUpdate() => client.TickOutgoing(); - // scene change message will disable transports. - // kcp processes messages in an internal loop which should be - // stopped immediately after scene change (= after disabled) - // => kcp has tests to guaranteed that calling .Pause() during the - // receive loop stops the receive loop immediately, not after. - void OnEnable() - { - // unpause when enabled again - client?.Unpause(); - server?.Unpause(); - } - - void OnDisable() - { - // pause immediately when not enabled anymore - client?.Pause(); - server?.Pause(); - } - // server public override Uri ServerUri() { @@ -155,28 +209,22 @@ namespace kcp2k public override void ServerStart() => server.Start(Port); public override void ServerSend(int connectionId, ArraySegment segment, int channelId) { - // switch to kcp channel. - // unreliable or reliable. - // default to reliable just to be sure. - switch (channelId) - { - case Channels.Unreliable: - server.Send(connectionId, segment, KcpChannel.Unreliable); - break; - default: - server.Send(connectionId, segment, KcpChannel.Reliable); - break; - } + server.Send(connectionId, segment, ToKcpChannel(channelId)); + + // call event. might be null if no statistics are listening etc. + OnServerDataSent?.Invoke(connectionId, segment, channelId); } public override void ServerDisconnect(int connectionId) => server.Disconnect(connectionId); - public override string ServerGetClientAddress(int connectionId) => server.GetClientAddress(connectionId); + public override string ServerGetClientAddress(int connectionId) + { + IPEndPoint endPoint = server.GetClientEndPoint(connectionId); + return endPoint != null ? endPoint.Address.ToString() : ""; + } public override void ServerStop() => server.Stop(); public override void ServerEarlyUpdate() { - // scene change messages disable transports to stop them from - // processing while changing the scene. - // -> we need to check enabled here - // -> and in kcp's internal loops, see Awake() OnCheckEnabled setup! + // only process messages while transport is enabled. + // scene change messsages disable it to stop processing. // (see also: https://github.com/vis2k/Mirror/pull/379) if (enabled) server.TickIncoming(); } @@ -197,7 +245,7 @@ namespace kcp2k case Channels.Unreliable: return KcpConnection.UnreliableMaxMessageSize; default: - return KcpConnection.ReliableMaxMessageSize; + return KcpConnection.ReliableMaxMessageSize(ReceiveWindowSize); } } @@ -209,25 +257,27 @@ namespace kcp2k // network. // => instead we always use MTU sized batches. // => people can still send maxed size if needed. - public override int GetMaxBatchSize(int channelId) => + public override int GetBatchThreshold(int channelId) => KcpConnection.UnreliableMaxMessageSize; // server statistics - public int GetAverageMaxSendRate() => + // LONG to avoid int overflows with connections.Sum. + // see also: https://github.com/vis2k/Mirror/pull/2777 + public long GetAverageMaxSendRate() => server.connections.Count > 0 - ? server.connections.Values.Sum(conn => (int)conn.MaxSendRate) / server.connections.Count + ? server.connections.Values.Sum(conn => (long)conn.MaxSendRate) / server.connections.Count : 0; - public int GetAverageMaxReceiveRate() => + public long GetAverageMaxReceiveRate() => server.connections.Count > 0 - ? server.connections.Values.Sum(conn => (int)conn.MaxReceiveRate) / server.connections.Count + ? server.connections.Values.Sum(conn => (long)conn.MaxReceiveRate) / server.connections.Count : 0; - int GetTotalSendQueue() => + long GetTotalSendQueue() => server.connections.Values.Sum(conn => conn.SendQueueCount); - int GetTotalReceiveQueue() => + long GetTotalReceiveQueue() => server.connections.Values.Sum(conn => conn.ReceiveQueueCount); - int GetTotalSendBuffer() => + long GetTotalSendBuffer() => server.connections.Values.Sum(conn => conn.SendBufferCount); - int GetTotalReceiveBuffer() => + long GetTotalReceiveBuffer() => server.connections.Values.Sum(conn => conn.ReceiveBufferCount); // PrettyBytes function from DOTSNET @@ -249,6 +299,8 @@ namespace kcp2k return $"{(bytes / (1024f * 1024f * 1024f)):F2} GB"; } +// OnGUI allocates even if it does nothing. avoid in release. +#if UNITY_EDITOR || DEVELOPMENT_BUILD void OnGUI() { if (!statisticsGUI) return; @@ -284,12 +336,13 @@ namespace kcp2k GUILayout.EndArea(); } +#endif void OnLogStatistics() { if (ServerActive()) { - string log = "kcp SERVER @ time: " + NetworkTime.time + "\n"; + string log = "kcp SERVER @ time: " + NetworkTime.localTime + "\n"; log += $" connections: {server.connections.Count}\n"; log += $" MaxSendRate (avg): {PrettyBytes(GetAverageMaxSendRate())}/s\n"; log += $" MaxRecvRate (avg): {PrettyBytes(GetAverageMaxReceiveRate())}/s\n"; @@ -302,7 +355,7 @@ namespace kcp2k if (ClientConnected()) { - string log = "kcp CLIENT @ time: " + NetworkTime.time + "\n"; + string log = "kcp CLIENT @ time: " + NetworkTime.localTime + "\n"; log += $" MaxSendRate: {PrettyBytes(client.connection.MaxSendRate)}/s\n"; log += $" MaxRecvRate: {PrettyBytes(client.connection.MaxReceiveRate)}/s\n"; log += $" SendQueue: {client.connection.SendQueueCount}\n"; diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/MirrorTransport/KcpTransport.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/MirrorTransport/KcpTransport.cs.meta similarity index 79% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/MirrorTransport/KcpTransport.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/MirrorTransport/KcpTransport.cs.meta index 14c1089..f7280c8 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/MirrorTransport/KcpTransport.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/MirrorTransport/KcpTransport.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k.meta new file mode 100644 index 0000000..1dceadf --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 71a1c8e8c022d4731a481c1808f37e5d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp2k.asmdef b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/KCP.asmdef similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp2k.asmdef rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/KCP.asmdef diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp2k.asmdef.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/KCP.asmdef.meta similarity index 63% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp2k.asmdef.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/KCP.asmdef.meta index d01a562..1d70e80 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp2k.asmdef.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/KCP.asmdef.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 6806a62c384838046a3c66c44f06d75f AssemblyDefinitionImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/LICENSE b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/LICENSE similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/LICENSE rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/LICENSE diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/LICENSE.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/LICENSE.meta new file mode 100644 index 0000000..49dc767 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/LICENSE.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9a3e8369060cf4e94ac117603de47aa6 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/VERSION b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/VERSION similarity index 65% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/VERSION rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/VERSION index 44a22ce..992fe9f 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/VERSION +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/VERSION @@ -1,6 +1,54 @@ -V1.11 [2021-06-01] -- perf: where-allocation (https://github.com/vis2k/where-allocation): - nearly removes all socket.SendTo/ReceiveFrom allocations +V1.19 [2022-05-12] +- feature: OnError ErrorCodes + +V1.18 [2022-05-08] +- feature: OnError to allow higher level to show popups etc. +- feature: KcpServer.GetClientAddress is now GetClientEndPoint in order to + expose more details +- ResolveHostname: include exception in log for easier debugging +- fix: KcpClientConnection.RawReceive now logs the SocketException even if + it was expected. makes debugging easier. +- fix: KcpServer.TickIncoming now logs the SocketException even if it was + expected. makes debugging easier. +- fix: KcpClientConnection.RawReceive now calls Disconnect() if the other end + has closed the connection. better than just remaining in a state with unusable + sockets. + +V1.17 [2022-01-09] +- perf: server/client MaximizeSendReceiveBuffersToOSLimit option to set send/recv + buffer sizes to OS limit. avoids drops due to small buffers under heavy load. + +V1.16 [2022-01-06] +- fix: SendUnreliable respects ArraySegment.Offset +- fix: potential bug with negative length (see PR #2) +- breaking: removed pause handling because it's not necessary for Mirror anymore + +V1.15 [2021-12-11] +- feature: feature: MaxRetransmits aka dead_link now configurable +- dead_link disconnect message improved to show exact retransmit count + +V1.14 [2021-11-30] +- fix: Send() now throws an exception for messages which require > 255 fragments +- fix: ReliableMaxMessageSize is now limited to messages which require <= 255 fragments + +V1.13 [2021-11-28] +- fix: perf: uncork max message size from 144 KB to as much as we want based on + receive window size. + fixes https://github.com/vis2k/kcp2k/issues/22 + fixes https://github.com/skywind3000/kcp/pull/291 +- feature: OnData now includes channel it was received on + +V1.12 [2021-07-16] +- Tests: don't depend on Unity anymore +- fix: #26 - Kcp now catches exception if host couldn't be resolved, and calls + OnDisconnected to let the user now. +- fix: KcpServer.DualMode is now configurable in the constructor instead of + using #if UNITY_SWITCH. makes it run on all other non dual mode platforms too. +- fix: where-allocation made optional via virtuals and inheriting + KcpServer/Client/Connection NonAlloc classes. fixes a bug where some platforms + might not support where-allocation. + +V1.11 rollback [2021-06-01] - perf: Segment MemoryStream initial capacity set to MTU to avoid early runtime resizing/allocations diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/VERSION.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/VERSION.meta new file mode 100644 index 0000000..2a07daa --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/VERSION.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ed3f2cf1bbf1b4d53a6f2c103d311f71 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel.meta new file mode 100644 index 0000000..1c11c3d --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5a54d18b954cb4407a28b633fc32ea6d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/ErrorCode.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/ErrorCode.cs new file mode 100644 index 0000000..15b872f --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/ErrorCode.cs @@ -0,0 +1,15 @@ +// kcp specific error codes to allow for error switching, localization, +// translation to Mirror errors, etc. +namespace kcp2k +{ + public enum ErrorCode : byte + { + DnsResolve, // failed to resolve a host name + Timeout, // ping timeout or dead link + Congestion, // more messages than transport / network can process + InvalidReceive, // recv invalid packet (possibly intentional attack) + InvalidSend, // user tried to send invalid data + ConnectionClosed, // connection closed voluntarily or lost involuntarily + Unexpected // unexpected error / exception, requires fix. + } +} \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/ErrorCode.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/ErrorCode.cs.meta new file mode 100644 index 0000000..42f163f --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/ErrorCode.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3abbeffc1d794f11a45b7fcf110353f5 +timeCreated: 1652320712 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/Extensions.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/Extensions.cs new file mode 100644 index 0000000..6115bc8 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/Extensions.cs @@ -0,0 +1,33 @@ +using System.Net.Sockets; + +namespace kcp2k +{ + public static class Extensions + { + // 100k attempts of 1 KB increases = default + 100 MB max + public static void SetReceiveBufferToOSLimit(this Socket socket, int stepSize = 1024, int attempts = 100_000) + { + // setting a too large size throws a socket exception. + // so let's keep increasing until we encounter it. + for (int i = 0; i < attempts; ++i) + { + // increase in 1 KB steps + try { socket.ReceiveBufferSize += stepSize; } + catch (SocketException) { break; } + } + } + + // 100k attempts of 1 KB increases = default + 100 MB max + public static void SetSendBufferToOSLimit(this Socket socket, int stepSize = 1024, int attempts = 100_000) + { + // setting a too large size throws a socket exception. + // so let's keep increasing until we encounter it. + for (int i = 0; i < attempts; ++i) + { + // increase in 1 KB steps + try { socket.SendBufferSize += stepSize; } + catch (SocketException) { break; } + } + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/Extensions.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/Extensions.cs.meta new file mode 100644 index 0000000..36d3193 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/Extensions.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c0649195e5ba4fcf8e0e1231fee7d5f6 +timeCreated: 1641701011 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpChannel.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpChannel.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpChannel.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpChannel.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpChannel.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpChannel.cs.meta new file mode 100644 index 0000000..2721025 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpChannel.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9e852b2532fb248d19715cfebe371db3 +timeCreated: 1610081248 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpClient.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpClient.cs similarity index 56% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpClient.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpClient.cs index d2420a9..58249e7 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpClient.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpClient.cs @@ -8,21 +8,45 @@ namespace kcp2k { // events public Action OnConnected; - public Action> OnData; + public Action, KcpChannel> OnData; public Action OnDisconnected; + // error callback instead of logging. + // allows libraries to show popups etc. + // (string instead of Exception for ease of use and to avoid user panic) + public Action OnError; // state public KcpClientConnection connection; public bool connected; - public KcpClient(Action OnConnected, Action> OnData, Action OnDisconnected) + public KcpClient(Action OnConnected, + Action, + KcpChannel> OnData, + Action OnDisconnected, + Action OnError) { this.OnConnected = OnConnected; this.OnData = OnData; this.OnDisconnected = OnDisconnected; + this.OnError = OnError; } - public void Connect(string address, ushort port, bool noDelay, uint interval, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = Kcp.WND_SND, uint receiveWindowSize = Kcp.WND_RCV, int timeout = KcpConnection.DEFAULT_TIMEOUT) + // CreateConnection can be overwritten for where-allocation: + // https://github.com/vis2k/where-allocation + protected virtual KcpClientConnection CreateConnection() => + new KcpClientConnection(); + + public void Connect(string address, + ushort port, + bool noDelay, + uint interval, + int fastResend = 0, + bool congestionWindow = true, + uint sendWindowSize = Kcp.WND_SND, + uint receiveWindowSize = Kcp.WND_RCV, + int timeout = KcpConnection.DEFAULT_TIMEOUT, + uint maxRetransmits = Kcp.DEADLINK, + bool maximizeSendReceiveBuffersToOSLimit = false) { if (connected) { @@ -30,30 +54,45 @@ namespace kcp2k return; } - connection = new KcpClientConnection(); + // create connection + connection = CreateConnection(); // setup events connection.OnAuthenticated = () => { Log.Info($"KCP: OnClientConnected"); connected = true; - OnConnected.Invoke(); + OnConnected(); }; - connection.OnData = (message) => + connection.OnData = (message, channel) => { //Log.Debug($"KCP: OnClientData({BitConverter.ToString(message.Array, message.Offset, message.Count)})"); - OnData.Invoke(message); + OnData(message, channel); }; connection.OnDisconnected = () => { Log.Info($"KCP: OnClientDisconnected"); connected = false; connection = null; - OnDisconnected.Invoke(); + OnDisconnected(); + }; + connection.OnError = (error, reason) => + { + OnError(error, reason); }; // connect - connection.Connect(address, port, noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout); + connection.Connect(address, + port, + noDelay, + interval, + fastResend, + congestionWindow, + sendWindowSize, + receiveWindowSize, + timeout, + maxRetransmits, + maximizeSendReceiveBuffersToOSLimit); } public void Send(ArraySegment segment, KcpChannel channel) @@ -105,10 +144,5 @@ namespace kcp2k TickIncoming(); TickOutgoing(); } - - // pause/unpause to safely support mirror scene handling and to - // immediately pause the receive while loop if needed. - public void Pause() => connection?.Pause(); - public void Unpause() => connection?.Unpause(); } } diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpClient.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpClient.cs.meta new file mode 100644 index 0000000..e55306b --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpClient.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6aa069a28ed24fedb533c102d9742b36 +timeCreated: 1603786960 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpClientConnection.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpClientConnection.cs new file mode 100644 index 0000000..a843a8d --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpClientConnection.cs @@ -0,0 +1,156 @@ +using System.Net; +using System.Net.Sockets; + +namespace kcp2k +{ + public class KcpClientConnection : KcpConnection + { + // IMPORTANT: raw receive buffer always needs to be of 'MTU' size, even + // if MaxMessageSize is larger. kcp always sends in MTU + // segments and having a buffer smaller than MTU would + // silently drop excess data. + // => we need the MTU to fit channel + message! + readonly byte[] rawReceiveBuffer = new byte[Kcp.MTU_DEF]; + + // helper function to resolve host to IPAddress + public static bool ResolveHostname(string hostname, out IPAddress[] addresses) + { + try + { + // NOTE: dns lookup is blocking. this can take a second. + addresses = Dns.GetHostAddresses(hostname); + return addresses.Length >= 1; + } + catch (SocketException exception) + { + Log.Info($"Failed to resolve host: {hostname} reason: {exception}"); + addresses = null; + return false; + } + } + + // EndPoint & Receive functions can be overwritten for where-allocation: + // https://github.com/vis2k/where-allocation + // NOTE: Client's SendTo doesn't allocate, don't need a virtual. + protected virtual void CreateRemoteEndPoint(IPAddress[] addresses, ushort port) => + remoteEndPoint = new IPEndPoint(addresses[0], port); + + protected virtual int ReceiveFrom(byte[] buffer) => + socket.ReceiveFrom(buffer, ref remoteEndPoint); + + // if connections drop under heavy load, increase to OS limit. + // if still not enough, increase the OS limit. + void ConfigureSocketBufferSizes(bool maximizeSendReceiveBuffersToOSLimit) + { + if (maximizeSendReceiveBuffersToOSLimit) + { + // log initial size for comparison. + // remember initial size for log comparison + int initialReceive = socket.ReceiveBufferSize; + int initialSend = socket.SendBufferSize; + + socket.SetReceiveBufferToOSLimit(); + socket.SetSendBufferToOSLimit(); + Log.Info($"KcpClient: RecvBuf = {initialReceive}=>{socket.ReceiveBufferSize} ({socket.ReceiveBufferSize/initialReceive}x) SendBuf = {initialSend}=>{socket.SendBufferSize} ({socket.SendBufferSize/initialSend}x) increased to OS limits!"); + } + // otherwise still log the defaults for info. + else Log.Info($"KcpClient: RecvBuf = {socket.ReceiveBufferSize} SendBuf = {socket.SendBufferSize}. If connections drop under heavy load, enable {nameof(maximizeSendReceiveBuffersToOSLimit)} to increase it to OS limit. If they still drop, increase the OS limit."); + } + + public void Connect(string host, + ushort port, + bool noDelay, + uint interval = Kcp.INTERVAL, + int fastResend = 0, + bool congestionWindow = true, + uint sendWindowSize = Kcp.WND_SND, + uint receiveWindowSize = Kcp.WND_RCV, + int timeout = DEFAULT_TIMEOUT, + uint maxRetransmits = Kcp.DEADLINK, + bool maximizeSendReceiveBuffersToOSLimit = false) + { + Log.Info($"KcpClient: connect to {host}:{port}"); + + // try resolve host name + if (ResolveHostname(host, out IPAddress[] addresses)) + { + // create remote endpoint + CreateRemoteEndPoint(addresses, port); + + // create socket + socket = new Socket(remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); + + // configure buffer sizes + ConfigureSocketBufferSizes(maximizeSendReceiveBuffersToOSLimit); + + // connect + socket.Connect(remoteEndPoint); + + // set up kcp + SetupKcp(noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout, maxRetransmits); + + // client should send handshake to server as very first message + SendHandshake(); + + RawReceive(); + } + // otherwise call OnDisconnected to let the user know. + else + { + // pass error to user callback. no need to log it manually. + OnError(ErrorCode.DnsResolve, $"Failed to resolve host: {host}"); + OnDisconnected(); + } + } + + // call from transport update + public void RawReceive() + { + try + { + if (socket != null) + { + while (socket.Poll(0, SelectMode.SelectRead)) + { + int msgLength = ReceiveFrom(rawReceiveBuffer); + // IMPORTANT: detect if buffer was too small for the + // received msgLength. otherwise the excess + // data would be silently lost. + // (see ReceiveFrom documentation) + if (msgLength <= rawReceiveBuffer.Length) + { + //Log.Debug($"KCP: client raw recv {msgLength} bytes = {BitConverter.ToString(buffer, 0, msgLength)}"); + RawInput(rawReceiveBuffer, msgLength); + } + else + { + // pass error to user callback. no need to log it manually. + OnError(ErrorCode.InvalidReceive, $"KCP ClientConnection: message of size {msgLength} does not fit into buffer of size {rawReceiveBuffer.Length}. The excess was silently dropped. Disconnecting."); + Disconnect(); + } + } + } + } + // this is fine, the socket might have been closed in the other end + catch (SocketException ex) + { + // the other end closing the connection is not an 'error'. + // but connections should never just end silently. + // at least log a message for easier debugging. + Log.Info($"KCP ClientConnection: looks like the other end has closed the connection. This is fine: {ex}"); + Disconnect(); + } + } + + protected override void Dispose() + { + socket.Close(); + socket = null; + } + + protected override void RawSend(byte[] data, int length) + { + socket.Send(data, length, SocketFlags.None); + } + } +} diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpClientConnection.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpClientConnection.cs.meta new file mode 100644 index 0000000..3369918 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpClientConnection.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 96512e74aa8214a6faa8a412a7a07877 +timeCreated: 1602601237 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpConnection.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpConnection.cs similarity index 78% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpConnection.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpConnection.cs index adbd927..e5bc0f3 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpConnection.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpConnection.cs @@ -10,21 +10,19 @@ namespace kcp2k public abstract class KcpConnection { protected Socket socket; - protected EndPoint remoteEndpoint; + protected EndPoint remoteEndPoint; internal Kcp kcp; // kcp can have several different states, let's use a state machine KcpState state = KcpState.Disconnected; public Action OnAuthenticated; - public Action> OnData; + public Action, KcpChannel> OnData; public Action OnDisconnected; - - // Mirror needs a way to stop the kcp message processing while loop - // immediately after a scene change message. Mirror can't process any - // other messages during a scene change. - // (could be useful for others too) - bool paused; + // error callback instead of logging. + // allows libraries to show popups etc. + // (string instead of Exception for ease of use and to avoid user panic) + public Action OnError; // If we don't receive anything these many milliseconds // then consider us disconnected @@ -43,19 +41,20 @@ namespace kcp2k const int CHANNEL_HEADER_SIZE = 1; // reliable channel (= kcp) MaxMessageSize so the outside knows largest - // allowed message to send the calculation in Send() is not obvious at + // allowed message to send. the calculation in Send() is not obvious at // all, so let's provide the helper here. // // kcp does fragmentation, so max message is way larger than MTU. // // -> runtime MTU changes are disabled: mss is always MTU_DEF-OVERHEAD - // -> Send() checks if fragment count < WND_RCV, so we use WND_RCV - 1. - // note that Send() checks WND_RCV instead of wnd_rcv which may or - // may not be a bug in original kcp. but since it uses the define, we - // can use that here too. + // -> Send() checks if fragment count < rcv_wnd, so we use rcv_wnd - 1. + // NOTE that original kcp has a bug where WND_RCV default is used + // instead of configured rcv_wnd, limiting max message size to 144 KB + // https://github.com/skywind3000/kcp/pull/291 + // we fixed this in kcp2k. // -> we add 1 byte KcpHeader enum to each message, so -1 // - // IMPORTANT: max message is MTU * WND_RCV, in other words it completely + // IMPORTANT: max message is MTU * rcv_wnd, in other words it completely // fills the receive window! due to head of line blocking, // all other messages have to wait while a maxed size message // is being delivered. @@ -63,7 +62,15 @@ namespace kcp2k // for batching. // => sending UNRELIABLE max message size most of the time is // best for performance (use that one for batching!) - public const int ReliableMaxMessageSize = (Kcp.MTU_DEF - Kcp.OVERHEAD - CHANNEL_HEADER_SIZE) * (Kcp.WND_RCV - 1) - 1; + static int ReliableMaxMessageSize_Unconstrained(uint rcv_wnd) => (Kcp.MTU_DEF - Kcp.OVERHEAD - CHANNEL_HEADER_SIZE) * ((int)rcv_wnd - 1) - 1; + + // kcp encodes 'frg' as 1 byte. + // max message size can only ever allow up to 255 fragments. + // WND_RCV gives 127 fragments. + // WND_RCV * 2 gives 255 fragments. + // so we can limit max message size by limiting rcv_wnd parameter. + public static int ReliableMaxMessageSize(uint rcv_wnd) => + ReliableMaxMessageSize_Unconstrained(Math.Min(rcv_wnd, Kcp.FRG_MAX)); // unreliable max message size is simply MTU - channel header size public const int UnreliableMaxMessageSize = Kcp.MTU_DEF - CHANNEL_HEADER_SIZE; @@ -71,13 +78,13 @@ namespace kcp2k // buffer to receive kcp's processed messages (avoids allocations). // IMPORTANT: this is for KCP messages. so it needs to be of size: // 1 byte header + MaxMessageSize content - byte[] kcpMessageBuffer = new byte[1 + ReliableMaxMessageSize]; + byte[] kcpMessageBuffer;// = new byte[1 + ReliableMaxMessageSize]; // send buffer for handing user messages to kcp for processing. // (avoids allocations). // IMPORTANT: needs to be of size: // 1 byte header + MaxMessageSize content - byte[] kcpSendBuffer = new byte[1 + ReliableMaxMessageSize]; + byte[] kcpSendBuffer;// = new byte[1 + ReliableMaxMessageSize]; // raw send buffer is exactly MTU. byte[] rawSendBuffer = new byte[Kcp.MTU_DEF]; @@ -128,7 +135,7 @@ namespace kcp2k // => useful to start from a fresh state every time the client connects // => NoDelay, interval, wnd size are the most important configurations. // let's force require the parameters so we don't forget it anywhere. - protected void SetupKcp(bool noDelay, uint interval = Kcp.INTERVAL, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = Kcp.WND_SND, uint receiveWindowSize = Kcp.WND_RCV, int timeout = DEFAULT_TIMEOUT) + protected void SetupKcp(bool noDelay, uint interval = Kcp.INTERVAL, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = Kcp.WND_SND, uint receiveWindowSize = Kcp.WND_RCV, int timeout = DEFAULT_TIMEOUT, uint maxRetransmits = Kcp.DEADLINK) { // set up kcp over reliable channel (that's what kcp is for) kcp = new Kcp(0, RawSendReliable); @@ -143,6 +150,14 @@ namespace kcp2k // message afterwards. kcp.SetMtu(Kcp.MTU_DEF - CHANNEL_HEADER_SIZE); + // set maximum retransmits (aka dead_link) + kcp.dead_link = maxRetransmits; + + // create message buffers AFTER window size is set + // see comments on buffer definition for the "+1" part + kcpMessageBuffer = new byte[1 + ReliableMaxMessageSize(receiveWindowSize)]; + kcpSendBuffer = new byte[1 + ReliableMaxMessageSize(receiveWindowSize)]; + this.timeout = timeout; state = KcpState.Connected; @@ -155,7 +170,8 @@ namespace kcp2k // only ever happen if the connection is truly gone. if (time >= lastReceiveTime + timeout) { - Log.Warning($"KCP: Connection timed out after not receiving any message for {timeout}ms. Disconnecting."); + // pass error to user callback. no need to log it manually. + OnError(ErrorCode.Timeout, $"KCP: Connection timed out after not receiving any message for {timeout}ms. Disconnecting."); Disconnect(); } } @@ -165,7 +181,8 @@ namespace kcp2k // kcp has 'dead_link' detection. might as well use it. if (kcp.state == -1) { - Log.Warning("KCP Connection dead_link detected. Disconnecting."); + // pass error to user callback. no need to log it manually. + OnError(ErrorCode.Timeout, $"KCP Connection dead_link detected: a message was retransmitted {kcp.dead_link} times without ack. Disconnecting."); Disconnect(); } } @@ -192,10 +209,12 @@ namespace kcp2k kcp.rcv_buf.Count + kcp.snd_buf.Count; if (total >= QueueDisconnectThreshold) { - Log.Warning($"KCP: disconnecting connection because it can't process data fast enough.\n" + - $"Queue total {total}>{QueueDisconnectThreshold}. rcv_queue={kcp.rcv_queue.Count} snd_queue={kcp.snd_queue.Count} rcv_buf={kcp.rcv_buf.Count} snd_buf={kcp.snd_buf.Count}\n" + - $"* Try to Enable NoDelay, decrease INTERVAL, disable Congestion Window (= enable NOCWND!), increase SEND/RECV WINDOW or compress data.\n" + - $"* Or perhaps the network is simply too slow on our end, or on the other end.\n"); + // pass error to user callback. no need to log it manually. + OnError(ErrorCode.Congestion, + $"KCP: disconnecting connection because it can't process data fast enough.\n" + + $"Queue total {total}>{QueueDisconnectThreshold}. rcv_queue={kcp.rcv_queue.Count} snd_queue={kcp.snd_queue.Count} rcv_buf={kcp.rcv_buf.Count} snd_buf={kcp.snd_buf.Count}\n" + + $"* Try to Enable NoDelay, decrease INTERVAL, disable Congestion Window (= enable NOCWND!), increase SEND/RECV WINDOW or compress data.\n" + + $"* Or perhaps the network is simply too slow on our end, or on the other end."); // let's clear all pending sends before disconnting with 'Bye'. // otherwise a single Flush in Disconnect() won't be enough to @@ -231,7 +250,8 @@ namespace kcp2k else { // if receive failed, close everything - Log.Warning($"Receive failed with error={received}. closing connection."); + // pass error to user callback. no need to log it manually. + OnError(ErrorCode.InvalidReceive, $"Receive failed with error={received}. closing connection."); Disconnect(); } } @@ -239,7 +259,8 @@ namespace kcp2k // attacker. let's disconnect to avoid allocation attacks etc. else { - Log.Warning($"KCP: possible allocation attack for msgSize {msgSize} > buffer {kcpMessageBuffer.Length}. Disconnecting the connection."); + // pass error to user callback. no need to log it manually. + OnError(ErrorCode.InvalidReceive, $"KCP: possible allocation attack for msgSize {msgSize} > buffer {kcpMessageBuffer.Length}. Disconnecting the connection."); Disconnect(); } } @@ -281,7 +302,8 @@ namespace kcp2k case KcpHeader.Disconnect: { // everything else is not allowed during handshake! - Log.Warning($"KCP: received invalid header {header} while Connected. Disconnecting the connection."); + // pass error to user callback. no need to log it manually. + OnError(ErrorCode.InvalidReceive, $"KCP: received invalid header {header} while Connected. Disconnecting the connection."); Disconnect(); break; } @@ -298,19 +320,7 @@ namespace kcp2k HandleChoked(); // process all received messages - // - // Mirror scene changing requires transports to immediately stop - // processing any more messages after a scene message was - // received. and since we are in a while loop here, we need this - // extra check. - // - // note while that this is mainly for Mirror, but might be - // useful in other applications too. - // - // note that we check it BEFORE ever calling ReceiveNext. otherwise - // we would silently eat the received message and never process it. - while (!paused && - ReceiveNextReliable(out KcpHeader header, out ArraySegment message)) + while (ReceiveNextReliable(out KcpHeader header, out ArraySegment message)) { // message type FSM. no default so we never miss a case. switch (header) @@ -328,12 +338,13 @@ namespace kcp2k if (message.Count > 0) { //Log.Warning($"Kcp recv msg: {BitConverter.ToString(message.Array, message.Offset, message.Count)}"); - OnData?.Invoke(message); + OnData?.Invoke(message, KcpChannel.Reliable); } // empty data = attacker, or something went wrong else { - Log.Warning("KCP: received empty Data message while Authenticated. Disconnecting the connection."); + // pass error to user callback. no need to log it manually. + OnError(ErrorCode.InvalidReceive, "KCP: received empty Data message while Authenticated. Disconnecting the connection."); Disconnect(); } break; @@ -382,19 +393,22 @@ namespace kcp2k catch (SocketException exception) { // this is ok, the connection was closed - Log.Info($"KCP Connection: Disconnecting because {exception}. This is fine."); + // pass error to user callback. no need to log it manually. + OnError(ErrorCode.ConnectionClosed, $"KCP Connection: Disconnecting because {exception}. This is fine."); Disconnect(); } catch (ObjectDisposedException exception) { // fine, socket was closed - Log.Info($"KCP Connection: Disconnecting because {exception}. This is fine."); + // pass error to user callback. no need to log it manually. + OnError(ErrorCode.ConnectionClosed, $"KCP Connection: Disconnecting because {exception}. This is fine."); Disconnect(); } - catch (Exception ex) + catch (Exception exception) { // unexpected - Log.Error(ex.ToString()); + // pass error to user callback. no need to log it manually. + OnError(ErrorCode.Unexpected, $"KCP Connection: unexpected Exception: {exception}"); Disconnect(); } } @@ -424,19 +438,22 @@ namespace kcp2k catch (SocketException exception) { // this is ok, the connection was closed - Log.Info($"KCP Connection: Disconnecting because {exception}. This is fine."); + // pass error to user callback. no need to log it manually. + OnError(ErrorCode.ConnectionClosed, $"KCP Connection: Disconnecting because {exception}. This is fine."); Disconnect(); } catch (ObjectDisposedException exception) { // fine, socket was closed - Log.Info($"KCP Connection: Disconnecting because {exception}. This is fine."); + // pass error to user callback. no need to log it manually. + OnError(ErrorCode.ConnectionClosed, $"KCP Connection: Disconnecting because {exception}. This is fine."); Disconnect(); } - catch (Exception ex) + catch (Exception exception) { // unexpected - Log.Error(ex.ToString()); + // pass error to user callback. no need to log it manually. + OnError(ErrorCode.Unexpected, $"KCP Connection: unexpected exception: {exception}"); Disconnect(); } } @@ -482,15 +499,8 @@ namespace kcp2k // the current state allows it. if (state == KcpState.Authenticated) { - // only process messages while not paused for Mirror - // scene switching etc. - // -> if an unreliable message comes in while - // paused, simply drop it. it's unreliable! - if (!paused) - { - ArraySegment message = new ArraySegment(buffer, 1, msgLength - 1); - OnData?.Invoke(message); - } + ArraySegment message = new ArraySegment(buffer, 1, msgLength - 1); + OnData?.Invoke(message, KcpChannel.Unreliable); // set last receive time to avoid timeout. // -> we do this in ANY case even if not enabled. @@ -504,8 +514,9 @@ namespace kcp2k } else { - // should never - Log.Warning($"KCP: received unreliable message in state {state}. Disconnecting the connection."); + // should never happen + // pass error to user callback. no need to log it manually. + OnError(ErrorCode.InvalidReceive, $"KCP: received unreliable message in state {state}. Disconnecting the connection."); Disconnect(); } break; @@ -513,7 +524,8 @@ namespace kcp2k default: { // not a valid channel. random data or attacks. - Log.Info($"Disconnecting connection because of invalid channel header: {channel}"); + // pass error to user callback. no need to log it manually. + OnError(ErrorCode.InvalidReceive, $"Disconnecting connection because of invalid channel header: {channel}"); Disconnect(); break; } @@ -551,7 +563,7 @@ namespace kcp2k } } // otherwise content is larger than MaxMessageSize. let user know! - else Log.Error($"Failed to send reliable message of size {content.Count} because it's larger than ReliableMaxMessageSize={ReliableMaxMessageSize}"); + else Log.Error($"Failed to send reliable message of size {content.Count} because it's larger than ReliableMaxMessageSize={ReliableMaxMessageSize(kcp.rcv_wnd)}"); } void SendUnreliable(ArraySegment message) @@ -561,7 +573,7 @@ namespace kcp2k { // copy channel header, data into raw send buffer, then send rawSendBuffer[0] = (byte)KcpChannel.Unreliable; - Buffer.BlockCopy(message.Array, 0, rawSendBuffer, 1, message.Count); + Buffer.BlockCopy(message.Array, message.Offset, rawSendBuffer, 1, message.Count); RawSend(rawSendBuffer, message.Count + 1); } // otherwise content is larger than MaxMessageSize. let user know! @@ -588,7 +600,8 @@ namespace kcp2k // let's make it obvious so it's easy to debug. if (data.Count == 0) { - Log.Warning("KcpConnection: tried sending empty message. This should never happen. Disconnecting."); + // pass error to user callback. no need to log it manually. + OnError(ErrorCode.InvalidSend, "KcpConnection: tried sending empty message. This should never happen. Disconnecting."); Disconnect(); return; } @@ -650,25 +663,6 @@ namespace kcp2k } // get remote endpoint - public EndPoint GetRemoteEndPoint() => remoteEndpoint; - - // pause/unpause to safely support mirror scene handling and to - // immediately pause the receive while loop if needed. - public void Pause() => paused = true; - public void Unpause() - { - // unpause - paused = false; - - // reset the timeout. - // we have likely been paused for > timeout seconds, but that - // doesn't mean we should disconnect. for example, Mirror pauses - // kcp during scene changes which could easily take > 10s timeout: - // see also: https://github.com/vis2k/kcp2k/issues/8 - // => Unpause completely resets the timeout instead of restoring the - // time difference when we started pausing. it's more simple and - // it's a good idea to start counting from 0 after we unpaused! - lastReceiveTime = (uint)refTime.ElapsedMilliseconds; - } + public EndPoint GetRemoteEndPoint() => remoteEndPoint; } } diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpConnection.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpConnection.cs.meta new file mode 100644 index 0000000..fa5dcff --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpConnection.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3915c7c62b72d4dc2a9e4e76c94fc484 +timeCreated: 1602600432 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpHeader.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpHeader.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpHeader.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpHeader.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpHeader.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpHeader.cs.meta new file mode 100644 index 0000000..9e81c94 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpHeader.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 91b5edac31224a49bd76f960ae018942 +timeCreated: 1610081248 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpServer.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpServer.cs similarity index 63% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpServer.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpServer.cs index b198ae4..5e48688 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/KcpServer.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpServer.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; -using WhereAllocation; namespace kcp2k { @@ -12,10 +11,22 @@ namespace kcp2k { // events public Action OnConnected; - public Action> OnData; + public Action, KcpChannel> OnData; public Action OnDisconnected; + // error callback instead of logging. + // allows libraries to show popups etc. + // (string instead of Exception for ease of use and to avoid user panic) + public Action OnError; - // configuration + // socket configuration + // DualMode uses both IPv6 and IPv4. not all platforms support it. + // (Nintendo Switch, etc.) + public bool DualMode; + // too small send/receive buffers might cause connection drops under + // heavy load. using the OS max size can make a difference already. + public bool MaximizeSendReceiveBuffersToOSLimit; + + // kcp configuration // NoDelay is recommended to reduce latency. This also scales better // without buffers getting full. public bool NoDelay; @@ -39,17 +50,13 @@ namespace kcp2k public uint ReceiveWindowSize; // timeout in milliseconds public int Timeout; + // maximum retransmission attempts until dead_link + public uint MaxRetransmits; // state - Socket socket; -#if UNITY_SWITCH - // switch does not support ipv6 - //EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0); - IPEndPointNonAlloc reusableClientEP = new IPEndPointNonAlloc(IPAddress.Any, 0); // where-allocation -#else - //EndPoint newClientEP = new IPEndPoint(IPAddress.IPv6Any, 0); - IPEndPointNonAlloc reusableClientEP = new IPEndPointNonAlloc(IPAddress.IPv6Any, 0); // where-allocation -#endif + protected Socket socket; + EndPoint newClientEP; + // IMPORTANT: raw receive buffer always needs to be of 'MTU' size, even // if MaxMessageSize is larger. kcp always sends in MTU // segments and having a buffer smaller than MTU would @@ -61,19 +68,25 @@ namespace kcp2k public Dictionary connections = new Dictionary(); public KcpServer(Action OnConnected, - Action> OnData, + Action, KcpChannel> OnData, Action OnDisconnected, + Action OnError, + bool DualMode, bool NoDelay, uint Interval, int FastResend = 0, bool CongestionWindow = true, uint SendWindowSize = Kcp.WND_SND, uint ReceiveWindowSize = Kcp.WND_RCV, - int Timeout = KcpConnection.DEFAULT_TIMEOUT) + int Timeout = KcpConnection.DEFAULT_TIMEOUT, + uint MaxRetransmits = Kcp.DEADLINK, + bool MaximizeSendReceiveBuffersToOSLimit = false) { this.OnConnected = OnConnected; this.OnData = OnData; this.OnDisconnected = OnDisconnected; + this.OnError = OnError; + this.DualMode = DualMode; this.NoDelay = NoDelay; this.Interval = Interval; this.FastResend = FastResend; @@ -81,10 +94,36 @@ namespace kcp2k this.SendWindowSize = SendWindowSize; this.ReceiveWindowSize = ReceiveWindowSize; this.Timeout = Timeout; + this.MaxRetransmits = MaxRetransmits; + this.MaximizeSendReceiveBuffersToOSLimit = MaximizeSendReceiveBuffersToOSLimit; + + // create newClientEP either IPv4 or IPv6 + newClientEP = DualMode + ? new IPEndPoint(IPAddress.IPv6Any, 0) + : new IPEndPoint(IPAddress.Any, 0); } public bool IsActive() => socket != null; + // if connections drop under heavy load, increase to OS limit. + // if still not enough, increase the OS limit. + void ConfigureSocketBufferSizes() + { + if (MaximizeSendReceiveBuffersToOSLimit) + { + // log initial size for comparison. + // remember initial size for log comparison + int initialReceive = socket.ReceiveBufferSize; + int initialSend = socket.SendBufferSize; + + socket.SetReceiveBufferToOSLimit(); + socket.SetSendBufferToOSLimit(); + Log.Info($"KcpServer: RecvBuf = {initialReceive}=>{socket.ReceiveBufferSize} ({socket.ReceiveBufferSize/initialReceive}x) SendBuf = {initialSend}=>{socket.SendBufferSize} ({socket.SendBufferSize/initialSend}x) increased to OS limits!"); + } + // otherwise still log the defaults for info. + else Log.Info($"KcpServer: RecvBuf = {socket.ReceiveBufferSize} SendBuf = {socket.SendBufferSize}. If connections drop under heavy load, enable {nameof(MaximizeSendReceiveBuffersToOSLimit)} to increase it to OS limit. If they still drop, increase the OS limit."); + } + public void Start(ushort port) { // only start once @@ -94,15 +133,22 @@ namespace kcp2k } // listen -#if UNITY_SWITCH - // Switch does not support ipv6 - socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - socket.Bind(new IPEndPoint(IPAddress.Any, port)); -#else - socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); - socket.DualMode = true; - socket.Bind(new IPEndPoint(IPAddress.IPv6Any, port)); -#endif + if (DualMode) + { + // IPv6 socket with DualMode + socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); + socket.DualMode = true; + socket.Bind(new IPEndPoint(IPAddress.IPv6Any, port)); + } + else + { + // IPv4 socket + socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + socket.Bind(new IPEndPoint(IPAddress.Any, port)); + } + + // configure socket buffer size. + ConfigureSocketBufferSizes(); } public void Send(int connectionId, ArraySegment segment, KcpChannel channel) @@ -121,15 +167,43 @@ namespace kcp2k } } - public string GetClientAddress(int connectionId) + // expose the whole IPEndPoint, not just the IP address. some need it. + public IPEndPoint GetClientEndPoint(int connectionId) { if (connections.TryGetValue(connectionId, out KcpServerConnection connection)) { - return (connection.GetRemoteEndPoint() as IPEndPoint).Address.ToString(); + return (connection.GetRemoteEndPoint() as IPEndPoint); } - return ""; + return null; } + // EndPoint & Receive functions can be overwritten for where-allocation: + // https://github.com/vis2k/where-allocation + protected virtual int ReceiveFrom(byte[] buffer, out int connectionHash) + { + // NOTE: ReceiveFrom allocates. + // we pass our IPEndPoint to ReceiveFrom. + // receive from calls newClientEP.Create(socketAddr). + // IPEndPoint.Create always returns a new IPEndPoint. + // https://github.com/mono/mono/blob/f74eed4b09790a0929889ad7fc2cf96c9b6e3757/mcs/class/System/System.Net.Sockets/Socket.cs#L1761 + int read = socket.ReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref newClientEP); + + // calculate connectionHash from endpoint + // NOTE: IPEndPoint.GetHashCode() allocates. + // it calls m_Address.GetHashCode(). + // m_Address is an IPAddress. + // GetHashCode() allocates for IPv6: + // https://github.com/mono/mono/blob/bdd772531d379b4e78593587d15113c37edd4a64/mcs/class/referencesource/System/net/System/Net/IPAddress.cs#L699 + // + // => using only newClientEP.Port wouldn't work, because + // different connections can have the same port. + connectionHash = newClientEP.GetHashCode(); + return read; + } + + protected virtual KcpServerConnection CreateConnection() => + new KcpServerConnection(socket, newClientEP, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout, MaxRetransmits); + // process incoming messages. should be called before updating the world. HashSet connectionsToRemove = new HashSet(); public void TickIncoming() @@ -138,32 +212,10 @@ namespace kcp2k { try { - // NOTE: ReceiveFrom allocates. - // we pass our IPEndPoint to ReceiveFrom. - // receive from calls newClientEP.Create(socketAddr). - // IPEndPoint.Create always returns a new IPEndPoint. - // https://github.com/mono/mono/blob/f74eed4b09790a0929889ad7fc2cf96c9b6e3757/mcs/class/System/System.Net.Sockets/Socket.cs#L1761 - //int msgLength = socket.ReceiveFrom(rawReceiveBuffer, 0, rawReceiveBuffer.Length, SocketFlags.None, ref newClientEP); + // receive + int msgLength = ReceiveFrom(rawReceiveBuffer, out int connectionId); //Log.Info($"KCP: server raw recv {msgLength} bytes = {BitConverter.ToString(buffer, 0, msgLength)}"); - // where-allocation nonalloc ReceiveFrom. - int msgLength = socket.ReceiveFrom_NonAlloc(rawReceiveBuffer, 0, rawReceiveBuffer.Length, SocketFlags.None, reusableClientEP); - SocketAddress remoteAddress = reusableClientEP.temp; - - // calculate connectionId from endpoint - // NOTE: IPEndPoint.GetHashCode() allocates. - // it calls m_Address.GetHashCode(). - // m_Address is an IPAddress. - // GetHashCode() allocates for IPv6: - // https://github.com/mono/mono/blob/bdd772531d379b4e78593587d15113c37edd4a64/mcs/class/referencesource/System/net/System/Net/IPAddress.cs#L699 - // - // => using only newClientEP.Port wouldn't work, because - // different connections can have the same port. - //int connectionId = newClientEP.GetHashCode(); - - // where-allocation nonalloc GetHashCode - int connectionId = remoteAddress.GetHashCode(); - // IMPORTANT: detect if buffer was too small for the received // msgLength. otherwise the excess data would be // silently lost. @@ -173,19 +225,9 @@ namespace kcp2k // is this a new connection? if (!connections.TryGetValue(connectionId, out KcpServerConnection connection)) { - // IPEndPointNonAlloc is reused all the time. - // we can't store that as the connection's endpoint. - // we need a new copy! - IPEndPoint newClientEP = reusableClientEP.DeepCopyIPEndPoint(); - - // for allocation free sending, we also need another - // IPEndPointNonAlloc... - IPEndPointNonAlloc reusableSendEP = new IPEndPointNonAlloc(newClientEP.Address, newClientEP.Port); - - // create a new KcpConnection - // -> where-allocation IPEndPointNonAlloc is reused. - // need to create a new one from the temp address. - connection = new KcpServerConnection(socket, newClientEP, reusableSendEP, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout); + // create a new KcpConnection based on last received + // EndPoint. can be overwritten for where-allocation. + connection = CreateConnection(); // DO NOT add to connections yet. only if the first message // is actually the kcp handshake. otherwise it's either: @@ -216,7 +258,7 @@ namespace kcp2k // add to connections dict after being authenticated. connections.Add(connectionId, connection); - Log.Info($"KCP: server added connection({connectionId}): {newClientEP}"); + Log.Info($"KCP: server added connection({connectionId})"); // setup Data + Disconnected events only AFTER the // handshake. we don't want to fire OnServerDisconnected @@ -224,11 +266,11 @@ namespace kcp2k // internet. // setup data event - connection.OnData = (message) => + connection.OnData = (message, channel) => { // call mirror event //Log.Info($"KCP: OnServerDataReceived({connectionId}, {BitConverter.ToString(message.Array, message.Offset, message.Count)})"); - OnData.Invoke(connectionId, message); + OnData.Invoke(connectionId, message, channel); }; // setup disconnected event @@ -241,12 +283,18 @@ namespace kcp2k // call mirror event Log.Info($"KCP: OnServerDisconnected({connectionId})"); - OnDisconnected.Invoke(connectionId); + OnDisconnected(connectionId); + }; + + // setup error event + connection.OnError = (error, reason) => + { + OnError(connectionId, error, reason); }; // finally, call mirror OnConnected event Log.Info($"KCP: OnServerConnected({connectionId})"); - OnConnected.Invoke(connectionId); + OnConnected(connectionId); }; // now input the message & process received ones @@ -273,7 +321,13 @@ namespace kcp2k } } // this is fine, the socket might have been closed in the other end - catch (SocketException) {} + catch (SocketException ex) + { + // the other end closing the connection is not an 'error'. + // but connections should never just end silently. + // at least log a message for easier debugging. + Log.Info($"KCP ClientConnection: looks like the other end has closed the connection. This is fine: {ex}"); + } } // process inputs for all server connections @@ -317,19 +371,5 @@ namespace kcp2k socket?.Close(); socket = null; } - - // pause/unpause to safely support mirror scene handling and to - // immediately pause the receive while loop if needed. - public void Pause() - { - foreach (KcpServerConnection connection in connections.Values) - connection.Pause(); - } - - public void Unpause() - { - foreach (KcpServerConnection connection in connections.Values) - connection.Unpause(); - } } } diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpServer.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpServer.cs.meta new file mode 100644 index 0000000..ef720d4 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpServer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9759159c6589494a9037f5e130a867ed +timeCreated: 1603787747 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpServerConnection.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpServerConnection.cs new file mode 100644 index 0000000..a902865 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpServerConnection.cs @@ -0,0 +1,22 @@ +using System.Net; +using System.Net.Sockets; + +namespace kcp2k +{ + public class KcpServerConnection : KcpConnection + { + // Constructor & Send functions can be overwritten for where-allocation: + // https://github.com/vis2k/where-allocation + public KcpServerConnection(Socket socket, EndPoint remoteEndPoint, bool noDelay, uint interval = Kcp.INTERVAL, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = Kcp.WND_SND, uint receiveWindowSize = Kcp.WND_RCV, int timeout = DEFAULT_TIMEOUT, uint maxRetransmits = Kcp.DEADLINK) + { + this.socket = socket; + this.remoteEndPoint = remoteEndPoint; + SetupKcp(noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout, maxRetransmits); + } + + protected override void RawSend(byte[] data, int length) + { + socket.SendTo(data, 0, length, SocketFlags.None, remoteEndPoint); + } + } +} diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpServerConnection.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpServerConnection.cs.meta new file mode 100644 index 0000000..10d9803 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/KcpServerConnection.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 80a9b1ce9a6f14abeb32bfa9921d097b +timeCreated: 1602601483 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/Log.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/Log.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/highlevel/Log.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/Log.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/Log.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/Log.cs.meta new file mode 100644 index 0000000..333bee5 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/Log.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7b5e1de98d6d84c3793a61cf7d8da9a4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc.meta new file mode 100644 index 0000000..4cbc909 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0b320ff06046474eae7bce7240ea478c +timeCreated: 1626430641 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpClientConnectionNonAlloc.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpClientConnectionNonAlloc.cs new file mode 100644 index 0000000..b3e1b27 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpClientConnectionNonAlloc.cs @@ -0,0 +1,24 @@ +// where-allocation version of KcpClientConnection. +// may not be wanted on all platforms, so it's an extra optional class. +using System.Net; +using WhereAllocation; + +namespace kcp2k +{ + public class KcpClientConnectionNonAlloc : KcpClientConnection + { + IPEndPointNonAlloc reusableEP; + + protected override void CreateRemoteEndPoint(IPAddress[] addresses, ushort port) + { + // create reusableEP with same address family as remoteEndPoint. + // otherwise ReceiveFrom_NonAlloc couldn't use it. + reusableEP = new IPEndPointNonAlloc(addresses[0], port); + base.CreateRemoteEndPoint(addresses, port); + } + + // where-allocation nonalloc recv + protected override int ReceiveFrom(byte[] buffer) => + socket.ReceiveFrom_NonAlloc(buffer, reusableEP); + } +} \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpClientConnectionNonAlloc.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpClientConnectionNonAlloc.cs.meta new file mode 100644 index 0000000..9d4a42e --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpClientConnectionNonAlloc.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4c1b235bbe054706bef6d092f361006e +timeCreated: 1626430539 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpClientNonAlloc.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpClientNonAlloc.cs new file mode 100644 index 0000000..2417408 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpClientNonAlloc.cs @@ -0,0 +1,20 @@ +// where-allocation version of KcpClientConnectionNonAlloc. +// may not be wanted on all platforms, so it's an extra optional class. +using System; + +namespace kcp2k +{ + public class KcpClientNonAlloc : KcpClient + { + public KcpClientNonAlloc(Action OnConnected, + Action, KcpChannel> OnData, + Action OnDisconnected, + Action OnError) + : base(OnConnected, OnData, OnDisconnected, OnError) + { + } + + protected override KcpClientConnection CreateConnection() => + new KcpClientConnectionNonAlloc(); + } +} \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpClientNonAlloc.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpClientNonAlloc.cs.meta new file mode 100644 index 0000000..266dafb --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpClientNonAlloc.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2cf0ccf7d551480bb5af08fcbe169f84 +timeCreated: 1626435264 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpServerConnectionNonAlloc.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpServerConnectionNonAlloc.cs new file mode 100644 index 0000000..7986bea --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpServerConnectionNonAlloc.cs @@ -0,0 +1,25 @@ +// where-allocation version of KcpServerConnection. +// may not be wanted on all platforms, so it's an extra optional class. +using System.Net; +using System.Net.Sockets; +using WhereAllocation; + +namespace kcp2k +{ + public class KcpServerConnectionNonAlloc : KcpServerConnection + { + IPEndPointNonAlloc reusableSendEndPoint; + + public KcpServerConnectionNonAlloc(Socket socket, EndPoint remoteEndpoint, IPEndPointNonAlloc reusableSendEndPoint, bool noDelay, uint interval = Kcp.INTERVAL, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = Kcp.WND_SND, uint receiveWindowSize = Kcp.WND_RCV, int timeout = DEFAULT_TIMEOUT, uint maxRetransmits = Kcp.DEADLINK) + : base(socket, remoteEndpoint, noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout, maxRetransmits) + { + this.reusableSendEndPoint = reusableSendEndPoint; + } + + protected override void RawSend(byte[] data, int length) + { + // where-allocation nonalloc send + socket.SendTo_NonAlloc(data, 0, length, SocketFlags.None, reusableSendEndPoint); + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpServerConnectionNonAlloc.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpServerConnectionNonAlloc.cs.meta new file mode 100644 index 0000000..383fe02 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpServerConnectionNonAlloc.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4e1b74cc224b4c83a0f6c8d8da9090ab +timeCreated: 1626430608 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpServerNonAlloc.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpServerNonAlloc.cs new file mode 100644 index 0000000..001a64b --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpServerNonAlloc.cs @@ -0,0 +1,77 @@ +// where-allocation version of KcpServer. +// may not be wanted on all platforms, so it's an extra optional class. +using System; +using System.Net; +using System.Net.Sockets; +using WhereAllocation; + +namespace kcp2k +{ + public class KcpServerNonAlloc : KcpServer + { + IPEndPointNonAlloc reusableClientEP; + + public KcpServerNonAlloc(Action OnConnected, + Action, KcpChannel> OnData, + Action OnDisconnected, + Action OnError, + bool DualMode, + bool NoDelay, + uint Interval, + int FastResend = 0, + bool CongestionWindow = true, + uint SendWindowSize = Kcp.WND_SND, + uint ReceiveWindowSize = Kcp.WND_RCV, + int Timeout = KcpConnection.DEFAULT_TIMEOUT, + uint MaxRetransmits = Kcp.DEADLINK, + bool MaximizeSendReceiveBuffersToOSLimit = false) + : base(OnConnected, + OnData, + OnDisconnected, + OnError, + DualMode, + NoDelay, + Interval, + FastResend, + CongestionWindow, + SendWindowSize, + ReceiveWindowSize, + Timeout, + MaxRetransmits, + MaximizeSendReceiveBuffersToOSLimit) + { + // create reusableClientEP either IPv4 or IPv6 + reusableClientEP = DualMode + ? new IPEndPointNonAlloc(IPAddress.IPv6Any, 0) + : new IPEndPointNonAlloc(IPAddress.Any, 0); + } + + protected override int ReceiveFrom(byte[] buffer, out int connectionHash) + { + // where-allocation nonalloc ReceiveFrom. + int read = socket.ReceiveFrom_NonAlloc(buffer, 0, buffer.Length, SocketFlags.None, reusableClientEP); + SocketAddress remoteAddress = reusableClientEP.temp; + + // where-allocation nonalloc GetHashCode + connectionHash = remoteAddress.GetHashCode(); + return read; + } + + protected override KcpServerConnection CreateConnection() + { + // IPEndPointNonAlloc is reused all the time. + // we can't store that as the connection's endpoint. + // we need a new copy! + IPEndPoint newClientEP = reusableClientEP.DeepCopyIPEndPoint(); + + // for allocation free sending, we also need another + // IPEndPointNonAlloc... + IPEndPointNonAlloc reusableSendEP = new IPEndPointNonAlloc(newClientEP.Address, newClientEP.Port); + + // create a new KcpConnection NonAlloc version + // -> where-allocation IPEndPointNonAlloc is reused. + // need to create a new one from the temp address. + return new KcpServerConnectionNonAlloc(socket, newClientEP, reusableSendEP, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout, MaxRetransmits); + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpServerNonAlloc.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpServerNonAlloc.cs.meta new file mode 100644 index 0000000..a878cc1 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/highlevel/NonAlloc/KcpServerNonAlloc.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 54b8398dcd544c8a93bcad846214cc40 +timeCreated: 1626432191 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp.meta new file mode 100644 index 0000000..a7d6e11 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5cafb8851a0084f3e94a580c207b3923 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/AssemblyInfo.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/AssemblyInfo.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/AssemblyInfo.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/AssemblyInfo.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/AssemblyInfo.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/AssemblyInfo.cs.meta new file mode 100644 index 0000000..6b442a9 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/AssemblyInfo.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: aec6a15ac7bd43129317ea1f01f19782 +timeCreated: 1602665988 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Kcp.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Kcp.cs similarity index 95% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Kcp.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Kcp.cs index 253757a..dff49e1 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Kcp.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Kcp.cs @@ -26,7 +26,8 @@ namespace kcp2k public const int ACK_FAST = 3; public const int INTERVAL = 100; public const int OVERHEAD = 24; - public const int DEADLINK = 20; + public const int FRG_MAX = byte.MaxValue; // kcp encodes 'frg' as byte. so we can only ever send up to 255 fragments. + public const int DEADLINK = 20; // default maximum amount of 'xmit' retransmissions until a segment is considered lost public const int THRESH_INIT = 2; public const int THRESH_MIN = 2; public const int PROBE_INIT = 7000; // 7 secs to probe window size @@ -64,7 +65,7 @@ namespace kcp2k internal bool updated; internal uint ts_probe; // timestamp probe internal uint probe_wait; - internal uint dead_link; + internal uint dead_link; // maximum amount of 'xmit' retransmissions until a segment is considered lost internal uint incr; internal uint current; // current time (milliseconds). set by Update. @@ -262,10 +263,19 @@ namespace kcp2k if (len <= mss) count = 1; else count = (int)((len + mss - 1) / mss); - // original kcp uses WND_RCV const even though rcv_wnd is the - // runtime variable. may or may not be correct, see also: - // see also: https://github.com/skywind3000/kcp/pull/291/files - if (count >= WND_RCV) return -2; + // IMPORTANT kcp encodes 'frg' as 1 byte. + // so we can only support up to 255 fragments. + // (which limits max message size to around 288 KB) + // this is really nasty to debug. let's make this 100% obvious. + if (count > FRG_MAX) + throw new Exception($"Send len={len} requires {count} fragments, but kcp can only handle up to {FRG_MAX} fragments."); + + // original kcp uses WND_RCV const instead of rcv_wnd runtime: + // https://github.com/skywind3000/kcp/pull/291/files + // which always limits max message size to 144 KB: + //if (count >= WND_RCV) return -2; + // using configured rcv_wnd uncorks max message size to 'any': + if (count >= rcv_wnd) return -2; if (count == 0) count = 1; @@ -511,6 +521,9 @@ namespace kcp2k if (conv_ != conv) return -1; offset += Utils.Decode8u(data, offset, ref cmd); + // IMPORTANT kcp encodes 'frg' as 1 byte. + // so we can only support up to 255 fragments. + // (which limits max message size to around 288 KB) offset += Utils.Decode8u(data, offset, ref frg); offset += Utils.Decode16U(data, offset, ref wnd); offset += Utils.Decode32U(data, offset, ref ts); @@ -522,7 +535,8 @@ namespace kcp2k size -= OVERHEAD; // enough remaining to read 'len' bytes of the actual payload? - if (size < len || len < 0) return -2; + // note: original kcp casts uint len to int for <0 check. + if (size < len || (int)len < 0) return -2; if (cmd != CMD_PUSH && cmd != CMD_ACK && cmd != CMD_WASK && cmd != CMD_WINS) @@ -840,6 +854,8 @@ namespace kcp2k offset += (int)segment.data.Position; } + // dead link happens if a message was resent N times, but an + // ack was still not received. if (segment.xmit >= dead_link) { state = -1; diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Kcp.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Kcp.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Kcp.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Kcp.cs.meta index ec0a3e1..935b423 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Kcp.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Kcp.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Pool.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Pool.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Pool.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Pool.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Pool.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Pool.cs.meta new file mode 100644 index 0000000..5eba0e0 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Pool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 35c07818fc4784bb4ba472c8e5029002 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Segment.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Segment.cs similarity index 87% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Segment.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Segment.cs index b5c9dcf..b82935a 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Segment.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Segment.cs @@ -7,7 +7,7 @@ namespace kcp2k { internal uint conv; // conversation internal uint cmd; // command, e.g. Kcp.CMD_ACK etc. - internal uint frg; // fragment + internal uint frg; // fragment (sent as 1 byte) internal uint wnd; // window size that the receive can currently receive internal uint ts; // timestamp internal uint sn; // serial number @@ -15,7 +15,7 @@ namespace kcp2k internal uint resendts; // resend timestamp internal int rto; internal uint fastack; - internal uint xmit; + internal uint xmit; // retransmit count // we need an auto scaling byte[] with a WriteBytes function. // MemoryStream does that perfectly, no need to reinvent the wheel. @@ -30,6 +30,9 @@ namespace kcp2k int offset_ = offset; offset += Utils.Encode32U(ptr, offset, conv); offset += Utils.Encode8u(ptr, offset, (byte)cmd); + // IMPORTANT kcp encodes 'frg' as 1 byte. + // so we can only support up to 255 fragments. + // (which limits max message size to around 288 KB) offset += Utils.Encode8u(ptr, offset, (byte)frg); offset += Utils.Encode16U(ptr, offset, (ushort)wnd); offset += Utils.Encode32U(ptr, offset, ts); diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Segment.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Segment.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Segment.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Segment.cs.meta index 913d137..d14dc1a 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Segment.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Segment.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Utils.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Utils.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/kcp/Utils.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Utils.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Utils.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Utils.cs.meta new file mode 100644 index 0000000..86118bc --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/kcp/Utils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ef959eb716205bd48b050f010a9a35ae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation.meta new file mode 100644 index 0000000..5c72cf0 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e9de45e025f26411bbb52d1aefc8d5a5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/LICENSE b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/LICENSE similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/LICENSE rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/LICENSE diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/LICENSE.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/LICENSE.meta new file mode 100644 index 0000000..4fadbdf --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/LICENSE.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a857d4e863bbf4a7dba70bc2cd1b5949 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts.meta new file mode 100644 index 0000000..6878ad8 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6b7f3f8e8fa16475bbe48a8e9fbe800b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/AssemblyInfo.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/AssemblyInfo.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/AssemblyInfo.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/AssemblyInfo.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/AssemblyInfo.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/AssemblyInfo.cs.meta new file mode 100644 index 0000000..1edb254 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/AssemblyInfo.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 158a96a7489b450485a8b06a13328871 +timeCreated: 1622356221 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/Extensions.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/Extensions.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/Extensions.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/Extensions.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/Extensions.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/Extensions.cs.meta new file mode 100644 index 0000000..c4fa54d --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/Extensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9e801942544d44d65808fb250623fe25 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/IPEndPointNonAlloc.cs b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/IPEndPointNonAlloc.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/IPEndPointNonAlloc.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/IPEndPointNonAlloc.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/IPEndPointNonAlloc.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/IPEndPointNonAlloc.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/IPEndPointNonAlloc.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/IPEndPointNonAlloc.cs.meta index 4a01958..ef424ba 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/IPEndPointNonAlloc.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/IPEndPointNonAlloc.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/where-allocations.asmdef b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/where-allocations.asmdef similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/where-allocations.asmdef rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/where-allocations.asmdef diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/where-allocations.asmdef.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/where-allocations.asmdef.meta similarity index 63% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/where-allocations.asmdef.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/where-allocations.asmdef.meta index 835fa1f..ce96c63 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/Scripts/where-allocations.asmdef.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/Scripts/where-allocations.asmdef.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 63c380d6dae6946209ed0832388a657c AssemblyDefinitionImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/VERSION b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/VERSION similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/KCP/kcp2k/where-allocation/VERSION rename to UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/VERSION diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/VERSION.meta b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/VERSION.meta new file mode 100644 index 0000000..67ab688 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/KCP/kcp2k/where-allocation/VERSION.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f1256cadc037546ccb66071784fce137 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/LatencySimulation.cs b/UnityProject/Assets/Mirror/Runtime/Transports/LatencySimulation.cs similarity index 90% rename from UnityProject/Assets/Mirror/Runtime/Transport/LatencySimulation.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/LatencySimulation.cs index d6f836d..5a3ce93 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/LatencySimulation.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/LatencySimulation.cs @@ -2,9 +2,14 @@ // // reliable: latency // unreliable: latency, loss, scramble (unreliable isn't ordered so we scramble) +// +// IMPORTANT: use Time.unscaledTime instead of Time.time. +// some games might have Time.timeScale modified. +// see also: https://github.com/vis2k/Mirror/issues/2907 using System; using System.Collections.Generic; using UnityEngine; +using UnityEngine.Serialization; namespace Mirror { @@ -22,10 +27,12 @@ namespace Mirror public Transport wrap; [Header("Common")] - [Tooltip("Spike latency via perlin(Time * speedMultiplier) * spikeMultiplier")] - [Range(0, 1)] public float latencySpikeMultiplier; - [Tooltip("Spike latency via perlin(Time * speedMultiplier) * spikeMultiplier")] - public float latencySpikeSpeedMultiplier = 1; + [Tooltip("Jitter latency via perlin(Time * jitterSpeed) * jitter")] + [FormerlySerializedAs("latencySpikeMultiplier")] + [Range(0, 1)] public float jitter; + [Tooltip("Jitter latency via perlin(Time * jitterSpeed) * jitter")] + [FormerlySerializedAs("latencySpikeSpeedMultiplier")] + public float jitterSpeed = 1; [Header("Reliable Messages")] [Tooltip("Reliable latency in seconds")] @@ -59,7 +66,7 @@ namespace Mirror public void Awake() { if (wrap == null) - throw new Exception("PressureDrop requires an underlying transport to wrap around."); + throw new Exception("LatencySimulationTransport requires an underlying transport to wrap around."); } // forward enable/disable to the wrapped transport @@ -76,7 +83,7 @@ namespace Mirror // no spikes isn't realistic. // sin is too predictable / no realistic. // perlin is still deterministic and random enough. - float spike = Noise(Time.time * latencySpikeSpeedMultiplier) * latencySpikeMultiplier; + float spike = Noise(Time.unscaledTime * jitterSpeed) * jitter; // base latency switch (channeldId) @@ -103,7 +110,7 @@ namespace Mirror { connectionId = connectionId, bytes = bytes, - time = Time.time + latency + time = Time.unscaledTime + latency }; switch (channelId) @@ -206,7 +213,7 @@ namespace Mirror { // check the first message time QueuedMessage message = reliableClientToServer[0]; - if (message.time <= Time.time) + if (message.time <= Time.unscaledTime) { // send and eat wrap.ClientSend(new ArraySegment(message.bytes), Channels.Reliable); @@ -221,7 +228,7 @@ namespace Mirror { // check the first message time QueuedMessage message = unreliableClientToServer[0]; - if (message.time <= Time.time) + if (message.time <= Time.unscaledTime) { // send and eat wrap.ClientSend(new ArraySegment(message.bytes), Channels.Unreliable); @@ -241,7 +248,7 @@ namespace Mirror { // check the first message time QueuedMessage message = reliableServerToClient[0]; - if (message.time <= Time.time) + if (message.time <= Time.unscaledTime) { // send and eat wrap.ServerSend(message.connectionId, new ArraySegment(message.bytes), Channels.Reliable); @@ -256,7 +263,7 @@ namespace Mirror { // check the first message time QueuedMessage message = unreliableServerToClient[0]; - if (message.time <= Time.time) + if (message.time <= Time.unscaledTime) { // send and eat wrap.ServerSend(message.connectionId, new ArraySegment(message.bytes), Channels.Unreliable); @@ -270,7 +277,7 @@ namespace Mirror wrap.ServerLateUpdate(); } - public override int GetMaxBatchSize(int channelId) => wrap.GetMaxBatchSize(channelId); + public override int GetBatchThreshold(int channelId) => wrap.GetBatchThreshold(channelId); public override int GetMaxPacketSize(int channelId = 0) => wrap.GetMaxPacketSize(channelId); public override void Shutdown() => wrap.Shutdown(); diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/LatencySimulation.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/LatencySimulation.cs.meta similarity index 79% rename from UnityProject/Assets/Mirror/Runtime/Transport/LatencySimulation.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/LatencySimulation.cs.meta index 8b02be0..eabbe4a 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/LatencySimulation.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/LatencySimulation.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/MiddlewareTransport.cs b/UnityProject/Assets/Mirror/Runtime/Transports/MiddlewareTransport.cs similarity index 83% rename from UnityProject/Assets/Mirror/Runtime/Transport/MiddlewareTransport.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/MiddlewareTransport.cs index a0a64ad..7dd934a 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/MiddlewareTransport.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/MiddlewareTransport.cs @@ -16,6 +16,7 @@ namespace Mirror public override bool Available() => inner.Available(); public override int GetMaxPacketSize(int channelId = 0) => inner.GetMaxPacketSize(channelId); + public override int GetBatchThreshold(int channelId = Channels.Reliable) => inner.GetBatchThreshold(channelId); public override void Shutdown() => inner.Shutdown(); #region Client @@ -31,6 +32,9 @@ namespace Mirror public override bool ClientConnected() => inner.ClientConnected(); public override void ClientDisconnect() => inner.ClientDisconnect(); public override void ClientSend(ArraySegment segment, int channelId) => inner.ClientSend(segment, channelId); + + public override void ClientEarlyUpdate() => inner.ClientEarlyUpdate(); + public override void ClientLateUpdate() => inner.ClientLateUpdate(); #endregion #region Server @@ -49,6 +53,9 @@ namespace Mirror public override void ServerDisconnect(int connectionId) => inner.ServerDisconnect(connectionId); public override string ServerGetClientAddress(int connectionId) => inner.ServerGetClientAddress(connectionId); public override Uri ServerUri() => inner.ServerUri(); + + public override void ServerEarlyUpdate() => inner.ServerEarlyUpdate(); + public override void ServerLateUpdate() => inner.ServerLateUpdate(); #endregion } } diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/MiddlewareTransport.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/MiddlewareTransport.cs.meta similarity index 79% rename from UnityProject/Assets/Mirror/Runtime/Transport/MiddlewareTransport.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/MiddlewareTransport.cs.meta index b76168d..dce8378 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/MiddlewareTransport.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/MiddlewareTransport.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/MultiplexTransport.cs b/UnityProject/Assets/Mirror/Runtime/Transports/MultiplexTransport.cs similarity index 99% rename from UnityProject/Assets/Mirror/Runtime/Transport/MultiplexTransport.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/MultiplexTransport.cs index 0d0503d..86ca6e4 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/MultiplexTransport.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/MultiplexTransport.cs @@ -185,9 +185,9 @@ namespace Mirror OnServerDataReceived.Invoke(FromBaseId(locali, baseConnectionId), data, channel); }; - transport.OnServerError = (baseConnectionId, error) => + transport.OnServerError = (baseConnectionId, error, reason) => { - OnServerError.Invoke(FromBaseId(locali, baseConnectionId), error); + OnServerError.Invoke(FromBaseId(locali, baseConnectionId), error, reason); }; transport.OnServerDisconnected = baseConnectionId => { diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/MultiplexTransport.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/MultiplexTransport.cs.meta similarity index 79% rename from UnityProject/Assets/Mirror/Runtime/Transport/MultiplexTransport.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/MultiplexTransport.cs.meta index 4e9e01f..6e97b28 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/MultiplexTransport.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/MultiplexTransport.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport.meta new file mode 100644 index 0000000..5baa80f --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a3ba68af305d809418d6c6a804939290 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/AssemblyInfo.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/AssemblyInfo.cs similarity index 73% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/AssemblyInfo.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/AssemblyInfo.cs index 25269e2..7bc5c17 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/AssemblyInfo.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/AssemblyInfo.cs @@ -1,4 +1,7 @@ +using System.Reflection; using System.Runtime.CompilerServices; +[assembly: AssemblyVersion("1.3.0")] + [assembly: InternalsVisibleTo("SimpleWebTransport.Tests.Runtime")] [assembly: InternalsVisibleTo("SimpleWebTransport.Tests.Editor")] diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/AssemblyInfo.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/AssemblyInfo.cs.meta new file mode 100644 index 0000000..028a307 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/AssemblyInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ee9e76201f7665244bd6ab8ea343a83f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/CHANGELOG.md b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/CHANGELOG.md new file mode 100644 index 0000000..d98f014 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/CHANGELOG.md @@ -0,0 +1,48 @@ +# [1.3.0](https://github.com/James-Frowen/SimpleWebTransport/compare/v1.2.7...v1.3.0) (2022-02-12) + + +### Features + +* Allowing max message size to be increase to int32.max ([#2](https://github.com/James-Frowen/SimpleWebTransport/issues/2)) ([4cc60fd](https://github.com/James-Frowen/SimpleWebTransport/commit/4cc60fd67f3c65d90ced0e6f9f97d15d0368076d)) + +## [1.2.7](https://github.com/James-Frowen/SimpleWebTransport/compare/v1.2.6...v1.2.7) (2022-02-12) + + +### Bug Fixes + +* fixing ObjectDisposedException in toString ([426de52](https://github.com/James-Frowen/SimpleWebTransport/commit/426de52ee4e98ac6212713b2b2272e3affb8fc99)) + +## [1.2.6](https://github.com/James-Frowen/SimpleWebTransport/compare/v1.2.5...v1.2.6) (2022-02-02) + + +### Bug Fixes + +* fixing Runtime is not defined for unity 2021 ([945b50d](https://github.com/James-Frowen/SimpleWebTransport/commit/945b50dbad5b71c43e2bdaa4033f87d3f62c5572)) + +## [1.2.5](https://github.com/James-Frowen/SimpleWebTransport/compare/v1.2.4...v1.2.5) (2022-02-02) + + +### Bug Fixes + +* updating Pointer_stringify to UTF8ToString ([2f5a74b](https://github.com/James-Frowen/SimpleWebTransport/commit/2f5a74ba10865e934be8d3b54ebfdeb14ca491f6)) + +## [1.2.4](https://github.com/James-Frowen/SimpleWebTransport/compare/v1.2.3...v1.2.4) (2021-12-16) + + +### Bug Fixes + +* adding meta file for changelog ([ba5b164](https://github.com/James-Frowen/SimpleWebTransport/commit/ba5b1647aa5cc69ca80f5b52c542a9b5ee749c7f)) + +## [1.2.3](https://github.com/James-Frowen/SimpleWebTransport/compare/v1.2.2...v1.2.3) (2021-12-16) + + +### Bug Fixes + +* fixing compile error in assemblyInfo ([7ee8380](https://github.com/James-Frowen/SimpleWebTransport/commit/7ee8380b4daf34d4e12017de55d8be481690046f)) + +## [1.2.2](https://github.com/James-Frowen/SimpleWebTransport/compare/v1.2.1...v1.2.2) (2021-12-16) + + +### Bug Fixes + +* fixing release with empty commit ([068af74](https://github.com/James-Frowen/SimpleWebTransport/commit/068af74f7399354081f25181f90fb060b0fa1524)) diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/CHANGELOG.md.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/CHANGELOG.md.meta new file mode 100644 index 0000000..bc43099 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/CHANGELOG.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b0ef23ac1c6a62546bbad5529b3bfdad +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client.meta new file mode 100644 index 0000000..e6e2943 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5faa957b8d9fc314ab7596ccf14750d9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/SimpleWebClient.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/SimpleWebClient.cs similarity index 83% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/SimpleWebClient.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/SimpleWebClient.cs index 5c9e030..3569af3 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/SimpleWebClient.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/SimpleWebClient.cs @@ -28,7 +28,7 @@ namespace Mirror.SimpleWeb readonly int maxMessagesPerTick; protected readonly int maxMessageSize; - protected readonly ConcurrentQueue receiveQueue = new ConcurrentQueue(); + public readonly ConcurrentQueue receiveQueue = new ConcurrentQueue(); protected readonly BufferPool bufferPool; protected ClientState state; @@ -47,12 +47,25 @@ namespace Mirror.SimpleWeb public event Action> onData; public event Action onError; + /// + /// Processes all new messages + /// + public void ProcessMessageQueue() + { + ProcessMessageQueue(null); + } + + /// + /// Processes all messages while is enabled + /// + /// public void ProcessMessageQueue(MonoBehaviour behaviour) { int processedCount = 0; + bool skipEnabled = behaviour == null; // check enabled every time in case behaviour was disabled after data while ( - behaviour.enabled && + (skipEnabled || behaviour.enabled) && processedCount < maxMessagesPerTick && // Dequeue last receiveQueue.TryDequeue(out Message next) diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/SimpleWebClient.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/SimpleWebClient.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/SimpleWebClient.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/SimpleWebClient.cs.meta index e588926..90c361b 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/SimpleWebClient.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/SimpleWebClient.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone.meta new file mode 100644 index 0000000..bf320c6 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a9c19d05220a87c4cbbe4d1e422da0aa +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone/ClientHandshake.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone/ClientHandshake.cs similarity index 97% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone/ClientHandshake.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone/ClientHandshake.cs index 24c3896..e5fccf9 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone/ClientHandshake.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone/ClientHandshake.cs @@ -55,7 +55,7 @@ namespace Mirror.SimpleWeb string responseString = Encoding.ASCII.GetString(responseBuffer, 0, lengthOrNull.Value); string acceptHeader = "Sec-WebSocket-Accept: "; - int startIndex = responseString.IndexOf(acceptHeader) + acceptHeader.Length; + int startIndex = responseString.IndexOf(acceptHeader, StringComparison.InvariantCultureIgnoreCase) + acceptHeader.Length; int endIndex = responseString.IndexOf("\r\n", startIndex); string responseKey = responseString.Substring(startIndex, endIndex - startIndex); diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone/ClientHandshake.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone/ClientHandshake.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone/ClientHandshake.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone/ClientHandshake.cs.meta index b3e4491..ad3d40d 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone/ClientHandshake.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone/ClientHandshake.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone/ClientSslHelper.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone/ClientSslHelper.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone/ClientSslHelper.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone/ClientSslHelper.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone/ClientSslHelper.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone/ClientSslHelper.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone/ClientSslHelper.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone/ClientSslHelper.cs.meta index 387f947..d6be2bb 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone/ClientSslHelper.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone/ClientSslHelper.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone/WebSocketClientStandAlone.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone/WebSocketClientStandAlone.cs similarity index 90% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone/WebSocketClientStandAlone.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone/WebSocketClientStandAlone.cs index 7eae284..3414afb 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone/WebSocketClientStandAlone.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone/WebSocketClientStandAlone.cs @@ -11,7 +11,6 @@ namespace Mirror.SimpleWeb readonly TcpConfig tcpConfig; Connection conn; - internal WebSocketClientStandAlone(int maxMessageSize, int maxMessagesPerTick, TcpConfig tcpConfig) : base(maxMessageSize, maxMessagesPerTick) { #if UNITY_WEBGL && !UNITY_EDITOR @@ -26,6 +25,14 @@ namespace Mirror.SimpleWeb public override void Connect(Uri serverAddress) { state = ClientState.Connecting; + + // create connection here before thread so that send queue exist before connected + TcpClient client = new TcpClient(); + tcpConfig.ApplyTo(client); + + // create connection object here so dispose correctly disconnects on failed connect + conn = new Connection(client, AfterConnectionDisposed); + Thread receiveThread = new Thread(() => ConnectAndReceiveLoop(serverAddress)); receiveThread.IsBackground = true; receiveThread.Start(); @@ -35,11 +42,8 @@ namespace Mirror.SimpleWeb { try { - TcpClient client = new TcpClient(); - tcpConfig.ApplyTo(client); - - // create connection object here so dispose correctly disconnects on failed connect - conn = new Connection(client, AfterConnectionDisposed); + // connection created above + TcpClient client = conn.client; conn.receiveThread = Thread.CurrentThread; try diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone/WebSocketClientStandAlone.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone/WebSocketClientStandAlone.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone/WebSocketClientStandAlone.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone/WebSocketClientStandAlone.cs.meta index 2935207..37229d3 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/StandAlone/WebSocketClientStandAlone.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/StandAlone/WebSocketClientStandAlone.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl.meta new file mode 100644 index 0000000..2d81f7f --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7142349d566213c4abc763afaf4d91a1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/SimpleWebJSLib.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/SimpleWebJSLib.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/SimpleWebJSLib.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/SimpleWebJSLib.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/SimpleWebJSLib.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/SimpleWebJSLib.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/SimpleWebJSLib.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/SimpleWebJSLib.cs.meta index de62085..9dfa12e 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/SimpleWebJSLib.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/SimpleWebJSLib.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/WebSocketClientWebGl.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/WebSocketClientWebGl.cs similarity index 73% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/WebSocketClientWebGl.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/WebSocketClientWebGl.cs index 0c953ef..ece94d6 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/WebSocketClientWebGl.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/WebSocketClientWebGl.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using AOT; namespace Mirror.SimpleWeb @@ -13,6 +14,15 @@ namespace Mirror.SimpleWeb /// int index; + /// + /// Queue for messages sent by high level while still connecting, they will be sent after onOpen is called. + /// + /// This is a workaround for anything that calls Send immediately after Connect. + /// Without this the JS websocket will give errors. + /// + /// + Queue ConnectingSendQueue; + internal WebSocketClientWebGl(int maxMessageSize, int maxMessagesPerTick) : base(maxMessageSize, maxMessagesPerTick) { #if !UNITY_WEBGL || UNITY_EDITOR @@ -44,13 +54,32 @@ namespace Mirror.SimpleWeb return; } - SimpleWebJSLib.Send(index, segment.Array, 0, segment.Count); + if (state == ClientState.Connected) + { + SimpleWebJSLib.Send(index, segment.Array, segment.Offset, segment.Count); + } + else + { + if (ConnectingSendQueue == null) + ConnectingSendQueue = new Queue(); + ConnectingSendQueue.Enqueue(segment.ToArray()); + } } void onOpen() { receiveQueue.Enqueue(new Message(EventType.Connected)); state = ClientState.Connected; + + if (ConnectingSendQueue != null) + { + while (ConnectingSendQueue.Count > 0) + { + byte[] next = ConnectingSendQueue.Dequeue(); + SimpleWebJSLib.Send(index, next, 0, next.Length); + } + ConnectingSendQueue = null; + } } void onClose() diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/WebSocketClientWebGl.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/WebSocketClientWebGl.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/WebSocketClientWebGl.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/WebSocketClientWebGl.cs.meta index f467f5a..3827d3a 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/WebSocketClientWebGl.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/WebSocketClientWebGl.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/plugin.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/plugin.meta new file mode 100644 index 0000000..b516a8f --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/plugin.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1999985791b91b9458059e88404885a7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/plugin/SimpleWeb.jslib b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/plugin/SimpleWeb.jslib similarity index 88% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/plugin/SimpleWeb.jslib rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/plugin/SimpleWeb.jslib index 13da1b7..02e6b93 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/plugin/SimpleWeb.jslib +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/plugin/SimpleWeb.jslib @@ -27,7 +27,16 @@ function IsConnected(index) { } function Connect(addressPtr, openCallbackPtr, closeCallBackPtr, messageCallbackPtr, errorCallbackPtr) { - const address = Pointer_stringify(addressPtr); + // fix for unity 2021 because unity bug in .jslib + if (typeof Runtime === "undefined") { + // if unity doesn't create Runtime, then make it here + // dont ask why this works, just be happy that it does + Runtime = { + dynCall: dynCall + } + } + + const address = UTF8ToString(addressPtr); console.log("Connecting to " + address); // Create webSocket connection. webSocket = new WebSocket(address); @@ -102,4 +111,4 @@ const SimpleWebLib = { Send }; autoAddDeps(SimpleWebLib, '$SimpleWeb'); -mergeInto(LibraryManager.library, SimpleWebLib); \ No newline at end of file +mergeInto(LibraryManager.library, SimpleWebLib); diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/plugin/SimpleWeb.jslib.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/plugin/SimpleWeb.jslib.meta similarity index 89% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/plugin/SimpleWeb.jslib.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/plugin/SimpleWeb.jslib.meta index 592c218..cc1319e 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Client/Webgl/plugin/SimpleWeb.jslib.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Client/Webgl/plugin/SimpleWeb.jslib.meta @@ -12,7 +12,7 @@ PluginImporter: validateReferences: 1 platformData: - first: - Any: '' + Any: second: enabled: 0 settings: {} @@ -32,6 +32,6 @@ PluginImporter: second: enabled: 1 settings: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common.meta new file mode 100644 index 0000000..078faaa --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 564d2cd3eee5b21419553c0528739d1b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/BufferPool.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/BufferPool.cs similarity index 97% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/BufferPool.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/BufferPool.cs index 315d371..4262feb 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/BufferPool.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/BufferPool.cs @@ -18,9 +18,9 @@ namespace Mirror.SimpleWeb public readonly byte[] array; /// - /// number of bytes writen to buffer + /// number of bytes written to buffer /// - internal int count; + public int count { get; internal set; } /// /// How many times release needs to be called before buffer is returned to pool @@ -52,7 +52,7 @@ namespace Mirror.SimpleWeb if (newValue <= 0) { count = 0; - owner.Return(this); + owner?.Return(this); } } public void Dispose() @@ -228,7 +228,7 @@ namespace Mirror.SimpleWeb // 3056 e^ (3 + 1.675 * 3) // 16,317 e^ (3 + 1.675 * 4) - // perceision wont be lose when using doubles + // precision wont be lose when using doubles } [Conditional("UNITY_ASSERTIONS")] diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/BufferPool.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/BufferPool.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/BufferPool.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/BufferPool.cs.meta index a93355f..0b1070f 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/BufferPool.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/BufferPool.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Connection.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Connection.cs similarity index 89% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Connection.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Connection.cs index f16dd7c..adc52db 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Connection.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Connection.cs @@ -32,7 +32,6 @@ namespace Mirror.SimpleWeb this.onDispose = onDispose; } - /// /// disposes client and stops threads /// @@ -83,8 +82,15 @@ namespace Mirror.SimpleWeb public override string ToString() { - System.Net.EndPoint endpoint = client?.Client?.RemoteEndPoint; - return $"[Conn:{connId}, endPoint:{endpoint}]"; + if (hasDisposed) + { + return $"[Conn:{connId}, Disposed]"; + } + else + { + System.Net.EndPoint endpoint = client?.Client?.RemoteEndPoint; + return $"[Conn:{connId}, endPoint:{endpoint}]"; + } } } } diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Connection.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Connection.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Connection.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Connection.cs.meta index 2238bb8..d48a835 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Connection.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Connection.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Constants.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Constants.cs similarity index 94% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Constants.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Constants.cs index cc94cf3..3aa16c3 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Constants.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Constants.cs @@ -31,6 +31,11 @@ namespace Mirror.SimpleWeb /// public const int ShortLength = 2; + /// + /// bytes for long length + /// + public const int LongLength = 8; + /// /// Message mask is always 4 bytes /// diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Constants.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Constants.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Constants.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Constants.cs.meta index e819821..ece602e 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Constants.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Constants.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/EventType.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/EventType.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/EventType.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/EventType.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/EventType.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/EventType.cs.meta new file mode 100644 index 0000000..a91403a --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/EventType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2d9cd7d2b5229ab42a12e82ae17d0347 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Log.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Log.cs similarity index 70% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Log.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Log.cs index a807b47..4b7bce5 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Log.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Log.cs @@ -19,6 +19,7 @@ namespace Mirror.SimpleWeb verbose = 4, } + public static ILogger logger = Debug.unityLogger; public static Levels level = Levels.none; public static string BufferToString(byte[] buffer, int offset = 0, int? length = null) @@ -32,7 +33,7 @@ namespace Mirror.SimpleWeb if (level < Levels.verbose) return; - Debug.Log($"VERBOSE: {label}: {BufferToString(buffer, offset, length)}"); + logger.Log(LogType.Log, $"VERBOSE: {label}: {BufferToString(buffer, offset, length)}"); } [Conditional(SIMPLEWEB_LOG_ENABLED)] @@ -41,7 +42,7 @@ namespace Mirror.SimpleWeb if (level < Levels.verbose) return; - Debug.Log($"VERBOSE: {label}: {BufferToString(arrayBuffer.array, 0, arrayBuffer.count)}"); + logger.Log(LogType.Log, $"VERBOSE: {label}: {BufferToString(arrayBuffer.array, 0, arrayBuffer.count)}"); } [Conditional(SIMPLEWEB_LOG_ENABLED)] @@ -51,9 +52,9 @@ namespace Mirror.SimpleWeb return; if (showColor) - Debug.Log($"VERBOSE: {msg}"); + logger.Log(LogType.Log, $"VERBOSE: {msg}"); else - Debug.Log($"VERBOSE: {msg}"); + logger.Log(LogType.Log, $"VERBOSE: {msg}"); } [Conditional(SIMPLEWEB_LOG_ENABLED)] @@ -63,9 +64,9 @@ namespace Mirror.SimpleWeb return; if (showColor) - Debug.Log($"INFO: {msg}"); + logger.Log(LogType.Log, $"INFO: {msg}"); else - Debug.Log($"INFO: {msg}"); + logger.Log(LogType.Log, $"INFO: {msg}"); } /// @@ -79,7 +80,7 @@ namespace Mirror.SimpleWeb if (level < Levels.info) return; - Debug.Log($"INFO_EXCEPTION: {e.GetType().Name} Message: {e.Message}"); + logger.Log(LogType.Log, $"INFO_EXCEPTION: {e.GetType().Name} Message: {e.Message}\n{e.StackTrace}\n\n"); } [Conditional(SIMPLEWEB_LOG_ENABLED), Conditional(DEBUG)] @@ -89,9 +90,9 @@ namespace Mirror.SimpleWeb return; if (showColor) - Debug.LogWarning($"WARN: {msg}"); + logger.Log(LogType.Warning, $"WARN: {msg}"); else - Debug.LogWarning($"WARN: {msg}"); + logger.Log(LogType.Warning, $"WARN: {msg}"); } [Conditional(SIMPLEWEB_LOG_ENABLED), Conditional(DEBUG)] @@ -101,15 +102,15 @@ namespace Mirror.SimpleWeb return; if (showColor) - Debug.LogError($"ERROR: {msg}"); + logger.Log(LogType.Error, $"ERROR: {msg}"); else - Debug.LogError($"ERROR: {msg}"); + logger.Log(LogType.Error, $"ERROR: {msg}"); } public static void Exception(Exception e) { // always log Exceptions - Debug.LogError($"EXCEPTION: {e.GetType().Name} Message: {e.Message}"); + logger.Log(LogType.Error, $"EXCEPTION: {e.GetType().Name} Message: {e.Message}\n{e.StackTrace}\n\n"); } } } diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Log.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Log.cs.meta new file mode 100644 index 0000000..beb2883 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Log.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3cf1521098e04f74fbea0fe2aa0439f8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Message.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Message.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Message.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Message.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Message.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Message.cs.meta new file mode 100644 index 0000000..3286a2c --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Message.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f5d05d71b09d2714b96ffe80bc3d2a77 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/MessageProcessor.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/MessageProcessor.cs similarity index 58% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/MessageProcessor.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/MessageProcessor.cs index 1bf98f0..59c9326 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/MessageProcessor.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/MessageProcessor.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using System.Runtime.CompilerServices; @@ -8,35 +9,55 @@ namespace Mirror.SimpleWeb [MethodImpl(MethodImplOptions.AggressiveInlining)] static byte FirstLengthByte(byte[] buffer) => (byte)(buffer[1] & 0b0111_1111); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool NeedToReadShortLength(byte[] buffer) { byte lenByte = FirstLengthByte(buffer); - return lenByte >= Constants.UshortPayloadLength; + return lenByte == Constants.UshortPayloadLength; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool NeedToReadLongLength(byte[] buffer) + { + byte lenByte = FirstLengthByte(buffer); + + return lenByte == Constants.UlongPayloadLength; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetOpcode(byte[] buffer) { return buffer[0] & 0b0000_1111; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetPayloadLength(byte[] buffer) { byte lenByte = FirstLengthByte(buffer); return GetMessageLength(buffer, 0, lenByte); } - public static void ValidateHeader(byte[] buffer, int maxLength, bool expectMask) + /// + /// Has full message been sent + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Finished(byte[] buffer) { - bool finished = (buffer[0] & 0b1000_0000) != 0; // has full message been sent + return (buffer[0] & 0b1000_0000) != 0; + } + + public static void ValidateHeader(byte[] buffer, int maxLength, bool expectMask, bool opCodeContinuation = false) + { + bool finished = Finished(buffer); bool hasMask = (buffer[1] & 0b1000_0000) != 0; // true from clients, false from server, "All messages from the client to the server have this bit set" int opcode = buffer[0] & 0b0000_1111; // expecting 1 - text message byte lenByte = FirstLengthByte(buffer); - ThrowIfNotFinished(finished); ThrowIfMaskNotExpected(hasMask, expectMask); - ThrowIfBadOpCode(opcode); + ThrowIfBadOpCode(opcode, finished, opCodeContinuation); int msglen = GetMessageLength(buffer, 0, lenByte); @@ -44,17 +65,20 @@ namespace Mirror.SimpleWeb ThrowIfMsgLengthTooLong(msglen, maxLength); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToggleMask(byte[] src, int sourceOffset, int messageLength, byte[] maskBuffer, int maskOffset) { ToggleMask(src, sourceOffset, src, sourceOffset, messageLength, maskBuffer, maskOffset); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToggleMask(byte[] src, int sourceOffset, ArrayBuffer dst, int messageLength, byte[] maskBuffer, int maskOffset) { ToggleMask(src, sourceOffset, dst.array, 0, messageLength, maskBuffer, maskOffset); dst.count = messageLength; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToggleMask(byte[] src, int srcOffset, byte[] dst, int dstOffset, int messageLength, byte[] maskBuffer, int maskOffset) { for (int i = 0; i < messageLength; i++) @@ -69,7 +93,7 @@ namespace Mirror.SimpleWeb { if (lenByte == Constants.UshortPayloadLength) { - // header is 4 bytes long + // header is 2 bytes ushort value = 0; value |= (ushort)(buffer[offset + 2] << 8); value |= buffer[offset + 3]; @@ -78,7 +102,22 @@ namespace Mirror.SimpleWeb } else if (lenByte == Constants.UlongPayloadLength) { - throw new InvalidDataException("Max length is longer than allowed in a single message"); + // header is 8 bytes + ulong value = 0; + value |= ((ulong)buffer[offset + 2] << 56); + value |= ((ulong)buffer[offset + 3] << 48); + value |= ((ulong)buffer[offset + 4] << 40); + value |= ((ulong)buffer[offset + 5] << 32); + value |= ((ulong)buffer[offset + 6] << 24); + value |= ((ulong)buffer[offset + 7] << 16); + value |= ((ulong)buffer[offset + 8] << 8); + value |= ((ulong)buffer[offset + 9] << 0); + + if (value > int.MaxValue) + { + throw new NotSupportedException($"Can't receive payloads larger that int.max: {int.MaxValue}"); + } + return (int)value; } else // is less than 126 { @@ -87,15 +126,6 @@ namespace Mirror.SimpleWeb } } - /// - static void ThrowIfNotFinished(bool finished) - { - if (!finished) - { - throw new InvalidDataException("Full message should have been sent, if the full message wasn't sent it wasn't sent from this trasnport"); - } - } - /// static void ThrowIfMaskNotExpected(bool hasMask, bool expectMask) { @@ -106,12 +136,36 @@ namespace Mirror.SimpleWeb } /// - static void ThrowIfBadOpCode(int opcode) + static void ThrowIfBadOpCode(int opcode, bool finished, bool opCodeContinuation) { + // 0 = continuation // 2 = binary // 8 = close - if (opcode != 2 && opcode != 8) + + // do we expect Continuation? + if (opCodeContinuation) { + // good it was Continuation + if (opcode == 0) + return; + + // bad, wasn't Continuation + throw new InvalidDataException("Expected opcode to be Continuation"); + } + else if (!finished) + { + // fragmented message, should be binary + if (opcode == 2) + return; + + throw new InvalidDataException("Expected opcode to be binary"); + } + else + { + // normal message, should be binary or close + if (opcode == 2 || opcode == 8) + return; + throw new InvalidDataException("Expected opcode to be binary or close"); } } @@ -128,8 +182,7 @@ namespace Mirror.SimpleWeb /// /// need to check this so that data from previous buffer isn't used /// - /// - static void ThrowIfMsgLengthTooLong(int msglen, int maxLength) + public static void ThrowIfMsgLengthTooLong(int msglen, int maxLength) { if (msglen > maxLength) { diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/MessageProcessor.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/MessageProcessor.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/MessageProcessor.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/MessageProcessor.cs.meta index f0ebf7b..7e3a7c4 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/MessageProcessor.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/MessageProcessor.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/ReadHelper.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/ReadHelper.cs similarity index 99% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/ReadHelper.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/ReadHelper.cs index 66f36c9..74cbf2d 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/ReadHelper.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/ReadHelper.cs @@ -123,7 +123,7 @@ namespace Mirror.SimpleWeb [Serializable] public class ReadHelperException : Exception { - public ReadHelperException(string message) : base(message) {} + public ReadHelperException(string message) : base(message) { } protected ReadHelperException(SerializationInfo info, StreamingContext context) : base(info, context) { diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/ReadHelper.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/ReadHelper.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/ReadHelper.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/ReadHelper.cs.meta index 1d32a00..77d09c1 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/ReadHelper.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/ReadHelper.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/ReceiveLoop.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/ReceiveLoop.cs similarity index 62% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/ReceiveLoop.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/ReceiveLoop.cs index 16126ab..952592c 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/ReceiveLoop.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/ReceiveLoop.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.IO; using System.Net.Sockets; using System.Text; @@ -37,6 +38,14 @@ namespace Mirror.SimpleWeb } } + struct Header + { + public int payloadLength; + public int offset; + public int opcode; + public bool finished; + } + public static void Loop(Config config) { (Connection conn, int maxMessageSize, bool expectMask, ConcurrentQueue queue, BufferPool _) = config; @@ -69,7 +78,6 @@ namespace Mirror.SimpleWeb catch (ObjectDisposedException e) { Log.InfoException(e); } catch (ReadHelperException e) { - // log as info only Log.InfoException(e); } catch (SocketException e) @@ -107,48 +115,114 @@ namespace Mirror.SimpleWeb (Connection conn, int maxMessageSize, bool expectMask, ConcurrentQueue queue, BufferPool bufferPool) = config; Stream stream = conn.stream; - int offset = 0; + Header header = ReadHeader(config, buffer); + + int msgOffset = header.offset; + header.offset = ReadHelper.Read(stream, buffer, header.offset, header.payloadLength); + + if (header.finished) + { + switch (header.opcode) + { + case 2: + HandleArrayMessage(config, buffer, msgOffset, header.payloadLength); + break; + case 8: + HandleCloseMessage(config, buffer, msgOffset, header.payloadLength); + break; + } + } + else + { + // todo cache this to avoid allocations + Queue fragments = new Queue(); + fragments.Enqueue(CopyMessageToBuffer(bufferPool, expectMask, buffer, msgOffset, header.payloadLength)); + int totalSize = header.payloadLength; + + while (!header.finished) + { + header = ReadHeader(config, buffer, opCodeContinuation: true); + + msgOffset = header.offset; + header.offset = ReadHelper.Read(stream, buffer, header.offset, header.payloadLength); + fragments.Enqueue(CopyMessageToBuffer(bufferPool, expectMask, buffer, msgOffset, header.payloadLength)); + + totalSize += header.payloadLength; + MessageProcessor.ThrowIfMsgLengthTooLong(totalSize, maxMessageSize); + } + + + ArrayBuffer msg = bufferPool.Take(totalSize); + msg.count = 0; + while (fragments.Count > 0) + { + ArrayBuffer part = fragments.Dequeue(); + + part.CopyTo(msg.array, msg.count); + msg.count += part.count; + + part.Release(); + } + + // dump after mask off + Log.DumpBuffer($"Message", msg); + + queue.Enqueue(new Message(conn.connId, msg)); + } + } + + static Header ReadHeader(Config config, byte[] buffer, bool opCodeContinuation = false) + { + (Connection conn, int maxMessageSize, bool expectMask, ConcurrentQueue queue, BufferPool bufferPool) = config; + Stream stream = conn.stream; + Header header = new Header(); + // read 2 - offset = ReadHelper.Read(stream, buffer, offset, Constants.HeaderMinSize); + header.offset = ReadHelper.Read(stream, buffer, header.offset, Constants.HeaderMinSize); // log after first blocking call Log.Verbose($"Message From {conn}"); if (MessageProcessor.NeedToReadShortLength(buffer)) { - offset = ReadHelper.Read(stream, buffer, offset, Constants.ShortLength); + header.offset = ReadHelper.Read(stream, buffer, header.offset, Constants.ShortLength); + } + if (MessageProcessor.NeedToReadLongLength(buffer)) + { + header.offset = ReadHelper.Read(stream, buffer, header.offset, Constants.LongLength); } - MessageProcessor.ValidateHeader(buffer, maxMessageSize, expectMask); + Log.DumpBuffer($"Raw Header", buffer, 0, header.offset); + + MessageProcessor.ValidateHeader(buffer, maxMessageSize, expectMask, opCodeContinuation); if (expectMask) { - offset = ReadHelper.Read(stream, buffer, offset, Constants.MaskSize); + header.offset = ReadHelper.Read(stream, buffer, header.offset, Constants.MaskSize); } - int opcode = MessageProcessor.GetOpcode(buffer); - int payloadLength = MessageProcessor.GetPayloadLength(buffer); + header.opcode = MessageProcessor.GetOpcode(buffer); + header.payloadLength = MessageProcessor.GetPayloadLength(buffer); + header.finished = MessageProcessor.Finished(buffer); - Log.Verbose($"Header ln:{payloadLength} op:{opcode} mask:{expectMask}"); - Log.DumpBuffer($"Raw Header", buffer, 0, offset); + Log.Verbose($"Header ln:{header.payloadLength} op:{header.opcode} mask:{expectMask}"); - int msgOffset = offset; - offset = ReadHelper.Read(stream, buffer, offset, payloadLength); - - switch (opcode) - { - case 2: - HandleArrayMessage(config, buffer, msgOffset, payloadLength); - break; - case 8: - HandleCloseMessage(config, buffer, msgOffset, payloadLength); - break; - } + return header; } static void HandleArrayMessage(Config config, byte[] buffer, int msgOffset, int payloadLength) { (Connection conn, int _, bool expectMask, ConcurrentQueue queue, BufferPool bufferPool) = config; + ArrayBuffer arrayBuffer = CopyMessageToBuffer(bufferPool, expectMask, buffer, msgOffset, payloadLength); + + // dump after mask off + Log.DumpBuffer($"Message", arrayBuffer); + + queue.Enqueue(new Message(conn.connId, arrayBuffer)); + } + + static ArrayBuffer CopyMessageToBuffer(BufferPool bufferPool, bool expectMask, byte[] buffer, int msgOffset, int payloadLength) + { ArrayBuffer arrayBuffer = bufferPool.Take(payloadLength); if (expectMask) @@ -162,10 +236,7 @@ namespace Mirror.SimpleWeb arrayBuffer.CopyFrom(buffer, msgOffset, payloadLength); } - // dump after mask off - Log.DumpBuffer($"Message", arrayBuffer); - - queue.Enqueue(new Message(conn.connId, arrayBuffer)); + return arrayBuffer; } static void HandleCloseMessage(Config config, byte[] buffer, int msgOffset, int payloadLength) diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/ReceiveLoop.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/ReceiveLoop.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/ReceiveLoop.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/ReceiveLoop.cs.meta index 8e37342..47c6ff5 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/ReceiveLoop.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/ReceiveLoop.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/SendLoop.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/SendLoop.cs similarity index 77% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/SendLoop.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/SendLoop.cs index 096b7ff..6dc1b1b 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/SendLoop.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/SendLoop.cs @@ -70,7 +70,12 @@ namespace Mirror.SimpleWeb while (conn.sendQueue.TryDequeue(out ArrayBuffer msg)) { // check if connected before sending message - if (!client.Connected) { Log.Info($"SendLoop {conn} not connected"); return; } + if (!client.Connected) + { + Log.Info($"SendLoop {conn} not connected"); + msg.Release(); + return; + } int maxLength = msg.count + Constants.HeaderSize + Constants.MaskSize; @@ -95,7 +100,12 @@ namespace Mirror.SimpleWeb while (conn.sendQueue.TryDequeue(out ArrayBuffer msg)) { // check if connected before sending message - if (!client.Connected) { Log.Info($"SendLoop {conn} not connected"); return; } + if (!client.Connected) + { + Log.Info($"SendLoop {conn} not connected"); + msg.Release(); + return; + } int length = SendMessage(writeBuffer, 0, msg, setMask, maskHelper); stream.Write(writeBuffer, 0, length); @@ -146,7 +156,7 @@ namespace Mirror.SimpleWeb return offset; } - static int WriteHeader(byte[] buffer, int startOffset, int msgLength, bool setMask) + public static int WriteHeader(byte[] buffer, int startOffset, int msgLength, bool setMask) { int sendLength = 0; const byte finished = 128; @@ -169,7 +179,18 @@ namespace Mirror.SimpleWeb } else { - throw new InvalidDataException($"Trying to send a message larger than {ushort.MaxValue} bytes"); + buffer[startOffset + 1] = 127; + // must be 64 bytes, but we only have 32 bit length, so first 4 bits are 0 + buffer[startOffset + 2] = 0; + buffer[startOffset + 3] = 0; + buffer[startOffset + 4] = 0; + buffer[startOffset + 5] = 0; + buffer[startOffset + 6] = (byte)(msgLength >> 24); + buffer[startOffset + 7] = (byte)(msgLength >> 16); + buffer[startOffset + 8] = (byte)(msgLength >> 8); + buffer[startOffset + 9] = (byte)msgLength; + + sendLength += 9; } if (setMask) @@ -180,28 +201,28 @@ namespace Mirror.SimpleWeb return sendLength + startOffset; } - sealed class MaskHelper : IDisposable + } + sealed class MaskHelper : IDisposable + { + readonly byte[] maskBuffer; + readonly RNGCryptoServiceProvider random; + + public MaskHelper() { - readonly byte[] maskBuffer; - readonly RNGCryptoServiceProvider random; + maskBuffer = new byte[4]; + random = new RNGCryptoServiceProvider(); + } + public void Dispose() + { + random.Dispose(); + } - public MaskHelper() - { - maskBuffer = new byte[4]; - random = new RNGCryptoServiceProvider(); - } - public void Dispose() - { - random.Dispose(); - } + public int WriteMask(byte[] buffer, int offset) + { + random.GetBytes(maskBuffer); + Buffer.BlockCopy(maskBuffer, 0, buffer, offset, 4); - public int WriteMask(byte[] buffer, int offset) - { - random.GetBytes(maskBuffer); - Buffer.BlockCopy(maskBuffer, 0, buffer, offset, 4); - - return offset + 4; - } + return offset + 4; } } } diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/SendLoop.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/SendLoop.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/SendLoop.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/SendLoop.cs.meta index 4dab909..09dfd1e 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/SendLoop.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/SendLoop.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/TcpConfig.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/TcpConfig.cs similarity index 92% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/TcpConfig.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/TcpConfig.cs index 8cb4779..230cd7a 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/TcpConfig.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/TcpConfig.cs @@ -1,7 +1,8 @@ -using System.Net.Sockets; +using System.Net.Sockets; namespace Mirror.SimpleWeb { + [System.Serializable] public struct TcpConfig { public readonly bool noDelay; diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/TcpConfig.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/TcpConfig.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/TcpConfig.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/TcpConfig.cs.meta index 2530f1f..62ba232 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/TcpConfig.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/TcpConfig.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Utils.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Utils.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Common/Utils.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Utils.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Utils.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Utils.cs.meta new file mode 100644 index 0000000..79a1583 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Common/Utils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4643ffb4cb0562847b1ae925d07e15b6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/LICENSE b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/LICENSE new file mode 100644 index 0000000..d2b4728 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 James Frowen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/LICENSE.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/LICENSE.meta new file mode 100644 index 0000000..8ece59e --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/LICENSE.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0a0cf751b4a201242ac60b4adbc54657 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/README.txt b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/README.txt new file mode 100644 index 0000000..fd59b3d --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/README.txt @@ -0,0 +1,19 @@ +SimpleWebTransport is a Transport that implements websocket for Webgl +Can be used in High level networking solution like Mirror or Mirage +This transport can also work on standalone builds and has support for +encryption with websocket secure. + +Requirements: + Unity 2019.4 LTS + +Documentation: + https://mirror-networking.gitbook.io/docs/ + https://github.com/James-Frowen/SimpleWebTransport/blob/master/README.md + +Support: + Discord: https://discord.gg/BZTQcftBkE + Bug Reports: https://github.com/James-Frowen/SimpleWebTransport/issues + + +**To get most recent updates and fixes download from github** +https://github.com/James-Frowen/SimpleWebTransport/releases diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Windows/README.txt.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/README.txt.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Windows/README.txt.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/README.txt.meta index b69d8f0..b63fe39 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Ignorance/Plugins/Windows/README.txt.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/README.txt.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 09ee288deb259474c819a5e3daf54b80 +guid: 0e3971d5783109f4d9ce93c7a689d701 TextScriptImporter: externalObjects: {} userData: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server.meta new file mode 100644 index 0000000..31f317f --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0e599e92544d43344a9a9060052add28 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/ServerHandshake.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/ServerHandshake.cs similarity index 93% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/ServerHandshake.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/ServerHandshake.cs index f186eb4..4a7304b 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/ServerHandshake.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/ServerHandshake.cs @@ -15,7 +15,7 @@ namespace Mirror.SimpleWeb const int ResponseLength = 129; const int KeyLength = 24; const int MergedKeyLength = 60; - const string KeyHeaderString = "Sec-WebSocket-Key: "; + const string KeyHeaderString = "\r\nSec-WebSocket-Key: "; // this isn't an official max, just a reasonable size for a websocket handshake readonly int maxHttpHeaderSize = 3000; @@ -25,7 +25,7 @@ namespace Mirror.SimpleWeb public ServerHandshake(BufferPool bufferPool, int handshakeMaxSize) { this.bufferPool = bufferPool; - this.maxHttpHeaderSize = handshakeMaxSize; + maxHttpHeaderSize = handshakeMaxSize; } ~ServerHandshake() @@ -97,7 +97,7 @@ namespace Mirror.SimpleWeb void AcceptHandshake(Stream stream, string msg) { using ( - ArrayBuffer keyBuffer = bufferPool.Take(KeyLength), + ArrayBuffer keyBuffer = bufferPool.Take(KeyLength + Constants.HandshakeGUIDLength), responseBuffer = bufferPool.Take(ResponseLength)) { GetKey(msg, keyBuffer.array); @@ -112,7 +112,7 @@ namespace Mirror.SimpleWeb static void GetKey(string msg, byte[] keyBuffer) { - int start = msg.IndexOf(KeyHeaderString) + KeyHeaderString.Length; + int start = msg.IndexOf(KeyHeaderString, StringComparison.InvariantCultureIgnoreCase) + KeyHeaderString.Length; Log.Verbose($"Handshake Key: {msg.Substring(start, KeyLength)}"); Encoding.ASCII.GetBytes(msg, start, KeyLength, keyBuffer, 0); @@ -120,7 +120,7 @@ namespace Mirror.SimpleWeb static void AppendGuid(byte[] keyBuffer) { - Buffer.BlockCopy(Constants.HandshakeGUIDBytes, 0, keyBuffer, KeyLength, Constants.HandshakeGUID.Length); + Buffer.BlockCopy(Constants.HandshakeGUIDBytes, 0, keyBuffer, KeyLength, Constants.HandshakeGUIDLength); } byte[] CreateHash(byte[] keyBuffer) diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/ServerHandshake.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/ServerHandshake.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/ServerHandshake.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/ServerHandshake.cs.meta index b0a80e9..6fa74da 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/ServerHandshake.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/ServerHandshake.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/ServerSslHelper.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/ServerSslHelper.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/ServerSslHelper.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/ServerSslHelper.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/ServerSslHelper.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/ServerSslHelper.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/ServerSslHelper.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/ServerSslHelper.cs.meta index 16d81e8..e0d133c 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/ServerSslHelper.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/ServerSslHelper.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/SimpleWebServer.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/SimpleWebServer.cs similarity index 87% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/SimpleWebServer.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/SimpleWebServer.cs index f1ed565..05cf996 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/SimpleWebServer.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/SimpleWebServer.cs @@ -52,6 +52,7 @@ namespace Mirror.SimpleWeb server.Send(id, buffer); } } + public void SendOne(int connectionId, ArraySegment source) { ArrayBuffer buffer = bufferPool.Take(source.Count); @@ -70,12 +71,25 @@ namespace Mirror.SimpleWeb return server.GetClientAddress(connectionId); } + /// + /// Processes all new messages + /// + public void ProcessMessageQueue() + { + ProcessMessageQueue(null); + } + + /// + /// Processes all messages while is enabled + /// + /// public void ProcessMessageQueue(MonoBehaviour behaviour) { int processedCount = 0; + bool skipEnabled = behaviour == null; // check enabled every time in case behaviour was disabled after data while ( - behaviour.enabled && + (skipEnabled || behaviour.enabled) && processedCount < maxMessagesPerTick && // Dequeue last server.receiveQueue.TryDequeue(out Message next) diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/SimpleWebServer.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/SimpleWebServer.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/SimpleWebServer.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/SimpleWebServer.cs.meta index 6d823bb..c8c6f5a 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/SimpleWebServer.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/SimpleWebServer.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/WebSocketServer.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/WebSocketServer.cs similarity index 98% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/WebSocketServer.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/WebSocketServer.cs index c924be4..da4f402 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/WebSocketServer.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/WebSocketServer.cs @@ -54,7 +54,6 @@ namespace Mirror.SimpleWeb listener?.Stop(); acceptThread = null; - Log.Info("Server stopped, Closing all connections..."); // make copy so that foreach doesn't break if values are removed Connection[] connectionsCopy = connections.Values.ToArray(); @@ -222,7 +221,7 @@ namespace Mirror.SimpleWeb } else { - Log.Error($"Cant close connection to {id} because connection was not found in dictionary"); + Log.Error($"Cant get address of connection {id} because connection was not found in dictionary"); return null; } } diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/WebSocketServer.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/WebSocketServer.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/WebSocketServer.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/WebSocketServer.cs.meta index e05ef3a..0a76a9f 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/Server/WebSocketServer.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/Server/WebSocketServer.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/SimpleWebTransport.asmdef b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/SimpleWebTransport.asmdef similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/SimpleWebTransport.asmdef rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/SimpleWebTransport.asmdef diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/SimpleWebTransport.asmdef.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/SimpleWebTransport.asmdef.meta similarity index 63% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/SimpleWebTransport.asmdef.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/SimpleWebTransport.asmdef.meta index 1186f32..99755b6 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/SimpleWebTransport.asmdef.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/SimpleWebTransport.asmdef.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 3b5390adca4e2bb4791cb930316d6f3e AssemblyDefinitionImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/SimpleWebTransport.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/SimpleWebTransport.cs similarity index 94% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/SimpleWebTransport.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/SimpleWebTransport.cs index b884121..f6a3a81 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/SimpleWebTransport.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/SimpleWebTransport.cs @@ -78,12 +78,6 @@ namespace Mirror.SimpleWeb void OnValidate() { - if (maxMessageSize > ushort.MaxValue) - { - Debug.LogWarning($"max supported value for maxMessageSize is {ushort.MaxValue}"); - maxMessageSize = ushort.MaxValue; - } - Log.level = _logLevels; } @@ -153,7 +147,7 @@ namespace Mirror.SimpleWeb client.onData += (ArraySegment data) => OnClientDataReceived.Invoke(data, Channels.Reliable); client.onError += (Exception e) => { - OnClientError.Invoke(e); + OnClientError.Invoke(TransportError.Unexpected, e.ToString()); ClientDisconnect(); }; @@ -187,6 +181,9 @@ namespace Mirror.SimpleWeb } client.Send(segment); + + // call event. might be null if no statistics are listening etc. + OnClientDataSent?.Invoke(segment, Channels.Reliable); } // messages should always be processed in early update @@ -209,13 +206,13 @@ namespace Mirror.SimpleWeb Debug.LogError("SimpleWebServer Already Started"); } - SslConfig config = SslConfigLoader.Load(this); + SslConfig config = SslConfigLoader.Load(sslEnabled, sslCertJson, sslProtocols); server = new SimpleWebServer(serverMaxMessagesPerTick, TcpConfig, maxMessageSize, handshakeMaxSize, config); server.onConnect += OnServerConnected.Invoke; server.onDisconnect += OnServerDisconnected.Invoke; server.onData += (int connId, ArraySegment data) => OnServerDataReceived.Invoke(connId, data, Channels.Reliable); - server.onError += OnServerError.Invoke; + server.onError += (connId, exception) => OnServerError(connId, TransportError.Unexpected, exception.ToString()); SendLoopConfig.batchSend = batchSend || waitBeforeSend; SendLoopConfig.sleepBeforeSend = waitBeforeSend; @@ -265,6 +262,9 @@ namespace Mirror.SimpleWeb } server.SendOne(connectionId, segment); + + // call event. might be null if no statistics are listening etc. + OnServerDataSent?.Invoke(connectionId, segment, Channels.Reliable); } public override string ServerGetClientAddress(int connectionId) diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/SimpleWebTransport.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/SimpleWebTransport.cs.meta similarity index 79% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/SimpleWebTransport.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/SimpleWebTransport.cs.meta index 5fc1631..381a5c7 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/SimpleWebTransport.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/SimpleWebTransport.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/SslConfigLoader.cs b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/SslConfigLoader.cs similarity index 67% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/SslConfigLoader.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/SslConfigLoader.cs index a6d661e..4baf8c5 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/SslConfigLoader.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/SslConfigLoader.cs @@ -1,28 +1,30 @@ using System.IO; +using System.Security.Authentication; using UnityEngine; namespace Mirror.SimpleWeb { - internal class SslConfigLoader + + public class SslConfigLoader { internal struct Cert { public string path; public string password; } - internal static SslConfig Load(SimpleWebTransport transport) + public static SslConfig Load(bool sslEnabled, string sslCertJson, SslProtocols sslProtocols) { // don't need to load anything if ssl is not enabled - if (!transport.sslEnabled) + if (!sslEnabled) return default; - string certJsonPath = transport.sslCertJson; + string certJsonPath = sslCertJson; Cert cert = LoadCertJson(certJsonPath); return new SslConfig( - enabled: transport.sslEnabled, - sslProtocols: transport.sslProtocols, + enabled: sslEnabled, + sslProtocols: sslProtocols, certPath: cert.path, certPassword: cert.password ); @@ -33,10 +35,11 @@ namespace Mirror.SimpleWeb string json = File.ReadAllText(certJsonPath); Cert cert = JsonUtility.FromJson(json); - if (string.IsNullOrEmpty(cert.path)) + if (string.IsNullOrWhiteSpace(cert.path)) { throw new InvalidDataException("Cert Json didn't not contain \"path\""); } + // don't use IsNullOrWhiteSpace here because whitespace could be a valid password for a cert if (string.IsNullOrEmpty(cert.password)) { // password can be empty diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/SslConfigLoader.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/SslConfigLoader.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/SslConfigLoader.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/SslConfigLoader.cs.meta index 6837a6c..e653532 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/SimpleWebTransport/SslConfigLoader.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/SimpleWebTransport/SslConfigLoader.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy.meta new file mode 100644 index 0000000..ede2d0e --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 552b3d8382916438d81fe7f39e18db72 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy.meta new file mode 100644 index 0000000..345a638 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a1233408bc4b145fb8f6f5a8e95790e0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Client.cs b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Client.cs similarity index 98% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Client.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Client.cs index 73e775c..b2a1c85 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Client.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Client.cs @@ -154,10 +154,6 @@ namespace Telepathy // this happens if (for example) the ip address is correct // but there is no server running on that ip/port Log.Info("Client Recv: failed to connect to ip=" + ip + " port=" + port + " reason=" + exception); - - // add 'Disconnected' event to receive pipe so that the caller - // knows that the Connect failed. otherwise they will never know - state.receivePipe.Enqueue(0, EventType.Disconnected, default); } catch (ThreadInterruptedException) { @@ -177,7 +173,10 @@ namespace Telepathy // something went wrong. probably important. Log.Error("Client Recv Exception: " + exception); } - + // add 'Disconnected' event to receive pipe so that the caller + // knows that the Connect failed. otherwise they will never know + state.receivePipe.Enqueue(0, EventType.Disconnected, default); + // sendthread might be waiting on ManualResetEvent, // so let's make sure to end it if the connection // closed. diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Client.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Client.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Client.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Client.cs.meta index d75406d..1b6d222 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Client.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Client.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Common.cs b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Common.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Common.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Common.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Common.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Common.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Common.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Common.cs.meta index db6a30c..5d8ab5b 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Common.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Common.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/ConnectionState.cs b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/ConnectionState.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/ConnectionState.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/ConnectionState.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/ConnectionState.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/ConnectionState.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/ConnectionState.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/ConnectionState.cs.meta index 8fbd716..3dcceaf 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/ConnectionState.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/ConnectionState.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty.meta new file mode 100644 index 0000000..1bc9652 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 885e89897e3a03241827ab7a14fe5fa0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/Logger.cs b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/Logger.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/Logger.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/Logger.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/Logger.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/Logger.cs.meta new file mode 100644 index 0000000..304866f --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/Logger.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aa8d703f0b73f4d6398b76812719b68b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/Message.cs b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/Message.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/Message.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/Message.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/Message.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/Message.cs.meta new file mode 100644 index 0000000..5937bb9 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/Message.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aedf812e9637b4f92a35db1aedca8c92 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/SafeQueue.cs b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/SafeQueue.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/SafeQueue.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/SafeQueue.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/SafeQueue.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/SafeQueue.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/SafeQueue.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/SafeQueue.cs.meta index 81789a6..f3a9310 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/SafeQueue.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/SafeQueue.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/ThreadExtensions.cs b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/ThreadExtensions.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/ThreadExtensions.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/ThreadExtensions.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/ThreadExtensions.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/ThreadExtensions.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/ThreadExtensions.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/ThreadExtensions.cs.meta index 9fc8924..77c885d 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Empty/ThreadExtensions.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Empty/ThreadExtensions.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/EventType.cs b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/EventType.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/EventType.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/EventType.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/EventType.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/EventType.cs.meta new file mode 100644 index 0000000..ac88c1b --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/EventType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 49f1a330755814803be5f27f493e1910 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/LICENSE b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/LICENSE similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/LICENSE rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/LICENSE diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/LICENSE.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/LICENSE.meta new file mode 100644 index 0000000..4d7664e --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/LICENSE.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0ba11103b95fd4721bffbb08440d5b8e +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Log.cs b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Log.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Log.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Log.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Log.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Log.cs.meta new file mode 100644 index 0000000..8f78650 --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Log.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0a123d054bef34d059057ac2ce936605 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/MagnificentReceivePipe.cs b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/MagnificentReceivePipe.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/MagnificentReceivePipe.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/MagnificentReceivePipe.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/MagnificentReceivePipe.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/MagnificentReceivePipe.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/MagnificentReceivePipe.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/MagnificentReceivePipe.cs.meta index 9bdccd3..614bab6 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/MagnificentReceivePipe.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/MagnificentReceivePipe.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/MagnificentSendPipe.cs b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/MagnificentSendPipe.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/MagnificentSendPipe.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/MagnificentSendPipe.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/MagnificentSendPipe.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/MagnificentSendPipe.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/MagnificentSendPipe.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/MagnificentSendPipe.cs.meta index aaf21dc..cf1415f 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/MagnificentSendPipe.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/MagnificentSendPipe.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/NetworkStreamExtensions.cs b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/NetworkStreamExtensions.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/NetworkStreamExtensions.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/NetworkStreamExtensions.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/NetworkStreamExtensions.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/NetworkStreamExtensions.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/NetworkStreamExtensions.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/NetworkStreamExtensions.cs.meta index 7dcf844..e7e5744 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/NetworkStreamExtensions.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/NetworkStreamExtensions.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Pool.cs b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Pool.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Pool.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Pool.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Pool.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Pool.cs.meta new file mode 100644 index 0000000..9a7dafc --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Pool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6d3e530f6872642ec81e9b8b76277c93 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Server.cs b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Server.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Server.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Server.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Server.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Server.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Server.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Server.cs.meta index 541f104..9cee8b7 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Server.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Server.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Telepathy.asmdef b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Telepathy.asmdef similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Telepathy.asmdef rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Telepathy.asmdef diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Telepathy.asmdef.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Telepathy.asmdef.meta similarity index 63% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Telepathy.asmdef.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Telepathy.asmdef.meta index bcd17a7..572c127 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Telepathy.asmdef.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Telepathy.asmdef.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 725ee7191c021de4dbf9269590ded755 AssemblyDefinitionImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/ThreadFunctions.cs b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/ThreadFunctions.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/ThreadFunctions.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/ThreadFunctions.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/ThreadFunctions.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/ThreadFunctions.cs.meta similarity index 75% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/ThreadFunctions.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/ThreadFunctions.cs.meta index 4c7f3b8..ea536ac 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/ThreadFunctions.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/ThreadFunctions.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Utils.cs b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Utils.cs similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/Utils.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Utils.cs diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Utils.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Utils.cs.meta new file mode 100644 index 0000000..0a9253b --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/Utils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 951d08c05297f4b3e8feb5bfcab86531 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/VERSION b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/VERSION similarity index 100% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/Telepathy/VERSION rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/VERSION diff --git a/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/VERSION.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/VERSION.meta new file mode 100644 index 0000000..04c1c8a --- /dev/null +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/Telepathy/VERSION.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d942af06608be434dbeeaa58207d20bd +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/TelepathyTransport.cs b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/TelepathyTransport.cs similarity index 82% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/TelepathyTransport.cs rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/TelepathyTransport.cs index cdb5e18..f723ec5 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/TelepathyTransport.cs +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/TelepathyTransport.cs @@ -63,15 +63,28 @@ namespace Mirror void Awake() { - // create client & server - client = new Telepathy.Client(clientMaxMessageSize); - server = new Telepathy.Server(serverMaxMessageSize); - // tell Telepathy to use Unity's Debug.Log Telepathy.Log.Info = Debug.Log; Telepathy.Log.Warning = Debug.LogWarning; Telepathy.Log.Error = Debug.LogError; + // allocate enabled check only once + enabledCheck = () => enabled; + + Debug.Log("TelepathyTransport initialized!"); + } + + public override bool Available() + { + // C#'s built in TCP sockets run everywhere except on WebGL + return Application.platform != RuntimePlatform.WebGLPlayer; + } + + // client + private void CreateClient() + { + // create client + client = new Telepathy.Client(clientMaxMessageSize); // client hooks // other systems hook into transport events in OnCreate or // OnStartRunning in no particular order. the only way to avoid @@ -89,6 +102,66 @@ namespace Mirror client.ReceiveTimeout = ReceiveTimeout; client.SendQueueLimit = clientSendQueueLimit; client.ReceiveQueueLimit = clientReceiveQueueLimit; + } + public override bool ClientConnected() => client != null && client.Connected; + public override void ClientConnect(string address) + { + CreateClient(); + client.Connect(address, port); + } + + public override void ClientConnect(Uri uri) + { + CreateClient(); + if (uri.Scheme != Scheme) + throw new ArgumentException($"Invalid url {uri}, use {Scheme}://host:port instead", nameof(uri)); + + int serverPort = uri.IsDefaultPort ? port : uri.Port; + client.Connect(uri.Host, serverPort); + } + public override void ClientSend(ArraySegment segment, int channelId) + { + client?.Send(segment); + + // call event. might be null if no statistics are listening etc. + OnClientDataSent?.Invoke(segment, Channels.Reliable); + } + public override void ClientDisconnect() + { + client?.Disconnect(); + client = null; + // client triggers the disconnected event in client.Tick() which won't be run anymore + OnClientDisconnected?.Invoke(); + } + + // messages should always be processed in early update + public override void ClientEarlyUpdate() + { + // note: we need to check enabled in case we set it to false + // when LateUpdate already started. + // (https://github.com/vis2k/Mirror/pull/379) + if (!enabled) return; + + // process a maximum amount of client messages per tick + // IMPORTANT: check .enabled to stop processing immediately after a + // scene change message arrives! + client?.Tick(clientMaxReceivesPerTick, enabledCheck); + } + + // server + public override Uri ServerUri() + { + UriBuilder builder = new UriBuilder(); + builder.Scheme = Scheme; + builder.Host = Dns.GetHostName(); + builder.Port = port; + return builder.Uri; + } + public override bool ServerActive() => server != null && server.Active; + public override void ServerStart() + { + // create server + server = new Telepathy.Server(serverMaxMessageSize); // server hooks // other systems hook into transport events in OnCreate or @@ -108,63 +181,22 @@ namespace Mirror server.SendQueueLimit = serverSendQueueLimitPerConnection; server.ReceiveQueueLimit = serverReceiveQueueLimitPerConnection; - // allocate enabled check only once - enabledCheck = () => enabled; - - Debug.Log("TelepathyTransport initialized!"); + server.Start(port); } - public override bool Available() + public override void ServerSend(int connectionId, ArraySegment segment, int channelId) { - // C#'s built in TCP sockets run everywhere except on WebGL - return Application.platform != RuntimePlatform.WebGLPlayer; - } + server?.Send(connectionId, segment); - // client - public override bool ClientConnected() => client.Connected; - public override void ClientConnect(string address) => client.Connect(address, port); - public override void ClientConnect(Uri uri) - { - if (uri.Scheme != Scheme) - throw new ArgumentException($"Invalid url {uri}, use {Scheme}://host:port instead", nameof(uri)); - - int serverPort = uri.IsDefaultPort ? port : uri.Port; - client.Connect(uri.Host, serverPort); + // call event. might be null if no statistics are listening etc. + OnServerDataSent?.Invoke(connectionId, segment, Channels.Reliable); } - public override void ClientSend(ArraySegment segment, int channelId) => client.Send(segment); - public override void ClientDisconnect() => client.Disconnect(); - // messages should always be processed in early update - public override void ClientEarlyUpdate() - { - // note: we need to check enabled in case we set it to false - // when LateUpdate already started. - // (https://github.com/vis2k/Mirror/pull/379) - if (!enabled) return; - - // process a maximum amount of client messages per tick - // IMPORTANT: check .enabled to stop processing immediately after a - // scene change message arrives! - client.Tick(clientMaxReceivesPerTick, enabledCheck); - } - - // server - public override Uri ServerUri() - { - UriBuilder builder = new UriBuilder(); - builder.Scheme = Scheme; - builder.Host = Dns.GetHostName(); - builder.Port = port; - return builder.Uri; - } - public override bool ServerActive() => server.Active; - public override void ServerStart() => server.Start(port); - public override void ServerSend(int connectionId, ArraySegment segment, int channelId) => server.Send(connectionId, segment); - public override void ServerDisconnect(int connectionId) => server.Disconnect(connectionId); + public override void ServerDisconnect(int connectionId) => server?.Disconnect(connectionId); public override string ServerGetClientAddress(int connectionId) { try { - return server.GetClientAddress(connectionId); + return server?.GetClientAddress(connectionId); } catch (SocketException) { @@ -179,7 +211,12 @@ namespace Mirror return "unknown"; } } - public override void ServerStop() => server.Stop(); + public override void ServerStop() + { + server?.Stop(); + server = null; + } + // messages should always be processed in early update public override void ServerEarlyUpdate() { @@ -191,15 +228,17 @@ namespace Mirror // process a maximum amount of server messages per tick // IMPORTANT: check .enabled to stop processing immediately after a // scene change message arrives! - server.Tick(serverMaxReceivesPerTick, enabledCheck); + server?.Tick(serverMaxReceivesPerTick, enabledCheck); } // common public override void Shutdown() { Debug.Log("TelepathyTransport Shutdown()"); - client.Disconnect(); - server.Stop(); + client?.Disconnect(); + client = null; + server?.Stop(); + server = null; } public override int GetMaxPacketSize(int channelId) @@ -209,7 +248,7 @@ namespace Mirror public override string ToString() { - if (server.Active && server.listener != null) + if (server != null && server.Active && server.listener != null) { // printing server.listener.LocalEndpoint causes an Exception // in UWP + Unity 2019: @@ -219,11 +258,11 @@ namespace Mirror // 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; + return $"Telepathy Server port: {port}"; } - else if (client.Connecting || client.Connected) + else if (client != null && (client.Connecting || client.Connected)) { - return "Telepathy Client port: " + port; + return $"Telepathy Client port: {port}"; } return "Telepathy (inactive/disconnected)"; } diff --git a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/TelepathyTransport.cs.meta b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/TelepathyTransport.cs.meta similarity index 79% rename from UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/TelepathyTransport.cs.meta rename to UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/TelepathyTransport.cs.meta index bf45433..99cde3e 100644 --- a/UnityProject/Assets/Mirror/Runtime/Transport/Telepathy/TelepathyTransport.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Transports/Telepathy/TelepathyTransport.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 1000 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Runtime/Utils.cs b/UnityProject/Assets/Mirror/Runtime/Utils.cs index cc8ba94..d39ed98 100644 --- a/UnityProject/Assets/Mirror/Runtime/Utils.cs +++ b/UnityProject/Assets/Mirror/Runtime/Utils.cs @@ -1,5 +1,5 @@ using System; -using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; using System.Security.Cryptography; using UnityEngine; @@ -16,19 +16,6 @@ namespace Mirror // Handles requests to unspawn objects on the client public delegate void UnSpawnDelegate(GameObject spawned); - // invoke type for Cmd/Rpc - public enum MirrorInvokeType - { - Command, - ClientRpc - } - - [Obsolete("Version has never been used, neither by UNET nor by Mirror.")] - public enum Version - { - Current = 1 - } - // channels are const ints instead of an enum so people can add their own // channels (can't extend an enum otherwise). // @@ -38,47 +25,8 @@ namespace Mirror // add custom channels anymore. public static class Channels { - public const int Reliable = 0; // ordered - public const int Unreliable = 1; // unordered - - [Obsolete("Use Channels.Reliable instead")] - public const int DefaultReliable = Reliable; - [Obsolete("Use Channels.Unreliable instead")] - public const int DefaultUnreliable = Unreliable; - } - - // -- helpers for float conversion without allocations -- - [StructLayout(LayoutKind.Explicit)] - internal struct UIntFloat - { - [FieldOffset(0)] - public float floatValue; - - [FieldOffset(0)] - public uint intValue; - } - - [StructLayout(LayoutKind.Explicit)] - internal struct UIntDouble - { - [FieldOffset(0)] - public double doubleValue; - - [FieldOffset(0)] - public ulong longValue; - } - - [StructLayout(LayoutKind.Explicit)] - internal struct UIntDecimal - { - [FieldOffset(0)] - public ulong longValue1; - - [FieldOffset(8)] - public ulong longValue2; - - [FieldOffset(0)] - public decimal decimalValue; + public const int Reliable = 0; // ordered + public const int Unreliable = 1; // unordered } public static class Utils @@ -117,10 +65,57 @@ namespace Mirror if (prefab == null) { - Debug.LogError("Failed to find prefab parent for scene object [name:" + gameObject.name + "]"); + Debug.LogError($"Failed to find prefab parent for scene object [name:{gameObject.name}]"); return false; } return true; } + + // is a 2D point in screen? (from ummorpg) + // (if width = 1024, then indices from 0..1023 are valid (=1024 indices) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsPointInScreen(Vector2 point) => + 0 <= point.x && point.x < Screen.width && + 0 <= point.y && point.y < Screen.height; + + // pretty print bytes as KB/MB/GB/etc. from DOTSNET + // long to support > 2GB + // divides by floats to return "2.5MB" etc. + public static string PrettyBytes(long bytes) + { + // bytes + if (bytes < 1024) + return $"{bytes} B"; + // kilobytes + else if (bytes < 1024L * 1024L) + return $"{(bytes / 1024f):F2} KB"; + // megabytes + else if (bytes < 1024 * 1024L * 1024L) + return $"{(bytes / (1024f * 1024f)):F2} MB"; + // gigabytes + return $"{(bytes / (1024f * 1024f * 1024f)):F2} GB"; + } + + // universal .spawned function + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NetworkIdentity GetSpawnedInServerOrClient(uint netId) + { + // server / host mode: use the one from server. + // host mode has access to all spawned. + if (NetworkServer.active) + { + NetworkServer.spawned.TryGetValue(netId, out NetworkIdentity entry); + return entry; + } + + // client + if (NetworkClient.active) + { + NetworkClient.spawned.TryGetValue(netId, out NetworkIdentity entry); + return entry; + } + + return null; + } } } diff --git a/UnityProject/Assets/Mirror/Runtime/Utils.cs.meta b/UnityProject/Assets/Mirror/Runtime/Utils.cs.meta index 14967ce..7cf1557 100644 --- a/UnityProject/Assets/Mirror/Runtime/Utils.cs.meta +++ b/UnityProject/Assets/Mirror/Runtime/Utils.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Mirror/Version.txt b/UnityProject/Assets/Mirror/Version.txt new file mode 100644 index 0000000..6de0c3e --- /dev/null +++ b/UnityProject/Assets/Mirror/Version.txt @@ -0,0 +1 @@ +2022.9.15 \ No newline at end of file diff --git a/UnityProject/Assets/Mirror/Version.txt.meta b/UnityProject/Assets/Mirror/Version.txt.meta new file mode 100644 index 0000000..097f259 --- /dev/null +++ b/UnityProject/Assets/Mirror/Version.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c6b1f72568a9340178b4c34608fbdbc3 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/ScriptTemplates/50-Mirror__Network Manager-NewNetworkManager.cs.txt b/UnityProject/Assets/ScriptTemplates/50-Mirror__Network Manager-NewNetworkManager.cs.txt index 4b086df..b2a4dec 100644 --- a/UnityProject/Assets/ScriptTemplates/50-Mirror__Network Manager-NewNetworkManager.cs.txt +++ b/UnityProject/Assets/ScriptTemplates/50-Mirror__Network Manager-NewNetworkManager.cs.txt @@ -1,3 +1,4 @@ +using System; using UnityEngine; using UnityEngine.SceneManagement; using Mirror; @@ -9,6 +10,10 @@ using Mirror; public class #SCRIPTNAME# : NetworkManager { + // Overrides the base singleton so we don't + // have to cast to this type everywhere. + public static new #SCRIPTNAME# singleton { get; private set; } + #region Unity Callbacks public override void OnValidate() @@ -31,6 +36,7 @@ public class #SCRIPTNAME# : NetworkManager /// public override void Start() { + singleton = this; base.Start(); } @@ -58,9 +64,9 @@ public class #SCRIPTNAME# : NetworkManager /// Set the frame rate for a headless server. /// Override if you wish to disable the behavior or set your own tick rate. /// - public override void ConfigureServerFrameRate() + public override void ConfigureHeadlessFrameRate() { - base.ConfigureServerFrameRate(); + base.ConfigureHeadlessFrameRate(); } /// @@ -111,10 +117,9 @@ public class #SCRIPTNAME# : NetworkManager /// Called on clients when a scene has completed loaded, when the scene load was initiated by the server. /// Scene changes can cause player objects to be destroyed. The default implementation of OnClientSceneChanged in the NetworkManager is to add a player object for the connection if no player object exists. /// - /// The network connection that the scene change message arrived on. - public override void OnClientSceneChanged(NetworkConnection conn) + public override void OnClientSceneChanged() { - base.OnClientSceneChanged(conn); + base.OnClientSceneChanged(); } #endregion @@ -126,14 +131,14 @@ public class #SCRIPTNAME# : NetworkManager /// Unity calls this on the Server when a Client connects to the Server. Use an override to tell the NetworkManager what to do when a client connects to the server. /// /// Connection from client. - public override void OnServerConnect(NetworkConnection conn) { } + public override void OnServerConnect(NetworkConnectionToClient conn) { } /// /// Called on the server when a client is ready. /// The default implementation of this function calls NetworkServer.SetClientReady() to continue the network setup process. /// /// Connection from client. - public override void OnServerReady(NetworkConnection conn) + public override void OnServerReady(NetworkConnectionToClient conn) { base.OnServerReady(conn); } @@ -143,7 +148,7 @@ public class #SCRIPTNAME# : NetworkManager /// The default implementation for this function creates a new player object from the playerPrefab. /// /// Connection from client. - public override void OnServerAddPlayer(NetworkConnection conn) + public override void OnServerAddPlayer(NetworkConnectionToClient conn) { base.OnServerAddPlayer(conn); } @@ -153,11 +158,19 @@ public class #SCRIPTNAME# : NetworkManager /// This is called on the Server when a Client disconnects from the Server. Use an override to decide what should happen when a disconnection is detected. /// /// Connection from client. - public override void OnServerDisconnect(NetworkConnection conn) + public override void OnServerDisconnect(NetworkConnectionToClient conn) { base.OnServerDisconnect(conn); } + /// + /// Called on server when transport raises an exception. + /// NetworkConnection may be null. + /// + /// Connection of the client...may be null + /// Exception thrown from the Transport. + public override void OnServerError(NetworkConnectionToClient conn, TransportError transportError, string message) { } + #endregion #region Client System Callbacks @@ -166,28 +179,31 @@ public class #SCRIPTNAME# : NetworkManager /// Called on the client when connected to a server. /// The default implementation of this function sets the client as ready and adds a player. Override the function to dictate what happens when the client connects. /// - /// Connection to the server. - public override void OnClientConnect(NetworkConnection conn) + public override void OnClientConnect() { - base.OnClientConnect(conn); + base.OnClientConnect(); } /// /// Called on clients when disconnected from a server. /// This is called on the client when it disconnects from the server. Override this function to decide what happens when the client disconnects. /// - /// Connection to the server. - public override void OnClientDisconnect(NetworkConnection conn) + public override void OnClientDisconnect() { - base.OnClientDisconnect(conn); + base.OnClientDisconnect(); } /// /// Called on clients when a servers tells the client it is no longer ready. /// This is commonly used when switching scenes. /// - /// Connection to the server. - public override void OnClientNotReady(NetworkConnection conn) { } + public override void OnClientNotReady() { } + + /// + /// Called on client when transport raises an exception. + /// + /// Exception thrown from the Transport. + public override void OnClientError(TransportError transportError, string message) { } #endregion diff --git a/UnityProject/Assets/ScriptTemplates/50-Mirror__Network Manager-NewNetworkManager.cs.txt.meta b/UnityProject/Assets/ScriptTemplates/50-Mirror__Network Manager-NewNetworkManager.cs.txt.meta index 8ec8d56..6221c57 100644 --- a/UnityProject/Assets/ScriptTemplates/50-Mirror__Network Manager-NewNetworkManager.cs.txt.meta +++ b/UnityProject/Assets/ScriptTemplates/50-Mirror__Network Manager-NewNetworkManager.cs.txt.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: ed73cc79a95879d4abd948a36043c798 TextScriptImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/ScriptTemplates/51-Mirror__Network Authenticator-NewNetworkAuthenticator.cs.txt b/UnityProject/Assets/ScriptTemplates/51-Mirror__Network Authenticator-NewNetworkAuthenticator.cs.txt index 90dac25..5ad1082 100644 --- a/UnityProject/Assets/ScriptTemplates/51-Mirror__Network Authenticator-NewNetworkAuthenticator.cs.txt +++ b/UnityProject/Assets/ScriptTemplates/51-Mirror__Network Authenticator-NewNetworkAuthenticator.cs.txt @@ -1,6 +1,8 @@ -using UnityEngine; -using UnityEngine.SceneManagement; +using System; +using System.Collections; +using System.Collections.Generic; using Mirror; +using UnityEngine; /* Documentation: https://mirror-networking.gitbook.io/docs/components/network-authenticators @@ -30,17 +32,17 @@ public class #SCRIPTNAME# : NetworkAuthenticator } /// - /// Called on server from OnServerAuthenticateInternal when a client needs to authenticate + /// Called on server from OnServerConnectInternal when a client needs to authenticate /// /// Connection to client. - public override void OnServerAuthenticate(NetworkConnection conn) { } + public override void OnServerAuthenticate(NetworkConnectionToClient conn) { } /// /// Called on server when the client's AuthRequestMessage arrives /// /// Connection to client. /// The message payload - public void OnAuthRequestMessage(NetworkConnection conn, AuthRequestMessage msg) + public void OnAuthRequestMessage(NetworkConnectionToClient conn, AuthRequestMessage msg) { AuthResponseMessage authResponseMessage = new AuthResponseMessage(); @@ -65,7 +67,7 @@ public class #SCRIPTNAME# : NetworkAuthenticator } /// - /// Called on client from OnClientAuthenticateInternal when a client needs to authenticate + /// Called on client from OnClientConnectInternal when a client needs to authenticate /// public override void OnClientAuthenticate() { diff --git a/UnityProject/Assets/ScriptTemplates/51-Mirror__Network Authenticator-NewNetworkAuthenticator.cs.txt.meta b/UnityProject/Assets/ScriptTemplates/51-Mirror__Network Authenticator-NewNetworkAuthenticator.cs.txt.meta index 8d95f68..be22fe6 100644 --- a/UnityProject/Assets/ScriptTemplates/51-Mirror__Network Authenticator-NewNetworkAuthenticator.cs.txt.meta +++ b/UnityProject/Assets/ScriptTemplates/51-Mirror__Network Authenticator-NewNetworkAuthenticator.cs.txt.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 12dc04aca2d89f744bef5a65622ba708 TextScriptImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/ScriptTemplates/52-Mirror__Network Behaviour-NewNetworkBehaviour.cs.txt b/UnityProject/Assets/ScriptTemplates/52-Mirror__Network Behaviour-NewNetworkBehaviour.cs.txt index 194ac34..a53aee4 100644 --- a/UnityProject/Assets/ScriptTemplates/52-Mirror__Network Behaviour-NewNetworkBehaviour.cs.txt +++ b/UnityProject/Assets/ScriptTemplates/52-Mirror__Network Behaviour-NewNetworkBehaviour.cs.txt @@ -1,6 +1,6 @@ +using System.Collections.Generic; using UnityEngine; using Mirror; -using System.Collections.Generic; /* Documentation: https://mirror-networking.gitbook.io/docs/guides/networkbehaviour @@ -44,10 +44,16 @@ public class #SCRIPTNAME# : NetworkBehaviour /// public override void OnStartLocalPlayer() { } + /// + /// Called when the local player object is being stopped. + /// This happens before OnStopClient(), as it may be triggered by an ownership message from the server, or because the player object is being destroyed. This is an appropriate place to deactivate components or functionality that should only be active for the local player, such as cameras and input. + /// + public override void OnStopLocalPlayer() {} + /// /// This is invoked on behaviours that have authority, based on context and NetworkIdentity.hasAuthority. /// This is called after OnStartServer and before OnStartClient. - /// When AssignClientAuthority is called on the server, this will be called on the client that owns the object. When an object is spawned with NetworkServer.Spawn with a NetworkConnection parameter included, this will be called on the client that owns the object. + /// When AssignClientAuthority is called on the server, this will be called on the client that owns the object. When an object is spawned with NetworkServer.Spawn with a NetworkConnectionToClient parameter included, this will be called on the client that owns the object. /// public override void OnStartAuthority() { } diff --git a/UnityProject/Assets/ScriptTemplates/52-Mirror__Network Behaviour-NewNetworkBehaviour.cs.txt.meta b/UnityProject/Assets/ScriptTemplates/52-Mirror__Network Behaviour-NewNetworkBehaviour.cs.txt.meta index 57c3e33..c5a0018 100644 --- a/UnityProject/Assets/ScriptTemplates/52-Mirror__Network Behaviour-NewNetworkBehaviour.cs.txt.meta +++ b/UnityProject/Assets/ScriptTemplates/52-Mirror__Network Behaviour-NewNetworkBehaviour.cs.txt.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 29b2ae9aeacc49b47b711838dd1876a4 TextScriptImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/ScriptTemplates/53-Mirror__Custom Interest Management-CustomInterestManagement.cs.txt b/UnityProject/Assets/ScriptTemplates/53-Mirror__Custom Interest Management-CustomInterestManagement.cs.txt new file mode 100644 index 0000000..37df416 --- /dev/null +++ b/UnityProject/Assets/ScriptTemplates/53-Mirror__Custom Interest Management-CustomInterestManagement.cs.txt @@ -0,0 +1,95 @@ +using System.Collections.Generic; +using UnityEngine; +using Mirror; + +/* + Documentation: https://mirror-networking.gitbook.io/docs/guides/interest-management + API Reference: https://mirror-networking.com/docs/api/Mirror.InterestManagement.html +*/ + +// NOTE: Attach this component to the same object as your Network Manager. + +public class #SCRIPTNAME# : InterestManagement +{ + /// + /// Callback used by the visibility system to determine if an observer (client) can see the NetworkIdentity. + /// If this function returns true, the network connection will be added as an observer. + /// + /// Object to be observed (or not) by a client + /// Network Connection of a client. + /// True if the client can see this object. + [ServerCallback] + public override bool OnCheckObserver(NetworkIdentity identity, NetworkConnectionToClient newObserver) + { + // Default behaviour of making the identity object visible to all clients. + // Replace this code with your own logic as appropriate. + return true; + } + + /// + /// Callback used by the visibility system to determine if an observer (client) can see the NetworkIdentity. + /// Add connections to newObservers that should see the identity object. + /// + /// Object to be observed (or not) by clients + /// cached hashset to put the result into + /// true if being rebuilt for the first time + [ServerCallback] + public override void OnRebuildObservers(NetworkIdentity identity, HashSet newObservers, bool initialize) + { + // Default behaviour of making the identity object visible to all clients. + // Replace this code with your own logic as appropriate. + foreach (NetworkConnectionToClient conn in NetworkServer.connections.Values) + newObservers.Add(conn); + } + + /// + /// Called on the server when a new networked object is spawned. + /// + /// NetworkIdentity of the object being spawned + [ServerCallback] + public override void OnSpawned(NetworkIdentity identity) { } + + /// + /// Called on the server when a networked object is destroyed. + /// + /// NetworkIdentity of the object being destroyed + [ServerCallback] + public override void OnDestroyed(NetworkIdentity identity) { } + + /// + /// Callback used by the visibility system for objects on a host. + /// Objects on a host (with a local client) cannot be disabled or destroyed when + /// they are not visible to the local client, so this function is called to allow + /// custom code to hide these objects. + /// A typical implementation will disable renderer components on the object. + /// This is only called on local clients on a host. + /// + /// NetworkIdentity of the object being considered for visibility + /// True if the identity object should be visible to the host client + [ServerCallback] + public override void SetHostVisibility(NetworkIdentity identity, bool visible) + { + base.SetHostVisibility(identity, visible); + } + + /// + /// Called by NetworkServer in Initialize and Shutdown + /// + [ServerCallback] + public override void Reset() { } + + [ServerCallback] + void Update() + { + // Here is where you'd need to evaluate if observers need to be rebuilt, + // either for a specific object, a subset of objects, or all objects. + + // Review the code in the various Interest Management components + // included with Mirror for inspiration: + // - Distance Interest Management + // - Spatial Hash Interest Management + // - Scene Interest Management + // - Match Interest Management + // - Team Interest Management + } +} diff --git a/UnityProject/Assets/ScriptTemplates/53-Mirror__Custom Interest Management-CustomInterestManagement.cs.txt.meta b/UnityProject/Assets/ScriptTemplates/53-Mirror__Custom Interest Management-CustomInterestManagement.cs.txt.meta new file mode 100644 index 0000000..328373e --- /dev/null +++ b/UnityProject/Assets/ScriptTemplates/53-Mirror__Custom Interest Management-CustomInterestManagement.cs.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: fc1892bf5b3ab304a9be7b71d05b6ae8 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/ScriptTemplates/54-Mirror__Network Room Manager-NewNetworkRoomManager.cs.txt b/UnityProject/Assets/ScriptTemplates/54-Mirror__Network Room Manager-NewNetworkRoomManager.cs.txt index e1c6485..a067926 100644 --- a/UnityProject/Assets/ScriptTemplates/54-Mirror__Network Room Manager-NewNetworkRoomManager.cs.txt +++ b/UnityProject/Assets/ScriptTemplates/54-Mirror__Network Room Manager-NewNetworkRoomManager.cs.txt @@ -44,13 +44,13 @@ public class #SCRIPTNAME# : NetworkRoomManager /// This is called on the server when a new client connects to the server. /// /// The new connection. - public override void OnRoomServerConnect(NetworkConnection conn) { } + public override void OnRoomServerConnect(NetworkConnectionToClient conn) { } /// /// This is called on the server when a client disconnects. /// /// The connection that disconnected. - public override void OnRoomServerDisconnect(NetworkConnection conn) { } + public override void OnRoomServerDisconnect(NetworkConnectionToClient conn) { } /// /// This is called on the server when a networked scene finishes loading. @@ -64,7 +64,7 @@ public class #SCRIPTNAME# : NetworkRoomManager /// /// The connection the player object is for. /// The new room-player object. - public override GameObject OnRoomServerCreateRoomPlayer(NetworkConnection conn) + public override GameObject OnRoomServerCreateRoomPlayer(NetworkConnectionToClient conn) { return base.OnRoomServerCreateRoomPlayer(conn); } @@ -76,7 +76,7 @@ public class #SCRIPTNAME# : NetworkRoomManager /// The connection the player object is for. /// The room player object for this connection. /// A new GamePlayer object. - public override GameObject OnRoomServerCreateGamePlayer(NetworkConnection conn, GameObject roomPlayer) + public override GameObject OnRoomServerCreateGamePlayer(NetworkConnectionToClient conn, GameObject roomPlayer) { return base.OnRoomServerCreateGamePlayer(conn, roomPlayer); } @@ -87,7 +87,7 @@ public class #SCRIPTNAME# : NetworkRoomManager /// See OnRoomServerCreateGamePlayer to customize the player object for the initial GamePlay scene. /// /// The connection the player object is for. - public override void OnRoomServerAddPlayer(NetworkConnection conn) + public override void OnRoomServerAddPlayer(NetworkConnectionToClient conn) { base.OnRoomServerAddPlayer(conn); } @@ -100,7 +100,7 @@ public class #SCRIPTNAME# : NetworkRoomManager /// The room player object. /// The game player object. /// False to not allow this player to replace the room player. - public override bool OnRoomServerSceneLoadedForPlayer(NetworkConnection conn, GameObject roomPlayer, GameObject gamePlayer) + public override bool OnRoomServerSceneLoadedForPlayer(NetworkConnectionToClient conn, GameObject roomPlayer, GameObject gamePlayer) { return base.OnRoomServerSceneLoadedForPlayer(conn, roomPlayer, gamePlayer); } @@ -137,19 +137,16 @@ public class #SCRIPTNAME# : NetworkRoomManager /// /// This is called on the client when it connects to server. /// - /// The connection that connected. - public override void OnRoomClientConnect(NetworkConnection conn) { } + public override void OnRoomClientConnect() { } /// /// This is called on the client when disconnected from a server. /// - /// The connection that disconnected. - public override void OnRoomClientDisconnect(NetworkConnection conn) { } + public override void OnRoomClientDisconnect() { } /// /// This is called on the client when a client is started. /// - /// The connection for the room. public override void OnRoomStartClient() { } /// @@ -160,8 +157,7 @@ public class #SCRIPTNAME# : NetworkRoomManager /// /// This is called on the client when the client is finished loading a new networked scene. /// - /// The connection that finished loading a new networked scene. - public override void OnRoomClientSceneChanged(NetworkConnection conn) { } + public override void OnRoomClientSceneChanged() { } /// /// Called on the client when adding a player to the room fails. diff --git a/UnityProject/Assets/ScriptTemplates/54-Mirror__Network Room Manager-NewNetworkRoomManager.cs.txt.meta b/UnityProject/Assets/ScriptTemplates/54-Mirror__Network Room Manager-NewNetworkRoomManager.cs.txt.meta index c43efe6..fe5bc32 100644 --- a/UnityProject/Assets/ScriptTemplates/54-Mirror__Network Room Manager-NewNetworkRoomManager.cs.txt.meta +++ b/UnityProject/Assets/ScriptTemplates/54-Mirror__Network Room Manager-NewNetworkRoomManager.cs.txt.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 2e5656107e61a93439544b91e5f541f6 TextScriptImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/ScriptTemplates/55-Mirror__Network Room Player-NewNetworkRoomPlayer.cs.txt b/UnityProject/Assets/ScriptTemplates/55-Mirror__Network Room Player-NewNetworkRoomPlayer.cs.txt index 52d7050..f970cfe 100644 --- a/UnityProject/Assets/ScriptTemplates/55-Mirror__Network Room Player-NewNetworkRoomPlayer.cs.txt +++ b/UnityProject/Assets/ScriptTemplates/55-Mirror__Network Room Player-NewNetworkRoomPlayer.cs.txt @@ -50,7 +50,7 @@ public class #SCRIPTNAME# : NetworkRoomPlayer /// /// This is invoked on behaviours that have authority, based on context and NetworkIdentity.hasAuthority. /// This is called after OnStartServer and before OnStartClient. - /// When is called on the server, this will be called on the client that owns the object. When an object is spawned with NetworkServer.Spawn with a NetworkConnection parameter included, this will be called on the client that owns the object. + /// When is called on the server, this will be called on the client that owns the object. When an object is spawned with NetworkServer.Spawn with a NetworkConnectionToClient parameter included, this will be called on the client that owns the object. /// public override void OnStartAuthority() { } diff --git a/UnityProject/Assets/ScriptTemplates/55-Mirror__Network Room Player-NewNetworkRoomPlayer.cs.txt.meta b/UnityProject/Assets/ScriptTemplates/55-Mirror__Network Room Player-NewNetworkRoomPlayer.cs.txt.meta index ef8142a..36a48dd 100644 --- a/UnityProject/Assets/ScriptTemplates/55-Mirror__Network Room Player-NewNetworkRoomPlayer.cs.txt.meta +++ b/UnityProject/Assets/ScriptTemplates/55-Mirror__Network Room Player-NewNetworkRoomPlayer.cs.txt.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 1ca8a6309173d4248bc7fa0c6ae001e0 TextScriptImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/ScriptTemplates/56-Mirror__Network Discovery-NewNetworkDiscovery.cs.txt.meta b/UnityProject/Assets/ScriptTemplates/56-Mirror__Network Discovery-NewNetworkDiscovery.cs.txt.meta index 0d988d9..a034ec8 100644 --- a/UnityProject/Assets/ScriptTemplates/56-Mirror__Network Discovery-NewNetworkDiscovery.cs.txt.meta +++ b/UnityProject/Assets/ScriptTemplates/56-Mirror__Network Discovery-NewNetworkDiscovery.cs.txt.meta @@ -2,6 +2,6 @@ fileFormatVersion: 2 guid: 04337367db30af3459bf9e9f3f880734 TextScriptImporter: externalObjects: {} - userData: '' - assetBundleName: '' - assetBundleVariant: '' + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/ScriptTemplates/57-Mirror__Network Transform-NewNetworkTransform.cs.txt b/UnityProject/Assets/ScriptTemplates/57-Mirror__Network Transform-NewNetworkTransform.cs.txt new file mode 100644 index 0000000..0e38c1a --- /dev/null +++ b/UnityProject/Assets/ScriptTemplates/57-Mirror__Network Transform-NewNetworkTransform.cs.txt @@ -0,0 +1,163 @@ +#define onlySyncOnChange_BANDWIDTH_SAVING +using System.Collections.Generic; +using UnityEngine; +using Mirror; + +/* + Documentation: https://mirror-networking.gitbook.io/docs/components/network-transform + API Reference: https://mirror-networking.com/docs/api/Mirror.NetworkTransformBase.html +*/ + +public class #SCRIPTNAME# : NetworkTransformBase +{ + protected override Transform targetComponent => transform; + + // If you need this template to reference a child target, + // replace the line above with the code below. + + /* + [Header("Target")] + public Transform target; + + protected override Transform targetComponent => target; + */ + + #region Unity Callbacks + + protected override void OnValidate() + { + base.OnValidate(); + } + + /// + /// This calls Reset() + /// + protected override void OnEnable() + { + base.OnEnable(); + } + + /// + /// This calls Reset() + /// + protected override void OnDisable() + { + base.OnDisable(); + } + + /// + /// Buffers are cleared and interpolation times are reset to zero here. + /// This may be called when you are implementing some system of not sending + /// if nothing changed, or just plain resetting if you have not received data + /// for some time, as this will prevent a long interpolation period between old + /// and just received data, as it will look like a lag. Reset() should also be + /// called when authority is changed to another client or server, to prevent + /// old buffers bugging out the interpolation if authority is changed back. + /// + public override void Reset() + { + base.Reset(); + } + + #endregion + + #region NT Base Callbacks + + /// + /// NTSnapshot struct is created from incoming data from server + /// and added to SnapshotInterpolation sorted list. + /// You may want to skip calling the base method for the local player + /// if doing client-side prediction, or perhaps pass altered values, + /// or compare the server data to local values and correct large differences. + /// + protected override void OnServerToClientSync(Vector3? position, Quaternion? rotation, Vector3? scale) + { + base.OnServerToClientSync(position, rotation, scale); + } + + /// + /// NTSnapshot struct is created from incoming data from client + /// and added to SnapshotInterpolation sorted list. + /// You may want to implement anti-cheat checks here in client authority mode. + /// + protected override void OnClientToServerSync(Vector3? position, Quaternion? rotation, Vector3? scale) + { + base.OnClientToServerSync(position, rotation, scale); + } + + /// + /// Called by both CmdTeleport and RpcTeleport on server and clients, respectively. + /// Here you can disable a Character Controller before calling the base method, + /// and re-enable it after the base method call to avoid conflicting with it. + /// + protected override void OnTeleport(Vector3 destination) + { + base.OnTeleport(destination); + } + + /// + /// Called by both CmdTeleport and RpcTeleport on server and clients, respectively. + /// Here you can disable a Character Controller before calling the base method, + /// and re-enable it after the base method call to avoid conflicting with it. + /// + protected override void OnTeleport(Vector3 destination, Quaternion rotation) + { + base.OnTeleport(destination, rotation); + } + + /// + /// NTSnapshot struct is created here + /// + protected override NTSnapshot ConstructSnapshot() + { + return base.ConstructSnapshot(); + } + + /// + /// localPosition, localRotation, and localScale are set here: + /// interpolated values are used if interpolation is enabled. + /// goal values are used if interpolation is disabled. + /// + protected override void ApplySnapshot(NTSnapshot start, NTSnapshot goal, NTSnapshot interpolated) + { + base.ApplySnapshot(start, goal, interpolated); + } + +#if onlySyncOnChange_BANDWIDTH_SAVING + + /// + /// Returns true if position, rotation AND scale are unchanged, within given sensitivity range. + /// + protected override bool CompareSnapshots(NTSnapshot currentSnapshot) + { + return base.CompareSnapshots(currentSnapshot); + } + +#endif + + #endregion + + #region GUI + +#if UNITY_EDITOR || DEVELOPMENT_BUILD + // OnGUI allocates even if it does nothing. avoid in release. + + protected override void OnGUI() + { + base.OnGUI(); + } + + protected override void DrawGizmos(SortedList buffer) + { + base.DrawGizmos(buffer); + } + + protected override void OnDrawGizmos() + { + base.OnDrawGizmos(); + } + +#endif + + #endregion +} diff --git a/UnityProject/Assets/ScriptTemplates/57-Mirror__Network Transform-NewNetworkTransform.cs.txt.meta b/UnityProject/Assets/ScriptTemplates/57-Mirror__Network Transform-NewNetworkTransform.cs.txt.meta new file mode 100644 index 0000000..be7e6d7 --- /dev/null +++ b/UnityProject/Assets/ScriptTemplates/57-Mirror__Network Transform-NewNetworkTransform.cs.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 38d41e2048919f64ea817eb343ffb09d +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Packages/manifest.json b/UnityProject/Packages/manifest.json index 72e5e2e..a635e0f 100644 --- a/UnityProject/Packages/manifest.json +++ b/UnityProject/Packages/manifest.json @@ -1,12 +1,12 @@ { "dependencies": { - "com.unity.collab-proxy": "1.10.2", - "com.unity.ide.rider": "1.2.1", - "com.unity.ide.visualstudio": "2.0.11", - "com.unity.ide.vscode": "1.2.4", - "com.unity.test-framework": "1.1.29", - "com.unity.textmeshpro": "2.1.4", - "com.unity.timeline": "1.2.18", + "com.unity.collab-proxy": "1.15.18", + "com.unity.ide.rider": "3.0.14", + "com.unity.ide.visualstudio": "2.0.15", + "com.unity.ide.vscode": "1.2.5", + "com.unity.test-framework": "1.1.31", + "com.unity.textmeshpro": "3.0.6", + "com.unity.timeline": "1.6.4", "com.unity.ugui": "1.0.0", "com.unity.modules.ai": "1.0.0", "com.unity.modules.androidjni": "1.0.0", diff --git a/UnityProject/Packages/packages-lock.json b/UnityProject/Packages/packages-lock.json index 6556ef1..e33e7e8 100644 --- a/UnityProject/Packages/packages-lock.json +++ b/UnityProject/Packages/packages-lock.json @@ -1,10 +1,12 @@ { "dependencies": { "com.unity.collab-proxy": { - "version": "1.10.2", + "version": "1.15.18", "depth": 0, "source": "registry", - "dependencies": {}, + "dependencies": { + "com.unity.services.core": "1.0.1" + }, "url": "https://packages.unity.com" }, "com.unity.ext.nunit": { @@ -15,16 +17,16 @@ "url": "https://packages.unity.com" }, "com.unity.ide.rider": { - "version": "1.2.1", + "version": "3.0.14", "depth": 0, "source": "registry", "dependencies": { - "com.unity.test-framework": "1.1.1" + "com.unity.ext.nunit": "1.0.6" }, "url": "https://packages.unity.com" }, "com.unity.ide.visualstudio": { - "version": "2.0.11", + "version": "2.0.15", "depth": 0, "source": "registry", "dependencies": { @@ -33,14 +35,32 @@ "url": "https://packages.unity.com" }, "com.unity.ide.vscode": { - "version": "1.2.4", + "version": "1.2.5", "depth": 0, "source": "registry", "dependencies": {}, "url": "https://packages.unity.com" }, + "com.unity.nuget.newtonsoft-json": { + "version": "3.0.2", + "depth": 2, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.services.core": { + "version": "1.4.0", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.nuget.newtonsoft-json": "3.0.2", + "com.unity.modules.androidjni": "1.0.0" + }, + "url": "https://packages.unity.com" + }, "com.unity.test-framework": { - "version": "1.1.29", + "version": "1.1.31", "depth": 0, "source": "registry", "dependencies": { @@ -51,7 +71,7 @@ "url": "https://packages.unity.com" }, "com.unity.textmeshpro": { - "version": "2.1.4", + "version": "3.0.6", "depth": 0, "source": "registry", "dependencies": { @@ -60,7 +80,7 @@ "url": "https://packages.unity.com" }, "com.unity.timeline": { - "version": "1.2.18", + "version": "1.6.4", "depth": 0, "source": "registry", "dependencies": { @@ -213,6 +233,18 @@ "depth": 0, "source": "builtin", "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.uielementsnative": "1.0.0" + } + }, + "com.unity.modules.uielementsnative": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", "com.unity.modules.imgui": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0" } diff --git a/UnityProject/ProjectSettings/MemorySettings.asset b/UnityProject/ProjectSettings/MemorySettings.asset new file mode 100644 index 0000000..5b5face --- /dev/null +++ b/UnityProject/ProjectSettings/MemorySettings.asset @@ -0,0 +1,35 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!387306366 &1 +MemorySettings: + m_ObjectHideFlags: 0 + m_EditorMemorySettings: + m_MainAllocatorBlockSize: -1 + m_ThreadAllocatorBlockSize: -1 + m_MainGfxBlockSize: -1 + m_ThreadGfxBlockSize: -1 + m_CacheBlockSize: -1 + m_TypetreeBlockSize: -1 + m_ProfilerBlockSize: -1 + m_ProfilerEditorBlockSize: -1 + m_BucketAllocatorGranularity: -1 + m_BucketAllocatorBucketsCount: -1 + m_BucketAllocatorBlockSize: -1 + m_BucketAllocatorBlockCount: -1 + m_ProfilerBucketAllocatorGranularity: -1 + m_ProfilerBucketAllocatorBucketsCount: -1 + m_ProfilerBucketAllocatorBlockSize: -1 + m_ProfilerBucketAllocatorBlockCount: -1 + m_TempAllocatorSizeMain: -1 + m_JobTempAllocatorBlockSize: -1 + m_BackgroundJobTempAllocatorBlockSize: -1 + m_JobTempAllocatorReducedBlockSize: -1 + m_TempAllocatorSizeGIBakingWorker: -1 + m_TempAllocatorSizeNavMeshWorker: -1 + m_TempAllocatorSizeAudioWorker: -1 + m_TempAllocatorSizeCloudWorker: -1 + m_TempAllocatorSizeGfx: -1 + m_TempAllocatorSizeJobWorker: -1 + m_TempAllocatorSizeBackgroundWorker: -1 + m_TempAllocatorSizePreloadManager: -1 + m_PlatformMemorySettings: {} diff --git a/UnityProject/ProjectSettings/PackageManagerSettings.asset b/UnityProject/ProjectSettings/PackageManagerSettings.asset index ca9e773..112a053 100644 --- a/UnityProject/ProjectSettings/PackageManagerSettings.asset +++ b/UnityProject/ProjectSettings/PackageManagerSettings.asset @@ -9,10 +9,14 @@ MonoBehaviour: m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 13960, guid: 0000000000000000e000000000000000, type: 0} + m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: + m_EnablePreReleasePackages: 0 + m_EnablePackageDependencies: 0 + m_AdvancedSettingsExpanded: 1 m_ScopedRegistriesSettingsExpanded: 1 + m_SeeAllPackageVersions: 0 oneTimeWarningShown: 0 m_Registries: - m_Id: main @@ -20,19 +24,12 @@ MonoBehaviour: m_Url: https://packages.unity.com m_Scopes: [] m_IsDefault: 1 + m_Capabilities: 7 m_UserSelectedRegistryName: m_UserAddingNewScopedRegistry: 0 m_RegistryInfoDraft: - m_ErrorMessage: - m_Original: - m_Id: - m_Name: - m_Url: - m_Scopes: [] - m_IsDefault: 0 m_Modified: 0 - m_Name: - m_Url: - m_Scopes: - - - m_SelectedScopeIndex: 0 + m_ErrorMessage: + m_UserModificationsInstanceId: -830 + m_OriginalInstanceId: -832 + m_LoadAssets: 0 diff --git a/UnityProject/ProjectSettings/ProjectSettings.asset b/UnityProject/ProjectSettings/ProjectSettings.asset index 2d0b05a..6ce95a6 100644 --- a/UnityProject/ProjectSettings/ProjectSettings.asset +++ b/UnityProject/ProjectSettings/ProjectSettings.asset @@ -3,7 +3,7 @@ --- !u!129 &1 PlayerSettings: m_ObjectHideFlags: 0 - serializedVersion: 20 + serializedVersion: 23 productGUID: 6a73dd68b09754c4f94f10516ae1a93a AndroidProfiler: 0 AndroidFilterTouchesWhenObscured: 0 @@ -49,6 +49,8 @@ PlayerSettings: m_StereoRenderingPath: 0 m_ActiveColorSpace: 0 m_MTRendering: 1 + mipStripping: 0 + numberOfMipsStripped: 0 m_StackTraceTypes: 010000000100000001000000010000000100000001000000 iosShowActivityIndicatorOnLoading: -1 androidShowActivityIndicatorOnLoading: -1 @@ -123,7 +125,9 @@ PlayerSettings: stadiaTargetFramerate: 0 vulkanNumSwapchainBuffers: 3 vulkanEnableSetSRGBWrite: 0 + vulkanEnablePreTransform: 0 vulkanEnableLateAcquireNextImage: 0 + vulkanEnableCommandBufferRecycling: 1 m_SupportedAspectRatios: 4:3: 1 5:4: 1 @@ -138,45 +142,27 @@ PlayerSettings: xboxOneDisableKinectGpuReservation: 1 xboxOneEnable7thCore: 1 vrSettings: - cardboard: - depthFormat: 0 - enableTransitionView: 0 - daydream: - depthFormat: 0 - useSustainedPerformanceMode: 0 - enableVideoLayer: 0 - useProtectedVideoMemory: 0 - minimumSupportedHeadTracking: 0 - maximumSupportedHeadTracking: 1 - hololens: - depthFormat: 1 - depthBufferSharingEnabled: 1 - lumin: - depthFormat: 0 - frameTiming: 2 - enableGLCache: 0 - glCacheMaxBlobSize: 524288 - glCacheMaxFileSize: 8388608 - oculus: - sharedDepthBuffer: 1 - dashSupport: 1 - lowOverheadMode: 0 - protectedContext: 0 - v2Signing: 1 enable360StereoCapture: 0 isWsaHolographicRemotingEnabled: 0 enableFrameTimingStats: 0 + enableOpenGLProfilerGPURecorders: 1 useHDRDisplay: 0 D3DHDRBitDepth: 0 m_ColorGamuts: 00000000 targetPixelDensity: 30 resolutionScalingMode: 0 + resetResolutionOnWindowResize: 0 androidSupportedAspectRatio: 1 androidMaxAspectRatio: 2.1 - applicationIdentifier: {} - buildNumber: {} + applicationIdentifier: + Standalone: com.DefaultCompany.UnityProject + buildNumber: + Standalone: 0 + iPhone: 0 + tvOS: 0 + overrideDefaultApplicationIdentifier: 0 AndroidBundleVersionCode: 1 - AndroidMinSdkVersion: 19 + AndroidMinSdkVersion: 22 AndroidTargetSdkVersion: 0 AndroidPreferredInstallLocation: 1 aotOptions: @@ -191,10 +177,10 @@ PlayerSettings: StripUnusedMeshComponents: 0 VertexChannelCompressionMask: 4054 iPhoneSdkVersion: 988 - iOSTargetOSVersionString: 10.0 + iOSTargetOSVersionString: 11.0 tvOSSdkVersion: 0 tvOSRequireExtendedGameController: 0 - tvOSTargetOSVersionString: 10.0 + tvOSTargetOSVersionString: 11.0 uIPrerenderedIcon: 0 uIRequiresPersistentWiFi: 0 uIRequiresFullScreen: 1 @@ -228,10 +214,11 @@ PlayerSettings: iOSLaunchScreeniPadFillPct: 100 iOSLaunchScreeniPadSize: 100 iOSLaunchScreeniPadCustomXibPath: - iOSUseLaunchScreenStoryboard: 0 iOSLaunchScreenCustomStoryboardPath: + iOSLaunchScreeniPadCustomStoryboardPath: iOSDeviceRequirements: [] iOSURLSchemes: [] + macOSURLSchemes: [] iOSBackgroundModes: 0 iOSMetalForceHardShadows: 0 metalEditorSupport: 1 @@ -247,9 +234,17 @@ PlayerSettings: iOSRequireARKit: 0 iOSAutomaticallyDetectAndAddCapabilities: 1 appleEnableProMotion: 0 + shaderPrecisionModel: 0 clonedFromGUID: 00000000000000000000000000000000 templatePackageId: templateDefaultScene: + useCustomMainManifest: 0 + useCustomLauncherManifest: 0 + useCustomMainGradleTemplate: 0 + useCustomLauncherGradleManifest: 0 + useCustomBaseGradleTemplate: 0 + useCustomGradlePropertiesTemplate: 0 + useCustomProguardFile: 0 AndroidTargetArchitectures: 1 AndroidTargetDevices: 0 AndroidSplashScreenScale: 0 @@ -269,6 +264,9 @@ PlayerSettings: banner: {fileID: 0} androidGamepadSupportLevel: 0 chromeosInputEmulation: 1 + AndroidMinifyWithR8: 0 + AndroidMinifyRelease: 0 + AndroidMinifyDebug: 0 AndroidValidateAppBundleSize: 1 AndroidAppBundleSizeToValidate: 150 m_BuildTargetIcons: [] @@ -276,7 +274,13 @@ PlayerSettings: m_BuildTargetBatching: [] m_BuildTargetGraphicsJobs: [] m_BuildTargetGraphicsJobMode: [] - m_BuildTargetGraphicsAPIs: [] + m_BuildTargetGraphicsAPIs: + - m_BuildTarget: iOSSupport + m_APIs: 10000000 + m_Automatic: 1 + - m_BuildTarget: AndroidPlayer + m_APIs: 0b00000008000000 + m_Automatic: 0 m_BuildTargetVRSettings: [] openGLRequireES31: 0 openGLRequireES31AEP: 0 @@ -288,6 +292,8 @@ PlayerSettings: tvOS: 1 m_BuildTargetGroupLightmapEncodingQuality: [] m_BuildTargetGroupLightmapSettings: [] + m_BuildTargetNormalMapEncoding: [] + m_BuildTargetDefaultTextureCompressionFormat: [] playModeTestRunnerEnabled: 0 runPlayModeTestAsEditModeTest: 0 actionOnDotNetUnhandledException: 1 @@ -297,12 +303,16 @@ PlayerSettings: cameraUsageDescription: locationUsageDescription: microphoneUsageDescription: + bluetoothUsageDescription: + switchNMETAOverride: switchNetLibKey: switchSocketMemoryPoolSize: 6144 switchSocketAllocatorPoolSize: 128 switchSocketConcurrencyLimit: 14 switchScreenResolutionBehavior: 2 switchUseCPUProfiler: 0 + switchUseGOLDLinker: 0 + switchLTOSetting: 0 switchApplicationID: 0x01004b9000490000 switchNSODependencies: switchTitleNames_0: @@ -431,8 +441,11 @@ PlayerSettings: switchSocketInitializeEnabled: 1 switchNetworkInterfaceManagerInitializeEnabled: 1 switchPlayerConnectionEnabled: 1 + switchUseNewStyleFilepaths: 0 switchUseMicroSleepForYield: 1 + switchEnableRamDiskSupport: 0 switchMicroSleepForYieldTime: 25 + switchRamDiskSpaceSize: 12 ps4NPAgeRating: 12 ps4NPTitleSecret: ps4NPTrophyPackPath: @@ -508,31 +521,6 @@ PlayerSettings: ps4attribEyeToEyeDistanceSettingVR: 0 ps4IncludedModules: [] ps4attribVROutputEnabled: 0 - ps5ParamFilePath: - ps5VideoOutPixelFormat: 0 - ps5VideoOutInitialWidth: 1920 - ps5VideoOutOutputMode: 1 - ps5BackgroundImagePath: - ps5StartupImagePath: - ps5Pic2Path: - ps5StartupImagesFolder: - ps5IconImagesFolder: - ps5SaveDataImagePath: - ps5SdkOverride: - ps5BGMPath: - ps5ShareOverlayImagePath: - ps5NPConfigZipPath: - ps5Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ - ps5UseResolutionFallback: 0 - ps5UseAudio3dBackend: 0 - ps5ScriptOptimizationLevel: 2 - ps5Audio3dVirtualSpeakerCount: 14 - ps5UpdateReferencePackage: - ps5disableAutoHideSplash: 0 - ps5OperatingSystemCanDisableSplashScreen: 0 - ps5IncludedModules: [] - ps5SharedBinaryContentLabels: [] - ps5SharedBinarySystemFolders: [] monoEnv: splashScreenBackgroundSourceLandscape: {fileID: 0} splashScreenBackgroundSourcePortrait: {fileID: 0} @@ -549,12 +537,14 @@ PlayerSettings: webGLAnalyzeBuildSize: 0 webGLUseEmbeddedResources: 0 webGLCompressionFormat: 0 + webGLWasmArithmeticExceptions: 0 webGLLinkerTarget: 1 webGLThreadsSupport: 0 - webGLWasmStreaming: 0 + webGLDecompressionFallback: 0 scriptingDefineSymbols: - 1: MIRROR;MIRROR_17_0_OR_NEWER;MIRROR_18_0_OR_NEWER;MIRROR_24_0_OR_NEWER;MIRROR_26_0_OR_NEWER;MIRROR_27_0_OR_NEWER;MIRROR_28_0_OR_NEWER;MIRROR_29_0_OR_NEWER;MIRROR_30_0_OR_NEWER;MIRROR_30_5_2_OR_NEWER;MIRROR_32_1_2_OR_NEWER;MIRROR_32_1_4_OR_NEWER;MIRROR_35_0_OR_NEWER;MIRROR_35_1_OR_NEWER;MIRROR_37_0_OR_NEWER;MIRROR_38_0_OR_NEWER;MIRROR_39_0_OR_NEWER;MIRROR_40_0_OR_NEWER;IGNORANCE;IGNORANCE_1;IGNORANCE_1_4 - 13: MIRROR;MIRROR_17_0_OR_NEWER;MIRROR_18_0_OR_NEWER;MIRROR_24_0_OR_NEWER;MIRROR_26_0_OR_NEWER;MIRROR_27_0_OR_NEWER;MIRROR_28_0_OR_NEWER;MIRROR_29_0_OR_NEWER;MIRROR_30_0_OR_NEWER;MIRROR_30_5_2_OR_NEWER;MIRROR_32_1_2_OR_NEWER;MIRROR_32_1_4_OR_NEWER;MIRROR_35_0_OR_NEWER;MIRROR_35_1_OR_NEWER + Standalone: MIRROR;MIRROR_17_0_OR_NEWER;MIRROR_18_0_OR_NEWER;MIRROR_24_0_OR_NEWER;MIRROR_26_0_OR_NEWER;MIRROR_27_0_OR_NEWER;MIRROR_28_0_OR_NEWER;MIRROR_29_0_OR_NEWER;MIRROR_30_0_OR_NEWER;MIRROR_30_5_2_OR_NEWER;MIRROR_32_1_2_OR_NEWER;MIRROR_32_1_4_OR_NEWER;MIRROR_35_0_OR_NEWER;MIRROR_35_1_OR_NEWER;MIRROR_37_0_OR_NEWER;MIRROR_38_0_OR_NEWER;MIRROR_39_0_OR_NEWER;MIRROR_40_0_OR_NEWER;MIRROR_41_0_OR_NEWER;MIRROR_42_0_OR_NEWER;MIRROR_43_0_OR_NEWER;MIRROR_44_0_OR_NEWER;MIRROR_46_0_OR_NEWER;MIRROR_47_0_OR_NEWER;MIRROR_53_0_OR_NEWER;MIRROR_55_0_OR_NEWER;MIRROR_57_0_OR_NEWER;MIRROR_58_0_OR_NEWER;MIRROR_65_0_OR_NEWER;MIRROR_66_0_OR_NEWER;MIRROR_2022_9_OR_NEWER + WebGL: MIRROR;MIRROR_17_0_OR_NEWER;MIRROR_18_0_OR_NEWER;MIRROR_24_0_OR_NEWER;MIRROR_26_0_OR_NEWER;MIRROR_27_0_OR_NEWER;MIRROR_28_0_OR_NEWER;MIRROR_29_0_OR_NEWER;MIRROR_30_0_OR_NEWER;MIRROR_30_5_2_OR_NEWER;MIRROR_32_1_2_OR_NEWER;MIRROR_32_1_4_OR_NEWER;MIRROR_35_0_OR_NEWER;MIRROR_35_1_OR_NEWER + additionalCompilerArguments: {} platformArchitecture: {} scriptingBackend: Standalone: 0 @@ -563,6 +553,8 @@ PlayerSettings: incrementalIl2cppBuild: {} suppressCommonWarnings: 1 allowUnsafeCode: 0 + useDeterministicCompilation: 1 + enableRoslynAnalyzers: 1 additionalIl2CppArgs: scriptingRuntimeVersion: 1 gcIncremental: 0 @@ -600,6 +592,7 @@ PlayerSettings: metroFTAName: metroFTAFileTypes: [] metroProtocolName: + vcxProjDefaultLanguage: XboxOneProductId: XboxOneUpdateKey: XboxOneSandboxId: @@ -627,10 +620,7 @@ PlayerSettings: XboxOneXTitleMemory: 8 XboxOneOverrideIdentityName: XboxOneOverrideIdentityPublisher: - vrEditorSettings: - daydream: - daydreamIconForeground: {fileID: 0} - daydreamIconBackground: {fileID: 0} + vrEditorSettings: {} cloudServicesEnabled: {} luminIcon: m_Name: @@ -644,11 +634,14 @@ PlayerSettings: m_VersionCode: 1 m_VersionName: apiCompatibilityLevel: 6 + activeInputHandler: 0 cloudProjectId: framebufferDepthMemorylessMode: 0 + qualitySettingsNames: [] projectName: organizationId: cloudEnabled: 0 - enableNativePlatformBackendsForNewInputSystem: 0 - disableOldInputManagerSupport: 0 legacyClampBlendShapeWeights: 0 + playerDataPath: + forceSRGBBlit: 1 + virtualTexturingSupportEnabled: 0 diff --git a/UnityProject/ProjectSettings/ProjectVersion.txt b/UnityProject/ProjectSettings/ProjectVersion.txt index 5549a7f..bdb3ba5 100644 --- a/UnityProject/ProjectSettings/ProjectVersion.txt +++ b/UnityProject/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 2019.4.31f1 -m_EditorVersionWithRevision: 2019.4.31f1 (bd5abf232a62) +m_EditorVersion: 2021.3.5f1 +m_EditorVersionWithRevision: 2021.3.5f1 (40eb3a945986) diff --git a/UnityProject/ProjectSettings/TimelineSettings.asset b/UnityProject/ProjectSettings/TimelineSettings.asset new file mode 100644 index 0000000..cfaebd7 --- /dev/null +++ b/UnityProject/ProjectSettings/TimelineSettings.asset @@ -0,0 +1,16 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a287be6c49135cd4f9b2b8666c39d999, type: 3} + m_Name: + m_EditorClassIdentifier: + assetDefaultFramerate: 60 + m_DefaultFrameRate: 60 diff --git a/UnityProject/ProjectSettings/VersionControlSettings.asset b/UnityProject/ProjectSettings/VersionControlSettings.asset new file mode 100644 index 0000000..dca2881 --- /dev/null +++ b/UnityProject/ProjectSettings/VersionControlSettings.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!890905787 &1 +VersionControlSettings: + m_ObjectHideFlags: 0 + m_Mode: Visible Meta Files + m_CollabEditorSettings: + inProgressEnabled: 1 diff --git a/UnityProject/ProjectSettings/boot.config b/UnityProject/ProjectSettings/boot.config new file mode 100644 index 0000000..e69de29