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;;
};
}