A bunch of work to get external multiplayer components to actually work

This commit is contained in:
karlberg
2021-05-12 16:01:59 -07:00
parent 293e0057f4
commit 254ad165c1
70 changed files with 162 additions and 185 deletions
@@ -13,7 +13,7 @@
#pragma once
#include <Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.h>
#include <Source/Components/NetBindComponent.h>
#include <Multiplayer/NetBindComponent.h>
namespace Multiplayer
{
@@ -10,8 +10,8 @@
*
*/
#include <Source/Components/MultiplayerComponent.h>
#include <Source/Components/NetBindComponent.h>
#include <Multiplayer/MultiplayerComponent.h>
#include <Multiplayer/NetBindComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace Multiplayer
@@ -1,144 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/DataStructures/FixedSizeBitsetView.h>
#include <Include/NetworkEntityHandle.h>
#include <Include/MultiplayerTypes.h>
#include <Include/IMultiplayer.h>
//! Macro to declare bindings for a multiplayer component inheriting from MultiplayerComponent
#define AZ_MULTIPLAYER_COMPONENT(ComponentClass, Guid, Base) \
AZ_RTTI(ComponentClass, Guid, Base) \
AZ_COMPONENT_INTRUSIVE_DESCRIPTOR_TYPE(ComponentClass) \
AZ_COMPONENT_BASE(ComponentClass, Guid, Base)
namespace Multiplayer
{
class NetworkEntityRpcMessage;
class ReplicationRecord;
class NetBindComponent;
class MultiplayerController;
class MultiplayerComponent
: public AZ::Component
{
public:
AZ_CLASS_ALLOCATOR(MultiplayerComponent, AZ::SystemAllocator, 0);
AZ_RTTI(MultiplayerComponent, "{B7F5B743-CCD3-4981-8F1A-FC2B95CE22D7}", AZ::Component);
static void Reflect(AZ::ReflectContext* context);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
MultiplayerComponent() = default;
~MultiplayerComponent() override = default;
//! Returns the NetBindComponent responsible for network binding for this entity.
//! @return the NetBindComponent responsible for network binding for this entity
//! @{
const NetBindComponent* GetNetBindComponent() const;
NetBindComponent* GetNetBindComponent();
//! @}
//! Linearly searches the components attached to the entity and returns the requested component.
//! @return the requested component, or nullptr if the component does not exist on the entity
//! @{
template <typename ComponentType>
const ComponentType* FindComponent() const;
template <typename ComponentType>
ComponentType* FindComponent();
//! @}
NetEntityId GetNetEntityId() const;
NetEntityRole GetNetEntityRole() const;
ConstNetworkEntityHandle GetEntityHandle() const;
NetworkEntityHandle GetEntityHandle();
void MarkDirty();
virtual NetComponentId GetNetComponentId() const = 0;
virtual bool HandleRpcMessage(AzNetworking::IConnection* invokingConnection, NetEntityRole netEntityRole, NetworkEntityRpcMessage& rpcMessage) = 0;
virtual bool SerializeStateDeltaMessage(ReplicationRecord& replicationRecord, AzNetworking::ISerializer& serializer) = 0;
virtual void NotifyStateDeltaChanges(ReplicationRecord& replicationRecord) = 0;
virtual bool HasController() const = 0;
virtual MultiplayerController* GetController() = 0;
protected:
virtual void ConstructController() = 0;
virtual void DestructController() = 0;
virtual void ActivateController(EntityIsMigrating entityIsMigrating) = 0;
virtual void DeactivateController(EntityIsMigrating entityIsMigrating) = 0;
virtual void NetworkAttach(NetBindComponent* netBindComponent, ReplicationRecord& currentEntityRecord, ReplicationRecord& predictableEntityRecord) = 0;
mutable NetBindComponent* m_netBindComponent = nullptr;
friend class NetworkEntityHandle;
friend class NetBindComponent;
friend class MultiplayerController;
};
template <typename ComponentType>
inline const ComponentType* MultiplayerComponent::FindComponent() const
{
return GetEntity()->FindComponent<ComponentType>();
}
template <typename ComponentType>
inline ComponentType* MultiplayerComponent::FindComponent()
{
return GetEntity()->FindComponent<ComponentType>();
}
template <typename TYPE>
inline void SerializeNetworkPropertyHelper
(
AzNetworking::ISerializer& serializer,
bool modifyRecord,
AzNetworking::FixedSizeBitsetView& bitset,
int32_t bitIndex,
TYPE& value,
const char* name,
NetComponentId componentId,
PropertyIndex propertyIndex,
MultiplayerStats& stats
)
{
if (bitset.GetBit(bitIndex))
{
const uint32_t prevUpdateSize = serializer.GetSize();
serializer.ClearTrackedChangesFlag();
serializer.Serialize(value, name);
if (modifyRecord && !serializer.GetTrackedChangesFlag())
{
// If the serializer didn't change any values, then lower the flag so we don't unnecessarily notify
bitset.SetBit(bitIndex, false);
}
const uint32_t postUpdateSize = serializer.GetSize();
// Network Property metrics
const uint32_t updateSize = (postUpdateSize - prevUpdateSize);
if (updateSize > 0)
{
if (modifyRecord)
{
stats.RecordPropertyReceived(componentId, propertyIndex, updateSize);
}
else
{
stats.RecordPropertySent(componentId, propertyIndex, updateSize);
}
}
}
}
}
@@ -10,7 +10,7 @@
*
*/
#include <Source/Components/MultiplayerComponentRegistry.h>
#include <Multiplayer/MultiplayerComponentRegistry.h>
namespace Multiplayer
{
@@ -1,70 +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/Name/Name.h>
#include <AzCore/std/containers/unordered_map.h>
#include <Source/Components/MultiplayerComponent.h>
namespace Multiplayer
{
class MultiplayerComponentRegistry
{
public:
using PropertyNameLookupFunction = AZStd::function<const char*(PropertyIndex index)>;
using RpcNameLookupFunction = AZStd::function<const char* (RpcIndex index)>;
struct ComponentData
{
AZ::Name m_gemName;
AZ::Name m_componentName;
PropertyNameLookupFunction m_componentPropertyNameLookupFunction;
RpcNameLookupFunction 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, PropertyIndex propertyIndex) const;
//! Returns the Rpc name associated with the provided NetComponentId and rpcId.
//! @param netComponentId the NetComponentId to return the property name of
//! @param rpcIndex the index of the rpc to return the rpc name of
//! @return the name of the requested rpc
const char* GetComponentRpcName(NetComponentId netComponentId, RpcIndex rpcIndex) 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;
};
}
@@ -10,9 +10,9 @@
*
*/
#include <Source/Components/MultiplayerController.h>
#include <Source/Components/MultiplayerComponent.h>
#include <Source/Components/NetBindComponent.h>
#include <Multiplayer/MultiplayerController.h>
#include <Multiplayer/MultiplayerComponent.h>
#include <Multiplayer/NetBindComponent.h>
namespace Multiplayer
{
@@ -1,137 +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 <Include/NetworkEntityHandle.h>
#include <AzCore/Math/Aabb.h>
namespace Multiplayer
{
class NetworkInput;
class NetBindComponent;
class MultiplayerComponent;
//! @class MultiplayerController
//! @brief A base class for all multiplayer component controllers responsible for running local prediction logic.
class MultiplayerController
{
public:
enum class InputPriorityOrder
{
First = 0,
Default = 1000,
SubEntities = 90000,
Last = 100000
};
MultiplayerController(MultiplayerComponent& owner);
virtual ~MultiplayerController() = default;
//! Activates the controller.
virtual void Activate(EntityIsMigrating entityIsMigrating) = 0;
//! Deactivates the controller.
virtual void Deactivate(EntityIsMigrating entityIsMigrating) = 0;
//! Returns the networkId for the entity that owns this controller.
//! @return the networkId for the entity that owns this controller
NetEntityId GetNetEntityId() const;
//! Returns the networkRole for the entity that owns this controller.
//! @return the networkRole for the entity that owns this controller
NetEntityRole GetNetEntityRole() const;
//! Returns the raw AZ::Entity pointer for the entity that owns this controller.
//! @return the raw AZ::Entity pointer for the entity that owns this controller
AZ::Entity* GetEntity() const;
//! Returns the network entity handle for the entity that owns this controller.
//! @return the network entity handle for the entity that owns this controller
ConstNetworkEntityHandle GetEntityHandle() const;
//! Returns the network entity handle for the entity that owns this controller.
//! @return the network entity handle for the entity that owns this controller
NetworkEntityHandle GetEntityHandle();
protected:
//! Returns the NetBindComponent responsible for net binding for this controller
//! @{
const NetBindComponent* GetNetBindComponent() const;
NetBindComponent* GetNetBindComponent();
//! @}
//! Returns the MultiplayerComponent that owns this controller instance.
//! @return the MultiplayerComponent that owns this controller instance
//! @{
const MultiplayerComponent& GetOwner() const;
MultiplayerComponent& GetOwner();
//! @}
//! Returns true if the owning entity is currently inside ProcessInput scope.
bool IsProcessingInput() const;
//! Returns the input priority ordering for determining the order of ProcessInput or CreateInput functions.
virtual InputPriorityOrder GetInputOrder() const = 0;
//! Queries the rewind system to determine what volume is relevent for a given input, this is very important for performance at scale.
//! @param networkInput input structure to process
//! @param deltaTime amount of time the provided input would be integrated over
//! @return a world-space aabb representing the volume relevent to the provided input
virtual AZ::Aabb GetRewindBoundsForInput(const NetworkInput& networkInput, float deltaTime) const = 0;
//! Base execution for ProcessInput packet, do not call directly.
//! @param networkInput input structure to process
//! @param deltaTime amount of time to integrate the provided inputs over
virtual void ProcessInput(NetworkInput& networkInput, float deltaTime) = 0;
//! Only valid on a client, should never be invoked on the server.
//! @param networkInput input structure to process
//! @param deltaTime amount of time to integrate the provided inputs over
virtual void CreateInput(NetworkInput& networkInput, float deltaTime) = 0;
template <typename ComponentType>
const ComponentType* FindComponent() const;
template <typename ComponentType>
ComponentType* FindComponent();
template <typename ControllerType>
ControllerType* FindController(const NetworkEntityHandle& entityHandle) const;
MultiplayerController* FindController(const AZ::Uuid& typeId, const NetworkEntityHandle& entityHandle) const;
private:
MultiplayerComponent& m_owner;
friend class NetBindComponent; // For access to create and process input methods
};
template <typename ComponentType>
inline const ComponentType* MultiplayerController::FindComponent() const
{
return GetEntity()->FindComponent<ComponentType>();
}
template <typename ComponentType>
inline ComponentType* MultiplayerController::FindComponent()
{
return GetEntity()->FindComponent<ComponentType>();
}
template <typename ControllerType>
inline ControllerType* MultiplayerController::FindController(const NetworkEntityHandle& entityHandle) const
{
return static_cast<ControllerType*>(FindController(typename ControllerType::ComponentType::RTTI_Type(), entityHandle));
}
}
@@ -10,13 +10,13 @@
*
*/
#include <Source/Components/NetBindComponent.h>
#include <Source/Components/MultiplayerComponent.h>
#include <Source/Components/MultiplayerController.h>
#include <Source/NetworkEntity/NetworkEntityRpcMessage.h>
#include <Source/NetworkEntity/NetworkEntityUpdateMessage.h>
#include <Source/NetworkInput/NetworkInput.h>
#include <Include/INetworkEntityManager.h>
#include <Multiplayer/NetBindComponent.h>
#include <Multiplayer/NetworkEntityRpcMessage.h>
#include <Multiplayer/NetworkEntityUpdateMessage.h>
#include <Multiplayer/NetworkInput.h>
#include <Multiplayer/INetworkEntityManager.h>
#include <Multiplayer/MultiplayerComponent.h>
#include <Multiplayer/MultiplayerController.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
#include <AzCore/Interface/Interface.h>
@@ -177,21 +177,6 @@ namespace Multiplayer
}
}
AZ::Aabb NetBindComponent::GetRewindBoundsForInput(const NetworkInput& networkInput, float deltaTime) const
{
AZ_Assert(m_netEntityRole == NetEntityRole::Authority, "Incorrect network role for computing rewind bounds");
AZ::Aabb bounds = AZ::Aabb::CreateNull();
for (MultiplayerComponent* multiplayerComponent : m_multiplayerInputComponentVector)
{
const AZ::Aabb componentBounds = multiplayerComponent->GetController()->GetRewindBoundsForInput(networkInput, deltaTime);
if (componentBounds.IsValid())
{
bounds.AddAabb(componentBounds);
}
}
return bounds;
}
bool NetBindComponent::HandleRpcMessage(AzNetworking::IConnection* invokingConnection, NetEntityRole remoteRole, NetworkEntityRpcMessage& message)
{
auto findIt = m_multiplayerComponentMap.find(message.GetComponentId());
@@ -1,165 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/ConnectionLayer/IConnection.h>
#include <Source/NetworkEntity/EntityReplication/ReplicationRecord.h>
#include <Include/NetworkEntityHandle.h>
#include <Include/IMultiplayerComponentInput.h>
#include <Include/INetworkTime.h>
#include <Include/MultiplayerTypes.h>
#include <AzCore/EBus/Event.h>
namespace Multiplayer
{
class NetworkInput;
class ReplicationRecord;
class MultiplayerComponent;
using EntityStopEvent = AZ::Event<const ConstNetworkEntityHandle&>;
using EntityDirtiedEvent = AZ::Event<>;
using EntityMigrationStartEvent = AZ::Event<ClientInputId>;
using EntityMigrationEndEvent = AZ::Event<>;
using EntityServerMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, HostId, AzNetworking::ConnectionId>;
//! @class NetBindComponent
//! @brief Component that provides net-binding to a networked entity.
class NetBindComponent final
: public AZ::Component
{
public:
AZ_COMPONENT(NetBindComponent, "{DAA076B3-1A1C-4FEF-8583-1DF696971604}");
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
NetBindComponent();
~NetBindComponent() override = default;
//! AZ::Component overrides.
//! @{
void Init() override;
void Activate() override;
void Deactivate() override;
//! @}
NetEntityRole GetNetEntityRole() const;
bool IsAuthority() const;
bool HasController() const;
NetEntityId GetNetEntityId() const;
const PrefabEntityId& GetPrefabEntityId() const;
ConstNetworkEntityHandle GetEntityHandle() const;
NetworkEntityHandle GetEntityHandle();
MultiplayerComponentInputVector AllocateComponentInputs();
bool IsProcessingInput() const;
void CreateInput(NetworkInput& networkInput, float deltaTime);
void ProcessInput(NetworkInput& networkInput, float deltaTime);
AZ::Aabb GetRewindBoundsForInput(const NetworkInput& networkInput, float deltaTime) const;
bool HandleRpcMessage(AzNetworking::IConnection* invokingConnection, NetEntityRole remoteRole, NetworkEntityRpcMessage& message);
bool HandlePropertyChangeMessage(AzNetworking::ISerializer& serializer, bool notifyChanges = true);
RpcSendEvent& GetSendAuthorityToClientRpcEvent();
RpcSendEvent& GetSendAuthorityToAutonomousRpcEvent();
RpcSendEvent& GetSendServerToAuthorityRpcEvent();
RpcSendEvent& GetSendAutonomousToAuthorityRpcEvent();
const ReplicationRecord& GetPredictableRecord() const;
void MarkDirty();
void NotifyLocalChanges();
void NotifyMigrationStart(ClientInputId migratedInputId);
void NotifyMigrationEnd();
void NotifyServerMigration(HostId hostId, AzNetworking::ConnectionId connectionId);
void AddEntityStopEventHandler(EntityStopEvent::Handler& eventHandler);
void AddEntityDirtiedEventHandler(EntityDirtiedEvent::Handler& eventHandler);
void AddEntityMigrationStartEventHandler(EntityMigrationStartEvent::Handler& eventHandler);
void AddEntityMigrationEndEventHandler(EntityMigrationEndEvent::Handler& eventHandler);
void AddEntityServerMigrationEventHandler(EntityServerMigrationEvent::Handler& eventHandler);
bool SerializeEntityCorrection(AzNetworking::ISerializer& serializer);
bool SerializeStateDeltaMessage(ReplicationRecord& replicationRecord, AzNetworking::ISerializer& serializer);
void NotifyStateDeltaChanges(ReplicationRecord& replicationRecord);
void FillReplicationRecord(ReplicationRecord& replicationRecord) const;
void FillTotalReplicationRecord(ReplicationRecord& replicationRecord) const;
private:
void PreInit(AZ::Entity* entity, const PrefabEntityId& prefabEntityId, NetEntityId netEntityId, NetEntityRole netEntityRole);
void ConstructControllers();
void DestructControllers();
void ActivateControllers(EntityIsMigrating entityIsMigrating);
void DeactivateControllers(EntityIsMigrating entityIsMigrating);
void OnEntityStateEvent(AZ::Entity::State oldState, AZ::Entity::State newState);
void NetworkAttach();
void HandleMarkedDirty();
void HandleLocalServerRpcMessage(NetworkEntityRpcMessage& message);
void DetermineInputOrdering();
void StopEntity();
ReplicationRecord m_currentRecord = NetEntityRole::InvalidRole;
ReplicationRecord m_totalRecord = NetEntityRole::InvalidRole;
ReplicationRecord m_predictableRecord = NetEntityRole::Autonomous;
ReplicationRecord m_localNotificationRecord = NetEntityRole::InvalidRole;
PrefabEntityId m_prefabEntityId;
AZStd::unordered_map<NetComponentId, MultiplayerComponent*> m_multiplayerComponentMap;
AZStd::vector<MultiplayerComponent*> m_multiplayerSerializationComponentVector;
AZStd::vector<MultiplayerComponent*> m_multiplayerInputComponentVector;
RpcSendEvent m_sendAuthorityToClientRpcEvent;
RpcSendEvent m_sendAuthorityToAutonomousRpcEvent;
RpcSendEvent m_sendServertoAuthorityRpcEvent;
RpcSendEvent m_sendAutonomousToAuthorityRpcEvent;
EntityStopEvent m_entityStopEvent;
EntityDirtiedEvent m_dirtiedEvent;
EntityMigrationStartEvent m_entityMigrationStartEvent;
EntityMigrationEndEvent m_entityMigrationEndEvent;
EntityServerMigrationEvent m_entityServerMigrationEvent;
AZ::Event<> m_onRemove;
RpcSendEvent::Handler m_handleLocalServerRpcMessageEventHandle;
AZ::Event<>::Handler m_handleMarkedDirty;
AZ::Event<>::Handler m_handleNotifyChanges;
AZ::Entity::EntityStateEvent::Handler m_handleEntityStateEvent;
NetworkEntityHandle m_netEntityHandle;
NetEntityRole m_netEntityRole = NetEntityRole::InvalidRole;
NetEntityId m_netEntityId = InvalidNetEntityId;
bool m_isProcessingInput = false;
bool m_isMigrationDataValid = false;
bool m_needsToBeStopped = false;
friend class NetworkEntityManager;
friend class EntityReplicationManager;
};
bool NetworkRoleHasController(NetEntityRole networkRole);
}