123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include "MultiplayerDebugPerEntityReporter.h"
- #include <AzCore/Component/TransformBus.h>
- #include <AzFramework/Entity/EntityDebugDisplayBus.h>
- #include <Multiplayer/IMultiplayer.h>
- #if defined(IMGUI_ENABLED)
- #include <imgui/imgui.h>
- #endif
- AZ_CVAR(float, net_DebugEntities_ShowAboveKbps, 1.f, nullptr, AZ::ConsoleFunctorFlags::Null,
- "Prints bandwidth on network entities with higher kpbs than this value");
- AZ_CVAR(float, net_DebugEntities_WarnAboveKbps, 10.f, nullptr, AZ::ConsoleFunctorFlags::Null,
- "Prints bandwidth on network entities with higher kpbs than this value");
- AZ_CVAR(AZ::Color, net_DebugEntities_WarningColor, AZ::Colors::Red, nullptr, AZ::ConsoleFunctorFlags::Null,
- "If true, prints debug text over entities that use a considerable amount of network traffic");
- AZ_CVAR(AZ::Color, net_DebugEntities_BelowWarningColor, AZ::Colors::Grey, nullptr, AZ::ConsoleFunctorFlags::Null,
- "If true, prints debug text over entities that use a considerable amount of network traffic");
- namespace Multiplayer
- {
- #if defined(IMGUI_ENABLED)
- static const ImVec4 k_ImGuiTomato = ImVec4(1.0f, 0.4f, 0.3f, 1.0f);
- static const ImVec4 k_ImGuiKhaki = ImVec4(0.9f, 0.8f, 0.5f, 1.0f);
- static const ImVec4 k_ImGuiCyan = ImVec4(0.5f, 1.0f, 1.0f, 1.0f);
- static const ImVec4 k_ImGuiDusk = ImVec4(0.7f, 0.7f, 1.0f, 1.0f);
- static const ImVec4 k_ImGuiWhite = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
- // --------------------------------------------------------------------------------------------
- template <typename Reporter>
- bool ReplicatedStateTreeNode(const AZStd::string& name, Reporter& report, const ImVec4& color, int depth = 0)
- {
- const int defaultPadAmount = 55;
- const int depthReduction = 3;
- ImGui::PushStyleColor(ImGuiCol_Text, color);
- const bool expanded = ImGui::TreeNode(name.c_str(),
- "%-*s %7.2f kbps %7.2f B Avg. %4zu B Max %10zu B Payload",
- defaultPadAmount - depthReduction * depth,
- name.c_str(),
- report.GetKbitsPerSecond(),
- report.GetAverageBytes(),
- report.GetMaxBytes(),
- report.GetTotalBytes());
- ImGui::PopStyleColor();
- return expanded;
- }
- // --------------------------------------------------------------------------------------------
- void DisplayReplicatedStateReport(AZStd::map<AZStd::string, MultiplayerDebugComponentReporter>& componentReports, float kbpsWarn, float maxWarn)
- {
- for (auto& componentPair : componentReports)
- {
- ImGui::Separator();
- MultiplayerDebugComponentReporter& componentReport = componentPair.second;
- if (ReplicatedStateTreeNode(componentPair.first, componentReport, k_ImGuiCyan, 1))
- {
- ImGui::Separator();
- ImGui::Columns(6, "replicated_field_columns");
- ImGui::NextColumn();
- ImGui::Text("kbps");
- ImGui::NextColumn();
- ImGui::Text("Avg. Bytes");
- ImGui::NextColumn();
- ImGui::Text("Min Bytes");
- ImGui::NextColumn();
- ImGui::Text("Max Bytes");
- ImGui::NextColumn();
- ImGui::Text("Total Bytes");
- ImGui::NextColumn();
- auto fieldReports = componentReport.GetFieldReports();
- for (auto& fieldPair : fieldReports)
- {
- MultiplayerDebugByteReporter& fieldReport = *fieldPair.second;
- const float kbitsLastSecond = fieldReport.GetKbitsPerSecond();
- const ImVec4* textColor = &k_ImGuiWhite;
- if (aznumeric_cast<float>(fieldReport.GetMaxBytes()) > maxWarn)
- {
- textColor = &k_ImGuiKhaki;
- }
- if (kbitsLastSecond > kbpsWarn)
- {
- textColor = &k_ImGuiTomato;
- }
- ImGui::PushStyleColor(ImGuiCol_Text, *textColor);
- ImGui::Text("%s", fieldPair.first.c_str());
- ImGui::NextColumn();
- ImGui::Text("%.2f", kbitsLastSecond);
- ImGui::NextColumn();
- ImGui::Text("%.2f", fieldReport.GetAverageBytes());
- ImGui::NextColumn();
- ImGui::Text("%zu", fieldReport.GetMinBytes());
- ImGui::NextColumn();
- ImGui::Text("%zu", fieldReport.GetMaxBytes());
- ImGui::NextColumn();
- ImGui::Text("%zu", fieldReport.GetTotalBytes());
- ImGui::NextColumn();
- ImGui::PopStyleColor();
- }
- ImGui::Columns(1);
- ImGui::TreePop();
- }
- }
- }
- #endif
- MultiplayerDebugPerEntityReporter::MultiplayerDebugPerEntityReporter()
- : m_updateDebugOverlay([this]() { UpdateDebugOverlay(); }, AZ::Name("UpdateDebugPerEntityOverlay"))
- {
- m_updateDebugOverlay.Enqueue(AZ::Time::ZeroTimeMs, true);
- m_eventHandlers.m_entitySerializeStart = decltype(m_eventHandlers.m_entitySerializeStart)([this](AzNetworking::SerializerMode mode, AZ::EntityId entityId, const char* entityName)
- {
- RecordEntitySerializeStart(mode, entityId, entityName);
- });
- m_eventHandlers.m_componentSerializeEnd = decltype(m_eventHandlers.m_componentSerializeEnd)([this](AzNetworking::SerializerMode mode,
- NetComponentId netComponentId)
- {
- RecordComponentSerializeEnd(mode, netComponentId);
- });
- m_eventHandlers.m_entitySerializeStop = decltype(m_eventHandlers.m_entitySerializeStop)([this](AzNetworking::SerializerMode mode, AZ::EntityId entityId, const char* entityName)
- {
- RecordEntitySerializeStop(mode, entityId, entityName);
- });
- m_eventHandlers.m_propertySent = decltype(m_eventHandlers.m_propertySent)([this](NetComponentId netComponentId,
- PropertyIndex propertyId, uint32_t totalBytes)
- {
- RecordPropertySent(netComponentId, propertyId, totalBytes);
- });
- m_eventHandlers.m_propertyReceived = decltype(m_eventHandlers.m_propertyReceived)([this](NetComponentId netComponentId,
- PropertyIndex propertyId, uint32_t totalBytes)
- {
- RecordPropertyReceived(netComponentId, propertyId, totalBytes);
- });
- m_eventHandlers.m_rpcSent = decltype(m_eventHandlers.m_rpcSent)([this](AZ::EntityId entityId, const char* entityName,
- NetComponentId netComponentId,
- RpcIndex rpcId, uint32_t totalBytes)
- {
- RecordRpcSent(entityId, entityName, netComponentId, rpcId, totalBytes);
- });
- m_eventHandlers.m_rpcReceived = decltype(m_eventHandlers.m_rpcReceived)([this](AZ::EntityId entityId, const char* entityName,
- NetComponentId netComponentId,
- RpcIndex rpcId, uint32_t totalBytes)
- {
- RecordRpcSent(entityId, entityName, netComponentId, rpcId, totalBytes);
- });
- GetMultiplayer()->GetStats().ConnectHandlers(m_eventHandlers);
- }
- // --------------------------------------------------------------------------------------------
- void MultiplayerDebugPerEntityReporter::OnImGuiUpdate()
- {
- #if defined(IMGUI_ENABLED)
- static ImGuiTextFilter filter;
- filter.Draw();
- if (ImGui::CollapsingHeader("Receiving Entities"))
- {
- for (AZStd::pair<AZ::EntityId, MultiplayerDebugEntityReporter>& entityPair : m_receivingEntityReports)
- {
- if (!filter.PassFilter(entityPair.second.GetEntityName()))
- {
- continue;
- }
- ImGui::Separator();
- if (ReplicatedStateTreeNode(entityPair.second.GetEntityName(), entityPair.second, k_ImGuiDusk))
- {
- DisplayReplicatedStateReport(entityPair.second.GetComponentReports(), m_replicatedStateKbpsWarn, m_replicatedStateMaxSizeWarn);
- ImGui::TreePop();
- }
- }
- }
- if (ImGui::CollapsingHeader("Sending Entities"))
- {
- for (AZStd::pair<AZ::EntityId, MultiplayerDebugEntityReporter>& entityPair : m_sendingEntityReports)
- {
- const char* name = entityPair.second.GetEntityName();
- if (!filter.PassFilter(name))
- {
- continue;
- }
- ImGui::Separator();
- if (ReplicatedStateTreeNode(name, entityPair.second, k_ImGuiDusk))
- {
- DisplayReplicatedStateReport(entityPair.second.GetComponentReports(), m_replicatedStateKbpsWarn, m_replicatedStateMaxSizeWarn);
- ImGui::TreePop();
- }
- }
- }
- #endif
- }
- void MultiplayerDebugPerEntityReporter::RecordEntitySerializeStart(AzNetworking::SerializerMode mode,
- [[maybe_unused]] AZ::EntityId entityId, [[maybe_unused]] const char* entityName)
- {
- switch (mode)
- {
- case AzNetworking::SerializerMode::ReadFromObject:
- m_currentSendingEntityReport.Reset();
- m_currentSendingEntityReport.SetEntityName(entityName);
- break;
- case AzNetworking::SerializerMode::WriteToObject:
- m_currentReceivingEntityReport.Reset();
- m_currentReceivingEntityReport.SetEntityName(entityName);
- break;
- }
- }
- void MultiplayerDebugPerEntityReporter::RecordComponentSerializeEnd(AzNetworking::SerializerMode mode, [[maybe_unused]] NetComponentId
- netComponentId)
- {
- switch (mode)
- {
- case AzNetworking::SerializerMode::ReadFromObject:
- m_currentSendingEntityReport.ReportFragmentEnd();
- break;
- case AzNetworking::SerializerMode::WriteToObject:
- m_currentReceivingEntityReport.ReportFragmentEnd();
- break;
- }
- }
- void MultiplayerDebugPerEntityReporter::RecordEntitySerializeStop(AzNetworking::SerializerMode mode,
- [[maybe_unused]] AZ::EntityId entityId, [[maybe_unused]] const char* entityName)
- {
- switch (mode)
- {
- case AzNetworking::SerializerMode::ReadFromObject:
- m_sendingEntityReports[entityId].Combine(m_currentSendingEntityReport);
- break;
- case AzNetworking::SerializerMode::WriteToObject:
- m_receivingEntityReports[entityId].Combine(m_currentReceivingEntityReport);
- break;
- }
- }
- void MultiplayerDebugPerEntityReporter::RecordPropertySent(
- NetComponentId netComponentId,
- PropertyIndex propertyId,
- uint32_t totalBytes)
- {
- if (const MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry())
- {
- m_currentSendingEntityReport.ReportField(static_cast<AZ::u32>(netComponentId),
- componentRegistry->GetComponentName(netComponentId),
- componentRegistry->GetComponentPropertyName(netComponentId, propertyId), totalBytes);
- }
- }
- void MultiplayerDebugPerEntityReporter::RecordPropertyReceived(
- NetComponentId netComponentId,
- PropertyIndex propertyId,
- uint32_t totalBytes)
- {
- if (const MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry())
- {
- m_currentReceivingEntityReport.ReportField(static_cast<AZ::u32>(netComponentId),
- componentRegistry->GetComponentName(netComponentId),
- componentRegistry->GetComponentPropertyName(netComponentId, propertyId), totalBytes);
- }
- }
- void MultiplayerDebugPerEntityReporter::RecordRpcSent(AZ::EntityId entityId, const char* entityName, NetComponentId netComponentId,
- RpcIndex rpcId, uint32_t totalBytes)
- {
- if (const MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry())
- {
- // MultiplayerDebugByteReporter requires a
- RecordEntitySerializeStart(AzNetworking::SerializerMode::ReadFromObject, entityId, entityName);
- m_currentSendingEntityReport.ReportField(static_cast<AZ::u32>(netComponentId),
- componentRegistry->GetComponentName(netComponentId),
- componentRegistry->GetComponentRpcName(netComponentId, rpcId), totalBytes);
- RecordComponentSerializeEnd(AzNetworking::SerializerMode::ReadFromObject, netComponentId);
- RecordEntitySerializeStop(AzNetworking::SerializerMode::ReadFromObject, entityId, entityName);
- }
- }
- void MultiplayerDebugPerEntityReporter::RecordRpcReceived(
- AZ::EntityId entityId, const char* entityName,
- NetComponentId netComponentId,
- RpcIndex rpcId,
- uint32_t totalBytes)
- {
- if (const MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry())
- {
- RecordEntitySerializeStart(AzNetworking::SerializerMode::WriteToObject, entityId, entityName);
- m_currentReceivingEntityReport.ReportField(static_cast<AZ::u32>(netComponentId),
- componentRegistry->GetComponentName(netComponentId),
- componentRegistry->GetComponentRpcName(netComponentId, rpcId), totalBytes);
- RecordComponentSerializeEnd(AzNetworking::SerializerMode::WriteToObject, netComponentId);
- RecordEntitySerializeStop(AzNetworking::SerializerMode::WriteToObject, entityId, entityName);
- }
- }
- void MultiplayerDebugPerEntityReporter::UpdateDebugOverlay()
- {
- m_networkEntitiesTraffic.clear();
- // Merging up and down traffic to provide a unified debug text per entity
- for (AZStd::pair<AZ::EntityId, MultiplayerDebugEntityReporter>& entityPair : m_receivingEntityReports)
- {
- m_networkEntitiesTraffic[entityPair.first].m_name = entityPair.second.GetEntityName();
- m_networkEntitiesTraffic[entityPair.first].m_down = entityPair.second.GetKbitsPerSecond();
- }
- for (AZStd::pair<AZ::EntityId, MultiplayerDebugEntityReporter>& entityPair : m_sendingEntityReports)
- {
- m_networkEntitiesTraffic[entityPair.first].m_name = entityPair.second.GetEntityName();
- m_networkEntitiesTraffic[entityPair.first].m_up = entityPair.second.GetKbitsPerSecond();
- }
- if (m_debugDisplay == nullptr)
- {
- AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
- AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, AzFramework::g_defaultSceneEntityDebugDisplayId);
- m_debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus);
- }
- const AZ::u32 stateBefore = m_debugDisplay->GetState();
- for (const AZStd::pair<AZ::EntityId, NetworkEntityTraffic>& networkEntity : m_networkEntitiesTraffic)
- {
- if (networkEntity.second.m_down < net_DebugEntities_ShowAboveKbps && networkEntity.second.m_up < net_DebugEntities_ShowAboveKbps)
- {
- continue;
- }
- if (networkEntity.second.m_down > net_DebugEntities_WarnAboveKbps || networkEntity.second.m_up > net_DebugEntities_WarnAboveKbps)
- {
- m_debugDisplay->SetColor(net_DebugEntities_WarningColor);
- }
- else
- {
- m_debugDisplay->SetColor(net_DebugEntities_BelowWarningColor);
- }
- if (networkEntity.second.m_down > net_DebugEntities_ShowAboveKbps && networkEntity.second.m_up > net_DebugEntities_ShowAboveKbps)
- {
- azsnprintf(m_statusBuffer, AZ_ARRAY_SIZE(m_statusBuffer), "[%s] %.0f down / %0.f up (kbps)", networkEntity.second.m_name,
- networkEntity.second.m_down, networkEntity.second.m_up);
- }
- else if (networkEntity.second.m_down > net_DebugEntities_ShowAboveKbps)
- {
- azsnprintf(m_statusBuffer, AZ_ARRAY_SIZE(m_statusBuffer), "[%s] %.0f down (kbps)", networkEntity.second.m_name, networkEntity.second.m_down);
- }
- else
- {
- azsnprintf(m_statusBuffer, AZ_ARRAY_SIZE(m_statusBuffer), "[%s] %.0f up (kbps)", networkEntity.second.m_name, networkEntity.second.m_up);
- }
- AZ::Vector3 entityPosition = AZ::Vector3::CreateZero();
- AZ::TransformBus::EventResult(entityPosition, networkEntity.first, &AZ::TransformBus::Events::GetWorldTranslation);
- if (entityPosition.IsZero() == false)
- {
- constexpr bool centerText = true;
- m_debugDisplay->DrawTextLabel(entityPosition, 1.0f, m_statusBuffer, centerText, 0, 0);
- }
- }
- m_debugDisplay->SetState(stateBefore);
- }
- }
|