diff --git a/Gems/Multiplayer/Code/Include/IMultiplayer.h b/Gems/Multiplayer/Code/Include/IMultiplayer.h index 94744dbb54..8d003122e0 100644 --- a/Gems/Multiplayer/Code/Include/IMultiplayer.h +++ b/Gems/Multiplayer/Code/Include/IMultiplayer.h @@ -15,6 +15,7 @@ #include #include #include +#include namespace AzNetworking { @@ -23,21 +24,6 @@ namespace AzNetworking namespace Multiplayer { - struct MultiplayerStats - { - uint64_t m_entityCount = 0; - uint64_t m_clientConnectionCount = 0; - uint64_t m_serverConnectionCount = 0; - uint64_t m_propertyUpdatesSent = 0; - uint64_t m_propertyUpdatesSentBytes = 0; - uint64_t m_propertyUpdatesRecv = 0; - uint64_t m_propertyUpdatesRecvBytes = 0; - uint64_t m_rpcsSent = 0; - uint64_t m_rpcsSentBytes = 0; - uint64_t m_rpcsRecv = 0; - uint64_t m_rpcsRecvBytes = 0; - }; - //! Collection of types of Multiplayer Connections enum class MultiplayerAgentType { diff --git a/Gems/Multiplayer/Code/Include/MultiplayerStats.cpp b/Gems/Multiplayer/Code/Include/MultiplayerStats.cpp new file mode 100644 index 0000000000..27dba01c84 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/MultiplayerStats.cpp @@ -0,0 +1,147 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include + +namespace Multiplayer +{ + void MultiplayerStats::ReserveComponentStats(uint16_t netComponentId, uint16_t propertyCount, uint16_t rpcCount) + { + if (m_componentStats.size() <= netComponentId) + { + m_componentStats.resize(netComponentId + 1); + } + m_componentStats[netComponentId].m_propertyUpdatesSent.resize(propertyCount); + m_componentStats[netComponentId].m_propertyUpdatesRecv.resize(propertyCount); + m_componentStats[netComponentId].m_rpcsSent.resize(rpcCount); + m_componentStats[netComponentId].m_rpcsRecv.resize(rpcCount); + } + + void MultiplayerStats::RecordPropertySent(uint16_t netComponentId, uint16_t propertyId, uint32_t totalBytes) + { + m_componentStats[netComponentId].m_propertyUpdatesSent[propertyId].m_totalCalls++; + m_componentStats[netComponentId].m_propertyUpdatesSent[propertyId].m_totalBytes += totalBytes; + m_componentStats[netComponentId].m_propertyUpdatesSent[propertyId].m_callHistory[m_recordMetricIndex]++; + m_componentStats[netComponentId].m_propertyUpdatesSent[propertyId].m_byteHistory[m_recordMetricIndex] += totalBytes; + } + + void MultiplayerStats::RecordPropertyReceived(uint16_t netComponentId, uint16_t propertyId, uint32_t totalBytes) + { + m_componentStats[netComponentId].m_propertyUpdatesRecv[propertyId].m_totalCalls++; + m_componentStats[netComponentId].m_propertyUpdatesRecv[propertyId].m_totalBytes += totalBytes; + m_componentStats[netComponentId].m_propertyUpdatesRecv[propertyId].m_callHistory[m_recordMetricIndex]++; + m_componentStats[netComponentId].m_propertyUpdatesRecv[propertyId].m_byteHistory[m_recordMetricIndex] += totalBytes; + } + + void MultiplayerStats::RecordRpcSent(uint16_t netComponentId, uint16_t rpcId, uint32_t totalBytes) + { + m_componentStats[netComponentId].m_rpcsSent[rpcId].m_totalCalls++; + m_componentStats[netComponentId].m_rpcsSent[rpcId].m_totalBytes += totalBytes; + m_componentStats[netComponentId].m_rpcsSent[rpcId].m_callHistory[m_recordMetricIndex]++; + m_componentStats[netComponentId].m_rpcsSent[rpcId].m_byteHistory[m_recordMetricIndex] += totalBytes; + } + + void MultiplayerStats::RecordRpcReceived(uint16_t netComponentId, uint16_t rpcId, uint32_t totalBytes) + { + m_componentStats[netComponentId].m_rpcsRecv[rpcId].m_totalCalls++; + m_componentStats[netComponentId].m_rpcsRecv[rpcId].m_totalBytes += totalBytes; + m_componentStats[netComponentId].m_rpcsRecv[rpcId].m_callHistory[m_recordMetricIndex]++; + m_componentStats[netComponentId].m_rpcsRecv[rpcId].m_byteHistory[m_recordMetricIndex] += totalBytes; + } + + void MultiplayerStats::TickStats(AZ::TimeMs metricFrameTimeMs) + { + m_totalHistoryTimeMs = metricFrameTimeMs * static_cast(RingbufferSamples); + m_recordMetricIndex = ++m_recordMetricIndex % RingbufferSamples; + } + + static void CombineMetrics(MultiplayerStats::Metric& outArg1, const MultiplayerStats::Metric& arg2) + { + outArg1.m_totalCalls += arg2.m_totalCalls; + outArg1.m_totalBytes += arg2.m_totalBytes; + for (uint32_t index = 0; index < MultiplayerStats::RingbufferSamples; ++index) + { + outArg1.m_callHistory[index] += arg2.m_callHistory[index]; + outArg1.m_byteHistory[index] += arg2.m_byteHistory[index]; + } + } + + static MultiplayerStats::Metric SumMetricVector(const AZStd::vector& metricVector) + { + MultiplayerStats::Metric result; + for (AZStd::size_t index = 0; index < metricVector.size(); ++index) + { + CombineMetrics(result, metricVector[index]); + } + return result; + } + + MultiplayerStats::Metric MultiplayerStats::CalculateComponentPropertyUpdateSentMetrics(uint16_t netComponentId) const + { + return SumMetricVector(m_componentStats[netComponentId].m_propertyUpdatesSent); + } + + MultiplayerStats::Metric MultiplayerStats::CalculateComponentPropertyUpdateRecvMetrics(uint16_t netComponentId) const + { + return SumMetricVector(m_componentStats[netComponentId].m_propertyUpdatesRecv); + } + + MultiplayerStats::Metric MultiplayerStats::CalculateComponentRpcsSentMetrics(uint16_t netComponentId) const + { + return SumMetricVector(m_componentStats[netComponentId].m_rpcsSent); + } + + MultiplayerStats::Metric MultiplayerStats::CalculateComponentRpcsRecvMetrics(uint16_t netComponentId) const + { + return SumMetricVector(m_componentStats[netComponentId].m_rpcsRecv); + } + + MultiplayerStats::Metric MultiplayerStats::CalculateTotalPropertyUpdateSentMetrics() const + { + Metric result; + for (AZStd::size_t index = 0; index < m_componentStats.size(); ++index) + { + CombineMetrics(result, CalculateComponentPropertyUpdateSentMetrics(index)); + } + return result; + } + + MultiplayerStats::Metric MultiplayerStats::CalculateTotalPropertyUpdateRecvMetrics() const + { + Metric result; + for (AZStd::size_t index = 0; index < m_componentStats.size(); ++index) + { + CombineMetrics(result, CalculateComponentPropertyUpdateRecvMetrics(index)); + } + return result; + } + + MultiplayerStats::Metric MultiplayerStats::CalculateTotalRpcsSentMetrics() const + { + Metric result; + for (AZStd::size_t index = 0; index < m_componentStats.size(); ++index) + { + CombineMetrics(result, CalculateComponentRpcsSentMetrics(index)); + } + return result; + } + + MultiplayerStats::Metric MultiplayerStats::CalculateTotalRpcsRecvMetrics() const + { + Metric result; + for (AZStd::size_t index = 0; index < m_componentStats.size(); ++index) + { + CombineMetrics(result, CalculateComponentRpcsRecvMetrics(index)); + } + return result; + } +} diff --git a/Gems/Multiplayer/Code/Include/MultiplayerStats.h b/Gems/Multiplayer/Code/Include/MultiplayerStats.h new file mode 100644 index 0000000000..43101f6543 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/MultiplayerStats.h @@ -0,0 +1,70 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include + +namespace AzNetworking +{ + class INetworkInterface; +} + +namespace Multiplayer +{ + struct MultiplayerStats + { + uint64_t m_entityCount = 0; + uint64_t m_clientConnectionCount = 0; + uint64_t m_serverConnectionCount = 0; + + uint64_t m_recordMetricIndex = 0; + AZ::TimeMs m_totalHistoryTimeMs = AZ::TimeMs{ 0 }; + + static const uint32_t RingbufferSamples = 32; + using MetricRingbuffer = AZStd::fixed_vector; + struct Metric + { + uint64_t m_totalCalls = 0; + uint64_t m_totalBytes = 0; + MetricRingbuffer m_callHistory; + MetricRingbuffer m_byteHistory; + }; + + struct ComponentStats + { + AZStd::vector m_propertyUpdatesSent; + AZStd::vector m_propertyUpdatesRecv; + AZStd::vector m_rpcsSent; + AZStd::vector m_rpcsRecv; + }; + AZStd::vector m_componentStats; + + void ReserveComponentStats(uint16_t netComponentId, uint16_t propertyCount, uint16_t rpcCount); + void RecordPropertySent(uint16_t netComponentId, uint16_t propertyId, uint32_t totalBytes); + void RecordPropertyReceived(uint16_t netComponentId, uint16_t propertyId, uint32_t totalBytes); + void RecordRpcSent(uint16_t netComponentId, uint16_t rpcId, uint32_t totalBytes); + void RecordRpcReceived(uint16_t netComponentId, uint16_t rpcId, uint32_t totalBytes); + void TickStats(AZ::TimeMs metricFrameTimeMs); + + Metric CalculateComponentPropertyUpdateSentMetrics(uint16_t netComponentId) const; + Metric CalculateComponentPropertyUpdateRecvMetrics(uint16_t netComponentId) const; + Metric CalculateComponentRpcsSentMetrics(uint16_t netComponentId) const; + Metric CalculateComponentRpcsRecvMetrics(uint16_t netComponentId) const; + Metric CalculateTotalPropertyUpdateSentMetrics() const; + Metric CalculateTotalPropertyUpdateRecvMetrics() const; + Metric CalculateTotalRpcsSentMetrics() const; + Metric CalculateTotalRpcsRecvMetrics() const; + }; +} diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Source.jinja index 1726aa17e8..584dde06e0 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponentTypes_Source.jinja @@ -22,12 +22,16 @@ namespace {{ Namespace }} void RegisterMultiplayerComponents() { Multiplayer::MultiplayerComponentRegistry* multiplayerComponentRegistry = GetMultiplayerComponentRegistry(); + Multiplayer::MultiplayerStats& stats = AZ::Interface::Get()->GetStats(); {% for Component in dataFiles %} -{% set ComponentName = Component.attrib['Name'] %} -{% set ComponentBaseName = ComponentName %} -{% if Component.attrib['OverrideComponent']|booleanTrue %} -{% set ComponentBaseName = ComponentName + "Base" %} -{% endif %} +{% set ComponentName = Component.attrib['Name'] %} +{% set ComponentBaseName = ComponentName %} +{% if Component.attrib['OverrideComponent']|booleanTrue %} +{% set ComponentBaseName = ComponentName + "Base" %} +{% endif %} +{% set NetworkInputCount = Component.findall('NetworkInput') | len %} +{% set NetworkPropertyCount = Component.findall('NetworkProperty') | len %} +{% set RpcCount = Component.findall('RemoteProcedure') | len %} { Multiplayer::MultiplayerComponentRegistry::ComponentData componentData; componentData.m_gemName = AZ::Name("{{ Namespace }}"); @@ -35,6 +39,7 @@ namespace {{ Namespace }} componentData.m_componentPropertyNameLookupFunction = {{ ComponentBaseName }}::GetNetworkPropertyName; componentData.m_componentRpcNameLookupFunction = {{ ComponentBaseName }}::GetRpcName; {{ ComponentBaseName }}::s_netComponentId = multiplayerComponentRegistry->RegisterMultiplayerComponent(componentData); + stats.ReserveComponentStats(static_cast({{ ComponentBaseName }}::s_netComponentId), static_cast({{ NetworkPropertyCount }}), static_cast({{ RpcCount }})); } {% endfor %} } diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index 0b1df34ed7..2c460648fc 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -508,7 +508,8 @@ bool {{ ClassName }}::Serialize{{ AutoComponentMacros.GetNetPropertiesSetName(Re static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property) }}), m_{{ LowerFirst(Property.attrib['Name']) }}, "{{ Property.attrib['Name'] }}", - GetNetComponentId(), + static_cast(GetNetComponentId()), + static_cast({{ UpperFirst(Component.attrib['Name']) }}Internal::NetworkProperties::{{ UpperFirst(Property.attrib['Name']) }}), stats ); {% endif %} @@ -646,6 +647,16 @@ enum class RemoteProcedure MAX }; +{% endmacro %} +{% macro DeclareNetworkPropertyEnumerations(Component) %} +enum class NetworkProperties +{ +{% for NetworkProperty in Component.iter('NetworkProperty') %} + {{ UpperFirst(NetworkProperty.attrib['Name']) }}, +{% endfor %} + MAX +}; + {% endmacro %} {# @@ -881,6 +892,9 @@ m_{{ LowerFirst(Property.attrib['Name']) }} = m_{{ LowerFirst(Property.attrib['N {% else %} {% set ControllerBaseName = ControllerName %} {% endif %} +{% set NetworkInputCount = Component.findall('NetworkInput') | len %} +{% set NetworkPropertyCount = Component.findall('NetworkProperty') | len %} +{% set RpcCount = Component.findall('RemoteProcedure') | len %} #include "{{ includeFile }}" #include #include @@ -906,6 +920,7 @@ namespace {{ Component.attrib['Namespace'] }} namespace {{ UpperFirst(Component.attrib['Name']) }}Internal { {{ DeclareRemoteProcedureEnumerations(Component)|indent(8) }} + {{ DeclareNetworkPropertyEnumerations(Component)|indent(8) }} {{ DefineNetworkPropertyDirtyEnumeration(Component, ClassType, 'Authority', 'Authority')|indent(8) }} {{ DefineNetworkPropertyDirtyEnumeration(Component, ClassType, 'Authority', 'Client')|indent(8) }} {{ DefineNetworkPropertyDirtyEnumeration(Component, ClassType, 'Authority', 'Server')|indent(8) }} @@ -1383,12 +1398,32 @@ namespace {{ Component.attrib['Namespace'] }} {% endif %} const char* {{ ComponentBaseName }}::GetNetworkPropertyName([[maybe_unused]] uint16_t propertyIndex) { - return ""; +{% if NetworkPropertyCount > 0 %} + const {{ UpperFirst(Component.attrib['Name']) }}Internal::NetworkProperties propertyId = static_cast<{{ UpperFirst(Component.attrib['Name']) }}Internal::NetworkProperties>(propertyIndex); + switch (propertyId) + { +{% for NetworkProperty in Component.iter('NetworkProperty') %} + case {{ UpperFirst(Component.attrib['Name']) }}Internal::NetworkProperties::{{ UpperFirst(NetworkProperty.attrib['Name']) }}: + return "{{ UpperFirst(NetworkProperty.attrib['Name']) }}"; +{% endfor %} + } +{% endif %} + return "Unknown network property"; } const char* {{ ComponentBaseName }}::GetRpcName([[maybe_unused]] uint16_t rpcIndex) { - return ""; +{% if RpcCount > 0 %} + const {{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure rpcId = static_cast<{{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure>(rpcIndex); + switch (rpcId) + { +{% for RemoteProcedure in Component.iter('RemoteProcedure') %} + case {{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ RemoteProcedure.attrib['Name'] }}: + return "{{ RemoteProcedure.attrib['Name'] }}"; +{% endfor %} + } +{% endif %} + return "Unknown Rpc"; } {% endfor %} } diff --git a/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.h b/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.h index 9efc13ed4b..42f995f764 100644 --- a/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.h +++ b/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.h @@ -104,13 +104,14 @@ namespace Multiplayer template inline void SerializeNetworkPropertyHelper ( - AzNetworking::ISerializer& serializer, - bool modifyRecord, - AzNetworking::FixedSizeBitsetView& bitset, - int32_t bitIndex, - TYPE& value, - const char* name, - [[maybe_unused]] NetComponentId componentId, + AzNetworking::ISerializer& serializer, + bool modifyRecord, + AzNetworking::FixedSizeBitsetView& bitset, + int32_t bitIndex, + TYPE& value, + const char* name, + uint16_t componentId, + uint16_t propertyId, MultiplayerStats& stats ) { @@ -131,13 +132,11 @@ namespace Multiplayer { if (modifyRecord) { - stats.m_propertyUpdatesRecv++; - stats.m_propertyUpdatesRecvBytes += updateSize; + stats.RecordPropertyReceived(componentId, propertyId, updateSize); } else { - stats.m_propertyUpdatesSent++; - stats.m_propertyUpdatesSentBytes += updateSize; + stats.RecordPropertySent(componentId, propertyId, updateSize); } } } diff --git a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp index 67dd678c54..64a5856434 100644 --- a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp @@ -95,26 +95,72 @@ namespace Multiplayer } } + void ComputePerSecondValues(const MultiplayerStats& stats, const MultiplayerStats::Metric& metric, float& outCallsPerSecond, float& outBytesPerSecond) + { + uint64_t summedCalls = 0; + uint64_t summedBytes = 0; + for (uint32_t index = 0; index < MultiplayerStats::RingbufferSamples; ++index) + { + summedCalls += metric.m_callHistory[index]; + 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; + } + + void DrawMetricTitle(const ImVec4& entryColour) + { + ImGui::Columns(6); + + ImGui::TextColored(entryColour, "Name"); ImGui::NextColumn(); + ImGui::TextColored(entryColour, "Category"); ImGui::NextColumn(); + ImGui::TextColored(entryColour, "Total Calls"); ImGui::NextColumn(); + ImGui::TextColored(entryColour, "Total Bytes"); ImGui::NextColumn(); + ImGui::TextColored(entryColour, "Calls/Sec"); ImGui::NextColumn(); + ImGui::TextColored(entryColour, "Bytes/Sec"); ImGui::NextColumn(); + } + + void DrawMetricRow(const char* name, const char* category, const ImVec4& entryColour, const MultiplayerStats& stats, const MultiplayerStats::Metric& metric) + { + float callsPerSecond = 0.0f; + float bytesPerSecond = 0.0f; + ComputePerSecondValues(stats, metric, callsPerSecond, bytesPerSecond); + + ImGui::TextColored(entryColour, "%s", name); ImGui::NextColumn(); + ImGui::TextColored(entryColour, "%s", category); ImGui::NextColumn(); + ImGui::TextColored(entryColour, "%10llu", aznumeric_cast(metric.m_totalCalls)); ImGui::NextColumn(); + ImGui::TextColored(entryColour, "%10llu", aznumeric_cast(metric.m_totalBytes)); ImGui::NextColumn(); + ImGui::TextColored(entryColour, "%10.2f", callsPerSecond); ImGui::NextColumn(); + ImGui::TextColored(entryColour, "%10.2f", bytesPerSecond); ImGui::NextColumn(); + } + void MultiplayerDebugSystemComponent::OnImGuiUpdate() { + const ImVec4 titleColour = ImColor(1.00f, 0.80f, 0.12f); + const ImVec4 entryColour = ImColor(0.32f, 1.00f, 1.00f); + if (m_displayStats) { if (ImGui::Begin("Multiplayer Stats", &m_displayStats, ImGuiWindowFlags_HorizontalScrollbar)) { IMultiplayer* multiplayer = AZ::Interface::Get(); - Multiplayer::MultiplayerStats& stats = multiplayer->GetStats(); + const Multiplayer::MultiplayerStats& stats = multiplayer->GetStats(); ImGui::Text("Multiplayer operating in %s mode", GetEnumString(multiplayer->GetAgentType())); ImGui::Text("Total networked entities: %llu", aznumeric_cast(stats.m_entityCount)); ImGui::Text("Total client connections: %llu", aznumeric_cast(stats.m_clientConnectionCount)); ImGui::Text("Total server connections: %llu", aznumeric_cast(stats.m_serverConnectionCount)); - ImGui::Text("Total property updates sent: %llu", aznumeric_cast(stats.m_propertyUpdatesSent)); - ImGui::Text("Total property updates sent bytes: %llu", aznumeric_cast(stats.m_propertyUpdatesSentBytes)); - ImGui::Text("Total property updates received: %llu", aznumeric_cast(stats.m_propertyUpdatesRecv)); - ImGui::Text("Total property updates received bytes: %llu", aznumeric_cast(stats.m_propertyUpdatesRecvBytes)); - ImGui::Text("Total RPCs sent: %llu", aznumeric_cast(stats.m_rpcsSent)); - ImGui::Text("Total RPCs sent bytes: %llu", aznumeric_cast(stats.m_rpcsSentBytes)); - ImGui::Text("Total RPCs received: %llu", aznumeric_cast(stats.m_rpcsRecv)); - ImGui::Text("Total RPCs received bytes: %llu", aznumeric_cast(stats.m_rpcsRecvBytes)); + + const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateTotalPropertyUpdateSentMetrics(); + const MultiplayerStats::Metric propertyUpdatesRecv = stats.CalculateTotalPropertyUpdateRecvMetrics(); + const MultiplayerStats::Metric rpcsSent = stats.CalculateTotalRpcsSentMetrics(); + const MultiplayerStats::Metric rpcsRecv = stats.CalculateTotalRpcsRecvMetrics(); + + DrawMetricTitle(titleColour); + DrawMetricRow("Total", "PropertyUpdates Sent", entryColour, stats, propertyUpdatesSent); + DrawMetricRow("Total", "PropertyUpdates Received", entryColour, stats, propertyUpdatesRecv); + DrawMetricRow("Total", "Rpcs Sent", entryColour, stats, rpcsSent); + DrawMetricRow("Total", "Rpcs Received", entryColour, stats, rpcsRecv); } ImGui::End(); } diff --git a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.h b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.h index 81940423d7..722f25c7d0 100644 --- a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.h @@ -52,5 +52,7 @@ namespace Multiplayer #endif private: bool m_displayStats = 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 b933e71a97..c21ff10aa0 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -513,14 +513,20 @@ namespace Multiplayer AZLOG_INFO("Total networked entities: %llu", aznumeric_cast(stats.m_entityCount)); AZLOG_INFO("Total client connections: %llu", aznumeric_cast(stats.m_clientConnectionCount)); AZLOG_INFO("Total server connections: %llu", aznumeric_cast(stats.m_serverConnectionCount)); - AZLOG_INFO("Total property updates sent: %llu", aznumeric_cast(stats.m_propertyUpdatesSent)); - AZLOG_INFO("Total property updates sent bytes: %llu", aznumeric_cast(stats.m_propertyUpdatesSentBytes)); - AZLOG_INFO("Total property updates received: %llu", aznumeric_cast(stats.m_propertyUpdatesRecv)); - AZLOG_INFO("Total property updates received bytes: %llu", aznumeric_cast(stats.m_propertyUpdatesRecvBytes)); - AZLOG_INFO("Total RPCs sent: %llu", aznumeric_cast(stats.m_rpcsSent)); - AZLOG_INFO("Total RPCs sent bytes: %llu", aznumeric_cast(stats.m_rpcsSentBytes)); - AZLOG_INFO("Total RPCs received: %llu", aznumeric_cast(stats.m_rpcsRecv)); - AZLOG_INFO("Total RPCs received bytes: %llu", aznumeric_cast(stats.m_rpcsRecvBytes)); + + const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateTotalPropertyUpdateSentMetrics(); + const MultiplayerStats::Metric propertyUpdatesRecv = stats.CalculateTotalPropertyUpdateRecvMetrics(); + const MultiplayerStats::Metric rpcsSent = stats.CalculateTotalRpcsSentMetrics(); + const MultiplayerStats::Metric rpcsRecv = stats.CalculateTotalRpcsRecvMetrics(); + + AZLOG_INFO("Total property updates sent: %llu", aznumeric_cast(propertyUpdatesSent.m_totalCalls)); + AZLOG_INFO("Total property updates sent bytes: %llu", aznumeric_cast(propertyUpdatesSent.m_totalBytes)); + AZLOG_INFO("Total property updates received: %llu", aznumeric_cast(propertyUpdatesRecv.m_totalCalls)); + AZLOG_INFO("Total property updates received bytes: %llu", aznumeric_cast(propertyUpdatesRecv.m_totalBytes)); + AZLOG_INFO("Total RPCs sent: %llu", aznumeric_cast(rpcsSent.m_totalCalls)); + AZLOG_INFO("Total RPCs sent bytes: %llu", aznumeric_cast(rpcsSent.m_totalBytes)); + AZLOG_INFO("Total RPCs received: %llu", aznumeric_cast(rpcsRecv.m_totalCalls)); + AZLOG_INFO("Total RPCs received bytes: %llu", aznumeric_cast(rpcsRecv.m_totalBytes)); } void MultiplayerSystemComponent::OnConsoleCommandInvoked diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp index 0edb5db250..42bc9d99d8 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp @@ -449,8 +449,7 @@ namespace Multiplayer { // Received rpc metrics, log rpc sent, number of bytes, and the componentId/rpcId for bandwidth metrics MultiplayerStats& stats = AZ::Interface::Get()->GetStats(); - stats.m_rpcsSent++; - stats.m_rpcsSentBytes += entityRpcMessage.GetEstimatedSerializeSize(); + stats.RecordRpcSent(static_cast(entityRpcMessage.GetComponentId()), entityRpcMessage.GetRpcMessageType(), entityRpcMessage.GetEstimatedSerializeSize()); m_replicationManager.AddDeferredRpcMessage(entityRpcMessage); } @@ -633,8 +632,7 @@ namespace Multiplayer { // Received rpc metrics, log rpc received, time spent, number of bytes, and the componentId/rpcId for bandwidth metrics MultiplayerStats& stats = AZ::Interface::Get()->GetStats(); - stats.m_rpcsRecv++; - stats.m_rpcsRecvBytes += entityRpcMessage.GetEstimatedSerializeSize(); + stats.RecordRpcReceived(static_cast(entityRpcMessage.GetComponentId()), entityRpcMessage.GetRpcMessageType(), entityRpcMessage.GetEstimatedSerializeSize()); if (!m_netBindComponent) { diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake index c1f8fb3111..58601423da 100644 --- a/Gems/Multiplayer/Code/multiplayer_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_files.cmake @@ -11,6 +11,8 @@ set(FILES Include/IMultiplayer.h + Include/MultiplayerStats.cpp + Include/MultiplayerStats.h Source/Multiplayer_precompiled.cpp Source/Multiplayer_precompiled.h Source/MultiplayerSystemComponent.cpp