InterestManagement.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. // interest management component for custom solutions like
  2. // distance based, spatial hashing, raycast based, etc.
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. namespace Mirror
  6. {
  7. [DisallowMultipleComponent]
  8. [HelpURL("https://mirror-networking.gitbook.io/docs/guides/interest-management")]
  9. public abstract class InterestManagement : InterestManagementBase
  10. {
  11. // allocate newObservers helper HashSet
  12. readonly HashSet<NetworkConnectionToClient> newObservers =
  13. new HashSet<NetworkConnectionToClient>();
  14. // rebuild observers for the given NetworkIdentity.
  15. // Server will automatically spawn/despawn added/removed ones.
  16. // newObservers: cached hashset to put the result into
  17. // initialize: true if being rebuilt for the first time
  18. //
  19. // IMPORTANT:
  20. // => global rebuild would be more simple, BUT
  21. // => local rebuild is way faster for spawn/despawn because we can
  22. // simply rebuild a select NetworkIdentity only
  23. // => having both .observers and .observing is necessary for local
  24. // rebuilds
  25. //
  26. // in other words, this is the perfect solution even though it's not
  27. // completely simple (due to .observers & .observing).
  28. //
  29. // Mirror maintains .observing automatically in the background. best of
  30. // both worlds without any worrying now!
  31. public abstract void OnRebuildObservers(NetworkIdentity identity, HashSet<NetworkConnectionToClient> newObservers);
  32. // helper function to trigger a full rebuild.
  33. // most implementations should call this in a certain interval.
  34. // some might call this all the time, or only on team changes or
  35. // scene changes and so on.
  36. //
  37. // IMPORTANT: check if NetworkServer.active when using Update()!
  38. [ServerCallback]
  39. protected void RebuildAll()
  40. {
  41. foreach (NetworkIdentity identity in NetworkServer.spawned.Values)
  42. {
  43. NetworkServer.RebuildObservers(identity, false);
  44. }
  45. }
  46. public override void Rebuild(NetworkIdentity identity, bool initialize)
  47. {
  48. // clear newObservers hashset before using it
  49. newObservers.Clear();
  50. // not force hidden?
  51. if (identity.visible != Visibility.ForceHidden)
  52. {
  53. OnRebuildObservers(identity, newObservers);
  54. }
  55. // IMPORTANT: AFTER rebuilding add own player connection in any case
  56. // to ensure player always sees himself no matter what.
  57. // -> OnRebuildObservers might clear observers, so we need to add
  58. // the player's own connection AFTER. 100% fail safe.
  59. // -> fixes https://github.com/vis2k/Mirror/issues/692 where a
  60. // player might teleport out of the ProximityChecker's cast,
  61. // losing the own connection as observer.
  62. if (identity.connectionToClient != null)
  63. {
  64. newObservers.Add(identity.connectionToClient);
  65. }
  66. bool changed = false;
  67. // add all newObservers that aren't in .observers yet
  68. foreach (NetworkConnectionToClient conn in newObservers)
  69. {
  70. // only add ready connections.
  71. // otherwise the player might not be in the world yet or anymore
  72. if (conn != null && conn.isReady)
  73. {
  74. if (initialize || !identity.observers.ContainsKey(conn.connectionId))
  75. {
  76. // new observer
  77. conn.AddToObserving(identity);
  78. // Debug.Log($"New Observer for {gameObject} {conn}");
  79. changed = true;
  80. }
  81. }
  82. }
  83. // remove all old .observers that aren't in newObservers anymore
  84. foreach (NetworkConnectionToClient conn in identity.observers.Values)
  85. {
  86. if (!newObservers.Contains(conn))
  87. {
  88. // removed observer
  89. conn.RemoveFromObserving(identity, false);
  90. // Debug.Log($"Removed Observer for {gameObject} {conn}");
  91. changed = true;
  92. }
  93. }
  94. // copy new observers to observers
  95. if (changed)
  96. {
  97. identity.observers.Clear();
  98. foreach (NetworkConnectionToClient conn in newObservers)
  99. {
  100. if (conn != null && conn.isReady)
  101. identity.observers.Add(conn.connectionId, conn);
  102. }
  103. }
  104. // special case for host mode: we use SetHostVisibility to hide
  105. // NetworkIdentities that aren't in observer range from host.
  106. // this is what games like Dota/Counter-Strike do too, where a host
  107. // does NOT see all players by default. they are in memory, but
  108. // hidden to the host player.
  109. //
  110. // this code is from UNET, it's a bit strange but it works:
  111. // * it hides newly connected identities in host mode
  112. // => that part was the intended behaviour
  113. // * it hides ALL NetworkIdentities in host mode when the host
  114. // connects but hasn't selected a character yet
  115. // => this only works because we have no .localConnection != null
  116. // check. at this stage, localConnection is null because
  117. // StartHost starts the server first, then calls this code,
  118. // then starts the client and sets .localConnection. so we can
  119. // NOT add a null check without breaking host visibility here.
  120. // * it hides ALL NetworkIdentities in server-only mode because
  121. // observers never contain the 'null' .localConnection
  122. // => that was not intended, but let's keep it as it is so we
  123. // don't break anything in host mode. it's way easier than
  124. // iterating all identities in a special function in StartHost.
  125. if (initialize)
  126. {
  127. if (!newObservers.Contains(NetworkServer.localConnection))
  128. {
  129. SetHostVisibility(identity, false);
  130. }
  131. }
  132. }
  133. }
  134. }