Integrating latest 47acbe8
This commit is contained in:
+1258
File diff suppressed because it is too large
Load Diff
+217
@@ -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;;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user