diff --git a/Assets/Mirage/Components/NetworkMatchChecker.cs b/Assets/Mirage/Components/NetworkMatchChecker.cs deleted file mode 100644 index c4e51a71c6e..00000000000 --- a/Assets/Mirage/Components/NetworkMatchChecker.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; - -namespace Mirage -{ - /// - /// 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. - /// - [DisallowMultipleComponent] - [AddComponentMenu("Network/NetworkMatchChecker")] - [RequireComponent(typeof(NetworkIdentity))] - [HelpURL("https://mirror-networking.com/docs/Components/NetworkMatchChecker.html")] - public class NetworkMatchChecker : NetworkVisibility - { - static readonly Dictionary> matchPlayers = new Dictionary>(); - - Guid currentMatch = Guid.Empty; - - [Header("Diagnostics")] - [SyncVar] - public string currentMatchDebug; - - public NetworkIdentity Identity => GetComponent(); - - /// - /// 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(Identity); - - // 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(Identity); - - // 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 - Identity.RebuildObservers(false); - } - } - } - - public void Awake() - { - Identity.OnStartServer.AddListener(OnStartServer); - } - - public void OnStartServer() - { - if (currentMatch == Guid.Empty) return; - - if (!matchPlayers.ContainsKey(currentMatch)) - matchPlayers.Add(currentMatch, new HashSet()); - - matchPlayers[currentMatch].Add(Identity); - - // No need to rebuild anything here. - // identity.RebuildObservers is called right after this from NetworkServer.SpawnObject - } - - 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(INetworkPlayer player) - { - // Not Visible if not in a match - if (MatchId == Guid.Empty) - return false; - - NetworkMatchChecker networkMatchChecker = player.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/Assets/Mirage/Components/NetworkMatchChecker.cs.meta b/Assets/Mirage/Components/NetworkMatchChecker.cs.meta deleted file mode 100644 index 7c7d6cfc4f2..00000000000 --- a/Assets/Mirage/Components/NetworkMatchChecker.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 1020a74962faada4b807ac5dc053a4cf -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Mirage/Components/NetworkProximityChecker.cs b/Assets/Mirage/Components/NetworkProximityChecker.cs deleted file mode 100644 index 5bf047484e4..00000000000 --- a/Assets/Mirage/Components/NetworkProximityChecker.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; -using Mirage.Logging; - -namespace Mirage -{ - /// - /// 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. - /// - [AddComponentMenu("Network/NetworkProximityChecker")] - [RequireComponent(typeof(NetworkIdentity))] - [HelpURL("https://mirror-networking.com/docs/Components/NetworkProximityChecker.html")] - public class NetworkProximityChecker : NetworkVisibility - { - static readonly ILogger logger = LogFactory.GetLogger(typeof(NetworkProximityChecker)); - - /// - /// The maximim range that objects will be visible at. - /// - [Tooltip("The maximum range that objects will be visible at.")] - public int VisibilityRange = 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 VisibilityUpdateInterval = 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. - /// - [Tooltip("Enable to force this object to be hidden from players.")] - public bool ForceHidden; - - public void Awake() - { - NetIdentity.OnStartServer.AddListener(() => - { - InvokeRepeating(nameof(RebuildObservers), 0, VisibilityUpdateInterval); - }); - - NetIdentity.OnStopServer.AddListener(() => - { - 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(INetworkPlayer player) - { - if (ForceHidden) - return false; - - return Vector3.Distance(player.Identity.transform.position, transform.position) < VisibilityRange; - } - - /// - /// 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 (INetworkPlayer player in Server.Players) - { - // check distance - if (player != null && player.Identity != null && Vector3.Distance(player.Identity.transform.position, position) < VisibilityRange) - { - observers.Add(player); - } - } - } - } -} diff --git a/Assets/Mirage/Components/NetworkProximityChecker.cs.meta b/Assets/Mirage/Components/NetworkProximityChecker.cs.meta deleted file mode 100644 index c5aa1123365..00000000000 --- a/Assets/Mirage/Components/NetworkProximityChecker.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 1731d8de2d0c84333b08ebe1e79f4118 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Mirage/Components/NetworkSceneChecker.cs b/Assets/Mirage/Components/NetworkSceneChecker.cs deleted file mode 100644 index ed286e17d94..00000000000 --- a/Assets/Mirage/Components/NetworkSceneChecker.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; -using UnityEngine.SceneManagement; -using Mirage.Logging; - -namespace Mirage -{ - /// - /// 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 - /// - [DisallowMultipleComponent] - [AddComponentMenu("Network/NetworkSceneChecker")] - [RequireComponent(typeof(NetworkIdentity))] - [HelpURL("https://mirror-networking.com/docs/Components/NetworkSceneChecker.html")] - public class NetworkSceneChecker : NetworkVisibility - { - static readonly ILogger logger = LogFactory.GetLogger(typeof(NetworkSceneChecker)); - - /// - /// 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; - - [Server(error = false)] - void Awake() - { - currentScene = gameObject.scene; - if (logger.LogEnabled()) logger.Log($"NetworkSceneChecker.Awake currentScene: {currentScene}"); - - OnStartServer(); - } - - public void OnStartServer() - { - if (!sceneCheckerObjects.ContainsKey(currentScene)) - sceneCheckerObjects.Add(currentScene, new HashSet()); - - sceneCheckerObjects[currentScene].Add(NetIdentity); - } - - [Server(error = false)] - 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(INetworkPlayer player) - { - if (forceHidden) - return false; - - return player.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/Assets/Mirage/Components/NetworkSceneChecker.cs.meta b/Assets/Mirage/Components/NetworkSceneChecker.cs.meta deleted file mode 100644 index b451655a379..00000000000 --- a/Assets/Mirage/Components/NetworkSceneChecker.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: b7fdb599e1359924bad6255660370252 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Mirage/Editor/NetworkInformationPreview.cs b/Assets/Mirage/Editor/NetworkInformationPreview.cs index ee907c7bbed..68442757e58 100644 --- a/Assets/Mirage/Editor/NetworkInformationPreview.cs +++ b/Assets/Mirage/Editor/NetworkInformationPreview.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using UnityEditor; using UnityEngine; +using System.Linq; namespace Mirage { @@ -172,7 +173,9 @@ float DrawNetworkBehaviors(NetworkIdentity identity, float initialX, float Y) float DrawObservers(NetworkIdentity identity, float initialX, float Y) { - if (identity.observers.Count > 0) + IEnumerable observers = identity.Observers; + + if (observers.Any()) { var observerRect = new Rect(initialX, Y + 10, 200, 20); @@ -181,7 +184,7 @@ float DrawObservers(NetworkIdentity identity, float initialX, float Y) observerRect.x += 20; observerRect.y += observerRect.height; - foreach (INetworkPlayer player in identity.observers) + foreach (INetworkPlayer player in observers) { GUI.Label(observerRect, player.Connection.GetEndPointAddress() + ":" + player, styles.ComponentName); observerRect.y += observerRect.height; diff --git a/Assets/Mirage/Runtime/INetworkPlayer.cs b/Assets/Mirage/Runtime/INetworkPlayer.cs index c27be6653ef..f54c04488a3 100644 --- a/Assets/Mirage/Runtime/INetworkPlayer.cs +++ b/Assets/Mirage/Runtime/INetworkPlayer.cs @@ -71,17 +71,6 @@ public interface IMessageHandler : IMessageSender, IMessageReceiver, INotifySend } - /// - /// An object that can observe NetworkIdentities. - /// this is useful for interest management - /// - public interface IVisibilityTracker - { - void AddToVisList(NetworkIdentity identity); - void RemoveFromVisList(NetworkIdentity identity); - void RemoveObservers(); - } - /// /// An object that can own networked objects /// @@ -97,7 +86,7 @@ public interface IObjectOwner /// A connection to a remote endpoint. /// May be from the server to client or from client to server /// - public interface INetworkPlayer : IMessageHandler, IVisibilityTracker, IObjectOwner, IAuthenticatedObject, ISceneLoader + public interface INetworkPlayer : IMessageHandler, IObjectOwner, IAuthenticatedObject, ISceneLoader { IConnection Connection { get; } } diff --git a/Assets/Mirage/Runtime/InterestManagement.meta b/Assets/Mirage/Runtime/InterestManagement.meta new file mode 100644 index 00000000000..47b87ee5126 --- /dev/null +++ b/Assets/Mirage/Runtime/InterestManagement.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 60a4aeac526944324921b45b65b50ffa +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirage/Runtime/InterestManagement/GlobalInterestManager.cs b/Assets/Mirage/Runtime/InterestManagement/GlobalInterestManager.cs new file mode 100644 index 00000000000..bdbc23f996c --- /dev/null +++ b/Assets/Mirage/Runtime/InterestManagement/GlobalInterestManager.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; + +namespace Mirage.InterestManagement +{ + /// + /// A simple interest manager where all players see all objects + /// + public class GlobalInterestManager : InterestManager + { + public override IEnumerable Observers(NetworkIdentity identity) + { + return ServerObjectManager.Server.Players; + } + + protected override void OnAuthenticated(INetworkPlayer player) + { + foreach (NetworkIdentity identity in ServerObjectManager.SpawnedObjects.Values) + { + ServerObjectManager.ShowForConnection(identity, player); + } + } + + protected override void OnSpawned(NetworkIdentity identity) + { + foreach (INetworkPlayer player in ServerObjectManager.Server.Players) + { + ServerObjectManager.ShowForConnection(identity, player); + } + } + } +} \ No newline at end of file diff --git a/Assets/Tests/Runtime/NetworkIdentityCallbackTests.cs.meta b/Assets/Mirage/Runtime/InterestManagement/GlobalInterestManager.cs.meta similarity index 83% rename from Assets/Tests/Runtime/NetworkIdentityCallbackTests.cs.meta rename to Assets/Mirage/Runtime/InterestManagement/GlobalInterestManager.cs.meta index 7ecc98c8b6a..33295d3ba9b 100644 --- a/Assets/Tests/Runtime/NetworkIdentityCallbackTests.cs.meta +++ b/Assets/Mirage/Runtime/InterestManagement/GlobalInterestManager.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d30db0a07349e4eec880cda9012fbb0a +guid: 5c6ee0ff31a6248959cbb910c2daebf3 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/Mirage/Runtime/InterestManagement/InterestManager.cs b/Assets/Mirage/Runtime/InterestManagement/InterestManager.cs new file mode 100644 index 00000000000..3a469638382 --- /dev/null +++ b/Assets/Mirage/Runtime/InterestManagement/InterestManager.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Mirage.InterestManagement +{ + /// + /// An interest manager is responsible for showing and hiding objects to players + /// based on an area if interest policy. + /// + /// Implement this class to provide a interest management policy and assign + /// it to the + /// + public abstract class InterestManager : MonoBehaviour + { + public ServerObjectManager ServerObjectManager; + + public void Start() + { + if (ServerObjectManager == null) + ServerObjectManager = GetComponent(); + + ServerObjectManager.Spawned.AddListener(OnSpawned); + + NetworkServer server = ServerObjectManager.Server; + if (server == null) + server = GetComponent(); + + server.Authenticated.AddListener(OnAuthenticated); + } + + /// + /// Invoked when a player joins the server + /// It should show all objects relevant to that player + /// + /// + protected abstract void OnAuthenticated(INetworkPlayer player); + + /// + /// Invoked when an object is spawned in the server + /// It should show that object to all relevant players + /// + /// The object just spawned + protected abstract void OnSpawned(NetworkIdentity identity); + + /// + /// Find out all the players that can see an object + /// + /// + /// + public abstract IEnumerable Observers(NetworkIdentity identity); + } +} diff --git a/Assets/Tests/Runtime/NetworkMatchCheckerTest.cs.meta b/Assets/Mirage/Runtime/InterestManagement/InterestManager.cs.meta similarity index 83% rename from Assets/Tests/Runtime/NetworkMatchCheckerTest.cs.meta rename to Assets/Mirage/Runtime/InterestManagement/InterestManager.cs.meta index b3c51cf813f..ff5b120d989 100644 --- a/Assets/Tests/Runtime/NetworkMatchCheckerTest.cs.meta +++ b/Assets/Mirage/Runtime/InterestManagement/InterestManager.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 2fa0a455ab9b4cf47b9eab0f2b03ce0c +guid: 57b9a2e5882b242a58372f2e02b7c3ff MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/Mirage/Runtime/NetworkIdentity.cs b/Assets/Mirage/Runtime/NetworkIdentity.cs index 2857d01aa08..e2f518ee132 100644 --- a/Assets/Mirage/Runtime/NetworkIdentity.cs +++ b/Assets/Mirage/Runtime/NetworkIdentity.cs @@ -7,6 +7,8 @@ using UnityEngine.Events; using Mirage.Logging; using Mirage.Serialization; +using Mirage.InterestManagement; +using System.Linq; #if UNITY_EDITOR using UnityEditor; #if UNITY_2018_3_OR_NEWER @@ -143,17 +145,34 @@ public sealed class NetworkIdentity : MonoBehaviour /// public bool HasAuthority { get; internal set; } - /// - /// The set of network connections (players) that can see this object. - /// - public readonly HashSet observers = new HashSet(); - /// /// Unique identifier for this particular object instance, used for tracking objects between networked clients and the server. /// This is a unique identifier for this particular GameObject instance. Use it to track GameObjects between networked clients and the server. /// public uint NetId { get; internal set; } + /// + /// Returns list of players that can see this object + /// + /// Returns empty list if the object has not been spawned yet + /// Returns empty list if this is not a server object + public IEnumerable Observers + { + get + { + if (ServerObjectManager == null) + return Enumerable.Empty(); + + InterestManager interestManager = ServerObjectManager.InterestManager; + + if (interestManager == null) + return Enumerable.Empty(); + + return interestManager.Observers(this); + } + } + + /// /// A unique identifier for NetworkIdentity objects within a scene. /// This is used for spawning scene objects on clients. @@ -227,20 +246,6 @@ public NetworkBehaviour[] NetworkBehaviours } } - - NetworkVisibility visibilityCache; - public NetworkVisibility Visibility - { - get - { - if (visibilityCache is null) - { - visibilityCache = GetComponent(); - } - return visibilityCache; - } - } - [SerializeField, HideInInspector] string m_AssetId; @@ -394,15 +399,6 @@ internal void SetClientOwner(INetworkPlayer player) /// public static event ClientAuthorityCallback clientAuthorityCallback; - /// - /// this is used when a connection is destroyed, since the "observers" property is read-only - /// - /// - internal void RemoveObserverInternal(INetworkPlayer player) - { - observers.Remove(player); - } - /// /// hasSpawned should always be false before runtime /// @@ -714,28 +710,6 @@ internal void StopAuthority() OnStopAuthority?.Invoke(); } - /// - /// check if observer can be seen by connection. - /// - /// - /// returns visibility.OnCheckObserver - /// - /// - /// returns true if we have no NetworkVisibility, default objects are visible - /// - /// - /// - /// - /// - internal bool OnCheckObserver(INetworkPlayer player) - { - if (Visibility != null) - { - return Visibility.OnCheckObserver(player); - } - return true; - } - internal void StopClient() { OnStopClient?.Invoke(); @@ -914,170 +888,6 @@ internal void HandleRemoteCall(Skeleton skeleton, int componentIndex, NetworkRea } } - /// - /// Called when NetworkIdentity is destroyed - /// - internal void ClearObservers() - { - foreach (INetworkPlayer player in observers) - { - player.RemoveFromVisList(this); - } - observers.Clear(); - } - - internal void AddObserver(INetworkPlayer player) - { - if (observers.Contains(player)) - { - // if we try to add a connectionId that was already added, then - // we may have generated one that was already in use. - return; - } - - if (logger.LogEnabled()) logger.Log("Added observer " + player.Connection.GetEndPointAddress() + " added for " + gameObject); - observers.Add(player); - player.AddToVisList(this); - - // spawn identity for this conn - ServerObjectManager.ShowForConnection(this, player); - } - - /// - /// Helper function to call OnRebuildObservers in all components - /// HashSet is passed in so we can cache it! - /// Returns true if we have a NetworkVisibility, false otherwise - /// Initialize is true on first rebuild, false on consecutive rebuilds - /// - /// - /// - /// - internal bool GetNewObservers(HashSet observersSet, bool initialize) - { - observersSet.Clear(); - - if (Visibility != null) - { - Visibility.OnRebuildObservers(observersSet, initialize); - return true; - } - - // we have no NetworkVisibility. return false to indicate that we - // should use the default implementation. - return false; - } - - /// - /// Helper function to add all server connections as observers. - /// This is used if none of the components provides their own - /// OnRebuildObservers function. - /// - internal void AddAllReadyServerConnectionsToObservers() - { - // add all server connections - foreach (INetworkPlayer player in Server.Players) - { - if (player.IsReady) - AddObserver(player); - } - - // add local host connection (if any) - if (Server.LocalPlayer != null && Server.LocalPlayer.IsReady) - { - AddObserver(Server.LocalPlayer); - } - } - - static readonly HashSet newObservers = new HashSet(); - - /// - /// This causes the set of players that can see this object to be rebuild. - /// The OnRebuildObservers callback function will be invoked on each NetworkBehaviour. - /// - /// True if this is the first time. - public void RebuildObservers(bool initialize) - { - bool changed = false; - - // call OnRebuildObservers function - bool rebuildOverwritten = GetNewObservers(newObservers, initialize); - - // if player connection: ensure player always see himself no matter what. - // -> fixes https://github.com/vis2k/Mirror/issues/692 where a - // player might teleport out of the ProximityChecker's cast, - // losing the own connection as observer. - if (ConnectionToClient != null && ConnectionToClient.IsReady) - { - newObservers.Add(ConnectionToClient); - } - - // if no NetworkVisibility component, then add all server connections. - if (!rebuildOverwritten) - { - // only add all connections when rebuilding the first time. - // second time we just keep them without rebuilding anything. - if (initialize) - { - AddAllReadyServerConnectionsToObservers(); - } - return; - } - - changed = AddNewObservers(initialize, changed); - - changed = RemoveOldObservers(changed); - - if (changed) - { - observers.Clear(); - foreach (INetworkPlayer player in newObservers) - { - if (player != null && player.IsReady) - observers.Add(player); - } - } - } - - // remove all old .observers that aren't in newObservers anymore - bool RemoveOldObservers(bool changed) - { - foreach (INetworkPlayer player in observers) - { - if (!newObservers.Contains(player)) - { - // removed observer - player.RemoveFromVisList(this); - ServerObjectManager.HideForConnection(this, player); - - if (logger.LogEnabled()) logger.Log("Removed Observer for " + gameObject + " " + player); - changed = true; - } - } - - return changed; - } - - // add all newObservers that aren't in .observers yet - bool AddNewObservers(bool initialize, bool changed) - { - foreach (INetworkPlayer player in newObservers) - { - // only add ready connections. - // otherwise the player might not be in the world yet or anymore - if (player != null && player.IsReady && (initialize || !observers.Contains(player))) - { - // new observer - player.AddToVisList(this); - // spawn identity for this conn - ServerObjectManager.ShowForConnection(this, player); - if (logger.LogEnabled()) logger.Log("New Observer for " + gameObject + " " + player); - changed = true; - } - } - - return changed; - } - /// /// 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. This object then will be in the NetworkConnection.clientOwnedObjects list for the connection. @@ -1164,13 +974,11 @@ internal void Reset() ConnectionToServer = null; ConnectionToClient = null; networkBehavioursCache = null; - - ClearObservers(); } internal void ServerUpdate() { - if (observers.Count > 0) + if (Observers.Any()) { SendUpdateVarsMessage(); } @@ -1242,17 +1050,17 @@ internal void SendToObservers(T msg, bool includeOwner = true, int channelId { if (logger.LogEnabled()) logger.Log("Server.SendToObservers id:" + typeof(T)); - if (observers.Count == 0) + if (!Observers.Any()) return; if (includeOwner) { - NetworkServer.SendToMany(observers, msg, channelId); + NetworkServer.SendToMany(Observers, msg, channelId); } else { connectionsExcludeSelf.Clear(); - foreach (INetworkPlayer player in observers) + foreach (INetworkPlayer player in Observers) { if (ConnectionToClient != player) { diff --git a/Assets/Mirage/Runtime/NetworkPlayer.cs b/Assets/Mirage/Runtime/NetworkPlayer.cs index 4ca4fddbabe..915ca09514d 100644 --- a/Assets/Mirage/Runtime/NetworkPlayer.cs +++ b/Assets/Mirage/Runtime/NetworkPlayer.cs @@ -32,9 +32,6 @@ public class NetworkPlayer : INetworkPlayer // Handles network messages on client and server internal delegate void NetworkMessageDelegate(INetworkPlayer player, NetworkReader reader, int channelId); - // internal so it can be tested - private readonly HashSet visList = new HashSet(); - // message handlers for this connection internal readonly Dictionary messageHandlers = new Dictionary(); @@ -207,25 +204,6 @@ public override string ToString() return $"connection({Address})"; } - public void AddToVisList(NetworkIdentity identity) - { - visList.Add(identity); - } - - public void RemoveFromVisList(NetworkIdentity identity) - { - visList.Remove(identity); - } - - public void RemoveObservers() - { - foreach (NetworkIdentity identity in visList) - { - identity.RemoveObserverInternal(this); - } - visList.Clear(); - } - internal void InvokeHandler(int msgType, NetworkReader reader, int channelId) { if (messageHandlers.TryGetValue(msgType, out NetworkMessageDelegate msgDelegate)) diff --git a/Assets/Mirage/Runtime/NetworkVisibility.cs b/Assets/Mirage/Runtime/NetworkVisibility.cs deleted file mode 100644 index 3e94f92c50b..00000000000 --- a/Assets/Mirage/Runtime/NetworkVisibility.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; - -namespace Mirage -{ - // 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. - [DisallowMultipleComponent] - public abstract class NetworkVisibility : NetworkBehaviour - { - /// - /// 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 abstract bool OnCheckObserver(INetworkPlayer player); - - /// - /// 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 abstract void OnRebuildObservers(HashSet observers, bool initialize); - - } -} diff --git a/Assets/Mirage/Runtime/NetworkVisibility.cs.meta b/Assets/Mirage/Runtime/NetworkVisibility.cs.meta deleted file mode 100644 index 1e6665899e3..00000000000 --- a/Assets/Mirage/Runtime/NetworkVisibility.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: c08f1a030234d49d391d7223a8592f15 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Mirage/Runtime/ServerObjectManager.cs b/Assets/Mirage/Runtime/ServerObjectManager.cs index 52d104d7344..3bf70897697 100644 --- a/Assets/Mirage/Runtime/ServerObjectManager.cs +++ b/Assets/Mirage/Runtime/ServerObjectManager.cs @@ -6,6 +6,7 @@ using UnityEngine.Serialization; using Mirage.Logging; using Mirage.Serialization; +using Mirage.InterestManagement; namespace Mirage { @@ -44,6 +45,8 @@ public class ServerObjectManager : MonoBehaviour, IServerObjectManager, IObjectL [SerializeField] SpawnEvent _unSpawned = new SpawnEvent(); public SpawnEvent UnSpawned => _unSpawned; + public InterestManager InterestManager; + uint nextNetworkId = 1; uint GetNextNetworkId() => nextNetworkId++; @@ -63,6 +66,15 @@ public NetworkIdentity this[uint netId] public void Start() { + if (InterestManager == null) + { + InterestManager = gameObject.AddComponent(); + } + if (Server == null) + { + Server = GetComponent(); + } + if (Server != null) { Server.Started.AddListener(SpawnOrActivate); @@ -179,7 +191,7 @@ void StartedHost() /// /// Connection which is adding the player. /// Client associated to the player. - /// Player object spawned for the player. + /// Character object spawned for the player. /// /// Does the previous player remain attached to this connection? /// @@ -204,34 +216,6 @@ public bool ReplaceCharacter(INetworkPlayer player, NetworkClient client, GameOb return InternalReplacePlayerForConnection(player, client, character, keepAuthority); } - void SpawnObserversForConnection(INetworkPlayer player) - { - if (logger.LogEnabled()) logger.Log("Spawning " + SpawnedObjects.Count + " objects for conn " + player); - - if (!player.IsReady) - { - // client needs to finish initializing before we can spawn objects - // otherwise it would not find them. - return; - } - - // add connection to each nearby NetworkIdentity's observers, which - // internally sends a spawn message for each one to the connection. - foreach (NetworkIdentity identity in SpawnedObjects.Values) - { - if (identity.gameObject.activeSelf) - { - if (logger.LogEnabled()) logger.Log("Sending spawn message for current server objects name='" + identity.name + "' netId=" + identity.NetId + " sceneId=" + identity.sceneId); - - bool visible = identity.OnCheckObserver(player); - if (visible) - { - identity.AddObserver(player); - } - } - } - } - /// /// When an message handler has received a request from a player, the server calls this to associate the player object with the connection. /// When a player is added for a connection, the client for that connection is made ready automatically. The player object is automatically spawned, so you do not need to call NetworkServer.Spawn 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. @@ -347,13 +331,6 @@ internal bool InternalReplacePlayerForConnection(INetworkPlayer player, NetworkC Server.LocalClient.Player.Identity = identity; } - // add connection to observers AFTER the playerController was set. - // by definition, there is nothing to observe if there is no player - // controller. - // - // IMPORTANT: do this in AddCharacter & ReplaceCharacter! - SpawnObserversForConnection(player); - if (logger.LogEnabled()) logger.Log("Replacing playerGameObject object netId: " + character.GetComponent().NetId + " asset ID " + character.GetComponent().AssetId); Respawn(identity); @@ -376,8 +353,7 @@ internal NetworkIdentity GetNetworkIdentity(GameObject go) internal void ShowForConnection(NetworkIdentity identity, INetworkPlayer player) { - if (player.IsReady) - SendSpawnMessage(identity, player); + SendSpawnMessage(identity, player); } internal void HideForConnection(NetworkIdentity identity, INetworkPlayer player) @@ -477,8 +453,6 @@ internal void SpawnObject(GameObject obj, INetworkPlayer ownerPlayer) } if (logger.LogEnabled()) logger.Log("SpawnObject instance ID " + identity.NetId + " asset ID " + identity.AssetId); - - identity.RebuildObservers(true); } internal void SendSpawnMessage(NetworkIdentity identity, INetworkPlayer player) @@ -625,7 +599,6 @@ void DestroyObject(NetworkIdentity identity, bool destroyServerObject) identity.SendToObservers(new ObjectDestroyMessage { netId = identity.NetId }); - identity.ClearObservers(); if (Server.LocalClientActive) { identity.StopClient(); @@ -740,10 +713,6 @@ public void SetClientReady(INetworkPlayer player) // set ready player.IsReady = true; - - // client is ready to start spawning objects - if (player.Identity != null) - SpawnObserversForConnection(player); } /// @@ -769,8 +738,6 @@ public void SetClientNotReady(INetworkPlayer player) { if (logger.LogEnabled()) logger.Log("PlayerNotReady " + player); player.IsReady = false; - player.RemoveObservers(); - player.Send(new NotReadyMessage()); } } diff --git a/Assets/Mirage/Samples~/AdditiveScenes/Scripts/ShootingTankBehaviour.cs b/Assets/Mirage/Samples~/AdditiveScenes/Scripts/ShootingTankBehaviour.cs index af1f3ea60ed..f6490855cd6 100644 --- a/Assets/Mirage/Samples~/AdditiveScenes/Scripts/ShootingTankBehaviour.cs +++ b/Assets/Mirage/Samples~/AdditiveScenes/Scripts/ShootingTankBehaviour.cs @@ -1,4 +1,5 @@ using UnityEngine; +using System.Linq; namespace Mirage.Examples.Additive { @@ -23,7 +24,7 @@ void Start() void Update() { - if (IsServer && NetIdentity.observers.Count > 0) + if (IsServer && NetIdentity.Observers.Any()) ShootNearestPlayer(); if (IsClient) @@ -36,7 +37,7 @@ void ShootNearestPlayer() GameObject target = null; float distance = 100f; - foreach (INetworkPlayer networkConnection in NetIdentity.observers) + foreach (INetworkPlayer networkConnection in NetIdentity.Observers) { GameObject tempTarget = networkConnection.Identity.gameObject; float tempDistance = Vector3.Distance(tempTarget.transform.position, transform.position); diff --git a/Assets/Mirage/Samples~/Basic/Scenes/Example.unity b/Assets/Mirage/Samples~/Basic/Scenes/Example.unity index 292c115cc5d..1763c0addc7 100644 --- a/Assets/Mirage/Samples~/Basic/Scenes/Example.unity +++ b/Assets/Mirage/Samples~/Basic/Scenes/Example.unity @@ -186,6 +186,7 @@ MonoBehaviour: ClientObjectManager: {fileID: 249891961} ServerObjectManager: {fileID: 249891962} PlayerPrefab: {fileID: 1088833923132447018, guid: 22f1fa3a0aff72b46a371f667bb4fb30, type: 3} + AutoSpawn: 1 startPositionIndex: 0 startPositions: [] playerSpawnMethod: 1 @@ -232,17 +233,17 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: abe6be14204d94224a3e7cd99dd2ea73, type: 3} m_Name: m_EditorClassIdentifier: + Transport: {fileID: 249891954} authenticator: {fileID: 0} - Connected: + _connected: m_PersistentCalls: m_Calls: [] - Authenticated: + _authenticated: m_PersistentCalls: m_Calls: [] - Disconnected: + _disconnected: m_PersistentCalls: m_Calls: [] - Transport: {fileID: 249891954} --- !u!114 &249891959 MonoBehaviour: m_ObjectHideFlags: 0 @@ -256,30 +257,30 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: MaxConnections: 4 - Started: + Listening: 1 + Transport: {fileID: 249891954} + authenticator: {fileID: 0} + _started: m_PersistentCalls: m_Calls: [] - Connected: + _connected: m_PersistentCalls: m_Calls: [] - Authenticated: + _authenticated: m_PersistentCalls: m_Calls: [] - Disconnected: + _disconnected: m_PersistentCalls: m_Calls: [] - Stopped: + _stopped: m_PersistentCalls: m_Calls: [] - OnStartHost: + _onStartHost: m_PersistentCalls: m_Calls: [] - OnStopHost: + _onStopHost: m_PersistentCalls: m_Calls: [] - authenticator: {fileID: 0} - Listening: 1 - Transport: {fileID: 249891954} --- !u!114 &249891960 MonoBehaviour: m_ObjectHideFlags: 0 @@ -294,16 +295,17 @@ MonoBehaviour: m_EditorClassIdentifier: Client: {fileID: 249891958} Server: {fileID: 249891959} - ClientChangeScene: + DontDestroy: 1 + _clientChangeScene: m_PersistentCalls: m_Calls: [] - ClientSceneChanged: + _clientSceneChanged: m_PersistentCalls: m_Calls: [] - ServerChangeScene: + _serverChangeScene: m_PersistentCalls: m_Calls: [] - ServerSceneChanged: + _serverSceneChanged: m_PersistentCalls: m_Calls: [] --- !u!114 &249891961 @@ -320,10 +322,10 @@ MonoBehaviour: m_EditorClassIdentifier: Client: {fileID: 249891958} NetworkSceneManager: {fileID: 249891960} - Spawned: + _spawned: m_PersistentCalls: m_Calls: [] - UnSpawned: + _unSpawned: m_PersistentCalls: m_Calls: [] spawnPrefabs: @@ -342,12 +344,13 @@ MonoBehaviour: m_EditorClassIdentifier: Server: {fileID: 249891959} NetworkSceneManager: {fileID: 249891960} - Spawned: + _spawned: m_PersistentCalls: m_Calls: [] - UnSpawned: + _unSpawned: m_PersistentCalls: m_Calls: [] + InterestManager: {fileID: 0} --- !u!114 &249891963 MonoBehaviour: m_ObjectHideFlags: 0 @@ -361,26 +364,30 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Levels: - - Name: NetworkIdentity + - Name: NetworkClient level: 2 - - Name: ClientObjectManager + - Name: NetworkTime level: 2 - - Name: ServerObjectManager + - Name: NetworkSceneManager level: 2 - - Name: NetworkClient + - Name: NetworkIdentity level: 2 - - Name: NetworkTime + - Name: ClientObjectManager level: 2 - Name: NetworkServer level: 2 - - Name: NetworkSceneManager - level: 2 - Name: NetworkBehaviour level: 2 + - Name: CharacterSpawner + level: 2 + - Name: ServerObjectManager + level: 2 - Name: PlayerSpawner level: 2 - Name: RemoteCallHelper level: 2 + - Name: NetworkBehaviourInspector + level: 2 - Name: EnterPlayModeSettingsCheck level: 2 - Name: NetworkScenePostProcess @@ -413,8 +420,6 @@ MonoBehaviour: level: 2 - Name: WeaverTests level: 4 - - Name: NetworkBehaviourInspector - level: 2 - Name: ChatWindow level: 2 - Name: AdditiveNetworkManager @@ -431,6 +436,10 @@ MonoBehaviour: level: 2 - Name: Tests level: 4 + - Name: WelcomeWindow + level: 2 + - Name: NetworkPlayer + level: 2 --- !u!1 &283483315 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Tests/Editor/NetworkIdentityCallbackTests.cs b/Assets/Tests/Editor/NetworkIdentityCallbackTests.cs index 7e740f4108a..563895a0a6d 100644 --- a/Assets/Tests/Editor/NetworkIdentityCallbackTests.cs +++ b/Assets/Tests/Editor/NetworkIdentityCallbackTests.cs @@ -14,41 +14,6 @@ public class NetworkIdentityCallbackTests { #region test components - class CheckObserverExceptionNetworkBehaviour : NetworkVisibility - { - public int called; - public INetworkPlayer valuePassed; - public override void OnRebuildObservers(HashSet observers, bool initialize) { } - public override bool OnCheckObserver(INetworkPlayer player) - { - ++called; - valuePassed = player; - throw new Exception("some exception"); - } - } - - class CheckObserverTrueNetworkBehaviour : NetworkVisibility - { - public int called; - public override void OnRebuildObservers(HashSet observers, bool initialize) { } - public override bool OnCheckObserver(INetworkPlayer player) - { - ++called; - return true; - } - } - - class CheckObserverFalseNetworkBehaviour : NetworkVisibility - { - public int called; - public override void OnRebuildObservers(HashSet observers, bool initialize) { } - public override bool OnCheckObserver(INetworkPlayer player) - { - ++called; - return false; - } - } - class SerializeTest1NetworkBehaviour : NetworkBehaviour { public int value; @@ -105,22 +70,6 @@ public override void OnDeserialize(NetworkReader reader, bool initialState) } } - class RebuildObserversNetworkBehaviour : NetworkVisibility - { - public INetworkPlayer observer; - public override bool OnCheckObserver(INetworkPlayer player) { return true; } - public override void OnRebuildObservers(HashSet observers, bool initialize) - { - observers.Add(observer); - } - } - - class RebuildEmptyObserversNetworkBehaviour : NetworkVisibility - { - public override bool OnCheckObserver(INetworkPlayer player) { return true; } - public override void OnRebuildObservers(HashSet observers, bool initialize) { } - } - #endregion GameObject gameObject; @@ -262,26 +211,6 @@ public void SetOverrideClientOwner() Assert.That(identity.ConnectionToClient, Is.EqualTo(original)); } - [Test] - public void RemoveObserverInternal() - { - // call OnStartServer so that observers dict is created - identity.StartServer(); - - // add an observer connection - INetworkPlayer player = Substitute.For(); - identity.observers.Add(player); - - INetworkPlayer player2 = Substitute.For(); - // RemoveObserverInternal with invalid connection should do nothing - identity.RemoveObserverInternal(player2); - Assert.That(identity.observers, Is.EquivalentTo(new[] { player })); - - // RemoveObserverInternal with existing connection should remove it - identity.RemoveObserverInternal(player); - Assert.That(identity.observers, Is.Empty); - } - [Test] public void AssignSceneID() { @@ -466,48 +395,6 @@ public void NotifyAuthorityCallsOnStartStopAuthority() stopAuthFunc.Received(1).Invoke(); } - [Test] - public void OnCheckObserverCatchesException() - { - // add component - gameObject.AddComponent(); - - var connection = new NetworkPlayer(tconn42); - - // should catch the exception internally and not throw it - Assert.Throws(() => - { - identity.OnCheckObserver(connection); - }); - } - - [Test] - public void OnCheckObserverTrue() - { - // create a networkidentity with a component that returns true - // result should still be true. - var gameObjectTrue = new GameObject(); - NetworkIdentity identityTrue = gameObjectTrue.AddComponent(); - CheckObserverTrueNetworkBehaviour compTrue = gameObjectTrue.AddComponent(); - var connection = new NetworkPlayer(tconn42); - Assert.That(identityTrue.OnCheckObserver(connection), Is.True); - Assert.That(compTrue.called, Is.EqualTo(1)); - } - - [Test] - public void OnCheckObserverFalse() - { - // create a networkidentity with a component that returns true and - // one component that returns false. - // result should still be false if any one returns false. - var gameObjectFalse = new GameObject(); - NetworkIdentity identityFalse = gameObjectFalse.AddComponent(); - CheckObserverFalseNetworkBehaviour compFalse = gameObjectFalse.AddComponent(); - var connection = new NetworkPlayer(tconn42); - Assert.That(identityFalse.OnCheckObserver(connection), Is.False); - Assert.That(compFalse.called, Is.EqualTo(1)); - } - [Test] public void OnSerializeAllSafely() { @@ -661,43 +548,6 @@ public void OnStopServerEx() }); } - [Test] - public void AddObserver() - { - identity.Server = server; - // create some connections - var connection1 = new NetworkPlayer(tconn42); - var connection2 = new NetworkPlayer(tconn43); - - // call OnStartServer so that observers dict is created - identity.StartServer(); - - // call AddObservers - identity.AddObserver(connection1); - identity.AddObserver(connection2); - Assert.That(identity.observers, Is.EquivalentTo(new[] { connection1, connection2 })); - - // adding a duplicate connectionId shouldn't overwrite the original - identity.AddObserver(connection1); - Assert.That(identity.observers, Is.EquivalentTo(new[] { connection1, connection2 })); - } - - [Test] - public void ClearObservers() - { - // call OnStartServer so that observers dict is created - identity.StartServer(); - - // add some observers - identity.observers.Add(new NetworkPlayer(tconn42)); - identity.observers.Add(new NetworkPlayer(tconn43)); - - // call ClearObservers - identity.ClearObservers(); - Assert.That(identity.observers.Count, Is.EqualTo(0)); - } - - [Test] public void Reset() { @@ -705,7 +555,6 @@ public void Reset() identity.StartServer(); identity.ConnectionToClient = new NetworkPlayer(tconn42); identity.ConnectionToServer = new NetworkPlayer(tconn43); - identity.observers.Add(new NetworkPlayer(tconn42)); // mark for reset and reset identity.Reset(); @@ -713,112 +562,5 @@ public void Reset() Assert.That(identity.ConnectionToClient, Is.Null); Assert.That(identity.ConnectionToServer, Is.Null); } - - [Test] - public void GetNewObservers() - { - // add components - RebuildObserversNetworkBehaviour comp = gameObject.AddComponent(); - comp.observer = new NetworkPlayer(tconn42); - - // get new observers - var observers = new HashSet(); - bool result = identity.GetNewObservers(observers, true); - Assert.That(result, Is.True); - Assert.That(observers.Count, Is.EqualTo(1)); - Assert.That(observers.Contains(comp.observer), Is.True); - } - - [Test] - public void GetNewObserversClearsHashSet() - { - // get new observers. no observer components so it should just clear - // it and not do anything else - var observers = new HashSet - { - new NetworkPlayer(tconn42) - }; - identity.GetNewObservers(observers, true); - Assert.That(observers.Count, Is.EqualTo(0)); - } - - [Test] - public void GetNewObserversFalseIfNoComponents() - { - // get new observers. no observer components so it should be false - var observers = new HashSet(); - bool result = identity.GetNewObservers(observers, true); - Assert.That(result, Is.False); - } - - // RebuildObservers should always add the own ready connection - // (if any). fixes https://github.com/vis2k/Mirror/issues/692 - [Test] - public void RebuildObserversOnlyAddsOwnPlayerIfReady() - { - // add at least one observers component, otherwise it will just add - // all server connections - gameObject.AddComponent(); - - // add own player connection that isn't ready - (_, NetworkPlayer connection) = PipedConnections(); - identity.ConnectionToClient = connection; - - // call OnStartServer so that observers dict is created - identity.StartServer(); - - // rebuild shouldn't add own player because conn wasn't set ready - identity.RebuildObservers(true); - Assert.That(identity.observers, Does.Not.Contains(identity.ConnectionToClient)); - } - - [Test] - public void RebuildObserversAddsReadyConnectionsIfImplemented() - { - - // add a proximity checker - // one with a ready connection, one with no ready connection, one with null connection - RebuildObserversNetworkBehaviour comp = gameObject.AddComponent(); - comp.observer = Substitute.For(); - comp.observer.IsReady.Returns(true); - - // rebuild observers should add all component's ready observers - identity.RebuildObservers(true); - Assert.That(identity.observers, Is.EquivalentTo(new[] { comp.observer })); - } - - - [Test] - public void RebuildObserversDoesntAddNotReadyConnectionsIfImplemented() - { - // add a proximity checker - // one with a ready connection, one with no ready connection, one with null connection - RebuildObserversNetworkBehaviour comp = gameObject.AddComponent(); - comp.observer = Substitute.For(); - comp.observer.IsReady.Returns(false); - - // rebuild observers should add all component's ready observers - identity.RebuildObservers(true); - Assert.That(identity.observers, Is.Empty); - } - - [Test] - public void RebuildObserversAddsReadyServerConnectionsIfNotImplemented() - { - INetworkPlayer readyConnection = Substitute.For(); - readyConnection.IsReady.Returns(true); - INetworkPlayer notReadyConnection = Substitute.For(); - notReadyConnection.IsReady.Returns(false); - - // add some server connections - server.Players.Add(readyConnection); - server.Players.Add(notReadyConnection); - - // rebuild observers should add all ready server connections - // because no component implements OnRebuildObservers - identity.RebuildObservers(true); - Assert.That(identity.observers, Is.EquivalentTo(new[] { readyConnection })); - } - } } diff --git a/Assets/Tests/Performance/Runtime/NetworkWriter/NetworkIdentityPerformance.cs b/Assets/Tests/Performance/Runtime/NetworkWriter/NetworkIdentityPerformance.cs index 97d46e00664..df180027eb9 100644 --- a/Assets/Tests/Performance/Runtime/NetworkWriter/NetworkIdentityPerformance.cs +++ b/Assets/Tests/Performance/Runtime/NetworkWriter/NetworkIdentityPerformance.cs @@ -29,7 +29,6 @@ public void SetUp() gameObject = new GameObject(); identity = gameObject.AddComponent(); identity.ConnectionToClient = Substitute.For(); - identity.observers.Add(identity.ConnectionToClient); health = gameObject.AddComponent(); health.syncMode = SyncMode.Owner; health.syncInterval = 0f; diff --git a/Assets/Tests/Performance/Runtime/NetworkWriter/NetworkIdentityPerformanceWithMultipleBehaviour.cs b/Assets/Tests/Performance/Runtime/NetworkWriter/NetworkIdentityPerformanceWithMultipleBehaviour.cs index 46edbe855fb..5d0873a57e6 100644 --- a/Assets/Tests/Performance/Runtime/NetworkWriter/NetworkIdentityPerformanceWithMultipleBehaviour.cs +++ b/Assets/Tests/Performance/Runtime/NetworkWriter/NetworkIdentityPerformanceWithMultipleBehaviour.cs @@ -21,7 +21,6 @@ public void SetUp() gameObject = new GameObject(); identity = gameObject.AddComponent(); identity.ConnectionToClient = Substitute.For(); - identity.observers.Add(identity.ConnectionToClient); health = new Health[healthCount]; for (int i = 0; i < healthCount; i++) { diff --git a/Assets/Tests/Runtime/ClientServer/ClientServerSetup.cs b/Assets/Tests/Runtime/ClientServer/ClientServerSetup.cs index a6e063c947a..55f1cb1dfd7 100644 --- a/Assets/Tests/Runtime/ClientServer/ClientServerSetup.cs +++ b/Assets/Tests/Runtime/ClientServer/ClientServerSetup.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Linq; using Cysharp.Threading.Tasks; +using Mirage.InterestManagement; using UnityEngine; using UnityEngine.TestTools; @@ -18,6 +19,7 @@ public class ClientServerSetup where T : NetworkBehaviour protected NetworkServer server; protected NetworkSceneManager serverSceneManager; protected ServerObjectManager serverObjectManager; + protected InterestManager interestManager; protected GameObject serverPlayerGO; protected NetworkIdentity serverIdentity; protected T serverComponent; @@ -41,7 +43,7 @@ public virtual void ExtraSetup() { } [UnitySetUp] public IEnumerator Setup() => UniTask.ToCoroutine(async () => { - serverGo = new GameObject("server", typeof(NetworkSceneManager), typeof(ServerObjectManager), typeof(NetworkServer)); + serverGo = new GameObject("server", typeof(NetworkSceneManager), typeof(ServerObjectManager), typeof(NetworkServer), typeof(GlobalInterestManager)); clientGo = new GameObject("client", typeof(NetworkSceneManager), typeof(ClientObjectManager), typeof(NetworkClient)); testTransport = serverGo.AddComponent(); @@ -70,6 +72,9 @@ public IEnumerator Setup() => UniTask.ToCoroutine(async () => clientObjectManager.NetworkSceneManager = clientSceneManager; clientObjectManager.Start(); + interestManager = serverGo.GetComponent(); + interestManager.Start(); + ExtraSetup(); // create and register a prefab diff --git a/Assets/Tests/Runtime/ClientServer/ServerObjectManagerTests.cs b/Assets/Tests/Runtime/ClientServer/ServerObjectManagerTests.cs index 9376ad88b5d..1eb09e92d34 100644 --- a/Assets/Tests/Runtime/ClientServer/ServerObjectManagerTests.cs +++ b/Assets/Tests/Runtime/ClientServer/ServerObjectManagerTests.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Linq; using Cysharp.Threading.Tasks; +using Mirage.InterestManagement; using NSubstitute; using NUnit.Framework; using UnityEngine; @@ -20,6 +21,7 @@ public class ServerObjectManagerTest : ClientServerSetup public void SpawnObjectExposeExceptionTest() { var gameObject = new GameObject(); + gameObject.AddComponent(); ServerObjectManager comp = gameObject.AddComponent(); var obj = new GameObject(); diff --git a/Assets/Tests/Runtime/Host/NetworkIdentityTests.cs b/Assets/Tests/Runtime/Host/NetworkIdentityTests.cs index fd323cb685d..692816fbda3 100644 --- a/Assets/Tests/Runtime/Host/NetworkIdentityTests.cs +++ b/Assets/Tests/Runtime/Host/NetworkIdentityTests.cs @@ -235,29 +235,16 @@ public IEnumerator DestroyOwnedObjectsTest() => UniTask.ToCoroutine(async () => public class NetworkIdentityStartedTests : HostSetup { - #region SetUp - - GameObject gameObject; - NetworkIdentity testIdentity; - - public override void ExtraSetup() - { - gameObject = new GameObject(); - testIdentity = gameObject.AddComponent(); - server.Started.AddListener(() => serverObjectManager.Spawn(gameObject)); - } - - public override void ExtraTearDown() - { - Object.Destroy(gameObject); - } - - #endregion - [UnityTest] public IEnumerator ClientNotNullAfterSpawnInStarted() => UniTask.ToCoroutine(async () => { + var gameObject = new GameObject(); + NetworkIdentity testIdentity = gameObject.AddComponent(); + serverObjectManager.Spawn(gameObject); + await AsyncUtil.WaitUntilWithTimeout(() => testIdentity.Client == client); + + Object.Destroy(gameObject); }); } } diff --git a/Assets/Tests/Runtime/NetworkIdentityCallbackTests.cs b/Assets/Tests/Runtime/NetworkIdentityCallbackTests.cs deleted file mode 100644 index 346c3ee92a5..00000000000 --- a/Assets/Tests/Runtime/NetworkIdentityCallbackTests.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System.Collections.Generic; -using NSubstitute; -using NUnit.Framework; -using UnityEngine; -using static Mirage.Tests.LocalConnections; -using Object = UnityEngine.Object; - -namespace Mirage.Tests -{ - public class NetworkIdentityCallbackTests - { - #region test components - class RebuildEmptyObserversNetworkBehaviour : NetworkVisibility - { - public override bool OnCheckObserver(INetworkPlayer player) { return true; } - public override void OnRebuildObservers(HashSet observers, bool initialize) { } - } - - - #endregion - - GameObject gameObject; - NetworkIdentity identity; - private NetworkServer server; - private ServerObjectManager serverObjectManager; - private NetworkClient client; - private GameObject networkServerGameObject; - - IConnection tconn42; - IConnection tconn43; - - [SetUp] - public void SetUp() - { - networkServerGameObject = new GameObject(); - server = networkServerGameObject.AddComponent(); - serverObjectManager = networkServerGameObject.AddComponent(); - serverObjectManager.Server = server; - client = networkServerGameObject.AddComponent(); - - gameObject = new GameObject(); - identity = gameObject.AddComponent(); - identity.Server = server; - identity.ServerObjectManager = serverObjectManager; - - tconn42 = Substitute.For(); - tconn43 = Substitute.For(); - } - - [TearDown] - public void TearDown() - { - // set isServer is false. otherwise Destroy instead of - // DestroyImmediate is called internally, giving an error in Editor - Object.DestroyImmediate(gameObject); - Object.DestroyImmediate(networkServerGameObject); - } - - - [Test] - public void AddAllReadyServerConnectionsToObservers() - { - var connection1 = new NetworkPlayer(tconn42) { IsReady = true }; - var connection2 = new NetworkPlayer(tconn43) { IsReady = false }; - // add some server connections - server.Players.Add(connection1); - server.Players.Add(connection2); - - // add a host connection - (_, IConnection localConnection) = PipeConnection.CreatePipe(); - - server.SetLocalConnection(client, localConnection); - server.LocalPlayer.IsReady = true; - - // call OnStartServer so that observers dict is created - identity.StartServer(); - - // add all to observers. should have the two ready connections then. - identity.AddAllReadyServerConnectionsToObservers(); - Assert.That(identity.observers, Is.EquivalentTo(new[] { connection1, server.LocalPlayer })); - - // clean up - server.Disconnect(); - } - - // RebuildObservers should always add the own ready connection - // (if any). fixes https://github.com/vis2k/Mirror/issues/692 - [Test] - public void RebuildObserversAddsOwnReadyPlayer() - { - // add at least one observers component, otherwise it will just add - // all server connections - gameObject.AddComponent(); - - // add own player connection - (_, NetworkPlayer connection) = PipedConnections(); - connection.IsReady = true; - identity.ConnectionToClient = connection; - - // call OnStartServer so that observers dict is created - identity.StartServer(); - - // rebuild should at least add own ready player - identity.RebuildObservers(true); - Assert.That(identity.observers, Does.Contain(identity.ConnectionToClient)); - } - } -} diff --git a/Assets/Tests/Runtime/NetworkMatchCheckerTest.cs b/Assets/Tests/Runtime/NetworkMatchCheckerTest.cs deleted file mode 100644 index d53642b9e90..00000000000 --- a/Assets/Tests/Runtime/NetworkMatchCheckerTest.cs +++ /dev/null @@ -1,212 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using NUnit.Framework; -using UnityEngine; -using Object = UnityEngine.Object; - -namespace Mirage.Tests -{ - public class NetworkMatchCheckerTest - { - private GameObject serverGO; - private NetworkServer server; - private ServerObjectManager serverObjectManager; - private GameObject player1; - private GameObject player2; - private GameObject player3; - private NetworkMatchChecker player1MatchChecker; - private NetworkMatchChecker player2MatchChecker; - private NetworkPlayer player1Connection; - private NetworkPlayer player2Connection; - private NetworkPlayer player3Connection; - private Dictionary> matchPlayers; - - [SetUp] - public void Setup() - { - serverGO = new GameObject("Network Server", typeof(LoopbackTransport), typeof(NetworkServer), typeof(ServerObjectManager)); - - server = serverGO.GetComponent(); - serverObjectManager = serverGO.GetComponent(); - serverObjectManager.Server = server; - - player1 = new GameObject("TestPlayer1", typeof(NetworkIdentity), typeof(NetworkMatchChecker)); - player2 = new GameObject("TestPlayer2", typeof(NetworkIdentity), typeof(NetworkMatchChecker)); - player3 = new GameObject("TestPlayer3", typeof(NetworkIdentity)); - - - player1.GetComponent().Server = server; - player1.GetComponent().ServerObjectManager = serverObjectManager; - player2.GetComponent().Server = server; - player2.GetComponent().ServerObjectManager = serverObjectManager; - player3.GetComponent().Server = server; - player3.GetComponent().ServerObjectManager = serverObjectManager; - - player1MatchChecker = player1.GetComponent(); - player2MatchChecker = player2.GetComponent(); - - - player1Connection = CreateNetworkConnection(player1); - player2Connection = CreateNetworkConnection(player2); - player3Connection = CreateNetworkConnection(player3); - Dictionary> g = GetMatchPlayersDictionary(); - matchPlayers = g; - } - - static Dictionary> GetMatchPlayersDictionary() - { - Type type = typeof(NetworkMatchChecker); - FieldInfo fieldInfo = type.GetField("matchPlayers", BindingFlags.Static | BindingFlags.NonPublic); - return (Dictionary>)fieldInfo.GetValue(null); - } - - static NetworkPlayer CreateNetworkConnection(GameObject player) - { - (IConnection conn1, IConnection _) = PipeConnection.CreatePipe(); - - var connection = new NetworkPlayer(conn1) - { - Identity = player.GetComponent() - }; - connection.Identity.ConnectionToClient = connection; - connection.IsReady = true; - return connection; - } - - [TearDown] - public void TearDown() - { - Object.DestroyImmediate(player1); - Object.DestroyImmediate(player2); - Object.DestroyImmediate(player3); - - Object.DestroyImmediate(serverGO); - matchPlayers.Clear(); - matchPlayers = null; - } - - static void SetMatchId(NetworkMatchChecker target, Guid guid) - { - // set using reflection so bypass property - FieldInfo field = typeof(NetworkMatchChecker).GetField("currentMatch", BindingFlags.Instance | BindingFlags.NonPublic); - field.SetValue(target, guid); - } - - [Test] - public void OnCheckObserverShouldBeTrueForSameMatchId() - { - string guid = Guid.NewGuid().ToString(); - - SetMatchId(player1MatchChecker, new Guid(guid)); - SetMatchId(player2MatchChecker, new Guid(guid)); - - bool player1Visable = player1MatchChecker.OnCheckObserver(player1Connection); - Assert.IsTrue(player1Visable); - - bool player2Visable = player1MatchChecker.OnCheckObserver(player2Connection); - Assert.IsTrue(player2Visable); - } - - [Test] - public void OnCheckObserverShouldBeFalseForDifferentMatchId() - { - string guid1 = Guid.NewGuid().ToString(); - string guid2 = Guid.NewGuid().ToString(); - - SetMatchId(player1MatchChecker, new Guid(guid1)); - SetMatchId(player2MatchChecker, new Guid(guid2)); - - bool player1VisableToPlayer1 = player1MatchChecker.OnCheckObserver(player1Connection); - Assert.IsTrue(player1VisableToPlayer1); - - bool player2VisableToPlayer1 = player1MatchChecker.OnCheckObserver(player2Connection); - Assert.IsFalse(player2VisableToPlayer1); - - - bool player1VisableToPlayer2 = player2MatchChecker.OnCheckObserver(player1Connection); - Assert.IsFalse(player1VisableToPlayer2); - - bool player2VisableToPlayer2 = player2MatchChecker.OnCheckObserver(player2Connection); - Assert.IsTrue(player2VisableToPlayer2); - } - - [Test] - public void OnCheckObserverShouldBeFalseIfObjectDoesNotHaveNetworkMatchChecker() - { - string guid = Guid.NewGuid().ToString(); - - SetMatchId(player1MatchChecker, new Guid(guid)); - - bool player3Visable = player1MatchChecker.OnCheckObserver(player3Connection); - Assert.IsFalse(player3Visable); - } - - [Test] - public void OnCheckObserverShouldBeFalseForEmptyGuid() - { - string guid = Guid.Empty.ToString(); - - SetMatchId(player1MatchChecker, new Guid(guid)); - SetMatchId(player2MatchChecker, new Guid(guid)); - - bool player1Visable = player1MatchChecker.OnCheckObserver(player1Connection); - Assert.IsFalse(player1Visable); - - bool player2Visable = player1MatchChecker.OnCheckObserver(player2Connection); - Assert.IsFalse(player2Visable); - } - - [Test] - public void SettingMatchIdShouldRebuildObservers() - { - string guidMatch1 = Guid.NewGuid().ToString(); - - // make players join same match - player1MatchChecker.MatchId = new Guid(guidMatch1); - player2MatchChecker.MatchId = new Guid(guidMatch1); - - // check player1's observers contains player 2 - Assert.That(player1MatchChecker.Identity.observers, Contains.Item(player2MatchChecker.ConnectionToClient)); - // check player2's observers contains player 1 - Assert.That(player2MatchChecker.Identity.observers, Contains.Item(player1MatchChecker.ConnectionToClient)); - } - - [Test] - public void ChangingMatchIdShouldRebuildObservers() - { - string guidMatch1 = Guid.NewGuid().ToString(); - string guidMatch2 = Guid.NewGuid().ToString(); - - // make players join same match - player1MatchChecker.MatchId = new Guid(guidMatch1); - player2MatchChecker.MatchId = new Guid(guidMatch1); - - // make player2 join different match - player2MatchChecker.MatchId = new Guid(guidMatch2); - - // check player1's observers does NOT contain player 2 - Assert.That(player1MatchChecker.Identity.observers, !Contains.Item(player2MatchChecker.ConnectionToClient)); - // check player2's observers does NOT contain player 1 - Assert.That(player2MatchChecker.Identity.observers, !Contains.Item(player1MatchChecker.ConnectionToClient)); - } - - [Test] - public void ClearingMatchIdShouldRebuildObservers() - { - string guidMatch1 = Guid.NewGuid().ToString(); - - // make players join same match - player1MatchChecker.MatchId = new Guid(guidMatch1); - player2MatchChecker.MatchId = new Guid(guidMatch1); - - // make player 2 leave match - player2MatchChecker.MatchId = Guid.Empty; - - // check player1's observers does NOT contain player 2 - Assert.That(player1MatchChecker.Identity.observers, !Contains.Item(player2MatchChecker.ConnectionToClient)); - // check player2's observers does NOT contain player 1 - Assert.That(player2MatchChecker.Identity.observers, !Contains.Item(player1MatchChecker.ConnectionToClient)); - } - } -}