You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp

732 lines
30 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Multiplayer/IMultiplayer.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
#include <Multiplayer/Components/NetworkTransformComponent.h>
#include <Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h>
#include <Source/NetworkEntity/NetworkEntityTracker.h>
#include <Source/NetworkEntity/EntityReplication/PropertyPublisher.h>
#include <Source/NetworkEntity/EntityReplication/PropertySubscriber.h>
#include <AzNetworking/ConnectionLayer/IConnection.h>
#include <AzNetworking/PacketLayer/IPacket.h>
#include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/Serialization/NetworkInputSerializer.h>
#include <AzNetworking/Serialization/NetworkOutputSerializer.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
#include <AzFramework/Components/TransformComponent.h>
namespace Multiplayer
{
EntityReplicator::EntityReplicator
(
EntityReplicationManager& replicationManager,
AzNetworking::IConnection* connection,
NetEntityRole remoteNetworkRole,
const ConstNetworkEntityHandle& entityHandle
)
: m_replicationManager(replicationManager)
, m_connection(connection)
, m_entityHandle(entityHandle)
, m_remoteNetworkRole(remoteNetworkRole)
, m_onEntityDirtiedHandler([this]() { OnEntityDirtiedEvent(); })
, m_onSendRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); })
, m_onForwardRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); })
, m_onSendAutonomousRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); })
, m_onForwardAutonomousRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); })
, m_onEntityStopHandler([this](const ConstNetworkEntityHandle&) { OnEntityRemovedEvent(); })
, m_proxyRemovalEvent([this] { OnProxyRemovalTimedEvent(); }, AZ::Name("ProxyRemovalTimedEvent"))
{
if (auto localEnt = m_entityHandle.GetEntity())
{
m_netBindComponent = m_entityHandle.GetNetBindComponent();
m_boundLocalNetworkRole = m_netBindComponent->GetNetEntityRole();
}
}
EntityReplicator::~EntityReplicator()
{
AZ::EntityBus::Handler::BusDisconnect();
}
void EntityReplicator::SetPrefabEntityId(const PrefabEntityId& prefabEntityId)
{
m_prefabEntityId = prefabEntityId;
m_prefabEntityIdSet = true;
}
void EntityReplicator::Reset(NetEntityRole remoteNetworkRole)
{
AZ::EntityBus::Handler::BusDisconnect();
m_remoteNetworkRole = remoteNetworkRole;
m_propertyPublisher = nullptr;
m_propertySubscriber = nullptr;
m_wasMigrated = false;
m_onSendRpcHandler.Disconnect();
m_onForwardRpcHandler.Disconnect();
m_onSendAutonomousRpcHandler.Disconnect();
m_onForwardAutonomousRpcHandler.Disconnect();
m_onEntityStopHandler.Disconnect();
}
void EntityReplicator::Initialize(const ConstNetworkEntityHandle& entityHandle)
{
AZ_Assert(entityHandle, "Empty handle passed to Initialize");
m_entityHandle = entityHandle;
if (auto localEntity = m_entityHandle.GetEntity())
{
m_netBindComponent = m_entityHandle.GetNetBindComponent();
AZ_Assert(m_netBindComponent, "No Multiplayer::NetBindComponent");
m_boundLocalNetworkRole = m_netBindComponent->GetNetEntityRole();
SetPrefabEntityId(m_netBindComponent->GetPrefabEntityId());
}
AZ_Assert
(
m_boundLocalNetworkRole != m_remoteNetworkRole,
"Invalid configuration detected, bound local role must differ from remote network role: %s",
GetEnumString(m_boundLocalNetworkRole)
);
if (RemoteManagerOwnsEntityLifetime())
{
// Make sure we don't have any outstanding entity migration timeouts since we now have a new replicator
GetNetworkEntityAuthorityTracker()->AddEntityAuthorityManager(entityHandle, m_replicationManager.GetRemoteHostId());
}
// We got re-added
m_proxyRemovalEvent.RemoveFromQueue();
if (CanSendUpdates())
{
m_replicationManager.AddReplicatorToPendingSend(*this);
m_propertyPublisher = AZStd::make_unique<PropertyPublisher>
(
GetRemoteNetworkRole(),
!RemoteManagerOwnsEntityLifetime() ? PropertyPublisher::OwnsLifetime::True : PropertyPublisher::OwnsLifetime::False,
m_netBindComponent,
*m_connection
);
m_onEntityDirtiedHandler.Disconnect();
m_netBindComponent->AddEntityDirtiedEventHandler(m_onEntityDirtiedHandler);
}
else
{
m_propertyPublisher = nullptr;
}
if (m_remoteNetworkRole == NetEntityRole::Authority ||
m_remoteNetworkRole == NetEntityRole::Autonomous)
{
m_propertySubscriber = AZStd::make_unique<PropertySubscriber>(m_replicationManager, m_netBindComponent);
}
else
{
m_propertySubscriber = nullptr;
}
// Prepare event handlers
if (auto localEntity = m_entityHandle.GetEntity())
{
NetBindComponent* netBindComponent = m_entityHandle.GetNetBindComponent();
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
m_onEntityStopHandler.Disconnect();
netBindComponent->AddEntityStopEventHandler(m_onEntityStopHandler);
AttachRPCHandlers();
}
AZ_Assert(m_remoteNetworkRole != NetEntityRole::InvalidRole, "Trying to add an entity replicator with the remote role as invalid");
AZ_Assert(m_boundLocalNetworkRole != NetEntityRole::InvalidRole, "Trying to add an entity replicator with the bound local role as invalid");
m_wasMigrated = false;
}
void EntityReplicator::AttachRPCHandlers()
{
// Make sure all handlers are detached first
m_onSendRpcHandler.Disconnect();
m_onSendAutonomousRpcHandler.Disconnect();
m_onForwardRpcHandler.Disconnect();
m_onForwardAutonomousRpcHandler.Disconnect();
if (auto localEntity = m_entityHandle.GetEntity())
{
NetBindComponent* netBindComponent = m_entityHandle.GetNetBindComponent();
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
switch (GetBoundLocalNetworkRole())
{
case NetEntityRole::Authority:
if (GetRemoteNetworkRole() == NetEntityRole::Client || GetRemoteNetworkRole() == NetEntityRole::Autonomous)
{
m_onSendRpcHandler.Connect(netBindComponent->GetSendAuthorityToClientRpcEvent());
if (GetRemoteNetworkRole() == NetEntityRole::Autonomous)
{
m_onSendAutonomousRpcHandler.Connect(netBindComponent->GetSendAuthorityToAutonomousRpcEvent());
}
}
else if (GetRemoteNetworkRole() == NetEntityRole::Server)
{
m_onForwardRpcHandler.Connect(netBindComponent->GetSendAuthorityToClientRpcEvent());
}
break;
case NetEntityRole::Server:
if (GetRemoteNetworkRole() == NetEntityRole::Authority)
{
m_onSendRpcHandler.Connect(netBindComponent->GetSendServerToAuthorityRpcEvent());
m_onForwardRpcHandler.Connect(netBindComponent->GetSendAuthorityToClientRpcEvent());
m_onForwardAutonomousRpcHandler.Connect(netBindComponent->GetSendAuthorityToAutonomousRpcEvent());
}
else if (GetRemoteNetworkRole() == NetEntityRole::Client)
{
// Listen for these to forward the rpc along to the other Client replicators
m_onSendRpcHandler.Connect(netBindComponent->GetSendAuthorityToClientRpcEvent());
}
else if (GetRemoteNetworkRole() == NetEntityRole::Autonomous)
{
// NOTE: Autonomous is not connected to ServerProxy, it is always connected to an Authority
AZ_Assert(false, "Unexpected autonomous remote role")
}
break;
case NetEntityRole::Client:
// Nothing allowed, no Client to Server communication
break;
case NetEntityRole::Autonomous:
if (GetRemoteNetworkRole() == NetEntityRole::Authority)
{
m_onSendRpcHandler.Connect(netBindComponent->GetSendAutonomousToAuthorityRpcEvent());
}
break;
default:
AZ_Assert(false, "Unexpected network role");
}
}
}
void EntityReplicator::ActivateNetworkEntity()
{
ActivateNetworkEntityInternal();
}
void EntityReplicator::OnEntityActivated(const AZ::EntityId&)
{
ActivateNetworkEntityInternal();
AZ::EntityBus::Handler::BusDisconnect();
}
void EntityReplicator::OnEntityDestroyed(const AZ::EntityId&)
{
AZ::EntityBus::Handler::BusDisconnect();
}
void EntityReplicator::ActivateNetworkEntityInternal()
{
AZ::EntityBus::Handler::BusDisconnect();
AZ::Entity* entity = GetEntityHandle().GetEntity();
AZ_Assert(entity, "Entity replicator entity unexpectedly missing");
if (entity->GetState() != AZ::Entity::State::Init)
{
AZLOG_WARN("Trying to activate an entity that is not in the Init state (%llu)", static_cast<AZ::u64>(GetEntityHandle().GetNetEntityId()));
}
entity->Activate();
m_replicationManager.m_orphanedEntityRpcs.DispatchOrphanedRpcs(*this);
}
bool EntityReplicator::CanSendUpdates()
{
bool ret(false);
if (auto localEnt = GetEntityHandle().GetEntity())
{
NetBindComponent* netBindComponent = m_netBindComponent;
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
bool isAuthority = (GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole());
bool isClient = GetRemoteNetworkRole() == NetEntityRole::Client;
bool isAutonomous = GetBoundLocalNetworkRole() == NetEntityRole::Autonomous;
if (isAuthority || isClient || isAutonomous)
{
ret = true;
}
}
return ret;
}
bool EntityReplicator::OwnsReplicatorLifetime() const
{
bool ret(false);
if (GetBoundLocalNetworkRole() == NetEntityRole::Authority // Authority always owns lifetime
|| (GetBoundLocalNetworkRole() == NetEntityRole::Server // Server also owns lifetime if the remote endpoint is a client of some form
&& (GetRemoteNetworkRole() == NetEntityRole::Client
|| GetRemoteNetworkRole() == NetEntityRole::Autonomous)))
{
ret = true;
}
return ret;
}
bool EntityReplicator::RemoteManagerOwnsEntityLifetime() const
{
bool isServer = (GetBoundLocalNetworkRole() == NetEntityRole::Server)
&& (GetRemoteNetworkRole() == NetEntityRole::Authority);
bool isClient = (GetBoundLocalNetworkRole() == NetEntityRole::Client)
|| (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous);
return isServer || isClient;
}
void EntityReplicator::MarkForRemoval()
{
AZ::EntityBus::Handler::BusDisconnect();
if (RemoteManagerOwnsEntityLifetime())
{
GetNetworkEntityAuthorityTracker()->RemoveEntityAuthorityManager(m_entityHandle, m_replicationManager.GetRemoteHostId());
}
ClearPendingRemoval();
if (m_propertyPublisher)
{
m_propertyPublisher->SetDeleting();
m_replicationManager.AddReplicatorToPendingSend(*this);
m_onEntityDirtiedHandler.Disconnect();
}
else if (m_propertySubscriber)
{
m_propertySubscriber->SetDeleting();
}
m_replicationManager.AddReplicatorToPendingRemoval(*this);
m_onForwardRpcHandler.Disconnect();
m_onForwardAutonomousRpcHandler.Disconnect();
m_onEntityStopHandler.Disconnect();
}
bool EntityReplicator::IsMarkedForRemoval() const
{
bool ret(true);
if (m_propertyPublisher)
{
ret = m_propertyPublisher->IsDeleting();
}
else
{
AZ_Assert(m_propertySubscriber, "Expected to have at least a subscriber when deleting");
ret = m_propertySubscriber->IsDeleting();
}
return ret;
}
void EntityReplicator::SetPendingRemoval(AZ::TimeMs pendingRemovalTimeMs)
{
AZ_Assert(m_propertyPublisher, "Only valid if we are publishing updates");
if (pendingRemovalTimeMs > AZ::Time::ZeroTimeMs)
{
if (!IsPendingRemoval())
{
m_proxyRemovalEvent.Enqueue(pendingRemovalTimeMs);
}
}
else
{
MarkForRemoval();
}
}
bool EntityReplicator::IsPendingRemoval() const
{
return m_proxyRemovalEvent.IsScheduled();
}
void EntityReplicator::ClearPendingRemoval()
{
m_proxyRemovalEvent.RemoveFromQueue();
}
bool EntityReplicator::IsDeletionAcknowledged() const
{
bool ret(true);
// we sent the delete message, make sure it gets there
if (m_propertyPublisher)
{
ret = m_propertyPublisher->IsDeleted();
}
else
{
AZ_Assert(m_propertySubscriber, "Expected to have at least a subscriber when deleting");
ret = m_propertySubscriber->IsDeleted();
}
return ret;
}
AZ::TimeMs EntityReplicator::GetResendTimeoutTimeMs() const
{
return m_replicationManager.GetResendTimeoutTimeMs();
}
bool EntityReplicator::IsReadyToActivate() const
{
const AZ::Entity* entity = m_entityHandle.GetEntity();
AZ_Assert(entity, "Entity replicator entity unexpectedly missing");
const NetworkHierarchyChildComponent* hierarchyChildComponent = entity->FindComponent<NetworkHierarchyChildComponent>();
const NetworkHierarchyRootComponent* hierarchyRootComponent = nullptr;
if (hierarchyChildComponent == nullptr)
{
// Child and root hierarchy components are mutually exclusive
hierarchyRootComponent = entity->FindComponent<NetworkHierarchyRootComponent>();
}
if ((hierarchyChildComponent && hierarchyChildComponent->IsHierarchicalChild())
|| (hierarchyRootComponent && hierarchyRootComponent->IsHierarchicalChild()))
{
// If hierarchy is enabled for the entity, check if the parent is available
if (const NetworkTransformComponent* networkTransform = entity->FindComponent<NetworkTransformComponent>())
{
const NetEntityId parentId = networkTransform->GetParentEntityId();
// For root entities attached to a level, a network parent won't be set.
// In this case, this entity is the root entity of the hierarchy and it will be activated first.
if (parentId != InvalidNetEntityId)
{
ConstNetworkEntityHandle parentHandle = GetNetworkEntityManager()->GetEntity(parentId);
const AZ::Entity* parentEntity = parentHandle.GetEntity();
if (parentEntity && parentEntity->GetState() == AZ::Entity::State::Active)
{
AZLOG
(
NET_HierarchyActivationInfo,
"Hierchical entity %s asking for activation - granted",
entity->GetName().c_str()
);
return true;
}
AZLOG
(
NET_HierarchyActivationInfo,
"Hierchical entity %s asking for activation - waiting on the parent %llu",
entity->GetName().c_str(),
aznumeric_cast<AZ::u64>(parentId)
);
return false;
}
}
}
return true;
}
NetworkEntityUpdateMessage EntityReplicator::GenerateUpdatePacket()
{
if (IsMarkedForRemoval() && OwnsReplicatorLifetime()) // TODO: clean this up
{
// If the remote replicator is not established, we need to take ownership of the entity
AZLOG
(
NET_RepDeletes,
"Sending delete replicator id %llu migrated %d to remote host %s",
aznumeric_cast<AZ::u64>(GetEntityHandle().GetNetEntityId()),
WasMigrated() ? 1 : 0,
m_replicationManager.GetRemoteHostId().GetString().c_str()
);
return NetworkEntityUpdateMessage(GetEntityHandle().GetNetEntityId(), WasMigrated());
}
NetBindComponent* netBindComponent = GetNetBindComponent();
const bool sendSliceName = !m_propertyPublisher->IsRemoteReplicatorEstablished();
NetworkEntityUpdateMessage updateMessage(GetRemoteNetworkRole(), GetEntityHandle().GetNetEntityId());
if (sendSliceName)
{
updateMessage.SetPrefabEntityId(netBindComponent->GetPrefabEntityId());
}
AzNetworking::NetworkInputSerializer inputSerializer(updateMessage.ModifyData().GetBuffer(), static_cast<uint32_t>(updateMessage.ModifyData().GetCapacity()));
m_propertyPublisher->UpdateSerialization(inputSerializer);
updateMessage.ModifyData().Resize(inputSerializer.GetSize());
return updateMessage;
}
void EntityReplicator::FinalizeSerialization(AzNetworking::PacketId sentId)
{
m_propertyPublisher->FinalizeSerialization(sentId);
}
void EntityReplicator::DeferRpcMessage(NetworkEntityRpcMessage& entityRpcMessage)
{
// Received rpc metrics, log rpc sent, number of bytes, and the componentId/rpcId for bandwidth metrics
MultiplayerStats& stats = GetMultiplayer()->GetStats();
stats.RecordRpcSent(GetEntityHandle().GetEntity()->GetId(), GetEntityHandle().GetEntity()->GetName().c_str(),
entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex(), entityRpcMessage.GetEstimatedSerializeSize());
m_replicationManager.AddDeferredRpcMessage(entityRpcMessage);
}
void EntityReplicator::OnSendRpcEvent(NetworkEntityRpcMessage& entityRpcMessage)
{
if (IsMarkedForRemoval() && GetNetworkEntityAuthorityTracker()->DoesEntityHaveOwner(GetEntityHandle()))
{
// The remote end no longer owns this entity, so don't try and send to it (let another replicator send to it)
return;
}
if (m_isForwardingRpc)
{
return;
}
if (auto localEntity = m_entityHandle.GetEntity())
{
DeferRpcMessage(entityRpcMessage);
}
}
void EntityReplicator::OnEntityDirtiedEvent()
{
AZ_Assert(m_propertyPublisher, "Expected to have a publisher, did we forget to disconnect?");
m_propertyPublisher->GenerateRecord();
m_replicationManager.AddReplicatorToPendingSend(*this);
}
void EntityReplicator::OnEntityRemovedEvent()
{
m_netBindComponent = nullptr;
MarkForRemoval();
}
void EntityReplicator::OnProxyRemovalTimedEvent()
{
MarkForRemoval();
}
EntityReplicator::RpcValidationResult EntityReplicator::ValidateRpcMessage(const NetworkEntityRpcMessage& entityRpcMessage) const
{
RpcValidationResult result = RpcValidationResult::DropRpcAndDisconnect;
switch (entityRpcMessage.GetRpcDeliveryType())
{
case RpcDeliveryType::AuthorityToClient:
if (((GetBoundLocalNetworkRole() == NetEntityRole::Client) || (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous))
&& (GetRemoteNetworkRole() == NetEntityRole::Authority))
{
// We are a local client, and we are connected to server, aka AuthorityToClient
result = RpcValidationResult::HandleRpc;
}
if ((GetBoundLocalNetworkRole() == NetEntityRole::Server) && (GetRemoteNetworkRole() == NetEntityRole::Authority))
{
// We are on a server, and we received this message from another server, therefore we should forward this to any connected clients
result = RpcValidationResult::ForwardToClient;
}
break;
case RpcDeliveryType::AuthorityToAutonomous:
if ((GetBoundLocalNetworkRole() == NetEntityRole::Autonomous) && (GetRemoteNetworkRole() == NetEntityRole::Authority))
{
// We are an autonomous client, and we are connected to server, aka AuthorityToAutonomous
result = RpcValidationResult::HandleRpc;
}
if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetRemoteNetworkRole() == NetEntityRole::Server))
{
// We are on a server, and we received this message from another server, therefore we should forward this to our autonomous player
// This can occur if we've recently migrated
result = RpcValidationResult::ForwardToAutonomous;
}
break;
case RpcDeliveryType::AutonomousToAuthority:
if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetRemoteNetworkRole() == NetEntityRole::Autonomous))
{
if (IsMarkedForRemoval())
{
// we've likely migrated, forward if the message is reliable
if (entityRpcMessage.GetReliability() == ReliabilityType::Reliable)
{
// We only forward messages that should be reliable
result = RpcValidationResult::ForwardToAuthority;
}
else
{
// this isn't reliable, so we can just drop it
result = RpcValidationResult::DropRpc;
}
}
else
{
// We are on a server, and we got a message from the autonomous, aka AutonomousToAuthority, so handle
result = RpcValidationResult::HandleRpc;
}
}
break;
case RpcDeliveryType::ServerToAuthority:
if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetRemoteNetworkRole() == NetEntityRole::Server))
{
// if we're marked for removal, then we should forward to whomever now owns this entity
if (IsMarkedForRemoval())
{
// we've likely migrated, forward if the message is reliable
if (entityRpcMessage.GetReliability() == ReliabilityType::Reliable)
{
// We only forward messages that should be reliable
result = RpcValidationResult::ForwardToAuthority;
}
else
{
// this isn't reliable, so we can just drop it
result = RpcValidationResult::DropRpc;
}
}
else
{
// We are the authority, and we got this message from a server proxy, aka ServerToAuthority, so handle
result = RpcValidationResult::HandleRpc;
}
}
break;
}
if (result == RpcValidationResult::DropRpcAndDisconnect)
{
bool isLocalServer = (GetBoundLocalNetworkRole() == NetEntityRole::Authority) || (GetBoundLocalNetworkRole() == NetEntityRole::Server);
bool isRemoteServer = (GetRemoteNetworkRole() == NetEntityRole::Authority) || (GetRemoteNetworkRole() == NetEntityRole::Server);
if (isLocalServer && isRemoteServer)
{
// Demote this to just a drop message, we didn't want to handle the message, but we don't want to drop the connection
result = EntityReplicator::RpcValidationResult::DropRpc;
}
else
{
AZLOG_ERROR
(
"Dropping RPC and Connection EntityId=%llu LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s",
aznumeric_cast<AZ::u64>(m_entityHandle.GetNetEntityId()),
GetEnumString(GetBoundLocalNetworkRole()),
GetEnumString(GetRemoteNetworkRole()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()),
GetMultiplayerComponentRegistry()->GetComponentRpcName(entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex()),
entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false",
IsMarkedForRemoval() ? "true" : "false"
);
}
}
if (result == RpcValidationResult::DropRpc)
{
AZLOG
(
NET_Rpc,
"Dropping RPC EntityId=%llu LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s",
aznumeric_cast<AZ::u64>(m_entityHandle.GetNetEntityId()),
GetEnumString(GetBoundLocalNetworkRole()),
GetEnumString(GetRemoteNetworkRole()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()),
GetMultiplayerComponentRegistry()->GetComponentRpcName(entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex()),
entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false",
IsMarkedForRemoval() ? "true" : "false"
);
}
return result;
}
bool EntityReplicator::HandleRpcMessage(AzNetworking::IConnection* invokingConnection, NetworkEntityRpcMessage& entityRpcMessage)
{
// Received rpc metrics, log rpc received, time spent, number of bytes, and the componentId/rpcId for bandwidth metrics
MultiplayerStats& stats = GetMultiplayer()->GetStats();
stats.RecordRpcReceived(GetEntityHandle().GetEntity()->GetId(), GetEntityHandle().GetEntity()->GetName().c_str(),
entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex(), entityRpcMessage.GetEstimatedSerializeSize());
if (!m_netBindComponent)
{
AZLOG_WARN
(
"Dropping RPC since entity deleted EntityId=%llu LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s",
aznumeric_cast<AZ::u64>(m_entityHandle.GetNetEntityId()),
GetEnumString(GetBoundLocalNetworkRole()),
GetEnumString(GetRemoteNetworkRole()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()),
GetMultiplayerComponentRegistry()->GetComponentRpcName(entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex()),
entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false",
IsMarkedForRemoval() ? "true" : "false"
);
return false;
}
// When we forward a message, we'll likely hit the this entity replicator again (since it's already listening on the RPC events)
// Therefore, we need to ignore the re-entrant case.
class ScopedForwardingMessage
{
public:
ScopedForwardingMessage(EntityReplicator& replicator)
: m_replicator(replicator)
{
m_isForwardingCache = m_replicator.m_isForwardingRpc;
m_replicator.m_isForwardingRpc = true;
}
~ScopedForwardingMessage()
{
m_replicator.m_isForwardingRpc = m_isForwardingCache;
}
bool m_isForwardingCache = false;
EntityReplicator& m_replicator;
};
// First validate the message with local & remote roles
RpcValidationResult result = ValidateRpcMessage(entityRpcMessage);
switch (result)
{
case RpcValidationResult::HandleRpc:
return m_netBindComponent->HandleRpcMessage(invokingConnection, GetRemoteNetworkRole(), entityRpcMessage);
case RpcValidationResult::DropRpc:
return true;
case RpcValidationResult::DropRpcAndDisconnect:
return false;
case RpcValidationResult::ForwardToClient:
{
ScopedForwardingMessage forwarding(*this);
m_netBindComponent->GetSendAuthorityToClientRpcEvent().Signal(entityRpcMessage);
}
return true;
case RpcValidationResult::ForwardToAutonomous:
{
ScopedForwardingMessage forwarding(*this);
m_netBindComponent->GetSendAuthorityToAutonomousRpcEvent().Signal(entityRpcMessage);
}
return true;
case RpcValidationResult::ForwardToAuthority:
{
ScopedForwardingMessage forwarding(*this);
m_netBindComponent->GetSendServerToAuthorityRpcEvent().Signal(entityRpcMessage);
}
return true;
default:
break;
}
AZ_Assert(false, "Unhandled RpcValidationResult %d", result);
return false;
}
}