Merge branch 'main' into ly-as-sdk/LYN-2948
This commit is contained in:
@@ -1 +0,0 @@
|
||||
*.xml
|
||||
@@ -1 +0,0 @@
|
||||
*.xml
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Math/Uuid.h>
|
||||
#include <AzCore/Preprocessor/Enum.h>
|
||||
#include <AzCore/std/containers/bitset.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/string/string_view.h>
|
||||
@@ -216,16 +217,14 @@ namespace AZ
|
||||
/**
|
||||
* Setting for each reference (Asset<T>) to control loading of referenced assets during serialization.
|
||||
*/
|
||||
enum class AssetLoadBehavior : u8
|
||||
{
|
||||
PreLoad = 0, ///< Serializer will "Pre load" dependencies, asset containers may load in parallel but will not signal AssetReady
|
||||
QueueLoad = 1, ///< Serializer will queue an asynchronous load of the referenced asset and return the object to the user. User code should use the \ref AZ::Data::AssetBus to monitor for when it's ready.
|
||||
NoLoad = 2, ///< Serializer will load reference information, but asset loading will be left to the user. User code should call Asset<T>::QueueLoad and use the \ref AZ::Data::AssetBus to monitor for when it's ready.
|
||||
///< AssetContainers will skip NoLoad dependencies
|
||||
|
||||
AZ_ENUM_WITH_UNDERLYING_TYPE(AssetLoadBehavior, u8,
|
||||
(PreLoad, 0), ///< Serializer will "Pre load" dependencies, asset containers may load in parallel but will not signal AssetReady
|
||||
(QueueLoad, 1), ///< Serializer will queue an asynchronous load of the referenced asset and return the object to the user. User code should use the \ref AZ::Data::AssetBus to monitor for when it's ready.
|
||||
(NoLoad, 2), ///< Serializer will load reference information, but asset loading will be left to the user. User code should call Asset<T>::QueueLoad and use the \ref AZ::Data::AssetBus to monitor for when it's ready.
|
||||
///< AssetContainers will skip NoLoad dependencies
|
||||
Count,
|
||||
Default = QueueLoad,
|
||||
};
|
||||
(Default, QueueLoad)
|
||||
);
|
||||
|
||||
struct AssetFilterInfo
|
||||
{
|
||||
@@ -1222,6 +1221,7 @@ namespace AZ
|
||||
} // namespace ProductDependencyInfo
|
||||
} // namespace Data
|
||||
|
||||
AZ_TYPE_INFO_SPECIALIZE(Data::AssetLoadBehavior, "{DAF9ECED-FEF3-4D7A-A220-8CFD6A5E6DA1}");
|
||||
AZ_TYPE_INFO_TEMPLATE_WITH_NAME(AZ::Data::Asset, "Asset", "{C891BF19-B60C-45E2-BFD0-027D15DDC939}", AZ_TYPE_INFO_CLASS);
|
||||
|
||||
} // namespace AZ
|
||||
|
||||
@@ -70,6 +70,17 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const AZ::Data::AssetLoadBehavior autoLoadBehavior = instance->GetAutoLoadBehavior();
|
||||
const AZ::Data::AssetLoadBehavior defaultAutoLoadBehavior = defaultInstance ?
|
||||
defaultInstance->GetAutoLoadBehavior() : AZ::Data::AssetLoadBehavior::Default;
|
||||
|
||||
result.Combine(
|
||||
ContinueStoringToJsonObjectField(outputValue, "loadBehavior",
|
||||
&autoLoadBehavior, &defaultAutoLoadBehavior,
|
||||
azrtti_typeid<Data::AssetLoadBehavior>(), context));
|
||||
}
|
||||
|
||||
{
|
||||
ScopedContextPath subPathHint(context, "m_assetHint");
|
||||
const AZStd::string* hint = &instance->GetHint();
|
||||
@@ -100,14 +111,28 @@ namespace AZ
|
||||
AssetId id;
|
||||
JSR::ResultCode result(JSR::Tasks::ReadField);
|
||||
|
||||
SerializedAssetTracker* assetTracker =
|
||||
context.GetMetadata().Find<SerializedAssetTracker>();
|
||||
|
||||
{
|
||||
Data::AssetLoadBehavior loadBehavior = instance->GetAutoLoadBehavior();
|
||||
|
||||
result =
|
||||
ContinueLoadingFromJsonObjectField(&loadBehavior,
|
||||
azrtti_typeid<Data::AssetLoadBehavior>(),
|
||||
inputValue, "loadBehavior", context);
|
||||
|
||||
instance->SetAutoLoadBehavior(loadBehavior);
|
||||
}
|
||||
|
||||
auto it = inputValue.FindMember("assetId");
|
||||
if (it != inputValue.MemberEnd())
|
||||
{
|
||||
ScopedContextPath subPath(context, "assetId");
|
||||
result = ContinueLoading(&id, azrtti_typeid<AssetId>(), it->value, context);
|
||||
result.Combine(ContinueLoading(&id, azrtti_typeid<AssetId>(), it->value, context));
|
||||
if (!id.m_guid.IsNull())
|
||||
{
|
||||
*instance = AssetManager::Instance().FindOrCreateAsset(id, instance->GetType(), AssetLoadBehavior::NoLoad);
|
||||
*instance = AssetManager::Instance().FindOrCreateAsset(id, instance->GetType(), instance->GetAutoLoadBehavior());
|
||||
|
||||
|
||||
result.Combine(context.Report(result, "Successfully created Asset<T> with id."));
|
||||
@@ -142,6 +167,11 @@ namespace AZ
|
||||
"The asset hint is missing for Asset<T>, so it will be left empty."));
|
||||
}
|
||||
|
||||
if (assetTracker)
|
||||
{
|
||||
assetTracker->AddAsset(*instance);
|
||||
}
|
||||
|
||||
bool success = result.GetOutcome() <= JSR::Outcomes::PartialSkip;
|
||||
bool defaulted = result.GetOutcome() == JSR::Outcomes::DefaultsUsed || result.GetOutcome() == JSR::Outcomes::PartialDefaults;
|
||||
AZStd::string_view message =
|
||||
@@ -150,5 +180,20 @@ namespace AZ
|
||||
"Not enough information was available to create an instance of Asset<T> or data was corrupted.";
|
||||
return context.Report(result, message);
|
||||
}
|
||||
|
||||
void SerializedAssetTracker::AddAsset(Asset<AssetData>& asset)
|
||||
{
|
||||
m_serializedAssets.emplace_back(asset);
|
||||
}
|
||||
|
||||
const AZStd::vector<Asset<AssetData>>& SerializedAssetTracker::GetTrackedAssets() const
|
||||
{
|
||||
return m_serializedAssets;
|
||||
}
|
||||
|
||||
AZStd::vector<Asset<AssetData>>& SerializedAssetTracker::GetTrackedAssets()
|
||||
{
|
||||
return m_serializedAssets;
|
||||
}
|
||||
} // namespace Data
|
||||
} // namespace AZ
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
|
||||
|
||||
namespace AZ
|
||||
@@ -37,5 +38,18 @@ namespace AZ
|
||||
private:
|
||||
JsonSerializationResult::Result LoadAsset(void* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context);
|
||||
};
|
||||
|
||||
class SerializedAssetTracker final
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(SerializedAssetTracker, "{1E067091-8C0A-44B1-A455-6E97663F6963}");
|
||||
|
||||
void AddAsset(Asset<AssetData>& asset);
|
||||
AZStd::vector<Asset<AssetData>>& GetTrackedAssets();
|
||||
const AZStd::vector<Asset<AssetData>>& GetTrackedAssets() const;
|
||||
|
||||
private:
|
||||
AZStd::vector<Asset<AssetData>> m_serializedAssets;
|
||||
};
|
||||
} // namespace Data
|
||||
} // namespace AZ
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <AzCore/Asset/AssetJsonSerializer.h>
|
||||
#include <AzCore/Asset/AssetManagerComponent.h>
|
||||
#include <AzCore/Asset/AssetManagerBus.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Preprocessor/EnumReflectUtils.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
@@ -24,6 +24,11 @@
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Data
|
||||
{
|
||||
AZ_ENUM_DEFINE_REFLECT_UTILITIES(AssetLoadBehavior);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// AssetDatabaseComponent
|
||||
// [6/25/2012]
|
||||
@@ -99,6 +104,8 @@ namespace AZ
|
||||
|
||||
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
AZ::Data::AssetLoadBehaviorReflect(*serializeContext);
|
||||
|
||||
serializeContext->RegisterGenericType<Data::Asset<Data::AssetData>>();
|
||||
|
||||
serializeContext->Class<AssetManagerComponent, AZ::Component>()
|
||||
|
||||
@@ -1304,7 +1304,7 @@ namespace AZ
|
||||
// Add all auto loadable non-asset gems to the list of gem modules to load
|
||||
if (!moduleLoadData.m_autoLoad)
|
||||
{
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
for (AZ::OSString& dynamicLibraryPath : moduleLoadData.m_dynamicLibraryPaths)
|
||||
{
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
|
||||
@@ -1042,11 +1042,7 @@ namespace UnitTest
|
||||
|
||||
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
|
||||
TEST_F(AssetJobsFloodTest, DISABLED_ContainerCoreTest_BasicDependencyManagement_Success)
|
||||
#else
|
||||
TEST_F(AssetJobsFloodTest, ContainerCoreTest_BasicDependencyManagement_Success)
|
||||
#endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
|
||||
{
|
||||
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
|
||||
// Setup has already created/destroyed assets
|
||||
|
||||
@@ -135,6 +135,7 @@ namespace JsonSerializationTests
|
||||
auto instance = AZStd::make_shared<Asset>();
|
||||
instance->Create(id, false);
|
||||
instance->SetHint("TestFile");
|
||||
instance->SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad);
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -158,6 +159,7 @@ namespace JsonSerializationTests
|
||||
"guid": "{BBEAC89F-8BAD-4A9D-BF6E-D0DF84A8DFD6}",
|
||||
"subId": 1
|
||||
},
|
||||
"loadBehavior": "PreLoad",
|
||||
"assetHint": "TestFile"
|
||||
})";
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -37,29 +37,6 @@ namespace AzFramework
|
||||
void NonUniformScaleComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
incompatible.push_back(AZ_CRC_CE("NonUniformScaleService"));
|
||||
|
||||
incompatible.push_back(AZ_CRC_CE("DebugDrawObbService"));
|
||||
incompatible.push_back(AZ_CRC_CE("DebugDrawService"));
|
||||
incompatible.push_back(AZ_CRC_CE("EMotionFXActorService"));
|
||||
incompatible.push_back(AZ_CRC_CE("EMotionFXSimpleMotionService"));
|
||||
incompatible.push_back(AZ_CRC_CE("GradientTransformService"));
|
||||
incompatible.push_back(AZ_CRC_CE("LegacyMeshService"));
|
||||
incompatible.push_back(AZ_CRC_CE("LookAtService"));
|
||||
incompatible.push_back(AZ_CRC_CE("SequenceService"));
|
||||
incompatible.push_back(AZ_CRC_CE("ClothMeshService"));
|
||||
incompatible.push_back(AZ_CRC_CE("PhysXJointService"));
|
||||
incompatible.push_back(AZ_CRC_CE("PhysXCharacterControllerService"));
|
||||
incompatible.push_back(AZ_CRC_CE("PhysXRagdollService"));
|
||||
incompatible.push_back(AZ_CRC_CE("WhiteBoxService"));
|
||||
incompatible.push_back(AZ_CRC_CE("NavigationAreaService"));
|
||||
incompatible.push_back(AZ_CRC_CE("GeometryService"));
|
||||
incompatible.push_back(AZ_CRC_CE("CapsuleShapeService"));
|
||||
incompatible.push_back(AZ_CRC_CE("CompoundShapeService"));
|
||||
incompatible.push_back(AZ_CRC_CE("CylinderShapeService"));
|
||||
incompatible.push_back(AZ_CRC_CE("DiskShapeService"));
|
||||
incompatible.push_back(AZ_CRC_CE("SphereShapeService"));
|
||||
incompatible.push_back(AZ_CRC_CE("SplineService"));
|
||||
incompatible.push_back(AZ_CRC_CE("TubeShapeService"));
|
||||
}
|
||||
|
||||
void NonUniformScaleComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
|
||||
@@ -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
|
||||
+6
-2
@@ -99,6 +99,11 @@ namespace AzPhysics
|
||||
classElement.RemoveElementByName(AZ_CRC_CE("Property Visibility Flags"));
|
||||
}
|
||||
|
||||
if (classElement.GetVersion() <= 4)
|
||||
{
|
||||
classElement.RemoveElementByName(AZ_CRC_CE("Simulated"));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -110,7 +115,7 @@ namespace AzPhysics
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<RigidBodyConfiguration, AzPhysics::SimulatedBodyConfiguration>()
|
||||
->Version(4, &Internal::RigidBodyVersionConverter)
|
||||
->Version(5, &Internal::RigidBodyVersionConverter)
|
||||
->Field("Initial linear velocity", &RigidBodyConfiguration::m_initialLinearVelocity)
|
||||
->Field("Initial angular velocity", &RigidBodyConfiguration::m_initialAngularVelocity)
|
||||
->Field("Linear damping", &RigidBodyConfiguration::m_linearDamping)
|
||||
@@ -119,7 +124,6 @@ namespace AzPhysics
|
||||
->Field("Start Asleep", &RigidBodyConfiguration::m_startAsleep)
|
||||
->Field("Interpolate Motion", &RigidBodyConfiguration::m_interpolateMotion)
|
||||
->Field("Gravity Enabled", &RigidBodyConfiguration::m_gravityEnabled)
|
||||
->Field("Simulated", &RigidBodyConfiguration::m_simulated)
|
||||
->Field("Kinematic", &RigidBodyConfiguration::m_kinematic)
|
||||
->Field("CCD Enabled", &RigidBodyConfiguration::m_ccdEnabled)
|
||||
->Field("Compute Mass", &RigidBodyConfiguration::m_computeMass)
|
||||
|
||||
@@ -57,7 +57,6 @@ namespace AzPhysics
|
||||
bool m_startAsleep = false;
|
||||
bool m_interpolateMotion = false;
|
||||
bool m_gravityEnabled = true;
|
||||
bool m_simulated = true;
|
||||
bool m_kinematic = false;
|
||||
bool m_ccdEnabled = false; //!< Whether continuous collision detection is enabled.
|
||||
float m_ccdMinAdvanceCoefficient = 0.15f; //!< Coefficient affecting how granularly time is subdivided in CCD.
|
||||
|
||||
@@ -88,13 +88,13 @@ namespace AzPhysics
|
||||
|
||||
//! Remove a simulated body from the Scene.z
|
||||
//! @param sceneHandle A handle to the scene to remove the requested simulated body.
|
||||
//! @param bodyHandle A handle to the simulated body being removed.
|
||||
virtual void RemoveSimulatedBody(SceneHandle sceneHandle, SimulatedBodyHandle bodyHandle) = 0;
|
||||
//! @param bodyHandle A handle to the simulated body being removed. This will be set to AzPhysics::InvalidSimulatedBodyHandle as they're no longer valid.
|
||||
virtual void RemoveSimulatedBody(SceneHandle sceneHandle, SimulatedBodyHandle& bodyHandle) = 0;
|
||||
|
||||
//! Remove a list of simulated bodies from the Scene.
|
||||
//! @param sceneHandle A handle to the scene to remove the simulated bodies from.
|
||||
//! @param bodyHandles A list of simulated body handles to be removed.
|
||||
virtual void RemoveSimulatedBodies(SceneHandle sceneHandle, const SimulatedBodyHandleList& bodyHandles) = 0;
|
||||
//! @param bodyHandles A list of simulated body handles to be removed. All handles will be set to AzPhysics::InvalidSimulatedBodyHandle as they're no longer valid.
|
||||
virtual void RemoveSimulatedBodies(SceneHandle sceneHandle, SimulatedBodyHandleList& bodyHandles) = 0;
|
||||
|
||||
//! Enable / Disable simulation of the requested body. By default all bodies added are enabled.
|
||||
//! Disabling simulation the body will no longer be affected by any forces, collisions, or found with scene queries.
|
||||
@@ -286,12 +286,12 @@ namespace AzPhysics
|
||||
virtual SimulatedBodyList GetSimulatedBodiesFromHandle(const SimulatedBodyHandleList& bodyHandles) = 0;
|
||||
|
||||
//! Remove a simulated body from the Scene.
|
||||
//! @param bodyHandle A handle to the simulated body being removed.
|
||||
virtual void RemoveSimulatedBody(SimulatedBodyHandle bodyHandle) = 0;
|
||||
//! @param bodyHandle A handle to the simulated body being removed. This will be set to AzPhysics::InvalidSimulatedBodyHandle as they're no longer valid.
|
||||
virtual void RemoveSimulatedBody(SimulatedBodyHandle& bodyHandle) = 0;
|
||||
|
||||
//! Remove a list of simulated bodies from the Scene.
|
||||
//! @param bodyHandles A list of simulated body handles to be removed.
|
||||
virtual void RemoveSimulatedBodies(const SimulatedBodyHandleList& bodyHandles) = 0;
|
||||
//! @param bodyHandles A list of simulated body handles to be removed. All handles will be set to AzPhysics::InvalidSimulatedBodyHandle as they're no longer valid.
|
||||
virtual void RemoveSimulatedBodies(SimulatedBodyHandleList& bodyHandles) = 0;
|
||||
|
||||
//! Enable / Disable simulation of the requested body. By default all bodies added are enabled.
|
||||
//! Disabling simulation the body will no longer be affected by any forces, collisions, or found with scene queries.
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace AzPhysics
|
||||
virtual void SetLinearVelocity(const AZ::Vector3& velocity) = 0;
|
||||
virtual AZ::Vector3 GetAngularVelocity() const = 0;
|
||||
virtual void SetAngularVelocity(const AZ::Vector3& angularVelocity) = 0;
|
||||
virtual AZ::Vector3 GetLinearVelocityAtWorldPoint(const AZ::Vector3& worldPoint) = 0;
|
||||
virtual AZ::Vector3 GetLinearVelocityAtWorldPoint(const AZ::Vector3& worldPoint) const = 0;
|
||||
virtual void ApplyLinearImpulse(const AZ::Vector3& impulse) = 0;
|
||||
virtual void ApplyLinearImpulseAtWorldPoint(const AZ::Vector3& impulse, const AZ::Vector3& worldPoint) = 0;
|
||||
virtual void ApplyAngularImpulse(const AZ::Vector3& angularImpulse) = 0;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Math/MathUtils.h>
|
||||
#include <AzCore/Math/Plane.h>
|
||||
#include <AzCore/std/numeric.h>
|
||||
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
|
||||
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
|
||||
#include <AzFramework/Windowing/WindowBus.h>
|
||||
@@ -156,35 +157,25 @@ namespace AzFramework
|
||||
camera.m_lookAt = transform.GetTranslation() + (camera.Rotation().GetBasisY() * -camera.m_lookDist);
|
||||
}
|
||||
|
||||
static ScreenVector CursorDelta(const AZStd::optional<ScreenPoint>& currentPosition, const AZStd::optional<ScreenPoint>& lastPosition)
|
||||
{
|
||||
return currentPosition.has_value() && lastPosition.has_value() ? currentPosition.value() - lastPosition.value()
|
||||
: ScreenVector(0, 0);
|
||||
}
|
||||
|
||||
bool CameraSystem::HandleEvents(const InputEvent& event)
|
||||
{
|
||||
if (const auto& cursor = AZStd::get_if<CursorEvent>(&event))
|
||||
{
|
||||
m_currentCursorPosition = cursor->m_position;
|
||||
m_cursorState.SetCurrentPosition(cursor->m_position);
|
||||
}
|
||||
else if (const auto& scroll = AZStd::get_if<ScrollEvent>(&event))
|
||||
{
|
||||
m_scrollDelta = scroll->m_delta;
|
||||
}
|
||||
|
||||
return m_cameras.HandleEvents(event, CursorDelta(m_currentCursorPosition, m_lastCursorPosition), m_scrollDelta);
|
||||
return m_cameras.HandleEvents(event, m_cursorState.CursorDelta(), m_scrollDelta);
|
||||
}
|
||||
|
||||
Camera CameraSystem::StepCamera(const Camera& targetCamera, const float deltaTime)
|
||||
{
|
||||
const auto cursorDelta = CursorDelta(m_currentCursorPosition, m_lastCursorPosition);
|
||||
if (m_currentCursorPosition.has_value())
|
||||
{
|
||||
m_lastCursorPosition = m_currentCursorPosition;
|
||||
}
|
||||
const auto nextCamera = m_cameras.StepCamera(targetCamera, m_cursorState.CursorDelta(), m_scrollDelta, deltaTime);
|
||||
|
||||
const auto nextCamera = m_cameras.StepCamera(targetCamera, cursorDelta, m_scrollDelta, deltaTime);
|
||||
m_cursorState.Update();
|
||||
|
||||
m_scrollDelta = 0.0f;
|
||||
|
||||
@@ -236,12 +227,12 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
// accumulate
|
||||
Camera nextCamera = targetCamera;
|
||||
for (auto& cameraInput : m_activeCameraInputs)
|
||||
{
|
||||
nextCamera = cameraInput->StepCamera(nextCamera, cursorDelta, scrollDelta, deltaTime);
|
||||
}
|
||||
const Camera nextCamera = AZStd::accumulate(
|
||||
AZStd::begin(m_activeCameraInputs), AZStd::end(m_activeCameraInputs), targetCamera,
|
||||
[cursorDelta, scrollDelta, deltaTime](Camera acc, auto& camera) {
|
||||
acc = camera->StepCamera(acc, cursorDelta, scrollDelta, deltaTime);
|
||||
return acc;
|
||||
});
|
||||
|
||||
for (int i = 0; i < m_activeCameraInputs.size();)
|
||||
{
|
||||
@@ -275,34 +266,42 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
RotateCameraInput::RotateCameraInput(const InputChannelId rotateChannelId)
|
||||
: m_rotateChannelId(rotateChannelId)
|
||||
{
|
||||
}
|
||||
|
||||
void RotateCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
|
||||
{
|
||||
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
|
||||
{
|
||||
if (input->m_channelId == m_rotateChannelId)
|
||||
const ClickDetector::ClickEvent clickEvent = [&event, this] {
|
||||
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
|
||||
{
|
||||
if (input->m_state == InputChannel::State::Began)
|
||||
if (input->m_channelId == m_rotateChannelId)
|
||||
{
|
||||
m_tryingToBegin = true;
|
||||
m_moveAccumulator = 0.0f;
|
||||
}
|
||||
else if (input->m_state == InputChannel::State::Ended)
|
||||
{
|
||||
m_tryingToBegin = false;
|
||||
EndActivation();
|
||||
if (input->m_state == InputChannel::State::Began)
|
||||
{
|
||||
return ClickDetector::ClickEvent::Down;
|
||||
}
|
||||
else if (input->m_state == InputChannel::State::Ended)
|
||||
{
|
||||
return ClickDetector::ClickEvent::Up;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ClickDetector::ClickEvent::Nil;
|
||||
}();
|
||||
|
||||
if (m_tryingToBegin)
|
||||
switch (const auto outcome = m_clickDetector.DetectClick(clickEvent, cursorDelta); outcome)
|
||||
{
|
||||
// only allow the action to begin if the mouse has been moved a small amount
|
||||
m_moveAccumulator += ScreenVectorLength(cursorDelta);
|
||||
if (m_moveAccumulator > ed_cameraSystemLookDeadzone)
|
||||
{
|
||||
BeginActivation();
|
||||
m_tryingToBegin = false;
|
||||
}
|
||||
case ClickDetector::ClickOutcome::Move:
|
||||
BeginActivation();
|
||||
break;
|
||||
case ClickDetector::ClickOutcome::Release:
|
||||
EndActivation();
|
||||
break;
|
||||
default:
|
||||
// noop
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,6 +323,12 @@ namespace AzFramework
|
||||
return nextCamera;
|
||||
}
|
||||
|
||||
PanCameraInput::PanCameraInput(const InputChannelId panChannelId, PanAxesFn panAxesFn)
|
||||
: m_panAxesFn(AZStd::move(panAxesFn))
|
||||
, m_panChannelId(panChannelId)
|
||||
{
|
||||
}
|
||||
|
||||
void PanCameraInput::HandleEvents(
|
||||
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
|
||||
{
|
||||
@@ -400,6 +405,11 @@ namespace AzFramework
|
||||
return TranslationType::Nil;
|
||||
}
|
||||
|
||||
TranslateCameraInput::TranslateCameraInput(TranslationAxesFn translationAxesFn)
|
||||
: m_translationAxesFn(AZStd::move(translationAxesFn))
|
||||
{
|
||||
}
|
||||
|
||||
void TranslateCameraInput::HandleEvents(
|
||||
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
|
||||
{
|
||||
@@ -407,11 +417,6 @@ namespace AzFramework
|
||||
{
|
||||
if (input->m_state == InputChannel::State::Began)
|
||||
{
|
||||
if (input->m_state == InputChannel::State::Updated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_translation |= translationFromKey(input->m_channelId);
|
||||
if (m_translation != TranslationType::Nil)
|
||||
{
|
||||
@@ -579,6 +584,11 @@ namespace AzFramework
|
||||
return nextCamera;
|
||||
}
|
||||
|
||||
OrbitDollyCursorMoveCameraInput::OrbitDollyCursorMoveCameraInput(const InputChannelId dollyChannelId)
|
||||
: m_dollyChannelId(dollyChannelId)
|
||||
{
|
||||
}
|
||||
|
||||
void OrbitDollyCursorMoveCameraInput::HandleEvents(
|
||||
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
|
||||
{
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include <AzCore/std/containers/variant.h>
|
||||
#include <AzCore/std/optional.h>
|
||||
#include <AzFramework/Input/Channels/InputChannel.h>
|
||||
#include <AzFramework/Viewport/ClickDetector.h>
|
||||
#include <AzFramework/Viewport/CursorState.h>
|
||||
#include <AzFramework/Viewport/ScreenGeometry.h>
|
||||
#include <AzFramework/Viewport/ViewportId.h>
|
||||
|
||||
@@ -188,26 +190,21 @@ namespace AzFramework
|
||||
Cameras m_cameras;
|
||||
|
||||
private:
|
||||
CursorState m_cursorState;
|
||||
float m_scrollDelta = 0.0f;
|
||||
AZStd::optional<ScreenPoint> m_lastCursorPosition;
|
||||
AZStd::optional<ScreenPoint> m_currentCursorPosition;
|
||||
};
|
||||
|
||||
class RotateCameraInput : public CameraInput
|
||||
{
|
||||
public:
|
||||
explicit RotateCameraInput(const InputChannelId rotateChannelId)
|
||||
: m_rotateChannelId(rotateChannelId)
|
||||
{
|
||||
}
|
||||
explicit RotateCameraInput(InputChannelId rotateChannelId);
|
||||
|
||||
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
|
||||
|
||||
private:
|
||||
InputChannelId m_rotateChannelId;
|
||||
float m_moveAccumulator = 0.0f;
|
||||
bool m_tryingToBegin = false;
|
||||
ClickDetector m_clickDetector;
|
||||
};
|
||||
|
||||
struct PanAxes
|
||||
@@ -240,11 +237,8 @@ namespace AzFramework
|
||||
class PanCameraInput : public CameraInput
|
||||
{
|
||||
public:
|
||||
PanCameraInput(const InputChannelId panChannelId, PanAxesFn panAxesFn)
|
||||
: m_panAxesFn(AZStd::move(panAxesFn))
|
||||
, m_panChannelId(panChannelId)
|
||||
{
|
||||
}
|
||||
PanCameraInput(InputChannelId panChannelId, PanAxesFn panAxesFn);
|
||||
|
||||
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
|
||||
|
||||
@@ -283,10 +277,8 @@ namespace AzFramework
|
||||
class TranslateCameraInput : public CameraInput
|
||||
{
|
||||
public:
|
||||
explicit TranslateCameraInput(TranslationAxesFn translationAxesFn)
|
||||
: m_translationAxesFn(AZStd::move(translationAxesFn))
|
||||
{
|
||||
}
|
||||
explicit TranslateCameraInput(TranslationAxesFn translationAxesFn);
|
||||
|
||||
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
|
||||
void ResetImpl() override;
|
||||
@@ -363,8 +355,7 @@ namespace AzFramework
|
||||
class OrbitDollyCursorMoveCameraInput : public CameraInput
|
||||
{
|
||||
public:
|
||||
explicit OrbitDollyCursorMoveCameraInput(const InputChannelId dollyChannelId)
|
||||
: m_dollyChannelId(dollyChannelId) {}
|
||||
explicit OrbitDollyCursorMoveCameraInput(InputChannelId dollyChannelId);
|
||||
|
||||
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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/Viewport/ClickDetector.h>
|
||||
#include <AzFramework/Viewport/ScreenGeometry.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
ClickDetector::ClickOutcome ClickDetector::DetectClick(const ClickEvent clickEvent, const ScreenVector& cursorDelta)
|
||||
{
|
||||
if (clickEvent == ClickEvent::Down)
|
||||
{
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (m_tryBeginTime)
|
||||
{
|
||||
const std::chrono::duration<float> diff = now - m_tryBeginTime.value();
|
||||
if (diff.count() < m_doubleClickInterval)
|
||||
{
|
||||
return ClickOutcome::Nil;
|
||||
}
|
||||
}
|
||||
|
||||
m_detectionState = DetectionState::WaitingForMove;
|
||||
m_moveAccumulator = 0.0f;
|
||||
|
||||
m_tryBeginTime = now;
|
||||
}
|
||||
else if (clickEvent == ClickEvent::Up)
|
||||
{
|
||||
const auto clickOutcome = [detectionState = m_detectionState] {
|
||||
if (detectionState == DetectionState::WaitingForMove)
|
||||
{
|
||||
return ClickOutcome::Click;
|
||||
}
|
||||
if (detectionState == DetectionState::Moved)
|
||||
{
|
||||
return ClickOutcome::Release;
|
||||
}
|
||||
return ClickOutcome::Nil;
|
||||
}();
|
||||
|
||||
m_detectionState = DetectionState::Nil;
|
||||
return clickOutcome;
|
||||
}
|
||||
|
||||
if (m_detectionState == DetectionState::WaitingForMove)
|
||||
{
|
||||
// only allow the action to begin if the mouse has been moved a small amount
|
||||
m_moveAccumulator += ScreenVectorLength(cursorDelta);
|
||||
if (m_moveAccumulator > m_deadZone)
|
||||
{
|
||||
m_detectionState = DetectionState::Moved;
|
||||
return ClickOutcome::Move;
|
||||
}
|
||||
}
|
||||
|
||||
return ClickOutcome::Nil;
|
||||
}
|
||||
} // namespace AzFramework
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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/std/optional.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
struct ScreenVector;
|
||||
|
||||
//! Utility class to help detect different types of mouse click (mouse down and up with
|
||||
//! no movement), mouse move (down and initial move after some threshold) and mouse release
|
||||
//! (mouse down with movement and then mouse up).
|
||||
class ClickDetector
|
||||
{
|
||||
//! Alias for recording time of mouse down events
|
||||
using Time = std::chrono::time_point<std::chrono::steady_clock>;
|
||||
|
||||
public:
|
||||
//! Internal representation of click event (map from external event for this when
|
||||
//! calling DetectClick).
|
||||
enum class ClickEvent
|
||||
{
|
||||
Nil,
|
||||
Down,
|
||||
Up
|
||||
};
|
||||
|
||||
//! The type of mouse click.
|
||||
enum class ClickOutcome
|
||||
{
|
||||
Nil, //!< Not recognized.
|
||||
Move, //!< Initial move after mouse down.
|
||||
Click, //!< Mouse down and up with no intermediate movement.
|
||||
Release //!< Mouse down with movement and then mouse up.
|
||||
};
|
||||
|
||||
//! Called from any type of 'handle event' function.
|
||||
ClickOutcome DetectClick(ClickEvent clickEvent, const ScreenVector& cursorDelta);
|
||||
|
||||
void SetDoubleClickInterval(float doubleClickInterval);
|
||||
|
||||
private:
|
||||
//! Internal state of ClickDetector based on incoming events.
|
||||
enum class DetectionState
|
||||
{
|
||||
Nil, //!< Initial state
|
||||
WaitingForMove, //! Mouse down has happened but mouse hasn't yet moved.
|
||||
Moved //! Mouse has moved, no longer will be counted as a click.
|
||||
};
|
||||
|
||||
float m_moveAccumulator = 0.0f; //!< How far the mouse has moved after mouse down.
|
||||
float m_deadZone = 2.0f; //!< How far to move before a click is cancelled (when Move will fire).
|
||||
float m_doubleClickInterval = 0.4f; //!< Default double click interval, can be overridden.
|
||||
DetectionState m_detectionState; //!< Internal state of ClickDetector.
|
||||
AZStd::optional<Time> m_tryBeginTime; //!< Mouse down time (happens each mouse down, helps with double click handling).
|
||||
};
|
||||
|
||||
inline void ClickDetector::SetDoubleClickInterval(const float doubleClickInterval)
|
||||
{
|
||||
m_doubleClickInterval = doubleClickInterval;
|
||||
}
|
||||
} // namespace AzFramework
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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/Viewport/ScreenGeometry.h>
|
||||
|
||||
#include <AzCore/std/optional.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
//! Utility type to wrap a current and last cursor position.
|
||||
struct CursorState
|
||||
{
|
||||
//! Returns the delta between the current and last cursor position.
|
||||
[[nodiscard]] ScreenVector CursorDelta() const;
|
||||
//! Call this in a 'handle event' call to update the most recent cursor position.
|
||||
void SetCurrentPosition(const ScreenPoint& currentPosition);
|
||||
//! Call this in an 'update' call to copy the current cursor position to the last
|
||||
//! cursor position.
|
||||
void Update();
|
||||
|
||||
private:
|
||||
AZStd::optional<ScreenPoint> m_lastCursorPosition;
|
||||
AZStd::optional<ScreenPoint> m_currentCursorPosition;
|
||||
};
|
||||
|
||||
inline void CursorState::SetCurrentPosition(const ScreenPoint& currentPosition)
|
||||
{
|
||||
m_currentCursorPosition = currentPosition;
|
||||
}
|
||||
|
||||
inline ScreenVector CursorState::CursorDelta() const
|
||||
{
|
||||
return m_currentCursorPosition.has_value() && m_lastCursorPosition.has_value()
|
||||
? m_currentCursorPosition.value() - m_lastCursorPosition.value()
|
||||
: ScreenVector(0, 0);
|
||||
}
|
||||
|
||||
inline void CursorState::Update()
|
||||
{
|
||||
if (m_currentCursorPosition.has_value())
|
||||
{
|
||||
m_lastCursorPosition = m_currentCursorPosition;
|
||||
}
|
||||
}
|
||||
} // namespace AzFramework
|
||||
@@ -103,6 +103,9 @@ set(FILES
|
||||
Viewport/CameraState.cpp
|
||||
Viewport/CameraInput.h
|
||||
Viewport/CameraInput.cpp
|
||||
Viewport/ClickDetector.h
|
||||
Viewport/ClickDetector.cpp
|
||||
Viewport/CursorState.h
|
||||
Viewport/DisplayContextRequestBus.h
|
||||
Entity/BehaviorEntity.cpp
|
||||
Entity/BehaviorEntity.h
|
||||
@@ -161,26 +164,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 +186,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 +258,7 @@ set(FILES
|
||||
Physics/ClassConverters.cpp
|
||||
Physics/ClassConverters.h
|
||||
Physics/MaterialBus.h
|
||||
Physics/WindBus.h
|
||||
Process/ProcessCommunicator.cpp
|
||||
Process/ProcessCommunicator.h
|
||||
Process/ProcessWatcher.cpp
|
||||
|
||||
-2
@@ -263,8 +263,6 @@ namespace AzToolsFramework
|
||||
return SourceFileDetails("Icons/AssetBrowser/XML_16.svg");
|
||||
}
|
||||
|
||||
|
||||
// this is here to prevent having to include IResourceCompilerHelper, which is in CryCommon.
|
||||
static const char* sourceFormats[] = { ".tif", ".bmp", ".gif", ".jpg", ".jpeg", ".jpe", ".tga", ".png" };
|
||||
|
||||
for (unsigned int sourceImageFormatIndex = 0, numSources = AZ_ARRAY_SIZE(sourceFormats); sourceImageFormatIndex < numSources; ++sourceImageFormatIndex)
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#include <AzToolsFramework/UI/PropertyEditor/PropertyManagerComponent.h>
|
||||
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiSystemComponent.h>
|
||||
#include <AzToolsFramework/Thumbnails/ThumbnailerComponent.h>
|
||||
#include <AzToolsFramework/Thumbnails/ThumbnailerNullComponent.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserComponent.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.h>
|
||||
|
||||
@@ -91,6 +92,7 @@ namespace AzToolsFramework
|
||||
AzToolsFramework::AssetBundleComponent::CreateDescriptor(),
|
||||
AzToolsFramework::SliceDependencyBrowserComponent::CreateDescriptor(),
|
||||
AzToolsFramework::Thumbnailer::ThumbnailerComponent::CreateDescriptor(),
|
||||
AzToolsFramework::Thumbnailer::ThumbnailerNullComponent::CreateDescriptor(),
|
||||
AzToolsFramework::AssetBrowser::AssetBrowserComponent::CreateDescriptor(),
|
||||
AzToolsFramework::EditorInteractionSystemComponent::CreateDescriptor(),
|
||||
AzToolsFramework::Components::EditorComponentAPIComponent::CreateDescriptor(),
|
||||
|
||||
+66
-9
@@ -13,6 +13,7 @@
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/Script/ScriptSystemBus.h>
|
||||
#include <AzCore/Serialization/Utils.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/Entity/GameEntityContextBus.h>
|
||||
#include <AzFramework/Spawnable/RootSpawnableInterface.h>
|
||||
@@ -281,14 +282,6 @@ namespace AzToolsFramework
|
||||
containerEntity->AddComponent(aznew Prefab::EditorPrefabComponent());
|
||||
HandleEntitiesAdded({containerEntity});
|
||||
HandleEntitiesAdded(entities);
|
||||
|
||||
// Update the template of the instance since we modified the entities of the instance by calling HandleEntitiesAdded.
|
||||
Prefab::PrefabDom serializedInstance;
|
||||
if (Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(addedInstance, serializedInstance))
|
||||
{
|
||||
m_prefabSystemComponent->UpdatePrefabTemplate(addedInstance.GetTemplateId(), serializedInstance);
|
||||
}
|
||||
|
||||
return addedInstance;
|
||||
}
|
||||
|
||||
@@ -342,6 +335,65 @@ namespace AzToolsFramework
|
||||
m_validateEntitiesCallback = AZStd::move(validateEntitiesCallback);
|
||||
}
|
||||
|
||||
void PrefabEditorEntityOwnershipService::LoadReferencedAssets(AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& referencedAssets)
|
||||
{
|
||||
// Start our loads on all assets by calling GetAsset from the AssetManager
|
||||
for (AZ::Data::Asset<AZ::Data::AssetData>& asset : referencedAssets)
|
||||
{
|
||||
if (!asset.GetId().IsValid())
|
||||
{
|
||||
AZ_Error("Prefab", false, "Invalid asset found referenced in scene while entering game mode");
|
||||
continue;
|
||||
}
|
||||
|
||||
const AZ::Data::AssetLoadBehavior loadBehavior = asset.GetAutoLoadBehavior();
|
||||
|
||||
if (loadBehavior == AZ::Data::AssetLoadBehavior::NoLoad)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AZ::Data::AssetId assetId = asset.GetId();
|
||||
AZ::Data::AssetType assetType = asset.GetType();
|
||||
|
||||
asset = AZ::Data::AssetManager::Instance().GetAsset(assetId, assetType, loadBehavior);
|
||||
|
||||
if (!asset.GetId().IsValid())
|
||||
{
|
||||
AZ_Error("Prefab", false, "Invalid asset found referenced in scene while entering game mode");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// For all Preload assets we block until they're ready
|
||||
// We do this as a seperate pass so that we don't interrupt queuing up all other asset loads
|
||||
for (AZ::Data::Asset<AZ::Data::AssetData>& asset : referencedAssets)
|
||||
{
|
||||
if (!asset.GetId().IsValid())
|
||||
{
|
||||
AZ_Error("Prefab", false, "Invalid asset found referenced in scene while entering game mode");
|
||||
continue;
|
||||
}
|
||||
|
||||
const AZ::Data::AssetLoadBehavior loadBehavior = asset.GetAutoLoadBehavior();
|
||||
|
||||
if (loadBehavior != AZ::Data::AssetLoadBehavior::PreLoad)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
asset.BlockUntilLoadComplete();
|
||||
|
||||
if (asset.IsError())
|
||||
{
|
||||
AZ_Error("Prefab", false, "Asset with id %s failed to preload while entering game mode",
|
||||
asset.GetId().ToString<AZStd::string>().c_str());
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabEditorEntityOwnershipService::StartPlayInEditor()
|
||||
{
|
||||
// This is a workaround until the replacement for GameEntityContext is done
|
||||
@@ -381,16 +433,21 @@ namespace AzToolsFramework
|
||||
rootSpawnableIndex = m_playInEditorData.m_assets.size();
|
||||
}
|
||||
|
||||
LoadReferencedAssets(product.GetReferencedAssets());
|
||||
|
||||
AZ::Data::AssetInfo info;
|
||||
info.m_assetId = product.GetAsset().GetId();
|
||||
info.m_assetType = product.GetAssetType();
|
||||
info.m_relativePath = product.GetId();
|
||||
|
||||
AZ::Data::AssetCatalogRequestBus::Broadcast(
|
||||
&AZ::Data::AssetCatalogRequestBus::Events::RegisterAsset, product.GetAsset().GetId(), info);
|
||||
&AZ::Data::AssetCatalogRequestBus::Events::RegisterAsset, info.m_assetId, info);
|
||||
m_playInEditorData.m_assets.emplace_back(product.ReleaseAsset().release(), AZ::Data::AssetLoadBehavior::Default);
|
||||
}
|
||||
|
||||
// make sure that PRE_NOTIFY assets get their notify before we activate, so that we can preserve the order of
|
||||
// (load asset) -> (notify) -> (init) -> (activate)
|
||||
AZ::Data::AssetManager::Instance().DispatchEvents();
|
||||
|
||||
if (rootSpawnableIndex != NoRootSpawnable)
|
||||
{
|
||||
|
||||
+2
@@ -199,6 +199,8 @@ namespace AzToolsFramework
|
||||
|
||||
void OnEntityRemoved(AZ::EntityId entityId);
|
||||
|
||||
void LoadReferencedAssets(AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& referencedAssets);
|
||||
|
||||
OnEntitiesAddedCallback m_entitiesAddedCallback;
|
||||
OnEntitiesRemovedCallback m_entitiesRemovedCallback;
|
||||
ValidateEntitiesCallback m_validateEntitiesCallback;
|
||||
|
||||
+9
-2
@@ -270,7 +270,7 @@ namespace AzToolsFramework
|
||||
return parentInstance;
|
||||
}
|
||||
|
||||
void InstanceToTemplatePropagator::AddPatchesToLink(PrefabDom& patches, Link& link)
|
||||
void InstanceToTemplatePropagator::AddPatchesToLink(const PrefabDom& patches, Link& link)
|
||||
{
|
||||
PrefabDom& linkDom = link.GetLinkDom();
|
||||
PrefabDomValueReference linkPatchesReference =
|
||||
@@ -279,7 +279,14 @@ namespace AzToolsFramework
|
||||
// This logic only covers addition of patches. If patches already exists, the given list of patches must be appended to them.
|
||||
if (!linkPatchesReference.has_value())
|
||||
{
|
||||
linkDom.AddMember(rapidjson::StringRef(PrefabDomUtils::PatchesName), patches, linkDom.GetAllocator());
|
||||
/*
|
||||
If the original allocator the patches were created with gets destroyed, then the patches would become garbage in the
|
||||
linkDom. Since we cannot guarantee the lifecycle of the patch allocators, we are doing a copy of the patches here to
|
||||
associate them with the linkDom's allocator.
|
||||
*/
|
||||
PrefabDom patchesCopy;
|
||||
patchesCopy.CopyFrom(patches, linkDom.GetAllocator());
|
||||
linkDom.AddMember(rapidjson::StringRef(PrefabDomUtils::PatchesName), patchesCopy, linkDom.GetAllocator());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ namespace AzToolsFramework
|
||||
|
||||
void ApplyPatchesToInstance(const AZ::EntityId& entityId, PrefabDom& patches, const Instance& instanceToAddPatches) override;
|
||||
|
||||
void AddPatchesToLink(PrefabDom& patches, Link& link);
|
||||
void AddPatchesToLink(const PrefabDom& patches, Link& link);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace AzToolsFramework
|
||||
using PrefabDomList = AZStd::vector<PrefabDom>;
|
||||
|
||||
using PrefabDomReference = AZStd::optional<AZStd::reference_wrapper<PrefabDom>>;
|
||||
using PrefabDomConstReference = AZStd::optional<AZStd::reference_wrapper<const PrefabDom>>;
|
||||
using PrefabDomValueReference = AZStd::optional<AZStd::reference_wrapper<PrefabDomValue>>;
|
||||
using PrefabDomValueConstReference = AZStd::optional<AZStd::reference_wrapper<const PrefabDomValue>>;
|
||||
|
||||
|
||||
@@ -11,8 +11,10 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
#include <AzCore/Asset/AssetJsonSerializer.h>
|
||||
#include <AzCore/JSON/prettywriter.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
@@ -115,6 +117,48 @@ namespace AzToolsFramework
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadInstanceFromPrefabDom(
|
||||
Instance& instance, const PrefabDom& prefabDom, AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& referencedAssets, LoadInstanceFlags flags)
|
||||
{
|
||||
// When entities are rebuilt they are first destroyed. As a result any assets they were exclusively holding on to will
|
||||
// be released and reloaded once the entities are built up again. By suspending asset release temporarily the asset reload
|
||||
// is avoided.
|
||||
AZ::Data::AssetManager::Instance().SuspendAssetRelease();
|
||||
|
||||
InstanceEntityIdMapper entityIdMapper;
|
||||
entityIdMapper.SetLoadingInstance(instance);
|
||||
if ((flags & LoadInstanceFlags::AssignRandomEntityId) == LoadInstanceFlags::AssignRandomEntityId)
|
||||
{
|
||||
entityIdMapper.SetEntityIdGenerationApproach(InstanceEntityIdMapper::EntityIdGenerationApproach::Random);
|
||||
}
|
||||
|
||||
AZ::JsonDeserializerSettings settings;
|
||||
// The InstanceEntityIdMapper is registered twice because it's used in several places during deserialization where one is
|
||||
// specific for the InstanceEntityIdMapper and once for the generic JsonEntityIdMapper. Because the Json Serializer's meta
|
||||
// data has strict typing and doesn't look for inheritance both have to be explicitly added so they're found both locations.
|
||||
settings.m_metadata.Add(static_cast<AZ::JsonEntityIdSerializer::JsonEntityIdMapper*>(&entityIdMapper));
|
||||
settings.m_metadata.Add(&entityIdMapper);
|
||||
settings.m_metadata.Create<AZ::Data::SerializedAssetTracker>();
|
||||
|
||||
AZ::JsonSerializationResult::ResultCode result =
|
||||
AZ::JsonSerialization::Load(instance, prefabDom, settings);
|
||||
|
||||
AZ::Data::AssetManager::Instance().ResumeAssetRelease();
|
||||
|
||||
if (result.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
|
||||
{
|
||||
AZ_Error("Prefab", false,
|
||||
"Failed to de-serialize Prefab Instance from Prefab DOM. "
|
||||
"Unable to proceed.");
|
||||
|
||||
return false;
|
||||
}
|
||||
AZ::Data::SerializedAssetTracker* assetTracker = settings.m_metadata.Find<AZ::Data::SerializedAssetTracker>();
|
||||
|
||||
referencedAssets = AZStd::move(assetTracker->GetTrackedAssets());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadInstanceFromPrefabDom(
|
||||
Instance& instance, Instance::EntityList& newlyAddedEntities, const PrefabDom& prefabDom, LoadInstanceFlags flags)
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/optional.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
|
||||
|
||||
@@ -42,7 +43,7 @@ namespace AzToolsFramework
|
||||
/**
|
||||
* Stores a valid Prefab Instance within a Prefab Dom. Useful for generating Templates
|
||||
* @param instance The instance to store
|
||||
* @param prefabDom the prefabDom that will be used to store the Instance data
|
||||
* @param prefabDom The prefabDom that will be used to store the Instance data
|
||||
* @return bool on whether the operation succeeded
|
||||
*/
|
||||
bool StoreInstanceInPrefabDom(const Instance& instance, PrefabDom& prefabDom);
|
||||
@@ -60,20 +61,32 @@ namespace AzToolsFramework
|
||||
/**
|
||||
* Loads a valid Prefab Instance from a Prefab Dom. Useful for generating Instances.
|
||||
* @param instance The Instance to load.
|
||||
* @param prefabDom the prefabDom that will be used to load the Instance data.
|
||||
* @param shouldClearContainers whether to clear containers in Instance while loading.
|
||||
* @param prefabDom The prefabDom that will be used to load the Instance data.
|
||||
* @param shouldClearContainers Whether to clear containers in Instance while loading.
|
||||
* @return bool on whether the operation succeeded.
|
||||
*/
|
||||
bool LoadInstanceFromPrefabDom(
|
||||
Instance& instance, const PrefabDom& prefabDom, LoadInstanceFlags flags = LoadInstanceFlags::None);
|
||||
|
||||
/**
|
||||
* Loads a valid Prefab Instance from a Prefab Dom. Useful for generating Instances.
|
||||
* @param instance The Instance to load.
|
||||
* @param referencedAssets AZ::Assets discovered during json load are added to this list
|
||||
* @param prefabDom The prefabDom that will be used to load the Instance data.
|
||||
* @param shouldClearContainers Whether to clear containers in Instance while loading.
|
||||
* @return bool on whether the operation succeeded.
|
||||
*/
|
||||
bool LoadInstanceFromPrefabDom(
|
||||
Instance& instance, const PrefabDom& prefabDom, AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& referencedAssets,
|
||||
LoadInstanceFlags flags = LoadInstanceFlags::None);
|
||||
|
||||
/**
|
||||
* Loads a valid Prefab Instance from a Prefab Dom. Useful for generating Instances.
|
||||
* @param instance The Instance to load.
|
||||
* @param newlyAddedEntities The new instances added during deserializing the instance. These are the entities found
|
||||
* in the prefabDom.
|
||||
* @param prefabDom the prefabDom that will be used to load the Instance data.
|
||||
* @param shouldClearContainers whether to clear containers in Instance while loading.
|
||||
* @param prefabDom The prefabDom that will be used to load the Instance data.
|
||||
* @param shouldClearContainers Whether to clear containers in Instance while loading.
|
||||
* @return bool on whether the operation succeeded.
|
||||
*/
|
||||
bool LoadInstanceFromPrefabDom(
|
||||
|
||||
@@ -122,30 +122,49 @@ namespace AzToolsFramework
|
||||
|
||||
AZ::EntityId containerEntityId = instanceToCreate->get().GetContainerEntityId();
|
||||
|
||||
// Parent the entities to the container entity. Parenting the container entities of the instances passed to createPrefab
|
||||
// will be done during the creation of links below.
|
||||
for (AZ::Entity* topLevelEntity : entities)
|
||||
{
|
||||
AZ::TransformBus::Event(topLevelEntity->GetId(), &AZ::TransformBus::Events::SetParent, containerEntityId);
|
||||
}
|
||||
|
||||
// Update the template of the instance since the entities are modified since the template creation.
|
||||
Prefab::PrefabDom serializedInstance;
|
||||
if (Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(instanceToCreate->get(), serializedInstance))
|
||||
{
|
||||
m_prefabSystemComponentInterface->UpdatePrefabTemplate(instanceToCreate->get().GetTemplateId(), serializedInstance);
|
||||
}
|
||||
|
||||
instanceToCreate->get().GetNestedInstances([&](AZStd::unique_ptr<Instance>& nestedInstance) {
|
||||
AZ_Assert(nestedInstance, "Invalid nested instance found in the new prefab created.");
|
||||
EntityOptionalReference nestedInstanceContainerEntity = nestedInstance->GetContainerEntity();
|
||||
AZ_Assert(
|
||||
nestedInstanceContainerEntity, "Invalid container entity found for the nested instance used in prefab creation.");
|
||||
|
||||
// These link creations shouldn't be undone because that would put the template in a non-usable state if a user
|
||||
// chooses to instantiate the template after undoing the creation.
|
||||
CreateLink(
|
||||
{&nestedInstanceContainerEntity->get()}, *nestedInstance, instanceToCreate->get().GetTemplateId(),
|
||||
undoBatch.GetUndoBatch(), containerEntityId);
|
||||
undoBatch.GetUndoBatch(), containerEntityId, false);
|
||||
});
|
||||
|
||||
// Create a link between the templates of the newly created instance and the instance it's being parented under.
|
||||
CreateLink(
|
||||
topLevelEntities, instanceToCreate->get(), commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch(),
|
||||
commonRootEntityId);
|
||||
topLevelEntities, instanceToCreate->get(), commonRootEntityOwningInstance->get().GetTemplateId(),
|
||||
undoBatch.GetUndoBatch(), commonRootEntityId);
|
||||
|
||||
// Change top level entities to be parented to the container entity
|
||||
// Mark them as dirty so this change is correctly applied to the template
|
||||
for (AZ::Entity* topLevelEntity : topLevelEntities)
|
||||
{
|
||||
AZ::EntityId topLevelEntityId = topLevelEntity->GetId();
|
||||
if (topLevelEntityId.IsValid())
|
||||
{
|
||||
m_prefabUndoCache.UpdateCache(topLevelEntityId);
|
||||
undoBatch.MarkEntityDirty(topLevelEntityId);
|
||||
AZ::TransformBus::Event(topLevelEntityId, &AZ::TransformBus::Events::SetParent, containerEntityId);
|
||||
m_prefabUndoCache.UpdateCache(topLevelEntity->GetId());
|
||||
|
||||
// Parenting entities would mark entities as dirty. But we want to unmark the top level entities as dirty because
|
||||
// if we don't, the template created would be updated and cause issues with undo operation followed by instantiation.
|
||||
ToolsApplicationRequests::Bus::Broadcast(
|
||||
&ToolsApplicationRequests::Bus::Events::RemoveDirtyEntity, topLevelEntity->GetId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +315,7 @@ namespace AzToolsFramework
|
||||
|
||||
void PrefabPublicHandler::CreateLink(
|
||||
const EntityList& topLevelEntities, Instance& sourceInstance, TemplateId targetTemplateId,
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId commonRootEntityId)
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId commonRootEntityId, const bool isUndoRedoSupportNeeded)
|
||||
{
|
||||
AZ::EntityId containerEntityId = sourceInstance.GetContainerEntityId();
|
||||
AZ::Entity* containerEntity = GetEntityById(containerEntityId);
|
||||
@@ -322,9 +341,19 @@ namespace AzToolsFramework
|
||||
m_instanceToTemplateInterface->GeneratePatch(patch, containerEntityDomBefore, containerEntityDomAfter);
|
||||
m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId);
|
||||
|
||||
LinkId linkId = PrefabUndoHelpers::CreateLink(
|
||||
sourceInstance.GetTemplateId(), targetTemplateId, patch, sourceInstance.GetInstanceAlias(),
|
||||
undoBatch);
|
||||
LinkId linkId;
|
||||
if (isUndoRedoSupportNeeded)
|
||||
{
|
||||
linkId = PrefabUndoHelpers::CreateLink(
|
||||
sourceInstance.GetTemplateId(), targetTemplateId, AZStd::move(patch), sourceInstance.GetInstanceAlias(), undoBatch);
|
||||
}
|
||||
else
|
||||
{
|
||||
linkId = m_prefabSystemComponentInterface->CreateLink(
|
||||
targetTemplateId, sourceInstance.GetTemplateId(), sourceInstance.GetInstanceAlias(), patch,
|
||||
InvalidLinkId);
|
||||
m_prefabSystemComponentInterface->PropagateTemplateChanges(targetTemplateId);
|
||||
}
|
||||
|
||||
sourceInstance.SetLinkId(linkId);
|
||||
|
||||
@@ -357,7 +386,7 @@ namespace AzToolsFramework
|
||||
patchesCopyForUndoSupport.CopyFrom(nestedInstanceLinkPatches->get(), patchesCopyForUndoSupport.GetAllocator());
|
||||
PrefabUndoHelpers::RemoveLink(
|
||||
sourceInstance->GetTemplateId(), targetTemplateId, sourceInstance->GetInstanceAlias(), sourceInstance->GetLinkId(),
|
||||
patchesCopyForUndoSupport, undoBatch);
|
||||
AZStd::move(patchesCopyForUndoSupport), undoBatch);
|
||||
}
|
||||
|
||||
PrefabOperationResult PrefabPublicHandler::SavePrefab(AZ::IO::Path filePath)
|
||||
|
||||
@@ -77,10 +77,11 @@ namespace AzToolsFramework
|
||||
* \param targetInstance The id of the target template.
|
||||
* \param undoBatch The undo batch to set as parent for this create link action.
|
||||
* \param commonRootEntityId The id of the entity that the source instance should be parented under.
|
||||
* \param isUndoRedoSupportNeeded The flag indicating whether the link should be created with undo/redo support or not.
|
||||
*/
|
||||
void CreateLink(
|
||||
const EntityList& topLevelEntities, Instance& sourceInstance, TemplateId targetTemplateId,
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId commonRootEntityId);
|
||||
UndoSystem::URSequencePoint* undoBatch, AZ::EntityId commonRootEntityId, const bool isUndoRedoSupportNeeded = true);
|
||||
|
||||
/**
|
||||
* Removes the link between template of the sourceInstance and the template corresponding to targetTemplateId.
|
||||
|
||||
@@ -583,7 +583,7 @@ namespace AzToolsFramework
|
||||
const TemplateId& linkTargetId,
|
||||
const TemplateId& linkSourceId,
|
||||
const InstanceAlias& instanceAlias,
|
||||
const PrefabDomReference linkPatch,
|
||||
const PrefabDomConstReference linkPatches,
|
||||
const LinkId& linkId)
|
||||
{
|
||||
if (linkTargetId == InvalidTemplateId)
|
||||
@@ -667,9 +667,9 @@ namespace AzToolsFramework
|
||||
rapidjson::StringRef(PrefabDomUtils::SourceName), rapidjson::StringRef(sourceTemplate.GetFilePath().c_str()),
|
||||
newLink.GetLinkDom().GetAllocator());
|
||||
|
||||
if (linkPatch && linkPatch->get().IsArray() && !(linkPatch->get().Empty()))
|
||||
if (linkPatches && linkPatches->get().IsArray() && !(linkPatches->get().Empty()))
|
||||
{
|
||||
m_instanceToTemplatePropagator.AddPatchesToLink(linkPatch.value(), newLink);
|
||||
m_instanceToTemplatePropagator.AddPatchesToLink(linkPatches.value(), newLink);
|
||||
}
|
||||
|
||||
//update the target template dom to have the proper values for the source template dom
|
||||
|
||||
@@ -156,7 +156,7 @@ namespace AzToolsFramework
|
||||
const TemplateId& linkTargetId,
|
||||
const TemplateId& linkSourceId,
|
||||
const InstanceAlias& instanceAlias,
|
||||
const PrefabDomReference linkPatch,
|
||||
const PrefabDomConstReference linkPatches,
|
||||
const LinkId& linkId = InvalidLinkId) override;
|
||||
|
||||
/**
|
||||
|
||||
+3
-3
@@ -43,9 +43,9 @@ namespace AzToolsFramework
|
||||
PrefabDomValue::MemberIterator& instanceIterator, InstanceOptionalReference instance) = 0;
|
||||
|
||||
//creates a new Link
|
||||
virtual LinkId CreateLink(const TemplateId& linkTargetId, const TemplateId& linkSourceId,
|
||||
const InstanceAlias& instanceAlias, const PrefabDomReference linkPatch,
|
||||
const LinkId& linkId = InvalidLinkId) = 0;
|
||||
virtual LinkId CreateLink(
|
||||
const TemplateId& linkTargetId, const TemplateId& linkSourceId, const InstanceAlias& instanceAlias,
|
||||
const PrefabDomConstReference linkPatches, const LinkId& linkId = InvalidLinkId) = 0;
|
||||
|
||||
virtual void RemoveLink(const LinkId& linkId) = 0;
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace AzToolsFramework
|
||||
const TemplateId& targetId,
|
||||
const TemplateId& sourceId,
|
||||
const InstanceAlias& instanceAlias,
|
||||
PrefabDomReference linkPatches,
|
||||
PrefabDom linkPatches,
|
||||
const LinkId linkId)
|
||||
{
|
||||
m_targetId = targetId;
|
||||
@@ -132,10 +132,7 @@ namespace AzToolsFramework
|
||||
m_instanceAlias = instanceAlias;
|
||||
m_linkId = linkId;
|
||||
|
||||
if (linkPatches.has_value())
|
||||
{
|
||||
m_linkPatches = AZStd::move(linkPatches->get());
|
||||
}
|
||||
m_linkPatches = AZStd::move(linkPatches);
|
||||
|
||||
//if linkId is invalid, set as ADD
|
||||
if (m_linkId == InvalidLinkId)
|
||||
@@ -228,7 +225,7 @@ namespace AzToolsFramework
|
||||
|
||||
if (link.has_value())
|
||||
{
|
||||
m_linkDomPrevious = AZStd::move(link->get().GetLinkDom());
|
||||
m_linkDomPrevious.CopyFrom(link->get().GetLinkDom(), m_linkDomPrevious.GetAllocator());
|
||||
}
|
||||
|
||||
//get source templateDom
|
||||
@@ -275,7 +272,7 @@ namespace AzToolsFramework
|
||||
if (patchesIter == m_linkDomNext.MemberEnd())
|
||||
{
|
||||
m_linkDomNext.AddMember(
|
||||
rapidjson::GenericStringRef(PrefabDomUtils::PatchesName), patchLinkCopy, m_linkDomNext.GetAllocator());
|
||||
rapidjson::GenericStringRef(PrefabDomUtils::PatchesName), AZStd::move(patchLinkCopy), m_linkDomNext.GetAllocator());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -303,9 +300,7 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
PrefabDom moveLink;
|
||||
moveLink.CopyFrom(linkDom, linkDom.GetAllocator());
|
||||
link->get().GetLinkDom() = AZStd::move(moveLink);
|
||||
link->get().SetLinkDom(linkDom);
|
||||
|
||||
//propagate the link changes
|
||||
link->get().UpdateTarget();
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace AzToolsFramework
|
||||
const TemplateId& targetId,
|
||||
const TemplateId& sourceId,
|
||||
const InstanceAlias& instanceAlias,
|
||||
PrefabDomReference linkPatches = PrefabDomReference(),
|
||||
PrefabDom linkPatches = PrefabDom(),
|
||||
const LinkId linkId = InvalidLinkId);
|
||||
|
||||
void Undo() override;
|
||||
|
||||
@@ -34,11 +34,11 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
LinkId CreateLink(
|
||||
TemplateId sourceTemplateId, TemplateId targetTemplateId, PrefabDomReference patch,
|
||||
TemplateId sourceTemplateId, TemplateId targetTemplateId, PrefabDom patch,
|
||||
const InstanceAlias& instanceAlias, UndoSystem::URSequencePoint* undoBatch)
|
||||
{
|
||||
auto linkAddUndo = aznew PrefabUndoInstanceLink("Create Link");
|
||||
linkAddUndo->Capture(targetTemplateId, sourceTemplateId, instanceAlias, patch, InvalidLinkId);
|
||||
linkAddUndo->Capture(targetTemplateId, sourceTemplateId, instanceAlias, AZStd::move(patch), InvalidLinkId);
|
||||
linkAddUndo->SetParent(undoBatch);
|
||||
linkAddUndo->Redo();
|
||||
|
||||
@@ -47,10 +47,10 @@ namespace AzToolsFramework
|
||||
|
||||
void RemoveLink(
|
||||
TemplateId sourceTemplateId, TemplateId targetTemplateId, const InstanceAlias& instanceAlias, LinkId linkId,
|
||||
PrefabDomReference linkPatches, UndoSystem::URSequencePoint* undoBatch)
|
||||
PrefabDom linkPatches, UndoSystem::URSequencePoint* undoBatch)
|
||||
{
|
||||
auto linkRemoveUndo = aznew PrefabUndoInstanceLink("Remove Link");
|
||||
linkRemoveUndo->Capture(targetTemplateId, sourceTemplateId, instanceAlias, linkPatches, linkId);
|
||||
linkRemoveUndo->Capture(targetTemplateId, sourceTemplateId, instanceAlias, AZStd::move(linkPatches), linkId);
|
||||
linkRemoveUndo->SetParent(undoBatch);
|
||||
linkRemoveUndo->Redo();
|
||||
}
|
||||
|
||||
@@ -22,11 +22,11 @@ namespace AzToolsFramework
|
||||
const Instance& instance, AZStd::string_view undoMessage, const PrefabDom& instanceDomBeforeUpdate,
|
||||
UndoSystem::URSequencePoint* undoBatch);
|
||||
LinkId CreateLink(
|
||||
TemplateId sourceTemplateId, TemplateId targetTemplateId, PrefabDomReference patch,
|
||||
TemplateId sourceTemplateId, TemplateId targetTemplateId, PrefabDom patch,
|
||||
const InstanceAlias& instanceAlias, UndoSystem::URSequencePoint* undoBatch);
|
||||
void RemoveLink(
|
||||
TemplateId sourceTemplateId, TemplateId targetTemplateId, const InstanceAlias& instanceAlias, LinkId linkId,
|
||||
PrefabDomReference linkPatches, UndoSystem::URSequencePoint* undoBatch);
|
||||
PrefabDom linkPatches, UndoSystem::URSequencePoint* undoBatch);
|
||||
}
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+1
-3
@@ -63,7 +63,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
AZStd::move(uniqueName), context.GetSourceUuid(), AZStd::move(serializer));
|
||||
AZ_Assert(spawnable, "Failed to create a new spawnable.");
|
||||
|
||||
bool result = SpawnableUtils::CreateSpawnable(*spawnable, prefab);
|
||||
bool result = SpawnableUtils::CreateSpawnable(*spawnable, prefab, object.GetReferencedAssets());
|
||||
if (result)
|
||||
{
|
||||
AzFramework::Spawnable::EntityList& entities = spawnable->GetEntities();
|
||||
@@ -84,8 +84,6 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
}
|
||||
SpawnableUtils::SortEntitiesByTransformHierarchy(*spawnable);
|
||||
context.GetProcessedObjects().push_back(AZStd::move(object));
|
||||
|
||||
context.RemovePrefab(prefabName);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+51
-24
@@ -10,6 +10,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
@@ -24,37 +26,14 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
return result.second;
|
||||
}
|
||||
|
||||
bool PrefabProcessorContext::RemovePrefab(AZStd::string_view prefabName)
|
||||
{
|
||||
if (!m_isIterating)
|
||||
{
|
||||
return m_prefabs.erase(prefabName) > 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_delayedDelete.emplace_back(prefabName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PrefabProcessorContext::ListPrefabs(const AZStd::function<void(AZStd::string_view, PrefabDom&)>& callback)
|
||||
{
|
||||
m_isIterating = true;
|
||||
for (auto& it : m_prefabs)
|
||||
{
|
||||
if (AZStd::find(m_delayedDelete.begin(), m_delayedDelete.end(), it.first) == m_delayedDelete.end())
|
||||
{
|
||||
callback(it.first, it.second);
|
||||
}
|
||||
callback(it.first, it.second);
|
||||
}
|
||||
m_isIterating = false;
|
||||
|
||||
// Clear out any prefabs that have been deleted.
|
||||
for (AZStd::string& deleted : m_delayedDelete)
|
||||
{
|
||||
m_prefabs.erase(deleted);
|
||||
}
|
||||
m_delayedDelete.clear();
|
||||
}
|
||||
|
||||
void PrefabProcessorContext::ListPrefabs(const AZStd::function<void(AZStd::string_view, const PrefabDom&)>& callback) const
|
||||
@@ -70,6 +49,44 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
return !m_prefabs.empty();
|
||||
}
|
||||
|
||||
bool PrefabProcessorContext::RegisterSpawnableProductAssetDependency(AZStd::string prefabName, AZStd::string dependentPrefabName)
|
||||
{
|
||||
using ConversionUtils = PrefabConversionUtils::ProcessedObjectStore;
|
||||
|
||||
prefabName += AzFramework::Spawnable::DotFileExtension;
|
||||
uint32_t spawnableSubId = ConversionUtils::BuildSubId(AZStd::move(prefabName));
|
||||
|
||||
dependentPrefabName += AzFramework::Spawnable::DotFileExtension;
|
||||
uint32_t spawnablePrefabSubId = ConversionUtils::BuildSubId(AZStd::move(dependentPrefabName));
|
||||
|
||||
return RegisterSpawnableProductAssetDependency(spawnableSubId, spawnablePrefabSubId);
|
||||
}
|
||||
|
||||
bool PrefabProcessorContext::RegisterSpawnableProductAssetDependency(AZStd::string prefabName, const AZ::Data::AssetId& dependentAssetId)
|
||||
{
|
||||
using ConversionUtils = PrefabConversionUtils::ProcessedObjectStore;
|
||||
|
||||
prefabName += AzFramework::Spawnable::DotFileExtension;
|
||||
uint32_t spawnableSubId = ConversionUtils::BuildSubId(AZStd::move(prefabName));
|
||||
|
||||
AZ::Data::AssetId spawnableAssetId(GetSourceUuid(), spawnableSubId);
|
||||
|
||||
return RegisterProductAssetDependency(spawnableAssetId, dependentAssetId);
|
||||
}
|
||||
|
||||
bool PrefabProcessorContext::RegisterSpawnableProductAssetDependency(uint32_t spawnableAssetSubId, uint32_t dependentSpawnableAssetSubId)
|
||||
{
|
||||
AZ::Data::AssetId spawnableAssetId(GetSourceUuid(), spawnableAssetSubId);
|
||||
AZ::Data::AssetId dependentSpawnableAssetId(GetSourceUuid(), dependentSpawnableAssetSubId);
|
||||
|
||||
return RegisterProductAssetDependency(spawnableAssetId, dependentSpawnableAssetId);
|
||||
}
|
||||
|
||||
bool PrefabProcessorContext::RegisterProductAssetDependency(const AZ::Data::AssetId& assetId, const AZ::Data::AssetId& dependentAssetId)
|
||||
{
|
||||
return m_registeredProductAssetDependencies[assetId].emplace(dependentAssetId).second;
|
||||
}
|
||||
|
||||
PrefabProcessorContext::ProcessedObjectStoreContainer& PrefabProcessorContext::GetProcessedObjects()
|
||||
{
|
||||
return m_products;
|
||||
@@ -80,6 +97,16 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
return m_products;
|
||||
}
|
||||
|
||||
PrefabProcessorContext::ProductAssetDependencyContainer& PrefabProcessorContext::GetRegisteredProductAssetDependencies()
|
||||
{
|
||||
return m_registeredProductAssetDependencies;
|
||||
}
|
||||
|
||||
const PrefabProcessorContext::ProductAssetDependencyContainer& PrefabProcessorContext::GetRegisteredProductAssetDependencies() const
|
||||
{
|
||||
return m_registeredProductAssetDependencies;
|
||||
}
|
||||
|
||||
void PrefabProcessorContext::SetPlatformTags(AZ::PlatformTagSet tags)
|
||||
{
|
||||
m_platformTags = AZStd::move(tags);
|
||||
|
||||
+12
-2
@@ -29,6 +29,8 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
public:
|
||||
using ProcessedObjectStoreContainer = AZStd::vector<ProcessedObjectStore>;
|
||||
using ProductAssetDependencyContainer =
|
||||
AZStd::unordered_map<AZ::Data::AssetId, AZStd::unordered_set<AZ::Data::AssetId>>;
|
||||
|
||||
AZ_CLASS_ALLOCATOR(PrefabProcessorContext, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(PrefabProcessorContext, "{C7D77E3A-C544-486B-B774-7C82C38FE22F}");
|
||||
@@ -37,14 +39,21 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
virtual ~PrefabProcessorContext() = default;
|
||||
|
||||
virtual bool AddPrefab(AZStd::string prefabName, PrefabDom prefab);
|
||||
virtual bool RemovePrefab(AZStd::string_view prefabName);
|
||||
virtual void ListPrefabs(const AZStd::function<void(AZStd::string_view, PrefabDom&)>& callback);
|
||||
virtual void ListPrefabs(const AZStd::function<void(AZStd::string_view, const PrefabDom&)>& callback) const;
|
||||
virtual bool HasPrefabs() const;
|
||||
|
||||
virtual bool RegisterSpawnableProductAssetDependency(AZStd::string prefabName, AZStd::string dependentPrefabName);
|
||||
virtual bool RegisterSpawnableProductAssetDependency(AZStd::string prefabName, const AZ::Data::AssetId& dependentAssetId);
|
||||
virtual bool RegisterSpawnableProductAssetDependency(uint32_t spawnableAssetSubId, uint32_t dependentSpawnableAssetSubId);
|
||||
virtual bool RegisterProductAssetDependency(const AZ::Data::AssetId& assetId, const AZ::Data::AssetId& dependentAssetId);
|
||||
|
||||
virtual ProcessedObjectStoreContainer& GetProcessedObjects();
|
||||
virtual const ProcessedObjectStoreContainer& GetProcessedObjects() const;
|
||||
|
||||
virtual ProductAssetDependencyContainer& GetRegisteredProductAssetDependencies();
|
||||
virtual const ProductAssetDependencyContainer& GetRegisteredProductAssetDependencies() const;
|
||||
|
||||
virtual void SetPlatformTags(AZ::PlatformTagSet tags);
|
||||
virtual const AZ::PlatformTagSet& GetPlatformTags() const;
|
||||
virtual const AZ::Uuid& GetSourceUuid() const;
|
||||
@@ -57,7 +66,8 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
|
||||
NamedPrefabContainer m_prefabs;
|
||||
ProcessedObjectStoreContainer m_products;
|
||||
AZStd::vector<AZStd::string> m_delayedDelete;
|
||||
ProductAssetDependencyContainer m_registeredProductAssetDependencies;
|
||||
|
||||
AZ::PlatformTagSet m_platformTags;
|
||||
AZ::Uuid m_sourceUuid;
|
||||
bool m_isIterating{ false };
|
||||
|
||||
+10
@@ -56,6 +56,16 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
return *m_asset;
|
||||
}
|
||||
|
||||
AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& ProcessedObjectStore::GetReferencedAssets()
|
||||
{
|
||||
return m_referencedAssets;
|
||||
}
|
||||
|
||||
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& ProcessedObjectStore::GetReferencedAssets() const
|
||||
{
|
||||
return m_referencedAssets;
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<AZ::Data::AssetData> ProcessedObjectStore::ReleaseAsset()
|
||||
{
|
||||
return AZStd::move(m_asset);
|
||||
|
||||
+5
@@ -48,6 +48,10 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
AZ::Data::AssetData& GetAsset();
|
||||
AZStd::unique_ptr<AZ::Data::AssetData> ReleaseAsset();
|
||||
|
||||
AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& GetReferencedAssets();
|
||||
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& GetReferencedAssets() const;
|
||||
|
||||
|
||||
const AZStd::string& GetId() const;
|
||||
|
||||
private:
|
||||
@@ -55,6 +59,7 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
|
||||
SerializerFunction m_assetSerializer;
|
||||
AZStd::unique_ptr<AZ::Data::AssetData> m_asset;
|
||||
AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>> m_referencedAssets;
|
||||
AZStd::string m_uniqueId;
|
||||
};
|
||||
|
||||
|
||||
+9
-2
@@ -28,16 +28,23 @@ namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
AzFramework::Spawnable CreateSpawnable(const PrefabDom& prefabDom)
|
||||
{
|
||||
AzFramework::Spawnable spawnable;
|
||||
[[maybe_unused]] bool result = CreateSpawnable(spawnable, prefabDom);
|
||||
AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>> referencedAssets;
|
||||
[[maybe_unused]] bool result = CreateSpawnable(spawnable, prefabDom, referencedAssets);
|
||||
AZ_Assert(result,
|
||||
"Failed to Load Prefab Instance from given Prefab DOM while Spawnable creation.");
|
||||
return spawnable;
|
||||
}
|
||||
|
||||
bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom)
|
||||
{
|
||||
AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>> referencedAssets;
|
||||
return CreateSpawnable(spawnable, prefabDom, referencedAssets);
|
||||
}
|
||||
|
||||
bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom, AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& referencedAssets)
|
||||
{
|
||||
Instance instance;
|
||||
if (Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(instance, prefabDom,
|
||||
if (Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(instance, prefabDom, referencedAssets,
|
||||
Prefab::PrefabDomUtils::LoadInstanceFlags::AssignRandomEntityId)) // Always assign random entity ids because the spawnable is
|
||||
// going to be used to create clones of the entities.
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
{
|
||||
AzFramework::Spawnable CreateSpawnable(const PrefabDom& prefabDom);
|
||||
bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom);
|
||||
bool CreateSpawnable(AzFramework::Spawnable& spawnable, const PrefabDom& prefabDom, AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& referencedAssets);
|
||||
|
||||
void SortEntitiesByTransformHierarchy(AzFramework::Spawnable& spawnable);
|
||||
} // namespace AzToolsFramework::Prefab::SpawnableUtils
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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/std/smart_ptr/make_shared.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzToolsFramework/Thumbnails/ThumbnailerNullComponent.h>
|
||||
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
|
||||
#include <AzToolsFramework/Thumbnails/MissingThumbnail.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Thumbnailer
|
||||
{
|
||||
ThumbnailerNullComponent::ThumbnailerNullComponent() :
|
||||
m_nullThumbnail()
|
||||
{
|
||||
}
|
||||
|
||||
ThumbnailerNullComponent::~ThumbnailerNullComponent() = default;
|
||||
|
||||
void ThumbnailerNullComponent::Activate()
|
||||
{
|
||||
BusConnect();
|
||||
}
|
||||
|
||||
void ThumbnailerNullComponent::Deactivate()
|
||||
{
|
||||
BusDisconnect();
|
||||
}
|
||||
|
||||
void ThumbnailerNullComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serialize)
|
||||
{
|
||||
serialize->Class<ThumbnailerNullComponent, AZ::Component>();
|
||||
}
|
||||
}
|
||||
|
||||
void ThumbnailerNullComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
|
||||
{
|
||||
services.push_back(AZ_CRC("ThumbnailerService", 0x65422b97));
|
||||
}
|
||||
|
||||
void ThumbnailerNullComponent::RegisterContext(const char* /*contextName*/)
|
||||
{
|
||||
}
|
||||
|
||||
void ThumbnailerNullComponent::UnregisterContext(const char* /*contextName*/)
|
||||
{
|
||||
}
|
||||
|
||||
bool ThumbnailerNullComponent::HasContext(const char* /*contextName*/) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void ThumbnailerNullComponent::RegisterThumbnailProvider(AzToolsFramework::Thumbnailer::SharedThumbnailProvider /*provider*/, const char* /*contextName*/)
|
||||
{
|
||||
}
|
||||
|
||||
void ThumbnailerNullComponent::UnregisterThumbnailProvider(const char* /*providerName*/, const char* /*contextName*/)
|
||||
{
|
||||
}
|
||||
|
||||
AzToolsFramework::Thumbnailer::SharedThumbnail ThumbnailerNullComponent::GetThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey /*key*/, const char* /*contextName*/)
|
||||
{
|
||||
return m_nullThumbnail;
|
||||
}
|
||||
|
||||
bool ThumbnailerNullComponent::IsLoading(AzToolsFramework::Thumbnailer::SharedThumbnailKey /*thumbnailKey*/, const char* /*contextName*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} // namespace Thumbnailer
|
||||
} // namespace AzToolsFramework
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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/Component/Component.h>
|
||||
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
|
||||
|
||||
// ThumbnailerNullComponent is an alternative to ThumbnailerComponent that can be used by tools that don't use Qt.
|
||||
// It doesn't do anything (hence "null"), but it allows the system and editor components that rely on the ThumbnailService
|
||||
// to start up and function.
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Thumbnailer
|
||||
{
|
||||
class ThumbnailContext;
|
||||
|
||||
class ThumbnailerNullComponent
|
||||
: public AZ::Component
|
||||
, public AzToolsFramework::Thumbnailer::ThumbnailerRequestsBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(ThumbnailerNullComponent, "{8009D651-3FAA-9815-B99E-AF174A3B29D4}")
|
||||
|
||||
ThumbnailerNullComponent();
|
||||
virtual ~ThumbnailerNullComponent();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// AZ::Component
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// ThumbnailerRequests
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void RegisterContext(const char* contextName) override;
|
||||
void UnregisterContext(const char* contextName) override;
|
||||
bool HasContext(const char* contextName) const override;
|
||||
void RegisterThumbnailProvider(AzToolsFramework::Thumbnailer::SharedThumbnailProvider provider, const char* contextName) override;
|
||||
void UnregisterThumbnailProvider(const char* providerName, const char* contextName) override;
|
||||
AzToolsFramework::Thumbnailer::SharedThumbnail GetThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, const char* contextName) override;
|
||||
bool IsLoading(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, const char* contextName) override;
|
||||
private:
|
||||
AzToolsFramework::Thumbnailer::SharedThumbnail m_nullThumbnail;
|
||||
};
|
||||
} // Thumbnailer
|
||||
} // namespace AzToolsFramework
|
||||
-23
@@ -63,29 +63,6 @@ namespace AzToolsFramework
|
||||
void EditorNonUniformScaleComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
incompatible.push_back(AZ_CRC_CE("NonUniformScaleService"));
|
||||
|
||||
incompatible.push_back(AZ_CRC_CE("DebugDrawObbService"));
|
||||
incompatible.push_back(AZ_CRC_CE("DebugDrawService"));
|
||||
incompatible.push_back(AZ_CRC_CE("EMotionFXActorService"));
|
||||
incompatible.push_back(AZ_CRC_CE("EMotionFXSimpleMotionService"));
|
||||
incompatible.push_back(AZ_CRC_CE("GradientTransformService"));
|
||||
incompatible.push_back(AZ_CRC_CE("LegacyMeshService"));
|
||||
incompatible.push_back(AZ_CRC_CE("LookAtService"));
|
||||
incompatible.push_back(AZ_CRC_CE("SequenceService"));
|
||||
incompatible.push_back(AZ_CRC_CE("ClothMeshService"));
|
||||
incompatible.push_back(AZ_CRC_CE("PhysXJointService"));
|
||||
incompatible.push_back(AZ_CRC_CE("PhysXCharacterControllerService"));
|
||||
incompatible.push_back(AZ_CRC_CE("PhysXRagdollService"));
|
||||
incompatible.push_back(AZ_CRC_CE("WhiteBoxService"));
|
||||
incompatible.push_back(AZ_CRC_CE("NavigationAreaService"));
|
||||
incompatible.push_back(AZ_CRC_CE("GeometryService"));
|
||||
incompatible.push_back(AZ_CRC_CE("CapsuleShapeService"));
|
||||
incompatible.push_back(AZ_CRC_CE("CompoundShapeService"));
|
||||
incompatible.push_back(AZ_CRC_CE("CylinderShapeService"));
|
||||
incompatible.push_back(AZ_CRC_CE("DiskShapeService"));
|
||||
incompatible.push_back(AZ_CRC_CE("SphereShapeService"));
|
||||
incompatible.push_back(AZ_CRC_CE("SplineService"));
|
||||
incompatible.push_back(AZ_CRC_CE("TubeShapeService"));
|
||||
}
|
||||
|
||||
void EditorNonUniformScaleComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
|
||||
+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.")->
|
||||
|
||||
+30
-3
@@ -63,23 +63,40 @@ namespace AzToolsFramework
|
||||
|
||||
void PropertyManagerComponent::Deactivate()
|
||||
{
|
||||
// Delete all remaining auto-delete or built-in handlers.
|
||||
for (auto it = m_builtInHandlers.begin(); it != m_builtInHandlers.end(); ++it)
|
||||
{
|
||||
UnregisterPropertyType(*it);
|
||||
#ifdef AZ_DEBUG_BUILD
|
||||
// For debug builds, we'll take the extra time to delete each handler that we're deleting from m_Handlers.
|
||||
// We loop through m_Handlers below to ensure that we don't have any other handlers still registered after
|
||||
// we've deleted these.
|
||||
AZStd::erase_if(m_Handlers, [it](const auto& item) {
|
||||
auto const& [key, value] = item;
|
||||
return (key == (*it)->GetHandlerName()) && (value == (*it));
|
||||
});
|
||||
#endif
|
||||
|
||||
delete *it;
|
||||
}
|
||||
|
||||
m_builtInHandlers.clear();
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
#ifdef AZ_DEBUG_BUILD
|
||||
// Loop through all the remaining registered handlers (if any) and print out an error, as these all are probably memory
|
||||
// leaks. UnregisterPropertyType should have been called on these already, and their pointers should have been deleted
|
||||
// by the caller.
|
||||
auto it = m_Handlers.begin();
|
||||
while (it != m_Handlers.end())
|
||||
{
|
||||
AZ_Error("PropertyManager", false, "Property Handler 0x%08x is still registered during shutdown", it->first);
|
||||
++it;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
m_Handlers.clear();
|
||||
m_DefaultHandlers.clear();
|
||||
|
||||
PropertyTypeRegistrationMessages::Bus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
@@ -142,6 +159,16 @@ namespace AzToolsFramework
|
||||
}
|
||||
++defaultIt;
|
||||
}
|
||||
|
||||
if (pHandler->AutoDelete())
|
||||
{
|
||||
m_builtInHandlers.erase(AZStd::remove(m_builtInHandlers.begin(), m_builtInHandlers.end(), pHandler),
|
||||
m_builtInHandlers.end());
|
||||
AZ_Assert(false,
|
||||
"Handlers with AutoDelete set should not call UnregisterPropertyType. To fix, do one of the following:\n"
|
||||
" 1. Set AutoDelete to false in the handler, call UnregisterPropertyType, and the caller should delete the handler.\n"
|
||||
" 2. Set AutoDelete to true in the handler and do NOT call UnregisterPropertyType or delete the handler.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+32
-9
@@ -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>
|
||||
@@ -234,16 +237,15 @@ namespace AzToolsFramework
|
||||
mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Alt();
|
||||
}
|
||||
|
||||
static bool IndividualSelect(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
static bool IndividualSelect(const AzFramework::ClickDetector::ClickOutcome clickOutcome)
|
||||
{
|
||||
return mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() &&
|
||||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down;
|
||||
return clickOutcome == AzFramework::ClickDetector::ClickOutcome::Click;
|
||||
}
|
||||
|
||||
static bool AdditiveIndividualSelect(const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
static bool AdditiveIndividualSelect(
|
||||
const AzFramework::ClickDetector::ClickOutcome clickOutcome, const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
|
||||
{
|
||||
return mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() &&
|
||||
mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down &&
|
||||
return clickOutcome == AzFramework::ClickDetector::ClickOutcome::Click &&
|
||||
mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Ctrl() &&
|
||||
!mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Alt();
|
||||
}
|
||||
@@ -1780,6 +1782,25 @@ namespace AzToolsFramework
|
||||
|
||||
m_cachedEntityIdUnderCursor = m_editorHelpers->HandleMouseInteraction(cameraState, mouseInteraction);
|
||||
|
||||
const AzFramework::ClickDetector::ClickEvent selectClickEvent = [&mouseInteraction] {
|
||||
if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left())
|
||||
{
|
||||
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down)
|
||||
{
|
||||
return AzFramework::ClickDetector::ClickEvent::Down;
|
||||
}
|
||||
|
||||
if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Up)
|
||||
{
|
||||
return AzFramework::ClickDetector::ClickEvent::Up;
|
||||
}
|
||||
}
|
||||
return AzFramework::ClickDetector::ClickEvent::Nil;
|
||||
}();
|
||||
|
||||
m_cursorState.SetCurrentPosition(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates);
|
||||
const auto clickOutcome = m_clickDetector.DetectClick(selectClickEvent, m_cursorState.CursorDelta());
|
||||
|
||||
// for entities selected with no bounds of their own (just TransformComponent)
|
||||
// check selection against the selection indicator aabb
|
||||
for (AZ::EntityId entityId : m_selectedEntityIds)
|
||||
@@ -1838,7 +1859,7 @@ namespace AzToolsFramework
|
||||
if (!m_selectedEntityIds.empty())
|
||||
{
|
||||
// select/deselect (add/remove) entities with ctrl held
|
||||
if (Input::AdditiveIndividualSelect(mouseInteraction))
|
||||
if (Input::AdditiveIndividualSelect(clickOutcome, mouseInteraction))
|
||||
{
|
||||
if (SelectDeselect(entityIdUnderCursor))
|
||||
{
|
||||
@@ -2020,7 +2041,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
// standard toggle selection
|
||||
if (Input::IndividualSelect(mouseInteraction))
|
||||
if (Input::IndividualSelect(clickOutcome))
|
||||
{
|
||||
SelectDeselect(entityIdUnderCursor);
|
||||
}
|
||||
@@ -2523,7 +2544,7 @@ namespace AzToolsFramework
|
||||
// create the cluster for changing transform mode
|
||||
ViewportUi::ViewportUiRequestBus::EventResult(
|
||||
m_transformModeClusterId, ViewportUi::DefaultViewportId,
|
||||
&ViewportUi::ViewportUiRequestBus::Events::CreateCluster);
|
||||
&ViewportUi::ViewportUiRequestBus::Events::CreateCluster, ViewportUi::Alignment::TopLeft);
|
||||
|
||||
// create and register the buttons (strings correspond to icons even if the values appear different)
|
||||
m_translateButtonId = RegisterClusterButton(m_transformModeClusterId, "Move");
|
||||
@@ -3264,6 +3285,8 @@ namespace AzToolsFramework
|
||||
const auto modifiers = ViewportInteraction::KeyboardModifiers(
|
||||
ViewportInteraction::TranslateKeyboardModifiers(QApplication::queryKeyboardModifiers()));
|
||||
|
||||
m_cursorState.Update();
|
||||
|
||||
HandleAccents(
|
||||
!m_selectedEntityIds.empty(), m_cachedEntityIdUnderCursor,
|
||||
modifiers.Ctrl(), m_hoveredEntityId,
|
||||
|
||||
+80
-76
@@ -17,6 +17,8 @@
|
||||
#include <AzCore/std/optional.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzFramework/Components/CameraBus.h>
|
||||
#include <AzFramework/Viewport/ClickDetector.h>
|
||||
#include <AzFramework/Viewport/CursorState.h>
|
||||
#include <AzToolsFramework/API/EditorCameraBus.h>
|
||||
#include <AzToolsFramework/Commands/EntityManipulatorCommand.h>
|
||||
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
|
||||
@@ -35,37 +37,37 @@ namespace AzToolsFramework
|
||||
{
|
||||
class EditorVisibleEntityDataCache;
|
||||
|
||||
using EntityIdSet = AZStd::unordered_set<AZ::EntityId>; ///< Alias for unordered_set of EntityIds.
|
||||
using EntityIdSet = AZStd::unordered_set<AZ::EntityId>; //!< Alias for unordered_set of EntityIds.
|
||||
|
||||
/// Entity related data required by manipulators during action.
|
||||
//! Entity related data required by manipulators during action.
|
||||
struct EntityIdManipulatorLookup
|
||||
{
|
||||
AZ::Transform m_initial; /// Transform of Entity at mouse down on manipulator.
|
||||
AZ::Transform m_initial; //!< Transform of Entity at mouse down on manipulator.
|
||||
};
|
||||
|
||||
/// Alias for a mapping between EntityIds and Entity related data required by manipulators.
|
||||
//! Alias for a mapping between EntityIds and Entity related data required by manipulators.
|
||||
using EntityIdManipulatorLookups = AZStd::unordered_map<AZ::EntityId, EntityIdManipulatorLookup>;
|
||||
|
||||
/// Generic wrapper to handle specific manipulators controlling 1-* entities.
|
||||
//! Generic wrapper to handle specific manipulators controlling 1-* entities.
|
||||
struct EntityIdManipulators
|
||||
{
|
||||
EntityIdManipulatorLookups m_lookups; ///< Mapping between the EntityId and the transform of the Entity at
|
||||
///< the point a manipulator started adjusting it.
|
||||
AZStd::unique_ptr<Manipulators> m_manipulators; ///< The aggregate manipulator currently in use.
|
||||
EntityIdManipulatorLookups m_lookups; //!< Mapping between the EntityId and the transform of the Entity at
|
||||
//!< the point a manipulator started adjusting it.
|
||||
AZStd::unique_ptr<Manipulators> m_manipulators; //!< The aggregate manipulator currently in use.
|
||||
};
|
||||
|
||||
/// Store translation and orientation only (no scale).
|
||||
//! Store translation and orientation only (no scale).
|
||||
struct Frame
|
||||
{
|
||||
AZ::Vector3 m_translation = AZ::Vector3::CreateZero(); ///< Position of frame.
|
||||
AZ::Quaternion m_orientation = AZ::Quaternion::CreateIdentity(); ///< Orientation of frame.
|
||||
AZ::Vector3 m_translation = AZ::Vector3::CreateZero(); //!< Position of frame.
|
||||
AZ::Quaternion m_orientation = AZ::Quaternion::CreateIdentity(); //!< Orientation of frame.
|
||||
};
|
||||
|
||||
/// Temporary manipulator frame used during selection.
|
||||
//! Temporary manipulator frame used during selection.
|
||||
struct OptionalFrame
|
||||
{
|
||||
/// What part of the transform did we pick (when using ditto on
|
||||
/// the manipulator). This will depend on the transform mode we're in.
|
||||
//! What part of the transform did we pick (when using ditto on
|
||||
//! the manipulator). This will depend on the transform mode we're in.
|
||||
struct PickType
|
||||
{
|
||||
enum : AZ::u8
|
||||
@@ -83,29 +85,29 @@ namespace AzToolsFramework
|
||||
bool PickedTranslation() const;
|
||||
bool PickedOrientation() const;
|
||||
|
||||
/// Clear all state associated with the frame.
|
||||
//! Clear all state associated with the frame.
|
||||
void Reset();
|
||||
/// Clear only picked translation state.
|
||||
//! Clear only picked translation state.
|
||||
void ResetPickedTranslation();
|
||||
/// Clear only picked orientation state.
|
||||
//! Clear only picked orientation state.
|
||||
void ResetPickedOrientation();
|
||||
|
||||
AZ::EntityId m_pickedEntityIdOverride; ///< 'Picked' Entity - frame and parent space relative to this if active.
|
||||
AZStd::optional<AZ::Vector3> m_translationOverride; ///< Translation override, if set, reset when selection is empty.
|
||||
AZStd::optional<AZ::Quaternion> m_orientationOverride; ///< Orientation override, if set, reset when selection is empty.
|
||||
AZ::u8 m_pickTypes = PickType::None; ///< What mode(s) were we in when picking an EntityId override.
|
||||
AZ::EntityId m_pickedEntityIdOverride; //!< 'Picked' Entity - frame and parent space relative to this if active.
|
||||
AZStd::optional<AZ::Vector3> m_translationOverride; //!< Translation override, if set, reset when selection is empty.
|
||||
AZStd::optional<AZ::Quaternion> m_orientationOverride; //!< Orientation override, if set, reset when selection is empty.
|
||||
AZ::u8 m_pickTypes = PickType::None; //!< What mode(s) were we in when picking an EntityId override.
|
||||
};
|
||||
|
||||
/// What frame/space is the manipulator currently operating in.
|
||||
//! What frame/space is the manipulator currently operating in.
|
||||
enum class ReferenceFrame
|
||||
{
|
||||
Local, /// The local space of the individual entity.
|
||||
Parent, /// The parent space of the individual entity (world space if no parent exists).
|
||||
World, /// World space (space aligned to world axes - identity).
|
||||
Local, //!< The local space of the individual entity.
|
||||
Parent, //!< The parent space of the individual entity (world space if no parent exists).
|
||||
World, //!< World space (space aligned to world axes - identity).
|
||||
};
|
||||
|
||||
/// Entity selection/interaction handling.
|
||||
/// Provide a suite of functionality for manipulating entities, primarily through their TransformComponent.
|
||||
//! Entity selection/interaction handling.
|
||||
//! Provide a suite of functionality for manipulating entities, primarily through their TransformComponent.
|
||||
class EditorTransformComponentSelection
|
||||
: public ViewportInteraction::ViewportSelectionRequests
|
||||
, private EditorEventsBus::Handler
|
||||
@@ -127,15 +129,15 @@ namespace AzToolsFramework
|
||||
EditorTransformComponentSelection& operator=(const EditorTransformComponentSelection&) = delete;
|
||||
virtual ~EditorTransformComponentSelection();
|
||||
|
||||
/// Register entity manipulators with the ManipulatorManager.
|
||||
/// After being registered, the entity manipulators will draw and check for input.
|
||||
//! Register entity manipulators with the ManipulatorManager.
|
||||
//! After being registered, the entity manipulators will draw and check for input.
|
||||
void RegisterManipulator();
|
||||
/// Unregister entity manipulators with the ManipulatorManager.
|
||||
/// No longer draw or respond to input.
|
||||
//! Unregister entity manipulators with the ManipulatorManager.
|
||||
//! No longer draw or respond to input.
|
||||
void UnregisterManipulator();
|
||||
|
||||
/// ViewportInteraction::ViewportSelectionRequests
|
||||
/// Intercept all viewport mouse events and respond to inputs.
|
||||
//! ViewportInteraction::ViewportSelectionRequests
|
||||
//! Intercept all viewport mouse events and respond to inputs.
|
||||
bool HandleMouseInteraction(
|
||||
const ViewportInteraction::MouseInteractionEvent& mouseInteraction) override;
|
||||
void DisplayViewportSelection(
|
||||
@@ -145,9 +147,9 @@ namespace AzToolsFramework
|
||||
const AzFramework::ViewportInfo& viewportInfo,
|
||||
AzFramework::DebugDisplayRequests& debugDisplay) override;
|
||||
|
||||
/// Add an entity to the current selection
|
||||
//! Add an entity to the current selection
|
||||
void AddEntityToSelection(AZ::EntityId entityId);
|
||||
/// Remove an entity from the current selection
|
||||
//! Remove an entity from the current selection
|
||||
void RemoveEntityFromSelection(AZ::EntityId entityId);
|
||||
|
||||
private:
|
||||
@@ -161,8 +163,8 @@ namespace AzToolsFramework
|
||||
|
||||
void ClearManipulatorTranslationOverride();
|
||||
void ClearManipulatorOrientationOverride();
|
||||
/// Handle an event triggered by the user to clear any manipulator overrides.
|
||||
/// Delegate to either translation or orientation reset/clear depending on the state we're in.
|
||||
//! Handle an event triggered by the user to clear any manipulator overrides.
|
||||
//! Delegate to either translation or orientation reset/clear depending on the state we're in.
|
||||
void DelegateClearManipulatorOverride();
|
||||
|
||||
void ToggleCenterPivotSelection();
|
||||
@@ -251,63 +253,65 @@ namespace AzToolsFramework
|
||||
void SetEntityLocalScale(AZ::EntityId entityId, const AZ::Vector3& localScale);
|
||||
void SetEntityLocalRotation(AZ::EntityId entityId, const AZ::Vector3& localRotation);
|
||||
|
||||
AZ::EntityId m_hoveredEntityId; ///< What EntityId is the mouse currently hovering over (if any).
|
||||
AZ::EntityId m_cachedEntityIdUnderCursor; ///< Store the EntityId on each mouse move for use in Display.
|
||||
AZ::EntityId m_editorCameraComponentEntityId; ///< The EditorCameraComponent EntityId if it is set.
|
||||
EntityIdSet m_selectedEntityIds; ///< Represents the current entities in the selection.
|
||||
AZ::EntityId m_hoveredEntityId; //!< What EntityId is the mouse currently hovering over (if any).
|
||||
AZ::EntityId m_cachedEntityIdUnderCursor; //!< Store the EntityId on each mouse move for use in Display.
|
||||
AZ::EntityId m_editorCameraComponentEntityId; //!< The EditorCameraComponent EntityId if it is set.
|
||||
EntityIdSet m_selectedEntityIds; //!< Represents the current entities in the selection.
|
||||
|
||||
const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; ///< A cache of packed EntityData that can be
|
||||
///< iterated over efficiently without the need
|
||||
///< to make individual EBus calls.
|
||||
AZStd::unique_ptr<EditorHelpers> m_editorHelpers; ///< Editor visualization of entities (icons, shapes, debug visuals etc).
|
||||
EntityIdManipulators m_entityIdManipulators; ///< Mapping from a Manipulator to potentially many EntityIds.
|
||||
const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; //!< A cache of packed EntityData that can be
|
||||
//!< iterated over efficiently without the need
|
||||
//!< to make individual EBus calls.
|
||||
AZStd::unique_ptr<EditorHelpers> m_editorHelpers; //!< Editor visualization of entities (icons, shapes, debug visuals etc).
|
||||
EntityIdManipulators m_entityIdManipulators; //!< Mapping from a Manipulator to potentially many EntityIds.
|
||||
|
||||
EditorBoxSelect m_boxSelect; ///< Type responsible for handling box select.
|
||||
AZStd::unique_ptr<EntityManipulatorCommand> m_manipulatorMoveCommand; ///< Track adjustments to manipulator translation and orientation (during mouse press/move).
|
||||
AZStd::vector<AZStd::unique_ptr<QAction>> m_actions; ///< What actions are tied to this handler.
|
||||
ViewportInteraction::KeyboardModifiers m_previousModifiers; ///< What modifiers were held last frame.
|
||||
EditorContextMenu m_contextMenu; ///< Viewport right click context menu.
|
||||
OptionalFrame m_pivotOverrideFrame; ///< Has a pivot override been set.
|
||||
Mode m_mode = Mode::Translation; ///< Manipulator mode - default to translation.
|
||||
Pivot m_pivotMode = Pivot::Object; ///< Entity pivot mode - default to object (authored root).
|
||||
ReferenceFrame m_referenceFrame = ReferenceFrame::Parent; ///< What reference frame is the Manipulator currently operating in.
|
||||
Frame m_axisPreview; ///< Axes of entity at the time of mouse down to indicate delta of translation.
|
||||
bool m_triedToRefresh = false; ///< Did a refresh event occur to recalculate the current Manipulator transform.
|
||||
bool m_didSetSelectedEntities = false; ///< Was EditorTransformComponentSelection responsible for the most recent entity selection change.
|
||||
bool m_selectedEntityIdsAndManipulatorsDirty = false; ///< Do the active manipulators need to recalculated after a modification (lock/visibility etc).
|
||||
bool m_transformChangedInternally = false; ///< Was an OnTransformChanged event triggered internally or not.
|
||||
ViewportUi::ClusterId m_transformModeClusterId; ///< Id of the Viewport UI cluster for changing transform mode.
|
||||
ViewportUi::ButtonId m_translateButtonId; ///< Id of the Viewport UI button for translate mode.
|
||||
ViewportUi::ButtonId m_rotateButtonId; ///< Id of the Viewport UI button for rotate mode.
|
||||
ViewportUi::ButtonId m_scaleButtonId; ///< Id of the Viewport UI button for scale mode.
|
||||
AZ::Event<ViewportUi::ButtonId>::Handler m_transformModeSelectionHandler; ///< Event handler for the Viewport UI cluster.
|
||||
EditorBoxSelect m_boxSelect; //!< Type responsible for handling box select.
|
||||
AZStd::unique_ptr<EntityManipulatorCommand> m_manipulatorMoveCommand; //!< Track adjustments to manipulator translation and orientation (during mouse press/move).
|
||||
AZStd::vector<AZStd::unique_ptr<QAction>> m_actions; //!< What actions are tied to this handler.
|
||||
ViewportInteraction::KeyboardModifiers m_previousModifiers; //!< What modifiers were held last frame.
|
||||
EditorContextMenu m_contextMenu; //!< Viewport right click context menu.
|
||||
OptionalFrame m_pivotOverrideFrame; //!< Has a pivot override been set.
|
||||
Mode m_mode = Mode::Translation; //!< Manipulator mode - default to translation.
|
||||
Pivot m_pivotMode = Pivot::Object; //!< Entity pivot mode - default to object (authored root).
|
||||
ReferenceFrame m_referenceFrame = ReferenceFrame::Parent; //!< What reference frame is the Manipulator currently operating in.
|
||||
Frame m_axisPreview; //!< Axes of entity at the time of mouse down to indicate delta of translation.
|
||||
bool m_triedToRefresh = false; //!< Did a refresh event occur to recalculate the current Manipulator transform.
|
||||
bool m_didSetSelectedEntities = false; //!< Was EditorTransformComponentSelection responsible for the most recent entity selection change.
|
||||
bool m_selectedEntityIdsAndManipulatorsDirty = false; //!< Do the active manipulators need to recalculated after a modification (lock/visibility etc).
|
||||
bool m_transformChangedInternally = false; //!< Was an OnTransformChanged event triggered internally or not.
|
||||
ViewportUi::ClusterId m_transformModeClusterId; //!< Id of the Viewport UI cluster for changing transform mode.
|
||||
ViewportUi::ButtonId m_translateButtonId; //!< Id of the Viewport UI button for translate mode.
|
||||
ViewportUi::ButtonId m_rotateButtonId; //!< Id of the Viewport UI button for rotate mode.
|
||||
ViewportUi::ButtonId m_scaleButtonId; //!< Id of the Viewport UI button for scale mode.
|
||||
AZ::Event<ViewportUi::ButtonId>::Handler m_transformModeSelectionHandler; //!< Event handler for the Viewport UI cluster.
|
||||
AzFramework::ClickDetector m_clickDetector; //!< Detect different types of mouse click.
|
||||
AzFramework::CursorState m_cursorState; //!< Track the mouse position and delta movement each frame.
|
||||
};
|
||||
|
||||
/// The ETCS (EntityTransformComponentSelection) namespace contains functions and data used exclusively by
|
||||
/// the EditorTransformComponentSelection type. Functions in this namespace are exposed to facilitate testing
|
||||
/// and should not be used outside of EditorTransformComponentSelection or EditorTransformComponentSelectionTests.
|
||||
//! The ETCS (EntityTransformComponentSelection) namespace contains functions and data used exclusively by
|
||||
//! the EditorTransformComponentSelection type. Functions in this namespace are exposed to facilitate testing
|
||||
//! and should not be used outside of EditorTransformComponentSelection or EditorTransformComponentSelectionTests.
|
||||
namespace ETCS
|
||||
{
|
||||
/// The result from calculating the entity (transform component) orientation.
|
||||
/// Does the entity have a parent or not, and what orientation should the manipulator have when
|
||||
/// displayed at the object pivot (determined by the entity hierarchy and what modifiers are held).
|
||||
//! The result from calculating the entity (transform component) orientation.
|
||||
//! Does the entity have a parent or not, and what orientation should the manipulator have when
|
||||
//! displayed at the object pivot (determined by the entity hierarchy and what modifiers are held).
|
||||
struct PivotOrientationResult
|
||||
{
|
||||
AZ::Quaternion m_worldOrientation;
|
||||
AZ::EntityId m_parentId;
|
||||
};
|
||||
|
||||
/// Calculate the orientation for an individual entity based on the incoming reference frame.
|
||||
/// Note: If the entity is in a hierarchy the Parent reference frame will return the orientation of the parent.
|
||||
//! Calculate the orientation for an individual entity based on the incoming reference frame.
|
||||
//! Note: If the entity is in a hierarchy the Parent reference frame will return the orientation of the parent.
|
||||
PivotOrientationResult CalculatePivotOrientation(AZ::EntityId entityId, ReferenceFrame referenceFrame);
|
||||
|
||||
/// Calculate the orientation for a group of entities based on the incoming reference frame.
|
||||
//! Calculate the orientation for a group of entities based on the incoming reference frame.
|
||||
template<typename EntityIdMap>
|
||||
PivotOrientationResult CalculatePivotOrientationForEntityIds(
|
||||
const EntityIdMap& entityIdMap, const ReferenceFrame referenceFrame);
|
||||
|
||||
/// Calculate the orientation for a group of entities based on the incoming
|
||||
/// reference frame with possible pivot override.
|
||||
//! Calculate the orientation for a group of entities based on the incoming
|
||||
//! reference frame with possible pivot override.
|
||||
template<typename EntityIdMap>
|
||||
PivotOrientationResult CalculateSelectionPivotOrientation(
|
||||
const EntityIdMap& entityIdMap, const OptionalFrame& pivotOverrideFrame,
|
||||
|
||||
@@ -41,6 +41,28 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
}
|
||||
}
|
||||
|
||||
static Qt::Alignment GetQtAlignment(Alignment align)
|
||||
{
|
||||
switch (align)
|
||||
{
|
||||
case Alignment::TopRight:
|
||||
return Qt::AlignTop | Qt::AlignRight;
|
||||
case Alignment::TopLeft:
|
||||
return Qt::AlignTop | Qt::AlignLeft;
|
||||
case Alignment::BottomRight:
|
||||
return Qt::AlignBottom | Qt::AlignRight;
|
||||
case Alignment::BottomLeft:
|
||||
return Qt::AlignBottom | Qt::AlignLeft;
|
||||
case Alignment::Top:
|
||||
return Qt::AlignTop;
|
||||
case Alignment::Bottom:
|
||||
return Qt::AlignBottom;
|
||||
}
|
||||
|
||||
AZ_Assert(false, "ViewportUI", "Unhandled ViewportUI Alignment %d", static_cast<int>(align));
|
||||
return Qt::AlignTop;
|
||||
}
|
||||
|
||||
ViewportUiDisplay::ViewportUiDisplay(QWidget* parent, QWidget* renderOverlay)
|
||||
: m_renderOverlay(renderOverlay)
|
||||
, m_uiMainWindow(parent)
|
||||
@@ -56,7 +78,7 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
UnparentWidgets(m_viewportUiElements);
|
||||
}
|
||||
|
||||
void ViewportUiDisplay::AddCluster(AZStd::shared_ptr<ButtonGroup> buttonGroup)
|
||||
void ViewportUiDisplay::AddCluster(AZStd::shared_ptr<ButtonGroup> buttonGroup, const Alignment align)
|
||||
{
|
||||
if (!buttonGroup.get())
|
||||
{
|
||||
@@ -66,7 +88,7 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
auto viewportUiCluster = AZStd::make_shared<ViewportUiCluster>(buttonGroup);
|
||||
auto id = AddViewportUiElement(viewportUiCluster);
|
||||
buttonGroup->SetViewportUiElementId(id);
|
||||
PositionViewportUiElementAnchored(id, Qt::AlignTop | Qt::AlignLeft);
|
||||
PositionViewportUiElementAnchored(id, GetQtAlignment(align));
|
||||
}
|
||||
|
||||
void ViewportUiDisplay::AddClusterButton(
|
||||
@@ -94,7 +116,7 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportUiDisplay::AddSwitcher(AZStd::shared_ptr<ButtonGroup> buttonGroup)
|
||||
void ViewportUiDisplay::AddSwitcher(AZStd::shared_ptr<ButtonGroup> buttonGroup, const Alignment align)
|
||||
{
|
||||
if (!buttonGroup.get())
|
||||
{
|
||||
@@ -104,7 +126,7 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
auto viewportUiSwitcher = AZStd::make_shared<ViewportUiSwitcher>(buttonGroup);
|
||||
auto id = AddViewportUiElement(viewportUiSwitcher);
|
||||
buttonGroup->SetViewportUiElementId(id);
|
||||
PositionViewportUiElementAnchored(id, Qt::AlignTop | Qt::AlignLeft);
|
||||
PositionViewportUiElementAnchored(id, GetQtAlignment(align));
|
||||
}
|
||||
|
||||
void ViewportUiDisplay::AddSwitcherButton(const ViewportUiElementId clusterId, Button* button)
|
||||
|
||||
@@ -56,12 +56,12 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
ViewportUiDisplay(QWidget* parent, QWidget* renderOverlay);
|
||||
~ViewportUiDisplay();
|
||||
|
||||
void AddCluster(AZStd::shared_ptr<ButtonGroup> buttonGroup);
|
||||
void AddCluster(AZStd::shared_ptr<ButtonGroup> buttonGroup, Alignment align);
|
||||
void AddClusterButton(ViewportUiElementId clusterId, Button* button);
|
||||
void RemoveClusterButton(ViewportUiElementId clusterId, ButtonId buttonId);
|
||||
void UpdateCluster(const ViewportUiElementId clusterId);
|
||||
|
||||
void AddSwitcher(AZStd::shared_ptr<ButtonGroup> buttonGroup);
|
||||
void AddSwitcher(AZStd::shared_ptr<ButtonGroup> buttonGroup, Alignment align);
|
||||
void AddSwitcherButton(ViewportUiElementId switcherId, Button* button);
|
||||
void RemoveSwitcherButton(ViewportUiElementId switcherId, ButtonId buttonId);
|
||||
void UpdateSwitcher(ViewportUiElementId switcherId);
|
||||
|
||||
@@ -30,18 +30,18 @@ namespace AzToolsFramework::ViewportUi
|
||||
ViewportUiRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
const ClusterId ViewportUiManager::CreateCluster()
|
||||
const ClusterId ViewportUiManager::CreateCluster(const Alignment align)
|
||||
{
|
||||
auto buttonGroup = AZStd::make_shared<Internal::ButtonGroup>();
|
||||
m_viewportUi->AddCluster(buttonGroup);
|
||||
m_viewportUi->AddCluster(buttonGroup, align);
|
||||
|
||||
return RegisterNewCluster(buttonGroup);
|
||||
}
|
||||
|
||||
const SwitcherId ViewportUiManager::CreateSwitcher()
|
||||
const SwitcherId ViewportUiManager::CreateSwitcher(const Alignment align)
|
||||
{
|
||||
auto buttonGroup = AZStd::make_shared<Internal::ButtonGroup>();
|
||||
m_viewportUi->AddSwitcher(buttonGroup);
|
||||
m_viewportUi->AddSwitcher(buttonGroup, align);
|
||||
|
||||
return RegisterNewSwitcher(buttonGroup);
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ namespace AzToolsFramework::ViewportUi
|
||||
~ViewportUiManager() = default;
|
||||
|
||||
// ViewportUiRequestBus ...
|
||||
const ClusterId CreateCluster() override;
|
||||
const SwitcherId CreateSwitcher() override;
|
||||
const ClusterId CreateCluster(Alignment align) override;
|
||||
const SwitcherId CreateSwitcher(Alignment align) override;
|
||||
void SetClusterActiveButton(ClusterId clusterId, ButtonId buttonId) override;
|
||||
void SetSwitcherActiveButton(SwitcherId switcherId, ButtonId buttonId) override;
|
||||
const ButtonId CreateClusterButton(ClusterId clusterId, const AZStd::string& icon) override;
|
||||
|
||||
@@ -41,15 +41,26 @@ namespace AzToolsFramework::ViewportUi
|
||||
String
|
||||
};
|
||||
|
||||
//! Used to anchor widgets to a specific side of the viewport.
|
||||
enum class Alignment
|
||||
{
|
||||
TopRight,
|
||||
TopLeft,
|
||||
BottomRight,
|
||||
BottomLeft,
|
||||
Top,
|
||||
Bottom
|
||||
};
|
||||
|
||||
//! Viewport requests to interact with the Viewport UI. Viewport UI refers to the entire UI overlay (one per viewport).
|
||||
//! Each widget on the Viewport UI is referred to as an element.
|
||||
class ViewportUiRequests
|
||||
{
|
||||
public:
|
||||
//! Creates and registers a cluster with the Viewport UI system.
|
||||
virtual const ClusterId CreateCluster() = 0;
|
||||
virtual const ClusterId CreateCluster(Alignment align) = 0;
|
||||
//! Creates and registers a switcher with the Viewport UI system.
|
||||
virtual const SwitcherId CreateSwitcher() = 0;
|
||||
virtual const SwitcherId CreateSwitcher(Alignment align) = 0;
|
||||
//! Sets the active button of the cluster. This is the button which will display as highlighted.
|
||||
virtual void SetClusterActiveButton(ClusterId clusterId, ButtonId buttonId) = 0;
|
||||
//! Sets the active button of the switcher. This is the button which has a text label.
|
||||
|
||||
@@ -72,6 +72,8 @@ set(FILES
|
||||
AssetCatalog/PlatformAddressedAssetCatalogManager.cpp
|
||||
Thumbnails/ThumbnailerComponent.cpp
|
||||
Thumbnails/ThumbnailerComponent.h
|
||||
Thumbnails/ThumbnailerNullComponent.cpp
|
||||
Thumbnails/ThumbnailerNullComponent.h
|
||||
Thumbnails/LoadingThumbnail.cpp
|
||||
Thumbnails/LoadingThumbnail.h
|
||||
Thumbnails/MissingThumbnail.cpp
|
||||
|
||||
@@ -120,7 +120,7 @@ namespace UnitTest
|
||||
|
||||
//create an undo node to apply the patch and prep for undo
|
||||
PrefabUndoInstanceLink undoInstanceLinkNode("Undo Link Patch");
|
||||
undoInstanceLinkNode.Capture(rootTemplateId, nestedTemplateId, aliases[0], patch, InvalidLinkId);
|
||||
undoInstanceLinkNode.Capture(rootTemplateId, nestedTemplateId, aliases[0], AZStd::move(patch), InvalidLinkId);
|
||||
undoInstanceLinkNode.Redo();
|
||||
m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
|
||||
|
||||
@@ -196,7 +196,7 @@ namespace UnitTest
|
||||
|
||||
//create an undo node to apply the patch and prep for undo
|
||||
PrefabUndoInstanceLink undoInstanceLinkNode("Undo Link Patch");
|
||||
undoInstanceLinkNode.Capture(rootTemplateId, nestedTemplateId, aliases[0], linkPatch, InvalidLinkId);
|
||||
undoInstanceLinkNode.Capture(rootTemplateId, nestedTemplateId, aliases[0], AZStd::move(linkPatch), InvalidLinkId);
|
||||
undoInstanceLinkNode.Redo();
|
||||
m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace UnitTest
|
||||
TEST_F(ViewportUiDisplayTestFixture, RemoveViewportUiElementRemovesElementFromViewportUi)
|
||||
{
|
||||
ViewportUiDisplay viewportUi(m_parentWidget, m_mockRenderOverlay);
|
||||
viewportUi.AddCluster(m_buttonGroup);
|
||||
viewportUi.AddCluster(m_buttonGroup, AzToolsFramework::ViewportUi::Alignment::TopLeft);
|
||||
auto widget = viewportUi.GetViewportUiElement(m_buttonGroup->GetViewportUiElementId());
|
||||
|
||||
EXPECT_TRUE(widget.get() != nullptr);
|
||||
@@ -89,7 +89,7 @@ namespace UnitTest
|
||||
|
||||
ViewportUiDisplay viewportUi(m_parentWidget, m_mockRenderOverlay);
|
||||
viewportUi.InitializeUiOverlay();
|
||||
viewportUi.AddCluster(m_buttonGroup);
|
||||
viewportUi.AddCluster(m_buttonGroup, AzToolsFramework::ViewportUi::Alignment::TopLeft);
|
||||
viewportUi.Update();
|
||||
viewportUi.ShowViewportUiElement(m_buttonGroup->GetViewportUiElementId());
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace UnitTest
|
||||
|
||||
ViewportUiDisplay viewportUi(m_parentWidget, m_mockRenderOverlay);
|
||||
viewportUi.InitializeUiOverlay();
|
||||
viewportUi.AddCluster(m_buttonGroup);
|
||||
viewportUi.AddCluster(m_buttonGroup, AzToolsFramework::ViewportUi::Alignment::TopLeft);
|
||||
viewportUi.HideViewportUiElement(m_buttonGroup->GetViewportUiElementId());
|
||||
|
||||
EXPECT_FALSE(viewportUi.IsViewportUiElementVisible(m_buttonGroup->GetViewportUiElementId()));
|
||||
@@ -112,7 +112,7 @@ namespace UnitTest
|
||||
{
|
||||
ViewportUiDisplay viewportUi(m_parentWidget, m_mockRenderOverlay);
|
||||
viewportUi.InitializeUiOverlay();
|
||||
viewportUi.AddCluster(m_buttonGroup);
|
||||
viewportUi.AddCluster(m_buttonGroup, AzToolsFramework::ViewportUi::Alignment::TopLeft);
|
||||
|
||||
viewportUi.Update();
|
||||
auto widget = viewportUi.GetViewportUiElement(m_buttonGroup->GetViewportUiElementId());
|
||||
@@ -129,7 +129,7 @@ namespace UnitTest
|
||||
|
||||
auto buttonGroup = AZStd::make_shared<ButtonGroup>();
|
||||
buttonGroup->AddButton("");
|
||||
viewportUi.AddCluster(buttonGroup);
|
||||
viewportUi.AddCluster(buttonGroup, AzToolsFramework::ViewportUi::Alignment::TopLeft);
|
||||
viewportUi.Update();
|
||||
|
||||
EXPECT_TRUE(viewportUi.GetUiMainWindow()->isVisible());
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace UnitTest
|
||||
|
||||
TEST_F(ViewportUiManagerTestFixture, CreateClusterAddsNewClusterAndReturnsId)
|
||||
{
|
||||
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster();
|
||||
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
|
||||
auto clusterEntry = m_viewportManagerWrapper.GetViewportManager()->GetClusterMap().find(clusterId);
|
||||
|
||||
EXPECT_TRUE(clusterEntry != m_viewportManagerWrapper.GetViewportManager()->GetClusterMap().end());
|
||||
@@ -110,7 +110,7 @@ namespace UnitTest
|
||||
|
||||
TEST_F(ViewportUiManagerTestFixture, CreateClusterButtonAddsNewButtonAndReturnsId)
|
||||
{
|
||||
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster();
|
||||
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
|
||||
auto buttonId = m_viewportManagerWrapper.GetViewportManager()->CreateClusterButton(clusterId, "");
|
||||
|
||||
auto clusterEntry = m_viewportManagerWrapper.GetViewportManager()->GetClusterMap().find(clusterId);
|
||||
@@ -120,7 +120,7 @@ namespace UnitTest
|
||||
|
||||
TEST_F(ViewportUiManagerTestFixture, SetClusterActiveButtonSetsButtonStateToActive)
|
||||
{
|
||||
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster();
|
||||
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
|
||||
auto buttonId = m_viewportManagerWrapper.GetViewportManager()->CreateClusterButton(clusterId, "");
|
||||
|
||||
auto clusterEntry = m_viewportManagerWrapper.GetViewportManager()->GetClusterMap().find(clusterId);
|
||||
@@ -133,7 +133,7 @@ namespace UnitTest
|
||||
|
||||
TEST_F(ViewportUiManagerTestFixture, RegisterClusterEventHandlerConnectsHandlerToClusterEvent)
|
||||
{
|
||||
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster();
|
||||
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
|
||||
auto buttonId = m_viewportManagerWrapper.GetViewportManager()->CreateClusterButton(clusterId, "");
|
||||
|
||||
// create a handler which will be triggered by the cluster
|
||||
@@ -159,7 +159,7 @@ namespace UnitTest
|
||||
|
||||
TEST_F(ViewportUiManagerTestFixture, RemoveClusterRemovesClusterFromViewportUi)
|
||||
{
|
||||
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster();
|
||||
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
|
||||
m_viewportManagerWrapper.GetViewportManager()->RemoveCluster(clusterId);
|
||||
|
||||
auto clusterEntry = m_viewportManagerWrapper.GetViewportManager()->GetClusterMap().find(clusterId);
|
||||
@@ -171,7 +171,7 @@ namespace UnitTest
|
||||
{
|
||||
m_viewportManagerWrapper.GetMockRenderOverlay()->setVisible(true);
|
||||
|
||||
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster();
|
||||
auto clusterId = m_viewportManagerWrapper.GetViewportManager()->CreateCluster(AzToolsFramework::ViewportUi::Alignment::TopLeft);
|
||||
auto buttonId = m_viewportManagerWrapper.GetViewportManager()->CreateClusterButton(clusterId, "");
|
||||
m_viewportManagerWrapper.GetViewportManager()->Update();
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user