diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h index 579ca195e5..c60119cdee 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h @@ -45,6 +45,7 @@ namespace Multiplayer AzNetworking::ByteBuffer<2048> m_userData; }; + using ClientDisconnectedEvent = AZ::Event<>; using ConnectionAcquiredEvent = AZ::Event; using SessionInitEvent = AZ::Event; using SessionShutdownEvent = AZ::Event; @@ -65,8 +66,12 @@ namespace Multiplayer //! @param state The state of this connection virtual void InitializeMultiplayer(MultiplayerAgentType state) = 0; + //! Adds a ClientDisconnectedEvent Handler which is invoked on the client when a disconnectio occurs + //! @param handler The ClientDisconnectedEvent Handler to add + virtual void AddClientDisconnectedHandler(ClientDisconnectedEvent::Handler& handler) = 0; + //! Adds a ConnectionAcquiredEvent Handler which is invoked when a new endpoint connects to the session. - //! @param handler The SessionInitEvent Handler to add + //! @param handler The ConnectionAcquiredEvent Handler to add virtual void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) = 0; //! Adds a SessionInitEvent Handler which is invoked when a new network session starts. diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.cpp b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.cpp index 11207df27d..3e2a7ca006 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.cpp +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.cpp @@ -22,7 +22,7 @@ namespace Multiplayer ( AzNetworking::IConnection* connection, AzNetworking::IConnectionListener& connectionListener, - AZStd::string providerTicket + const AZStd::string& providerTicket ) : m_connection(connection) , m_entityReplicationManager(*connection, connectionListener, EntityReplicationManager::Mode::LocalClientToRemoteServer) diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.h b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.h index 52c5dea00d..693e3814c4 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.h +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.h @@ -25,7 +25,7 @@ namespace Multiplayer ( AzNetworking::IConnection* connection, AzNetworking::IConnectionListener& connectionListener, - AZStd::string providerTicket = "" + const AZStd::string& providerTicket = "" ); ~ClientToServerConnectionData() override; @@ -39,7 +39,7 @@ namespace Multiplayer void SetCanSendUpdates(bool canSendUpdates) override; //! @} - AZStd::string GetProviderTicket() const; + const AZStd::string& GetProviderTicket() const; private: EntityReplicationManager m_entityReplicationManager; diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.inl b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.inl index f23c8dd1d9..44cbf10350 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.inl +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.inl @@ -22,7 +22,7 @@ namespace Multiplayer m_canSendUpdates = canSendUpdates; } - inline AZStd::string ClientToServerConnectionData::GetProviderTicket() const + inline const AZStd::string& ClientToServerConnectionData::GetProviderTicket() const { return m_providerTicket; } diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h index c171cdbe5d..0851299498 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h @@ -41,7 +41,7 @@ namespace Multiplayer NetworkEntityHandle GetPrimaryPlayerEntity(); const NetworkEntityHandle& GetPrimaryPlayerEntity() const; - AZStd::string GetProviderTicket() const; + const AZStd::string& GetProviderTicket() const; void SetProviderTicket(AZStd::string); private: diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl index 0427936f00..b56ed7097e 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl @@ -33,7 +33,7 @@ namespace Multiplayer return m_controlledEntity; } - inline AZStd::string ServerToClientConnectionData::GetProviderTicket() const + inline const AZStd::string& ServerToClientConnectionData::GetProviderTicket() const { return m_ticket; } diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index d0d2c609b3..5282490ad6 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -117,6 +117,31 @@ namespace Multiplayer behaviorContext->Class(); behaviorContext->Class(); behaviorContext->Class(); + + behaviorContext->Class("MultiplayerSystemComponent") + ->Attribute(AZ::Script::Attributes::Module, "multiplayer") + ->Attribute(AZ::Script::Attributes::Category, "Multiplayer") + ->Method("GetOnClientDisconnectedEvent", [](AZ::EntityId id) -> AZ::Event<>* + { + AZ::Entity* entity = AZ::Interface::Get()->FindEntity(id); + if (!entity) + { + AZ_Warning("Network Property", false, "NetworkTransformComponent GetOnScaleChangedEvent failed. The entity with id %s doesn't exist, please provide a valid entity id.", id.ToString().c_str()) + return nullptr; + } + + MultiplayerSystemComponent* mpComponent = entity->FindComponent(); + if (!mpComponent) + { + AZ_Warning("Multiplayer Property", false, "NetworkTransformComponent GetScale failed. Entity '%s' (id: %s) is missing NetworkTransformComponent, be sure to add NetworkTransformComponent to this entity.", entity->GetName().c_str(), id.ToString().c_str()) + return nullptr; + } + + return &mpComponent->m_clientDisconnectedEvent; + }) + ->Attribute( + AZ::Script::Attributes::AzEventDescription, + AZ::BehaviorAzEventDescription{"On Client Disconnected Event"}); } MultiplayerComponent::Reflect(context); @@ -173,12 +198,11 @@ namespace Multiplayer bool MultiplayerSystemComponent::RequestPlayerJoinSession(const AzFramework::SessionConnectionConfig& config) { AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::Client); - INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MPNetworkInterfaceName)); - const IpAddress ipAddress(config.m_ipAddress.c_str(), config.m_port, networkInterface->GetType()); - ConnectionId connectionId = networkInterface->Connect(ipAddress); + const IpAddress ipAddress(config.m_ipAddress.c_str(), config.m_port, m_networkInterface->GetType()); + ConnectionId connectionId = m_networkInterface->Connect(ipAddress); - AzNetworking::IConnection* connection = networkInterface->GetConnectionSet().GetConnection(connectionId); + AzNetworking::IConnection* connection = m_networkInterface->GetConnectionSet().GetConnection(connectionId); if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so { connection->SetUserData(new ClientToServerConnectionData(connection, *this, config.m_playerSessionId)); @@ -188,10 +212,15 @@ namespace Multiplayer void MultiplayerSystemComponent::RequestPlayerLeaveSession() { - AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::Uninitialized); - INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MPNetworkInterfaceName)); - auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::TerminatedByUser, TerminationEndpoint::Local); }; - networkInterface->GetConnectionSet().VisitConnections(visitor); + if (GetAgentType() == MultiplayerAgentType::Client) + { + AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::Uninitialized); + auto visitor = [](IConnection& connection) + { + connection.Disconnect(DisconnectReason::TerminatedByUser, TerminationEndpoint::Local); + }; + m_networkInterface->GetConnectionSet().VisitConnections(visitor); + } } bool MultiplayerSystemComponent::OnSessionHealthCheck() @@ -379,19 +408,24 @@ namespace Multiplayer [[maybe_unused]] MultiplayerPackets::Connect& packet ) { - if (connection->SendReliablePacket(MultiplayerPackets::Accept(InvalidHostId, sv_map))) + // Validate our session with the provider if any + if (AZ::Interface::Get() != nullptr) { - // Validate our session with the provider if any - if (AZ::Interface::Get() != nullptr) + AzFramework::PlayerConnectionConfig config; + config.m_playerConnectionId = aznumeric_cast(connection->GetConnectionId()); + config.m_playerSessionId = packet.GetTicket(); + if(!AZ::Interface::Get()->ValidatePlayerJoinSession(config)) { - AzFramework::PlayerConnectionConfig config; - config.m_playerConnectionId = aznumeric_cast(connection->GetConnectionId()); - config.m_playerSessionId = packet.GetTicket(); - AZ::Interface::Get()->ValidatePlayerJoinSession(config); - - reinterpret_cast(connection->GetUserData())->SetProviderTicket(packet.GetTicket().c_str()); + auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::TerminatedByUser, TerminationEndpoint::Local); }; + m_networkInterface->GetConnectionSet().VisitConnections(visitor); + return true; } + reinterpret_cast(connection->GetUserData())->SetProviderTicket(packet.GetTicket().c_str()); + } + + if (connection->SendReliablePacket(MultiplayerPackets::Accept(InvalidHostId, sv_map))) + { // Sync our console ConsoleReplicator consoleReplicator(connection); AZ::Interface::Get()->VisitRegisteredFunctors([&consoleReplicator](AZ::ConsoleFunctorBase* functor) { consoleReplicator.Visit(functor); }); @@ -606,11 +640,16 @@ namespace Multiplayer AZStd::string reasonString = ToString(reason); AZLOG_INFO("%s due to %s from remote address: %s", endpointString, reasonString.c_str(), connection->GetRemoteAddress().GetString().c_str()); - // The authority is shutting down its connection if (connection->GetConnectionRole() == ConnectionRole::Acceptor) { + // The authority is shutting down its connection m_shutdownEvent.Signal(m_networkInterface); } + else if (GetAgentType() == MultiplayerAgentType::Client && connection->GetConnectionRole() == ConnectionRole::Connector) + { + // The client is disconnecting + m_clientDisconnectedEvent.Signal(); + } // Clean up any multiplayer connection data we've bound to this connection instance if (connection->GetUserData() != nullptr) @@ -679,6 +718,11 @@ namespace Multiplayer AZLOG_INFO("Multiplayer operating in %s mode", GetEnumString(m_agentType)); } + void MultiplayerSystemComponent::AddClientDisconnectedHandler(ClientDisconnectedEvent::Handler& handler) + { + handler.Connect(m_clientDisconnectedEvent); + } + void MultiplayerSystemComponent::AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) { handler.Connect(m_connAcquiredEvent); diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index c089b243bd..0efef3ebe4 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -108,6 +108,7 @@ namespace Multiplayer //! @{ MultiplayerAgentType GetAgentType() const override; void InitializeMultiplayer(MultiplayerAgentType state) override; + void AddClientDisconnectedHandler(ClientDisconnectedEvent::Handler& handler) override; void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override; void AddSessionInitHandler(SessionInitEvent::Handler& handler) override; void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override; @@ -143,6 +144,7 @@ namespace Multiplayer SessionInitEvent m_initEvent; SessionShutdownEvent m_shutdownEvent; ConnectionAcquiredEvent m_connAcquiredEvent; + ClientDisconnectedEvent m_clientDisconnectedEvent; AZ::TimeMs m_lastReplicatedHostTimeMs = AZ::TimeMs{ 0 }; HostFrameId m_lastReplicatedHostFrameId = InvalidHostFrameId;