Merge branch 'main' into LYN-1767-AB
This commit is contained in:
@@ -1 +0,0 @@
|
||||
*.xml
|
||||
@@ -189,7 +189,7 @@ namespace AZ
|
||||
if (!WasLoadSuccess(result.GetOutcome()))
|
||||
{
|
||||
// This if is a hack around fault in the JSON serialization system
|
||||
// Jira: https://jira.agscollab.com/browse/LY-106587
|
||||
// Jira: LY-106587
|
||||
if (message != "No part of the string could be interpreted as a uuid.")
|
||||
{
|
||||
deserializeError.append(message);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -269,13 +269,13 @@ namespace AZ
|
||||
{
|
||||
for (auto& [assetId, dependentAsset] : m_dependencies)
|
||||
{
|
||||
if (dependentAsset->IsReady())
|
||||
if (dependentAsset->IsReady() || dependentAsset->IsError())
|
||||
{
|
||||
HandleReadyAsset(dependentAsset);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (auto asset = m_rootAsset.GetStrongReference(); asset.IsReady())
|
||||
if (auto asset = m_rootAsset.GetStrongReference(); asset.IsReady() || asset.IsError())
|
||||
{
|
||||
HandleReadyAsset(asset);
|
||||
}
|
||||
@@ -496,10 +496,10 @@ namespace AZ
|
||||
m_waitingCount -= 1;
|
||||
disconnectEbus = true;
|
||||
|
||||
if (m_waitingAssets.empty())
|
||||
{
|
||||
allReady = true;
|
||||
}
|
||||
}
|
||||
if (m_waitingAssets.empty())
|
||||
{
|
||||
allReady = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -510,8 +510,15 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
if (allReady && m_initComplete)
|
||||
// If there are no assets left to be loaded, trigger the final AssetContainer notification (ready or canceled).
|
||||
// We guard against prematurely sending it (m_initComplete) because it's possible for assets to get removed from our waiting
|
||||
// list *while* we're still building up the list, so the list would appear to be empty too soon.
|
||||
// We also guard against sending it multiple times (m_finalNotificationSent), because in some error conditions, it may be
|
||||
// possible to try to remove the same asset multiple times, which if it's the last asset, it could trigger multiple
|
||||
// notifications.
|
||||
if (allReady && m_initComplete && !m_finalNotificationSent)
|
||||
{
|
||||
m_finalNotificationSent = true;
|
||||
if (m_rootAsset)
|
||||
{
|
||||
AssetManagerBus::Broadcast(&AssetManagerBus::Events::OnAssetContainerReady, this);
|
||||
|
||||
@@ -137,6 +137,7 @@ namespace AZ
|
||||
AZStd::atomic_int m_invalidDependencies{ 0 };
|
||||
AZStd::unordered_set<AZ::Data::AssetId> m_unloadedDependencies;
|
||||
AZStd::atomic_bool m_initComplete{ false };
|
||||
AZStd::atomic_bool m_finalNotificationSent{false};
|
||||
|
||||
mutable AZStd::recursive_mutex m_preloadMutex;
|
||||
// AssetId -> List of assets it is still waiting on
|
||||
|
||||
@@ -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>()
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Math/Sfmt.h>
|
||||
#include <AzCore/Math/Crc.h>
|
||||
|
||||
@@ -173,7 +174,11 @@ namespace AZ
|
||||
//=========================================================================
|
||||
void ComponentDescriptor::ReleaseDescriptor()
|
||||
{
|
||||
EBUS_EVENT(ComponentApplicationBus, UnregisterComponentDescriptor, this);
|
||||
AZ::ComponentApplicationRequests* componentApplication = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
|
||||
if (componentApplication != nullptr)
|
||||
{
|
||||
componentApplication->UnregisterComponentDescriptor(this);
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
} // namespace AZ
|
||||
|
||||
@@ -526,6 +526,11 @@ namespace AZ
|
||||
// are destroyed
|
||||
m_commandLine = {};
|
||||
|
||||
m_entityAddedEvent.DisconnectAllHandlers();
|
||||
m_entityRemovedEvent.DisconnectAllHandlers();
|
||||
m_entityActivatedEvent.DisconnectAllHandlers();
|
||||
m_entityDeactivatedEvent.DisconnectAllHandlers();
|
||||
|
||||
DestroyAllocator();
|
||||
}
|
||||
|
||||
@@ -980,6 +985,26 @@ namespace AZ
|
||||
handler.Connect(m_entityRemovedEvent);
|
||||
}
|
||||
|
||||
void ComponentApplication::RegisterEntityActivatedEventHandler(EntityActivatedEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_entityActivatedEvent);
|
||||
}
|
||||
|
||||
void ComponentApplication::RegisterEntityDeactivatedEventHandler(EntityDeactivatedEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_entityDeactivatedEvent);
|
||||
}
|
||||
|
||||
void ComponentApplication::SignalEntityActivated(AZ::Entity* entity)
|
||||
{
|
||||
m_entityActivatedEvent.Signal(entity);
|
||||
}
|
||||
|
||||
void ComponentApplication::SignalEntityDeactivated(AZ::Entity* entity)
|
||||
{
|
||||
m_entityDeactivatedEvent.Signal(entity);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// AddEntity
|
||||
// [5/30/2012]
|
||||
@@ -1279,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)
|
||||
{
|
||||
|
||||
@@ -204,6 +204,10 @@ namespace AZ
|
||||
void UnregisterComponentDescriptor(const ComponentDescriptor* descriptor) override final;
|
||||
void RegisterEntityAddedEventHandler(EntityAddedEvent::Handler& handler) override final;
|
||||
void RegisterEntityRemovedEventHandler(EntityRemovedEvent::Handler& handler) override final;
|
||||
void RegisterEntityActivatedEventHandler(EntityActivatedEvent::Handler& handler) override final;
|
||||
void RegisterEntityDeactivatedEventHandler(EntityDeactivatedEvent::Handler& handler) override final;
|
||||
void SignalEntityActivated(Entity* entity) override final;
|
||||
void SignalEntityDeactivated(Entity* entity) override final;
|
||||
bool AddEntity(Entity* entity) override;
|
||||
bool RemoveEntity(Entity* entity) override;
|
||||
bool DeleteEntity(const EntityId& id) override;
|
||||
@@ -382,6 +386,8 @@ namespace AZ
|
||||
AZStd::unique_ptr<SettingsRegistryInterface> m_settingsRegistry;
|
||||
EntityAddedEvent m_entityAddedEvent;
|
||||
EntityRemovedEvent m_entityRemovedEvent;
|
||||
EntityAddedEvent m_entityActivatedEvent;
|
||||
EntityRemovedEvent m_entityDeactivatedEvent;
|
||||
AZ::IConsole* m_console{};
|
||||
Descriptor m_descriptor;
|
||||
bool m_isStarted{ false };
|
||||
|
||||
@@ -72,6 +72,8 @@ namespace AZ
|
||||
|
||||
using EntityAddedEvent = AZ::Event<AZ::Entity*>;
|
||||
using EntityRemovedEvent = AZ::Event<AZ::Entity*>;
|
||||
using EntityActivatedEvent = AZ::Event<AZ::Entity*>;
|
||||
using EntityDeactivatedEvent = AZ::Event<AZ::Entity*>;
|
||||
|
||||
//! Interface that components can use to make requests of the main application.
|
||||
class ComponentApplicationRequests
|
||||
@@ -102,6 +104,22 @@ namespace AZ
|
||||
//! @param handler the event handler to signal.
|
||||
virtual void RegisterEntityRemovedEventHandler(EntityRemovedEvent::Handler& handler) = 0;
|
||||
|
||||
//! Registers an event handler that will be signalled whenever an entity is added.
|
||||
//! @param handler the event handler to signal.
|
||||
virtual void RegisterEntityActivatedEventHandler(EntityActivatedEvent::Handler& handler) = 0;
|
||||
|
||||
//! Registers an event handler that will be signalled whenever an entity is removed.
|
||||
//! @param handler the event handler to signal.
|
||||
virtual void RegisterEntityDeactivatedEventHandler(EntityDeactivatedEvent::Handler& handler) = 0;
|
||||
|
||||
//! Signals that the provided entity has been activated.
|
||||
//! @param entity the entity being activated.
|
||||
virtual void SignalEntityActivated(AZ::Entity* entity) = 0;
|
||||
|
||||
//! Signals that the provided entity has been deactivated.
|
||||
//! @param entity the entity being deactivated.
|
||||
virtual void SignalEntityDeactivated(AZ::Entity* entity) = 0;
|
||||
|
||||
//! Adds an entity to the application's registry.
|
||||
//! Calling Init() on an entity automatically performs this operation.
|
||||
//! @param entity A pointer to the entity to add to the application's registry.
|
||||
|
||||
@@ -112,7 +112,11 @@ namespace AZ
|
||||
{
|
||||
EBUS_EVENT(EntitySystemBus, OnEntityDestruction, m_id);
|
||||
EBUS_EVENT_ID(m_id, EntityBus, OnEntityDestruction, m_id);
|
||||
EBUS_EVENT(ComponentApplicationBus, RemoveEntity, this);
|
||||
AZ::ComponentApplicationRequests* componentApplication = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
|
||||
if (componentApplication != nullptr)
|
||||
{
|
||||
componentApplication->RemoveEntity(this);
|
||||
}
|
||||
m_stateEvent.Signal(State::Init, State::Destroying);
|
||||
}
|
||||
|
||||
@@ -216,12 +220,22 @@ namespace AZ
|
||||
|
||||
EBUS_EVENT_ID(m_id, EntityBus, OnEntityActivated, m_id);
|
||||
EBUS_EVENT(EntitySystemBus, OnEntityActivated, m_id);
|
||||
AZ::ComponentApplicationRequests* componentApplication = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
|
||||
if (componentApplication != nullptr)
|
||||
{
|
||||
componentApplication->SignalEntityActivated(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Entity::Deactivate()
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzCore);
|
||||
|
||||
AZ::ComponentApplicationRequests* componentApplication = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
|
||||
if (componentApplication != nullptr)
|
||||
{
|
||||
componentApplication->SignalEntityDeactivated(this);
|
||||
}
|
||||
EBUS_EVENT_ID(m_id, EntityBus, OnEntityDeactivated, m_id);
|
||||
EBUS_EVENT(EntitySystemBus, OnEntityDeactivated, m_id);
|
||||
|
||||
|
||||
@@ -126,9 +126,11 @@ namespace AZ
|
||||
char buffer[MaxLogBufferSize];
|
||||
|
||||
const AZStd::size_t length = azvsnprintf(buffer, MaxLogBufferSize, format, args);
|
||||
buffer[AZStd::min<AZStd::size_t>(length, MaxLogBufferSize - 2)] = '\n';
|
||||
buffer[AZStd::min<AZStd::size_t>(length + 1, MaxLogBufferSize - 1)] = '\0';
|
||||
m_logEvent.Signal(level, buffer, file, function, line);
|
||||
|
||||
// Force a new-line before calling the AZ::Debug::Trace functions, as they assume a newline is present
|
||||
buffer[AZStd::min<AZStd::size_t>(length + 1, MaxLogBufferSize - 2)] = '\n';
|
||||
switch (level)
|
||||
{
|
||||
case LogLevel::Warn:
|
||||
@@ -142,8 +144,6 @@ namespace AZ
|
||||
AZ::Debug::Trace::Output("Logger", buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
m_logEvent.Signal(level, buffer, file, function, line);
|
||||
}
|
||||
|
||||
void LoggerSystemComponent::SetLevel(const AZ::ConsoleCommandContainer& arguments)
|
||||
|
||||
@@ -233,6 +233,14 @@ namespace AZ
|
||||
AZ_Assert(handler->m_event == this, "Entry event does not match");
|
||||
handler->Disconnect();
|
||||
}
|
||||
|
||||
// Free up any owned memory
|
||||
AZStd::vector<Handler*> freeHandlers;
|
||||
m_handlers.swap(freeHandlers);
|
||||
AZStd::vector<Handler*> freeAdds;
|
||||
m_addList.swap(freeAdds);
|
||||
AZStd::stack<size_t> freeFree;
|
||||
m_freeList.swap(freeFree);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -13,16 +13,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/std/math.h>
|
||||
#include <AzCore/std/typetraits/conditional.h>
|
||||
#include <AzCore/std/typetraits/is_integral.h>
|
||||
#include <AzCore/std/typetraits/is_signed.h>
|
||||
#include <AzCore/std/typetraits/is_unsigned.h>
|
||||
#include <AzCore/std/utils.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <float.h>
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
#include <math.h>
|
||||
#include <utility>
|
||||
#include <AzCore/std/typetraits/conditional.h>
|
||||
#include <AzCore/std/typetraits/is_integral.h>
|
||||
|
||||
// We have a separate inline define for math functions.
|
||||
// The performance of these functions is very sensitive to inlining, and some compilers don't deal well with this.
|
||||
@@ -308,12 +309,12 @@ namespace AZ
|
||||
|
||||
AZ_MATH_INLINE bool IsClose(float a, float b, float tolerance = Constants::Tolerance)
|
||||
{
|
||||
return (fabsf(a - b) <= tolerance);
|
||||
return (AZStd::abs(a - b) <= tolerance);
|
||||
}
|
||||
|
||||
AZ_MATH_INLINE bool IsClose(double a, double b, double tolerance = Constants::Tolerance)
|
||||
{
|
||||
return (fabs(a - b) <= tolerance);
|
||||
return (AZStd::abs(a - b) <= tolerance);
|
||||
}
|
||||
|
||||
//! Returns x >= 0.0f ? 1.0f : -1.0f.
|
||||
@@ -402,12 +403,12 @@ namespace AZ
|
||||
|
||||
AZ_MATH_INLINE float GetAbs(float a)
|
||||
{
|
||||
return fabsf(a);
|
||||
return AZStd::abs(a);
|
||||
}
|
||||
|
||||
AZ_MATH_INLINE double GetAbs(double a)
|
||||
{
|
||||
return std::abs(a);
|
||||
return AZStd::abs(a);
|
||||
}
|
||||
|
||||
AZ_MATH_INLINE float GetMod(float a, float b)
|
||||
@@ -441,7 +442,7 @@ namespace AZ
|
||||
template<typename T>
|
||||
AZ_MATH_INLINE bool IsCloseMag(T x, T y, T epsilonValue = std::numeric_limits<T>::epsilon())
|
||||
{
|
||||
return (std::fabs(x - y) <= epsilonValue * GetMax<T>(GetMax<T>(T(1.0), std::fabs(x)), std::fabs(y)));
|
||||
return (AZStd::abs(x - y) <= epsilonValue * GetMax<T>(GetMax<T>(T(1.0), AZStd::abs(x)), AZStd::abs(y)));
|
||||
}
|
||||
|
||||
//! ClampIfCloseMag(x, y, epsilon) returns y when x and y are within epsilon of each other (taking magnitude into account). Otherwise returns x.
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
|
||||
@@ -33,6 +33,10 @@ namespace UnitTest
|
||||
MOCK_METHOD1(UnregisterComponentDescriptor, void (const AZ::ComponentDescriptor*));
|
||||
MOCK_METHOD1(RegisterEntityAddedEventHandler, void(AZ::EntityAddedEvent::Handler&));
|
||||
MOCK_METHOD1(RegisterEntityRemovedEventHandler, void(AZ::EntityRemovedEvent::Handler&));
|
||||
MOCK_METHOD1(RegisterEntityActivatedEventHandler, void(AZ::EntityActivatedEvent::Handler&));
|
||||
MOCK_METHOD1(RegisterEntityDeactivatedEventHandler, void(AZ::EntityDeactivatedEvent::Handler&));
|
||||
MOCK_METHOD1(SignalEntityActivated, void(AZ::Entity*));
|
||||
MOCK_METHOD1(SignalEntityDeactivated, void(AZ::Entity*));
|
||||
MOCK_METHOD1(RemoveEntity, bool (AZ::Entity*));
|
||||
MOCK_METHOD1(DeleteEntity, bool (const AZ::EntityId&));
|
||||
MOCK_METHOD1(GetEntityName, AZStd::string (const AZ::EntityId&));
|
||||
|
||||
@@ -169,5 +169,10 @@ namespace AZ::Utils
|
||||
template AZ::Outcome<AZStd::vector<int8_t>, AZStd::string> ReadFile(AZStd::string_view filePath, size_t maxFileSize);
|
||||
template AZ::Outcome<AZStd::vector<uint8_t>, AZStd::string> ReadFile(AZStd::string_view filePath, size_t maxFileSize);
|
||||
|
||||
|
||||
AZ::IO::FixedMaxPathString GetO3deManifestDirectory()
|
||||
{
|
||||
AZ::IO::FixedMaxPath path = GetHomeDirectory();
|
||||
path /= ".o3de";
|
||||
return path.Native();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,9 @@ namespace AZ
|
||||
//! Retrieves the project name from the settings registry
|
||||
AZ::SettingsRegistryInterface::FixedValueString GetProjectName();
|
||||
|
||||
//! Retrieves the full directory to the Home directory, i.e. "<userhome> or overrideHomeDirectory"
|
||||
AZ::IO::FixedMaxPathString GetHomeDirectory();
|
||||
|
||||
//! Retrieves the full directory to the O3DE manifest directory, i.e. "<userhome>/.o3de"
|
||||
AZ::IO::FixedMaxPathString GetO3deManifestDirectory();
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ set(FILES
|
||||
iterator.h
|
||||
limits.h
|
||||
numeric.h
|
||||
math.h
|
||||
optional.h
|
||||
ratio.h
|
||||
reference_wrapper.h
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 <cmath>
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
using std::abs;
|
||||
using std::acos;
|
||||
using std::asin;
|
||||
using std::atan;
|
||||
using std::atan2;
|
||||
using std::cos;
|
||||
using std::exp2;
|
||||
using std::fmod;
|
||||
using std::round;
|
||||
using std::sin;
|
||||
using std::sqrt;
|
||||
using std::tan;
|
||||
} // namespace AZStd
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
|
||||
namespace AZ::Utils
|
||||
{
|
||||
AZ::IO::FixedMaxPathString GetO3deManifestDirectory()
|
||||
AZ::IO::FixedMaxPathString GetHomeDirectory()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -25,8 +25,19 @@ namespace AZ
|
||||
|
||||
void NativeErrorMessageBox(const char*, const char*) {}
|
||||
|
||||
AZ::IO::FixedMaxPathString GetO3deManifestDirectory()
|
||||
AZ::IO::FixedMaxPathString GetHomeDirectory()
|
||||
{
|
||||
constexpr AZStd::string_view overrideHomeDirKey = "/Amazon/Settings/override_home_dir";
|
||||
AZ::IO::FixedMaxPathString overrideHomeDir;
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
if (settingsRegistry->Get(overrideHomeDir, overrideHomeDirKey))
|
||||
{
|
||||
AZ::IO::FixedMaxPath path{overrideHomeDir};
|
||||
return path.Native();
|
||||
}
|
||||
}
|
||||
|
||||
if (const char* homePath = std::getenv("HOME"); homePath != nullptr)
|
||||
{
|
||||
AZ::IO::FixedMaxPath path{homePath};
|
||||
|
||||
@@ -22,15 +22,25 @@ namespace AZ::Utils
|
||||
::MessageBox(0, message, title, MB_OK | MB_ICONERROR);
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPathString GetO3deManifestDirectory()
|
||||
AZ::IO::FixedMaxPathString GetHomeDirectory()
|
||||
{
|
||||
constexpr AZStd::string_view overrideHomeDirKey = "/Amazon/Settings/override_home_dir";
|
||||
AZ::IO::FixedMaxPathString overrideHomeDir;
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
if (settingsRegistry->Get(overrideHomeDir, overrideHomeDirKey))
|
||||
{
|
||||
AZ::IO::FixedMaxPath path{overrideHomeDir};
|
||||
return path.Native();
|
||||
}
|
||||
}
|
||||
|
||||
char userProfileBuffer[AZ::IO::MaxPathLength]{};
|
||||
size_t variableSize = 0;
|
||||
auto err = getenv_s(&variableSize, userProfileBuffer, AZ::IO::MaxPathLength, "USERPROFILE");
|
||||
if (!err)
|
||||
{
|
||||
AZ::IO::FixedMaxPath path{ userProfileBuffer };
|
||||
path /= ".o3de";
|
||||
return path.Native();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -351,6 +351,7 @@ namespace UnitTest
|
||||
public AZ::Data::AssetCatalog
|
||||
{
|
||||
static inline const AZ::Uuid TestAssetId{"{E970B177-5F45-44EB-A2C4-9F29D9A0B2A2}"};
|
||||
static inline const AZ::Uuid MissingAssetId{"{11111111-1111-1111-1111-111111111111}"};
|
||||
static inline constexpr AZStd::string_view TestAssetPath = "test";
|
||||
|
||||
void SetUp() override
|
||||
@@ -431,24 +432,40 @@ namespace UnitTest
|
||||
// AssetCatalogRequestBus implementation
|
||||
|
||||
// Minimalist mocks to provide our desired asset path or asset id
|
||||
AZStd::string GetAssetPathById([[maybe_unused]] const AZ::Data::AssetId& id) override
|
||||
AZStd::string GetAssetPathById(const AZ::Data::AssetId& id) override
|
||||
{
|
||||
return TestAssetPath;
|
||||
if (id == TestAssetId)
|
||||
{
|
||||
return TestAssetPath;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
AZ::Data::AssetId GetAssetIdByPath(
|
||||
[[maybe_unused]] const char* path, [[maybe_unused]] const AZ::Data::AssetType& typeToRegister,
|
||||
const char* path, [[maybe_unused]] const AZ::Data::AssetType& typeToRegister,
|
||||
[[maybe_unused]] bool autoRegisterIfNotFound) override
|
||||
{
|
||||
return TestAssetId;
|
||||
if (path == TestAssetPath)
|
||||
{
|
||||
return TestAssetId;
|
||||
}
|
||||
|
||||
return AZ::Data::AssetId();
|
||||
}
|
||||
|
||||
// Return the mocked-out information for our test asset
|
||||
AZ::Data::AssetInfo GetAssetInfoById([[maybe_unused]] const AZ::Data::AssetId& id) override
|
||||
AZ::Data::AssetInfo GetAssetInfoById(const AZ::Data::AssetId& id) override
|
||||
{
|
||||
AZ::Data::AssetInfo assetInfo;
|
||||
assetInfo.m_assetId = TestAssetId;
|
||||
assetInfo.m_assetType = AZ::AzTypeInfo<EmptyAsset>::Uuid();
|
||||
assetInfo.m_relativePath = TestAssetPath;
|
||||
|
||||
if (id == TestAssetId)
|
||||
{
|
||||
assetInfo.m_assetId = TestAssetId;
|
||||
assetInfo.m_assetType = AZ::AzTypeInfo<EmptyAsset>::Uuid();
|
||||
assetInfo.m_relativePath = TestAssetPath;
|
||||
}
|
||||
|
||||
return assetInfo;
|
||||
}
|
||||
|
||||
@@ -456,15 +473,20 @@ namespace UnitTest
|
||||
|
||||
// Set the mocked-out asset load to have a 0-byte length so that the load skips I/O and immediately returns success
|
||||
AZ::Data::AssetStreamInfo GetStreamInfoForLoad(
|
||||
[[maybe_unused]] const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override
|
||||
const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override
|
||||
{
|
||||
EXPECT_TRUE(type == AZ::AzTypeInfo<EmptyAsset>::Uuid());
|
||||
AZ::Data::AssetStreamInfo info;
|
||||
|
||||
info.m_dataOffset = 0;
|
||||
info.m_streamName = TestAssetPath;
|
||||
info.m_dataLen = 0;
|
||||
info.m_streamFlags = AZ::IO::OpenMode::ModeRead;
|
||||
|
||||
if (id == TestAssetId)
|
||||
{
|
||||
info.m_streamName = TestAssetPath;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -489,4 +511,27 @@ namespace UnitTest
|
||||
EXPECT_TRUE(testAsset.IsReady());
|
||||
}
|
||||
|
||||
// This test verifies that even if the asset loading returns immediately with an error, all of the loading code works
|
||||
// successfully. The test itself loads a missing asset twice - the first time is a non-immediate error, where the error
|
||||
// isn't reported until the DispatchEvents() call. The second time is an immediate error, because now the asset is already
|
||||
// registered in an Error state. If the test fails, it will likely get caught in the shutdown of the test class, if any
|
||||
// assets still exist at the point that the asset handler is unregistered. If they're present, then handling of the immediate
|
||||
// error didn't work, as it left around extra references to the asset that haven't been cleaned up.
|
||||
TEST_F(AssetManagerStreamerImmediateCompletionTests, ImmediateAssetError_WorksSuccessfully)
|
||||
{
|
||||
AZ::Data::AssetLoadParameters loadParams;
|
||||
|
||||
// Attempt to load a missing asset the first time. It will get an error, but not until the DispatchEvents() call happens.
|
||||
auto testAsset1 = AssetManager::Instance().GetAsset<EmptyAsset>(MissingAssetId, AZ::Data::AssetLoadBehavior::Default, loadParams);
|
||||
AZ::Data::AssetManager::Instance().DispatchEvents();
|
||||
EXPECT_TRUE(testAsset1.IsError());
|
||||
|
||||
// While the reference to the missing asset still exists, try to get it again. This will cause a more immediate error in
|
||||
// the AssetContainer code, which should still get handled correctly. In the failure condition, it will instead leave the
|
||||
// AssetContainer in a state where it never sends the final OnAssetContainerReady/Canceled message.
|
||||
auto testAsset2 = AssetManager::Instance().GetAsset<EmptyAsset>(MissingAssetId, AZ::Data::AssetLoadBehavior::Default, loadParams);
|
||||
AZ::Data::AssetManager::Instance().DispatchEvents();
|
||||
EXPECT_TRUE(testAsset2.IsError());
|
||||
}
|
||||
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -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"
|
||||
})";
|
||||
}
|
||||
|
||||
@@ -49,9 +49,13 @@ namespace UnitTest
|
||||
// ComponentApplicationBus
|
||||
AZ::ComponentApplication* GetApplication() override { return nullptr; }
|
||||
void RegisterComponentDescriptor(const AZ::ComponentDescriptor*) override {}
|
||||
void UnregisterComponentDescriptor(const AZ::ComponentDescriptor*) override {}
|
||||
void RegisterEntityAddedEventHandler(AZ::EntityAddedEvent::Handler&) override {}
|
||||
void RegisterEntityRemovedEventHandler(AZ::EntityRemovedEvent::Handler&) override {}
|
||||
void UnregisterComponentDescriptor(const AZ::ComponentDescriptor*) override {}
|
||||
void RegisterEntityActivatedEventHandler(AZ::EntityActivatedEvent::Handler&) override {}
|
||||
void RegisterEntityDeactivatedEventHandler(AZ::EntityDeactivatedEvent::Handler&) override {}
|
||||
void SignalEntityActivated(AZ::Entity*) override {}
|
||||
void SignalEntityDeactivated(AZ::Entity*) override {}
|
||||
bool AddEntity(AZ::Entity*) override { return true; }
|
||||
bool RemoveEntity(AZ::Entity*) override { return true; }
|
||||
bool DeleteEntity(const AZ::EntityId&) override { return true; }
|
||||
|
||||
@@ -1232,6 +1232,10 @@ namespace UnitTest
|
||||
void UnregisterComponentDescriptor(const ComponentDescriptor*) override { }
|
||||
void RegisterEntityAddedEventHandler(EntityAddedEvent::Handler&) override { }
|
||||
void RegisterEntityRemovedEventHandler(EntityRemovedEvent::Handler&) override { }
|
||||
void RegisterEntityActivatedEventHandler(EntityActivatedEvent::Handler&) override { }
|
||||
void RegisterEntityDeactivatedEventHandler(EntityDeactivatedEvent::Handler&) override { }
|
||||
void SignalEntityActivated(Entity*) override { }
|
||||
void SignalEntityDeactivated(Entity*) override { }
|
||||
bool AddEntity(Entity*) override { return false; }
|
||||
bool RemoveEntity(Entity*) override { return false; }
|
||||
bool DeleteEntity(const EntityId&) override { return false; }
|
||||
@@ -1252,6 +1256,7 @@ namespace UnitTest
|
||||
m_serializeContext.reset(aznew AZ::SerializeContext());
|
||||
|
||||
ComponentApplicationBus::Handler::BusConnect();
|
||||
AZ::Interface<AZ::ComponentApplicationRequests>::Register(this);
|
||||
|
||||
AZ::AllocatorInstance<AZ::PoolAllocator>::Create();
|
||||
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Create();
|
||||
@@ -1270,6 +1275,7 @@ namespace UnitTest
|
||||
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Destroy();
|
||||
AZ::AllocatorInstance<AZ::PoolAllocator>::Destroy();
|
||||
|
||||
AZ::Interface<AZ::ComponentApplicationRequests>::Unregister(this);
|
||||
ComponentApplicationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,7 +51,6 @@ namespace AZ::IO
|
||||
: ArchiveLocationPriority::ePakPriorityFileFirst }; // Which file location to favor (loose vs. pak files)
|
||||
int nMessageInvalidFileAccess{};
|
||||
int nLogInvalidFileAccess{ IsReleaseConfig ? 0 : 1 };
|
||||
int nLoadFrontendShaderCache{ FRONTEND_SHADER_CACHE_DEFAULT };
|
||||
int nDisableNonLevelRelatedPaks{ 1 };
|
||||
int nWarnOnPakAccessFails{ 1 }; // Whether to treat failed pak access as a warning or log message
|
||||
int nSetLogLevel{ 3 };
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
*/
|
||||
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <AzFramework/Visibility/EntityBoundsUnionBus.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <AzCore/Math/Quaternion.h>
|
||||
|
||||
@@ -693,9 +695,8 @@ namespace AzFramework
|
||||
parentId = handler->GetParentId();
|
||||
}
|
||||
#endif
|
||||
|
||||
AZ::Entity* parentEntity = nullptr;
|
||||
EBUS_EVENT_RESULT(parentEntity, AZ::ComponentApplicationBus, FindEntity, parentEntityId);
|
||||
AZ::ComponentApplicationRequests* componentApplication = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
|
||||
AZ::Entity* parentEntity = (componentApplication != nullptr) ? componentApplication->FindEntity(parentEntityId) : nullptr;
|
||||
AZ_Assert(parentEntity, "We expect to have a parent entity associated with the provided parent's entity Id.");
|
||||
if (parentEntity)
|
||||
{
|
||||
@@ -744,8 +745,8 @@ namespace AzFramework
|
||||
m_parentId = parentId;
|
||||
if (m_parentId.IsValid())
|
||||
{
|
||||
AZ::Entity* parentEntity = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(parentEntity, &AZ::ComponentApplicationBus::Events::FindEntity, m_parentId);
|
||||
AZ::ComponentApplicationRequests* componentApplication = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
|
||||
AZ::Entity* parentEntity = (componentApplication != nullptr) ? componentApplication->FindEntity(m_parentId) : nullptr;
|
||||
m_parentActive = parentEntity && (parentEntity->GetState() == AZ::Entity::State::Active);
|
||||
|
||||
m_onNewParentKeepWorldTM = isKeepWorldTM;
|
||||
@@ -832,6 +833,12 @@ namespace AzFramework
|
||||
|
||||
EBUS_EVENT_PTR(m_notificationBus, AZ::TransformNotificationBus, OnTransformChanged, m_localTM, m_worldTM);
|
||||
m_transformChangedEvent.Signal(m_localTM, m_worldTM);
|
||||
|
||||
AzFramework::IEntityBoundsUnion* boundsUnion = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get();
|
||||
if (boundsUnion != nullptr)
|
||||
{
|
||||
boundsUnion->OnTransformUpdated(GetEntity());
|
||||
}
|
||||
}
|
||||
|
||||
void TransformComponent::ComputeWorldTM()
|
||||
@@ -871,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
|
||||
|
||||
@@ -93,6 +93,8 @@ namespace AzFramework
|
||||
InitContext();
|
||||
|
||||
GameEntityContextRequestBus::Handler::BusConnect();
|
||||
|
||||
m_entityVisibilityBoundsUnionSystem.Connect();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
@@ -100,6 +102,8 @@ namespace AzFramework
|
||||
//=========================================================================
|
||||
void GameEntityContextComponent::Deactivate()
|
||||
{
|
||||
m_entityVisibilityBoundsUnionSystem.Disconnect();
|
||||
|
||||
GameEntityContextRequestBus::Handler::BusDisconnect();
|
||||
|
||||
DestroyContext();
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzFramework/Entity/GameEntityContextBus.h>
|
||||
#include <AzFramework/Entity/SliceGameEntityOwnershipService.h>
|
||||
#include <AzFramework/Visibility/EntityVisibilityBoundsUnionSystem.h>
|
||||
|
||||
#include "EntityContext.h"
|
||||
|
||||
@@ -91,6 +92,9 @@ namespace AzFramework
|
||||
{
|
||||
required.push_back(AZ_CRC("SliceSystemService", 0x1a5b7aad));
|
||||
}
|
||||
|
||||
private:
|
||||
AzFramework::EntityVisibilityBoundsUnionSystem m_entityVisibilityBoundsUnionSystem;
|
||||
};
|
||||
} // namespace AzFramework
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -139,7 +139,8 @@ namespace AzFramework
|
||||
"Implementers of IntersectionRequestBus must also implement BoundsRequestBus to ensure valid "
|
||||
"bounds are returned");
|
||||
|
||||
m_registeredEntities.Update({ entityId, CalculateEntityWorldBoundsUnion(entityId) });
|
||||
AZ::Entity* entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(entityId);
|
||||
m_registeredEntities.Update({ entityId, CalculateEntityWorldBoundsUnion(entity) });
|
||||
}
|
||||
|
||||
m_dirtyEntities.clear();
|
||||
|
||||
@@ -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>
|
||||
@@ -29,14 +30,14 @@ namespace AzFramework
|
||||
AZ_CVAR(float, ed_cameraSystemOrbitDollyScrollSpeed, 0.02f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(float, ed_cameraSystemOrbitDollyCursorSpeed, 0.01f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(float, ed_cameraSystemScrollTranslateSpeed, 0.02f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(float, ed_cameraSystemDefaultOrbitDistance, 60.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(float, ed_cameraSystemMaxOrbitDistance, 100.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(float, ed_cameraSystemMaxOrbitDistance, 60.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(float, ed_cameraSystemLookSmoothness, 5.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(float, ed_cameraSystemTranslateSmoothness, 5.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(float, ed_cameraSystemRotateSpeed, 0.005f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(float, ed_cameraSystemPanSpeed, 0.01f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(bool, ed_cameraSystemPanInvertX, true, nullptr, AZ::ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(bool, ed_cameraSystemPanInvertY, true, nullptr, AZ::ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(float, ed_cameraSystemLookDeadzone, 2.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
|
||||
|
||||
AZ_CVAR(
|
||||
AZ::CVarFixedString, ed_cameraSystemTranslateForwardKey, "keyboard_key_alphanumeric_W", nullptr, AZ::ConsoleFunctorFlags::Null, "");
|
||||
@@ -125,22 +126,22 @@ namespace AzFramework
|
||||
{
|
||||
if (orientation.GetElement(2, 0) > -1.0f)
|
||||
{
|
||||
x = std::atan2(orientation.GetElement(2, 1), orientation.GetElement(2, 2));
|
||||
y = std::asin(-orientation.GetElement(2, 0));
|
||||
z = std::atan2(orientation.GetElement(1, 0), orientation.GetElement(0, 0));
|
||||
x = AZStd::atan2(orientation.GetElement(2, 1), orientation.GetElement(2, 2));
|
||||
y = AZStd::asin(-orientation.GetElement(2, 0));
|
||||
z = AZStd::atan2(orientation.GetElement(1, 0), orientation.GetElement(0, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
x = 0.0f;
|
||||
y = AZ::Constants::Pi * 0.5f;
|
||||
z = -std::atan2(-orientation.GetElement(2, 1), orientation.GetElement(1, 1));
|
||||
z = -AZStd::atan2(-orientation.GetElement(2, 1), orientation.GetElement(1, 1));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
x = 0.0f;
|
||||
y = -AZ::Constants::Pi * 0.5f;
|
||||
z = std::atan2(-orientation.GetElement(1, 2), orientation.GetElement(1, 1));
|
||||
z = AZStd::atan2(-orientation.GetElement(1, 2), orientation.GetElement(1, 1));
|
||||
}
|
||||
|
||||
return {x, y, z};
|
||||
@@ -150,37 +151,31 @@ namespace AzFramework
|
||||
{
|
||||
const auto eulerAngles = AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromTransform(transform));
|
||||
|
||||
camera.m_lookAt = transform.GetTranslation();
|
||||
camera.m_pitch = eulerAngles.GetX();
|
||||
camera.m_yaw = eulerAngles.GetZ();
|
||||
// note: m_lookDist is negative so we must invert it here
|
||||
camera.m_lookAt = transform.GetTranslation() + (camera.Rotation().GetBasisY() * -camera.m_lookDist);
|
||||
}
|
||||
|
||||
bool CameraSystem::HandleEvents(const InputEvent& event)
|
||||
{
|
||||
if (const auto& cursor_motion = AZStd::get_if<CursorMotionEvent>(&event))
|
||||
if (const auto& cursor = AZStd::get_if<CursorEvent>(&event))
|
||||
{
|
||||
m_currentCursorPosition = cursor_motion->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);
|
||||
return m_cameras.HandleEvents(event, m_cursorState.CursorDelta(), m_scrollDelta);
|
||||
}
|
||||
|
||||
Camera CameraSystem::StepCamera(const Camera& targetCamera, const float deltaTime)
|
||||
{
|
||||
const auto cursorDelta = m_currentCursorPosition.has_value() && m_lastCursorPosition.has_value()
|
||||
? m_currentCursorPosition.value() - m_lastCursorPosition.value()
|
||||
: ScreenVector(0, 0);
|
||||
const auto nextCamera = m_cameras.StepCamera(targetCamera, m_cursorState.CursorDelta(), m_scrollDelta, deltaTime);
|
||||
|
||||
if (m_currentCursorPosition.has_value())
|
||||
{
|
||||
m_lastCursorPosition = m_currentCursorPosition;
|
||||
}
|
||||
|
||||
const auto nextCamera = m_cameras.StepCamera(targetCamera, cursorDelta, m_scrollDelta, deltaTime);
|
||||
m_cursorState.Update();
|
||||
|
||||
m_scrollDelta = 0.0f;
|
||||
|
||||
@@ -192,18 +187,18 @@ namespace AzFramework
|
||||
m_idleCameraInputs.push_back(AZStd::move(cameraInput));
|
||||
}
|
||||
|
||||
bool Cameras::HandleEvents(const InputEvent& event)
|
||||
bool Cameras::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta)
|
||||
{
|
||||
bool handling = false;
|
||||
for (auto& cameraInput : m_activeCameraInputs)
|
||||
{
|
||||
cameraInput->HandleEvents(event);
|
||||
cameraInput->HandleEvents(event, cursorDelta, scrollDelta);
|
||||
handling = !cameraInput->Idle() || handling;
|
||||
}
|
||||
|
||||
for (auto& cameraInput : m_idleCameraInputs)
|
||||
{
|
||||
cameraInput->HandleEvents(event);
|
||||
cameraInput->HandleEvents(event, cursorDelta, scrollDelta);
|
||||
}
|
||||
|
||||
return handling;
|
||||
@@ -215,8 +210,8 @@ namespace AzFramework
|
||||
{
|
||||
auto& cameraInput = m_idleCameraInputs[i];
|
||||
const bool canBegin = cameraInput->Beginning() &&
|
||||
std::all_of(m_activeCameraInputs.cbegin(), m_activeCameraInputs.cend(),
|
||||
[](const auto& input) { return !input->Exclusive(); }) &&
|
||||
AZStd::all_of(m_activeCameraInputs.cbegin(), m_activeCameraInputs.cend(),
|
||||
[](const auto& input) { return !input->Exclusive(); }) &&
|
||||
(!cameraInput->Exclusive() || (cameraInput->Exclusive() && m_activeCameraInputs.empty()));
|
||||
|
||||
if (canBegin)
|
||||
@@ -232,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();)
|
||||
{
|
||||
@@ -271,21 +266,42 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
void RotateCameraInput::HandleEvents(const InputEvent& event)
|
||||
RotateCameraInput::RotateCameraInput(const InputChannelId rotateChannelId)
|
||||
: m_rotateChannelId(rotateChannelId)
|
||||
{
|
||||
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
|
||||
{
|
||||
if (input->m_channelId == m_rotateChannelId)
|
||||
}
|
||||
|
||||
void RotateCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
|
||||
{
|
||||
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)
|
||||
{
|
||||
BeginActivation();
|
||||
}
|
||||
else if (input->m_state == InputChannel::State::Ended)
|
||||
{
|
||||
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;
|
||||
}();
|
||||
|
||||
switch (const auto outcome = m_clickDetector.DetectClick(clickEvent, cursorDelta); outcome)
|
||||
{
|
||||
case ClickDetector::ClickOutcome::Move:
|
||||
BeginActivation();
|
||||
break;
|
||||
case ClickDetector::ClickOutcome::Release:
|
||||
EndActivation();
|
||||
break;
|
||||
default:
|
||||
// noop
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,7 +314,7 @@ namespace AzFramework
|
||||
nextCamera.m_pitch -= float(cursorDelta.m_y) * ed_cameraSystemRotateSpeed;
|
||||
nextCamera.m_yaw -= float(cursorDelta.m_x) * ed_cameraSystemRotateSpeed;
|
||||
|
||||
const auto clampRotation = [](const float angle) { return std::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi); };
|
||||
const auto clampRotation = [](const float angle) { return AZStd::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi); };
|
||||
|
||||
nextCamera.m_yaw = clampRotation(nextCamera.m_yaw);
|
||||
// clamp pitch to be +-90 degrees
|
||||
@@ -307,7 +323,14 @@ namespace AzFramework
|
||||
return nextCamera;
|
||||
}
|
||||
|
||||
void PanCameraInput::HandleEvents(const InputEvent& event)
|
||||
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)
|
||||
{
|
||||
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
|
||||
{
|
||||
@@ -382,17 +405,18 @@ namespace AzFramework
|
||||
return TranslationType::Nil;
|
||||
}
|
||||
|
||||
void TranslateCameraInput::HandleEvents(const InputEvent& event)
|
||||
TranslateCameraInput::TranslateCameraInput(TranslationAxesFn translationAxesFn)
|
||||
: m_translationAxesFn(AZStd::move(translationAxesFn))
|
||||
{
|
||||
}
|
||||
|
||||
void TranslateCameraInput::HandleEvents(
|
||||
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
|
||||
{
|
||||
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -478,7 +502,7 @@ namespace AzFramework
|
||||
m_boost = false;
|
||||
}
|
||||
|
||||
void OrbitCameraInput::HandleEvents(const InputEvent& event)
|
||||
void OrbitCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta)
|
||||
{
|
||||
if (const auto* input = AZStd::get_if<DiscreteInputEvent>(&event))
|
||||
{
|
||||
@@ -497,7 +521,7 @@ namespace AzFramework
|
||||
|
||||
if (Active())
|
||||
{
|
||||
m_orbitCameras.HandleEvents(event);
|
||||
m_orbitCameras.HandleEvents(event, cursorDelta, scrollDelta);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,8 +533,10 @@ namespace AzFramework
|
||||
if (Beginning())
|
||||
{
|
||||
float hit_distance = 0.0f;
|
||||
if (AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3::CreateAxisZ(), AZ::Vector3::CreateAxisZ(ed_cameraSystemDefaultPlaneHeight))
|
||||
.CastRay(targetCamera.Translation(), targetCamera.Rotation().GetBasisY(), hit_distance))
|
||||
AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3::CreateAxisZ(), AZ::Vector3::CreateAxisZ(ed_cameraSystemDefaultPlaneHeight))
|
||||
.CastRay(targetCamera.Translation(), targetCamera.Rotation().GetBasisY(), hit_distance);
|
||||
|
||||
if (hit_distance > 0.0f)
|
||||
{
|
||||
hit_distance = AZStd::min<float>(hit_distance, ed_cameraSystemMaxOrbitDistance);
|
||||
nextCamera.m_lookDist = -hit_distance;
|
||||
@@ -539,7 +565,8 @@ namespace AzFramework
|
||||
return nextCamera;
|
||||
}
|
||||
|
||||
void OrbitDollyScrollCameraInput::HandleEvents(const InputEvent& event)
|
||||
void OrbitDollyScrollCameraInput::HandleEvents(
|
||||
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
|
||||
{
|
||||
if (const auto* scroll = AZStd::get_if<ScrollEvent>(&event))
|
||||
{
|
||||
@@ -557,7 +584,13 @@ namespace AzFramework
|
||||
return nextCamera;
|
||||
}
|
||||
|
||||
void OrbitDollyCursorMoveCameraInput::HandleEvents(const InputEvent& event)
|
||||
OrbitDollyCursorMoveCameraInput::OrbitDollyCursorMoveCameraInput(const InputChannelId dollyChannelId)
|
||||
: m_dollyChannelId(dollyChannelId)
|
||||
{
|
||||
}
|
||||
|
||||
void OrbitDollyCursorMoveCameraInput::HandleEvents(
|
||||
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
|
||||
{
|
||||
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
|
||||
{
|
||||
@@ -584,7 +617,8 @@ namespace AzFramework
|
||||
return nextCamera;
|
||||
}
|
||||
|
||||
void ScrollTranslationCameraInput::HandleEvents(const InputEvent& event)
|
||||
void ScrollTranslationCameraInput::HandleEvents(
|
||||
const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta)
|
||||
{
|
||||
if (const auto* scroll = AZStd::get_if<ScrollEvent>(&event))
|
||||
{
|
||||
@@ -610,7 +644,7 @@ namespace AzFramework
|
||||
|
||||
Camera SmoothCamera(const Camera& currentCamera, const Camera& targetCamera, const float deltaTime)
|
||||
{
|
||||
const auto clamp_rotation = [](const float angle) { return std::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi); };
|
||||
const auto clamp_rotation = [](const float angle) { return AZStd::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi); };
|
||||
|
||||
// keep yaw in 0 - 360 range
|
||||
float targetYaw = clamp_rotation(targetCamera.m_yaw);
|
||||
@@ -621,7 +655,7 @@ namespace AzFramework
|
||||
|
||||
// ensure smooth transition when moving across 0 - 360 boundary
|
||||
const float yawDelta = targetYaw - currentYaw;
|
||||
if (std::abs(yawDelta) >= AZ::Constants::Pi)
|
||||
if (AZStd::abs(yawDelta) >= AZ::Constants::Pi)
|
||||
{
|
||||
targetYaw -= AZ::Constants::TwoPi * sign(yawDelta);
|
||||
}
|
||||
@@ -629,12 +663,12 @@ namespace AzFramework
|
||||
Camera camera;
|
||||
// note: the math for the lerp smoothing implementation for camera rotation and translation was inspired by this excellent
|
||||
// article by Scott Lembcke: https://www.gamasutra.com/blogs/ScottLembcke/20180404/316046/Improved_Lerp_Smoothing.php
|
||||
const float lookRate = std::exp2(ed_cameraSystemLookSmoothness);
|
||||
const float lookT = std::exp2(-lookRate * deltaTime);
|
||||
const float lookRate = AZStd::exp2(ed_cameraSystemLookSmoothness);
|
||||
const float lookT = AZStd::exp2(-lookRate * deltaTime);
|
||||
camera.m_pitch = AZ::Lerp(targetCamera.m_pitch, currentCamera.m_pitch, lookT);
|
||||
camera.m_yaw = AZ::Lerp(targetYaw, currentYaw, lookT);
|
||||
const float moveRate = std::exp2(ed_cameraSystemTranslateSmoothness);
|
||||
const float moveT = std::exp2(-moveRate * deltaTime);
|
||||
const float moveRate = AZStd::exp2(ed_cameraSystemTranslateSmoothness);
|
||||
const float moveT = AZStd::exp2(-moveRate * deltaTime);
|
||||
camera.m_lookDist = AZ::Lerp(targetCamera.m_lookDist, currentCamera.m_lookDist, moveT);
|
||||
camera.m_lookAt = targetCamera.m_lookAt.Lerp(currentCamera.m_lookAt, moveT);
|
||||
return camera;
|
||||
@@ -655,7 +689,7 @@ namespace AzFramework
|
||||
const auto* position = inputChannel.GetCustomData<AzFramework::InputChannel::PositionData2D>();
|
||||
AZ_Assert(position, "Expected PositionData2D but found nullptr");
|
||||
|
||||
return CursorMotionEvent{ScreenPoint(
|
||||
return CursorEvent{ScreenPoint(
|
||||
position->m_normalizedPosition.GetX() * windowSize.m_width, position->m_normalizedPosition.GetY() * windowSize.m_height)};
|
||||
}
|
||||
else if (inputChannelId == InputDeviceMouse::Movement::Z)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -70,7 +72,7 @@ namespace AzFramework
|
||||
|
||||
void UpdateCameraFromTransform(Camera& camera, const AZ::Transform& transform);
|
||||
|
||||
struct CursorMotionEvent
|
||||
struct CursorEvent
|
||||
{
|
||||
ScreenPoint m_position;
|
||||
};
|
||||
@@ -86,7 +88,7 @@ namespace AzFramework
|
||||
InputChannel::State m_state; //!< Channel state. (e.g. Begin/update/end event).
|
||||
};
|
||||
|
||||
using InputEvent = AZStd::variant<AZStd::monostate, CursorMotionEvent, ScrollEvent, DiscreteInputEvent>;
|
||||
using InputEvent = AZStd::variant<AZStd::monostate, CursorEvent, ScrollEvent, DiscreteInputEvent>;
|
||||
|
||||
class CameraInput
|
||||
{
|
||||
@@ -147,7 +149,7 @@ namespace AzFramework
|
||||
ResetImpl();
|
||||
}
|
||||
|
||||
virtual void HandleEvents(const InputEvent& event) = 0;
|
||||
virtual void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) = 0;
|
||||
virtual Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) = 0;
|
||||
|
||||
virtual bool Exclusive() const
|
||||
@@ -170,7 +172,7 @@ namespace AzFramework
|
||||
{
|
||||
public:
|
||||
void AddCamera(AZStd::shared_ptr<CameraInput> cameraInput);
|
||||
bool HandleEvents(const InputEvent& event);
|
||||
bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta);
|
||||
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime);
|
||||
void Reset();
|
||||
|
||||
@@ -188,24 +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) override;
|
||||
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;
|
||||
ClickDetector m_clickDetector;
|
||||
};
|
||||
|
||||
struct PanAxes
|
||||
@@ -238,12 +237,9 @@ namespace AzFramework
|
||||
class PanCameraInput : public CameraInput
|
||||
{
|
||||
public:
|
||||
PanCameraInput(const InputChannelId panChannelId, PanAxesFn panAxesFn)
|
||||
: m_panAxesFn(AZStd::move(panAxesFn))
|
||||
, m_panChannelId(panChannelId)
|
||||
{
|
||||
}
|
||||
void HandleEvents(const InputEvent& event) override;
|
||||
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;
|
||||
|
||||
private:
|
||||
@@ -281,11 +277,9 @@ namespace AzFramework
|
||||
class TranslateCameraInput : public CameraInput
|
||||
{
|
||||
public:
|
||||
explicit TranslateCameraInput(TranslationAxesFn translationAxesFn)
|
||||
: m_translationAxesFn(AZStd::move(translationAxesFn))
|
||||
{
|
||||
}
|
||||
void HandleEvents(const InputEvent& event) override;
|
||||
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;
|
||||
|
||||
@@ -354,17 +348,16 @@ namespace AzFramework
|
||||
class OrbitDollyScrollCameraInput : public CameraInput
|
||||
{
|
||||
public:
|
||||
void HandleEvents(const InputEvent& event) override;
|
||||
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
|
||||
};
|
||||
|
||||
class OrbitDollyCursorMoveCameraInput : public CameraInput
|
||||
{
|
||||
public:
|
||||
explicit OrbitDollyCursorMoveCameraInput(const InputChannelId dollyChannelId)
|
||||
: m_dollyChannelId(dollyChannelId) {}
|
||||
explicit OrbitDollyCursorMoveCameraInput(InputChannelId dollyChannelId);
|
||||
|
||||
void HandleEvents(const InputEvent& event) override;
|
||||
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:
|
||||
@@ -374,14 +367,14 @@ namespace AzFramework
|
||||
class ScrollTranslationCameraInput : public CameraInput
|
||||
{
|
||||
public:
|
||||
void HandleEvents(const InputEvent& event) override;
|
||||
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
|
||||
};
|
||||
|
||||
class OrbitCameraInput : public CameraInput
|
||||
{
|
||||
public:
|
||||
void HandleEvents(const InputEvent& event) override;
|
||||
void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override;
|
||||
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
|
||||
bool Exclusive() const 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
|
||||
@@ -134,11 +134,16 @@ namespace AzFramework
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
inline float ScreenVectorLength(const ScreenVector& screenVector)
|
||||
{
|
||||
return aznumeric_cast<float>(AZStd::sqrt(screenVector.m_x * screenVector.m_x + screenVector.m_y * screenVector.m_y));
|
||||
}
|
||||
|
||||
inline ScreenPoint ScreenPointFromNDC(const AZ::Vector3& screenNDC, const AZ::Vector2& viewportSize)
|
||||
{
|
||||
return ScreenPoint(
|
||||
aznumeric_caster(std::round(screenNDC.GetX() * viewportSize.GetX())),
|
||||
aznumeric_caster(std::round((1.0f - screenNDC.GetY()) * viewportSize.GetY())));
|
||||
aznumeric_caster(AZStd::round(screenNDC.GetX() * viewportSize.GetX())),
|
||||
aznumeric_caster(AZStd::round((1.0f - screenNDC.GetY()) * viewportSize.GetY())));
|
||||
}
|
||||
|
||||
inline AZ::Vector2 NDCFromScreenPoint(const ScreenPoint& screenPoint, const AZ::Vector2& viewportSize)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/ComponentBus.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzFramework/Render/GeometryIntersectionStructures.h>
|
||||
@@ -27,7 +28,8 @@ namespace AZ
|
||||
namespace AzFramework
|
||||
{
|
||||
//! Implemented by components that provide bounds for use with various systems.
|
||||
class BoundsRequests : public AZ::ComponentBus
|
||||
class BoundsRequests
|
||||
: public AZ::ComponentBus
|
||||
{
|
||||
public:
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
@@ -37,6 +39,7 @@ namespace AzFramework
|
||||
//! more than one component may be providing a bound. It isn't guaranteed which bound
|
||||
//! will be returned by a single call to GetWorldBounds.
|
||||
virtual AZ::Aabb GetWorldBounds() = 0;
|
||||
|
||||
//! Returns an axis aligned bounding box in local space.
|
||||
//! @note It is preferred to use CalculateEntityLocalBoundsUnion in the general case as
|
||||
//! more than one component may be providing a bound. It isn't guaranteed which bound
|
||||
@@ -46,17 +49,15 @@ namespace AzFramework
|
||||
protected:
|
||||
~BoundsRequests() = default;
|
||||
};
|
||||
|
||||
using BoundsRequestBus = AZ::EBus<BoundsRequests>;
|
||||
|
||||
//! Returns a union of all local Aabbs provided by components implementing the BoundsRequestBus.
|
||||
//! @note It is preferred to call this function as opposed to GetLocalBounds directly as more than one
|
||||
//! component may be implementing this bus on an Entity and so only the first result (Aabb) will be returned.
|
||||
inline AZ::Aabb CalculateEntityLocalBoundsUnion(const AZ::EntityId entityId)
|
||||
inline AZ::Aabb CalculateEntityLocalBoundsUnion(const AZ::Entity* entity)
|
||||
{
|
||||
AZ::EBusReduceResult<AZ::Aabb, AabbUnionAggregator> aabbResult(AZ::Aabb::CreateNull());
|
||||
BoundsRequestBus::EventResult(
|
||||
aabbResult, entityId, &BoundsRequestBus::Events::GetLocalBounds);
|
||||
BoundsRequestBus::EventResult(aabbResult, entity->GetId(), &BoundsRequestBus::Events::GetLocalBounds);
|
||||
|
||||
if (aabbResult.value.IsValid())
|
||||
{
|
||||
@@ -69,18 +70,18 @@ namespace AzFramework
|
||||
//! Returns a union of all world Aabbs provided by components implementing the BoundsRequestBus.
|
||||
//! @note It is preferred to call this function as opposed to GetWorldBounds directly as more than one
|
||||
//! component may be implementing this bus on an Entity and so only the first result (Aabb) will be returned.
|
||||
inline AZ::Aabb CalculateEntityWorldBoundsUnion(const AZ::EntityId entityId)
|
||||
inline AZ::Aabb CalculateEntityWorldBoundsUnion(const AZ::Entity* entity)
|
||||
{
|
||||
AZ::EBusReduceResult<AZ::Aabb, AabbUnionAggregator> aabbResult(AZ::Aabb::CreateNull());
|
||||
BoundsRequestBus::EventResult(aabbResult, entityId, &BoundsRequestBus::Events::GetWorldBounds);
|
||||
BoundsRequestBus::EventResult(aabbResult, entity->GetId(), &BoundsRequestBus::Events::GetWorldBounds);
|
||||
|
||||
if (aabbResult.value.IsValid())
|
||||
{
|
||||
return aabbResult.value;
|
||||
}
|
||||
|
||||
AZ::Vector3 worldTranslation = AZ::Vector3::CreateZero();
|
||||
AZ::TransformBus::EventResult(worldTranslation, entityId, &AZ::TransformBus::Events::GetWorldTranslation);
|
||||
AZ::TransformInterface* transformInterface = entity->GetTransform();
|
||||
const AZ::Vector3 worldTranslation = transformInterface->GetWorldTranslation();
|
||||
return AZ::Aabb::CreateCenterHalfExtents(worldTranslation, AZ::Vector3(0.5f));
|
||||
}
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -23,9 +23,11 @@ namespace AzFramework
|
||||
{
|
||||
//! Provides an interface to retrieve and update the union of all Aabbs on a single Entity.
|
||||
//! @note This will be the combination/union of all individual Component Aabbs.
|
||||
class EntityBoundsUnionRequests : public AZ::EBusTraits
|
||||
class IEntityBoundsUnion
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(IEntityBoundsUnion, "{106968DD-43C0-478E-8045-523E0BF5D0F5}");
|
||||
|
||||
//! Requests the cached union of component Aabbs to be recalculated as one may have changed.
|
||||
//! @note This is used to drive event driven updates to the visibility system.
|
||||
virtual void RefreshEntityLocalBoundsUnion(AZ::EntityId entityId) = 0;
|
||||
@@ -38,9 +40,21 @@ namespace AzFramework
|
||||
//! also be called explicitly (e.g. For testing purposes).
|
||||
virtual void ProcessEntityBoundsUnionRequests() = 0;
|
||||
|
||||
//! Notifies the EntityBoundsUnion system that an entities transform has been modified.
|
||||
//! @param entity the entity whose transform has been modified.
|
||||
virtual void OnTransformUpdated(AZ::Entity* entity) = 0;
|
||||
|
||||
protected:
|
||||
~EntityBoundsUnionRequests() = default;
|
||||
~IEntityBoundsUnion() = default;
|
||||
};
|
||||
|
||||
using EntityBoundsUnionRequestBus = AZ::EBus<EntityBoundsUnionRequests>;
|
||||
// EBus wrapper for ScriptCanvas
|
||||
class IEntityBoundsUnionTraits
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
};
|
||||
using IEntityBoundsUnionRequestBus = AZ::EBus<IEntityBoundsUnion, IEntityBoundsUnionTraits>;
|
||||
} // namespace AzFramework
|
||||
|
||||
+57
-64
@@ -17,69 +17,70 @@
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
EntityVisibilityBoundsUnionSystem::EntityVisibilityBoundsUnionSystem()
|
||||
: m_entityActivatedEventHandler([this](AZ::Entity* entity) { OnEntityActivated(entity); })
|
||||
, m_entityDeactivatedEventHandler([this](AZ::Entity* entity) { OnEntityDeactivated(entity); })
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void EntityVisibilityBoundsUnionSystem::Connect()
|
||||
{
|
||||
EntityBoundsUnionRequestBus::Handler::BusConnect();
|
||||
AZ::TransformNotificationBus::Router::BusRouterConnect();
|
||||
AZ::EntitySystemBus::Handler::BusConnect();
|
||||
AZ::Interface<IEntityBoundsUnion>::Register(this);
|
||||
IEntityBoundsUnionRequestBus::Handler::BusConnect();
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
|
||||
AZ::Interface<AZ::ComponentApplicationRequests>::Get()->RegisterEntityActivatedEventHandler(m_entityActivatedEventHandler);
|
||||
AZ::Interface<AZ::ComponentApplicationRequests>::Get()->RegisterEntityDeactivatedEventHandler(m_entityDeactivatedEventHandler);
|
||||
}
|
||||
|
||||
void EntityVisibilityBoundsUnionSystem::Disconnect()
|
||||
{
|
||||
m_entityActivatedEventHandler.Disconnect();
|
||||
m_entityDeactivatedEventHandler.Disconnect();
|
||||
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
AZ::EntitySystemBus::Handler::BusDisconnect();
|
||||
AZ::TransformNotificationBus::Router::BusRouterDisconnect();
|
||||
EntityBoundsUnionRequestBus::Handler::BusDisconnect();
|
||||
IEntityBoundsUnionRequestBus::Handler::BusDisconnect();
|
||||
AZ::Interface<IEntityBoundsUnion>::Unregister(this);
|
||||
}
|
||||
|
||||
static void SetUserDataEntityId(VisibilityEntry& visibilityEntry, const AZ::EntityId entityId)
|
||||
{
|
||||
static_assert(
|
||||
sizeof(AZ::EntityId) <= sizeof(visibilityEntry.m_userData), "Ensure EntityId fits into m_userData");
|
||||
|
||||
visibilityEntry.m_typeFlags = VisibilityEntry::TYPE_Entity;
|
||||
|
||||
std::memcpy(&visibilityEntry.m_userData, &entityId, sizeof(AZ::EntityId));
|
||||
}
|
||||
|
||||
void EntityVisibilityBoundsUnionSystem::OnEntityActivated(const AZ::EntityId& entityId)
|
||||
void EntityVisibilityBoundsUnionSystem::OnEntityActivated(AZ::Entity* entity)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzFramework);
|
||||
|
||||
// ignore any entity that might activate which does not have a TransformComponent
|
||||
if (!AZ::TransformBus::HasHandlers(entityId))
|
||||
if (entity->GetTransform() == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto instance_it = m_entityVisibilityBoundsUnionInstanceMapping.find(entityId);
|
||||
if (auto instance_it = m_entityVisibilityBoundsUnionInstanceMapping.find(entity);
|
||||
instance_it == m_entityVisibilityBoundsUnionInstanceMapping.end())
|
||||
{
|
||||
AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity();
|
||||
AZ::TransformBus::EventResult(worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM);
|
||||
AZ::TransformInterface* transformInterface = entity->GetTransform();
|
||||
const AZ::Vector3 entityPosition = transformInterface->GetWorldTranslation();
|
||||
|
||||
EntityVisibilityBoundsUnionInstance instance;
|
||||
instance.m_worldTransform = worldFromLocal;
|
||||
instance.m_localEntityBoundsUnion = CalculateEntityLocalBoundsUnion(entityId);
|
||||
SetUserDataEntityId(instance.m_visibilityEntry, entityId);
|
||||
instance.m_localEntityBoundsUnion = CalculateEntityLocalBoundsUnion(entity);
|
||||
instance.m_visibilityEntry.m_typeFlags = VisibilityEntry::TYPE_Entity;
|
||||
instance.m_visibilityEntry.m_userData = static_cast<void*>(entity);
|
||||
|
||||
auto next_it = m_entityVisibilityBoundsUnionInstanceMapping.insert({entityId, instance});
|
||||
UpdateVisibilitySystem(next_it.first->second);
|
||||
auto next_it = m_entityVisibilityBoundsUnionInstanceMapping.insert({ entity, instance });
|
||||
UpdateVisibilitySystem(entity, next_it.first->second);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityVisibilityBoundsUnionSystem::OnEntityDeactivated(const AZ::EntityId& entityId)
|
||||
void EntityVisibilityBoundsUnionSystem::OnEntityDeactivated(AZ::Entity* entity)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzFramework);
|
||||
|
||||
// ignore any entity that might deactivate which does not have a TransformComponent
|
||||
if (!AZ::TransformBus::HasHandlers(entityId))
|
||||
if (entity->GetTransform() == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto instance_it = m_entityVisibilityBoundsUnionInstanceMapping.find(entityId);
|
||||
if (auto instance_it = m_entityVisibilityBoundsUnionInstanceMapping.find(entity);
|
||||
instance_it != m_entityVisibilityBoundsUnionInstanceMapping.end())
|
||||
{
|
||||
if (IVisibilitySystem* visibilitySystem = AZ::Interface<IVisibilitySystem>::Get())
|
||||
@@ -90,7 +91,7 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
void EntityVisibilityBoundsUnionSystem::UpdateVisibilitySystem(EntityVisibilityBoundsUnionInstance& instance)
|
||||
void EntityVisibilityBoundsUnionSystem::UpdateVisibilitySystem(AZ::Entity* entity, EntityVisibilityBoundsUnionInstance& instance)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzFramework);
|
||||
|
||||
@@ -98,8 +99,8 @@ namespace AzFramework
|
||||
{
|
||||
// note: worldEntityBounds will not be a 'tight-fit' Aabb but that of a transformed local aabb
|
||||
// there will be some wasted space but it should be sufficient for the visibility system
|
||||
const AZ::Aabb worldEntityBoundsUnion =
|
||||
localEntityBoundsUnions.GetTransformedAabb(instance.m_worldTransform);
|
||||
AZ::TransformInterface* transformInterface = entity->GetTransform();
|
||||
const AZ::Aabb worldEntityBoundsUnion = localEntityBoundsUnions.GetTransformedAabb(transformInterface->GetWorldTM());
|
||||
IVisibilitySystem* visibilitySystem = AZ::Interface<IVisibilitySystem>::Get();
|
||||
if (visibilitySystem && !worldEntityBoundsUnion.IsClose(instance.m_visibilityEntry.m_boundingVolume))
|
||||
{
|
||||
@@ -111,19 +112,27 @@ namespace AzFramework
|
||||
|
||||
void EntityVisibilityBoundsUnionSystem::RefreshEntityLocalBoundsUnion(const AZ::EntityId entityId)
|
||||
{
|
||||
// track entities that need their bounds union to be recalculated
|
||||
m_entityIdsBoundsDirty.insert(entityId);
|
||||
AZ::Entity* entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(entityId);
|
||||
if (entity != nullptr)
|
||||
{
|
||||
// track entities that need their bounds union to be recalculated
|
||||
m_entityBoundsDirty.insert(entity);
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Aabb EntityVisibilityBoundsUnionSystem::GetEntityLocalBoundsUnion(const AZ::EntityId entityId) const
|
||||
{
|
||||
// if the EntityId is not found in the mapping then return a null Aabb, this is to mimic
|
||||
// as closely as possible the behavior of an individual GetLocalBounds call to an Entity that
|
||||
// had been deleted (there would be no response, leaving the default value assigned)
|
||||
if (auto instance_it = m_entityVisibilityBoundsUnionInstanceMapping.find(entityId);
|
||||
instance_it != m_entityVisibilityBoundsUnionInstanceMapping.end())
|
||||
AZ::Entity* entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(entityId);
|
||||
if (entity != nullptr)
|
||||
{
|
||||
return instance_it->second.m_localEntityBoundsUnion;
|
||||
// if the entity is not found in the mapping then return a null Aabb, this is to mimic
|
||||
// as closely as possible the behavior of an individual GetLocalBounds call to an Entity that
|
||||
// had been deleted (there would be no response, leaving the default value assigned)
|
||||
if (auto instance_it = m_entityVisibilityBoundsUnionInstanceMapping.find(entity);
|
||||
instance_it != m_entityVisibilityBoundsUnionInstanceMapping.end())
|
||||
{
|
||||
return instance_it->second.m_localEntityBoundsUnion;
|
||||
}
|
||||
}
|
||||
|
||||
return AZ::Aabb::CreateNull();
|
||||
@@ -134,45 +143,29 @@ namespace AzFramework
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzFramework);
|
||||
|
||||
// iterate over all entities whose bounds changed and recalculate them
|
||||
for (const auto& entityId : m_entityIdsBoundsDirty)
|
||||
for (const auto& entity : m_entityBoundsDirty)
|
||||
{
|
||||
if (auto instance_it = m_entityVisibilityBoundsUnionInstanceMapping.find(entityId);
|
||||
if (auto instance_it = m_entityVisibilityBoundsUnionInstanceMapping.find(entity);
|
||||
instance_it != m_entityVisibilityBoundsUnionInstanceMapping.end())
|
||||
{
|
||||
instance_it->second.m_localEntityBoundsUnion = CalculateEntityLocalBoundsUnion(entityId);
|
||||
}
|
||||
}
|
||||
|
||||
auto allDirtyEntityIds = m_entityIdsTransformDirty;
|
||||
allDirtyEntityIds.insert(m_entityIdsBoundsDirty.begin(), m_entityIdsBoundsDirty.end());
|
||||
|
||||
for (const auto& dirtyEntityId : allDirtyEntityIds)
|
||||
{
|
||||
if (auto instance_it = m_entityVisibilityBoundsUnionInstanceMapping.find(dirtyEntityId);
|
||||
instance_it != m_entityVisibilityBoundsUnionInstanceMapping.end())
|
||||
{
|
||||
UpdateVisibilitySystem(instance_it->second);
|
||||
instance_it->second.m_localEntityBoundsUnion = CalculateEntityLocalBoundsUnion(entity);
|
||||
UpdateVisibilitySystem(entity, instance_it->second);
|
||||
}
|
||||
}
|
||||
|
||||
// clear dirty entities once the visibility system has been updated
|
||||
m_entityIdsBoundsDirty.clear();
|
||||
m_entityIdsTransformDirty.clear();
|
||||
m_entityBoundsDirty.clear();
|
||||
}
|
||||
|
||||
void EntityVisibilityBoundsUnionSystem::OnTransformChanged(
|
||||
[[maybe_unused]] const AZ::Transform& local, const AZ::Transform& world)
|
||||
void EntityVisibilityBoundsUnionSystem::OnTransformUpdated(AZ::Entity* entity)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzFramework);
|
||||
|
||||
const AZ::EntityId entityId = *AZ::TransformNotificationBus::GetCurrentBusId();
|
||||
m_entityIdsTransformDirty.insert(entityId);
|
||||
|
||||
// update the world transform of the visibility bounds union
|
||||
if (auto instance_it = m_entityVisibilityBoundsUnionInstanceMapping.find(entityId);
|
||||
if (auto instance_it = m_entityVisibilityBoundsUnionInstanceMapping.find(entity);
|
||||
instance_it != m_entityVisibilityBoundsUnionInstanceMapping.end())
|
||||
{
|
||||
instance_it->second.m_worldTransform = world;
|
||||
UpdateVisibilitySystem(entity, instance_it->second);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-18
@@ -23,12 +23,12 @@ namespace AzFramework
|
||||
{
|
||||
//! Provide a unified hook between entities and the visibility system.
|
||||
class EntityVisibilityBoundsUnionSystem
|
||||
: public EntityBoundsUnionRequestBus::Handler
|
||||
, private AZ::EntitySystemBus::Handler
|
||||
, private AZ::TransformNotificationBus::Router
|
||||
: public IEntityBoundsUnionRequestBus::Handler
|
||||
, private AZ::TickBus::Handler
|
||||
{
|
||||
public:
|
||||
EntityVisibilityBoundsUnionSystem();
|
||||
|
||||
void Connect();
|
||||
void Disconnect();
|
||||
|
||||
@@ -36,34 +36,31 @@ namespace AzFramework
|
||||
void RefreshEntityLocalBoundsUnion(AZ::EntityId entityId) override;
|
||||
AZ::Aabb GetEntityLocalBoundsUnion(AZ::EntityId entityId) const override;
|
||||
void ProcessEntityBoundsUnionRequests() override;
|
||||
void OnTransformUpdated(AZ::Entity* entity) override;
|
||||
|
||||
private:
|
||||
struct EntityVisibilityBoundsUnionInstance
|
||||
{
|
||||
AZ::Transform m_worldTransform = AZ::Transform::CreateIdentity(); //!< The world transform of the Entity.
|
||||
AZ::Aabb m_localEntityBoundsUnion =
|
||||
AZ::Aabb::CreateNull(); //!< Entity union bounding volume in local space.
|
||||
AZ::Aabb m_localEntityBoundsUnion = AZ::Aabb::CreateNull(); //!< Entity union bounding volume in local space.
|
||||
VisibilityEntry m_visibilityEntry; //!< Hook into the IVisibilitySystem interface.
|
||||
};
|
||||
|
||||
using UniqueEntityIds = AZStd::unordered_set<AZ::EntityId>;
|
||||
using UniqueEntities = AZStd::set<AZ::Entity*>;
|
||||
using EntityVisibilityBoundsUnionInstanceMapping =
|
||||
AZStd::unordered_map<AZ::EntityId, EntityVisibilityBoundsUnionInstance>;
|
||||
AZStd::unordered_map<AZ::Entity*, EntityVisibilityBoundsUnionInstance>;
|
||||
|
||||
void OnEntityActivated(AZ::Entity* entity);
|
||||
void OnEntityDeactivated(AZ::Entity* entity);
|
||||
|
||||
// TickBus overrides ...
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
|
||||
// EntitySystemBus overrides ...
|
||||
void OnEntityActivated(const AZ::EntityId& entityId) override;
|
||||
void OnEntityDeactivated(const AZ::EntityId& entityId) override;
|
||||
|
||||
// TransformNotificationBus overrides ...
|
||||
void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override;
|
||||
|
||||
void UpdateVisibilitySystem(EntityVisibilityBoundsUnionInstance& instance);
|
||||
void UpdateVisibilitySystem(AZ::Entity* entity, EntityVisibilityBoundsUnionInstance& instance);
|
||||
|
||||
EntityVisibilityBoundsUnionInstanceMapping m_entityVisibilityBoundsUnionInstanceMapping;
|
||||
UniqueEntityIds m_entityIdsBoundsDirty;
|
||||
UniqueEntityIds m_entityIdsTransformDirty;
|
||||
UniqueEntities m_entityBoundsDirty;
|
||||
|
||||
AZ::EntityActivatedEvent::Handler m_entityActivatedEventHandler;
|
||||
AZ::EntityDeactivatedEvent::Handler m_entityDeactivatedEventHandler;
|
||||
};
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <AzCore/Console/Console.h>
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Math/ShapeIntersection.h>
|
||||
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
|
||||
#include <AzFramework/Visibility/IVisibilitySystem.h>
|
||||
@@ -66,6 +67,7 @@ namespace AzFramework
|
||||
octreeDebug.m_nodeBounds.push_back(nodeData.m_bounds);
|
||||
}
|
||||
|
||||
visibleEntityIdsOut.reserve(visibleEntityIdsOut.size() + nodeData.m_entries.size());
|
||||
for (const auto* visibilityEntry : nodeData.m_entries)
|
||||
{
|
||||
if (ed_visibility_showDebug)
|
||||
@@ -88,8 +90,7 @@ namespace AzFramework
|
||||
octreeDebug.m_entryAabbsInFrustum.push_back(visibilityEntry->m_boundingVolume);
|
||||
}
|
||||
|
||||
AZ::EntityId entityId;
|
||||
std::memcpy(&entityId, &visibilityEntry->m_userData, sizeof(AZ::EntityId));
|
||||
AZ::EntityId entityId = static_cast<AZ::Entity*>(visibilityEntry->m_userData)->GetId();
|
||||
visibleEntityIdsOut.push_back(entityId);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -13,4 +13,3 @@
|
||||
#pragma once
|
||||
|
||||
#define STREAM_CACHE_DEFAULT 0
|
||||
#define FRONTEND_SHADER_CACHE_DEFAULT 0
|
||||
|
||||
@@ -13,4 +13,3 @@
|
||||
#pragma once
|
||||
|
||||
#define STREAM_CACHE_DEFAULT 0
|
||||
#define FRONTEND_SHADER_CACHE_DEFAULT 0
|
||||
|
||||
@@ -13,4 +13,3 @@
|
||||
#pragma once
|
||||
|
||||
#define STREAM_CACHE_DEFAULT 0
|
||||
#define FRONTEND_SHADER_CACHE_DEFAULT 0
|
||||
|
||||
@@ -13,4 +13,3 @@
|
||||
#pragma once
|
||||
|
||||
#define STREAM_CACHE_DEFAULT 0
|
||||
#define FRONTEND_SHADER_CACHE_DEFAULT 0
|
||||
|
||||
@@ -13,4 +13,3 @@
|
||||
#pragma once
|
||||
|
||||
#define STREAM_CACHE_DEFAULT 0
|
||||
#define FRONTEND_SHADER_CACHE_DEFAULT 0
|
||||
|
||||
@@ -19,9 +19,10 @@ namespace AzNetworking
|
||||
static const int32_t FloatHashMinValue = (INT_MIN >> 7);
|
||||
static const int32_t FloatHashMaxValue = (INT_MAX >> 7);
|
||||
|
||||
AZ::HashValue64 HashSerializer::GetHash() const
|
||||
AZ::HashValue32 HashSerializer::GetHash() const
|
||||
{
|
||||
return m_hash;
|
||||
// Just truncate the upper bits
|
||||
return static_cast<AZ::HashValue32>(m_hash);
|
||||
}
|
||||
|
||||
SerializerMode HashSerializer::GetSerializerMode() const
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace AzNetworking
|
||||
|
||||
HashSerializer() = default;
|
||||
|
||||
AZ::HashValue64 GetHash() const;
|
||||
AZ::HashValue32 GetHash() const;
|
||||
|
||||
// ISerializer interfaces
|
||||
SerializerMode GetSerializerMode() const override;
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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 <AzNetworking/Serialization/StringifySerializer.h>
|
||||
|
||||
namespace AzNetworking
|
||||
{
|
||||
StringifySerializer::StringifySerializer(char delimeter, bool outputFieldNames, const AZStd::string& seperator)
|
||||
: m_delimeter(delimeter)
|
||||
, m_outputFieldNames(outputFieldNames)
|
||||
, m_separator(seperator)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
const AZStd::string& StringifySerializer::GetString() const
|
||||
{
|
||||
return m_string;
|
||||
}
|
||||
|
||||
const StringifySerializer::StringMap& StringifySerializer::GetValueMap() const
|
||||
{
|
||||
return m_map;
|
||||
}
|
||||
|
||||
SerializerMode StringifySerializer::GetSerializerMode() const
|
||||
{
|
||||
return SerializerMode::ReadFromObject;
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(bool& value, const char* name)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(char& value, const char* name, char, char)
|
||||
{
|
||||
const int val = value; // Print chars as integers
|
||||
return ProcessData(name, val);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(int8_t& value, const char* name, int8_t, int8_t)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(int16_t& value, const char* name, int16_t, int16_t)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(int32_t& value, const char* name, int32_t, int32_t)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(int64_t& value, const char* name, int64_t, int64_t)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(uint8_t& value, const char* name, uint8_t, uint8_t)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(uint16_t& value, const char* name, uint16_t, uint16_t)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(uint32_t& value, const char* name, uint32_t, uint32_t)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(uint64_t& value, const char* name, uint64_t, uint64_t)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(float& value, const char* name, float, float)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::Serialize(double& value, const char* name, double, double)
|
||||
{
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
|
||||
bool StringifySerializer::SerializeBytes(uint8_t* buffer, uint32_t, bool isString, uint32_t&, const char* name)
|
||||
{
|
||||
if (isString)
|
||||
{
|
||||
AZ::CVarFixedString value = reinterpret_cast<char*>(buffer);
|
||||
return ProcessData(name, value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StringifySerializer::BeginObject(const char* name, const char*)
|
||||
{
|
||||
m_prefixSizeStack.push_back(m_prefix.size());
|
||||
m_prefix += name;
|
||||
m_prefix += ".";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StringifySerializer::EndObject(const char*, const char*)
|
||||
{
|
||||
m_prefix.resize(m_prefixSizeStack.back());
|
||||
m_prefixSizeStack.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint8_t* StringifySerializer::GetBuffer() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t StringifySerializer::GetCapacity() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t StringifySerializer::GetSize() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool StringifySerializer::ProcessData(const char* name, const T& value)
|
||||
{
|
||||
// Only add delimeters after we have processed at least one element
|
||||
if (!m_string.empty())
|
||||
{
|
||||
m_string += m_delimeter;
|
||||
}
|
||||
|
||||
if (m_outputFieldNames)
|
||||
{
|
||||
m_string += m_prefix;
|
||||
m_string += name;
|
||||
m_string += m_separator;
|
||||
}
|
||||
|
||||
AZ::CVarFixedString string = AZ::ConsoleTypeHelpers::ValueToString(value);
|
||||
m_string += string.c_str();
|
||||
m_map[m_prefix + name] = string.c_str();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 <AzNetworking/Serialization/ISerializer.h>
|
||||
#include <AzCore/std/containers/map.h>
|
||||
|
||||
namespace AzNetworking
|
||||
{
|
||||
// StringifySerializer
|
||||
// Generate a debug string of a serializable object
|
||||
class StringifySerializer
|
||||
: public ISerializer
|
||||
{
|
||||
public:
|
||||
|
||||
using StringMap = AZStd::map<AZStd::string, AZStd::string>;
|
||||
|
||||
StringifySerializer(char delimeter = ' ', bool outputFieldNames = true, const AZStd::string& seperator = "=");
|
||||
|
||||
// GetString
|
||||
// After serializing objects, get the serialized values as a single string
|
||||
const AZStd::string& GetString() const;
|
||||
|
||||
// GetValueMap
|
||||
// After serializing objects, get the serialized values as key value pairs
|
||||
const StringMap& GetValueMap() const;
|
||||
|
||||
// ISerializer interfaces
|
||||
SerializerMode GetSerializerMode() const override;
|
||||
bool Serialize(bool& value, const char* name) override;
|
||||
bool Serialize(char& value, const char* name, char minValue, char maxValue) override;
|
||||
bool Serialize(int8_t& value, const char* name, int8_t minValue, int8_t maxValue) override;
|
||||
bool Serialize(int16_t& value, const char* name, int16_t minValue, int16_t maxValue) override;
|
||||
bool Serialize(int32_t& value, const char* name, int32_t minValue, int32_t maxValue) override;
|
||||
bool Serialize(int64_t& value, const char* name, int64_t minValue, int64_t maxValue) override;
|
||||
bool Serialize(uint8_t& value, const char* name, uint8_t minValue, uint8_t maxValue) override;
|
||||
bool Serialize(uint16_t& value, const char* name, uint16_t minValue, uint16_t maxValue) override;
|
||||
bool Serialize(uint32_t& value, const char* name, uint32_t minValue, uint32_t maxValue) override;
|
||||
bool Serialize(uint64_t& value, const char* name, uint64_t minValue, uint64_t maxValue) override;
|
||||
bool Serialize(float& value, const char* name, float minValue, float maxValue) override;
|
||||
bool Serialize(double& value, const char* name, double minValue, double maxValue) override;
|
||||
bool SerializeBytes(uint8_t* buffer, uint32_t bufferCapacity, bool isString, uint32_t& outSize, const char* name) override;
|
||||
bool BeginObject(const char* name, const char* typeName) override;
|
||||
bool EndObject(const char* name, const char* typeName) override;
|
||||
|
||||
const uint8_t* GetBuffer() const override;
|
||||
uint32_t GetCapacity() const override;
|
||||
uint32_t GetSize() const override;
|
||||
void ClearTrackedChangesFlag() override {}
|
||||
bool GetTrackedChangesFlag() const override { return false; }
|
||||
// ISerializer interfaces
|
||||
|
||||
private:
|
||||
|
||||
template <typename T>
|
||||
bool ProcessData(const char* name, const T& value);
|
||||
|
||||
private:
|
||||
|
||||
char m_delimeter;
|
||||
bool m_outputFieldNames = true;
|
||||
|
||||
StringMap m_map;
|
||||
AZStd::string m_string;
|
||||
AZStd::string m_prefix;
|
||||
AZStd::string m_separator;
|
||||
AZStd::deque<AZStd::size_t> m_prefixSizeStack;
|
||||
};
|
||||
}
|
||||
@@ -65,6 +65,8 @@ set(FILES
|
||||
Serialization/NetworkOutputSerializer.cpp
|
||||
Serialization/NetworkOutputSerializer.h
|
||||
Serialization/NetworkOutputSerializer.inl
|
||||
Serialization/StringifySerializer.cpp
|
||||
Serialization/StringifySerializer.h
|
||||
Serialization/TrackChangedSerializer.h
|
||||
Serialization/TrackChangedSerializer.inl
|
||||
TcpTransport/TcpConnection.cpp
|
||||
|
||||
@@ -180,6 +180,8 @@ namespace AzQtComponents
|
||||
textSearch->setFrame(false);
|
||||
textSearch->setText(QString());
|
||||
textSearch->setPlaceholderText(QObject::tr("Search..."));
|
||||
textSearch->setClearButtonEnabled(true);
|
||||
LineEdit::applySearchStyle(textSearch);
|
||||
connect(textSearch, &QLineEdit::textChanged, this, &SearchTypeSelector::FilterTextChanged);
|
||||
|
||||
m_searchLayout->addWidget(textSearch);
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="22px" height="20px" viewBox="0 0 22 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Icon / Toolbar / Play Console / Simulate Physics</title>
|
||||
<defs>
|
||||
<filter id="filter-1">
|
||||
<feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 1.000000 0"></feColorMatrix>
|
||||
</filter>
|
||||
<path d="M15.6428742,11.9827626 C14.6770188,10.7687802 14.4956657,9.03975584 15.318317,7.61488202 C16.3923878,5.75453685 18.7712019,5.11713552 20.6315471,6.1912063 C22.4918923,7.26527709 23.1292936,9.64409122 22.0552228,11.5044364 C21.013572,13.3086286 18.7447543,13.9625898 16.9118187,13.0207075 C16.3649862,12.6909949 16.1306352,12.5503038 15.6428742,11.9827626 Z M21.8449657,10.7460039 C22.0595758,10.1342007 22.2373043,8.55906064 21.8449657,9.05833735 C21.4526271,9.55761407 21.2918858,9.96701497 20.8130083,10.4818331 C19.9042528,11.4587922 18.2692551,11.5405181 17.2685207,11.5405181 C16.2677863,11.5405181 17.2685207,12.7159692 18.7447536,12.7159692 C20.2209864,12.7159692 21.4894947,11.7593684 21.8449657,10.7460039 Z M16.197388,14.0090117 C16.3975463,13.8229455 16.701528,13.7946404 16.9039947,13.9824218 C17.1064613,14.1702033 17.0967404,14.4628636 16.9305845,14.6890285 C16.3625338,15.4622373 15.7176919,17.2794303 15.043117,20.0013354 L13.9731652,20.0013354 C13.74245,13.5862059 13.0475403,9.88991503 12.1237705,9.88991503 C11.4442663,9.88991503 10.8362307,11.6219 10.2533824,15.1128617 C10.1857467,15.5179652 9.53567637,19.2951617 9.47653253,20.0013354 L8.32801304,20.0013354 C8.32801304,11.4928776 6.37313749,5.95119082 2.36188186,4.12720757 C2.11050727,4.01290346 1.65264126,3.52516756 2.0367453,2.87964604 C2.42084934,2.23412452 3.17248781,2.63112648 3.42386239,2.7454306 C7.09988702,4.41697884 8.58435154,8.916133 9.2170288,15.2523916 C9.23745765,15.1271094 9.25478369,15.0215631 9.26703535,14.9481819 C9.97359325,10.7162629 10.1786779,8.42129315 12.1237705,8.42129315 C14.0688632,8.42129315 14.4491297,12.5275507 14.8251327,17.6881742 C15.0978485,16.4034552 15.5446067,14.6158341 16.197388,14.0090117 Z M2.0241711,20.6412764 L22.0595758,20.6412764 L20.6246231,21.7592943 L3.70859025,21.7592943 L2.0241711,20.6412764 Z" id="path-2"></path>
|
||||
</defs>
|
||||
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Play-Console-v2" transform="translate(-169.000000, -8.000000)">
|
||||
<g id="Play-Console" transform="translate(100.000000, 0.000000)">
|
||||
<g id="Icon-/-Toolbar-/-Play-Console-/-Simulate-Physics" transform="translate(68.000000, 6.000000)" filter="url(#filter-1)">
|
||||
<g>
|
||||
<mask id="mask-3" fill="white">
|
||||
<use xlink:href="#path-2"></use>
|
||||
</mask>
|
||||
<use id="Shape" fill="#FFFFFF" fill-rule="nonzero" xlink:href="#path-2"></use>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -372,6 +372,7 @@
|
||||
<file>img/UI20/toolbar/Select.svg</file>
|
||||
<file>img/UI20/toolbar/select_object.svg</file>
|
||||
<file>img/UI20/toolbar/Select_terrain.svg</file>
|
||||
<file>img/UI20/toolbar/Simulate_Physics.svg</file>
|
||||
<file>img/UI20/toolbar/Simulate_Physics_on_selected_objects.svg</file>
|
||||
<file>img/UI20/toolbar/Terrain.svg</file>
|
||||
<file>img/UI20/toolbar/Terrain_Texture.svg</file>
|
||||
|
||||
@@ -761,16 +761,6 @@ namespace AzToolsFramework
|
||||
/// If the view pane was not registered with the ViewPaneOptions.isDeletable set to true, the view pane will be hidden instead.
|
||||
virtual void CloseViewPane(const char* /*paneName*/) {}
|
||||
|
||||
/// Request generation of all level cubemaps.
|
||||
virtual void GenerateAllCubemaps() {}
|
||||
|
||||
/// Regenerate cubemap for a particular entity.
|
||||
/// \param entityId ID of the entity that the cubemap is for
|
||||
/// \param cubemapOutputPath path to a image file to generate
|
||||
/// \param hideEntity Indicates whether the entity should be hidden during cubemap generation. Controls whether the entity's current cubemap output is baked into the new cubemap.
|
||||
virtual void GenerateCubemapForEntity(AZ::EntityId /*entityId*/, AZStd::string* /*cubemapOutputPath*/, bool /*hideEntity*/) {}
|
||||
virtual void GenerateCubemapWithIDForEntity(AZ::EntityId /*entityId*/, AZ::Uuid /*cubemapId*/, AZStd::string* /*cubemapOutputPath*/, bool /*hideEntity*/, bool /*hasCubemapId*/) {}
|
||||
|
||||
//! Spawn asset browser for the appropriate asset types.
|
||||
virtual void BrowseForAssets(AssetBrowser::AssetSelectionModel& /*selection*/) = 0;
|
||||
|
||||
|
||||
-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)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user