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:
@@ -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
|
||||
+1
-1
@@ -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
|
||||
|
||||
+1
-16
@@ -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.")->
|
||||
|
||||
+3
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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>());
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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(¶m.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>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user