First crack at the multiplayer component registry to allow multiplayer components to live in any gem

This commit is contained in:
karlberg
2021-04-21 12:47:48 -07:00
parent 626f7c00fe
commit f26d7f9301
15 changed files with 210 additions and 24 deletions
@@ -12,15 +12,8 @@ namespace AZ
{% set Namespace = dataFiles[0].attrib['Namespace'] %}
namespace {{ Namespace }}
{
enum class ComponentTypes
{
{% for Component in dataFiles %}
{% set ComponentName = Component.attrib['Name'] %}
{{ ComponentName }},
{% endfor %}
Count
};
static_assert(ComponentTypes::Count < static_cast<ComponentTypes>(Multiplayer::InvalidNetComponentId), "ComponentId overflow");
//! Registers all multiplayer components contained within this gem with the MultiplayerComponentRegistry.
void RegisterMultiplayerComponents();
//! For reflecting multiplayer components into the serialize, edit, and behaviour contexts.
void CreateComponentDescriptors(AZStd::list<AZ::ComponentDescriptor*>& descriptors);
@@ -1,4 +1,6 @@
#include <AzCore/Component/Component.h>
#include <Source/Components/MultiplayerComponentRegistry.h>
#include <Source/NetworkEntity/INetworkEntityManager.h>
{% for Component in dataFiles %}
{% set ComponentDerived = Component.attrib['OverrideComponent']|booleanTrue %}
{% set ControllerDerived = Component.attrib['OverrideController']|booleanTrue %}
@@ -10,8 +12,33 @@
{% endfor %}
{% set Namespace = dataFiles[0].attrib['Namespace'] %}
{% for Component in dataFiles %}
{% if Component.attrib['Namespace'] != Namespace %}
#error "mismatched component namespaces detected in declared multiplayer components, expected {{ Namespace }} but found {{ Component.attrib['Namespace'] }}"
{% endif %}
{% endfor %}
namespace {{ Namespace }}
{
void RegisterMultiplayerComponents()
{
Multiplayer::MultiplayerComponentRegistry* multiplayerComponentRegistry = GetMultiplayerComponentRegistry();
{% for Component in dataFiles %}
{% set ComponentName = Component.attrib['Name'] %}
{% set ComponentBaseName = ComponentName %}
{% if Component.attrib['OverrideComponent']|booleanTrue %}
{% set ComponentBaseName = ComponentName + "Base" %}
{% endif %}
{
Multiplayer::MultiplayerComponentRegistry::ComponentData componentData;
componentData.m_gemName = AZ::Name("{{ Namespace }}");
componentData.m_componentName = AZ::Name("{{ Component.attrib['Name'] }}");
componentData.m_componentPropertyNameLookupFunction = {{ ComponentBaseName }}::GetNetworkPropertyName;
componentData.m_componentRpcNameLookupFunction = {{ ComponentBaseName }}::GetRpcName;
{{ ComponentBaseName }}::s_netComponentId = multiplayerComponentRegistry->RegisterMultiplayerComponent(componentData);
}
{% endfor %}
}
void CreateComponentDescriptors(AZStd::list<AZ::ComponentDescriptor*>& descriptors)
{
descriptors.insert(descriptors.end(), {
@@ -329,7 +329,6 @@ namespace {{ Component.attrib['Namespace'] }}
: public Multiplayer::IMultiplayerComponentInput
{
public:
static const Multiplayer::NetComponentId s_componentId = static_cast<Multiplayer::NetComponentId>({{ Component.attrib['Namespace'] }}::ComponentTypes::{{ Component.attrib['Name'] }});
Multiplayer::NetComponentId GetComponentId() const override;
INetworkInput& operator=(const INetworkInput& rhs) override;
bool Serialize(AzNetworking::ISerializer& serializer);
@@ -412,8 +411,6 @@ namespace {{ Component.attrib['Namespace'] }}
AZ_MULTIPLAYER_COMPONENT({{ Component.attrib['Namespace'] }}::{{ ComponentBaseName }}, s_{{ LowerFirst(ComponentName) }}ConcreteUuid, Multiplayer::MultiplayerComponent);
{% endif %}
static const Multiplayer::NetComponentId s_componentId = static_cast<Multiplayer::NetComponentId>({{ Component.attrib['Namespace'] }}::ComponentTypes::{{ Component.attrib['Name'] }});
static void Reflect(AZ::ReflectContext* context);
static void ReflectToEditContext(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
@@ -489,6 +486,10 @@ namespace {{ Component.attrib['Namespace'] }}
bool SerializeAutonomousToAuthorityProperties({{ RecordName }}& replicationRecord, AzNetworking::ISerializer& serializer);
void NotifyChangesAutonomousToAuthorityProperties(const {{ RecordName }}& replicationRecord) const;
//! Debug name helpers
static const char* GetNetworkPropertyName(uint16_t propertyIndex);
static const char* GetRpcName(uint16_t rpcIndex);
AZStd::unique_ptr<{{ RecordName }}> m_currentRecord;
AZStd::unique_ptr<{{ ControllerName }}> m_controller;
@@ -518,6 +519,9 @@ namespace {{ Component.attrib['Namespace'] }}
{% call(Type, Name) AutoComponentMacros.ParseComponentServiceTypeAndName(Component) %}
{{ Type }}* {{ Name }} = nullptr;
{% endcall %}
static NetComponentId s_netComponentId;
friend void RegisterMultiplayerComponents();
};
}
{% endfor %}
@@ -285,15 +285,15 @@ void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(const {{ Prop
{{ AutoComponentMacros.ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }}
void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramDefines) }})
{
constexpr uint8_t rpcId = static_cast<uint8_t>({{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ UpperFirst(Property.attrib['Name']) }});
constexpr Multiplayer::NetComponentId componentId = static_cast<Multiplayer::NetComponentId>({{ Component.attrib['Namespace'] }}::ComponentTypes::{{ Component.attrib['Name'] }});
constexpr uint16_t rpcId = static_cast<uint16_t>({{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ UpperFirst(Property.attrib['Name']) }});
{% if Property.attrib['IsReliable']|booleanTrue %}
constexpr AzNetworking::ReliabilityType isReliable = Multiplayer::ReliabilityType::Reliable;
{% else %}
constexpr AzNetworking::ReliabilityType isReliable = Multiplayer::ReliabilityType::Unreliable;
{% endif %}
Multiplayer::NetworkEntityRpcMessage rpcMessage(Multiplayer::RpcDeliveryType::{{ InvokeFrom }}To{{ HandleOn }}, GetNetEntityId(), componentId, rpcId, isReliable);
const Multiplayer::NetComponentId netComponentId = GetParent().GetNetComponentId();
Multiplayer::NetworkEntityRpcMessage rpcMessage(Multiplayer::RpcDeliveryType::{{ InvokeFrom }}To{{ HandleOn }}, GetNetEntityId(), netComponentId, rpcId, isReliable);
{% if paramNames|count > 0 %}
{{ UpperFirst(Component.attrib['Name']) }}Internal::{{ UpperFirst(Property.attrib['Name']) }}RpcStruct rpcStruct({{ ', '.join(paramNames) }});
{% else %}
@@ -901,6 +901,8 @@ m_{{ LowerFirst(Property.attrib['Name']) }} = m_{{ LowerFirst(Property.attrib['N
namespace {{ Component.attrib['Namespace'] }}
{
NetComponentId {{ UpperFirst(Component.attrib['Name']) }}::s_netComponentId = InvalidNetComponentId;
namespace {{ UpperFirst(Component.attrib['Name']) }}Internal
{
{{ DeclareRemoteProcedureEnumerations(Component)|indent(8) }}
@@ -1229,7 +1231,7 @@ namespace {{ Component.attrib['Namespace'] }}
Multiplayer::NetComponentId {{ ComponentBaseName }}::GetNetComponentId() const
{
return s_componentId;
return s_netComponentId;
}
#pragma warning(push)
@@ -1379,6 +1381,15 @@ namespace {{ Component.attrib['Namespace'] }}
}
{% endif %}
const char* {{ ComponentBaseName }}::GetNetworkPropertyName([[maybe_unused]] uint16_t propertyIndex)
{
return "";
}
const char* {{ ComponentBaseName }}::GetRpcName([[maybe_unused]] uint16_t rpcIndex)
{
return "";
}
{% endfor %}
}
{% endfor %}
@@ -0,0 +1,58 @@
/*
* 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 <Source/Components/MultiplayerComponentRegistry.h>
namespace Multiplayer
{
NetComponentId MultiplayerComponentRegistry::RegisterMultiplayerComponent(const ComponentData& componentData)
{
NetComponentId netComponentId = m_nextNetComponentId++;
m_componentData[netComponentId] = componentData;
return netComponentId;
}
const char* MultiplayerComponentRegistry::GetComponentGemName(NetComponentId netComponentId) const
{
const ComponentData& componentData = GetMultiplayerComponentData(netComponentId);
return componentData.m_gemName.GetCStr();
}
const char* MultiplayerComponentRegistry::GetComponentName(NetComponentId netComponentId) const
{
const ComponentData& componentData = GetMultiplayerComponentData(netComponentId);
return componentData.m_componentName.GetCStr();
}
const char* MultiplayerComponentRegistry::GetComponentPropertyName(NetComponentId netComponentId, uint16_t propertyIndex) const
{
const ComponentData& componentData = GetMultiplayerComponentData(netComponentId);
return componentData.m_componentPropertyNameLookupFunction(propertyIndex);
}
const char* MultiplayerComponentRegistry::GetComponentRpcName(NetComponentId netComponentId, uint16_t rpcId) const
{
const ComponentData& componentData = GetMultiplayerComponentData(netComponentId);
return componentData.m_componentRpcNameLookupFunction(rpcId);
}
const MultiplayerComponentRegistry::ComponentData& MultiplayerComponentRegistry::GetMultiplayerComponentData(NetComponentId netComponentId) const
{
static ComponentData nullComponentData;
auto it = m_componentData.find(netComponentId);
if (it != m_componentData.end())
{
return it->second;
}
return nullComponentData;
}
}
@@ -0,0 +1,69 @@
/*
* 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/Name/Name.h>
#include <AzCore/std/containers/unordered_map.h>
#include <Source/Components/MultiplayerComponent.h>
namespace Multiplayer
{
class MultiplayerComponentRegistry
{
public:
using NameLookupFunction = AZStd::function<const char*(uint16_t index)>;
struct ComponentData
{
AZ::Name m_gemName;
AZ::Name m_componentName;
NameLookupFunction m_componentPropertyNameLookupFunction;
NameLookupFunction m_componentRpcNameLookupFunction;
};
//! Registers a multiplayer component with the multiplayer system.
//! @param componentData the data associated with the component being registered
//! @return the NetComponentId assigned to this particular component
NetComponentId RegisterMultiplayerComponent(const ComponentData& componentData);
//! Returns the gem name associated with the provided NetComponentId.
//! @param netComponentId the NetComponentId to return the gem name of
//! @return the name of the gem that contains the requested component
const char* GetComponentGemName(NetComponentId netComponentId) const;
//! Returns the component name associated with the provided NetComponentId.
//! @param netComponentId the NetComponentId to return the component name of
//! @return the name of the component
const char* GetComponentName(NetComponentId netComponentId) const;
//! Returns the property name associated with the provided NetComponentId and propertyIndex.
//! @param netComponentId the NetComponentId to return the property name of
//! @param propertyIndex the index off the network property to return the property name of
//! @return the name of the network property
const char* GetComponentPropertyName(NetComponentId netComponentId, uint16_t propertyIndex) const;
//! Returns the Rpc name associated with the provided NetComponentId and rpcId.
//! @param netComponentId the NetComponentId to return the property name of
//! @param rpcId the index off the rpc to return the rpc name of
//! @return the name of the requested rpc
const char* GetComponentRpcName(NetComponentId netComponentId, uint16_t rpcId) const;
//! Retrieves the stored component data for a given NetComponentId.
//! @param netComponentId the NetComponentId to return component data for
//! @return reference to the requested component data, an empty container will be returned if the NetComponentId does not exist
const ComponentData& GetMultiplayerComponentData(NetComponentId netComponentId) const;
private:
NetComponentId m_nextNetComponentId = NetComponentId{ 0 };
AZStd::unordered_map<NetComponentId, ComponentData> m_componentData;
};
}
@@ -114,6 +114,9 @@ namespace Multiplayer
m_networkInterface = AZ::Interface<INetworking>::Get()->CreateNetworkInterface(AZ::Name(s_networkInterfaceName), sv_protocol, TrustZone::ExternalClientToServer, *this);
m_consoleCommandHandler.Connect(AZ::Interface<AZ::IConsole>::Get()->GetConsoleCommandInvokedEvent());
AZ::Interface<IMultiplayer>::Register(this);
//! Register our gems multiplayer components to assign NetComponentIds
RegisterMultiplayerComponents();
}
void MultiplayerSystemComponent::Deactivate()
@@ -49,7 +49,7 @@ namespace Multiplayer
: AZ::Module()
{
m_descriptors.insert(m_descriptors.end(), {
MultiplayerToolsSystemComponent::CreateDescriptor(),
MultiplayerToolsSystemComponent::CreateDescriptor(),
});
}
@@ -23,6 +23,7 @@ namespace Multiplayer
class NetworkEntityTracker;
class NetworkEntityAuthorityTracker;
class NetworkEntityRpcMessage;
class MultiplayerComponentRegistry;
using EntityExitDomainEvent = AZ::Event<const ConstNetworkEntityHandle&>;
using ControllersActivatedEvent = AZ::Event<const ConstNetworkEntityHandle&, EntityIsMigrating>;
@@ -48,6 +49,10 @@ namespace Multiplayer
//! @return the NetworkEntityAuthorityTracker for this INetworkEntityManager instance
virtual NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker() = 0;
//! Returns the MultiplayerComponentRegistry for this INetworkEntityManager instance.
//! @return the MultiplayerComponentRegistry for this INetworkEntityManager instance
virtual MultiplayerComponentRegistry* GetMultiplayerComponentRegistry() = 0;
//! Returns the HostId for this INetworkEntityManager instance.
//! @return the HostId for this INetworkEntityManager instance
virtual HostId GetHostId() const = 0;
@@ -144,4 +149,9 @@ namespace Multiplayer
{
return GetNetworkEntityManager()->GetNetworkEntityAuthorityTracker();
}
inline MultiplayerComponentRegistry* GetMultiplayerComponentRegistry()
{
return GetNetworkEntityManager()->GetMultiplayerComponentRegistry();
}
}
@@ -62,6 +62,11 @@ namespace Multiplayer
return &m_networkEntityAuthorityTracker;
}
MultiplayerComponentRegistry* NetworkEntityManager::GetMultiplayerComponentRegistry()
{
return &m_multiplayerComponentRegistry;
}
HostId NetworkEntityManager::GetHostId() const
{
return m_hostId;
@@ -21,7 +21,7 @@
#include <Source/NetworkEntity/NetworkEntityRpcMessage.h>
#include <Source/EntityDomains/IEntityDomain.h>
#include <Source/NetworkEntity/NetworkSpawnableLibrary.h>
#include <Source/Components/MultiplayerComponentRegistry.h>
namespace Multiplayer
{
@@ -42,6 +42,7 @@ namespace Multiplayer
//! @{
NetworkEntityTracker* GetNetworkEntityTracker() override;
NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker() override;
MultiplayerComponentRegistry* GetMultiplayerComponentRegistry() override;
HostId GetHostId() const override;
ConstNetworkEntityHandle GetEntity(NetEntityId netEntityId) const override;
@@ -85,6 +86,8 @@ namespace Multiplayer
NetworkEntityTracker m_networkEntityTracker;
NetworkEntityAuthorityTracker m_networkEntityAuthorityTracker;
MultiplayerComponentRegistry m_multiplayerComponentRegistry;
AZ::ScheduledEvent m_removeEntitiesEvent;
AZStd::vector<NetEntityId> m_removeList;
AZStd::unique_ptr<IEntityDomain> m_entityDomain;
@@ -42,7 +42,7 @@ namespace Multiplayer
}
}
NetworkEntityRpcMessage::NetworkEntityRpcMessage(RpcDeliveryType rpcDeliveryType, NetEntityId entityId, NetComponentId componentId, uint8_t rpcMessageType, ReliabilityType isReliable)
NetworkEntityRpcMessage::NetworkEntityRpcMessage(RpcDeliveryType rpcDeliveryType, NetEntityId entityId, NetComponentId componentId, uint16_t rpcMessageType, ReliabilityType isReliable)
: m_rpcDeliveryType(rpcDeliveryType)
, m_entityId(entityId)
, m_componentId(componentId)
@@ -98,7 +98,7 @@ namespace Multiplayer
static constexpr uint32_t sizeOfFields = sizeof(RpcDeliveryType)
+ sizeof(NetEntityId)
+ sizeof(NetComponentId)
+ sizeof(uint8_t);
+ sizeof(uint16_t);
// 2-byte size header + the actual blob payload itself
const uint32_t sizeOfBlob = (m_data != nullptr) ? sizeof(uint16_t) + m_data->GetSize() : 0;
@@ -127,7 +127,7 @@ namespace Multiplayer
return m_componentId;
}
uint8_t NetworkEntityRpcMessage::GetRpcMessageType() const
uint16_t NetworkEntityRpcMessage::GetRpcMessageType() const
{
return m_rpcMessageType;
}
@@ -40,7 +40,7 @@ namespace Multiplayer
//! @param componentType the networked componentId of the component handling this RPC
//! @param rpcMessageType the component defined RPC type, so the component knows which RPC this message corresponds to
//! @param isReliable whether or not this RPC should be sent reliably
explicit NetworkEntityRpcMessage(RpcDeliveryType rpcDeliveryType, NetEntityId entityId, NetComponentId componentId, uint8_t rpcMessageType, ReliabilityType isReliable);
explicit NetworkEntityRpcMessage(RpcDeliveryType rpcDeliveryType, NetEntityId entityId, NetComponentId componentId, uint16_t rpcMessageType, ReliabilityType isReliable);
NetworkEntityRpcMessage& operator =(NetworkEntityRpcMessage&& rhs);
NetworkEntityRpcMessage& operator =(const NetworkEntityRpcMessage& rhs);
@@ -69,7 +69,7 @@ namespace Multiplayer
//! Gets the current value of RpcMessageType.
//! @return the current value of RpcMessageType
uint8_t GetRpcMessageType() const;
uint16_t GetRpcMessageType() const;
//! Writes the data contained inside a_Params to this NetworkEntityRpcMessage's blob buffer.
//! @param params the parameters to save inside this NetworkEntityRpcMessage instance
@@ -98,7 +98,7 @@ namespace Multiplayer
RpcDeliveryType m_rpcDeliveryType = RpcDeliveryType::None;
NetEntityId m_entityId = InvalidNetEntityId;
NetComponentId m_componentId = InvalidNetComponentId;
uint8_t m_rpcMessageType = 0;
uint16_t m_rpcMessageType = 0;
// Only allocated if we actually have data
// This is to prevent blowing out stack memory if we declare an array of these EntityUpdateMessages
@@ -46,6 +46,7 @@ namespace Multiplayer
const AZ::Name name = AZ::Name(relativePath);
m_spawnables[name] = id;
m_spawnablesReverseLookup[id] = name;
}
void NetworkSpawnableLibrary::OnCatalogLoaded([[maybe_unused]] const char* catalogFile)
@@ -26,6 +26,8 @@ set(FILES
Source/AutoGen/NetworkTransformComponent.AutoComponent.xml
Source/Components/LocalPredictionPlayerInputComponent.cpp
Source/Components/LocalPredictionPlayerInputComponent.h
Source/Components/MultiplayerComponentRegistry.cpp
Source/Components/MultiplayerComponentRegistry.h
Source/Components/MultiplayerComponent.cpp
Source/Components/MultiplayerComponent.h
Source/Components/MultiplayerController.cpp