MultiplayerStatSystemComponent.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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 <AzCore/Console/IConsole.h>
  9. #include <AzCore/Metrics/IEventLoggerFactory.h>
  10. #include <AzCore/Metrics/JsonTraceEventLogger.h>
  11. #include <AzCore/Utils/Utils.h>
  12. #include <Multiplayer/IMultiplayer.h>
  13. #include <Source/MultiplayerStatSystemComponent.h>
  14. namespace Multiplayer
  15. {
  16. // Metrics cvars
  17. void OnEnableNetworkingMetricsChanged(const bool& enabled);
  18. AZ_CVAR(
  19. bool,
  20. bg_enableNetworkingMetrics,
  21. true,
  22. &OnEnableNetworkingMetricsChanged,
  23. AZ::ConsoleFunctorFlags::DontReplicate,
  24. "Whether to capture networking metrics");
  25. AZ_CVAR(
  26. AZ::TimeMs,
  27. bg_networkingMetricCollectionPeriod,
  28. AZ::TimeMs{ 1000 },
  29. nullptr,
  30. AZ::ConsoleFunctorFlags::DontReplicate,
  31. "How often to capture metrics by default.");
  32. AZ_CVAR(
  33. AZ::CVarFixedString,
  34. cl_metricsFile,
  35. "client_network_metrics.json",
  36. nullptr,
  37. AZ::ConsoleFunctorFlags::DontReplicate,
  38. "File of the client metrics file if enabled, placed under <ProjectFolder>/user/metrics");
  39. AZ_CVAR(
  40. AZ::CVarFixedString,
  41. sv_metricsFile,
  42. "server_network_metrics.json",
  43. nullptr,
  44. AZ::ConsoleFunctorFlags::DontReplicate,
  45. "File of the server metrics file if enabled, placed under <ProjectFolder>/user/metrics");
  46. void ConfigureEventLoggerHelper(const AZ::CVarFixedString& filename)
  47. {
  48. if (auto eventLoggerFactory = AZ::Interface<AZ::Metrics::IEventLoggerFactory>::Get())
  49. {
  50. const AZ::IO::FixedMaxPath metricsFilepath = AZ::IO::FixedMaxPath(AZ::Utils::GetProjectPath()) / "user/Metrics" / filename;
  51. constexpr AZ::IO::OpenMode openMode = AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath;
  52. auto stream = AZStd::make_unique<AZ::IO::SystemFileStream>(metricsFilepath.c_str(), openMode);
  53. AZ::Metrics::JsonTraceEventLoggerConfig config{ "Multiplayer" };
  54. auto eventLogger = AZStd::make_unique<AZ::Metrics::JsonTraceEventLogger>(AZStd::move(stream), config);
  55. eventLoggerFactory->RegisterEventLogger(NetworkingMetricsId, AZStd::move(eventLogger));
  56. }
  57. }
  58. void UnregisterEventLoggerHelper()
  59. {
  60. if (auto* eventLoggerFactory = AZ::Interface<AZ::Metrics::IEventLoggerFactory>::Get())
  61. {
  62. eventLoggerFactory->UnregisterEventLogger(NetworkingMetricsId);
  63. }
  64. }
  65. void OnEnableNetworkingMetricsChanged(const bool& enabled)
  66. {
  67. if (auto* statSystem = AZ::Interface<IMultiplayerStatSystem>::Get())
  68. {
  69. if (enabled)
  70. {
  71. statSystem->Register();
  72. }
  73. else
  74. {
  75. statSystem->Unregister();
  76. }
  77. }
  78. }
  79. void MultiplayerStatSystemComponent::Reflect(AZ::ReflectContext* context)
  80. {
  81. if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  82. {
  83. serializeContext->Class<MultiplayerStatSystemComponent, AZ::Component>()->Version(1);
  84. }
  85. }
  86. void MultiplayerStatSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  87. {
  88. provided.push_back(AZ_CRC_CE("MultiplayerStatSystemComponent"));
  89. }
  90. void MultiplayerStatSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
  91. {
  92. incompatible.push_back(AZ_CRC_CE("MultiplayerStatSystemComponent"));
  93. }
  94. MultiplayerStatSystemComponent::MultiplayerStatSystemComponent()
  95. {
  96. AZ::Interface<IMultiplayerStatSystem>::Register(this);
  97. }
  98. MultiplayerStatSystemComponent::~MultiplayerStatSystemComponent()
  99. {
  100. AZ::Interface<IMultiplayerStatSystem>::Unregister(this);
  101. }
  102. void MultiplayerStatSystemComponent::Activate()
  103. {
  104. }
  105. void MultiplayerStatSystemComponent::Deactivate()
  106. {
  107. Unregister();
  108. }
  109. void MultiplayerStatSystemComponent::Register()
  110. {
  111. UnregisterEventLoggerHelper();
  112. if (GetMultiplayer())
  113. {
  114. switch (GetMultiplayer()->GetAgentType())
  115. {
  116. case MultiplayerAgentType::DedicatedServer: // fallthrough
  117. case MultiplayerAgentType::ClientServer:
  118. ConfigureEventLoggerHelper(sv_metricsFile);
  119. break;
  120. case MultiplayerAgentType::Client:
  121. ConfigureEventLoggerHelper(cl_metricsFile);
  122. break;
  123. case MultiplayerAgentType::Uninitialized:
  124. AZLOG_WARN("Unitialized agent type isn't supported for networking metrics.");
  125. break;
  126. }
  127. }
  128. m_metricsEvent.Enqueue(bg_networkingMetricCollectionPeriod, true);
  129. }
  130. void MultiplayerStatSystemComponent::Unregister()
  131. {
  132. m_metricsEvent.RemoveFromQueue();
  133. if (bg_enableNetworkingMetrics)
  134. {
  135. UnregisterEventLoggerHelper();
  136. }
  137. }
  138. void MultiplayerStatSystemComponent::SetReportPeriod(AZ::TimeMs period)
  139. {
  140. m_metricsEvent.Requeue(period);
  141. }
  142. void MultiplayerStatSystemComponent::DeclareStatGroup(int uniqueGroupId, const char* groupName)
  143. {
  144. AZStd::lock_guard lock(m_access);
  145. StatGroup* newGroup = m_statGroups.AddNew(uniqueGroupId);
  146. newGroup->m_name = groupName;
  147. }
  148. void MultiplayerStatSystemComponent::DeclareStat(int uniqueGroupId, int uniqueStatId, const char* statName)
  149. {
  150. AZStd::lock_guard lock(m_access);
  151. if (StatGroup* group = m_statGroups.Find(uniqueGroupId))
  152. {
  153. auto* newStat = group->m_stats.AddNew(uniqueStatId);
  154. newStat->m_name = statName;
  155. const auto statIterator = m_statIdToGroupId.find(uniqueStatId);
  156. if (statIterator == m_statIdToGroupId.end())
  157. {
  158. m_statIdToGroupId.insert(AZStd::make_pair(uniqueStatId, uniqueGroupId));
  159. }
  160. else
  161. {
  162. AZLOG_WARN("A stat has already been declared using DECLARE_PERFORMANCE_STAT with id %d", uniqueStatId);
  163. }
  164. }
  165. else
  166. {
  167. AZLOG_WARN("Stat group with id %d has not been declared using DECLARE_PERFORMANCE_STAT_GROUP", uniqueGroupId);
  168. }
  169. }
  170. void MultiplayerStatSystemComponent::SetStat(int uniqueStatId, double value)
  171. {
  172. AZStd::lock_guard lock(m_access);
  173. const auto statIterator = m_statIdToGroupId.find(uniqueStatId);
  174. if (statIterator != m_statIdToGroupId.end())
  175. {
  176. if (const auto group = m_statGroups.Find(statIterator->second))
  177. {
  178. if (CumulativeAverage* stat = group->m_stats.Find(uniqueStatId))
  179. {
  180. stat->m_lastValue = value;
  181. stat->m_average.PushEntry(value);
  182. return;
  183. }
  184. }
  185. }
  186. AZLOG_WARN("Stat with id %d has not been declared using DECLARE_PERFORMANCE_STAT", uniqueStatId);
  187. }
  188. void MultiplayerStatSystemComponent::IncrementStat(int uniqueStatId)
  189. {
  190. AZStd::lock_guard lock(m_access);
  191. const auto statIterator = m_statIdToGroupId.find(uniqueStatId);
  192. if (statIterator != m_statIdToGroupId.end())
  193. {
  194. if (const auto group = m_statGroups.Find(statIterator->second))
  195. {
  196. if (CumulativeAverage* stat = group->m_stats.Find(uniqueStatId))
  197. {
  198. stat->m_counterValue++;
  199. return;
  200. }
  201. }
  202. }
  203. AZLOG_WARN("Stat with id %d has not been declared using DECLARE_PERFORMANCE_STAT", uniqueStatId);
  204. }
  205. void MultiplayerStatSystemComponent::RecordMetrics()
  206. {
  207. if (const auto* eventLoggerFactory = AZ::Interface<AZ::Metrics::IEventLoggerFactory>::Get())
  208. {
  209. if (auto* eventLogger = eventLoggerFactory->FindEventLogger(NetworkingMetricsId))
  210. {
  211. AZStd::lock_guard lock(m_access);
  212. for (StatGroup& group : m_statGroups.m_items)
  213. {
  214. AZStd::vector<AZ::Metrics::EventField> argsContainer;
  215. for (auto& stat : group.m_stats.m_items)
  216. {
  217. if (stat.m_average.GetNumRecorded() > 0)
  218. {
  219. // If there are new entries, update the average.
  220. argsContainer.emplace_back(stat.m_name.c_str(), stat.m_average.CalculateAverage());
  221. }
  222. else if (stat.m_counterValue > 0)
  223. {
  224. // counter metric
  225. argsContainer.emplace_back(stat.m_name.c_str(), stat.m_counterValue);
  226. stat.m_counterValue = 0;
  227. stat.m_lastValue = 0;
  228. }
  229. else
  230. {
  231. // If there were no entries within the last collection period, report the last value received.
  232. argsContainer.emplace_back(stat.m_name.c_str(), stat.m_lastValue);
  233. }
  234. // Reset average in order to measure average over the save period.
  235. stat.m_average = CumulativeAverage::AverageWindowType{};
  236. }
  237. AZ::Metrics::CounterArgs counterArgs;
  238. counterArgs.m_name = "Stats";
  239. counterArgs.m_cat = group.m_name;
  240. counterArgs.m_args = argsContainer;
  241. eventLogger->RecordCounterEvent(counterArgs);
  242. }
  243. }
  244. }
  245. }
  246. } // namespace Multiplayer