Merge pull request #704 from aws-lumberyard-dev/MultiplayerComponents

Removing unused GridMate touchpoints from AzFramework, and non-inclusive code removal
This commit is contained in:
kberg-amzn
2021-05-11 17:51:03 -07:00
committed by GitHub
87 changed files with 370 additions and 13015 deletions
@@ -52,8 +52,6 @@
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzFramework/IO/RemoteStorageDrive.h>
#include <AzFramework/Network/NetBindingComponent.h>
#include <AzFramework/Network/NetBindingSystemComponent.h>
#include <AzFramework/Physics/Utils.h>
#include <AzFramework/Render/GameIntersectorComponent.h>
#include <AzFramework/Platform/PlatformDefaults.h>
@@ -66,7 +64,6 @@
#include <AzFramework/TargetManagement/TargetManagementComponent.h>
#include <AzFramework/Viewport/CameraState.h>
#include <AzFramework/Driller/RemoteDrillerInterface.h>
#include <AzFramework/Network/NetworkContext.h>
#include <AzFramework/Metrics/MetricsPlainTextNameRegistration.h>
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
@@ -197,7 +194,6 @@ namespace AzFramework
ApplicationRequests::Bus::Handler::BusConnect();
AZ::UserSettingsFileLocatorBus::Handler::BusConnect();
NetSystemRequestBus::Handler::BusConnect();
}
Application::~Application()
@@ -207,7 +203,6 @@ namespace AzFramework
Stop();
}
NetSystemRequestBus::Handler::BusDisconnect();
AZ::UserSettingsFileLocatorBus::Handler::BusDisconnect();
ApplicationRequests::Bus::Handler::BusDisconnect();
@@ -285,13 +280,6 @@ namespace AzFramework
m_pimpl.reset();
/* The following line of code is a temporary fix.
* GridMate's ReplicaChunkDescriptor is stored in a global environment variable 'm_globalDescriptorTable'
* which does not get cleared when Application shuts down. We need to un-reflect here to clear ReplicaChunkDescriptor
* so that ReplicaChunkDescriptor::m_vdt doesn't get flooded when we repeatedly instantiate Application in unit tests.
*/
AZ::ReflectionEnvironment::GetReflectionManager()->RemoveReflectContext<NetworkContext>();
// Free any memory owned by the command line container.
m_commandLine = CommandLine();
@@ -320,8 +308,6 @@ namespace AzFramework
azrtti_typeid<AzFramework::AssetCatalogComponent>(),
azrtti_typeid<AzFramework::CustomAssetTypeComponent>(),
azrtti_typeid<AzFramework::FileTag::ExcludeFileComponent>(),
azrtti_typeid<AzFramework::NetBindingComponent>(),
azrtti_typeid<AzFramework::NetBindingSystemComponent>(),
azrtti_typeid<AzFramework::TransformComponent>(),
azrtti_typeid<AzFramework::SceneSystemComponent>(),
azrtti_typeid<AzFramework::AzFrameworkConfigurationSystemComponent>(),
@@ -457,9 +443,6 @@ namespace AzFramework
void Application::CreateReflectionManager()
{
ComponentApplication::CreateReflectionManager();
// Setup NetworkContext
AZ::ReflectionEnvironment::GetReflectionManager()->AddReflectContext<NetworkContext>();
}
////////////////////////////////////////////////////////////////////////////
@@ -479,19 +462,6 @@ namespace AzFramework
return uuid;
}
////////////////////////////////////////////////////////////////////////////
NetworkContext* Application::GetNetworkContext()
{
NetworkContext* result = nullptr;
if (auto reflectionManager = AZ::ReflectionEnvironment::GetReflectionManager())
{
result = reflectionManager->GetReflectContext<NetworkContext>();
}
return result;
}
void Application::ResolveEnginePath(AZStd::string& engineRelativePath) const
{
AZ::IO::FixedMaxPath fullPath = m_engineRoot / engineRelativePath;
@@ -21,7 +21,6 @@
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/string/fixed_string.h>
#include <AzFramework/Network/NetSystemBus.h>
#include <AzFramework/CommandLine/CommandLine.h>
#include <AzFramework/API/ApplicationAPI.h>
@@ -49,7 +48,6 @@ namespace AzFramework
: public AZ::ComponentApplication
, public AZ::UserSettingsFileLocatorBus::Handler
, public ApplicationRequests::Bus::Handler
, public NetSystemRequestBus::Handler
{
public:
// Base class for platform specific implementations of the application.
@@ -138,11 +136,6 @@ namespace AzFramework
// Convenience function that should be called instead of the standard exit() function to ensure platform requirements are met.
static void Exit(int errorCode) { ApplicationRequests::Bus::Broadcast(&ApplicationRequests::TerminateOnError, errorCode); }
//////////////////////////////////////////////////////////////////////////
//! NetSystemEventBus::Handler
//////////////////////////////////////////////////////////////////////////
NetworkContext* GetNetworkContext() override;
protected:
/**
@@ -22,8 +22,6 @@
#include <AzFramework/Entity/GameEntityContextComponent.h>
#include <AzFramework/FileTag/FileTagComponent.h>
#include <AzFramework/Input/System/InputSystemComponent.h>
#include <AzFramework/Network/NetBindingComponent.h>
#include <AzFramework/Network/NetBindingSystemComponent.h>
#include <AzFramework/Render/GameIntersectorComponent.h>
#include <AzFramework/Scene/SceneSystemComponent.h>
#include <AzFramework/Script/ScriptComponent.h>
@@ -42,8 +40,6 @@ namespace AzFramework
AzFramework::AssetCatalogComponent::CreateDescriptor(),
AzFramework::CustomAssetTypeComponent::CreateDescriptor(),
AzFramework::FileTag::ExcludeFileComponent::CreateDescriptor(),
AzFramework::NetBindingComponent::CreateDescriptor(),
AzFramework::NetBindingSystemComponent::CreateDescriptor(),
AzFramework::TransformComponent::CreateDescriptor(),
AzFramework::NonUniformScaleComponent::CreateDescriptor(),
AzFramework::GameEntityContextComponent::CreateDescriptor(),
@@ -878,15 +878,15 @@ namespace AzFramework
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
if (serializeContext)
{
serializeContext->Class<TransformComponent, AZ::Component, NetBindable>()
serializeContext->ClassDeprecate("NetBindable", "{80206665-D429-4703-B42E-94434F82F381}");
serializeContext->Class<TransformComponent, AZ::Component>()
->Version(4, &TransformComponentVersionConverter)
->Field("Parent", &TransformComponent::m_parentId)
->Field("Transform", &TransformComponent::m_worldTM)
->Field("LocalTransform", &TransformComponent::m_localTM)
->Field("ParentActivationTransformMode", &TransformComponent::m_parentActivationTransformMode)
->Field("IsStatic", &TransformComponent::m_isStatic)
->Field("InterpolatePosition", &TransformComponent::m_interpolatePosition)
->Field("InterpolateRotation", &TransformComponent::m_interpolateRotation)
;
}
@@ -17,7 +17,6 @@
#include <AzCore/Component/EntityBus.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/EBus/Event.h>
#include <AzFramework/Network/NetBindable.h>
namespace AzToolsFramework
{
@@ -41,10 +40,9 @@ namespace AzFramework
, public AZ::TransformBus::Handler
, public AZ::TransformNotificationBus::Handler
, private AZ::TransformHierarchyInformationBus::Handler
, public NetBindable
{
public:
AZ_COMPONENT(TransformComponent, AZ::TransformComponentTypeId, NetBindable, AZ::TransformInterface);
AZ_COMPONENT(TransformComponent, AZ::TransformComponentTypeId, AZ::TransformInterface);
friend class AzToolsFramework::Components::TransformComponent;
@@ -218,11 +216,5 @@ namespace AzFramework
bool m_parentActive = false; ///< Keeps track of the state of the parent entity.
bool m_onNewParentKeepWorldTM = true; ///< If set, recompute localTM instead of worldTM when parent becomes active.
bool m_isStatic = false; ///< If true, the transform is static and doesn't move while entity is active.
//! @deprecated
//! @{
AZ::InterpolationMode m_interpolatePosition = AZ::InterpolationMode::NoInterpolation;
AZ::InterpolationMode m_interpolateRotation = AZ::InterpolationMode::NoInterpolation;
//! @}
};
} // namespace AZ
@@ -1,146 +0,0 @@
/*
* 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.
*
*/
#pragma once
#ifndef AZFRAMEWORK_NETWORK_DYNAMICSERIALIZABLEFIELDMARSHALER_H
#define AZFRAMEWORK_NETWORK_DYNAMICSERIALIZABLEFIELDMARSHALER_H
#include <AzCore/IO/ByteContainerStream.h>
#include <AzCore/IO/GenericStreams.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/Serialization/DynamicSerializableField.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/ObjectStream.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <GridMate/Serialize/Buffer.h>
#include <GridMate/Serialize/MathMarshal.h>
#include <GridMate/Serialize/UuidMarshal.h>
namespace GridMate
{
/**
* Marshaler for DynamicSerializableField, contains a template param for allocating the memory buffer that it's going to use to write to.
*/
template<size_t BufferSize>
class DynamicSerializableFieldMarshaler
{
public:
DynamicSerializableFieldMarshaler()
: m_serializeContext(nullptr)
{
EBUS_EVENT_RESULT(m_serializeContext, AZ::ComponentApplicationBus, GetSerializeContext);
}
// Mainly here for unit test purposes.
DynamicSerializableFieldMarshaler(AZ::SerializeContext* context)
: m_serializeContext(context)
{
}
AZ_FORCE_INLINE void Marshal(WriteBuffer& wb, const AZ::DynamicSerializableField& value) const
{
AZ_Error("DynamicSerializableFieldMarshaler", m_serializeContext, "Unknown SerializationContext. Aborting Marshal attempt.\n");
if (m_serializeContext)
{
Marshaler<AZ::u32> sizeMarshaler;
Marshaler<AZ::Uuid> uuidMarshaler;
AZStd::vector<AZ::u8> memoryBuffer(BufferSize);
// Start buffer in write mode.
AZ::IO::ByteContainerStream<decltype(memoryBuffer)> memoryStream(&memoryBuffer);
AZ::u32 bufferSize = 0;
if (m_serializeContext->FindClassData(value.m_typeId))
{
if (AZ::Utils::SaveObjectToStream(memoryStream, AZ::DataStream::StreamType::ST_BINARY, value.m_data, value.m_typeId, m_serializeContext))
{
bufferSize = static_cast<AZ::u32>(memoryStream.GetCurPos());
}
}
else
{
AZ_Error("DynamicSerializableFieldMarshaler", !value.IsValid(), "Could not save object to stream because type Id %s is not registered with the serializer.\n", value.m_typeId.ToString<AZStd::string>().c_str());
}
sizeMarshaler.Marshal(wb, bufferSize);
uuidMarshaler.Marshal(wb, value.m_typeId);
wb.WriteRaw(memoryBuffer.data(), bufferSize);
}
}
AZ_FORCE_INLINE void Unmarshal(AZ::DynamicSerializableField& value, ReadBuffer& rb) const
{
value.DestroyData(m_serializeContext);
AZ_Error("DynamicSerializableFieldMarshaler", m_serializeContext, "Unknown SerializationContext. Aborting Unmarshal attempt.\n");
if (m_serializeContext)
{
Marshaler<AZ::u32> sizeMarshaler;
AZ::u32 marshaledBufferSize = 0;
sizeMarshaler.Unmarshal(marshaledBufferSize, rb);
AZ_Assert(marshaledBufferSize <= BufferSize,"Trying to deserialize too much data for the allocated buffer size\n");
// Marshal out the TypeId so I can use it on the receiving end.
Marshaler<AZ::Uuid> uuidMarshaler;
uuidMarshaler.Unmarshal(value.m_typeId, rb);
if (marshaledBufferSize > 0)
{
// See if there's some nice way to use this.
// - Can't make this a member variable, since both these methods are const.
AZStd::vector<AZ::u8> memoryBuffer(marshaledBufferSize + 1);
if (rb.ReadRaw(memoryBuffer.data(), marshaledBufferSize))
{
// Start buffer in read mode.
AZ::IO::ByteContainerStream<decltype(memoryBuffer)> memoryStream(&memoryBuffer);
// we'll use a strict filter here, one that doesn't allow deserialization to automatically start loading assets, nor tolerates errors.
// this is becuase this is coming from a network interface and should always be error-free.
AZ::ObjectStream::FilterDescriptor filterToUse(&AZ::Data::AssetFilterNoAssetLoading, AZ::ObjectStream::FILTERFLAG_STRICT);
value.m_data = AZ::Utils::LoadObjectFromStream(memoryStream, m_serializeContext, &value.m_typeId, filterToUse);
}
}
}
}
private:
AZ::SerializeContext* m_serializeContext;
};
/**
* Specialized marshaler for AZ::DynamicSerializableField
* Mainly here to hook into the DataSet Marshaler auto detection logic, and provide a default buffer size for the actual marshaler
*/
template<>
class Marshaler<AZ::DynamicSerializableField>
: public DynamicSerializableFieldMarshaler<1024>
{
public:
Marshaler()
{
}
// Mainly here for unit test purposes.
Marshaler(AZ::SerializeContext* context)
: DynamicSerializableFieldMarshaler(context)
{
}
};
}
#endif
@@ -1,76 +0,0 @@
/*
* 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.
*
*/
#pragma once
#ifndef AZFRAMEWORK_NETWORK_ENTITYIDMARSHALER_H
#define AZFRAMEWORK_NETWORK_ENTITYIDMARSHALER_H
#include <AzCore/Component/EntityId.h>
#include <AzCore/Component/NamedEntityId.h>
#include <GridMate/Serialize/ContainerMarshal.h>
#include <GridMate/Serialize/DataMarshal.h>
namespace GridMate
{
template<>
class Marshaler<AZ::EntityId>
{
public:
AZ_TYPE_INFO_LEGACY( Marshaler, "{23F4722F-D104-4E30-9342-43F4DDD1894D}", AZ::EntityId );
void Marshal(GridMate::WriteBuffer& wb, const AZ::EntityId& source) const
{
Marshaler<AZ::u64> idMarshaler;
idMarshaler.Marshal(wb,static_cast<AZ::u64>(source));
}
void Unmarshal(AZ::EntityId& target, GridMate::ReadBuffer& rb) const
{
AZ::u64 id = 0;
Marshaler<AZ::u64> idMarshaler;
idMarshaler.Unmarshal(id,rb);
target = AZ::EntityId(id);
}
};
template<>
class Marshaler<AZ::NamedEntityId>
{
public:
void Marshal(GridMate::WriteBuffer& wb, const AZ::NamedEntityId& source) const
{
Marshaler<AZ::u64> idMarshaler;
idMarshaler.Marshal(wb, static_cast<AZ::u64>(source));
Marshaler<AZStd::string> stringMarshaler;
stringMarshaler.Marshal(wb, source.GetName());
}
void Unmarshal(AZ::NamedEntityId& target, GridMate::ReadBuffer& rb) const
{
AZ::u64 id = 0;
Marshaler<AZ::u64> idMarshaler;
idMarshaler.Unmarshal(id, rb);
AZStd::string name;
Marshaler<AZStd::string> stringMarshaler;
stringMarshaler.Unmarshal(name, rb);
target = AZ::NamedEntityId(AZ::EntityId(id), name);
}
};
}
#endif
@@ -1,187 +0,0 @@
/*
* 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 <AzFramework/Network/InterestManagerComponent.h>
#include <AzCore/Memory/AllocationRecords.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <GridMate/GridMate.h>
#include <GridMate/Replica/Interest/BitmaskInterestHandler.h>
#include <GridMate/Replica/Interest/InterestManager.h>
#include <GridMate/Replica/Interest/ProximityInterestHandler.h>
using namespace GridMate;
namespace AzFramework
{
void InterestManagerComponent::Reflect(AZ::ReflectContext* context)
{
if (context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<InterestManagerComponent, AZ::Component>()
->Version(1);
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<InterestManagerComponent>(
"InterestManagerComponent", "Interest manager instance")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b));
}
}
// We need to register the chunk types for each handler here at reflect time
if (!GridMate::ReplicaChunkDescriptorTable::Get().FindReplicaChunkDescriptor(GridMate::ReplicaChunkClassId(ProximityInterestChunk::GetChunkName())))
{
GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType<GridMate::ProximityInterestChunk>();
}
if (!GridMate::ReplicaChunkDescriptorTable::Get().FindReplicaChunkDescriptor(GridMate::ReplicaChunkClassId(BitmaskInterestChunk::GetChunkName())))
{
GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType<GridMate::BitmaskInterestChunk>();
}
}
}
void InterestManagerComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("InterestManager", 0x79993873));
}
void InterestManagerComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("InterestManager", 0x79993873));
}
InterestManagerComponent::InterestManagerComponent()
: m_im(nullptr)
, m_bitmaskHandler(nullptr)
, m_proximityHandler(nullptr)
, m_session(nullptr)
{
}
void InterestManagerComponent::Activate()
{
InterestManagerRequestsBus::Handler::BusConnect();
NetBindingSystemEventsBus::Handler::BusConnect();
AZ::SystemTickBus::Handler::BusConnect();
}
void InterestManagerComponent::Deactivate()
{
AZ::SystemTickBus::Handler::BusDisconnect();
NetBindingSystemEventsBus::Handler::BusDisconnect();
InterestManagerRequestsBus::Handler::BusDisconnect();
ShutdownInterestManager();
}
void InterestManagerComponent::OnSystemTick()
{
if (m_im && m_im->IsReady())
{
m_im->Update();
}
}
InterestManager* InterestManagerComponent::GetInterestManager()
{
return m_im.get();
}
BitmaskInterestHandler* InterestManagerComponent::GetBitmaskInterest()
{
return m_bitmaskHandler.get();
}
ProximityInterestHandler* InterestManagerComponent::GetProximityInterest()
{
return m_proximityHandler.get();
}
void InterestManagerComponent::OnNetworkSessionActivated(GridSession* session)
{
AZ_Assert(m_session == nullptr, "Already bound to the session");
AZ_TracePrintf("AzFramework", "Interest manager hooked up to the session '%s'\n", session->GetId().c_str());
m_session = session;
m_session->GetReplicaMgr()->SetAutoBroadcast(false);
InitInterestManager();
}
void InterestManagerComponent::OnNetworkSessionDeactivated(GridSession* session)
{
if (m_session && m_session == session)
{
AZ_TracePrintf("AzFramework", "Interest manager disconnected from the session '%s'\n", session ? session->GetId().c_str() : "nullptr");
if (m_session->GetReplicaMgr())
{
m_session->GetReplicaMgr()->SetAutoBroadcast(true);
}
m_session = nullptr;
ShutdownInterestManager();
}
else
{
AZ_Warning("AzFramework", false, "Interest manager was never active for session '%s'\n", session ? session->GetId().c_str() : "nullptr");
}
}
void InterestManagerComponent::InitInterestManager()
{
AZ_Assert(m_im == nullptr, "Already initialized interest manager");
m_im = AZStd::make_unique<InterestManager>();
InterestManagerDesc desc;
desc.m_rm = m_session->GetReplicaMgr();
m_im->Init(desc);
m_bitmaskHandler = AZStd::make_unique<BitmaskInterestHandler>();
m_im->RegisterHandler(m_bitmaskHandler.get());
m_proximityHandler = AZStd::make_unique<ProximityInterestHandler>();
m_im->RegisterHandler(m_proximityHandler.get());
InterestManagerEventsBus::Broadcast(
&InterestManagerEventsBus::Events::OnInterestManagerActivate, m_im.get());
}
void InterestManagerComponent::ShutdownInterestManager()
{
if (m_im)
{
InterestManagerEventsBus::Broadcast(
&InterestManagerEventsBus::Events::OnInterestManagerDeactivate, m_im.get());
m_im->UnregisterHandler(m_bitmaskHandler.get());
m_im->UnregisterHandler(m_proximityHandler.get());
m_bitmaskHandler = nullptr;
m_proximityHandler = nullptr;
m_im = nullptr;
}
}
} // namespace AzFramework
@@ -1,120 +0,0 @@
/*
* 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.
*
*/
#ifndef AZFRAMEWORK_NET_INTERESTMANAGER_COMPONENT_H
#define AZFRAMEWORK_NET_INTERESTMANAGER_COMPONENT_H
#include <AzCore/Component/Component.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzFramework/Network/NetBindingSystemBus.h>
#include <GridMate/Session/Session.h>
namespace GridMate
{
class InterestManager;
class GridSession;
class BitmaskInterestHandler;
class ProximityInterestHandler;
}
namespace AzFramework
{
class InterestManagerSystemRequests
: public AZ::EBusTraits
{
public:
virtual ~InterestManagerSystemRequests() {}
// Returns interest manager instance
virtual GridMate::InterestManager* GetInterestManager() = 0;
// Returns interest manager instance
virtual GridMate::BitmaskInterestHandler* GetBitmaskInterest() = 0;
// Returns interest manager instance
virtual GridMate::ProximityInterestHandler* GetProximityInterest() = 0;
};
// Interface Bus
using InterestManagerRequestsBus = AZ::EBus<InterestManagerSystemRequests>;
class InterestManagerEvents
: public AZ::EBusTraits
{
public:
virtual ~InterestManagerEvents() {}
// Called when interest manager is initialized and ready to use
virtual void OnInterestManagerActivate(GridMate::InterestManager* im) { (void)im; }
// Called when interest manager is deactivated
virtual void OnInterestManagerDeactivate(GridMate::InterestManager* im) { (void)im; }
};
// Interface Bus
using InterestManagerEventsBus = AZ::EBus<InterestManagerEvents>;
/**
* Interest manager component.
* When component is activated replicas will go through interest filtering before being sent to other peers
*/
class InterestManagerComponent
: public AZ::Component
, public AZ::SystemTickBus::Handler
, public InterestManagerRequestsBus::Handler
, public NetBindingSystemEventsBus::Handler
{
public:
AZ_COMPONENT(InterestManagerComponent, "{55371FA7-2942-4A3C-A3EA-27FF2C7DB6C5}");
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
InterestManagerComponent();
void Activate() override;
void Deactivate() override;
protected:
// AZ::SystemTickBus::Listener interface implementation
void OnSystemTick() override;
// InterestManagerSystemRequests implementation
GridMate::InterestManager* GetInterestManager() override;
GridMate::BitmaskInterestHandler* GetBitmaskInterest() override;
GridMate::ProximityInterestHandler* GetProximityInterest() override;
// SessionEventBus
void OnNetworkSessionActivated(GridMate::GridSession* session) override;
void OnNetworkSessionDeactivated(GridMate::GridSession* session) override;
void InitInterestManager();
void ShutdownInterestManager();
// Interest handlers
AZStd::unique_ptr<GridMate::InterestManager> m_im;
AZStd::unique_ptr<GridMate::BitmaskInterestHandler> m_bitmaskHandler;
AZStd::unique_ptr<GridMate::ProximityInterestHandler> m_proximityHandler;
GridMate::GridSession* m_session; ///< currently bound session
private:
InterestManagerComponent(const InterestManagerComponent&) = delete; //Cannot use default due to unique_ptr.
};
} // namesapce AzFramework
#endif // AZFRAMEWORK_NET_INTERESTMANAGER_COMPONENT_H
@@ -1,111 +0,0 @@
/*
* 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 <AzFramework/Network/NetBindable.h>
#include <AzFramework/Network/NetBindingHandlerBus.h>
#include <AzFramework/Network/NetSystemBus.h>
#include <AzFramework/Network/NetworkContext.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
namespace AzFramework
{
////////////////
// NetBindable
////////////////
NetBindable::NetBindable()
: m_isSyncEnabled(true)
{
}
NetBindable::~NetBindable()
{
if (m_chunk)
{
// NetBindable is a base class for handlers for replica chunks, so we have to clear the handler since this object is about to go away
m_chunk->SetHandler(nullptr);
m_chunk = nullptr;
}
}
GridMate::ReplicaChunkPtr NetBindable::GetNetworkBinding()
{
NetworkContext* netContext = nullptr;
NetSystemRequestBus::BroadcastResult(netContext, &NetSystemRequestBus::Events::GetNetworkContext);
AZ_Assert(netContext, "Cannot bind objects to the network with no NetworkContext");
if (netContext)
{
m_chunk = netContext->CreateReplicaChunk(azrtti_typeid(this));
netContext->Bind(this, m_chunk, NetworkContextBindMode::Authoritative);
return m_chunk;
}
return nullptr;
}
void NetBindable::SetNetworkBinding (GridMate::ReplicaChunkPtr chunk)
{
m_chunk = chunk;
NetworkContext* netContext = nullptr;
NetSystemRequestBus::BroadcastResult(netContext, &NetSystemRequestBus::Events::GetNetworkContext);
AZ_Assert(netContext, "Cannot bind objects to the network with no NetworkContext");
if (netContext)
{
netContext->Bind(this, m_chunk, NetworkContextBindMode::NonAuthoritative);
}
}
void NetBindable::UnbindFromNetwork()
{
if (m_chunk)
{
// NetworkContext-reflected chunks need access to the handler when they are being destroyed, so we won't null handler in here
m_chunk = nullptr;
}
}
void NetBindable::NetInit()
{
NetworkContext* netContext = nullptr;
NetSystemRequestBus::BroadcastResult(netContext, &NetSystemRequestBus::Events::GetNetworkContext);
AZ_Assert(netContext, "Cannot bind objects to the network with no NetworkContext");
if (netContext)
{
netContext->Bind(this, nullptr, NetworkContextBindMode::NonAuthoritative);
}
}
void NetBindable::Reflect(AZ::ReflectContext* reflection)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
if (serializeContext)
{
serializeContext->Class<NetBindable>()
->Field("m_isSyncEnabled", &NetBindable::m_isSyncEnabled);
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<NetBindable>(
"Network Bindable", "Network-bindable components are synchronized over the network.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Networking")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->DataElement(AZ::Edit::UIHandlers::Default, &NetBindable::m_isSyncEnabled, "Bind To network", "Enable binding to the network.");
}
}
}
}
@@ -1,799 +0,0 @@
/*
* 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.
*
*/
#ifndef AZFRAMEWORK_NET_BINDABLE_H
#define AZFRAMEWORK_NET_BINDABLE_H
#include <AzCore/Component/EntityId.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/containers/list.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <GridMate/Replica/ReplicaCommon.h>
#include <GridMate/Replica/ReplicaChunkInterface.h>
#include <GridMate/Replica/DataSet.h>
#include <GridMate/Replica/RemoteProcedureCall.h>
/*
* Including common GridMate marshallers.
* Otherwise, users of NetBindable/NetworkContext have to find and include them themselves.
*/
#include <AzFramework/Network/EntityIdMarshaler.h>
#include <GridMate/Serialize/MathMarshal.h>
#include <GridMate/Serialize/CompressionMarshal.h>
#include <GridMate/Serialize/ContainerMarshal.h>
#include <GridMate/Serialize/DataMarshal.h>
#include <GridMate/Serialize/UtilityMarshal.h>
#include <GridMate/Serialize/UuidMarshal.h>
namespace AZ
{
class ReflectContext;
namespace Internal
{
template <class FieldType>
class AzFrameworkNetBindableFieldContainer;
}
}
namespace AzFramework
{
using GridMate::DataSetBase;
using GridMate::DataSet;
using GridMate::Marshaler;
using GridMate::BasicThrottle;
using GridMate::RpcBase;
using GridMate::TimeContext;
using GridMate::RpcContext;
using GridMate::RpcDefaultTraits;
enum class NetworkContextBindMode
{
Authoritative,
NonAuthoritative
};
/**
* Components that want to be synchronized over the network should implement NetBindable.
* The NetBindable interface is obtained via AZ_RTTI so components need to make sure to
* declare NetBindable as a base class in their AZ_RTTI declaration (or AZ_COMPONENT declaration),
* as well as to declare both AZ::Component and NetBindable as base classes in the reflection.
*
* For example, here is how to mark a component for network replication in its class declaration:
*
* class TestFieldComponent
* : public AZ::Component
* , public AzFramework::NetBindable
* {
* public:
* AZ_COMPONENT(TestFieldComponent, "{DD02A926-F6B3-4820-9587-62EED9EEBB3F}", NetBindable);
*
* static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
* {
* required.push_back(AZ_CRC("ReplicaChunkService"));
* }
*
* Note, you should declare a dependency on NetBindingComponent as it is done above with "ReplicaChunkService."
* NetBindingComponent is required for an entity to be considered for network replication and replicate your NetBindable-components.
*/
class NetBindable
: public GridMate::ReplicaChunkInterface
{
public:
AZ_RTTI(NetBindable, "{80206665-D429-4703-B42E-94434F82F381}");
NetBindable();
virtual ~NetBindable();
void NetInit();
//! Called during network binding on the master. The default implementation will use the
//! NetworkContext to create a chunk. User implementations should create and return a new binding.
virtual GridMate::ReplicaChunkPtr GetNetworkBinding();
//! Called during network binding on proxies.
virtual void SetNetworkBinding(GridMate::ReplicaChunkPtr chunk);
//! Called when network is unbound. Implementations should release their references to the binding, if they held a reference.
virtual void UnbindFromNetwork();
static void Reflect(AZ::ReflectContext* reflection);
template <class DataType, typename MarshalerType = Marshaler<DataType>, typename ThrottlerType = BasicThrottle<DataType> >
class Field;
template <class DataType, class InterfaceType, void (InterfaceType::*)(const DataType&, const TimeContext&), typename MarshalerType = Marshaler<DataType>, typename ThrottlerType = BasicThrottle<DataType> >
class BoundField;
template <typename ... Args>
class Rpc;
inline bool IsSyncEnabled() const { return m_isSyncEnabled; }
//! Can be used to disabled net sync on a per component basis
inline void SetSyncEnabled(bool enabled) { m_isSyncEnabled = enabled; }
protected:
bool m_isSyncEnabled;
GridMate::ReplicaChunkPtr m_chunk = nullptr;
};
class NetBindableFieldBase
{
public:
virtual ~NetBindableFieldBase() = default;
virtual void Bind(DataSetBase* dataSet, NetworkContextBindMode mode) = 0;
};
/**
* \brief NetBindable provides a simplified network interface to mark a member variable inside AZ::Component
* as a network field that will be replicated by GridMate.
*
* \tparam DataType data type of the field, can be either a common C++ type or a custom type
* \tparam MarshalerType optional, marshaler type that provides custom marshal and unmarshal logic, i.e. how to write @DataType to the network and back, see @GridMate::Marshaler
* \tparam ThrottlerType optional, throttler provides the ability to detect if a value is to be considered changed significantly enough for GridMate to replicate its state, see @GridMate::BasicThrottle
*
* Example:
*
* class TestFieldComponent : public AZ::Component , public AzFramework::NetBindable
* {
* public:
* Field<int> m_testInt;
*
* And it must be reflected to SerializeContext _and_ NetworkContext:
*
* void TestFieldComponent::Reflect(AZ::ReflectContext* context)
* {
* if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
* {
* serialize->Class<TestFieldComponent, AZ::Component, AzFramework::NetBindable>()
* ->Field("Test Int", &TestFieldComponent::m_testInt)
* ->Version(1);
* }
*
* if (AzFramework::NetworkContext* net = azrtti_cast<AzFramework::NetworkContext*>(context))
* {
* net->Class<TestFieldComponent>()
* ->Field("Test Int", &TestFieldComponent::m_testInt);
* }
* }
*
* Then you can simply write to it as it was an integer:
*
* m_testInt = 3;
* // or
* m_testInt = *m_testInt + 1;
*/
template <class DataType, typename MarshalerType, typename ThrottlerType>
class NetBindable::Field
: public NetBindableFieldBase
{
friend class AZ::Internal::AzFrameworkNetBindableFieldContainer<NetBindable::Field<DataType, MarshalerType, ThrottlerType> >;
public:
using DataSetType = DataSet<DataType, MarshalerType, ThrottlerType>;
using ValueType = DataType;
explicit Field(const DataType& value = DataType())
: m_dataSet(nullptr)
, m_value(value)
{}
~Field() override = default;
/*
* Disabling copy and move constructors in order to allow for a common use of fields, for example:
* m_field = m_field + 1;
*/
Field (const Field& other) = delete;
Field (Field&& other) = delete;
Field& operator= (const Field& other) = delete;
Field& operator= (Field&& other) = delete;
const DataType& Get() const
{
return m_dataSet ? m_dataSet->Get() : m_value;
}
virtual operator const DataType&() const
{
return Get();
}
virtual const DataType& operator*() const
{
return Get();
}
virtual Field& operator=(const DataType& val)
{
if (m_dataSet)
{
m_dataSet->Set(val);
}
else
{
m_value = val;
}
return *this;
}
virtual Field& operator=(const DataType&& val)
{
if (m_dataSet)
{
m_dataSet->Set(AZStd::forward<const DataType>(val));
}
else
{
m_value = AZStd::move(val);
}
return *this;
}
void Bind(DataSetBase* dataSet, NetworkContextBindMode mode) override
{
BindDataSet(static_cast<DataSetType*>(dataSet), mode);
}
static void ConstructDataSet(void* mem, const char* name)
{
new (mem) DataSetType(name, DataType(), MarshalerType(), ThrottlerType());
}
static void DestructDataSet(void* mem)
{
DataSetType* dataSet = reinterpret_cast<DataSetType*>(mem);
dataSet->~DataSetType();
}
protected:
template <class DST>
void BindDataSet(DST* dataSet, NetworkContextBindMode mode)
{
if (m_dataSet)
{
m_value = m_dataSet->Get();
}
m_dataSet = dataSet;
if (m_dataSet)
{
if (mode == NetworkContextBindMode::Authoritative)
{
/*
* If we are binding Field<> or BoundField<> on a component of an authoritative entity,
* then we want to bring over the value of the field in the component. This occurs during GetNetworkBinding().
*
* Whereas on a client's (non-authoritative entities and their components) dataSet already has the desired value
* and should not be overwritten here.
*/
m_dataSet->Set(AZStd::move(m_value));
}
m_value = DataType();
}
}
DataType* CacheValue()
{
if (m_dataSet)
{
m_value = m_dataSet->Get();
}
return &m_value;
}
const DataType& GetCachedValue() const
{
return m_value;
}
private:
DataSet<DataType, MarshalerType, ThrottlerType>* m_dataSet;
DataType m_value;
};
/**
* \brief An extension of @NetBindable::Field with an ability to invoke a callback whenever the value changes on both authoritative and non-authoritative components.
* Or in other terms, on both the server and clients (when GridMate is setup to run in server-authoritative mode).
*
* \tparam DataType data type, same as @NetBindable::Field
* \tparam InterfaceType Component type class that holds this @BoundField
* \tparam FuncPtr member function pointer to the callback to invoke when this value is updated on non-authoritative components.
* \tparam MarshalerType optional, same as @NetBindable::Field
* \tparam ThrottlerType optional, same as @NetBindable::Field
*
* Example:
*
* BoundField<int, TestBoundFieldComponent, &TestBoundFieldComponent::OnBoundFieldChanged> m_testInt;
*
* And it must be reflected to SerializeContext _and_ NetworkContext just like @NetBindable::Field
*
* void TestFieldComponent::Reflect(AZ::ReflectContext* context)
* {
* if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
* {
* serialize->Class<TestFieldComponent, AZ::Component, AzFramework::NetBindable>()
* ->Field("Test Int", &TestFieldComponent::m_testInt)
* ->Version(1);
* }
*
* if (AzFramework::NetworkContext* net = azrtti_cast<AzFramework::NetworkContext*>(context))
* {
* net->Class<TestFieldComponent>()
* ->Field("Test Int", &TestFieldComponent::m_testInt);
* }
* }
*/
template <class DataType, class InterfaceType, void (InterfaceType::* FuncPtr)(const DataType&, const TimeContext&), typename MarshalerType, typename ThrottlerType>
class NetBindable::BoundField
: public NetBindable::Field<DataType, MarshalerType, ThrottlerType>
{
using BaseClass = NetBindable::Field<DataType, MarshalerType, ThrottlerType>;
friend class AZ::Internal::AzFrameworkNetBindableFieldContainer<NetBindable::BoundField<DataType, InterfaceType, FuncPtr, MarshalerType, ThrottlerType> >;
public:
AZ_TYPE_INFO_LEGACY(BoundField, "{5151CEAF-6AC0-45D7-AEDF-8B6C46CE07B9}", DataType, InterfaceType, MarshalerType, ThrottlerType);
using DataSetType = typename DataSet<DataType, MarshalerType, ThrottlerType>::template BindInterface<InterfaceType, FuncPtr, GridMate::DataSetInvokeEverywhereTraits>;
explicit BoundField(const DataType& value = DataType())
: BaseClass(value)
{}
~BoundField() override = default;
/*
* Disabling copy and move constructors in order to allow for a common use of fields, for example:
* m_field = m_field + 1;
*/
BoundField (const BoundField& other) = delete;
BoundField (BoundField&& other) = delete;
BoundField& operator= (const BoundField& other) = delete;
BoundField& operator= (BoundField&& other) = delete;
operator DataType() const
{
return BaseClass::Get();
}
const DataType& operator*() const override
{
return BaseClass::Get();
}
BaseClass& operator=(const DataType& val) override
{
BaseClass::operator=(val);
return *this;
}
BaseClass& operator=(const DataType&& val) override
{
BaseClass::operator=(val);
return *this;
}
void Bind(DataSetBase* dataSet, NetworkContextBindMode mode) override
{
BaseClass::BindDataSet(static_cast<DataSetType*>(dataSet), mode);
}
static void ConstructDataSet(void* mem, const char* name)
{
new (mem) DataSetType(name, DataType(), MarshalerType(), ThrottlerType());
}
static void DestructDataSet(void* mem)
{
DataSetType* dataSet = reinterpret_cast<DataSetType*>(mem);
dataSet->~DataSetType();
}
};
class NetBindableRpcBase
{
public:
virtual ~NetBindableRpcBase() = default;
virtual void Bind(RpcBase* rpc) = 0;
virtual void Bind(NetBindable* handler) = 0;
};
/**
* \brief NetBindable::Rpc::Binder should be used for any RPC in a NetBindable that you want
* to be able to call remotely. If the object is not network bound, RPC
* calls will dispatch directly, as if the object was authoritative.
*
* \tparam Args any custom parameters for the remote procedure calls.
*
* Here is an example:
*
* // callback
* bool OnRpc(float value, const GridMate::RpcContext& rc);
*
* // Rpc declaration
* Rpc<float>::Binder<TestRPCComponent, &TestRPCComponent::OnRpc> m_testRpc;
*
* Rpc needs to be reflected in NetworkContext like this:
*
* void TestRPCComponent::Reflect(AZ::ReflectContext* context)
* {
* if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
* {
* serialize->Class<TestRPCComponent, AZ::Component, AzFramework::NetBindable>()
* ->Version(1);
* }
*
* if (AzFramework::NetworkContext* net = azrtti_cast<AzFramework::NetworkContext*>(context))
* {
* net->Class<TestRPCComponent>()
* ->RPC("Test RPC", &TestRPCComponent::m_testRpc);
* }
* }
*
* It can be invoked as if it was a method:
*
* m_testRpc(deltaTime);
*/
template <typename ... Args>
class NetBindable::Rpc
{
public:
/**
* \brief Binds rpc callback to a pointer to member function of AZ::Component derived from AzFramework::NetBindable
* See @NetBindable::Rpc
*/
template<class InterfaceType, bool (InterfaceType::* FuncPtr)(Args..., const RpcContext&), class Traits = RpcDefaultTraits>
class Binder
: public NetBindableRpcBase
{
friend class NetworkContext;
public:
using BindInterfaceType = typename GridMate::Rpc<GridMate::RpcArg<Args>...>::template BindInterface<InterfaceType, FuncPtr, Traits>;
Binder()
: m_rpc(nullptr)
, m_instance(nullptr)
{}
void Bind(RpcBase* rpc) override
{
m_rpc = static_cast<BindInterfaceType*>(rpc);
m_instance = nullptr;
}
void Bind(NetBindable* bindable) override
{
m_instance = static_cast<InterfaceType*>(bindable);
m_rpc = nullptr;
}
template <typename ... CallArgs>
void operator()(CallArgs&& ... args)
{
AZ_Assert(m_instance || m_rpc, "Cannot call an RPC without either a local instance or a network bound handler, did you forget to register with NetworkContext()?");
if (m_rpc) // connected to network
{
(*m_rpc)(AZStd::forward<CallArgs>(args) ...);
}
else if (m_instance) // local dispatch
{
(*m_instance.*FuncPtr)(AZStd::forward<CallArgs>(args) ..., RpcContext());
}
}
protected:
static void ConstructRpc(void* mem, const char* name)
{
new (mem) BindInterfaceType(name);
}
static void DestructRpc(void*) { }
private:
BindInterfaceType* m_rpc;
InterfaceType* m_instance;
};
Rpc() = delete;
};
} // namespace AzFramework
namespace AZ
{
AZ_TYPE_INFO_TEMPLATE_WITH_NAME(AzFramework::NetBindable::Field, "Field", "{00D56FA7-F8BD-402B-97FB-0E2599897056}", AZ_TYPE_INFO_CLASS, AZ_TYPE_INFO_TYPENAME, AZ_TYPE_INFO_TYPENAME);
namespace Internal
{
template <class FieldType>
class AzFrameworkNetBindableFieldContainer
: public SerializeContext::IDataContainer
{
using ValueType = typename FieldType::ValueType;
public:
AzFrameworkNetBindableFieldContainer()
{
m_classElement.m_name = GetDefaultElementName();
m_classElement.m_nameCrc = GetDefaultElementNameCrc();
m_classElement.m_dataSize = sizeof(ValueType);
m_classElement.m_offset = 0;
m_classElement.m_azRtti = GetRttiHelper<ValueType>();
m_classElement.m_flags = AZStd::is_pointer<ValueType>::value ? SerializeContext::ClassElement::FLG_POINTER : 0;
m_classElement.m_genericClassInfo = SerializeGenericTypeInfo<ValueType>::GetGenericInfo();
m_classElement.m_typeId = SerializeGenericTypeInfo<ValueType>::GetClassTypeId();
m_classElement.m_editData = nullptr;
}
/// Returns the element generic (offsets are mostly invalid 0xbad0ffe0, there are exceptions). Null if element with this name can't be found.
virtual const SerializeContext::ClassElement* GetElement(AZ::u32 elementNameCrc) const override
{
if (elementNameCrc == m_classElement.m_nameCrc)
{
return &m_classElement;
}
return nullptr;
}
bool GetElement(SerializeContext::ClassElement& classElement, const SerializeContext::DataElement& dataElement) const override
{
if (dataElement.m_nameCrc == m_classElement.m_nameCrc)
{
classElement = m_classElement;
return true;
}
return false;
}
/// Enumerate elements in the array
virtual void EnumElements(void* instance, const ElementCB& cb) override
{
FieldType* field = reinterpret_cast<FieldType*>(instance);
// We can't mess with the internal storage of the dataset safely, so we copy it into
// the field's local value cache temporarily, then hand that to the callback
// This will modify the local value cache, but that shouldn't matter as it will never
// be used as long as a dataset is bound
// If this turns out to be a perf problem due to copies of complex types, then
// the easy solution is to get DataSets to expose a pointer to their underlying
// data storage, and then we can return a pointer to that and modify it directly
// if the field is bound to the network
ValueType* valPtr = field->CacheValue();
cb(valPtr, m_classElement.m_typeId, m_classElement.m_genericClassInfo ? m_classElement.m_genericClassInfo->GetClassData() : nullptr, &m_classElement);
// Ensure that the dataset is updated if changes happened
*field = *valPtr;
}
void EnumTypes(const ElementTypeCB& cb) override
{
cb(m_classElement.m_typeId, &m_classElement);
}
/// Return number of elements in the container.
virtual size_t Size(void*) const override
{
return 1;
}
/// Returns the capacity of the container. Returns 0 for objects without fixed capacity.
virtual size_t Capacity(void* instance) const override
{
(void)instance;
return 1;
}
/// Returns true if elements pointers don't change on add/remove. If false you MUST enumerate all elements.
virtual bool IsStableElements() const override { return true; }
/// Returns true if the container is fixed size, otherwise false.
virtual bool IsFixedSize() const override { return true; }
/// Returns if the container is fixed capacity, otherwise false
virtual bool IsFixedCapacity() const override { return true; }
/// Returns true if the container is a smart pointer.
virtual bool IsSmartPointer() const override { return true; }
/// Returns true if the container elements can be addressed by index, otherwise false.
virtual bool CanAccessElementsByIndex() const override { return false; }
/// Reserve element
virtual void* ReserveElement(void* instance, const SerializeContext::ClassElement*) override
{
FieldType* field = reinterpret_cast<FieldType*>(instance);
*field = ValueType();
return field->CacheValue(); // return the local value, should be accurate as the field will be unbound at serialization time
}
/// Get an element's address by its index (called before the element is loaded).
virtual void* GetElementByIndex(void*, const SerializeContext::ClassElement*, size_t) override
{
return nullptr;
}
/// Store element
virtual void StoreElement(void* instance, void*) override
{
// force store the value again, just in case the field is bound to a dataset
FieldType* field = reinterpret_cast<FieldType*>(instance);
*field = field->GetCachedValue();
}
/// Remove element in the container.
virtual bool RemoveElement(void* instance, const void*, SerializeContext*) override
{
FieldType* field = reinterpret_cast<FieldType*>(instance);
*field = ValueType();
return false; // you can't remove element from this container.
}
/// Remove elements (removed array of elements) regardless if the container is Stable or not (IsStableElements)
virtual size_t RemoveElements(void* instance, const void**, size_t, SerializeContext*) override
{
RemoveElement(instance, nullptr, nullptr);
return 0; // you can't remove elements from this container.
}
/// Clear elements in the instance.
virtual void ClearElements(void* instance, SerializeContext*) override
{
RemoveElement(instance, nullptr, nullptr);
}
SerializeContext::ClassElement m_classElement; ///< Generic class element covering as must as possible of the element (offset, and some other fields are invalid)
};
}
template <class DataType, typename MarshalerType, typename ThrottlerType>
struct SerializeGenericTypeInfo< AzFramework::NetBindable::Field<DataType, MarshalerType, ThrottlerType> >
{
typedef typename AzFramework::NetBindable::Field<DataType, MarshalerType, ThrottlerType> ContainerType;
class GenericClassNetBindableField
: public GenericClassInfo
{
public:
AZ_TYPE_INFO(GenericClassNetBindableField, "{C1D4DD97-5DD7-42ED-969C-7435F27F5D8C}");
GenericClassNetBindableField()
: m_classData{ SerializeContext::ClassData::Create<ContainerType>("AzFramework::NetBindable::Field", GetSpecializedTypeId(), Internal::NullFactory::GetInstance(), nullptr, &m_containerStorage) }
{
}
SerializeContext::ClassData* GetClassData() override
{
return &m_classData;
}
size_t GetNumTemplatedArguments() override
{
return 1;
}
const Uuid& GetTemplatedTypeId(size_t) override
{
return SerializeGenericTypeInfo<DataType>::GetClassTypeId();
}
const Uuid& GetSpecializedTypeId() const override
{
return azrtti_typeid<ContainerType>();
}
const Uuid& GetGenericTypeId() const override
{
return TYPEINFO_Uuid();
}
const Uuid& GetLegacySpecializedTypeId() const override
{
return AZ::AzTypeInfo<ContainerType>::template Uuid<AZ::PointerRemovedTypeIdTag>();
}
void Reflect(SerializeContext* serializeContext)
{
if (serializeContext)
{
serializeContext->RegisterGenericClassInfo(GetSpecializedTypeId(), this, &AnyTypeInfoConcept<ContainerType>::CreateAny);
if (GenericClassInfo* containerGenericClassInfo = m_containerStorage.m_classElement.m_genericClassInfo)
{
containerGenericClassInfo->Reflect(serializeContext);
}
}
}
protected:
Internal::AzFrameworkNetBindableFieldContainer<ContainerType> m_containerStorage;
SerializeContext::ClassData m_classData;
};
using ClassInfoType = GenericClassNetBindableField;
static ClassInfoType* GetGenericInfo()
{
return GetCurrentSerializeContextModule().CreateGenericClassInfo<ContainerType>();
}
static const Uuid& GetClassTypeId()
{
return GetGenericInfo()->GetClassData()->m_typeId;
}
};
template <class DataType, class InterfaceType, void (InterfaceType::* FuncPtr)(const DataType&, const AzFramework::TimeContext&), typename MarshalerType, typename ThrottlerType>
struct SerializeGenericTypeInfo< typename AzFramework::NetBindable::BoundField<DataType, InterfaceType, FuncPtr, MarshalerType, ThrottlerType> >
{
typedef typename AzFramework::NetBindable::BoundField<DataType, InterfaceType, FuncPtr, MarshalerType, ThrottlerType> ContainerType;
class GenericClassNetBindableBoundField
: public GenericClassInfo
{
public:
AZ_TYPE_INFO(GenericClassNetBindableBoundField, "{EFD64FE7-9432-401A-B7A1-1767F4C5A7F0}");
GenericClassNetBindableBoundField()
: m_classData{ SerializeContext::ClassData::Create<ContainerType>("AzFramework::NetBindable::BoundField", GetSpecializedTypeId(), Internal::NullFactory::GetInstance(), nullptr, &m_containerStorage) }
{
}
SerializeContext::ClassData* GetClassData() override
{
return &m_classData;
}
size_t GetNumTemplatedArguments() override
{
return 1;
}
const Uuid& GetTemplatedTypeId(size_t) override
{
return SerializeGenericTypeInfo<DataType>::GetClassTypeId();
}
const Uuid& GetSpecializedTypeId() const override
{
return azrtti_typeid<ContainerType>();
}
const Uuid& GetGenericTypeId() const override
{
return TYPEINFO_Uuid();
}
const Uuid& GetLegacySpecializedTypeId() const override
{
return AZ::AzTypeInfo<ContainerType>::template Uuid<AZ::PointerRemovedTypeIdTag>();
}
void Reflect(SerializeContext* serializeContext)
{
if (serializeContext)
{
serializeContext->RegisterGenericClassInfo(GetSpecializedTypeId(), this, &AnyTypeInfoConcept<ContainerType>::CreateAny);
if (GenericClassInfo* containerGenericClassInfo = m_containerStorage.m_classElement.m_genericClassInfo)
{
containerGenericClassInfo->Reflect(serializeContext);
}
}
}
protected:
Internal::AzFrameworkNetBindableFieldContainer<ContainerType> m_containerStorage;
SerializeContext::ClassData m_classData;
};
using ClassInfoType = GenericClassNetBindableBoundField;
static ClassInfoType* GetGenericInfo()
{
return GetCurrentSerializeContextModule().CreateGenericClassInfo<ContainerType>();
}
static const Uuid& GetClassTypeId()
{
return GetGenericInfo()->GetClassData()->m_typeId;
}
};
}
#endif // AZFRAMEWORK_NET_BINDABLE_H
#pragma once
@@ -1,287 +0,0 @@
/*
* 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 <AzFramework/Network/NetBindingComponent.h>
#include <AzFramework/Network/NetBindable.h>
#include <AzFramework/Network/NetBindingSystemBus.h>
#include <AzFramework/Network/NetBindingComponentChunk.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <GridMate/Replica/Replica.h>
#include <GridMate/Replica/ReplicaChunk.h>
#include <GridMate/Replica/ReplicaFunctions.h>
#include <GridMate/Replica/ReplicaChunkDescriptor.h>
namespace AzFramework
{
void NetBindingComponent::Reflect(AZ::ReflectContext* reflection)
{
NetBindable::Reflect(reflection);
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
if (serializeContext)
{
serializeContext->Class<NetBindingComponent, AZ::Component>()
;
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<NetBindingComponent>(
"Network Binding", "The Network Binding component marks an entity as able to be replicated across the network")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Networking")
->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/NetBinding.svg")
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/NetBinding.png")
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.aws.amazon.com/lumberyard/latest/userguide/component-network-binding.html")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c));
}
}
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(reflection);
if (behaviorContext)
{
behaviorContext->EBus<NetBindingHandlerBus>("NetBindingHandlerBus")
->Event("IsEntityBoundToNetwork", &NetBindingHandlerBus::Events::IsEntityBoundToNetwork)
->Event("IsEntityAuthoritative", &NetBindingHandlerBus::Events::IsEntityAuthoritative)
// Desired, but currently unsupported events.
// Seems to be an unsupported type(AZ::u16)
//->Event("SetReplicaPriority", &NetBindingHandlerBus::Events::SetReplicaPriority)
//->Event("GetReplicaPriority", &NetBindingHandlerBus::Events::GetReplicaPriority)
;
}
// We also need to register the chunk type, and this would be a good time to do so.
if (!GridMate::ReplicaChunkDescriptorTable::Get().FindReplicaChunkDescriptor(GridMate::ReplicaChunkClassId(NetBindingComponentChunk::GetChunkName())))
{
GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType<AzFramework::NetBindingComponentChunk>();
}
}
NetBindingComponent::NetBindingComponent()
: m_isLevelSliceEntity(false)
{
}
void NetBindingComponent::Activate()
{
NetBindingHandlerBus::Handler::BusConnect(GetEntityId());
if (!IsEntityBoundToNetwork())
{
bool shouldBind = false;
NetBindingSystemBus::BroadcastResult( shouldBind, &NetBindingSystemBus::Events::ShouldBindToNetwork);
if (shouldBind)
{
BindToNetwork(nullptr);
}
else
{
/*
* This is the Editor path. We still need to call NetBindable::NetInit() in order
* to initialize NetworkContext Fields and RPCs, so that they behave as
* authoritative in game editor mode. Without this call RPCs callbacks won't invoke inside the Editor.
* For example:
*
* static void Reflect(...)
* {
* NetworkContext->Class<MyNetworkComponent>()->RPC("my rpc", &MyNetworkComponent::m_myRpc);
* }
* ...
* m_myRpc(); // <--- will not invoke the callback inside the Editor unless NetInit() is called below.
*/
for (Component* component : GetEntity()->GetComponents())
{
if (NetBindable* netBindable = azrtti_cast<NetBindable*>(component))
{
netBindable->NetInit();
}
}
}
}
}
void NetBindingComponent::Deactivate()
{
NetBindingHandlerBus::Handler::BusDisconnect();
if (IsEntityBoundToNetwork())
{
static_cast<NetBindingComponentChunk*>(m_chunk.get())->SetBinding(nullptr);
if (m_chunk->IsMaster())
{
m_chunk->GetReplica()->Destroy();
}
m_chunk = nullptr;
}
}
bool NetBindingComponent::IsEntityBoundToNetwork()
{
return m_chunk && m_chunk->GetReplica();
}
bool NetBindingComponent::IsEntityAuthoritative()
{
return !m_chunk || m_chunk->IsMaster();
}
void NetBindingComponent::BindToNetwork(GridMate::ReplicaPtr bindTo)
{
AZ_Assert(!IsEntityBoundToNetwork(), "We shouldn't be bound to the network if the network is just starting!");
if (bindTo)
{
NetBindingComponentChunkPtr bindingChunk = bindTo->FindReplicaChunk<NetBindingComponentChunk>();
AZ_Assert(bindingChunk, "Can't find NetBindingComponentChunk!");
m_chunk = bindingChunk;
bindingChunk->SetBinding(this);
GridMate::Replica* replica = bindingChunk->GetReplica();
size_t nChunks = replica->GetNumChunks();
size_t nBindings = bindingChunk->m_bindMap.Get().size();
AZ_Assert(nChunks == nBindings, "Number of chunks received is not the same as the size of the bind map!");
nBindings = AZ::GetMin(nBindings, nChunks);
for (size_t i = 0; i < nBindings; ++i)
{
AZ::ComponentId bindToId = bindingChunk->m_bindMap.Get()[i];
if (bindToId != AZ::InvalidComponentId)
{
AZ::Component* component = GetEntity()->FindComponent(bindToId);
NetBindable* netBindable = azrtti_cast<NetBindable*>(component);
AZ_Assert(netBindable, "Can't find net bindable component with id %llu to be bound to chunk type %s!", bindToId, replica->GetChunkByIndex(i)->GetDescriptor()->GetChunkName());
if (netBindable && netBindable->IsSyncEnabled())
{
netBindable->SetNetworkBinding(replica->GetChunkByIndex(i));
}
}
}
}
else
{
GridMate::ReplicaPtr replica = GridMate::Replica::CreateReplica(GetEntity()->GetName().c_str());
NetBindingComponentChunk* chunk = GridMate::CreateReplicaChunk<NetBindingComponentChunk>();
m_chunk = chunk;
chunk->SetBinding(this);
replica->AttachReplicaChunk(chunk);
chunk->m_bindMap.Modify([&](AZStd::vector<AZ::ComponentId>& bindMap)
{
// Mark the chunks already in the replica as non-components.
bindMap.resize(replica->GetNumChunks(), AZ::InvalidComponentId);
// Collect the bindings and add the to the replica
AZ::Entity* entity = GetEntity();
for (Component* component : entity->GetComponents())
{
NetBindable* netBindable = azrtti_cast<NetBindable*>(component);
if (netBindable && netBindable->IsSyncEnabled())
{
GridMate::ReplicaChunkPtr bindingChunk = netBindable->GetNetworkBinding();
if (bindingChunk)
{
bindMap.push_back(component->GetId());
replica->AttachReplicaChunk(bindingChunk);
}
}
}
return true;
});
// Add replica to session replica manager (may be deferred)
NetBindingSystemBus::Broadcast( &NetBindingSystemBus::Events::AddReplicaMaster, GetEntity(), replica);
}
}
void NetBindingComponent::UnbindFromNetwork()
{
if (m_chunk)
{
for (Component* component : GetEntity()->GetComponents())
{
NetBindable* netBindable = azrtti_cast<NetBindable*>(component);
if (netBindable && netBindable->IsSyncEnabled())
{
netBindable->UnbindFromNetwork();
}
}
NetBindingComponentChunkPtr chunk = static_cast<NetBindingComponentChunk*>(m_chunk.get());
chunk->SetBinding(nullptr);
m_chunk = nullptr;
if (chunk->IsProxy())
{
EntityContextId contextId = EntityContextId::CreateNull();
EntityIdContextQueryBus::EventResult( contextId, GetEntityId(), &EntityIdContextQueryBus::Events::GetOwningContextId);
if (contextId.IsNull())
{
delete GetEntity();
}
else if (!IsLevelSliceEntity())
{
NetBindingSystemBus::Broadcast( &NetBindingSystemBus::Events::UnbindGameEntity, GetEntityId(), m_sliceInstanceId);
}
}
}
}
void NetBindingComponent::MarkAsLevelSliceEntity()
{
AZ_Assert(!IsEntityBoundToNetwork(), "MarkAsLevelSliceEntity() has to be called before the entity is bound to the network!");
m_isLevelSliceEntity = true;
}
void NetBindingComponent::SetSliceInstanceId(const AZ::SliceComponent::SliceInstanceId& sliceInstanceId)
{
m_sliceInstanceId = sliceInstanceId;
}
void NetBindingComponent::RequestEntityChangeOwnership(GridMate::PeerId peerId)
{
if (m_chunk && m_chunk->GetReplica())
{
m_chunk->GetReplica()->RequestChangeOwnership(peerId);
}
}
void NetBindingComponent::SetReplicaPriority(GridMate::ReplicaPriority replicaPriority)
{
if (m_chunk)
{
m_chunk->SetPriority(replicaPriority);
}
}
GridMate::ReplicaPriority NetBindingComponent::GetReplicaPriority() const
{
if (m_chunk && m_chunk->GetReplica())
{
return m_chunk->GetReplica()->GetPriority();
}
else
{
AZ_Error("NetBindingComponent",false,"Trying to gather ReplicaPriority without having a Replica.");
return GridMate::k_replicaPriorityLowest;
}
}
bool NetBindingComponent::IsLevelSliceEntity() const
{
return m_isLevelSliceEntity;
}
} // namespace AzFramework
@@ -1,85 +0,0 @@
/*
* 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.
*
*/
#ifndef AZFRAMEWORK_NET_BINDING_COMPONENT_H
#define AZFRAMEWORK_NET_BINDING_COMPONENT_H
#include <AzCore/Component/Component.h>
#include <AzFramework/Network/NetBindingHandlerBus.h>
namespace AzFramework
{
/**
* NetBindingComponent enables network synchronization for the entity.
* It works in conjunction with NetBindingComponentChunk and NetBindingSystemComponent
* to perform network binding and notifies other components on the entity to bind
* their ReplicaChunks via the NetBindable interface.
*
* Entities bound to proxy replicas will be automatically destroyed when they are
* unbound from the network.
*/
class NetBindingComponent
: public AZ::Component
, public NetBindingHandlerBus::Handler
{
friend class NetBindingComponentChunk;
public:
AZ_COMPONENT(NetBindingComponent, "{E9CA5D63-ED2D-4B59-B3C4-EBCD4A0013E4}", NetBindingHandlerInterface);
NetBindingComponent();
protected:
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("ReplicaChunkService", 0xf86b88a8));
}
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("ReplicaChunkService", 0xf86b88a8));
}
///////////////////////////////////////////////////////////////////////
// AZ::Component
static void Reflect(AZ::ReflectContext* reflection);
void Activate() override;
void Deactivate() override;
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// NetBindingHandlerBus::Handler
void BindToNetwork(GridMate::ReplicaPtr bindTo) override;
void UnbindFromNetwork() override;
bool IsEntityBoundToNetwork() override;
bool IsEntityAuthoritative() override;
void MarkAsLevelSliceEntity() override;
void SetSliceInstanceId(const AZ::SliceComponent::SliceInstanceId& sliceInstanceId) override;
void RequestEntityChangeOwnership(GridMate::PeerId peerId = GridMate::InvalidReplicaPeerId) override;
void SetReplicaPriority(GridMate::ReplicaPriority replicaPriority) override;
GridMate::ReplicaPriority GetReplicaPriority() const override;
///////////////////////////////////////////////////////////////////////
//! Returns if the entity belongs to the level slice for binding purposes.
bool IsLevelSliceEntity() const;
//! Points to the NetBindingComponentChunk counterpart.
GridMate::ReplicaChunkPtr m_chunk;
bool m_isLevelSliceEntity;
AZ::SliceComponent::SliceInstanceId m_sliceInstanceId;
};
} // namespace AzFramework
#endif // AZFRAMEWORK_NET_BINDING_COMPONENT_H
#pragma once
@@ -1,254 +0,0 @@
/*
* 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 <AzFramework/Network/NetBindingComponentChunk.h>
#include <AzFramework/Network/NetBindingComponent.h>
#include <AzFramework/Network/NetBindingSystemBus.h>
#include <AzFramework/Network/NetBindingEventsBus.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/Slice/SliceEntityBus.h>
#include <GridMate/Serialize/Buffer.h>
#include <GridMate/Serialize/UuidMarshal.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Slice/SliceComponent.h>
#include <AzCore/Serialization/ObjectStream.h>
#include <AzCore/IO/ByteContainerStream.h>
namespace AzFramework
{
NetBindingComponentChunk::SpawnInfo::SpawnInfo()
: m_runtimeEntityId(AZ::EntityId::InvalidEntityId)
, m_owningContextId(UnspecifiedNetBindingContextSequence)
, m_staticEntityId(AZ::EntityId::InvalidEntityId)
, m_sliceInstanceId(UnspecifiedSliceInstanceId)
, m_sliceAssetId(UnspecifiedSliceInstanceId, 0)
{
}
bool NetBindingComponentChunk::SpawnInfo::operator==(const SpawnInfo& rhs)
{
return m_owningContextId == rhs.m_owningContextId
&& m_runtimeEntityId == rhs.m_runtimeEntityId
&& m_staticEntityId == rhs.m_staticEntityId
&& m_serializedState == rhs.m_serializedState
&& m_sliceAssetId == rhs.m_sliceAssetId;
}
bool NetBindingComponentChunk::SpawnInfo::ContainsSerializedState() const
{
return !m_serializedState.empty();
}
void NetBindingComponentChunk::SpawnInfo::Marshaler::Marshal(GridMate::WriteBuffer& wb, const SpawnInfo& data)
{
wb.Write(data.m_owningContextId, GridMate::VlqU32Marshaler());
wb.Write(data.m_runtimeEntityId);
bool useSerializedState = data.ContainsSerializedState();
wb.Write(useSerializedState);
if (useSerializedState)
{
wb.Write(data.m_serializedState);
}
else
{
wb.Write(data.m_sliceAssetId);
wb.Write(data.m_staticEntityId);
wb.Write(data.m_sliceInstanceId);
}
}
void NetBindingComponentChunk::SpawnInfo::Marshaler::Unmarshal(SpawnInfo& data, GridMate::ReadBuffer& rb)
{
rb.Read(data.m_owningContextId, GridMate::VlqU32Marshaler());
rb.Read(data.m_runtimeEntityId);
bool hasSerializedState = false;
rb.Read(hasSerializedState);
if (hasSerializedState)
{
rb.Read(data.m_serializedState);
}
else
{
rb.Read(data.m_sliceAssetId);
rb.Read(data.m_staticEntityId);
rb.Read(data.m_sliceInstanceId);
}
}
NetBindingComponentChunk::NetBindingComponentChunk()
: m_bindingComponent(nullptr)
, m_spawnInfo("SpawnInfo")
, m_bindMap("ComponentBindMap")
{
m_spawnInfo.SetMaxIdleTime(0.f);
m_bindMap.SetMaxIdleTime(0.f);
}
void NetBindingComponentChunk::OnReplicaActivate(const GridMate::ReplicaContext& rc)
{
(void)rc;
if (IsMaster())
{
// Get and store entity spawn data
AZ_Assert(m_bindingComponent, "Entity binding is invalid!");
m_spawnInfo.Modify([&](SpawnInfo& spawnInfo)
{
spawnInfo.m_runtimeEntityId = static_cast<AZ::u64>(m_bindingComponent->GetEntity()->GetId());
bool isProceduralEntity = true;
AZ::SliceComponent::SliceInstanceAddress sliceInfo;
EntityContextId contextId = EntityContextId::CreateNull();
const AZ::EntityId bindingComponentEntityId = m_bindingComponent->GetEntityId();
EntityIdContextQueryBus::EventResult(contextId, bindingComponentEntityId,
&EntityIdContextQueryBus::Events::GetOwningContextId);
if (!contextId.IsNull())
{
EBUS_EVENT_RESULT(spawnInfo.m_owningContextId, NetBindingSystemBus, GetCurrentContextSequence);
SliceEntityRequestBus::EventResult(sliceInfo, bindingComponentEntityId,
&SliceEntityRequestBus::Events::GetOwningSlice);
bool isDynamicSliceEntity = sliceInfo.IsValid();
isProceduralEntity = !m_bindingComponent->IsLevelSliceEntity() && !isDynamicSliceEntity;
}
if (isProceduralEntity)
{
// write cloning info
AZ::SerializeContext* sc = nullptr;
EBUS_EVENT_RESULT(sc, AZ::ComponentApplicationBus, GetSerializeContext);
AZ_Assert(sc, "Can't find SerializeContext!");
AZ::IO::ByteContainerStream<AZStd::vector<AZ::u8>> spawnDataStream(&spawnInfo.m_serializedState);
AZ::ObjectStream* objStream = AZ::ObjectStream::Create(&spawnDataStream, *sc, AZ::DataStream::ST_BINARY);
objStream->WriteClass(m_bindingComponent->GetEntity());
objStream->Finalize();
}
else
{
// write slice info
if (sliceInfo.IsValid())
{
AZ::Data::AssetId sliceAssetId = sliceInfo.GetReference()->GetSliceAsset().GetId();
spawnInfo.m_sliceAssetId = AZStd::make_pair(sliceAssetId.m_guid, sliceAssetId.m_subId);
}
if (sliceInfo.GetInstance())
{
spawnInfo.m_sliceInstanceId = sliceInfo.GetInstance()->GetId();
}
AZ::EntityId staticEntityId;
EBUS_EVENT_RESULT(staticEntityId, NetBindingSystemBus, GetStaticIdFromEntityId, m_bindingComponent->GetEntity()->GetId());
spawnInfo.m_staticEntityId = static_cast<AZ::u64>(staticEntityId);
}
return true;
});
}
else
{
AZ::EntityId runtimeEntityId(m_spawnInfo.Get().m_runtimeEntityId);
NetBindingContextSequence owningContextId = m_spawnInfo.Get().m_owningContextId;
//TODO Move to Filter Hook
// Reject and cancel sessions with duplicate MachineIds?
// Reject and cancel sessions with duplicate entity ID creation requests?
//Check MachineId collision
bool collision = AZ::Entity::GetProcessSignature() == (m_spawnInfo.Get().m_runtimeEntityId & 0xFFFFFFFF);
AZ_Error("GridMate", !collision, "Replica received with duplicate Entity Machine IDs. Ignoring");
if (!collision)
{
//Check EntityID collision
AZ::Entity* entity = nullptr;
EBUS_EVENT_RESULT(entity, AZ::ComponentApplicationBus, FindEntity, runtimeEntityId);
/*
* Only false if no machine ID collision and no entity ID collision
* And the entity is already active, it's possible the entity already exists in deactivated state as a cache mechanism
*/
collision = (entity != nullptr) && (entity->GetState() == AZ::Entity::State::Active);
}
/**
* Special case - static entities should not count as duplicates.
* Static entities are loaded with the level and will be bounded here.
*/
if (collision)
{
AZ::EntityId staticEntityId;
EBUS_EVENT_RESULT(staticEntityId, NetBindingSystemBus, GetStaticIdFromEntityId, runtimeEntityId);
if (staticEntityId == runtimeEntityId)
{
collision = false;
}
}
if (!collision) //Ignore duplicate runtime entity IDs
{
if (m_spawnInfo.Get().ContainsSerializedState())
{
// Spawn the entity from stream input data
AZ::IO::MemoryStream spawnData(m_spawnInfo.Get().m_serializedState.data(), m_spawnInfo.Get().m_serializedState.size());
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromStream, spawnData, runtimeEntityId, GetReplicaId(), owningContextId);
}
else
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = owningContextId;
spawnContext.m_sliceAssetId = AZ::Data::AssetId(m_spawnInfo.Get().m_sliceAssetId.first, m_spawnInfo.Get().m_sliceAssetId.second);
spawnContext.m_runtimeEntityId = runtimeEntityId;
spawnContext.m_staticEntityId = AZ::EntityId(m_spawnInfo.Get().m_staticEntityId);
spawnContext.m_sliceInstanceId = m_spawnInfo.Get().m_sliceInstanceId;
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, GetReplicaId(), spawnContext);
}
}
else //Fail early to prevent unnecessary spawning of duplicate entity IDs
{
//Misconfiguration or potential cheating/DoS?
AZ_Warning("NetBinding", false, "Received duplicate Entity ID %llu. Ignoring.", runtimeEntityId);
}
}
}
void NetBindingComponentChunk::OnReplicaDeactivate(const GridMate::ReplicaContext& rc)
{
(void)rc;
if (m_bindingComponent)
{
m_bindingComponent->UnbindFromNetwork();
}
}
bool NetBindingComponentChunk::AcceptChangeOwnership(GridMate::PeerId requestor, const GridMate::ReplicaContext& rc)
{
bool result = true;
if (m_bindingComponent)
{
EBUS_EVENT_ID_RESULT(result, m_bindingComponent->GetEntityId(), NetBindingEventsBus, OnEntityAcceptChangeOwnership, requestor, rc);
}
return result;
}
void NetBindingComponentChunk::OnReplicaChangeOwnership(const GridMate::ReplicaContext& rc)
{
if (m_bindingComponent)
{
EBUS_EVENT_ID(m_bindingComponent->GetEntityId(), NetBindingEventsBus, OnEntityChangeOwnership, rc);
}
}
} // namespace AzFramework
@@ -1,112 +0,0 @@
/*
* 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.
*
*/
#ifndef AZFRAMEWORK_NET_BINDING_COMPONENT_CHUNK_H
#define AZFRAMEWORK_NET_BINDING_COMPONENT_CHUNK_H
#include <AzCore/Component/ComponentBus.h>
#include <AzFramework/Network/NetBindingSystemBus.h>
#include <GridMate/Replica/ReplicaChunk.h>
#include <GridMate/Serialize/ContainerMarshal.h>
#include <GridMate/Serialize/DataMarshal.h>
#include <GridMate/Serialize/CompressionMarshal.h>
#include <AzFramework/Network/NetBindingSystemImpl.h>
namespace AzFramework
{
class NetBindingComponent;
class NetBindingComponentChunkDescriptor;
/**
* NetBindingComponentChunk is the counterpart of NetBindingComponent on the network side.
* It contains entity spawn data. It is created by NetBindingComponent during network
* binding on the master and initiates entity creation and binding on the proxy side.
*/
class NetBindingComponentChunk
: public GridMate::ReplicaChunk
{
friend NetBindingComponent;
friend NetBindingComponentChunkDescriptor;
public:
AZ_CLASS_ALLOCATOR(NetBindingComponentChunk, AZ::SystemAllocator, 0);
static const char* GetChunkName() { return "NetBindingComponentChunk"; }
NetBindingComponentChunk();
void SetBinding(NetBindingComponent* bindingComponent) { m_bindingComponent = bindingComponent; }
NetBindingComponent* GetBinding() const { return m_bindingComponent; }
protected:
///////////////////////////////////////////////////////////////////////
// ReplicaChunk
bool IsReplicaMigratable() override { return true; }
void OnReplicaActivate(const GridMate::ReplicaContext& rc) override;
void OnReplicaDeactivate(const GridMate::ReplicaContext& rc) override;
bool AcceptChangeOwnership(GridMate::PeerId requestor, const GridMate::ReplicaContext& rc) override;
void OnReplicaChangeOwnership(const GridMate::ReplicaContext& rc) override;
///////////////////////////////////////////////////////////////////////
NetBindingComponent* m_bindingComponent;
class SpawnInfo
{
public:
class Marshaler
{
public:
void Marshal(GridMate::WriteBuffer& wb, const SpawnInfo& data);
void Unmarshal(SpawnInfo& data, GridMate::ReadBuffer& rb);
};
class Throttle
{
public:
//! Always return true because SpawnInfo never changes
bool WithinThreshold(const SpawnInfo&) const { return true; }
void UpdateBaseline(const SpawnInfo& baseline) { (void)baseline; }
};
SpawnInfo();
bool operator==(const SpawnInfo& rhs);
bool ContainsSerializedState() const;
/**
* \brief Same as m_staticEntityId on authoritative entity with master replica
*/
AZ::u64 m_runtimeEntityId;
NetBindingContextSequence m_owningContextId;
AZStd::vector<AZ::u8> m_serializedState;
/**
* \brief EntityId of authoritative entity with master replica
*/
AZ::u64 m_staticEntityId;
AZStd::pair<AZ::Uuid, AZ::u32> m_sliceAssetId;
/**
* \brief uniquely identifies the slice instance that this entity is being replicated from
*/
AZ::SliceComponent::SliceInstanceId m_sliceInstanceId;
};
GridMate::DataSet<SpawnInfo, SpawnInfo::Marshaler, SpawnInfo::Throttle> m_spawnInfo;
GridMate::DataSet<AZStd::vector<AZ::ComponentId> > m_bindMap;
};
typedef AZStd::intrusive_ptr<NetBindingComponentChunk> NetBindingComponentChunkPtr;
} // namespace AZ
#endif // AZFRAMEWORK_NET_BINDING_COMPONENT_CHUNK_H
#pragma once
@@ -1,51 +0,0 @@
/*
* 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.
*
*/
#ifndef AZFRAMEWORK_NET_BINDING_EVENTS_BUS_H
#define AZFRAMEWORK_NET_BINDING_EVENTS_BUS_H
#include <AzCore/EBus/EBus.h>
#include <AzCore/Component/EntityId.h>
#include <GridMate/Replica/ReplicaCommon.h>
namespace AzFramework
{
/**
* NetBindingEventsBus
* Throws networking related entity events
*/
class NetBindingEvents
: public AZ::EBusTraits
{
public:
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
typedef AZ::EntityId BusIdType;
virtual ~NetBindingEvents() {}
/**
* Called on authoritative(Master) entity when ownership of this entity is about to be transferred to another peer
* Returning false from this call will result in denying request for ownership transfer
*/
virtual bool OnEntityAcceptChangeOwnership(GridMate::PeerId requestor, const GridMate::ReplicaContext& rc) { (void)requestor; (void)rc; return true; }
/**
* Called when ownership transfer of an entity is finished.
*/
virtual void OnEntityChangeOwnership(const GridMate::ReplicaContext& rc) { (void)rc; }
};
typedef AZ::EBus<NetBindingEvents> NetBindingEventsBus;
} // namespace AzFramework
#endif // AZFRAMEWORK_NET_BINDING_EVENTS_BUS_H
#pragma once
@@ -1,112 +0,0 @@
/*
* 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.
*
*/
#ifndef AZFRAMEWORK_NET_BINDING_HANDLER_BUS_H
#define AZFRAMEWORK_NET_BINDING_HANDLER_BUS_H
#include <AzCore/EBus/EBus.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/std/parallel/mutex.h>
#include <GridMate/Replica/ReplicaCommon.h>
#include <AzCore/Slice/SliceComponent.h>
namespace AzFramework
{
/**
* The NetBindingSystemComponent notifies net binding handlers of binding events on this bus.
* The net binding component implements this interface and listens on the NetBindingHandlerBus.
*/
class NetBindingHandlerInterface
: public AZ::EBusTraits
{
public:
AZ_RTTI(NetBindingHandlerInterface, "{9F84E9FE-81A0-4105-9C51-6C42C83FECAF}");
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
typedef AZ::EntityId BusIdType;
virtual ~NetBindingHandlerInterface() {}
/**
* Called to let the entity know that it should bind to the network.
* If bindTo is set, it means that the entity is a proxy and the handler
* should bind the entity to the specified
* replica, otherwise it should bind to a new replica and add it via
* NetBindingSystemBus::AddReplicaMaster.
*/
virtual void BindToNetwork(GridMate::ReplicaPtr bindTo) = 0;
/**
* Called to let the entity know that it should unbind from the network.
*/
virtual void UnbindFromNetwork() = 0;
/**
* Returns true if the entity is bound to the network.
*/
virtual bool IsEntityBoundToNetwork() = 0;
/**
* Returns true if the entity is authoritative on the local node.
*/
virtual bool IsEntityAuthoritative() = 0;
/**
* Flags the entity as part of the level slice.
*/
virtual void MarkAsLevelSliceEntity() = 0;
/**
* Set the slice instance id that this entity was spawned by and belongs to.
*/
virtual void SetSliceInstanceId(const AZ::SliceComponent::SliceInstanceId& sliceInstanceId) = 0;
/**
* Sets the Replica Priority
*/
virtual void SetReplicaPriority(GridMate::ReplicaPriority replicaPriority) = 0;
/**
* Request entity ownership to a given peer (by default to local peer)
*/
virtual void RequestEntityChangeOwnership(GridMate::PeerId peerId = GridMate::InvalidReplicaPeerId) = 0;
/**
* Gets the Replica Priority
*/
virtual GridMate::ReplicaPriority GetReplicaPriority() const = 0;
};
typedef AZ::EBus<NetBindingHandlerInterface> NetBindingHandlerBus;
/**
* Set of queries that might want to be made about the networking system
* mainly wraps up EBus calls to keep the implementing code a bit more readable
*/
class NetQuery
{
public:
AZ_RTTI(NetQuery, "{AA4C5699-889D-4A73-9AD2-53EB03D8BB99}");
virtual ~NetQuery() = default;
static AZ_FORCE_INLINE bool IsEntityAuthoritative(AZ::EntityId entityId)
{
bool result = true;
EBUS_EVENT_ID_RESULT(result,entityId,NetBindingHandlerBus,IsEntityAuthoritative);
return result;
}
};
} // namespace AzFramework
#endif // AZFRAMEWORK_NET_BINDING_HANDLER_BUS_H
#pragma once
@@ -1,119 +0,0 @@
/*
* 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.
*
*/
#pragma once
#ifndef AZFRAMEWORK_NET_BINDING_SYSTEM_BUS_H
#define AZFRAMEWORK_NET_BINDING_SYSTEM_BUS_H
#include <AzCore/EBus/EBus.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/Asset/AssetCommon.h>
#include <GridMate/Replica/ReplicaCommon.h>
#include <GridMate/Session/Session.h>
#include <AzCore/Slice/SliceComponent.h>
namespace AZ
{
namespace IO
{
class GenericStream;
}
}
namespace AzFramework
{
const AZ::SliceComponent::SliceInstanceId UnspecifiedSliceInstanceId = AZ::Uuid::CreateNull();
/**
*/
typedef AZ::u32 NetBindingContextSequence;
const NetBindingContextSequence UnspecifiedNetBindingContextSequence = 0;
/**
*/
struct NetBindingSliceContext
{
NetBindingContextSequence m_contextSequence;
AZ::Data::AssetId m_sliceAssetId;
AZ::EntityId m_staticEntityId;
AZ::EntityId m_runtimeEntityId;
/**
* \brief uniquely identifies the slice instance that this entity is being replicated from
*/
AZ::SliceComponent::SliceInstanceId m_sliceInstanceId;
};
/**
* The net binding system implements this interface and listens on the NetBindingSystemBus.
*
* Network binding is activated when OnNetworkSessionActivated event is received with the binding session,
* and is deactivated by the OnNetworkSessionDeactivated event.
*/
class NetBindingSystemInterface
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
virtual ~NetBindingSystemInterface() {}
//! Returns true if a network session is available and entities should bind themselves to the network.
virtual bool ShouldBindToNetwork() = 0;
//! Returns the current entity context sequence
virtual NetBindingContextSequence GetCurrentContextSequence() = 0;
//! Get a level entity's static id.
virtual AZ::EntityId GetStaticIdFromEntityId(AZ::EntityId entity) = 0;
//! Get a level entity's id based on the static id
virtual AZ::EntityId GetEntityIdFromStaticId(AZ::EntityId staticEntityId) = 0;
//! Adds a bound replica to the network session as master.
virtual void AddReplicaMaster(AZ::Entity* entity, GridMate::ReplicaPtr replica) = 0;
//! Spawn and bind an entity from a slice
virtual void SpawnEntityFromSlice(GridMate::ReplicaId bindTo, const NetBindingSliceContext& bindToContext) = 0;
//! Spawn and bind an entity from stream
virtual void SpawnEntityFromStream(AZ::IO::GenericStream& spawnData, AZ::EntityId useEntityId, GridMate::ReplicaId bindTo, NetBindingContextSequence addToContext) = 0;
//! De-spawn an entity: deactivates or removes the entity.
/**
* /note @sliceInstanceId is the slice instance that the entity belongs to. If it's a level entity, then this should be AZ::Uuid::CreateNull()
*/
virtual void UnbindGameEntity(AZ::EntityId entity, const AZ::SliceComponent::SliceInstanceId& sliceInstanceId) = 0;
};
typedef AZ::EBus<NetBindingSystemInterface> NetBindingSystemBus;
class NetBindingSystemEvents
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//! Notification that a network session is created
virtual void OnNetworkSessionCreated(GridMate::GridSession* session) { (void)session; }
//! Notification that a network session is ready
virtual void OnNetworkSessionActivated(GridMate::GridSession* session) { (void)session; }
//! Notification that a network session is no longer available
virtual void OnNetworkSessionDeactivated(GridMate::GridSession* session) { (void)session; }
};
typedef AZ::EBus<NetBindingSystemEvents> NetBindingSystemEventsBus;
} // namespace AzFramework
#endif // AZFRAMEWORK_NET_BINDING_SYSTEM_BUS_H
@@ -1,66 +0,0 @@
/*
* 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 <AzCore/Serialization/EditContext.h>
#include <AzFramework/Network/NetBindingSystemComponent.h>
namespace AzFramework
{
NetBindingSystemComponent::NetBindingSystemComponent()
{
}
NetBindingSystemComponent::~NetBindingSystemComponent()
{
}
void NetBindingSystemComponent::Activate()
{
NetBindingSystemImpl::Init();
}
void NetBindingSystemComponent::Deactivate()
{
NetBindingSystemImpl::Shutdown();
}
void NetBindingSystemComponent::Reflect(AZ::ReflectContext* context)
{
NetBindingSystemImpl::Reflect(context);
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<NetBindingSystemComponent, AZ::Component>()
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<NetBindingSystemComponent>(
"NetBinding System", "Performs network binding for game entities.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Engine")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
;
}
}
}
void NetBindingSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("NetBindingSystemService", 0xa0ad6656));
}
void NetBindingSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("NetBindingSystemService", 0xa0ad6656));
}
} // namespace AzFramework
@@ -1,53 +0,0 @@
/*
* 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.
*
*/
#ifndef AZFRAMEWORK_NET_BINDING_SYSTEM_COMPONENT_H
#define AZFRAMEWORK_NET_BINDING_SYSTEM_COMPONENT_H
#include <AzFramework/Network/NetBindingSystemImpl.h>
#include <AzCore/Component/Component.h>
namespace AZ
{
class ReflectContext;
}
namespace AzFramework
{
/**
* NetBindingSystemComponent exposes NetBindingSystemImpl as a component
*/
class NetBindingSystemComponent
: public AZ::Component
, public NetBindingSystemImpl
{
friend class NetBindingSystemContextData;
public:
AZ_COMPONENT(NetBindingSystemComponent, "{B96548CC-0866-4BB3-A87B-BF0C4F69E8AC}");
NetBindingSystemComponent();
~NetBindingSystemComponent() override;
//////////////////////////////////////////////////////////////////////////
// Component overrides
void Activate() override;
void Deactivate() override;
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
//////////////////////////////////////////////////////////////////////////
};
} // namespace AzFramework
#endif // AZFRAMEWORK_NET_BINDING_SYSTEM_COMPONENT_H
#pragma once
@@ -1,957 +0,0 @@
/*
* 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 <AzFramework/Network/NetBindingSystemImpl.h>
#include <AzFramework/Network/NetBindingComponent.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Entity/SliceGameEntityOwnershipServiceBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Serialization/ObjectStream.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Slice/SliceAsset.h>
#include <GridMate/Replica/Replica.h>
#include <GridMate/Replica/ReplicaChunk.h>
#include <GridMate/Replica/ReplicaChunkDescriptor.h>
#include <GridMate/Replica/ReplicaFunctions.h>
//#define Extra_Tracing
#undef Extra_Tracing
#if defined(Extra_Tracing)
#include <AzCore/Debug/Timer.h>
#define AZ_ExtraTracePrintf(window, ...) AZ::Debug::Trace::Instance().Printf(window, __VA_ARGS__);
#else
#define AZ_ExtraTracePrintf(window, ...)
#endif
namespace AzFramework
{
const AZStd::chrono::milliseconds NetBindingSystemImpl::s_sliceBindingTimeout = AZStd::chrono::milliseconds(5000);
namespace
{
NetBindingHandlerInterface* GetNetBindingHandler(AZ::Entity* entity)
{
NetBindingHandlerInterface* handler = nullptr;
for (AZ::Component* component : entity->GetComponents())
{
handler = azrtti_cast<NetBindingHandlerInterface*>(component);
if (handler)
{
break;
}
}
return handler;
}
}
NetBindingSliceInstantiationHandler::~NetBindingSliceInstantiationHandler()
{
// m_bindRequests in NetBindingSystemImpl could be cleaned before slice instantiation finished
if (m_state == State::Spawning)
{
AzFramework::SliceInstantiationResultBus::Handler::BusDisconnect();
SliceGameEntityOwnershipServiceRequestBus::Broadcast(
&SliceGameEntityOwnershipServiceRequests::CancelDynamicSliceInstantiation, m_ticket
);
}
for (AZ::Entity* entity : m_boundEntities)
{
AZ_ExtraTracePrintf("NetBindingSystemImpl", "Cleanup - deleting %llu\n", entity->GetId());
EBUS_EVENT(GameEntityContextRequestBus, DestroyGameEntity, entity->GetId());
}
}
void NetBindingSliceInstantiationHandler::InstantiateEntities()
{
if (m_sliceAssetId.IsValid())
{
AZ_ExtraTracePrintf("NetBindingSystemImpl", "InstantiateEntities sliceid %s\n",
m_sliceInstanceId.ToString<AZStd::string>(false, false).c_str());
if (AZ::Data::AssetManager::IsReady())
{
auto remapFunc = [bindingQueue=m_bindingQueue](AZ::EntityId originalId, bool /*isEntityId*/, const AZStd::function<AZ::EntityId()>&) -> AZ::EntityId
{
auto iter = bindingQueue.find(originalId);
if (iter != bindingQueue.end())
{
return iter->second.m_desiredRuntimeEntityId;
}
return AZ::Entity::MakeId();
};
AZ::Data::Asset<AZ::Data::AssetData> asset = AZ::Data::AssetManager::Instance().FindOrCreateAsset<AZ::DynamicSliceAsset>(m_sliceAssetId, AZ::Data::AssetLoadBehavior::Default);
SliceGameEntityOwnershipServiceRequestBus::BroadcastResult(m_ticket,
&SliceGameEntityOwnershipServiceRequests::InstantiateDynamicSlice, asset, AZ::Transform::Identity(), remapFunc);
SliceInstantiationResultBus::Handler::BusConnect(m_ticket);
m_state = State::Spawning;
}
else
{
AZ_Warning("NetBindingSystemImpl", false, "AssetManager was not ready when attempting to instantiate sliceid %s\n",
m_sliceInstanceId.ToString<AZStd::string>(false, false).c_str());
InstantiationFailureCleanup();
}
}
}
bool NetBindingSliceInstantiationHandler::IsInstantiated() const
{
return m_state == State::Spawned;
}
bool NetBindingSliceInstantiationHandler::IsANewSliceRequest() const
{
return m_state == State::NewRequest && m_sliceAssetId.IsValid() && !m_ticket.IsValid();
}
bool NetBindingSliceInstantiationHandler::IsBindingComplete() const
{
return !SliceInstantiationResultBus::Handler::BusIsConnected() && m_bindingQueue.empty();
}
bool NetBindingSliceInstantiationHandler::HasActiveEntities() const
{
for (const AZ::Entity* entity : m_boundEntities)
{
if (entity->GetState() == AZ::Entity::State::Active)
{
return true;
}
}
return false;
}
void NetBindingSliceInstantiationHandler::OnSlicePreInstantiate(const AZ::Data::AssetId& /*sliceAssetId*/, const AZ::SliceComponent::SliceInstanceAddress& sliceAddress)
{
const auto& entityMapping = sliceAddress.GetInstance()->GetEntityIdToBaseMap();
const AZ::SliceComponent::EntityList& sliceEntities = sliceAddress.GetInstance()->GetInstantiated()->m_entities;
for (AZ::Entity *sliceEntity : sliceEntities)
{
auto it = entityMapping.find(sliceEntity->GetId());
AZ_Assert(it != entityMapping.end(), "Failed to retrieve static entity id for a slice entity!");
const AZ::EntityId staticEntityId = it->second;
auto itBindRecord = m_bindingQueue.find(staticEntityId);
if (itBindRecord != m_bindingQueue.end())
{
AZ_Assert(GetNetBindingHandler(sliceEntity), "Slice entity matched the static id of replicated entity, but there is no valid NetBindingHandlerInterface on it!");
itBindRecord->second.m_actualRuntimeEntityId = sliceEntity->GetId();
}
else if (GetNetBindingHandler(sliceEntity))
{
AZ_ExtraTracePrintf("NetBindingSystemImpl", "OnSlicePreInstantiate late bindRequest, slice %s, staticid %llu, spawned %llu\n",
m_sliceInstanceId.ToString<AZStd::string>(false, false).c_str(),
static_cast<AZ::u64>(staticEntityId),
static_cast<AZ::u64>(sliceEntity->GetId()));
BindRequest& request = m_bindingQueue[staticEntityId];
request.m_desiredRuntimeEntityId = staticEntityId;
request.m_actualRuntimeEntityId = sliceEntity->GetId();
request.m_requestTime = m_bindTime;
request.m_state = BindRequest::State::PlaceholderBind;
}
sliceEntity->SetRuntimeActiveByDefault(false);
}
}
void NetBindingSliceInstantiationHandler::OnSliceInstantiated(const AZ::Data::AssetId& /*sliceAssetId*/, const AZ::SliceComponent::SliceInstanceAddress& sliceAddress)
{
SliceInstantiationResultBus::Handler::BusDisconnect();
CloseEntityMap(sliceAddress.GetInstance()->GetEntityIdMap());
const AZ::SliceComponent::EntityList sliceEntities = sliceAddress.GetInstance()->GetInstantiated()->m_entities;
for (AZ::Entity *sliceEntity : sliceEntities)
{
auto it = sliceAddress.GetInstance()->GetEntityIdToBaseMap().find(sliceEntity->GetId());
AZ_Assert(it != sliceAddress.GetInstance()->GetEntityIdToBaseMap().end(), "Failed to retrieve static entity id for a slice entity!");
const AZ::EntityId staticEntityId = it->second;
const auto itUnbound = m_bindingQueue.find(staticEntityId);
if (itUnbound == m_bindingQueue.end())
{
/*
* Remove entities that aren't meant to be net bounded.
*/
if (!GetNetBindingHandler(sliceEntity))
{
EBUS_EVENT(GameEntityContextRequestBus, DestroyGameEntity, sliceEntity->GetId());
continue;
}
}
AZ_ExtraTracePrintf("NetBindingSystemImpl", "Adding %llu \n", sliceEntity->GetId());
m_boundEntities.push_back(sliceEntity);
}
m_state = State::Spawned;
}
void NetBindingSliceInstantiationHandler::OnSliceInstantiationFailed(const AZ::Data::AssetId& sliceAssetId)
{
SliceInstantiationResultBus::Handler::BusDisconnect();
AZ_UNUSED(sliceAssetId);
AZ_TracePrintf("NetBindingSystemImpl", "Failed to instantiate a slice %s!", sliceAssetId.ToString<AZStd::string>().c_str());
InstantiationFailureCleanup();
}
void NetBindingSliceInstantiationHandler::InstantiationFailureCleanup()
{
m_boundEntities.clear();
m_bindingQueue.clear();
// With m_bindingQueue empty, this slice instance handler will be removed on the next tick of NetBindingSystemImpl
m_state = State::Failed;
}
void NetBindingSliceInstantiationHandler::UseCacheFor(BindRequest& request, const AZ::EntityId& staticEntityId)
{
AZ_Warning("NetBindingSystemImpl", !m_staticToRuntimeEntityMap.empty(), "An empty slice, really? static %llu",
static_cast<AZ::u64>(staticEntityId));
const auto actualRuntimeIter = m_staticToRuntimeEntityMap.find(staticEntityId);
if (actualRuntimeIter == m_staticToRuntimeEntityMap.end())
{
AZ_Warning("NetBindingSystemImpl", false, "Wrong mapping, expected cache to have entity %llu for slice %s \n",
static_cast<AZ::u64>(staticEntityId),
m_sliceInstanceId.ToString<AZStd::string>(false, false).c_str());
#if defined(Extra_Tracing)
for (auto& item: m_staticToRuntimeEntityMap)
{
AZ_UNUSED(item);
AZ_ExtraTracePrintf("NetBindingSystemImpl", "mapping had %llu to %llu \n",
static_cast<AZ::u64>(item.first),
static_cast<AZ::u64>(item.second));
}
#endif
return;
}
const AZ::EntityId actualRuntimeEntityId = actualRuntimeIter->second;
const auto itCache = AZStd::find_if(m_boundEntities.begin(), m_boundEntities.end(), [&actualRuntimeEntityId](AZ::Entity* entity) {
return entity->GetId() == actualRuntimeEntityId;
});
if (itCache != m_boundEntities.end())
{
AZ_ExtraTracePrintf("NetBindingSystemImpl", "OnSlicePreInstantiate late bindRequest, slice %s, staticid %llu, spawned %llu\n",
m_sliceInstanceId.ToString<AZStd::string>(false, false).c_str(),
static_cast<AZ::u64>(staticEntityId),
static_cast<AZ::u64>(actualRuntimeEntityId));
request.m_actualRuntimeEntityId = actualRuntimeEntityId;
request.m_desiredRuntimeEntityId = staticEntityId;
}
else
{
AZ_Warning("NetBindingSystemImpl", false, "Expected cache to have entity %llu for slice %s \n",
static_cast<AZ::u64>(request.m_desiredRuntimeEntityId),
m_sliceInstanceId.ToString<AZStd::string>(false, false).c_str());
}
}
void NetBindingSliceInstantiationHandler::CloseEntityMap(
const AZ::SliceComponent::EntityIdToEntityIdMap& staticToRuntimeMap)
{
m_staticToRuntimeEntityMap.clear();
for (auto& item : staticToRuntimeMap)
{
m_staticToRuntimeEntityMap[item.first] = item.second;
}
}
NetBindingSystemContextData::NetBindingSystemContextData()
: m_bindingContextSequence("BindingContextSequence", UnspecifiedNetBindingContextSequence)
{
}
void NetBindingSystemContextData::OnReplicaActivate(const GridMate::ReplicaContext& rc)
{
(void)rc;
NetBindingSystemImpl* system = static_cast<NetBindingSystemImpl*>(NetBindingSystemBus::FindFirstHandler());
AZ_Assert(system, "NetBindingSystemContextData requires a valid NetBindingSystemComponent to function!");
system->OnContextDataActivated(this);
}
void NetBindingSystemContextData::OnReplicaDeactivate(const GridMate::ReplicaContext& rc)
{
(void)rc;
NetBindingSystemImpl* system = static_cast<NetBindingSystemImpl*>(NetBindingSystemBus::FindFirstHandler());
if (system)
{
system->OnContextDataDeactivated(this);
}
}
NetBindingSystemImpl::NetBindingSystemImpl()
: m_bindingSession(nullptr)
, m_currentBindingContextSequence(UnspecifiedNetBindingContextSequence)
, m_isAuthoritativeRootSliceLoad(false)
, m_overrideRootSliceLoadAuthoritative(false)
{
}
NetBindingSystemImpl::~NetBindingSystemImpl()
{
}
void NetBindingSystemImpl::Init()
{
NetBindingSystemBus::Handler::BusConnect();
NetBindingSystemEventsBus::Handler::BusConnect();
// Start listening for game context events
EntityContextId gameContextId = EntityContextId::CreateNull();
EBUS_EVENT_RESULT(gameContextId, GameEntityContextRequestBus, GetGameEntityContextId);
if (!gameContextId.IsNull())
{
EntityContextEventBus::Handler::BusConnect(gameContextId);
}
}
void NetBindingSystemImpl::Shutdown()
{
EntityContextEventBus::Handler::BusDisconnect();
NetBindingSystemEventsBus::Handler::BusDisconnect();
NetBindingSystemBus::Handler::BusDisconnect();
m_contextData.reset();
}
bool NetBindingSystemImpl::ShouldBindToNetwork()
{
return m_contextData && m_contextData->ShouldBindToNetwork();
}
NetBindingContextSequence NetBindingSystemImpl::GetCurrentContextSequence()
{
return m_currentBindingContextSequence;
}
bool NetBindingSystemImpl::ReadyToAddReplica() const
{
return m_bindingSession && m_bindingSession->GetReplicaMgr();
}
void NetBindingSystemImpl::AddReplicaMaster(AZ::Entity* entity, GridMate::ReplicaPtr replica)
{
bool addReplica = ShouldBindToNetwork();
AZ_Assert(addReplica, "Entities shouldn't be binding to the network right now!");
if (addReplica)
{
if (ReadyToAddReplica())
{
m_bindingSession->GetReplicaMgr()->AddMaster(replica);
}
else
{
m_addMasterRequests.push_back(AZStd::make_pair(entity->GetId(), replica));
}
}
}
AZ::EntityId NetBindingSystemImpl::GetStaticIdFromEntityId(AZ::EntityId entityId)
{
AZ::EntityId staticId = entityId; // if no static id mapping is found, then the static id is the same as the runtime id
// If entity came from a slice, try to get the mapping from it
AZ::SliceComponent::SliceInstanceAddress sliceInfo;
SliceEntityRequestBus::EventResult(sliceInfo, entityId, &SliceEntityRequestBus::Events::GetOwningSlice);
AZ::SliceComponent::SliceInstance* sliceInstance = sliceInfo.GetInstance();
if (sliceInstance)
{
const auto it = sliceInstance->GetEntityIdToBaseMap().find(entityId);
if (it != sliceInstance->GetEntityIdToBaseMap().end())
{
staticId = it->second;
}
}
return staticId;
}
AZ::EntityId NetBindingSystemImpl::GetEntityIdFromStaticId(AZ::EntityId staticEntityId)
{
AZ::EntityId runtimeId = AZ::EntityId();
// if we can find an entity with the static id, then the static id is the same as the runtime id.
AZ::Entity* entity = nullptr;
EBUS_EVENT(AZ::ComponentApplicationBus, FindEntity, staticEntityId);
if (entity)
{
runtimeId = staticEntityId;
}
return runtimeId;
}
void NetBindingSystemImpl::SpawnEntityFromSlice(GridMate::ReplicaId bindTo, const NetBindingSliceContext& bindToContext)
{
auto& sliceQueue = m_bindRequests[bindToContext.m_contextSequence];
const bool slicePresent = sliceQueue.find(bindToContext.m_sliceInstanceId) != sliceQueue.end();
auto iterSliceRequest = sliceQueue.insert_key(bindToContext.m_sliceInstanceId);
NetBindingSliceInstantiationHandler& sliceHandler = iterSliceRequest.first->second;
sliceHandler.m_sliceAssetId = bindToContext.m_sliceAssetId;
sliceHandler.m_sliceInstanceId = bindToContext.m_sliceInstanceId;
BindRequest& request = sliceHandler.m_bindingQueue[bindToContext.m_staticEntityId];
if (!slicePresent)
{
request.m_state = BindRequest::State::FirstBindInSlice;
}
else
{
request.m_state = BindRequest::State::LateBind;
}
AZ_ExtraTracePrintf("NetBindingSystemImpl", "SpawnEntityFromSlice late, slice %s, static %llu, desired %llu, state %d \n",
bindToContext.m_sliceInstanceId.ToString<AZStd::string>(false, false).c_str(),
static_cast<AZ::u64>(bindToContext.m_staticEntityId),
static_cast<AZ::u64>(bindToContext.m_runtimeEntityId),
request.m_state);
sliceHandler.m_bindTime = Now();
request.m_bindTo = bindTo;
request.m_desiredRuntimeEntityId = bindToContext.m_runtimeEntityId;
request.m_requestTime = Now();
if (sliceHandler.IsInstantiated())
{
// The slice has been instantiated now, thus we have to use the cache to populated the request with the entity.
sliceHandler.UseCacheFor(request, bindToContext.m_staticEntityId);
}
}
void NetBindingSystemImpl::SpawnEntityFromStream(AZ::IO::GenericStream& spawnData, AZ::EntityId useEntityId, GridMate::ReplicaId bindTo, NetBindingContextSequence addToContext)
{
auto& requestQueue = m_spawnRequests[addToContext];
requestQueue.push_back();
SpawnRequest& request = requestQueue.back();
request.m_bindTo = bindTo;
request.m_useEntityId = useEntityId;
request.m_spawnDataBuffer.resize_no_construct(spawnData.GetLength());
spawnData.Read(request.m_spawnDataBuffer.size(), request.m_spawnDataBuffer.data());
}
void NetBindingSystemImpl::OnNetworkSessionActivated(GridMate::GridSession* session)
{
AZ_Assert(!m_bindingSession, "We already have an active session! Was the previous session deactivated?");
if (!m_bindingSession)
{
m_bindingSession = session;
if (m_bindingSession->IsHost())
{
GridMate::Replica* replica = CreateSystemReplica();
session->GetReplicaMgr()->AddMaster(replica);
}
}
}
void NetBindingSystemImpl::OnNetworkSessionDeactivated(GridMate::GridSession* session)
{
if (session == m_bindingSession)
{
m_bindingSession = nullptr;
}
}
void NetBindingSystemImpl::UnbindGameEntity(AZ::EntityId entityId, const AZ::SliceComponent::SliceInstanceId& sliceInstanceId)
{
if (!m_bindRequests.empty())
{
const auto itCurrentContextQueue = m_bindRequests.lower_bound(GetCurrentContextSequence());
if (itCurrentContextQueue != m_bindRequests.end())
{
if (itCurrentContextQueue->first == GetCurrentContextSequence())
{
const auto itSliceHandler = itCurrentContextQueue->second.find(sliceInstanceId);
if (itSliceHandler != itCurrentContextQueue->second.end())
{
NetBindingSliceInstantiationHandler& sliceHandler = itSliceHandler->second;
for (AZ::Entity* entity : sliceHandler.m_boundEntities)
{
if (entity->GetId() == entityId)
{
entity->Deactivate();
return;
}
}
// clean any relevant bind requests as well
const auto bindQueueItem = sliceHandler.m_bindingQueue.find(entityId);
if (bindQueueItem != sliceHandler.m_bindingQueue.end())
{
sliceHandler.m_bindingQueue.erase(bindQueueItem);
return;
}
}
}
}
}
AZ_ExtraTracePrintf("NetBindingSystemImpl", "Not in cache - deleting %llu \n", entityId);
EBUS_EVENT(GameEntityContextRequestBus, DestroyGameEntity, entityId);
}
void NetBindingSystemImpl::OnEntityContextReset()
{
const bool isContextOwner = m_contextData && m_contextData->IsMaster() && m_bindingSession && m_bindingSession->IsHost();
if (isContextOwner)
{
++m_currentBindingContextSequence;
NetBindingSystemContextData* context = static_cast<NetBindingSystemContextData*>(m_contextData.get());
context->m_bindingContextSequence.Set(m_currentBindingContextSequence);
}
}
bool NetBindingSystemImpl::IsAuthoritateLoad() const
{
if (m_overrideRootSliceLoadAuthoritative)
{
return m_isAuthoritativeRootSliceLoad;
}
return !m_bindingSession || m_bindingSession->IsHost();
}
void NetBindingSystemImpl::UpdateClock(float deltaTime)
{
m_currentTime += AZStd::chrono::milliseconds(aznumeric_cast<int>(deltaTime * AZStd::milli::den));
}
AZStd::chrono::system_clock::time_point NetBindingSystemImpl::Now() const
{
return m_currentTime;
}
void NetBindingSystemImpl::OnEntityContextLoadedFromStream(const AZ::SliceComponent::EntityList& contextEntities)
{
const bool isAuthoritativeLoad = IsAuthoritateLoad();
for (AZ::Entity* entity : contextEntities)
{
NetBindingHandlerInterface* netBinder = GetNetBindingHandler(entity);
if (netBinder)
{
netBinder->MarkAsLevelSliceEntity();
}
if (!isAuthoritativeLoad && netBinder)
{
entity->SetRuntimeActiveByDefault(false);
auto& slicesQueue = m_bindRequests[GetCurrentContextSequence()];
auto& sliceHandler = slicesQueue[UnspecifiedSliceInstanceId];
BindRequest& request = sliceHandler.m_bindingQueue[entity->GetId()];
request.m_actualRuntimeEntityId = entity->GetId();
request.m_requestTime = Now();
}
}
}
void NetBindingSystemImpl::OnTick(float deltaTime, AZ::ScriptTimePoint time)
{
AZ_UNUSED(time);
UpdateClock(deltaTime);
UpdateContextSequence();
#if defined(Extra_Tracing)
static AZ::Debug::Timer sTimer;
sTimer.Stamp();
#endif
ProcessBindRequests();
#if defined(Extra_Tracing)
const float seconds = sTimer.StampAndGetDeltaTimeInSeconds();
static float debugPeriod = 2.f;
static float accumulator = 0;
static float totalTimeTaken = 0;
static AZ::u32 totalTicks = 0;
accumulator += deltaTime;
totalTimeTaken += seconds;
totalTicks++;
if (accumulator >= debugPeriod)
{
AZ_ExtraTracePrintf("NetBindingSystemImpl", "ProcessBindRequests() took %f sec \n", totalTicks > 0 ? totalTimeTaken / totalTicks : 0);
accumulator -= debugPeriod;
totalTimeTaken = 0;
totalTicks = 0;
}
#endif
ProcessSpawnRequests();
}
int NetBindingSystemImpl::GetTickOrder()
{
return AZ::TICK_PLACEMENT + 1;
}
void NetBindingSystemImpl::UpdateContextSequence()
{
NetBindingSystemContextData* contextChunk = static_cast<NetBindingSystemContextData*>(m_contextData.get());
if (m_currentBindingContextSequence != contextChunk->m_bindingContextSequence.Get())
{
m_currentBindingContextSequence = contextChunk->m_bindingContextSequence.Get();
}
}
GridMate::Replica* NetBindingSystemImpl::CreateSystemReplica()
{
AZ_Assert(m_bindingSession->IsHost(), "CreateSystemReplica should only be called on the host!");
GridMate::Replica* replica = GridMate::Replica::CreateReplica("NetBindingSystem");
NetBindingSystemContextData* contextChunk = GridMate::CreateReplicaChunk<NetBindingSystemContextData>();
replica->AttachReplicaChunk(contextChunk);
return replica;
}
void NetBindingSystemImpl::OnContextDataActivated(GridMate::ReplicaChunkPtr contextData)
{
AZ_Assert(!m_contextData, "We already have our context!");
m_contextData = contextData;
// Make sure we always have the unspecified entry. This should also
// be the lower_bound in the map and assuming it is always there
// makes things simpler.
m_spawnRequests.insert(UnspecifiedNetBindingContextSequence);
m_bindRequests.insert(UnspecifiedNetBindingContextSequence);
if (contextData->IsMaster())
{
++m_currentBindingContextSequence;
static_cast<NetBindingSystemContextData*>(contextData.get())->m_bindingContextSequence.Set(m_currentBindingContextSequence);
}
else
{
UpdateContextSequence();
}
AZ::TickBus::Handler::BusConnect();
EBUS_EVENT(AzFramework::NetBindingHandlerBus, BindToNetwork, nullptr);
}
void NetBindingSystemImpl::OnContextDataDeactivated(GridMate::ReplicaChunkPtr contextData)
{
AZ_Assert(m_contextData == contextData, "This is not our context!");
m_contextData = nullptr;
AZ::TickBus::Handler::BusDisconnect();
m_spawnRequests.clear();
m_bindRequests.clear();
m_addMasterRequests.clear();
m_currentBindingContextSequence = UnspecifiedNetBindingContextSequence;
}
void NetBindingSystemImpl::ProcessSpawnRequests()
{
AZ::SerializeContext* serializeContext = nullptr;
EBUS_EVENT_RESULT(serializeContext, AZ::ComponentApplicationBus, GetSerializeContext);
AZ_Assert(serializeContext, "NetBindingSystemComponent requires a valid SerializeContext in order to spawn entities!");
const auto spawnFunc = [=](SpawnRequest& spawnData, AZ::EntityId useEntityId, bool addToContext)
{
AZ::Entity* proxyEntity = nullptr;
AZ::ObjectStream::ClassReadyCB readyCB([&](void* classPtr, const AZ::Uuid& classId, AZ::SerializeContext* sc)
{
(void)classId;
(void)sc;
proxyEntity = static_cast<AZ::Entity*>(classPtr);
});
AZ::IO::ByteContainerStream<AZStd::vector<AZ::u8> > stream(&spawnData.m_spawnDataBuffer);
AZ::ObjectStream::LoadBlocking(&stream, *serializeContext, readyCB);
AZ_Warning("NetBindingSystemImpl", proxyEntity, "Could not spawn entity from stream %llu", useEntityId);
if (proxyEntity)
{
proxyEntity->SetId(useEntityId);
if (!BindAndActivate(proxyEntity, spawnData.m_bindTo, addToContext, AZ::Uuid::CreateNull()))
{
AzFramework::EntityContextId contextId = AzFramework::EntityContextId::CreateNull();
AzFramework::EntityIdContextQueryBus::EventResult(
contextId, proxyEntity->GetId(), &AzFramework::EntityIdContextQueryBus::Events::GetOwningContextId);
if (contextId.IsNull())
{
delete proxyEntity;
}
else
{
GameEntityContextRequestBus::Broadcast(
&GameEntityContextRequestBus::Events::DestroyGameEntity, proxyEntity->GetId());
}
}
}
};
if (!m_spawnRequests.empty())
{
SpawnRequestContextContainerType::iterator itContextQueue = m_spawnRequests.lower_bound(UnspecifiedNetBindingContextSequence);
AZ_Assert(itContextQueue->first == UnspecifiedNetBindingContextSequence, "We should always have the unspecified (aka global entity) spawn queue!");//
// Process requests for global entities (not part of any context)
SpawnRequestContainerType& globalQueue = itContextQueue->second;
for (SpawnRequest& request : globalQueue)
{
spawnFunc(request, request.m_useEntityId, false);
}
globalQueue.clear();
if (GetCurrentContextSequence() != UnspecifiedNetBindingContextSequence)
{
++itContextQueue;
// Clear any obsolete requests (any contexts below the current context sequence)
SpawnRequestContextContainerType::iterator itCurrentContextQueue = m_spawnRequests.lower_bound(GetCurrentContextSequence());
if (itContextQueue != itCurrentContextQueue)
{
m_spawnRequests.erase(itContextQueue, itCurrentContextQueue);
}
// Spawn any entities for the current context
if (itCurrentContextQueue != m_spawnRequests.end())
{
if (itCurrentContextQueue->first == GetCurrentContextSequence())
{
for (SpawnRequest& request : itCurrentContextQueue->second)
{
spawnFunc(request, request.m_useEntityId, true);
}
itCurrentContextQueue->second.clear();
}
}
}
}
}
void NetBindingSystemImpl::ProcessBindRequests()
{
AZ::SerializeContext* serializeContext = nullptr;
EBUS_EVENT_RESULT(serializeContext, AZ::ComponentApplicationBus, GetSerializeContext);
AZ_Assert(serializeContext, "NetBindingSystemComponent requires a valid SerializeContext in order to spawn entities!");
if (!m_bindRequests.empty())
{
BindRequestContextContainerType::iterator itContextQueue = m_bindRequests.lower_bound(UnspecifiedNetBindingContextSequence);
AZ_Assert(itContextQueue->first == UnspecifiedNetBindingContextSequence, "We should always have the unspecified/global spawn queue!");
if (GetCurrentContextSequence() != UnspecifiedNetBindingContextSequence)
{
++itContextQueue;
// Clear any obsolete requests (any contexts below the current context sequence)
BindRequestContextContainerType::iterator itCurrentContextQueue = m_bindRequests.lower_bound(GetCurrentContextSequence());
if (itContextQueue != itCurrentContextQueue)
{
m_bindRequests.erase(itContextQueue, itCurrentContextQueue);
}
// Spawn any proxy entities for the current context
if (itCurrentContextQueue != m_bindRequests.end())
{
if (itCurrentContextQueue->first == GetCurrentContextSequence())
{
for (auto itSliceHandler = itCurrentContextQueue->second.begin(); itSliceHandler != itCurrentContextQueue->second.end(); /*++itSliceHandler*/)
{
NetBindingSliceInstantiationHandler& sliceHandler = itSliceHandler->second;
// If this is a new slice request, instantiate it
if (sliceHandler.IsANewSliceRequest())
{
sliceHandler.InstantiateEntities();
}
/*
* A slice instance is kept alive for caching purposes. As we check each bind request for its readiness,
* we are also going to check if the slice instance itself has become inactive and needs to be removed.
*/
bool mightBeInactiveSlice = true;
if (sliceHandler.m_bindingQueue.empty() && sliceHandler.HasActiveEntities())
{
// The slice instance is spawned and full bound.
mightBeInactiveSlice = false;
}
// If the entity is ready to be bound to the network, bind it.
// NOTE: It is possible for entities spawned from a slice containing multiple entities with net binding
// to never receive their replica counterpart, either because the replica was destroyed, or was interest
// filtered. We don't have a very good pipeline to prevent these slices from being authored, so if we
// encounter them, we will delete them after a timeout.
for (auto itRequest = sliceHandler.m_bindingQueue.begin(); itRequest != sliceHandler.m_bindingQueue.end(); /*++itRequest*/)
{
BindRequest& request = itRequest->second;
if (request.m_bindTo != GridMate::InvalidReplicaId && request.m_actualRuntimeEntityId.IsValid())
{
AZ::Entity* proxyEntity = nullptr;
EBUS_EVENT_RESULT(proxyEntity, AZ::ComponentApplicationBus, FindEntity, request.m_actualRuntimeEntityId);
AZ_Warning("NetBindingSystemImpl", proxyEntity, "Could not find entity for binding %llu", request.m_actualRuntimeEntityId);
if (proxyEntity)
{
AZ_ExtraTracePrintf("NetBindingSystemImpl", "BindAndActivate desired id %llu, actual %llu, slice %s \n",
static_cast<AZ::u64>(request.m_desiredRuntimeEntityId),
static_cast<AZ::u64>(request.m_actualRuntimeEntityId),
sliceHandler.m_sliceInstanceId.ToString<AZStd::string>(false, false).c_str());
BindAndActivate(proxyEntity, request.m_bindTo, false, sliceHandler.m_sliceInstanceId);
}
itRequest = sliceHandler.m_bindingQueue.erase(itRequest);
// The slice instance is not fully bound. It may remain for a while for caching purposes.
mightBeInactiveSlice = false;
}
else if (AZStd::chrono::milliseconds(Now() - request.m_requestTime) > s_sliceBindingTimeout)
{
// If the real request never showed up, then no need for a trace
if (request.m_state == BindRequest::State::FirstBindInSlice ||
request.m_state == BindRequest::State::LateBind)
{
AZ_TracePrintf("NetBindingSystemImpl", "Entity with static id [%llu], slice [%s]\n is still unbound after %llu ms. Discarding unbound entity.\n",
static_cast<AZ::u64>(request.m_actualRuntimeEntityId),
sliceHandler.m_sliceInstanceId.ToString<AZStd::string>(false, false).c_str(),
s_sliceBindingTimeout.count());
}
switch (sliceHandler.m_state)
{
case NetBindingSliceInstantiationHandler::State::NewRequest:
case NetBindingSliceInstantiationHandler::State::Spawning:
// The slice instance isn't ready yet. We will wait to consider the timing logic until it is ready.
mightBeInactiveSlice = false;
break;
case NetBindingSliceInstantiationHandler::State::Spawned:
case NetBindingSliceInstantiationHandler::State::Failed:
// Now the timing logic for removing the slice instance becomes valid.
mightBeInactiveSlice = true;
break;
default:
break;
}
++itRequest;
}
else
{
mightBeInactiveSlice = false;
++itRequest;
}
}
if (mightBeInactiveSlice && !sliceHandler.HasActiveEntities())
{
AZ_ExtraTracePrintf("NetBindingSystemImpl", "Removing inactive slice %s \n",
sliceHandler.m_sliceInstanceId.ToString<AZStd::string>(false, false).c_str());
itSliceHandler = itCurrentContextQueue->second.erase(itSliceHandler);
}
else
{
++itSliceHandler;
}
}
}
}
}
}
// Spawn replicas for any local entities that are still valid
for (auto& addRequest : m_addMasterRequests)
{
AZ::Entity* entity = nullptr;
EBUS_EVENT_RESULT(entity, AZ::ComponentApplicationBus, FindEntity, addRequest.first);
if (entity)
{
m_bindingSession->GetReplicaMgr()->AddMaster(addRequest.second);
}
}
m_addMasterRequests.clear();
}
bool NetBindingSystemImpl::BindAndActivate(AZ::Entity* entity, GridMate::ReplicaId replicaId, bool addToContext,
const AZ::SliceComponent::SliceInstanceId& sliceInstanceId)
{
bool success = false;
if ( ShouldBindToNetwork() )
{
const GridMate::ReplicaPtr bindTo = m_contextData->GetReplicaManager()->FindReplica(replicaId);
if (bindTo)
{
if (addToContext)
{
EBUS_EVENT(GameEntityContextRequestBus, AddGameEntity, entity);
}
if (entity->GetState() == AZ::Entity::State::Constructed)
{
entity->Init();
}
NetBindingHandlerInterface* binding = GetNetBindingHandler(entity);
AZ_Warning("NetBindingSystemImpl", binding, "Can't find NetBindingComponent on entity %llu (%s)!", static_cast<AZ::u64>(entity->GetId()), entity->GetName().c_str());
if (binding)
{
binding->BindToNetwork(bindTo);
binding->SetSliceInstanceId(sliceInstanceId);
entity->Activate();
success = true;
}
}
else
{
// NOTE: It is possible for entities spawned from a slice containing multiple entities with net binding
// to never receive their replica counterpart, either because the replica was destroyed, or was interest
// filtered.
AZ_ExtraTracePrintf("NetBindingSystemImpl", "Failed to bind entity %llu - could not find replica %u", entity->GetId(), replicaId);
}
}
return success;
}
void NetBindingSystemImpl::Reflect(AZ::ReflectContext* context)
{
if (context)
{
// We need to register the chunk type, and this would be a good time to do so.
if (!GridMate::ReplicaChunkDescriptorTable::Get().FindReplicaChunkDescriptor(GridMate::ReplicaChunkClassId(NetBindingSystemContextData::GetChunkName())))
{
GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType<AzFramework::NetBindingSystemContextData>();
}
}
}
} // namespace AzFramework
@@ -1,311 +0,0 @@
/*
* 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.
*
*/
#pragma once
#include <AzFramework/Network/NetBindingSystemBus.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/Slice/SliceInstantiationBus.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/std/containers/unordered_map.h>
#include <GridMate/Serialize/CompressionMarshal.h>
namespace AzFramework
{
/**
* \brief Represents a request to bind a particular replica to an entity
*/
class BindRequest
{
public:
BindRequest()
: m_bindTo(GridMate::InvalidReplicaId)
, m_state(State::None)
{
}
GridMate::ReplicaId m_bindTo;
AZ::EntityId m_desiredRuntimeEntityId;
AZ::EntityId m_actualRuntimeEntityId;
AZStd::chrono::system_clock::time_point m_requestTime;
/**
* \brief Represents the state of this bind request and it's relation to the slice instantiation process
*/
enum class State : AZ::u8
{
None,
/**
* \brief This is the first request that led to instantiating a slice
*/
FirstBindInSlice,
/**
* \brief The request is a placeholder in case a real bind request arrives later.
* Some part of the slice may never be bound (e.g. if a replica is omitted by Interest Manager)
*/
PlaceholderBind,
/**
* \brief The real request did arrive to replace a placeholder request.
*/
LateBind,
};
State m_state;
};
typedef AZStd::unordered_map<AZ::EntityId, BindRequest> BindRequestContainerType;
/**
* \brief Represents a slice instance being instantiated and bound to replicas
* \note It's possible that only some of the entities are activated and bound to replicas.
*/
class NetBindingSliceInstantiationHandler
: public SliceInstantiationResultBus::Handler
{
public:
~NetBindingSliceInstantiationHandler() override;
void InstantiateEntities();
bool IsInstantiated() const;
bool IsANewSliceRequest() const;
bool IsBindingComplete() const;
/**
* \note Returns false if there are no entities in the slice or the slice instance isn't ready yet.
* \return true if any of the entities from the slice are active
*/
bool HasActiveEntities() const;
//////////////////////////////////////////////////////////////////////////
// SliceInstantiationResultBus
void OnSlicePreInstantiate(const AZ::Data::AssetId& sliceAssetId, const AZ::SliceComponent::SliceInstanceAddress& sliceAddress) override;
void OnSliceInstantiated(const AZ::Data::AssetId& sliceAssetId, const AZ::SliceComponent::SliceInstanceAddress& sliceAddress) override;
void OnSliceInstantiationFailed(const AZ::Data::AssetId& sliceAssetId) override;
//////////////////////////////////////////////////////////////////////////
void InstantiationFailureCleanup();
void UseCacheFor(BindRequest& request, const AZ::EntityId& staticEntityId);
void CloseEntityMap(const AZ::SliceComponent::EntityIdToEntityIdMap& staticToRuntimeMap);
AZ::Data::AssetId m_sliceAssetId;
BindRequestContainerType m_bindingQueue;
SliceInstantiationTicket m_ticket;
/**
* \breif a cache of entities that might be networked at some point
* \note they might be bound and unbound if their replicas leave and come back in the view
*/
AZStd::vector<AZ::Entity*> m_boundEntities;
/**
* \brief identifies which slice instance the instantiation will be performed for
*/
AZ::SliceComponent::SliceInstanceId m_sliceInstanceId;
/**
* \brief when was the request to spawn a slice and bind it made
*/
AZStd::chrono::system_clock::time_point m_bindTime;
AZ::SliceComponent::EntityIdToEntityIdMap m_staticToRuntimeEntityMap;
/**
* \brief The state of the slice instance.
*/
enum class State
{
/**
* \brief Has not started instantiating the slice instance.
*/
NewRequest,
/**
* \brief Waiting on the slice to spawn.
*/
Spawning,
/**
* \brief Successfully spawned the slice assets.
*/
Spawned,
/**
* \brief Failed to spawn the slice.
*/
Failed
};
State m_state = State::NewRequest;
};
/**
* NetBindingSystemImpl works in conjunction with NetBindingComponent and
* NetBindingComponentChunk to perform network binding for game entities.
*
* It is responsible for adding entity replicas to the network on the master side
* and servicing entity spawn requests from the network on the proxy side, as
* well as detecting network availability and triggering network binding/unbinding.
*
* The system is first activated on the host side when OnNetworkSessionActivated event
* is received, and NetBindingSystemContextData is created.
* The system becomes fully operational when the NetBindingSystemContextData is activated
* and bound to the system, and remains operational as long as the NetBindingSystemContextData
* remains valid.
*
* Level switching is tracked by a monotonically increasing context sequence number controlled
* by the host. Spawn and bind operations are deferred until the correct sequence number
* is reached. Spawning is always performed from the game thread.
*/
class NetBindingSystemImpl
: public NetBindingSystemBus::Handler
, public NetBindingSystemEventsBus::Handler
, public EntityContextEventBus::Handler
, public AZ::TickBus::Handler
{
friend class NetBindingSystemContextData;
public:
NetBindingSystemImpl();
~NetBindingSystemImpl() override;
static void Reflect(AZ::ReflectContext* context);
virtual void Init();
virtual void Shutdown();
static const AZStd::chrono::milliseconds s_sliceBindingTimeout;
//////////////////////////////////////////////////////////////////////////
// NetBindingSystemBus
bool ShouldBindToNetwork() override;
NetBindingContextSequence GetCurrentContextSequence() override;
void AddReplicaMaster(AZ::Entity* entity, GridMate::ReplicaPtr replica) override;
AZ::EntityId GetStaticIdFromEntityId(AZ::EntityId entity) override;
AZ::EntityId GetEntityIdFromStaticId(AZ::EntityId staticEntityId) override;
void SpawnEntityFromSlice(GridMate::ReplicaId bindTo, const NetBindingSliceContext& bindToContext) override;
void SpawnEntityFromStream(AZ::IO::GenericStream& spawnData, AZ::EntityId useEntityId, GridMate::ReplicaId bindTo, NetBindingContextSequence addToContext) override;
void OnNetworkSessionActivated(GridMate::GridSession* session) override;
void OnNetworkSessionDeactivated(GridMate::GridSession* session) override;
void UnbindGameEntity(AZ::EntityId entity, const AZ::SliceComponent::SliceInstanceId& sliceInstanceId) override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// EntityContextEventBus::Handler
void OnEntityContextReset() override;
void OnEntityContextLoadedFromStream(const AZ::SliceComponent::EntityList& contextEntities) override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// TickBus::Handler
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
int GetTickOrder() override;
//////////////////////////////////////////////////////////////////////////
protected:
//! Called by the NetBindingContext chunk when it is activated
void OnContextDataActivated(GridMate::ReplicaChunkPtr contextData);
//! Called by the NetBindingContext chunk when it is deactivated
void OnContextDataDeactivated(GridMate::ReplicaChunkPtr contextData);
//! Update the current binding context sequence
virtual void UpdateContextSequence();
//! Process pending spawn requests
virtual void ProcessSpawnRequests();
//! Process pending bind requests
virtual void ProcessBindRequests();
//! Performs final stage of entity spawning process
virtual bool BindAndActivate(AZ::Entity* entity, GridMate::ReplicaId replicaId, bool addToContext, const AZ::SliceComponent::SliceInstanceId& sliceInstanceId);
//! Called on the host to spawn the net binding system replica
virtual GridMate::Replica* CreateSystemReplica();
AZ_FORCE_INLINE bool ReadyToAddReplica() const;
class SpawnRequest
{
public:
GridMate::ReplicaId m_bindTo;
AZ::EntityId m_useEntityId;
AZStd::vector<AZ::u8> m_spawnDataBuffer;
};
typedef AZStd::list<SpawnRequest> SpawnRequestContainerType;
typedef AZStd::map<NetBindingContextSequence, SpawnRequestContainerType> SpawnRequestContextContainerType;
typedef AZStd::unordered_map<AZ::SliceComponent::SliceInstanceId, NetBindingSliceInstantiationHandler> SliceRequestContainerType;
typedef AZStd::map<NetBindingContextSequence, SliceRequestContainerType> BindRequestContextContainerType;
GridMate::GridSession* m_bindingSession;
GridMate::ReplicaChunkPtr m_contextData;
NetBindingContextSequence m_currentBindingContextSequence;
SpawnRequestContextContainerType m_spawnRequests;
BindRequestContextContainerType m_bindRequests;
AZStd::list<AZStd::pair<AZ::EntityId, GridMate::ReplicaPtr>> m_addMasterRequests;
/**
* \brief override how root slice entities' replicas should be loaded
*
* We occasionally get GameContextBridge replica (that tells us what level to load) before we get
* a replica that tells us that we are connecting to a network sessions, thus we may not figure out in time if we
* need to load the root slice entities with NetBindingComponent as master replicas or proxy replicas.
* This is a fix until proper order is established.
*
* \param isAuthoritative true if root slice entities with NetBindingComponents to be loaded authoritatively
*/
void OverrideRootSliceLoadMode(bool isAuthoritative)
{
m_isAuthoritativeRootSliceLoad = isAuthoritative;
m_overrideRootSliceLoadAuthoritative = true;
}
private:
/**
* \brief True if the root slice is to be loaded authoritatively
*/
bool m_isAuthoritativeRootSliceLoad;
/**
* \brief True if root slice loading mode was overriden, otherwise the mode would be determined via m_bindingSession
*/
bool m_overrideRootSliceLoadAuthoritative;
/**
* \brief A helper method to figure the mode of loading root slice entities' replicas
* \return True if the root slice entities is to be loaded authoritatively
*/
bool IsAuthoritateLoad() const;
void UpdateClock(float deltaTime);
AZStd::chrono::system_clock::time_point Now() const;
AZStd::chrono::system_clock::time_point m_currentTime;
};
class NetBindingSystemContextData
: public GridMate::ReplicaChunk
{
public:
AZ_CLASS_ALLOCATOR(NetBindingSystemContextData, AZ::SystemAllocator, 0);
static const char* GetChunkName() { return "NetBindingSystemContextData"; }
NetBindingSystemContextData();
bool IsReplicaMigratable() override { return true; }
bool IsBroadcast() override { return true; }
void OnReplicaActivate(const GridMate::ReplicaContext& rc) override;
void OnReplicaDeactivate(const GridMate::ReplicaContext& rc) override;
GridMate::DataSet<AZ::u32, GridMate::VlqU32Marshaler> m_bindingContextSequence;
};
} // namespace AzFramework
@@ -1,38 +0,0 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
namespace AzFramework
{
class NetworkContext;
/**
* The NetSystemRequestBus services requests for global networking systems in AzFramework
*/
class NetSystemRequests
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
NetSystemRequests() = default;
virtual ~NetSystemRequests() = default;
virtual NetworkContext* GetNetworkContext() = 0;
};
using NetSystemRequestBus = AZ::EBus<NetSystemRequests>;
}
@@ -1,378 +0,0 @@
/*
* 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 <AzFramework/Network/NetworkContext.h>
#include <AzFramework/Network/NetBindable.h>
#include <GridMate/Replica/DataSet.h>
namespace AzFramework
{
NetworkContext::DescBase::DescBase(const char* name, ptrdiff_t offset)
: m_name(name)
, m_offset(offset)
{
}
NetworkContext::FieldDescBase::FieldDescBase(const char* name, ptrdiff_t offset)
: DescBase(name, offset)
, m_dataSetIdx(static_cast<size_t>(-1))
{
}
NetworkContext::RpcDescBase::RpcDescBase(const char* name, ptrdiff_t offset)
: DescBase(name, offset)
, m_rpcIdx(static_cast<size_t>(-1))
{
}
NetworkContext::CtorDataBase::CtorDataBase(const char* name)
: m_name(name)
{
}
NetworkContext::ClassBuilder::ClassBuilder(NetworkContext* context, ClassDescPtr binding)
: m_binding(binding)
, m_context(context)
{
}
NetworkContext::ClassBuilder::~ClassBuilder()
{
if (m_context->IsRemovingReflection())
{
if (m_binding->UnregisterChunkType)
{
m_binding->UnregisterChunkType();
}
}
else
{
if (m_binding->RegisterChunkType)
{
m_binding->RegisterChunkType();
}
}
}
NetworkContext::ClassDesc::ClassDesc(const char* name, const AZ::Uuid& typeId /* = AZ::Uuid() */)
: m_name(name)
, m_typeId(typeId)
{
}
///////////////////////////////////////////////////////////////////////////
/// NetworkContext
///////////////////////////////////////////////////////////////////////////
NetworkContext::NetworkContext()
{
}
NetworkContext::~NetworkContext()
{
}
size_t NetworkContext::GetReflectedChunkSize(const AZ::Uuid& typeId) const
{
size_t totalSize = 0;
auto it = m_classBindings.find(typeId);
if (it != m_classBindings.end())
{
ClassDescPtr binding = it->second;
for (const auto& field : binding->m_chunkDesc.m_fields)
{
totalSize += field->GetDataSetSize();
}
for (const auto& rpc : binding->m_chunkDesc.m_rpcs)
{
totalSize += rpc->GetRpcSize();
}
}
return totalSize;
}
bool NetworkContext::UsesSelfAsChunk(const AZ::Uuid& typeId) const
{
auto it = m_classBindings.find(typeId);
if (it != m_classBindings.end())
{
ClassDescPtr binding = it->second;
return !binding->m_chunkDesc.m_external && binding->m_chunkDesc.m_fields.size() > 0;
}
return false;
}
bool NetworkContext::UsesExternalChunk(const AZ::Uuid& typeId) const
{
auto it = m_classBindings.find(typeId);
if (it != m_classBindings.end())
{
ClassDescPtr binding = it->second;
return binding->m_chunkDesc.m_external && (AZ::u32(binding->m_chunkDesc.m_chunkId) != 0);
}
return false;
}
ReplicaChunkBase* NetworkContext::CreateReplicaChunk(const AZ::Uuid& typeId)
{
const auto it = m_classBindings.find(typeId);
if (it != m_classBindings.end())
{
const ClassDescPtr binding = it->second;
if (binding->CreateReplicaChunk)
{
ReplicaChunkDescriptor* descriptor = ReplicaChunkDescriptorTable::Get().FindReplicaChunkDescriptor(binding->m_chunkDesc.m_chunkId);
AZ_Assert(descriptor, "NetworkContext cannot find replica chunk descriptor for %s. Did you remember to register the chunk type?", binding->m_name);
ReplicaChunkDescriptorTable::Get().BeginConstructReplicaChunk(descriptor);
ReplicaChunkBase* chunk = binding->CreateReplicaChunk();
ReplicaChunkDescriptorTable::Get().EndConstructReplicaChunk();
chunk->Init(descriptor);
return chunk;
}
}
/*
* Special case: empty declarations such as:
*
* static void Reflect() {
* ....
* NetworkContext->Class<MyComponent>();
* }
*
* Result in no ReplicaChunks being created. It's treated as a no-op. No replication will be performed.
*/
return nullptr;
}
void NetworkContext::DestroyReplicaChunk(ReplicaChunkBase* chunk)
{
ReplicaChunkClassId chunkId = chunk->GetDescriptor()->GetChunkTypeId();
auto it = m_chunkBindings.find(chunkId);
if (it != m_chunkBindings.end())
{
ClassDescPtr binding = it->second;
binding->DestroyReplicaChunk(chunk);
return;
}
AZ_Warning("NetworkContext", false, "DestroyReplicaChunk could not find a binding for %s", chunk->GetDescriptor()->GetChunkName());
}
void NetworkContext::Bind(NetBindable* instance, ReplicaChunkPtr chunk, NetworkContextBindMode mode)
{
const AZ::Uuid& typeId = instance->RTTI_GetType();
auto it = m_classBindings.find(typeId);
if (it != m_classBindings.end())
{
ClassDescPtr binding = it->second;
if (chunk)
{
ReplicaChunkClassId chunkId = chunk->GetDescriptor()->GetChunkTypeId();
AZ_Assert(binding->m_chunkDesc.m_chunkId == chunkId, "NetworkContext detected a type mismatch while trying to bind an instance to a ReplicaChunk");
if (binding->m_chunkDesc.m_chunkId == chunkId)
{
if (!binding->m_chunkDesc.m_external)
{
ReflectedReplicaChunkBase* refChunk = static_cast<ReflectedReplicaChunkBase*>(chunk.get());
refChunk->Bind(instance, mode);
}
}
}
else
{
if (binding->BindRpcs)
{
binding->BindRpcs(instance);
}
}
}
}
void NetworkContext::EnumerateFields(const ReplicaChunkClassId& chunkId, FieldVisitor visitor) const
{
auto it = m_chunkBindings.find(chunkId);
if (it != m_chunkBindings.end())
{
const ChunkDesc& chunkDesc = it->second->m_chunkDesc;
for (const auto& field : chunkDesc.m_fields)
{
visitor(field.get());
}
}
}
void NetworkContext::EnumerateFields(const AZ::Uuid& typeId, FieldVisitor visitor) const
{
auto it = m_classBindings.find(typeId);
if (it != m_classBindings.end())
{
const ChunkDesc& chunkDesc = it->second->m_chunkDesc;
for (const auto& field : chunkDesc.m_fields)
{
visitor(field.get());
}
}
}
void NetworkContext::EnumerateRpcs(const ReplicaChunkClassId& chunkId, RpcVisitor visitor) const
{
auto it = m_chunkBindings.find(chunkId);
if (it != m_chunkBindings.end())
{
const ChunkDesc& chunkDesc = it->second->m_chunkDesc;
for (const auto& rpc : chunkDesc.m_rpcs)
{
visitor(rpc.get());
}
}
}
void NetworkContext::EnumerateRpcs(const AZ::Uuid& typeId, RpcVisitor visitor) const
{
auto it = m_classBindings.find(typeId);
if (it != m_classBindings.end())
{
const ChunkDesc& chunkDesc = it->second->m_chunkDesc;
for (const auto& rpc : chunkDesc.m_rpcs)
{
visitor(rpc.get());
}
}
}
void NetworkContext::EnumerateCtorData(const ReplicaChunkClassId& chunkId, CtorVisitor visitor) const
{
auto it = m_chunkBindings.find(chunkId);
if (it != m_chunkBindings.end())
{
const ChunkDesc& chunkDesc = it->second->m_chunkDesc;
for (const auto& ctor : chunkDesc.m_ctors)
{
visitor(ctor.get());
}
}
}
void NetworkContext::EnumerateCtorData(const AZ::Uuid& typeId, CtorVisitor visitor) const
{
auto it = m_classBindings.find(typeId);
if (it != m_classBindings.end())
{
const ChunkDesc& chunkDesc = it->second->m_chunkDesc;
for (const auto& ctor : chunkDesc.m_ctors)
{
visitor(ctor.get());
}
}
}
///////////////////////////////////////////////////////////////////////////
ReflectedReplicaChunkBase::ReflectedReplicaChunkBase()
: m_ctorBuffer(GridMate::EndianType::IgnoreEndian, 0)
{
}
///////////////////////////////////////////////////////////////////////////
NetworkContextChunkDescriptor::NetworkContextChunkDescriptor(const char* name, size_t size, const AZ::Uuid& typeId)
: ReplicaChunkDescriptor(name, size)
, m_typeId(typeId)
{
}
ReplicaChunkBase* NetworkContextChunkDescriptor::CreateFromStream(UnmarshalContext& ctx)
{
AZ_Assert(!m_typeId.IsNull(), "No typeid associated with NetworkContextChunkDescriptor, cannot spawn Chunk");
if (!m_typeId.IsNull())
{
NetworkContext* netContext = nullptr;
EBUS_EVENT_RESULT(netContext, NetSystemRequestBus, GetNetworkContext);
AZ_Assert(netContext, "No NetworkContext found while trying to construct ReflectedReplicaChunk");
ReplicaChunkBase* replicaChunk = netContext->CreateReplicaChunk(m_typeId);
if (ctx.m_hasCtorData && ctx.m_iBuf)
{
NetworkContextChunkDescriptor* netChunkDesc = static_cast<NetworkContextChunkDescriptor*>(replicaChunk->GetDescriptor());
if (netChunkDesc->IsAuto())
{
// copy each ctor data field into the ctor buffer
ReflectedReplicaChunkBase* refChunk = static_cast<ReflectedReplicaChunkBase*>(replicaChunk);
netContext->EnumerateCtorData(m_typeId,
[&ctx, refChunk](NetworkContext::CtorDataBase* ctorData)
{
ctorData->Copy(*ctx.m_iBuf, refChunk->m_ctorBuffer);
});
}
}
return replicaChunk;
}
return nullptr;
}
void NetworkContextChunkDescriptor::DeleteReplicaChunk(ReplicaChunkBase* chunk)
{
NetworkContext* netContext = nullptr;
EBUS_EVENT_RESULT(netContext, NetSystemRequestBus, GetNetworkContext);
AZ_Assert(netContext, "No NetworkContext found while trying to destroy ReflectedReplicaChunk");
netContext->DestroyReplicaChunk(chunk);
}
void NetworkContextChunkDescriptor::MarshalCtorData(ReplicaChunkBase* chunk, WriteBuffer& buffer)
{
NetworkContext* netContext = nullptr;
EBUS_EVENT_RESULT(netContext, NetSystemRequestBus, GetNetworkContext);
AZ_Assert(netContext, "No NetworkContext found while trying to collect ctor data for ReflectedReplicaChunk");
NetBindable* netBindable = static_cast<NetBindable*>(chunk->GetHandler());
NetworkContextChunkDescriptor* netChunkDesc = static_cast<NetworkContextChunkDescriptor*>(chunk->GetDescriptor());
if (!netChunkDesc->IsAuto())
{
return;
}
if (netBindable) // chunk is bound, get source data from the netBindable
{
netContext->EnumerateCtorData(m_typeId,
[netBindable, &buffer](NetworkContext::CtorDataBase* ctorData)
{
ctorData->Marshal(netBindable, buffer);
});
}
else // chunk is not bound yet, copy the ctor data for forwarding
{
ReflectedReplicaChunkBase* refChunk = static_cast<ReflectedReplicaChunkBase*>(chunk);
ReadBuffer src(refChunk->m_ctorBuffer.GetEndianType(), refChunk->m_ctorBuffer.Get(), refChunk->m_ctorBuffer.Size());
netContext->EnumerateCtorData(m_typeId,
[&src, &buffer](NetworkContext::CtorDataBase* ctorData)
{
ctorData->Copy(src, buffer);
});
}
}
void NetworkContextChunkDescriptor::DiscardCtorStream(UnmarshalContext& ctx)
{
NetworkContext* netContext = nullptr;
EBUS_EVENT_RESULT(netContext, NetSystemRequestBus, GetNetworkContext);
AZ_Assert(netContext, "No NetworkContext found while trying to skip ctor data for ReflectedReplicaChunk");
if (ctx.m_hasCtorData)
{
// Iterate over all of the ctor data and unmarshal it with no destination,
// which will advance the buffer past the ctor data for this object
netContext->EnumerateCtorData(m_typeId,
[&ctx](NetworkContext::CtorDataBase* ctorData)
{
ctorData->Unmarshal(*ctx.m_iBuf, nullptr);
});
}
}
}
@@ -1,969 +0,0 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzFramework/Network/NetSystemBus.h>
#include <AzFramework/Network/NetBindable.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/typetraits/is_base_of.h>
#include <AzCore/std/functional.h>
namespace AzFramework
{
class NetBindable;
using GridMate::ReplicaChunkInterface;
using GridMate::ReplicaChunkBase;
using GridMate::ReplicaChunk;
using GridMate::ReplicaChunkDescriptor;
using GridMate::DefaultReplicaChunkDescriptor;
using GridMate::ReplicaChunkDescriptorTable;
using GridMate::ReplicaChunkClassId;
using GridMate::ReplicaChunkPtr;
using GridMate::Rpc;
using GridMate::ZoneMask;
using GridMate::ZoneMask_All;
using GridMate::UnmarshalContext;
using GridMate::ReadBuffer;
using GridMate::WriteBuffer;
using GridMate::WriteBufferDynamic;
///////////////////////////////////////////////////////////////////////////
// GridMate ReplicaChunk/ReplicaChunkDescriptors
///////////////////////////////////////////////////////////////////////////
class ReflectedReplicaChunkBase
: public ReplicaChunkBase
, public ReplicaChunkInterface
{
friend NetworkContext;
public:
ReflectedReplicaChunkBase();
bool IsReplicaMigratable() override { return true; }
/// Returns the chunk type name, e.g. "ReflectedReplicaChunk<MyClass>"
virtual const char* GetName() const = 0;
/// Returns the linear size of the chunk including DataSets and RPCs
virtual size_t GetSize() const = 0;
/// Returns a pointer to the start of the DataSet/RPC storage allocated with the chunk
virtual AZ::u8* GetDataStart() const = 0;
/// Binds an instance of the reflected class to this chunk
virtual void Bind(NetBindable* instance, NetworkContextBindMode mode) = 0;
/// Removes network bindings from the bound NetBindable
virtual void Unbind() = 0;
WriteBufferDynamic m_ctorBuffer; ///< Buffer to hold ctor data before the chunk is bound
};
/// This will be the header for a blob in memory:
/// The layout looks like:
/// * ReflectedReplicaChunk<T>
/// * DataSets
/// * RPCs
template <class ClassType>
class ReflectedReplicaChunk
: public ReflectedReplicaChunkBase
{
friend NetworkContext;
public:
static const char* GetChunkName();
static size_t GetChunkSize();
public:
AZ_CLASS_ALLOCATOR(ReflectedReplicaChunk, AZ::SystemAllocator, 0);
ReflectedReplicaChunk()
: m_dataSets(reinterpret_cast<AZ::u8*>(this) + sizeof(*this))
{
}
const char* GetName() const override { return GetChunkName(); }
size_t GetSize() const override { return GetChunkSize(); }
AZ::u8* GetDataStart() const override { return const_cast<AZ::u8*>(m_dataSets); }
void Bind(NetBindable* instance, NetworkContextBindMode mode) override;
void Unbind() override;
private:
const AZ::u8* m_dataSets; ///< Points to the beginning of the datasets for this chunk
};
class NetworkContextChunkDescriptor
: public ReplicaChunkDescriptor
{
public:
NetworkContextChunkDescriptor(const char* name, size_t size, const AZ::Uuid& typeId = AZ::Uuid());
ReplicaChunkBase* CreateFromStream(UnmarshalContext& ctx) override;
void DeleteReplicaChunk(ReplicaChunkBase* chunkInstance) override;
void DiscardCtorStream(UnmarshalContext&) override;
void MarshalCtorData(ReplicaChunkBase*, WriteBuffer&) override;
void Bind(const AZ::Uuid& typeId) { m_typeId = typeId; }
virtual bool IsAuto() const { return false; }
private:
AZ::Uuid m_typeId; ///< TypeId of the class this descriptor represents (not the chunk type)
};
template <class ClassType, ZoneMask mask = ZoneMask_All>
class AutoChunkDescriptor
: public NetworkContextChunkDescriptor
{
public:
AutoChunkDescriptor()
: NetworkContextChunkDescriptor(ReflectedReplicaChunk<ClassType>::GetChunkName(), ReflectedReplicaChunk<ClassType>::GetChunkSize(), AZ::RttiTypeId<ClassType>())
{
}
ZoneMask GetZoneMask() const override { return mask; }
bool IsAuto() const override { return true; }
};
template <class ChunkType, ZoneMask mask = ZoneMask_All>
class ExternalChunkDescriptor
: public NetworkContextChunkDescriptor
{
public:
ExternalChunkDescriptor()
: NetworkContextChunkDescriptor(ChunkType::GetChunkName(), sizeof(ChunkType))
{}
ZoneMask GetZoneMask() const override { return mask; }
};
///////////////////////////////////////////////////////////////////////////
/// NetworkContext can be used to reflect classes for network serialization
/// It will automatically generate ReplicaChunks and bind them to instances
/// when requested. It also serves as a binding registry for binding a class
/// to the ReplicaChunk that should be used to replicate it.
///////////////////////////////////////////////////////////////////////////
class NetworkContext
: public AZ::ReflectContext
{
public:
/// @cond EXCLUDE_DOCS
class ClassBuilder;
class ClassDesc;
using ClassDescPtr = AZStd::intrusive_ptr<ClassDesc>;
using ClassBuilderPtr = AZStd::intrusive_ptr<ClassBuilder>;
using ClassBindings = AZStd::unordered_map<AZ::Uuid, ClassDescPtr>;
using ChunkBindings = AZStd::unordered_map<ReplicaChunkClassId, ClassDescPtr>;
using ClassInfo = ClassBuilder; ///< @deprecated Use NetworkContext::ClassBuilder
using ClassInfoPtr = ClassBuilderPtr; ///< @deprecated Use NetworkContext::ClassBuilderPtr
/// @endcond
class IntrusiveRefCounted
{
public:
virtual ~IntrusiveRefCounted() {}
private:
// refcount
template<class T>
friend struct AZStd::IntrusivePtrCountPolicy;
mutable unsigned int m_refCount = 0;
AZ_FORCE_INLINE void add_ref() { ++m_refCount; }
AZ_FORCE_INLINE void release()
{
AZ_Assert(m_refCount > 0, "Reference count logic error, trying to remove reference when refcount is 0");
if (--m_refCount == 0)
{
delete this;
}
}
};
/**
* Interface for recording classes, chunks, and datasets
* When destructed at the end of reflection, it will register/unregister the ChunkDescriptor
*/
class ClassBuilder
: public IntrusiveRefCounted
{
friend class NetworkContext;
protected:
AZ_CLASS_ALLOCATOR(ClassBuilder, AZ::SystemAllocator, 0);
ClassBuilder(NetworkContext* context, ClassDescPtr binding);
public:
~ClassBuilder();
ClassBuilderPtr operator->() { return this; }
/// Bind a ReplicaChunk type to this class for network serialization
template <class ChunkType, typename DescriptorType = ExternalChunkDescriptor<ChunkType> >
ClassBuilderPtr Chunk();
/// Bind a NetBindable's Field
template <class ClassType, typename FieldType>
typename AZStd::enable_if<AZStd::is_base_of<NetBindableFieldBase, FieldType>::value, ClassBuilderPtr>::type
Field(const char* name, FieldType ClassType::* address);
/// Declare an external chunk's DataSet
template <class ClassType, typename DataSetType>
typename AZStd::enable_if<AZStd::is_base_of<DataSetBase, DataSetType>::value, ClassBuilderPtr>::type
Field(const char* name, DataSetType ClassType::* address);
/// Bind an Rpc::BindInterface for this chunk
template <class ClassType, // class this RPC is part of
class InterfaceType = ClassType, // class implementing the RPC, must derive from ReplicaChunkInterface
typename ... Args,
class Traits = RpcDefaultTraits,
typename RpcBindType = typename Rpc<Args...>::template BindInterface<InterfaceType, bool (InterfaceType::*)(typename Args::Type..., const RpcContext&), Traits> >
typename AZStd::enable_if<AZStd::is_base_of<RpcBase, RpcBindType>::value, ClassBuilderPtr>::type
RPC(const char* name, RpcBindType ClassType::* rpc);
/// Bind a NetBindable::Rpc for this NetBindable
template <class ClassType,
class InterfaceType = ClassType,
typename ... Args,
class Traits = RpcDefaultTraits,
typename RpcBindType = typename NetBindable::Rpc<Args...>::template Bind<InterfaceType, bool (InterfaceType::*)(Args..., const RpcContext&), Traits> >
typename AZStd::enable_if<AZStd::is_base_of<NetBindableRpcBase, RpcBindType>::value, ClassBuilderPtr>::type
RPC(const char* name, RpcBindType ClassType::* rpc);
#define CTOR_DATA_OVERLOAD(_getsig, _setsig) \
template <class ClassType, class DataType, typename MarshalerType = Marshaler<DataType> > \
ClassBuilderPtr CtorData(const char* name, _getsig, _setsig, const MarshalerType&marshaler = MarshalerType()) \
{ \
return CtorDataImpl<ClassType, DataType>(name, getter, setter, marshaler); \
}
/// Bind a getter/setter pair for data required during object construction
// this has to be done via overload so that the user does not have to explicitly provide
// the template arguments, they can be divined from the function call
CTOR_DATA_OVERLOAD(DataType(ClassType::* getter)(), void (ClassType::* setter)(const DataType&));
CTOR_DATA_OVERLOAD(DataType(ClassType::* getter)() const, void (ClassType::* setter)(const DataType&));
CTOR_DATA_OVERLOAD(DataType & (ClassType::* getter)(), void (ClassType::* setter)(const DataType&));
CTOR_DATA_OVERLOAD(DataType & (ClassType::* getter)() const, void (ClassType::* setter)(const DataType&));
CTOR_DATA_OVERLOAD(const DataType&(ClassType::* getter)(), void (ClassType::* setter)(const DataType&));
CTOR_DATA_OVERLOAD(const DataType&(ClassType::* getter)() const, void (ClassType::* setter)(const DataType&));
CTOR_DATA_OVERLOAD(DataType(ClassType::* getter)(), void (ClassType::* setter)(DataType));
CTOR_DATA_OVERLOAD(DataType(ClassType::* getter)() const, void (ClassType::* setter)(DataType));
CTOR_DATA_OVERLOAD(DataType & (ClassType::* getter)(), void (ClassType::* setter)(DataType));
CTOR_DATA_OVERLOAD(DataType & (ClassType::* getter)() const, void (ClassType::* setter)(DataType));
CTOR_DATA_OVERLOAD(const DataType&(ClassType::* getter)(), void (ClassType::* setter)(DataType));
CTOR_DATA_OVERLOAD(const DataType&(ClassType::* getter)() const, void (ClassType::* setter)(DataType));
#undef CTOR_DATA_OVERLOAD
private:
template <class ClassType,
class DataType,
class GetterFunction,
class SetterFunction,
typename MarshalerType = Marshaler<DataType> >
ClassBuilderPtr CtorDataImpl(const char* name, GetterFunction getter, SetterFunction setter, const MarshalerType& marshaler = MarshalerType());
private:
ClassDescPtr m_binding;
NetworkContext* m_context;
};
class DescBase
: public IntrusiveRefCounted
{
friend class NetworkContext;
public:
AZ_CLASS_ALLOCATOR(DescBase, AZ::SystemAllocator, 0);
DescBase(const char* name, ptrdiff_t offset);
virtual ~DescBase() {}
const char* GetName() const { return m_name; }
ptrdiff_t GetOffset() const { return m_offset; }
protected:
const char* m_name; ///< Field name, will be used as DataSet debug name
ptrdiff_t m_offset; ///< Offset from an instance pointer (a ReplicaChunk or the actual class instance)
};
class FieldDescBase
: public DescBase
{
friend class NetworkContext;
public:
AZ_CLASS_ALLOCATOR(FieldDescBase, AZ::SystemAllocator, 0);
FieldDescBase(const char* name, ptrdiff_t offset);
virtual ~FieldDescBase() {}
virtual void ConstructDataSet(void*) const = 0;
virtual void DestructDataSet(void*) const = 0;
virtual size_t GetDataSetSize() const = 0;
size_t GetDataSetIndex() const { return m_dataSetIdx; }
protected:
size_t m_dataSetIdx;
};
/**
* Represents a DataSet in a chunk or class
* NOTE: m_offset in this class is the offset from ReplicaChunk* -> DataSet
*/
template <typename DataSetType>
class DataSetDesc
: public FieldDescBase
{
public:
AZ_CLASS_ALLOCATOR(DataSetDesc, AZ::SystemAllocator, 0);
DataSetDesc(const char* name, ptrdiff_t offset);
void ConstructDataSet(void*) const override {}
void DestructDataSet(void*) const override {}
size_t GetDataSetSize() const override { return sizeof(DataSetType); }
};
/**
* Represents a field in a chunk, responsible for creating a DataSet<T, Marshaler, Throttler>
* that represents the field
* NOTE: m_offset in this class is the offset from NetBindable* -> NetBindable::Field
*/
template <typename FieldType>
class NetBindableFieldDesc
: public FieldDescBase
{
public:
using DataSetType = typename FieldType::DataSetType;
public:
AZ_CLASS_ALLOCATOR(NetBindableFieldDesc, AZ::SystemAllocator, 0);
NetBindableFieldDesc(const char* name, ptrdiff_t offset);
void ConstructDataSet(void* mem) const override { FieldType::ConstructDataSet(mem, m_name); }
void DestructDataSet(void* mem) const override { FieldType::DestructDataSet(mem); }
size_t GetDataSetSize() const override { return sizeof(DataSetType); }
};
class RpcDescBase
: public DescBase
{
friend class NetworkContext;
public:
AZ_CLASS_ALLOCATOR(RpcDescBase, AZ::SystemAllocator, 0);
RpcDescBase(const char* name, ptrdiff_t offset);
virtual ~RpcDescBase() {}
virtual void ConstructRpc(void*) const {}
virtual void DestructRpc(void*) const {}
virtual size_t GetRpcSize() const { return 0; }
size_t GetRpcIndex() const { return m_rpcIdx; }
protected:
size_t m_rpcIdx;
};
template <typename RpcBindType>
class NetBindableRpcDesc
: public RpcDescBase
{
friend class NetworkContext;
public:
AZ_CLASS_ALLOCATOR(NetBindableRpcDesc, AZ::SystemAllocator, 0);
NetBindableRpcDesc(const char* name, ptrdiff_t offset)
: RpcDescBase(name, offset)
{
static_assert((AZStd::is_base_of<NetBindableRpcBase, RpcBindType>::value), "NetBindableRpcDesc is intended for use only with NetBindableRpcs");
}
void ConstructRpc(void* mem) const override { RpcBindType::ConstructRpc(mem, m_name); }
void DestructRpc(void* mem) const override { RpcBindType::DestructRpc(mem); }
size_t GetRpcSize() const override { return sizeof(typename RpcBindType::BindInterfaceType); }
};
class CtorDataBase
: public IntrusiveRefCounted
{
public:
AZ_CLASS_ALLOCATOR(CtorDataBase, AZ::SystemAllocator, 0);
CtorDataBase(const char* name);
virtual ~CtorDataBase() {}
virtual void Marshal(NetBindable* netBindable, WriteBuffer& buffer) const = 0;
virtual void Unmarshal(ReadBuffer& buffer, NetBindable* netBindable) const = 0;
virtual void Copy(ReadBuffer& src, WriteBuffer& dest) const = 0;
protected:
const char* m_name;
};
template <class ClassType, class DataType, typename MarshalerType>
class CtorDataDesc
: public CtorDataBase
{
using GetterFunction = AZStd::function<DataType(ClassType*)>;
using SetterFunction = AZStd::function<void (ClassType*, const DataType&)>;
public:
AZ_CLASS_ALLOCATOR(CtorDataDesc, AZ::SystemAllocator, 0);
CtorDataDesc(const char* name, GetterFunction get, SetterFunction set)
: CtorDataBase(name)
, m_get(get)
, m_set(set)
{}
CtorDataDesc(const char* name, DataType(ClassType::* getter)(), void (ClassType::* setter)(const DataType&))
: CtorDataBase(name)
, m_get(AZStd::bind(getter, AZStd::placeholders::_1))
, m_set(AZStd::bind(setter, AZStd::placeholders::_1, AZStd::placeholders::_2))
{}
void Marshal(NetBindable* netBindable, WriteBuffer& buffer) const override;
void Unmarshal(ReadBuffer& buffer, NetBindable* netBindable) const override;
virtual void Copy(ReadBuffer& src, WriteBuffer& dest) const override;
GetterFunction m_get;
SetterFunction m_set;
MarshalerType m_marshaler;
};
struct ChunkDesc
{
public:
using Fields = AZStd::vector<AZStd::intrusive_ptr<FieldDescBase> >;
using Rpcs = AZStd::vector<AZStd::intrusive_ptr<RpcDescBase> >;
using Ctors = AZStd::vector<AZStd::intrusive_ptr<CtorDataBase> >;
const char* m_name = nullptr; ///< The name of the chunk
ReplicaChunkClassId m_chunkId; ///< The registered id of the ReplicaChunk this class will use
Fields m_fields; ///< list of data fields in the ReplicaChunk
Rpcs m_rpcs; ///< list of RPCs in the ReplicaChunk
Ctors m_ctors; ///< list of ctor callbacks to gather/apply ctor data
bool m_external = false; ///< If true, this chunk is separate from the class bound to it
};
/**
* Contains the chunk factory and field descriptions for a given class
*/
class ClassDesc
: public IntrusiveRefCounted
{
public:
AZ_CLASS_ALLOCATOR(ClassDesc, AZ::SystemAllocator, 0);
ClassDesc(const char* name = nullptr, const AZ::Uuid& typeId = AZ::Uuid());
public:
const char* m_name; ///< The name of the class that is bound
AZ::Uuid m_typeId; ///< The type that this binding represents (null for chunks)
ChunkDesc m_chunkDesc; ///< Descriptor for the chunk for this type
/// Functor which will register the ReplicaChunkDescriptor with the global registry
AZStd::function<bool()> RegisterChunkType;
/// Functor to unregister the ReplicaChunkDescriptor (during reflection removal)
AZStd::function<void()> UnregisterChunkType;
/// Functor which will create a ReplicaChunk and bind it to the given instance
AZStd::function<ReplicaChunkBase*()> CreateReplicaChunk;
/// Functor which can destroy a ReplicaChunk and free its memory
AZStd::function<void(ReplicaChunkBase*)> DestroyReplicaChunk;
/// Functor which binds an instance of this class to its RPCs for local dispatch
AZStd::function<void(NetBindable* bindable)> BindRpcs;
};
AZ_CLASS_ALLOCATOR(NetworkContext, AZ::SystemAllocator, 0);
AZ_RTTI(NetworkContext, "{B1172D4A-EA1B-441D-AAE6-A9933DAECA8A}", AZ::ReflectContext);
NetworkContext();
virtual ~NetworkContext();
/// Register a class with the NetworkContext for replication
template <class ClassType>
ClassBuilderPtr Class();
/// Create a replica chunk for a given class
ReplicaChunkBase* CreateReplicaChunk(const AZ::Uuid& typeId);
/// Create a replica chunk for a given class, template version
template <class ClassType>
ReplicaChunkBase* CreateReplicaChunk();
/// Destroy a replica chunk for a given class
void DestroyReplicaChunk(ReplicaChunkBase * chunk);
/// Bind an instance and a chunk to each other
void Bind(NetBindable * instance, ReplicaChunkPtr chunk, NetworkContextBindMode mode);
/// Returns whether or not a given type uses a reflected (automatic) ReplicaChunk
bool UsesSelfAsChunk(const AZ::Uuid & typeId) const;
/// Returns whether or not a given type uses a custom ReplicaChunk
bool UsesExternalChunk(const AZ::Uuid & typeId) const;
/// Return the size of the the chunk which will represent the given type
size_t GetReflectedChunkSize(const AZ::Uuid & typeId) const;
using FieldVisitor = AZStd::function<void(FieldDescBase*)>;
void EnumerateFields(const ReplicaChunkClassId&chunkId, FieldVisitor visitor) const;
void EnumerateFields(const AZ::Uuid & typeId, FieldVisitor visitor) const;
using RpcVisitor = AZStd::function<void(RpcDescBase*)>;
void EnumerateRpcs(const ReplicaChunkClassId&chunkId, RpcVisitor visitor) const;
void EnumerateRpcs(const AZ::Uuid & typeId, RpcVisitor visitor) const;
using CtorVisitor = AZStd::function<void(CtorDataBase*)>;
void EnumerateCtorData(const ReplicaChunkClassId&chunkId, CtorVisitor visitor) const;
void EnumerateCtorData(const AZ::Uuid & typeId, CtorVisitor visitor) const;
private:
template <class ClassType>
void InitReflectedChunkBinding(ClassDescPtr binding);
template <class ChunkType, typename DescriptorType = ExternalChunkDescriptor<ChunkType> >
void InitExternalChunkBinding(ClassDescPtr binding);
private:
ClassBindings m_classBindings;
ChunkBindings m_chunkBindings;
};
///////////////////////////////////////////////////////////////////////////
template <class ClassType>
NetworkContext::ClassBuilderPtr NetworkContext::Class()
{
static_assert((AZStd::is_base_of<NetBindable, ClassType>::value), "Classes reflected through NetworkContext must be derived from NetBindable");
const AZ::Uuid& typeId = AZ::AzTypeInfo<ClassType>::Uuid();
ClassDescPtr binding = nullptr;
if (IsRemovingReflection()) // Just remove the entire class definition
{
auto it = m_classBindings.find(typeId);
if (it != m_classBindings.end())
{
binding = it->second;
m_chunkBindings.erase(binding->m_chunkDesc.m_chunkId);
m_classBindings.erase(it);
}
}
else
{
auto ret = m_classBindings.insert_key(typeId);
AZ_Assert(ret.second, "Cannot register more than one type with the same Uuid in the NetworkContext");
binding = ret.first->second = aznew ClassDesc(AZ::AzTypeInfo<ClassType>::Name(), AZ::AzTypeInfo<ClassType>::Uuid());
}
return aznew ClassBuilder(this, binding);
}
template <class ClassType>
void NetworkContext::InitReflectedChunkBinding(ClassDescPtr binding)
{
if (!binding->RegisterChunkType)
{
binding->m_chunkDesc.m_name = ReflectedReplicaChunk<ClassType>::GetChunkName();
ReplicaChunkClassId chunkClassId = ReplicaChunkClassId(binding->m_chunkDesc.m_name);
m_chunkBindings[chunkClassId] = binding;
NetworkContext* netContext = this;
binding->RegisterChunkType = [chunkClassId, netContext]()
{
bool result = ReplicaChunkDescriptorTable::Get().RegisterChunkType<ReflectedReplicaChunk<ClassType>, AutoChunkDescriptor<ClassType> >();
ReplicaChunkDescriptor* desc = ReplicaChunkDescriptorTable::Get().FindReplicaChunkDescriptor(chunkClassId);
ReplicaChunkDescriptorTable::Get().BeginConstructReplicaChunk(desc);
// The offset recorded in NetBindableFields is the offset in the NetBindable
// We must compute the offset of the generated DataSets here and record the
// index from the descriptor
ptrdiff_t offset = sizeof(ReflectedReplicaChunk<ClassType>); // data sets are right after the ReflectedReplicaChunk<> in memory
netContext->EnumerateFields(chunkClassId,
[desc, &offset](FieldDescBase* field)
{
desc->RegisterDataSet(field->m_name, offset);
field->m_dataSetIdx = desc->GetDataSetIndex(offset);
offset += field->GetDataSetSize();
});
netContext->EnumerateRpcs(chunkClassId,
[desc, &offset](RpcDescBase* rpc)
{
desc->RegisterRPC(rpc->m_name, offset);
rpc->m_rpcIdx = desc->GetRpcIndex(offset);
offset += rpc->GetRpcSize();
});
AZ_Assert(offset == static_cast<ptrdiff_t>(ReflectedReplicaChunk<ClassType>::GetChunkSize()), "Overflow/underflow while registering DataSets for %s", ReflectedReplicaChunk<ClassType>::GetChunkName());
ReplicaChunkDescriptorTable::Get().EndConstructReplicaChunk();
return result;
};
binding->UnregisterChunkType = [chunkClassId]()
{
ReplicaChunkDescriptorTable::Get().UnregisterReplicaChunkDescriptor(chunkClassId);
};
binding->CreateReplicaChunk = [netContext, chunkClassId]()
{
ReflectedReplicaChunkBase* chunk = new(azmalloc(ReflectedReplicaChunk<ClassType>::GetChunkSize(), AZStd::alignment_of<ReflectedReplicaChunk<ClassType> >::value, AZ::SystemAllocator, ReflectedReplicaChunk<ClassType>::GetChunkName()))ReflectedReplicaChunk<ClassType>();
AZ::u8* dataStart = chunk->GetDataStart();
AZ::u8* dataEnd = reinterpret_cast<AZ::u8*>(chunk) + chunk->GetSize();
ptrdiff_t offset = 0;
netContext->EnumerateFields(chunkClassId,
[&offset, dataStart, dataEnd](FieldDescBase* field)
{
AZ_Assert((dataStart + offset) < dataEnd, "Overflow in NetworkContext::CreateReplicaChunk while creating %s", ReflectedReplicaChunk<ClassType>::GetChunkName());
void* dataSetMem = reinterpret_cast<void*>(dataStart + offset);
field->ConstructDataSet(dataSetMem);
offset += field->GetDataSetSize();
});
netContext->EnumerateRpcs(chunkClassId,
[&offset, dataStart, dataEnd](RpcDescBase* rpc)
{
AZ_Assert((dataStart + offset) < dataEnd, "Overflow in NetworkContext::CreateReplicaChunk while creating %s", ReflectedReplicaChunk<ClassType>::GetChunkName());
void* rpcMem = reinterpret_cast<void*>(dataStart + offset);
rpc->ConstructRpc(rpcMem);
offset += rpc->GetRpcSize();
});
AZ_Assert((dataStart + offset) == dataEnd, "Overflow/underflow in NetworkContext::CreateReplicaChunk while creating %s", ReflectedReplicaChunk<ClassType>::GetChunkName());
return chunk;
};
binding->DestroyReplicaChunk = [netContext, chunkClassId](ReplicaChunkBase* chunkBase)
{
AZ_Assert(chunkBase->GetDescriptor()->GetChunkTypeId() == chunkClassId, "Mismatched chunk type id for %s (0x%p)", ReflectedReplicaChunk<ClassType>::GetChunkName(), chunkBase);
ReflectedReplicaChunkBase* chunk = static_cast<ReflectedReplicaChunkBase*>(chunkBase);
chunk->Unbind();
AZ::u8* dataStart = chunk->GetDataStart();
AZ::u8* dataEnd = reinterpret_cast<AZ::u8*>(chunk) + chunk->GetSize();
ptrdiff_t offset = 0;
netContext->EnumerateFields(chunkClassId,
[&offset, dataStart, dataEnd](FieldDescBase* field)
{
AZ_Assert((dataStart + offset) < dataEnd, "Overflow in dtor while destroying %s", ReflectedReplicaChunk<ClassType>::GetChunkName());
void* dataSetMem = reinterpret_cast<void*>(dataStart + offset);
field->DestructDataSet(dataSetMem);
offset += field->GetDataSetSize();
});
netContext->EnumerateRpcs(chunkClassId,
[&offset, dataStart, dataEnd](RpcDescBase* rpc)
{
AZ_Assert((dataStart + offset) < dataEnd, "Overflow in NetworkContext::CreateReplicaChunk while creating %s", ReflectedReplicaChunk<ClassType>::GetChunkName());
void* rpcMem = reinterpret_cast<void*>(dataStart + offset);
rpc->DestructRpc(rpcMem);
offset += rpc->GetRpcSize();
});
AZ_Assert((dataStart + offset) == dataEnd, "Overflow/underflow in dtor while destroying %s", ReflectedReplicaChunk<ClassType>::GetChunkName());
chunk->~ReflectedReplicaChunkBase();
azfree(chunk, AZ::SystemAllocator, ReflectedReplicaChunk<ClassType>::GetChunkSize(), AZStd::alignment_of<ReflectedReplicaChunk<ClassType> >::value);
};
binding->BindRpcs = [netContext, chunkClassId](NetBindable* bindable)
{
ClassType* derivedInstance = static_cast<ClassType*>(bindable);
netContext->EnumerateRpcs(chunkClassId,
[derivedInstance](const RpcDescBase* rpc)
{
NetBindableRpcBase* bindableRpc = reinterpret_cast<NetBindableRpcBase*>(reinterpret_cast<AZ::u8*>(derivedInstance) + rpc->GetOffset());
bindableRpc->Bind(derivedInstance);
});
};
binding->m_chunkDesc.m_chunkId = chunkClassId;
}
}
template <class ChunkType, typename DescriptorType>
void NetworkContext::InitExternalChunkBinding(ClassDescPtr binding)
{
if (!binding->RegisterChunkType)
{
binding->m_chunkDesc.m_name = ChunkType::GetChunkName();
ReplicaChunkClassId chunkClassId = ReplicaChunkClassId(binding->m_chunkDesc.m_name);
m_chunkBindings[chunkClassId] = binding;
const AZ::Uuid& typeId = binding->m_typeId;
NetworkContext* netContext = this;
binding->RegisterChunkType = [chunkClassId, typeId, netContext]()
{
bool result = ReplicaChunkDescriptorTable::Get().RegisterChunkType<ChunkType, DescriptorType>();
NetworkContextChunkDescriptor* desc = static_cast<NetworkContextChunkDescriptor*>(ReplicaChunkDescriptorTable::Get().FindReplicaChunkDescriptor(chunkClassId));
desc->Bind(typeId);
netContext->EnumerateFields(chunkClassId,
[desc](FieldDescBase* field)
{
desc->RegisterDataSet(field->m_name, field->m_offset);
field->m_dataSetIdx = desc->GetDataSetIndex(field->m_offset);
});
netContext->EnumerateRpcs(chunkClassId,
[desc](RpcDescBase* rpc)
{
desc->RegisterRPC(rpc->m_name, rpc->m_offset);
});
return result;
};
binding->UnregisterChunkType = [chunkClassId]()
{
return ReplicaChunkDescriptorTable::Get().UnregisterReplicaChunkDescriptor(chunkClassId);
};
binding->CreateReplicaChunk = []()
{
return aznew ChunkType();
};
binding->DestroyReplicaChunk = [](ReplicaChunkBase* chunk)
{
delete chunk;
};
binding->m_chunkDesc.m_chunkId = chunkClassId;
}
}
template <class ClassType>
ReplicaChunkBase* NetworkContext::CreateReplicaChunk()
{
return CreateReplicaChunk(AZ::AzTypeInfo<ClassType>::Uuid());
}
///////////////////////////////////////////////////////////////////////////
template <class DataSetType>
NetworkContext::DataSetDesc<DataSetType>::DataSetDesc(const char* name, ptrdiff_t offset)
: NetworkContext::FieldDescBase(name, offset)
{
}
///////////////////////////////////////////////////////////////////////////
template <typename FieldType>
NetworkContext::NetBindableFieldDesc<FieldType>::NetBindableFieldDesc(const char* name, ptrdiff_t offset)
: NetworkContext::FieldDescBase(name, offset)
{
}
///////////////////////////////////////////////////////////////////////////
template <class ClassType, class DataType, typename MarshalerType>
void NetworkContext::CtorDataDesc<ClassType, DataType, MarshalerType>::Marshal(NetBindable* netBindable, WriteBuffer& buffer) const
{
ClassType* instance = static_cast<ClassType*>(netBindable);
DataType data = m_get(instance);
buffer.Write(data, m_marshaler);
}
template <class ClassType, class DataType, typename MarshalerType>
void NetworkContext::CtorDataDesc<ClassType, DataType, MarshalerType>::Unmarshal(ReadBuffer& buffer, NetBindable* netBindable) const
{
ClassType* instance = static_cast<ClassType*>(netBindable);
DataType data;
buffer.Read(data, m_marshaler);
if (instance)
{
m_set(instance, data);
}
}
template <class ClassType, class DataType, typename MarshalerType>
void NetworkContext::CtorDataDesc<ClassType, DataType, MarshalerType>::Copy(ReadBuffer& src, WriteBuffer& dest) const
{
DataType data;
src.Read(data, m_marshaler);
dest.Write(data, m_marshaler);
}
///////////////////////////////////////////////////////////////////////////
template <class ChunkType, typename DescriptorType>
NetworkContext::ClassBuilderPtr NetworkContext::ClassBuilder::Chunk()
{
if (!m_context->IsRemovingReflection())
{
static_assert((AZStd::is_base_of<ReplicaChunkBase, ChunkType>::value), "ReplicaChunks being registered with the NetworkContext must derive from ReplicaChunk");
static_assert((AZStd::is_base_of<NetworkContextChunkDescriptor, DescriptorType>::value), "Chunk bindings via NetworkContext must use a NetworkContextChunkDescriptor derived descriptor");
AZ_Assert(!m_binding->m_typeId.IsNull(), "Cannot register a ReplicaChunk for a class which has not been declared to the NetworkContext");
AZ_Assert(!m_binding->m_chunkDesc.m_chunkId, "Cannot register more than one ReplicaChunk binding for a class in the NetworkContext");
m_context->InitExternalChunkBinding<ChunkType, DescriptorType>(m_binding);
m_binding->m_chunkDesc.m_external = true;
}
return this;
}
template <class ClassType, typename FieldType>
typename AZStd::enable_if<AZStd::is_base_of<NetBindableFieldBase, FieldType>::value, NetworkContext::ClassBuilderPtr>::type
NetworkContext::ClassBuilder::Field(const char* name, FieldType ClassType::* address)
{
if (!m_context->IsRemovingReflection())
{
AZ_Assert(!m_binding->m_typeId.IsNull(), "Cannot register a field for a class which has not been declared to the NetworkContext");
AZ_Assert(!m_binding->m_chunkDesc.m_external, "Cannot register a NetBindable::Field from within an external chunk");
m_context->InitReflectedChunkBinding<ClassType>(m_binding);
ptrdiff_t offset = reinterpret_cast<ptrdiff_t>(&(reinterpret_cast<ClassType const volatile*>(0)->*address));
m_binding->m_chunkDesc.m_fields.push_back(aznew NetBindableFieldDesc<FieldType>(name, offset));
}
return this;
}
template <class ClassType, typename DataSetType>
typename AZStd::enable_if<AZStd::is_base_of<DataSetBase, DataSetType>::value, NetworkContext::ClassBuilderPtr>::type
NetworkContext::ClassBuilder::Field(const char* name, DataSetType ClassType::* address)
{
if (!m_context->IsRemovingReflection())
{
AZ_Assert(!m_binding->m_typeId.IsNull(), "Cannot register a field for a class which has not been declared to the NetworkContext");
ptrdiff_t offset = reinterpret_cast<ptrdiff_t>(&(reinterpret_cast<ClassType const volatile*>(0)->*address));
m_binding->m_chunkDesc.m_fields.push_back(aznew DataSetDesc<DataSetType>(name, offset));
}
return this;
}
template <class ClassType, class InterfaceType, typename ... Args, class Traits, typename RpcBindType>
typename AZStd::enable_if<AZStd::is_base_of<RpcBase, RpcBindType>::value, NetworkContext::ClassBuilderPtr>::type
NetworkContext::ClassBuilder::RPC(const char* name, RpcBindType ClassType::* rpc)
{
if (!m_context->IsRemovingReflection())
{
static_assert((AZStd::is_base_of<ReplicaChunkInterface, InterfaceType>::value), "Cannot bind an RPC call to an object which is not a ReplicaChunkInterface");
AZ_Assert(!m_binding->m_typeId.IsNull(), "Cannot register an RPC for a class which has not been declared to the NetworkContext");
ptrdiff_t offset = reinterpret_cast<ptrdiff_t>(&(reinterpret_cast<ClassType const volatile*>(0)->*rpc));
m_binding->m_chunkDesc.m_rpcs.push_back(aznew RpcDescBase(name, offset));
}
return this;
}
template <class ClassType, class InterfaceType, typename ... Args, class Traits, typename RpcBindType>
typename AZStd::enable_if<AZStd::is_base_of<NetBindableRpcBase, RpcBindType>::value, NetworkContext::ClassBuilderPtr>::type
NetworkContext::ClassBuilder::RPC(const char* name, RpcBindType ClassType::* rpc)
{
if (!m_context->IsRemovingReflection())
{
static_assert((AZStd::is_base_of<ReplicaChunkInterface, InterfaceType>::value), "Cannot bind an RPC call to an object which is not a ReplicaChunkInterface");
AZ_Assert(!m_binding->m_typeId.IsNull(), "Cannot register an RPC for a class which has not been declared to the NetworkContext");
m_context->InitReflectedChunkBinding<ClassType>(m_binding);
ptrdiff_t offset = reinterpret_cast<ptrdiff_t>(&(reinterpret_cast<ClassType const volatile*>(0)->*rpc));
m_binding->m_chunkDesc.m_rpcs.push_back(aznew NetBindableRpcDesc<RpcBindType>(name, offset));
}
return this;
}
template <class ClassType, class DataType, typename GetterFunction, typename SetterFunction, typename MarshalerType>
NetworkContext::ClassBuilderPtr NetworkContext::ClassBuilder::CtorDataImpl(const char* name, GetterFunction getter, SetterFunction setter, const MarshalerType&)
{
if (!m_context->IsRemovingReflection())
{
m_context->InitReflectedChunkBinding<ClassType>(m_binding);
auto get = [getter](NetBindable* nb) -> DataType { return (*static_cast<ClassType*>(nb).*getter)(); };
auto set = [setter](NetBindable* nb, const DataType& data) { (*static_cast<ClassType*>(nb).*setter)(data); };
m_binding->m_chunkDesc.m_ctors.push_back(aznew CtorDataDesc<ClassType, DataType, MarshalerType>(name, get, set));
}
return this;
}
///////////////////////////////////////////////////////////////////////////
template <class ClassType>
const char* ReflectedReplicaChunk<ClassType>::GetChunkName()
{
static char name[128] = { 0 };
if (!name[0])
{
AZ::Internal::AzTypeInfoSafeCat(name, AZ_ARRAY_SIZE(name), "ReflectedReplicaChunk<");
AZ::Internal::AzTypeInfoSafeCat(name, AZ_ARRAY_SIZE(name), AZ::AzTypeInfo<ClassType>::Name());
AZ::Internal::AzTypeInfoSafeCat(name, AZ_ARRAY_SIZE(name), ">");
}
return name;
}
template <class ClassType>
size_t ReflectedReplicaChunk<ClassType>::GetChunkSize()
{
static size_t chunkSize = 0;
if (chunkSize == 0)
{
NetworkContext* netContext = nullptr;
EBUS_EVENT_RESULT(netContext, NetSystemRequestBus, GetNetworkContext);
AZ_Assert(netContext, "No NetworkContext found while trying to compute chunk size");
if (!netContext)
{
return 0;
}
chunkSize = sizeof(ReflectedReplicaChunk<ClassType>) + netContext->GetReflectedChunkSize(AZ::AzTypeInfo<ClassType>::Uuid());
}
return chunkSize;
}
template <class ClassType>
void ReflectedReplicaChunk<ClassType>::Bind(NetBindable* instance, NetworkContextBindMode mode)
{
SetHandler(instance);
ClassType* derivedInstance = azrtti_cast<ClassType*>(instance);
AZ_Assert(derivedInstance, "Unable to convert NetBindable to %s", AZ::AzTypeInfo<ClassType>::Name());
ReplicaChunkDescriptor* desc = GetDescriptor();
NetworkContext* netContext = nullptr;
EBUS_EVENT_RESULT(netContext, NetSystemRequestBus, GetNetworkContext);
netContext->EnumerateFields(desc->GetChunkTypeId(),
[this, derivedInstance, desc, mode](NetworkContext::FieldDescBase* field)
{
NetBindableFieldBase* bindableField = reinterpret_cast<NetBindableFieldBase*>(reinterpret_cast<AZ::u8*>(derivedInstance) + field->GetOffset());
DataSetBase* dataSet = desc->GetDataSet(this, field->GetDataSetIndex());
bindableField->Bind(dataSet, mode);
});
netContext->EnumerateRpcs(desc->GetChunkTypeId(),
[this, derivedInstance, desc](NetworkContext::RpcDescBase* rpc)
{
NetBindableRpcBase* bindableRpc = reinterpret_cast<NetBindableRpcBase*>(reinterpret_cast<AZ::u8*>(derivedInstance) + rpc->GetOffset());
RpcBase* rpcBase = desc->GetRpc(this, rpc->GetRpcIndex());
bindableRpc->Bind(rpcBase);
});
// Transfer any stored ctor data from the buffer -> NetBindable instance
if (m_ctorBuffer.Size() > 0)
{
ReadBuffer ctorBuffer(m_ctorBuffer.GetEndianType(), m_ctorBuffer.Get(), m_ctorBuffer.Size());
netContext->EnumerateCtorData(desc->GetChunkTypeId(),
[instance, &ctorBuffer](NetworkContext::CtorDataBase* ctorData)
{
ctorData->Unmarshal(ctorBuffer, instance);
});
}
}
template <class ClassType>
void ReflectedReplicaChunk<ClassType>::Unbind()
{
ReplicaChunkInterface* handler = GetHandler();
if (!handler || handler == this)
{
return;
}
NetBindable* netBindable = static_cast<NetBindable*>(handler);
ClassType* derivedInstance = azrtti_cast<ClassType*>(netBindable);
AZ_Assert(derivedInstance, "Unable to convert NetBindable to %s. Have you forgotten to derive your component from AzFramework::NetBindable?", AZ::AzTypeInfo<ClassType>::Name());
if (derivedInstance)
{
ReplicaChunkDescriptor* desc = GetDescriptor();
NetworkContext* netContext = nullptr;
EBUS_EVENT_RESULT(netContext, NetSystemRequestBus, GetNetworkContext);
netContext->EnumerateFields(desc->GetChunkTypeId(),
[derivedInstance](NetworkContext::FieldDescBase* field)
{
NetBindableFieldBase* bindableField = reinterpret_cast<NetBindableFieldBase*>(reinterpret_cast<AZ::u8*>(derivedInstance) + field->GetOffset());
bindableField->Bind(nullptr, NetworkContextBindMode::NonAuthoritative);
});
netContext->EnumerateRpcs(desc->GetChunkTypeId(),
[derivedInstance](NetworkContext::RpcDescBase* rpc)
{
NetBindableRpcBase* bindableRpc = reinterpret_cast<NetBindableRpcBase*>(reinterpret_cast<AZ::u8*>(derivedInstance) + rpc->GetOffset());
bindableRpc->Bind(derivedInstance);
});
}
// We have disconnected from the handler and erased any connections from DataFields or Rpcs
SetHandler(nullptr);
}
} // namespace AZ
@@ -13,6 +13,7 @@
#include <AzCore/Component/EntityId.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Vector3.h>
namespace Physics
@@ -26,12 +26,9 @@
#include <AzCore/std/string/conversions.h>
#include <AzFramework/Script/ScriptComponent.h>
#include <AzFramework/Script/ScriptNetBindings.h>
#include <AzFramework/Network/NetworkContext.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <GridMate/Replica/ReplicaChunk.h>
#include <AzFramework/IO/LocalFileIO.h>
@@ -429,83 +426,60 @@ namespace AzFramework
{
LSV_BEGIN(lua, 1);
// calling format __index(table,key)
ScriptNetBindingTable* netBindingTable = reinterpret_cast<ScriptNetBindingTable*>(lua_touserdata(lua, lua_upvalueindex(1)));
AZ::ScriptContext::FromNativeContext(lua)->Error(AZ::ScriptContext::ErrorType::Warning, true,
"Property %s not found in entity table. Please push this property to your slice to avoid decrease in performance.", lua_tostring(lua, -1));
int lookupKey = lua_gettop(lua);
bool readValue = false;
if (netBindingTable != nullptr)
int lookupTable = lookupKey - 1;
// This is a slow function and it's made slow so we don't cache any extra data.
// This is done because this function will be called only the exported components
// and script are not in sync and we added new properties.
lua_getmetatable(lua, -2); // get the metatable which will be the top property table
int entityProperties = lua_gettop(lua);
if (lua_getmetatable(lua, -1) == 0) // get the metatable of the property which will be the original table
{
AZ_Error("ScriptComponent",netBindingTable->GetScriptContext() != nullptr,"ScriptNetBindingTable is missing ScriptContext.");
AZ_Error("ScriptComponent",netBindingTable->GetScriptContext() == nullptr || netBindingTable->GetScriptContext()->NativeContext() == lua,"Trying to use a NetBindingTable in wrong lua context");
AZ::ScriptContext* scriptContext = netBindingTable->GetScriptContext();
if (scriptContext)
{
AZ::ScriptDataContext stackContext;
scriptContext->ReadStack(stackContext);
readValue = netBindingTable->InspectTableValue(stackContext);
}
// we are looking at top level properties
lua_pushvalue(lua, -2); // copy the key
lua_rawget(lua, -2); // read the value
}
if (!readValue)
else
{
AZ::ScriptContext::FromNativeContext(lua)->Error(AZ::ScriptContext::ErrorType::Warning, true,
"Property %s not found in entity table. Please push this property to your slice to avoid decrease in performance.", lua_tostring(lua, -1));
int lookupKey = lua_gettop(lua);
int lookupTable = lookupKey - 1;
// This is a slow function and it's made slow so we don't cache any extra data.
// This is done because this function will be called only the exported components
// and script are not in sync and we added new properties.
lua_getmetatable(lua, -2); // get the metatable which will be the top property table
int entityProperties = lua_gettop(lua);
if (lua_getmetatable(lua, -1) == 0) // get the metatable of the property which will be the original table
// we are looking into the sub table, so do a slow traversal
int scriptProperties = lua_gettop(lua);
if (!Properties__IndexFindSubtable(lua, lookupTable, entityProperties, scriptProperties))
{
// we are looking at top level properties
lua_pushvalue(lua, -2); // copy the key
lua_rawget(lua, -2); // read the value
lua_pushnil(lua);
return 1; // we did not find the table
}
else
{
// we are looking into the sub table, so do a slow traversal
int scriptProperties = lua_gettop(lua);
if (!Properties__IndexFindSubtable(lua, lookupTable, entityProperties, scriptProperties))
{
lua_pushnil(lua);
return 1; // we did not find the table
}
else
{
lua_pushvalue(lua, lookupKey);
lua_rawget(lua, -2);
}
}
if (lua_istable(lua, -1))
{
// if we are here the target table is on the top if the stack
lua_pushstring(lua, ScriptComponent::DefaultFieldName);
lua_pushvalue(lua, lookupKey);
lua_rawget(lua, -2);
if (lua_isnil(lua, -1))
{
// parent table is a group, pop the value and return the table
lua_pop(lua, 1);
}
}
// Duplicate the value, so once the storage is done its on top of the stack, and returned
lua_pushvalue(lua, -1);
// Push key, and then move it below the value
lua_pushvalue(lua, lookupKey);
lua_insert(lua, -2);
// Cache the value so that subsequent accesses to this property don't result in warnings
lua_rawset(lua, lookupTable);
}
if (lua_istable(lua, -1))
{
// if we are here the target table is on the top if the stack
lua_pushstring(lua, ScriptComponent::DefaultFieldName);
lua_rawget(lua, -2);
if (lua_isnil(lua, -1))
{
// parent table is a group, pop the value and return the table
lua_pop(lua, 1);
}
}
// Duplicate the value, so once the storage is done its on top of the stack, and returned
lua_pushvalue(lua, -1);
// Push key, and then move it below the value
lua_pushvalue(lua, lookupKey);
lua_insert(lua, -2);
// Cache the value so that subsequent accesses to this property don't result in warnings
lua_rawset(lua, lookupTable);
return 1;
}
//=========================================================================
@@ -515,30 +489,7 @@ namespace AzFramework
{
LSV_BEGIN_VARIABLE(lua);
// calling format __newindex(table,key,value)
ScriptNetBindingTable* netBindingTable = reinterpret_cast<ScriptNetBindingTable*>(lua_touserdata(lua, lua_upvalueindex(1)));
if (netBindingTable != nullptr)
{
AZ_Error("ScriptContext",netBindingTable->GetScriptContext() != nullptr,"ScriptNetBindingTable is missing ScriptContext.");
AZ_Error("ScriptContext",netBindingTable->GetScriptContext() == nullptr || netBindingTable->GetScriptContext()->NativeContext() == lua,"Trying to use a NetBindingTable in wrong lua context");
AZ::ScriptContext* scriptContext = netBindingTable->GetScriptContext();
if (scriptContext)
{
AZ::ScriptDataContext stackContext;
scriptContext->ReadStack(stackContext);
const bool assignedValue = netBindingTable->AssignTableValue(stackContext);
if (assignedValue)
{
LSV_END_VARIABLE(0);
return 0;
}
}
}
// If we didn't assign the value above, we want
// to raw set the value to avoid coming back in here.
// We want to raw set the value to avoid coming back in here.
lua_rawset(lua, 1);
LSV_END_VARIABLE(-2);
return 0;
@@ -553,7 +504,6 @@ namespace AzFramework
// [8/9/2013]
//=========================================================================
const char* ScriptComponent::NetRPCFieldName = "NetRPCs";
const char* ScriptComponent::DefaultFieldName = "default";
ScriptComponent::ScriptComponent()
@@ -561,7 +511,6 @@ namespace AzFramework
, m_contextId(AZ::ScriptContextIds::DefaultScriptContextId)
, m_script(AZ::Data::AssetLoadBehavior::PreLoad)
, m_table(LUA_NOREF)
, m_netBindingTable(nullptr)
{
m_properties.m_name = "Properties";
}
@@ -573,8 +522,6 @@ namespace AzFramework
ScriptComponent::~ScriptComponent()
{
m_properties.Clear();
delete m_netBindingTable;
}
//=========================================================================
@@ -604,11 +551,6 @@ namespace AzFramework
return m_properties.GetProperty(propertyName);
}
const AZ::ScriptProperty* ScriptComponent::GetNetworkedScriptProperty(const char* propertyName) const
{
return m_netBindingTable->FindScriptProperty(propertyName);
}
void ScriptComponent::Init()
{
// Grab the script context
@@ -622,11 +564,6 @@ namespace AzFramework
//=========================================================================
void ScriptComponent::Activate()
{
if (m_isSyncEnabled && m_netBindingTable == nullptr)
{
m_netBindingTable = aznew ScriptNetBindingTable();
}
// if we have valid asset listen for script asset events, like reload
if (m_script.GetId().IsValid())
{
@@ -681,43 +618,6 @@ namespace AzFramework
LoadScript();
}
//=========================================================================
// ScriptComponent::GetNetworkBinding
//=========================================================================
GridMate::ReplicaChunkPtr ScriptComponent::GetNetworkBinding()
{
if (m_netBindingTable == nullptr)
{
m_netBindingTable = aznew ScriptNetBindingTable();
}
return m_netBindingTable->GetNetworkBinding();
}
//=========================================================================
// ScriptComponent::SetNetworkBinding
//=========================================================================
void ScriptComponent::SetNetworkBinding(GridMate::ReplicaChunkPtr chunk)
{
if (m_netBindingTable == nullptr)
{
m_netBindingTable = aznew ScriptNetBindingTable();
}
m_netBindingTable->SetNetworkBinding(chunk);
}
//=========================================================================
// ScriptComponent::UnbindFromNetwork
//=========================================================================
void ScriptComponent::UnbindFromNetwork()
{
if (m_netBindingTable)
{
m_netBindingTable->UnbindFromNetwork();
}
}
//=========================================================================
// LoadScript
//=========================================================================
@@ -741,11 +641,6 @@ namespace AzFramework
AZ_PROFILE_SCOPE_DYNAMIC(AZ::Debug::ProfileCategory::Script, "Unload: %s", m_script.GetHint().c_str());
DestroyEntityTable();
if (m_netBindingTable)
{
m_netBindingTable->Unload();
}
}
//=========================================================================
@@ -798,12 +693,10 @@ namespace AzFramework
// set the __index so we can read values in case we change the script
// after we export the component
lua_pushliteral(lua, "__index");
lua_pushlightuserdata(lua, m_netBindingTable);
lua_pushcclosure(lua, &Internal::Properties__Index, 1);
lua_rawset(lua, -3);
lua_pushliteral(lua, "__newindex");
lua_pushlightuserdata(lua, m_netBindingTable);
lua_pushcclosure(lua, &Internal::Properties__NewIndex, 1);
lua_rawset(lua, -3);
}
@@ -835,8 +728,7 @@ namespace AzFramework
{
const char* tableName = lua_tolstring(lua, -2, nullptr);
if (strncmp(tableName, "__", 2) == 0 || // skip metatables
strcmp(tableName, propertyTableName) == 0 || // Skip the Properties table
strcmp(tableName, ScriptComponent::NetRPCFieldName) == 0) // Want to skip the RPC table as well
strcmp(tableName, propertyTableName) == 0) // Skip the Properties table
{
break;
}
@@ -904,13 +796,10 @@ namespace AzFramework
}
lua_createtable(lua, 0, 1); // Create entity table;
int entityStackIndex = lua_gettop(lua);
[[maybe_unused]] int entityStackIndex = lua_gettop(lua);
// Stack: ScriptRootTable PropertiesTable EntityTable
// Create our network binding.
CreateNetworkBindingTable(baseStackIndex, entityStackIndex);
if (basePropertyTable > -1) // if property table exists
{
CreatePropertyGroup(m_properties, basePropertyTable, lua_gettop(lua), basePropertyTable, true);
@@ -932,11 +821,6 @@ namespace AzFramework
// Keep the entity table in the registry
m_table = luaL_ref(lua, LUA_REGISTRYINDEX);
if (m_netBindingTable)
{
m_netBindingTable->FinalizeNetworkTable(m_context, m_table);
}
// call OnActivate
lua_pushliteral(lua, "OnActivate");
lua_rawget(lua, baseStackIndex); // ScriptTable[OnActivate]
@@ -993,18 +877,6 @@ namespace AzFramework
}
}
//=========================================================================
// CreateNetworkBindingTable
// [6/27/2016]
//=========================================================================
void ScriptComponent::CreateNetworkBindingTable(int baseStackIndex, int entityStackIndex)
{
if (m_netBindingTable)
{
m_netBindingTable->CreateNetworkBindingTable(m_context, baseStackIndex, entityStackIndex);
}
}
//=========================================================================
// CreatePropertyGroup
// [3/3/2014]
@@ -1028,12 +900,10 @@ namespace AzFramework
// Ensure that this instance of Properties table has the proper __index and __newIndex metamethods.
lua_newtable(lua); // This new table will become the Properties instance metatable. Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {}
lua_pushliteral(lua, "__index"); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {} __index
lua_pushlightuserdata(lua, m_netBindingTable); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {} __index m_netBinding
lua_pushcclosure(lua, &Internal::Properties__Index, 1); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {} __index function
lua_rawset(lua, -3); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {__index=Internal::Properties__Index}
lua_pushliteral(lua, "__newindex");
lua_pushlightuserdata(lua, m_netBindingTable);
lua_pushcclosure(lua, &Internal::Properties__NewIndex, 1);
lua_rawset(lua, -3); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {__index=Internal::Properties__Index __newindex=Internal::Properties__NewIndex}
lua_setmetatable(lua, -2); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {Meta{__index=Internal::Properties__Index __newindex=Internal::Properties__NewIndex} }
@@ -1050,55 +920,6 @@ namespace AzFramework
{
AZ::ScriptProperty* prop = group.m_properties[i];
if (m_netBindingTable != nullptr)
{
lua_pushlstring(lua, prop->m_name.c_str(), prop->m_name.length());
lua_rawget(lua, propertyGroupTableIndex);
// Stack: ... SomePropertyInThePropertiesTable. This may be any basic lua type (number, string, table etc)
if (lua_istable(lua, -1))
{
bool isNetworkedProperty = false;
AZ::ScriptDataContext stackContext;
// If we find a table value. We want to inspect it for information.
if (m_context->ReadStack(stackContext))
{
// check if the current property, which is a table, has a sub-table called "netSynched"
lua_pushliteral(lua, "netSynched"); // Stack: ... SomePropertyInThePropertiesTable netSynched
lua_rawget(lua, -2); // Stack: ... SomePropertyInThePropertiesTable NetSynchedSubTable/nil
if (stackContext.IsTable(-1))
{
AZ::ScriptDataContext networkTableContext;
if (stackContext.InspectTable(-1, networkTableContext)) // Stack: ... SomePropertyInThePropertiesTable NetSynchedSubTable NetSynchedSubTable nil nil
{
// RegisterDataSet will make sure our __NewIndex function callback will be triggered whenever modifying netSynched Properties.
//isNetworkedProperty = true;
isNetworkedProperty = m_netBindingTable->RegisterDataSet(networkTableContext, prop);
}
}
// Network binding table
lua_pop(lua, 1); // Stack: ... SomePropertyInThePropertiesTable
}
// Pop this PropertiesTable's property
lua_pop(lua, 1);
// If the property is networked, we don't want to copy it over into the table.
if (isNetworkedProperty)
{
continue;
}
}
else
{
// Remove the value we just pushed onto the stack
lua_pop(lua, 1);
}
}
lua_pushlstring(lua, prop->m_name.c_str(), prop->m_name.length());
if (prop->Write(*m_context))
{
@@ -1157,7 +978,7 @@ namespace AzFramework
return true;
};
serializeContext->Class<ScriptComponent, AZ::Component, NetBindable>()
serializeContext->Class<ScriptComponent, AZ::Component>()
->Version(3, converter)
->Field("ContextID", &ScriptComponent::m_contextId)
->Field("Properties", &ScriptComponent::m_properties)
@@ -1174,8 +995,6 @@ namespace AzFramework
AZ::ScriptProperties::Reflect(reflection);
}
}
ScriptNetBindingTable::Reflect(reflection);
}
//=========================================================================
@@ -20,8 +20,6 @@
#include <AzCore/std/string/string.h>
#include <AzCore/std/smart_ptr/intrusive_ptr.h>
#include <AzFramework/Network/NetBindable.h>
namespace AZ
{
class ScriptProperty;
@@ -37,8 +35,6 @@ namespace AzToolsFramework
namespace AzFramework
{
class ScriptNetBindingTable;
struct ScriptCompileRequest;
using WriteFunction = AZStd::function< AZ::Outcome<void, AZStd::string>(const ScriptCompileRequest&, AZ::IO::GenericStream& in, AZ::IO::GenericStream& out) >;
@@ -92,15 +88,13 @@ namespace AzFramework
class ScriptComponent
: public AZ::Component
, private AZ::Data::AssetBus::Handler
, public AzFramework::NetBindable
{
friend class AzToolsFramework::Components::ScriptEditorComponent;
public:
static const char* NetRPCFieldName;
static const char* DefaultFieldName;
AZ_COMPONENT(AzFramework::ScriptComponent, "{8D1BC97E-C55D-4D34-A460-E63C57CD0D4B}", NetBindable);
AZ_COMPONENT(AzFramework::ScriptComponent, "{8D1BC97E-C55D-4D34-A460-E63C57CD0D4B}", AZ::Component);
/// \red ComponentDescriptor::Reflect
static void Reflect(AZ::ReflectContext* reflection);
@@ -116,7 +110,6 @@ namespace AzFramework
// Methods used for unit tests
AZ::ScriptProperty* GetScriptProperty(const char* propertyName);
const AZ::ScriptProperty* GetNetworkedScriptProperty(const char* propertyName) const;
protected:
ScriptComponent(const ScriptComponent&) = delete;
@@ -133,13 +126,6 @@ namespace AzFramework
void OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// NetBindable
GridMate::ReplicaChunkPtr GetNetworkBinding() override;
void SetNetworkBinding(GridMate::ReplicaChunkPtr chunk) override;
void UnbindFromNetwork() override;
//////////////////////////////////////////////////////////////////////////
/// Load script (unless already by other instances) and creates the script instance into the VM
void LoadScript();
/// Removes the script instance and unloads the script (unless needed by other instances)
@@ -152,8 +138,6 @@ namespace AzFramework
void CreateEntityTable();
void DestroyEntityTable();
void CreateNetworkBindingTable(int baseStackIndex, int entityStackIndex);
void CreatePropertyGroup(const ScriptPropertyGroup& group, int propertyGroupTableIndex, int parentIndex, int metatableIndex, bool isRoot);
AZ::ScriptContext* m_context; ///< Context in which the script will be running
@@ -161,7 +145,6 @@ namespace AzFramework
AZ::Data::Asset<AZ::ScriptAsset> m_script; ///< Reference to the script asset used for this component.
int m_table; ///< Cached table index
ScriptPropertyGroup m_properties; ///< List with all properties that were tweaked in the editor and should override values in the m_sourceScriptName class inside m_script.
ScriptNetBindingTable* m_netBindingTable; ///< Table that will hold our networked script values, and manage callbacks
};
} // namespace AZ
@@ -1,573 +0,0 @@
/*
* 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 <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Script/ScriptProperty.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/string/string.h>
#include <GridMate/Serialize/Buffer.h>
#include <GridMate/Serialize/DataMarshal.h>
#include <GridMate/Serialize/UuidMarshal.h>
#include <GridMate/Serialize/ContainerMarshal.h>
#include <AzFramework/Script/ScriptNetBindings.h>
#include <AzFramework/Network/DynamicSerializableFieldMarshaler.h>
#include <AzFramework/Network/EntityIdMarshaler.h>
#include "AzFramework/Script/ScriptMarshal.h"
namespace AzFramework
{
////////////////////////////
// ScriptPropertyMarshaler
////////////////////////////
template<class T>
bool UnmarshalGenericType(AZ::DynamicSerializableField& serializableField, GridMate::ReadBuffer& rb)
{
bool valueChanged = true;
GridMate::Marshaler<AZ::DynamicSerializableField> serializableFieldMarshaler;
// Store the old value, to compare with the unmarshaled value, to signal
T oldValue = (*serializableField.Get<T>());
serializableFieldMarshaler.Unmarshal(serializableField,rb);
// If our type hasn't changed, compare the values.
if (serializableField.m_typeId == T::TYPEINFO_Uuid())
{
valueChanged = !(oldValue == (*serializableField.Get<T>()));
}
return valueChanged;
}
class ScriptPropertyTableMarshalerHelper
{
public:
template<typename T>
static void MarshalScriptPropertyGenericMap(const ScriptPropertyMarshaler& scriptPropertyMarshaler, GridMate::WriteBuffer& wb, const AZ::ScriptPropertyTable* scriptPropertyTable)
{
GridMate::Marshaler<AZ::u32> sizeMarshaler;
auto mapIter = scriptPropertyTable->m_genericMapping.find(T::TYPEINFO_Uuid());
if (mapIter != scriptPropertyTable->m_genericMapping.end())
{
AZ::ScriptPropertyGenericClassMapImpl<T>* genericClassKeyMap = static_cast<AZ::ScriptPropertyGenericClassMapImpl<T>*>(mapIter->second);
auto& valueMap = genericClassKeyMap->GetPairMapping();
// We will write out all of our keys. Since it is easier to write out nil values for the properties.
sizeMarshaler.Marshal(wb,static_cast<AZ::u32>(valueMap.size()));
GridMate::Marshaler<T> keyMarshaler;
for (auto& mapPair : valueMap)
{
keyMarshaler.Marshal(wb,mapPair.first);
scriptPropertyMarshaler.Marshal(wb,mapPair.second.m_valueProperty);
}
}
else
{
sizeMarshaler.Marshal(wb,0);
}
}
template<typename T>
static bool UnmarshalScriptPropertyGenericMap(const ScriptPropertyMarshaler& scriptPropertyMarshaler, AZ::ScriptPropertyTable* scriptPropertyTable, GridMate::ReadBuffer& rb)
{
bool valueChanged = false;
AZ::SerializeContext* useContext = nullptr;
EBUS_EVENT_RESULT(useContext, AZ::ComponentApplicationBus, GetSerializeContext);
if (useContext)
{
const AZ::SerializeContext::ClassData* classData = useContext->FindClassData(T::TYPEINFO_Uuid());
if (classData && classData->m_factory)
{
auto mapIter = scriptPropertyTable->m_genericMapping.find(T::TYPEINFO_Uuid());
if (mapIter != scriptPropertyTable->m_genericMapping.end())
{
// This whole thing is an in-place map update.
// to try to minimize the number of allocations. We try to re-use objects as much as possible.
//
// Two phase approach: Step one, update all of the existing properties, while keeping track of all of the used keys.
// Step two, go through and delete any unupdated keys from the mapping.
AZ::ScriptPropertyGenericClassMapImpl<T>* genericClassKeyMap = static_cast<AZ::ScriptPropertyGenericClassMapImpl<T>*>(mapIter->second);
AZStd::unordered_set<T> newKeys;
GridMate::Marshaler<AZ::u32> sizeMarshaler;
AZ::u32 mapSize;
sizeMarshaler.Unmarshal(mapSize,rb);
auto& valueMap = genericClassKeyMap->GetPairMapping();
GridMate::Marshaler<T> keyMarshaler;
for (unsigned int i=0; i < mapSize; ++i)
{
T propertyKey;
keyMarshaler.Unmarshal(propertyKey,rb);
newKeys.insert(propertyKey);
auto valueIter = valueMap.find(propertyKey);
if (valueIter != valueMap.end())
{
if (scriptPropertyMarshaler.UnmarshalToPointer(valueIter->second.m_valueProperty,rb))
{
valueChanged = true;
}
}
else
{
valueChanged = true;
AZ::ScriptProperty* newValueProperty = nullptr;
scriptPropertyMarshaler.UnmarshalToPointer(newValueProperty,rb);
AZ::ScriptPropertyGenericClassMap::MapValuePair newPair;
newPair.m_valueProperty = newValueProperty;
T* serializableData = nullptr;
serializableData = static_cast<T*>(classData->m_factory->Create("ScriptProperty"));
(*serializableData) = propertyKey;
AZ::ScriptPropertyGenericClass* genericPropertyClass = aznew AZ::ScriptPropertyGenericClass();
genericPropertyClass->Set<T>(serializableData);
newPair.m_keyProperty = genericPropertyClass;
valueMap.emplace(propertyKey,newPair);
}
}
// Delete all of the unused keyes from the map
auto valueIter = valueMap.begin();
while (valueIter != valueMap.end())
{
if (newKeys.find(valueIter->first) == newKeys.end())
{
valueChanged = true;
valueIter->second.Destroy();
valueIter = valueMap.erase(valueIter);
}
else
{
++valueIter;
}
}
}
}
}
return valueChanged;
}
};
void ScriptPropertyMarshaler::Marshal(GridMate::WriteBuffer& wb, AZ::ScriptProperty*const& property) const
{
GridMate::Marshaler<AZ::Uuid> typeMarshaler;
GridMate::Marshaler<AZ::u64> idMarshaler;
GridMate::Marshaler<AZStd::string> nameMarshaler;
if (property == nullptr)
{
// Write out a nil property if we have a nullptr property
nameMarshaler.Marshal(wb,"");
idMarshaler.Marshal(wb,0);
typeMarshaler.Marshal(wb,AZ::ScriptPropertyNil::RTTI_Type());
return;
}
// Common points:
// Always going to marshal the uuid of the type(or something similar)
// so we know what type we have on the other side.
//
// Next need to pass along the name field.
const AZ::Uuid& typeId = azrtti_typeid(property);
nameMarshaler.Marshal(wb,property->m_name);
idMarshaler.Marshal(wb,property->m_id);
// Method 1:
// - Allow each ScriptProperty to marshal itself.
// - Currently unavailable since the ScriptProperties live in AZCore
// and the WriteBuffer is in GridMate.
// cont.Marshal(wb);
// Method 2:
// - Process all of our known marshallable types and use the appropriate marshaler
if (typeId == AZ::ScriptPropertyBoolean::RTTI_Type())
{
typeMarshaler.Marshal(wb,typeId);
GridMate::Marshaler<bool> boolMarshaler;
boolMarshaler.Marshal(wb,static_cast<const AZ::ScriptPropertyBoolean*>(property)->m_value);
}
else if (typeId == AZ::ScriptPropertyNumber::RTTI_Type())
{
typeMarshaler.Marshal(wb,typeId);
GridMate::Marshaler<double> doubleMarshaler;
doubleMarshaler.Marshal(wb,static_cast<const AZ::ScriptPropertyNumber*>(property)->m_value);
}
else if (typeId == AZ::ScriptPropertyString::RTTI_Type())
{
typeMarshaler.Marshal(wb,typeId);
GridMate::Marshaler<AZStd::string> stringMarshaler;
stringMarshaler.Marshal(wb,static_cast<const AZ::ScriptPropertyString*>(property)->m_value);
}
else if (typeId == AZ::ScriptPropertyGenericClass::RTTI_Type())
{
const AZ::DynamicSerializableField& serializableField = static_cast<const AZ::ScriptPropertyGenericClass*>(property)->GetSerializableField();
typeMarshaler.Marshal(wb,typeId);
GridMate::Marshaler<AZ::DynamicSerializableField> serializableFieldMarshaler;
serializableFieldMarshaler.Marshal(wb,serializableField);
}
else if (typeId == AZ::ScriptPropertyTable::TYPEINFO_Uuid())
{
const AZ::ScriptPropertyTable* scriptPropertyTable = static_cast<const AZ::ScriptPropertyTable*>(property);
typeMarshaler.Marshal(wb,typeId);
GridMate::Marshaler<AZ::u32> mapSizeMarshaler;
mapSizeMarshaler.Marshal(wb,static_cast<AZ::u32>(scriptPropertyTable->m_indexMapping.size()));
GridMate::Marshaler<int> indexMarshaler;
// Currently only support integers as keys inside of the table.
for (auto& mapPair : scriptPropertyTable->m_indexMapping)
{
indexMarshaler.Marshal(wb,mapPair.first);
this->Marshal(wb,mapPair.second);
}
mapSizeMarshaler.Marshal(wb, static_cast<AZ::u32>(scriptPropertyTable->m_keyMapping.size()));
GridMate::Marshaler<AZ::u32> hashMarshaler;
for (auto& mapPair : scriptPropertyTable->m_keyMapping)
{
// For hashed values. The name of the script property is the same as the hash it should be using.
// We still synchronize the Crc so we can unmarshal in place on the other side.
hashMarshaler.Marshal(wb,mapPair.first);
Marshal(wb,mapPair.second);
}
// EntityId's
ScriptPropertyTableMarshalerHelper::MarshalScriptPropertyGenericMap<AZ::EntityId>((*this), wb, scriptPropertyTable);
}
else
{
typeMarshaler.Marshal(wb,AZ::ScriptPropertyNil::RTTI_Type());
}
}
bool ScriptPropertyMarshaler::UnmarshalToPointer(AZ::ScriptProperty*& target, GridMate::ReadBuffer& rb) const
{
bool typeChanged = false;
AZ::Uuid typeId;
AZ::u64 id;
AZStd::string name;
GridMate::Marshaler<AZ::Uuid> typeMarshaler;
GridMate::Marshaler<AZ::u64> idMarshaler;
GridMate::Marshaler<AZStd::string> nameMarshaler;
nameMarshaler.Unmarshal(name,rb);
idMarshaler.Unmarshal(id,rb);
typeMarshaler.Unmarshal(typeId,rb);
if (target == nullptr || typeId != azrtti_typeid(target))
{
typeChanged = true;
AZ::ScriptProperty* actualScriptProperty = nullptr;
if (typeId == AZ::ScriptPropertyBoolean::RTTI_Type())
{
actualScriptProperty = aznew AZ::ScriptPropertyBoolean();
}
else if (typeId == AZ::ScriptPropertyNumber::RTTI_Type())
{
actualScriptProperty = aznew AZ::ScriptPropertyNumber();
}
else if (typeId == AZ::ScriptPropertyString::RTTI_Type())
{
actualScriptProperty = aznew AZ::ScriptPropertyString();
}
else if (typeId == AZ::ScriptPropertyGenericClass::RTTI_Type())
{
actualScriptProperty = aznew AZ::ScriptPropertyGenericClass();
}
else if (typeId == AZ::ScriptPropertyTable::RTTI_Type())
{
actualScriptProperty = aznew AZ::ScriptPropertyTable();
}
else
{
actualScriptProperty = aznew AZ::ScriptPropertyNil();
}
actualScriptProperty->m_name = name;
delete target;
target = actualScriptProperty;
}
// Update our ID
target->m_id = id;
// Method 1:
// - Allow each ScriptProperty to unmarshal itself
// - Currently unavailable since the ScriptProperties live in AZCore
// and the WriteBuffer is in GridMate
// actualScriptProperty->Unmarshal(rb);
//
// Method 2:
// - Process all of our known marshallable types and use the appropriate marshaler
bool valueChanged = false;
if (typeId == AZ::ScriptPropertyBoolean::RTTI_Type())
{
AZ::ScriptPropertyBoolean* booleanProperty = static_cast<AZ::ScriptPropertyBoolean*>(target);
bool oldValue = booleanProperty->m_value;
GridMate::Marshaler<bool> boolMarshaler;
boolMarshaler.Unmarshal(booleanProperty->m_value,rb);
valueChanged = !(oldValue == booleanProperty->m_value);
}
else if (typeId == AZ::ScriptPropertyString::RTTI_Type())
{
AZ::ScriptPropertyString* stringProperty = static_cast<AZ::ScriptPropertyString*>(target);
AZStd::string oldValue = stringProperty->m_value;
GridMate::Marshaler<AZStd::string> stringMarshaler;
stringMarshaler.Unmarshal(stringProperty->m_value,rb);
valueChanged = !(oldValue == stringProperty->m_value);
}
else if (typeId == AZ::ScriptPropertyNumber::RTTI_Type())
{
AZ::ScriptPropertyNumber* numberProperty = static_cast<AZ::ScriptPropertyNumber*>(target);
double oldValue = numberProperty->m_value;
GridMate::Marshaler<double> numberMarshaler;
numberMarshaler.Unmarshal(numberProperty->m_value,rb);
valueChanged = !(oldValue == numberProperty->m_value);
}
else if (typeId == AZ::ScriptPropertyGenericClass::RTTI_Type())
{
AZ::ScriptPropertyGenericClass* genericProperty = static_cast<AZ::ScriptPropertyGenericClass*>(target);
AZ::DynamicSerializableField& serializableField = genericProperty->m_value;
AZ::DynamicSerializableField oldField;
oldField.CopyDataFrom(serializableField);
GridMate::Marshaler<AZ::DynamicSerializableField> serializableFieldMarshaler;
serializableFieldMarshaler.Unmarshal(serializableField,rb);
// If our type hasn't changed, compare the values.
valueChanged = !oldField.IsEqualTo(serializableField);
}
else if (typeId == AZ::ScriptPropertyTable::RTTI_Type())
{
AZ::ScriptPropertyTable* scriptPropertyTable = static_cast<AZ::ScriptPropertyTable*>(target);
GridMate::Marshaler<AZ::u32> mapSizeMarshaler;
// Unmarshal all of the indexes properties
{
AZ::u32 mapSize = 0;
mapSizeMarshaler.Unmarshal(mapSize, rb);
AZStd::unordered_set<int> newIndexes;
GridMate::Marshaler<int> indexMarshaler;
for (AZ::u32 i=0; i < mapSize; ++i)
{
int index = 0;
indexMarshaler.Unmarshal(index,rb);
auto mapIter = scriptPropertyTable->m_indexMapping.find(index);
if (mapIter != scriptPropertyTable->m_indexMapping.end())
{
if (UnmarshalToPointer(mapIter->second,rb))
{
valueChanged = true;
}
}
else
{
valueChanged = true;
AZ::ScriptProperty* scriptProperty = nullptr;
UnmarshalToPointer(scriptProperty,rb);
auto insertResult = scriptPropertyTable->m_indexMapping.emplace(index,scriptProperty);
mapIter = insertResult.first;
}
if (mapIter->second == nullptr || azrtti_istypeof<AZ::ScriptPropertyNil>(mapIter->second))
{
valueChanged = true;
delete mapIter->second;
scriptPropertyTable->m_indexMapping.erase(mapIter);
}
else
{
newIndexes.insert(index);
}
}
auto mapIter = scriptPropertyTable->m_indexMapping.begin();
while (mapIter != scriptPropertyTable->m_indexMapping.end())
{
if (newIndexes.find(mapIter->first) == newIndexes.end())
{
valueChanged = true;
delete mapIter->second;
mapIter = scriptPropertyTable->m_indexMapping.erase(mapIter);
}
else
{
++mapIter;
}
}
}
// Unmarshal all of the hashed values
{
AZ::u32 mapSize = 0;
mapSizeMarshaler.Unmarshal(mapSize, rb);
AZStd::unordered_set<AZ::u32> newHashes;
GridMate::Marshaler<AZ::u32> hashMarshaler;
for (AZ::u32 i=0; i < mapSize; ++i)
{
AZ::u32 newHash;
hashMarshaler.Unmarshal(newHash, rb);
auto mapIter = scriptPropertyTable->m_keyMapping.find(newHash);
if (mapIter != scriptPropertyTable->m_keyMapping.end())
{
if (UnmarshalToPointer(mapIter->second,rb))
{
valueChanged = true;
}
}
else
{
valueChanged = true;
AZ::ScriptProperty* scriptProperty = nullptr;
UnmarshalToPointer(scriptProperty,rb);
auto emplaceResult = scriptPropertyTable->m_keyMapping.emplace(newHash,scriptProperty);
mapIter = emplaceResult.first;
}
if (mapIter->second == nullptr || azrtti_istypeof<AZ::ScriptPropertyNil>(mapIter->second))
{
valueChanged = true;
delete mapIter->second;
scriptPropertyTable->m_keyMapping.erase(mapIter);
}
else
{
newHashes.insert(newHash);
}
}
auto mapIter = scriptPropertyTable->m_keyMapping.begin();
while (mapIter != scriptPropertyTable->m_keyMapping.end())
{
if (newHashes.find(mapIter->first) == newHashes.end())
{
valueChanged = true;
delete mapIter->second;
mapIter = scriptPropertyTable->m_keyMapping.erase(mapIter);
}
else
{
++mapIter;
}
}
}
// Unmarshal all of the generic properties
// EntityId's
if (ScriptPropertyTableMarshalerHelper::UnmarshalScriptPropertyGenericMap<AZ::EntityId>((*this), scriptPropertyTable, rb))
{
valueChanged = true;
}
}
return typeChanged || valueChanged;
}
////////////////////////////
// ScriptPropertyThrottler
////////////////////////////
ScriptPropertyThrottler::ScriptPropertyThrottler()
: m_isDirty(true)
{
}
void ScriptPropertyThrottler::SignalDirty()
{
m_isDirty = true;
}
bool ScriptPropertyThrottler::WithinThreshold(AZ::ScriptProperty* newValue) const
{
return newValue == nullptr || !m_isDirty;
}
void ScriptPropertyThrottler::UpdateBaseline(AZ::ScriptProperty* baseline)
{
(void)baseline;
m_isDirty = false;
}
}
@@ -1,94 +0,0 @@
/*
* 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.
*
*/
#ifndef AZFRAMEWORK_SCRIPT_SCRIPTMARSHAL_H
#define AZFRAMEWORK_SCRIPT_SCRIPTMARSHAL_H
#include <GridMate/Serialize/ContainerMarshal.h>
#include <AzCore/RTTI/BehaviorObjectSignals.h>
namespace AZ
{
class ScriptProperty;
}
namespace AzFramework
{
/**
* Specalized helper marshaler for ScriptProperty class
*/
class ScriptPropertyMarshaler
{
public:
void Marshal(GridMate::WriteBuffer& wb, AZ::ScriptProperty*const& cont) const;
bool UnmarshalToPointer(AZ::ScriptProperty*& target, GridMate::ReadBuffer& rb) const;
};
class ScriptPropertyThrottler
{
public:
ScriptPropertyThrottler();
void SignalDirty();
bool WithinThreshold(AZ::ScriptProperty* newValue) const;
void UpdateBaseline(AZ::ScriptProperty* baseline);
private:
bool m_isDirty;
};
/**
* Specialized helper marshaler to help with the vector creation/destruction
*/
class ScriptRPCMarshaler
{
public:
typedef AZStd::vector< AZ::ScriptProperty* > Container;
ScriptRPCMarshaler()
{
}
AZ_FORCE_INLINE void Marshal(GridMate::WriteBuffer& wb, const Container& container) const
{
AZ_Assert(container.size() < USHRT_MAX, "Container has too many elements for marshaling!");
AZ::u16 size = static_cast<AZ::u16>(container.size());
wb.Write(size);
for (const auto& i : container)
{
m_marshaler.Marshal(wb, i);
}
}
AZ_FORCE_INLINE void Unmarshal(Container& container, GridMate::ReadBuffer& rb) const
{
container.clear();
AZ::u16 size;
rb.Read(size);
container.reserve(size);
for (AZ::u16 i = 0; i < size; ++i)
{
AZ::ScriptProperty* readProperty = nullptr;
m_marshaler.UnmarshalToPointer(readProperty, rb);
container.insert(container.end(), readProperty);
}
}
protected:
ScriptPropertyMarshaler m_marshaler;
};
}
#endif
File diff suppressed because it is too large Load Diff
@@ -1,320 +0,0 @@
/*
* 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.
*
*/
#ifndef AZFRAMEWORK_SCRIPT_NET_BINDINGS_H
#define AZFRAMEWORK_SCRIPT_NET_BINDINGS_H
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/string/string.h>
#include <GridMate/Replica/ReplicaChunkInterface.h>
#include <GridMate/Replica/DataSet.h>
#include <GridMate/Replica/RemoteProcedureCall.h>
#include <GridMate/Replica/RemoteProcedureCall.h>
#include <AzCore/Script/ScriptProperty.h>
#include <AzCore/Script/ScriptPropertyTable.h>
#include <AzCore/Script/ScriptPropertyWatcherBus.h>
#include <AzFramework/Script/ScriptMarshal.h>
namespace AzFramework
{
class ScriptPropertyDataSet;
class ScriptComponentReplicaChunk;
// ScriptNetBindingTable will act as the go between for the ScriptComponent and the Replica's.
// It will also allow for holding of values in the case where you haven't been bound to a replica chunk yet and the
// script tries to interact with something that is networked.
//
// Allows for scripts to be re-used seamlessly in a offline vs online scenario(and support for going from offline to online),
// including RPCs(will alawys call the master version if offline)
class ScriptNetBindingTable
: public GridMate::ReplicaChunkInterface
{
private:
friend class ScriptComponentReplicaChunk;
friend class ScriptPropertyDataSet;
// Helper struct to keep track of a a ScriptContext
// and the entityTableReference. Mainly used for
// calling in to functions in LUA where we want
// to push in the table reference as the first parameter
struct EntityScriptContext
{
public:
EntityScriptContext();
void Unload();
bool HasEntityTableRegistryIndex() const;
int GetEntityTableRegistryIndex() const;
bool HasScriptContext() const;
AZ::ScriptContext* GetScriptContext() const;
void ConfigureContext(AZ::ScriptContext* scriptContext, int entityTableRegistryIndex);
private:
bool SanityCheckContext() const;
AZ::ScriptContext* m_scriptContext;
int m_entityTableRegistryIndex;
};
class NetworkedTableValue;
friend NetworkedTableValue;
typedef AZStd::unordered_map<AZStd::string, NetworkedTableValue> NetworkedTableMap;
class RPCBindingHelper;
friend RPCBindingHelper;
typedef AZStd::unordered_map<AZStd::string, RPCBindingHelper> RPCHelperMap;
// Helper class that will wrap up our interactions with the actual stored value
// to hide the general use case of if we are connected to a replica or not.
//
// Additionally this will serve as a holding ground for a 'networked'
// value that doesn't have a dataset.
//
// Lastly holds onto the Callback references.
class NetworkedTableValue
{
public:
AZ_CLASS_ALLOCATOR(NetworkedTableValue, AZ::SystemAllocator, 0);
NetworkedTableValue(AZ::ScriptProperty* initialValue = nullptr);
~NetworkedTableValue();
void Destroy();
// Methods to register this value to a chunk
bool HasDataSet() const;
void RegisterDataSet(ScriptPropertyDataSet* dataSet);
void UnbindFromDataSet();
ScriptPropertyDataSet* GetDataSet() const;
// Information kept in order to force these values to use a particular dataset for debugging.
bool HasForcedDataSetIndex() const;
void SetForcedDataSetIndex(int index);
int GetForcedDataSetIndex() const;
// Callback functions
bool HasCallback() const;
void RegisterCallback(int functionReference);
void ReleaseCallback(AZ::ScriptContext& scriptContext);
void InvokeCallback(EntityScriptContext& scriptContext, const GridMate::TimeContext& timeContext);
bool AssignValue(AZ::ScriptDataContext& scriptDataContext, const AZStd::string& propertyName);
bool InspectValue(AZ::ScriptContext* scriptContext) const;
// Methods used for unit tests
const AZ::ScriptProperty* GetShimmedScriptProperty() const { return m_shimmedScriptProperty; }
private:
// This value will be used if we have a networked property, but don't have a valid chunk yet.
// Works as a temporary store, which will be resolved once we get assigned to a DataSet
AZ::ScriptProperty* m_shimmedScriptProperty;
// The data set we are bound to
ScriptPropertyDataSet* m_dataSet;
int m_forcedDataSetIndex;
int m_functionReference;
};
// Future thoughts
// - Move the actual RPC meta table creation
// into this guy
class RPCBindingHelper
{
public:
AZ_CLASS_ALLOCATOR(RPCBindingHelper, AZ::SystemAllocator, 0);
RPCBindingHelper();
~RPCBindingHelper();
void ReleaseTableIndex(AZ::ScriptContext& scriptContext);
bool IsValid() const;
void SetMasterFunction(int masterReference);
bool InvokeMaster(EntityScriptContext& entityScriptContext, const ScriptRPCMarshaler::Container& params);
void SetProxyFunction(int masterReference);
void InvokeProxy(EntityScriptContext& entityScriptContext, const ScriptRPCMarshaler::Container& params);
private:
int m_masterReference;
int m_proxyReference;
};
public:
AZ_CLASS_ALLOCATOR(ScriptNetBindingTable, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflect);
ScriptNetBindingTable();
~ScriptNetBindingTable();
void Unload();
void CreateNetworkBindingTable(AZ::ScriptContext* scriptContext, int baseTableIndex, int entityTableIndex);
void FinalizeNetworkTable(AZ::ScriptContext* scriptContext, int entityTableRegistryIndex);
AZ::ScriptContext* GetScriptContext() const;
bool IsMaster() const;
//////////////////////////////////////////////////////////////////////////////////////////////////////
// DataSet Functionality
//
// Called when the script wants to bind a function callback to when
// a value changes
//
// Might change this to just be register DataSet
bool RegisterDataSet(AZ::ScriptDataContext& stackContext, AZ::ScriptProperty* scriptProperty);
// Called when the script wants to assign a value to the script value
bool AssignTableValue(AZ::ScriptDataContext& stackContext);
// Called when the script wants to know the value of a script value.
bool InspectTableValue(AZ::ScriptDataContext& stackContext) const;
//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
/// RPC Functionality
void RegisterRPC(AZ::ScriptDataContext& rpcTableContext, const AZStd::string& rpcName, int elementIndex, int tableStackIndex);
bool InvokeRPC(AZ::ScriptDataContext& stackContext);
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Netbinding Interface duplication here to be called from the ScriptComponent
GridMate::ReplicaChunkPtr GetNetworkBinding();
void SetNetworkBinding(GridMate::ReplicaChunkPtr chunk);
void UnbindFromNetwork();
void OnPropertyUpdate(AZ::ScriptProperty*const& scriptProperty, const GridMate::TimeContext& tc);
bool OnInvokeRPC(AZStd::string functionName, AZStd::vector< AZ::ScriptProperty*> properties, const GridMate::RpcContext& rpcContext);
// Methods used for unit tests
const AZ::ScriptProperty* FindScriptProperty(const AZStd::string& name) const;
private:
void RegisterMetaTableCache();
template<typename PropertyType, typename PropertyArrayType>
AZ::ScriptPropertyTable* ConvertPropertyArrayToTable(PropertyArrayType* arrayProperty)
{
AZ::ScriptPropertyTable* scriptPropertyTable = aznew AZ::ScriptPropertyTable(arrayProperty->m_name.c_str());
PropertyType propertyType;
for (unsigned int i=0; i < arrayProperty->m_values.size(); ++i)
{
propertyType.m_value = arrayProperty->m_values[i];
// Offset by 1 to deal with lua 1 indexing.
// Table will make a clone of our object.
scriptPropertyTable->SetTableValue(i+1, &propertyType);
}
return scriptPropertyTable;
}
void AssignDataSets();
NetworkedTableValue* FindTableValue(const AZStd::string& name);
const NetworkedTableValue* FindTableValue(const AZStd::string& name) const;
EntityScriptContext m_entityScriptContext;
GridMate::ReplicaChunkPtr m_replicaChunk;
NetworkedTableMap m_networkedTable;
RPCHelperMap m_rpcHelperMap;
};
// Typedeffing out the RPC and DataSet definitions.
typedef GridMate::Rpc< GridMate::RpcArg< AZStd::string >, GridMate::RpcArg< ScriptRPCMarshaler::Container, ScriptRPCMarshaler > >::BindInterface<ScriptNetBindingTable, &ScriptNetBindingTable::OnInvokeRPC> ScriptPropertyRPC;
typedef GridMate::DataSet<AZ::ScriptProperty*, ScriptPropertyMarshaler, ScriptPropertyThrottler>::BindInterface<ScriptNetBindingTable, &ScriptNetBindingTable::OnPropertyUpdate> ScriptPropertyDataSetType;
class ScriptComponentReplicaChunk;
// Specialized DataSet used by the ScriptProperties, just to add some wrapped around functionality
// and to allow me to manipulate the DataSet throttler in order to properly manage a dirty flag
class ScriptPropertyDataSet
: public ScriptPropertyDataSetType
, public AZ::ScriptPropertyWatcherBus::Handler
, public AZ::ScriptPropertyWatcher
{
private:
friend class ScriptComponentReplicaChunk;
friend class ScriptNetBindingTable::NetworkedTableValue;
const char* GetDataSetName();
public:
ScriptPropertyDataSet();
~ScriptPropertyDataSet();
bool IsReserved() const;
bool UpdateScriptProperty(AZ::ScriptDataContext& scriptDataContext, const AZStd::string& propertyName);
void SetScriptProperty(AZ::ScriptProperty* scriptProperty);
void OnObjectModified() override;
private:
void Reserve(ScriptNetBindingTable::NetworkedTableValue* reserver);
void Release(ScriptNetBindingTable::NetworkedTableValue* reserver);
ScriptNetBindingTable::NetworkedTableValue* m_reserver;
};
// The actual ReplicaChunk that the script will use
class ScriptComponentReplicaChunk
: public GridMate::ReplicaChunkBase
{
public:
AZ_CLASS_ALLOCATOR(ScriptComponentReplicaChunk, AZ::SystemAllocator,0);
static const int k_maxScriptableDataSets = GM_MAX_DATASETS_IN_CHUNK;
static const char* GetChunkName() { return "ScriptComponentReplicaChunk"; }
// Might want to add some type of comment field into the various fields so this can be properly parsed
// and determined what we are actually sending.
ScriptComponentReplicaChunk();
~ScriptComponentReplicaChunk();
bool IsReplicaMigratable() override;
AZ::u32 CalculateDirtyDataSetMask(GridMate::MarshalContext& marshalContext) override;
// Called from the Master, will assign the table value to the DataSet specified by the helper.
bool AssignDataSet(ScriptNetBindingTable::NetworkedTableValue& helper);
// Called from teh Proxy. Will Assign the TableValue to the DataSet that contains the target property
void AssignDataSetForProperty(ScriptNetBindingTable::NetworkedTableValue& helper, AZ::ScriptProperty* targetProperty);
// Only called inside of an assert, checks that the DataSet that the targetProperty is in is the same as the assumedDataSet
// Used to confirm that we don't get a confusion between master/proxy about which ScriptProperty is assigned to which DataSet.
bool SanityCheckDataSet(AZ::ScriptProperty* targetProperty, ScriptPropertyDataSet* assumedDataSet);
ScriptPropertyRPC m_scriptRPC;
private:
AZ::u32 m_enabledDataSetMask;
ScriptPropertyDataSet m_propertyDataSets[k_maxScriptableDataSets];
};
}
#endif
@@ -177,7 +177,7 @@ namespace AzFramework
Neighborhood::NeighborReplicaPtr replicaChunk = GridMate::CreateReplicaChunk<Neighborhood::NeighborReplica>(session->GetMyMember()->GetId().Compact(), m_component->m_settings->m_persistentName.c_str(), Neighborhood::NEIGHBOR_CAP_LUA_VM | Neighborhood::NEIGHBOR_CAP_LUA_DEBUGGER);
replicaChunk->SetDisplayName(m_component->m_settings->m_persistentName.c_str());
replica->AttachReplicaChunk(replicaChunk);
session->GetReplicaMgr()->AddMaster(replica);
session->GetReplicaMgr()->AddPrimary(replica);
}
}
@@ -161,26 +161,6 @@ set(FILES
Metrics/MetricsPlainTextNameRegistration.h
Network/AssetProcessorConnection.cpp
Network/AssetProcessorConnection.h
Network/DynamicSerializableFieldMarshaler.h
Network/EntityIdMarshaler.h
Network/InterestManagerComponent.h
Network/InterestManagerComponent.cpp
Network/NetBindable.h
Network/NetBindable.cpp
Network/NetBindingEventsBus.h
Network/NetBindingHandlerBus.h
Network/NetBindingSystemBus.h
Network/NetBindingComponent.h
Network/NetBindingComponent.cpp
Network/NetBindingComponentChunk.h
Network/NetBindingComponentChunk.cpp
Network/NetBindingSystemImpl.h
Network/NetBindingSystemImpl.cpp
Network/NetBindingSystemComponent.h
Network/NetBindingSystemComponent.cpp
Network/NetworkContext.h
Network/NetworkContext.cpp
Network/NetSystemBus.h
Network/SocketConnection.cpp
Network/SocketConnection.h
Logging/LogFile.cpp
@@ -203,10 +183,6 @@ set(FILES
Script/ScriptDebugAgentBus.h
Script/ScriptDebugMsgReflection.cpp
Script/ScriptDebugMsgReflection.h
Script/ScriptMarshal.h
Script/ScriptMarshal.cpp
Script/ScriptNetBindings.h
Script/ScriptNetBindings.cpp
Script/ScriptRemoteDebugging.cpp
Script/ScriptRemoteDebugging.h
StreamingInstall/StreamingInstall.h
@@ -279,6 +255,7 @@ set(FILES
Physics/ClassConverters.cpp
Physics/ClassConverters.h
Physics/MaterialBus.h
Physics/WindBus.h
Process/ProcessCommunicator.cpp
Process/ProcessCommunicator.h
Process/ProcessWatcher.cpp
@@ -1287,7 +1287,6 @@ namespace AzToolsFramework
Field("Cached World Transform Parent", &TransformComponent::m_cachedWorldTransformParent)->
Field("Parent Activation Transform Mode", &TransformComponent::m_parentActivationTransformMode)->
Field("IsStatic", &TransformComponent::m_isStatic)->
Field("Sync Enabled", &TransformComponent::m_netSyncEnabled)->
Field("InterpolatePosition", &TransformComponent::m_interpolatePosition)->
Field("InterpolateRotation", &TransformComponent::m_interpolateRotation)->
Version(9, &Internal::TransformComponentDataConverter);
@@ -1322,21 +1321,7 @@ namespace AzToolsFramework
Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Hide)->
DataElement(AZ::Edit::UIHandlers::Default, &TransformComponent::m_cachedWorldTransform, "Cached World Transform", "")->
Attribute(AZ::Edit::Attributes::SliceFlags, AZ::Edit::SliceFlags::NotPushable)->
Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Hide)->
ClassElement(AZ::Edit::ClassElements::Group, "Network Sync")->
Attribute(AZ::Edit::Attributes::AutoExpand, true)->
DataElement(AZ::Edit::UIHandlers::Default, &TransformComponent::m_netSyncEnabled, "Sync to replicas", "Sync to network replicas.")->
DataElement(AZ::Edit::UIHandlers::ComboBox, &TransformComponent::m_interpolatePosition,
"Position Interpolation", "Enable local interpolation of position.")->
EnumAttribute(AZ::InterpolationMode::NoInterpolation, "None")->
EnumAttribute(AZ::InterpolationMode::LinearInterpolation, "Linear")->
DataElement(AZ::Edit::UIHandlers::ComboBox, &TransformComponent::m_interpolateRotation,
"Rotation Interpolation", "Enable local interpolation of rotation.")->
EnumAttribute(AZ::InterpolationMode::NoInterpolation, "None")->
EnumAttribute(AZ::InterpolationMode::LinearInterpolation, "Linear");
Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Hide);
ptrEdit->Class<EditorTransform>("Values", "XYZ PYR")->
DataElement(AZ::Edit::UIHandlers::Default, &EditorTransform::m_translate, "Translate", "Local Position (Relative to parent) in meters.")->
@@ -13,6 +13,9 @@
#include "EditorTransformComponentSelection.h"
#include <AzCore/std/algorithm.h>
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Matrix3x4.h>
#include <AzCore/Math/Matrix4x4.h>
#include <AzCore/Math/VectorConversions.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/Viewport/CameraState.h>
@@ -1486,7 +1486,7 @@ namespace GridMate
// Only support a single cipher suite in OpenSSL that supports:
//
// ECDHE Master key exchange using ephemeral elliptic curve diffie-hellman.
// ECDHE Key exchange using ephemeral elliptic curve diffie-hellman.
// RSA Authentication (public and private key) used to sign ECDHE parameters and can be checked against a CA.
// AES256 AES cipher for symmetric key encryption using a 256-bit key.
// GCM Mode of operation for symmetric key encryption.
@@ -97,7 +97,7 @@ namespace GridMate
// Only support a single cipher suite in OpenSSL that supports:
//
// ECDHE Master key exchange using ephemeral elliptic curve diffie-hellman.
// ECDHE Key exchange using ephemeral elliptic curve diffie-hellman.
// RSA Authentication (public and private key) used to sign ECDHE parameters and can be checked against a CA.
// AES256 AES cipher for symmetric key encryption using a 256-bit key.
// GCM Mode of operation for symmetric key encryption.
@@ -52,6 +52,6 @@ namespace GridMate
bool DataSetBase::CanSet() const
{
return m_replicaChunk ? m_replicaChunk->IsMaster() : true;
return m_replicaChunk ? m_replicaChunk->IsPrimary() : true;
}
}
@@ -43,7 +43,7 @@ namespace GridMate
struct DataSetDefaultTraits
{
/**
* \brief Should a change in DataSet value invoke a callback on a master replica chunk?
* \brief Should a change in DataSet value invoke a callback on a primary replica chunk?
*
* By default, DataSet::BindInterface<C, &C::Callback> only invokes on client/non-authoritative replica chunks.
* This switch enables the callback on server/authoritative replica chunks.
@@ -55,7 +55,7 @@ namespace GridMate
};
/**
* \brief Turns on DataSet callbacks to be invoked on the master replica as well as client replicas.
* \brief Turns on DataSet callbacks to be invoked on the primary replica as well as client replicas.
*/
struct DataSetInvokeEverywhereTraits : DataSetDefaultTraits
{
@@ -200,7 +200,7 @@ namespace GridMate
}
/**
Modify the DataSet. Call this on the Master node to change the data,
Modify the DataSet. Call this on the Primary node to change the data,
which will be propagated to all proxies.
**/
void Set(const DataType& v)
@@ -214,7 +214,7 @@ namespace GridMate
}
/**
Modify the DataSet. Call this on the Master node to change the data,
Modify the DataSet. Call this on the Primary node to change the data,
which will be propagated to all proxies.
**/
void Set(DataType&& v)
@@ -228,7 +228,7 @@ namespace GridMate
}
/**
Modify the DataSet. Call this on the Master node to change the data,
Modify the DataSet. Call this on the Primary node to change the data,
which will be propagated to all proxies.
**/
template <class ... Args>
@@ -243,7 +243,7 @@ namespace GridMate
}
/**
Modify the DataSet directly without copying it. Call this on the Master node,
Modify the DataSet directly without copying it. Call this on the Primary node,
passing in a function object that takes the value by reference, optionally
modifies the data, and returns true if the data was changed.
**/
@@ -159,7 +159,7 @@ namespace GridMate
}
/**
Modify the DataSet. Call this on the Master node to change the data,
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)
@@ -201,7 +201,7 @@ namespace GridMate
private:
DataSet<FieldType, MarshalerType> m_absolutePortion;
DataSet<FieldType, DeltaMarshalerType> m_relativePortion;
FieldType m_combinedValue; // the latest value on either master or proxy
FieldType m_combinedValue; // the latest value on either primary or proxy
};
//-----------------------------------------------------------------------------
@@ -366,7 +366,7 @@ namespace GridMate
auto replica = Replica::CreateReplica("BitmaskInterestHandlerRules");
m_rulesReplica = CreateAndAttachReplicaChunk<BitmaskInterestChunk>(replica);
m_rm->AddMaster(replica);
m_rm->AddPrimary(replica);
}
void BitmaskInterestHandler::OnRulesHandlerUnregistered(InterestManager* manager)
@@ -206,7 +206,7 @@ namespace GridMate
return false;
}
if (replica->IsMaster()) // own the replica?
if (replica->IsPrimary()) // own the replica?
{
return true;
}
@@ -508,7 +508,7 @@ namespace GridMate
auto replica = Replica::CreateReplica("ProximityInterestHandlerRules");
m_rulesReplica = CreateAndAttachReplicaChunk<ProximityInterestChunk>(replica);
m_rm->AddMaster(replica);
m_rm->AddPrimary(replica);
}
void ProximityInterestHandler::OnRulesHandlerUnregistered(InterestManager* manager)
@@ -26,7 +26,7 @@ namespace GridMate
m_replicaMgr = replica->GetReplicaManager();
if (replica->IsMaster() && newOwnerId != m_replicaMgr->GetLocalPeerId())
if (replica->IsPrimary() && newOwnerId != m_replicaMgr->GetLocalPeerId())
{
m_sm.SetStateHandler(AZ_HSM_STATE_NAME(MST_TOP), AZ::HSM::StateHandler(this, &MigrationSequence::DefaultHandler), AZ::HSM::InvalidStateId, MST_MIGRATING);
}
@@ -312,7 +312,7 @@ namespace GridMate
return true;
case ME_MODIFY_NEW_OWNER:
m_newOwnerId = *static_cast<PeerId*>(event.userData);
if (m_replica->IsMaster() && m_newOwnerId != m_replicaMgr->m_self.GetId())
if (m_replica->IsPrimary() && m_newOwnerId != m_replicaMgr->m_self.GetId())
{
sm.Transition(MST_MIGRATING);
}
@@ -370,8 +370,8 @@ namespace GridMate
PeerId sourcePeerId = GetSourcePeerId();
bool shouldQueue = true;
bool processed = false;
bool isMaster = m_replicaChunk->IsMaster();
if (isMaster)
bool isPrimary = m_replicaChunk->IsPrimary();
if (isPrimary)
{
// We are authoritative so execute the RPC immediately, forwarding the args along
RpcRequest localRequest(this, rc.m_realTime, rc.m_realTime, rc.m_localTime);
@@ -385,7 +385,7 @@ namespace GridMate
if (shouldQueue)
{
TypeTuple* storage = aznew TypeTuple(this, RpcContext(rc.m_realTime, rc.m_realTime, rc.m_localTime, sourcePeerId), AZStd::forward<LocalArgs>(args) ...);
storage->m_authoritative = isMaster;
storage->m_authoritative = isPrimary;
storage->m_processed = processed;
storage->m_reliable = Traits::s_isReliable;
OnRpcRequest(storage);
@@ -106,7 +106,7 @@ namespace GridMate
//-----------------------------------------------------------------------------
void Replica::Destroy()
{
AZ_Assert(IsMaster(), "We don't own replica 0x%x!", GetRepId());
AZ_Assert(IsPrimary(), "We don't own replica 0x%x!", GetRepId());
if (m_manager)
{
m_manager->Destroy(this);
@@ -327,7 +327,7 @@ namespace GridMate
if (IsActive())
{
if (IsMaster())
if (IsPrimary())
{
EBUS_EVENT(Debug::ReplicaDrillerBus, OnRequestReplicaChangeOwnership, this, requestor);
@@ -68,7 +68,7 @@ namespace GridMate
Rep_ManagedAlloc = 1 << 1,
Rep_CanMigrate = 1 << 2,
Rep_New = 1 << 3,
Rep_Master = 1 << 4,
Rep_Primary = 1 << 4,
Rep_Active = 1 << 6,
Rep_ChangedOwner = 1 << 7,
Rep_SuspendDownstream = 1 << 8,
@@ -85,7 +85,7 @@ namespace GridMate
void Destroy();
void UpdateReplica(const ReplicaContext& rc); // Called when updating replica master from source
void UpdateReplica(const ReplicaContext& rc); // Called when updating replica primary from source
void UpdateFromReplica(const ReplicaContext& rc); // Called when updating game with replica info
bool AcceptChangeOwnership(PeerId requestor, const ReplicaContext& rc); // Return true to accept the transfer
void OnActivate(const ReplicaContext& rc);
@@ -112,8 +112,8 @@ namespace GridMate
void RequestChangeOwnership(PeerId newOwner = InvalidReplicaPeerId); // If newOwner is not specified we assume it should be the local peer
bool IsMaster() const { return !IsActive() || !!(m_flags & Rep_Master); }
bool IsProxy() const { return !IsMaster(); }
bool IsPrimary() const { return !IsActive() || !!(m_flags & Rep_Primary); }
bool IsProxy() const { return !IsPrimary(); }
bool IsNew() const { return !!(m_flags & Rep_New); }
bool IsNewOwner() const { return !!(m_flags & Rep_ChangedOwner); }
bool IsActive() const { return !!(m_flags & Rep_Active); }
@@ -170,7 +170,7 @@ namespace GridMate
void MarkRPCsAsRelayed();
void SetMaster(bool isMaster) { m_flags = isMaster ? m_flags | Rep_Master : m_flags & ~Rep_Master; }
void SetPrimary(bool isPrimary) { m_flags = isPrimary ? m_flags | Rep_Primary : m_flags & ~Rep_Primary; }
void SetNew() { m_flags |= Rep_New; }
void SetRepId(ReplicaId id);
void SetMigratable(bool migratable);
@@ -125,18 +125,18 @@ namespace GridMate
return false;
}
//-----------------------------------------------------------------------------
bool ReplicaChunkBase::IsMaster() const
bool ReplicaChunkBase::IsPrimary() const
{
if (m_replica)
{
return m_replica->IsMaster();
return m_replica->IsPrimary();
}
return true;
}
//-----------------------------------------------------------------------------
bool ReplicaChunkBase::IsProxy() const
{
return !IsMaster();
return !IsPrimary();
}
//-----------------------------------------------------------------------------
bool ReplicaChunkBase::IsDirty(AZ::u32 marshalFlags) const
@@ -398,8 +398,8 @@ namespace GridMate
{
if (mc.m_peer != m_replica->m_upstreamHop)
{
AZ_TracePrintf("GridMate", "Received dataset updates for replica id %08x(%s) from unexpected peer.", GetReplicaId(), IsActive() && IsMaster() ? "master" : "proxy");
if (IsMaster())
AZ_TracePrintf("GridMate", "Received dataset updates for replica id %08x(%s) from unexpected peer.", GetReplicaId(), IsActive() && IsPrimary() ? "primary" : "proxy");
if (IsPrimary())
{
mc.m_iBuf->Skip(mc.m_iBuf->Left());
return;
@@ -547,7 +547,7 @@ namespace GridMate
AZ_Assert(false, "Discarding non-authoritative RPC <%s> because s_allowNonAuthoritativeRequests trait is disabled!", GetDescriptor()->GetRpcName(this, rpc));
isRpcValid = false;
}
if (!rpc->IsAllowNonAuthoritativeRequestsRelay() && !IsMaster())
if (!rpc->IsAllowNonAuthoritativeRequestsRelay() && !IsPrimary())
{
AZ_Assert(false, "Discarding non-authoritative RPC <%s> because s_allowNonAuthoritativeRequestRelay trait is disabled!", GetDescriptor()->GetRpcName(this, rpc));
isRpcValid = false;
@@ -639,19 +639,19 @@ namespace GridMate
for (RPCQueue::iterator iRPC = m_rpcQueue.begin(); iRPC != m_rpcQueue.end(); )
{
Internal::RpcRequest* request = *iRPC;
bool isMaster = IsMaster(); // need to do this check after each RPC because ownership may change
bool isPrimary = IsPrimary(); // need to do this check after each RPC because ownership may change
if (!m_replica->IsActive()) // this can happen if replica was deactivated within a previous RPC call
{
request->m_relayed = true;
}
else if (!request->m_processed && (isMaster || request->m_authoritative))
else if (!request->m_processed && (isPrimary || request->m_authoritative))
{
request->m_realTime = rc.m_realTime;
request->m_localTime = rc.m_localTime;
bool ret = request->m_rpc->Invoke(request);
request->m_processed = true;
if (isMaster)
if (isPrimary)
{
if (ret)
{
@@ -45,17 +45,17 @@ namespace GridMate
/** A single unit of network functionality
A ReplicaChunk is a user extendable network object. One or more ReplicaChunks can be
owned by a Replica, which is both a container and manager for them. A replica is owned
by a Master, and is propagated to other network nodes, who interact with is as a Proxy.
by a Primary, and is propagated to other network nodes, who interact with is as a Proxy.
The data a ReplicaChunk contains should generally be related to the other data stored
within it. Since multiple chunks can be attached to a Replica, unrelated data can simply
be stored in other chunks on the same Replica.
A ReplicaChunk has two primary ways to interact with it: DataSets and Remote Procedure
Calls (RPCs).
DataSets store arbitrary data, which only the Master is able to modify. Any changes are
DataSets store arbitrary data, which only the Primary is able to modify. Any changes are
propagated to the Proxy ReplicaChunks on the other nodes.
RPCs are methods that can be executed on a remote node. They are first invoked on the
Master, who then decides if the invocation should be propagated to the Proxies.
Primary, who then decides if the invocation should be propagated to the Proxies.
ReplicaChunks can be created by inheriting from the class and registered by calling
ReplicaChunkDescriptorTable::RegisterChunkType() to create the factory required by
@@ -107,7 +107,7 @@ namespace GridMate
PeerId GetPeerId() const;
virtual ReplicaManager* GetReplicaManager();
bool IsActive() const;
bool IsMaster() const;
bool IsPrimary() const;
bool IsProxy() const;
virtual void OnAttachedToReplica(Replica* replica) { (void) replica; }
@@ -191,7 +191,7 @@ namespace GridMate
void AddDataSetEvent(DataSetBase* dataset); // Called to enqueue a user event handler for a modified DataSet on a proxy node
void SignalDataSetChanged(const DataSetBase& dataset); // Called when the DataSet changes on the master node
void SignalDataSetChanged(const DataSetBase& dataset); // Called when the DataSet changes on the primary node
void EnqueueMarshalTask();
@@ -62,7 +62,7 @@ namespace GridMate
//! Called when an ownership transfer request is received.
virtual void OnRequestReplicaChangeOwnership(Replica* replica, PeerId requestor) { (void)replica; (void)requestor; }
//! Called when a replica changes ownership, not necessarily to or from the local node.
virtual void OnReplicaChangeOwnership(Replica* replica, bool wasMaster) { (void)replica; (void)wasMaster; }
virtual void OnReplicaChangeOwnership(Replica* replica, bool wasPrimary) { (void)replica; (void)wasPrimary; }
//! Called when a chunk has been created. It doesn't mean it will be added to the system.
//! Object will be partially constructed at this point if you inherit from ReplicaChunk
@@ -91,9 +91,9 @@ namespace GridMate
//! Called when data is received for a dataset.
virtual void OnReceiveDataSet(ReplicaChunkBase* chunk, AZ::u32 chunkIndex, DataSetBase* dataSet, PeerId from, PeerId to, const void* data, size_t len) { (void)chunk; (void)chunkIndex; (void)dataSet; (void)from; (void)to; (void)data; (void)len; }
//! Called when an rpc request is received. RpcRequest pointer will be null if rpc is called on master replica.
//! Called when an rpc request is received. RpcRequest pointer will be null if rpc is called on primary replica.
virtual void OnRequestRpc(ReplicaChunkBase* chunk, Internal::RpcRequest* rpc) { (void)chunk; (void)rpc; }
//! Called when an rpc is invoked. RpcRequest pointer will be null if rpc is called on master replica.
//! Called when an rpc is invoked. RpcRequest pointer will be null if rpc is called on primary replica.
virtual void OnInvokeRpc(ReplicaChunkBase* chunk, Internal::RpcRequest* rpc) { (void)chunk; (void)rpc; }
//! Called every time an rpc is sent to a peer.
virtual void OnSendRpc(ReplicaChunkBase* chunk, AZ::u32 chunkIndex, Internal::RpcRequest* rpc, PeerId from, PeerId to, const void* data, size_t len) { (void)chunk; (void)chunkIndex; (void)rpc; (void)from; (void)to; (void)data; (void)len; }
@@ -386,7 +386,7 @@ namespace GridMate
replica->SetMigratable(true);
// register global session info
// this one is kind of special so don't go through AddMaster()
// this one is kind of special so don't go through AddPrimary()
ReplicaContext rc(this, GetTime(), &m_self);
replica->m_createTime = rc.m_realTime;
replica->SetRepId(RepId_SessionInfo);
@@ -405,7 +405,7 @@ namespace GridMate
else
{
// take over ownership of the session info
AZ_Assert(!m_sessionInfo->IsMaster(), "We just became host but we were already the owner of sessionInfo!");
AZ_Assert(!m_sessionInfo->IsPrimary(), "We just became host but we were already the owner of sessionInfo!");
AZ_Assert(m_sessionInfo->m_pHostPeer->IsOrphan(), "We can't be promoted if we are still connected to the host!");
m_sessionInfo->m_pHostPeer->Remove(m_sessionInfo->GetReplica());
m_self.Add(m_sessionInfo->GetReplica());
@@ -903,7 +903,7 @@ namespace GridMate
ReplicaContext rc(this, GetTime());
for (auto& replicaObject : m_self.m_objectsTimeSort)
{
if (replicaObject.m_replica->IsMaster())
if (replicaObject.m_replica->IsPrimary())
{
replicaObject.m_replica->UpdateReplica(rc);
}
@@ -1357,11 +1357,11 @@ namespace GridMate
m_flags &= ~Rm_Processing;
}
//-----------------------------------------------------------------------------
void ReplicaManager::RegisterReplica(const ReplicaPtr& pReplica, bool isMaster, ReplicaContext& rc)
void ReplicaManager::RegisterReplica(const ReplicaPtr& pReplica, bool isPrimary, ReplicaContext& rc)
{
AZ_Assert((pReplica->m_flags & ~Replica::Rep_Traits) == 0, "This replica is not clean, flags=0x%x, rpcs=%d!", pReplica->m_flags);
AZ_Assert(pReplica->GetRepId() != InvalidReplicaId, "You should set the replica ID before you register it!");
pReplica->SetMaster(isMaster);
pReplica->SetPrimary(isPrimary);
pReplica->SetNew();
auto it = m_replicas.insert(AZStd::make_pair(pReplica->GetRepId(), pReplica)); // register with lookup table
(void)it;
@@ -1371,21 +1371,21 @@ namespace GridMate
OnReplicaChanged(pReplica);
}
//-----------------------------------------------------------------------------
ReplicaId ReplicaManager::AddMaster(const ReplicaPtr& pMaster)
ReplicaId ReplicaManager::AddPrimary(const ReplicaPtr& pPrimary)
{
AZ_Assert(IsReady(), "ReplicaManager is not ready!");
AZ_Assert(pMaster, "Attempting to register NULL replica!");
AZ_Assert(pPrimary, "Attempting to register NULL replica!");
ReplicaId newId = m_localIdBlocks.Alloc();
ReplicaContext rc(this, GetTime(), &m_self);
pMaster->m_createTime = rc.m_realTime;
pMaster->SetRepId(newId);
m_self.Add(pMaster.get());
AZStd::static_pointer_cast<ReplicaStatus>(pMaster->m_replicaStatus)->m_ownerSeq.Set(1);
pMaster->InitReplica(this);
RegisterReplica(pMaster, true, rc);
pPrimary->m_createTime = rc.m_realTime;
pPrimary->SetRepId(newId);
m_self.Add(pPrimary.get());
AZStd::static_pointer_cast<ReplicaStatus>(pPrimary->m_replicaStatus)->m_ownerSeq.Set(1);
pPrimary->InitReplica(this);
RegisterReplica(pPrimary, true, rc);
//AZ_TracePrintf("GridMate", "Peer 0x%x: Added replica master 0x%x.\n",
// GetLocalPeerId(), pMaster->GetRepId());
//AZ_TracePrintf("GridMate", "Peer 0x%x: Added replica primary 0x%x.\n",
// GetLocalPeerId(), pPrimary->GetRepId());
return newId;
}
@@ -1458,9 +1458,9 @@ namespace GridMate
continue;
}
ReplicaPeer* source = replica->IsMaster() ? &m_self : replica->m_upstreamHop;
ReplicaPeer* source = replica->IsPrimary() ? &m_self : replica->m_upstreamHop;
if (replica->IsMaster()
if (replica->IsPrimary()
|| (IsSyncHost() && source->GetId() != target->GetId() && !(source->GetMode() == Mode_Peer && target->GetMode() == Mode_Peer)))
{
ReplicaTarget::AddReplicaTarget(target, replica.get());
@@ -1471,7 +1471,7 @@ namespace GridMate
else
{
// Replica might've changed owner -> we need to update its targets accordingly
ReplicaPeer* source = replica->IsMaster() ? &m_self : replica->m_upstreamHop;
ReplicaPeer* source = replica->IsPrimary() ? &m_self : replica->m_upstreamHop;
for (auto it = replica->m_targets.begin(); it != replica->m_targets.end(); )
{
@@ -1652,14 +1652,14 @@ namespace GridMate
}
}
//-----------------------------------------------------------------------------
void ReplicaManager::ChangeReplicaOwnership(ReplicaPtr replica, const ReplicaContext& rc, bool isMaster)
void ReplicaManager::ChangeReplicaOwnership(ReplicaPtr replica, const ReplicaContext& rc, bool isPrimary)
{
bool wasMaster = replica->IsMaster();
if (wasMaster != isMaster) // wasMaster == isMaster can happen when host's replica is moved to client, and client confirms with Cmd_NewOwner
bool wasPrimary = replica->IsPrimary();
if (wasPrimary != isPrimary) // wasPrimary == isPrimary can happen when host's replica is moved to client, and client confirms with Cmd_NewOwner
{
replica->SetMaster(isMaster);
replica->SetPrimary(isPrimary);
replica->OnChangeOwnership(rc);
EBUS_EVENT(Debug::ReplicaDrillerBus, OnReplicaChangeOwnership, replica.get(), wasMaster);
EBUS_EVENT(Debug::ReplicaDrillerBus, OnReplicaChangeOwnership, replica.get(), wasPrimary);
}
}
//-----------------------------------------------------------------------------
@@ -190,7 +190,7 @@ namespace GridMate
//-----------------------------------------------------------------------------
struct ReplicaMgrDesc
{
// Single-master roles that replica managers can have
// Single-primary roles that replica managers can have
enum Roles
{
Role_SyncHost = 1 << 0,
@@ -421,13 +421,13 @@ namespace GridMate
size_t ReleaseIdBlock(PeerId requestor);
void _Unmarshal(ReadBuffer& rb, ReplicaPeer* from);
void RegisterReplica(const ReplicaPtr& pReplica, bool isMaster, ReplicaContext& rc);
void RegisterReplica(const ReplicaPtr& pReplica, bool isPrimary, ReplicaContext& rc);
void UnregisterReplica(const ReplicaPtr& replica, const ReplicaContext& rc);
void RemoveReplicaFromDownstream(const ReplicaPtr& replica, const ReplicaContext& rc);
void MigrateReplica(ReplicaPtr replica, PeerId newOwnerId);
void AnnounceReplicaMigrated(ReplicaId replicaId, PeerId newOwnerId);
void OnReplicaMigrated(ReplicaPtr replica, bool isOwner, const ReplicaContext& rc);
void ChangeReplicaOwnership(ReplicaPtr replica, const ReplicaContext& rc, bool isMaster);
void ChangeReplicaOwnership(ReplicaPtr replica, const ReplicaContext& rc, bool isPrimary);
void AckUpstreamSuspended(ReplicaId replicaId, PeerId sendTo, AZ::u32 requestTime);
void OnAckUpstreamSuspended(ReplicaId replicaId, PeerId from, AZ::u32 requestTime);
void AckDownstream(ReplicaId replicaId, PeerId sendTo, AZ::u32 requestTime);
@@ -595,7 +595,7 @@ namespace GridMate
* Replicas
*/
virtual ReplicaPtr FindReplica(ReplicaId replicaId);
ReplicaId AddMaster(const ReplicaPtr& pMaster);
ReplicaId AddPrimary(const ReplicaPtr& pPrimary);
/*
* Tasks
@@ -50,10 +50,10 @@ namespace GridMate
//! Called on the originator node to request replica migration.
Rpc<RpcArg<PeerId> >::BindInterface<ReplicaStatusInterface, & ReplicaStatusInterface::RequestOwnershipFn> RequestOwnership;
//! Called by the master to suspend upstream requests during replica migration.
//! Called by the primary to suspend upstream requests during replica migration.
Rpc<RpcArg<PeerId>, RpcArg<AZ::u32> >::BindInterface<ReplicaStatusInterface, & ReplicaStatusInterface::MigrationSuspendUpstreamFn, RpcAuthoritativeTraits> MigrationSuspendUpstream;
//! Called by the master to signal downstream flush during replica migration.
//! Called by the primary to signal downstream flush during replica migration.
Rpc<RpcArg<PeerId>, RpcArg<AZ::u32> >::BindInterface<ReplicaStatusInterface, & ReplicaStatusInterface::MigrationRequestDownstreamAckFn, RpcAuthoritativeTraits> MigrationRequestDownstreamAck;
struct ReplicaOptions
@@ -84,7 +84,7 @@ namespace GridMate
// on activation of this replica, create our PeerInfo replica
Replica* peerReplica = Replica::CreateReplica("PeerInfo");
CreateAndAttachReplicaChunk<PeerReplica>(peerReplica);
rc.m_rm->AddMaster(peerReplica);
rc.m_rm->AddPrimary(peerReplica);
}
//-----------------------------------------------------------------------------
void SessionInfo::OnReplicaDeactivate(const ReplicaContext& rc)
@@ -132,7 +132,7 @@ namespace GridMate
(void)rc;
if (m_pMgr->IsSyncHost())
{
AZ_Assert(IsMaster(), "The host should always own sessionInfo!!!");
AZ_Assert(IsPrimary(), "The host should always own sessionInfo!!!");
AZ_Assert(m_pendingPeerReports.find(peerId) == m_pendingPeerReports.end(), "We are already waiting for reports for peer 0x%8x!", peerId);
vector<PeerId> peers;
@@ -205,7 +205,7 @@ namespace GridMate
//-----------------------------------------------------------------------------
void PeerReplica::OnReplicaActivate(const ReplicaContext& rc)
{
if (IsMaster())
if (IsPrimary())
{
m_peerId.Set(rc.m_rm->GetLocalPeerId());
}
@@ -294,7 +294,7 @@ namespace GridMate
//-----------------------------------------------------------------------------
ReplicaTask::TaskStatus ReplicaMarshalZombieTask::Run(const RunContext& context)
{
if (m_replica->IsMaster() || context.m_replicaManager->IsSyncHost())
if (m_replica->IsPrimary() || context.m_replicaManager->IsSyncHost())
{
m_replica->PrepareData(context.m_replicaManager->GetGridMate()->GetDefaultEndianType(),
// A zombie task occurs right before replica gets removed, by design it needs to set all properties one last time.
@@ -35,7 +35,7 @@ namespace GridMate
};
/**
* Task to update master & proxy replicas.
* Task to update primary & proxy replicas.
* Processes RPCs and calls replicas UpdateFromReplica. Will complete immediately if no RPCs
* left queued after processing, otherweise will be repeated next update tick.
* Initiates replica migration if proxy owner has died.
@@ -960,7 +960,7 @@ GridSession::AddMember(GridMember* member)
replica->AttachReplicaChunk(member);
}
m_replicaMgr->AddMaster(replica);
m_replicaMgr->AddPrimary(replica);
member->m_isHost.Set(member->IsLocal());
}
@@ -1583,7 +1583,7 @@ GridSession::OnStateCreate(HSM& sm, const HSM::Event& e)
// Bind session replica
Replica* stateReplica = Replica::CreateReplica("SessionStateInfo");
stateReplica->AttachReplicaChunk(m_state);
m_replicaMgr->AddMaster(stateReplica);
m_replicaMgr->AddPrimary(stateReplica);
// Bind member replica
bool isAdded = AddMember(m_myMember);
@@ -2005,7 +2005,7 @@ GridMember::OnReplicaActivate(const ReplicaContext& rc)
{
// if this member is me... add my state to the system.
AZ_Assert(m_session->GetMyMember() == this, "The only local member should be myMember too!");
rc.m_rm->AddMaster(m_clientState->GetReplica());
rc.m_rm->AddPrimary(m_clientState->GetReplica());
// Both member and client state are valid! send member joined message
EBUS_DBG_EVENT(Debug::SessionDrillerBus, OnMemberJoined, m_session, this);
@@ -2052,7 +2052,7 @@ GridMember::OnReplicaChangeOwnership(const ReplicaContext& rc)
{
(void)rc;
AZ_Assert(m_session->IsMigratingHost(), "This function can be called only during host migration!");
if (IsMaster())
if (IsPrimary())
{
// Host owns the members, if I became the owner means I am the HOST!
if (m_session->m_myMember == this)
@@ -2084,7 +2084,7 @@ GridMember::OnKick(AZ::u8 reason, const RpcContext& rc)
m_session->Leave(false);
}
return true; // this is called only on the master
return true; // this is called only on the primary
}
return false;
}
@@ -2320,8 +2320,8 @@ void
GridMemberStateReplica::OnReplicaDeactivate(const ReplicaContext& rc)
{
(void)rc;
// for master (this is our state) we always keep it. So don't do anything.
if (IsMaster())
// for primary (this is our state) we always keep it. So don't do anything.
if (IsPrimary())
{
return;
}
+9 -9
View File
@@ -534,7 +534,7 @@ namespace GridMate
auto replica = Replica::CreateReplica("ProximityInterestHandlerRules");
m_rulesReplica = CreateAndAttachReplicaChunk<ProximityInterestChunk>(replica);
m_rm->AddMaster(replica);
m_rm->AddPrimary(replica);
}
void ProximityInterestHandler::OnRulesHandlerUnregistered(InterestManager* manager)
@@ -658,7 +658,7 @@ class Integ_InterestTest
{
AZ_Printf("GridMate", "InterestTestChunk::OnReplicaActivate repId=%08X(%s) fromPeerId=%08X localPeerId=%08X\n",
GetReplicaId(),
IsMaster() ? "master" : "proxy",
IsPrimary() ? "primary" : "proxy",
rc.m_peer ? rc.m_peer->GetId() : 0,
rc.m_rm->GetLocalPeerId());
@@ -674,7 +674,7 @@ class Integ_InterestTest
{
AZ_Printf("GridMate", "InterestTestChunk::OnReplicaDeactivate repId=%08X(%s) fromPeerId=%08X localPeerId=%08X\n",
GetReplicaId(),
IsMaster() ? "master" : "proxy",
IsPrimary() ? "primary" : "proxy",
rc.m_peer ? rc.m_peer->GetId() : 0,
rc.m_rm->GetLocalPeerId());
@@ -734,7 +734,7 @@ class Integ_InterestTest
m_replica->m_data.Set(m_num);
m_replica->m_bitmaskAttributeData.Set(1 << i);
m_session->GetReplicaMgr()->AddMaster(r);
m_session->GetReplicaMgr()->AddPrimary(r);
}
void UpdateAttribute()
@@ -952,7 +952,7 @@ public:
if (numUpdates == 250)
{
// Checking everybody lost all replicas (except master)
// Checking everybody lost all replicas (except primary)
for (int i = 0; i < k_numMachines; ++i)
{
for (int j = 0; j < k_numMachines; ++j)
@@ -1079,11 +1079,11 @@ class LargeWorldTest
void OnReplicaActivate(const ReplicaContext& rc) override
{
/*if (!IsMaster())*/
/*if (!IsPrimary())*/
/*{
AZ_Printf("GridMate", "LargeWorldTestChunk::OnReplicaActivate repId=%08X(%s) fromPeerId=%08X localPeerId=%08X\n",
GetReplicaId(),
IsMaster() ? "master" : "proxy",
IsPrimary() ? "primary" : "proxy",
rc.m_peer ? rc.m_peer->GetId() : 0,
rc.m_rm->GetLocalPeerId());
}*/
@@ -1214,7 +1214,7 @@ class LargeWorldTest
m_replicas.push_back(replica);
m_session->GetReplicaMgr()->AddMaster(r);
m_session->GetReplicaMgr()->AddPrimary(r);
}
void PopulateWorld()
@@ -1453,7 +1453,7 @@ public:
if (numUpdates == 250)
{
// Checking everybody lost all replicas (except master)
// Checking everybody lost all replicas (except primary)
for (int i = 0; i < k_numMachines; ++i)
{
/*for (int j = 0; j < k_numMachines; ++j)
+95 -95
View File
@@ -1615,7 +1615,7 @@ public:
{
(void)f;
(void)rc;
AZ_TracePrintf("GridMate", "Executed MyHandler123 requested at %u with %g on %s at %u.\n", rc.m_timestamp, f, GetReplica()->IsMaster() ? "Master" : "Proxy", rc.m_realTime);
AZ_TracePrintf("GridMate", "Executed MyHandler123 requested at %u with %g on %s at %u.\n", rc.m_timestamp, f, GetReplica()->IsPrimary() ? "Primary" : "Proxy", rc.m_realTime);
return true;
}
@@ -1653,14 +1653,14 @@ public:
(void)rc;
if (rc.m_rm->GetUserContext(12345))
{
AZ_TracePrintf("GridMate", "Activate %s with UserData:%p\n", GetReplica()->IsMaster() ? "master" : "proxy", rc.m_rm->GetUserContext(12345));
AZ_TracePrintf("GridMate", "Activate %s with UserData:%p\n", GetReplica()->IsPrimary() ? "primary" : "proxy", rc.m_rm->GetUserContext(12345));
}
if (IsProxy())
{
Bind(aznew MyObj());
}
if (IsMaster())
if (IsPrimary())
{
EBUS_EVENT(MigratableReplicaDebugMsgs::EBus, OnNewOwner, GetReplicaId(), rc.m_rm);
}
@@ -1679,9 +1679,9 @@ public:
void OnReplicaChangeOwnership(const ReplicaContext& rc) override
{
(void)rc;
AZ_TracePrintf("GridMate", "Migratable replica 0x%x became %s on Peer %d\n", (int) GetReplicaId(), IsMaster() ? "master" : "proxy", (int) rc.m_rm->GetLocalPeerId());
AZ_TracePrintf("GridMate", "Migratable replica 0x%x became %s on Peer %d\n", (int) GetReplicaId(), IsPrimary() ? "primary" : "proxy", (int) rc.m_rm->GetLocalPeerId());
if (IsMaster())
if (IsPrimary())
{
EBUS_EVENT(MigratableReplicaDebugMsgs::EBus, OnNewOwner, GetReplicaId(), rc.m_rm);
}
@@ -1725,7 +1725,7 @@ protected:
{
(void)f;
(void)rc;
AZ_TracePrintf("GridMate", "Executed MyHandler123 requested at %u with %g on %s at %u.\n", rc.m_timestamp, f, IsMaster() ? "Master" : "Proxy", rc.m_realTime);
AZ_TracePrintf("GridMate", "Executed MyHandler123 requested at %u with %g on %s at %u.\n", rc.m_timestamp, f, IsPrimary() ? "Primary" : "Proxy", rc.m_realTime);
return true;
}
bool MyHandler2(const float& f, int p2, const RpcContext& rc)
@@ -1733,7 +1733,7 @@ protected:
(void)f;
(void)p2;
(void)rc;
AZ_TracePrintf("GridMate", "Executed MyHandler2 requested at %u with %g,%d on %s at %u.\n", rc.m_timestamp, f, p2, IsMaster() ? "Master" : "Proxy", rc.m_realTime);
AZ_TracePrintf("GridMate", "Executed MyHandler2 requested at %u with %g,%d on %s at %u.\n", rc.m_timestamp, f, p2, IsPrimary() ? "Primary" : "Proxy", rc.m_realTime);
return true;
}
bool MyHandler3(const float& f, int p2, EBla p3, const RpcContext& rc)
@@ -1742,7 +1742,7 @@ protected:
(void)p2;
(void)p3;
(void)rc;
AZ_TracePrintf("GridMate", "Executed MyHandler3 requested at %u with %g,%d,%d on %s at %u.\n", rc.m_timestamp, f, p2, p3, IsMaster() ? "Master" : "Proxy", rc.m_realTime);
AZ_TracePrintf("GridMate", "Executed MyHandler3 requested at %u with %g,%d,%d on %s at %u.\n", rc.m_timestamp, f, p2, p3, IsPrimary() ? "Primary" : "Proxy", rc.m_realTime);
return true;
}
bool MyHandler4(const float& f, int p2, EBla p3, const IntVectorType& p4, const RpcContext& rc)
@@ -1752,13 +1752,13 @@ protected:
(void)p3;
(void)p4;
(void)rc;
AZ_TracePrintf("GridMate", "Executed MyHandler4 requested at %u with %g,%d,%d,%d,%d on %s at %u.\n", rc.m_timestamp, f, p2, p3, p4[0], p4[1], IsMaster() ? "Master" : "Proxy", rc.m_realTime);
AZ_TracePrintf("GridMate", "Executed MyHandler4 requested at %u with %g,%d,%d,%d,%d on %s at %u.\n", rc.m_timestamp, f, p2, p3, p4[0], p4[1], IsPrimary() ? "Primary" : "Proxy", rc.m_realTime);
return true;
}
bool MyHandlerUnreliable(const int& i, const RpcContext& rc)
{
(void)rc;
AZ_TracePrintf("GridMate", "Executed MyHandlerUnreliable requested at %u with %d on %s at %u.\n", rc.m_timestamp, i, IsMaster() ? "Master" : "Proxy", rc.m_realTime);
AZ_TracePrintf("GridMate", "Executed MyHandlerUnreliable requested at %u with %d on %s at %u.\n", rc.m_timestamp, i, IsPrimary() ? "Primary" : "Proxy", rc.m_realTime);
AZ_TEST_ASSERT(i > m_prevUnreliableValue);
if ((i - m_prevUnreliableValue) > 1)
{
@@ -1824,7 +1824,7 @@ public:
(void)rc;
if (rc.m_rm->GetUserContext(12345))
{
AZ_TracePrintf("GridMate", "Activate %s with UserData:%p\n", IsMaster() ? "master" : "proxy", rc.m_rm->GetUserContext(12345));
AZ_TracePrintf("GridMate", "Activate %s with UserData:%p\n", IsPrimary() ? "primary" : "proxy", rc.m_rm->GetUserContext(12345));
}
if (IsProxy())
{
@@ -1845,7 +1845,7 @@ public:
void OnReplicaChangeOwnership(const ReplicaContext& rc) override
{
(void)rc;
AZ_TracePrintf("GridMate", "NonMigratable replica 0x%x became %s on Peer %d\n", (int) GetReplicaId(), IsMaster() ? "master" : "proxy", (int) rc.m_rm->GetLocalPeerId());
AZ_TracePrintf("GridMate", "NonMigratable replica 0x%x became %s on Peer %d\n", (int) GetReplicaId(), IsPrimary() ? "primary" : "proxy", (int) rc.m_rm->GetLocalPeerId());
}
void Bind(MyObj* pObj)
@@ -1950,7 +1950,7 @@ TEST_F(Integ_ReplicaGMTest, ReplicaTest)
// put something on s1 to get it going
auto rep = Replica::CreateReplica(nullptr);
s1rep1 = CreateAndAttachReplicaChunk<MigratableReplica>(rep);
s1rep1id = sessions[s1].GetReplicaMgr().AddMaster(rep);
s1rep1id = sessions[s1].GetReplicaMgr().AddPrimary(rep);
s1rep1->Bind(s1obj1 = aznew MyObj());
// connect s2 to s1
@@ -1993,7 +1993,7 @@ TEST_F(Integ_ReplicaGMTest, ReplicaTest)
{
auto newReplica = Replica::CreateReplica(nullptr);
s2rep1 = CreateAndAttachReplicaChunk<MyDerivedReplica>(newReplica);
s2rep1id = sessions[s2].GetReplicaMgr().AddMaster(newReplica);
s2rep1id = sessions[s2].GetReplicaMgr().AddPrimary(newReplica);
s2rep1->Bind(s2obj1 = aznew MyObj());
}
else
@@ -2020,7 +2020,7 @@ TEST_F(Integ_ReplicaGMTest, ReplicaTest)
{
auto newReplica = Replica::CreateReplica(nullptr);
s1rep2 = CreateAndAttachReplicaChunk<NonMigratableReplica>(newReplica);
s1rep2id = sessions[s1].GetReplicaMgr().AddMaster(newReplica);
s1rep2id = sessions[s1].GetReplicaMgr().AddPrimary(newReplica);
s1rep2->Bind(s1obj2 = aznew MyObj);
}
else
@@ -2041,7 +2041,7 @@ TEST_F(Integ_ReplicaGMTest, ReplicaTest)
{
auto newReplica = Replica::CreateReplica(nullptr);
s3rep1 = CreateAndAttachReplicaChunk<MigratableReplica>(newReplica);
s3rep1id = sessions[s3].GetReplicaMgr().AddMaster(newReplica);
s3rep1id = sessions[s3].GetReplicaMgr().AddPrimary(newReplica);
s3rep1->Bind(s3obj1 = aznew MyObj());
}
else
@@ -2289,13 +2289,13 @@ TEST_F(Integ_ForcedReplicaMigrationTest, ForcedReplicaMigrationTest)
{
auto rep = Replica::CreateReplica(nullptr);
migrRep[i] = CreateAndAttachReplicaChunk<MigratableReplica>(rep, aznew MyObj());
peers[i].GetReplicaMgr().AddMaster(rep);
peers[i].GetReplicaMgr().AddPrimary(rep);
AZ_TEST_ASSERT(m_replicaOwnership[migrRep[i]->GetReplicaId()] == &peers[i].GetReplicaMgr());
}
{
auto rep = Replica::CreateReplica(nullptr);
nonMigrRep[i] = CreateAndAttachReplicaChunk<NonMigratableReplica>(rep, aznew MyObj());
peers[i].GetReplicaMgr().AddMaster(rep);
peers[i].GetReplicaMgr().AddPrimary(rep);
}
}
addReplicas = false;
@@ -2418,7 +2418,7 @@ public:
void OnReplicaActivate(const ReplicaContext& rc) override
{
if (IsMaster())
if (IsPrimary())
{
m_owner.Set(rc.m_rm->GetLocalPeerId() - 1);
m_control.Set(rc.m_rm->GetLocalPeerId() - 1);
@@ -2427,9 +2427,9 @@ public:
void OnReplicaChangeOwnership(const ReplicaContext& rc) override
{
if (IsMaster())
if (IsPrimary())
{
AZ_TracePrintf("GridMate", "OnChangeOwnership: 0x%04x Became master on node %d\n", GetReplicaId(), rc.m_rm->GetLocalPeerId() - 1);
AZ_TracePrintf("GridMate", "OnChangeOwnership: 0x%04x Became primary on node %d\n", GetReplicaId(), rc.m_rm->GetLocalPeerId() - 1);
m_owner.Set(rc.m_rm->GetLocalPeerId() - 1);
}
else
@@ -2591,18 +2591,18 @@ TEST_F(Integ_ReplicaMigrationRequestTest, ReplicaMigrationRequestTest)
{
auto rep = Replica::CreateReplica(nullptr);
nodes[iNode].m_always = CreateAndAttachReplicaChunk<AlwaysMigratable>(rep);
nodes[iNode].m_session.GetReplicaMgr().AddMaster(rep);
nodes[iNode].m_session.GetReplicaMgr().AddPrimary(rep);
}
{
auto rep = Replica::CreateReplica(nullptr);
nodes[iNode].m_never = CreateAndAttachReplicaChunk<NeverMigratable>(rep);
nodes[iNode].m_session.GetReplicaMgr().AddMaster(rep);
nodes[iNode].m_session.GetReplicaMgr().AddPrimary(rep);
}
{
auto rep = Replica::CreateReplica(nullptr);
nodes[iNode].m_sometimes = CreateAndAttachReplicaChunk<SometimesMigratable>(rep);
nodes[iNode].m_sometimes->m_acceptMigrationRequests = iNode == Peer1 || iNode == Client1;
nodes[iNode].m_session.GetReplicaMgr().AddMaster(rep);
nodes[iNode].m_session.GetReplicaMgr().AddPrimary(rep);
}
}
}
@@ -2675,7 +2675,7 @@ TEST_F(Integ_ReplicaMigrationRequestTest, ReplicaMigrationRequestTest)
AZ_TEST_ASSERT(nodes[Client1].m_always->m_accepted == 1);
AZ_TEST_ASSERT(nodes[Client1].m_always->GetReplica()->IsProxy());
AZ_TEST_ASSERT(nodes[Client1].m_always->m_owner.Get() == Client2);
AZ_TEST_ASSERT(nodes[Client2].m_session.GetReplicaMgr().FindReplica(nodes[Client1].m_always->GetReplicaId())->IsMaster());
AZ_TEST_ASSERT(nodes[Client2].m_session.GetReplicaMgr().FindReplica(nodes[Client1].m_always->GetReplicaId())->IsPrimary());
// C1 -> C2 -> Host (2nd migration)
ReplicaPtr aHonC1 = nodes[Host].m_session.GetReplicaMgr().FindReplica(nodes[Client1].m_always->GetReplicaId());
@@ -2737,7 +2737,7 @@ TEST_F(Integ_ReplicaMigrationRequestTest, ReplicaMigrationRequestTest)
AZ_TEST_ASSERT(nodes[Peer1].m_always->m_accepted == 1);
AZ_TEST_ASSERT(nodes[Peer1].m_always->GetReplica()->IsProxy());
AZ_TEST_ASSERT(nodes[Peer1].m_always->m_owner.Get() == Peer2);
AZ_TEST_ASSERT(nodes[Peer2].m_session.GetReplicaMgr().FindReplica(nodes[Peer1].m_always->GetReplicaId())->IsMaster());
AZ_TEST_ASSERT(nodes[Peer2].m_session.GetReplicaMgr().FindReplica(nodes[Peer1].m_always->GetReplicaId())->IsPrimary());
AZ_TEST_ASSERT(nodes[Peer1].m_always->m_control.Get() == Peer2);
// P2 -> Host -> C2 (both at same time, with C2 arriving second)
@@ -2750,7 +2750,7 @@ TEST_F(Integ_ReplicaMigrationRequestTest, ReplicaMigrationRequestTest)
AZ_TEST_ASSERT(aP2onH->m_accepted == 1);
AZ_TEST_ASSERT(aP2onH->GetReplica()->IsProxy());
AZ_TEST_ASSERT(aP2onH->m_owner.Get() == Client2);
AZ_TEST_ASSERT(nodes[Client2].m_session.GetReplicaMgr().FindReplica(nodes[Peer2].m_always->GetReplicaId())->IsMaster());
AZ_TEST_ASSERT(nodes[Client2].m_session.GetReplicaMgr().FindReplica(nodes[Peer2].m_always->GetReplicaId())->IsPrimary());
AZ_TEST_ASSERT(nodes[Peer2].m_always->m_control.Get() == Client2);
// Host -> C1
@@ -2758,7 +2758,7 @@ TEST_F(Integ_ReplicaMigrationRequestTest, ReplicaMigrationRequestTest)
AZ_TEST_ASSERT(nodes[Host].m_always->m_accepted == 1);
AZ_TEST_ASSERT(nodes[Host].m_always->GetReplica()->IsProxy());
AZ_TEST_ASSERT(nodes[Host].m_always->m_owner.Get() == Client1);
AZ_TEST_ASSERT(nodes[Client1].m_session.GetReplicaMgr().FindReplica(nodes[Host].m_always->GetReplicaId())->IsMaster());
AZ_TEST_ASSERT(nodes[Client1].m_session.GetReplicaMgr().FindReplica(nodes[Host].m_always->GetReplicaId())->IsPrimary());
AZ_TEST_ASSERT(nodes[Host].m_always->m_control.Get() == Client1);
// C1 -> C2 -> Host (2nd migration)
@@ -2771,7 +2771,7 @@ TEST_F(Integ_ReplicaMigrationRequestTest, ReplicaMigrationRequestTest)
AZ_TEST_ASSERT(aC1onC2->m_accepted == 1);
AZ_TEST_ASSERT(aC1onC2->GetReplica()->IsProxy());
AZ_TEST_ASSERT(aC1onC2->m_owner.Get() == Host);
AZ_TEST_ASSERT(nodes[Host].m_session.GetReplicaMgr().FindReplica(nodes[Client1].m_always->GetReplicaId())->IsMaster());
AZ_TEST_ASSERT(nodes[Host].m_session.GetReplicaMgr().FindReplica(nodes[Client1].m_always->GetReplicaId())->IsPrimary());
AZ_TEST_ASSERT(nodes[Client1].m_always->m_control.Get() == Host);
// C2 -> P1
@@ -2779,13 +2779,13 @@ TEST_F(Integ_ReplicaMigrationRequestTest, ReplicaMigrationRequestTest)
AZ_TEST_ASSERT(nodes[Client2].m_always->m_accepted == 1);
AZ_TEST_ASSERT(nodes[Client2].m_always->GetReplica()->IsProxy());
AZ_TEST_ASSERT(nodes[Client2].m_always->m_owner.Get() == Peer1);
AZ_TEST_ASSERT(nodes[Peer1].m_session.GetReplicaMgr().FindReplica(nodes[Client2].m_always->GetReplicaId())->IsMaster());
AZ_TEST_ASSERT(nodes[Peer1].m_session.GetReplicaMgr().FindReplica(nodes[Client2].m_always->GetReplicaId())->IsPrimary());
AZ_TEST_ASSERT(nodes[Client2].m_always->m_control.Get() == Peer1);
// P1 -> C1 (Forbidden)
AZ_TEST_ASSERT(nodes[Peer1].m_never->m_requests == 0);
AZ_TEST_ASSERT(nodes[Peer1].m_never->m_accepted == 0);
AZ_TEST_ASSERT(nodes[Peer1].m_never->GetReplica()->IsMaster());
AZ_TEST_ASSERT(nodes[Peer1].m_never->GetReplica()->IsPrimary());
AZ_TEST_ASSERT(nodes[Peer1].m_never->m_owner.Get() == Peer1);
AZ_TEST_ASSERT(nodes[Client1].m_session.GetReplicaMgr().FindReplica(nodes[Peer1].m_never->GetReplicaId())->IsProxy());
AZ_TEST_ASSERT(nodes[Peer1].m_never->m_control.Get() == Peer1);
@@ -2793,7 +2793,7 @@ TEST_F(Integ_ReplicaMigrationRequestTest, ReplicaMigrationRequestTest)
// C2 -> P2 (Forbidden)
AZ_TEST_ASSERT(nodes[Client2].m_never->m_requests == 0);
AZ_TEST_ASSERT(nodes[Client2].m_never->m_accepted == 0);
AZ_TEST_ASSERT(nodes[Client2].m_never->GetReplica()->IsMaster());
AZ_TEST_ASSERT(nodes[Client2].m_never->GetReplica()->IsPrimary());
AZ_TEST_ASSERT(nodes[Client2].m_never->m_owner.Get() == Client2);
AZ_TEST_ASSERT(nodes[Peer2].m_session.GetReplicaMgr().FindReplica(nodes[Client2].m_never->GetReplicaId())->IsProxy());
AZ_TEST_ASSERT(nodes[Client2].m_never->m_control.Get() == Client2);
@@ -2803,7 +2803,7 @@ TEST_F(Integ_ReplicaMigrationRequestTest, ReplicaMigrationRequestTest)
AZ_TEST_ASSERT(nodes[Peer1].m_sometimes->m_accepted == 1);
AZ_TEST_ASSERT(nodes[Peer1].m_sometimes->GetReplica()->IsProxy());
AZ_TEST_ASSERT(nodes[Peer1].m_sometimes->m_owner.Get() == Host);
AZ_TEST_ASSERT(nodes[Host].m_session.GetReplicaMgr().FindReplica(nodes[Peer1].m_sometimes->GetReplicaId())->IsMaster());
AZ_TEST_ASSERT(nodes[Host].m_session.GetReplicaMgr().FindReplica(nodes[Peer1].m_sometimes->GetReplicaId())->IsPrimary());
AZ_TEST_ASSERT(nodes[Peer1].m_sometimes->m_control.Get() == Host);
// C1 -> P1
@@ -2811,13 +2811,13 @@ TEST_F(Integ_ReplicaMigrationRequestTest, ReplicaMigrationRequestTest)
AZ_TEST_ASSERT(nodes[Client1].m_sometimes->m_accepted == 1);
AZ_TEST_ASSERT(nodes[Client1].m_sometimes->GetReplica()->IsProxy());
AZ_TEST_ASSERT(nodes[Client1].m_sometimes->m_owner.Get() == Peer1);
AZ_TEST_ASSERT(nodes[Peer1].m_session.GetReplicaMgr().FindReplica(nodes[Client1].m_sometimes->GetReplicaId())->IsMaster());
AZ_TEST_ASSERT(nodes[Peer1].m_session.GetReplicaMgr().FindReplica(nodes[Client1].m_sometimes->GetReplicaId())->IsPrimary());
AZ_TEST_ASSERT(nodes[Client1].m_sometimes->m_control.Get() == Peer1);
// P2 -> C2 (Forbidden)
AZ_TEST_ASSERT(nodes[Peer2].m_sometimes->m_requests == 1);
AZ_TEST_ASSERT(nodes[Peer2].m_sometimes->m_accepted == 0);
AZ_TEST_ASSERT(nodes[Peer2].m_sometimes->GetReplica()->IsMaster());
AZ_TEST_ASSERT(nodes[Peer2].m_sometimes->GetReplica()->IsPrimary());
AZ_TEST_ASSERT(nodes[Peer2].m_sometimes->m_owner.Get() == Peer2);
AZ_TEST_ASSERT(nodes[Client2].m_session.GetReplicaMgr().FindReplica(nodes[Peer2].m_never->GetReplicaId())->IsProxy());
AZ_TEST_ASSERT(nodes[Peer2].m_sometimes->m_control.Get() == Peer2);
@@ -2825,7 +2825,7 @@ TEST_F(Integ_ReplicaMigrationRequestTest, ReplicaMigrationRequestTest)
// C2 -> Host (Forbidden)
AZ_TEST_ASSERT(nodes[Client2].m_sometimes->m_requests == 1);
AZ_TEST_ASSERT(nodes[Client2].m_sometimes->m_accepted == 0);
AZ_TEST_ASSERT(nodes[Client2].m_sometimes->GetReplica()->IsMaster());
AZ_TEST_ASSERT(nodes[Client2].m_sometimes->GetReplica()->IsPrimary());
AZ_TEST_ASSERT(nodes[Client2].m_sometimes->m_owner.Get() == Client2);
AZ_TEST_ASSERT(nodes[Host].m_session.GetReplicaMgr().FindReplica(nodes[Client2].m_never->GetReplicaId())->IsProxy());
AZ_TEST_ASSERT(nodes[Client2].m_sometimes->m_control.Get() == Client2);
@@ -2946,12 +2946,12 @@ TEST_F(Integ_PeerRejoinTest, PeerRejoinTest)
{
auto rep = Replica::CreateReplica(nullptr);
migrRep[i] = CreateAndAttachReplicaChunk<MigratableReplica>(rep, aznew MyObj());
peers[i].GetReplicaMgr().AddMaster(rep);
peers[i].GetReplicaMgr().AddPrimary(rep);
}
{
auto rep = Replica::CreateReplica(nullptr);
nonMigrRep[i] = CreateAndAttachReplicaChunk<NonMigratableReplica>(rep, aznew MyObj());
peers[i].GetReplicaMgr().AddMaster(rep);
peers[i].GetReplicaMgr().AddPrimary(rep);
}
}
addReplicas = false;
@@ -3099,7 +3099,7 @@ public:
{
// make sure the requestor is set to s1
AZ_TEST_ASSERT(rpcContext.m_sourcePeer == s1 + 1);
if (IsMaster())
if (IsPrimary())
{
m_nAuthoritativeOnlyRpcCallsFromS1.Modify([](int& value) { ++value; return true; });
}
@@ -3114,7 +3114,7 @@ public:
{
// make sure the requestor is set to s2
AZ_TEST_ASSERT(rpcContext.m_sourcePeer == s2 + 1);
if (IsMaster())
if (IsPrimary())
{
m_nAuthoritativeOnlyRpcCallsFromS2.Modify([](int& value) { ++value; return true; });
}
@@ -3129,7 +3129,7 @@ public:
{
// make sure the requestor is set to s3
AZ_TEST_ASSERT(rpcContext.m_sourcePeer == s3 + 1);
if (IsMaster())
if (IsPrimary())
{
m_nAuthoritativeOnlyRpcCallsFromS3.Modify([](int& value) { ++value; return true; });
}
@@ -3168,7 +3168,7 @@ TEST_F(Integ_ReplicationSecurityOptionsTest, ReplicationSecurityOptionsTest)
ReplicaChunkDescriptorTable::Get().RegisterChunkType<TestChunk>();
MPSession sessions[nSessions];
ReplicaPtr masters[nSessions];
ReplicaPtr primarys[nSessions];
// initialize transport
int basePort = 4427;
@@ -3202,10 +3202,10 @@ TEST_F(Integ_ReplicationSecurityOptionsTest, ReplicationSecurityOptionsTest)
for (int i = 0; i < nSessions; ++i)
{
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().IsReady());
masters[i] = Replica::CreateReplica("ReplicationSecurityOptionsTest::TestReplica");
primarys[i] = Replica::CreateReplica("ReplicationSecurityOptionsTest::TestReplica");
TestChunkPtr chunk = CreateReplicaChunk<TestChunk>();
masters[i]->AttachReplicaChunk(chunk);
sessions[i].GetReplicaMgr().AddMaster(masters[i]);
primarys[i]->AttachReplicaChunk(chunk);
sessions[i].GetReplicaMgr().AddPrimary(primarys[i]);
}
}
@@ -3214,9 +3214,9 @@ TEST_F(Integ_ReplicationSecurityOptionsTest, ReplicationSecurityOptionsTest)
AZ_TEST_START_TRACE_SUPPRESSION;
for (int i = 0; i < nSessions; ++i)
{
sessions[s1].GetReplicaMgr().FindReplica(masters[i]->GetRepId())->FindReplicaChunk<TestChunk>()->ForwardSourcePeerRpcFromS1();
sessions[s2].GetReplicaMgr().FindReplica(masters[i]->GetRepId())->FindReplicaChunk<TestChunk>()->ForwardSourcePeerRpcFromS2();
sessions[s3].GetReplicaMgr().FindReplica(masters[i]->GetRepId())->FindReplicaChunk<TestChunk>()->ForwardSourcePeerRpcFromS3();
sessions[s1].GetReplicaMgr().FindReplica(primarys[i]->GetRepId())->FindReplicaChunk<TestChunk>()->ForwardSourcePeerRpcFromS1();
sessions[s2].GetReplicaMgr().FindReplica(primarys[i]->GetRepId())->FindReplicaChunk<TestChunk>()->ForwardSourcePeerRpcFromS2();
sessions[s3].GetReplicaMgr().FindReplica(primarys[i]->GetRepId())->FindReplicaChunk<TestChunk>()->ForwardSourcePeerRpcFromS3();
}
}
@@ -3227,36 +3227,36 @@ TEST_F(Integ_ReplicationSecurityOptionsTest, ReplicationSecurityOptionsTest)
AZ_TEST_STOP_TRACE_SUPPRESSION(2);
// All chunks should have received the call from the host
AZ_TEST_ASSERT(masters[s1]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1 == 1);
AZ_TEST_ASSERT(masters[s2]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1 == 1);
AZ_TEST_ASSERT(masters[s3]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1 == 1);
AZ_TEST_ASSERT(primarys[s1]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1 == 1);
AZ_TEST_ASSERT(primarys[s2]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1 == 1);
AZ_TEST_ASSERT(primarys[s3]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1 == 1);
// the host chunk should have received calls from both clients
AZ_TEST_ASSERT(masters[s1]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2 == 1);
AZ_TEST_ASSERT(masters[s1]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3 == 1);
AZ_TEST_ASSERT(primarys[s1]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2 == 1);
AZ_TEST_ASSERT(primarys[s1]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3 == 1);
// the chunk on s2 should receive its own call but not from s3
AZ_TEST_ASSERT(masters[s2]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2 == 1);
AZ_TEST_ASSERT(masters[s2]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3 == 0);
AZ_TEST_ASSERT(primarys[s2]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2 == 1);
AZ_TEST_ASSERT(primarys[s2]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3 == 0);
// the chunk on s3 should receive its own call but not from s2
AZ_TEST_ASSERT(masters[s3]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2 == 0);
AZ_TEST_ASSERT(masters[s3]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3 == 1);
AZ_TEST_ASSERT(primarys[s3]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2 == 0);
AZ_TEST_ASSERT(primarys[s3]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3 == 1);
// all datasets should have propagated properly
for (int i = 0; i < nSessions; ++i)
{
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(masters[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1.Get() == masters[s1]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(masters[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2.Get() == masters[s1]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(masters[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3.Get() == masters[s1]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1.Get() == primarys[s1]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2.Get() == primarys[s1]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3.Get() == primarys[s1]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(masters[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1.Get() == masters[s2]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(masters[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2.Get() == masters[s2]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(masters[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3.Get() == masters[s2]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1.Get() == primarys[s2]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2.Get() == primarys[s2]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3.Get() == primarys[s2]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(masters[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1.Get() == masters[s3]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(masters[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2.Get() == masters[s3]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(masters[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3.Get() == masters[s3]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1.Get() == primarys[s3]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2.Get() == primarys[s3]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3.Get() == primarys[s3]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3.Get());
}
}
@@ -3265,9 +3265,9 @@ TEST_F(Integ_ReplicationSecurityOptionsTest, ReplicationSecurityOptionsTest)
AZ_TEST_START_TRACE_SUPPRESSION;
for (int i = 0; i < nSessions; ++i)
{
sessions[s1].GetReplicaMgr().FindReplica(masters[i]->GetRepId())->FindReplicaChunk<TestChunk>()->AuthoritativeOnlyRpcFromS1();
sessions[s2].GetReplicaMgr().FindReplica(masters[i]->GetRepId())->FindReplicaChunk<TestChunk>()->AuthoritativeOnlyRpcFromS2();
sessions[s3].GetReplicaMgr().FindReplica(masters[i]->GetRepId())->FindReplicaChunk<TestChunk>()->AuthoritativeOnlyRpcFromS3();
sessions[s1].GetReplicaMgr().FindReplica(primarys[i]->GetRepId())->FindReplicaChunk<TestChunk>()->AuthoritativeOnlyRpcFromS1();
sessions[s2].GetReplicaMgr().FindReplica(primarys[i]->GetRepId())->FindReplicaChunk<TestChunk>()->AuthoritativeOnlyRpcFromS2();
sessions[s3].GetReplicaMgr().FindReplica(primarys[i]->GetRepId())->FindReplicaChunk<TestChunk>()->AuthoritativeOnlyRpcFromS3();
}
}
@@ -3278,40 +3278,40 @@ TEST_F(Integ_ReplicationSecurityOptionsTest, ReplicationSecurityOptionsTest)
AZ_TEST_STOP_TRACE_SUPPRESSION(6);
// Each chunk should have received their own AuthoritativeOnlyRpc once.
AZ_TEST_ASSERT(masters[s1]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1 == 1);
AZ_TEST_ASSERT(masters[s2]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2 == 1);
AZ_TEST_ASSERT(masters[s3]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3 == 1);
AZ_TEST_ASSERT(primarys[s1]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1 == 1);
AZ_TEST_ASSERT(primarys[s2]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2 == 1);
AZ_TEST_ASSERT(primarys[s3]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3 == 1);
// Calls from other nodes should have been discarded.
AZ_TEST_ASSERT(masters[s1]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2 == 0);
AZ_TEST_ASSERT(masters[s1]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3 == 0);
AZ_TEST_ASSERT(masters[s2]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1 == 0);
AZ_TEST_ASSERT(masters[s2]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3 == 0);
AZ_TEST_ASSERT(masters[s3]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1 == 0);
AZ_TEST_ASSERT(masters[s3]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2 == 0);
AZ_TEST_ASSERT(primarys[s1]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2 == 0);
AZ_TEST_ASSERT(primarys[s1]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3 == 0);
AZ_TEST_ASSERT(primarys[s2]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1 == 0);
AZ_TEST_ASSERT(primarys[s2]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3 == 0);
AZ_TEST_ASSERT(primarys[s3]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1 == 0);
AZ_TEST_ASSERT(primarys[s3]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2 == 0);
// Calls should have successfully propagated to the other 2 proxies
AZ_TEST_ASSERT(sessions[s1].GetReplicaMgr().FindReplica(masters[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyProxyRpcCallsFromS2 == 1);
AZ_TEST_ASSERT(sessions[s1].GetReplicaMgr().FindReplica(masters[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyProxyRpcCallsFromS3 == 1);
AZ_TEST_ASSERT(sessions[s2].GetReplicaMgr().FindReplica(masters[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyProxyRpcCallsFromS1 == 1);
AZ_TEST_ASSERT(sessions[s2].GetReplicaMgr().FindReplica(masters[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyProxyRpcCallsFromS3 == 1);
AZ_TEST_ASSERT(sessions[s3].GetReplicaMgr().FindReplica(masters[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyProxyRpcCallsFromS1 == 1);
AZ_TEST_ASSERT(sessions[s3].GetReplicaMgr().FindReplica(masters[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyProxyRpcCallsFromS2 == 1);
AZ_TEST_ASSERT(sessions[s1].GetReplicaMgr().FindReplica(primarys[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyProxyRpcCallsFromS2 == 1);
AZ_TEST_ASSERT(sessions[s1].GetReplicaMgr().FindReplica(primarys[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyProxyRpcCallsFromS3 == 1);
AZ_TEST_ASSERT(sessions[s2].GetReplicaMgr().FindReplica(primarys[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyProxyRpcCallsFromS1 == 1);
AZ_TEST_ASSERT(sessions[s2].GetReplicaMgr().FindReplica(primarys[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyProxyRpcCallsFromS3 == 1);
AZ_TEST_ASSERT(sessions[s3].GetReplicaMgr().FindReplica(primarys[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyProxyRpcCallsFromS1 == 1);
AZ_TEST_ASSERT(sessions[s3].GetReplicaMgr().FindReplica(primarys[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyProxyRpcCallsFromS2 == 1);
// all datasets should have propagated properly
for (int i = 0; i < nSessions; ++i)
{
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(masters[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1.Get() == masters[s1]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(masters[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2.Get() == masters[s1]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(masters[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3.Get() == masters[s1]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1.Get() == primarys[s1]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2.Get() == primarys[s1]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3.Get() == primarys[s1]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(masters[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1.Get() == masters[s2]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(masters[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2.Get() == masters[s2]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(masters[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3.Get() == masters[s2]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1.Get() == primarys[s2]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2.Get() == primarys[s2]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3.Get() == primarys[s2]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(masters[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1.Get() == masters[s3]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(masters[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2.Get() == masters[s3]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(masters[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3.Get() == masters[s3]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1.Get() == primarys[s3]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2.Get() == primarys[s3]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2.Get());
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3.Get() == primarys[s3]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3.Get());
}
}
@@ -3594,7 +3594,7 @@ public:
auto rep = Replica::CreateReplica(nullptr);
auto chunk = CreateAndAttachReplicaChunk<StressTestReplica>(rep);
replicas.push_back(AZStd::make_pair(rep, chunk));
session.GetReplicaMgr().AddMaster(rep);
session.GetReplicaMgr().AddPrimary(rep);
}
}
@@ -3730,7 +3730,7 @@ public:
{
auto rep = Replica::CreateReplica(nullptr);
chunks[i] = CreateAndAttachReplicaChunk<BandwidthTestChunk>(rep);
sessions[sHost].GetReplicaMgr().AddMaster(rep);
sessions[sHost].GetReplicaMgr().AddPrimary(rep);
}
// connect to host
@@ -687,7 +687,7 @@ namespace ReplicaBehavior {
AZ_TEST_ASSERT(chunk->Data1.IsDefaultValue());
AZ_TEST_ASSERT(chunk->Data2.IsDefaultValue());
m_replicaIdDefault = m_sessions[sHost].GetReplicaMgr().AddMaster(replica);
m_replicaIdDefault = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica);
}
}
@@ -725,7 +725,7 @@ namespace ReplicaBehavior {
AZ_TEST_ASSERT(!chunk->Data1.IsDefaultValue());
AZ_TEST_ASSERT(!chunk->Data2.IsDefaultValue());
m_replicaIdModified = m_sessions[sHost].GetReplicaMgr().AddMaster(replica);
m_replicaIdModified = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica);
}
break;
}
@@ -816,7 +816,7 @@ namespace ReplicaBehavior {
LargeChunkWithDefaults* chunk = CreateAndAttachReplicaChunk<LargeChunkWithDefaults>(replica);
AZ_TEST_ASSERT(chunk);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddMaster(replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica);
}
~Integ_ReplicaDefaultDataSetDriller()
@@ -923,13 +923,13 @@ namespace ReplicaBehavior {
ChunkWithBools* chunk1 = CreateAndAttachReplicaChunk<ChunkWithBools>(replica1);
AZ_TEST_ASSERT(chunk1);
m_replicaBoolsId = m_sessions[sHost].GetReplicaMgr().AddMaster(replica1);
m_replicaBoolsId = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica1);
ReplicaPtr replica2 = Replica::CreateReplica(nullptr);
ChunkWithShortInts* chunk2 = CreateAndAttachReplicaChunk<ChunkWithShortInts>(replica2);
AZ_TEST_ASSERT(chunk2);
m_replicaU8Id = m_sessions[sHost].GetReplicaMgr().AddMaster(replica2);
m_replicaU8Id = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica2);
}
~Integ_Replica_ComparePackingBoolsVsU8()
@@ -1058,7 +1058,7 @@ namespace ReplicaBehavior {
auto chunk = CreateAndAttachReplicaChunk<CustomMarshalerTestChunk>(replica);
AZ_TEST_ASSERT(chunk);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddMaster(replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica);
}
~Integ_CheckDataSetStreamIsntWrittenMoreThanNecessary()
@@ -1155,7 +1155,7 @@ namespace ReplicaBehavior {
auto chunk = CreateAndAttachReplicaChunk<CustomMarshalerTestChunk>(replica);
AZ_TEST_ASSERT(chunk);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddMaster(replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica);
}
~Integ_CheckDataSetStreamIsntWrittenMoreThanNecessaryOnceDirty()
@@ -1249,7 +1249,7 @@ namespace ReplicaBehavior {
ForcingDirtyTestChunk* chunk = CreateAndAttachReplicaChunk<ForcingDirtyTestChunk>(replica);
AZ_TEST_ASSERT(chunk);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddMaster(replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica);
}
~Integ_CheckReplicaIsntSentWithNoChanges()
@@ -1360,7 +1360,7 @@ namespace ReplicaBehavior {
auto chunk = CreateAndAttachReplicaChunk<EntityLikeScriptReplicaChunk>(replica);
AZ_TEST_ASSERT(chunk);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddMaster(replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica);
}
~Integ_CheckEntityScriptReplicaIsntSentWithNoChanges()
+105 -105
View File
@@ -73,12 +73,12 @@ public:
static const char* GetChunkName() { return "RPCChunk"; }
RPCChunk()
: m_fromMasterBroadcast(0)
, m_fromMasterNotBroadcast(0)
: m_fromPrimaryBroadcast(0)
, m_fromPrimaryNotBroadcast(0)
, m_fromProxyBroadcast(0)
, m_fromProxyNotBroadcast(0)
, FromMasterBroadcast("FromMasterBroadcast")
, FromMasterNotBroadcast("FromMasterNotBroadcast")
, FromPrimaryBroadcast("FromPrimaryBroadcast")
, FromPrimaryNotBroadcast("FromPrimaryNotBroadcast")
, FromProxyBroadcast("FromProxyBroadcast")
, FromProxyNotBroadcast("FromProxyNotBroadcast")
, BroadcastInt("BroadcastInt")
@@ -86,30 +86,30 @@ public:
bool IsReplicaMigratable() override { return false; }
bool FromMasterBroadcastFn(const RpcContext&)
bool FromPrimaryBroadcastFn(const RpcContext&)
{
AZ_TracePrintf("GridMate", "Executed FromMasterBroadcast %d %s\n", GetReplicaId(), GetReplica()->IsMaster() ? "master" : "proxy");
m_fromMasterBroadcast++;
AZ_TracePrintf("GridMate", "Executed FromPrimaryBroadcast %d %s\n", GetReplicaId(), GetReplica()->IsPrimary() ? "primary" : "proxy");
m_fromPrimaryBroadcast++;
return true;
}
bool FromMasterNotBroadcastFn(const RpcContext&)
bool FromPrimaryNotBroadcastFn(const RpcContext&)
{
AZ_TracePrintf("GridMate", "Executed FromMasterNotBroadcast %d %s\n", GetReplicaId(), GetReplica()->IsMaster() ? "master" : "proxy");
m_fromMasterNotBroadcast++;
AZ_TracePrintf("GridMate", "Executed FromPrimaryNotBroadcast %d %s\n", GetReplicaId(), GetReplica()->IsPrimary() ? "primary" : "proxy");
m_fromPrimaryNotBroadcast++;
return false;
}
bool FromProxyBroadcastFn(const RpcContext&)
{
AZ_TracePrintf("GridMate", "Executed FromProxyBroadcast %d %s\n", GetReplicaId(), GetReplica()->IsMaster() ? "master" : "proxy");
AZ_TracePrintf("GridMate", "Executed FromProxyBroadcast %d %s\n", GetReplicaId(), GetReplica()->IsPrimary() ? "primary" : "proxy");
m_fromProxyBroadcast++;
return true;
}
bool FromProxyNotBroadcastFn(const RpcContext&)
{
AZ_TracePrintf("GridMate", "Executed FromProxyNotBroadcast %d %s\n", GetReplicaId(), GetReplica()->IsMaster() ? "master" : "proxy");
AZ_TracePrintf("GridMate", "Executed FromProxyNotBroadcast %d %s\n", GetReplicaId(), GetReplica()->IsPrimary() ? "primary" : "proxy");
m_fromProxyNotBroadcast++;
return false;
}
@@ -120,14 +120,14 @@ public:
return true;
}
int m_fromMasterBroadcast;
int m_fromMasterNotBroadcast;
int m_fromPrimaryBroadcast;
int m_fromPrimaryNotBroadcast;
int m_fromProxyBroadcast;
int m_fromProxyNotBroadcast;
AZStd::vector<int> m_sentData;
Rpc<>::BindInterface<RPCChunk, & RPCChunk::FromMasterBroadcastFn> FromMasterBroadcast;
Rpc<>::BindInterface<RPCChunk, & RPCChunk::FromMasterNotBroadcastFn> FromMasterNotBroadcast;
Rpc<>::BindInterface<RPCChunk, & RPCChunk::FromPrimaryBroadcastFn> FromPrimaryBroadcast;
Rpc<>::BindInterface<RPCChunk, & RPCChunk::FromPrimaryNotBroadcastFn> FromPrimaryNotBroadcast;
Rpc<>::BindInterface<RPCChunk, & RPCChunk::FromProxyBroadcastFn> FromProxyBroadcast;
Rpc<>::BindInterface<RPCChunk, & RPCChunk::FromProxyNotBroadcastFn> FromProxyNotBroadcast;
Rpc<RpcArg<int> >::BindInterface<RPCChunk, & RPCChunk::BroadcastIntFn> BroadcastInt;
@@ -158,21 +158,21 @@ public:
bool Zero(const RpcContext&)
{
auto& list = (IsMaster() ? m_sentData : m_receivedData)[0];
auto& list = (IsPrimary() ? m_sentData : m_receivedData)[0];
(void) list;
return true;
}
bool One(AZ::u32 t1, const RpcContext&)
{
auto& list = (IsMaster() ? m_sentData : m_receivedData)[1];
auto& list = (IsPrimary() ? m_sentData : m_receivedData)[1];
list.push_back(t1);
return true;
}
bool Two(AZ::u32 t1, AZ::u32 t2, const RpcContext&)
{
auto& list = (IsMaster() ? m_sentData : m_receivedData)[2];
auto& list = (IsPrimary() ? m_sentData : m_receivedData)[2];
list.push_back(t1);
list.push_back(t2);
return true;
@@ -180,7 +180,7 @@ public:
bool Three(AZ::u32 t1, AZ::u32 t2, AZ::u32 t3, const RpcContext&)
{
auto& list = (IsMaster() ? m_sentData : m_receivedData)[3];
auto& list = (IsPrimary() ? m_sentData : m_receivedData)[3];
list.push_back(t1);
list.push_back(t2);
list.push_back(t3);
@@ -189,7 +189,7 @@ public:
bool Four(AZ::u32 t1, AZ::u32 t2, AZ::u32 t3, AZ::u32 t4, const RpcContext&)
{
auto& list = (IsMaster() ? m_sentData : m_receivedData)[4];
auto& list = (IsPrimary() ? m_sentData : m_receivedData)[4];
list.push_back(t1);
list.push_back(t2);
list.push_back(t3);
@@ -199,7 +199,7 @@ public:
bool Five(AZ::u32 t1, AZ::u32 t2, AZ::u32 t3, AZ::u32 t4, AZ::u32 t5, const RpcContext&)
{
auto& list = (IsMaster() ? m_sentData : m_receivedData)[5];
auto& list = (IsPrimary() ? m_sentData : m_receivedData)[5];
list.push_back(t1);
list.push_back(t2);
list.push_back(t3);
@@ -210,7 +210,7 @@ public:
bool Six(AZ::u32 t1, AZ::u32 t2, AZ::u32 t3, AZ::u32 t4, AZ::u32 t5, AZ::u32 t6, const RpcContext&)
{
auto& list = (IsMaster() ? m_sentData : m_receivedData)[6];
auto& list = (IsPrimary() ? m_sentData : m_receivedData)[6];
list.push_back(t1);
list.push_back(t2);
list.push_back(t3);
@@ -222,7 +222,7 @@ public:
bool Seven(AZ::u32 t1, AZ::u32 t2, AZ::u32 t3, AZ::u32 t4, AZ::u32 t5, AZ::u32 t6, AZ::u32 t7, const RpcContext&)
{
auto& list = (IsMaster() ? m_sentData : m_receivedData)[7];
auto& list = (IsPrimary() ? m_sentData : m_receivedData)[7];
list.push_back(t1);
list.push_back(t2);
list.push_back(t3);
@@ -235,7 +235,7 @@ public:
bool Eight(AZ::u32 t1, AZ::u32 t2, AZ::u32 t3, AZ::u32 t4, AZ::u32 t5, AZ::u32 t6, AZ::u32 t7, AZ::u32 t8, const RpcContext&)
{
auto& list = (IsMaster() ? m_sentData : m_receivedData)[8];
auto& list = (IsPrimary() ? m_sentData : m_receivedData)[8];
list.push_back(t1);
list.push_back(t2);
list.push_back(t3);
@@ -249,7 +249,7 @@ public:
bool Nine(AZ::u32 t1, AZ::u32 t2, AZ::u32 t3, AZ::u32 t4, AZ::u32 t5, AZ::u32 t6, AZ::u32 t7, AZ::u32 t8, AZ::u32 t9, const RpcContext&)
{
auto& list = (IsMaster() ? m_sentData : m_receivedData)[9];
auto& list = (IsPrimary() ? m_sentData : m_receivedData)[9];
list.push_back(t1);
list.push_back(t2);
list.push_back(t3);
@@ -890,7 +890,7 @@ public:
// put something on s1 to get it going
auto replica = Replica::CreateReplica(nullptr);
m_chunk = CreateAndAttachReplicaChunk<RPCChunk>(replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddMaster(replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica);
}
RPCChunk::Ptr m_chunk;
@@ -905,11 +905,11 @@ TEST_F(Integ_ReplicaChunkRPCExec, ReplicaChunkRPCExec)
switch (tick)
{
case 10:
m_chunk->FromMasterBroadcast();
m_chunk->FromPrimaryBroadcast();
break;
case 20:
m_chunk->FromMasterNotBroadcast();
m_chunk->FromPrimaryNotBroadcast();
break;
case 30:
@@ -932,13 +932,13 @@ TEST_F(Integ_ReplicaChunkRPCExec, ReplicaChunkRPCExec)
auto s2proxy = m_sessions[s2].GetReplicaMgr().FindReplica(m_replicaId)->FindReplicaChunk<RPCChunk>();
auto s3proxy = m_sessions[s3].GetReplicaMgr().FindReplica(m_replicaId)->FindReplicaChunk<RPCChunk>();
AZ_TEST_ASSERT(s1host->m_fromMasterBroadcast == 1);
AZ_TEST_ASSERT(s2proxy->m_fromMasterBroadcast == 1);
AZ_TEST_ASSERT(s3proxy->m_fromMasterBroadcast == 1);
AZ_TEST_ASSERT(s1host->m_fromPrimaryBroadcast == 1);
AZ_TEST_ASSERT(s2proxy->m_fromPrimaryBroadcast == 1);
AZ_TEST_ASSERT(s3proxy->m_fromPrimaryBroadcast == 1);
AZ_TEST_ASSERT(s1host->m_fromMasterNotBroadcast == 1);
AZ_TEST_ASSERT(s2proxy->m_fromMasterNotBroadcast == 0);
AZ_TEST_ASSERT(s3proxy->m_fromMasterNotBroadcast == 0);
AZ_TEST_ASSERT(s1host->m_fromPrimaryNotBroadcast == 1);
AZ_TEST_ASSERT(s2proxy->m_fromPrimaryNotBroadcast == 0);
AZ_TEST_ASSERT(s3proxy->m_fromPrimaryNotBroadcast == 0);
AZ_TEST_ASSERT(s1host->m_fromProxyBroadcast == 1);
AZ_TEST_ASSERT(s2proxy->m_fromProxyBroadcast == 1);
@@ -965,12 +965,12 @@ public:
GM_CLASS_ALLOCATOR(DestroyRPCChunk);
DestroyRPCChunk()
: DestroyFromMaster("DestroyFromMaster")
: DestroyFromPrimary("DestroyFromPrimary")
, DestroyFromProxy("DestroyFromProxy")
, BeforeDestroyFromProxy("BeforeDestroyFromProxy")
, AfterDestroyFromProxy("AfterDestroyFromProxy")
, BeforeDestroyFromMaster("BeforeDestroyFromMaster")
, AfterDestroyFromMaster("AfterDestroyFromMaster")
, BeforeDestroyFromPrimary("BeforeDestroyFromPrimary")
, AfterDestroyFromPrimary("AfterDestroyFromPrimary")
{
}
@@ -979,11 +979,11 @@ public:
bool IsReplicaMigratable() override { return false; }
bool DestroyFromMasterFn(const RpcContext&)
bool DestroyFromPrimaryFn(const RpcContext&)
{
AZ_TracePrintf("GridMate", "Executed DestroyFromMaster %d %s\n", GetReplicaId(), GetReplica()->IsMaster() ? "master" : "proxy");
++s_destroyFromMasterCalls;
if (GetReplica()->IsMaster())
AZ_TracePrintf("GridMate", "Executed DestroyFromPrimary %d %s\n", GetReplicaId(), GetReplica()->IsPrimary() ? "primary" : "proxy");
++s_destroyFromPrimaryCalls;
if (GetReplica()->IsPrimary())
{
GetReplica()->Destroy();
}
@@ -992,9 +992,9 @@ public:
bool DestroyFromProxyFn(const RpcContext&)
{
AZ_TracePrintf("GridMate", "Executed DestroyFromProxy %d %s\n", GetReplicaId(), GetReplica()->IsMaster() ? "master" : "proxy");
AZ_TracePrintf("GridMate", "Executed DestroyFromProxy %d %s\n", GetReplicaId(), GetReplica()->IsPrimary() ? "primary" : "proxy");
++s_destroyFromProxyCalls;
if (GetReplica()->IsMaster())
if (GetReplica()->IsPrimary())
{
GetReplica()->Destroy();
}
@@ -1003,53 +1003,53 @@ public:
bool BeforeDestroyFromProxyFn(const RpcContext&)
{
AZ_TracePrintf("GridMate", "Executed BeforeDestroyFromProxy %d %s\n", GetReplicaId(), GetReplica()->IsMaster() ? "master" : "proxy");
AZ_TracePrintf("GridMate", "Executed BeforeDestroyFromProxy %d %s\n", GetReplicaId(), GetReplica()->IsPrimary() ? "primary" : "proxy");
++s_beforeDestroyFromProxyCalls;
return true;
}
bool AfterDestroyFromProxyFn(const RpcContext&)
{
AZ_TracePrintf("GridMate", "Executed AfterDestroyFromProxy %d %s\n", GetReplicaId(), GetReplica()->IsMaster() ? "master" : "proxy");
AZ_TracePrintf("GridMate", "Executed AfterDestroyFromProxy %d %s\n", GetReplicaId(), GetReplica()->IsPrimary() ? "primary" : "proxy");
++s_afterDestroyFromProxyCalls;
return true;
}
bool BeforeDestroyFromMasterFn(const RpcContext&)
bool BeforeDestroyFromPrimaryFn(const RpcContext&)
{
AZ_TracePrintf("GridMate", "Executed BeforeDestroyFromMaster %d %s\n", GetReplicaId(), GetReplica()->IsMaster() ? "master" : "proxy");
++s_beforeDestroyFromMasterCalls;
AZ_TracePrintf("GridMate", "Executed BeforeDestroyFromPrimary %d %s\n", GetReplicaId(), GetReplica()->IsPrimary() ? "primary" : "proxy");
++s_beforeDestroyFromPrimaryCalls;
return true;
}
bool AfterDestroyFromMasterFn(const RpcContext&)
bool AfterDestroyFromPrimaryFn(const RpcContext&)
{
AZ_TracePrintf("GridMate", "Executed AfterDestroyFromMaster %d %s\n", GetReplicaId(), GetReplica()->IsMaster() ? "master" : "proxy");
++s_afterDestroyFromMasterCalls;
AZ_TracePrintf("GridMate", "Executed AfterDestroyFromPrimary %d %s\n", GetReplicaId(), GetReplica()->IsPrimary() ? "primary" : "proxy");
++s_afterDestroyFromPrimaryCalls;
return true;
}
Rpc<>::BindInterface<DestroyRPCChunk, & DestroyRPCChunk::DestroyFromMasterFn> DestroyFromMaster;
Rpc<>::BindInterface<DestroyRPCChunk, & DestroyRPCChunk::DestroyFromPrimaryFn> DestroyFromPrimary;
Rpc<>::BindInterface<DestroyRPCChunk, & DestroyRPCChunk::DestroyFromProxyFn> DestroyFromProxy;
Rpc<>::BindInterface<DestroyRPCChunk, & DestroyRPCChunk::BeforeDestroyFromProxyFn> BeforeDestroyFromProxy;
Rpc<>::BindInterface<DestroyRPCChunk, & DestroyRPCChunk::AfterDestroyFromProxyFn> AfterDestroyFromProxy;
Rpc<>::BindInterface<DestroyRPCChunk, & DestroyRPCChunk::BeforeDestroyFromMasterFn> BeforeDestroyFromMaster;
Rpc<>::BindInterface<DestroyRPCChunk, & DestroyRPCChunk::AfterDestroyFromMasterFn> AfterDestroyFromMaster;
Rpc<>::BindInterface<DestroyRPCChunk, & DestroyRPCChunk::BeforeDestroyFromPrimaryFn> BeforeDestroyFromPrimary;
Rpc<>::BindInterface<DestroyRPCChunk, & DestroyRPCChunk::AfterDestroyFromPrimaryFn> AfterDestroyFromPrimary;
static int s_destroyFromMasterCalls;
static int s_beforeDestroyFromMasterCalls;
static int s_afterDestroyFromMasterCalls;
static int s_destroyFromPrimaryCalls;
static int s_beforeDestroyFromPrimaryCalls;
static int s_afterDestroyFromPrimaryCalls;
static int s_destroyFromProxyCalls;
static int s_beforeDestroyFromProxyCalls;
static int s_afterDestroyFromProxyCalls;
};
int DestroyRPCChunk::s_destroyFromProxyCalls = 0;
int DestroyRPCChunk::s_destroyFromMasterCalls = 0;
int DestroyRPCChunk::s_destroyFromPrimaryCalls = 0;
int DestroyRPCChunk::s_beforeDestroyFromProxyCalls = 0;
int DestroyRPCChunk::s_afterDestroyFromProxyCalls = 0;
int DestroyRPCChunk::s_beforeDestroyFromMasterCalls = 0;
int DestroyRPCChunk::s_afterDestroyFromMasterCalls = 0;
int DestroyRPCChunk::s_beforeDestroyFromPrimaryCalls = 0;
int DestroyRPCChunk::s_afterDestroyFromPrimaryCalls = 0;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
@@ -1077,7 +1077,7 @@ public:
{
auto replica = Replica::CreateReplica(nullptr);
CreateAndAttachReplicaChunk<DestroyRPCChunk>(replica);
m_repId[i] = m_sessions[sHost].GetReplicaMgr().AddMaster(replica);
m_repId[i] = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica);
}
}
@@ -1092,12 +1092,12 @@ TEST_F(Integ_ReplicaDestroyedInRPC, ReplicaDestroyedInRPC)
{
case 10:
{
// calling destroy on master
auto master = m_sessions[sHost].GetReplicaMgr().FindReplica(m_repId[0]);
auto masterChunk = master->FindReplicaChunk<DestroyRPCChunk>();
masterChunk->BeforeDestroyFromMaster();
masterChunk->DestroyFromMaster();
masterChunk->AfterDestroyFromMaster();
// calling destroy on primary
auto primary = m_sessions[sHost].GetReplicaMgr().FindReplica(m_repId[0]);
auto primaryChunk = primary->FindReplicaChunk<DestroyRPCChunk>();
primaryChunk->BeforeDestroyFromPrimary();
primaryChunk->DestroyFromPrimary();
primaryChunk->AfterDestroyFromPrimary();
// calling destroy on proxy
auto proxy = m_sessions[s2].GetReplicaMgr().FindReplica(m_repId[1]);
@@ -1113,15 +1113,15 @@ TEST_F(Integ_ReplicaDestroyedInRPC, ReplicaDestroyedInRPC)
{
// checking if before destroy RPC was called on every peer
AZ_TEST_ASSERT(DestroyRPCChunk::s_beforeDestroyFromProxyCalls == nSessions);
AZ_TEST_ASSERT(DestroyRPCChunk::s_beforeDestroyFromMasterCalls == nSessions);
AZ_TEST_ASSERT(DestroyRPCChunk::s_beforeDestroyFromPrimaryCalls == nSessions);
// checking if destroy itself was called on every peer
AZ_TEST_ASSERT(DestroyRPCChunk::s_destroyFromProxyCalls == nSessions);
AZ_TEST_ASSERT(DestroyRPCChunk::s_destroyFromMasterCalls == nSessions);
AZ_TEST_ASSERT(DestroyRPCChunk::s_destroyFromPrimaryCalls == nSessions);
// checking if after destroy RPC was never called
AZ_TEST_ASSERT(DestroyRPCChunk::s_afterDestroyFromProxyCalls == 0); // RPCs that arrive via the network after deactivation should be dropped.
AZ_TEST_ASSERT(DestroyRPCChunk::s_afterDestroyFromMasterCalls == 1); // RPCs explicitly called on an inactive replica should still be executed.
AZ_TEST_ASSERT(DestroyRPCChunk::s_afterDestroyFromPrimaryCalls == 1); // RPCs explicitly called on an inactive replica should still be executed.
return TestStatus::Completed;
}
@@ -1157,7 +1157,7 @@ public:
{
// put something on s1 to get it going
m_replica = Replica::CreateReplica(nullptr);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddMaster(m_replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(m_replica);
}
ReplicaPtr m_replica;
@@ -1232,7 +1232,7 @@ public:
// put something on s1 to get it going
m_replica = Replica::CreateReplica(nullptr);
m_chunk = CreateAndAttachReplicaChunk<RPCChunk>(m_replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddMaster(m_replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(m_replica);
}
ReplicaPtr m_replica;
@@ -1286,7 +1286,7 @@ public:
// put something on s1 to get it going
m_replica = Replica::CreateReplica(nullptr);
m_chunk = CreateAndAttachReplicaChunk<FullRPCChunk>(m_replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddMaster(m_replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(m_replica);
}
ReplicaPtr m_replica;
@@ -1391,7 +1391,7 @@ public:
{
// put something on s1 to get it going
m_replica = Replica::CreateReplica(nullptr);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddMaster(m_replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(m_replica);
}
@@ -1453,7 +1453,7 @@ public:
{
ReplicaPtr replica = Replica::CreateReplica(nullptr);
m_chunk = CreateAndAttachReplicaChunk<AllEventChunk>(replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddMaster(replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica);
AZ_TEST_ASSERT(m_chunk->m_attaches == 1);
AZ_TEST_ASSERT(m_chunk->m_activates == 1);
@@ -1531,7 +1531,7 @@ public:
auto chunk = CreateAndAttachReplicaChunk<AllEventChunk>(replica);
AZ_TEST_ASSERT(chunk);
}
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddMaster(replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica);
auto numChunks = replica->GetNumChunks();
AZ_TEST_ASSERT(numChunks == GM_MAX_CHUNKS_PER_REPLICA);
@@ -1594,7 +1594,7 @@ public:
{
m_replica = Replica::CreateReplica(nullptr);
m_chunk = CreateAndAttachReplicaChunk<AllEventChunk>(m_replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddMaster(m_replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(m_replica);
AZ_TEST_ASSERT(m_chunk->m_attaches == 1);
AZ_TEST_ASSERT(m_chunk->m_activates == 1);
@@ -1820,16 +1820,16 @@ public:
++m_numRequestChangeOwnership;
}
void OnReplicaChangeOwnership(Replica* replica, bool wasMaster) override
void OnReplicaChangeOwnership(Replica* replica, bool wasPrimary) override
{
AZ_TEST_ASSERT(replica);
switch (m_numChangedOwnership)
{
case 0: // host loses ownership
AZ_TEST_ASSERT(replica->IsProxy() && wasMaster == true);
AZ_TEST_ASSERT(replica->IsProxy() && wasPrimary == true);
break;
case 1: // peer acquires ownership
AZ_TEST_ASSERT(replica->IsMaster() && wasMaster == false);
AZ_TEST_ASSERT(replica->IsPrimary() && wasPrimary == false);
break;
default:
AZ_TEST_ASSERT(0);
@@ -2008,7 +2008,7 @@ public:
ReplicaPtr replica = Replica::CreateReplica(nullptr);
CreateAndAttachReplicaChunk<DrillerTestChunk>(replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddMaster(replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica);
}
~Integ_ReplicaDriller()
@@ -2038,7 +2038,7 @@ TEST_F(Integ_ReplicaDriller, ReplicaDriller)
{
auto rep = m_sessions[s2].GetReplicaMgr().FindReplica(m_replicaId);
AZ_TEST_ASSERT(rep);
AZ_TEST_ASSERT(rep->IsMaster());
AZ_TEST_ASSERT(rep->IsPrimary());
rep->Destroy();
break;
}
@@ -2110,7 +2110,7 @@ public:
{
m_replica = Replica::CreateReplica(nullptr);
m_chunk = CreateAndAttachReplicaChunk<DataSetChunk>(m_replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddMaster(m_replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(m_replica);
}
@@ -2172,16 +2172,16 @@ public:
{
m_replica = Replica::CreateReplica(nullptr);
m_chunk = CreateAndAttachReplicaChunk<CustomHandlerChunk>(m_replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddMaster(m_replica);
m_masterHandler.reset(aznew CustomHandler());
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(m_replica);
m_primaryHandler.reset(aznew CustomHandler());
m_proxyHandler.reset(aznew CustomHandler());
m_chunk->SetHandler(m_masterHandler.get());
m_chunk->SetHandler(m_primaryHandler.get());
}
ReplicaPtr m_replica;
ReplicaId m_replicaId;
CustomHandlerChunk::Ptr m_chunk;
AZStd::scoped_ptr<CustomHandler> m_masterHandler;
AZStd::scoped_ptr<CustomHandler> m_primaryHandler;
AZStd::scoped_ptr<CustomHandler> m_proxyHandler;
};
@@ -2262,7 +2262,7 @@ public:
{
m_replica = Replica::CreateReplica(nullptr);
m_chunk = CreateAndAttachReplicaChunk<NonConstMarshalerChunk>(m_replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddMaster(m_replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(m_replica);
}
ReplicaPtr m_replica;
@@ -2338,7 +2338,7 @@ public:
{
m_replica = Replica::CreateReplica(nullptr);
m_chunk = CreateAndAttachReplicaChunk<SourcePeerChunk>(m_replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddMaster(m_replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(m_replica);
}
ReplicaPtr m_replica;
@@ -2488,7 +2488,7 @@ public:
m_chunks[i] = CreateAndAttachReplicaChunk<PriorityChunk>(replica);
m_chunks[i]->m_value.Set(i + 1); // setting dataset values to 1..kNumReplicas
m_chunks[i]->SetPriority(k_replicaPriorityNormal + static_cast<ReplicaPriority>(i)); // the later created - the higher priorities, so should be sent in reverse order
m_sessions[sHost].GetReplicaMgr().AddMaster(replica);
m_sessions[sHost].GetReplicaMgr().AddPrimary(replica);
}
}
@@ -2594,7 +2594,7 @@ public:
ReplicaPtr replica = Replica::CreateReplica(nullptr);
m_chunk = CreateAndAttachReplicaChunk<SuspendUpdatesChunk>(replica);
m_sessions[sHost].GetReplicaMgr().AddMaster(replica);
m_sessions[sHost].GetReplicaMgr().AddPrimary(replica);
}
SuspendUpdatesChunk::Ptr m_chunk = nullptr;
@@ -2684,9 +2684,9 @@ public:
void OnReplicaActivate(const GridMate::ReplicaContext&) override
{
if (IsMaster())
if (IsPrimary())
{
nMasterActivations++;
nPrimaryActivations++;
}
else
{
@@ -2694,11 +2694,11 @@ public:
}
}
static int nMasterActivations;
static int nPrimaryActivations;
static int nProxyActivations;
};
};
int Integ_BasicHostChunkDescriptorTest::HostChunk::nMasterActivations = 0;
int Integ_BasicHostChunkDescriptorTest::HostChunk::nPrimaryActivations = 0;
int Integ_BasicHostChunkDescriptorTest::HostChunk::nProxyActivations = 0;
TEST_F(Integ_BasicHostChunkDescriptorTest, BasicHostChunkDescriptorTest)
@@ -2744,25 +2744,25 @@ TEST_F(Integ_BasicHostChunkDescriptorTest, BasicHostChunkDescriptorTest)
{
hostReplica = Replica::CreateReplica("HostReplica");
hostReplica->AttachReplicaChunk(CreateReplicaChunk<HostChunk>());
nodes[Host].GetReplicaMgr().AddMaster(hostReplica);
nodes[Host].GetReplicaMgr().AddPrimary(hostReplica);
}
if (tick == 300)
{
AZ_TEST_ASSERT(HostChunk::nMasterActivations == 1);
AZ_TEST_ASSERT(HostChunk::nPrimaryActivations == 1);
AZ_TEST_ASSERT(HostChunk::nProxyActivations == 1);
AZ_TEST_ASSERT(nodes[Client].GetReplicaMgr().FindReplica(hostReplica->GetRepId())->FindReplicaChunk<HostChunk>());
AZ_TEST_START_TRACE_SUPPRESSION;
clientReplica = Replica::CreateReplica("ClientReplica");
clientReplica->AttachReplicaChunk(CreateReplicaChunk<HostChunk>());
nodes[Client].GetReplicaMgr().AddMaster(clientReplica);
nodes[Client].GetReplicaMgr().AddPrimary(clientReplica);
}
if (tick == 400)
{
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
AZ_TEST_ASSERT(HostChunk::nMasterActivations == 2);
AZ_TEST_ASSERT(HostChunk::nPrimaryActivations == 2);
AZ_TEST_ASSERT(HostChunk::nProxyActivations == 1);
AZ_TEST_ASSERT(!nodes[Host].GetReplicaMgr().FindReplica(clientReplica->GetRepId())->FindReplicaChunk<HostChunk>());
}
@@ -2792,10 +2792,10 @@ TEST_F(Integ_BasicHostChunkDescriptorTest, BasicHostChunkDescriptorTest)
}
/*
* Create and immedietly destroy master replica
* Create and immedietly destroy primary replica
* Test that it does not result in any network sync
*/
class Integ_CreateDestroyMaster
class Integ_CreateDestroyPrimary
: public Integ_SimpleTest
, public Debug::ReplicaDrillerBus::Handler
{
@@ -2831,7 +2831,7 @@ public:
}
};
TEST_F(Integ_CreateDestroyMaster, CreateDestroyMaster)
TEST_F(Integ_CreateDestroyPrimary, CreateDestroyPrimary)
{
RunTickLoop([this](int tick)-> TestStatus
{
@@ -2843,7 +2843,7 @@ TEST_F(Integ_CreateDestroyMaster, CreateDestroyMaster)
ConnectDriller();
auto replica = Replica::CreateReplica(nullptr);
CreateAndAttachReplicaChunk<DataSetChunk>(replica);
m_sessions[sHost].GetReplicaMgr().AddMaster(replica);
m_sessions[sHost].GetReplicaMgr().AddPrimary(replica);
// Destroying replica right away
replica->Destroy();
@@ -2894,7 +2894,7 @@ public:
LargeChunkWithDefaultsMedium* chunk = CreateAndAttachReplicaChunk<LargeChunkWithDefaultsMedium>(replica);
AZ_TEST_ASSERT(chunk);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddMaster(replica);
m_replicaId = m_sessions[sHost].GetReplicaMgr().AddPrimary(replica);
}
~ReplicaACKfeedbackTestFixture()
@@ -234,7 +234,7 @@ public:
/**
* OfflineModeTest verifies that replica chunks are usable without
* an active session, and basically behave as masters.
* an active session, and basically behave as primarys.
*/
class OfflineModeTest
: public UnitTest::GridMateMPTestFixture
@@ -297,7 +297,7 @@ public:
AZ_TEST_ASSERT(OfflineChunk::s_nInstances == 1);
ReplicaChunkPtr chunkPtr = offlineChunk;
chunkPtr->Init(ReplicaChunkClassId(OfflineChunk::GetChunkName()));
AZ_TEST_ASSERT(chunkPtr->IsMaster());
AZ_TEST_ASSERT(chunkPtr->IsPrimary());
AZ_TEST_ASSERT(!chunkPtr->IsProxy());
offlineChunk->m_data1.Set(5);
AZ_TEST_ASSERT(offlineChunk->m_data1.Get() == 5);
@@ -315,7 +315,7 @@ public:
return true;
});
AZ_TEST_ASSERT(offlineChunk->m_data2.Get() == 10);
AZ_TEST_ASSERT(offlineChunk->m_nCallsDataSetChangeCB == 0); // DataSet change CB doesn't get called on master.
AZ_TEST_ASSERT(offlineChunk->m_nCallsDataSetChangeCB == 0); // DataSet change CB doesn't get called on primary.
offlineChunk->CallRpc();
AZ_TEST_ASSERT(offlineChunk->m_nCallsRpcHandlerCB == 1);
@@ -325,11 +325,11 @@ public:
AZ_TEST_ASSERT(strcmp(offlineReplica->GetDebugName(), replicaName) == 0);
offlineReplica->AttachReplicaChunk(chunkPtr);
AZ_TEST_ASSERT(chunkPtr->IsMaster());
AZ_TEST_ASSERT(chunkPtr->IsPrimary());
AZ_TEST_ASSERT(!chunkPtr->IsProxy());
offlineReplica->DetachReplicaChunk(chunkPtr);
AZ_TEST_ASSERT(chunkPtr->IsMaster());
AZ_TEST_ASSERT(chunkPtr->IsPrimary());
AZ_TEST_ASSERT(!chunkPtr->IsProxy());
AZ_TEST_ASSERT(OfflineChunk::s_nInstances == 1);
@@ -462,7 +462,7 @@ public:
ReplicaPeer peer(&rm);
AZ_TracePrintf("GridMate", "\n");
Replica* replica = Replica::CreateReplica("TestMasterReplica");
Replica* replica = Replica::CreateReplica("TestPrimaryReplica");
ReplicaChunkDescriptorTable::Get().RegisterChunkType<SimpleDataSetChunk>();
AZStd::unique_ptr<SimpleDataSetChunk> chunk(CreateReplicaChunk<SimpleDataSetChunk>());
-58
View File
@@ -1,58 +0,0 @@
/*
* 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.
*
*/
#pragma once
#include <AzTest/AzTest.h>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <GridMate/Session/Session.h>
namespace UnitTest
{
class MockSession
: public GridMate::GridSession
{
public:
MockSession(GridMate::SessionService* service)
: GridSession(service)
{
}
void SetReplicaManager(GridMate::ReplicaManager* replicaManager)
{
m_replicaMgr = replicaManager;
}
MOCK_METHOD4(CreateRemoteMember, GridMate::GridMember*(const GridMate::string&, GridMate::ReadBuffer&, GridMate::RemotePeerMode, GridMate::ConnectionID));
MOCK_METHOD1(OnSessionParamChanged, void(const GridMate::GridSessionParam&));
MOCK_METHOD1(OnSessionParamRemoved, void(const GridMate::string&));
};
class MockSessionService
: public GridMate::SessionService
{
public:
MockSessionService()
: SessionService(GridMate::SessionServiceDesc())
{
}
~MockSessionService()
{
m_activeSearches.clear();
m_gridMate = nullptr;
}
MOCK_CONST_METHOD0(IsReady, bool());
};
}
@@ -1,151 +0,0 @@
/*
* 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 "GridMocks.h"
#include <GridMate/Replica/Interest/BitmaskInterestHandler.h>
#include <GridMate/Replica/Interest/InterestManager.h>
#include <GridMate/Replica/Interest/ProximityInterestHandler.h>
#include <AzFramework/Network/InterestManagerComponent.h>
#include <AzCore/Socket/AzSocket.h>
#include <AzCore/UnitTest/TestTypes.h>
namespace UnitTest
{
using testing::_;
class MockInterestManagerEvents
: public AzFramework::InterestManagerEventsBus::Handler
{
public:
MockInterestManagerEvents()
{
BusConnect();
}
virtual ~MockInterestManagerEvents()
{
BusDisconnect();
}
MOCK_METHOD1(OnInterestManagerActivate, void(GridMate::InterestManager* im));
MOCK_METHOD1(OnInterestManagerDeactivate, void(GridMate::InterestManager* im));
};
class InterestManagerComponentFixture
: public AllocatorsFixture
{
public:
InterestManagerComponentFixture()
: AllocatorsFixture()
{
}
~InterestManagerComponentFixture()
{
}
void SetUp() override
{
AZ::AzSock::Startup();
AllocatorsFixture::SetUp();
AZ::AllocatorInstance<GridMate::GridMateAllocatorMP>::Create();
m_gridMate = GridMate::GridMateCreate(GridMate::GridMateDesc());
m_carrier = GridMate::DefaultCarrier::Create(GridMate::CarrierDesc(), m_gridMate);
m_sessionService = AZStd::make_unique<UnitTest::MockSessionService>();
m_gridSession = AZStd::make_unique<UnitTest::MockSession>(m_sessionService.get());
m_replicaManagerDesc.m_carrier = m_carrier;
m_replicaManagerDesc.m_myPeerId = AZ::Crc32(testing::UnitTest::GetInstance()->current_test_info()->test_case_name());
m_replicaManagerDesc.m_roles = GridMate::ReplicaMgrDesc::Role_SyncHost;
m_replicaManager = AZStd::make_unique<GridMate::ReplicaManager>();
m_replicaManager->Init(m_replicaManagerDesc);
m_gridSession->SetReplicaManager(m_replicaManager.get());
}
void TearDown() override
{
m_gridSession = nullptr;
m_sessionService = nullptr;
m_replicaManager->Shutdown();
m_replicaManager = nullptr;
m_carrier->Shutdown();
delete m_carrier;
GridMate::GridMateDestroy(m_gridMate);
AZ::AllocatorInstance<GridMate::GridMateAllocatorMP>::Destroy();
AllocatorsFixture::TearDown();
AZ::AzSock::Cleanup();
}
AZStd::unique_ptr<UnitTest::MockSessionService> m_sessionService;
AZStd::unique_ptr<UnitTest::MockSession> m_gridSession;
GridMate::IGridMate* m_gridMate;
GridMate::Carrier* m_carrier;
GridMate::ReplicaMgrDesc m_replicaManagerDesc;
AZStd::unique_ptr<GridMate::ReplicaManager> m_replicaManager;
};
TEST_F(InterestManagerComponentFixture, TestNetworkSessionDeactivate)
{
// Using StrictMock here will ensure that the test fails if any of the events fire (as no EXPECT_CALL has been set).
testing::StrictMock<MockInterestManagerEvents> interestManagerEvents;
AzFramework::InterestManagerComponent interestManagerComponent;
// This will connect the component to the NetBindingSystemEventsBus
interestManagerComponent.Activate();
// Ensure that the interest manager component handles receiving OnNetworkSessionDeactivated for a session that was never activated.
// This can happen in the event of a client failing to connect to a host.
AzFramework::NetBindingSystemEventsBus::Broadcast(
&AzFramework::NetBindingSystemEvents::OnNetworkSessionDeactivated, m_gridSession.get());
interestManagerComponent.Deactivate();
}
TEST_F(InterestManagerComponentFixture, TestNetworkSessionActivateAndDeactivate)
{
// Using StrictMock here will ensure that the test fails if any of the events fire (as no EXPECT_CALL has been set).
testing::StrictMock<MockInterestManagerEvents> interestManagerEvents;
AzFramework::InterestManagerComponent interestManagerComponent;
GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType<GridMate::BitmaskInterestChunk>();
GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType<GridMate::ProximityInterestChunk>();
// This will connect the component to the NetBindingSystemEventsBus
interestManagerComponent.Activate();
// Golden path test that the interest manager component behaves as expected under normal conditions
// (receiving OnNetworkSessionActivated followed by OnNetworkSessionDeactivated).
testing::Expectation activationEvent = EXPECT_CALL(interestManagerEvents, OnInterestManagerActivate(_))
.Times(1);
AzFramework::NetBindingSystemEventsBus::Broadcast(
&AzFramework::NetBindingSystemEvents::OnNetworkSessionActivated, m_gridSession.get());
EXPECT_CALL(interestManagerEvents, OnInterestManagerDeactivate(_))
.Times(1)
.After(activationEvent);
AzFramework::NetBindingSystemEventsBus::Broadcast(
&AzFramework::NetBindingSystemEvents::OnNetworkSessionDeactivated, m_gridSession.get());
interestManagerComponent.Deactivate();
}
}
-600
View File
@@ -1,600 +0,0 @@
/*
* 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 <AzCore/Component/ComponentApplication.h>
#include <AzCore/std/parallel/thread.h>
#include <AzCore/std/containers/ring_buffer.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzFramework/Network/NetBindingComponent.h>
#include <AzFramework/Network/NetBindingSystemComponent.h>
#include <AzFramework/Network/NetBindable.h>
#include <GridMate/GridMate.h>
#include <GridMate/Session/LANSession.h>
#include <GridMate/Replica/ReplicaChunkDescriptor.h>
#include <GridMate/Replica/ReplicaChunk.h>
#include <GridMate/Replica/ReplicaFunctions.h>
#include <GridMate/Replica/DataSet.h>
#include <GridMate/Replica/RemoteProcedureCall.h>
#include <GridMate/Serialize/DataMarshal.h>
#include <GridMate/Serialize/CompressionMarshal.h>
#include <GridMate/Carrier/Utils.h>
#include <AzCore/Asset/AssetManagerComponent.h>
#include <AzCore/Memory/MemoryComponent.h>
#include <AzCore/Memory/AllocationRecords.h>
#include <AzFramework/Entity/GameEntityContextComponent.h>
namespace UnitTest
{
#if 0
using namespace AZ;
/**
*/
class NetBindingTestComponent
: public AZ::Component
, public AzFramework::NetBindable
{
friend class NetBindingComponentChunk;
public:
AZ_COMPONENT(NetBindingTestComponent, "{DE5CF1C0-B4B6-4BB0-86FE-936B400871E0}", AzFramework::NetBindable);
protected:
class NetChunk
: public GridMate::ReplicaChunk
{
public:
AZ_CLASS_ALLOCATOR(NetChunk, AZ::SystemAllocator, 0);
static const char* GetChunkName() { return "NetBindingTestComponent::NetChunk"; }
bool IsReplicaMigratable() override { return false; }
};
///////////////////////////////////////////////////////////////////////
// NetBindable
GridMate::ReplicaChunkPtr GetNetworkBinding() override
{
AZ_TracePrintf("NetBinding", "NetBindingTestComponent::GetNetworkBinding()\n");
m_chunk = GridMate::CreateReplicaChunk<NetChunk>();
AZ_Assert(m_chunk, "Failed to create NetBindingTestComponent::NetChunk!");
return m_chunk;
}
void SetNetworkBinding(GridMate::ReplicaChunkPtr binding) override
{
AZ_TracePrintf("NetBinding", "NetBindingTestComponent::SetNetworkBinding()\n");
AZ_TEST_ASSERT(binding);
AZ_TEST_ASSERT(binding->GetDescriptor()->GetChunkTypeId() == GridMate::ReplicaChunkClassId(NetChunk::GetChunkName()));
m_chunk = AZStd::static_pointer_cast<NetChunk>(binding);
}
void UnbindFromNetwork() override
{
if (m_chunk)
{
AZ_TracePrintf("NetBinding", "NetBindingTestComponent::UnbindFromNetwork()\n");
m_chunk = nullptr;
}
}
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// AZ::Component
static void Reflect(AZ::ReflectContext* reflection)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
if (serializeContext)
{
serializeContext->Class<NetBindingTestComponent, AZ::Component, AzFramework::NetBindable>()
;
}
// We also need to register the chunk type, and this would be a good time to do so.
GridMate::ReplicaChunkDescriptorTable::Get().RegisterChunkType<NetChunk>();
}
void Activate() override
{
AZ_TracePrintf("NetBinding", "NetBindingTestComponent::Activate()\n");
}
void Deactivate() override
{
AZ_TracePrintf("NetBinding", "NetBindingTestComponent::Deactivate()\n");
UnbindFromNetwork();
}
///////////////////////////////////////////////////////////////////////
AZStd::intrusive_ptr<NetChunk> m_chunk;
};
/**
* Fakes the behavior of NetBindingSystemContextData on the host side
*/
class FakeNetBindingContextChunk
: public GridMate::ReplicaChunk
{
public:
AZ_CLASS_ALLOCATOR(FakeNetBindingContextChunk, AZ::SystemAllocator, 0);
static const char* GetChunkName() { return "NetBindingSystemContextData"; } // We are pretending to be a NetBindingSystemContextData
FakeNetBindingContextChunk()
: m_bindingContextSequence("BindingContextSequence", AzFramework::UnspecifiedNetBindingContextSequence)
{
}
bool IsReplicaMigratable() override { return true; }
GridMate::DataSet<AZ::u32, GridMate::VlqU32Marshaler> m_bindingContextSequence;
};
/*
* NetBindingSystemComponentLifecycleTest
*/
class NetBindingSystemComponentLifecycleTest
: public GridMate::SessionEventBus::Handler
, public AzFramework::NetBindingHandlerBus::Handler
{
public:
void OnSessionCreated(GridMate::GridSession* session) override
{
if (session == m_session)
{
if (session->IsHost())
{
EBUS_EVENT(AzFramework::NetBindingSystemBus, OnNetworkSessionActivated, session);
}
}
}
void OnSessionJoined(GridMate::GridSession* session) override
{
if (session == m_session)
{
EBUS_EVENT(AzFramework::NetBindingSystemBus, OnNetworkSessionActivated, session);
}
}
void OnSessionDelete(GridMate::GridSession* session)
{
if (session == m_session)
{
EBUS_EVENT(AzFramework::NetBindingSystemBus, OnNetworkSessionDeactivated, session);
m_session = nullptr;
}
}
void BindToNetwork(GridMate::ReplicaPtr bindTo) override
{
// Verify that BindToNetwork() is not called more than once
AZ_TEST_ASSERT(!m_receivedBindEvent);
m_receivedBindEvent = true;
// Test that now we should be binding to the network
bool shouldBindToNetwork = false;
EBUS_EVENT_RESULT(shouldBindToNetwork, AzFramework::NetBindingSystemBus, ShouldBindToNetwork);
AZ_TEST_ASSERT(shouldBindToNetwork);
// Verify that the context sequence is no longer unspecified
AzFramework::NetBindingContextSequence contextSequence = AzFramework::UnspecifiedNetBindingContextSequence;
EBUS_EVENT_RESULT(contextSequence, AzFramework::NetBindingSystemBus, GetCurrentContextSequence);
AZ_TEST_ASSERT(contextSequence != AzFramework::UnspecifiedNetBindingContextSequence);
}
void UnbindFromNetwork() override
{
// Verify that UnbindFromNetwork() is not called more than once
AZ_TEST_ASSERT(!m_receivedUnbindEvent);
m_receivedUnbindEvent = true;
}
void run()
{
// Setup
AZ::ComponentApplication app;
AZ::ComponentApplication::Descriptor appDesc;
appDesc.m_recordsMode = AZ::Debug::AllocationRecords::RECORD_FULL;
AZ::Entity* systemEntity = app.Create(appDesc);
app.RegisterComponentDescriptor(AzFramework::NetBindingSystemComponent::CreateDescriptor());
app.RegisterComponentDescriptor(AzFramework::GameEntityContextComponent::CreateDescriptor());
systemEntity->Init();
systemEntity->CreateComponent<AZ::MemoryComponent>();
systemEntity->CreateComponent<AZ::AssetManagerComponent>();
systemEntity->CreateComponent<AzFramework::GameEntityContextComponent>();
systemEntity->CreateComponent<AzFramework::NetBindingSystemComponent>();
systemEntity->Activate();
AzFramework::NetBindingHandlerBus::Handler::BusConnect();
GridMate::GridMateDesc gridMateDesc;
GridMate::IGridMate* gridMate = GridMate::GridMateCreate(gridMateDesc);
GridMate::GridMateAllocatorMP::Descriptor allocDesc;
allocDesc.m_stackRecordLevels = 15;
allocDesc.m_custom = &AZ::AllocatorInstance<AZ::SystemAllocator>::Get();
AZ::AllocatorInstance<GridMate::GridMateAllocatorMP>::Create(allocDesc);
if (AZ::AllocatorInstance<GridMate::GridMateAllocatorMP>::Get().GetRecords())
{
AZ::AllocatorInstance<GridMate::GridMateAllocatorMP>::Get().GetRecords()->SetMode(AZ::Debug::AllocationRecords::RECORD_FULL);
}
GridMate::StartGridMateService<GridMate::LANSessionService>(gridMate, GridMate::SessionServiceDesc());
GridMate::SessionEventBus::Handler::BusConnect(gridMate);
// Test offline behavior
{
bool shouldBindToNetwork = true;
EBUS_EVENT_RESULT(shouldBindToNetwork, AzFramework::NetBindingSystemBus, ShouldBindToNetwork);
AZ_TEST_ASSERT(!shouldBindToNetwork);
AzFramework::NetBindingContextSequence offlineContextSequence = 0xBADF00D;
EBUS_EVENT_RESULT(offlineContextSequence, AzFramework::NetBindingSystemBus, GetCurrentContextSequence);
AZ_TEST_ASSERT(offlineContextSequence == AzFramework::UnspecifiedNetBindingContextSequence);
}
// Test host-side behavior
{
m_receivedBindEvent = m_receivedUnbindEvent = false;
// Host a session
GridMate::CarrierDesc carrierDesc;
carrierDesc.m_enableDisconnectDetection = true;
GridMate::LANSessionParams sessionParams;
sessionParams.m_numPublicSlots = 10;
sessionParams.m_flags = 0;
sessionParams.m_port = HOST_PORT;
sessionParams.m_params[sessionParams.m_numParams].m_id = "filter";
sessionParams.m_params[sessionParams.m_numParams].m_value = GridMate::Utils::GetMachineAddress();
sessionParams.m_numParams++;
m_session = gridMate->GetMultiplayerService()->HostSession(&sessionParams, carrierDesc);
int nFrame = 0;
while (m_session)
{
if (nFrame == 10)
{
// Verify that BindToNetwork() has been called
AZ_TEST_ASSERT(m_receivedBindEvent);
// Verify that we have a valid context sequence
AzFramework::NetBindingContextSequence contextSequence1 = AzFramework::UnspecifiedNetBindingContextSequence;
EBUS_EVENT_RESULT(contextSequence1, AzFramework::NetBindingSystemBus, GetCurrentContextSequence);
AZ_TEST_ASSERT(contextSequence1 != AzFramework::UnspecifiedNetBindingContextSequence);
EBUS_EVENT(AzFramework::GameEntityContextRequestBus, ResetGameContext);
// Verify that the context sequence was incremented
AzFramework::NetBindingContextSequence contextSequence2 = contextSequence1;
EBUS_EVENT_RESULT(contextSequence2, AzFramework::NetBindingSystemBus, GetCurrentContextSequence);
AZ_TEST_ASSERT(contextSequence2 != AzFramework::UnspecifiedNetBindingContextSequence);
AZ_TEST_ASSERT(contextSequence2 > contextSequence1);
m_session->Leave(false);
}
app.Tick();
gridMate->Update();
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(10));
nFrame++;
}
// Verify that we should no longer bind to the network
bool shouldBindToNetwork = true;
EBUS_EVENT_RESULT(shouldBindToNetwork, AzFramework::NetBindingSystemBus, ShouldBindToNetwork);
AZ_TEST_ASSERT(!shouldBindToNetwork);
// Verify that the context sequence was reset to unspecified
AzFramework::NetBindingContextSequence offlineContextSequence = 0xBADF00D;
EBUS_EVENT_RESULT(offlineContextSequence, AzFramework::NetBindingSystemBus, GetCurrentContextSequence);
AZ_TEST_ASSERT(offlineContextSequence == AzFramework::UnspecifiedNetBindingContextSequence);
}
// Test nonhost-side behavior by faking the behavior on the host side and then joining the host session.
{
m_receivedBindEvent = m_receivedUnbindEvent = false;
// Host a session
GridMate::CarrierDesc carrierDesc;
carrierDesc.m_enableDisconnectDetection = true;
GridMate::LANSessionParams sessionParams;
sessionParams.m_numPublicSlots = 10;
sessionParams.m_flags = 0;
sessionParams.m_port = HOST_PORT;
sessionParams.m_params[sessionParams.m_numParams].m_id = "filter";
sessionParams.m_params[sessionParams.m_numParams].m_value = GridMate::Utils::GetMachineAddress();
sessionParams.m_numParams++;
GridMate::GridSession* hostSession = gridMate->GetMultiplayerService()->HostSession(&sessionParams, carrierDesc);
// Add the fake context replica on the host and set the context sequence to 1
GridMate::ReplicaPtr replica = GridMate::Replica::CreateReplica("Potato");
FakeNetBindingContextChunk* contextChunk = GridMate::CreateReplicaChunk<FakeNetBindingContextChunk>();
replica->AttachReplicaChunk(contextChunk);
while (!hostSession->IsReady())
{
gridMate->Update();
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(10));
}
hostSession->GetReplicaMgr()->AddMaster(replica);
contextChunk->m_bindingContextSequence.Set(1);
int nFrame = 0;
while (m_session)
{
if (nFrame == 10)
{
// Join the hosted session
GridMate::SessionIdInfo sessionInfo;
sessionInfo.m_sessionId = hostSession->GetId();
m_session = gridMate->GetMultiplayerService()->JoinSession(&sessionInfo, GridMate::JoinParams(), carrierDesc);
}
if (nFrame == 20)
{
// Verify that BindToNetwork() has been called
AZ_TEST_ASSERT(m_receivedBindEvent);
// Verify that we have a valid context sequence
AzFramework::NetBindingContextSequence contextSequence = AzFramework::UnspecifiedNetBindingContextSequence;
EBUS_EVENT_RESULT(contextSequence, AzFramework::NetBindingSystemBus, GetCurrentContextSequence);
AZ_TEST_ASSERT(contextSequence == contextChunk->m_bindingContextSequence.Get());
// Simulate a context switch on the host
contextChunk->m_bindingContextSequence.Set(contextChunk->m_bindingContextSequence.Get() + 1);
}
if (nFrame == 30)
{
// Verify that the context sequence was incremented
AzFramework::NetBindingContextSequence contextSequence = AzFramework::UnspecifiedNetBindingContextSequence;
EBUS_EVENT_RESULT(contextSequence, AzFramework::NetBindingSystemBus, GetCurrentContextSequence);
AZ_TEST_ASSERT(contextSequence == contextChunk->m_bindingContextSequence.Get());
hostSession->Leave(false);
}
app.Tick();
gridMate->Update();
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(10));
nFrame++;
}
// Verify that we should no longer bind to the network
bool shouldBindToNetwork = true;
EBUS_EVENT_RESULT(shouldBindToNetwork, AzFramework::NetBindingSystemBus, ShouldBindToNetwork);
AZ_TEST_ASSERT(!shouldBindToNetwork);
// Verify that the context sequence was reset to unspecified
AzFramework::NetBindingContextSequence offlineContextSequence = 0xBADF00D;
EBUS_EVENT_RESULT(offlineContextSequence, AzFramework::NetBindingSystemBus, GetCurrentContextSequence);
AZ_TEST_ASSERT(offlineContextSequence == AzFramework::UnspecifiedNetBindingContextSequence);
}
// Clean up
GridMate::SessionEventBus::Handler::BusDisconnect();
GridMate::GridMateDestroy(gridMate);
AZ::AllocatorInstance<GridMate::GridMateAllocatorMP>::Destroy();
AzFramework::NetBindingHandlerBus::Handler::BusDisconnect();
app.Destroy();
}
static const int HOST_PORT = 5000;
GridMate::GridSession* m_session;
bool m_receivedBindEvent;
bool m_receivedUnbindEvent;
};
/*
* NetBindingFeatureTest (requires two instances)
*/
class NetBindingFeatureTest
: public GridMate::SessionEventBus::Handler
{
public:
void OnSessionCreated(GridMate::GridSession* session) override
{
if (session == m_session)
{
if (session->IsHost())
{
EBUS_EVENT(AzFramework::NetBindingSystemBus, OnNetworkSessionActivated, session);
}
}
}
void OnSessionJoined(GridMate::GridSession* session) override
{
if (session == m_session)
{
EBUS_EVENT(AzFramework::NetBindingSystemBus, OnNetworkSessionActivated, session);
}
}
void OnSessionDelete(GridMate::GridSession* session)
{
if (session == m_session)
{
EBUS_EVENT(AzFramework::NetBindingSystemBus, OnNetworkSessionDeactivated, session);
m_session = nullptr;
}
}
void OnGridSearchComplete(GridMate::GridSearch* results) override
{
if (results == m_search)
{
GridMate::CarrierDesc carrierDesc;
carrierDesc.m_enableDisconnectDetection = true;
// Create an entity before we get in the session
AZ_TracePrintf("NetBinding", "Spawning master entity...\n");
AZ::Entity* newEntity = nullptr;
newEntity = aznew Entity;
newEntity->CreateComponent<NetBindingTestComponent>();
newEntity->CreateComponent<AzFramework::NetBindingComponent>();
newEntity->Init();
newEntity->Activate();
m_entities.push_back(newEntity);
if (results->GetNumResults() == 0)
{
// Host a session instead
GridMate::LANSessionParams sessionParams;
sessionParams.m_numPublicSlots = 10;
sessionParams.m_flags = 0;
sessionParams.m_port = HOST_PORT;
sessionParams.m_params[sessionParams.m_numParams].m_id = "filter";
sessionParams.m_params[sessionParams.m_numParams].m_value = GridMate::Utils::GetMachineAddress();
sessionParams.m_numParams++;
m_session = m_gridMate->GetMultiplayerService()->HostSession(&sessionParams, carrierDesc);
m_search->Release();
}
else
{
// Join the session
GridMate::JoinParams joinParams;
m_session = m_gridMate->GetMultiplayerService()->JoinSession(results->GetResult(0), joinParams, carrierDesc);
}
m_search = nullptr;
}
}
void OnMemberJoined(GridMate::GridSession* session, GridMate::GridMember* member) override
{
if (session == m_session)
{
if (session->IsHost())
{
if (member != session->GetMyMember())
{
// Spawn an entity after session creation
AZ_TracePrintf("NetBinding", "Spawning master entity...\n");
AZ::Entity* newEntity = nullptr;
EBUS_EVENT_RESULT(newEntity, AzFramework::GameEntityContextRequestBus, CreateGameEntity, "ReplicatedEntity2");
newEntity->CreateComponent<NetBindingTestComponent>();
newEntity->CreateComponent<AzFramework::NetBindingComponent>();
newEntity->Init();
newEntity->Activate();
m_entities.push_back(newEntity);
}
}
}
}
void run()
{
m_gridMate = nullptr;
m_session = nullptr;
AZ::ComponentApplication app;
AZ::ComponentApplication::Descriptor appDesc;
AZ::Entity* systemEntity = app.Create(appDesc);
app.RegisterComponentDescriptor(AzFramework::NetBindingSystemComponent::CreateDescriptor());
app.RegisterComponentDescriptor(AzFramework::NetBindingComponent::CreateDescriptor());
app.RegisterComponentDescriptor(NetBindingTestComponent::CreateDescriptor());
app.RegisterComponentDescriptor(AzFramework::GameEntityContextComponent::CreateDescriptor());
systemEntity->Init();
systemEntity->CreateComponent<AZ::MemoryComponent>();
systemEntity->CreateComponent<AZ::AssetManagerComponent>();
systemEntity->CreateComponent<AzFramework::GameEntityContextComponent>();
systemEntity->CreateComponent<AzFramework::NetBindingSystemComponent>();
systemEntity->Activate();
GridMate::GridMateDesc gridMateDesc;
m_gridMate = GridMate::GridMateCreate(gridMateDesc);
GridMate::GridMateAllocatorMP::Descriptor allocDesc;
allocDesc.m_custom = &AZ::AllocatorInstance<AZ::SystemAllocator>::Get();
AZ::AllocatorInstance<GridMate::GridMateAllocatorMP>::Create(allocDesc);
GridMate::StartGridMateService<GridMate::LANSessionService>(m_gridMate, GridMate::SessionServiceDesc());
GridMate::SessionEventBus::Handler::BusConnect(m_gridMate);
// Search for an existing session
// If a session is not found, we will host a session from within the search callback.
{
GridMate::LANSearchParams searchParams;
searchParams.m_serverPort = HOST_PORT;
searchParams.m_params[searchParams.m_numParams].m_id = "filter";
searchParams.m_params[searchParams.m_numParams].m_value = GridMate::Utils::GetMachineAddress();
searchParams.m_params[searchParams.m_numParams].m_op = GridMate::GridSessionSearchOperators::SSO_OPERATOR_EQUAL;
searchParams.m_numParams++;
m_search = m_gridMate->GetMultiplayerService()->StartGridSearch(&searchParams);
while (m_search)
{
m_gridMate->Update();
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(10));
}
}
// Tick for a while
//static int nTicks = 100;
for (int i = 0; m_session; ++i)
{
if (m_session->IsHost())
{
if (i > 4000 && m_session->GetNumberOfMembers() == 1)
{
m_session->Leave(false);
}
}
m_gridMate->Update();
app.Tick();
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(10));
}
GridMate::SessionEventBus::Handler::BusDisconnect();
GridMate::GridMateDestroy(m_gridMate);
AZ::AllocatorInstance<GridMate::GridMateAllocatorMP>::Destroy();
for (AZ::Entity* entity : m_entities)
{
AzFramework::EntityContextId contextId = AzFramework::EntityContextId::CreateNull();
EBUS_EVENT_ID_RESULT(contextId, entity->GetId(), AzFramework::EntityIdContextQueryBus, GetOwningContextId);
if (contextId.IsNull())
{
delete entity;
}
else
{
EBUS_EVENT(AzFramework::GameEntityContextRequestBus, DestroyGameEntity, entity);
}
}
app.Destroy();
}
static const int HOST_PORT = 6000;
GridMate::IGridMate* m_gridMate;
GridMate::GridSession* m_session;
GridMate::GridSearch* m_search;
AZStd::fixed_vector<AZ::Entity*, 10> m_entities;
};
#endif
}
AZ_TEST_SUITE(NetBinding)
//AZ_TEST(UnitTest::NetBindingSystemComponentLifecycleTest)
//AZ_TEST(UnitTest::NetBindingFeatureTest)
AZ_TEST_SUITE_END
-335
View File
@@ -1,335 +0,0 @@
/*
* 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.
*
*/
#ifndef AZCORE_UNITTEST_NETBINDINGMOCKS_H
#define AZCORE_UNITTEST_NETBINDINGMOCKS_H
#include <AzTest/AzTest.h>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Entity/SliceGameEntityOwnershipServiceBus.h>
#include <AzCore/Slice/SliceComponent.h>
#include <AzFramework/Network/NetBindingHandlerBus.h>
namespace UnitTest
{
class MockGameEntityContext
: public AzFramework::GameEntityContextRequestBus::Handler
, public AzFramework::SliceGameEntityOwnershipServiceRequestBus::Handler
{
public:
MockGameEntityContext()
{
AzFramework::GameEntityContextRequestBus::Handler::BusConnect();
AzFramework::SliceGameEntityOwnershipServiceRequestBus::Handler::BusConnect();
}
~MockGameEntityContext()
{
AzFramework::SliceGameEntityOwnershipServiceRequestBus::Handler::BusDisconnect();
AzFramework::GameEntityContextRequestBus::Handler::BusDisconnect();
}
MOCK_METHOD3(InstantiateDynamicSlice, AzFramework::SliceInstantiationTicket(const AZ::Data::Asset<AZ::Data::AssetData>&, const AZ::Transform&, const AZ::IdUtils::Remapper<AZ::EntityId>::IdMapper&));
MOCK_METHOD0(GetGameEntityContextId, AzFramework::EntityContextId());
MOCK_METHOD0(GetGameEntityContextInstance, AzFramework::EntityContext*());
MOCK_METHOD1(CreateGameEntity, AZ::Entity*(const char*));
MOCK_METHOD1(AddGameEntity, void (AZ::Entity*));
MOCK_METHOD1(DestroyGameEntity, void (const AZ::EntityId&));
MOCK_METHOD1(DestroyGameEntityAndDescendants, void (const AZ::EntityId&));
MOCK_METHOD1(ActivateGameEntity, void (const AZ::EntityId&));
MOCK_METHOD1(DeactivateGameEntity, void (const AZ::EntityId&));
MOCK_METHOD1(DestroyDynamicSliceByEntity, bool (const AZ::EntityId&));
MOCK_METHOD2(LoadFromStream, bool (AZ::IO::GenericStream&, bool));
MOCK_METHOD0(ResetGameContext, void ());
MOCK_METHOD1(GetEntityName, AZStd::string (const AZ::EntityId&));
MOCK_METHOD1(DestroySliceByEntity, bool(const AZ::EntityId&));
MOCK_METHOD1(CreateGameEntityForBehaviorContext, AzFramework::BehaviorEntity (const char *));
MOCK_METHOD1(CancelDynamicSliceInstantiation, void (const AzFramework::SliceInstantiationTicket &));
};
class MockNetBindingSystemContextData
: public AzFramework::NetBindingSystemContextData
{
public:
AZ_CLASS_ALLOCATOR(MockNetBindingSystemContextData, AZ::SystemAllocator, 0);
static const char* GetChunkName()
{
return "MockNetBindingSystemContextData";
}
MOCK_METHOD1(OnAttachedToReplica, void (GridMate::Replica*));
MOCK_METHOD1(OnDetachedFromReplica, void (GridMate::Replica*));
MOCK_METHOD1(UpdateChunk, void (const GridMate::ReplicaContext&));
MOCK_METHOD1(UpdateFromChunk, void (const GridMate::ReplicaContext&));
MOCK_METHOD2(AcceptChangeOwnership, bool (GridMate::PeerId, const GridMate::ReplicaContext&));
MOCK_METHOD1(OnReplicaChangeOwnership, void (const GridMate::ReplicaContext&));
MOCK_METHOD0(IsUpdateFromReplicaEnabled, bool ());
MOCK_CONST_METHOD1(ShouldSendToPeer, bool (GridMate::ReplicaPeer*));
MOCK_METHOD1(CalculateDirtyDataSetMask, AZ::u32 (GridMate::MarshalContext&));
MOCK_METHOD1(OnDataSetChanged, void (const GridMate::DataSetBase&));
MOCK_METHOD2(Marshal, void (GridMate::MarshalContext&, AZ::u32));
MOCK_METHOD2(Unmarshal, void (GridMate::UnmarshalContext&, AZ::u32));
MOCK_METHOD0(IsReplicaMigratable, bool ());
MOCK_METHOD0(IsBroadcast, bool ());
MOCK_METHOD1(OnReplicaActivate, void (const GridMate::ReplicaContext&));
MOCK_METHOD1(OnReplicaDeactivate, void (const GridMate::ReplicaContext&));
/**
* \brief Helper method for GoogleMock to call NetBindingSystemContextData::OnReplicaActivate
*/
void Base_OnReplicaActivate(const GridMate::ReplicaContext& rc)
{
NetBindingSystemContextData::OnReplicaActivate(rc);
}
MOCK_METHOD0(GetReplicaManager, GridMate::ReplicaManager* ());
MOCK_METHOD0(ShouldBindToNetwork, bool ());
};
class MockReplicaManager
: public GridMate::ReplicaManager
{
public:
MOCK_METHOD2(OnIncomingConnection, void (GridMate::Carrier*, GridMate::ConnectionID));
MOCK_METHOD3(OnFailedToConnect, void (GridMate::Carrier*, GridMate::ConnectionID, GridMate::CarrierDisconnectReason));
MOCK_METHOD3(OnDriverError, void (GridMate::Carrier*, GridMate::ConnectionID, const GridMate::DriverError&));
MOCK_METHOD3(OnSecurityError, void (GridMate::Carrier*, GridMate::ConnectionID, const GridMate::SecurityError&));
MOCK_METHOD1(Destroy, bool (GridMate::Replica*));
MOCK_METHOD2(GetReplicaContext, void (const GridMate::Replica*, GridMate::ReplicaContext&));
MOCK_METHOD2(OnConnectionEstablished, void (GridMate::Carrier*, GridMate::ConnectionID));
MOCK_METHOD3(OnDisconnect, void (GridMate::Carrier*, GridMate::ConnectionID, GridMate::CarrierDisconnectReason));
MOCK_METHOD3(OnRateChange, void (GridMate::Carrier*, GridMate::ConnectionID, AZ::u32));
MOCK_METHOD1(FindReplica, GridMate::ReplicaPtr (GridMate::ReplicaId));
};
class MockAssetHandler
: public AZ::Data::AssetHandler
{
public:
AZ_CLASS_ALLOCATOR(MockAssetHandler, AZ::SystemAllocator, 0)
MOCK_METHOD2(CreateAsset, AZ::Data::AssetPtr (const AZ::Data::AssetId&, const AZ::Data::AssetType&));
MOCK_METHOD3(LoadAssetData, AZ::Data::AssetHandler::LoadResult (
const AZ::Data::Asset<AZ::Data::AssetData>&,
AZStd::shared_ptr<AZ::Data::AssetDataStream>,
const AZ::Data::AssetFilterCB&));
MOCK_METHOD2(SaveAssetData, bool (const AZ::Data::Asset<AZ::Data::AssetData>&, AZ::IO::GenericStream*));
MOCK_METHOD3(InitAsset, void (const AZ::Data::Asset<AZ::Data::AssetData>&, bool, bool));
MOCK_METHOD1(DestroyAsset, void (AZ::Data::AssetPtr));
MOCK_METHOD1(GetHandledAssetTypes, void (AZStd::vector<AZ::Data::AssetType>&));
MOCK_CONST_METHOD1(CanHandleAsset, bool (const AZ::Data::AssetId&));
};
class MockAsset
: public AZ::DynamicSliceAsset
{
public:
AZ_RTTI(MockAsset, "{78ABC204-452E-4621-A552-F04D3ABF1690}", DynamicSliceAsset);
MockAsset(const AZ::Data::AssetId& assetId = AZ::Data::AssetId())
: DynamicSliceAsset(assetId)
{
}
~MockAsset() = default;
};
class MockSliceReference
: public AZ::SliceComponent::SliceReference
{
public:
using SliceReference::SliceReference;
MOCK_METHOD1(CreateInstance, AZ::SliceComponent::SliceInstance*(const AZ::IdUtils::Remapper<AZ::EntityId>::IdMapper&));
MOCK_METHOD2(CloneInstance, AZ::SliceComponent::SliceInstance*(AZ::SliceComponent::SliceInstance*, AZ::SliceComponent::EntityIdToEntityIdMap&));
MOCK_METHOD1(FindInstance, AZ::SliceComponent::SliceInstance*(const AZ::SliceComponent::SliceInstanceId&));
MOCK_METHOD1(RemoveInstance, bool(AZ::SliceComponent::SliceInstance*));
MOCK_METHOD3(RemoveEntity, bool(AZ::EntityId, bool, AZ::SliceComponent::SliceInstance*));
MOCK_CONST_METHOD0(GetInstances, const AZ::SliceComponent::SliceReference::SliceInstances&());
MOCK_CONST_METHOD0(GetSliceAsset, const AZ::Data::Asset<AZ::SliceAsset>& ());
MOCK_CONST_METHOD0(GetSliceComponent, AZ::SliceComponent*());
MOCK_CONST_METHOD0(IsInstantiated, bool ());
MOCK_CONST_METHOD3(GetInstanceEntityAncestry, bool(const AZ::EntityId&, AZ::SliceComponent::EntityAncestorList&, AZ::u32));
MOCK_METHOD0(ComputeDataPatch, void());
};
class MockSliceInstance
: public AZ::SliceComponent::SliceInstance
{
public:
using SliceInstance::SliceInstance;
void SetMockInstantiatedContainer(AZ::SliceComponent::InstantiatedContainer* newContainer)
{
m_instantiated = newContainer;
for (AZ::Entity* entity : m_instantiated->m_entities)
{
m_entityIdToBaseCache.insert(AZStd::make_pair(entity->GetId(), entity->GetId()));
}
for (AZ::Entity* entity : m_instantiated->m_entities)
{
m_baseToNewEntityIdMap.insert(AZStd::make_pair(entity->GetId(), entity->GetId()));
}
}
MOCK_CONST_METHOD0(GetInstantiated, const AZ::SliceComponent::InstantiatedContainer*());
MOCK_CONST_METHOD0(GetDataPatch, const AZ::DataPatch&());
MOCK_CONST_METHOD0(GetDataFlags, const AZ::SliceComponent::DataFlagsPerEntity&());
MOCK_METHOD0(GetDataFlags, AZ::SliceComponent::DataFlagsPerEntity&());
MOCK_CONST_METHOD0(GetEntityIdMap, const AZ::SliceComponent::EntityIdToEntityIdMap& ());
MOCK_CONST_METHOD0(GetEntityIdToBaseMap, const AZ::SliceComponent::EntityIdToEntityIdMap& ());
MOCK_CONST_METHOD0(GetId, const AZ::SliceComponent::SliceInstanceId& ());
MOCK_CONST_METHOD0(GetMetadataEntity, AZ::Entity* ());
};
class MockEntity
: public AZ::Entity
{
public:
~MockEntity() override {}
MOCK_METHOD0(Init, void ());
MOCK_METHOD0(Activate, void ());
MOCK_METHOD0(Deactivate, void ());
/**
* \brief Helper method for GoogleMock to call base class method
*/
void Base_Init()
{
Entity::Init();
}
/**
* \brief Helper method for GoogleMock to mark an entity as activated
*/
void Base_Activate()
{
m_state = State::Active;
}
/**
* \brief Helper method for GoogleMock to mark an entity as deactivated
*/
void Base_Deactivate()
{
m_state = State::Init;
}
};
class MockComponentApplication
: public AZ::ComponentApplicationBus::Handler
{
public:
MockComponentApplication()
{
AZ::ComponentApplicationBus::Handler::BusConnect();
AZ::Interface<AZ::ComponentApplicationRequests>::Register(this);
}
~MockComponentApplication()
{
AZ::Interface<AZ::ComponentApplicationRequests>::Unregister(this);
AZ::ComponentApplicationBus::Handler::BusDisconnect();
}
AZStd::vector<AZ::Entity*> m_mockEntities;
bool AddEntity(AZ::Entity* entity) override
{
const auto it = AZStd::find(m_mockEntities.begin(), m_mockEntities.end(), entity);
if (it == m_mockEntities.end())
{
m_mockEntities.push_back(entity);
return true;
}
return false;
}
AZ::Entity* FindEntity(const AZ::EntityId& id) override
{
const auto it = AZStd::find_if(m_mockEntities.begin(), m_mockEntities.end(), [id](AZ::Entity* entity)
{
return entity->GetId() == id;
});
if (it != m_mockEntities.end())
{
return *it;
}
return nullptr;
}
MOCK_METHOD0(Destroy, void ());
MOCK_METHOD1(RegisterComponentDescriptor, void (const AZ::ComponentDescriptor*));
MOCK_METHOD1(UnregisterComponentDescriptor, void (const AZ::ComponentDescriptor*));
MOCK_METHOD1(RegisterEntityAddedEventHandler, void(AZ::EntityAddedEvent::Handler&));
MOCK_METHOD1(RegisterEntityRemovedEventHandler, void(AZ::EntityRemovedEvent::Handler&));
MOCK_METHOD1(RegisterEntityActivatedEventHandler, void(AZ::EntityActivatedEvent::Handler&));
MOCK_METHOD1(RegisterEntityDeactivatedEventHandler, void(AZ::EntityDeactivatedEvent::Handler&));
MOCK_METHOD1(SignalEntityActivated, void(AZ::Entity*));
MOCK_METHOD1(SignalEntityDeactivated, void(AZ::Entity*));
MOCK_METHOD1(RemoveEntity, bool (AZ::Entity*));
MOCK_METHOD1(DeleteEntity, bool (const AZ::EntityId&));
MOCK_METHOD1(GetEntityName, AZStd::string (const AZ::EntityId&));
MOCK_METHOD1(EnumerateEntities, void (const ComponentApplicationRequests::EntityCallback&));
MOCK_METHOD0(GetApplication, AZ::ComponentApplication* ());
MOCK_METHOD0(GetSerializeContext, AZ::SerializeContext* ());
MOCK_METHOD0(GetBehaviorContext, AZ::BehaviorContext* ());
MOCK_METHOD0(GetJsonRegistrationContext, AZ::JsonRegistrationContext* ());
MOCK_CONST_METHOD0(GetAppRoot, const char* ());
MOCK_CONST_METHOD0(GetEngineRoot, const char* ());
MOCK_CONST_METHOD0(GetExecutableFolder, const char* ());
MOCK_METHOD0(GetDrillerManager, AZ::Debug::DrillerManager* ());
MOCK_METHOD0(GetTickDeltaTime, float ());
MOCK_METHOD0(GetTimeAtCurrentTick, AZ::ScriptTimePoint ());
MOCK_METHOD1(Tick, void (float));
MOCK_METHOD0(TickSystem, void ());
MOCK_CONST_METHOD0(GetRequiredSystemComponents, AZ::ComponentTypeList ());
MOCK_METHOD1(ResolveModulePath, void (AZ::OSString&));
MOCK_METHOD0(RegisterCoreComponents, void ());
MOCK_METHOD1(Reflect, void (AZ::ReflectContext*));
MOCK_CONST_METHOD1(QueryApplicationType, void(AZ::ApplicationTypeQuery&));
};
class MockBindingComponent
: public AZ::Component
, public AzFramework::NetBindingHandlerBus::Handler
{
public:
AZ_COMPONENT(MockBindingComponent, "{8393809A-3256-4865-97A9-1CCA43073B4A}", NetBindingHandlerInterface);
static void Reflect(AZ::ReflectContext*) {}
MOCK_METHOD0(Init, void ());
MOCK_METHOD0(Activate, void ());
MOCK_METHOD0(Deactivate, void ());
MOCK_METHOD1(ReadInConfig, bool (const AZ::ComponentConfig*));
MOCK_CONST_METHOD1(WriteOutConfig, bool (AZ::ComponentConfig*));
MOCK_METHOD1(BindToNetwork, void (GridMate::ReplicaPtr));
MOCK_METHOD0(UnbindFromNetwork, void ());
MOCK_METHOD0(IsEntityBoundToNetwork, bool ());
MOCK_METHOD0(IsEntityAuthoritative, bool ());
MOCK_METHOD0(MarkAsLevelSliceEntity, void ());
MOCK_METHOD1(SetSliceInstanceId, void (const AZ::SliceComponent::SliceInstanceId&));
MOCK_METHOD1(SetReplicaPriority, void (GridMate::ReplicaPriority));
MOCK_METHOD1(RequestEntityChangeOwnership, void (GridMate::PeerId));
MOCK_CONST_METHOD0(GetReplicaPriority, GridMate::ReplicaPriority ());
};
}
#endif // AZCORE_UNITTEST_NETBINDINGMOCKS_H
@@ -1,605 +0,0 @@
/*
* 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 <AzCore/Component/ComponentApplication.h>
#include <AzFramework/Network/NetBindingSystemImpl.h>
#include <AzFramework/Network/NetBindable.h>
#include <AzFramework/Network/NetBindingSystemComponent.h>
#include <AzCore/Asset/AssetManagerComponent.h>
#include <AzCore/Memory/AllocationRecords.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <GridMate/Serialize/CompressionMarshal.h>
#include <GridMate/Replica/ReplicaFunctions.h>
#include <AzCore/Asset/AssetManager.h>
#include "NetBindingMocks.h"
#include <gmock/gmock-matchers.h>
#include <gmock/gmock-more-actions.h>
#include <gmock/gmock-spec-builders.h>
#include <AzCore/Slice/SliceComponent.h>
namespace UnitTest
{
using namespace AZ;
using namespace AzFramework;
using namespace GridMate;
class NetBindingWithSlicesTest
: public ScopedAllocatorSetupFixture
{
public:
const NetBindingContextSequence k_fakeContextSeq = 1;
const AZ::SliceComponent::SliceInstanceId k_fakeSliceInstanceId = Uuid::CreateRandom();
const AZ::SliceComponent::SliceInstanceId k_fakeSliceInstanceId_Another = Uuid::CreateRandom();
SliceInstantiationTicket m_sliceTicket = SliceInstantiationTicket(EntityContextId::CreateName("Test"), 1);
const Data::AssetId k_fakeAssetId = Data::AssetId(Uuid::CreateRandom(), 0);
const EntityId k_fakeEntityId_One = EntityId(9001);
const ReplicaId k_repId_One = 1001;
const EntityId k_fakeEntityId_Two = EntityId(9002);
const ReplicaId k_repId_Two = 1002;
AZStd::unique_ptr<NetBindingSystemImpl> m_netBindingImpl;
AZStd::unique_ptr<MockComponentApplication> m_componentApplication;
AZStd::unique_ptr<SerializeContext> m_applicationContext;
AZStd::unique_ptr<MockGameEntityContext> m_gameEntityMock;
AZStd::unique_ptr<MockReplicaManager> m_replicaManagerMock;
ReplicaPtr m_replicaMock;
ComponentDescriptor* m_netBindingSystemComponentDescriptor = nullptr;
AZStd::intrusive_ptr<MockNetBindingSystemContextData> m_contextChunkMock;
MockAssetHandler* m_myAssetHandlerAndCatalog = nullptr; // owned by AssetManager
AZStd::unique_ptr<MockAsset> m_fakeAsset;
const float k_wayOverSliceTimeout = NetBindingSystemImpl::s_sliceBindingTimeout.count() * 2.f;
const float k_smallStep = 0.1f;
void SetUpFakeAssetManager()
{
using namespace testing;
const Data::AssetManager::Descriptor desc;
Data::AssetManager::Create(desc);
m_myAssetHandlerAndCatalog = aznew NiceMock<MockAssetHandler>;
ON_CALL(*m_myAssetHandlerAndCatalog, CreateAsset(_, _))
.WillByDefault(Invoke([this](const Data::AssetId&, const Data::AssetType&) -> Data::AssetPtr
{
m_fakeAsset = AZStd::make_unique<NiceMock<MockAsset>>(k_fakeAssetId);
return m_fakeAsset.get();
}));
ON_CALL(*m_myAssetHandlerAndCatalog, DestroyAsset(_))
.WillByDefault(Invoke([this](const Data::AssetPtr asset)
{
EXPECT_EQ(asset, m_fakeAsset.get());
m_fakeAsset.reset();
}));
Data::AssetManager::Instance().RegisterHandler(m_myAssetHandlerAndCatalog, AzTypeInfo<DynamicSliceAsset>::Uuid());
Data::AssetManager::Instance().RegisterHandler(m_myAssetHandlerAndCatalog, AzTypeInfo<MockAsset>::Uuid());
}
void SetUp() override
{
using namespace testing;
m_applicationContext.reset(aznew SerializeContext());
AllocatorInstance<GridMateAllocatorMP>::Create();
AllocatorInstance<ThreadPoolAllocator>::Create();
DefaultValue<SliceInstantiationTicket>::Set(m_sliceTicket);
m_gameEntityMock = AZStd::make_unique<NiceMock<MockGameEntityContext>>();
m_componentApplication = AZStd::make_unique<NiceMock<MockComponentApplication>>();
ON_CALL(*m_componentApplication, GetSerializeContext())
.WillByDefault(Invoke([this]()
{
return m_applicationContext.get();
}));
ON_CALL(*m_gameEntityMock, GetGameEntityContextId())
.WillByDefault(Return(EntityContextId::CreateRandom()));
m_netBindingSystemComponentDescriptor = NetBindingSystemComponent::CreateDescriptor();
ReplicaChunkDescriptorTable::Get().RegisterChunkType<MockNetBindingSystemContextData>();
m_contextChunkMock.reset(CreateReplicaChunk<NiceMock<MockNetBindingSystemContextData>>());
ON_CALL(*m_contextChunkMock, ShouldBindToNetwork())
.WillByDefault(Return(true));
m_replicaManagerMock = AZStd::make_unique<NiceMock<MockReplicaManager>>();
ON_CALL(*m_contextChunkMock, GetReplicaManager())
.WillByDefault(Invoke([this]()
{
return m_replicaManagerMock.get();
}));
m_replicaMock = Replica::CreateReplica("unittest");
ON_CALL(*m_replicaManagerMock, FindReplica(_))
.WillByDefault(Invoke([this](ReplicaId id) -> ReplicaPtr
{
AZ_UNUSED(id);
return m_replicaMock;
}));
ON_CALL(*m_contextChunkMock, OnReplicaActivate(_))
.WillByDefault(Invoke(m_contextChunkMock.get(), &MockNetBindingSystemContextData::Base_OnReplicaActivate));
m_netBindingImpl = AZStd::make_unique<AzFramework::NetBindingSystemImpl>();
m_netBindingImpl->Init();
m_contextChunkMock->OnReplicaActivate(ReplicaContext(nullptr, TimeContext()));
SetUpFakeAssetManager();
}
void TearDown() override
{
Data::AssetManager::Destroy();
m_replicaMock.reset();
m_replicaManagerMock.reset();
m_contextChunkMock.reset();
m_fakeAsset.reset();
m_netBindingImpl->Shutdown();
m_netBindingImpl.reset();
ReplicaChunkDescriptorTable::Get().UnregisterReplicaChunkDescriptor(ReplicaChunkClassId(MockNetBindingSystemContextData::GetChunkName()));
m_netBindingSystemComponentDescriptor->ReleaseDescriptor();
m_componentApplication.reset();
m_gameEntityMock.reset();
AllocatorInstance<GridMateAllocatorMP>::Destroy();
AllocatorInstance<ThreadPoolAllocator>::Destroy();
m_applicationContext.reset();
}
};
TEST_F(NetBindingWithSlicesTest, SameSliceInstanceId_InstantiateDynamicSlice_CallOnce)
{
using namespace testing;
EXPECT_CALL(*m_gameEntityMock, InstantiateDynamicSlice(_, _, _))
.Times(1);
EXPECT_CALL(*m_gameEntityMock, CancelDynamicSliceInstantiation(_))
.Times(1);
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId; // both mock entities come from the same slice
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_Two;
spawnContext.m_staticEntityId = k_fakeEntityId_Two;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId; // both mock entities come from the same slice
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_Two, spawnContext);
}
// this should kick off NetBindingSystemImpl::ProcessBindRequests
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
}
TEST_F(NetBindingWithSlicesTest, DifferentSliceInstanceId_InstantiateDynamicSlice_CalledTwice)
{
using namespace testing;
EXPECT_CALL(*m_gameEntityMock, InstantiateDynamicSlice(_, _, _))
.Times(2);
EXPECT_CALL(*m_gameEntityMock, CancelDynamicSliceInstantiation(_))
.Times(2);
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId;
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_Two;
spawnContext.m_staticEntityId = k_fakeEntityId_Two;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId_Another; // different slice entity
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_Two, spawnContext);
}
// this should kick off NetBindingSystemImpl::ProcessBindRequests
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
}
TEST_F(NetBindingWithSlicesTest, AssetManagerDestroyed_InstantiateDynamicSlice_NotCalled)
{
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId; // both mock entities come from the same slice
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
Data::AssetManager::Destroy();
// this should kick off NetBindingSystemImpl::ProcessBindRequests, but InstantiateDynamicSlice will not be called
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
}
class ExtendedBindingWithSlicesTest
: public NetBindingWithSlicesTest
{
public:
void SetUp() override
{
NetBindingWithSlicesTest::SetUp();
}
void TearDown() override
{
using namespace testing;
EXPECT_CALL(*m_gameEntityMock, DestroyGameEntity(k_fakeEntityId_One))
.Times(AtMost(1));
EXPECT_CALL(*m_gameEntityMock, DestroyGameEntity(k_fakeEntityId_Two))
.Times(AtMost(1));
NetBindingWithSlicesTest::TearDown();
}
class InstantiateMockSlice
{
public:
explicit InstantiateMockSlice(ExtendedBindingWithSlicesTest* parent)
{
using namespace testing;
m_mockSliceRef = AZStd::make_unique<MockSliceReference>();
m_mockSliceInstance = AZStd::make_unique<MockSliceInstance>();
// container owns the entities and will delete them
auto mockContainer = AZStd::make_unique<SliceComponent::InstantiatedContainer>();
auto binding1 = AZStd::make_unique<NiceMock<MockBindingComponent>>();
mockContainer->m_entities.push_back(CreateMockEntity(parent->k_fakeEntityId_One, binding1.release()));
auto binding2 = AZStd::make_unique<NiceMock<MockBindingComponent>>();
mockContainer->m_entities.push_back(CreateMockEntity(parent->k_fakeEntityId_Two, binding2.release()));
m_mockSliceInstance->SetMockInstantiatedContainer(mockContainer.release());
SliceComponent::SliceInstanceAddress sliceInstanceAddress(m_mockSliceRef.get(), m_mockSliceInstance.get());
// This will pass our mock slice to NetBindingSystem
EBUS_EVENT_ID(parent->m_sliceTicket, SliceInstantiationResultBus, OnSlicePreInstantiate, parent->k_fakeAssetId, sliceInstanceAddress);
EBUS_EVENT_ID(parent->m_sliceTicket, SliceInstantiationResultBus, OnSliceInstantiated, parent->k_fakeAssetId, sliceInstanceAddress);
}
Entity* CreateMockEntity(const EntityId& id, Component* optional = nullptr)
{
using namespace testing;
auto mock = AZStd::make_unique<NiceMock<MockEntity>>();
mock->SetId(EntityId(id));
if (optional)
{
mock->AddComponent(optional); // entity owns the component
}
ON_CALL(*mock, Init())
.WillByDefault(Invoke(mock.get(), &MockEntity::Base_Init));
mock->Init();
ON_CALL(*mock, Activate())
.WillByDefault(Invoke(mock.get(), &MockEntity::Base_Activate));
ON_CALL(*mock, Deactivate())
.WillByDefault(Invoke(mock.get(), &MockEntity::Base_Deactivate));
return mock.release();
}
AZStd::unique_ptr<MockSliceReference> m_mockSliceRef;
AZStd::unique_ptr<MockSliceInstance> m_mockSliceInstance;
};
AZStd::unique_ptr<InstantiateMockSlice> m_slice;
void CreateMockSlice()
{
m_slice = AZStd::make_unique<InstantiateMockSlice>(this);
}
};
TEST_F(ExtendedBindingWithSlicesTest, ActiveSlice_EntitiesThatWerentBounded_StayDeactivated)
{
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId;
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
CreateMockSlice();
MockEntity* mock1 = static_cast<MockEntity*>(m_componentApplication->FindEntity(k_fakeEntityId_One));
EXPECT_CALL(*mock1, Activate()).
Times(1);
MockEntity* mock2 = static_cast<MockEntity*>(m_componentApplication->FindEntity(k_fakeEntityId_Two));
EXPECT_CALL(*mock2, Activate()).
Times(0);
EXPECT_CALL(*m_gameEntityMock, DestroyGameEntity(k_fakeEntityId_Two))
.Times(0);
// Now it should time out the slice handler and the second entity should remain deactivated since we didn't give binding request for it
EBUS_EVENT(AZ::TickBus, OnTick, k_wayOverSliceTimeout, AZ::ScriptTimePoint());
}
TEST_F(ExtendedBindingWithSlicesTest, ActiveSlice_SpawnSecondEntity_AfterLongDelay_InSameSlicenInstance)
{
EXPECT_CALL(*m_gameEntityMock, DestroyGameEntity(k_fakeEntityId_Two))
.Times(0);
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId;
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
CreateMockSlice();
MockEntity* mock2 = static_cast<MockEntity*>(m_componentApplication->FindEntity(k_fakeEntityId_Two));
EXPECT_CALL(*mock2, Activate()).
Times(0);
// This should not trigger removal of the second entity yet
auto halfTimeoutInSeconds = AZStd::chrono::seconds(NetBindingSystemImpl::s_sliceBindingTimeout).count() * 10.f;
EBUS_EVENT(AZ::TickBus, OnTick, halfTimeoutInSeconds, AZ::ScriptTimePoint());
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_Two;
spawnContext.m_staticEntityId = k_fakeEntityId_Two;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId; // both mock entities come from the same slice
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_Two, spawnContext);
}
EXPECT_CALL(*mock2, Activate()).
Times(1);
// This should give net binding system time to bind the second entity
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
// Let the slice timeout, this should lead to no destruction since both entities ought to have been bound by now
EBUS_EVENT(AZ::TickBus, OnTick, k_wayOverSliceTimeout, AZ::ScriptTimePoint());
}
TEST_F(ExtendedBindingWithSlicesTest, ActiveSlice_DespawnLastEntity_DespawnWholeSliceAfterTimeout)
{
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId;
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
CreateMockSlice();
MockEntity* mock1 = static_cast<MockEntity*>(m_componentApplication->FindEntity(k_fakeEntityId_One));
EXPECT_CALL(*mock1, Activate()).
Times(1);
// This should not trigger removal of the second entity yet
auto halfTimeoutInSeconds = AZStd::chrono::seconds(NetBindingSystemImpl::s_sliceBindingTimeout).count() * 10.f;
EBUS_EVENT(AZ::TickBus, OnTick, halfTimeoutInSeconds, AZ::ScriptTimePoint());
EXPECT_CALL(*mock1, Deactivate()).
Times(1);
EBUS_EVENT(NetBindingSystemBus, UnbindGameEntity, k_fakeEntityId_One, k_fakeSliceInstanceId);
EXPECT_CALL(*m_gameEntityMock, DestroyGameEntity(k_fakeEntityId_One))
.Times(1);
EXPECT_CALL(*m_gameEntityMock, DestroyGameEntity(k_fakeEntityId_Two))
.Times(1);
EBUS_EVENT(AZ::TickBus, OnTick, k_wayOverSliceTimeout, AZ::ScriptTimePoint());
}
TEST_F(ExtendedBindingWithSlicesTest, ActiveSlice_DespawnLastEntityBeforeSliceInstantiation_DespawnWholeSlice)
{
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId;
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
EBUS_EVENT(NetBindingSystemBus, UnbindGameEntity, k_fakeEntityId_One, k_fakeSliceInstanceId);
CreateMockSlice();
MockEntity* mock1 = static_cast<MockEntity*>(m_componentApplication->FindEntity(k_fakeEntityId_One));
EXPECT_CALL(*mock1, Activate()).
Times(0);
auto halfTimeoutInSeconds = AZStd::chrono::seconds(NetBindingSystemImpl::s_sliceBindingTimeout).count() * 10.f;
EBUS_EVENT(AZ::TickBus, OnTick, halfTimeoutInSeconds, AZ::ScriptTimePoint());
EBUS_EVENT(AZ::TickBus, OnTick, k_wayOverSliceTimeout, AZ::ScriptTimePoint());
}
TEST_F(ExtendedBindingWithSlicesTest, ActiveSlice_ReuseEntity)
{
EXPECT_CALL(*m_gameEntityMock, DestroyGameEntity(k_fakeEntityId_Two))
.Times(0);
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId;
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_Two;
spawnContext.m_staticEntityId = k_fakeEntityId_Two;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId; // both mock entities come from the same slice
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_Two, spawnContext);
}
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
CreateMockSlice();
MockEntity* mock2 = static_cast<MockEntity*>(m_componentApplication->FindEntity(k_fakeEntityId_Two));
EXPECT_CALL(*mock2, Activate()).
Times(1);
EBUS_EVENT(AZ::TickBus, OnTick, k_wayOverSliceTimeout, AZ::ScriptTimePoint());
EXPECT_CALL(*mock2, Deactivate()).
Times(1);
// some time later the second entity goes away and comes back
EBUS_EVENT(NetBindingSystemBus, UnbindGameEntity, k_fakeEntityId_Two, k_fakeSliceInstanceId);
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_Two;
spawnContext.m_staticEntityId = k_fakeEntityId_Two;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId; // both mock entities come from the same slice
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_Two, spawnContext);
}
// The same entity should be activated for the second time
EXPECT_CALL(*mock2, Activate()).
Times(1); // Note, Google Mock treats each expect_call separately and satisfies them separately. That's why it's 1 here, despite being a second call.
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
}
TEST_F(ExtendedBindingWithSlicesTest, SliceFailedToSpawn)
{
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId;
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
EBUS_EVENT_ID(m_sliceTicket, SliceInstantiationResultBus, OnSliceInstantiationFailed, k_fakeAssetId);
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
EXPECT_TRUE(m_componentApplication->FindEntity(k_fakeEntityId_One) == nullptr);
}
TEST_F(ExtendedBindingWithSlicesTest, SliceSpawned_AfterTimeout)
{
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId;
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
EBUS_EVENT(AZ::TickBus, OnTick, k_wayOverSliceTimeout, AZ::ScriptTimePoint());
CreateMockSlice();
MockEntity* mock1 = static_cast<MockEntity*>(m_componentApplication->FindEntity(k_fakeEntityId_One));
EXPECT_CALL(*mock1, Activate()).
Times(1);
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
}
}
-801
View File
@@ -1,801 +0,0 @@
/*
* 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 <AzFramework/Network/NetworkContext.h>
#include <AzFramework/Application/Application.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/Memory/MemoryComponent.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/IO/ByteContainerStream.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <GridMate/Replica/ReplicaChunk.h>
#include <GridMate/Replica/DataSet.h>
#include <GridMate/Replica/RemoteProcedureCall.h>
#include <GridMate/Serialize/DataMarshal.h>
#include <GridMate/Serialize/UtilityMarshal.h>
#include <GridMate/Serialize/ContainerMarshal.h>
#include <GridMate/Replica/ReplicaMgr.h>
#include <AzFramework/Network/InterestManagerComponent.h>
namespace UnitTest
{
using namespace AZ;
using namespace AzFramework;
class TestComponentExternalChunk
: public AZ::Component
, public NetBindable
{
public:
AZ_COMPONENT(TestComponentExternalChunk, "{73BB3B15-7C4D-4BD5-9568-F3B2DCBC7725}", AZ::Component);
static void Reflect(ReflectContext* context);
void Init() override
{
NetBindable::NetInit();
}
void Activate() override {}
void Deactivate() override {}
bool SetPos(float x, float y, const RpcContext&)
{
m_x = x;
m_y = y;
return true;
}
void OnFloatChanged(const float&, const TimeContext&)
{
m_floatChanged = true;
}
bool m_floatChanged = false;
private:
float m_x = 0, m_y = 0;
};
class TestComponentReplicaChunk
: public ReplicaChunkBase
, public ReplicaChunkInterface
{
public:
GM_CLASS_ALLOCATOR(TestComponentReplicaChunk);
static const char* GetChunkName() { return "TestComponentReplicaChunk"; }
bool IsReplicaMigratable() override { return true; }
public:
TestComponentReplicaChunk()
: m_int("m_int", 42)
, m_float("m_float", 96.4f)
, SetInt("SetInt")
, SetPos("SetPos")
{
}
bool SetIntImpl(int newValue, const RpcContext&)
{
m_int.Set(newValue);
return true;
}
DataSet<int> m_int;
DataSet<float>::BindInterface<TestComponentExternalChunk, &TestComponentExternalChunk::OnFloatChanged> m_float;
GridMate::Rpc<GridMate::RpcArg<int, Marshaler<int> > >::BindInterface<TestComponentReplicaChunk, &TestComponentReplicaChunk::SetIntImpl> SetInt;
GridMate::Rpc<GridMate::RpcArg<float>, GridMate::RpcArg<float> >::BindInterface<TestComponentExternalChunk, &TestComponentExternalChunk::SetPos> SetPos;
};
void TestComponentExternalChunk::Reflect(ReflectContext* context)
{
NetworkContext* netContext = azrtti_cast<NetworkContext*>(context);
if (netContext)
{
netContext->Class<TestComponentExternalChunk>()
->Chunk<TestComponentReplicaChunk>()
->Field("m_int", &TestComponentReplicaChunk::m_int)
->Field("m_float", &TestComponentReplicaChunk::m_float)
->RPC("SetInt", &TestComponentReplicaChunk::SetInt)
->RPC("SetPos", &TestComponentReplicaChunk::SetPos);
}
}
class TestComponentAutoChunk
: public AZ::Component
, public NetBindable
{
public:
enum TestEnum
{
TEST_Value0 = 0,
TEST_Value1 = 1,
TEST_Value255 = 255
};
AZ_COMPONENT(TestComponentAutoChunk, "{003FD1BC-8456-43D5-9879-1B3804327A4F}", AZ::Component);
static void Reflect(ReflectContext* context)
{
NetworkContext* netContext = azrtti_cast<NetworkContext*>(context);
if (netContext)
{
netContext->Class<TestComponentAutoChunk>()
->Field("m_int", &TestComponentAutoChunk::m_int)
->Field("m_float", &TestComponentAutoChunk::m_float)
->Field("m_enum", &TestComponentAutoChunk::m_enum)
->RPC("SetInt", &TestComponentAutoChunk::SetInt)
->CtorData("CtorInt", &TestComponentAutoChunk::GetCtorInt, &TestComponentAutoChunk::SetCtorInt)
->CtorData("CtorVec", &TestComponentAutoChunk::GetCtorVec, &TestComponentAutoChunk::SetCtorVec);
}
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<TestComponentAutoChunk, AZ::Component>()
->Version(1)
->Field("m_int", &TestComponentAutoChunk::m_int)
->Field("m_float", &TestComponentAutoChunk::m_float)
->Field("m_enum", &TestComponentAutoChunk::m_enum)
->Field("ctorInt", &TestComponentAutoChunk::m_ctorInt)
->Field("ctorVec", &TestComponentAutoChunk::m_ctorVec);
}
}
void Init() override
{
NetBindable::NetInit();
}
void Activate() override {}
void Deactivate() override {}
void SetNetworkBinding(ReplicaChunkPtr chunk) override {}
void UnbindFromNetwork() override {}
bool SetIntImpl(int val, const RpcContext&)
{
m_int = val;
return true;
}
void OnFloatChanged(const float&, const TimeContext&)
{
}
int GetCtorInt() const { return m_ctorInt; }
void SetCtorInt(const int& ctorInt) { m_ctorInt = ctorInt; }
AZStd::vector<int>& GetCtorVec() { return m_ctorVec; }
void SetCtorVec(const AZStd::vector<int>& vec) { m_ctorVec = vec; }
int m_ctorInt;
AZStd::vector<int> m_ctorVec;
Field<int> m_int;
BoundField<float, TestComponentAutoChunk, &TestComponentAutoChunk::OnFloatChanged> m_float;
Field<TestEnum, GridMate::ConversionMarshaler<AZ::u8, TestEnum> > m_enum;
Rpc<int>::Binder<TestComponentAutoChunk, &TestComponentAutoChunk::SetIntImpl> SetInt;
};
class NetContextReflectionTest
: public AllocatorsTestFixture
{
public:
void SetUp() override
{
AllocatorsTestFixture::SetUp();
AZ::AllocatorInstance<GridMate::GridMateAllocatorMP>::Create();
}
void TearDown() override
{
AZ::AllocatorInstance<GridMate::GridMateAllocatorMP>::Destroy();
AllocatorsTestFixture::TearDown();
}
void run()
{
AzFramework::Application app;
AzFramework::Application::Descriptor appDesc;
appDesc.m_recordingMode = Debug::AllocationRecords::RECORD_NO_RECORDS;
appDesc.m_allocationRecords = false;
appDesc.m_enableDrilling = false;
app.Start(appDesc);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
AzFramework::NetworkContext* netContext = nullptr;
EBUS_EVENT_RESULT(netContext, NetSystemRequestBus, GetNetworkContext);
AZ_TEST_ASSERT(netContext);
AZ::ComponentDescriptor* descTestComponentExternalChunk = TestComponentExternalChunk::CreateDescriptor();
app.RegisterComponentDescriptor(descTestComponentExternalChunk);
AZ::ComponentDescriptor* descTestComponentAutoChunk = TestComponentAutoChunk::CreateDescriptor();
app.RegisterComponentDescriptor(descTestComponentAutoChunk);
AZ::Entity* testEntity = aznew AZ::Entity("TestEntity");
testEntity->Init();
testEntity->CreateComponent<TestComponentAutoChunk>();
testEntity->CreateComponent<TestComponentExternalChunk>();
testEntity->Activate();
// test field binding/auto reflection/creation
{
TestComponentAutoChunk* testComponent = testEntity->FindComponent<TestComponentAutoChunk>();
AZ_TEST_ASSERT(testComponent);
testComponent->SetInt(2048); // should happen locally
AZ_TEST_ASSERT(testComponent->m_int == 2048);
ReplicaChunkPtr chunk = testComponent->GetNetworkBinding();
AZ_TEST_ASSERT(chunk);
GridMate::ReplicaChunkDescriptor* desc = chunk->GetDescriptor();
AZ_TEST_ASSERT(desc);
testComponent->m_ctorInt = 8192;
for (int n = 0; n < 16; ++n)
{
testComponent->m_ctorVec.push_back(n);
}
GridMate::WriteBufferDynamic wb(GridMate::EndianType::IgnoreEndian);
desc->MarshalCtorData(chunk.get(), wb);
{
// Create a chunk from the recorded ctor data, ensure that it stores
// the ctor data in preparation for copying it to the instance
GridMate::TimeContext tc;
GridMate::ReplicaContext rc(nullptr, tc);
GridMate::ReadBuffer rb(wb.GetEndianType(), wb.Get(), wb.Size());
GridMate::UnmarshalContext ctx(rc);
ctx.m_hasCtorData = true;
ctx.m_iBuf = &rb;
ReplicaChunkPtr chunk2 = desc->CreateFromStream(ctx);
AZ_TEST_ASSERT(chunk2); // ensure a new chunk was created
ReflectedReplicaChunkBase* refChunk = static_cast<ReflectedReplicaChunkBase*>(chunk2.get());
AZ_TEST_ASSERT(refChunk->m_ctorBuffer.Size() == sizeof(int) + sizeof(AZ::u16) + (sizeof(int) * testComponent->m_ctorVec.size()));
}
{
// discard a ctor data stream and ensure that the stream is emptied
GridMate::TimeContext tc;
GridMate::ReplicaContext rc(nullptr, tc);
GridMate::ReadBuffer rb(wb.GetEndianType(), wb.Get(), wb.Size());
GridMate::UnmarshalContext ctx(rc);
ctx.m_hasCtorData = true;
ctx.m_iBuf = &rb;
desc->DiscardCtorStream(ctx);
AZ_TEST_ASSERT(rb.IsEmptyIgnoreTrailingBits()); // should have discarded the whole stream
}
{
// Make another chunk and bind it to a new component and make sure the ctor data matches
AZ::Entity* testEntity2 = aznew AZ::Entity("TestEntity2");
testEntity2->Init();
testEntity2->CreateComponent<TestComponentAutoChunk>();
testEntity2->Activate();
GridMate::TimeContext tc;
GridMate::ReplicaContext rc(nullptr, tc);
GridMate::ReadBuffer rb(wb.GetEndianType(), wb.Get(), wb.Size());
GridMate::UnmarshalContext ctx(rc);
ctx.m_hasCtorData = true;
ctx.m_iBuf = &rb;
ReplicaChunkPtr chunk2 = desc->CreateFromStream(ctx);
TestComponentAutoChunk* testComponent2 = testEntity2->FindComponent<TestComponentAutoChunk>();
netContext->Bind(testComponent2, chunk2, NetworkContextBindMode::NonAuthoritative);
// Ensure values match after ctor data is applied
AZ_TEST_ASSERT(testComponent2->m_ctorInt == testComponent->m_ctorInt);
AZ_TEST_ASSERT(testComponent2->m_ctorVec == testComponent->m_ctorVec);
}
testComponent->SetInt(4096);
AZ_TEST_ASSERT(testComponent->m_int == 4096);
testComponent->m_int = 42; // now it should change
AZ_TEST_ASSERT(testComponent->m_int == 42);
testComponent->m_enum = TestComponentAutoChunk::TEST_Value1;
chunk.reset(); // should cause netContext->DestroyReplicaChunk()
}
// test chunk binding/creation
{
TestComponentExternalChunk* testComponent = testEntity->FindComponent<TestComponentExternalChunk>();
AZ_TEST_ASSERT(testComponent);
ReplicaChunkPtr chunk = testComponent->GetNetworkBinding();
AZ_TEST_ASSERT(chunk);
TestComponentReplicaChunk* testChunk = static_cast<TestComponentReplicaChunk*>(chunk.get());
// for now, this will throw a warning, but will at least attempt the dispatch
testChunk->SetPos(42.0f, 96.0f);
AZ_TEST_ASSERT(testComponent->m_floatChanged == false);
testChunk->m_float.Set(1024.0f);
// I would like to test that the notify fired, but without a Replica, cant :(
testComponent->UnbindFromNetwork();
chunk.reset(); ///// CRASHES FROM HERE
}
// test serialization of NetBindable::Fields
{
TestComponentAutoChunk* testComponent = testEntity->FindComponent<TestComponentAutoChunk>();
AZStd::vector<AZ::u8> buffer;
AZ::IO::ByteContainerStream<AZStd::vector<AZ::u8> > saveStream(&buffer);
bool saved = AZ::Utils::SaveObjectToStream(saveStream, AZ::DataStream::ST_XML, testComponent);
AZ_TEST_ASSERT(saved);
AZ::IO::ByteContainerStream<AZStd::vector<AZ::u8> > loadStream(&buffer);
TestComponentAutoChunk* testCopy = AZ::Utils::LoadObjectFromStream<TestComponentAutoChunk>(loadStream);
AZ_TEST_ASSERT(testCopy);
delete testCopy;
}
testEntity->Deactivate();
delete testEntity;
descTestComponentExternalChunk->ReleaseDescriptor();
descTestComponentAutoChunk->ReleaseDescriptor();
app.Stop();
}
};
TEST_F(NetContextReflectionTest, Test)
{
run();
}
template <typename ComponentType>
class NetContextFixture
: public ::testing::Test
{
public:
NetContextFixture() = default;
~NetContextFixture() = default;
void SetUp() override
{
AZ::AllocatorInstance<SystemAllocator>::Create();
m_app = AZStd::make_unique<AzFramework::Application>();
m_app->Start(AzFramework::Application::Descriptor());
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
AzFramework::NetworkContext* netContext = nullptr;
EBUS_EVENT_RESULT(netContext, NetSystemRequestBus, GetNetworkContext);
AZ_TEST_ASSERT(netContext);
m_descTestComponentAutoChunk = ComponentType::CreateDescriptor();
m_app->RegisterComponentDescriptor(m_descTestComponentAutoChunk);
m_entity = AZStd::make_unique<AZ::Entity>("TestEntity");
m_entity->Init();
m_entity->CreateComponent<ComponentType>();
m_entity->Activate();
}
void TearDown() override
{
m_descTestComponentAutoChunk->ReleaseDescriptor();
m_entity->Deactivate();
m_entity.reset();
m_app->Stop();
m_app.reset();
AZ::AllocatorInstance<SystemAllocator>::Destroy();
}
void RunTest()
{
const ComponentType* testComponent = m_entity->FindComponent<ComponentType>();
AZStd::vector<AZ::u8> buffer;
AZ::IO::ByteContainerStream<AZStd::vector<AZ::u8> > saveStream(&buffer);
const bool saved = AZ::Utils::SaveObjectToStream(saveStream, AZ::DataStream::ST_XML, testComponent);
AZ_TEST_ASSERT(saved);
AZ::IO::ByteContainerStream<AZStd::vector<AZ::u8> > loadStream(&buffer);
const AZStd::unique_ptr<ComponentType> testCopy(AZ::Utils::LoadObjectFromStream<ComponentType>(loadStream));
AZ_TEST_ASSERT(testCopy);
}
AZStd::unique_ptr<AzFramework::Application> m_app;
AZStd::unique_ptr<AZ::Entity> m_entity;
AZ::ComponentDescriptor* m_descTestComponentAutoChunk = nullptr;
};
class TestComponent_EmptyNetContext
: public AZ::Component
, public NetBindable
{
public:
AZ_COMPONENT(TestComponent_EmptyNetContext, "{B1E2E2DD-DA70-4D59-A185-AF9A5CCF1574}", AZ::Component, NetBindable);
static void Reflect(ReflectContext* context)
{
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
{
serializeContext->Class<TestComponent_EmptyNetContext, AZ::Component>()
->Version(1);
}
if (NetworkContext* netContext = azrtti_cast<NetworkContext*>(context))
{
netContext->Class<TestComponent_EmptyNetContext>();
}
}
void Activate() override {}
void Deactivate() override {}
};
using NetContextEmpty = NetContextFixture<TestComponent_EmptyNetContext>;
TEST_F(NetContextEmpty, SerializationTests)
{
RunTest();
}
template<typename FieldType>
class TestComponent_OneField
: public AZ::Component
, public NetBindable
{
public:
AZ_COMPONENT(TestComponent_OneField, "{A7BCDBEF-3D4F-4D04-A6FA-DF48D4B66ABE}", AZ::Component, NetBindable);
using ThisComponentType = TestComponent_OneField<FieldType>;
static void Reflect(ReflectContext* context)
{
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
{
serializeContext->Class<TestComponent_OneField, AZ::Component>()
->Field("Field", &TestComponent_OneField::m_field)
->Version(1);
}
if (NetworkContext* netContext = azrtti_cast<NetworkContext*>(context))
{
netContext->Class<TestComponent_OneField>()
->Field("Field", &TestComponent_OneField::m_field);
}
}
void Activate() override {}
void Deactivate() override {}
Field<FieldType> m_field;
};
TYPED_TEST_CASE_P(NetContextFixture);
TYPED_TEST_P(NetContextFixture, SerializationTests)
{
this->RunTest();
}
REGISTER_TYPED_TEST_CASE_P(NetContextFixture, SerializationTests);
/*
* Testing the basic common types.
*/
using CommonTypes = ::testing::Types<
TestComponent_OneField<bool>,
TestComponent_OneField<float>,
TestComponent_OneField<AZ::u32>,
TestComponent_OneField<AZ::EntityId>,
TestComponent_OneField<AZ::Vector2>,
TestComponent_OneField<AZ::Vector3>,
TestComponent_OneField<AZ::Quaternion>
>;
INSTANTIATE_TYPED_TEST_CASE_P(NetContextCommonSerialization, NetContextFixture, CommonTypes);
/*
* And some less common types.
*/
using LessCommonTypes = ::testing::Types<
TestComponent_OneField<AZStd::string>,
TestComponent_OneField<AZ::Transform>,
TestComponent_OneField<AZ::Color>,
TestComponent_OneField<AZStd::vector<int>>,
TestComponent_OneField<AZ::Uuid>
>;
INSTANTIATE_TYPED_TEST_CASE_P(NetContextLessCommonSerialization, NetContextFixture, LessCommonTypes);
/*
* Next up are marshal and unmarshal tests.
*/
template <typename ComponentType>
class NetContextMarshalFixture
: public UnitTest::AllocatorsTestFixture
{
public:
NetContextMarshalFixture() = default;
~NetContextMarshalFixture() = default;
void SetUp() override
{
UnitTest::AllocatorsTestFixture::SetUp();
AZ::AllocatorInstance<GridMate::GridMateAllocator>::Create();
AZ::AllocatorInstance<GridMate::GridMateAllocatorMP>::Create();
m_app = AZStd::make_unique<AzFramework::Application>();
m_app->Start(AzFramework::Application::Descriptor());
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
AzFramework::NetworkContext* netContext = nullptr;
EBUS_EVENT_RESULT(netContext, NetSystemRequestBus, GetNetworkContext);
AZ_TEST_ASSERT(netContext);
m_descTestComponentAutoChunk = ComponentType::CreateDescriptor();
m_app->RegisterComponentDescriptor(m_descTestComponentAutoChunk);
m_entityFrom = AZStd::make_unique<AZ::Entity>("TestEntityFrom");
m_entityFrom->Init();
m_componentFrom = m_entityFrom->CreateComponent<ComponentType>();
m_entityFrom->Activate();
m_entityTo = AZStd::make_unique<AZ::Entity>("TestEntityTo");
m_entityTo->Init();
m_componentTo = m_entityTo->CreateComponent<ComponentType>();
m_entityTo->Activate();
}
void MarshalUnMarshal()
{
AzFramework::NetworkContext* netContext = nullptr;
NetSystemRequestBus::BroadcastResult(netContext, &NetSystemRequestBus::Events::GetNetworkContext);
AZ_TEST_ASSERT(netContext);
ComponentType* testComponent = m_entityFrom->FindComponent<ComponentType>();
AZ_TEST_ASSERT(testComponent);
ReplicaChunkPtr chunk = testComponent->GetNetworkBinding();
AZ_TEST_ASSERT(chunk);
m_outReplica = AZStd::make_unique<GridMate::Replica>("ReplicaTo");
{
m_outManager = AZStd::make_unique<GridMate::ReplicaManager>();
m_outPeer = AZStd::make_unique<GridMate::ReplicaPeer>(m_outManager.get());
GridMate::WriteBufferDynamic wb(GridMate::EndianType::IgnoreEndian);
{
GridMate::TimeContext tc;
const GridMate::ReplicaContext rc(nullptr, tc);
GridMate::MarshalContext mc(GridMate::ReplicaMarshalFlags::FullSync, &wb, nullptr, rc);
mc.m_peer = m_outPeer.get();
mc.m_rm = m_outManager.get();
chunk->Debug_PrepareData(wb.GetEndianType(), GridMate::ReplicaMarshalFlags::FullSync);
chunk->Debug_Marshal(mc, 0);
}
// and now unmarshal into the other entity
{
GridMate::TimeContext tc;
const GridMate::ReplicaContext rc(nullptr, tc);
GridMate::ReadBuffer rb(wb.GetEndianType(), wb.Get(), wb.Size());
GridMate::UnmarshalContext ctx(rc);
ctx.m_hasCtorData = false;
ctx.m_iBuf = &rb;
ctx.m_peer = m_outPeer.get();
ctx.m_rm = m_outManager.get();
m_outReplicaChunk = chunk->GetDescriptor()->CreateFromStream(ctx);
m_outReplicaChunk->Debug_AttachedToReplica(m_outReplica.get());
ctx.m_peer->Debug_Add(m_outReplica.get());
m_outReplicaChunk->Debug_Unmarshal(ctx, 0);
/*
* Note the order: unmarshal first to populate the chunk with data, then apply it to a component.
* The expectation is that the valid will apply to NetBindable::Field without being overwritten.
*/
m_componentTo->SetNetworkBinding(m_outReplicaChunk);
// the main test body can now test for the equality
}
}
}
void TearDown() override
{
m_outReplicaChunk.reset();
m_outManager.reset();
m_outPeer.reset();
m_outReplica.release(); // Replica is held by as an intrusive pointer in @m_outPeer and is destroyed there.
if (m_entityFrom)
{
m_entityFrom->Deactivate();
m_entityFrom.reset();
}
if (m_entityTo)
{
m_entityTo->Deactivate();
m_entityTo.reset();
}
m_descTestComponentAutoChunk->ReleaseDescriptor();
m_app->Stop();
m_app.reset();
AZ::AllocatorInstance<GridMate::GridMateAllocatorMP>::Destroy();
AZ::AllocatorInstance<GridMate::GridMateAllocator>::Destroy();
UnitTest::AllocatorsTestFixture::TearDown();
}
AZStd::unique_ptr<AzFramework::Application> m_app;
AZStd::unique_ptr<AZ::Entity> m_entityFrom;
AZStd::unique_ptr<AZ::Entity> m_entityTo;
ComponentType* m_componentFrom = nullptr;
ComponentType* m_componentTo = nullptr;
AZ::ComponentDescriptor* m_descTestComponentAutoChunk = nullptr;
GridMate::ReplicaChunkPtr m_outReplicaChunk;
AZStd::unique_ptr<GridMate::Replica> m_outReplica;
AZStd::unique_ptr<GridMate::ReplicaManager> m_outManager;
AZStd::unique_ptr<GridMate::ReplicaPeer> m_outPeer;
};
using NetContextVector3 = NetContextMarshalFixture<TestComponent_OneField<AZ::Vector3>>;
TEST_F(NetContextVector3, SerializationTests)
{
const Vector3 value = AZ::Vector3::CreateAxisZ( 1.f );
m_componentFrom->m_field = value;
MarshalUnMarshal();
AZ_TEST_ASSERT(m_componentTo->m_field.Get() == value);
}
/*
* Now the same test but with NetBindable::BoundField<>
*/
template<typename FieldType>
class TestComponent_OneBoundField
: public AZ::Component
, public NetBindable
{
public:
AZ_COMPONENT(TestComponent_OneBoundField, "{2B283821-41DF-46BB-BE8E-66EF7301B62A}", AZ::Component, NetBindable);
using ThisComponentType = TestComponent_OneBoundField<FieldType>;
static void Reflect(ReflectContext* context)
{
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
{
serializeContext->Class<ThisComponentType, AZ::Component>()
->Field("Field", &ThisComponentType::m_boundField)
->Version(1);
}
if (NetworkContext* netContext = azrtti_cast<NetworkContext*>(context))
{
netContext->Class<ThisComponentType>()
->Field("Field", &ThisComponentType::m_boundField);
}
}
void Activate() override {}
void Deactivate() override {}
void OnBoundFieldChanged( const FieldType&, const GridMate::TimeContext& ) {}
BoundField<FieldType, ThisComponentType, &ThisComponentType::OnBoundFieldChanged> m_boundField;
};
using NetContextBoundVector2 = NetContextMarshalFixture<TestComponent_OneBoundField<AZ::Vector2>>;
TEST_F(NetContextBoundVector2, SerializationTests)
{
const Vector2 value = AZ::Vector2::CreateAxisX( 4.f );
m_componentFrom->m_boundField = value;
MarshalUnMarshal();
AZ_TEST_ASSERT(m_componentTo->m_boundField.Get() == value);
}
TEST_F(NetContextBoundVector2, Delete_Authoritative_Entity)
{
using ThisComponentType = TestComponent_OneBoundField<AZ::Vector2>;
AzFramework::NetworkContext* netContext = nullptr;
NetSystemRequestBus::BroadcastResult(netContext, &NetSystemRequestBus::Events::GetNetworkContext);
AZ_TEST_ASSERT(netContext);
ThisComponentType* testComponent = m_entityFrom->FindComponent<ThisComponentType>();
AZ_TEST_ASSERT(testComponent);
ReplicaChunkPtr chunk = testComponent->GetNetworkBinding();
// Testing early deletion of an entity on the server.
m_entityFrom->Deactivate();
m_entityFrom.reset();
// This test passes if it doesn't crash on cleanup.
chunk.reset();
}
template<typename FieldType>
class TestComponent_OneBoundField_ServerCallback
: public AZ::Component
, public NetBindable
{
public:
AZ_COMPONENT(TestComponent_OneBoundField_ServerCallback, "{74F5B232-0544-45CA-B207-9846052ED1AD}", AZ::Component, NetBindable);
using ThisComponentType = TestComponent_OneBoundField_ServerCallback<FieldType>;
static void Reflect(ReflectContext* context)
{
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
{
serializeContext->Class<ThisComponentType, AZ::Component>()
->Field("Field", &ThisComponentType::m_boundField)
->Version(1);
}
if (NetworkContext* netContext = azrtti_cast<NetworkContext*>(context))
{
netContext->Class<ThisComponentType>()
->Field("Field", &ThisComponentType::m_boundField);
}
}
void Activate() override {}
void Deactivate() override {}
void OnBoundFieldChanged( const FieldType&, const GridMate::TimeContext& )
{
++m_callbacksInvokeCount;
}
AZ::u8 m_callbacksInvokeCount = 0;
BoundField<FieldType, ThisComponentType, &ThisComponentType::OnBoundFieldChanged> m_boundField;
};
using NetContextBoundVector2WithCallbackCount = NetContextMarshalFixture<TestComponent_OneBoundField_ServerCallback<AZ::Vector2>>;
TEST_F(NetContextBoundVector2WithCallbackCount, BoundField_Invoke_OnServer_Test)
{
MarshalUnMarshal();
m_componentFrom->m_callbacksInvokeCount = 0; // resetting the count
const Vector2 value = AZ::Vector2::CreateAxisX( 4.f );
m_componentFrom->m_boundField = value;
AZ_TEST_ASSERT(m_componentFrom->m_callbacksInvokeCount == 1);
}
}
-552
View File
@@ -1,552 +0,0 @@
/*
* 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 "TestTypes.h"
#include <AzCore/Math/Random.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzFramework/Network/EntityIdMarshaler.h>
#include <AzFramework/Network/DynamicSerializableFieldMarshaler.h>
#include <GridMate/Serialize/Buffer.h>
namespace UnitTest
{
template<class T>
class MarshalerTester
: public AllocatorsFixture
{
public:
MarshalerTester()
: m_writeBuffer(GridMate::EndianType::BigEndian)
, m_readBuffer(GridMate::EndianType::BigEndian)
{
}
void SetUp() override
{
AllocatorsFixture::SetUp();
m_random.SetSeed(AZStd::chrono::milliseconds().count());
}
void PopulateReadBuffer()
{
m_readBuffer = GridMate::ReadBuffer(m_writeBuffer.GetEndianType(), m_writeBuffer.Get(), m_writeBuffer.Size());
}
AZ::SimpleLcgRandom m_random;
GridMate::Marshaler<T> m_marshaler;
GridMate::WriteBufferStatic<> m_writeBuffer;
GridMate::ReadBuffer m_readBuffer;
};
// EntityIdMarshalerTest
typedef MarshalerTester<AZ::EntityId> EntityIdMarshalerTest;
TEST_F(EntityIdMarshalerTest, SingleMarshalUnmarshalTest_EquivalentEmptyValue)
{
AZ::EntityId initialId;
m_marshaler.Marshal(m_writeBuffer, initialId);
PopulateReadBuffer();
AZ::EntityId receivedId;
m_marshaler.Unmarshal(receivedId, m_readBuffer);
EXPECT_EQ(initialId,receivedId);
EXPECT_FALSE(receivedId.IsValid());
}
TEST_F(EntityIdMarshalerTest, SingleMarshalUnmarshalTest_EquivalentRandomValue)
{
AZ::EntityId initialId = AZ::EntityId(m_random.GetRandom());
m_marshaler.Marshal(m_writeBuffer, initialId);
PopulateReadBuffer();
AZ::EntityId receivedId;
m_marshaler.Unmarshal(receivedId, m_readBuffer);
EXPECT_EQ(initialId,receivedId);
}
TEST_F(EntityIdMarshalerTest, MultipleMarshalUnmarshalTest_EquivalentEmptyRandomEmptyRandomValueChain)
{
AZ::EntityId sentId1_empty;
AZ::EntityId sentId2_random = AZ::EntityId(m_random.GetRandom());
AZ::EntityId sentId3_empty;
AZ::EntityId sentId4_random = AZ::EntityId(m_random.GetRandom());
m_marshaler.Marshal(m_writeBuffer, sentId1_empty);
m_marshaler.Marshal(m_writeBuffer, sentId2_random);
m_marshaler.Marshal(m_writeBuffer, sentId3_empty);
m_marshaler.Marshal(m_writeBuffer, sentId4_random);
PopulateReadBuffer();
AZ::EntityId receivedId1_empty;
AZ::EntityId receivedId2_random;
AZ::EntityId receivedId3_empty;
AZ::EntityId receivedId4_random;
m_marshaler.Unmarshal(receivedId1_empty, m_readBuffer);
m_marshaler.Unmarshal(receivedId2_random, m_readBuffer);
m_marshaler.Unmarshal(receivedId3_empty, m_readBuffer);
m_marshaler.Unmarshal(receivedId4_random, m_readBuffer);
EXPECT_EQ(sentId1_empty, receivedId1_empty);
EXPECT_EQ(sentId2_random, receivedId2_random);
EXPECT_EQ(sentId3_empty, receivedId3_empty);
EXPECT_EQ(sentId4_random, receivedId4_random);
}
// AZ::DynamicSerializableFieldMarshaler
class FooSerializable
{
public:
AZ_RTTI(FooSerializable, "{A60F0B2B-6085-4FF1-BD17-A0B0143BB03D}");
AZ_CLASS_ALLOCATOR(FooSerializable, AZ::SystemAllocator,0);
static void Reflect(AZ::SerializeContext& serializeContext)
{
serializeContext.Class<FooSerializable>()
->Version(1)
->Field("IntValue", &FooSerializable::m_intValue)
->Field("FloatValue", &FooSerializable::m_floatValue)
;
}
FooSerializable()
: m_intValue(0)
, m_floatValue(0.0f)
{
}
bool operator==(const FooSerializable& other) const
{
return m_intValue == other.m_intValue && AZ::IsClose(m_floatValue, other.m_floatValue,0.0001f);
}
AZ::u32 m_intValue;
float m_floatValue;
};
class BarSerializable
{
public:
AZ_RTTI(BarSerializable, "{2389C23F-D247-420B-A385-71AB8455CD2E}");
AZ_CLASS_ALLOCATOR(BarSerializable, AZ::SystemAllocator,0);
static void Reflect(AZ::SerializeContext& serializeContext)
{
serializeContext.Class<BarSerializable>()
->Version(1)
->Field("LongValue", &BarSerializable::m_longValue)
->Field("DoubleValue", &BarSerializable::m_doubleValue)
;
}
BarSerializable()
: m_longValue(0)
, m_doubleValue(0.0)
{
}
bool operator==(const BarSerializable& other) const
{
return m_longValue == other.m_longValue && AZ::IsClose(m_doubleValue,other.m_doubleValue,0.0001);
}
long m_longValue;
double m_doubleValue;
};
class ComplexSerializable
{
public:
AZ_RTTI(ComplexSerializable,"{055CB45C-702C-499F-8221-E9ABB21CF1D4}");
AZ_CLASS_ALLOCATOR(ComplexSerializable, AZ::SystemAllocator,0);
static void Reflect(AZ::SerializeContext& serializeContext)
{
serializeContext.Class<ComplexSerializable>()
->Version(1)
->Field("FooSerializable",&ComplexSerializable::m_fooField)
->Field("BarSerializable",&ComplexSerializable::m_barField)
;
}
bool operator==(const ComplexSerializable& other) const
{
return m_fooField == other.m_fooField && m_barField == other.m_barField;
}
FooSerializable m_fooField;
BarSerializable m_barField;
};
class DynamicSerializableFieldMarshalerTest
: public MarshalerTester<AZ::DynamicSerializableField>
, public AZ::ComponentApplicationBus::Handler
{
public:
DynamicSerializableFieldMarshalerTest()
: MarshalerTester<AZ::DynamicSerializableField>()
{
}
void SetUp() override
{
MarshalerTester<AZ::DynamicSerializableField>::SetUp();
FooSerializable::Reflect(m_serializeContext);
BarSerializable::Reflect(m_serializeContext);
ComplexSerializable::Reflect(m_serializeContext);
// Create the Marshaler with access to our custom serialize context.
m_marshaler = GridMate::Marshaler<AZ::DynamicSerializableField>(&m_serializeContext);
AZ::ComponentApplicationBus::Handler::BusConnect();
}
void TearDown() override
{
MarshalerTester<AZ::DynamicSerializableField>::TearDown();
AZ::ComponentApplicationBus::Handler::BusDisconnect();
}
FooSerializable* GenerateFooSerializable()
{
FooSerializable* field = new FooSerializable();
RandomizeFooSerializable((*field));
return field;
}
void RandomizeFooSerializable(FooSerializable& serializable)
{
serializable.m_intValue = m_random.GetRandom();
serializable.m_floatValue = m_random.GetRandomFloat();
}
BarSerializable* GenerateBarSerializable()
{
BarSerializable* field = new BarSerializable();
return field;
}
void RandomizeBarSerializable(BarSerializable& serializable)
{
serializable.m_longValue = static_cast<long>(m_random.GetRandom());
serializable.m_doubleValue = static_cast<double>(m_random.GetRandomFloat());
}
ComplexSerializable* GenerateComplexSerializable()
{
ComplexSerializable* complexField = new ComplexSerializable();
RandomizeFooSerializable(complexField->m_fooField);
RandomizeBarSerializable(complexField->m_barField);
return complexField;
}
// Used Component Application Methods
AZ::SerializeContext* GetSerializeContext() { return &m_serializeContext; }
// Unused ComponentApplication methods
void RegisterComponentDescriptor(const AZ::ComponentDescriptor* descriptor) override { (void)descriptor; AZ_Assert(false,"Unsupported method in Unit Test"); }
void UnregisterComponentDescriptor(const AZ::ComponentDescriptor* descriptor) override { (void)descriptor; AZ_Assert(false,"Unsupported method in Unit Test"); }
AZ::ComponentApplication* GetApplication() override { AZ_Assert(false,"Unsupported method in Unit Test"); return nullptr; }
bool AddEntity(AZ::Entity* entity) override { (void)entity; AZ_Assert(false,"Unsupported method in Unit Test"); return false; }
bool RemoveEntity(AZ::Entity* entity) override { (void)entity; AZ_Assert(false,"Unsupported method in Unit Test"); return false; }
bool DeleteEntity(const AZ::EntityId& id) override { (void)id; AZ_Assert(false,"Unsupported method in Unit Test"); return false; }
AZ::Entity* FindEntity(const AZ::EntityId& id) override { (void)id; AZ_Assert(false,"Unsupported method in Unit Test"); return nullptr; }
void EnumerateEntities(const EntityCallback& callback) override { (void)callback; AZ_Assert(false,"Unsupported method in Unit Test"); }
AZ::BehaviorContext* GetBehaviorContext() override { AZ_Assert(false,"Unsupported method in Unit Test"); return nullptr; }
const char* GetAppRoot() override { AZ_Assert(false,"Unsupported method in Unit Test"); return nullptr; }
const char* GetExecutableFolder() override { AZ_Assert(false,"Unsupported method in Unit Test"); return nullptr; }
AZ::Debug::DrillerManager* GetDrillerManager() override { AZ_Assert(false,"Unsupported method in Unit Test"); return nullptr; }
void ReloadModule(const char* moduleFullPath) override { (void)moduleFullPath; AZ_Assert(false,"Unsupported method in Unit Test"); }
AZ::SerializeContext m_serializeContext;
};
TEST_F(DynamicSerializableFieldMarshalerTest, SingleMarshalUnmarshalTest_EquivalentEmptyValue)
{
AZ::DynamicSerializableField sentField;
m_marshaler.Marshal(m_writeBuffer, sentField);
PopulateReadBuffer();
AZ::DynamicSerializableField receivedField;
m_marshaler.Unmarshal(receivedField,m_readBuffer);
EXPECT_TRUE(sentField.IsEqualTo(receivedField, &m_serializeContext));
}
TEST_F(DynamicSerializableFieldMarshalerTest, SingleMarshalUnmarshalTest_EquivalentFooValue)
{
AZ::DynamicSerializableField sentField;
FooSerializable* fooSerializable = GenerateFooSerializable();
sentField.Set(fooSerializable);
m_marshaler.Marshal(m_writeBuffer, sentField);
PopulateReadBuffer();
AZ::DynamicSerializableField receivedField;
m_marshaler.Unmarshal(receivedField,m_readBuffer);
EXPECT_TRUE(sentField.IsEqualTo(receivedField, &m_serializeContext));
sentField.DestroyData(&m_serializeContext);
receivedField.DestroyData(&m_serializeContext);
}
TEST_F(DynamicSerializableFieldMarshalerTest, SingleMarshalUnmarshalTest_EquivalentBarValue)
{
AZ::DynamicSerializableField sentField;
BarSerializable* barSerializable = GenerateBarSerializable();
sentField.Set(barSerializable);
m_marshaler.Marshal(m_writeBuffer, sentField);
PopulateReadBuffer();
AZ::DynamicSerializableField receivedField;
m_marshaler.Unmarshal(receivedField,m_readBuffer);
EXPECT_TRUE(sentField.IsEqualTo(receivedField, &m_serializeContext));
sentField.DestroyData(&m_serializeContext);
receivedField.DestroyData(&m_serializeContext);
}
TEST_F(DynamicSerializableFieldMarshalerTest, SingleMarshalUnmarshalTest_EquivalentComplexValue)
{
AZ::DynamicSerializableField sentField;
ComplexSerializable* complexSerializable = GenerateComplexSerializable();
sentField.Set(complexSerializable);
m_marshaler.Marshal(m_writeBuffer, sentField);
PopulateReadBuffer();
AZ::DynamicSerializableField receivedField;
m_marshaler.Unmarshal(receivedField,m_readBuffer);
EXPECT_TRUE(sentField.IsEqualTo(receivedField, &m_serializeContext));
sentField.DestroyData(&m_serializeContext);
receivedField.DestroyData(&m_serializeContext);
}
TEST_F(DynamicSerializableFieldMarshalerTest, MultipleMarshalUnmarshalTest_EmptyEmptyChainEquivalentValue)
{
AZ::DynamicSerializableField sentField1;
AZ::DynamicSerializableField sentField2;
m_marshaler.Marshal(m_writeBuffer, sentField1);
m_marshaler.Marshal(m_writeBuffer, sentField2);
PopulateReadBuffer();
AZ::DynamicSerializableField receivedField1;
AZ::DynamicSerializableField receivedField2;
m_marshaler.Unmarshal(receivedField1,m_readBuffer);
m_marshaler.Unmarshal(receivedField2,m_readBuffer);
EXPECT_TRUE(sentField1.IsEqualTo(receivedField1, &m_serializeContext));
EXPECT_TRUE(sentField2.IsEqualTo(receivedField2, &m_serializeContext));
sentField1.DestroyData(&m_serializeContext);
sentField2.DestroyData(&m_serializeContext);
receivedField1.DestroyData(&m_serializeContext);
receivedField2.DestroyData(&m_serializeContext);
}
TEST_F(DynamicSerializableFieldMarshalerTest, MultipleMarshalUnmarshalTest_FooBarComplexChainEquivalentValue)
{
AZ::DynamicSerializableField sentField1;
FooSerializable* fooSerializable = GenerateFooSerializable();
sentField1.Set(fooSerializable);
AZ::DynamicSerializableField sentField2;
BarSerializable* barSerializable = GenerateBarSerializable();
sentField2.Set(barSerializable);
AZ::DynamicSerializableField sentField3;
ComplexSerializable* complexSerializable = GenerateComplexSerializable();
sentField3.Set(complexSerializable);
m_marshaler.Marshal(m_writeBuffer, sentField1);
m_marshaler.Marshal(m_writeBuffer, sentField2);
m_marshaler.Marshal(m_writeBuffer, sentField3);
PopulateReadBuffer();
AZ::DynamicSerializableField receivedField1;
AZ::DynamicSerializableField receivedField2;
AZ::DynamicSerializableField receivedField3;
m_marshaler.Unmarshal(receivedField1, m_readBuffer);
m_marshaler.Unmarshal(receivedField2, m_readBuffer);
m_marshaler.Unmarshal(receivedField3, m_readBuffer);
EXPECT_TRUE(sentField1.IsEqualTo(receivedField1, &m_serializeContext));
EXPECT_TRUE(sentField2.IsEqualTo(receivedField2, &m_serializeContext));
EXPECT_TRUE(sentField3.IsEqualTo(receivedField3, &m_serializeContext));
sentField1.DestroyData(&m_serializeContext);
sentField2.DestroyData(&m_serializeContext);
sentField3.DestroyData(&m_serializeContext);
receivedField1.DestroyData(&m_serializeContext);
receivedField2.DestroyData(&m_serializeContext);
receivedField3.DestroyData(&m_serializeContext);
}
TEST_F(DynamicSerializableFieldMarshalerTest, MultipleMarshalUnmarshalTest_EmptyFooEmptyBarEmptyComplexChainEquivalentValue)
{
AZ::DynamicSerializableField emptyField;
AZ::DynamicSerializableField sentField1;
FooSerializable* fooSerializable = GenerateFooSerializable();
sentField1.Set(fooSerializable);
AZ::DynamicSerializableField sentField2;
BarSerializable* barSerializable = GenerateBarSerializable();
sentField2.Set(barSerializable);
AZ::DynamicSerializableField sentField3;
ComplexSerializable* complexSerializable = GenerateComplexSerializable();
sentField3.Set(complexSerializable);
m_marshaler.Marshal(m_writeBuffer, emptyField);
m_marshaler.Marshal(m_writeBuffer, sentField1);
m_marshaler.Marshal(m_writeBuffer, emptyField);
m_marshaler.Marshal(m_writeBuffer, sentField2);
m_marshaler.Marshal(m_writeBuffer, emptyField);
m_marshaler.Marshal(m_writeBuffer, sentField3);
m_marshaler.Marshal(m_writeBuffer, emptyField);
PopulateReadBuffer();
AZ::DynamicSerializableField receivedEmptyField1;
AZ::DynamicSerializableField receivedField1;
AZ::DynamicSerializableField receivedEmptyField2;
AZ::DynamicSerializableField receivedField2;
AZ::DynamicSerializableField receivedEmptyField3;
AZ::DynamicSerializableField receivedField3;
AZ::DynamicSerializableField receivedEmptyField4;
m_marshaler.Unmarshal(receivedEmptyField1, m_readBuffer);
m_marshaler.Unmarshal(receivedField1, m_readBuffer);
m_marshaler.Unmarshal(receivedEmptyField2, m_readBuffer);
m_marshaler.Unmarshal(receivedField2, m_readBuffer);
m_marshaler.Unmarshal(receivedEmptyField3, m_readBuffer);
m_marshaler.Unmarshal(receivedField3, m_readBuffer);
m_marshaler.Unmarshal(receivedEmptyField4, m_readBuffer);
EXPECT_TRUE(emptyField.IsEqualTo(receivedEmptyField1, &m_serializeContext));
EXPECT_TRUE(sentField1.IsEqualTo(receivedField1, &m_serializeContext));
EXPECT_TRUE(emptyField.IsEqualTo(receivedEmptyField2, &m_serializeContext));
EXPECT_TRUE(sentField2.IsEqualTo(receivedField2, &m_serializeContext));
EXPECT_TRUE(emptyField.IsEqualTo(receivedEmptyField3, &m_serializeContext));
EXPECT_TRUE(sentField3.IsEqualTo(receivedField3, &m_serializeContext));
EXPECT_TRUE(emptyField.IsEqualTo(receivedEmptyField4, &m_serializeContext));
emptyField.DestroyData(&m_serializeContext);
sentField1.DestroyData(&m_serializeContext);
sentField2.DestroyData(&m_serializeContext);
sentField3.DestroyData(&m_serializeContext);
receivedEmptyField1.DestroyData(&m_serializeContext);
receivedField1.DestroyData(&m_serializeContext);
receivedEmptyField2.DestroyData(&m_serializeContext);
receivedField2.DestroyData(&m_serializeContext);
receivedEmptyField3.DestroyData(&m_serializeContext);
receivedField3.DestroyData(&m_serializeContext);
receivedEmptyField4.DestroyData(&m_serializeContext);
}
TEST_F(DynamicSerializableFieldMarshalerTest, MultipleMarshalUnmarshalTest_RandomChainEquivalentValue)
{
// Need to watch out for the size of the WriteBuffer. It's about ~2048 bytes, at worst case here, I'll write ~100 Bytes to the field per test object)
// So I need to keep this ~20 elements.
int numValues = 5 + m_random.GetRandom()%10;
AZStd::vector< AZ::DynamicSerializableField > sentValues;
AZStd::vector< AZ::DynamicSerializableField > receivedValues;
sentValues.resize(numValues);
receivedValues.resize(numValues);
for (auto& currentField : sentValues)
{
int value = m_random.GetRandom() % 4;
switch (value)
{
case 0:
{
currentField.Set(GenerateFooSerializable());
}
break;
case 1:
{
currentField.Set(GenerateBarSerializable());
}
break;
case 2:
{
currentField.Set(GenerateComplexSerializable());
}
break;
case 3:
default:
// Empty field
break;
}
}
for (auto& currentField : sentValues)
{
m_marshaler.Marshal(m_writeBuffer,currentField);
}
PopulateReadBuffer();
for (auto& currentField : receivedValues)
{
m_marshaler.Unmarshal(currentField,m_readBuffer);
}
for (unsigned int i=0; i < sentValues.size(); ++i)
{
AZ::DynamicSerializableField& sentField = sentValues[i];
AZ::DynamicSerializableField& receivedField = receivedValues[i];
EXPECT_TRUE(sentField.IsEqualTo(receivedField, &m_serializeContext));
sentField.DestroyData(&m_serializeContext);
receivedField.DestroyData(&m_serializeContext);
}
}
}
@@ -17,7 +17,6 @@
#include <AzCore/Script/ScriptContext.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzToolsFramework/ToolsComponents/ScriptEditorComponent.h>
#include <AzFramework/Script/ScriptNetBindings.h>
#include "EntityTestbed.h"
@@ -63,8 +62,6 @@ namespace UnitTest
EBUS_EVENT_RESULT(m_behaviorContext, AZ::ComponentApplicationBus, GetBehaviorContext);
EBUS_EVENT_RESULT(m_serializeContext, AZ::ComponentApplicationBus, GetSerializeContext);
NetBindable::Reflect(m_serializeContext);
AzToolsFramework::Components::ScriptEditorComponent::CreateDescriptor(); // descriptor is deleted by app
AzToolsFramework::Components::ScriptEditorComponent::Reflect(m_serializeContext);
@@ -228,63 +225,4 @@ namespace UnitTest
EXPECT_NE(scriptComponent->GetScriptProperty("myNum"), nullptr);
}
TEST_F(ScriptComponentTest, UpdateNetSynchedProperty)
{
// Make sure altering a netsynched property in script only affects the single entity instance
const AZStd::string script = "local test = {\
Properties = {\
myNetSynchedNum = { default = 41, netSynched ={} },\
doUpdate = { default = false },\
},\
}\
function test:OnActivate()\
self.tickBusHandler = TickBus.Connect(self, self.entityId)\
end\
function test:OnDeactivate()\
self.tickBusHandler:Disconnect()\
end\
function test:OnTick(deltaTime, timePoint)\
if self.Properties.doUpdate then\
self.Properties.myNetSynchedNum = self.Properties.myNetSynchedNum+1\
end\
end\
return test";
const Data::Asset<ScriptAsset> scriptAsset = CreateAndLoadScriptAsset(script);
Entity entity1, entity2;
ScriptComponent* scriptComponentInstance1 = BuildGameEntity(scriptAsset, entity1);
ScriptComponent* scriptComponentInstance2 = BuildGameEntity(scriptAsset, entity2);
// Change the value of entity1's doUpdate to true.
// This way entity1's myNetSynchedNum should be incremented during OnTick
auto* doUpdateScriptProperty = azrtti_cast<ScriptPropertyBoolean*>(scriptComponentInstance1->GetScriptProperty("doUpdate"));
ASSERT_NE(doUpdateScriptProperty, nullptr);
doUpdateScriptProperty->m_value = true;
entity1.Init();
entity2.Init();
entity1.Activate();
entity2.Activate();
// Tick in order to call OnTick in our lua script.
m_app.Tick();
m_app.TickSystem();
// Ensure Entity1's myNetSynchedNum updated, but not Entity2
auto* netSynchedProperty1 = scriptComponentInstance1->GetNetworkedScriptProperty("myNetSynchedNum");
auto* netSynchedProperty2 = scriptComponentInstance2->GetNetworkedScriptProperty("myNetSynchedNum");
ASSERT_NE(netSynchedProperty1, nullptr);
ASSERT_NE(netSynchedProperty2, nullptr);
auto* num1 = azrtti_cast<const ScriptPropertyNumber*>(netSynchedProperty1);
auto* num2 = azrtti_cast<const ScriptPropertyNumber*>(netSynchedProperty2);
ASSERT_NE(num1, nullptr);
ASSERT_NE(num2, nullptr);
EXPECT_EQ(num1->m_value, 42);
EXPECT_EQ(num2->m_value, 41);
}
} // namespace UnitTest
@@ -955,37 +955,6 @@ namespace UnitTest
const char* m_objectStreamBuffer = nullptr;
};
class TransformComponentConvertFromV2
: public TransformComponentVersionConverter
{
public:
TransformComponentConvertFromV2()
{
m_objectStreamBuffer =
R"DELIMITER(<ObjectStream version="1">
<Class name="TransformComponent" field="element" version="2" type="{22B10178-39B6-4C12-BB37-77DB45FDD3B6}">
<Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
<Class name="AZ::u64" field="Id" value="18023671824091307142" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
<Class name="NetBindable" field="BaseClass2" type="{80206665-D429-4703-B42E-94434F82F381}">
<Class name="bool" field="m_isSyncEnabled" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
</Class>
<Class name="EntityId" field="Parent" version="1" type="{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}">
<Class name="AZ::u64" field="id" value="4294967295" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
<Class name="Transform" field="Transform" value="1.0000000 0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000" type="{5D9958E9-9F1E-4985-B532-FFFDE75FEDFD}"/>
<Class name="Transform" field="LocalTransform" value="1.0000000 0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000" type="{5D9958E9-9F1E-4985-B532-FFFDE75FEDFD}"/>
<Class name="unsigned int" field="ParentActivationTransformMode" value="0" type="{43DA906B-7DEF-4CA8-9790-854106D3F983}"/>
</Class>
</ObjectStream>)DELIMITER";
}
};
TEST_F(TransformComponentConvertFromV2, IsStatic_False)
{
EXPECT_FALSE(m_transformInterface->IsStaticTransform());
}
///////////////////////////////////////////////////////////////////////////
// TransformConfig
@@ -26,8 +26,6 @@ set(FILES
GenAppDescriptors.cpp
GenericComponentWrapperTest.cpp
InstanceDataHierarchy.cpp
NetBinding.cpp
NetworkContext.cpp
OctreePerformanceTests.cpp
OctreeTests.cpp
Slices.cpp
@@ -35,12 +33,8 @@ set(FILES
Script/ScriptEntityTests.cpp
AssetCatalog.cpp
AssetProcessorConnection.cpp
NetBindingSystemImplTest.cpp
NetBindingMocks.h
NativeWindow.cpp
TransformComponent.cpp
GridMocks.h
InterestManagerComponentTests.cpp
SQLiteConnectionTests.cpp
ProcessLaunchParseTests.cpp
Application.cpp
@@ -1,448 +0,0 @@
/*
* 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 "EMotionFX_precompiled.h"
#include <Integration/Components/AnimGraphNetSyncComponent.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <GridMate/Replica/ReplicaFunctions.h>
#include <GridMate/Serialize/MathMarshal.h>
#include <AzFramework/Network/NetBindingHandlerBus.h>
namespace EMotionFX
{
namespace Integration
{
namespace Network
{
/**
* \brief This is a GridMate chunk that replicates Anim Graph parameters.
* It's challenge is to replicate any of the supported parameter types where
* the types are only known at runtime. To solve that, many datasets are created
* with helper macros to avoid code duplication (@PARAM_DATASET and @PARAM_DATASET_NAME).
*
* For maximum compression, one should build a custom component that specifies the anim graph parameters by hand, for example:
*
* DataSet<float> m_param0;
*
* or if using delta compression feature of GridMate:
*
* DeltaCompressedDataSet<float, 1> m_param1;
*
* Active nodes (@m_activeNodes) change infrequently.
*
* Warning: @m_motionNodes motion nodes often do change frequently as their motion play time ticks down.
* Care must be applied when aiming for the network budget of a project.
*/
class AnimGraphNetSyncComponent::Chunk : public GridMate::ReplicaChunkBase
{
public:
GM_CLASS_ALLOCATOR(Chunk);
Chunk() : m_activeNodes("Active Nodes", NodeIndexContainer{}), m_motionNodes("Motion Nodes", MotionNodePlaytimeContainer{}) {}
static const char* GetChunkName() { return "AnimGraphNetSyncComponent::Chunk"; }
bool IsReplicaMigratable() override { return true; }
using AnimDataSetType = GridMate::DataSet<AnimParameter, AnimParameterMarshaler, AnimParameterThrottler>;
template <void (AnimGraphNetSyncComponent::* CallbackMethod)(const AnimParameter&, const GridMate::TimeContext&)>
using AnimDataSet = AnimDataSetType::BindInterface<AnimGraphNetSyncComponent, CallbackMethod>;
// A helper macro that creates a variable like this one:
// AnimDataSet<&AnimGraphNetSyncComponent::OnAnimParameterChanged<0>> m_parameter0 = { "Param 0" };
#define PARAM_DATASET( N ) AnimDataSet<&AnimGraphNetSyncComponent::OnAnimParameterChanged< N >> m_parameter##N = { "Param " #N }
PARAM_DATASET(0);
PARAM_DATASET(1);
PARAM_DATASET(2);
PARAM_DATASET(3);
PARAM_DATASET(4);
PARAM_DATASET(5);
PARAM_DATASET(6);
PARAM_DATASET(7);
PARAM_DATASET(8);
PARAM_DATASET(9);
/*
* Note: GridMate by default supports up to 32 DataSets per ReplicaChunk: @GM_MAX_DATASETS_IN_CHUNK.
* That means that a component can sync up to 32 separate network fields. One can vary the number of supported number
* of parameters by simply creating new entries of @PARAM_DATASET above and @PARAM_DATASET_NAME below.
*/
// A collection of datasets that are used to synchronize anim graph parameters.
AZStd::array<AnimDataSetType*, 10> m_parameters = { { // clang pre-6.0 requires double "{{" here but doesn't perform compile length verification :(
&m_parameter0,
&m_parameter1,
&m_parameter2,
&m_parameter3,
&m_parameter4,
&m_parameter5,
&m_parameter6,
&m_parameter7,
&m_parameter8,
&m_parameter9,
} };
GridMate::DataSet<NodeIndexContainer, NodeIndexContainerMarshaler>::
BindInterface<AnimGraphNetSyncComponent, &AnimGraphNetSyncComponent::OnActiveNodesChanged> m_activeNodes;
GridMate::DataSet<MotionNodePlaytimeContainer, MotionNodePlaytimeContainerMarshaler>::
BindInterface<AnimGraphNetSyncComponent, &AnimGraphNetSyncComponent::OnMotionNodesChanged> m_motionNodes;
};
void AnimGraphNetSyncComponent::Reflect(AZ::ReflectContext* context)
{
GridMate::ReplicaChunkDescriptorTable& descTable = GridMate::ReplicaChunkDescriptorTable::Get();
if (!descTable.FindReplicaChunkDescriptor(GridMate::ReplicaChunkClassId(Chunk::GetChunkName())))
{
descTable.RegisterChunkType<Chunk>();
}
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<AnimGraphNetSyncComponent, AZ::Component>()
->Version(1)
->Field( "Sync parameters", &AnimGraphNetSyncComponent::m_syncParameters )
->Field( "Sync active nodes", &AnimGraphNetSyncComponent::m_syncActiveNodes )
->Field( "Sync motion nodes", &AnimGraphNetSyncComponent::m_syncMotionNodes )
;
AZ::EditContext* editContent = serializeContext->GetEditContext();
if (editContent)
{
editContent->Class<AnimGraphNetSyncComponent>("Anim Graph Net Sync",
"Replicates anim graph parameters over the network using GridMate")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::Category, "Networking")
->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/AnimGraphNetSync.svg")
->DataElement( AZ::Edit::UIHandlers::Default, &AnimGraphNetSyncComponent::m_syncParameters, "Sync parameters",
"Synchronize parameters of the anim graph on the entity" )
->DataElement( AZ::Edit::UIHandlers::Default, &AnimGraphNetSyncComponent::m_syncActiveNodes, "Sync active nodes",
"Synchronize active nodes in the anim graph on the entity" )
->DataElement( AZ::Edit::UIHandlers::Default, &AnimGraphNetSyncComponent::m_syncMotionNodes, "Sync motion nodes",
"Synchronize motion nodes in the anim graph on the entity. Warning: this may take a significant amount of network bandwidth" )
;
}
}
}
void AnimGraphNetSyncComponent::Activate()
{
AnimGraphComponentNotificationBus::Handler::BusConnect(GetEntityId());
if (m_syncMotionNodes || m_syncActiveNodes) // if there is anything synchronize over the network
{
const bool isAuthoritative = AzFramework::NetQuery::IsEntityAuthoritative(GetEntityId());
if (isAuthoritative)
{
// Only the server (or authoritative entity) needs to watch the nodes values.
AZ::TickBus::Handler::BusConnect();
}
// We need to get anim graph instance. It will be either available to us now or later via a notification bus. See @OnAnimGraphInstanceCreated
AnimGraphComponentRequestBus::EventResult(m_instance, GetEntityId(), &AnimGraphComponentRequestBus::Events::GetAnimGraphInstance);
if (m_instance)
{
if (!m_instance->GetSnapshot())
{
m_instance->CreateSnapshot(isAuthoritative);
}
}
}
}
void AnimGraphNetSyncComponent::Deactivate()
{
AnimGraphComponentNotificationBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
}
void AnimGraphNetSyncComponent::SetParameterOnClient(const AnimParameter& value, AZ::u8 index)
{
switch (value.m_type)
{
case AnimParameter::Type::Unsupported:
break;
case AnimParameter::Type::Float:
AnimGraphComponentRequestBus::Event(GetEntityId(), &AnimGraphComponentRequestBus::Events::SetParameterFloat, index, value.m_value.f);
break;
case AnimParameter::Type::Bool:
AnimGraphComponentRequestBus::Event(GetEntityId(), &AnimGraphComponentRequestBus::Events::SetParameterBool, index, value.m_value.b);
break;
case AnimParameter::Type::Vector2:
AnimGraphComponentRequestBus::Event(GetEntityId(), &AnimGraphComponentRequestBus::Events::SetParameterVector2, index, value.m_value.v2);
break;
case AnimParameter::Type::Vector3:
AnimGraphComponentRequestBus::Event(GetEntityId(), &AnimGraphComponentRequestBus::Events::SetParameterVector3, index, value.m_value.v3);
break;
case AnimParameter::Type::Quaternion:
AnimGraphComponentRequestBus::Event(GetEntityId(), &AnimGraphComponentRequestBus::Events::SetParameterRotation, index, value.m_value.q);
break;
default:
AZ_Assert(false, "Unsupported type");
break;
}
}
template <AZ::u8 Index>
void AnimGraphNetSyncComponent::OnAnimParameterChanged(const AnimParameter& value, const GridMate::TimeContext&)
{
SetParameterOnClient(value, Index);
}
template <AnimParameter::Type AnimParameterType, typename FieldType>
void AnimGraphNetSyncComponent::SetParameterOnServer(AZ::u8 parameterIndex, const FieldType& newValue)
{
if (m_syncParameters)
{
if (Chunk* chunk = GetChunk())
{
if (parameterIndex < chunk->m_parameters.size())
{
AnimParameter param;
param.m_type = AnimParameterType;
static_assert(sizeof(FieldType) <= sizeof(param.m_value), "The largest value param.m_value can store is a Quaternion");
// This is to simplify writing a value into a union.
// Ideally, one would use std::variant (C++17) instead of a union.
memcpy(&param.m_value, &newValue, sizeof(FieldType));
chunk->m_parameters[parameterIndex]->Set(param);
}
else
{
AZ_Warning("EMotionFX", false, "AnimGraphNetSyncComponent does not support synchronizing more than %u parameters", chunk->m_parameters.size());
}
}
}
}
void AnimGraphNetSyncComponent::OnAnimGraphFloatParameterChanged(EMotionFX::AnimGraphInstance*,
AZ::u32 parameterIndex,
float beforeValue,
float afterValue)
{
AZ_UNUSED(beforeValue);
SetParameterOnServer<AnimParameter::Type::Float>(static_cast<AZ::u8>(parameterIndex), afterValue);
}
void AnimGraphNetSyncComponent::OnAnimGraphBoolParameterChanged(EMotionFX::AnimGraphInstance*,
AZ::u32 parameterIndex,
bool beforeValue,
bool afterValue)
{
AZ_UNUSED(beforeValue);
SetParameterOnServer<AnimParameter::Type::Bool>(static_cast<AZ::u8>(parameterIndex), afterValue);
}
void AnimGraphNetSyncComponent::OnAnimGraphStringParameterChanged(EMotionFX::AnimGraphInstance*,
AZ::u32 parameterIndex,
const char* beforeValue,
const char* afterValue)
{
AZ_UNUSED(parameterIndex);
AZ_UNUSED(beforeValue);
AZ_UNUSED(afterValue);
AZ_Warning("EMotionFX", false, "AnimGraphNetSync component does not supported synchronizing string parameters, please consider refactoring your anim graph to replace strings with integers or enum values.");
}
void AnimGraphNetSyncComponent::OnAnimGraphVector2ParameterChanged(EMotionFX::AnimGraphInstance*,
AZ::u32 parameterIndex,
const AZ::Vector2& beforeValue,
const AZ::Vector2& afterValue)
{
AZ_UNUSED(beforeValue);
SetParameterOnServer<AnimParameter::Type::Vector2>(static_cast<AZ::u8>(parameterIndex), afterValue);
}
void AnimGraphNetSyncComponent::OnAnimGraphVector3ParameterChanged(EMotionFX::AnimGraphInstance*,
AZ::u32 parameterIndex,
const AZ::Vector3& beforeValue,
const AZ::Vector3& afterValue)
{
AZ_UNUSED(beforeValue);
SetParameterOnServer<AnimParameter::Type::Vector3>(static_cast<AZ::u8>(parameterIndex), afterValue);
}
void AnimGraphNetSyncComponent::OnAnimGraphRotationParameterChanged(EMotionFX::AnimGraphInstance*,
AZ::u32 parameterIndex,
const AZ::Quaternion& beforeValue,
const AZ::Quaternion& afterValue)
{
AZ_UNUSED(beforeValue);
SetParameterOnServer<AnimParameter::Type::Quaternion>(static_cast<AZ::u8>(parameterIndex), afterValue);
}
void AnimGraphNetSyncComponent::OnActiveNodesChanged(const NodeIndexContainer& activeNodes, const GridMate::TimeContext& tc)
{
AZ_UNUSED(tc);
// Client receiving values
if (m_instance)
{
if (const AZStd::shared_ptr<AnimGraphSnapshot> snapshot = m_instance->GetSnapshot())
{
snapshot->SetActiveNodes(activeNodes);
}
}
}
void AnimGraphNetSyncComponent::OnMotionNodesChanged(const MotionNodePlaytimeContainer& motionNodes, const GridMate::TimeContext& tc)
{
AZ_UNUSED(tc);
// Client receiving values
if (m_instance)
{
if (const AZStd::shared_ptr<AnimGraphSnapshot> snapshot = m_instance->GetSnapshot())
{
snapshot->SetMotionNodePlaytimes(motionNodes);
}
}
}
bool AnimGraphNetSyncComponent::IsDifferent(const MotionNodePlaytimeContainer& oldList, const MotionNodePlaytimeContainer& newList) const
{
if (oldList.size() != newList.size())
{
return true;
}
AZStd::size_t i = 0;
for (auto& value : oldList)
{
if (value.first != newList[i].first || value.second != newList[i].second)
{
return true;
}
++i;
}
return false;
}
bool AnimGraphNetSyncComponent::IsDifferent(const NodeIndexContainer& oldList, const NodeIndexContainer& newList) const
{
if (oldList.size() != newList.size())
{
return true;
}
AZStd::size_t i = 0;
for (AZ::u32 value : oldList)
{
if (value != newList[i])
{
return true;
}
++i;
}
return false;
}
void AnimGraphNetSyncComponent::OnTick(float deltaTime, AZ::ScriptTimePoint time)
{
AZ_UNUSED(deltaTime);
AZ_UNUSED(time);
if (!GetChunk())
{
return; // network is not ready yet
}
if (m_instance)
{
if (const AZStd::shared_ptr<AnimGraphSnapshot> snapshot = m_instance->GetSnapshot())
{
if (m_syncActiveNodes)
{
const NodeIndexContainer& activeNodes = snapshot->GetActiveNodes();
const NodeIndexContainer& currentValue = GetChunk()->m_activeNodes.Get();
if (IsDifferent(currentValue, activeNodes))
{
GetChunk()->m_activeNodes.Set(activeNodes); // Server sending the values
}
}
if (m_syncMotionNodes)
{
const MotionNodePlaytimeContainer& playTimes = snapshot->GetMotionNodePlaytimes();
const MotionNodePlaytimeContainer& currentTimes = GetChunk()->m_motionNodes.Get();
if (IsDifferent(currentTimes, playTimes))
{
GetChunk()->m_motionNodes.Set(playTimes); // Server sending the values
}
}
}
}
}
void AnimGraphNetSyncComponent::OnAnimGraphInstanceCreated(EMotionFX::AnimGraphInstance* instance)
{
m_instance = instance;
if (m_instance)
{
const bool isAuthoritative = AzFramework::NetQuery::IsEntityAuthoritative(GetEntityId());
if (!m_instance->GetSnapshot())
{
m_instance->CreateSnapshot(isAuthoritative);
}
}
}
void AnimGraphNetSyncComponent::OnAnimGraphInstanceDestroyed(EMotionFX::AnimGraphInstance*)
{
m_instance = nullptr;
}
AnimGraphNetSyncComponent::Chunk* AnimGraphNetSyncComponent::GetChunk() const
{
return static_cast<Chunk*>(m_chunk.get());
}
GridMate::ReplicaChunkPtr AnimGraphNetSyncComponent::GetNetworkBinding()
{
m_chunk = GridMate::CreateReplicaChunk<Chunk>();
AZ_Assert(m_chunk, "Failed to create a chunk");
if (m_instance)
{
if (!m_instance->GetSnapshot())
{
m_instance->CreateSnapshot(true /* authoritative */);
}
}
return m_chunk;
}
void AnimGraphNetSyncComponent::SetNetworkBinding(GridMate::ReplicaChunkPtr chunk)
{
m_chunk = chunk;
m_chunk->SetHandler(this);
}
void AnimGraphNetSyncComponent::UnbindFromNetwork()
{
AZ_Assert(m_chunk, "There wasn't any chunk present");
if (m_chunk)
{
m_chunk->SetHandler(nullptr);
m_chunk = nullptr;
}
}
}
} // namespace Integration
} // namespace EMotionFXAnimation
@@ -1,153 +0,0 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/Component/TickBus.h>
#include <AzFramework/Network/NetBindable.h>
#include <Integration/AnimGraphComponentBus.h>
#include <Integration/Components/AnimGraphNetSyncTypes.h>
namespace EMotionFX
{
namespace Integration
{
namespace Network
{
/**
* \brief Generic solution for synchronizing parameters of Anim Graph component.
* Synchronization is done over GridMate.
*
* Note that this is not the most optimal synchronization but it does
* work for just about all Anim Graphs.
*
* Disclaimer: string parameters are not supported! Because one should not synchronize
* strings over the network. They ought to be converted to enum/int values beforehand.
*/
class AnimGraphNetSyncComponent
: public AZ::Component
, public AzFramework::NetBindable
, public AnimGraphComponentNotificationBus::Handler
, public AZ::TickBus::Handler
{
public:
AZ_COMPONENT(AnimGraphNetSyncComponent, "{2F9428C1-0F07-4667-B052-40D9BC473AD3}", NetBindable);
static void Reflect(AZ::ReflectContext* context);
// AZ::Component interface implementation
void Activate() override;
void Deactivate() override;
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("EMotionFXAnimGraphNetSyncService", 0x42e6f127));
}
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("EMotionFXAnimGraphNetSyncService", 0x42e6f127));
}
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("EMotionFXAnimGraphService", 0x9ec3c819));
required.push_back(AZ_CRC("ReplicaChunkService", 0xf86b88a8));
}
protected:
// NetBindable interface implementation
GridMate::ReplicaChunkPtr GetNetworkBinding() override;
void SetNetworkBinding(GridMate::ReplicaChunkPtr chunk) override;
void UnbindFromNetwork() override;
// AnimGraphComponentNotificationBus interface implementation
void OnAnimGraphFloatParameterChanged(EMotionFX::AnimGraphInstance*,
AZ::u32 parameterIndex,
float beforeValue,
float afterValue) override;
void OnAnimGraphBoolParameterChanged(EMotionFX::AnimGraphInstance*,
AZ::u32 parameterIndex,
bool beforeValue,
bool afterValue) override;
void OnAnimGraphStringParameterChanged(EMotionFX::AnimGraphInstance*,
AZ::u32 parameterIndex,
const char* beforeValue,
const char* afterValue) override;
void OnAnimGraphVector2ParameterChanged(EMotionFX::AnimGraphInstance*,
AZ::u32 parameterIndex,
const AZ::Vector2& beforeValue,
const AZ::Vector2& afterValue) override;
void OnAnimGraphVector3ParameterChanged(EMotionFX::AnimGraphInstance*,
AZ::u32 parameterIndex,
const AZ::Vector3& beforeValue,
const AZ::Vector3& afterValue) override;
void OnAnimGraphRotationParameterChanged(EMotionFX::AnimGraphInstance*,
AZ::u32 parameterIndex,
const AZ::Quaternion& beforeValue,
const AZ::Quaternion& afterValue) override;
// TickBus
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
// AnimGraphComponentNotificationBus
void OnAnimGraphInstanceCreated(EMotionFX::AnimGraphInstance* instance) override;
void OnAnimGraphInstanceDestroyed(EMotionFX::AnimGraphInstance* instance) override;
private:
class Chunk;
GridMate::ReplicaChunkPtr m_chunk;
Chunk* GetChunk() const;
// DataSet callback, it's a template to avoid duplicating similar callbacks
template <AZ::u8 Index>
void OnAnimParameterChanged(const AnimParameter& value, const GridMate::TimeContext& tc);
// Helper on a client side
void SetParameterOnClient(const AnimParameter& value, AZ::u8 index);
// Helper on the server side to avoid duplicating very similar callbacks
template <AnimParameter::Type AnimParameterType, typename FieldType>
void SetParameterOnServer(AZ::u8 parameterIndex, const FieldType& newValue);
/**
* \brief Optionally turn on or off replicating parameters of an anim graph on the same entity as this component.
*/
bool m_syncParameters = true;
/**
* \brief Optionally turn on or off replicating active nodes of an anim graph on the same entity as this component.
*/
bool m_syncActiveNodes = false;
/**
* \brief Optionally turn on or off replicating motion playtime nodes of an anim graph on the same entity as this component.
*
* It's off by default because these nodes are very frequently changing and would result in a high network bandwidth use.
*/
bool m_syncMotionNodes = false;
// GridMate DataSet callback on clients
void OnActiveNodesChanged(const NodeIndexContainer& activeNodes, const GridMate::TimeContext& tc);
// GridMate DataSet callback on clients
void OnMotionNodesChanged(const MotionNodePlaytimeContainer& motionNodes, const GridMate::TimeContext& tc);
// Helper comparison method to avoid sending the same data
bool IsDifferent(const NodeIndexContainer& oldList, const NodeIndexContainer& newList) const;
// Helper comparison method to avoid sending the same data
bool IsDifferent(const MotionNodePlaytimeContainer& oldList, const MotionNodePlaytimeContainer& newList) const;
EMotionFX::AnimGraphInstance* m_instance = nullptr;
};
}
} // namespace Integration
} // namespace EMotionFX
@@ -1,285 +0,0 @@
/*
* 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.
*
*/
#pragma once
#include <GridMate/Serialize/Buffer.h>
#include <GridMate/Serialize/MathMarshal.h>
#include <GridMate/Serialize/CompressionMarshal.h>
namespace EMotionFX
{
namespace Integration
{
namespace Network
{
/**
* \brief A general storage for an anim graph parameter.
*/
class AnimParameter
{
public:
/**
* \brief String type is not supported because one should not be syncing strings over the network.
*/
enum class Type : AZ::u8
{
Unsupported,
Float,
Bool,
Vector2,
Vector3,
Quaternion,
};
/**
* \brief A storage for all possible supported types in @AnimGraphNetSyncComponent
*/
union Value
{
Value()
{
q = AZ::Quaternion::CreateZero();
}
float f;
bool b = false;
AZ::Vector2 v2;
AZ::Vector3 v3;
AZ::Quaternion q;
};
AnimParameter() : m_type(Type::Unsupported) {}
Type m_type;
Value m_value;
AnimParameter(const AnimParameter& other)
{
m_type = other.m_type;
CopyValue(other);
}
AnimParameter& operator=(const AnimParameter& other)
{
m_type = other.m_type;
CopyValue(other);
return *this;
}
friend bool operator==(const AnimParameter& lhs, const AnimParameter& rhs)
{
if (lhs.m_type != rhs.m_type)
{
return false;
}
switch (lhs.m_type)
{
case Type::Float:
return lhs.m_value.f == rhs.m_value.f;
case Type::Bool:
return lhs.m_value.b == rhs.m_value.b;
case Type::Vector2:
return lhs.m_value.v2 == rhs.m_value.v2;
case Type::Vector3:
return lhs.m_value.v3 == rhs.m_value.v3;
case Type::Quaternion:
return lhs.m_value.q == rhs.m_value.q;
default:
return true;
}
}
private:
void CopyValue(const AnimParameter& other)
{
switch (m_type)
{
case Type::Float:
m_value.f = other.m_value.f;
break;
case Type::Bool:
m_value.b = other.m_value.b;
break;
case Type::Vector2:
m_value.v2 = other.m_value.v2;
break;
case Type::Vector3:
m_value.v3 = other.m_value.v3;
break;
case Type::Quaternion:
m_value.q = other.m_value.q;
break;
default:
break;
}
}
};
/**
* \brief Custom GridMate throttler. See GridMate:: @BasicThrottle
*/
class AnimParameterThrottler
{
public:
bool WithinThreshold(const AnimParameter& newValue) const
{
return m_baseline == newValue;
}
void UpdateBaseline(const AnimParameter& baseline)
{
m_baseline = baseline;
}
private:
AnimParameter m_baseline;
};
/**
* \brief A custom GridMate marshaler.
* 1 byte is spend on the type. And a variable number of bytes afterwards for the value.
*/
class AnimParameterMarshaler
{
public:
void Marshal(GridMate::WriteBuffer& wb, const AnimParameter& parameter)
{
wb.Write(AZ::u8(parameter.m_type));
switch (parameter.m_type)
{
case AnimParameter::Type::Float:
wb.Write(parameter.m_value.f);
break;
case AnimParameter::Type::Bool:
wb.Write(parameter.m_value.b);
break;
case AnimParameter::Type::Vector2:
wb.Write(parameter.m_value.v2);
break;
case AnimParameter::Type::Vector3:
wb.Write(parameter.m_value.v3);
break;
case AnimParameter::Type::Quaternion:
wb.Write(parameter.m_value.q);
break;
default:
// other types are not supported
break;
}
}
void Unmarshal(AnimParameter& parameter, GridMate::ReadBuffer& rb)
{
AZ::u8 type;
rb.Read(type);
parameter.m_type = static_cast<AnimParameter::Type>(type);
switch (parameter.m_type)
{
case AnimParameter::Type::Float:
rb.Read(parameter.m_value.f);
break;
case AnimParameter::Type::Bool:
rb.Read(parameter.m_value.b);
break;
case AnimParameter::Type::Vector2:
rb.Read(parameter.m_value.v2);
break;
case AnimParameter::Type::Vector3:
rb.Read(parameter.m_value.v3);
break;
case AnimParameter::Type::Quaternion:
rb.Read(parameter.m_value.q);
break;
default:
// other types are not supported
break;
}
}
};
/**
* \brief Custom marshaler for Animation node index that is used by Activate Nodes list
*/
struct NodeIndexContainerMarshaler
{
void Marshal(GridMate::WriteBuffer& wb, const NodeIndexContainer& source) const
{
GridMate::VlqU64Marshaler m64;
GridMate::VlqU32Marshaler m32;
m64.Marshal(wb, source.size()); // 1 byte most of the time (if the size is less than 127)
for (AZ::u32 item : source)
{
m32.Marshal(wb, item); // 1 byte most of the time (if the value is less than 127)
}
}
void Unmarshal(NodeIndexContainer& target, GridMate::ReadBuffer& rb) const
{
target.clear();
GridMate::VlqU64Marshaler m64;
GridMate::VlqU32Marshaler m32;
AZ::u64 arraySize;
m64.Unmarshal(arraySize, rb);
target.resize(arraySize);
for (AZ::u64 i = 0; i < arraySize; ++i)
{
m32.Unmarshal(target[i], rb);
}
}
};
/**
* \brief Custom marshaler for Animation motion node information that is used by motion node playtime list
*/
struct MotionNodePlaytimeContainerMarshaler
{
void Marshal(GridMate::WriteBuffer& wb, const MotionNodePlaytimeContainer& source) const
{
GridMate::VlqU64Marshaler m64;
GridMate::VlqU32Marshaler m32;
m64.Marshal(wb, source.size());
for (const AZStd::pair<AZ::u32, float>& item : source)
{
m32.Marshal(wb, item.first); // average of 1 byte
wb.Write(item.second); // 4 bytes
}
}
void Unmarshal(MotionNodePlaytimeContainer& target, GridMate::ReadBuffer& rb) const
{
target.clear();
GridMate::VlqU64Marshaler m64;
GridMate::VlqU32Marshaler m32;
AZ::u64 arraySize;
m64.Unmarshal(arraySize, rb);
target.resize(arraySize);
for (AZ::u64 i = 0; i < arraySize; ++i)
{
m32.Unmarshal(target[i].first, rb);
rb.Read(target[i].second);
}
}
};
}
} // namespace Integration
} // namespace EMotionFXAnimation
@@ -17,7 +17,6 @@
#include <Integration/Components/ActorComponent.h>
#include <Integration/Components/AnimAudioComponent.h>
#include <Integration/Components/AnimGraphComponent.h>
#include <Integration/Components/AnimGraphNetSyncComponent.h>
#include <Integration/Components/SimpleMotionComponent.h>
#include <Integration/Components/SimpleLODComponent.h>
#include <AzCore/Module/DynamicModuleHandle.h>
@@ -74,8 +73,6 @@ namespace EMotionFX
AnimGraphComponent::CreateDescriptor(),
SimpleMotionComponent::CreateDescriptor(),
SimpleLODComponent::CreateDescriptor(),
Network::AnimGraphNetSyncComponent::CreateDescriptor(),
#if defined(EMOTIONFXANIMATION_EDITOR)
// Pipeline components
@@ -31,9 +31,6 @@ set(FILES
Source/Integration/Components/ActorComponent.cpp
Source/Integration/Components/AnimAudioComponent.h
Source/Integration/Components/AnimAudioComponent.cpp
Source/Integration/Components/AnimGraphNetSyncComponent.h
Source/Integration/Components/AnimGraphNetSyncTypes.h
Source/Integration/Components/AnimGraphNetSyncComponent.cpp
Source/Integration/Components/AnimGraphComponent.h
Source/Integration/Components/AnimGraphComponent.cpp
Source/Integration/Components/SimpleMotionComponent.h
@@ -14,6 +14,7 @@
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Math/Aabb.h>
#include <AzFramework/Physics/WindBus.h>
+1
View File
@@ -15,6 +15,7 @@
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/Physics/Material.h>
#include <AzFramework/Physics/Common/PhysicsTypes.h>
@@ -19,11 +19,9 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/Utils.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/Network/NetBindingHandlerBus.h>
#include <ScriptCanvas/Execution/ExecutionBus.h>
#include <ScriptCanvas/Execution/ExecutionContext.h>
#include <ScriptCanvas/Execution/ExecutionState.h>
#include <ScriptCanvas/Variable/GraphVariableNetBindings.h>
#if !defined(_RELEASE) && !defined(PERFORMANCE_BUILD)
#define SCRIPT_CANVAS_RUNTIME_ASSET_CHECK
@@ -163,8 +161,6 @@ namespace ScriptCanvas
->Field("m_variableOverrides", &RuntimeComponent::m_variableOverrides)
;
}
GraphVariableNetBindingTable::Reflect(context);
}
void RuntimeComponent::SetVariableOverrides(const VariableData& overrideData)
@@ -15,8 +15,6 @@
#include <AzCore/Component/Component.h>
#include <AzCore/Component/EntityBus.h>
#include <AzFramework/Network/NetBindable.h>
#include <ScriptCanvas/Asset/RuntimeAsset.h>
#include <ScriptCanvas/Core/Core.h>
#include <ScriptCanvas/Core/ExecutionNotificationsBus.h>
@@ -41,11 +39,10 @@ namespace ScriptCanvas
//! This component should only be used at runtime
class RuntimeComponent
: public AZ::Component
, public AzFramework::NetBindable
, public AZ::EntityBus::Handler
{
public:
AZ_COMPONENT(RuntimeComponent, "{95BFD916-E832-4956-837D-525DE8384282}", NetBindable);
AZ_COMPONENT(RuntimeComponent, "{95BFD916-E832-4956-837D-525DE8384282}", AZ::Component);
static void Reflect(AZ::ReflectContext* context);
@@ -67,8 +64,6 @@ namespace ScriptCanvas
const VariableData& GetVariableOverrides() const;
void SetNetworkBinding(GridMate::ReplicaChunkPtr) {}
void SetVariableOverrides(const VariableData& overrideData);
protected:
@@ -103,8 +98,6 @@ namespace ScriptCanvas
void StopExecution();
void UnbindFromNetwork(void) {}
private:
AZ::Data::Asset<RuntimeAsset> m_runtimeAsset;
ExecutionStatePtr m_executionState;
@@ -1,245 +0,0 @@
/*
* 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 <ScriptCanvas/Variable/GraphVariableMarshal.h>
#include <ScriptCanvas/Variable/GraphVariable.h>
#include <ScriptCanvas/Variable/GraphVariableNetBindings.h>
#include <ScriptCanvas/Core/ModifiableDatumView.h>
#include <AzFramework/Network/EntityIdMarshaler.h>
#include <GridMate/Serialize/MathMarshal.h>
#include <GridMate/Serialize/UtilityMarshal.h>
#include <GridMate/Serialize/UuidMarshal.h>
namespace ScriptCanvas
{
void DatumMarshaler::SetNetBindingTable(GraphVariableNetBindingTable* netBindingTable)
{
m_graphVariableNetBindingTable = netBindingTable;
}
void DatumMarshaler::Marshal(GridMate::WriteBuffer& wb, const Datum* const & property) const
{
if (!property)
{
return;
}
GridMate::Marshaler<Data::eType> typeMarshaler;
const Data::eType& datumType = property->GetType().GetType();
typeMarshaler.Marshal(wb, datumType);
VariableId assetVariableId;
AZStd::unordered_map<VariableId, AZStd::pair<GraphVariable*, int>>& variableIdMap = m_graphVariableNetBindingTable->GetVariableIdMap();
for (AZStd::pair<VariableId, AZStd::pair<GraphVariable*, int>>& pair : variableIdMap)
{
AZStd::pair<GraphVariable*, int>& variableIndexPair = pair.second;
if (variableIndexPair.first->GetDatum() == property)
{
assetVariableId = m_graphVariableNetBindingTable->FindAssetVariableIdByRuntimeVariableId(pair.first);
break;
}
}
if (!assetVariableId.IsValid())
{
return;
}
GridMate::Marshaler<AZ::Uuid> uuidMarshaler;
uuidMarshaler.Marshal(wb, assetVariableId.GetDatumId());
AZStd::string uuidString = assetVariableId.m_id.ToString<AZStd::string>();
switch (datumType)
{
case Data::eType::AABB:
MarshalType<Data::AABBType>(wb, property);
break;
case Data::eType::Boolean:
MarshalType<Data::BooleanType>(wb, property);
break;
case Data::eType::Color:
MarshalType<Data::ColorType>(wb, property);
break;
case Data::eType::CRC:
MarshalType<Data::CRCType>(wb, property);
break;
case Data::eType::EntityID:
MarshalType<Data::EntityIDType>(wb, property);
break;
case Data::eType::Matrix3x3:
MarshalType<Data::Matrix3x3Type>(wb, property);
break;
case Data::eType::Matrix4x4:
MarshalType<Data::Matrix4x4Type>(wb, property);
break;
case Data::eType::NamedEntityID:
MarshalType<Data::NamedEntityIDType>(wb, property);
break;
case Data::eType::Number:
MarshalType<Data::NumberType>(wb, property);
break;
case Data::eType::OBB:
MarshalType<Data::OBBType>(wb, property);
break;
case Data::eType::Plane:
MarshalType<Data::PlaneType>(wb, property);
break;
case Data::eType::Quaternion:
MarshalType<Data::QuaternionType>(wb, property);
break;
case Data::eType::String:
MarshalType<Data::StringType>(wb, property);
break;
case Data::eType::Transform:
MarshalType<Data::TransformType>(wb, property);
break;
case Data::eType::Vector2:
MarshalType<Data::Vector2Type>(wb, property);
break;
case Data::eType::Vector3:
MarshalType<Data::Vector3Type>(wb, property);
break;
case Data::eType::Vector4:
MarshalType<Data::Vector4Type>(wb, property);
break;
default:
AZ_Warning("ScriptCanvasNetworking", false, "Marshal unsupported data type");
break;
}
}
bool DatumMarshaler::UnmarshalToPointer(const Datum*& target, GridMate::ReadBuffer& rb)
{
// :SCTODO: for some reason, this UnmarshalToPointer can get called before SetNetworkBinding is called
// (which is where we set m_graphVariableNetBindingTable). So we check for nullptr here just in case.
if (!m_graphVariableNetBindingTable)
{
return false;
}
ScriptCanvas::Data::eType datumType = Data::eType::Invalid;
GridMate::Marshaler<Data::eType> typeMarshaler;
typeMarshaler.Unmarshal(datumType, rb);
AZ::Uuid uuid;
GridMate::Marshaler<AZ::Uuid> uuidMarshaler;
uuidMarshaler.Unmarshal(uuid, rb);
VariableId runtimeVariableId = m_graphVariableNetBindingTable->FindRuntimeVariableIdByAssetVariableId(VariableId(uuid));
if (!runtimeVariableId.IsValid())
{
return false;
}
AZStd::string uuidString = runtimeVariableId.m_id.ToString<AZStd::string>();
AZStd::unordered_map<VariableId, AZStd::pair<GraphVariable*, int>>& m_variableIdMap = m_graphVariableNetBindingTable->GetVariableIdMap();
AZStd::pair<GraphVariable*, int>& variableIndexPair = m_variableIdMap[runtimeVariableId];
GraphVariable* graphVariable = variableIndexPair.first;
switch (datumType)
{
case Data::eType::AABB:
return UnmarshalType<Data::AABBType>(target, rb, graphVariable);
case Data::eType::Boolean:
return UnmarshalType<Data::BooleanType>(target, rb, graphVariable);
case Data::eType::Color:
return UnmarshalType<Data::ColorType>(target, rb, graphVariable);
case Data::eType::CRC:
return UnmarshalType<Data::CRCType>(target, rb, graphVariable);
case Data::eType::EntityID:
return UnmarshalType<Data::EntityIDType>(target, rb, graphVariable);
case Data::eType::Matrix3x3:
return UnmarshalType<Data::Matrix3x3Type>(target, rb, graphVariable);
case Data::eType::Matrix4x4:
return UnmarshalType<Data::Matrix4x4Type>(target, rb, graphVariable);
case Data::eType::NamedEntityID:
return UnmarshalType<Data::NamedEntityIDType>(target, rb, graphVariable);
case Data::eType::Number:
return UnmarshalType<Data::NumberType>(target, rb, graphVariable);
case Data::eType::OBB:
return UnmarshalType<Data::OBBType>(target, rb, graphVariable);
case Data::eType::Plane:
return UnmarshalType<Data::PlaneType>(target, rb, graphVariable);
case Data::eType::Quaternion:
return UnmarshalType<Data::QuaternionType>(target, rb, graphVariable);
case Data::eType::String:
return UnmarshalType<Data::StringType>(target, rb, graphVariable);
case Data::eType::Transform:
return UnmarshalType<Data::TransformType>(target, rb, graphVariable);
case Data::eType::Vector2:
return UnmarshalType<Data::Vector2Type>(target, rb, graphVariable);
case Data::eType::Vector3:
return UnmarshalType<Data::Vector3Type>(target, rb, graphVariable);
case Data::eType::Vector4:
return UnmarshalType<Data::Vector4Type>(target, rb, graphVariable);
default:
AZ_Warning("ScriptCanvasNetworking", false, "Unmarshal unsupported data type");
break;
}
return false;
}
void DatumThrottler::SignalDirty()
{
m_isDirty = true;
}
bool DatumThrottler::WithinThreshold(const Datum* newValue) const
{
return (newValue == nullptr || !m_isDirty);
}
void DatumThrottler::UpdateBaseline([[maybe_unused]] const Datum* baseline)
{
m_isDirty = false;
}
}
@@ -1,86 +0,0 @@
/*
* 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.
*
*/
#pragma once
#include <GridMate/Serialize/ContainerMarshal.h>
#include <ScriptCanvas/Core/Datum.h>
#include <ScriptCanvas/Core/ModifiableDatumView.h>
#include <ScriptCanvas/Variable/GraphVariable.h>
namespace ScriptCanvas
{
class GraphVariableNetBindingTable;
class DatumMarshaler
{
public:
void SetNetBindingTable(GraphVariableNetBindingTable* netBindingTable);
void Marshal(GridMate::WriteBuffer& wb, const Datum* const & cont) const;
bool UnmarshalToPointer(const Datum*& target, GridMate::ReadBuffer& rb);
private:
template <typename T>
void MarshalType(GridMate::WriteBuffer& wb, const Datum* const & property) const
{
GridMate::Marshaler<T> marshaler;
const T* value = property->GetAs<T>();
marshaler.Marshal(wb, *value);
}
template <typename T>
bool UnmarshalType(const Datum*& target, GridMate::ReadBuffer& rb, GraphVariable* graphVariable)
{
bool valueChanged = false;
ModifiableDatumView datumView;
if (graphVariable)
{
graphVariable->ConfigureDatumView(datumView);
if (datumView.IsValid())
{
GridMate::Marshaler<T> marshaler;
T value;
marshaler.Unmarshal(value, rb);
datumView.SetAs(value);
target = graphVariable->GetDatum();
valueChanged = true;
}
}
return valueChanged;
}
private:
//! The network binding table is needed to determine which Datum to update
//! when unmarshaling data.
// :SCTODO: synced Datums should be tracked via ID
//! and that ID should be used to lookup Datums (right now we can assume
//! which Datum should be updated, since only one Datum is supported).
GraphVariableNetBindingTable* m_graphVariableNetBindingTable = nullptr;
};
//! Simple throttler that simple operates via dirty flag.
class DatumThrottler
{
public:
DatumThrottler() = default;
void SignalDirty();
bool WithinThreshold(const Datum* newValue) const;
void UpdateBaseline(const Datum* baseline);
private:
bool m_isDirty = false;
};
}
@@ -1,181 +0,0 @@
/*
* 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 <ScriptCanvas/Execution/RuntimeBus.h>
#include <ScriptCanvas/Variable/GraphVariable.h>
#include <ScriptCanvas/Variable/GraphVariableNetBindings.h>
#include <ScriptCanvas/Core/Datum.h>
#include <GridMate/Replica/DataSet.h>
#include <GridMate/Replica/ReplicaFunctions.h>
#include <AzFramework/Network/NetworkContext.h>
namespace ScriptCanvas
{
const char* DatumDataSet::GetDataSetName()
{
static size_t 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"
};
if (s_chunkIndex > AZ_ARRAY_SIZE(s_nameArray) && AZ_ARRAY_SIZE(s_nameArray) >= 0)
{
s_chunkIndex = s_chunkIndex % AZ_ARRAY_SIZE(s_nameArray);
}
return s_nameArray[s_chunkIndex++];
}
DatumDataSet::DatumDataSet()
: DatumDataSetType(DatumDataSet::GetDataSetName())
{
}
//////////////////////////
// GraphVariableReplicaChunk
//////////////////////////
const char* GraphVariableReplicaChunk::GetChunkName()
{
return "GraphVariableReplicaChunk";
}
bool GraphVariableReplicaChunk::IsReplicaMigratable()
{
return true;
}
//////////////////////////
// GraphVariableNetBindingTable
//////////////////////////
void GraphVariableNetBindingTable::Reflect([[maybe_unused]] AZ::ReflectContext* reflect)
{
GridMate::ReplicaChunkDescriptorTable& descriptorTable = GridMate::ReplicaChunkDescriptorTable::Get();
AZ::Crc32 hash = GridMate::ReplicaChunkClassId(GraphVariableReplicaChunk::GetChunkName());
if (!descriptorTable.FindReplicaChunkDescriptor(hash))
{
descriptorTable.RegisterChunkType<GraphVariableReplicaChunk>();
}
}
GridMate::ReplicaChunkPtr GraphVariableNetBindingTable::GetNetworkBinding()
{
if (!m_replicaChunk)
{
m_replicaChunk = GridMate::CreateReplicaChunk<GraphVariableReplicaChunk>();
m_replicaChunk->SetHandler(this);
SetGraphNetBindingTable();
}
return m_replicaChunk;
}
void GraphVariableNetBindingTable::SetNetworkBinding(GridMate::ReplicaChunkPtr chunk)
{
m_replicaChunk = chunk;
m_replicaChunk->SetHandler(this);
SetGraphNetBindingTable();
}
void GraphVariableNetBindingTable::UnbindFromNetwork()
{
if (m_replicaChunk)
{
m_replicaChunk->SetHandler(nullptr);
m_replicaChunk = nullptr;
}
}
void GraphVariableNetBindingTable::OnPropertyUpdate([[maybe_unused]] const Datum* const & scriptProperty, [[maybe_unused]] const GridMate::TimeContext& tc)
{
}
void GraphVariableNetBindingTable::AddDatum(GraphVariable* variable)
{
size_t index = m_variableIdMap.size();
m_variableIdMap[variable->GetVariableId()] = AZStd::make_pair(variable, static_cast<int>(index));
}
void GraphVariableNetBindingTable::OnDatumChanged(GraphVariable& variable)
{
if (m_replicaChunk && m_replicaChunk->IsMaster())
{
GraphVariableReplicaChunk* graphVarChunk = static_cast<GraphVariableReplicaChunk*>(m_replicaChunk.get());
auto iter = m_variableIdMap.find(variable.GetVariableId());
if (iter == m_variableIdMap.end())
{
AZ_TracePrintf("ScriptCanvasNetworking", "GraphVariableNetBindingTable::OnDatumChanged: variable not found");
return;
}
const AZStd::pair<GraphVariable*, int>& pair = iter->second;
DatumDataSet& datumDataSet = graphVarChunk->m_properties[pair.second];
datumDataSet.GetThrottler().SignalDirty();
datumDataSet.Set(variable.GetDatum());
}
}
void GraphVariableNetBindingTable::SetVariableMappings(const AZStd::unordered_map<VariableId, VariableId>& assetToRuntimeVariableMap, const AZStd::unordered_map<VariableId, VariableId>& runtimeToAssetVariableMap)
{
m_assetToRuntimeVariableMap = assetToRuntimeVariableMap;
m_runtimeToAssetVariableMap = runtimeToAssetVariableMap;
}
VariableId GraphVariableNetBindingTable::FindAssetVariableIdByRuntimeVariableId(VariableId runtimeVariableId)
{
auto iter = m_runtimeToAssetVariableMap.find(runtimeVariableId);
if (iter != m_runtimeToAssetVariableMap.end())
{
return iter->second;
}
return VariableId();
}
VariableId GraphVariableNetBindingTable::FindRuntimeVariableIdByAssetVariableId(VariableId assetVariableId)
{
auto iter = m_assetToRuntimeVariableMap.find(assetVariableId);
if (iter != m_assetToRuntimeVariableMap.end())
{
return iter->second;
}
return VariableId();
}
AZStd::unordered_map<VariableId, AZStd::pair<GraphVariable*, int>>& GraphVariableNetBindingTable::GetVariableIdMap()
{
return m_variableIdMap;
}
void GraphVariableNetBindingTable::SetGraphNetBindingTable()
{
GraphVariableReplicaChunk* graphVariableChunk = static_cast<GraphVariableReplicaChunk*>(m_replicaChunk.get());
for (DatumDataSet& dataSet : graphVariableChunk->m_properties)
{
dataSet.GetMarshaler().SetNetBindingTable(this);
}
}
}
@@ -1,101 +0,0 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/RTTI/ReflectContext.h>
#include <GridMate/Replica/DataSet.h>
#include <GridMate/Replica/ReplicaChunkInterface.h>
#include <GridMate/Replica/ReplicaCommon.h>
#include <ScriptCanvas/Variable/GraphVariableMarshal.h>
namespace ScriptCanvas
{
class GraphVariable;
class GraphVariableReplicaChunk;
//! Core functionality for managing replicated Datums in a script canvas and the
//! corresponding GridMate callbacks and data structs (DataSets).
class GraphVariableNetBindingTable
: public GridMate::ReplicaChunkInterface
{
public:
AZ_CLASS_ALLOCATOR(GraphVariableNetBindingTable, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflect);
GraphVariableNetBindingTable() = default;
~GraphVariableNetBindingTable() = default;
GridMate::ReplicaChunkPtr GetNetworkBinding();
void SetNetworkBinding(GridMate::ReplicaChunkPtr chunk);
void UnbindFromNetwork();
//! Gets called when the given Datum object is updated with a new value
//! that was received over the network.
void OnPropertyUpdate(const Datum* const & scriptProperty, const GridMate::TimeContext& tc);
//! Adds the given Datum to the list of "synced datums" for this instance.
void AddDatum(GraphVariable* variable);
//! Called when local data changes for a Datum whose values should be replicated
//! over the network.
void OnDatumChanged(GraphVariable& variable);
void SetVariableMappings(const AZStd::unordered_map<VariableId, VariableId>& assetToRuntimeVariableMap, const AZStd::unordered_map<VariableId, VariableId>& runtimeToAssetVariableMap);
VariableId FindAssetVariableIdByRuntimeVariableId(VariableId runtimeVariableId);
VariableId FindRuntimeVariableIdByAssetVariableId(VariableId assetVariableId);
AZStd::unordered_map<VariableId, AZStd::pair<GraphVariable*, int>>& GetVariableIdMap();
private:
void SetGraphNetBindingTable();
private:
AZStd::unordered_map<VariableId, VariableId> m_assetToRuntimeVariableMap;
AZStd::unordered_map<VariableId, VariableId> m_runtimeToAssetVariableMap;
//! Replica chunk used for GridMate networking binding. See GraphVariableReplicaChunk.
GridMate::ReplicaChunkPtr m_replicaChunk;
//! Contains pointers to all replicated variables contained within the runtime component
//! of the canvas this net binding is associated with.
AZStd::unordered_map<VariableId, AZStd::pair<GraphVariable*, int>> m_variableIdMap;
};
typedef GridMate::DataSet<const Datum*, DatumMarshaler, DatumThrottler>::BindInterface<GraphVariableNetBindingTable, &GraphVariableNetBindingTable::OnPropertyUpdate> DatumDataSetType;
class DatumDataSet
: public DatumDataSetType
{
public:
DatumDataSet();
~DatumDataSet() = default;
private:
const char* GetDataSetName();
};
class GraphVariableReplicaChunk
: public GridMate::ReplicaChunkBase
{
public:
AZ_CLASS_ALLOCATOR(GraphVariableReplicaChunk, AZ::SystemAllocator, 0);
static const char* GetChunkName();
GraphVariableReplicaChunk() = default;
~GraphVariableReplicaChunk() = default;
bool IsReplicaMigratable() override;
DatumDataSet m_properties[GM_MAX_DATASETS_IN_CHUNK];
};
}
@@ -597,10 +597,6 @@ set(FILES
Include/ScriptCanvas/Variable/GraphVariable.cpp
Include/ScriptCanvas/Variable/GraphVariableManagerComponent.h
Include/ScriptCanvas/Variable/GraphVariableManagerComponent.cpp
Include/ScriptCanvas/Variable/GraphVariableNetBindings.h
Include/ScriptCanvas/Variable/GraphVariableNetBindings.cpp
Include/ScriptCanvas/Variable/GraphVariableMarshal.h
Include/ScriptCanvas/Variable/GraphVariableMarshal.cpp
Include/ScriptCanvas/Variable/VariableCore.h
Include/ScriptCanvas/Variable/VariableCore.cpp
Include/ScriptCanvas/Variable/VariableData.h