NetworkTime.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  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/NetworkTime/NetworkTime.h>
  9. #include <Multiplayer/IMultiplayer.h>
  10. #include <Multiplayer/Components/NetBindComponent.h>
  11. #include <Multiplayer/Components/NetworkTransformComponent.h>
  12. #include <AzCore/Math/ShapeIntersection.h>
  13. #include <AzFramework/Visibility/IVisibilitySystem.h>
  14. #include <AzFramework/Visibility/EntityBoundsUnionBus.h>
  15. #include <AzFramework/Entity/EntityDebugDisplayBus.h>
  16. namespace Multiplayer
  17. {
  18. AZ_CVAR(float, sv_RewindVolumeExtrudeDistance, 50.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "The amount to increase rewind volume checks to account for fast moving entities");
  19. AZ_CVAR(bool, bg_RewindDebugDraw, false, nullptr, AZ::ConsoleFunctorFlags::Null, "If true enables debug draw of rewind operations");
  20. void NetworkTime::Reflect(AZ::ReflectContext* context)
  21. {
  22. if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  23. {
  24. behaviorContext->EBus<INetworkTimeRequestBus>("Network Time Requests")
  25. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  26. ->Attribute(AZ::Script::Attributes::Module, "multiplayer")
  27. ->Attribute(AZ::Script::Attributes::Category, "Multiplayer")
  28. ->Event("IsTimeRewound", &INetworkTimeRequestBus::Events::IsTimeRewound)
  29. ->Event("GetHostFrameId", &INetworkTimeRequestBus::Events::GetHostFrameId)
  30. ->Event("GetHostFrameId (Unaltered)", &INetworkTimeRequestBus::Events::GetUnalteredHostFrameId)
  31. ;
  32. }
  33. }
  34. NetworkTime::NetworkTime()
  35. {
  36. AZ::Interface<INetworkTime>::Register(this);
  37. INetworkTimeRequestBus::Handler::BusConnect();
  38. }
  39. NetworkTime::~NetworkTime()
  40. {
  41. INetworkTimeRequestBus::Handler::BusDisconnect();
  42. AZ::Interface<INetworkTime>::Unregister(this);
  43. }
  44. bool NetworkTime::IsTimeRewound() const
  45. {
  46. return m_rewindingConnectionId != AzNetworking::InvalidConnectionId;
  47. }
  48. HostFrameId NetworkTime::GetHostFrameId() const
  49. {
  50. return m_hostFrameId;
  51. }
  52. HostFrameId NetworkTime::GetUnalteredHostFrameId() const
  53. {
  54. return m_unalteredFrameId;
  55. }
  56. void NetworkTime::IncrementHostFrameId()
  57. {
  58. AZ_Assert(!IsTimeRewound(), "Incrementing the global application frameId is unsupported under a rewound time scope");
  59. ++m_unalteredFrameId;
  60. m_hostFrameId = m_unalteredFrameId;
  61. m_hostTimeMs = AZ::GetElapsedTimeMs();
  62. }
  63. AZ::TimeMs NetworkTime::GetHostTimeMs() const
  64. {
  65. return m_hostTimeMs;
  66. }
  67. float NetworkTime::GetHostBlendFactor() const
  68. {
  69. return m_hostBlendFactor;
  70. }
  71. AzNetworking::ConnectionId NetworkTime::GetRewindingConnectionId() const
  72. {
  73. return m_rewindingConnectionId;
  74. }
  75. void NetworkTime::ForceSetTime(HostFrameId frameId, AZ::TimeMs timeMs)
  76. {
  77. AZ_Assert(!IsTimeRewound(), "Forcibly setting network time is unsupported under a rewound time scope");
  78. m_unalteredFrameId = frameId;
  79. m_hostFrameId = frameId;
  80. m_hostTimeMs = timeMs;
  81. m_rewindingConnectionId = AzNetworking::InvalidConnectionId;
  82. }
  83. void NetworkTime::AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, float blendFactor, AzNetworking::ConnectionId rewindConnectionId)
  84. {
  85. m_hostFrameId = frameId;
  86. m_hostTimeMs = timeMs;
  87. m_hostBlendFactor = blendFactor;
  88. m_rewindingConnectionId = rewindConnectionId;
  89. }
  90. void NetworkTime::SyncEntitiesToRewindState(const AZ::Aabb& rewindVolume)
  91. {
  92. if (!IsTimeRewound())
  93. {
  94. // If we're not inside a rewind scope then reset any rewound state and exit
  95. ClearRewoundEntities();
  96. return;
  97. }
  98. // Since the vis system doesn't support rewound queries, first query with an expanded volume to catch any fast moving entities
  99. const AZ::Aabb expandedVolume = rewindVolume.GetExpanded(AZ::Vector3(sv_RewindVolumeExtrudeDistance));
  100. AzFramework::DebugDisplayRequests* debugDisplay = nullptr;
  101. if (bg_RewindDebugDraw)
  102. {
  103. AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
  104. AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, AzFramework::g_defaultSceneEntityDebugDisplayId);
  105. debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus);
  106. }
  107. if (debugDisplay)
  108. {
  109. debugDisplay->SetColor(AZ::Colors::Red);
  110. debugDisplay->DrawWireBox(expandedVolume.GetMin(), expandedVolume.GetMax());
  111. }
  112. NetworkEntityTracker* networkEntityTracker = GetNetworkEntityTracker();
  113. AzFramework::IEntityBoundsUnion* entityBoundsUnion = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get();
  114. AZ::Interface<AzFramework::IVisibilitySystem>::Get()->GetDefaultVisibilityScene()->Enumerate(expandedVolume,
  115. [this, debugDisplay, networkEntityTracker, entityBoundsUnion, rewindVolume](const AzFramework::IVisibilityScene::NodeData& nodeData)
  116. {
  117. m_rewoundEntities.reserve(m_rewoundEntities.size() + nodeData.m_entries.size());
  118. for (AzFramework::VisibilityEntry* visEntry : nodeData.m_entries)
  119. {
  120. if (visEntry->m_typeFlags & AzFramework::VisibilityEntry::TypeFlags::TYPE_Entity)
  121. {
  122. AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
  123. NetworkEntityHandle entityHandle(entity, networkEntityTracker);
  124. if (entityHandle.GetNetBindComponent() != nullptr)
  125. {
  126. const AZ::Aabb currentBounds = entityBoundsUnion->GetEntityWorldBoundsUnion(entity->GetId());
  127. const AZ::Vector3 currentCenter = currentBounds.GetCenter();
  128. NetworkTransformComponent* networkTransform = entity->template FindComponent<NetworkTransformComponent>();
  129. if (debugDisplay)
  130. {
  131. debugDisplay->SetColor(AZ::Colors::White);
  132. debugDisplay->DrawWireBox(currentBounds.GetMin(), currentBounds.GetMax());
  133. }
  134. if (networkTransform != nullptr)
  135. {
  136. // Get the rewound position for target host frame ID plus the one preceding it for potential lerp
  137. AZ::Vector3 rewindCenter = networkTransform->GetTranslation();
  138. const AZ::Vector3 rewindCenterPrevious = networkTransform->GetTranslationPrevious();
  139. const float blendFactor = GetNetworkTime()->GetHostBlendFactor();
  140. if (!AZ::IsClose(blendFactor, 1.0f) && !rewindCenter.IsClose(rewindCenterPrevious))
  141. {
  142. // If we have a blend factor, lerp the translation for accuracy
  143. rewindCenter = rewindCenterPrevious.Lerp(rewindCenter, blendFactor);
  144. }
  145. const AZ::Vector3 rewindOffset = rewindCenter - currentCenter; // Compute offset between rewound and current positions
  146. const AZ::Aabb rewoundAabb = currentBounds.GetTranslated(rewindOffset); // Apply offset to the entity aabb
  147. if (debugDisplay)
  148. {
  149. debugDisplay->SetColor(AZ::Colors::Grey);
  150. debugDisplay->DrawWireBox(rewoundAabb.GetMin(), rewoundAabb.GetMax());
  151. }
  152. if (AZ::ShapeIntersection::Overlaps(rewoundAabb, rewindVolume)) // Validate the rewound aabb intersects our rewind volume
  153. {
  154. m_rewoundEntities.push_back(entityHandle);
  155. entityHandle.GetNetBindComponent()->NotifySyncRewindState();
  156. }
  157. }
  158. }
  159. }
  160. }
  161. });
  162. }
  163. void NetworkTime::ClearRewoundEntities()
  164. {
  165. AZ_Assert(!IsTimeRewound(), "Cannot clear rewound entity state while still within scoped rewind");
  166. for (NetworkEntityHandle entityHandle : m_rewoundEntities)
  167. {
  168. if (NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent())
  169. {
  170. netBindComponent->NotifySyncRewindState();
  171. }
  172. }
  173. m_rewoundEntities.clear();
  174. }
  175. }