Further fixes to get client migrations functional, plus a bug fix from the netBindComponent lookup optimization
Signed-off-by: kberg-amzn <karlberg@amazon.com>
This commit is contained in:
@@ -32,7 +32,7 @@ namespace Multiplayer
|
||||
using EntityStopEvent = AZ::Event<const ConstNetworkEntityHandle&>;
|
||||
using EntityDirtiedEvent = AZ::Event<>;
|
||||
using EntitySyncRewindEvent = AZ::Event<>;
|
||||
using EntityServerMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, const HostId&, const HostId&, AzNetworking::ConnectionId>;
|
||||
using EntityServerMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, const HostId&>;
|
||||
using EntityPreRenderEvent = AZ::Event<float>;
|
||||
using EntityCorrectionEvent = AZ::Event<>;
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace Multiplayer
|
||||
void MarkDirty();
|
||||
void NotifyLocalChanges();
|
||||
void NotifySyncRewindState();
|
||||
void NotifyServerMigration(const HostId& remoteHostId, const HostId& migrateHostId, AzNetworking::ConnectionId connectionId);
|
||||
void NotifyServerMigration(const HostId& remoteHostId);
|
||||
void NotifyPreRender(float deltaTime);
|
||||
void NotifyCorrection();
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Multiplayer
|
||||
using ClientMigrationStartEvent = AZ::Event<ClientInputId>;
|
||||
using ClientMigrationEndEvent = AZ::Event<>;
|
||||
using ClientDisconnectedEvent = AZ::Event<>;
|
||||
using NotifyClientMigrationEvent = AZ::Event<const HostId&, uint64_t, ClientInputId, NetEntityId>;
|
||||
using NotifyClientMigrationEvent = AZ::Event<AzNetworking::ConnectionId, const HostId&, uint64_t, ClientInputId, NetEntityId>;
|
||||
using NotifyEntityMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, const HostId&>;
|
||||
using ConnectionAcquiredEvent = AZ::Event<MultiplayerAgentDatum>;
|
||||
using SessionInitEvent = AZ::Event<AzNetworking::INetworkInterface*>;
|
||||
@@ -131,11 +131,12 @@ namespace Multiplayer
|
||||
virtual void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) = 0;
|
||||
|
||||
//! Signals a NotifyClientMigrationEvent with the provided parameters.
|
||||
//! @param connectionId the connection id of the client that is migrating
|
||||
//! @param hostId the host id of the host the client is migrating to
|
||||
//! @param userIdentifier the user identifier the client will provide the new host to validate identity
|
||||
//! @param lastClientInputId the last processed clientInputId by the current host
|
||||
//! @param controlledEntityId the entityId of the clients autonomous entity
|
||||
virtual void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) = 0;
|
||||
virtual void SendNotifyClientMigrationEvent(AzNetworking::ConnectionId connectionId, const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) = 0;
|
||||
|
||||
//! Signals a NotifyEntityMigrationEvent with the provided parameters.
|
||||
//! @param entityHandle the network entity handle of the entity being migrated
|
||||
@@ -182,6 +183,13 @@ namespace Multiplayer
|
||||
//! @param controlledEntityId the controlled entityId of the players autonomous entity
|
||||
virtual void RegisterPlayerIdentifierForRejoin(uint64_t temporaryUserIdentifier, NetEntityId controlledEntityId) = 0;
|
||||
|
||||
//! Completes a client migration event by informing the appropriate client to migrate between hosts.
|
||||
//! @param temporaryUserIdentifier the temporary user identifier used to identify a player across hosts
|
||||
//! @param connectionId the connection id of the player being migrated
|
||||
//! @param publicHostId the public address of the new host the client should connect to
|
||||
//! @param migratedClientInputId the last clientInputId processed prior to migration
|
||||
virtual void CompleteClientMigration(uint64_t temporaryUserIdentifier, AzNetworking::ConnectionId connectionId, const HostId& publicHostId, ClientInputId migratedClientInputId) = 0;
|
||||
|
||||
//! Enables or disables automatic instantiation of netbound entities.
|
||||
//! This setting is controlled by the networking layer and should not be touched
|
||||
//! If enabled, netbound entities will instantiate as spawnables are loaded into the game world, generally true for the server
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Multiplayer
|
||||
using HostId = AzNetworking::IpAddress;
|
||||
static const HostId InvalidHostId = HostId();
|
||||
|
||||
AZ_TYPE_SAFE_INTEGRAL(NetEntityId, uint32_t);
|
||||
AZ_TYPE_SAFE_INTEGRAL(NetEntityId, uint64_t);
|
||||
static constexpr NetEntityId InvalidNetEntityId = static_cast<NetEntityId>(-1);
|
||||
|
||||
AZ_TYPE_SAFE_INTEGRAL(NetComponentId, uint16_t);
|
||||
|
||||
@@ -394,9 +394,9 @@ namespace Multiplayer
|
||||
m_syncRewindEvent.Signal();
|
||||
}
|
||||
|
||||
void NetBindComponent::NotifyServerMigration(const HostId& remoteHostId, const HostId& migrateHostId, AzNetworking::ConnectionId connectionId)
|
||||
void NetBindComponent::NotifyServerMigration(const HostId& remoteHostId)
|
||||
{
|
||||
m_entityServerMigrationEvent.Signal(m_netEntityHandle, remoteHostId, migrateHostId, connectionId);
|
||||
m_entityServerMigrationEvent.Signal(m_netEntityHandle, remoteHostId);
|
||||
}
|
||||
|
||||
void NetBindComponent::NotifyPreRender(float deltaTime)
|
||||
|
||||
@@ -27,9 +27,9 @@ namespace Multiplayer
|
||||
)
|
||||
: m_connection(connection)
|
||||
, m_controlledEntityRemovedHandler([this](const ConstNetworkEntityHandle&) { OnControlledEntityRemove(); })
|
||||
, m_controlledEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, const HostId& migrateHostId, AzNetworking::ConnectionId connectionId)
|
||||
, m_controlledEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId)
|
||||
{
|
||||
OnControlledEntityMigration(entityHandle, remoteHostId, migrateHostId, connectionId);
|
||||
OnControlledEntityMigration(entityHandle, remoteHostId);
|
||||
})
|
||||
, m_entityReplicationManager(*connection, connectionListener, EntityReplicationManager::Mode::LocalServerToRemoteClient)
|
||||
{
|
||||
@@ -102,9 +102,7 @@ namespace Multiplayer
|
||||
void ServerToClientConnectionData::OnControlledEntityMigration
|
||||
(
|
||||
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle,
|
||||
const HostId& remoteHostId,
|
||||
const HostId& migrateHostId,
|
||||
[[maybe_unused]] AzNetworking::ConnectionId connectionId
|
||||
const HostId& remoteHostId
|
||||
)
|
||||
{
|
||||
ClientInputId migratedClientInputId = ClientInputId{ 0 };
|
||||
@@ -121,11 +119,9 @@ namespace Multiplayer
|
||||
const uint64_t temporaryUserIdentifier = AzNetworking::CryptoRand64();
|
||||
|
||||
// Tell the new host that a client is about to (re)join
|
||||
GetMultiplayer()->SendNotifyClientMigrationEvent(remoteHostId, temporaryUserIdentifier, migratedClientInputId, m_controlledEntity.GetNetEntityId());
|
||||
|
||||
// Tell the client who to join
|
||||
MultiplayerPackets::ClientMigration clientMigration(migrateHostId, temporaryUserIdentifier, migratedClientInputId);
|
||||
GetConnection()->SendReliablePacket(clientMigration);
|
||||
GetMultiplayer()->SendNotifyClientMigrationEvent(GetConnection()->GetConnectionId(), remoteHostId, temporaryUserIdentifier, migratedClientInputId, m_controlledEntity.GetNetEntityId());
|
||||
// We need to send a MultiplayerPackets::ClientMigration packet to complete this process
|
||||
// This happens inside MultiplayerSystemComponent, once we're certain the remote host has appropriately prepared
|
||||
|
||||
m_controlledEntity = NetworkEntityHandle();
|
||||
m_canSendUpdates = false;
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Multiplayer
|
||||
|
||||
private:
|
||||
void OnControlledEntityRemove();
|
||||
void OnControlledEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, const HostId& migrateHostId, AzNetworking::ConnectionId connectionId);
|
||||
void OnControlledEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId);
|
||||
void OnGameplayStarted();
|
||||
|
||||
EntityReplicationManager m_entityReplicationManager;
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace Multiplayer
|
||||
m_canSendUpdates = canSendUpdates;
|
||||
}
|
||||
|
||||
|
||||
inline NetworkEntityHandle ServerToClientConnectionData::GetPrimaryPlayerEntity()
|
||||
{
|
||||
return m_controlledEntity;
|
||||
|
||||
@@ -212,12 +212,12 @@ namespace Multiplayer
|
||||
sv_port = port;
|
||||
}
|
||||
|
||||
InitializeMultiplayer(isDedicated ? MultiplayerAgentType::DedicatedServer : MultiplayerAgentType::ClientServer);
|
||||
const uint16_t maxPort = sv_port + sv_portRange;
|
||||
while (sv_port <= maxPort)
|
||||
{
|
||||
if (m_networkInterface->Listen(sv_port))
|
||||
{
|
||||
InitializeMultiplayer(isDedicated ? MultiplayerAgentType::DedicatedServer : MultiplayerAgentType::ClientServer);
|
||||
return true;
|
||||
}
|
||||
AZLOG_WARN("Failed to start listening on port %u, port is in use?", static_cast<uint32_t>(sv_port));
|
||||
@@ -510,6 +510,12 @@ namespace Multiplayer
|
||||
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<ServerToClientReplicationWindow>(controlledEntity, connection);
|
||||
connectionData->GetReplicationManager().SetReplicationWindow(AZStd::move(window));
|
||||
connectionData->SetControlledEntity(controlledEntity);
|
||||
|
||||
// If this is a migrate or rejoin, immediately ready the connection for updates
|
||||
if (packet.GetTemporaryUserId() != 0)
|
||||
{
|
||||
connectionData->SetCanSendUpdates(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (connection->SendReliablePacket(MultiplayerPackets::Accept(sv_map)))
|
||||
@@ -543,14 +549,16 @@ namespace Multiplayer
|
||||
}
|
||||
else
|
||||
{
|
||||
OnAutonomousEntityReplicatorCreated();
|
||||
// IConnectionData* connectionData = reinterpret_cast<IConnectionData*>(connection->GetUserData());
|
||||
// if (connectionData)
|
||||
// {
|
||||
// // @nt: TODO - delete once dropped RPC problem fixed
|
||||
// // Connection has migrated, we are now waiting for the autonomous entity replicator to be created
|
||||
// connectionData->GetReplicationManager().AddAutonomousEntityReplicatorCreatedHandler(m_autonomousEntityReplicatorCreatedHandler);
|
||||
// }
|
||||
// Bypass map loading and immediately ready the connection for updates
|
||||
IConnectionData* connectionData = reinterpret_cast<IConnectionData*>(connection->GetUserData());
|
||||
if (connectionData)
|
||||
{
|
||||
connectionData->SetCanSendUpdates(true);
|
||||
|
||||
// @nt: TODO - delete once dropped RPC problem fixed
|
||||
// Connection has migrated, we are now waiting for the autonomous entity replicator to be created
|
||||
connectionData->GetReplicationManager().AddAutonomousEntityReplicatorCreatedHandler(m_autonomousEntityReplicatorCreatedHandler);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -880,9 +888,9 @@ namespace Multiplayer
|
||||
handler.Connect(m_shutdownEvent);
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId)
|
||||
void MultiplayerSystemComponent::SendNotifyClientMigrationEvent(AzNetworking::ConnectionId connectionId, const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId)
|
||||
{
|
||||
m_notifyClientMigrationEvent.Signal(hostId, userIdentifier, lastClientInputId, controlledEntityId);
|
||||
m_notifyClientMigrationEvent.Signal(connectionId, hostId, userIdentifier, lastClientInputId, controlledEntityId);
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId)
|
||||
@@ -941,6 +949,17 @@ namespace Multiplayer
|
||||
m_playerRejoinData[temporaryUserIdentifier] = controlledEntityId;
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::CompleteClientMigration(uint64_t temporaryUserIdentifier, AzNetworking::ConnectionId connectionId, const HostId& publicHostId, ClientInputId migratedClientInputId)
|
||||
{
|
||||
IConnection* connection = m_networkInterface->GetConnectionSet().GetConnection(connectionId);
|
||||
if (connection != nullptr) // Make sure the player has not disconnected since the start of migration
|
||||
{
|
||||
// Tell the client who to join
|
||||
MultiplayerPackets::ClientMigration clientMigration(publicHostId, temporaryUserIdentifier, migratedClientInputId);
|
||||
connection->SendReliablePacket(clientMigration);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::SetShouldSpawnNetworkEntities(bool value)
|
||||
{
|
||||
m_spawnNetboundEntities = value;
|
||||
|
||||
@@ -116,7 +116,7 @@ namespace Multiplayer
|
||||
void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override;
|
||||
void AddSessionInitHandler(SessionInitEvent::Handler& handler) override;
|
||||
void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override;
|
||||
void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) override;
|
||||
void SendNotifyClientMigrationEvent(AzNetworking::ConnectionId connectionId, const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) override;
|
||||
void SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) override;
|
||||
void SendReadyForEntityUpdates(bool readyForEntityUpdates) override;
|
||||
AZ::TimeMs GetCurrentHostTimeMs() const override;
|
||||
@@ -126,6 +126,7 @@ namespace Multiplayer
|
||||
void SetFilterEntityManager(IFilterEntityManager* entityFilter) override;
|
||||
IFilterEntityManager* GetFilterEntityManager() override;
|
||||
void RegisterPlayerIdentifierForRejoin(uint64_t temporaryUserIdentifier, NetEntityId controlledEntityId) override;
|
||||
void CompleteClientMigration(uint64_t temporaryUserIdentifier, AzNetworking::ConnectionId connectionId, const HostId& publicHostId, ClientInputId migratedClientInputId) override;
|
||||
void SetShouldSpawnNetworkEntities(bool value) override;
|
||||
bool GetShouldSpawnNetworkEntities() const override;
|
||||
//! @}
|
||||
|
||||
+10
-8
@@ -386,17 +386,18 @@ namespace Multiplayer
|
||||
if (changedRemoteRole || changedLocalRole)
|
||||
{
|
||||
const uint32_t intEntityId = static_cast<uint32_t>(netBindComponent->GetNetEntityId());
|
||||
const char* entityName = entityReplicator->GetEntityHandle().GetEntity()->GetName().c_str();
|
||||
if (changedLocalRole)
|
||||
{
|
||||
const char* oldRoleString = GetEnumString(entityReplicator->GetRemoteNetworkRole());
|
||||
const char* newRoleString = GetEnumString(remoteNetworkRole);
|
||||
AZLOG(NET_ReplicatorRoles, "Replicator %u changed local role, old role = %s, new role = %s", intEntityId, oldRoleString, newRoleString);
|
||||
AZLOG(NET_ReplicatorRoles, "Replicator %s(%u) changed local role, old role = %s, new role = %s", entityName, intEntityId, oldRoleString, newRoleString);
|
||||
}
|
||||
if (changedRemoteRole)
|
||||
{
|
||||
const char* oldRoleString = GetEnumString(entityReplicator->GetBoundLocalNetworkRole());
|
||||
const char* newRoleString = GetEnumString(netBindComponent->GetNetEntityRole());
|
||||
AZLOG(NET_ReplicatorRoles, "Replicator %u changed remote role, old role = %s, new role = %s", intEntityId, oldRoleString, newRoleString);
|
||||
AZLOG(NET_ReplicatorRoles, "Replicator %s(%u) changed remote role, old role = %s, new role = %s", entityName, intEntityId, oldRoleString, newRoleString);
|
||||
}
|
||||
|
||||
// If we changed roles, we need to reset everything
|
||||
@@ -605,9 +606,9 @@ namespace Multiplayer
|
||||
NetBindComponent* netBindComponent = replicatorEntity.GetNetBindComponent();
|
||||
AZ_Assert(netBindComponent != nullptr, "No NetBindComponent");
|
||||
|
||||
if (createEntity)
|
||||
if (netBindComponent->GetOwningConnectionId() != invokingConnection->GetConnectionId())
|
||||
{
|
||||
// Always set our invoking connectionId for any newly created entities, since this connection now 'owns' them from a rewind perspective
|
||||
// Always ensure our owning connectionId is correct for correct rewind behaviour
|
||||
netBindComponent->SetOwningConnectionId(invokingConnection->GetConnectionId());
|
||||
}
|
||||
|
||||
@@ -617,10 +618,11 @@ namespace Multiplayer
|
||||
AZ_Assert(localNetworkRole != NetEntityRole::Authority, "UpdateMessage trying to set local role to Authority, this should only happen via migration");
|
||||
AZLOG_INFO
|
||||
(
|
||||
"EntityReplicationManager: Changing network role on entity %u, old role %u new role %u",
|
||||
"EntityReplicationManager: Changing network role on entity %s(%u), old role %s new role %s",
|
||||
replicatorEntity.GetEntity()->GetName().c_str(),
|
||||
aznumeric_cast<uint32_t>(netEntityId),
|
||||
aznumeric_cast<uint32_t>(netBindComponent->GetNetEntityRole()),
|
||||
aznumeric_cast<uint32_t>(localNetworkRole)
|
||||
GetEnumString(netBindComponent->GetNetEntityRole()),
|
||||
GetEnumString(localNetworkRole)
|
||||
);
|
||||
|
||||
if (NetworkRoleHasController(localNetworkRole))
|
||||
@@ -1135,7 +1137,7 @@ namespace Multiplayer
|
||||
|
||||
if (m_updateMode == EntityReplicationManager::Mode::LocalServerToRemoteServer)
|
||||
{
|
||||
netBindComponent->NotifyServerMigration(GetRemoteHostId(), GetMigrateHostId(), GetConnection().GetConnectionId());
|
||||
netBindComponent->NotifyServerMigration(GetRemoteHostId());
|
||||
}
|
||||
|
||||
bool didSucceed = true;
|
||||
|
||||
@@ -18,22 +18,14 @@ namespace Multiplayer
|
||||
{
|
||||
ConstNetworkEntityHandle::ConstNetworkEntityHandle(AZ::Entity* entity, const NetworkEntityTracker* networkEntityTracker)
|
||||
: m_entity(entity)
|
||||
, m_networkEntityTracker(networkEntityTracker)
|
||||
, m_networkEntityTracker((networkEntityTracker != nullptr) ? networkEntityTracker : GetNetworkEntityTracker())
|
||||
{
|
||||
if (m_networkEntityTracker == nullptr)
|
||||
{
|
||||
m_networkEntityTracker = GetNetworkEntityTracker();
|
||||
}
|
||||
|
||||
if (m_networkEntityTracker)
|
||||
{
|
||||
m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity);
|
||||
}
|
||||
AZ_Assert(m_networkEntityTracker, "NetworkEntityTracker is not valid");
|
||||
m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity);
|
||||
|
||||
if (entity)
|
||||
{
|
||||
AZ_Assert(networkEntityTracker, "NetworkEntityTracker is not valid");
|
||||
m_netBindComponent = networkEntityTracker->GetNetBindComponent(entity);
|
||||
m_netBindComponent = m_networkEntityTracker->GetNetBindComponent(entity);
|
||||
if (m_netBindComponent != nullptr)
|
||||
{
|
||||
m_netEntityId = m_netBindComponent->GetNetEntityId();
|
||||
|
||||
@@ -46,6 +46,20 @@ namespace Multiplayer
|
||||
void NetworkEntityManager::Initialize(const HostId& hostId, AZStd::unique_ptr<IEntityDomain> entityDomain)
|
||||
{
|
||||
m_hostId = hostId;
|
||||
|
||||
// Configure our vended NetEntityIds so that no two hosts generate the same NetEntityId
|
||||
{
|
||||
// Needs more thought
|
||||
const uint64_t addrPortion = hostId.GetAddress(AzNetworking::ByteOrder::Host);
|
||||
const uint64_t portPortion = hostId.GetPort(AzNetworking::ByteOrder::Host);
|
||||
const uint64_t hostIdentifier = (portPortion << 32) | addrPortion;
|
||||
const AZ::HashValue32 hostHash = AZ::TypeHash32(hostIdentifier);
|
||||
|
||||
NetEntityId hostEntityIdOffset = static_cast<NetEntityId>(hostHash) << 32;
|
||||
m_nextEntityId &= NetEntityId{ 0x0000000000000000FFFFFFFFFFFFFFFF };
|
||||
m_nextEntityId |= hostEntityIdOffset;
|
||||
}
|
||||
|
||||
m_entityDomain = AZStd::move(entityDomain);
|
||||
m_updateEntityDomainEvent.Enqueue(net_EntityDomainUpdateMs, true);
|
||||
m_entityDomain->ActivateTracking(m_ownedEntities);
|
||||
|
||||
Reference in New Issue
Block a user