Integrating latest 47acbe8

This commit is contained in:
alexpete
2021-03-25 13:57:57 -07:00
parent 448c549698
commit 75dc720198
10312 changed files with 2711566 additions and 671451 deletions
@@ -0,0 +1,217 @@
/*
* 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 <Source/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Source/NetworkEntity/INetworkEntityManager.h>
#include <Source/ReplicationWindows/IReplicationWindow.h>
#include <Source/EntityDomains/IEntityDomain.h>
#include <Source/NetworkEntity/NetworkEntityHandle.h>
#include <Source/Components/NetBindComponent.h>
#include <AzNetworking/DataStructures/TimeoutQueue.h>
#include <AzNetworking/PacketLayer/IPacketHeader.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/limits.h>
#include <AzCore/EBus/Event.h>
#include <AzCore/EBus/ScheduledEvent.h>
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
namespace AzNetworking
{
class IConnection;
class IConnectionListener;
}
namespace Multiplayer
{
class IEntityDomain;
class EntityReplicator;
class EntityReplicationManager final
{
public:
using EntityReplicatorMap = AZStd::map<NetEntityId, AZStd::unique_ptr<EntityReplicator>>;
enum class Mode
{
Invalid,
LocalServerToRemoteClient,
LocalServerToRemoteServer,
LocalClientToRemoteServer,
};
EntityReplicationManager(AzNetworking::IConnection& connection, AzNetworking::IConnectionListener& connectionListener, Mode mode);
~EntityReplicationManager() = default;
void SetRemoteHostId(HostId hostId);
HostId GetRemoteHostId() const;
void ActivatePendingEntities();
void SendUpdates(AZ::TimeMs serverGameTimeMs);
void Clear(bool forMigration);
bool SetEntityRebasing(NetworkEntityHandle& entityHandle);
void MigrateAllEntities();
void MigrateEntity(NetEntityId netEntityId);
bool CanMigrateEntity(const ConstNetworkEntityHandle& entityHandle) const;
bool HasRemoteAuthority(const ConstNetworkEntityHandle& entityHandle) const;
void SetEntityDomain(AZStd::unique_ptr<IEntityDomain> entityDomain);
IEntityDomain* GetEntityDomain();
void SetReplicationWindow(AZStd::unique_ptr<IReplicationWindow> replicationWindow);
IReplicationWindow* GetReplicationWindow();
void GetEntityReplicatorIdList(AZStd::list<NetEntityId>& outList);
uint32_t GetEntityReplicatorCount(NetEntityRole localNetworkRole);
void AddDeferredRpcMessage(NetworkEntityRpcMessage& rpcMessage);
void AddAutonomousEntityReplicatorCreatedHandle(AZ::Event<NetEntityId>::Handler& handler);
bool HandleMessage(AzNetworking::IConnection* connection, MultiplayerPackets::EntityMigration& message);
bool HandleEntityDeleteMessage(EntityReplicator* entityReplicator, const AzNetworking::IPacketHeader& packetHeader, const NetworkEntityUpdateMessage& updateMessage);
bool HandleEntityUpdateMessage(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, const NetworkEntityUpdateMessage& updateMessage);
bool HandleEntityRpcMessage(AzNetworking::IConnection* connection, NetworkEntityRpcMessage& message);
AZ::TimeMs GetResendTimeoutTimeMs() const;
void SetMaxRemoteEntitiesPendingCreationCount(uint32_t maxPendingEntities);
void SetEntityActivationTimeSliceMs(AZ::TimeMs timeSliceMs);
void SetEntityPendingRemovalMs(AZ::TimeMs entityPendingRemovalMs);
AzNetworking::IConnection& GetConnection();
AZ::TimeMs GetFrameTimeMs();
void AddReplicatorToPendingSend(const EntityReplicator& entityReplicator);
bool IsUpdateModeToServerClient();
private:
AZ_DISABLE_COPY_MOVE(EntityReplicationManager);
enum class UpdateValidationResult
{
HandleMessage, // Handle an entity update message
DropMessage, // Do not handle an entity update message, but don't disconnect (could be out of order/date and isn't relevant)
DropMessageAndDisconnect, // Do not handle the message, it is malformed and we should disconnect the connection
};
UpdateValidationResult ValidateUpdate(const NetworkEntityUpdateMessage& updateMessage, AzNetworking::PacketId packetId, EntityReplicator* entityReplicator);
using RpcMessages = AZStd::list<NetworkEntityRpcMessage>;
bool DispatchOrphanedRpc(NetworkEntityRpcMessage& message, EntityReplicator* entityReplicator);
using EntityReplicatorList = AZStd::vector<EntityReplicator*>;
EntityReplicatorList GenerateEntityUpdateList();
void SendEntityUpdatesPacketHelper(AZ::TimeMs serverGameTimeMs, EntityReplicatorList& toSendList, uint32_t maxPayloadSize, AzNetworking::IConnection& connection);
void SendEntityUpdates(AZ::TimeMs serverGameTimeMs);
void SendEntityRpcs(RpcMessages& deferredRpcs, bool reliable);
void MigrateEntityInternal(NetEntityId entityId);
void OnEntityExitDomain(const ConstNetworkEntityHandle& entityHandle);
void OnPostEntityMigration(const ConstNetworkEntityHandle& entityHandle, HostId remoteHostId, AzNetworking::ConnectionId connectionId);
EntityReplicator* AddEntityReplicator(const ConstNetworkEntityHandle& entityHandle, NetEntityRole netEntityRole);
const EntityReplicator* GetEntityReplicator(NetEntityId entityId) const;
EntityReplicator* GetEntityReplicator(NetEntityId entityId);
EntityReplicator* GetEntityReplicator(const ConstNetworkEntityHandle& entityHandle);
void UpdateWindow();
bool HandlePropertyChangeMessage
(
EntityReplicator* entityReplicator,
AzNetworking::PacketId packetId,
NetEntityId netEntityId,
NetEntityRole netEntityRole,
AzNetworking::ISerializer& serializer,
const PrefabEntityId& prefabEntityId
);
void AddReplicatorToPendingRemoval(const EntityReplicator& replicator);
void ClearRemovedReplicators();
class OrphanedEntityRpcs
: public AzNetworking::ITimeoutHandler
{
public:
OrphanedEntityRpcs(EntityReplicationManager& replicationManager);
void Update();
bool DispatchOrphanedRpcs(EntityReplicator& entityReplicator);
void AddOrphanedRpc(NetEntityId entityId, NetworkEntityRpcMessage& entityPrcMessage);
AZStd::size_t Size() const { return m_entityRpcMap.size(); }
private:
AzNetworking::TimeoutResult HandleTimeout(AzNetworking::TimeoutQueue::TimeoutItem& item) override;
struct OrphanedRpcs
{
OrphanedRpcs() = default;
OrphanedRpcs(OrphanedRpcs&& rhs)
{
m_timeoutId = rhs.m_timeoutId;
rhs.m_timeoutId = AzNetworking::TimeoutId{ 0 };
m_rpcMessages.swap(rhs.m_rpcMessages);
}
AzNetworking::TimeoutId m_timeoutId = AzNetworking::TimeoutId{ 0 };
RpcMessages m_rpcMessages;
};
typedef AZStd::unordered_map<NetEntityId, OrphanedRpcs> EntityRpcMap;
EntityRpcMap m_entityRpcMap;
AzNetworking::TimeoutQueue m_timeoutQueue;
EntityReplicationManager& m_replicationManager;
};
OrphanedEntityRpcs m_orphanedEntityRpcs;
EntityReplicatorMap m_entityReplicatorMap;
//! The set of entities that we have sent creation messages for, but have not received confirmation back that the create has occurred
AZStd::unordered_set<NetEntityId> m_remoteEntitiesPendingCreation;
AZStd::deque<NetEntityId> m_entitiesPendingActivation;
AZStd::set<NetEntityId> m_replicatorsPendingRemoval;
AZStd::unordered_set<NetEntityId> m_replicatorsPendingSend;
// Deferred RPC Sends
RpcMessages m_deferredRpcMessagesReliable;
RpcMessages m_deferredRpcMessagesUnreliable;
AZ::Event<NetEntityId> m_autonomousEntityReplicatorCreated;
EntityExitDomainEvent::Handler m_entityExitDomainEventHandler;
AZ::ScheduledEvent m_clearRemovedReplicators;
AZ::ScheduledEvent m_updateWindow;
AzNetworking::IConnectionListener& m_connectionListener;
AzNetworking::IConnection& m_connection;
AZStd::unique_ptr<IReplicationWindow> m_replicationWindow;
AZStd::unique_ptr<IEntityDomain> m_remoteEntityDomain;
AZ::TimeMs m_entityActivationTimeSliceMs = AZ::TimeMs{ 0 };
AZ::TimeMs m_entityPendingRemovalMs = AZ::TimeMs{ 0 };
AZ::TimeMs m_frameTimeMs = AZ::TimeMs{ 0 };
HostId m_remoteHostId = InvalidHostId;
uint32_t m_maxRemoteEntitiesPendingCreationCount = AZStd::numeric_limits<uint32_t>::max();
uint32_t m_maxPayloadSize = 0;
Mode m_updateMode = Mode::Invalid;
friend class EntityReplicator;
};
}
@@ -0,0 +1,705 @@
/*
* 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/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Source/NetworkEntity/EntityReplication/PropertyPublisher.h>
#include <Source/NetworkEntity/EntityReplication/PropertySubscriber.h>
#include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h>
#include <Source/NetworkEntity/NetworkEntityTracker.h>
#include <Source/NetworkEntity/NetworkEntityRpcMessage.h>
#include <Source/Components/NetBindComponent.h>
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
//#include "Generated/NovaGameCommon/Component/Multiplayer/LocationComponentCommon.AutoComponent.h"
#include <AzNetworking/PacketLayer/IPacket.h>
#include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/Serialization/NetworkInputSerializer.h>
#include <AzNetworking/Serialization/NetworkOutputSerializer.h>
#include <AzNetworking/ConnectionLayer/IConnection.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_onSendClientAutonomousRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); })
, m_onForwardClientAutonomousRpcHandler([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 = localEnt->FindComponent<NetBindComponent>();
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 a_RemoteNetworkRole)
{
AZ::EntityBus::Handler::BusDisconnect();
m_remoteNetworkRole = a_RemoteNetworkRole;
m_propertyPublisher = nullptr;
m_propertySubscriber = nullptr;
m_wasMigrated = false;
m_onSendRpcHandler.Disconnect();
m_onForwardRpcHandler.Disconnect();
m_onSendClientAutonomousRpcHandler.Disconnect();
m_onForwardClientAutonomousRpcHandler.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 = localEntity->FindComponent<NetBindComponent>();
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 Role: %d",
aznumeric_cast<int32_t>(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_netBindComponent->AddEntityDirtiedEventHandler(m_onEntityDirtiedHandler);
}
else
{
m_propertyPublisher = nullptr;
}
if (m_remoteNetworkRole == NetEntityRole::ServerAuthority ||
m_remoteNetworkRole == NetEntityRole::ClientAutonomous)
{
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 = localEntity->FindComponent<NetBindComponent>();
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
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_onSendClientAutonomousRpcHandler.Disconnect();
m_onForwardRpcHandler.Disconnect();
m_onForwardClientAutonomousRpcHandler.Disconnect();
if (auto localEntity = m_entityHandle.GetEntity())
{
NetBindComponent* netBindComponent = localEntity->FindComponent<NetBindComponent>();
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
switch (GetBoundLocalNetworkRole())
{
case NetEntityRole::ServerAuthority:
{
if (GetRemoteNetworkRole() == NetEntityRole::ClientSimulation || GetRemoteNetworkRole() == NetEntityRole::ClientAutonomous)
{
m_onSendRpcHandler.Connect(netBindComponent->GetSendServerAuthorityToClientSimulationRpcEvent());
if (GetRemoteNetworkRole() == NetEntityRole::ClientAutonomous)
{
m_onSendClientAutonomousRpcHandler.Connect(netBindComponent->GetSendServerAuthorityToClientAutonomousRpcEvent());
}
}
else if (GetRemoteNetworkRole() == NetEntityRole::ServerSimulation)
{
m_onForwardRpcHandler.Connect(netBindComponent->GetSendServerAuthorityToClientSimulationRpcEvent());
}
}
break;
case NetEntityRole::ServerSimulation:
{
if (GetRemoteNetworkRole() == NetEntityRole::ServerAuthority)
{
m_onSendRpcHandler.Connect(netBindComponent->GetSendServerSimulationToServerAuthorityRpcEvent());
m_onForwardRpcHandler.Connect(netBindComponent->GetSendServerAuthorityToClientSimulationRpcEvent());
m_onForwardClientAutonomousRpcHandler.Connect(netBindComponent->GetSendServerAuthorityToClientAutonomousRpcEvent());
}
else if (GetRemoteNetworkRole() == NetEntityRole::ClientSimulation)
{
// Listen for these to forward the rpc along to the other Client replicators
m_onSendRpcHandler.Connect(netBindComponent->GetSendServerAuthorityToClientSimulationRpcEvent());
}
// NOTE: e_ClientAutonomous is not connected to e_ServerProxy, it is always connected to an e_ServerAuthority
AZ_Assert(GetRemoteNetworkRole() != NetEntityRole::ClientAutonomous, "Unexpected autonomous remote role")
}
break;
case NetEntityRole::ClientSimulation:
{
// Nothing allowed, no ClientSimulation to Server communication
}
break;
case NetEntityRole::ClientAutonomous:
{
if (GetRemoteNetworkRole() == NetEntityRole::ServerAuthority)
{
m_onSendRpcHandler.Connect(netBindComponent->GetSendClientAutonomousToServerAuthorityRpcEvent());
}
}
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 (%u)", GetEntityHandle().GetNetEntityId());
}
// First we need to make sure the transform component has been updated with the correct value prior to activation
// This is because vanilla az components may only depend on the transform component, not the multiplayer transform component
//if (auto* locationComponent = FindCommonComponent<LocationComponent::Common>(GetEntityHandle()))
//{
// AZ::Transform newTransform = locationComponent->GetTransform();
// auto* transformComponent = entity->FindComponent<AzFramework::TransformComponent>();
// if (transformComponent)
// {
// // We can't use EBus here since the TransFormBus does not get connected until the activate call below
// transformComponent->SetWorldTM(newTransform);
// }
//}
// Ugly, but this is the only time we need to call a non-const function on this entity
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 isServerAuthority = (GetBoundLocalNetworkRole() == NetEntityRole::ServerAuthority)
&& (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole());
bool isClientSimulation = GetRemoteNetworkRole() == NetEntityRole::ClientSimulation;
bool isClientAutonomous = GetBoundLocalNetworkRole() == NetEntityRole::ClientAutonomous;
if (isServerAuthority || isClientSimulation || isClientAutonomous)
{
ret = true;
}
}
return ret;
}
bool EntityReplicator::OwnsReplicatorLifetime() const
{
bool ret(false);
if (GetBoundLocalNetworkRole() == NetEntityRole::ServerAuthority
|| (GetBoundLocalNetworkRole() == NetEntityRole::ServerSimulation
&& (GetRemoteNetworkRole() == NetEntityRole::ClientSimulation
|| GetRemoteNetworkRole() == NetEntityRole::ClientAutonomous)))
{
ret = true;
}
return ret;
}
bool EntityReplicator::RemoteManagerOwnsEntityLifetime() const
{
bool ret(false);
bool isServerSimulation = (GetBoundLocalNetworkRole() == NetEntityRole::ServerSimulation)
&& (GetRemoteNetworkRole() == NetEntityRole::ServerAuthority);
bool isClientSimulation = (GetBoundLocalNetworkRole() == NetEntityRole::ClientSimulation)
|| (GetBoundLocalNetworkRole() == NetEntityRole::ClientAutonomous);
if (isServerSimulation || isClientSimulation)
{
ret = true;
}
return ret;
}
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_onForwardClientAutonomousRpcHandler.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::TimeMs{ 0 })
{
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();
}
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 %u migrated %d to remote manager id %d",
aznumeric_cast<uint32_t>(GetEntityHandle().GetNetEntityId()),
WasMigrated() ? 1 : 0,
aznumeric_cast<int32_t>(m_replicationManager.GetRemoteHostId())
);
return NetworkEntityUpdateMessage(GetEntityHandle().GetNetEntityId(), WasMigrated(), m_propertyPublisher->IsRemoteReplicatorEstablished());
}
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(), updateMessage.ModifyData().GetCapacity());
m_propertyPublisher->UpdateSerialization(inputSerializer);
updateMessage.ModifyData().Resize(inputSerializer.GetSize());
return updateMessage;
}
void EntityReplicator::DeferRpcMessage(NetworkEntityRpcMessage& entityRpcMessage)
{
//Multiplayer::GetPacketHandlerMetricsInstance().LogSentRpc(entityRpcMessage.GetEntityComponentType(), entityRpcMessage.GetRpcMessageType(), 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::ServerAuthorityToClientSimulation:
{
if (((GetBoundLocalNetworkRole() == NetEntityRole::ClientSimulation) || (GetBoundLocalNetworkRole() == NetEntityRole::ClientAutonomous))
&& (GetRemoteNetworkRole() == NetEntityRole::ServerAuthority))
{
// We are a local client, and we are connected to server, aka AuthorityToClient
result = RpcValidationResult::HandleRpc;
}
if ((GetBoundLocalNetworkRole() == NetEntityRole::ServerSimulation)
&& (GetRemoteNetworkRole() == NetEntityRole::ServerAuthority))
{
// 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::ServerAuthorityToClientAutonomous:
{
if ((GetBoundLocalNetworkRole() == NetEntityRole::ClientAutonomous)
&& (GetRemoteNetworkRole() == NetEntityRole::ServerAuthority))
{
// We are an autonomous client, and we are connected to server, aka AuthorityToAutonomous
result = RpcValidationResult::HandleRpc;
}
if ((GetBoundLocalNetworkRole() == NetEntityRole::ServerAuthority)
&& (GetRemoteNetworkRole() == NetEntityRole::ServerSimulation))
{
// 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::ClientAutonomousToServerAuthority:
{
if ((GetBoundLocalNetworkRole() == NetEntityRole::ServerAuthority)
&& (GetRemoteNetworkRole() == NetEntityRole::ClientAutonomous))
{
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::ServerSimulationToServerAuthority:
{
if ((GetBoundLocalNetworkRole() == NetEntityRole::ServerAuthority)
&& (GetRemoteNetworkRole() == NetEntityRole::ServerSimulation))
{
// 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::ServerAuthority) || (GetBoundLocalNetworkRole() == NetEntityRole::ServerSimulation);
bool isRemoteServer = (GetRemoteNetworkRole() == NetEntityRole::ServerAuthority) || (GetRemoteNetworkRole() == NetEntityRole::ServerSimulation);
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=%u LocalRole=%u RemoteRole=%u RpcDeliveryType=%u ComponentId=%u RpcType=%u IsReliable=%s IsMarkedForRemoval=%s",
aznumeric_cast<uint32_t>(m_entityHandle.GetNetEntityId()),
aznumeric_cast<uint32_t>(GetBoundLocalNetworkRole()),
aznumeric_cast<uint32_t>(GetRemoteNetworkRole()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetComponentId()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcMessageType()),
entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false",
IsMarkedForRemoval() ? "true" : "false"
);
}
}
if (result == RpcValidationResult::DropRpc)
{
AZLOG
(
NET_Rpc,
"Dropping RPC EntityId=%u LocalRole=%u RemoteRole=%u RpcDeliveryType=%u ComponentId=%u RpcType=%u IsReliable=%s IsMarkedForRemoval=%s",
aznumeric_cast<uint32_t>(m_entityHandle.GetNetEntityId()),
aznumeric_cast<uint32_t>(GetBoundLocalNetworkRole()),
aznumeric_cast<uint32_t>(GetRemoteNetworkRole()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetComponentId()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcMessageType()),
entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false",
IsMarkedForRemoval() ? "true" : "false"
);
}
return result;
}
bool EntityReplicator::HandleRpcMessage(NetworkEntityRpcMessage& entityRpcMessage)
{
// Received rpc metrics
//ScopedTimer processTimer(Multiplayer::GetPacketHandlerMetricsInstance().LogReceivedRpc(entityRpcMessage.GetEntityComponentType(), entityRpcMessage.GetRpcMessageType(), entityRpcMessage.GetEstimatedSerializeSize()));
if (!m_netBindComponent)
{
AZLOG_WARN
(
"Dropping RPC since entity deleted EntityId=%u LocalRole=%u RemoteRole=%u RpcDeliveryType=%u ComponentId=%u RpcType=%u IsReliable=%s IsMarkedForRemoval=%s",
aznumeric_cast<uint32_t>(m_entityHandle.GetNetEntityId()),
aznumeric_cast<uint32_t>(GetBoundLocalNetworkRole()),
aznumeric_cast<uint32_t>(GetRemoteNetworkRole()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetComponentId()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcMessageType()),
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(GetRemoteNetworkRole(), entityRpcMessage);
case RpcValidationResult::DropRpc:
return true;
case RpcValidationResult::DropRpcAndDisconnect:
return false;
case RpcValidationResult::ForwardToClient:
{
ScopedForwardingMessage forwarding(*this);
m_netBindComponent->GetSendServerAuthorityToClientSimulationRpcEvent().Signal(entityRpcMessage);
return true;
}
case RpcValidationResult::ForwardToAutonomous:
{
ScopedForwardingMessage forwarding(*this);
m_netBindComponent->GetSendServerAuthorityToClientAutonomousRpcEvent().Signal(entityRpcMessage);
return true;
}
case RpcValidationResult::ForwardToAuthority:
{
ScopedForwardingMessage forwarding(*this);
m_netBindComponent->GetSendServerSimulationToServerAuthorityRpcEvent().Signal(entityRpcMessage);
return true;
}
default:
break;
}
AZ_Assert(false, "Unhandled ERpcValidationResult %d", result);
return false;
}
}
@@ -0,0 +1,146 @@
/*
* 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/Event.h>
#include <AzCore/EBus/ScheduledEvent.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/EntityBus.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/containers/ring_buffer.h>
#include <Source/Components/NetBindComponent.h>
#include <Source/NetworkEntity/NetworkEntityUpdateMessage.h>
namespace AzNetworking
{
class IConnection;
}
namespace Multiplayer
{
class EntityReplicationManager;
class NetworkEntityRpcMessage;
class NetBindComponent;
class PropertyPublisher;
class PropertySubscriber;
class EntityReplicator final
: public AZ::EntityBus::Handler
{
public:
EntityReplicator(EntityReplicationManager& replicationManager, AzNetworking::IConnection* connection, NetEntityRole remoteNetworkRole, const ConstNetworkEntityHandle& entityHandle);
virtual ~EntityReplicator();
NetEntityRole GetBoundLocalNetworkRole() const;
NetEntityRole GetRemoteNetworkRole() const;
ConstNetworkEntityHandle GetEntityHandle() const;
NetBindComponent* GetNetBindComponent();
void ActivateNetworkEntity();
const PrefabEntityId& GetPrefabEntityId() const;
bool IsPrefabEntityIdSet() const;
bool OwnsReplicatorLifetime() const;
bool RemoteManagerOwnsEntityLifetime() const;
// Interface for ReplicationManager to modify state of replication
void Initialize(const ConstNetworkEntityHandle& entityHandle);
void Reset(NetEntityRole remoteNetworkRole);
void MarkForRemoval();
bool IsMarkedForRemoval() const;
void SetPendingRemoval(AZ::TimeMs pendingRemovalTimeMs);
bool IsPendingRemoval() const;
void ClearPendingRemoval();
bool IsDeletionAcknowledged() const;
bool WasMigrated() const;
void SetWasMigrated(bool wasMigrated);
NetworkEntityUpdateMessage GenerateUpdatePacket();
AZ::TimeMs GetResendTimeoutTimeMs() const;
PropertyPublisher* GetPropertyPublisher();
const PropertyPublisher* GetPropertyPublisher() const;
PropertySubscriber* GetPropertySubscriber();
// Handlers for messages
bool HandleRpcMessage(NetworkEntityRpcMessage& entityRpcMessage);
//! AZ::EntityBus overrides
//! @{
void OnEntityActivated(const AZ::EntityId&) override;
void OnEntityDestroyed(const AZ::EntityId&) override;
//! @}
private:
enum class RpcValidationResult
{
HandleRpc, // Handle Rpc message
DropRpc, // Do not handle Rpc
DropRpcAndDisconnect, // Do not handle the Rpc, it is disallowed from this endpoint we should disconnect the connection
ForwardToClient, // Forward this message to the Client
ForwardToAutonomous, // Forward this message to the Autonomous
ForwardToAuthority, // Forward this message to the Authority
};
RpcValidationResult ValidateRpcMessage(const NetworkEntityRpcMessage& entityRpcMessage) const;
// Internal state tracking
bool CanSendUpdates();
void SetPrefabEntityId(const PrefabEntityId& prefabEntityId); // cache assetId so authority doesn't need to keep sending it
// Event processing
void OnSendRpcEvent(NetworkEntityRpcMessage& message);
void OnForwardRpcEvent(NetworkEntityRpcMessage& message);
void OnEntityDirtiedEvent();
void OnEntityRemovedEvent();
void OnProxyRemovalTimedEvent();
void ActivateNetworkEntityInternal();
void AttachRPCHandlers();
void DeferRpcMessage(NetworkEntityRpcMessage& message);
AZ_DISABLE_COPY_MOVE(EntityReplicator);
// Events
RpcSendEvent::Handler m_onSendRpcHandler;
RpcSendEvent::Handler m_onForwardRpcHandler;
RpcSendEvent::Handler m_onSendClientAutonomousRpcHandler;
RpcSendEvent::Handler m_onForwardClientAutonomousRpcHandler;
EntityDirtiedEvent::Handler m_onEntityDirtiedHandler;
EntityStopEvent::Handler m_onEntityStopHandler;
AZ::ScheduledEvent m_proxyRemovalEvent;
ConstNetworkEntityHandle m_entityHandle;
PrefabEntityId m_prefabEntityId;
AZStd::unique_ptr<PropertyPublisher> m_propertyPublisher;
AZStd::unique_ptr<PropertySubscriber> m_propertySubscriber;
NetBindComponent* m_netBindComponent = nullptr;
EntityReplicationManager& m_replicationManager;
AzNetworking::IConnection* m_connection;
NetEntityRole m_boundLocalNetworkRole;
NetEntityRole m_remoteNetworkRole;
bool m_wasMigrated = false;
bool m_isForwardingRpc = false;
bool m_prefabEntityIdSet = false;
};
}
#include <Source/NetworkEntity/EntityReplication/EntityReplicator.inl>
@@ -0,0 +1,72 @@
/*
* 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
namespace Multiplayer
{
inline NetEntityRole EntityReplicator::GetBoundLocalNetworkRole() const
{
return m_boundLocalNetworkRole;
}
inline NetEntityRole EntityReplicator::GetRemoteNetworkRole() const
{
return m_remoteNetworkRole;
}
inline ConstNetworkEntityHandle EntityReplicator::GetEntityHandle() const
{
return m_entityHandle;
}
inline NetBindComponent* EntityReplicator::GetNetBindComponent()
{
return m_netBindComponent;
}
inline const PrefabEntityId& EntityReplicator::GetPrefabEntityId() const
{
AZ_Assert(IsPrefabEntityIdSet(), "PrefabEntityId not set for Entity");
return m_prefabEntityId;
}
inline bool EntityReplicator::IsPrefabEntityIdSet() const
{
return m_prefabEntityIdSet;
}
inline bool EntityReplicator::WasMigrated() const
{
return m_wasMigrated;
}
inline void EntityReplicator::SetWasMigrated(bool wasMigrated)
{
m_wasMigrated = wasMigrated;
}
inline PropertyPublisher* EntityReplicator::GetPropertyPublisher()
{
return m_propertyPublisher.get();
}
inline const PropertyPublisher* EntityReplicator::GetPropertyPublisher() const
{
return m_propertyPublisher.get();
}
inline PropertySubscriber* EntityReplicator::GetPropertySubscriber()
{
return m_propertySubscriber.get();
}
}
@@ -0,0 +1,344 @@
/*
* 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/NetworkEntity/EntityReplication/PropertyPublisher.h>
#include <AzNetworking/ConnectionLayer/IConnection.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
namespace Multiplayer
{
AZ_CVAR(uint32_t, net_EntityReplicatorRecordsMax, 45, nullptr, AZ::ConsoleFunctorFlags::Null, "Number of allowed outstanding entity records");
PropertyPublisher::PropertyPublisher(NetEntityRole remoteNetworkRole, OwnsLifetime ownsLifetime, NetBindComponent* netBindComponent, AzNetworking::IConnection& connection)
: m_ownsLifetime(ownsLifetime)
, m_netBindComponent(netBindComponent)
, m_connection(connection)
, m_pendingRecord(remoteNetworkRole)
, m_sentRecords(net_EntityReplicatorRecordsMax)
{
AZ_Assert(m_netBindComponent, "NetBindComponent is nullptr");
m_pendingRecord.SetNetworkRole(remoteNetworkRole);
}
bool PropertyPublisher::IsDeleting() const
{
return (PropertyPublisher::EntityReplicatorState::Deleting == m_replicatorState);
}
bool PropertyPublisher::IsDeleted() const
{
bool result = false;
for (AzNetworking::PacketId deletePacket : m_deletePacketIds)
{
if (m_connection.WasPacketAcked(deletePacket))
{
result = true;
break;
}
}
return result;
}
void PropertyPublisher::SetDeleting()
{
m_netBindComponent = nullptr;
m_replicatorState = EntityReplicatorState::Deleting;
}
bool PropertyPublisher::IsRemoteReplicatorEstablished() const
{
return m_remoteReplicatorEstablished;
}
PropertyPublisher::EntityReplicatorState PropertyPublisher::GetReplicatorState() const
{
return m_replicatorState;
}
void PropertyPublisher::SetRebasing()
{
AZ_Assert(m_pendingRecord.GetNetworkRole() == NetEntityRole::ClientAutonomous, "Expected to be rebasing on a ClientAutonomous entity");
m_replicatorState = EntityReplicatorState::Rebasing;
}
void PropertyPublisher::GenerateRecord()
{
AZ_Assert(m_netBindComponent, "NetBindComponent is nullptr");
m_netBindComponent->FillReplicationRecord(m_pendingRecord);
}
bool PropertyPublisher::HasUpdateEntityRecord()
{
auto mostRecentAckedIter = m_sentRecords.end();
for (auto iter = m_sentRecords.begin(); iter != m_sentRecords.end(); ++iter)
{
if (m_connection.WasPacketAcked(iter->m_sentPacketId))
{
// This has been acked, so everything after to this and this replication record are not useful
mostRecentAckedIter = iter;
m_remoteReplicatorEstablished = true;
break;
}
}
// delete everything prior to this
m_sentRecords.erase(mostRecentAckedIter, m_sentRecords.end());
// Nothing to send
if (!m_pendingRecord.HasChanges() && m_sentRecords.empty() && m_remoteReplicatorEstablished)
{
return false;
}
return true;
}
bool PropertyPublisher::PrepareAddEntityRecord()
{
m_sentRecords.clear();
m_netBindComponent->FillTotalReplicationRecord(m_pendingRecord);
m_sentRecords.push_front(m_pendingRecord);
return true;
}
bool PropertyPublisher::PrepareRebaseEntityRecord()
{
AZ_Assert(m_netBindComponent, "NetBindComponent is nullptr");
// This is basically an Add record, but we don't want to send back predictable values
m_sentRecords.clear();
m_netBindComponent->FillTotalReplicationRecord(m_pendingRecord);
// Don't send predictable properties back to the ClientAutonomous unless we correct them
if (m_pendingRecord.GetNetworkRole() == NetEntityRole::ClientAutonomous)
{
m_pendingRecord.Subtract(m_netBindComponent->GetPredictableRecord());
}
m_sentRecords.push_front(m_pendingRecord);
return true;
}
bool PropertyPublisher::PrepareUpdateEntityRecord()
{
// If we reach the maximum outstanding records, reset the replication state
if (m_sentRecords.size() >= net_EntityReplicatorRecordsMax)
{
return PrepareAddEntityRecord();
}
// We need to clear out old records, and build up a list of everything that has changed since the last acked packet
m_sentRecords.push_front(m_pendingRecord);
auto iter = m_sentRecords.begin();
++iter; // consider everything after the record we are going to send
for (; iter != m_sentRecords.end(); ++iter)
{
// Sequence wasn't acked, so we need to send these bits again
m_pendingRecord.Append(*iter);
}
// Don't send predictable properties back to the ClientAutonomous unless we correct them
if (m_pendingRecord.GetNetworkRole() == NetEntityRole::ClientAutonomous)
{
m_pendingRecord.Subtract(m_netBindComponent->GetPredictableRecord());
}
return true;
}
bool PropertyPublisher::PrepareDeleteEntityRecord()
{
m_sentRecords.clear();
m_pendingRecord.Clear();
return !IsDeleted();
}
bool PropertyPublisher::SerializeUpdateEntityRecord(AzNetworking::ISerializer &serializer)
{
AZ_Assert(m_netBindComponent, "NetBindComponent is nullptr");
m_pendingRecord.ResetConsumedBits();
m_pendingRecord.Serialize(serializer);
m_netBindComponent->SerializeStateDeltaMessage(m_pendingRecord, serializer, ComponentSerializationType::Properties);
return serializer.IsValid();
}
bool PropertyPublisher::SerializeDeleteEntityRecord(AzNetworking::ISerializer &serializer)
{
return serializer.IsValid();
}
void PropertyPublisher::FinalizeUpdateEntityRecord(AzNetworking::PacketId packetId)
{
// Fill in the packet id for the last sent update
ReplicationRecord& lastSentRecord = m_sentRecords.front();
AZ_Assert(lastSentRecord.m_sentPacketId == AzNetworking::InvalidPacketId, "Assumed we pushed on a packet in UpdateSerialization");
lastSentRecord.m_sentPacketId = packetId;
AZ_Assert(lastSentRecord.m_sentPacketId != AzNetworking::InvalidPacketId, "Got a bad packet id");
if (lastSentRecord.m_sentPacketId == AzNetworking::InvalidPacketId)
{
// The packet failed to be generated, pop off the failed sent record
m_sentRecords.pop_front();
return;
}
m_pendingRecord.Clear();
}
void PropertyPublisher::FinalizeDeleteEntityRecord(AzNetworking::PacketId packetId)
{
// If we have more than our max records, just clear it and restart tracking again
if (m_deletePacketIds.size() >= net_EntityReplicatorRecordsMax)
{
m_deletePacketIds.clear();
}
m_deletePacketIds.push_back(packetId);
}
bool PropertyPublisher::RequiresSerialization()
{
// Send our entity replication update
AZ_Assert(m_serializationPhase == PropertyPublisher::EntityReplicatorSerializationPhase::Ready, "Unexpected serialization phase");
switch (m_replicatorState)
{
case PropertyPublisher::EntityReplicatorState::Invalid:
AZ_Assert(false, "EntityReplicator: Initialize() was not called on this entity replicator");
return false;
case PropertyPublisher::EntityReplicatorState::Creating:
case PropertyPublisher::EntityReplicatorState::Rebasing:
return true;
case PropertyPublisher::EntityReplicatorState::Updating:
return HasUpdateEntityRecord();
case PropertyPublisher::EntityReplicatorState::Deleting:
if (m_ownsLifetime == PropertyPublisher::OwnsLifetime::True)
{
return !IsDeleted();
}
return false;
default:
AZ_Assert(false, "EntityReplicator: Unexpected state");
return false;
}
}
bool PropertyPublisher::PrepareSerialization()
{
// Send our entity replication update
AZ_Assert(m_serializationPhase == PropertyPublisher::EntityReplicatorSerializationPhase::Ready, "Unexpected serialization phase");
bool needsUpdate(false);
switch (m_replicatorState)
{
case PropertyPublisher::EntityReplicatorState::Invalid:
AZ_Assert(false, "EntityReplicator: Initialize() was not called on this entity replicator");
break;
case PropertyPublisher::EntityReplicatorState::Creating:
if (m_ownsLifetime == PropertyPublisher::OwnsLifetime::True)
{
needsUpdate = PrepareAddEntityRecord();
}
m_replicatorState = PropertyPublisher::EntityReplicatorState::Updating;
break;
case PropertyPublisher::EntityReplicatorState::Rebasing:
AZ_Assert(m_ownsLifetime == PropertyPublisher::OwnsLifetime::True, "Expected to own our lifetime if we rebase");
needsUpdate = PrepareRebaseEntityRecord();
m_replicatorState = PropertyPublisher::EntityReplicatorState::Updating;
break;
case PropertyPublisher::EntityReplicatorState::Updating:
needsUpdate = PrepareUpdateEntityRecord();
break;
case PropertyPublisher::EntityReplicatorState::Deleting:
if (m_ownsLifetime == PropertyPublisher::OwnsLifetime::True)
{
needsUpdate = PrepareDeleteEntityRecord();
}
break;
default:
AZ_Assert(false, "EntityReplicator: Unexpected state");
break;
}
m_serializationPhase = needsUpdate ? PropertyPublisher::EntityReplicatorSerializationPhase::Prepared
: PropertyPublisher::EntityReplicatorSerializationPhase::Ready;
return needsUpdate;
}
bool PropertyPublisher::UpdateSerialization(AzNetworking::ISerializer& serializer)
{
bool success(true);
switch (m_replicatorState)
{
case PropertyPublisher::EntityReplicatorState::Invalid:
AZ_Assert(false, "EntityReplicator: Initialize() was not called on this entity replicator");
break;
case PropertyPublisher::EntityReplicatorState::Creating:
case PropertyPublisher::EntityReplicatorState::Updating:
{
AZ_Assert(m_serializationPhase == PropertyPublisher::EntityReplicatorSerializationPhase::Prepared, "Unexpected serialization phase");
success = SerializeUpdateEntityRecord(serializer);
}
break;
case PropertyPublisher::EntityReplicatorState::Deleting:
{
AZ_Assert(m_serializationPhase == PropertyPublisher::EntityReplicatorSerializationPhase::Prepared, "Unexpected serialization phase");
success = SerializeDeleteEntityRecord(serializer);
}
break;
default:
AZ_Assert(false, "EntityReplicator: Unexpected state");
break;
}
if (!success)
{
AZLOG_ERROR("EntityReplicator: Serialization failed");
}
AZ_Assert(success, "EntityReplicator: Serialization failed");
return success;
}
void PropertyPublisher::FinalizeSerialization(AzNetworking::PacketId sentId)
{
switch (m_replicatorState)
{
case PropertyPublisher::EntityReplicatorState::Invalid:
AZ_Assert(false, "EntityReplicator: Initialize() was not called on this entity replicator");
break;
case PropertyPublisher::EntityReplicatorState::Creating:
case PropertyPublisher::EntityReplicatorState::Updating:
{
AZ_Assert(m_serializationPhase == PropertyPublisher::EntityReplicatorSerializationPhase::Prepared, "Unexpected serialization phase");
FinalizeUpdateEntityRecord(sentId);
m_replicatorState = PropertyPublisher::EntityReplicatorState::Updating;
}
break;
case PropertyPublisher::EntityReplicatorState::Deleting:
{
AZ_Assert(m_serializationPhase == PropertyPublisher::EntityReplicatorSerializationPhase::Prepared, "Unexpected serialization phase");
FinalizeDeleteEntityRecord(sentId);
}
break;
default:
AZ_Assert(false, "EntityReplicator: Unexpected state");
break;
}
// Reset our state for the next frame
AZ_Assert(m_serializationPhase == PropertyPublisher::EntityReplicatorSerializationPhase::Prepared, "Unexpected serialization phase");
m_serializationPhase = PropertyPublisher::EntityReplicatorSerializationPhase::Ready;
}
}
@@ -0,0 +1,104 @@
/*
* 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 <Source/Components/NetBindComponent.h>
#include <AzCore/std/containers/ring_buffer.h>
namespace AzNetworking
{
class IConnection;
}
namespace Multiplayer
{
class PropertyPublisher
{
public:
enum class OwnsLifetime
{
True,
False,
};
PropertyPublisher(NetEntityRole remoteNetworkRole, OwnsLifetime ownsLifetime, NetBindComponent* netBindComponent, AzNetworking::IConnection& connection);
void SetRebasing();
bool IsDeleting() const;
bool IsDeleted() const;
void SetDeleting();
bool IsRemoteReplicatorEstablished() const;
void GenerateRecord();
//! Interface for ReplicationManager to manage serialization of entities
//! @{
bool RequiresSerialization();
bool PrepareSerialization();
bool UpdateSerialization(AzNetworking::ISerializer& serializer);
void FinalizeSerialization(AzNetworking::PacketId sentId);
//! @}
private:
enum class EntityReplicatorState
{
Invalid, // Invalid state - do not use
Creating, // Create an initial replication record, transitions to updating after that first packet
Rebasing, // Create an initial replication record, without predictable values, transitions to updating after that first packet - used for client migrations
Updating, // Create delta update packets based off what the remote endpoint has received
Deleting, // Awaiting confirmation that the delete packet was received
};
enum class EntityReplicatorSerializationPhase
{
Ready,
Prepared,
};
EntityReplicatorState GetReplicatorState() const;
//! Check if we have data to send
bool HasUpdateEntityRecord();
//! Phase 1, setup of the record
bool PrepareAddEntityRecord();
bool PrepareRebaseEntityRecord();
bool PrepareUpdateEntityRecord();
bool PrepareDeleteEntityRecord();
//! Phase 2, serialize the record
//! No add, they share the update path
bool SerializeUpdateEntityRecord(AzNetworking::ISerializer& serializer);
bool SerializeDeleteEntityRecord(AzNetworking::ISerializer& serializer);
//! Phase 3, finalize with the packet id
void FinalizeUpdateEntityRecord(AzNetworking::PacketId packetId);
void FinalizeDeleteEntityRecord(AzNetworking::PacketId packetId);
EntityReplicatorState m_replicatorState = EntityReplicatorState::Creating;
EntityReplicatorSerializationPhase m_serializationPhase = EntityReplicatorSerializationPhase::Ready;
OwnsLifetime m_ownsLifetime = OwnsLifetime::False;
AzNetworking::IConnection& m_connection;
NetBindComponent* m_netBindComponent = nullptr;
//! Aggregate changes that we need to serialize (m_currentRecord + outstanding m_sentRecords)
ReplicationRecord m_pendingRecord;
//! List of sent records (history of m_currentRecord)
AZStd::ring_buffer<ReplicationRecord> m_sentRecords;
AZStd::vector<AzNetworking::PacketId> m_deletePacketIds;
bool m_remoteReplicatorEstablished = false;
};
}
@@ -0,0 +1,57 @@
/*
* 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/NetworkEntity/EntityReplication/PropertySubscriber.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Source/Components/NetBindComponent.h>
namespace Multiplayer
{
PropertySubscriber::PropertySubscriber(EntityReplicationManager& replicationManager, NetBindComponent* netBindComponent)
: m_replicationManager(replicationManager)
, m_netBindComponent(netBindComponent)
{
;
}
AzNetworking::PacketId PropertySubscriber::GetLastReceivedPacketId() const
{
return m_lastReceivedPacketId;
}
bool PropertySubscriber::IsDeleting() const
{
return m_markForRemovalTimeMs > AZ::TimeMs{ 0 };
}
bool PropertySubscriber::IsDeleted() const
{
return m_markForRemovalTimeMs < m_replicationManager.GetFrameTimeMs();
}
void PropertySubscriber::SetDeleting()
{
m_markForRemovalTimeMs = AZ::TimeMs(m_replicationManager.GetFrameTimeMs() + m_replicationManager.GetResendTimeoutTimeMs());
}
bool PropertySubscriber::IsPacketIdValid(AzNetworking::PacketId packetId) const
{
return m_lastReceivedPacketId == AzNetworking::InvalidPacketId || packetId > m_lastReceivedPacketId;
}
bool PropertySubscriber::HandlePropertyChangeMessage(AzNetworking::PacketId packetId, AzNetworking::ISerializer* serializer, bool notifyChanges)
{
AZ_Assert(IsPacketIdValid(packetId), "Packet expected to be valid");
m_lastReceivedPacketId = packetId;
return m_netBindComponent->HandlePropertyChangeMessage(*serializer, notifyChanges);
}
}
@@ -0,0 +1,50 @@
/*
* 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/Utilities/NetworkCommon.h>
namespace AzNetworking
{
class ISerializer;
}
namespace Multiplayer
{
class NetBindComponent;
class EntityReplicationManager;
class PropertySubscriber
{
public:
PropertySubscriber(EntityReplicationManager& replicationManager, NetBindComponent* netBindComponent);
bool IsDeleting() const;
bool IsDeleted() const;
void SetDeleting();
bool IsPacketIdValid(AzNetworking::PacketId packetId) const;
AzNetworking::PacketId GetLastReceivedPacketId() const;
bool HandlePropertyChangeMessage(AzNetworking::PacketId packetId, AzNetworking::ISerializer* serializer, bool notifyChanges = true);
private:
EntityReplicationManager& m_replicationManager;
NetBindComponent* m_netBindComponent;
// The last packet to have been received about this entity
AzNetworking::PacketId m_lastReceivedPacketId = AzNetworking::InvalidPacketId;
AZ::TimeMs m_lastRecievedTimeMs = AZ::TimeMs{ 0 };
AZ::TimeMs m_markForRemovalTimeMs = AZ::TimeMs{ 0 };
};
}
@@ -0,0 +1,251 @@
/*
* 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/NetworkEntity/EntityReplication/ReplicationRecord.h>
namespace Multiplayer
{
ReplicationRecordStats::ReplicationRecordStats
(
uint32_t authorityToClientCount,
uint32_t authorityToServerCount,
uint32_t authorityToAutonomousCount,
uint32_t autonomousToAuthorityCount
)
: m_authorityToClientCount(authorityToClientCount)
, m_authorityToServerCount(authorityToServerCount)
, m_authorityToAutonomousCount(authorityToAutonomousCount)
, m_autonomousToAuthorityCount(autonomousToAuthorityCount)
{
;
}
bool ReplicationRecordStats::operator ==(const ReplicationRecordStats& rhs) const
{
return (m_authorityToClientCount == rhs.m_authorityToClientCount)
&& (m_authorityToServerCount == rhs.m_authorityToServerCount)
&& (m_authorityToAutonomousCount == rhs.m_authorityToAutonomousCount)
&& (m_autonomousToAuthorityCount == rhs.m_autonomousToAuthorityCount);
}
ReplicationRecordStats ReplicationRecordStats::operator-(const ReplicationRecordStats& rhs) const
{
return ReplicationRecordStats
{
(m_authorityToClientCount - rhs.m_authorityToClientCount),
(m_authorityToServerCount - rhs.m_authorityToServerCount),
(m_authorityToAutonomousCount - rhs.m_authorityToAutonomousCount),
(m_autonomousToAuthorityCount - rhs.m_autonomousToAuthorityCount)
};
}
ReplicationRecord::ReplicationRecord(NetEntityRole netEntityRole)
: m_netEntityRole(netEntityRole)
{
;
}
void ReplicationRecord::SetNetworkRole(NetEntityRole netEntityRole)
{
m_netEntityRole = netEntityRole;
}
NetEntityRole ReplicationRecord::GetNetworkRole() const
{
return m_netEntityRole;
}
bool ReplicationRecord::AreAllBitsConsumed() const
{
bool ret = true;
ret &= m_authorityToClientConsumedBits == m_authorityToClientRecord.GetSize();
ret &= m_authorityToServerConsumedBits == m_authorityToServerRecord.GetSize();
ret &= m_authorityToAutonomousConsumedBits == m_authorityToAutonomousRecord.GetSize();
ret &= m_autonomousToAuthorityConsumedBits == m_autonomousToAuthorityRecord.GetSize();
return ret;
}
void ReplicationRecord::ResetConsumedBits()
{
m_authorityToClientConsumedBits = 0;
m_authorityToServerConsumedBits = 0;
m_authorityToAutonomousConsumedBits = 0;
m_autonomousToAuthorityConsumedBits = 0;
}
void ReplicationRecord::Clear()
{
ResetConsumedBits();
uint32_t recordSize = m_authorityToClientRecord.GetSize();
m_authorityToClientRecord.Clear();
m_authorityToClientRecord.Resize(recordSize);
recordSize = m_authorityToServerRecord.GetSize();
m_authorityToServerRecord.Clear();
m_authorityToServerRecord.Resize(recordSize);
recordSize = m_authorityToAutonomousRecord.GetSize();
m_authorityToAutonomousRecord.Clear();
m_authorityToAutonomousRecord.Resize(recordSize);
recordSize = m_autonomousToAuthorityRecord.GetSize();
m_autonomousToAuthorityRecord.Clear();
m_autonomousToAuthorityRecord.Resize(recordSize);
}
void ReplicationRecord::Append(const ReplicationRecord &rhs)
{
m_authorityToClientRecord |= rhs.m_authorityToClientRecord;
m_authorityToServerRecord |= rhs.m_authorityToServerRecord;
m_authorityToAutonomousRecord |= rhs.m_authorityToAutonomousRecord;
m_autonomousToAuthorityRecord |= rhs.m_autonomousToAuthorityRecord;
}
void ReplicationRecord::Subtract(const ReplicationRecord &rhs)
{
m_authorityToClientRecord.Subtract(rhs.m_authorityToClientRecord);
m_authorityToServerRecord.Subtract(rhs.m_authorityToServerRecord);
m_authorityToAutonomousRecord.Subtract(rhs.m_authorityToAutonomousRecord);
m_autonomousToAuthorityRecord.Subtract(rhs.m_autonomousToAuthorityRecord);
}
bool ReplicationRecord::HasChanges() const
{
bool hasChanges(false);
if (ContainsAuthorityToClientBits())
{
hasChanges = hasChanges ? hasChanges : m_authorityToClientRecord.AnySet();
}
if (ContainsAuthorityToServerBits())
{
hasChanges = hasChanges ? hasChanges : m_authorityToServerRecord.AnySet();
}
if (ContainsAuthorityToAutonomousBits())
{
hasChanges = hasChanges ? hasChanges : m_authorityToAutonomousRecord.AnySet();
}
if (ContainsAutonomousToAuthorityBits())
{
hasChanges = hasChanges ? hasChanges : m_autonomousToAuthorityRecord.AnySet();
}
return hasChanges;
}
bool ReplicationRecord::Serialize(AzNetworking::ISerializer& a_Serializer)
{
if (ContainsAuthorityToClientBits())
{
a_Serializer.Serialize(m_authorityToClientRecord, "ServerToClientsRecord");
}
if (ContainsAuthorityToServerBits())
{
a_Serializer.Serialize(m_authorityToServerRecord, "ServerToServersRecord");
}
if (ContainsAuthorityToAutonomousBits())
{
a_Serializer.Serialize(m_authorityToAutonomousRecord, "ServerToClientAutonomousRecord");
}
if (ContainsAutonomousToAuthorityBits())
{
a_Serializer.Serialize(m_autonomousToAuthorityRecord, "ClientToServersRecord");
}
return a_Serializer.IsValid();
}
void ReplicationRecord::ConsumeAuthorityToClientBits(uint32_t consumedBits)
{
if (ContainsAuthorityToClientBits())
{
m_authorityToClientConsumedBits += consumedBits;
}
}
void ReplicationRecord::ConsumeAuthorityToServerBits(uint32_t consumedBits)
{
if (ContainsAuthorityToServerBits())
{
m_authorityToServerConsumedBits += consumedBits;
}
}
void ReplicationRecord::ConsumeAuthorityToAutonomousBits(uint32_t consumedBits)
{
if (ContainsAuthorityToAutonomousBits())
{
m_authorityToAutonomousConsumedBits += consumedBits;
}
}
void ReplicationRecord::ConsumeAutonomousToAuthorityBits(uint32_t consumedBits)
{
if (ContainsAutonomousToAuthorityBits())
{
m_autonomousToAuthorityConsumedBits += consumedBits;
}
}
bool ReplicationRecord::ContainsAuthorityToClientBits() const
{
return (m_netEntityRole != NetEntityRole::ServerAuthority)
|| (m_netEntityRole == NetEntityRole::InvalidRole);
}
bool ReplicationRecord::ContainsAuthorityToServerBits() const
{
return (m_netEntityRole == NetEntityRole::ServerSimulation)
|| (m_netEntityRole == NetEntityRole::InvalidRole);
}
bool ReplicationRecord::ContainsAuthorityToAutonomousBits() const
{
return (m_netEntityRole == NetEntityRole::ClientAutonomous || m_netEntityRole == NetEntityRole::ServerSimulation)
|| (m_netEntityRole == NetEntityRole::InvalidRole);
}
bool ReplicationRecord::ContainsAutonomousToAuthorityBits() const
{
return (m_netEntityRole == NetEntityRole::ServerAuthority)
|| (m_netEntityRole == NetEntityRole::InvalidRole);
}
uint32_t ReplicationRecord::GetRemainingAuthorityToClientBits() const
{
return m_authorityToClientConsumedBits < m_authorityToClientRecord.GetValidBitCount() ? m_authorityToClientRecord.GetValidBitCount() - m_authorityToClientConsumedBits : 0;
}
uint32_t ReplicationRecord::GetRemainingAuthorityToServerBits() const
{
return m_authorityToServerConsumedBits < m_authorityToServerRecord.GetValidBitCount() ? m_authorityToServerRecord.GetValidBitCount() - m_authorityToServerConsumedBits : 0;
}
uint32_t ReplicationRecord::GetRemainingAuthorityToAutonomousBits() const
{
return m_authorityToAutonomousConsumedBits < m_authorityToAutonomousRecord.GetValidBitCount() ? m_authorityToAutonomousRecord.GetValidBitCount() - m_authorityToAutonomousConsumedBits : 0;
}
uint32_t ReplicationRecord::GetRemainingAutonomousToAuthorityBits() const
{
return m_autonomousToAuthorityConsumedBits < m_autonomousToAuthorityRecord.GetValidBitCount() ? m_autonomousToAuthorityRecord.GetValidBitCount() - m_autonomousToAuthorityConsumedBits : 0;
}
ReplicationRecordStats ReplicationRecord::GetStats() const
{
return ReplicationRecordStats
{
m_authorityToClientConsumedBits,
m_authorityToServerConsumedBits,
m_authorityToAutonomousConsumedBits,
m_autonomousToAuthorityConsumedBits
};
}
}
@@ -0,0 +1,97 @@
/*
* 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/DataStructures/FixedSizeVectorBitset.h>
#include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/Utilities/NetworkCommon.h>
#include <Source/MultiplayerTypes.h>
namespace Multiplayer
{
struct ReplicationRecordStats
{
ReplicationRecordStats() = default;
ReplicationRecordStats
(
uint32_t authorityToClientCount,
uint32_t authorityToServerCount,
uint32_t authorityToAutonomousCount,
uint32_t autonomousToAuthorityCount
);
uint32_t m_authorityToClientCount = 0;
uint32_t m_authorityToServerCount = 0;
uint32_t m_authorityToAutonomousCount = 0;
uint32_t m_autonomousToAuthorityCount = 0;
bool operator ==(const ReplicationRecordStats& rhs) const;
ReplicationRecordStats operator-(const ReplicationRecordStats& rhs) const;
};
class ReplicationRecord
{
public:
static constexpr uint32_t MaxRecordBits = 2048;
ReplicationRecord() = default;
ReplicationRecord(NetEntityRole netEntityRole);
void SetNetworkRole(NetEntityRole netEntityRole);
NetEntityRole GetNetworkRole() const;
bool AreAllBitsConsumed() const;
void ResetConsumedBits();
void Clear();
void Append(const ReplicationRecord &rhs);
void Subtract(const ReplicationRecord &rhs);
bool HasChanges() const;
bool Serialize(AzNetworking::ISerializer& serializer);
void ConsumeAuthorityToClientBits(uint32_t consumedBits);
void ConsumeAuthorityToServerBits(uint32_t consumedBits);
void ConsumeAuthorityToAutonomousBits(uint32_t consumedBits);
void ConsumeAutonomousToAuthorityBits(uint32_t consumedBits);
bool ContainsAuthorityToClientBits() const;
bool ContainsAuthorityToServerBits() const;
bool ContainsAuthorityToAutonomousBits() const;
bool ContainsAutonomousToAuthorityBits() const;
uint32_t GetRemainingAuthorityToClientBits() const;
uint32_t GetRemainingAuthorityToServerBits() const;
uint32_t GetRemainingAuthorityToAutonomousBits() const;
uint32_t GetRemainingAutonomousToAuthorityBits() const;
ReplicationRecordStats GetStats() const;
using RecordBitset = AzNetworking::FixedSizeVectorBitset<MaxRecordBits>;
RecordBitset m_authorityToClientRecord;
RecordBitset m_authorityToServerRecord;
RecordBitset m_authorityToAutonomousRecord;
RecordBitset m_autonomousToAuthorityRecord;
uint32_t m_authorityToClientConsumedBits = 0;
uint32_t m_authorityToServerConsumedBits = 0;
uint32_t m_authorityToAutonomousConsumedBits = 0;
uint32_t m_autonomousToAuthorityConsumedBits = 0;
// Sequence number this ReplicationRecord was sent on
AzNetworking::PacketId m_sentPacketId = AzNetworking::InvalidPacketId;
NetEntityRole m_netEntityRole = NetEntityRole::InvalidRole;;
};
}
@@ -0,0 +1,41 @@
/*
* 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 <Source/NetworkEntity/INetworkEntityManager.h>
namespace Multiplayer
{
//! @class INetworkEntityDomain
//! @brief A class that determines if an entity should belong to a particular EntityManager.
class INetworkEntityDomain
{
public:
using EntitiesNotInDomain = AZStd::unordered_set<NetEntityId>;
virtual ~INetworkEntityDomain() = default;
//! Enable Entity Domain Exit Tracking for entities on the server.
//! @param ownedEntitySet
virtual void ActivateTracking(const INetworkEntityManager::OwnedEntitySet& ownedEntitySet) = 0;
//! Return the set of entities not in this domain.
//! @param outEntitiesNotInDomain
virtual void RetrieveEntitiesNotInDomain(EntitiesNotInDomain& outEntitiesNotInDomain) const = 0;
//! Returns whether or not an entity should be owned by an entity manager.
//! @param entityHandle the handle of the entity to check for inclusion in the domain
//! @return false if this entity should not belong to the entity manger, true if it could be owned by the entity manager
virtual bool IsInDomain(const ConstNetworkEntityHandle& entityHandle) const = 0;
};
}
@@ -0,0 +1,142 @@
/*
* 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 <Source/MultiplayerTypes.h>
#include <Source/NetworkEntity/NetworkEntityHandle.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/EBus/Event.h>
namespace Multiplayer
{
class NetworkEntityTracker;
class NetworkEntityAuthorityTracker;
class NetworkEntityRpcMessage;
using EntityExitDomainEvent = AZ::Event<const ConstNetworkEntityHandle&>;
using ControllersActivatedEvent = AZ::Event<const ConstNetworkEntityHandle&, EntityIsMigrating>;
using ControllersDeactivatedEvent = AZ::Event<const ConstNetworkEntityHandle&, EntityIsMigrating>;
//! @class INetworkEntityManager
//! @brief The interface for managing all networked entities.
class INetworkEntityManager
{
public:
AZ_RTTI(INetworkEntityManager, "{109759DE-9492-439C-A0B1-AE46E6FD029C}");
using OwnedEntitySet = AZStd::unordered_set<ConstNetworkEntityHandle>;
virtual ~INetworkEntityManager() = default;
//! Returns the NetworkEntityTracker for this INetworkEntityManager instance.
//! @return the NetworkEntityTracker for this INetworkEntityManager instance
virtual NetworkEntityTracker* GetNetworkEntityTracker() = 0;
//! Returns the NetworkEntityAuthorityTracker for this INetworkEntityManager instance.
//! @return the NetworkEntityAuthorityTracker for this INetworkEntityManager instance
virtual NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker() = 0;
//! Returns the HostId for this INetworkEntityManager instance.
//! @return the HostId for this INetworkEntityManager instance
virtual HostId GetHostId() const = 0;
// TODO: Spawn methods for entities within slices/prefabs/levels
//! Returns an ConstEntityPtr for the provided entityId.
//! @param netEntityId the netEntityId to get an ConstEntityPtr for
//! @return the requested ConstEntityPtr
virtual ConstNetworkEntityHandle GetEntity(NetEntityId netEntityId) const = 0;
//! Returns the total number of entities tracked by this INetworkEntityManager instance.
//! @return the total number of entities tracked by this INetworkEntityManager instance
virtual uint32_t GetEntityCount() const = 0;
//! Adds the provided entity to the internal entity map identified by the provided netEntityId.
//! @param netEntityId the identifier to use for the added entity
//! @param entity the entity to add to the internal entity map
//! @return a NetworkEntityHandle for the newly added entity
virtual NetworkEntityHandle AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity) = 0;
//! Marks the specified entity for removal and deletion.
//! @param entityHandle the entity to remove and delete
virtual void MarkForRemoval(const ConstNetworkEntityHandle& entityHandle) = 0;
//! Returns true if the indicated entity is marked for removal.
//! @param entityHandle the entity to test if marked for removal
//! @return boolean true if the specified entity is marked for removal, false otherwise
virtual bool IsMarkedForRemoval(const ConstNetworkEntityHandle& entityHandle) const = 0;
//! Unmarks the specified entity so it will no longer be removed and deleted.
//! @param entityHandle the entity to unmark for removal and deletion
virtual void ClearEntityFromRemovalList(const ConstNetworkEntityHandle& entityHandle) = 0;
//! Clears out and deletes all entities registered with the entity manager.
virtual void ClearAllEntities() = 0;
//! Adds an event handler to be invoked when we notify which entities have been marked dirty.
//! @param entityMarkedDirtyHandle event handler for the dirtied entity
virtual void AddEntityMarkedDirtyHandler(AZ::Event<>::Handler& entityMarkedDirtyHandle) = 0;
//! Adds an event handler to be invoked when we notify entities to send their change notifications.
//! @param entityNotifyChangesHandle event handler for the dirtied entity
virtual void AddEntityNotifyChangesHandler(AZ::Event<>::Handler& entityNotifyChangesHandle) = 0;
//! Adds an event handler to be invoked when we notify entities to send their change notifications.
//! @param entityNotifyChangesHandle event handler for the dirtied entity
virtual void AddEntityExitDomainHandler(EntityExitDomainEvent::Handler& entityExitDomainHandler) = 0;
//! Adds an event handler to be invoked when an entities controllers have activated
//! @param controllersActivatedHandler event handler for the entity
virtual void AddControllersActivatedHandler(ControllersActivatedEvent::Handler& controllersActivatedHandler) = 0;
//! Adds an event handler to be invoked when an entities controllers have been deactivated
//! @param controllersDeactivatedHandler event handler for the entity
virtual void AddControllersDeactivatedHandler(ControllersDeactivatedEvent::Handler& controllersDeactivatedHandler) = 0;
//! Notifies entities that they should process their dirty state.
virtual void NotifyEntitiesDirtied() = 0;
//! Notifies entities that they should process change notifications.
virtual void NotifyEntitiesChanged() = 0;
//! Notifies that an entities controllers have activated.
//! @param entityHandle handle to the entity whose controllers have activated
//! @param entityIsMigrating true if the entity is activating after a migration
virtual void NotifyControllersActivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) = 0;
//! Notifies that an entities controllers have been deactivated.
//! @param entityHandle handle to the entity whose controllers have been deactivated
//! @param entityIsMigrating true if the entity is deactivating due to a migration
virtual void NotifyControllersDeactivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) = 0;
//! Handle a local rpc message.
//! @param entityRpcMessage the local rpc message to handle
virtual void HandleLocalRpcMessage(NetworkEntityRpcMessage& message) = 0;
};
// Convenience helpers
inline INetworkEntityManager* GetNetworkEntityManager()
{
return AZ::Interface<INetworkEntityManager>::Get();
}
inline NetworkEntityTracker* GetNetworkEntityTracker()
{
return GetNetworkEntityManager()->GetNetworkEntityTracker();
}
inline NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker()
{
return GetNetworkEntityManager()->GetNetworkEntityAuthorityTracker();
}
}
@@ -0,0 +1,222 @@
/*
* 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/NetworkEntity/NetworkEntityAuthorityTracker.h>
#include <Source/NetworkEntity/INetworkEntityManager.h>
#include <Source/Components/NetBindComponent.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
#include <AzNetworking/Utilities/NetworkCommon.h>
#include <AzNetworking/Serialization/NetworkOutputSerializer.h>
namespace Multiplayer
{
AZ_CVAR(AZ::TimeMs, net_EntityMigrationTimeoutMs, AZ::TimeMs{ 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time to wait for a new authority to attach to an entity before we delete the entity");
NetworkEntityAuthorityTracker::NetworkEntityAuthorityTracker(INetworkEntityManager& networkEntityManager)
: m_networkEntityManager(networkEntityManager)
{
;
}
bool NetworkEntityAuthorityTracker::AddEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, HostId newOwner)
{
bool ret = false;
auto timeoutData = m_timeoutDataMap.find(entityHandle.GetNetEntityId());
if (timeoutData != m_timeoutDataMap.end())
{
AZLOG
(
NET_AuthTracker,
"AuthTracker: Removing timeout for networkEntityId %u from %u, new owner is %u",
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()),
aznumeric_cast<uint32_t>(timeoutData->second.m_previousOwner),
aznumeric_cast<uint32_t>(newOwner)
);
m_timeoutDataMap.erase(timeoutData);
ret = true;
}
auto iter = m_entityAuthorityMap.find(entityHandle.GetNetEntityId());
if (iter != m_entityAuthorityMap.end())
{
AZLOG
(
NET_AuthTracker,
"AuthTracker: Assigning networkEntityId %u from %u to %u",
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()),
aznumeric_cast<uint32_t>(iter->second.back()),
aznumeric_cast<uint32_t>(newOwner)
);
}
else
{
AZLOG
(
NET_AuthTracker,
"AuthTracker: Assigning networkEntityId %u to %u",
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()),
aznumeric_cast<uint32_t>(newOwner)
);
}
m_entityAuthorityMap[entityHandle.GetNetEntityId()].push_back(newOwner);
return ret;
}
void NetworkEntityAuthorityTracker::RemoveEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, HostId previousOwner)
{
auto mapIter = m_entityAuthorityMap.find(entityHandle.GetNetEntityId());
if (mapIter != m_entityAuthorityMap.end())
{
auto& authorityStack = mapIter->second;
for (auto stackIter = authorityStack.begin(); stackIter != authorityStack.end();)
{
if (*stackIter == previousOwner)
{
stackIter = authorityStack.erase(stackIter);
}
else
{
++stackIter;
}
}
AZLOG(NET_AuthTracker, "AuthTracker: Removing networkEntityId %u from %u", aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()), aznumeric_cast<uint32_t>(previousOwner));
if (auto localEnt = entityHandle.GetEntity())
{
if (authorityStack.empty())
{
m_entityAuthorityMap.erase(entityHandle.GetNetEntityId());
NetEntityRole networkRole = NetEntityRole::InvalidRole;
NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
if (netBindComponent != nullptr)
{
networkRole = netBindComponent->GetNetEntityRole();
}
if (networkRole != NetEntityRole::ClientAutonomous)
{
AZ_Assert
(
(m_timeoutDataMap.find(entityHandle.GetNetEntityId()) == m_timeoutDataMap.end()) ||
(m_timeoutDataMap[entityHandle.GetNetEntityId()].m_previousOwner == previousOwner),
"Trying to add something twice to the timeout map, this is unexpected"
);
m_timeoutQueue.RegisterItem(aznumeric_cast<uint64_t>(entityHandle.GetNetEntityId()), net_EntityMigrationTimeoutMs);
TimeoutData& timeoutData = m_timeoutDataMap[entityHandle.GetNetEntityId()];
timeoutData.m_entityHandle = entityHandle;
timeoutData.m_previousOwner = previousOwner;
}
else
{
AZLOG(NET_AuthTracker, "AuthTracker: Skipping timeout for ClientAutonomous networkEntityId %u", aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()));
}
}
}
}
else
{
AZLOG(NET_AuthTracker, "AuthTracker: Remove authority called on networkEntityId that was never added %u", aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()));
AZ_Assert(false, "AuthTracker: Remove authority called on entity that was never added");
}
}
HostId NetworkEntityAuthorityTracker::GetEntityAuthorityManager(ConstNetworkEntityHandle entityHandle) const
{
HostId hostId = GetEntityAuthorityManagerInternal(entityHandle);
AZ_Assert(hostId != InvalidHostId, "Unable to determine manager for entity");
return hostId;
}
bool NetworkEntityAuthorityTracker::DoesEntityHaveOwner(ConstNetworkEntityHandle entityHandle) const
{
return InvalidHostId != GetEntityAuthorityManagerInternal(entityHandle);
}
HostId NetworkEntityAuthorityTracker::GetEntityAuthorityManagerInternal(ConstNetworkEntityHandle entityHandle) const
{
if (auto localEnt = entityHandle.GetEntity())
{
NetEntityRole networkRole = NetEntityRole::InvalidRole;
NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
if (netBindComponent != nullptr)
{
networkRole = netBindComponent->GetNetEntityRole();
}
if (networkRole == NetEntityRole::ServerAuthority)
{
return m_networkEntityManager.GetHostId();
}
else
{
auto iter = m_entityAuthorityMap.find(entityHandle.GetNetEntityId());
if (iter != m_entityAuthorityMap.end())
{
if (!iter->second.empty())
{
return iter->second.back();
}
}
}
}
return InvalidHostId;
}
NetworkEntityAuthorityTracker::TimeoutData::TimeoutData(ConstNetworkEntityHandle entityHandle, HostId previousOwner)
: m_entityHandle(entityHandle)
, m_previousOwner(previousOwner)
{
;
}
NetworkEntityAuthorityTracker::NetworkEntityTimeoutFunctor::NetworkEntityTimeoutFunctor
(
NetworkEntityAuthorityTracker& networkEntityAuthorityTracker,
INetworkEntityManager& networkEntityManager
)
: m_networkEntityAuthorityTracker(networkEntityAuthorityTracker)
, m_networkEntityManager(networkEntityManager)
{
;
}
AzNetworking::TimeoutResult NetworkEntityAuthorityTracker::NetworkEntityTimeoutFunctor::HandleTimeout(AzNetworking::TimeoutQueue::TimeoutItem& item)
{
const NetEntityId netEntityId = aznumeric_cast<NetEntityId>(item.m_userData);
auto timeoutData = m_networkEntityAuthorityTracker.m_timeoutDataMap.find(netEntityId);
if (timeoutData != m_networkEntityAuthorityTracker.m_timeoutDataMap.end())
{
m_networkEntityAuthorityTracker.m_timeoutDataMap.erase(timeoutData);
ConstNetworkEntityHandle entityHandle = m_networkEntityManager.GetEntity(netEntityId);
if (auto entity = entityHandle.GetEntity())
{
NetEntityRole networkRole = NetEntityRole::InvalidRole;
NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
if (netBindComponent != nullptr)
{
networkRole = netBindComponent->GetNetEntityRole();
}
if (networkRole != NetEntityRole::ServerAuthority)
{
AZLOG_ERROR
(
"Timed out entity id %u during migration previous owner %u, removing it",
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()),
aznumeric_cast<uint32_t>(timeoutData->second.m_previousOwner)
);
m_networkEntityManager.MarkForRemoval(entityHandle);
}
}
}
return AzNetworking::TimeoutResult::Delete;
}
}
@@ -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/EBus/Event.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzNetworking/DataStructures/TimeoutQueue.h>
#include <Source/NetworkEntity/NetworkEntityTracker.h>
namespace Multiplayer
{
class INetworkEntityManager;
class NetworkEntityAuthorityTracker
{
public:
NetworkEntityAuthorityTracker(INetworkEntityManager& networkEntityManager);
bool DoesEntityHaveOwner(ConstNetworkEntityHandle entityHandle) const;
bool AddEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, HostId newOwner);
void RemoveEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, HostId previousOwner);
HostId GetEntityAuthorityManager(ConstNetworkEntityHandle entityHandle) const;
private:
HostId GetEntityAuthorityManagerInternal(ConstNetworkEntityHandle entityHandle) const;
NetworkEntityAuthorityTracker& operator= (const NetworkEntityAuthorityTracker&) = delete;
struct TimeoutData final
{
TimeoutData() = default;
TimeoutData(ConstNetworkEntityHandle entityHandle, HostId previousOwner);
ConstNetworkEntityHandle m_entityHandle;
HostId m_previousOwner = InvalidHostId;
};
struct NetworkEntityTimeoutFunctor final
: public AzNetworking::ITimeoutHandler
{
NetworkEntityTimeoutFunctor(NetworkEntityAuthorityTracker& networkEntityAuthorityTracker, INetworkEntityManager& m_networkEntityManager);
AzNetworking::TimeoutResult HandleTimeout(AzNetworking::TimeoutQueue::TimeoutItem& item) override;
private:
AZ_DISABLE_COPY_MOVE(NetworkEntityTimeoutFunctor);
NetworkEntityAuthorityTracker& m_networkEntityAuthorityTracker;
INetworkEntityManager& m_networkEntityManager;
};
using TimeoutDataMap = AZStd::unordered_map<NetEntityId, TimeoutData>;
using EntityAuthorityMap = AZStd::unordered_map<NetEntityId, AZStd::vector<HostId>>;
TimeoutDataMap m_timeoutDataMap;
EntityAuthorityMap m_entityAuthorityMap;
INetworkEntityManager& m_networkEntityManager;
AzNetworking::TimeoutQueue m_timeoutQueue;
};
}
@@ -0,0 +1,182 @@
/*
* 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/NetworkEntity/NetworkEntityHandle.h>
#include <Source/NetworkEntity/NetworkEntityTracker.h>
#include <Source/Components/NetBindComponent.h>
#include <Source/Components/MultiplayerController.h>
#include <Source/Components/MultiplayerComponent.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
namespace Multiplayer
{
ConstNetworkEntityHandle::ConstNetworkEntityHandle(AZ::Entity* entity, const NetworkEntityTracker* networkEntityTracker)
: m_entity(entity)
, m_networkEntityTracker(networkEntityTracker)
{
if (m_networkEntityTracker)
{
m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity);
}
if (entity)
{
AZ_Assert(networkEntityTracker, "NetworkEntityTracker is not valid");
NetBindComponent* netBindComponent = m_entity->template FindComponent<NetBindComponent>();
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
m_netBindComponent = netBindComponent;
m_netEntityId = netBindComponent->GetNetEntityId();
}
}
ConstNetworkEntityHandle::ConstNetworkEntityHandle(AZ::Entity* entity, NetEntityId netEntityId, const NetworkEntityTracker* networkEntityTracker)
: m_entity(entity)
, m_netEntityId(netEntityId)
, m_networkEntityTracker(networkEntityTracker)
{
if (m_networkEntityTracker)
{
m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity);
}
}
ConstNetworkEntityHandle::ConstNetworkEntityHandle(NetBindComponent* netBindComponent, const NetworkEntityTracker* networkEntityTracker)
: m_entity(netBindComponent->GetEntity())
, m_netBindComponent(netBindComponent)
, m_networkEntityTracker(networkEntityTracker)
, m_netEntityId(netBindComponent->GetNetEntityId())
{
if (m_networkEntityTracker)
{
m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity);
}
AZ_Assert(networkEntityTracker, "NetworkEntityTracker is not valid");
}
bool ConstNetworkEntityHandle::Exists() const
{
if (!m_networkEntityTracker)
{
return false;
}
const uint32_t changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity);
if (m_changeDirty != changeDirty)
{
// Make sure to get change dirty with updated m_entity
m_changeDirty = changeDirty;
AZ::Entity* newEntity = m_networkEntityTracker->GetRaw(m_netEntityId);
if (newEntity != m_entity)
{
// If the entity pointer has changed, update our entity pointer and reset our netBindComponent pointer
m_entity = newEntity;
m_netBindComponent = nullptr;
}
}
return m_entity != nullptr;
}
AZ::Entity* ConstNetworkEntityHandle::GetEntity()
{
if (!Exists())
{
return nullptr;
}
return m_entity;
}
const AZ::Entity* ConstNetworkEntityHandle::GetEntity() const
{
if (!Exists())
{
return nullptr;
}
return m_entity;
}
ConstNetworkEntityHandle::operator bool() const
{
return Exists();
}
bool ConstNetworkEntityHandle::operator==(const ConstNetworkEntityHandle &b) const
{
return m_netEntityId == b.m_netEntityId;
}
bool ConstNetworkEntityHandle::operator!=(const ConstNetworkEntityHandle &b) const
{
return m_netEntityId != b.m_netEntityId;
}
bool ConstNetworkEntityHandle::operator<(const ConstNetworkEntityHandle &b) const
{
return m_netEntityId < b.m_netEntityId;
}
void ConstNetworkEntityHandle::Reset()
{
m_entity = nullptr;
m_netBindComponent = nullptr;
m_netEntityId = InvalidNetEntityId;
}
void ConstNetworkEntityHandle::Reset(const ConstNetworkEntityHandle& handle)
{
m_changeDirty = handle.m_changeDirty;
m_entity = handle.m_entity;
m_netBindComponent = handle.m_netBindComponent;
m_networkEntityTracker = handle.m_networkEntityTracker;
m_netEntityId = handle.m_netEntityId;
}
NetBindComponent* ConstNetworkEntityHandle::GetNetBindComponent() const
{
if (!Exists())
{
return nullptr;
}
if (m_netBindComponent == nullptr)
{
m_netBindComponent = m_entity->template FindComponent<NetBindComponent>();
}
return m_netBindComponent;
}
const AZ::Component* ConstNetworkEntityHandle::FindComponent(const AZ::TypeId& typeId) const
{
if (const AZ::Entity* entity{ GetEntity() })
{
return entity->FindComponent(typeId);
}
return nullptr;
}
MultiplayerController* NetworkEntityHandle::FindController(const AZ::TypeId& typeId)
{
if (AZ::Entity* entity{ GetEntity() })
{
MultiplayerComponent* component = azrtti_cast<MultiplayerComponent*>(entity->FindComponent(typeId));
if (component != nullptr)
{
return component->GetController();
}
}
return nullptr;
}
AZ::Component* NetworkEntityHandle::FindComponent(const AZ::TypeId& typeId)
{
return const_cast<AZ::Component*>(const_cast<const ConstNetworkEntityHandle*>(static_cast<ConstNetworkEntityHandle*>(this))->FindComponent(typeId));
}
}
@@ -0,0 +1,141 @@
/*
* 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/Entity.h>
#include <Source/MultiplayerTypes.h>
namespace Multiplayer
{
class MultiplayerController;
class NetworkEntityTracker;
class NetBindComponent;
//! @class ConstNetworkEntityHandle
//! @brief This class provides a wrapping around handle ids.
//! It is optimized to avoid using the hashmap lookup unless the hashmap has had an item removed.
class ConstNetworkEntityHandle
{
public:
//! Constructs a nullptr handle.
ConstNetworkEntityHandle() = default;
//! Constructs a ConstNetworkEntityHandle given an entity, an entity tracker
//! @param entity pointer to the entity to construct a ConstNetworkEntityHandle for
//! @param entityTracker pointer to the entity tracker that tracks the entity
ConstNetworkEntityHandle(AZ::Entity* entity, const NetworkEntityTracker* entityTracker);
//! Constructs a ConstNetworkEntityHandle given an entity, a networkEntityId, and an entity tracker
//! @param entity pointer to the entity to construct a ConstNetworkEntityHandle for
//! @param netEntityId the networkEntityId of the entity
//! @param entityTracker pointer to the entity tracker that tracks the entity
ConstNetworkEntityHandle(AZ::Entity* entity, NetEntityId netEntityId, const NetworkEntityTracker* entityTracker);
//! Constructs a ConstNetworkEntityHandle given an entity, a networked entityId, an entity tracker, and a dirty version
//! @param netBindComponent pointer to the entities NetBindComponent
//! @param entityTracker pointer to the entity tracker that tracks the entity
ConstNetworkEntityHandle(NetBindComponent* netBindComponent, const NetworkEntityTracker* entityTracker);
ConstNetworkEntityHandle(const ConstNetworkEntityHandle&) = default;
//! Access the AZ::Entity if it safely exists, nullptr or false is returned if the entity does not exist.
//! @{
bool Exists() const;
AZ::Entity* GetEntity();
const AZ::Entity* GetEntity() const;
//! @}
//! Operators providing pointer semantics.
//! @{
bool operator ==(const ConstNetworkEntityHandle& rhs) const;
bool operator !=(const ConstNetworkEntityHandle& rhs) const;
friend bool operator ==(const ConstNetworkEntityHandle& lhs, AZStd::nullptr_t);
friend bool operator ==(AZStd::nullptr_t, const ConstNetworkEntityHandle& rhs);
friend bool operator !=(const ConstNetworkEntityHandle& lhs, AZStd::nullptr_t);
friend bool operator !=(AZStd::nullptr_t, const ConstNetworkEntityHandle& rhs);
friend bool operator ==(const ConstNetworkEntityHandle& lhs, const AZ::Entity* rhs);
friend bool operator ==(const AZ::Entity* lhs, const ConstNetworkEntityHandle& rhs);
friend bool operator !=(const ConstNetworkEntityHandle& lhs, const AZ::Entity* rhs);
friend bool operator !=(const AZ::Entity* lhs, const ConstNetworkEntityHandle& rhs);
//! @}
bool operator <(const ConstNetworkEntityHandle& rhs) const;
explicit operator bool() const;
//! Resets the handle to a nullptr state.
void Reset();
void Reset(const ConstNetworkEntityHandle& handle);
//! Returns the networkEntityId of the entity this handle points to.
//! @return the networkEntityId of the entity this handle points to
NetEntityId GetNetEntityId() const;
//! Returns the cached netBindComponent for this entity, or nullptr if it doesn't exist.
//! @return the cached netBindComponent for this entity, or nullptr if it doesn't exist
NetBindComponent* GetNetBindComponent() const;
//! Returns a specific component on of entity given a typeId.
//! @param typeId the typeId of the component to find and return
//! @return pointer to the requested component, or nullptr if it doesn't exist on the entity
const AZ::Component* FindComponent(const AZ::TypeId& typeId) const;
//! Returns a specific component on of entity by class type.
//! @return pointer to the requested component, or nullptr if it doesn't exist on the entity
template <typename Component>
const Component* FindComponent() const;
//! Helper function for sorting EntityHandles by netEntityId.
static bool Compare(const ConstNetworkEntityHandle& lhs, const ConstNetworkEntityHandle& rhs);
protected:
mutable uint32_t m_changeDirty = 0; // Optimization so we don't need to recheck the hashmap
mutable AZ::Entity* m_entity = nullptr;
mutable NetBindComponent* m_netBindComponent = nullptr;
const NetworkEntityTracker* m_networkEntityTracker = nullptr;
NetEntityId m_netEntityId = InvalidNetEntityId;
};
class NetworkEntityHandle
: public ConstNetworkEntityHandle
{
public:
using ConstNetworkEntityHandle::ConstNetworkEntityHandle;
//! Initializes the underlying entity if possible.
void Init();
//! Activates the underlying entity if possible.
void Activate();
//! Deactivates the underlying entity if possible.
void Deactivate();
//! Gets the BaseController from the first component on an Entity with the supplied typeId AND which inherits from Multiplayer::BaseComponent
MultiplayerController* FindController(const AZ::TypeId& typeId);
template <typename Controller>
Controller* FindController();
using ConstNetworkEntityHandle::FindComponent;
AZ::Component* FindComponent(const AZ::TypeId& typeId);
template <typename ComponentType>
ComponentType* FindComponent();
};
}
#include "Source/NetworkEntity/NetworkEntityHandle.inl"
@@ -0,0 +1,138 @@
/*
* 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.
*
*/
namespace Multiplayer
{
inline bool operator ==(const ConstNetworkEntityHandle& lhs, AZStd::nullptr_t)
{
return !lhs.Exists();
}
inline bool operator ==(AZStd::nullptr_t, const ConstNetworkEntityHandle& rhs)
{
return !rhs.Exists();
}
inline bool operator !=(const ConstNetworkEntityHandle& lhs, AZStd::nullptr_t)
{
return lhs.Exists();
}
inline bool operator !=(AZStd::nullptr_t, const ConstNetworkEntityHandle& rhs)
{
return rhs.Exists();
}
inline bool operator==(const ConstNetworkEntityHandle& lhs, const AZ::Entity* rhs)
{
return lhs.m_entity == rhs;
}
inline bool operator==(const AZ::Entity* lhs, const ConstNetworkEntityHandle& rhs)
{
return operator==(rhs, lhs);
}
inline bool operator!=(const ConstNetworkEntityHandle& lhs, const AZ::Entity* rhs)
{
return lhs.m_entity != rhs;
}
inline bool operator!=(const AZ::Entity* lhs, const ConstNetworkEntityHandle& rhs)
{
return operator!=(rhs, lhs);
}
inline NetEntityId ConstNetworkEntityHandle::GetNetEntityId() const
{
return m_netEntityId;
}
template <class ComponentType>
inline const ComponentType* ConstNetworkEntityHandle::FindComponent() const
{
if (const AZ::Entity* entity{ GetEntity() })
{
return entity->template FindComponent<ComponentType>();
}
return nullptr;
}
inline bool ConstNetworkEntityHandle::Compare(const ConstNetworkEntityHandle& lhs, const ConstNetworkEntityHandle& rhs)
{
return lhs.m_netEntityId < rhs.m_netEntityId;
}
inline void NetworkEntityHandle::Init()
{
if (AZ::Entity* entity{ GetEntity() })
{
entity->Init();
}
}
inline void NetworkEntityHandle::Activate()
{
if (AZ::Entity* entity{ GetEntity() })
{
entity->Activate();
}
}
inline void NetworkEntityHandle::Deactivate()
{
if (AZ::Entity* entity{ GetEntity() })
{
entity->Deactivate();
}
}
template <typename ControllerType>
inline ControllerType* NetworkEntityHandle::FindController()
{
return static_cast<ControllerType*>(FindController(ControllerType::ComponentType::RTTI_Type()));
}
template <typename ComponentType>
inline ComponentType* NetworkEntityHandle::FindComponent()
{
if (AZ::Entity* entity{ GetEntity() })
{
return entity->template FindComponent<ComponentType>();
}
return nullptr;
}
}
//! AZStd::hash support.
namespace AZStd
{
template <>
class hash<Multiplayer::NetworkEntityHandle>
{
public:
size_t operator()(const Multiplayer::NetworkEntityHandle &rhs) const
{
return hash<Multiplayer::NetEntityId>()(rhs.GetNetEntityId());
}
};
template <>
class hash<Multiplayer::ConstNetworkEntityHandle>
{
public:
size_t operator()(const Multiplayer::ConstNetworkEntityHandle &rhs) const
{
return hash<Multiplayer::NetEntityId>()(rhs.GetNetEntityId());
}
};
}
@@ -0,0 +1,226 @@
/*
* 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/NetworkEntity/NetworkEntityManager.h>
#include <Source/Components/NetBindComponent.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Slice/SliceMetadataInfoComponent.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Components/TransformComponent.h>
namespace Multiplayer
{
AZ_CVAR(bool, net_DebugCheckNetworkEntityManager, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Enables extra debug checks inside the NetworkEntityManager");
NetworkEntityManager::NetworkEntityManager()
: m_networkEntityAuthorityTracker(*this)
, m_removeEntitiesEvent([this] { RemoveEntities(); }, AZ::Name("NetworkEntityManager remove entities event"))
{
AZ::Interface<INetworkEntityManager>::Register(this);
}
NetworkEntityManager::~NetworkEntityManager()
{
AZ::Interface<INetworkEntityManager>::Unregister(this);
}
NetworkEntityTracker* NetworkEntityManager::GetNetworkEntityTracker()
{
return &m_networkEntityTracker;
}
NetworkEntityAuthorityTracker* NetworkEntityManager::GetNetworkEntityAuthorityTracker()
{
return &m_networkEntityAuthorityTracker;
}
HostId NetworkEntityManager::GetHostId() const
{
return m_hostId;
}
ConstNetworkEntityHandle NetworkEntityManager::GetEntity(NetEntityId netEntityId) const
{
return m_networkEntityTracker.Get(netEntityId);
}
uint32_t NetworkEntityManager::GetEntityCount() const
{
return m_networkEntityTracker.size();
}
NetworkEntityHandle NetworkEntityManager::AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity)
{
m_networkEntityTracker.Add(netEntityId, entity);
return NetworkEntityHandle(entity, netEntityId, &m_networkEntityTracker);
}
void NetworkEntityManager::MarkForRemoval(const ConstNetworkEntityHandle& entityHandle)
{
if (entityHandle.Exists())
{
if (net_DebugCheckNetworkEntityManager)
{
NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
AZ_Assert(netBindComponent, "No NetBindComponent found on networked entity");
const bool isClientOnlyEntity = false;// (ServerIdFromEntityId(it->first) == InvalidHostId);
AZ_Assert(netBindComponent->IsAuthority() || isClientOnlyEntity, "Trying to delete a proxy entity, this will lead to issues deserializing entity updates");
}
m_removeList.push_back(entityHandle.GetNetEntityId());
m_removeEntitiesEvent.Enqueue(AZ::TimeMs{ 0 });
}
}
bool NetworkEntityManager::IsMarkedForRemoval(const ConstNetworkEntityHandle& entityHandle) const
{
for (auto removeEntId : m_removeList)
{
if (entityHandle.GetNetEntityId() == removeEntId)
{
return true;
}
}
return false;
}
void NetworkEntityManager::ClearEntityFromRemovalList(const ConstNetworkEntityHandle& entityHandle)
{
for (auto iter = m_removeList.begin(); iter != m_removeList.end(); ++iter)
{
if (*iter == entityHandle.GetNetEntityId())
{
iter = m_removeList.erase(iter);
break;
}
}
}
void NetworkEntityManager::ClearAllEntities()
{
// Note is looping through a hash map not a vector. Could cause performance issues even on shutdown.
for (NetworkEntityTracker::iterator it = m_networkEntityTracker.begin(); it != m_networkEntityTracker.end(); ++it)
{
m_removeList.push_back(it->first);
}
RemoveEntities();
// Keystone has refactored these API's, rewrite required
//AZ::SliceComponent* rootSlice = nullptr;
//{
// AzFramework::EntityContextId gameContextId = AzFramework::EntityContextId::CreateNull();
// AzFramework::GameEntityContextRequestBus::BroadcastResult(gameContextId, &AzFramework::GameEntityContextRequests::GetGameEntityContextId);
// AzFramework::EntityContextRequestBus::BroadcastResult(rootSlice, &AzFramework::EntityContextRequests::GetRootSlice);
// AZ_Assert(rootSlice != nullptr, "Root slice returned was nullptr");
//}
//
//for (AZ::Entity* entity : m_nonNetworkedEntities)
//{
// rootSlice->RemoveEntity(entity);
//}
m_nonNetworkedEntities.clear();
m_networkEntityTracker.clear();
}
void NetworkEntityManager::AddEntityMarkedDirtyHandler(AZ::Event<>::Handler& entityMarkedDirtyHandler)
{
entityMarkedDirtyHandler.Connect(m_onEntityMarkedDirty);
}
void NetworkEntityManager::AddEntityNotifyChangesHandler(AZ::Event<>::Handler& entityNotifyChangesHandler)
{
entityNotifyChangesHandler.Connect(m_onEntityNotifyChanges);
}
void NetworkEntityManager::AddEntityExitDomainHandler(EntityExitDomainEvent::Handler& entityExitDomainHandler)
{
entityExitDomainHandler.Connect(m_entityExitDomainEvent);
}
void NetworkEntityManager::AddControllersActivatedHandler(ControllersActivatedEvent::Handler& controllersActivatedHandler)
{
controllersActivatedHandler.Connect(m_controllersActivatedEvent);
}
void NetworkEntityManager::AddControllersDeactivatedHandler(ControllersDeactivatedEvent::Handler& controllersDeactivatedHandler)
{
controllersDeactivatedHandler.Connect(m_controllersDeactivatedEvent);
}
void NetworkEntityManager::NotifyEntitiesDirtied()
{
m_onEntityMarkedDirty.Signal();
}
void NetworkEntityManager::NotifyEntitiesChanged()
{
m_onEntityNotifyChanges.Signal();
}
void NetworkEntityManager::NotifyControllersActivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating)
{
m_controllersActivatedEvent.Signal(entityHandle, entityIsMigrating);
}
void NetworkEntityManager::NotifyControllersDeactivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating)
{
m_controllersDeactivatedEvent.Signal(entityHandle, entityIsMigrating);
}
void NetworkEntityManager::HandleLocalRpcMessage(NetworkEntityRpcMessage& message)
{
AZ_Assert(message.GetRpcDeliveryType() == RpcDeliveryType::ServerSimulationToServerAuthority, "Only ServerSimulationToServerAuthority rpc messages can be locally deferred");
m_localDeferredRpcMessages.emplace_back(AZStd::move(message));
}
void NetworkEntityManager::RemoveEntities()
{
//RewindableObjectState::ClearRewoundEntities();
// Keystone has refactored these API's, rewrite required
//AZ::SliceComponent* rootSlice = nullptr;
//{
// AzFramework::EntityContextId gameContextId = AzFramework::EntityContextId::CreateNull();
// AzFramework::GameEntityContextRequestBus::BroadcastResult(gameContextId, &AzFramework::GameEntityContextRequests::GetGameEntityContextId);
// AzFramework::EntityContextRequestBus::BroadcastResult(rootSlice, &AzFramework::EntityContextRequests::GetRootSlice);
// AZ_Assert(rootSlice != nullptr, "Root slice returned was NULL");
//}
AZStd::vector<NetEntityId> removeList;
removeList.swap(m_removeList);
for (NetEntityId entityId : removeList)
{
NetworkEntityHandle removeEntity = m_networkEntityTracker.Get(entityId);
if (removeEntity != nullptr)
{
// We need to notify out that our entity is about to deactivate so that other entities can read state before we clean up
NetBindComponent* netBindComponent = removeEntity.GetNetBindComponent();
AZ_Assert(netBindComponent != nullptr, "NetBindComponent not found on networked entity");
netBindComponent->StopEntity();
// Delete Entity, method depends on how it was loaded
// Try slice removal first, then force delete
AZ::Entity* rawEntity = removeEntity.GetEntity();
//if (!rootSlice->RemoveEntity(rawEntity))
//{
delete rawEntity;
//}
}
m_networkEntityTracker.erase(entityId);
}
}
}
@@ -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 <AzCore/EBus/ScheduledEvent.h>
#include <Source/NetworkEntity/INetworkEntityManager.h>
#include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h>
#include <Source/NetworkEntity/NetworkEntityTracker.h>
#include <Source/NetworkEntity/NetworkEntityRpcMessage.h>
namespace Multiplayer
{
//! Implementation of the networked entity manager interface.
//! This class creates and manages all networked entities.
class NetworkEntityManager final
: public INetworkEntityManager
{
public:
NetworkEntityManager();
~NetworkEntityManager();
//! INetworkEntityManager overrides.
//! @{
NetworkEntityTracker* GetNetworkEntityTracker() override;
NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker() override;
HostId GetHostId() const override;
ConstNetworkEntityHandle GetEntity(NetEntityId netEntityId) const override;
uint32_t GetEntityCount() const override;
NetworkEntityHandle AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity) override;
void MarkForRemoval(const ConstNetworkEntityHandle& entityHandle) override;
bool IsMarkedForRemoval(const ConstNetworkEntityHandle& entityHandle) const override;
void ClearEntityFromRemovalList(const ConstNetworkEntityHandle& entityHandle) override;
void ClearAllEntities() override;
void AddEntityMarkedDirtyHandler(AZ::Event<>::Handler& entityMarkedDirtyHandle) override;
void AddEntityNotifyChangesHandler(AZ::Event<>::Handler& entityNotifyChangesHandle) override;
void AddEntityExitDomainHandler(EntityExitDomainEvent::Handler& entityExitDomainHandler) override;
void AddControllersActivatedHandler(ControllersActivatedEvent::Handler& controllersActivatedHandler) override;
void AddControllersDeactivatedHandler(ControllersDeactivatedEvent::Handler& controllersDeactivatedHandler) override;
void NotifyEntitiesDirtied() override;
void NotifyEntitiesChanged() override;
void NotifyControllersActivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) override;
void NotifyControllersDeactivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) override;
void HandleLocalRpcMessage(NetworkEntityRpcMessage& message) override;
//! @}
private:
void RemoveEntities();
NetworkEntityTracker m_networkEntityTracker;
NetworkEntityAuthorityTracker m_networkEntityAuthorityTracker;
AZ::ScheduledEvent m_removeEntitiesEvent;
AZStd::vector<NetEntityId> m_removeList;
AZStd::vector<AZ::Entity*> m_nonNetworkedEntities; // Contains entities that we've instantiated, but are not networked entities
EntityExitDomainEvent m_entityExitDomainEvent;
AZ::Event<> m_onEntityMarkedDirty;
AZ::Event<> m_onEntityNotifyChanges;
ControllersActivatedEvent m_controllersActivatedEvent;
ControllersDeactivatedEvent m_controllersDeactivatedEvent;
HostId m_hostId = InvalidHostId;
int32_t m_nextEntityIndex = 0;
// Local RPCs are buffered and dispatched at the end of the frame rather than processed immediately
// This is done to prevent local and network sent RPC's from having different dispatch behaviours
typedef AZStd::deque<NetworkEntityRpcMessage> DeferredRpcMessages;
DeferredRpcMessages m_localDeferredRpcMessages;
};
}
@@ -0,0 +1,192 @@
/*
* 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/NetworkEntity/NetworkEntityRpcMessage.h>
#include <AzNetworking/Serialization/NetworkInputSerializer.h>
#include <AzNetworking/Serialization/NetworkOutputSerializer.h>
#include <AzCore/Console/ILogger.h>
namespace Multiplayer
{
NetworkEntityRpcMessage::NetworkEntityRpcMessage(NetworkEntityRpcMessage&& rhs)
: m_rpcDeliveryType(rhs.m_rpcDeliveryType)
, m_entityId(rhs.m_entityId)
, m_componentId(rhs.m_componentId)
, m_rpcMessageType(rhs.m_rpcMessageType)
, m_data(AZStd::move(rhs.m_data))
, m_isReliable(rhs.m_isReliable)
{
;
}
NetworkEntityRpcMessage::NetworkEntityRpcMessage(const NetworkEntityRpcMessage& rhs)
: m_rpcDeliveryType(rhs.m_rpcDeliveryType)
, m_entityId(rhs.m_entityId)
, m_componentId(rhs.m_componentId)
, m_rpcMessageType(rhs.m_rpcMessageType)
, m_isReliable(rhs.m_isReliable)
{
if (rhs.m_data != nullptr)
{
m_data = AZStd::make_unique<AzNetworking::PacketEncodingBuffer>();
(*m_data) = (*rhs.m_data); // Deep-copy
}
}
NetworkEntityRpcMessage::NetworkEntityRpcMessage(RpcDeliveryType rpcDeliveryType, NetEntityId entityId, NetComponentId componentId, uint8_t rpcMessageType, ReliabilityType isReliable)
: m_rpcDeliveryType(rpcDeliveryType)
, m_entityId(entityId)
, m_componentId(componentId)
, m_rpcMessageType(rpcMessageType)
, m_isReliable(isReliable)
{
;
}
NetworkEntityRpcMessage& NetworkEntityRpcMessage::operator =(NetworkEntityRpcMessage&& rhs)
{
m_rpcDeliveryType = rhs.m_rpcDeliveryType;
m_entityId = rhs.m_entityId;
m_componentId = rhs.m_componentId;
m_rpcMessageType = rhs.m_rpcMessageType;
m_isReliable = rhs.m_isReliable;
m_data = AZStd::move(rhs.m_data);
return *this;
}
NetworkEntityRpcMessage& NetworkEntityRpcMessage::operator =(const NetworkEntityRpcMessage& rhs)
{
m_rpcDeliveryType = rhs.m_rpcDeliveryType;
m_entityId = rhs.m_entityId;
m_componentId = rhs.m_componentId;
m_rpcMessageType = rhs.m_rpcMessageType;
m_isReliable = rhs.m_isReliable;
if (rhs.m_data != nullptr)
{
m_data = AZStd::make_unique<AzNetworking::PacketEncodingBuffer>();
*m_data = (*rhs.m_data);
}
return *this;
}
bool NetworkEntityRpcMessage::operator ==(const NetworkEntityRpcMessage& rhs) const
{
// Note that we intentionally don't compare the blob buffers themselves
return ((m_rpcDeliveryType == rhs.m_rpcDeliveryType)
&& (m_entityId == rhs.m_entityId)
&& (m_componentId == rhs.m_componentId)
&& (m_rpcMessageType == rhs.m_rpcMessageType));
}
bool NetworkEntityRpcMessage::operator !=(const NetworkEntityRpcMessage& rhs) const
{
return !(*this == rhs);
}
uint32_t NetworkEntityRpcMessage::GetEstimatedSerializeSize() const
{
static constexpr uint32_t sizeOfFields = sizeof(RpcDeliveryType)
+ sizeof(NetEntityId)
+ sizeof(NetComponentId)
+ sizeof(uint8_t);
// 2-byte size header + the actual blob payload itself
const uint32_t sizeOfBlob = (m_data != nullptr) ? sizeof(uint16_t) + m_data->GetSize() : 0;
// No sliceId, remote replicator already exists so we don't need to know what type of entity this is
return sizeOfFields + sizeOfBlob;
}
RpcDeliveryType NetworkEntityRpcMessage::GetRpcDeliveryType() const
{
return m_rpcDeliveryType;
}
void NetworkEntityRpcMessage::SetRpcDeliveryType(RpcDeliveryType value)
{
m_rpcDeliveryType = value;
}
NetEntityId NetworkEntityRpcMessage::GetEntityId() const
{
return m_entityId;
}
NetComponentId NetworkEntityRpcMessage::GetComponentId() const
{
return m_componentId;
}
uint8_t NetworkEntityRpcMessage::GetRpcMessageType() const
{
return m_rpcMessageType;
}
bool NetworkEntityRpcMessage::SetRpcParams(IRpcParamStruct& params)
{
if (m_data == nullptr)
{
m_data = AZStd::make_unique<AzNetworking::PacketEncodingBuffer>();
}
AzNetworking::NetworkInputSerializer serializer(m_data->GetBuffer(), m_data->GetCapacity());
if (params.Serialize(serializer))
{
m_data->Resize(serializer.GetSize());
return true;
}
// Serialization failed, just leave the blob at zero size
return false;
}
bool NetworkEntityRpcMessage::GetRpcParams(IRpcParamStruct& outParams)
{
if (m_data == nullptr)
{
AZLOG_ERROR("Trying to retrieve RpcParams from an NetworkEntityRpcMessage with no blob buffer, this NetworkEntityRpcMessage has not been constructed or serialized");
return false;
}
AzNetworking::NetworkOutputSerializer serializer(m_data->GetBuffer(), m_data->GetSize());
return outParams.Serialize(serializer);
}
bool NetworkEntityRpcMessage::Serialize(AzNetworking::ISerializer& serializer)
{
serializer.Serialize(m_rpcDeliveryType, "RpcDeliveryType");
serializer.Serialize(m_entityId, "EntityId");
serializer.Serialize(m_componentId, "ComponentId");
serializer.Serialize(m_rpcMessageType, "RpcMessageType");
// m_data should never be nullptr, it contains serialized data for our Rpc params struct
if (m_data == nullptr)
{
m_data = AZStd::make_unique<AzNetworking::PacketEncodingBuffer>();
}
serializer.Serialize(*m_data, "data");
// We intentionally do not serialize the reliability flag, or any other RPC metadata
return serializer.IsValid();
}
void NetworkEntityRpcMessage::SetReliability(ReliabilityType reliabilityType)
{
m_isReliable = reliabilityType;
}
ReliabilityType NetworkEntityRpcMessage::GetReliability() const
{
return m_isReliable;
}
}
@@ -0,0 +1,122 @@
/*
* 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 <AzNetworking/DataStructures/ByteBuffer.h>
#include <Source/MultiplayerTypes.h>
namespace Multiplayer
{
struct IRpcParamStruct;
// The maximum number of RPC's we can aggregate into a single packet
static constexpr uint32_t MaxAggregateRpcMessages = 1024;
//! @class NetworkEntityRpcMessage
//! @brief Remote procedure call data.
class NetworkEntityRpcMessage
{
public:
AZ_TYPE_INFO(NetworkEntityRpcMessage, "{3AA5E1A5-6383-46C1-9817-F1B8C2325178}");
NetworkEntityRpcMessage() = default;
NetworkEntityRpcMessage(NetworkEntityRpcMessage&& rhs);
NetworkEntityRpcMessage(const NetworkEntityRpcMessage& rhs);
//! Fill explicit constructor.
//! @param rpcDeliveryType the delivery type (origin and target) for this RPC
//! @param entityId the networked entityId of the entity handling this RPC
//! @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);
NetworkEntityRpcMessage& operator =(NetworkEntityRpcMessage&& rhs);
NetworkEntityRpcMessage& operator =(const NetworkEntityRpcMessage& rhs);
bool operator ==(const NetworkEntityRpcMessage& rhs) const;
bool operator !=(const NetworkEntityRpcMessage& rhs) const;
//! Returns an estimated serialization footprint for this NetworkEntityRpcMessage.
//! @return an estimated serialization footprint for this NetworkEntityRpcMessage
uint32_t GetEstimatedSerializeSize() const;
//! Gets the current value of RpcDeliveryType.
//! @return the current value of RpcDeliveryType
RpcDeliveryType GetRpcDeliveryType() const;
//! Sets the current value for RpcDeliveryType.
//! @param a_Value the value to set RpcDeliveryType to
void SetRpcDeliveryType(RpcDeliveryType a_Value);
//! Gets the current value of EntityId.
//! @return the current value of EntityId
NetEntityId GetEntityId() const;
//! Gets the current value of EntityComponentType.
//! @return the current value of EntityComponentType
NetComponentId GetComponentId() const;
//! Gets the current value of RpcMessageType.
//! @return the current value of RpcMessageType
uint8_t GetRpcMessageType() const;
//! Writes the data contained inside a_Params to this NetworkEntityRpcMessage's blob buffer.
//! @param a_Params the parameters to save inside this NetworkEntityRpcMessage instance
bool SetRpcParams(IRpcParamStruct& params);
//! Reads the data contained inside this NetworkEntityRpcMessage's blob buffer and stores them in outParams.
//! @param outParams the parameters instance to store to the resulting data inside
bool GetRpcParams(IRpcParamStruct& outParams);
//! Base serialize method for all serializable structures or classes to implement.
//! @param serializer ISerializer instance to use for serialization
//! @return boolean true for success, false for serialization failure
bool Serialize(AzNetworking::ISerializer& serializer);
//! Sets this RPC's reliable delivery flag.
//! @param reliabilityType the reliability type for this RPC
void SetReliability(ReliabilityType reliabilityType);
//! Returns whether or not this RPC has been flagged for reliable delivery.
//! @return the reliability type of this RPC
ReliabilityType GetReliability() const;
private:
// Serialized payload data
RpcDeliveryType m_rpcDeliveryType = RpcDeliveryType::None;
NetEntityId m_entityId = InvalidNetEntityId;
NetComponentId m_componentId = InvalidNetComponentId;
uint8_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
AZStd::unique_ptr<AzNetworking::PacketEncodingBuffer> m_data;
// Non-serialized RPC metadata
ReliabilityType m_isReliable = ReliabilityType::Reliable;
};
struct IRpcParamStruct
{
virtual ~IRpcParamStruct() {}
virtual bool Serialize(AzNetworking::ISerializer& serializer) = 0;
};
struct ComponentRpcEmptyStruct
: public IRpcParamStruct
{
bool Serialize(AzNetworking::ISerializer&) override { return true; }
};
}
@@ -0,0 +1,72 @@
/*
* 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/NetworkEntity/NetworkEntityTracker.h>
#include <Source/NetworkEntity/NetworkEntityHandle.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
namespace Multiplayer
{
void NetworkEntityTracker::Add(NetEntityId netEntityId, AZ::Entity* entity)
{
++m_addChangeDirty;
AZ_Assert(m_entityMap.end() == m_entityMap.find(netEntityId), "Attempting to add the same entity to the entity map multiple times");
m_entityMap[netEntityId] = entity;
}
NetworkEntityHandle NetworkEntityTracker::Get(NetEntityId netEntityId)
{
AZ::Entity* entity = GetRaw(netEntityId);
return NetworkEntityHandle(entity, netEntityId, this);
}
ConstNetworkEntityHandle NetworkEntityTracker::Get(NetEntityId netEntityId) const
{
AZ::Entity* entity = GetRaw(netEntityId);
return ConstNetworkEntityHandle(entity, netEntityId, this);
}
bool NetworkEntityTracker::Exists(NetEntityId netEntityId) const
{
return (m_entityMap.find(netEntityId) != m_entityMap.end());
}
AZ::Entity* NetworkEntityTracker::GetRaw(NetEntityId netEntityId) const
{
auto found = m_entityMap.find(netEntityId);
if (found != m_entityMap.end())
{
return found->second;
}
return nullptr;
}
void NetworkEntityTracker::erase(NetEntityId netEntityId)
{
++m_deleteChangeDirty;
m_entityMap.erase(netEntityId);
}
NetworkEntityTracker::EntityMap::iterator NetworkEntityTracker::erase(EntityMap::iterator iter)
{
++m_deleteChangeDirty;
return m_entityMap.erase(iter);
}
AZ::Entity *NetworkEntityTracker::Move(EntityMap::iterator iter)
{
AZ::Entity *ptr = iter->second;
erase(iter);
return ptr;
}
}
@@ -0,0 +1,86 @@
/*
* 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 <Source/MultiplayerTypes.h>
#include <Source/NetworkEntity/NetworkEntityHandle.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/Component/Entity.h>
namespace Multiplayer
{
//! @class NetworkEntityTracker
//! @brief The responsibly of this class is to allow entity netEntityId's to be looked up.
class NetworkEntityTracker
{
public:
using EntityMap = AZStd::unordered_map<NetEntityId, AZ::Entity*>;
using iterator = EntityMap::iterator;
using const_iterator = EntityMap::const_iterator;
NetworkEntityTracker() = default;
//! Adds a networked entity to the tracker
//! @param netEntityId the networkId of the entity to add
//! @param entity pointer to the entity corresponding to the networkId
void Add(NetEntityId netEntityId, AZ::Entity* entity);
//! Returns an entity handle which can validate entity existence.
NetworkEntityHandle Get(NetEntityId netEntityId);
ConstNetworkEntityHandle Get(NetEntityId netEntityId) const;
//! Returns true if the netEntityId exists.
bool Exists(NetEntityId netEntityId) const;
//! Get a raw pointer of an entity.
AZ::Entity *GetRaw(NetEntityId netEntityId) const;
//! Moves the given iterator out of the entity holder and returns the ptr
AZ::Entity *Move(EntityMap::iterator iter);
//! Container overloads
//!@{
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
iterator find(NetEntityId netEntityId);
const_iterator find(NetEntityId netEntityId) const;
void erase(NetEntityId netEntityId);
iterator erase(EntityMap::iterator iter);
AZStd::size_t size() const;
void clear();
//! @}
//! Dirty tracking optimizations to avoid unnecessary hash lookups.
//! There are two counts, one for adds and one for deletes
//! If an entity is nullptr, check adds to check to see if our entity was added again
//! If an entity is not nullptr, check removes which reminds us to see if the entity no longer exists
//! Passing in the entity into this helper assists in retrieving the correct count, so we do not need to store both counts inside each handle
uint32_t GetChangeDirty(const AZ::Entity* entity) const;
uint32_t GetDeleteChangeDirty() const;
uint32_t GetAddChangeDirty() const;
//! Prevent copying and heap allocation.
AZ_DISABLE_COPY_MOVE(NetworkEntityTracker);
private:
EntityMap m_entityMap;
uint32_t m_deleteChangeDirty = 0;
uint32_t m_addChangeDirty = 0;
};
}
#include "Source/NetworkEntity/NetworkEntityTracker.inl"
@@ -0,0 +1,71 @@
/*
* 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
namespace Multiplayer
{
inline NetworkEntityTracker::iterator NetworkEntityTracker::begin()
{
return m_entityMap.begin();
}
inline NetworkEntityTracker::const_iterator NetworkEntityTracker::begin() const
{
return m_entityMap.begin();
}
inline NetworkEntityTracker::iterator NetworkEntityTracker::end()
{
return m_entityMap.end();
}
inline NetworkEntityTracker::const_iterator NetworkEntityTracker::end() const
{
return m_entityMap.end();
}
inline NetworkEntityTracker::iterator NetworkEntityTracker::find(NetEntityId netEntityId)
{
return m_entityMap.find(netEntityId);
}
inline NetworkEntityTracker::const_iterator NetworkEntityTracker::find(NetEntityId netEntityId) const
{
return m_entityMap.find(netEntityId);
}
inline AZStd::size_t NetworkEntityTracker::size() const
{
return m_entityMap.size();
}
inline void NetworkEntityTracker::clear()
{
m_entityMap.clear();
}
inline uint32_t NetworkEntityTracker::GetChangeDirty(const AZ::Entity* entity) const
{
return (entity != nullptr) ? GetDeleteChangeDirty() : GetAddChangeDirty();
}
inline uint32_t NetworkEntityTracker::GetDeleteChangeDirty() const
{
return m_deleteChangeDirty;
}
inline uint32_t NetworkEntityTracker::GetAddChangeDirty() const
{
return m_addChangeDirty;
}
}
@@ -0,0 +1,252 @@
/*
* 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/NetworkEntity/NetworkEntityUpdateMessage.h>
#include <AzNetworking/Serialization/NetworkInputSerializer.h>
#include <AzNetworking/Serialization/NetworkOutputSerializer.h>
#include <AzCore/Console/ILogger.h>
namespace Multiplayer
{
NetworkEntityUpdateMessage::NetworkEntityUpdateMessage(NetworkEntityUpdateMessage&& rhs)
: m_networkRole(rhs.m_networkRole)
, m_entityId(rhs.m_entityId)
, m_isDelete(rhs.m_isDelete)
, m_wasMigrated(rhs.m_wasMigrated)
, m_takeOwnership(rhs.m_takeOwnership)
, m_hasValidPrefabId(rhs.m_hasValidPrefabId)
, m_prefabEntityId(rhs.m_prefabEntityId)
, m_data(AZStd::move(rhs.m_data))
{
;
}
NetworkEntityUpdateMessage::NetworkEntityUpdateMessage(const NetworkEntityUpdateMessage& rhs)
: m_networkRole(rhs.m_networkRole)
, m_entityId(rhs.m_entityId)
, m_isDelete(rhs.m_isDelete)
, m_wasMigrated(rhs.m_wasMigrated)
, m_takeOwnership(rhs.m_takeOwnership)
, m_hasValidPrefabId(rhs.m_hasValidPrefabId)
, m_prefabEntityId(rhs.m_prefabEntityId)
{
if (rhs.m_data != nullptr)
{
m_data = AZStd::make_unique<AzNetworking::PacketEncodingBuffer>();
(*m_data) = (*rhs.m_data); // Deep-copy
}
}
NetworkEntityUpdateMessage::NetworkEntityUpdateMessage(NetEntityRole networkRole, NetEntityId entityId)
: m_networkRole(networkRole)
, m_entityId(entityId)
{
;
}
NetworkEntityUpdateMessage::NetworkEntityUpdateMessage(NetEntityRole networkRole, NetEntityId entityId, const PrefabEntityId& prefabEntityId)
: m_networkRole(networkRole)
, m_entityId(entityId)
, m_hasValidPrefabId(true)
, m_prefabEntityId(prefabEntityId)
{
;
}
NetworkEntityUpdateMessage::NetworkEntityUpdateMessage(NetEntityId entityId, bool wasMigrated, bool takeOwnership)
: m_entityId(entityId)
, m_wasMigrated(wasMigrated)
, m_takeOwnership(takeOwnership)
{
;
}
NetworkEntityUpdateMessage& NetworkEntityUpdateMessage::operator =(NetworkEntityUpdateMessage&& rhs)
{
m_networkRole = rhs.m_networkRole;
m_entityId = rhs.m_entityId;
m_isDelete = rhs.m_isDelete;
m_wasMigrated = rhs.m_wasMigrated;
m_takeOwnership = rhs.m_takeOwnership;
m_hasValidPrefabId = rhs.m_hasValidPrefabId;
m_prefabEntityId = rhs.m_prefabEntityId;
m_data = AZStd::move(rhs.m_data);
return *this;
}
NetworkEntityUpdateMessage& NetworkEntityUpdateMessage::operator =(const NetworkEntityUpdateMessage& rhs)
{
m_networkRole = rhs.m_networkRole;
m_entityId = rhs.m_entityId;
m_isDelete = rhs.m_isDelete;
m_wasMigrated = rhs.m_wasMigrated;
m_takeOwnership = rhs.m_takeOwnership;
m_hasValidPrefabId = rhs.m_hasValidPrefabId;
m_prefabEntityId = rhs.m_prefabEntityId;
if (rhs.m_data != nullptr)
{
m_data = AZStd::make_unique<AzNetworking::PacketEncodingBuffer>();
*m_data = (*rhs.m_data);
}
return *this;
}
bool NetworkEntityUpdateMessage::operator ==(const NetworkEntityUpdateMessage& rhs) const
{
// Note that we intentionally don't compare the blob buffers themselves
return ((m_networkRole == rhs.m_networkRole)
&& (m_entityId == rhs.m_entityId)
&& (m_isDelete == rhs.m_isDelete)
&& (m_wasMigrated == rhs.m_wasMigrated)
&& (m_takeOwnership == rhs.m_takeOwnership)
&& (m_hasValidPrefabId == rhs.m_hasValidPrefabId)
&& (m_prefabEntityId == rhs.m_prefabEntityId));
}
bool NetworkEntityUpdateMessage::operator !=(const NetworkEntityUpdateMessage& rhs) const
{
return !(*this == rhs);
}
uint32_t NetworkEntityUpdateMessage::GetEstimatedSerializeSize() const
{
// * NOTE * Keep this in sync with the actual serialize method for this class
// If we return an underestimate, the replicator could start generating update packets that fragment, which would be terrible for gameplay latency
static const uint32_t sizeOfFlags = 1;
static const uint32_t sizeOfEntityId = sizeof(NetEntityId);
static const uint32_t sizeOfSliceId = 6;
if (m_isDelete)
{
return sizeOfFlags + sizeOfEntityId;
}
// 2-byte size header + the actual blob payload itself
const uint32_t sizeOfBlob = (m_data != nullptr) ? sizeof(uint16_t) + m_data->GetSize() : 0;
if (m_hasValidPrefabId)
{
// sliceId is transmitted
return sizeOfFlags + sizeOfEntityId + sizeOfSliceId + sizeOfBlob;
}
// No sliceId, remote replicator already exists so we don't need to know what type of entity this is
return sizeOfFlags + sizeOfEntityId + sizeOfBlob;
}
NetEntityRole NetworkEntityUpdateMessage::GetNetworkRole() const
{
return m_networkRole;
}
NetEntityId NetworkEntityUpdateMessage::GetEntityId() const
{
return m_entityId;
}
bool NetworkEntityUpdateMessage::GetIsDelete() const
{
return m_isDelete;
}
bool NetworkEntityUpdateMessage::GetWasMigrated() const
{
return m_wasMigrated;
}
bool NetworkEntityUpdateMessage::GetTakeOwnership() const
{
return m_takeOwnership;
}
bool NetworkEntityUpdateMessage::GetHasValidPrefabId() const
{
return m_hasValidPrefabId;
}
void NetworkEntityUpdateMessage::SetPrefabEntityId(const PrefabEntityId& value)
{
m_hasValidPrefabId = true;
m_prefabEntityId = value;
}
const PrefabEntityId& NetworkEntityUpdateMessage::GetPrefabEntityId() const
{
return m_prefabEntityId;
}
void NetworkEntityUpdateMessage::SetData(const AzNetworking::PacketEncodingBuffer& value)
{
if (m_data == nullptr)
{
m_data = AZStd::make_unique<AzNetworking::PacketEncodingBuffer>();
}
(*m_data) = value;
}
const AzNetworking::PacketEncodingBuffer* NetworkEntityUpdateMessage::GetData() const
{
return m_data.get();
}
AzNetworking::PacketEncodingBuffer& NetworkEntityUpdateMessage::ModifyData()
{
if (m_data == nullptr)
{
m_data = AZStd::make_unique<AzNetworking::PacketEncodingBuffer>();
}
return *m_data;
}
bool NetworkEntityUpdateMessage::Serialize(AzNetworking::ISerializer& serializer)
{
// Always serialize the entityId
serializer.Serialize(m_entityId, "EntityId");
// Use the upper 4 bits for boolean flags, and the lower 4 bits for the network role
uint8_t networkTypeAndFlags = (m_isDelete ? 0x80 : 0x00)
| (m_wasMigrated ? 0x40 : 0x00)
| (m_takeOwnership ? 0x20 : 0x00)
| (m_hasValidPrefabId ? 0x10 : 0x00)
| static_cast<uint8_t>(m_networkRole);
if (serializer.Serialize(networkTypeAndFlags, "TypeAndFlags"))
{
m_isDelete = (networkTypeAndFlags & 0x80) == 0x80;
m_wasMigrated = (networkTypeAndFlags & 0x40) == 0x40;
m_takeOwnership = (networkTypeAndFlags & 0x20) == 0x20;
m_hasValidPrefabId = (networkTypeAndFlags & 0x10) == 0x10;
m_networkRole = static_cast<NetEntityRole>(networkTypeAndFlags & 0x0F);
}
if (!m_isDelete)
{
// We only transmit sliceEntryId's and property data globs if we're not deleting the entity
if (m_hasValidPrefabId)
{
// Only serialize the sliceEntryId if one was provided to the update message constructor
// otherwise a remote replicator should be set up and the sliceEntryId would be redundant
serializer.Serialize(m_prefabEntityId, "PrefabEntityId");
}
// m_data should never be nullptr unless this is a delete packet
if (m_data == nullptr)
{
m_data = AZStd::make_unique<AzNetworking::PacketEncodingBuffer>();
}
serializer.Serialize(*m_data, "Data");;
}
return serializer.IsValid();
}
}
@@ -0,0 +1,125 @@
/*
* 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 <AzNetworking/DataStructures/ByteBuffer.h>
#include <AzCore/Name/Name.h>
#include <Source/MultiplayerTypes.h>
namespace Multiplayer
{
// The maximum number of entity updates we can stuff into a single update packet
static const uint32_t MaxAggregateEntityMessages = 2048;
//! @class NetworkEntityUpdateMessage
//! @brief Property replication packet.
class NetworkEntityUpdateMessage
{
public:
AZ_TYPE_INFO(NetworkEntityUpdateMessage, "{CFCA08F7-547B-4B89-9794-37A8679608DF}");
NetworkEntityUpdateMessage() = default;
NetworkEntityUpdateMessage(NetworkEntityUpdateMessage&& rhs);
NetworkEntityUpdateMessage(const NetworkEntityUpdateMessage& rhs);
//! Constructor for update without a slice name (remote replicator established).
//! @param entityRole the role of the entity being replicated
//! @param entityId the networkId of the entity being replicated
explicit NetworkEntityUpdateMessage(NetEntityRole entityRole, NetEntityId entityId);
//! Constructor for update with a slice name (no remote replicator established).
//! @param entityRole the role of the entity being replicated
//! @param entityId the networkId of the entity being replicated
//! @param prefabEntityId the prefab entityId to clone this replicated entity from
explicit NetworkEntityUpdateMessage(NetEntityRole entityRole, NetEntityId entityId, const PrefabEntityId& prefabEntityId);
//! Constructor for an entity delete message.
//! @param entityId the networkId of the entity being deleted
//! @param isMigrated whether or not the entity is being migrated or deleted
//! @param takeOwnership true if the remote replicator should take ownership of the entity
explicit NetworkEntityUpdateMessage(NetEntityId entityId, bool isMigrated, bool takeOwnership);
NetworkEntityUpdateMessage& operator =(NetworkEntityUpdateMessage&& rhs);
NetworkEntityUpdateMessage& operator =(const NetworkEntityUpdateMessage& rhs);
bool operator ==(const NetworkEntityUpdateMessage& rhs) const;
bool operator !=(const NetworkEntityUpdateMessage& rhs) const;
//! Returns an estimated serialization footprint for this NetworkEntityUpdateMessage.
//! @return an estimated serialization footprint for this NetworkEntityUpdateMessage
uint32_t GetEstimatedSerializeSize() const;
//! Gets the current value of NetworkRole.
//! @return the current value of NetworkRole
NetEntityRole GetNetworkRole() const;
//! Gets the entities networkId.
//! @return the entities networkId
NetEntityId GetEntityId() const;
//! Gets the current value of IsDelete (true if this represents a DeleteProxy message).
//! @return the current value of IsDelete
bool GetIsDelete() const;
//! Returns whether or not the entity was migrated.
//! @return whether or not the entity was migrated
bool GetWasMigrated() const;
//! Gets the current value of TakeOwnership.
//! @return the current value of TakeOwnership
bool GetTakeOwnership() const;
//! Gets the current value of HasValidPrefabId.
//! @return the current value of HasValidPrefabId
bool GetHasValidPrefabId() const;
//! Sets the current value for PrefabEntityId.
//! @param value the value to set PrefabEntityId to
void SetPrefabEntityId(const PrefabEntityId& value);
//! Gets the current value of PrefabEntityId.
//! @return the current value of PrefabEntityId
const PrefabEntityId& GetPrefabEntityId() const;
//! Sets the current value for Data
//! @param value the value to set Data to
void SetData(const AzNetworking::PacketEncodingBuffer& value);
//! Gets the current value of Data.
//! @return the current value of Data
const AzNetworking::PacketEncodingBuffer* GetData() const;
//! Retrieves a non-const reference to the value of Data.
//! @return a non-const reference to the value of Data
AzNetworking::PacketEncodingBuffer& ModifyData();
//! Base serialize method for all serializable structures or classes to implement.
//! @param serializer ISerializer instance to use for serialization
//! @return boolean true for success, false for serialization failure
bool Serialize(AzNetworking::ISerializer& serializer);
private:
NetEntityRole m_networkRole = NetEntityRole::InvalidRole;
NetEntityId m_entityId = InvalidNetEntityId;
bool m_isDelete = false;
bool m_wasMigrated = false;
bool m_takeOwnership = false;
bool m_hasValidPrefabId = false;
PrefabEntityId m_prefabEntityId;
// Only allocated if we actually have data
// This is to prevent blowing out stack memory if we declare an array of these EntityUpdateMessages
AZStd::unique_ptr<AzNetworking::PacketEncodingBuffer> m_data;
};
}