Further fixes to get client migrations functional, plus a bug fix from the netBindComponent lookup optimization

Signed-off-by: kberg-amzn <karlberg@amazon.com>
monroegm-disable-blank-issue-2
kberg-amzn 4 years ago
parent 18340f2b1b
commit 4449e83c3b

@ -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;
//! @}

@ -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);

Loading…
Cancel
Save