Merging latest development
Signed-off-by: kberg-amzn <karlberg@amazon.com>
This commit is contained in:
@@ -179,6 +179,10 @@ if (PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
ly_add_googletest(
|
||||
NAME Gem::Multiplayer.Tests
|
||||
)
|
||||
ly_add_googlebenchmark(
|
||||
NAME Gem::Multiplayer.Benchmarks
|
||||
TARGET Gem::Multiplayer.Tests
|
||||
)
|
||||
|
||||
if (PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
ly_add_target(
|
||||
|
||||
@@ -193,6 +193,7 @@ namespace Multiplayer
|
||||
friend class EntityReplicationManager;
|
||||
|
||||
friend class HierarchyTests;
|
||||
friend class HierarchyBenchmarkBase;
|
||||
};
|
||||
|
||||
bool NetworkRoleHasController(NetEntityRole networkRole);
|
||||
|
||||
@@ -96,5 +96,7 @@ namespace Multiplayer
|
||||
|
||||
//! Set to false when deactivating or otherwise not to be included in hierarchy considerations.
|
||||
bool m_isHierarchyEnabled = true;
|
||||
|
||||
friend class HierarchyBenchmarkBase;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -49,5 +49,13 @@ namespace Multiplayer
|
||||
//! Sets the state of connection whether update messages can be sent or not.
|
||||
//! @param canSendUpdates the state value
|
||||
virtual void SetCanSendUpdates(bool canSendUpdates) = 0;
|
||||
|
||||
//! Fetches the state of connection whether handshake logic has completed
|
||||
//! @return true if handshake has completed
|
||||
virtual bool DidHandshake() const = 0;
|
||||
|
||||
//! Sets the state of connection whether handshake logic has completed
|
||||
//! @param didHandshake if handshake logic has completed
|
||||
virtual void SetDidHandshake(bool didHandshake) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ namespace Multiplayer
|
||||
using NotifyClientMigrationEvent = AZ::Event<AzNetworking::ConnectionId, const HostId&, uint64_t, ClientInputId, NetEntityId>;
|
||||
using NotifyEntityMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, const HostId&>;
|
||||
using ConnectionAcquiredEvent = AZ::Event<MultiplayerAgentDatum>;
|
||||
using ServerAcceptanceReceivedEvent = AZ::Event<>;
|
||||
using SessionInitEvent = AZ::Event<AzNetworking::INetworkInterface*>;
|
||||
using SessionShutdownEvent = AZ::Event<AzNetworking::INetworkInterface*>;
|
||||
|
||||
@@ -122,6 +123,10 @@ namespace Multiplayer
|
||||
//! @param handler The ConnectionAcquiredEvent Handler to add
|
||||
virtual void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) = 0;
|
||||
|
||||
//! Adds a ServerAcceptanceReceived Handler which is invoked when the client receives the accept packet from the server.
|
||||
//! @param handler The ServerAcceptanceReceived Handler to add
|
||||
virtual void AddServerAcceptanceReceivedHandler(ServerAcceptanceReceivedEvent::Handler& handler) = 0;
|
||||
|
||||
//! Adds a SessionInitEvent Handler which is invoked when a new network session starts.
|
||||
//! @param handler The SessionInitEvent Handler to add
|
||||
virtual void AddSessionInitHandler(SessionInitEvent::Handler& handler) = 0;
|
||||
|
||||
@@ -33,6 +33,8 @@ namespace Multiplayer
|
||||
void Update() override;
|
||||
bool CanSendUpdates() const override;
|
||||
void SetCanSendUpdates(bool canSendUpdates) override;
|
||||
bool DidHandshake() const override;
|
||||
void SetDidHandshake(bool didHandshake) override;
|
||||
//! @}
|
||||
|
||||
const AZStd::string& GetProviderTicket() const;
|
||||
@@ -43,6 +45,7 @@ namespace Multiplayer
|
||||
AZStd::string m_providerTicket;
|
||||
AzNetworking::IConnection* m_connection = nullptr;
|
||||
bool m_canSendUpdates = true;
|
||||
bool m_didHandshake = false;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -27,4 +27,14 @@ namespace Multiplayer
|
||||
{
|
||||
m_providerTicket = ticket;
|
||||
}
|
||||
|
||||
inline bool ClientToServerConnectionData::DidHandshake() const
|
||||
{
|
||||
return m_didHandshake;
|
||||
}
|
||||
|
||||
inline void ClientToServerConnectionData::SetDidHandshake(bool didHandshake)
|
||||
{
|
||||
m_didHandshake = didHandshake;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace Multiplayer
|
||||
void Update() override;
|
||||
bool CanSendUpdates() const override;
|
||||
void SetCanSendUpdates(bool canSendUpdates) override;
|
||||
bool DidHandshake() const override;
|
||||
void SetDidHandshake(bool didHandshake) override;
|
||||
//! @}
|
||||
|
||||
NetworkEntityHandle GetPrimaryPlayerEntity();
|
||||
@@ -53,6 +55,7 @@ namespace Multiplayer
|
||||
AZStd::string m_providerTicket;
|
||||
AzNetworking::IConnection* m_connection = nullptr;
|
||||
bool m_canSendUpdates = false;
|
||||
bool m_didHandshake = false;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -37,4 +37,14 @@ namespace Multiplayer
|
||||
{
|
||||
m_providerTicket = ticket;
|
||||
}
|
||||
|
||||
inline bool ServerToClientConnectionData::DidHandshake() const
|
||||
{
|
||||
return m_didHandshake;
|
||||
}
|
||||
|
||||
inline void ServerToClientConnectionData::SetDidHandshake(bool didHandshake)
|
||||
{
|
||||
m_didHandshake = didHandshake;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* 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 "MultiplayerDebugHierarchyReporter.h"
|
||||
|
||||
#include <Atom/RPI.Public/ViewportContext.h>
|
||||
#include <Atom/RPI.Public/ViewportContextBus.h>
|
||||
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
|
||||
#include <AzFramework/Visibility/IVisibilitySystem.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
|
||||
|
||||
#if defined(IMGUI_ENABLED)
|
||||
#include <imgui/imgui.h>
|
||||
#endif
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
MultiplayerDebugHierarchyReporter::MultiplayerDebugHierarchyReporter()
|
||||
: m_updateDebugOverlay([this]() { UpdateDebugOverlay(); }, AZ::Name("UpdateHierarchyDebug"))
|
||||
{
|
||||
CollectHierarchyRoots();
|
||||
|
||||
AZ::EntitySystemBus::Handler::BusConnect();
|
||||
m_updateDebugOverlay.Enqueue(AZ::TimeMs{ 0 }, true);
|
||||
}
|
||||
|
||||
MultiplayerDebugHierarchyReporter::~MultiplayerDebugHierarchyReporter()
|
||||
{
|
||||
AZ::EntitySystemBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
void MultiplayerDebugHierarchyReporter::OnImGuiUpdate()
|
||||
{
|
||||
#if defined(IMGUI_ENABLED)
|
||||
ImGui::Text("Hierarchies");
|
||||
ImGui::Separator();
|
||||
|
||||
for (const auto& root : m_hierarchyRoots)
|
||||
{
|
||||
if (const auto* rootComponent = root.second.m_rootComponent)
|
||||
{
|
||||
if (rootComponent->IsHierarchicalRoot())
|
||||
{
|
||||
const AZStd::vector<AZ::Entity*>& hierarchicalChildren = rootComponent->GetHierarchicalEntities();
|
||||
|
||||
if (ImGui::TreeNode(rootComponent->GetEntity()->GetName().c_str(),
|
||||
"[%s] %4zu members",
|
||||
rootComponent->GetEntity()->GetName().c_str(),
|
||||
hierarchicalChildren.size()))
|
||||
{
|
||||
ImGui::Separator();
|
||||
ImGui::Columns(4, "hierarchy_columns");
|
||||
ImGui::Text("EntityId");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("NetEntityId");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Entity Name");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Role");
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Columns(4, "hierarchy child info");
|
||||
|
||||
bool firstEntity = true;
|
||||
for (const AZ::Entity* entity : hierarchicalChildren)
|
||||
{
|
||||
ImGui::Text("%s", entity->GetId().ToString().c_str());
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%u", GetMultiplayer()->GetNetworkEntityManager()->GetNetEntityIdById(entity->GetId()));
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%s", entity->GetName().c_str());
|
||||
ImGui::NextColumn();
|
||||
|
||||
if (firstEntity)
|
||||
{
|
||||
ImGui::Text("Root node");
|
||||
}
|
||||
else if (entity->FindComponent<NetworkHierarchyRootComponent>())
|
||||
{
|
||||
ImGui::Text("Inner root node");
|
||||
}
|
||||
else if (entity->FindComponent<NetworkHierarchyChildComponent>())
|
||||
{
|
||||
ImGui::Text("Child node");
|
||||
}
|
||||
ImGui::NextColumn();
|
||||
|
||||
firstEntity = false;
|
||||
}
|
||||
|
||||
ImGui::Columns(1);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
if (ImGui::InputFloat("Awareness Radius", &m_awarenessRadius))
|
||||
{
|
||||
CollectHierarchyRoots();
|
||||
}
|
||||
if (ImGui::Button("Refresh"))
|
||||
{
|
||||
CollectHierarchyRoots();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void MultiplayerDebugHierarchyReporter::UpdateDebugOverlay()
|
||||
{
|
||||
if (!m_hierarchyRoots.empty())
|
||||
{
|
||||
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();
|
||||
m_debugDisplay->SetColor(AZ::Colors::White);
|
||||
|
||||
for (const auto& root : m_hierarchyRoots)
|
||||
{
|
||||
if (const auto* rootComponent = root.second.m_rootComponent)
|
||||
{
|
||||
if (rootComponent->IsHierarchicalRoot())
|
||||
{
|
||||
const AZStd::vector<AZ::Entity*>& hierarchicalChildren = rootComponent->GetHierarchicalEntities();
|
||||
|
||||
azsprintf(m_statusBuffer, "Hierarchy [%s] %u members", rootComponent->GetEntity()->GetName().c_str(),
|
||||
aznumeric_cast<AZ::u32>(hierarchicalChildren.size()));
|
||||
|
||||
AZ::Vector3 entityPosition = rootComponent->GetEntity()->GetTransform()->GetWorldTranslation();
|
||||
constexpr bool centerText = true;
|
||||
m_debugDisplay->DrawTextLabel(entityPosition, 1.0f, m_statusBuffer, centerText, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_debugDisplay->SetState(stateBefore);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerDebugHierarchyReporter::OnEntityActivated(const AZ::EntityId& entityId)
|
||||
{
|
||||
if (const AZ::Entity* childEntity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(entityId))
|
||||
{
|
||||
if (auto* rootComponent = childEntity->FindComponent<NetworkHierarchyRootComponent>())
|
||||
{
|
||||
HierarchyRootInfo info;
|
||||
info.m_rootComponent = rootComponent;
|
||||
rootComponent->BindNetworkHierarchyChangedEventHandler(info.m_changedEvent);
|
||||
rootComponent->BindNetworkHierarchyLeaveEventHandler(info.m_leaveEvent);
|
||||
|
||||
m_hierarchyRoots.insert(AZStd::make_pair(rootComponent, info));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerDebugHierarchyReporter::OnEntityDeactivated(const AZ::EntityId& entityId)
|
||||
{
|
||||
if (const AZ::Entity* childEntity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(entityId))
|
||||
{
|
||||
if (auto* rootComponent = childEntity->FindComponent<NetworkHierarchyRootComponent>())
|
||||
{
|
||||
m_hierarchyRoots.erase(rootComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerDebugHierarchyReporter::CollectHierarchyRoots()
|
||||
{
|
||||
m_hierarchyRoots.clear();
|
||||
AZ::Sphere awarenessSphere(AZ::Vector3::CreateZero(), m_awarenessRadius);
|
||||
|
||||
const auto viewportContextManager = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
|
||||
if (const auto viewportContext = viewportContextManager->GetDefaultViewportContext())
|
||||
{
|
||||
awarenessSphere.SetCenter(viewportContext->GetCameraTransform().GetTranslation());
|
||||
}
|
||||
|
||||
AZStd::vector<AzFramework::VisibilityEntry*> gatheredEntries;
|
||||
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->GetDefaultVisibilityScene()->Enumerate(awarenessSphere,
|
||||
[&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData)
|
||||
{
|
||||
gatheredEntries.reserve(gatheredEntries.size() + nodeData.m_entries.size());
|
||||
for (AzFramework::VisibilityEntry* visEntry : nodeData.m_entries)
|
||||
{
|
||||
if (visEntry->m_typeFlags & AzFramework::VisibilityEntry::TypeFlags::TYPE_Entity)
|
||||
{
|
||||
gatheredEntries.push_back(visEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
for (const AzFramework::VisibilityEntry* entry : gatheredEntries)
|
||||
{
|
||||
const AZ::Entity* entity = static_cast<AZ::Entity*>(entry->m_userData);
|
||||
if (auto* rootComponent = entity->FindComponent<NetworkHierarchyRootComponent>())
|
||||
{
|
||||
if (awarenessSphere.GetCenter().GetDistanceEstimate(entity->GetTransform()->GetWorldTranslation()) < m_awarenessRadius)
|
||||
{
|
||||
HierarchyRootInfo info;
|
||||
info.m_rootComponent = rootComponent;
|
||||
rootComponent->BindNetworkHierarchyChangedEventHandler(info.m_changedEvent);
|
||||
rootComponent->BindNetworkHierarchyLeaveEventHandler(info.m_leaveEvent);
|
||||
|
||||
m_hierarchyRoots.insert(AZStd::make_pair(rootComponent, info));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/EntityBus.h>
|
||||
#include <AzCore/EBus/ScheduledEvent.h>
|
||||
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
/**
|
||||
* /brief Provides ImGui and debug draw hierarchy information at runtime.
|
||||
*/
|
||||
class MultiplayerDebugHierarchyReporter
|
||||
: public AZ::EntitySystemBus::Handler
|
||||
{
|
||||
public:
|
||||
MultiplayerDebugHierarchyReporter();
|
||||
~MultiplayerDebugHierarchyReporter() override;
|
||||
|
||||
//! Main update loop.
|
||||
void OnImGuiUpdate();
|
||||
|
||||
//! Draws hierarchy information over hierarchy root entities.
|
||||
void UpdateDebugOverlay();
|
||||
|
||||
//! EntitySystemBus overrides.
|
||||
//! @{
|
||||
void OnEntityActivated(const AZ::EntityId& entityId) override;
|
||||
void OnEntityDeactivated(const AZ::EntityId& entityId) override;
|
||||
//! @}
|
||||
|
||||
private:
|
||||
AZ::ScheduledEvent m_updateDebugOverlay;
|
||||
|
||||
AzFramework::DebugDisplayRequests* m_debugDisplay = nullptr;
|
||||
|
||||
struct HierarchyRootInfo
|
||||
{
|
||||
NetworkHierarchyRootComponent* m_rootComponent = nullptr;
|
||||
NetworkHierarchyChangedEvent::Handler m_changedEvent;
|
||||
NetworkHierarchyLeaveEvent::Handler m_leaveEvent;
|
||||
};
|
||||
|
||||
AZStd::unordered_map<NetworkHierarchyRootComponent*, HierarchyRootInfo> m_hierarchyRoots;
|
||||
void CollectHierarchyRoots();
|
||||
|
||||
char m_statusBuffer[100] = {};
|
||||
|
||||
float m_awarenessRadius = 1000.f;
|
||||
};
|
||||
}
|
||||
@@ -76,6 +76,7 @@ namespace Multiplayer
|
||||
ImGui::Checkbox("Networking Stats", &m_displayNetworkingStats);
|
||||
ImGui::Checkbox("Multiplayer Stats", &m_displayMultiplayerStats);
|
||||
ImGui::Checkbox("Multiplayer Entity Stats", &m_displayPerEntityStats);
|
||||
ImGui::Checkbox("Multiplayer Hierarchy Debugger", &m_displayHierarchyDebugger);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
@@ -473,6 +474,29 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_displayHierarchyDebugger)
|
||||
{
|
||||
if (ImGui::Begin("Multiplayer Hierarchy Debugger", &m_displayHierarchyDebugger))
|
||||
{
|
||||
if (m_hierarchyDebugger == nullptr)
|
||||
{
|
||||
m_hierarchyDebugger = AZStd::make_unique<MultiplayerDebugHierarchyReporter>();
|
||||
}
|
||||
|
||||
if (m_hierarchyDebugger)
|
||||
{
|
||||
m_hierarchyDebugger->OnImGuiUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_hierarchyDebugger)
|
||||
{
|
||||
m_hierarchyDebugger.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MultiplayerDebugHierarchyReporter.h"
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <Debug/MultiplayerDebugPerEntityReporter.h>
|
||||
@@ -62,5 +64,8 @@ namespace Multiplayer
|
||||
|
||||
bool m_displayPerEntityStats = false;
|
||||
AZStd::unique_ptr<MultiplayerDebugPerEntityReporter> m_reporter;
|
||||
|
||||
bool m_displayHierarchyDebugger = false;
|
||||
AZStd::unique_ptr<MultiplayerDebugHierarchyReporter> m_hierarchyDebugger;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -146,7 +146,6 @@ namespace Multiplayer
|
||||
{
|
||||
// Connect the Editor to the editor server for Multiplayer simulation
|
||||
AZ::Interface<IMultiplayer>::Get()->Connect(remoteAddress.c_str(), remotePort);
|
||||
AZ::Interface<IMultiplayer>::Get()->SendReadyForEntityUpdates(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ namespace Multiplayer
|
||||
MultiplayerEditorConnection();
|
||||
~MultiplayerEditorConnection() = default;
|
||||
|
||||
bool IsHandshakeComplete() const { return true; };
|
||||
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerInit& packet);
|
||||
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerReady& packet);
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ namespace Multiplayer
|
||||
}
|
||||
|
||||
MultiplayerEditorSystemComponent::MultiplayerEditorSystemComponent()
|
||||
: m_serverAcceptanceReceivedHandler([this](){OnServerAcceptanceReceived();})
|
||||
{
|
||||
;
|
||||
}
|
||||
@@ -70,6 +71,7 @@ namespace Multiplayer
|
||||
{
|
||||
AzFramework::GameEntityContextEventBus::Handler::BusConnect();
|
||||
AzToolsFramework::EditorEvents::Bus::Handler::BusConnect();
|
||||
AZ::Interface<IMultiplayer>::Get()->AddServerAcceptanceReceivedHandler(m_serverAcceptanceReceivedHandler);
|
||||
}
|
||||
|
||||
void MultiplayerEditorSystemComponent::Deactivate()
|
||||
@@ -143,7 +145,11 @@ namespace Multiplayer
|
||||
|
||||
// Start the configured server if it's available
|
||||
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
|
||||
processLaunchInfo.m_commandlineParameters = AZStd::string::format("\"%s\" --editorsv_isDedicated true", serverPath.c_str());
|
||||
processLaunchInfo.m_commandlineParameters = AZStd::string::format(
|
||||
R"("%s" --project-path "%s" --editorsv_isDedicated true --sv_defaultPlayerSpawnAsset "%s")",
|
||||
serverPath.c_str(),
|
||||
AZ::Utils::GetProjectPath().c_str(),
|
||||
static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset).c_str());
|
||||
processLaunchInfo.m_showWindow = true;
|
||||
processLaunchInfo.m_processPriority = AzFramework::ProcessPriority::PROCESSPRIORITY_NORMAL;
|
||||
|
||||
@@ -239,4 +245,12 @@ namespace Multiplayer
|
||||
void MultiplayerEditorSystemComponent::OnGameEntitiesReset()
|
||||
{
|
||||
}
|
||||
|
||||
void MultiplayerEditorSystemComponent::OnServerAcceptanceReceived()
|
||||
{
|
||||
// We're now accepting the connection to the EditorServer.
|
||||
// In normal game clients SendReadyForEntityUpdates will be enabled once the appropriate level's root spawnable is loaded,
|
||||
// but since we're in Editor, we're already in the level.
|
||||
AZ::Interface<IMultiplayer>::Get()->SendReadyForEntityUpdates(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
|
||||
#include <IEditor.h>
|
||||
|
||||
#include <Editor/MultiplayerEditorConnection.h>
|
||||
@@ -45,6 +47,9 @@ namespace Multiplayer
|
||||
MultiplayerEditorSystemComponent();
|
||||
~MultiplayerEditorSystemComponent() override = default;
|
||||
|
||||
//! Called once the editor receives the server's accept packet
|
||||
void OnServerAcceptanceReceived();
|
||||
|
||||
//! AZ::Component overrides.
|
||||
//! @{
|
||||
void Activate() override;
|
||||
@@ -71,5 +76,7 @@ namespace Multiplayer
|
||||
IEditor* m_editor = nullptr;
|
||||
AzFramework::ProcessWatcher* m_serverProcess = nullptr;
|
||||
AzNetworking::ConnectionId m_editorConnId;
|
||||
|
||||
ServerAcceptanceReceivedEvent::Handler m_serverAcceptanceReceivedHandler;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -325,6 +325,12 @@ namespace Multiplayer
|
||||
return true;
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::OnUpdateSessionBegin(const AzFramework::SessionConfig& sessionConfig, const AZStd::string& updateReason)
|
||||
{
|
||||
AZ_UNUSED(sessionConfig);
|
||||
AZ_UNUSED(updateReason);
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
{
|
||||
if (bg_multiplayerDebugDraw)
|
||||
@@ -463,9 +469,9 @@ namespace Multiplayer
|
||||
MultiplayerPackets::SyncConsole m_syncPacket;
|
||||
};
|
||||
|
||||
bool MultiplayerSystemComponent::IsHandshakeComplete() const
|
||||
bool MultiplayerSystemComponent::IsHandshakeComplete(AzNetworking::IConnection* connection) const
|
||||
{
|
||||
return m_didHandshake;
|
||||
return reinterpret_cast<IConnectionData*>(connection->GetUserData())->DidHandshake();
|
||||
}
|
||||
|
||||
bool MultiplayerSystemComponent::HandleRequest
|
||||
@@ -520,7 +526,7 @@ namespace Multiplayer
|
||||
|
||||
if (connection->SendReliablePacket(MultiplayerPackets::Accept(sv_map)))
|
||||
{
|
||||
m_didHandshake = true;
|
||||
reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->SetDidHandshake(true);
|
||||
if (packet.GetTemporaryUserId() == 0)
|
||||
{
|
||||
// Sync our console
|
||||
@@ -539,7 +545,7 @@ namespace Multiplayer
|
||||
[[maybe_unused]] MultiplayerPackets::Accept& packet
|
||||
)
|
||||
{
|
||||
m_didHandshake = true;
|
||||
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->SetDidHandshake(true);
|
||||
if (m_temporaryUserIdentifier == 0)
|
||||
{
|
||||
AZ::CVarFixedString commandString = "sv_map " + packet.GetMap();
|
||||
@@ -560,6 +566,8 @@ namespace Multiplayer
|
||||
connectionData->GetReplicationManager().AddAutonomousEntityReplicatorCreatedHandler(m_autonomousEntityReplicatorCreatedHandler);
|
||||
}
|
||||
}
|
||||
|
||||
m_serverAcceptanceReceivedEvent.Signal();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -878,6 +886,11 @@ namespace Multiplayer
|
||||
handler.Connect(m_connectionAcquiredEvent);
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::AddServerAcceptanceReceivedHandler(ServerAcceptanceReceivedEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_serverAcceptanceReceivedEvent);
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::AddSessionInitHandler(SessionInitEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_initEvent);
|
||||
@@ -1120,7 +1133,7 @@ namespace Multiplayer
|
||||
INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity(), Multiplayer::AutoActivate::DoNotActivate);
|
||||
|
||||
NetworkEntityHandle controlledEntity;
|
||||
if (entityList.size() > 0)
|
||||
if (!entityList.empty())
|
||||
{
|
||||
controlledEntity = entityList[0];
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ namespace AzNetworking
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
AZ_CVAR_EXTERNED(AZ::CVarFixedString, sv_defaultPlayerSpawnAsset);
|
||||
|
||||
//! Multiplayer system component wraps the bridging logic between the game and transport layer.
|
||||
class MultiplayerSystemComponent final
|
||||
: public AZ::Component
|
||||
@@ -68,6 +70,7 @@ namespace Multiplayer
|
||||
bool OnSessionHealthCheck() override;
|
||||
bool OnCreateSessionBegin(const AzFramework::SessionConfig& sessionConfig) override;
|
||||
bool OnDestroySessionBegin() override;
|
||||
void OnUpdateSessionBegin(const AzFramework::SessionConfig& sessionConfig, const AZStd::string& updateReason) override;
|
||||
//! @}
|
||||
|
||||
//! AZ::TickBus::Handler overrides.
|
||||
@@ -76,7 +79,7 @@ namespace Multiplayer
|
||||
int GetTickOrder() override;
|
||||
//! @}
|
||||
|
||||
bool IsHandshakeComplete() const;
|
||||
bool IsHandshakeComplete(AzNetworking::IConnection* connection) const;
|
||||
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::Connect& packet);
|
||||
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::Accept& packet);
|
||||
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ReadyForEntityUpdates& packet);
|
||||
@@ -116,6 +119,7 @@ namespace Multiplayer
|
||||
void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override;
|
||||
void AddSessionInitHandler(SessionInitEvent::Handler& handler) override;
|
||||
void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override;
|
||||
void AddServerAcceptanceReceivedHandler(ServerAcceptanceReceivedEvent::Handler& handler) override;
|
||||
void SendNotifyClientMigrationEvent(AzNetworking::ConnectionId connectionId, const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) override;
|
||||
void SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) override;
|
||||
void SendReadyForEntityUpdates(bool readyForEntityUpdates) override;
|
||||
@@ -160,6 +164,7 @@ namespace Multiplayer
|
||||
SessionInitEvent m_initEvent;
|
||||
SessionShutdownEvent m_shutdownEvent;
|
||||
ConnectionAcquiredEvent m_connectionAcquiredEvent;
|
||||
ServerAcceptanceReceivedEvent m_serverAcceptanceReceivedEvent;
|
||||
ClientDisconnectedEvent m_clientDisconnectedEvent;
|
||||
ClientMigrationStartEvent m_clientMigrationStartEvent;
|
||||
ClientMigrationEndEvent m_clientMigrationEndEvent;
|
||||
@@ -178,7 +183,6 @@ namespace Multiplayer
|
||||
double m_serverSendAccumulator = 0.0;
|
||||
float m_renderBlendFactor = 0.0f;
|
||||
float m_tickFactor = 0.0f;
|
||||
bool m_didHandshake = false;
|
||||
bool m_spawnNetboundEntities = true;
|
||||
|
||||
#if !defined(AZ_RELEASE_BUILD)
|
||||
|
||||
@@ -0,0 +1,660 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef HAVE_BENCHMARK
|
||||
#include <CommonHierarchySetup.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Console/Console.h>
|
||||
#include <AzCore/Name/Name.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
|
||||
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
class BenchmarkComponentApplicationRequests : public AZ::ComponentApplicationRequests
|
||||
{
|
||||
public:
|
||||
void RegisterComponentDescriptor([[maybe_unused]] const AZ::ComponentDescriptor* descriptor) override {}
|
||||
void UnregisterComponentDescriptor([[maybe_unused]] const AZ::ComponentDescriptor* descriptor) override {}
|
||||
AZ::ComponentApplication* GetApplication() override { return {}; }
|
||||
void RegisterEntityAddedEventHandler([[maybe_unused]] AZ::Event<AZ::Entity*>::Handler& handler) override {}
|
||||
void RegisterEntityRemovedEventHandler([[maybe_unused]] AZ::Event<AZ::Entity*>::Handler& handler) override {}
|
||||
void RegisterEntityActivatedEventHandler([[maybe_unused]] AZ::Event<AZ::Entity*>::Handler& handler) override {}
|
||||
void RegisterEntityDeactivatedEventHandler([[maybe_unused]] AZ::Event<AZ::Entity*>::Handler& handler) override {}
|
||||
void SignalEntityActivated([[maybe_unused]] AZ::Entity* entity) override {}
|
||||
void SignalEntityDeactivated([[maybe_unused]] AZ::Entity* entity) override {}
|
||||
bool RemoveEntity([[maybe_unused]] AZ::Entity* entity) override { return {}; }
|
||||
bool DeleteEntity([[maybe_unused]] const AZ::EntityId& id) override { return {}; }
|
||||
void EnumerateEntities([[maybe_unused]] const EntityCallback& callback) override {}
|
||||
AZ::SerializeContext* GetSerializeContext() override { return {}; }
|
||||
AZ::BehaviorContext* GetBehaviorContext() override { return {}; }
|
||||
AZ::JsonRegistrationContext* GetJsonRegistrationContext() override { return {}; }
|
||||
const char* GetAppRoot() const override { return {}; }
|
||||
const char* GetEngineRoot() const override { return {}; }
|
||||
const char* GetExecutableFolder() const override { return {}; }
|
||||
void QueryApplicationType([[maybe_unused]] AZ::ApplicationTypeQuery& appType) const override {}
|
||||
|
||||
AZStd::map<AZ::EntityId, AZ::Entity*> m_entities;
|
||||
|
||||
bool AddEntity(AZ::Entity* entity) override
|
||||
{
|
||||
m_entities[entity->GetId()] = entity;
|
||||
return true;
|
||||
}
|
||||
|
||||
AZ::Entity* FindEntity(const AZ::EntityId& id) override
|
||||
{
|
||||
const auto iterator = m_entities.find(id);
|
||||
if (iterator != m_entities.end())
|
||||
{
|
||||
return iterator->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class BenchmarkConnectionListener : public AzNetworking::IConnectionListener
|
||||
{
|
||||
public:
|
||||
ConnectResult ValidateConnect([[maybe_unused]] const IpAddress& remoteAddress, [[maybe_unused]] const IPacketHeader& packetHeader, [[maybe_unused]] ISerializer& serializer) override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void OnConnect([[maybe_unused]] IConnection* connection) override
|
||||
{
|
||||
}
|
||||
|
||||
PacketDispatchResult OnPacketReceived([[maybe_unused]] IConnection* connection, [[maybe_unused]] const IPacketHeader& packetHeader, [[maybe_unused]] ISerializer& serializer) override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void OnPacketLost([[maybe_unused]] IConnection* connection, [[maybe_unused]] PacketId packetId) override
|
||||
{
|
||||
}
|
||||
|
||||
void OnDisconnect([[maybe_unused]] IConnection* connection, [[maybe_unused]] DisconnectReason reason, [[maybe_unused]] TerminationEndpoint endpoint) override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class BenchmarkTime : public AZ::ITime
|
||||
{
|
||||
public:
|
||||
AZ::TimeMs GetElapsedTimeMs() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
AZ::TimeUs GetElapsedTimeUs() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
class BenchmarkNetworkTime : public Multiplayer::INetworkTime
|
||||
{
|
||||
public:
|
||||
bool IsTimeRewound() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
HostFrameId GetHostFrameId() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
HostFrameId GetUnalteredHostFrameId() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void IncrementHostFrameId() override
|
||||
{
|
||||
}
|
||||
|
||||
AZ::TimeMs GetHostTimeMs() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
float GetHostBlendFactor() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
AzNetworking::ConnectionId GetRewindingConnectionId() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void ForceSetTime([[maybe_unused]] HostFrameId frameId, [[maybe_unused]] AZ::TimeMs timeMs) override
|
||||
{
|
||||
}
|
||||
|
||||
void SyncEntitiesToRewindState([[maybe_unused]] const AZ::Aabb& rewindVolume) override
|
||||
{
|
||||
}
|
||||
|
||||
void ClearRewoundEntities() override
|
||||
{
|
||||
}
|
||||
|
||||
void AlterTime([[maybe_unused]] HostFrameId frameId, [[maybe_unused]] AZ::TimeMs timeMs, [[maybe_unused]] float blendFactor, [[maybe_unused]] AzNetworking::ConnectionId rewindConnectionId) override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class BenchmarkMultiplayerConnection : public IConnection
|
||||
{
|
||||
public:
|
||||
BenchmarkMultiplayerConnection(ConnectionId connectionId, const IpAddress& address, [[maybe_unused]] ConnectionRole connectionRole)
|
||||
: IConnection(connectionId, address)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
~BenchmarkMultiplayerConnection() override = default;
|
||||
|
||||
bool SendReliablePacket([[maybe_unused]] const IPacket& packet) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
PacketId SendUnreliablePacket([[maybe_unused]] const IPacket& packet) override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
bool WasPacketAcked([[maybe_unused]] PacketId packetId) const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ConnectionState GetConnectionState() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
ConnectionRole GetConnectionRole() const override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
bool Disconnect([[maybe_unused]] DisconnectReason reason, [[maybe_unused]] TerminationEndpoint endpoint) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetConnectionMtu([[maybe_unused]] uint32_t connectionMtu) override
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t GetConnectionMtu() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class BenchmarkNetworkEntityManager : public Multiplayer::INetworkEntityManager
|
||||
{
|
||||
public:
|
||||
BenchmarkNetworkEntityManager() : m_authorityTracker(*this) {}
|
||||
|
||||
NetworkEntityTracker* GetNetworkEntityTracker() override { return &m_tracker; }
|
||||
NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker() override { return &m_authorityTracker; }
|
||||
MultiplayerComponentRegistry* GetMultiplayerComponentRegistry() override { return &m_multiplayerComponentRegistry; }
|
||||
const HostId& GetHostId() const override { return m_hostId; }
|
||||
EntityList CreateEntitiesImmediate(
|
||||
[[maybe_unused]] const PrefabEntityId& prefabEntryId,
|
||||
[[maybe_unused]] NetEntityRole netEntityRole,
|
||||
[[maybe_unused]] const AZ::Transform& transform,
|
||||
[[maybe_unused]] AutoActivate autoActivate) override {
|
||||
return {};
|
||||
}
|
||||
EntityList CreateEntitiesImmediate(
|
||||
[[maybe_unused]] const PrefabEntityId& prefabEntryId,
|
||||
[[maybe_unused]] NetEntityId netEntityId,
|
||||
[[maybe_unused]] NetEntityRole netEntityRole,
|
||||
[[maybe_unused]] AutoActivate autoActivate,
|
||||
[[maybe_unused]] const AZ::Transform& transform) override {
|
||||
return {};
|
||||
}
|
||||
void SetupNetEntity(
|
||||
[[maybe_unused]] AZ::Entity* netEntity,
|
||||
[[maybe_unused]] PrefabEntityId prefabEntityId,
|
||||
[[maybe_unused]] NetEntityRole netEntityRole) override {}
|
||||
uint32_t GetEntityCount() const override { return {}; }
|
||||
void MarkForRemoval(
|
||||
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) override {}
|
||||
bool IsMarkedForRemoval(
|
||||
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) const override {
|
||||
return {};
|
||||
}
|
||||
void ClearEntityFromRemovalList(
|
||||
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) override {}
|
||||
void ClearAllEntities() override {}
|
||||
void AddEntityMarkedDirtyHandler(
|
||||
[[maybe_unused]] AZ::Event<>::Handler& entityMarkedDirtyHandle) override {}
|
||||
void AddEntityNotifyChangesHandler(
|
||||
[[maybe_unused]] AZ::Event<>::Handler& entityNotifyChangesHandle) override {}
|
||||
void AddEntityExitDomainHandler(
|
||||
[[maybe_unused]] EntityExitDomainEvent::Handler& entityExitDomainHandler) override {}
|
||||
void AddControllersActivatedHandler(
|
||||
[[maybe_unused]] ControllersActivatedEvent::Handler& controllersActivatedHandler) override {}
|
||||
void AddControllersDeactivatedHandler(
|
||||
[[maybe_unused]] ControllersDeactivatedEvent::Handler& controllersDeactivatedHandler) override {}
|
||||
void NotifyEntitiesDirtied() override {}
|
||||
void NotifyEntitiesChanged() override {}
|
||||
void NotifyControllersActivated(
|
||||
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle,
|
||||
[[maybe_unused]] EntityIsMigrating entityIsMigrating) override {}
|
||||
void NotifyControllersDeactivated(
|
||||
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle,
|
||||
[[maybe_unused]] EntityIsMigrating entityIsMigrating) override {}
|
||||
void HandleLocalRpcMessage(
|
||||
[[maybe_unused]] NetworkEntityRpcMessage& message) override {}
|
||||
|
||||
mutable AZStd::map<NetEntityId, AZ::Entity*> m_networkEntityMap;
|
||||
|
||||
NetworkEntityHandle AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity) override
|
||||
{
|
||||
m_networkEntityMap[netEntityId] = entity;
|
||||
return NetworkEntityHandle(entity, &m_tracker);
|
||||
}
|
||||
|
||||
ConstNetworkEntityHandle GetEntity(NetEntityId netEntityId) const override
|
||||
{
|
||||
AZ::Entity* entity = m_networkEntityMap[netEntityId];
|
||||
return ConstNetworkEntityHandle(entity, &m_tracker);
|
||||
}
|
||||
|
||||
NetEntityId GetNetEntityIdById(const AZ::EntityId& entityId) const override
|
||||
{
|
||||
for (const auto& pair : m_networkEntityMap)
|
||||
{
|
||||
if (pair.second->GetId() == entityId)
|
||||
{
|
||||
return pair.first;
|
||||
}
|
||||
}
|
||||
|
||||
return InvalidNetEntityId;
|
||||
}
|
||||
|
||||
[[nodiscard]] AZStd::unique_ptr<AzFramework::EntitySpawnTicket> RequestNetSpawnableInstantiation(
|
||||
[[maybe_unused]] const AZ::Data::Asset<AzFramework::Spawnable>& netSpawnable,
|
||||
[[maybe_unused]] const AZ::Transform& transform) override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void Initialize([[maybe_unused]] const HostId& hostId, [[maybe_unused]] AZStd::unique_ptr<IEntityDomain> entityDomain) override {}
|
||||
bool IsInitialized() const override { return true; }
|
||||
IEntityDomain* GetEntityDomain() const override { return nullptr; }
|
||||
void DebugDraw() const override {}
|
||||
|
||||
NetworkEntityTracker m_tracker;
|
||||
NetworkEntityAuthorityTracker m_authorityTracker;
|
||||
MultiplayerComponentRegistry m_multiplayerComponentRegistry;
|
||||
HostId m_hostId;
|
||||
};
|
||||
|
||||
class BenchmarkMultiplayer : public Multiplayer::IMultiplayer
|
||||
{
|
||||
public:
|
||||
BenchmarkMultiplayer(BenchmarkNetworkEntityManager& manager) : m_manager(manager) {}
|
||||
|
||||
MultiplayerAgentType GetAgentType() const override { return {}; }
|
||||
void InitializeMultiplayer([[maybe_unused]] MultiplayerAgentType state) override {}
|
||||
bool StartHosting([[maybe_unused]] uint16_t port, [[maybe_unused]] bool isDedicated) override { return {}; }
|
||||
bool Connect([[maybe_unused]] const AZStd::string& remoteAddress, [[maybe_unused]] uint16_t port) override { return {}; }
|
||||
void Terminate([[maybe_unused]] AzNetworking::DisconnectReason reason) override {}
|
||||
void AddClientDisconnectedHandler([[maybe_unused]] ClientDisconnectedEvent::Handler& handler) override {}
|
||||
void AddConnectionAcquiredHandler([[maybe_unused]] ConnectionAcquiredEvent::Handler& handler) override {}
|
||||
void AddServerAcceptanceReceivedHandler([[maybe_unused]] ServerAcceptanceReceivedEvent::Handler& handler) override {}
|
||||
void AddSessionInitHandler([[maybe_unused]] SessionInitEvent::Handler& handler) override {}
|
||||
void AddSessionShutdownHandler([[maybe_unused]] SessionShutdownEvent::Handler& handler) override {}
|
||||
void SendReadyForEntityUpdates([[maybe_unused]] bool readyForEntityUpdates) override {}
|
||||
AZ::TimeMs GetCurrentHostTimeMs() const override { return {}; }
|
||||
float GetCurrentBlendFactor() const override { return {}; }
|
||||
INetworkTime* GetNetworkTime() override { return {}; }
|
||||
INetworkEntityManager* GetNetworkEntityManager() override { return &m_manager; }
|
||||
void SetFilterEntityManager([[maybe_unused]] IFilterEntityManager* entityFilter) override {}
|
||||
IFilterEntityManager* GetFilterEntityManager() override { return {}; }
|
||||
void AddClientMigrationStartEventHandler([[maybe_unused]] ClientMigrationStartEvent::Handler& handler) override {}
|
||||
void AddClientMigrationEndEventHandler([[maybe_unused]] ClientMigrationEndEvent::Handler& handler) override {}
|
||||
void AddNotifyClientMigrationHandler([[maybe_unused]] NotifyClientMigrationEvent::Handler& handler) override {}
|
||||
void AddNotifyEntityMigrationEventHandler([[maybe_unused]] NotifyEntityMigrationEvent::Handler& handler) override {}
|
||||
void SendNotifyClientMigrationEvent([[maybe_unused]] const HostId& hostId, [[maybe_unused]] uint64_t userIdentifier, [[maybe_unused]] ClientInputId lastClientInputId) override {}
|
||||
void SendNotifyEntityMigrationEvent([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle, [[maybe_unused]] const HostId& remoteHostId) override {}
|
||||
void SetShouldSpawnNetworkEntities([[maybe_unused]] bool value) override {}
|
||||
bool GetShouldSpawnNetworkEntities() const override { return true; }
|
||||
|
||||
BenchmarkNetworkEntityManager& m_manager;
|
||||
};
|
||||
|
||||
class HierarchyBenchmarkBase
|
||||
: public benchmark::Fixture
|
||||
, public AllocatorsBase
|
||||
{
|
||||
public:
|
||||
void SetUp(const benchmark::State&) override
|
||||
{
|
||||
internalSetUp();
|
||||
}
|
||||
void SetUp(benchmark::State&) override
|
||||
{
|
||||
internalSetUp();
|
||||
}
|
||||
|
||||
void TearDown(const benchmark::State&) override
|
||||
{
|
||||
internalTearDown();
|
||||
}
|
||||
void TearDown(benchmark::State&) override
|
||||
{
|
||||
internalTearDown();
|
||||
}
|
||||
|
||||
virtual void internalSetUp()
|
||||
{
|
||||
SetupAllocator();
|
||||
AZ::NameDictionary::Create();
|
||||
|
||||
m_ComponentApplicationRequests = AZStd::make_unique<BenchmarkComponentApplicationRequests>();
|
||||
AZ::Interface<AZ::ComponentApplicationRequests>::Register(m_ComponentApplicationRequests.get());
|
||||
|
||||
// register components involved in testing
|
||||
m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
|
||||
|
||||
m_transformDescriptor.reset(AzFramework::TransformComponent::CreateDescriptor());
|
||||
m_transformDescriptor->Reflect(m_serializeContext.get());
|
||||
|
||||
m_netBindDescriptor.reset(NetBindComponent::CreateDescriptor());
|
||||
m_netBindDescriptor->Reflect(m_serializeContext.get());
|
||||
|
||||
m_hierarchyRootDescriptor.reset(NetworkHierarchyRootComponent::CreateDescriptor());
|
||||
m_hierarchyRootDescriptor->Reflect(m_serializeContext.get());
|
||||
|
||||
m_hierarchyChildDescriptor.reset(NetworkHierarchyChildComponent::CreateDescriptor());
|
||||
m_hierarchyChildDescriptor->Reflect(m_serializeContext.get());
|
||||
|
||||
m_netTransformDescriptor.reset(NetworkTransformComponent::CreateDescriptor());
|
||||
m_netTransformDescriptor->Reflect(m_serializeContext.get());
|
||||
|
||||
m_NetworkEntityManager = AZStd::make_unique<BenchmarkNetworkEntityManager>();
|
||||
|
||||
m_Multiplayer = AZStd::make_unique<BenchmarkMultiplayer>(*m_NetworkEntityManager);
|
||||
AZ::Interface<IMultiplayer>::Register(m_Multiplayer.get());
|
||||
|
||||
// Create space for replication stats
|
||||
// Without Multiplayer::RegisterMultiplayerComponents() the stats go to invalid id, which is fine for unit tests
|
||||
GetMultiplayer()->GetStats().ReserveComponentStats(Multiplayer::InvalidNetComponentId, 50, 0);
|
||||
|
||||
m_Time = AZStd::make_unique<BenchmarkTime>();
|
||||
AZ::Interface<AZ::ITime>::Register(m_Time.get());
|
||||
|
||||
m_NetworkTime = AZStd::make_unique<BenchmarkNetworkTime>();
|
||||
AZ::Interface<INetworkTime>::Register(m_NetworkTime.get());
|
||||
|
||||
EXPECT_NE(AZ::Interface<IMultiplayer>::Get()->GetNetworkEntityManager(), nullptr);
|
||||
|
||||
const IpAddress address("localhost", 1, ProtocolType::Udp);
|
||||
m_Connection = AZStd::make_unique<BenchmarkMultiplayerConnection>(ConnectionId{ 1 }, address, ConnectionRole::Connector);
|
||||
m_ConnectionListener = AZStd::make_unique<BenchmarkConnectionListener>();
|
||||
|
||||
m_entityReplicationManager = AZStd::make_unique<EntityReplicationManager>(*m_Connection, *m_ConnectionListener, EntityReplicationManager::Mode::LocalClientToRemoteServer);
|
||||
|
||||
m_console.reset(aznew AZ::Console());
|
||||
AZ::Interface<AZ::IConsole>::Register(m_console.get());
|
||||
m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());
|
||||
|
||||
RegisterMultiplayerComponents();
|
||||
}
|
||||
|
||||
virtual void internalTearDown()
|
||||
{
|
||||
AZ::Interface<AZ::IConsole>::Unregister(m_console.get());
|
||||
m_console.reset();
|
||||
|
||||
m_entityReplicationManager.reset();
|
||||
|
||||
m_Connection.reset();
|
||||
m_ConnectionListener.reset();
|
||||
|
||||
AZ::Interface<INetworkTime>::Unregister(m_NetworkTime.get());
|
||||
AZ::Interface<AZ::ITime>::Unregister(m_Time.get());
|
||||
AZ::Interface<IMultiplayer>::Unregister(m_Multiplayer.get());
|
||||
AZ::Interface<AZ::ComponentApplicationRequests>::Unregister(m_ComponentApplicationRequests.get());
|
||||
|
||||
m_Time.reset();
|
||||
|
||||
m_NetworkEntityManager.reset();
|
||||
m_Multiplayer.reset();
|
||||
|
||||
m_transformDescriptor.reset();
|
||||
m_netTransformDescriptor.reset();
|
||||
m_hierarchyRootDescriptor.reset();
|
||||
m_hierarchyChildDescriptor.reset();
|
||||
m_netBindDescriptor.reset();
|
||||
m_serializeContext.reset();
|
||||
m_ComponentApplicationRequests.reset();
|
||||
|
||||
AZ::NameDictionary::Destroy();
|
||||
TeardownAllocator();
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AZ::IConsole> m_console;
|
||||
|
||||
AZStd::unique_ptr<BenchmarkComponentApplicationRequests> m_ComponentApplicationRequests;
|
||||
AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_transformDescriptor;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_netBindDescriptor;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_hierarchyRootDescriptor;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_hierarchyChildDescriptor;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_netTransformDescriptor;
|
||||
|
||||
AZStd::unique_ptr<BenchmarkMultiplayer> m_Multiplayer;
|
||||
AZStd::unique_ptr<BenchmarkNetworkEntityManager> m_NetworkEntityManager;
|
||||
AZStd::unique_ptr<BenchmarkTime> m_Time;
|
||||
AZStd::unique_ptr<BenchmarkNetworkTime> m_NetworkTime;
|
||||
|
||||
AZStd::unique_ptr<BenchmarkMultiplayerConnection> m_Connection;
|
||||
AZStd::unique_ptr<BenchmarkConnectionListener> m_ConnectionListener;
|
||||
|
||||
AZStd::unique_ptr<EntityReplicationManager> m_entityReplicationManager;
|
||||
|
||||
void SetupEntity(const AZStd::unique_ptr<AZ::Entity>& entity, NetEntityId netId, NetEntityRole role)
|
||||
{
|
||||
const auto netBindComponent = entity->FindComponent<Multiplayer::NetBindComponent>();
|
||||
EXPECT_NE(netBindComponent, nullptr);
|
||||
netBindComponent->PreInit(entity.get(), PrefabEntityId{ AZ::Name("test"), 1 }, netId, role);
|
||||
entity->Init();
|
||||
}
|
||||
|
||||
static void StopEntity(const AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
{
|
||||
const auto netBindComponent = entity->FindComponent<Multiplayer::NetBindComponent>();
|
||||
EXPECT_NE(netBindComponent, nullptr);
|
||||
netBindComponent->StopEntity();
|
||||
}
|
||||
|
||||
static void StopAndDeactivateEntity(AZStd::unique_ptr<AZ::Entity>& entity)
|
||||
{
|
||||
if (entity)
|
||||
{
|
||||
StopEntity(entity);
|
||||
entity->Deactivate();
|
||||
entity.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void CreateEntityWithRootHierarchy(AZStd::unique_ptr<AZ::Entity>& rootEntity)
|
||||
{
|
||||
rootEntity->CreateComponent<AzFramework::TransformComponent>();
|
||||
rootEntity->CreateComponent<NetBindComponent>();
|
||||
rootEntity->CreateComponent<NetworkTransformComponent>();
|
||||
rootEntity->CreateComponent<NetworkHierarchyRootComponent>();
|
||||
}
|
||||
|
||||
void CreateEntityWithChildHierarchy(AZStd::unique_ptr<AZ::Entity>& childEntity)
|
||||
{
|
||||
childEntity->CreateComponent<AzFramework::TransformComponent>();
|
||||
childEntity->CreateComponent<NetBindComponent>();
|
||||
childEntity->CreateComponent<NetworkTransformComponent>();
|
||||
childEntity->CreateComponent<NetworkHierarchyChildComponent>();
|
||||
}
|
||||
|
||||
void SetParentIdOnNetworkTransform(const AZStd::unique_ptr<AZ::Entity>& entity, NetEntityId netParentId)
|
||||
{
|
||||
/* Derived from NetworkTransformComponent.AutoComponent.xml */
|
||||
constexpr int totalBits = 6 /*NetworkTransformComponentInternal::AuthorityToClientDirtyEnum::Count*/;
|
||||
constexpr int parentIdBit = 4 /*NetworkTransformComponentInternal::AuthorityToClientDirtyEnum::parentEntityId_DirtyFlag*/;
|
||||
|
||||
ReplicationRecord currentRecord;
|
||||
currentRecord.m_authorityToClient.AddBits(totalBits);
|
||||
currentRecord.m_authorityToClient.SetBit(parentIdBit, true);
|
||||
|
||||
constexpr uint32_t bufferSize = 100;
|
||||
AZStd::array<uint8_t, bufferSize> buffer = {};
|
||||
NetworkInputSerializer inSerializer(buffer.begin(), bufferSize);
|
||||
inSerializer.Serialize(reinterpret_cast<uint32_t&>(netParentId),
|
||||
"parentEntityId", /* Derived from NetworkTransformComponent.AutoComponent.xml */
|
||||
AZStd::numeric_limits<uint32_t>::min(), AZStd::numeric_limits<uint32_t>::max());
|
||||
|
||||
NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);
|
||||
|
||||
ReplicationRecord notifyRecord = currentRecord;
|
||||
entity->FindComponent<NetworkTransformComponent>()->SerializeStateDeltaMessage(currentRecord, outSerializer);
|
||||
entity->FindComponent<NetworkTransformComponent>()->NotifyStateDeltaChanges(notifyRecord);
|
||||
}
|
||||
|
||||
template <typename Component>
|
||||
void SetHierarchyRootFieldOnNetworkHierarchyChild(const AZStd::unique_ptr<AZ::Entity>& entity, NetEntityId value)
|
||||
{
|
||||
/* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */
|
||||
constexpr int totalBits = 1 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::Count*/;
|
||||
constexpr int inHierarchyBit = 0 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::hierarchyRoot_DirtyFlag*/;
|
||||
|
||||
ReplicationRecord currentRecord;
|
||||
currentRecord.m_authorityToClient.AddBits(totalBits);
|
||||
currentRecord.m_authorityToClient.SetBit(inHierarchyBit, true);
|
||||
|
||||
constexpr uint32_t bufferSize = 100;
|
||||
AZStd::array<uint8_t, bufferSize> buffer = {};
|
||||
NetworkInputSerializer inSerializer(buffer.begin(), bufferSize);
|
||||
inSerializer.Serialize(reinterpret_cast<uint32_t&>(value),
|
||||
"hierarchyRoot", /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */
|
||||
AZStd::numeric_limits<uint32_t>::min(), AZStd::numeric_limits<uint32_t>::max());
|
||||
|
||||
NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);
|
||||
|
||||
ReplicationRecord notifyRecord = currentRecord;
|
||||
entity->FindComponent<Component>()->SerializeStateDeltaMessage(currentRecord, outSerializer);
|
||||
entity->FindComponent<Component>()->NotifyStateDeltaChanges(notifyRecord);
|
||||
}
|
||||
|
||||
struct EntityInfo
|
||||
{
|
||||
enum class Role
|
||||
{
|
||||
Root,
|
||||
Child,
|
||||
None
|
||||
};
|
||||
|
||||
EntityInfo(AZ::u64 entityId, const char* entityName, NetEntityId netId, Role role)
|
||||
: m_entity(AZStd::make_unique<AZ::Entity>(AZ::EntityId(entityId), entityName))
|
||||
, m_netId(netId)
|
||||
, m_role(role)
|
||||
{
|
||||
}
|
||||
|
||||
~EntityInfo()
|
||||
{
|
||||
StopAndDeactivateEntity(m_entity);
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> m_entity;
|
||||
NetEntityId m_netId;
|
||||
AZStd::unique_ptr<EntityReplicator> m_replicator;
|
||||
Role m_role = Role::None;
|
||||
};
|
||||
|
||||
void PopulateHierarchicalEntity(const EntityInfo& entityInfo)
|
||||
{
|
||||
entityInfo.m_entity->CreateComponent<AzFramework::TransformComponent>();
|
||||
entityInfo.m_entity->CreateComponent<NetBindComponent>();
|
||||
entityInfo.m_entity->CreateComponent<NetworkTransformComponent>();
|
||||
switch (entityInfo.m_role)
|
||||
{
|
||||
case EntityInfo::Role::Root:
|
||||
entityInfo.m_entity->CreateComponent<NetworkHierarchyRootComponent>();
|
||||
break;
|
||||
case EntityInfo::Role::Child:
|
||||
entityInfo.m_entity->CreateComponent<NetworkHierarchyChildComponent>();
|
||||
break;
|
||||
case EntityInfo::Role::None:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CreateParent(EntityInfo& parent)
|
||||
{
|
||||
PopulateHierarchicalEntity(parent);
|
||||
|
||||
SetupEntity(parent.m_entity, parent.m_netId, NetEntityRole::Authority);
|
||||
|
||||
// Create an entity replicator for the child entity
|
||||
const NetworkEntityHandle childHandle(parent.m_entity.get(), m_NetworkEntityManager->GetNetworkEntityTracker());
|
||||
parent.m_replicator = AZStd::make_unique<EntityReplicator>(*m_entityReplicationManager, m_Connection.get(), NetEntityRole::Client, childHandle);
|
||||
parent.m_replicator->Initialize(childHandle);
|
||||
|
||||
parent.m_entity->Activate();
|
||||
}
|
||||
|
||||
void CreateChildForParent(EntityInfo& child, EntityInfo& parent)
|
||||
{
|
||||
PopulateHierarchicalEntity(child);
|
||||
|
||||
SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Authority);
|
||||
|
||||
// we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller)
|
||||
SetParentIdOnNetworkTransform(child.m_entity, parent.m_netId);
|
||||
|
||||
// Create an entity replicator for the child entity
|
||||
const NetworkEntityHandle childHandle(child.m_entity.get(), m_NetworkEntityManager->GetNetworkEntityTracker());
|
||||
child.m_replicator = AZStd::make_unique<EntityReplicator>(*m_entityReplicationManager, m_Connection.get(), NetEntityRole::Client, childHandle);
|
||||
child.m_replicator->Initialize(childHandle);
|
||||
|
||||
child.m_entity->Activate();
|
||||
}
|
||||
|
||||
void ForceRebuildHierarchy(const AZStd::unique_ptr<AZ::Entity>& rootEntity)
|
||||
{
|
||||
if (NetworkHierarchyRootComponent* root = rootEntity->FindComponent<NetworkHierarchyRootComponent>())
|
||||
{
|
||||
root->RebuildHierarchy();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -29,9 +29,10 @@ namespace UnitTest
|
||||
MOCK_METHOD1(AddClientDisconnectedHandler, void(AZ::Event<>::Handler&));
|
||||
MOCK_METHOD1(AddNotifyClientMigrationHandler, void(Multiplayer::NotifyClientMigrationEvent::Handler&));
|
||||
MOCK_METHOD1(AddNotifyEntityMigrationEventHandler, void(Multiplayer::NotifyEntityMigrationEvent::Handler&));
|
||||
MOCK_METHOD1(AddConnectionAcquiredHandler, void(AZ::Event<Multiplayer::MultiplayerAgentDatum>::Handler&));
|
||||
MOCK_METHOD1(AddSessionInitHandler, void(AZ::Event<AzNetworking::INetworkInterface*>::Handler&));
|
||||
MOCK_METHOD1(AddSessionShutdownHandler, void(AZ::Event<AzNetworking::INetworkInterface*>::Handler&));
|
||||
MOCK_METHOD1(AddConnectionAcquiredHandler, void(Multiplayer::ConnectionAcquiredEvent::Handler&));
|
||||
MOCK_METHOD1(AddServerAcceptanceReceivedHandler, void(Multiplayer::ServerAcceptanceReceivedEvent::Handler&));
|
||||
MOCK_METHOD1(AddSessionInitHandler, void(Multiplayer::SessionInitEvent::Handler&));
|
||||
MOCK_METHOD1(AddSessionShutdownHandler, void(Multiplayer::SessionShutdownEvent::Handler&));
|
||||
MOCK_METHOD3(SendNotifyClientMigrationEvent, void(const Multiplayer::HostId&, uint64_t, Multiplayer::ClientInputId));
|
||||
MOCK_METHOD2(SendNotifyEntityMigrationEvent, void(const Multiplayer::ConstNetworkEntityHandle&, const Multiplayer::HostId&));
|
||||
MOCK_METHOD1(SendReadyForEntityUpdates, void(bool));
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <NetworkEntity/EntityReplication/EntityReplicator.h>
|
||||
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_BENCHMARK
|
||||
#include <CommonBenchmarkSetup.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
/*
|
||||
* Hierarchy of 16 entities: Parent -> Child_2 -> .. -> Child_16
|
||||
* By default the maximum size of a hierarchy is defined by bg_hierarchyEntityMaxLimit (16).
|
||||
*/
|
||||
class ServerDeepHierarchyBenchmark : public HierarchyBenchmarkBase
|
||||
{
|
||||
public:
|
||||
const NetEntityId RootNetEntityId = NetEntityId{ 1 };
|
||||
const NetEntityId ChildNetEntityId = NetEntityId{ 2 };
|
||||
const NetEntityId ChildOfChildNetEntityId = NetEntityId{ 3 };
|
||||
|
||||
void internalSetUp() override
|
||||
{
|
||||
HierarchyBenchmarkBase::internalSetUp();
|
||||
|
||||
m_root = AZStd::make_unique<EntityInfo>((1), "root", RootNetEntityId, EntityInfo::Role::Root);
|
||||
CreateParent(*m_root);
|
||||
|
||||
m_children = AZStd::make_unique<AZStd::vector<AZStd::shared_ptr<EntityInfo>>>();
|
||||
|
||||
EntityInfo* parent = m_root.get();
|
||||
for (int i = 0; i < 15; ++i)
|
||||
{
|
||||
m_children->push_back(AZStd::make_shared<EntityInfo>((i + 2), "child", ChildNetEntityId, EntityInfo::Role::Child));
|
||||
CreateChildForParent(*m_children->back(), *parent);
|
||||
|
||||
m_children->back()->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(parent->m_entity->GetId());
|
||||
parent = m_children->back().get();
|
||||
}
|
||||
}
|
||||
|
||||
void internalTearDown() override
|
||||
{
|
||||
m_children.reset();
|
||||
m_root.reset();
|
||||
|
||||
HierarchyBenchmarkBase::internalTearDown();
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<EntityInfo> m_root;
|
||||
AZStd::unique_ptr<AZStd::vector<AZStd::shared_ptr<EntityInfo>>> m_children;
|
||||
};
|
||||
|
||||
BENCHMARK_DEFINE_F(ServerDeepHierarchyBenchmark, RebuildHierarchy)(benchmark::State& state)
|
||||
{
|
||||
for ([[maybe_unused]] auto value : state)
|
||||
{
|
||||
ForceRebuildHierarchy(m_root->m_entity);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(ServerDeepHierarchyBenchmark, RebuildHierarchy)
|
||||
->Unit(benchmark::kMicrosecond)
|
||||
;
|
||||
|
||||
// Should be roughly twice the time of @RebuildHierarchy benchmark
|
||||
BENCHMARK_DEFINE_F(ServerDeepHierarchyBenchmark, RebuildHierarchyRemoveAndAddLastChild)(benchmark::State& state)
|
||||
{
|
||||
const AZ::EntityId parentOfLastChild = m_children->at(m_children->size() - 2)->m_entity->GetId();
|
||||
|
||||
for ([[maybe_unused]] auto value : state)
|
||||
{
|
||||
m_children->back()->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(AZ::EntityId());
|
||||
m_children->back()->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(parentOfLastChild);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(ServerDeepHierarchyBenchmark, RebuildHierarchyRemoveAndAddLastChild)
|
||||
->Unit(benchmark::kMicrosecond)
|
||||
;
|
||||
|
||||
// Should be roughly twice the time of @RebuildHierarchy benchmark
|
||||
BENCHMARK_DEFINE_F(ServerDeepHierarchyBenchmark, RebuildHierarchyRemoveAndAddMiddleChild)(benchmark::State& state)
|
||||
{
|
||||
const AZ::EntityId parentOfMiddleChild = m_children->at(4)->m_entity->GetId();
|
||||
|
||||
for ([[maybe_unused]] auto value : state)
|
||||
{
|
||||
m_children->at(5)->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(AZ::EntityId());
|
||||
m_children->at(5)->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(parentOfMiddleChild);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(ServerDeepHierarchyBenchmark, RebuildHierarchyRemoveAndAddMiddleChild)
|
||||
->Unit(benchmark::kMicrosecond)
|
||||
;
|
||||
|
||||
// Should be roughly twice the time of @RebuildHierarchy benchmark
|
||||
BENCHMARK_DEFINE_F(ServerDeepHierarchyBenchmark, RebuildHierarchyRemoveAndAddFirstChild)(benchmark::State& state)
|
||||
{
|
||||
const AZ::EntityId rootId = m_children->front()->m_entity->GetId();
|
||||
|
||||
for ([[maybe_unused]] auto value : state)
|
||||
{
|
||||
m_children->at(1)->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(AZ::EntityId());
|
||||
m_children->at(1)->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(rootId);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(ServerDeepHierarchyBenchmark, RebuildHierarchyRemoveAndAddFirstChild)
|
||||
->Unit(benchmark::kMicrosecond)
|
||||
;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -9,6 +9,8 @@
|
||||
set(FILES
|
||||
Source/Debug/MultiplayerDebugByteReporter.cpp
|
||||
Source/Debug/MultiplayerDebugByteReporter.h
|
||||
Source/Debug/MultiplayerDebugHierarchyReporter.cpp
|
||||
Source/Debug/MultiplayerDebugHierarchyReporter.h
|
||||
Source/Debug/MultiplayerDebugPerEntityReporter.cpp
|
||||
Source/Debug/MultiplayerDebugPerEntityReporter.h
|
||||
Source/Debug/MultiplayerDebugModule.cpp
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
|
||||
set(FILES
|
||||
Tests/ClientHierarchyTests.cpp
|
||||
Tests/ServerHierarchyBenchmarks.cpp
|
||||
Tests/CommonHierarchySetup.h
|
||||
Tests/CommonBenchmarkSetup.h
|
||||
Tests/IMultiplayerConnectionMock.h
|
||||
Tests/Main.cpp
|
||||
Tests/MockInterfaces.h
|
||||
|
||||
Reference in New Issue
Block a user