diff --git a/Gems/Multiplayer/Code/Include/IMultiplayer.h b/Gems/Multiplayer/Code/Include/IMultiplayer.h index 8d003122e0..9b50a966e6 100644 --- a/Gems/Multiplayer/Code/Include/IMultiplayer.h +++ b/Gems/Multiplayer/Code/Include/IMultiplayer.h @@ -74,6 +74,28 @@ namespace Multiplayer //! @param handler The SessionShutdownEvent handler to add virtual void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) = 0; + //! Returns the gem name associated with the provided component index. + //! @param netComponentIndex the component index to return the gem name of + //! @return the name of the gem that contains the requested component + virtual const char* GetComponentGemName(uint16_t netComponentIndex) const = 0; + + //! Returns the component name associated with the provided component index. + //! @param netComponentIndex the component index to return the component name of + //! @return the name of the component + virtual const char* GetComponentName(uint16_t netComponentIndex) const = 0; + + //! Returns the property name associated with the provided component index and property index. + //! @param netComponentIndex the component index to return the property name of + //! @param propertyIndex the index off the network property to return the property name of + //! @return the name of the network property + virtual const char* GetComponentPropertyName(uint16_t netComponentIndex, uint16_t propertyIndex) const = 0; + + //! Returns the Rpc name associated with the provided component index and rpc index. + //! @param netComponentIndex the component index to return the property name of + //! @param rpcIndex the index off the rpc to return the rpc name of + //! @return the name of the requested rpc + virtual const char* GetComponentRpcName(uint16_t netComponentIndex, uint16_t rpcIndex) const = 0; + //! Retrieve the stats object bound to this multiplayer instance. //! @return the stats object bound to this multiplayer instance MultiplayerStats& GetStats() { return m_stats; } diff --git a/Gems/Multiplayer/Code/Include/MultiplayerStats.h b/Gems/Multiplayer/Code/Include/MultiplayerStats.h index 43101f6543..e3081b8149 100644 --- a/Gems/Multiplayer/Code/Include/MultiplayerStats.h +++ b/Gems/Multiplayer/Code/Include/MultiplayerStats.h @@ -14,7 +14,7 @@ #include #include -#include +#include namespace AzNetworking { @@ -33,7 +33,7 @@ namespace Multiplayer AZ::TimeMs m_totalHistoryTimeMs = AZ::TimeMs{ 0 }; static const uint32_t RingbufferSamples = 32; - using MetricRingbuffer = AZStd::fixed_vector; + using MetricRingbuffer = AZStd::array; struct Metric { uint64_t m_totalCalls = 0; diff --git a/Gems/Multiplayer/Code/Source/Components/MultiplayerComponentRegistry.cpp b/Gems/Multiplayer/Code/Source/Components/MultiplayerComponentRegistry.cpp index 6d2d1bcf85..ab701c754d 100644 --- a/Gems/Multiplayer/Code/Source/Components/MultiplayerComponentRegistry.cpp +++ b/Gems/Multiplayer/Code/Source/Components/MultiplayerComponentRegistry.cpp @@ -39,10 +39,10 @@ namespace Multiplayer return componentData.m_componentPropertyNameLookupFunction(propertyIndex); } - const char* MultiplayerComponentRegistry::GetComponentRpcName(NetComponentId netComponentId, uint16_t rpcId) const + const char* MultiplayerComponentRegistry::GetComponentRpcName(NetComponentId netComponentId, uint16_t rpcIndex) const { const ComponentData& componentData = GetMultiplayerComponentData(netComponentId); - return componentData.m_componentRpcNameLookupFunction(rpcId); + return componentData.m_componentRpcNameLookupFunction(rpcIndex); } const MultiplayerComponentRegistry::ComponentData& MultiplayerComponentRegistry::GetMultiplayerComponentData(NetComponentId netComponentId) const diff --git a/Gems/Multiplayer/Code/Source/Components/MultiplayerComponentRegistry.h b/Gems/Multiplayer/Code/Source/Components/MultiplayerComponentRegistry.h index 550301b2d8..372de9320a 100644 --- a/Gems/Multiplayer/Code/Source/Components/MultiplayerComponentRegistry.h +++ b/Gems/Multiplayer/Code/Source/Components/MultiplayerComponentRegistry.h @@ -53,9 +53,9 @@ namespace Multiplayer //! Returns the Rpc name associated with the provided NetComponentId and rpcId. //! @param netComponentId the NetComponentId to return the property name of - //! @param rpcId the index off the rpc to return the rpc name of + //! @param rpcIndex the index of the rpc to return the rpc name of //! @return the name of the requested rpc - const char* GetComponentRpcName(NetComponentId netComponentId, uint16_t rpcId) const; + const char* GetComponentRpcName(NetComponentId netComponentId, uint16_t rpcIndex) const; //! Retrieves the stored component data for a given NetComponentId. //! @param netComponentId the NetComponentId to return component data for diff --git a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp index c3d21023e9..e009ee0d86 100644 --- a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp @@ -91,6 +91,9 @@ namespace Multiplayer //} ImGui::Checkbox("Multiplayer Stats", &m_displayStats); + ImGui::Checkbox("Component Stats", &m_displayComponentStats); + ImGui::Checkbox("Property Stats", &m_displayPropertyStats); + ImGui::Checkbox("Rpc Stats", &m_displayRpcStats); ImGui::EndMenu(); } } @@ -105,8 +108,8 @@ namespace Multiplayer summedBytes += metric.m_byteHistory[index]; } const float totalTimeSeconds = static_cast(stats.m_totalHistoryTimeMs) / 1000.0f; - outCallsPerSecond = static_cast(summedCalls) / totalTimeSeconds; - outBytesPerSecond = static_cast(summedBytes) / totalTimeSeconds; + outCallsPerSecond = (summedCalls > 0 && totalTimeSeconds > 0.0f) ? static_cast(summedCalls) / totalTimeSeconds : 0.0f; + outBytesPerSecond = (summedBytes > 0 && totalTimeSeconds > 0.0f) ? static_cast(summedBytes) / totalTimeSeconds : 0.0f; } void DrawMetricTitle(const ImVec4& entryColour) @@ -158,9 +161,111 @@ namespace Multiplayer DrawMetricTitle(titleColour); DrawMetricRow("Total", "PropertyUpdates Sent", entryColour, stats, propertyUpdatesSent); - DrawMetricRow("Total", "PropertyUpdates Received", entryColour, stats, propertyUpdatesRecv); + DrawMetricRow("Total", "PropertyUpdates Recv", entryColour, stats, propertyUpdatesRecv); DrawMetricRow("Total", "Rpcs Sent", entryColour, stats, rpcsSent); - DrawMetricRow("Total", "Rpcs Received", entryColour, stats, rpcsRecv); + DrawMetricRow("Total", "Rpcs Recv", entryColour, stats, rpcsRecv); + ImGui::Columns(1); + ImGui::End(); + } + } + + if (m_displayComponentStats) + { + if (ImGui::Begin("Component Stats", &m_displayComponentStats, ImGuiWindowFlags_HorizontalScrollbar)) + { + IMultiplayer* multiplayer = AZ::Interface::Get(); + const Multiplayer::MultiplayerStats& stats = multiplayer->GetStats(); + + DrawMetricTitle(titleColour); + for (AZStd::size_t index = 0; index < stats.m_componentStats.size(); ++index) + { + const uint16_t componentIndex = aznumeric_cast(index); + + const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateComponentPropertyUpdateSentMetrics(componentIndex); + const MultiplayerStats::Metric propertyUpdatesRecv = stats.CalculateComponentPropertyUpdateRecvMetrics(componentIndex); + const MultiplayerStats::Metric rpcsSent = stats.CalculateComponentRpcsSentMetrics(componentIndex); + const MultiplayerStats::Metric rpcsRecv = stats.CalculateComponentRpcsRecvMetrics(componentIndex); + + using StringLabel = AZStd::fixed_string<128>; + const StringLabel gemName = multiplayer->GetComponentGemName(componentIndex); + const StringLabel componentName = multiplayer->GetComponentName(componentIndex); + const StringLabel label = gemName + "::" + componentName; + + DrawMetricRow(label.c_str(), "PropertyUpdates Sent", entryColour, stats, propertyUpdatesSent); + DrawMetricRow(label.c_str(), "PropertyUpdates Recv", entryColour, stats, propertyUpdatesRecv); + DrawMetricRow(label.c_str(), "Rpcs Sent", entryColour, stats, rpcsSent); + DrawMetricRow(label.c_str(), "Rpcs Recv", entryColour, stats, rpcsRecv); + } + ImGui::Columns(1); + ImGui::End(); + } + } + + if (m_displayPropertyStats) + { + if (ImGui::Begin("Network Property Stats", &m_displayPropertyStats, ImGuiWindowFlags_HorizontalScrollbar)) + { + IMultiplayer* multiplayer = AZ::Interface::Get(); + const Multiplayer::MultiplayerStats& stats = multiplayer->GetStats(); + + DrawMetricTitle(titleColour); + for (AZStd::size_t index = 0; index < stats.m_componentStats.size(); ++index) + { + const uint16_t componentIndex = aznumeric_cast(index); + const MultiplayerStats::ComponentStats& componentStats = stats.m_componentStats[componentIndex]; + for (AZStd::size_t index2 = 0; index2 < componentStats.m_propertyUpdatesSent.size(); ++index2) + { + const MultiplayerStats::Metric& propertyUpdatesSent = componentStats.m_propertyUpdatesSent[index2]; + const MultiplayerStats::Metric& propertyUpdatesRecv = componentStats.m_propertyUpdatesRecv[index2]; + + using StringLabel = AZStd::fixed_string<128>; + const StringLabel gemName = multiplayer->GetComponentGemName(componentIndex); + const StringLabel componentName = multiplayer->GetComponentName(componentIndex); + const StringLabel propertyName = multiplayer->GetComponentPropertyName(componentIndex, aznumeric_cast(index2)); + const StringLabel label = gemName + "::" + componentName; + + const StringLabel sentLabel = propertyName + " Sent"; + const StringLabel recvLabel = propertyName + " Recv"; + + DrawMetricRow(label.c_str(), sentLabel.c_str(), entryColour, stats, propertyUpdatesSent); + DrawMetricRow(label.c_str(), recvLabel.c_str(), entryColour, stats, propertyUpdatesRecv); + } + } + ImGui::Columns(1); + ImGui::End(); + } + } + + if (m_displayRpcStats) + { + if (ImGui::Begin("Rpc Stats", &m_displayRpcStats, ImGuiWindowFlags_HorizontalScrollbar)) + { + IMultiplayer* multiplayer = AZ::Interface::Get(); + const Multiplayer::MultiplayerStats& stats = multiplayer->GetStats(); + + DrawMetricTitle(titleColour); + for (AZStd::size_t index = 0; index < stats.m_componentStats.size(); ++index) + { + const uint16_t componentIndex = aznumeric_cast(index); + const MultiplayerStats::ComponentStats& componentStats = stats.m_componentStats[componentIndex]; + for (AZStd::size_t index2 = 0; index2 < componentStats.m_rpcsSent.size(); ++index2) + { + const MultiplayerStats::Metric& rpcsSent = componentStats.m_rpcsSent[index2]; + const MultiplayerStats::Metric& rpcsRecv = componentStats.m_rpcsRecv[index2]; + + using StringLabel = AZStd::fixed_string<128>; + const StringLabel gemName = multiplayer->GetComponentGemName(componentIndex); + const StringLabel componentName = multiplayer->GetComponentName(componentIndex); + const StringLabel rpcName = multiplayer->GetComponentRpcName(componentIndex, aznumeric_cast(index2)); + const StringLabel label = gemName + "::" + componentName; + + const StringLabel sentLabel = rpcName + " Sent"; + const StringLabel recvLabel = rpcName + " Recv"; + + DrawMetricRow(label.c_str(), sentLabel.c_str(), entryColour, stats, rpcsSent); + DrawMetricRow(label.c_str(), recvLabel.c_str(), entryColour, stats, rpcsRecv); + } + } ImGui::Columns(1); ImGui::End(); } diff --git a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.h b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.h index 722f25c7d0..90d121aa9a 100644 --- a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.h @@ -52,6 +52,7 @@ namespace Multiplayer #endif private: bool m_displayStats = false; + bool m_displayComponentStats = false; bool m_displayPropertyStats = false; bool m_displayRpcStats = false; }; diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index c21ff10aa0..8976cb46b4 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -506,6 +506,26 @@ namespace Multiplayer handler.Connect(m_shutdownEvent); } + const char* MultiplayerSystemComponent::GetComponentGemName(uint16_t netComponentIndex) const + { + return GetMultiplayerComponentRegistry()->GetComponentGemName(static_cast(netComponentIndex)); + } + + const char* MultiplayerSystemComponent::GetComponentName(uint16_t netComponentIndex) const + { + return GetMultiplayerComponentRegistry()->GetComponentName(static_cast(netComponentIndex)); + } + + const char* MultiplayerSystemComponent::GetComponentPropertyName(uint16_t netComponentIndex, uint16_t propertyIndex) const + { + return GetMultiplayerComponentRegistry()->GetComponentPropertyName(static_cast(netComponentIndex), propertyIndex); + } + + const char* MultiplayerSystemComponent::GetComponentRpcName(uint16_t netComponentIndex, uint16_t rpcIndex) const + { + return GetMultiplayerComponentRegistry()->GetComponentRpcName(static_cast(netComponentIndex), rpcIndex); + } + void MultiplayerSystemComponent::DumpStats([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { const MultiplayerStats& stats = GetStats(); diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index 1e10f9841e..6010a132ea 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -88,6 +88,10 @@ namespace Multiplayer void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override; void AddSessionInitHandler(SessionInitEvent::Handler& handler) override; void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override; + const char* GetComponentGemName(uint16_t netComponentIndex) const override; + const char* GetComponentName(uint16_t netComponentIndex) const override; + const char* GetComponentPropertyName(uint16_t netComponentIndex, uint16_t propertyIndex) const override; + const char* GetComponentRpcName(uint16_t netComponentIndex, uint16_t rpcIndex) const override; //! @} //! Console commands.