MultiplayerStats.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  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 <Multiplayer/MultiplayerMetrics.h>
  9. #include <Multiplayer/MultiplayerPerformanceStats.h>
  10. #include <Multiplayer/MultiplayerStats.h>
  11. namespace Multiplayer
  12. {
  13. MultiplayerStats::Metric::Metric()
  14. {
  15. AZStd::uninitialized_fill_n(m_callHistory.data(), RingbufferSamples, 0);
  16. AZStd::uninitialized_fill_n(m_byteHistory.data(), RingbufferSamples, 0);
  17. }
  18. void MultiplayerStats::ReserveComponentStats(NetComponentId netComponentId, uint16_t propertyCount, uint16_t rpcCount)
  19. {
  20. const uint16_t netComponentIndex = aznumeric_cast<uint16_t>(netComponentId);
  21. if (m_componentStats.size() <= netComponentIndex)
  22. {
  23. m_componentStats.resize(netComponentIndex + 1);
  24. }
  25. m_componentStats[netComponentIndex].m_propertyUpdatesSent.resize(propertyCount);
  26. m_componentStats[netComponentIndex].m_propertyUpdatesRecv.resize(propertyCount);
  27. m_componentStats[netComponentIndex].m_rpcsSent.resize(rpcCount);
  28. m_componentStats[netComponentIndex].m_rpcsRecv.resize(rpcCount);
  29. }
  30. void MultiplayerStats::RecordEntitySerializeStart(AzNetworking::SerializerMode mode, AZ::EntityId entityId, const char* entityName)
  31. {
  32. m_events.m_entitySerializeStart.Signal(mode, entityId, entityName);
  33. }
  34. void MultiplayerStats::RecordComponentSerializeEnd(AzNetworking::SerializerMode mode, NetComponentId netComponentId)
  35. {
  36. m_events.m_componentSerializeEnd.Signal(mode, netComponentId);
  37. }
  38. void MultiplayerStats::RecordEntitySerializeStop(AzNetworking::SerializerMode mode, AZ::EntityId entityId, const char* entityName)
  39. {
  40. m_events.m_entitySerializeStop.Signal(mode, entityId, entityName);
  41. }
  42. void MultiplayerStats::RecordPropertySent(NetComponentId netComponentId, PropertyIndex propertyId, uint32_t totalBytes)
  43. {
  44. const uint16_t netComponentIndex = aznumeric_cast<uint16_t>(netComponentId);
  45. const uint16_t propertyIndex = aznumeric_cast<uint16_t>(propertyId);
  46. if (m_componentStats[netComponentIndex].m_propertyUpdatesSent.size() > propertyIndex)
  47. {
  48. m_componentStats[netComponentIndex].m_propertyUpdatesSent[propertyIndex].m_totalCalls++;
  49. m_componentStats[netComponentIndex].m_propertyUpdatesSent[propertyIndex].m_totalBytes += totalBytes;
  50. m_componentStats[netComponentIndex].m_propertyUpdatesSent[propertyIndex].m_callHistory[m_recordMetricIndex]++;
  51. m_componentStats[netComponentIndex].m_propertyUpdatesSent[propertyIndex].m_byteHistory[m_recordMetricIndex] += totalBytes;
  52. }
  53. else
  54. {
  55. AZ_Warning("MultiplayerStats", false,
  56. "Component ID %u has fewer than %u sent propertyIndex. Mismatch by caller suspected.", netComponentIndex, propertyIndex);
  57. }
  58. m_events.m_propertySent.Signal(netComponentId, propertyId, totalBytes);
  59. }
  60. void MultiplayerStats::RecordPropertyReceived(NetComponentId netComponentId, PropertyIndex propertyId, uint32_t totalBytes)
  61. {
  62. const uint16_t netComponentIndex = aznumeric_cast<uint16_t>(netComponentId);
  63. const uint16_t propertyIndex = aznumeric_cast<uint16_t>(propertyId);
  64. if (m_componentStats[netComponentIndex].m_propertyUpdatesRecv.size() > propertyIndex)
  65. {
  66. m_componentStats[netComponentIndex].m_propertyUpdatesRecv[propertyIndex].m_totalCalls++;
  67. m_componentStats[netComponentIndex].m_propertyUpdatesRecv[propertyIndex].m_totalBytes += totalBytes;
  68. m_componentStats[netComponentIndex].m_propertyUpdatesRecv[propertyIndex].m_callHistory[m_recordMetricIndex]++;
  69. m_componentStats[netComponentIndex].m_propertyUpdatesRecv[propertyIndex].m_byteHistory[m_recordMetricIndex] += totalBytes;
  70. }
  71. else
  72. {
  73. AZ_Warning("MultiplayerStats", false,
  74. "Component ID %u has fewer than %u receive propertyIndex. Mismatch by caller suspected.", netComponentIndex, propertyIndex);
  75. }
  76. m_events.m_propertyReceived.Signal(netComponentId, propertyId, totalBytes);
  77. }
  78. void MultiplayerStats::RecordRpcSent(AZ::EntityId entityId, const char* entityName, NetComponentId netComponentId, RpcIndex rpcId, uint32_t totalBytes)
  79. {
  80. const uint16_t netComponentIndex = aznumeric_cast<uint16_t>(netComponentId);
  81. const uint16_t rpcIndex = aznumeric_cast<uint16_t>(rpcId);
  82. if (m_componentStats[netComponentIndex].m_rpcsSent.size() > rpcIndex)
  83. {
  84. m_componentStats[netComponentIndex].m_rpcsSent[rpcIndex].m_totalCalls++;
  85. m_componentStats[netComponentIndex].m_rpcsSent[rpcIndex].m_totalBytes += totalBytes;
  86. m_componentStats[netComponentIndex].m_rpcsSent[rpcIndex].m_callHistory[m_recordMetricIndex]++;
  87. m_componentStats[netComponentIndex].m_rpcsSent[rpcIndex].m_byteHistory[m_recordMetricIndex] += totalBytes;
  88. }
  89. else
  90. {
  91. AZ_Warning("MultiplayerStats", false,
  92. "Component ID %u has fewer than %u sent rpcIndex. Mismatch by caller suspected.", netComponentIndex, rpcIndex);
  93. }
  94. m_events.m_rpcSent.Signal(entityId, entityName, netComponentId, rpcId, totalBytes);
  95. }
  96. void MultiplayerStats::RecordRpcReceived(AZ::EntityId entityId, const char* entityName, NetComponentId netComponentId, RpcIndex rpcId, uint32_t totalBytes)
  97. {
  98. const uint16_t netComponentIndex = aznumeric_cast<uint16_t>(netComponentId);
  99. const uint16_t rpcIndex = aznumeric_cast<uint16_t>(rpcId);
  100. if (m_componentStats[netComponentIndex].m_rpcsRecv.size() > rpcIndex)
  101. {
  102. m_componentStats[netComponentIndex].m_rpcsRecv[rpcIndex].m_totalCalls++;
  103. m_componentStats[netComponentIndex].m_rpcsRecv[rpcIndex].m_totalBytes += totalBytes;
  104. m_componentStats[netComponentIndex].m_rpcsRecv[rpcIndex].m_callHistory[m_recordMetricIndex]++;
  105. m_componentStats[netComponentIndex].m_rpcsRecv[rpcIndex].m_byteHistory[m_recordMetricIndex] += totalBytes;
  106. }
  107. else
  108. {
  109. AZ_Warning("MultiplayerStats", false,
  110. "Component ID %u has fewer than %u receive rpcIndex. Mismatch by caller suspected.", netComponentIndex, rpcIndex);
  111. }
  112. m_events.m_rpcReceived.Signal(entityId, entityName, netComponentId, rpcId, totalBytes);
  113. }
  114. void MultiplayerStats::TickStats(AZ::TimeMs metricFrameTimeMs)
  115. {
  116. SET_PERFORMANCE_STAT(MultiplayerStat_EntityCount, m_entityCount);
  117. SET_PERFORMANCE_STAT(MultiplayerStat_ClientConnectionCount, m_clientConnectionCount);
  118. m_totalHistoryTimeMs = metricFrameTimeMs * static_cast<AZ::TimeMs>(RingbufferSamples);
  119. m_recordMetricIndex = ++m_recordMetricIndex % RingbufferSamples;
  120. for (ComponentStats& componentStats : m_componentStats)
  121. {
  122. for (Metric& metric : componentStats.m_propertyUpdatesSent)
  123. {
  124. metric.m_callHistory[m_recordMetricIndex] = 0;
  125. metric.m_byteHistory[m_recordMetricIndex] = 0;
  126. }
  127. for (Metric& metric : componentStats.m_propertyUpdatesRecv)
  128. {
  129. metric.m_callHistory[m_recordMetricIndex] = 0;
  130. metric.m_byteHistory[m_recordMetricIndex] = 0;
  131. }
  132. for (Metric& metric : componentStats.m_rpcsSent)
  133. {
  134. metric.m_callHistory[m_recordMetricIndex] = 0;
  135. metric.m_byteHistory[m_recordMetricIndex] = 0;
  136. }
  137. for (Metric& metric : componentStats.m_rpcsRecv)
  138. {
  139. metric.m_callHistory[m_recordMetricIndex] = 0;
  140. metric.m_byteHistory[m_recordMetricIndex] = 0;
  141. }
  142. }
  143. }
  144. static void CombineMetrics(MultiplayerStats::Metric& outArg1, const MultiplayerStats::Metric& arg2)
  145. {
  146. outArg1.m_totalCalls += arg2.m_totalCalls;
  147. outArg1.m_totalBytes += arg2.m_totalBytes;
  148. for (uint32_t index = 0; index < MultiplayerStats::RingbufferSamples; ++index)
  149. {
  150. outArg1.m_callHistory[index] += arg2.m_callHistory[index];
  151. outArg1.m_byteHistory[index] += arg2.m_byteHistory[index];
  152. }
  153. }
  154. static MultiplayerStats::Metric SumMetricVector(const AZStd::vector<MultiplayerStats::Metric>& metricVector)
  155. {
  156. MultiplayerStats::Metric result;
  157. for (AZStd::size_t index = 0; index < metricVector.size(); ++index)
  158. {
  159. CombineMetrics(result, metricVector[index]);
  160. }
  161. return result;
  162. }
  163. MultiplayerStats::Metric MultiplayerStats::CalculateComponentPropertyUpdateSentMetrics(NetComponentId netComponentId) const
  164. {
  165. const uint16_t netComponentIndex = aznumeric_cast<uint16_t>(netComponentId);
  166. return SumMetricVector(m_componentStats[netComponentIndex].m_propertyUpdatesSent);
  167. }
  168. MultiplayerStats::Metric MultiplayerStats::CalculateComponentPropertyUpdateRecvMetrics(NetComponentId netComponentId) const
  169. {
  170. const uint16_t netComponentIndex = aznumeric_cast<uint16_t>(netComponentId);
  171. return SumMetricVector(m_componentStats[netComponentIndex].m_propertyUpdatesRecv);
  172. }
  173. MultiplayerStats::Metric MultiplayerStats::CalculateComponentRpcsSentMetrics(NetComponentId netComponentId) const
  174. {
  175. const uint16_t netComponentIndex = aznumeric_cast<uint16_t>(netComponentId);
  176. return SumMetricVector(m_componentStats[netComponentIndex].m_rpcsSent);
  177. }
  178. MultiplayerStats::Metric MultiplayerStats::CalculateComponentRpcsRecvMetrics(NetComponentId netComponentId) const
  179. {
  180. const uint16_t netComponentIndex = aznumeric_cast<uint16_t>(netComponentId);
  181. return SumMetricVector(m_componentStats[netComponentIndex].m_rpcsRecv);
  182. }
  183. MultiplayerStats::Metric MultiplayerStats::CalculateTotalPropertyUpdateSentMetrics() const
  184. {
  185. Metric result;
  186. for (AZStd::size_t index = 0; index < m_componentStats.size(); ++index)
  187. {
  188. const NetComponentId netComponentId = aznumeric_cast<NetComponentId>(index);
  189. CombineMetrics(result, CalculateComponentPropertyUpdateSentMetrics(netComponentId));
  190. }
  191. return result;
  192. }
  193. MultiplayerStats::Metric MultiplayerStats::CalculateTotalPropertyUpdateRecvMetrics() const
  194. {
  195. Metric result;
  196. for (AZStd::size_t index = 0; index < m_componentStats.size(); ++index)
  197. {
  198. const NetComponentId netComponentId = aznumeric_cast<NetComponentId>(index);
  199. CombineMetrics(result, CalculateComponentPropertyUpdateRecvMetrics(netComponentId));
  200. }
  201. return result;
  202. }
  203. MultiplayerStats::Metric MultiplayerStats::CalculateTotalRpcsSentMetrics() const
  204. {
  205. Metric result;
  206. for (AZStd::size_t index = 0; index < m_componentStats.size(); ++index)
  207. {
  208. const NetComponentId netComponentId = aznumeric_cast<NetComponentId>(index);
  209. CombineMetrics(result, CalculateComponentRpcsSentMetrics(netComponentId));
  210. }
  211. return result;
  212. }
  213. MultiplayerStats::Metric MultiplayerStats::CalculateTotalRpcsRecvMetrics() const
  214. {
  215. Metric result;
  216. for (AZStd::size_t index = 0; index < m_componentStats.size(); ++index)
  217. {
  218. const NetComponentId netComponentId = aznumeric_cast<NetComponentId>(index);
  219. CombineMetrics(result, CalculateComponentRpcsRecvMetrics(netComponentId));
  220. }
  221. return result;
  222. }
  223. void MultiplayerStats::ConnectHandlers(EventHandlers& handlers)
  224. {
  225. handlers.m_entitySerializeStart.Connect(m_events.m_entitySerializeStart);
  226. handlers.m_componentSerializeEnd.Connect(m_events.m_componentSerializeEnd);
  227. handlers.m_entitySerializeStop.Connect(m_events.m_entitySerializeStop);
  228. handlers.m_propertySent.Connect(m_events.m_propertySent);
  229. handlers.m_propertyReceived.Connect(m_events.m_propertyReceived);
  230. handlers.m_rpcSent.Connect(m_events.m_rpcSent);
  231. handlers.m_rpcReceived.Connect(m_events.m_rpcReceived);
  232. }
  233. void MultiplayerStats::RecordFrameTime(AZ::TimeUs networkFrameTime)
  234. {
  235. SET_PERFORMANCE_STAT(MultiplayerStat_FrameTimeUs, networkFrameTime);
  236. }
  237. } // namespace Multiplayer