Files
o3de/Code/CryEngine/CryNetwork/GridMate/Replicas/EntityScriptReplicaChunk.cpp
T
2021-03-08 14:30:57 -08:00

296 lines
10 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 "CryNetwork_precompiled.h"
#include "EntityScriptReplicaChunk.h"
#include "EntityReplica.h"
#include "../NetworkGridMateSystemEvents.h"
#include "../NetworkGridmateMarshaling.h"
#include "../Compatibility/GridMateNetSerialize.h"
#include "../Compatibility/GridMateNetSerializeAspectProfiles.h"
namespace GridMate
{
////////////////////////
// EntityScriptDataSet
////////////////////////
const char* EntityScriptDataSet::GetDataSetName()
{
static int s_chunkIndex = 0;
static const char* s_nameArray[] = {
"DataSet1","DataSet2","DataSet3","DataSet4","DataSet5",
"DataSet6","DataSet7","DataSet8","DataSet9","DataSet10",
"DataSet11","DataSet12","DataSet13","DataSet14","DataSet15",
"DataSet16","DataSet17","DataSet18","DataSet19","DataSet20",
"DataSet21","DataSet22","DataSet23","DataSet24","DataSet25",
"DataSet26","DataSet27","DataSet28","DataSet29","DataSet30",
"DataSet31","DataSet32"
};
static_assert(EntityScriptReplicaChunk::k_maxScriptableDataSets <= AZ_ARRAY_SIZE(s_nameArray),"Insufficient number of names supplied to EntityScriptDataSet::GetDataSetName()");
if (s_chunkIndex > EntityScriptReplicaChunk::k_maxScriptableDataSets && EntityScriptReplicaChunk::k_maxScriptableDataSets >= 0)
{
s_chunkIndex = s_chunkIndex%EntityScriptReplicaChunk::k_maxScriptableDataSets;
}
return s_nameArray[s_chunkIndex++];
}
EntityScriptDataSet::EntityScriptDataSet()
: EntityScriptDataSetType(GetDataSetName())
, m_isEnabled(false)
{
/*
* ReplicaChunk has to declare all of its dataset at compile, however, scripts decide that at runtime.
* Thus, we create the maximum possible datasets and disable those that aren't being used.
*
* So a lot of time these datasets aren't used in scripts and thus they don't have any useful value in them.
* Marking them as default achieves not sending them on the network.
*/
MarkAsDefaultValue();
}
void EntityScriptDataSet::DispatchChangedEvent([[maybe_unused]] const TimeContext& tc)
{
SetIsEnabled(true);
static_cast<EntityScriptReplicaChunk*>(m_replicaChunk)->OnPropertyUpdate((*this));
}
void EntityScriptDataSet::SetIsEnabled(bool isEnabled)
{
m_isEnabled = isEnabled;
}
bool EntityScriptDataSet::IsEnabled() const
{
return m_isEnabled;
}
GridMate::PrepareDataResult EntityScriptDataSet::PrepareData(GridMate::EndianType endianType, AZ::u32 marshalFlags)
{
if (!IsEnabled())
{
return PrepareDataResult(false, false, false, false);
}
return EntityScriptDataSetType::PrepareData(endianType, marshalFlags);
}
void EntityScriptDataSet::SetDirty()
{
if (!IsEnabled())
{
return;
}
EntityScriptDataSetType::SetDirty();
}
/////////////////////////////
// EntityScriptReplicaChunk
/////////////////////////////
EntityScriptReplicaChunk::EntityScriptReplicaChunk()
: m_masterDataSetScratchBuffer(EndianType::BigEndian)
, m_serializerImpl(m_masterDataSetScratchBuffer)
, m_masterWriteSerializer(m_serializerImpl)
, m_localEntityId(kInvalidEntityId)
, m_enabledDataSetMask(0)
{
}
void EntityScriptReplicaChunk::OnReplicaActivate([[maybe_unused]] const GridMate::ReplicaContext& rc)
{
}
TSerialize EntityScriptReplicaChunk::FindSerializer(const char* name)
{
AZ_Error("EntityScriptReplicaChunk",!IsMarshaling(),"Trying to marshal two data sets at once.");
FRAME_PROFILER("StartMarshal",GetISystem(), PROFILE_NETWORK);
TSerialize retVal = nullptr;
if (!IsMarshaling() && name)
{
m_serializationTarget = name;
// Clear out our buffer and return the master serializer
m_masterDataSetScratchBuffer.Clear();
retVal = &m_masterWriteSerializer;
}
return retVal;
}
bool EntityScriptReplicaChunk::IsMarshaling() const
{
return !m_serializationTarget.empty();
}
bool EntityScriptReplicaChunk::CommitSerializer(const char* name, TSerialize serializer)
{
(void)name;
(void)serializer;
AZ_Error("EntityScriptReplicaChunk",IsMarshaling(),"Committing a serializer without finding it first.");
bool didMarshal = false;
if (IsMarshaling())
{
EntityScriptDataSet* scriptDataSet = FindDataSet(m_serializationTarget);
m_serializationTarget.clear();
AZ_Error("EntityScriptReplicaChunk",scriptDataSet,"Invalid SerializationTarget");
if (scriptDataSet)
{
didMarshal = true;
NetSerialize::AspectSerializeState::Marshaler& marshaler = scriptDataSet->GetMarshaler();
if (marshaler.GetStorageSize() < m_masterDataSetScratchBuffer.Size())
{
marshaler.AllocateAspectSerializationBuffer(m_masterDataSetScratchBuffer.Size());
}
if (m_masterDataSetScratchBuffer.Size() > 0)
{
FRAME_PROFILER("AspectBufferCopy",GetISystem(), PROFILE_NETWORK);
WriteBufferType writeBuffer = marshaler.GetWriteBuffer();
writeBuffer.Clear();
writeBuffer.WriteRaw(m_masterDataSetScratchBuffer.Get(),m_masterDataSetScratchBuffer.Size());
}
// Store update contents & hash. Any change willr esult in a downstream update.
NetSerialize::AspectSerializeState updatedState = scriptDataSet->Get();
bool changed = false;
{
FRAME_PROFILER("AspectBufferHash", GetISystem(), PROFILE_NETWORK);
changed = updatedState.UpdateHash(NetSerialize::HashBuffer(m_masterDataSetScratchBuffer.Get(), m_masterDataSetScratchBuffer.Size()), m_masterDataSetScratchBuffer.Size());
}
{
FRAME_PROFILER("AspectUpdate", GetISystem(), PROFILE_NETWORK);
scriptDataSet->Set(updatedState);
}
if (changed)
{
FRAME_PROFILER("AspectSentEvent", GetISystem(), PROFILE_NETWORK);
EBUS_EVENT(NetworkSystemEventBus, AspectSent, m_localEntityId, BIT(eEA_Script), m_masterDataSetScratchBuffer.Size());
}
}
}
return didMarshal;
}
void EntityScriptReplicaChunk::Synchronize()
{
// Only want to synchronize on the proxy(otherwise we might be overwriting
// good data inside of the script table that we actually want to pull out.
if (IsProxy())
{
for (int i=0; i < k_maxScriptableDataSets; ++i)
{
ReadBufferType rb = m_scriptDataSets[i].GetMarshaler().GetReadBuffer();
if (rb.Get())
{
m_enabledDataSetMask |= (1 << i);
OnPropertyUpdate(m_scriptDataSets[i]);
}
}
}
}
void EntityScriptReplicaChunk::OnPropertyUpdate(EntityScriptDataSet& dataSet)
{
ReadBufferType rb = dataSet.GetMarshaler().GetReadBuffer();
if (rb.Get())
{
{
FRAME_PROFILER(Debug::GetAspectNameByBitIndex(eEA_Script),GetISystem(),PROFILE_NETWORK);
EBUS_EVENT(NetworkSystemEventBus, AspectReceived, m_localEntityId, eEA_Script, rb.Size().GetSizeInBytesRoundUp());
}
}
}
void EntityScriptReplicaChunk::SetLocalEntityId(EntityId localEntityId)
{
m_localEntityId = localEntityId;
Synchronize();
}
EntityScriptDataSet* EntityScriptReplicaChunk::FindDataSet(const AZStd::string& valueName)
{
EntityScriptDataSet* retVal = nullptr;
DataSetIndexMapping::iterator indexIter = m_nameToIndex.find(valueName);
if (indexIter == m_nameToIndex.end())
{
for (int i = 0; i < k_maxScriptableDataSets; ++i)
{
if (!m_scriptDataSets[i].IsEnabled())
{
m_enabledDataSetMask |= (1 << i);
m_scriptDataSets[i].SetIsEnabled(true);
AZStd::pair<DataSetIndexMapping::iterator, bool> insertResult = m_nameToIndex.insert(DataSetIndexMapping::value_type(valueName,i));
indexIter = insertResult.first;
break;
}
}
}
if (indexIter != m_nameToIndex.end())
{
retVal = &m_scriptDataSets[indexIter->second];
}
return retVal;
}
void EntityScriptReplicaChunk::EnsureMapping(const AZStd::string& valueName, EntityScriptDataSet& dataSet)
{
if (!dataSet.IsEnabled())
{
DataSetIndexMapping::iterator nameIter = m_nameToIndex.find(valueName);
AZ_Error("EntityScriptReplicaChunk",nameIter == m_nameToIndex.end(),"Trying to create two script values with the same name.");
if (nameIter == m_nameToIndex.end())
{
for (int i=0; i < k_maxScriptableDataSets; ++i)
{
if (&m_scriptDataSets[i] == &dataSet)
{
m_nameToIndex.insert(DataSetIndexMapping::value_type(valueName,i));
break;
}
}
}
}
AZ_Error("EntityScriptReplicaChunk",m_nameToIndex.find(valueName) != m_nameToIndex.end() && &m_scriptDataSets[m_nameToIndex.find(valueName)->second] == &dataSet,"Given invalid DataSet for mapping to name");
}
AZ::u32 EntityScriptReplicaChunk::CalculateDirtyDataSetMask(MarshalContext& marshalContext)
{
return (m_enabledDataSetMask & GridMate::ReplicaChunk::CalculateDirtyDataSetMask(marshalContext));
}
}