You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Framework/GridMate/Tests/Interest.cpp

1824 lines
60 KiB
C++

/*
* 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 "Tests.h"
#include <GridMate_Traits_Platform.h>
#include <GridMate/Session/LANSession.h>
#include <GridMate/Replica/Interest/InterestManager.h>
#include <GridMate/Replica/Interest/BitmaskInterestHandler.h>
#include <GridMate/Replica/ReplicaFunctions.h>
#include <AzCore/Math/Random.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/Debug/Timer.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
using namespace GridMate;
// An easy switch to use an old brute force ProximityInterestHandler for performance comparison.
#if 0
namespace GridMate
{
/*
* Base interest
*/
class ProximityInterest
{
friend class ProximityInterestHandler;
public:
const AZ::Aabb& Get() const { return m_bbox; }
protected:
explicit ProximityInterest(ProximityInterestHandler* handler);
ProximityInterestHandler* m_handler;
AZ::Aabb m_bbox;
};
///////////////////////////////////////////////////////////////////////////
/*
* Proximity rule
*/
class ProximityInterestRule
: public InterestRule
, public ProximityInterest
{
friend class ProximityInterestHandler;
public:
using Ptr = AZStd::intrusive_ptr<ProximityInterestRule>;
GM_CLASS_ALLOCATOR(ProximityInterestRule);
void Set(const AZ::Aabb& bbox);
private:
// Intrusive ptr
template<class T>
friend struct AZStd::IntrusivePtrCountPolicy;
unsigned int m_refCount = 0;
AZ_FORCE_INLINE void add_ref() { ++m_refCount; }
AZ_FORCE_INLINE void release() { --m_refCount; if (!m_refCount) Destroy(); }
AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; }
///////////////////////////////////////////////////////////////////////////
ProximityInterestRule(ProximityInterestHandler* handler, PeerId peerId, RuleNetworkId netId)
: InterestRule(peerId, netId)
, ProximityInterest(handler)
{}
void Destroy();
};
///////////////////////////////////////////////////////////////////////////
/*
* Proximity attribute
*/
class ProximityInterestAttribute
: public InterestAttribute
, public ProximityInterest
{
friend class ProximityInterestHandler;
template<class T> friend class InterestPtr;
public:
using Ptr = AZStd::intrusive_ptr<ProximityInterestAttribute>;
GM_CLASS_ALLOCATOR(ProximityInterestAttribute);
void Set(const AZ::Aabb& bbox);
private:
// Intrusive ptr
template<class T>
friend struct AZStd::IntrusivePtrCountPolicy;
unsigned int m_refCount = 0;
AZ_FORCE_INLINE void add_ref() { ++m_refCount; }
AZ_FORCE_INLINE void release() { Destroy(); }
AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; }
///////////////////////////////////////////////////////////////////////////
ProximityInterestAttribute(ProximityInterestHandler* handler, ReplicaId repId)
: InterestAttribute(repId)
, ProximityInterest(handler)
{}
void Destroy();
};
///////////////////////////////////////////////////////////////////////////
/*
* Rules handler
*/
class ProximityInterestHandler
: public BaseRulesHandler
{
friend class ProximityInterestRule;
friend class ProximityInterestAttribute;
friend class ProximityInterestChunk;
public:
typedef unordered_set<ProximityInterestAttribute*> AttributeSet;
typedef unordered_set<ProximityInterestRule*> RuleSet;
GM_CLASS_ALLOCATOR(ProximityInterestHandler);
ProximityInterestHandler();
// Creates new proximity rule and binds it to the peer
ProximityInterestRule::Ptr CreateRule(PeerId peerId);
// Creates new proximity attribute and binds it to the replica
ProximityInterestAttribute::Ptr CreateAttribute(ReplicaId replicaId);
// Calculates rules and attributes matches
void Update() override;
// Returns last recalculated results
const InterestMatchResult& GetLastResult() override;
// Returns manager its bound with
InterestManager* GetManager() override { return m_im; }
const RuleSet& GetLocalRules() { return m_localRules; }
private:
void UpdateInternal(InterestMatchResult& result);
// BaseRulesHandler
void OnRulesHandlerRegistered(InterestManager* manager) override;
void OnRulesHandlerUnregistered(InterestManager* manager) override;
void DestroyRule(ProximityInterestRule* rule);
void FreeRule(ProximityInterestRule* rule);
void UpdateRule(ProximityInterestRule* rule);
void DestroyAttribute(ProximityInterestAttribute* attrib);
void FreeAttribute(ProximityInterestAttribute* attrib);
void UpdateAttribute(ProximityInterestAttribute* attrib);
void OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer);
void OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer);
RuleNetworkId GetNewRuleNetId();
ProximityInterestChunk* FindRulesChunkByPeerId(PeerId peerId);
InterestManager* m_im;
ReplicaManager* m_rm;
AZ::u32 m_lastRuleNetId;
unordered_map<PeerId, ProximityInterestChunk*> m_peerChunks;
RuleSet m_localRules;
AttributeSet m_attributes;
RuleSet m_rules;
ProximityInterestChunk* m_rulesReplica;
InterestMatchResult m_resultCache;
};
///////////////////////////////////////////////////////////////////////////
class ProximityInterestChunk
: public ReplicaChunk
{
public:
GM_CLASS_ALLOCATOR(ProximityInterestChunk);
// ReplicaChunk
typedef AZStd::intrusive_ptr<ProximityInterestChunk> Ptr;
bool IsReplicaMigratable() override { return false; }
static const char* GetChunkName() { return "ProximityInterestChunk"; }
bool IsBroadcast() { return true; }
///////////////////////////////////////////////////////////////////////////
ProximityInterestChunk()
: m_interestHandler(nullptr)
, AddRuleRpc("AddRule")
, RemoveRuleRpc("RemoveRule")
, UpdateRuleRpc("UpdateRule")
, AddRuleForPeerRpc("AddRuleForPeerRpc")
{
}
void OnReplicaActivate(const ReplicaContext& rc) override
{
m_interestHandler = static_cast<ProximityInterestHandler*>(rc.m_rm->GetUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4)));
AZ_Assert(m_interestHandler, "No proximity interest handler in the user context");
if (m_interestHandler)
{
m_interestHandler->OnNewRulesChunk(this, rc.m_peer);
}
}
void OnReplicaDeactivate(const ReplicaContext& rc) override
{
if (rc.m_peer && m_interestHandler)
{
m_interestHandler->OnDeleteRulesChunk(this, rc.m_peer);
}
}
bool AddRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext& ctx)
{
if (IsProxy())
{
auto rulePtr = m_interestHandler->CreateRule(ctx.m_sourcePeer);
rulePtr->Set(bbox);
m_rules.insert(AZStd::make_pair(netId, rulePtr));
}
return true;
}
bool RemoveRuleFn(RuleNetworkId netId, const RpcContext&)
{
if (IsProxy())
{
m_rules.erase(netId);
}
return true;
}
bool UpdateRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext&)
{
if (IsProxy())
{
auto it = m_rules.find(netId);
if (it != m_rules.end())
{
it->second->Set(bbox);
}
}
return true;
}
bool AddRuleForPeerFn(RuleNetworkId netId, PeerId peerId, AZ::Aabb bbox, const RpcContext&)
{
ProximityInterestChunk* peerChunk = m_interestHandler->FindRulesChunkByPeerId(peerId);
if (peerChunk)
{
auto it = peerChunk->m_rules.find(netId);
if (it == peerChunk->m_rules.end())
{
auto rulePtr = m_interestHandler->CreateRule(peerId);
peerChunk->m_rules.insert(AZStd::make_pair(netId, rulePtr));
rulePtr->Set(bbox);
}
}
return false;
}
Rpc<RpcArg<RuleNetworkId>, RpcArg<AZ::Aabb>>::BindInterface<ProximityInterestChunk, &ProximityInterestChunk::AddRuleFn> AddRuleRpc;
Rpc<RpcArg<RuleNetworkId>>::BindInterface<ProximityInterestChunk, &ProximityInterestChunk::RemoveRuleFn> RemoveRuleRpc;
Rpc<RpcArg<RuleNetworkId>, RpcArg<AZ::Aabb>>::BindInterface<ProximityInterestChunk, &ProximityInterestChunk::UpdateRuleFn> UpdateRuleRpc;
Rpc<RpcArg<RuleNetworkId>, RpcArg<PeerId>, RpcArg<AZ::Aabb>>::BindInterface<ProximityInterestChunk, &ProximityInterestChunk::AddRuleForPeerFn> AddRuleForPeerRpc;
ProximityInterestHandler* m_interestHandler;
unordered_map<RuleNetworkId, ProximityInterestRule::Ptr> m_rules;
};
///////////////////////////////////////////////////////////////////////////
/*
* ProximityInterest
*/
ProximityInterest::ProximityInterest(ProximityInterestHandler* handler)
: m_bbox(AZ::Aabb::CreateNull())
, m_handler(handler)
{
AZ_Assert(m_handler, "Invalid interest handler");
}
///////////////////////////////////////////////////////////////////////////
/*
* ProximityInterestRule
*/
void ProximityInterestRule::Set(const AZ::Aabb& bbox)
{
m_bbox = bbox;
m_handler->UpdateRule(this);
}
void ProximityInterestRule::Destroy()
{
m_handler->DestroyRule(this);
}
///////////////////////////////////////////////////////////////////////////
/*
* ProximityInterestAttribute
*/
void ProximityInterestAttribute::Set(const AZ::Aabb& bbox)
{
m_bbox = bbox;
m_handler->UpdateAttribute(this);
}
void ProximityInterestAttribute::Destroy()
{
m_handler->DestroyAttribute(this);
}
///////////////////////////////////////////////////////////////////////////
/*
* ProximityInterestHandler
*/
ProximityInterestHandler::ProximityInterestHandler()
: m_im(nullptr)
, m_rm(nullptr)
, m_lastRuleNetId(0)
, m_rulesReplica(nullptr)
{
if (!ReplicaChunkDescriptorTable::Get().FindReplicaChunkDescriptor(ReplicaChunkClassId(ProximityInterestChunk::GetChunkName())))
{
ReplicaChunkDescriptorTable::Get().RegisterChunkType<ProximityInterestChunk>();
}
}
ProximityInterestRule::Ptr ProximityInterestHandler::CreateRule(PeerId peerId)
{
ProximityInterestRule* rulePtr = aznew ProximityInterestRule(this, peerId, GetNewRuleNetId());
if (peerId == m_rm->GetLocalPeerId())
{
m_rulesReplica->AddRuleRpc(rulePtr->GetNetworkId(), rulePtr->Get());
m_localRules.insert(rulePtr);
}
return rulePtr;
}
void ProximityInterestHandler::FreeRule(ProximityInterestRule* rule)
{
//TODO: should be pool-allocated
delete rule;
}
void ProximityInterestHandler::DestroyRule(ProximityInterestRule* rule)
{
if (m_rm && rule->GetPeerId() == m_rm->GetLocalPeerId())
{
m_rulesReplica->RemoveRuleRpc(rule->GetNetworkId());
}
rule->m_bbox = AZ::Aabb::CreateNull();
m_rules.insert(rule);
m_localRules.erase(rule);
}
void ProximityInterestHandler::UpdateRule(ProximityInterestRule* rule)
{
if (rule->GetPeerId() == m_rm->GetLocalPeerId())
{
m_rulesReplica->UpdateRuleRpc(rule->GetNetworkId(), rule->Get());
}
m_rules.insert(rule);
}
ProximityInterestAttribute::Ptr ProximityInterestHandler::CreateAttribute(ReplicaId replicaId)
{
return aznew ProximityInterestAttribute(this, replicaId);
}
void ProximityInterestHandler::FreeAttribute(ProximityInterestAttribute* attrib)
{
//TODO: should be pool-allocated
delete attrib;
}
void ProximityInterestHandler::DestroyAttribute(ProximityInterestAttribute* attrib)
{
attrib->m_bbox = AZ::Aabb::CreateNull();
m_attributes.insert(attrib);
}
void ProximityInterestHandler::UpdateAttribute(ProximityInterestAttribute* attrib)
{
m_attributes.insert(attrib);
}
void ProximityInterestHandler::OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer)
{
if (chunk != m_rulesReplica) // non-local
{
m_peerChunks.insert(AZStd::make_pair(peer->GetId(), chunk));
for (auto& rule : m_localRules)
{
chunk->AddRuleForPeerRpc(rule->GetNetworkId(), rule->GetPeerId(), rule->Get());
}
}
}
void ProximityInterestHandler::OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer)
{
(void)chunk;
m_peerChunks.erase(peer->GetId());
}
RuleNetworkId ProximityInterestHandler::GetNewRuleNetId()
{
++m_lastRuleNetId;
return m_rulesReplica->GetReplicaId() | (static_cast<AZ::u64>(m_lastRuleNetId) << 32);
}
ProximityInterestChunk* ProximityInterestHandler::FindRulesChunkByPeerId(PeerId peerId)
{
auto it = m_peerChunks.find(peerId);
if (it == m_peerChunks.end())
{
return nullptr;
}
else
{
return it->second;
}
}
const InterestMatchResult& ProximityInterestHandler::GetLastResult()
{
return m_resultCache;
}
void ProximityInterestHandler::UpdateInternal(InterestMatchResult& result)
{
//////////////////////////////////////////////
// just recalculating the whole state for now
for (auto attrIt = m_attributes.begin(); attrIt != m_attributes.end(); )
{
ProximityInterestAttribute* attr = *attrIt;
auto resultIt = result.insert(attr->GetReplicaId());
for (auto ruleIt = m_rules.begin(); ruleIt != m_rules.end(); ++ruleIt)
{
ProximityInterestRule* rule = *ruleIt;
if (rule->m_bbox.Overlaps(attr->m_bbox))
{
resultIt.first->second.insert(rule->GetPeerId());
}
}
if ((*attrIt)->IsDeleted())
{
attrIt = m_attributes.erase(attrIt);
delete attr;
}
else
{
++attrIt;
}
}
for (auto ruleIt = m_rules.begin(); ruleIt != m_rules.end(); )
{
ProximityInterestRule* rule = *ruleIt;
if (rule->IsDeleted())
{
ruleIt = m_rules.erase(ruleIt);
delete rule;
}
else
{
++ruleIt;
}
}
//////////////////////////////////////////////
}
void ProximityInterestHandler::Update()
{
m_resultCache.clear();
UpdateInternal(m_resultCache);
}
void ProximityInterestHandler::OnRulesHandlerRegistered(InterestManager* manager)
{
AZ_Assert(m_im == nullptr, "Handler is already registered with manager %p (%p)\n", m_im, manager);
AZ_Assert(m_rulesReplica == nullptr, "Rules replica is already created\n");
AZ_TracePrintf("GridMate", "Proximity interest handler is registered\n");
m_im = manager;
m_rm = m_im->GetReplicaManager();
m_rm->RegisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4), this);
auto replica = Replica::CreateReplica("ProximityInterestHandlerRules");
m_rulesReplica = CreateAndAttachReplicaChunk<ProximityInterestChunk>(replica);
m_rm->AddPrimary(replica);
}
void ProximityInterestHandler::OnRulesHandlerUnregistered(InterestManager* manager)
{
(void)manager;
AZ_Assert(m_im == manager, "Handler was not registered with manager %p (%p)\n", manager, m_im);
AZ_TracePrintf("GridMate", "Proximity interest handler is unregistered\n");
m_rulesReplica = nullptr;
m_im = nullptr;
m_rm->UnregisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4));
m_rm = nullptr;
for (auto& chunk : m_peerChunks)
{
chunk.second->m_interestHandler = nullptr;
}
m_peerChunks.clear();
m_localRules.clear();
for (ProximityInterestRule* rule : m_rules)
{
delete rule;
}
for (ProximityInterestAttribute* attr : m_attributes)
{
delete attr;
}
m_attributes.clear();
m_rules.clear();
m_resultCache.clear();
}
///////////////////////////////////////////////////////////////////////////
}
#else
// Optimized spatial handler.
#include "GridMate/Replica/Interest/ProximityInterestHandler.h"
#endif
namespace UnitTest {
/*
* Helper class to capture performance of various Interest Managers
*/
class PerfForInterestManager
{
public:
void Reset();
void PreUpdate();
void PostUpdate();
AZ::u32 GetTotalFrames() const;
float GetAverageFrame() const;
float GetWorstFrame() const;
float GetBestFrame() const;
private:
AZ::Debug::Timer m_timer;
AZ::u32 m_frameCount = 0;
float m_totalUpdateTime = 0.f;
float m_fastestFrame = 100.f;
float m_slowestFrame = 0.f;
};
PerfForInterestManager g_PerfIM = PerfForInterestManager();
PerfForInterestManager g_PerfUpdatingAttributes = PerfForInterestManager();
/*
* Utility function to tick the replica manager
*/
static void UpdateReplicas(ReplicaManager* replicaManager, InterestManager* interestManager)
{
if (interestManager)
{
// Measuring time it takes to execute an update.
g_PerfIM.PreUpdate();
interestManager->Update();
g_PerfIM.PostUpdate();
}
if (replicaManager)
{
replicaManager->Unmarshal();
replicaManager->UpdateFromReplicas();
replicaManager->UpdateReplicas();
replicaManager->Marshal();
}
}
class Integ_InterestTest
: public GridMateMPTestFixture
{
///////////////////////////////////////////////////////////////////
class InterestTestChunk
: public ReplicaChunk
{
public:
GM_CLASS_ALLOCATOR(InterestTestChunk);
InterestTestChunk()
: m_data("Data", 0)
, m_bitmaskAttributeData("BitmaskAttributeData")
, m_attribute(nullptr)
{
}
///////////////////////////////////////////////////////////////////
typedef AZStd::intrusive_ptr<InterestTestChunk> Ptr;
static const char* GetChunkName() { return "InterestTestChunk"; }
bool IsReplicaMigratable() override { return false; }
bool IsBroadcast() override
{
return false;
}
///////////////////////////////////////////////////////////////////
void OnReplicaActivate(const ReplicaContext& rc) override
{
AZ_Printf("GridMate", "InterestTestChunk::OnReplicaActivate repId=%08X(%s) fromPeerId=%08X localPeerId=%08X\n",
GetReplicaId(),
IsPrimary() ? "primary" : "proxy",
rc.m_peer ? rc.m_peer->GetId() : 0,
rc.m_rm->GetLocalPeerId());
BitmaskInterestHandler* ih = static_cast<BitmaskInterestHandler*>(rc.m_rm->GetUserContext(AZ_CRC("BitmaskInterestHandler", 0x5bf5d75b)));
if (ih)
{
m_attribute = ih->CreateAttribute(GetReplicaId());
m_attribute->Set(m_bitmaskAttributeData.Get());
}
}
void OnReplicaDeactivate(const ReplicaContext& rc) override
{
AZ_Printf("GridMate", "InterestTestChunk::OnReplicaDeactivate repId=%08X(%s) fromPeerId=%08X localPeerId=%08X\n",
GetReplicaId(),
IsPrimary() ? "primary" : "proxy",
rc.m_peer ? rc.m_peer->GetId() : 0,
rc.m_rm->GetLocalPeerId());
m_attribute = nullptr;
}
void BitmaskHandler(const InterestBitmask& bitmask, const TimeContext&)
{
if (m_attribute)
{
m_attribute->Set(bitmask);
}
}
DataSet<int> m_data;
DataSet<InterestBitmask>::BindInterface<InterestTestChunk, &InterestTestChunk::BitmaskHandler> m_bitmaskAttributeData;
BitmaskInterestAttribute::Ptr m_attribute;
};
///////////////////////////////////////////////////////////////////
class TestPeerInfo
: public SessionEventBus::Handler
{
public:
TestPeerInfo()
: m_gridMate(nullptr)
, m_lanSearch(nullptr)
, m_session(nullptr)
, m_im(nullptr)
, m_bitmaskHandler(nullptr)
, m_num(0)
{
GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType<GridMate::BitmaskInterestChunk>();
GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType<GridMate::ProximityInterestChunk>();
}
void CreateTestReplica()
{
m_im = aznew InterestManager();
InterestManagerDesc desc;
desc.m_rm = m_session->GetReplicaMgr();
m_im->Init(desc);
m_bitmaskHandler = aznew BitmaskInterestHandler();
m_im->RegisterHandler(m_bitmaskHandler);
m_rule = m_bitmaskHandler->CreateRule(m_session->GetReplicaMgr()->GetLocalPeerId());
m_rule->Set(1 << m_num);
auto r = Replica::CreateReplica("InterestTestReplica");
m_replica = CreateAndAttachReplicaChunk<InterestTestChunk>(r);
// Initializing attribute
// Shifing all by one - peer0 will recv from peer1, peer2 will recv from peer2, peer2 will recv from peer0
unsigned i = (m_num + 2) % Integ_InterestTest::k_numMachines;
m_replica->m_data.Set(m_num);
m_replica->m_bitmaskAttributeData.Set(1 << i);
m_session->GetReplicaMgr()->AddPrimary(r);
}
void UpdateAttribute()
{
// Shifing all by one - peer0 will recv from peer2, peer1 will recv from peer0, peer2 will recv from peer1
unsigned i = (m_num + 1) % Integ_InterestTest::k_numMachines;
m_replica->m_bitmaskAttributeData.Set(1 << i);
m_replica->m_attribute->Set(1 << i);
}
void DeleteAttribute()
{
m_replica->m_attribute = nullptr;
}
void UpdateRule()
{
m_rule->Set(0xffff);
}
void DeleteRule()
{
m_rule = nullptr;
}
void CreateRule()
{
m_rule = m_bitmaskHandler->CreateRule(m_session->GetReplicaMgr()->GetLocalPeerId());
m_rule->Set(0xffff);
}
void OnSessionCreated(GridSession* session) override
{
m_session = session;
if (m_session->IsHost())
{
CreateTestReplica();
}
}
void OnSessionJoined(GridSession* session) override
{
m_session = session;
CreateTestReplica();
}
void OnSessionDelete(GridSession* session) override
{
if (session == m_session)
{
m_rule = nullptr;
m_session = nullptr;
m_im->UnregisterHandler(m_bitmaskHandler);
delete m_bitmaskHandler;
delete m_im;
m_im = nullptr;
m_bitmaskHandler = nullptr;
}
}
void OnSessionError(GridSession* session, const string& errorMsg) override
{
(void)session;
(void)errorMsg;
AZ_TracePrintf("GridMate", "Session error: %s\n", errorMsg.c_str());
}
IGridMate* m_gridMate;
GridSearch* m_lanSearch;
GridSession* m_session;
InterestManager* m_im;
BitmaskInterestHandler* m_bitmaskHandler;
BitmaskInterestRule::Ptr m_rule;
unsigned m_num;
InterestTestChunk::Ptr m_replica;
};
public:
Integ_InterestTest()
{
ReplicaChunkDescriptorTable::Get().RegisterChunkType<InterestTestChunk>();
//////////////////////////////////////////////////////////////////////////
// Create all grid mates
m_peers[0].m_gridMate = m_gridMate;
m_peers[0].SessionEventBus::Handler::BusConnect(m_peers[0].m_gridMate);
m_peers[0].m_num = 0;
for (int i = 1; i < k_numMachines; ++i)
{
GridMateDesc desc;
m_peers[i].m_gridMate = GridMateCreate(desc);
AZ_TEST_ASSERT(m_peers[i].m_gridMate);
m_peers[i].m_num = i;
m_peers[i].SessionEventBus::Handler::BusConnect(m_peers[i].m_gridMate);
}
//////////////////////////////////////////////////////////////////////////
for (int i = 0; i < k_numMachines; ++i)
{
// start the multiplayer service (session mgr, extra allocator, etc.)
StartGridMateService<LANSessionService>(m_peers[i].m_gridMate, SessionServiceDesc());
AZ_TEST_ASSERT(LANSessionServiceBus::FindFirstHandler(m_peers[i].m_gridMate) != nullptr);
}
}
virtual ~Integ_InterestTest()
{
StopGridMateService<LANSessionService>(m_peers[0].m_gridMate);
for (int i = 1; i < k_numMachines; ++i)
{
if (m_peers[i].m_gridMate)
{
m_peers[i].SessionEventBus::Handler::BusDisconnect();
GridMateDestroy(m_peers[i].m_gridMate);
}
}
// this will stop the first IGridMate which owns the memory allocators.
m_peers[0].SessionEventBus::Handler::BusDisconnect();
}
void run()
{
TestCarrierDesc carrierDesc;
carrierDesc.m_enableDisconnectDetection = false;// true;
carrierDesc.m_threadUpdateTimeMS = 10;
carrierDesc.m_familyType = Driver::BSD_AF_INET;
LANSessionParams sp;
sp.m_topology = ST_PEER_TO_PEER;
sp.m_numPublicSlots = 64;
sp.m_port = k_hostPort;
EBUS_EVENT_ID_RESULT(m_peers[k_host].m_session, m_peers[k_host].m_gridMate, LANSessionServiceBus, HostSession, sp, carrierDesc);
m_peers[k_host].m_session->GetReplicaMgr()->SetAutoBroadcast(false);
int listenPort = k_hostPort;
for (int i = 0; i < k_numMachines; ++i)
{
if (i == k_host)
{
continue;
}
LANSearchParams searchParams;
searchParams.m_serverPort = k_hostPort;
searchParams.m_listenPort = listenPort == k_hostPort ? 0 : ++listenPort; // first client will use ephemeral port, the rest specify return ports
searchParams.m_familyType = Driver::BSD_AF_INET;
EBUS_EVENT_ID_RESULT(m_peers[i].m_lanSearch, m_peers[i].m_gridMate, LANSessionServiceBus, StartGridSearch, searchParams);
}
static const int maxNumUpdates = 300;
int numUpdates = 0;
TimeStamp time = AZStd::chrono::system_clock::now();
while (numUpdates <= maxNumUpdates)
{
if (numUpdates == 100)
{
// Checking everybody received only one replica:
// peer0 -> rep1, peer1 -> rep2, peer2 -> rep0
for (int i = 0; i < k_numMachines; ++i)
{
ReplicaId repId = m_peers[(i + 1) % k_numMachines].m_replica->GetReplicaId();
auto repRecv = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId);
AZ_TEST_ASSERT(repRecv != nullptr);
repId = m_peers[(i + 2) % k_numMachines].m_replica->GetReplicaId();
auto repNotRecv = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId);
AZ_TEST_ASSERT(repNotRecv == nullptr);
// rotating mask left
m_peers[i].UpdateAttribute();
}
}
if (numUpdates == 150)
{
// Checking everybody received only one replica:
// peer0 -> rep2, peer1 -> rep0, peer2 -> rep1
for (int i = 0; i < k_numMachines; ++i)
{
ReplicaId repId = m_peers[(i + 2) % k_numMachines].m_replica->GetReplicaId();
auto repRecv = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId);
AZ_TEST_ASSERT(repRecv != nullptr);
repId = m_peers[(i + 1) % k_numMachines].m_replica->GetReplicaId();
auto repNotRecv = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId);
AZ_TEST_ASSERT(repNotRecv == nullptr);
// setting rules to accept all replicas
m_peers[i].UpdateRule();
}
}
if (numUpdates == 200)
{
// Checking everybody received all replicas
for (int i = 0; i < k_numMachines; ++i)
{
for (int j = 0; j < k_numMachines; ++j)
{
ReplicaId repId = m_peers[j].m_replica->GetReplicaId();
auto rep = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId);
AZ_TEST_ASSERT(rep);
}
// Deleting all attributes
m_peers[i].DeleteAttribute();
}
}
if (numUpdates == 250)
{
// Checking everybody lost all replicas (except primary)
for (int i = 0; i < k_numMachines; ++i)
{
for (int j = 0; j < k_numMachines; ++j)
{
if (i == j)
{
continue;
}
ReplicaId repId = m_peers[j].m_replica->GetReplicaId();
auto rep = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId);
AZ_TEST_ASSERT(rep == nullptr);
}
// deleting all rules
m_peers[i].DeleteRule();
}
}
//////////////////////////////////////////////////////////////////////////
for (int i = 0; i < k_numMachines; ++i)
{
if (m_peers[i].m_gridMate)
{
m_peers[i].m_gridMate->Update();
if (m_peers[i].m_session)
{
UpdateReplicas(m_peers[i].m_session->GetReplicaMgr(), m_peers[i].m_im);
}
}
}
Update();
//////////////////////////////////////////////////////////////////////////
for (int i = 0; i < k_numMachines; ++i)
{
if (m_peers[i].m_lanSearch && m_peers[i].m_lanSearch->IsDone())
{
AZ_TEST_ASSERT(m_peers[i].m_lanSearch->GetNumResults() == 1);
JoinParams jp;
EBUS_EVENT_ID_RESULT(m_peers[i].m_session, m_peers[i].m_gridMate, LANSessionServiceBus, JoinSessionBySearchInfo, static_cast<const LANSearchInfo&>(*m_peers[i].m_lanSearch->GetResult(0)), jp, carrierDesc);
m_peers[i].m_session->GetReplicaMgr()->SetAutoBroadcast(false);
m_peers[i].m_lanSearch->Release();
m_peers[i].m_lanSearch = nullptr;
}
}
//////////////////////////////////////////////////////////////////////////
// Debug Info
TimeStamp now = AZStd::chrono::system_clock::now();
if (AZStd::chrono::milliseconds(now - time).count() > 1000)
{
time = now;
for (int i = 0; i < k_numMachines; ++i)
{
if (m_peers[i].m_session == nullptr)
{
continue;
}
if (m_peers[i].m_session->IsHost())
{
AZ_Printf("GridMate", "------ Host %d ------\n", i);
}
else
{
AZ_Printf("GridMate", "------ Client %d ------\n", i);
}
AZ_Printf("GridMate", "Session %s Members: %d Host: %s Clock: %d\n", m_peers[i].m_session->GetId().c_str(), m_peers[i].m_session->GetNumberOfMembers(), m_peers[i].m_session->IsHost() ? "yes" : "no", m_peers[i].m_session->GetTime());
for (unsigned int iMember = 0; iMember < m_peers[i].m_session->GetNumberOfMembers(); ++iMember)
{
GridMember* member = m_peers[i].m_session->GetMemberByIndex(iMember);
AZ_Printf("GridMate", " Member: %s(%s) Host: %s Local: %s\n", member->GetName().c_str(), member->GetId().ToString().c_str(), member->IsHost() ? "yes" : "no", member->IsLocal() ? "yes" : "no");
}
AZ_Printf("GridMate", "\n");
}
}
//////////////////////////////////////////////////////////////////////////
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30));
numUpdates++;
}
}
static const int k_numMachines = 3;
static const int k_host = 0;
static const int k_hostPort = 5450;
TestPeerInfo m_peers[k_numMachines];
};
/*
* Testing worse case performance of thousands of replicas and a few peers where all replicas/attributes change every frame
* and peers/rules change every frame as well.
*/
class LargeWorldTest
: public GridMateMPTestFixture
{
///////////////////////////////////////////////////////////////////
class LargeWorldTestChunk
: public ReplicaChunk
{
public:
GM_CLASS_ALLOCATOR(LargeWorldTestChunk);
LargeWorldTestChunk()
: m_data("Data", 0)
, m_proximityAttributeData("LargeWorldAttributeData")
, m_attribute(nullptr)
{
}
///////////////////////////////////////////////////////////////////
typedef AZStd::intrusive_ptr<LargeWorldTestChunk> Ptr;
static const char* GetChunkName() { return "LargeWorldTestChunk"; }
bool IsReplicaMigratable() override { return false; }
bool IsBroadcast() override
{
return false;
}
///////////////////////////////////////////////////////////////////
void OnReplicaActivate(const ReplicaContext& rc) override
{
/*if (!IsPrimary())*/
/*{
AZ_Printf("GridMate", "LargeWorldTestChunk::OnReplicaActivate repId=%08X(%s) fromPeerId=%08X localPeerId=%08X\n",
GetReplicaId(),
IsPrimary() ? "primary" : "proxy",
rc.m_peer ? rc.m_peer->GetId() : 0,
rc.m_rm->GetLocalPeerId());
}*/
if (ProximityInterestHandler* ih = static_cast<ProximityInterestHandler*>(rc.m_rm->GetUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4))))
{
m_attribute = ih->CreateAttribute(GetReplicaId());
m_attribute->Set(m_proximityAttributeData.Get());
}
}
void OnReplicaDeactivate(const ReplicaContext& /*rc*/) override
{
m_attribute = nullptr;
}
void ProximityHandler(const AZ::Aabb& bounds, const TimeContext&)
{
if (m_attribute)
{
m_attribute->Set(bounds);
}
}
DataSet<int> m_data;
DataSet<AZ::Aabb>::BindInterface<LargeWorldTestChunk, &LargeWorldTestChunk::ProximityHandler> m_proximityAttributeData;
ProximityInterestAttribute::Ptr m_attribute;
};
///////////////////////////////////////////////////////////////////
struct LargeWorldParams
{
AZ::u32 index = 0;
const float commonSize = 50;
const AZ::Aabb box = AZ::Aabb::CreateFromMinMax(
AZ::Vector3::CreateZero(),
AZ::Vector3::CreateOne() * commonSize);
const float commonStep = commonSize + 1;
};
static LargeWorldParams& GetWorldParams()
{
static LargeWorldParams worldParams;
return worldParams;
}
/*
* Create a chain of boxes in spaces along X axis
*/
static AZ::Aabb CreateNextRuleSpace()
{
auto offset = GetWorldParams().commonStep * aznumeric_cast<float>(GetWorldParams().index);
float min[] = { offset, 0, 0 };
float max[] = {
GetWorldParams().commonSize + offset,
GetWorldParams().commonSize,
GetWorldParams().commonSize };
auto bounds = AZ::Aabb::CreateFromMinMax(
AZ::Vector3::CreateFromFloat3(min),
AZ::Vector3::CreateFromFloat3(max));
GetWorldParams().index++;
return bounds;
}
class LargeWorldTestPeerInfo
: public SessionEventBus::Handler
{
public:
LargeWorldTestPeerInfo()
: m_gridMate(nullptr)
, m_lanSearch(nullptr)
, m_session(nullptr)
, m_im(nullptr)
, m_proximityHandler(nullptr)
, m_num(0)
{
GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType<GridMate::BitmaskInterestChunk>();
GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType<GridMate::ProximityInterestChunk>();
}
~LargeWorldTestPeerInfo()
{
SessionEventBus::Handler::BusDisconnect();
}
void CreateHostRuleHandler()
{
m_im = aznew InterestManager();
InterestManagerDesc desc;
desc.m_rm = m_session->GetReplicaMgr();
m_im->Init(desc);
m_proximityHandler = aznew ProximityInterestHandler();
m_im->RegisterHandler(m_proximityHandler);
m_rule = m_proximityHandler->CreateRule(m_session->GetReplicaMgr()->GetLocalPeerId());
m_rule->Set(AZ::Aabb::CreateNull()); // host rule doesn't matter in this test
}
void CreateRuleHandler()
{
m_im = aznew InterestManager();
InterestManagerDesc desc;
desc.m_rm = m_session->GetReplicaMgr();
m_im->Init(desc);
m_proximityHandler = aznew ProximityInterestHandler();
m_im->RegisterHandler(m_proximityHandler);
m_rule = m_proximityHandler->CreateRule(m_session->GetReplicaMgr()->GetLocalPeerId());
m_rule->Set(CreateNextRuleSpace());
}
void CreateTestReplica(const AZ::Aabb& bounds)
{
auto r = Replica::CreateReplica("LargeWorldTestReplica");
auto replica = CreateAndAttachReplicaChunk<LargeWorldTestChunk>(r);
// Initializing attribute
replica->m_data.Set(m_num);
replica->m_proximityAttributeData.Set(bounds);
m_replicas.push_back(replica);
m_session->GetReplicaMgr()->AddPrimary(r);
}
void PopulateWorld()
{
AZ_Printf("GridMate", "LargeWorldTestChunk::PopulateWorld() starting...\n");
const float worldSizeInBoxes = 50;
const auto oneBox = AZ::Vector3::CreateOne();
const auto thickness = 1;
for (float dx = 0; dx < worldSizeInBoxes; ++dx)
{
for (float dy = 0; dy < thickness; ++dy)
{
for (float dz = 0; dz < thickness; ++dz)
{
auto aabb = AZ::Aabb::CreateFromMinMax(
AZ::Vector3(50 * dx + 5, dy, dz),
AZ::Vector3(50 * dx + 5, dy, dz) + oneBox);
CreateTestReplica(aabb);
}
}
}
AZ_Printf("GridMate", "LargeWorldTestChunk::PopulateWorld() ... DONE\n");
}
void UpdateAttribute(LargeWorldTestChunk::Ptr replica)
{
if (replica && replica->m_attribute)
{
auto sameValue = replica->m_attribute->Get();
replica->m_proximityAttributeData.Set(sameValue);
replica->m_attribute->Set(sameValue);
}
}
void UpdateRule()
{
// just make it dirty for now
if (m_rule)
{
auto sameValue = m_rule->Get();
m_rule->Set(sameValue);
}
}
void DeleteRule()
{
m_rule = nullptr;
}
void OnSessionCreated(GridSession* session) override
{
m_session = session;
if (m_session->IsHost())
{
CreateHostRuleHandler();
PopulateWorld();
}
}
void OnSessionJoined(GridSession* session) override
{
m_session = session;
CreateRuleHandler();
}
void OnSessionDelete(GridSession* session) override
{
if (session == m_session)
{
m_rule = nullptr;
m_session = nullptr;
m_im->UnregisterHandler(m_proximityHandler);
delete m_proximityHandler;
delete m_im;
m_im = nullptr;
m_proximityHandler = nullptr;
}
}
void OnSessionError(GridSession* session, const string& errorMsg) override
{
(void)session;
(void)errorMsg;
AZ_TracePrintf("GridMate", "Session error: %s\n", errorMsg.c_str());
}
IGridMate* m_gridMate;
GridSearch* m_lanSearch;
GridSession* m_session;
InterestManager* m_im;
ProximityInterestHandler* m_proximityHandler;
ProximityInterestRule::Ptr m_rule;
unsigned m_num;
AZStd::vector<LargeWorldTestChunk::Ptr> m_replicas;
};
public:
LargeWorldTest() : UnitTest::GridMateMPTestFixture(500u * 1024u * 1024u) // 500Mb
{
ReplicaChunkDescriptorTable::Get().RegisterChunkType<LargeWorldTestChunk>();
//////////////////////////////////////////////////////////////////////////
// Create all grid mates
m_peers[0].m_gridMate = m_gridMate;
m_peers[0].SessionEventBus::Handler::BusConnect(m_peers[0].m_gridMate);
m_peers[0].m_num = 0;
for (int i = 1; i < k_numMachines; ++i)
{
GridMateDesc desc;
m_peers[i].m_gridMate = GridMateCreate(desc);
AZ_TEST_ASSERT(m_peers[i].m_gridMate);
m_peers[i].m_num = i;
m_peers[i].SessionEventBus::Handler::BusConnect(m_peers[i].m_gridMate);
}
//////////////////////////////////////////////////////////////////////////
for (int i = 0; i < k_numMachines; ++i)
{
// start the multiplayer service (session mgr, extra allocator, etc.)
StartGridMateService<LANSessionService>(m_peers[i].m_gridMate, SessionServiceDesc());
AZ_TEST_ASSERT(LANSessionServiceBus::FindFirstHandler(m_peers[i].m_gridMate) != nullptr);
}
}
virtual ~LargeWorldTest()
{
StopGridMateService<LANSessionService>(m_peers[0].m_gridMate);
for (int i = 1; i < k_numMachines; ++i)
{
if (m_peers[i].m_gridMate)
{
m_peers[i].SessionEventBus::Handler::BusDisconnect();
GridMateDestroy(m_peers[i].m_gridMate);
}
}
// this will stop the first IGridMate which owns the memory allocators.
m_peers[0].SessionEventBus::Handler::BusDisconnect();
}
void run()
{
g_PerfIM.Reset();
g_PerfUpdatingAttributes.Reset();
TestCarrierDesc carrierDesc;
carrierDesc.m_enableDisconnectDetection = false;
carrierDesc.m_threadUpdateTimeMS = 10;
carrierDesc.m_familyType = Driver::BSD_AF_INET;
LANSessionParams sp;
sp.m_topology = ST_PEER_TO_PEER;
sp.m_numPublicSlots = 64;
sp.m_port = k_hostPort;
EBUS_EVENT_ID_RESULT(m_peers[k_host].m_session, m_peers[k_host].m_gridMate, LANSessionServiceBus, HostSession, sp, carrierDesc);
m_peers[k_host].m_session->GetReplicaMgr()->SetAutoBroadcast(false);
int listenPort = k_hostPort;
for (int i = 0; i < k_numMachines; ++i)
{
if (i == k_host)
{
continue;
}
LANSearchParams searchParams;
searchParams.m_serverPort = k_hostPort;
searchParams.m_listenPort = listenPort == k_hostPort ? 0 : ++listenPort; // first client will use ephemeral port, the rest specify return ports
searchParams.m_familyType = Driver::BSD_AF_INET;
EBUS_EVENT_ID_RESULT(m_peers[i].m_lanSearch, m_peers[i].m_gridMate, LANSessionServiceBus, StartGridSearch, searchParams);
}
static const int maxNumUpdates = 300;
int numUpdates = 0;
TimeStamp time = AZStd::chrono::system_clock::now();
while (numUpdates <= maxNumUpdates)
{
g_PerfUpdatingAttributes.PreUpdate();
for (LargeWorldTestChunk::Ptr replica : m_peers[0].m_replicas)
{
m_peers[0].UpdateAttribute(replica);
}
g_PerfUpdatingAttributes.PostUpdate();
for (auto peer : m_peers)
{
peer.UpdateRule();
}
//if (numUpdates == 100)
//{
// // check how many replicas each client got
// for (AZ::u32 i = 1; i < k_numMachines; ++i)
// {
// AZ::u32 count = 0;
// for (auto& replica : m_peers[0].m_replicas)
// {
// ReplicaId repId = replica->GetReplicaId();
// if (auto rep = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId))
// {
// count++;
// }
// }
// const AZ::Aabb& bounds = m_peers[i].m_rule->Get();
// AZ_Printf("GridMate", "Session %s Members: %d Bounds: %f.%f.%f-%f.%f.%f Replicas: %d\n", m_peers[i].m_session->GetId().c_str(), m_peers[i].m_session->GetNumberOfMembers(),
// static_cast<float>(bounds.GetMin().GetX()),
// static_cast<float>(bounds.GetMin().GetY()),
// static_cast<float>(bounds.GetMin().GetZ()),
// static_cast<float>(bounds.GetMax().GetX()),
// static_cast<float>(bounds.GetMax().GetY()),
// static_cast<float>(bounds.GetMax().GetZ()), count);
// AZ_Assert(count == 1, "Should have at least some replicas to start with");
// }
//}
if (numUpdates == 200)
{
// Deleting all attributes
for (auto& replica : m_peers[0].m_replicas)
{
replica->m_attribute = nullptr;
}
}
if (numUpdates == 250)
{
// Checking everybody lost all replicas (except primary)
for (int i = 0; i < k_numMachines; ++i)
{
/*for (int j = 0; j < k_numMachines; ++j)
{
if (i == j)
{
continue;
}
ReplicaId repId = m_peers[j].m_replica->GetReplicaId();
auto rep = m_peers[i].m_session->GetReplicaMgr()->FindReplica(repId);
AZ_TEST_ASSERT(rep == nullptr);
}*/
// deleting all rules
m_peers[i].DeleteRule();
}
}
//////////////////////////////////////////////////////////////////////////
for (int i = 0; i < k_numMachines; ++i)
{
if (m_peers[i].m_gridMate)
{
m_peers[i].m_gridMate->Update();
if (m_peers[i].m_session)
{
UpdateReplicas(m_peers[i].m_session->GetReplicaMgr(), m_peers[i].m_im);
}
}
}
Update();
//////////////////////////////////////////////////////////////////////////
for (int i = 0; i < k_numMachines; ++i)
{
if (m_peers[i].m_lanSearch && m_peers[i].m_lanSearch->IsDone())
{
AZ_TEST_ASSERT(m_peers[i].m_lanSearch->GetNumResults() == 1);
JoinParams jp;
EBUS_EVENT_ID_RESULT(m_peers[i].m_session, m_peers[i].m_gridMate, LANSessionServiceBus, JoinSessionBySearchInfo, static_cast<const LANSearchInfo&>(*m_peers[i].m_lanSearch->GetResult(0)), jp, carrierDesc);
m_peers[i].m_session->GetReplicaMgr()->SetAutoBroadcast(false);
m_peers[i].m_lanSearch->Release();
m_peers[i].m_lanSearch = nullptr;
}
}
//////////////////////////////////////////////////////////////////////////
// Debug Info
TimeStamp now = AZStd::chrono::system_clock::now();
if (AZStd::chrono::milliseconds(now - time).count() > 1000)
{
time = now;
for (int i = 0; i < k_numMachines; ++i)
{
if (m_peers[i].m_session == nullptr)
{
continue;
}
if (m_peers[i].m_session->IsHost())
{
AZ_Printf("GridMate", "------ Host %d ------\n", i);
}
else
{
AZ_Printf("GridMate", "------ Client %d ------\n", i);
}
AZ_Printf("GridMate", "Session %s Members: %d Host: %s Clock: %d\n", m_peers[i].m_session->GetId().c_str(), m_peers[i].m_session->GetNumberOfMembers(), m_peers[i].m_session->IsHost() ? "yes" : "no", m_peers[i].m_session->GetTime());
for (unsigned int iMember = 0; iMember < m_peers[i].m_session->GetNumberOfMembers(); ++iMember)
{
GridMember* member = m_peers[i].m_session->GetMemberByIndex(iMember);
AZ_Printf("GridMate", " Member: %s(%s) Host: %s Local: %s\n", member->GetName().c_str(), member->GetId().ToString().c_str(), member->IsHost() ? "yes" : "no", member->IsLocal() ? "yes" : "no");
}
AZ_Printf("GridMate", "\n");
}
}
//////////////////////////////////////////////////////////////////////////
//AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30));
numUpdates++;
}
auto averageFrame = g_PerfIM.GetAverageFrame();
auto bestFrame = g_PerfIM.GetBestFrame();
auto worstFrame = g_PerfIM.GetWorstFrame();
auto frames = g_PerfIM.GetTotalFrames();
AZ_Printf("GridMate", "Interest manager performance: average_frame = %f sec, frames = %d, best= %f sec, worst= %f sec\n",
averageFrame, frames, bestFrame, worstFrame);
AZ_Printf("GridMate", "Updating attributes: average_frame = %f sec, frames = %d, best= %f sec, worst= %f sec\n",
g_PerfUpdatingAttributes.GetAverageFrame(),
g_PerfUpdatingAttributes.GetTotalFrames(),
g_PerfUpdatingAttributes.GetBestFrame(),
g_PerfUpdatingAttributes.GetWorstFrame());
}
static const int k_numMachines = 3;
static const int k_host = 0;
static const int k_hostPort = 5450;
LargeWorldTestPeerInfo m_peers[k_numMachines];
};
void PerfForInterestManager::Reset()
{
m_frameCount = 0;
m_totalUpdateTime = 0;
m_slowestFrame = 0;
m_fastestFrame = 100.f;
}
void PerfForInterestManager::PreUpdate()
{
m_timer.Stamp();
}
void PerfForInterestManager::PostUpdate()
{
auto frameTime = m_timer.StampAndGetDeltaTimeInSeconds();
m_totalUpdateTime += frameTime;
m_frameCount++;
m_slowestFrame = AZStd::max<float>(m_slowestFrame, frameTime);
m_fastestFrame = AZStd::min<float>(m_fastestFrame, frameTime);
}
AZ::u32 PerfForInterestManager::GetTotalFrames() const
{
return m_frameCount;
}
float PerfForInterestManager::GetAverageFrame() const
{
if (m_frameCount > 0)
{
return m_totalUpdateTime / m_frameCount;
}
return 0;
}
float PerfForInterestManager::GetWorstFrame() const
{
return m_slowestFrame;
}
float PerfForInterestManager::GetBestFrame() const
{
return m_fastestFrame;
}
class ProximityHandlerTests
: public GridMateMPTestFixture
{
public:
struct xyz
{
float x, y, z;
};
static AZ::Aabb CreateBox(xyz min, float size)
{
return AZ::Aabb::CreateFromMinMax(
AZ::Vector3::CreateFromFloat3(&min.x),
AZ::Vector3::CreateFromFloat3(&min.x) + AZ::Vector3::CreateOne() * size);
}
static void run()
{
SimpleFirstUpdate();
SecondUpdateAfterNoChanges();
SimpleOutsideOfRule();
AttributeMovingOutsideOfRule();
RuleMovingAndAttributeIsOut();
RuleDestroyed();
AttributeDestroyed();
}
static void RuleMovingAndAttributeIsOut()
{
AZStd::unique_ptr<ProximityInterestHandler> handler(aznew ProximityInterestHandler());
auto attribute1 = handler->CreateAttribute(1);
attribute1->Set(CreateBox({ 0, 0, 0 }, 10));
auto rule1 = handler->CreateRule(100);
rule1->Set(CreateBox({ 0, 0, 0 }, 100));
handler->Update();
InterestMatchResult results = handler->GetLastResult();
//ProximityInterestHandler::DebugPrint(results, "1st");
AZ_TEST_ASSERT(results[1].size() == 1);
AZ_TEST_ASSERT(results[1].find(100) != results[1].end());
// now move the attribute outside of the rule
rule1->Set(CreateBox({ 1000, 0, 0 }, 100));
handler->Update();
results = handler->GetLastResult();
//ProximityInterestHandler::DebugPrint(results, "2nd");
AZ_TEST_ASSERT(results[1].size() == 0);
}
static void AttributeMovingOutsideOfRule()
{
AZStd::unique_ptr<ProximityInterestHandler> handler(aznew ProximityInterestHandler());
auto attribute1 = handler->CreateAttribute(1);
attribute1->Set(CreateBox({ 0, 0, 0 }, 10));
auto rule1 = handler->CreateRule(100);
rule1->Set(CreateBox({ 0, 0, 0 }, 100));
handler->Update();
InterestMatchResult results = handler->GetLastResult();
//ProximityInterestHandler::DebugPrint(results, "1st");
AZ_TEST_ASSERT(results[1].size() == 1);
AZ_TEST_ASSERT(results[1].find(100) != results[1].end());
// now move the attribute outside of the rule
attribute1->Set(CreateBox({ -1000, 0, 0 }, 10));
handler->Update();
results = handler->GetLastResult();
//ProximityInterestHandler::DebugPrint(results, "2nd");
AZ_TEST_ASSERT(results[1].size() == 0);
}
static void SimpleFirstUpdate()
{
AZStd::unique_ptr<ProximityInterestHandler> handler(aznew ProximityInterestHandler());
auto attribute1 = handler->CreateAttribute(1);
attribute1->Set(CreateBox({ 0, 0, 0 }, 10));
auto rule1 = handler->CreateRule(100);
rule1->Set(CreateBox({ 0, 0, 0 }, 100));
handler->Update();
InterestMatchResult results = handler->GetLastResult();
//ProximityInterestHandler::PrintMatchResult(results, "test");
AZ_TEST_ASSERT(results[1].size() == 1);
AZ_TEST_ASSERT(results[1].find(100) != results[1].end());
}
static void SecondUpdateAfterNoChanges()
{
AZStd::unique_ptr<ProximityInterestHandler> handler(aznew ProximityInterestHandler());
auto attribute1 = handler->CreateAttribute(1);
attribute1->Set(CreateBox({ 0, 0, 0 }, 10));
auto rule1 = handler->CreateRule(100);
rule1->Set(CreateBox({ 0, 0, 0 }, 100));
handler->Update();
handler->Update();
InterestMatchResult results = handler->GetLastResult();
//ProximityInterestHandler::PrintMatchResult(results, "test");
AZ_TEST_ASSERT(results.size() == 0);
}
static void SimpleOutsideOfRule()
{
AZStd::unique_ptr<ProximityInterestHandler> handler(aznew ProximityInterestHandler());
auto attribute1 = handler->CreateAttribute(1);
attribute1->Set(CreateBox({ -1000, 0, 0 }, 10));
auto rule1 = handler->CreateRule(100);
rule1->Set(CreateBox({ 0, 0, 0 }, 100));
handler->Update();
InterestMatchResult results = handler->GetLastResult();
//ProximityInterestHandler::PrintMatchResult(results, "test");
AZ_TEST_ASSERT(results.size() == 1);
AZ_TEST_ASSERT(results[1].size() == 0);
}
static void RuleDestroyed()
{
AZStd::unique_ptr<ProximityInterestHandler> handler(aznew ProximityInterestHandler());
auto attribute1 = handler->CreateAttribute(1);
attribute1->Set(CreateBox({ 0, 0, 0 }, 10));
{
auto rule1 = handler->CreateRule(100);
rule1->Set(CreateBox({ 0, 0, 0 }, 100));
handler->Update();
InterestMatchResult results = handler->GetLastResult();
AZ_TEST_ASSERT(results.size() == 1);
AZ_TEST_ASSERT(results[1].size() == 1);
}
// rule1 should have been destroyed by now
handler->Update();
InterestMatchResult results = handler->GetLastResult();
//ProximityInterestHandler::PrintMatchResult(results, "last");
AZ_TEST_ASSERT(results.size() == 1);
AZ_TEST_ASSERT(results[1].size() == 0);
}
static void AttributeDestroyed()
{
AZStd::unique_ptr<ProximityInterestHandler> handler(aznew ProximityInterestHandler());
auto rule1 = handler->CreateRule(100);
rule1->Set(CreateBox({ 0, 0, 0 }, 100));
{
auto attribute1 = handler->CreateAttribute(1);
attribute1->Set(CreateBox({ 0, 0, 0 }, 10));
handler->Update();
InterestMatchResult results = handler->GetLastResult();
AZ_TEST_ASSERT(results.size() == 1);
AZ_TEST_ASSERT(results[1].size() == 1);
}
// attribute1 should have been destroyed by now, but it will show up once to remove it from affected peers
handler->Update();
InterestMatchResult results = handler->GetLastResult();
results.PrintMatchResult("last");
AZ_TEST_ASSERT(results.size() == 1);
AZ_TEST_ASSERT(results[1].size() == 0);
// and now attribute1 should not show up in the changes
handler->Update();
results = handler->GetLastResult();
results.PrintMatchResult("last");
AZ_TEST_ASSERT(results.size() == 0);
}
};
}; // namespace UnitTest
GM_TEST_SUITE(InterestSuite)
GM_TEST(Integ_InterestTest);
#if AZ_TRAIT_GRIDMATE_TEST_EXCLUDE_LARGEWORLDTEST != 0
GM_TEST(LargeWorldTest);
#endif
GM_TEST(ProximityHandlerTests);
GM_TEST_SUITE_END()