Removes DeltaCompressedDataSet, ReplicaFunctions.inl and BitmaskInterestHandler from GridMate
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
@@ -1,257 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#ifndef GM_DELTACOMPRESSED_DATASET_H
|
||||
#define GM_DELTACOMPRESSED_DATASET_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <GridMate/Serialize/DataMarshal.h>
|
||||
#include <GridMate/Replica/DataSet.h>
|
||||
|
||||
namespace GridMate
|
||||
{
|
||||
namespace Helper
|
||||
{
|
||||
template<AZ::u32 DeltaRange>
|
||||
AZ::u8 GetQuantized(float value)
|
||||
{
|
||||
/*
|
||||
* Quantizing into a single byte, thus 255 values.
|
||||
* [-DeltaRange V +DeltaRange]
|
||||
* [0 Q 255]
|
||||
* Given V, solve for Q.
|
||||
*/
|
||||
const float quantized = (value + DeltaRange) * 255.f / (2.f * DeltaRange);
|
||||
const int clamped = AZ::GetClamp(static_cast<int>(quantized), 0, 255);
|
||||
return static_cast<AZ::u8>(clamped);
|
||||
}
|
||||
|
||||
template<AZ::u32 DeltaRange>
|
||||
float GetUnquantized(AZ::u8 quantized)
|
||||
{
|
||||
/*
|
||||
* Unquantizing from a single byte, out of 255 values.
|
||||
* [0 Q 255]
|
||||
* [-DeltaRange V +DeltaRange]
|
||||
* Given Q, solve for V.
|
||||
*/
|
||||
return 2 * DeltaRange * quantized / 255.f - DeltaRange;
|
||||
}
|
||||
|
||||
template<typename FieldType>
|
||||
struct DeltaHelper;
|
||||
|
||||
/**
|
||||
* \brief Works for integer and floating points numbers
|
||||
*/
|
||||
template<typename FieldType>
|
||||
struct DeltaHelper
|
||||
{
|
||||
static bool IsWithinDelta(const FieldType& base, const FieldType& another, AZ::u32 deltaRange)
|
||||
{
|
||||
return abs(base - another) < deltaRange;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Specialization for AZ::Vector3
|
||||
*/
|
||||
template<>
|
||||
struct DeltaHelper<AZ::Vector3>
|
||||
{
|
||||
static bool IsWithinDelta(const AZ::Vector3& base, const AZ::Vector3& another, AZ::u32 deltaRange)
|
||||
{
|
||||
const AZ::Vector3 absDiff = (base - another).GetAbs();
|
||||
return absDiff.GetX() < deltaRange && absDiff.GetY() < deltaRange && absDiff.GetZ() < deltaRange;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Packing a value into a single byte within +/- @DeltaRange
|
||||
*/
|
||||
template<AZ::u32 DeltaRange, typename FieldType>
|
||||
class DeltaMarshaller;
|
||||
|
||||
// float specialization
|
||||
template<AZ::u32 DeltaRange>
|
||||
class DeltaMarshaller<DeltaRange, float>
|
||||
{
|
||||
public:
|
||||
void Marshal(WriteBuffer& wb, const float &value)
|
||||
{
|
||||
wb.Write(Helper::GetQuantized<DeltaRange>(value));
|
||||
}
|
||||
|
||||
void Unmarshal(float& value, ReadBuffer &rb)
|
||||
{
|
||||
AZ::u8 delta;
|
||||
rb.Read(delta);
|
||||
value = Helper::GetUnquantized<DeltaRange>(delta);
|
||||
}
|
||||
};
|
||||
|
||||
// AZ::Vector3 specialization
|
||||
template<AZ::u32 DeltaRange>
|
||||
class DeltaMarshaller<DeltaRange, AZ::Vector3>
|
||||
{
|
||||
public:
|
||||
void Marshal(WriteBuffer& wb, const AZ::Vector3& value)
|
||||
{
|
||||
wb.Write(Helper::GetQuantized<DeltaRange>(value.GetX()));
|
||||
wb.Write(Helper::GetQuantized<DeltaRange>(value.GetY()));
|
||||
wb.Write(Helper::GetQuantized<DeltaRange>(value.GetZ()));
|
||||
}
|
||||
|
||||
void Unmarshal(AZ::Vector3& value, ReadBuffer& rb)
|
||||
{
|
||||
AZ::u8 delta[3];
|
||||
rb.Read(delta[0]);
|
||||
rb.Read(delta[1]);
|
||||
rb.Read(delta[2]);
|
||||
|
||||
value = AZ::Vector3(Helper::GetUnquantized<DeltaRange>(delta[0]), Helper::GetUnquantized<DeltaRange>(delta[1]), Helper::GetUnquantized<DeltaRange>(delta[2]));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Delta compressed DataSet, stateless and cacheless. Stateless - because it does not keep per-player state of any kind.
|
||||
* Cacheless - because it does not keep a history of its values.
|
||||
* This approach requires only one extra copy of a field, because the field is split into two portions: absolute and relative portions.
|
||||
* The value is always the sum of two portions. We leverage existing DataSets to omit sending the larger absolute value, thus achieving compression.
|
||||
*
|
||||
* \tparam FieldType
|
||||
* \tparam DeltaRange
|
||||
* \tparam MarshalerType
|
||||
* \tparam DeltaMarshalerType
|
||||
*/
|
||||
template<typename FieldType, AZ::u32 DeltaRange, typename MarshalerType = Marshaler<FieldType>, typename DeltaMarshalerType = DeltaMarshaller<DeltaRange, FieldType>>
|
||||
class DeltaCompressedDataSet
|
||||
{
|
||||
public:
|
||||
virtual ~DeltaCompressedDataSet() = default;
|
||||
|
||||
template<class C, void (C::* FuncPtr)(const FieldType&, const TimeContext&)>
|
||||
class BindInterface;
|
||||
|
||||
/**
|
||||
Constructs a DataSet.
|
||||
**/
|
||||
explicit DeltaCompressedDataSet(const char* debugName, const FieldType& value = FieldType())
|
||||
: m_absolutePortion(debugName, value)
|
||||
, m_relativePortion(debugName)
|
||||
{
|
||||
static_assert(DeltaRange > 0, "Delta range cannot be zero!");
|
||||
|
||||
// We need to intercept changes to our two DataSets, in order to calculate the combined value and report back to Replica Chunk on our time.
|
||||
m_absolutePortion.SetDispatchOverride([this](const TimeContext& tc) {OnAbsolutePortionChanged(tc); });
|
||||
m_relativePortion.SetDispatchOverride([this](const TimeContext& tc) {OnRelativePortionChanged(tc); });
|
||||
}
|
||||
|
||||
/**
|
||||
Modify the DataSet. Call this on the Primary node to change the data,
|
||||
which will be propagated to all proxies.
|
||||
**/
|
||||
void Set(const FieldType& v)
|
||||
{
|
||||
m_combinedValue = v;
|
||||
|
||||
if (Helper::DeltaHelper<FieldType>::IsWithinDelta(m_absolutePortion.Get(), v, DeltaRange))
|
||||
{
|
||||
// within bounds, so only the relative portion needs to be updated
|
||||
m_relativePortion.Set(v - m_absolutePortion.Get());
|
||||
}
|
||||
else
|
||||
{
|
||||
// relative out of range, reset absolute
|
||||
m_absolutePortion.Set(v);
|
||||
m_relativePortion.Set(static_cast<FieldType>(0));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the current value of the DataSet.
|
||||
**/
|
||||
const FieldType& Get() const
|
||||
{
|
||||
return m_combinedValue;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void OnAbsolutePortionChanged(const TimeContext& /*tc*/)
|
||||
{
|
||||
m_combinedValue = m_absolutePortion.Get() + m_relativePortion.Get();
|
||||
}
|
||||
|
||||
virtual void OnRelativePortionChanged(const TimeContext& /*tc*/)
|
||||
{
|
||||
m_combinedValue = m_absolutePortion.Get() + m_relativePortion.Get();
|
||||
}
|
||||
|
||||
private:
|
||||
DataSet<FieldType, MarshalerType> m_absolutePortion;
|
||||
DataSet<FieldType, DeltaMarshalerType> m_relativePortion;
|
||||
FieldType m_combinedValue; // the latest value on either primary or proxy
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Declares a DeltaCompressedDataSet with an event handler that is called when the value is changed.
|
||||
Use BindInterface<Class, FuncPtr> to dispatch to a method on the ReplicaChunk's
|
||||
ReplicaChunkInterface event handler instance.
|
||||
**/
|
||||
template<typename FieldType, AZ::u32 DeltaRange, typename MarshalerType, typename DeltaMarshalerType>
|
||||
template<class C, void (C::* FuncPtr)(const FieldType&, const TimeContext&)>
|
||||
class DeltaCompressedDataSet<FieldType, DeltaRange, MarshalerType, DeltaMarshalerType>::BindInterface
|
||||
: public DeltaCompressedDataSet<FieldType, DeltaRange, MarshalerType, DeltaMarshalerType>
|
||||
{
|
||||
public:
|
||||
explicit BindInterface(const char* debugName) : DeltaCompressedDataSet(debugName) { }
|
||||
|
||||
protected:
|
||||
void OnAbsolutePortionChanged(const GridMate::TimeContext& tc) override
|
||||
{
|
||||
DeltaCompressedDataSet::OnAbsolutePortionChanged(tc);
|
||||
|
||||
m_lastUpdateTime = m_absolutePortion.GetLastUpdateTime();
|
||||
if (m_relativePortion.GetLastUpdateTime() < m_lastUpdateTime)
|
||||
{
|
||||
// relative portion wasn't updated, so its callback won't be invoked this tick, therefore we need to dispatch change event now
|
||||
DispatchChangedEvent(tc);
|
||||
}
|
||||
}
|
||||
|
||||
void OnRelativePortionChanged(const GridMate::TimeContext& tc) override
|
||||
{
|
||||
DeltaCompressedDataSet::OnRelativePortionChanged(tc);
|
||||
|
||||
m_lastUpdateTime = m_relativePortion.GetLastUpdateTime();
|
||||
// Assuming that relative portion DataSet is dispatched after absolute portion by construction in DeltaCompressedDataSet
|
||||
DispatchChangedEvent(tc);
|
||||
}
|
||||
|
||||
void DispatchChangedEvent(const TimeContext& tc)
|
||||
{
|
||||
AZ_Assert(m_relativePortion.GetReplicaChunkBase(), "DataSets should be attached to replica chunks!");
|
||||
|
||||
if (C* c = static_cast<C*>(m_relativePortion.GetReplicaChunkBase()->GetHandler()))
|
||||
{
|
||||
const TimeContext changeTime{ m_lastUpdateTime, m_lastUpdateTime - (tc.m_realTime - tc.m_localTime) };
|
||||
(*c.*FuncPtr)(Get(), changeTime);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
AZ::u32 m_lastUpdateTime = 0; // the latest update time among m_absolutePortion and m_relativePortion
|
||||
};
|
||||
//-----------------------------------------------------------------------------
|
||||
} // namespace GridMate
|
||||
|
||||
#endif // GM_DELTACOMPRESSED_DATASET_H
|
||||
@@ -1,421 +0,0 @@
|
||||
/*
|
||||
* 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 <GridMate/Replica/Interest/BitmaskInterestHandler.h>
|
||||
|
||||
#include <GridMate/Replica/Interpolators.h>
|
||||
#include <GridMate/Replica/Replica.h>
|
||||
#include <GridMate/Replica/ReplicaFunctions.h>
|
||||
#include <GridMate/Replica/ReplicaMgr.h>
|
||||
|
||||
#include <GridMate/Replica/Interest/InterestManager.h>
|
||||
|
||||
namespace GridMate
|
||||
{
|
||||
|
||||
void BitmaskInterestChunk::OnReplicaActivate(const ReplicaContext& rc)
|
||||
{
|
||||
m_interestHandler = static_cast<BitmaskInterestHandler*>(rc.m_rm->GetUserContext(AZ_CRC("BitmaskInterestHandler", 0x5bf5d75b)));
|
||||
AZ_Warning("GridMate", m_interestHandler != nullptr, "No bitmask interest handler in the user context");
|
||||
if (m_interestHandler)
|
||||
{
|
||||
m_interestHandler->OnNewRulesChunk(this, rc.m_peer);
|
||||
}
|
||||
}
|
||||
|
||||
void BitmaskInterestChunk::OnReplicaDeactivate(const ReplicaContext& rc)
|
||||
{
|
||||
if (m_interestHandler)
|
||||
{
|
||||
// even if rc.m_peer is null, we still need to call OnDeleteRulesChunk so that the interest handler can clear m_rulesReplica
|
||||
m_interestHandler->OnDeleteRulesChunk(this, rc.m_peer);
|
||||
}
|
||||
}
|
||||
|
||||
bool BitmaskInterestChunk::AddRuleFn(RuleNetworkId netId, InterestBitmask bits, const RpcContext& ctx)
|
||||
{
|
||||
if (IsProxy())
|
||||
{
|
||||
auto rulePtr = m_interestHandler->CreateRule(ctx.m_sourcePeer);
|
||||
rulePtr->Set(bits);
|
||||
m_rules.insert(AZStd::make_pair(netId, rulePtr));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitmaskInterestChunk::RemoveRuleFn(RuleNetworkId netId, const RpcContext&)
|
||||
{
|
||||
if (IsProxy())
|
||||
{
|
||||
m_rules.erase(netId);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitmaskInterestChunk::UpdateRuleFn(RuleNetworkId netId, InterestBitmask bits, const RpcContext&)
|
||||
{
|
||||
if (IsProxy())
|
||||
{
|
||||
auto it = m_rules.find(netId);
|
||||
if (it != m_rules.end())
|
||||
{
|
||||
it->second->Set(bits);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitmaskInterestChunk::AddRuleForPeerFn(RuleNetworkId netId, PeerId peerId, InterestBitmask bitmask, const RpcContext&)
|
||||
{
|
||||
BitmaskInterestChunk::Ptr 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(bitmask);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/*
|
||||
* BitmaskInterest
|
||||
*/
|
||||
BitmaskInterest::BitmaskInterest(BitmaskInterestHandler* handler)
|
||||
: m_handler(handler)
|
||||
, m_bits(0)
|
||||
{
|
||||
AZ_Assert(m_handler, "Invalid interest handler");
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/*
|
||||
* BitmaskInterestRule
|
||||
*/
|
||||
void BitmaskInterestRule::Set(InterestBitmask newBitmask)
|
||||
{
|
||||
m_bits = newBitmask;
|
||||
m_handler->UpdateRule(this);
|
||||
}
|
||||
|
||||
void BitmaskInterestRule::Destroy()
|
||||
{
|
||||
m_handler->DestroyRule(this);
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/*
|
||||
* BitmaskInterestAttribute
|
||||
*/
|
||||
void BitmaskInterestAttribute::Set(InterestBitmask newBitmask)
|
||||
{
|
||||
m_bits = newBitmask;
|
||||
m_handler->UpdateAttribute(this);
|
||||
}
|
||||
|
||||
void BitmaskInterestAttribute::Destroy()
|
||||
{
|
||||
m_handler->DestroyAttribute(this);
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/*
|
||||
* BitmaskInterestHandler
|
||||
*/
|
||||
BitmaskInterestHandler::BitmaskInterestHandler()
|
||||
: m_im(nullptr)
|
||||
, m_rm(nullptr)
|
||||
, m_lastRuleNetId(0)
|
||||
, m_rulesReplica(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
BitmaskInterestRule::Ptr BitmaskInterestHandler::CreateRule(PeerId peerId)
|
||||
{
|
||||
BitmaskInterestRule* rulePtr = aznew BitmaskInterestRule(this, peerId, GetNewRuleNetId());
|
||||
m_rules.insert(rulePtr);
|
||||
|
||||
if (peerId == m_rm->GetLocalPeerId() && m_rulesReplica)
|
||||
{
|
||||
m_rulesReplica->AddRuleRpc(rulePtr->GetNetworkId(), rulePtr->Get());
|
||||
m_localRules.insert(rulePtr);
|
||||
}
|
||||
|
||||
return rulePtr;
|
||||
}
|
||||
|
||||
void BitmaskInterestHandler::FreeRule(BitmaskInterestRule* rule)
|
||||
{
|
||||
//TODO: should be pool-allocated
|
||||
m_rules.erase(rule);
|
||||
delete rule;
|
||||
}
|
||||
|
||||
void BitmaskInterestHandler::DestroyRule(BitmaskInterestRule* rule)
|
||||
{
|
||||
if (m_rm && rule->GetPeerId() == m_rm->GetLocalPeerId() && m_rulesReplica)
|
||||
{
|
||||
m_rulesReplica->RemoveRuleRpc(rule->GetNetworkId());
|
||||
}
|
||||
|
||||
rule->m_bits = 0;
|
||||
m_dirtyRules.insert(rule);
|
||||
m_localRules.erase(rule);
|
||||
}
|
||||
|
||||
void BitmaskInterestHandler::UpdateRule(BitmaskInterestRule* rule)
|
||||
{
|
||||
if (m_rm && m_rulesReplica && rule->GetPeerId() == m_rm->GetLocalPeerId())
|
||||
{
|
||||
m_rulesReplica->UpdateRuleRpc(rule->GetNetworkId(), rule->Get());
|
||||
}
|
||||
|
||||
m_dirtyRules.insert(rule);
|
||||
}
|
||||
|
||||
BitmaskInterestAttribute::Ptr BitmaskInterestHandler::CreateAttribute(ReplicaId replicaId)
|
||||
{
|
||||
auto ptr = aznew BitmaskInterestAttribute(this, replicaId);
|
||||
m_attrs.insert(ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void BitmaskInterestHandler::FreeAttribute(BitmaskInterestAttribute* attrib)
|
||||
{
|
||||
//TODO: should be pool-allocated
|
||||
m_attrs.erase(attrib);
|
||||
delete attrib;
|
||||
}
|
||||
|
||||
void BitmaskInterestHandler::DestroyAttribute(BitmaskInterestAttribute* attrib)
|
||||
{
|
||||
attrib->m_bits = 0;
|
||||
m_dirtyAttributes.insert(attrib);
|
||||
}
|
||||
|
||||
void BitmaskInterestHandler::UpdateAttribute(BitmaskInterestAttribute* attrib)
|
||||
{
|
||||
m_dirtyAttributes.insert(attrib);
|
||||
}
|
||||
|
||||
void BitmaskInterestHandler::OnNewRulesChunk(BitmaskInterestChunk::Ptr 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 BitmaskInterestHandler::OnDeleteRulesChunk(BitmaskInterestChunk::Ptr chunk, ReplicaPeer* peer)
|
||||
{
|
||||
AZ_UNUSED(chunk);
|
||||
m_rulesReplica = nullptr;
|
||||
|
||||
if (peer)
|
||||
{
|
||||
m_peerChunks.erase(peer->GetId());
|
||||
}
|
||||
}
|
||||
|
||||
RuleNetworkId BitmaskInterestHandler::GetNewRuleNetId()
|
||||
{
|
||||
++m_lastRuleNetId;
|
||||
if (m_rulesReplica)
|
||||
{
|
||||
return m_rulesReplica->GetReplicaId() | (static_cast<AZ::u64>(m_lastRuleNetId) << 32);
|
||||
}
|
||||
|
||||
return (static_cast<AZ::u64>(m_lastRuleNetId) << 32);
|
||||
}
|
||||
|
||||
BitmaskInterestChunk::Ptr BitmaskInterestHandler::FindRulesChunkByPeerId(PeerId peerId)
|
||||
{
|
||||
auto it = m_peerChunks.find(peerId);
|
||||
if (it == m_peerChunks.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
const InterestMatchResult& BitmaskInterestHandler::GetLastResult()
|
||||
{
|
||||
return m_resultCache;
|
||||
}
|
||||
|
||||
void BitmaskInterestHandler::Update()
|
||||
{
|
||||
m_resultCache.clear();
|
||||
|
||||
for (BitmaskInterestRule* rule : m_dirtyRules)
|
||||
{
|
||||
InterestBitmask j = 1;
|
||||
for (size_t i = 0; i < k_numGroups; ++i, j <<= 1)
|
||||
{
|
||||
auto ruleIt = m_ruleGroups[i].find(rule);
|
||||
bool isMatch = !!(rule->m_bits & j);
|
||||
if (isMatch && ruleIt == m_ruleGroups[i].end())
|
||||
{
|
||||
m_ruleGroups[i].insert(rule);
|
||||
|
||||
// recalculate all the attributes in this bucket
|
||||
for (BitmaskInterestAttribute* attr : m_attrGroups[i])
|
||||
{
|
||||
m_dirtyAttributes.insert(attr);
|
||||
}
|
||||
}
|
||||
else if (!isMatch && ruleIt != m_ruleGroups[i].end())
|
||||
{
|
||||
m_ruleGroups[i].erase(ruleIt);
|
||||
|
||||
// recalculate all the attributes in this bucket
|
||||
for (BitmaskInterestAttribute* attr : m_attrGroups[i])
|
||||
{
|
||||
m_dirtyAttributes.insert(attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rule->IsDeleted())
|
||||
{
|
||||
FreeRule(rule);
|
||||
}
|
||||
}
|
||||
|
||||
m_dirtyRules.clear();
|
||||
|
||||
for (BitmaskInterestAttribute* attr : m_dirtyAttributes)
|
||||
{
|
||||
InterestBitmask j = 1;
|
||||
for (size_t i = 0; i < k_numGroups; ++i, j <<= 1)
|
||||
{
|
||||
auto attrIt = m_attrGroups[i].find(attr);
|
||||
bool isMatch = !!(attr->m_bits & j);
|
||||
if (isMatch && attrIt == m_attrGroups[i].end())
|
||||
{
|
||||
m_attrGroups[i].insert(attr);
|
||||
}
|
||||
else if (!isMatch && attrIt != m_attrGroups[i].end())
|
||||
{
|
||||
m_attrGroups[i].erase(attrIt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (BitmaskInterestAttribute* attr : m_dirtyAttributes)
|
||||
{
|
||||
auto repIt = m_resultCache.insert(attr->GetReplicaId());
|
||||
|
||||
InterestBitmask j = 1;
|
||||
for (size_t i = 0; i < k_numGroups; ++i, j <<= 1)
|
||||
{
|
||||
if (!!(attr->m_bits & j))
|
||||
{
|
||||
for (BitmaskInterestRule* rule : m_ruleGroups[i])
|
||||
{
|
||||
repIt.first->second.insert(rule->GetPeerId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (attr->IsDeleted())
|
||||
{
|
||||
FreeAttribute(attr);
|
||||
}
|
||||
}
|
||||
|
||||
m_dirtyAttributes.clear();
|
||||
}
|
||||
|
||||
void BitmaskInterestHandler::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", "Bitmask interest handler is registered\n");
|
||||
m_im = manager;
|
||||
m_rm = m_im->GetReplicaManager();
|
||||
m_rm->RegisterUserContext(AZ_CRC("BitmaskInterestHandler", 0x5bf5d75b), this);
|
||||
|
||||
auto replica = Replica::CreateReplica("BitmaskInterestHandlerRules");
|
||||
m_rulesReplica = CreateAndAttachReplicaChunk<BitmaskInterestChunk>(replica);
|
||||
m_rm->AddPrimary(replica);
|
||||
}
|
||||
|
||||
void BitmaskInterestHandler::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", "Bitmask interest handler is unregistered\n");
|
||||
|
||||
if (m_rulesReplica)
|
||||
{
|
||||
m_rulesReplica->m_rules.clear();
|
||||
m_rulesReplica->m_interestHandler = nullptr;
|
||||
}
|
||||
|
||||
for (auto& chunk : m_peerChunks)
|
||||
{
|
||||
chunk.second->m_rules.clear();
|
||||
chunk.second->m_interestHandler = nullptr;
|
||||
}
|
||||
|
||||
m_rulesReplica = nullptr;
|
||||
m_im = nullptr;
|
||||
m_rm->UnregisterUserContext(AZ_CRC("BitmaskInterestHandler", 0x5bf5d75b));
|
||||
m_rm = nullptr;
|
||||
|
||||
m_peerChunks.clear();
|
||||
m_localRules.clear();
|
||||
|
||||
for (auto& a : m_attrs)
|
||||
{
|
||||
delete a;
|
||||
}
|
||||
|
||||
for (auto& r : m_rules)
|
||||
{
|
||||
delete r;
|
||||
}
|
||||
|
||||
m_dirtyAttributes.clear();
|
||||
m_dirtyRules.clear();
|
||||
|
||||
for (auto& group : m_attrGroups)
|
||||
{
|
||||
group.clear();
|
||||
}
|
||||
|
||||
for (auto& group : m_ruleGroups)
|
||||
{
|
||||
group.clear();
|
||||
}
|
||||
|
||||
m_resultCache.clear();
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GM_REPLICA_BITMASKINTERESTHANDLER_H
|
||||
#define GM_REPLICA_BITMASKINTERESTHANDLER_H
|
||||
|
||||
#include <GridMate/Replica/RemoteProcedureCall.h>
|
||||
#include <GridMate/Replica/ReplicaChunk.h>
|
||||
#include <GridMate/Replica/Interest/RulesHandler.h>
|
||||
#include <GridMate/Serialize/UtilityMarshal.h>
|
||||
|
||||
#include <GridMate/Containers/vector.h>
|
||||
#include <GridMate/Containers/unordered_set.h>
|
||||
#include <AzCore/std/containers/array.h>
|
||||
|
||||
|
||||
namespace GridMate
|
||||
{
|
||||
class BitmaskInterestHandler;
|
||||
using InterestBitmask = AZ::u32;
|
||||
|
||||
/*
|
||||
* Base interest
|
||||
*/
|
||||
class BitmaskInterest
|
||||
{
|
||||
friend class BitmaskInterestHandler;
|
||||
|
||||
public:
|
||||
InterestBitmask Get() const { return m_bits; }
|
||||
|
||||
protected:
|
||||
explicit BitmaskInterest(BitmaskInterestHandler* handler);
|
||||
|
||||
BitmaskInterestHandler* m_handler;
|
||||
InterestBitmask m_bits;
|
||||
};
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/*
|
||||
* Bitmask rule
|
||||
*/
|
||||
class BitmaskInterestRule
|
||||
: public InterestRule
|
||||
, public BitmaskInterest
|
||||
{
|
||||
friend class BitmaskInterestHandler;
|
||||
|
||||
public:
|
||||
using Ptr = AZStd::intrusive_ptr<BitmaskInterestRule>;
|
||||
|
||||
GM_CLASS_ALLOCATOR(BitmaskInterestRule);
|
||||
|
||||
void Set(InterestBitmask newBitmask);
|
||||
|
||||
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; }
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BitmaskInterestRule(BitmaskInterestHandler* handler, PeerId peerId, RuleNetworkId netId)
|
||||
: InterestRule(peerId, netId)
|
||||
, BitmaskInterest(handler)
|
||||
{}
|
||||
|
||||
void Destroy();
|
||||
};
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/*
|
||||
* Bitmask attribute
|
||||
*/
|
||||
class BitmaskInterestAttribute
|
||||
: public InterestAttribute
|
||||
, public BitmaskInterest
|
||||
{
|
||||
friend class BitmaskInterestHandler;
|
||||
template<class T> friend class InterestPtr;
|
||||
|
||||
public:
|
||||
using Ptr = AZStd::intrusive_ptr<BitmaskInterestAttribute>;
|
||||
|
||||
GM_CLASS_ALLOCATOR(BitmaskInterestAttribute);
|
||||
|
||||
void Set(InterestBitmask newBitmask);
|
||||
|
||||
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; }
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BitmaskInterestAttribute(BitmaskInterestHandler* handler, ReplicaId repId)
|
||||
: InterestAttribute(repId)
|
||||
, BitmaskInterest(handler)
|
||||
{}
|
||||
|
||||
void Destroy();
|
||||
};
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class BitmaskInterestChunk
|
||||
: public ReplicaChunk
|
||||
{
|
||||
public:
|
||||
GM_CLASS_ALLOCATOR(BitmaskInterestChunk);
|
||||
|
||||
BitmaskInterestChunk()
|
||||
: AddRuleRpc("AddRule")
|
||||
, RemoveRuleRpc("RemoveRule")
|
||||
, UpdateRuleRpc("UpdateRule")
|
||||
, AddRuleForPeerRpc("AddRuleForPeerRpc")
|
||||
, m_interestHandler(nullptr)
|
||||
{}
|
||||
|
||||
typedef AZStd::intrusive_ptr<BitmaskInterestChunk> Ptr;
|
||||
bool IsReplicaMigratable() override { return false; }
|
||||
bool IsBroadcast() override { return true; }
|
||||
static const char* GetChunkName() { return "BitmaskInterestChunk"; }
|
||||
|
||||
void OnReplicaActivate(const ReplicaContext& rc) override;
|
||||
void OnReplicaDeactivate(const ReplicaContext& rc) override;
|
||||
|
||||
bool AddRuleFn(RuleNetworkId netId, InterestBitmask bits, const RpcContext& ctx);
|
||||
bool RemoveRuleFn(RuleNetworkId netId, const RpcContext&);
|
||||
bool UpdateRuleFn(RuleNetworkId netId, InterestBitmask bits, const RpcContext&);
|
||||
bool AddRuleForPeerFn(RuleNetworkId netId, PeerId peerId, InterestBitmask bitmask, const RpcContext&);
|
||||
|
||||
Rpc<RpcArg<RuleNetworkId>, RpcArg<InterestBitmask>>::BindInterface<BitmaskInterestChunk, &BitmaskInterestChunk::AddRuleFn> AddRuleRpc;
|
||||
Rpc<RpcArg<RuleNetworkId>>::BindInterface<BitmaskInterestChunk, &BitmaskInterestChunk::RemoveRuleFn> RemoveRuleRpc;
|
||||
Rpc<RpcArg<RuleNetworkId>, RpcArg<InterestBitmask>>::BindInterface<BitmaskInterestChunk, &BitmaskInterestChunk::UpdateRuleFn> UpdateRuleRpc;
|
||||
|
||||
Rpc<RpcArg<RuleNetworkId>, RpcArg<PeerId>, RpcArg<InterestBitmask>>::BindInterface<BitmaskInterestChunk, &BitmaskInterestChunk::AddRuleForPeerFn> AddRuleForPeerRpc;
|
||||
|
||||
unordered_map<RuleNetworkId, BitmaskInterestRule::Ptr> m_rules;
|
||||
BitmaskInterestHandler* m_interestHandler;
|
||||
};
|
||||
|
||||
/*
|
||||
* Rules handler
|
||||
*/
|
||||
class BitmaskInterestHandler
|
||||
: public BaseRulesHandler
|
||||
{
|
||||
friend class BitmaskInterestRule;
|
||||
friend class BitmaskInterestAttribute;
|
||||
friend class BitmaskInterestChunk;
|
||||
|
||||
public:
|
||||
|
||||
GM_CLASS_ALLOCATOR(BitmaskInterestHandler);
|
||||
|
||||
BitmaskInterestHandler();
|
||||
|
||||
// Creates new bitmask rule and binds it to the peer
|
||||
BitmaskInterestRule::Ptr CreateRule(PeerId peerId);
|
||||
|
||||
// Creates new bitmask attribute and binds it to the replica
|
||||
BitmaskInterestAttribute::Ptr CreateAttribute(ReplicaId replicaId);
|
||||
|
||||
// Calculates rules and attributes matches
|
||||
void Update() override;
|
||||
|
||||
// Returns last recalculated results
|
||||
const InterestMatchResult& GetLastResult() override;
|
||||
|
||||
InterestManager* GetManager() override { return m_im; }
|
||||
private:
|
||||
|
||||
// BaseRulesHandler
|
||||
void OnRulesHandlerRegistered(InterestManager* manager) override;
|
||||
void OnRulesHandlerUnregistered(InterestManager* manager) override;
|
||||
|
||||
void DestroyRule(BitmaskInterestRule* rule);
|
||||
void FreeRule(BitmaskInterestRule* rule);
|
||||
void UpdateRule(BitmaskInterestRule* rule);
|
||||
|
||||
void DestroyAttribute(BitmaskInterestAttribute* attrib);
|
||||
void FreeAttribute(BitmaskInterestAttribute* attrib);
|
||||
void UpdateAttribute(BitmaskInterestAttribute* attrib);
|
||||
|
||||
|
||||
void OnNewRulesChunk(BitmaskInterestChunk::Ptr chunk, ReplicaPeer* peer);
|
||||
void OnDeleteRulesChunk(BitmaskInterestChunk::Ptr chunk, ReplicaPeer* peer);
|
||||
|
||||
RuleNetworkId GetNewRuleNetId();
|
||||
|
||||
BitmaskInterestChunk::Ptr FindRulesChunkByPeerId(PeerId peerId);
|
||||
|
||||
typedef unordered_set<BitmaskInterestAttribute*> AttributeSet;
|
||||
typedef unordered_set<BitmaskInterestRule*> RuleSet;
|
||||
static const size_t k_numGroups = sizeof(InterestBitmask) * CHAR_BIT;
|
||||
|
||||
InterestManager* m_im;
|
||||
ReplicaManager* m_rm;
|
||||
|
||||
AZ::u32 m_lastRuleNetId;
|
||||
|
||||
unordered_map<PeerId, BitmaskInterestChunk::Ptr> m_peerChunks;
|
||||
|
||||
RuleSet m_localRules;
|
||||
|
||||
AttributeSet m_dirtyAttributes;
|
||||
RuleSet m_dirtyRules;
|
||||
|
||||
AZStd::array<AttributeSet, k_numGroups> m_attrGroups;
|
||||
AZStd::array<RuleSet, k_numGroups> m_ruleGroups;
|
||||
|
||||
InterestMatchResult m_resultCache;
|
||||
|
||||
BitmaskInterestChunk* m_rulesReplica;
|
||||
|
||||
AttributeSet m_attrs;
|
||||
RuleSet m_rules;
|
||||
};
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#if (GM_FUNCTION_NUM_ARGS == 0)
|
||||
#define GM_FUNCTION_TEMPLATE_PARMS
|
||||
#define GM_FUNCTION_ARGS
|
||||
#define GM_FUNCTION_ARGS_CONCAT
|
||||
#define GM_FUNCTION_FORWARD
|
||||
#define GM_FUNCTION_FORWARD_CONCAT
|
||||
#elif (GM_FUNCTION_NUM_ARGS == 1)
|
||||
#define GM_FUNCTION_TEMPLATE_PARMS , typename T0
|
||||
#define GM_FUNCTION_ARGS T0 && t0
|
||||
#define GM_FUNCTION_ARGS_CONCAT , GM_FUNCTION_ARGS
|
||||
#define GM_FUNCTION_FORWARD AZStd::forward<T0>(t0)
|
||||
#define GM_FUNCTION_FORWARD_CONCAT , GM_FUNCTION_FORWARD
|
||||
#elif (GM_FUNCTION_NUM_ARGS == 2)
|
||||
#define GM_FUNCTION_TEMPLATE_PARMS , typename T0, typename T1
|
||||
#define GM_FUNCTION_ARGS T0 && t0, T1 && t1
|
||||
#define GM_FUNCTION_ARGS_CONCAT , GM_FUNCTION_ARGS
|
||||
#define GM_FUNCTION_FORWARD AZStd::forward<T0>(t0), AZStd::forward<T1>(t1)
|
||||
#define GM_FUNCTION_FORWARD_CONCAT , GM_FUNCTION_FORWARD
|
||||
#elif (GM_FUNCTION_NUM_ARGS == 3)
|
||||
#define GM_FUNCTION_TEMPLATE_PARMS , typename T0, typename T1, typename T2
|
||||
#define GM_FUNCTION_ARGS T0 && t0, T1 && t1, T2 && t2
|
||||
#define GM_FUNCTION_ARGS_CONCAT , GM_FUNCTION_ARGS
|
||||
#define GM_FUNCTION_FORWARD AZStd::forward<T0>(t0), AZStd::forward<T1>(t1), AZStd::forward<T2>(t2)
|
||||
#define GM_FUNCTION_FORWARD_CONCAT , GM_FUNCTION_FORWARD
|
||||
#elif (GM_FUNCTION_NUM_ARGS == 4)
|
||||
#define GM_FUNCTION_TEMPLATE_PARMS , typename T0, typename T1, typename T2, typename T3
|
||||
#define GM_FUNCTION_ARGS T0 && t0, T1 && t1, T2 && t2, T3 && t3
|
||||
#define GM_FUNCTION_ARGS_CONCAT , GM_FUNCTION_ARGS
|
||||
#define GM_FUNCTION_FORWARD AZStd::forward<T0>(t0), AZStd::forward<T1>(t1), AZStd::forward<T2>(t2), AZStd::forward<T3>(t3)
|
||||
#define GM_FUNCTION_FORWARD_CONCAT , GM_FUNCTION_FORWARD
|
||||
#elif (GM_FUNCTION_NUM_ARGS == 5)
|
||||
#define GM_FUNCTION_TEMPLATE_PARMS , typename T0, typename T1, typename T2, typename T3, typename T4
|
||||
#define GM_FUNCTION_ARGS T0 && t0, T1 && t1, T2 && t2, T3 && t3, T4 && t4
|
||||
#define GM_FUNCTION_ARGS_CONCAT , GM_FUNCTION_ARGS
|
||||
#define GM_FUNCTION_FORWARD AZStd::forward<T0>(t0), AZStd::forward<T1>(t1), AZStd::forward<T2>(t2), AZStd::forward<T3>(t3), AZStd::forward<T4>(t4)
|
||||
#define GM_FUNCTION_FORWARD_CONCAT , GM_FUNCTION_FORWARD
|
||||
#else
|
||||
#error Unsupported argument count
|
||||
#endif
|
||||
|
||||
/**
|
||||
Create a ReplicaChunk that isn't attached to a Replica. To attach it to a replica,
|
||||
call replica->AttachReplicaChunk(chunk).
|
||||
**/
|
||||
template<class ChunkType GM_FUNCTION_TEMPLATE_PARMS>
|
||||
ChunkType* CreateReplicaChunk(GM_FUNCTION_ARGS)
|
||||
{
|
||||
static_assert(AZStd::is_base_of<ReplicaChunkBase, ChunkType>::value, "Class must inherit from ReplicaChunk");
|
||||
|
||||
ReplicaChunkDescriptor* descriptor = ReplicaChunkDescriptorTable::Get().FindReplicaChunkDescriptor(ReplicaChunkClassId(ChunkType::GetChunkName()));
|
||||
AZ_Assert(descriptor, "Cannot find replica chunk descriptor for %s. Did you remember to register the chunk type?", ChunkType::GetChunkName());
|
||||
ReplicaChunkDescriptorTable::Get().BeginConstructReplicaChunk(descriptor);
|
||||
ChunkType* chunk = aznew ChunkType(GM_FUNCTION_FORWARD);
|
||||
ReplicaChunkDescriptorTable::Get().EndConstructReplicaChunk();
|
||||
chunk->Init(descriptor);
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
/**
|
||||
Create a ReplicaChunk that is automatically attached to the replica.
|
||||
**/
|
||||
template<class ChunkType GM_FUNCTION_TEMPLATE_PARMS>
|
||||
ChunkType* CreateAndAttachReplicaChunk(const ReplicaPtr& replica GM_FUNCTION_ARGS_CONCAT)
|
||||
{
|
||||
return CreateAndAttachReplicaChunk<ChunkType>(replica.get() GM_FUNCTION_FORWARD_CONCAT);
|
||||
}
|
||||
|
||||
/**
|
||||
Create a ReplicaChunk that is automatically attached to the replica.
|
||||
**/
|
||||
template<class ChunkType GM_FUNCTION_TEMPLATE_PARMS>
|
||||
ChunkType* CreateAndAttachReplicaChunk(Replica* replica GM_FUNCTION_ARGS_CONCAT)
|
||||
{
|
||||
// Chunks cannot be attached while active
|
||||
if (replica->IsActive())
|
||||
{
|
||||
AZ_Warning("GridMate", false, "Cannot attach chunk %s while replica is active", ChunkType::GetChunkName());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ChunkType* chunk = CreateReplicaChunk<ChunkType>(GM_FUNCTION_FORWARD);
|
||||
replica->AttachReplicaChunk(chunk);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
#undef GM_FUNCTION_TEMPLATE_PARMS
|
||||
#undef GM_FUNCTION_ARGS
|
||||
#undef GM_FUNCTION_ARGS_CONCAT
|
||||
#undef GM_FUNCTION_FORWARD
|
||||
#undef GM_FUNCTION_FORWARD_CONCAT
|
||||
@@ -40,7 +40,6 @@ set(FILES
|
||||
Containers/unordered_set.h
|
||||
Containers/vector.h
|
||||
Replica/BasicHostChunkDescriptor.h
|
||||
Replica/DeltaCompressedDataSet.h
|
||||
Replica/DataSet.cpp
|
||||
Replica/DataSet.h
|
||||
Replica/Interpolators.h
|
||||
@@ -58,7 +57,6 @@ set(FILES
|
||||
Replica/ReplicaCommon.h
|
||||
Replica/ReplicaDefs.h
|
||||
Replica/ReplicaFunctions.h
|
||||
Replica/ReplicaFunctions.inl
|
||||
Replica/ReplicaInline.inl
|
||||
Replica/ReplicaMgr.cpp
|
||||
Replica/ReplicaMgr.h
|
||||
@@ -80,8 +78,6 @@ set(FILES
|
||||
Replica/Tasks/ReplicaProcessPolicy.cpp
|
||||
Replica/Tasks/ReplicaProcessPolicy.h
|
||||
Replica/Tasks/ReplicaPriorityPolicy.h
|
||||
Replica/Interest/BitmaskInterestHandler.cpp
|
||||
Replica/Interest/BitmaskInterestHandler.h
|
||||
Replica/Interest/InterestDefs.h
|
||||
Replica/Interest/InterestManager.cpp
|
||||
Replica/Interest/InterestManager.h
|
||||
|
||||
Reference in New Issue
Block a user