3
0

ServerToClientReplicationWindow.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Source/ReplicationWindows/ServerToClientReplicationWindow.h>
  9. #include <Source/AutoGen/Multiplayer.AutoPackets.h>
  10. #include <Multiplayer/Components/NetBindComponent.h>
  11. #include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
  12. #include <AzFramework/Visibility/IVisibilitySystem.h>
  13. #include <AzCore/Component/TransformBus.h>
  14. #include <AzCore/Console/ILogger.h>
  15. #include <AzCore/std/sort.h>
  16. namespace Multiplayer
  17. {
  18. AZ_CVAR(bool, sv_ReplicateServerProxies, true, nullptr, AZ::ConsoleFunctorFlags::Null, "Enable sending of ServerProxy entities to clients");
  19. AZ_CVAR(uint32_t, sv_MaxEntitiesToTrackReplication, 512, nullptr, AZ::ConsoleFunctorFlags::Null, "The default max number of entities to track for replication");
  20. AZ_CVAR(uint32_t, sv_MinEntitiesToReplicate, 128, nullptr, AZ::ConsoleFunctorFlags::Null, "The default min number of entities to replicate to a client connection");
  21. AZ_CVAR(uint32_t, sv_MaxEntitiesToReplicate, 256, nullptr, AZ::ConsoleFunctorFlags::Null, "The default max number of entities to replicate to a client connection");
  22. AZ_CVAR(uint32_t, sv_PacketsToIntegrateQos, 1000, nullptr, AZ::ConsoleFunctorFlags::Null, "The number of packets to accumulate before updating connection quality of service metrics");
  23. AZ_CVAR(float, sv_BadConnectionThreshold, 0.25f, nullptr, AZ::ConsoleFunctorFlags::Null, "The loss percentage beyond which we consider our network bad");
  24. AZ_CVAR(float, sv_ClientAwarenessRadius, 500.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "The maximum distance entities can be from the client and still be relevant");
  25. const char* GetConnectionStateString(bool isPoor)
  26. {
  27. return isPoor ? "poor" : "ideal";
  28. }
  29. ServerToClientReplicationWindow::PrioritizedReplicationCandidate::PrioritizedReplicationCandidate
  30. (
  31. const ConstNetworkEntityHandle& entityHandle,
  32. float priority
  33. )
  34. : m_entityHandle(entityHandle)
  35. , m_priority(priority)
  36. {
  37. ;
  38. }
  39. bool ServerToClientReplicationWindow::PrioritizedReplicationCandidate::operator <(const PrioritizedReplicationCandidate& rhs) const
  40. {
  41. return m_priority > rhs.m_priority; // sort lowest priority front
  42. }
  43. bool ServerToClientReplicationWindow::PrioritizedReplicationCandidate::operator >(const PrioritizedReplicationCandidate& rhs) const
  44. {
  45. return m_priority < rhs.m_priority;
  46. }
  47. ServerToClientReplicationWindow::ServerToClientReplicationWindow(NetworkEntityHandle controlledEntity, AzNetworking::IConnection* connection)
  48. : m_controlledEntity(controlledEntity)
  49. , m_connection(connection)
  50. , m_lastCheckedSentPackets(connection->GetMetrics().m_packetsSent)
  51. , m_lastCheckedLostPackets(connection->GetMetrics().m_packetsLost)
  52. {
  53. AZ::Entity* entity = m_controlledEntity.GetEntity();
  54. AZ_Assert(entity, "Invalid controlled entity provided to replication window");
  55. m_controlledEntityTransform = entity ? entity->GetTransform() : nullptr;
  56. AZ_Assert(m_controlledEntityTransform, "Controlled player entity must have a transform");
  57. }
  58. bool ServerToClientReplicationWindow::ReplicationSetUpdateReady()
  59. {
  60. // if we don't have a controlled entity anymore, don't send updates (validate this)
  61. if (!m_controlledEntity.Exists())
  62. {
  63. m_replicationSet.clear();
  64. }
  65. return true;
  66. }
  67. const ReplicationSet& ServerToClientReplicationWindow::GetReplicationSet() const
  68. {
  69. return m_replicationSet;
  70. }
  71. uint32_t ServerToClientReplicationWindow::GetMaxProxyEntityReplicatorSendCount() const
  72. {
  73. return m_isPoorConnection ? sv_MinEntitiesToReplicate : sv_MaxEntitiesToReplicate;
  74. }
  75. bool ServerToClientReplicationWindow::IsInWindow([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle, NetEntityRole& outNetworkRole) const
  76. {
  77. AZ_Assert(false, "IsInWindow should not be called on the ServerToClientReplicationWindow");
  78. outNetworkRole = NetEntityRole::InvalidRole;
  79. return false;
  80. }
  81. void ServerToClientReplicationWindow::UpdateWindow()
  82. {
  83. // Clear the candidate queue, we're going to rebuild it
  84. ReplicationCandidateQueue::container_type clearQueueContainer;
  85. clearQueueContainer.reserve(sv_MaxEntitiesToTrackReplication);
  86. // Move the clearQueueContainer into the ReplicationCandidateQueue to maintain the reserved memory
  87. ReplicationCandidateQueue clearQueue(ReplicationCandidateQueue::value_compare{}, AZStd::move(clearQueueContainer));
  88. m_candidateQueue.swap(clearQueue);
  89. m_replicationSet.clear();
  90. NetBindComponent* netBindComponent = m_controlledEntity.GetNetBindComponent();
  91. if (!netBindComponent || !netBindComponent->HasController())
  92. {
  93. // If we don't have a controlled entity, or we no longer have control of the entity, don't run the update
  94. return;
  95. }
  96. EvaluateConnection();
  97. AZ::TransformInterface* transformInterface = m_controlledEntity.GetEntity()->GetTransform();
  98. const AZ::Vector3 controlledEntityPosition = transformInterface->GetWorldTranslation();
  99. AZStd::vector<AzFramework::VisibilityEntry*> gatheredEntries;
  100. AZ::Sphere awarenessSphere = AZ::Sphere(controlledEntityPosition, sv_ClientAwarenessRadius);
  101. AzFramework::IVisibilitySystem* visibilitySystem = AZ::Interface<AzFramework::IVisibilitySystem>::Get();
  102. if (visibilitySystem)
  103. {
  104. visibilitySystem->GetDefaultVisibilityScene()->Enumerate(
  105. awarenessSphere,
  106. [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData)
  107. {
  108. gatheredEntries.reserve(gatheredEntries.size() + nodeData.m_entries.size());
  109. for (AzFramework::VisibilityEntry* visEntry : nodeData.m_entries)
  110. {
  111. if (visEntry->m_typeFlags & AzFramework::VisibilityEntry::TypeFlags::TYPE_Entity)
  112. {
  113. gatheredEntries.push_back(visEntry);
  114. }
  115. }
  116. });
  117. }
  118. NetworkEntityTracker* networkEntityTracker = GetNetworkEntityTracker();
  119. IFilterEntityManager* filterEntityManager = AZ::Interface<IFilterEntityManager>::Get();
  120. // Add all the neighbours
  121. for (AzFramework::VisibilityEntry* visEntry : gatheredEntries)
  122. {
  123. AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
  124. NetworkEntityHandle entityHandle(entity, networkEntityTracker);
  125. if (entityHandle.GetNetBindComponent() == nullptr)
  126. {
  127. // Entity does not have netbinding, skip this entity
  128. continue;
  129. }
  130. if (filterEntityManager && filterEntityManager->IsEntityFiltered(entity, m_controlledEntity, m_connection->GetConnectionId()))
  131. {
  132. continue;
  133. }
  134. // We want to find the closest extent to the player and prioritize using that distance
  135. const AZ::Vector3 supportNormal = controlledEntityPosition - visEntry->m_boundingVolume.GetCenter();
  136. const AZ::Vector3 closestPosition = visEntry->m_boundingVolume.GetSupport(supportNormal);
  137. const float gatherDistanceSquared = controlledEntityPosition.GetDistanceSq(closestPosition);
  138. const float priority = (gatherDistanceSquared > 0.0f) ? 1.0f / gatherDistanceSquared : 0.0f;
  139. AddEntityToReplicationSet(entityHandle, priority, gatherDistanceSquared);
  140. }
  141. // Add in all entities that have forced relevancy
  142. const Multiplayer::NetEntityHandleSet& alwaysRelevantToClients = GetNetworkEntityManager()->GetAlwaysRelevantToClientsSet();
  143. for (const ConstNetworkEntityHandle& entityHandle : alwaysRelevantToClients)
  144. {
  145. if (entityHandle.Exists())
  146. {
  147. AZ_Assert(entityHandle.GetNetBindComponent()->IsNetEntityRoleAuthority(), "Encountered forced relevant entity that is not in an authority role");
  148. m_replicationSet[entityHandle] = { NetEntityRole::Client, 1.0f }; // Always replicate entities with forced relevancy
  149. }
  150. }
  151. // Add in Autonomous Entities
  152. // Note: Do not add any Client entities after this point, otherwise you stomp over the Autonomous mode
  153. m_replicationSet[m_controlledEntity] = { NetEntityRole::Autonomous, 1.0f }; // Always replicate autonomous entities
  154. auto* hierarchyComponent = m_controlledEntity.FindComponent<NetworkHierarchyRootComponent>();
  155. if (hierarchyComponent != nullptr)
  156. {
  157. UpdateHierarchyReplicationSet(m_replicationSet, *hierarchyComponent);
  158. }
  159. }
  160. AzNetworking::PacketId ServerToClientReplicationWindow::SendEntityUpdateMessages(NetworkEntityUpdateVector& entityUpdateVector)
  161. {
  162. MultiplayerPackets::EntityUpdates entityUpdatePacket;
  163. entityUpdatePacket.SetHostTimeMs(GetNetworkTime()->GetHostTimeMs());
  164. entityUpdatePacket.SetHostFrameId(GetNetworkTime()->GetHostFrameId());
  165. entityUpdatePacket.SetEntityMessages(entityUpdateVector);
  166. return m_connection->SendUnreliablePacket(entityUpdatePacket);
  167. }
  168. void ServerToClientReplicationWindow::SendEntityRpcs(NetworkEntityRpcVector& entityRpcVector, bool reliable)
  169. {
  170. MultiplayerPackets::EntityRpcs entityRpcsPacket;
  171. entityRpcsPacket.SetEntityRpcs(entityRpcVector);
  172. if (reliable)
  173. {
  174. m_connection->SendReliablePacket(entityRpcsPacket);
  175. }
  176. else
  177. {
  178. m_connection->SendUnreliablePacket(entityRpcsPacket);
  179. }
  180. }
  181. void ServerToClientReplicationWindow::SendEntityResets(const NetEntityIdSet& resetIds)
  182. {
  183. MultiplayerPackets::RequestReplicatorReset entityResetPacket;
  184. for (NetEntityId entityId : resetIds)
  185. {
  186. if (entityResetPacket.GetEntityIds().full())
  187. {
  188. m_connection->SendUnreliablePacket(entityResetPacket);
  189. entityResetPacket.ModifyEntityIds().clear();
  190. }
  191. entityResetPacket.ModifyEntityIds().push_back(entityId);
  192. }
  193. if (!entityResetPacket.GetEntityIds().empty())
  194. {
  195. m_connection->SendUnreliablePacket(entityResetPacket);
  196. }
  197. }
  198. void ServerToClientReplicationWindow::DebugDraw() const
  199. {
  200. //static const float BoundaryStripeHeight = 1.0f;
  201. //static const float BoundaryStripeSpacing = 0.5f;
  202. //static const int32_t BoundaryStripeCount = 10;
  203. //if (auto localEnt = m_ControlledEntity.lock())
  204. //{
  205. // auto* octreeNodeComponent = localEnt->FindComponent<OctreeNodeComponent::Server>();
  206. // AZ_Assert(octreeNodeComponent != nullptr, "Controlled entity does not have an octree node component");
  207. //
  208. // const float radius = octreeNodeComponent->GetAwarenessRadius();
  209. // Vec3 currPos = octreeNodeComponent->GetPosition();
  210. // currPos.z += BoundaryStripeHeight;
  211. //
  212. // for (int i = 0; i < BoundaryStripeCount; ++i)
  213. // {
  214. // draw.Circle(currPos, radius, DebugColors::green);
  215. // currPos.z +=BoundaryStripeSpacing;
  216. // }
  217. //}
  218. }
  219. bool ServerToClientReplicationWindow::AddEntity(AZ::Entity* entity)
  220. {
  221. ConstNetworkEntityHandle entityHandle(entity);
  222. if (IFilterEntityManager* filter = AZ::Interface<IFilterEntityManager>::Get())
  223. {
  224. if (filter->IsEntityFiltered(entity, m_controlledEntity, m_connection->GetConnectionId()))
  225. {
  226. return false;
  227. }
  228. }
  229. AZ::TransformInterface* transformInterface = entity->GetTransform();
  230. if (transformInterface != nullptr)
  231. {
  232. const AZ::Vector3 clientPosition = m_controlledEntityTransform->GetWorldTranslation();
  233. float distSq = clientPosition.GetDistanceSq(transformInterface->GetWorldTranslation());
  234. float awarenessSq = sv_ClientAwarenessRadius * sv_ClientAwarenessRadius;
  235. // Make sure we would be in the awareness radius
  236. if (distSq < awarenessSq)
  237. {
  238. AddEntityToReplicationSet(entityHandle, 1.0f, distSq);
  239. return true;
  240. }
  241. }
  242. return false;
  243. }
  244. void ServerToClientReplicationWindow::RemoveEntity(AZ::Entity* entity)
  245. {
  246. ConstNetworkEntityHandle entityHandle(entity);
  247. if (entityHandle.GetNetBindComponent() != nullptr)
  248. {
  249. m_replicationSet.erase(entityHandle);
  250. }
  251. }
  252. void ServerToClientReplicationWindow::EvaluateConnection()
  253. {
  254. const uint32_t newPacketsSent = m_connection->GetMetrics().m_packetsSent;
  255. const uint32_t packetSentDelta = newPacketsSent - m_lastCheckedSentPackets;
  256. if (packetSentDelta > sv_PacketsToIntegrateQos) // Just some threshold for having enough samples
  257. {
  258. const uint32_t newPacketsLost = m_connection->GetMetrics().m_packetsLost;
  259. const uint32_t packetLostDelta = newPacketsLost - m_lastCheckedLostPackets;
  260. const float packetLostPercent = float(packetLostDelta) / float(packetSentDelta);
  261. const bool isPoorConnection = (packetLostPercent > sv_BadConnectionThreshold);
  262. if (isPoorConnection != m_isPoorConnection)
  263. {
  264. m_isPoorConnection = isPoorConnection;
  265. AZLOG_INFO
  266. (
  267. "Connection# %u with entity %u quality state changed status from %s to %s",
  268. static_cast<uint32_t>(m_connection->GetConnectionId()),
  269. static_cast<uint32_t>(m_controlledEntity.GetNetEntityId()),
  270. GetConnectionStateString(!m_isPoorConnection),
  271. GetConnectionStateString(m_isPoorConnection)
  272. );
  273. }
  274. m_lastCheckedSentPackets = newPacketsSent;
  275. m_lastCheckedLostPackets = newPacketsLost;
  276. }
  277. }
  278. void ServerToClientReplicationWindow::AddEntityToReplicationSet(ConstNetworkEntityHandle& entityHandle, float priority, [[maybe_unused]] float distanceSquared)
  279. {
  280. // Assumption: the entity has been checked for filtering prior to this call.
  281. if (!sv_ReplicateServerProxies)
  282. {
  283. NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
  284. if ((netBindComponent != nullptr) && (netBindComponent->GetNetEntityRole() == NetEntityRole::Server))
  285. {
  286. // Proxy replication disabled
  287. return;
  288. }
  289. }
  290. const bool isQueueFull = (m_candidateQueue.size() >= sv_MaxEntitiesToTrackReplication); // See if have the maximum number of entities in our set
  291. const bool isInReplicationSet = m_replicationSet.find(entityHandle) != m_replicationSet.end();
  292. if (!isInReplicationSet)
  293. {
  294. if (isQueueFull) // If our set is full, then we need to remove the worst priority in our set
  295. {
  296. ConstNetworkEntityHandle removeEnt = m_candidateQueue.top().m_entityHandle;
  297. m_candidateQueue.pop();
  298. m_replicationSet.erase(removeEnt);
  299. }
  300. m_candidateQueue.push(PrioritizedReplicationCandidate(entityHandle, priority));
  301. m_replicationSet[entityHandle] = { NetEntityRole::Client, priority };
  302. }
  303. }
  304. void ServerToClientReplicationWindow::UpdateHierarchyReplicationSet(ReplicationSet& replicationSet, NetworkHierarchyRootComponent& hierarchyComponent)
  305. {
  306. INetworkEntityManager* networkEntityManager = AZ::Interface<INetworkEntityManager>::Get();
  307. AZ_Assert(networkEntityManager, "NetworkEntityManager must be created.");
  308. for (const AZ::Entity* controlledEntity : hierarchyComponent.GetHierarchicalEntities())
  309. {
  310. NetEntityId controlledNetEntitydId = networkEntityManager->GetNetEntityIdById(controlledEntity->GetId());
  311. AZ_Assert(controlledNetEntitydId != InvalidNetEntityId, "Unable to find the hierarchy entity in Network Entity Manager");
  312. ConstNetworkEntityHandle controlledEntityHandle = networkEntityManager->GetEntity(controlledNetEntitydId);
  313. AZ_Assert(controlledEntityHandle != nullptr, "We have lost a controlled entity unexpectedly");
  314. replicationSet[controlledEntityHandle] = { NetEntityRole::Autonomous, 1.0f };
  315. }
  316. }
  317. }