From c55f65b78ff1dbc36de9654af0b8e429a206ca42 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Thu, 3 Jun 2021 15:35:16 -0700 Subject: [PATCH 01/11] Integrate parts of Session Server API --- .../Source/MultiplayerSystemComponent.cpp | 28 +++++++++++++++++++ .../Code/Source/MultiplayerSystemComponent.h | 14 ++++++++++ 2 files changed, 42 insertions(+) diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 0818f605df..50da2136fb 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include @@ -142,6 +144,7 @@ namespace Multiplayer void MultiplayerSystemComponent::Activate() { AZ::TickBus::Handler::BusConnect(); + AzFramework::SessionNotificationBus::Handler::BusConnect(); m_networkInterface = AZ::Interface::Get()->CreateNetworkInterface(AZ::Name(MPNetworkInterfaceName), sv_protocol, TrustZone::ExternalClientToServer, *this); m_consoleCommandHandler.Connect(AZ::Interface::Get()->GetConsoleCommandInvokedEvent()); AZ::Interface::Register(this); @@ -153,9 +156,34 @@ namespace Multiplayer void MultiplayerSystemComponent::Deactivate() { AZ::Interface::Unregister(this); + AzFramework::SessionNotificationBus::Handler::BusDisconnect(); AZ::TickBus::Handler::BusDisconnect(); } + bool MultiplayerSystemComponent::OnSessionHealthCheck() + { + return true; + } + + bool MultiplayerSystemComponent::OnCreateSessionBegin(const AzFramework::SessionConfig& sessionConfig) + { + Multiplayer::MultiplayerAgentType serverType = sv_isDedicated ? MultiplayerAgentType::DedicatedServer : MultiplayerAgentType::ClientServer; + AZ::Interface::Get()->InitializeMultiplayer(serverType); + return m_networkInterface->Listen(sessionConfig.m_port); + } + + bool MultiplayerSystemComponent::OnDestroySessionBegin() + { + bool disconnectSuccessful = true; + IConnectionSet& connectionSet = m_networkInterface->GetConnectionSet(); + connectionSet.VisitConnections([&disconnectSuccessful](IConnection& connection) + { + bool didDisconnect = connection.Disconnect(DisconnectReason::TerminatedByServer, TerminationEndpoint::Remote); + disconnectSuccessful = disconnectSuccessful && didDisconnect; + }); + return disconnectSuccessful; + } + void MultiplayerSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) { AZ::TimeMs deltaTimeMs = aznumeric_cast(static_cast(deltaTime * 1000.0f)); diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index e8e05a9d4c..bd989cf841 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -25,8 +25,14 @@ #include #include #include +#include #include +namespace AzFramework +{ + struct SessionConfig; +} + namespace AzNetworking { class INetworkInterface; @@ -38,6 +44,7 @@ namespace Multiplayer class MultiplayerSystemComponent final : public AZ::Component , public AZ::TickBus::Handler + , public AzFramework::SessionNotificationBus::Handler , public AzNetworking::IConnectionListener , public IMultiplayer { @@ -58,6 +65,13 @@ namespace Multiplayer void Deactivate() override; //! @} + //! AzFramework::SessionNotificationBus::Handler overrides. + //! @{ + bool OnSessionHealthCheck() override; + bool OnCreateSessionBegin(const AzFramework::SessionConfig& sessionConfig) override; + bool OnDestroySessionBegin() override; + //! @} + //! AZ::TickBus::Handler overrides. //! @{ void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; From 40c7a6bd2d530d53a2f19d365b2639f40490f4c8 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Mon, 7 Jun 2021 17:02:42 -0700 Subject: [PATCH 02/11] Integrate remaining requests and rename Handling Requests interfaces for clarity --- .../Session/ISessionHandlingRequests.h | 33 +++++---- .../Source/MultiplayerSystemComponent.cpp | 67 +++++++++++++++++++ .../Code/Source/MultiplayerSystemComponent.h | 9 +++ 3 files changed, 95 insertions(+), 14 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h b/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h index a0731626ef..2537842d8a 100644 --- a/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h +++ b/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h @@ -45,14 +45,14 @@ namespace AzFramework AZStd::string m_playerSessionId; }; - //! ISessionHandlingClientRequests - //! The session handling events to invoke multiplayer component handle the work on client side - class ISessionHandlingClientRequests + //! ISessionLocalUserRequests + //! Requests made to the local user to manage their connection to a session + class ISessionLocalUserRequests { public: - AZ_RTTI(ISessionHandlingClientRequests, "{41DE6BD3-72BC-4443-BFF9-5B1B9396657A}"); - ISessionHandlingClientRequests() = default; - virtual ~ISessionHandlingClientRequests() = default; + AZ_RTTI(ISessionLocalUserRequests, "{41DE6BD3-72BC-4443-BFF9-5B1B9396657A}"); + ISessionLocalUserRequests() = default; + virtual ~ISessionLocalUserRequests() = default; // Request the player join session // @param sessionConnectionConfig The required properties to handle the player join session process @@ -63,14 +63,14 @@ namespace AzFramework virtual void RequestPlayerLeaveSession() = 0; }; - //! ISessionHandlingServerRequests - //! The session handling events to invoke server provider handle the work on server side - class ISessionHandlingServerRequests + //! ISessionProviderRequests + //! Requests made to the service providing server/fleet management by the server + class ISessionProviderRequests { public: - AZ_RTTI(ISessionHandlingServerRequests, "{4F0C17BA-F470-4242-A8CB-EC7EA805257C}"); - ISessionHandlingServerRequests() = default; - virtual ~ISessionHandlingServerRequests() = default; + AZ_RTTI(ISessionProviderRequests, "{4F0C17BA-F470-4242-A8CB-EC7EA805257C}"); + ISessionProviderRequests() = default; + virtual ~ISessionProviderRequests() = default; // Handle the destroy session process virtual void HandleDestroySession() = 0; @@ -84,9 +84,14 @@ namespace AzFramework // @param playerConnectionConfig The required properties to handle the player leave session process virtual void HandlePlayerLeaveSession(const PlayerConnectionConfig& playerConnectionConfig) = 0; - // Retrieves the file location of a pem-encoded TLS certificate + // Retrieves the file location of a pem-encoded TLS certificate for Client to Server communication // @return If successful, returns the file location of TLS certificate file; if not successful, returns // empty string. - virtual AZStd::string GetSessionCertificate() = 0; + virtual AZStd::string GetExternalSessionCertificate() = 0; + + // Retrieves the file location of a pem-encoded TLS certificate for Server to Server communication + // @return If successful, returns the file location of TLS certificate file; if not successful, returns + // empty string. + virtual AZStd::string GetInternalSessionCertificate() = 0; }; } // namespace AzFramework diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index ce4b7d8601..18d8bd6e2e 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -169,6 +170,24 @@ namespace Multiplayer AZ::TickBus::Handler::BusDisconnect(); } + 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()); + networkInterface->Connect(ipAddress); + return true; + } + + 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); + } + bool MultiplayerSystemComponent::OnSessionHealthCheck() { return true; @@ -176,6 +195,21 @@ namespace Multiplayer bool MultiplayerSystemComponent::OnCreateSessionBegin(const AzFramework::SessionConfig& sessionConfig) { + // Check if session manager has a certificate for us and pass it along if so + AZ::CVarFixedString externalCertPath = AZ::CVarFixedString(AZ::Interface::Get()->GetExternalSessionCertificate()); + if (!externalCertPath.empty()) + { + AZ::CVarFixedString commandString = "net_SslExternalCertificateFile " + externalCertPath; + AZ::Interface::Get()->PerformCommand(commandString.c_str()); + } + + AZ::CVarFixedString internalCertPath = AZ::CVarFixedString(AZ::Interface::Get()->GetInternalSessionCertificate()); + if (!internalCertPath.empty()) + { + AZ::CVarFixedString commandString = "net_SslInternalCertificateFile " + internalCertPath; + AZ::Interface::Get()->PerformCommand(commandString.c_str()); + } + Multiplayer::MultiplayerAgentType serverType = sv_isDedicated ? MultiplayerAgentType::DedicatedServer : MultiplayerAgentType::ClientServer; AZ::Interface::Get()->InitializeMultiplayer(serverType); return m_networkInterface->Listen(sessionConfig.m_port); @@ -497,6 +531,10 @@ namespace Multiplayer { AZLOG_INFO("New incoming connection from remote address: %s", connection->GetRemoteAddress().GetString().c_str()); m_connAcquiredEvent.Signal(datum); + AzFramework::PlayerConnectionConfig config; + config.m_playerConnectionId = aznumeric_cast(connection->GetConnectionId()); + config.m_playerSessionId = AZStd::to_string(config.m_playerConnectionId); + AZ::Interface::Get()->ValidatePlayerJoinSession(config); } // Hosts will spawn a new default player prefab for the user that just connected @@ -558,6 +596,34 @@ namespace Multiplayer delete connectionData; connection->SetUserData(nullptr); } + + // Signal to session management that a user triggered a disconnect + if (m_agentType == MultiplayerAgentType::Client && connection->GetConnectionRole() == ConnectionRole::Connector) + { + AZ::Interface::Get()->LeaveSession(); + } + + // Signal to session management that a user has left the server + if (m_agentType == MultiplayerAgentType::DedicatedServer || m_agentType == MultiplayerAgentType::ClientServer) + { + if (connection->GetConnectionRole() == ConnectionRole::Connector) + { + AzFramework::PlayerConnectionConfig config; + config.m_playerConnectionId = aznumeric_cast(connection->GetConnectionId()); + config.m_playerSessionId = AZStd::to_string(config.m_playerConnectionId); + AZ::Interface::Get()->HandlePlayerLeaveSession(config); + } + } + + // Signal to session management when there are no remaining players in a dedicated server for potential cleanup + // We avoid this for client server as the host itself is a user + if (m_agentType == MultiplayerAgentType::DedicatedServer) + { + if (m_networkInterface->GetConnectionSet().GetConnectionCount() == 0) + { + AZ::Interface::Get()->HandleDestroySession(); + } + } } MultiplayerAgentType MultiplayerSystemComponent::GetAgentType() const @@ -788,6 +854,7 @@ namespace Multiplayer } AZ_CONSOLEFREEFUNC(host, AZ::ConsoleFunctorFlags::DontReplicate, "Opens a multiplayer connection as a host for other clients to connect to"); + void connect([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::Client); diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index ab3e54ad6d..505df52a6a 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include @@ -45,6 +47,7 @@ namespace Multiplayer : public AZ::Component , public AZ::TickBus::Handler , public AzFramework::SessionNotificationBus::Handler + , public AzFramework::ISessionLocalUserRequests , public AzNetworking::IConnectionListener , public IMultiplayer { @@ -96,6 +99,12 @@ namespace Multiplayer void OnDisconnect(AzNetworking::IConnection* connection, AzNetworking::DisconnectReason reason, AzNetworking::TerminationEndpoint endpoint) override; //! @} + //! ISessionLocalUserRequests interface + //! @{ + bool RequestPlayerJoinSession(const AzFramework::SessionConnectionConfig& sessionConnectionConfig) override; + void RequestPlayerLeaveSession() override; + //! @} + //! IMultiplayer interface //! @{ MultiplayerAgentType GetAgentType() const override; From 03989f77bb120188bad5c0cb9fdd973becd988f7 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Mon, 7 Jun 2021 17:06:00 -0700 Subject: [PATCH 03/11] Cleanup includes --- Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp | 1 - Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h | 1 - 2 files changed, 2 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 18d8bd6e2e..44e17c0ef4 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index 505df52a6a..f6ca670d87 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include From 6f4c0c2ce898dcdf84cb02c0e34e3e740a72c1ae Mon Sep 17 00:00:00 2001 From: puvvadar Date: Mon, 7 Jun 2021 17:12:29 -0700 Subject: [PATCH 04/11] Rename interfaces for clarity --- .../Session/ISessionHandlingRequests.h | 20 +++++++++---------- .../Source/MultiplayerSystemComponent.cpp | 12 ++++++----- .../Code/Source/MultiplayerSystemComponent.h | 4 ++-- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h b/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h index 2537842d8a..10c1d1cdd5 100644 --- a/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h +++ b/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h @@ -45,14 +45,14 @@ namespace AzFramework AZStd::string m_playerSessionId; }; - //! ISessionLocalUserRequests - //! Requests made to the local user to manage their connection to a session - class ISessionLocalUserRequests + //! ISessionHandlingClientRequests + //! Requests made to the client to manage their connection to a session + class ISessionHandlingClientRequests { public: - AZ_RTTI(ISessionLocalUserRequests, "{41DE6BD3-72BC-4443-BFF9-5B1B9396657A}"); - ISessionLocalUserRequests() = default; - virtual ~ISessionLocalUserRequests() = default; + AZ_RTTI(ISessionHandlingClientRequests, "{41DE6BD3-72BC-4443-BFF9-5B1B9396657A}"); + ISessionHandlingClientRequests() = default; + virtual ~ISessionHandlingClientRequests() = default; // Request the player join session // @param sessionConnectionConfig The required properties to handle the player join session process @@ -65,12 +65,12 @@ namespace AzFramework //! ISessionProviderRequests //! Requests made to the service providing server/fleet management by the server - class ISessionProviderRequests + class ISessionHandlingProviderRequests { public: - AZ_RTTI(ISessionProviderRequests, "{4F0C17BA-F470-4242-A8CB-EC7EA805257C}"); - ISessionProviderRequests() = default; - virtual ~ISessionProviderRequests() = default; + AZ_RTTI(ISessionHandlingProviderRequests, "{4F0C17BA-F470-4242-A8CB-EC7EA805257C}"); + ISessionHandlingProviderRequests() = default; + virtual ~ISessionHandlingProviderRequests() = default; // Handle the destroy session process virtual void HandleDestroySession() = 0; diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 44e17c0ef4..336f27e7e4 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -157,6 +157,7 @@ namespace Multiplayer m_networkInterface = AZ::Interface::Get()->CreateNetworkInterface(AZ::Name(MPNetworkInterfaceName), sv_protocol, TrustZone::ExternalClientToServer, *this); m_consoleCommandHandler.Connect(AZ::Interface::Get()->GetConsoleCommandInvokedEvent()); AZ::Interface::Register(this); + AZ::Interface::Register(this); //! Register our gems multiplayer components to assign NetComponentIds RegisterMultiplayerComponents(); @@ -164,6 +165,7 @@ namespace Multiplayer void MultiplayerSystemComponent::Deactivate() { + AZ::Interface::Unregister(this); AZ::Interface::Unregister(this); AzFramework::SessionNotificationBus::Handler::BusDisconnect(); AZ::TickBus::Handler::BusDisconnect(); @@ -195,14 +197,14 @@ namespace Multiplayer bool MultiplayerSystemComponent::OnCreateSessionBegin(const AzFramework::SessionConfig& sessionConfig) { // Check if session manager has a certificate for us and pass it along if so - AZ::CVarFixedString externalCertPath = AZ::CVarFixedString(AZ::Interface::Get()->GetExternalSessionCertificate()); + AZ::CVarFixedString externalCertPath = AZ::CVarFixedString(AZ::Interface::Get()->GetExternalSessionCertificate()); if (!externalCertPath.empty()) { AZ::CVarFixedString commandString = "net_SslExternalCertificateFile " + externalCertPath; AZ::Interface::Get()->PerformCommand(commandString.c_str()); } - AZ::CVarFixedString internalCertPath = AZ::CVarFixedString(AZ::Interface::Get()->GetInternalSessionCertificate()); + AZ::CVarFixedString internalCertPath = AZ::CVarFixedString(AZ::Interface::Get()->GetInternalSessionCertificate()); if (!internalCertPath.empty()) { AZ::CVarFixedString commandString = "net_SslInternalCertificateFile " + internalCertPath; @@ -533,7 +535,7 @@ namespace Multiplayer AzFramework::PlayerConnectionConfig config; config.m_playerConnectionId = aznumeric_cast(connection->GetConnectionId()); config.m_playerSessionId = AZStd::to_string(config.m_playerConnectionId); - AZ::Interface::Get()->ValidatePlayerJoinSession(config); + AZ::Interface::Get()->ValidatePlayerJoinSession(config); } // Hosts will spawn a new default player prefab for the user that just connected @@ -610,7 +612,7 @@ namespace Multiplayer AzFramework::PlayerConnectionConfig config; config.m_playerConnectionId = aznumeric_cast(connection->GetConnectionId()); config.m_playerSessionId = AZStd::to_string(config.m_playerConnectionId); - AZ::Interface::Get()->HandlePlayerLeaveSession(config); + AZ::Interface::Get()->HandlePlayerLeaveSession(config); } } @@ -620,7 +622,7 @@ namespace Multiplayer { if (m_networkInterface->GetConnectionSet().GetConnectionCount() == 0) { - AZ::Interface::Get()->HandleDestroySession(); + AZ::Interface::Get()->HandleDestroySession(); } } } diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index f6ca670d87..c089b243bd 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -46,7 +46,7 @@ namespace Multiplayer : public AZ::Component , public AZ::TickBus::Handler , public AzFramework::SessionNotificationBus::Handler - , public AzFramework::ISessionLocalUserRequests + , public AzFramework::ISessionHandlingClientRequests , public AzNetworking::IConnectionListener , public IMultiplayer { @@ -98,7 +98,7 @@ namespace Multiplayer void OnDisconnect(AzNetworking::IConnection* connection, AzNetworking::DisconnectReason reason, AzNetworking::TerminationEndpoint endpoint) override; //! @} - //! ISessionLocalUserRequests interface + //! ISessionHandlingClientRequests interface //! @{ bool RequestPlayerJoinSession(const AzFramework::SessionConnectionConfig& sessionConnectionConfig) override; void RequestPlayerLeaveSession() override; From 45b1bbc85cd273a5f0392488b862b2395620e005 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Mon, 7 Jun 2021 17:13:52 -0700 Subject: [PATCH 05/11] Fix duplicate include --- Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 336f27e7e4..bd5f4e63de 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -36,7 +36,6 @@ #include #include #include -#include #include From 2b227a17d1ce1ddad9517c3ffa9b83ca269f8885 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Mon, 7 Jun 2021 17:20:12 -0700 Subject: [PATCH 06/11] Remove extraneous code --- .../Multiplayer/Code/Source/MultiplayerSystemComponent.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index bd5f4e63de..6f8569251f 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -597,12 +597,6 @@ namespace Multiplayer connection->SetUserData(nullptr); } - // Signal to session management that a user triggered a disconnect - if (m_agentType == MultiplayerAgentType::Client && connection->GetConnectionRole() == ConnectionRole::Connector) - { - AZ::Interface::Get()->LeaveSession(); - } - // Signal to session management that a user has left the server if (m_agentType == MultiplayerAgentType::DedicatedServer || m_agentType == MultiplayerAgentType::ClientServer) { @@ -854,7 +848,6 @@ namespace Multiplayer } AZ_CONSOLEFREEFUNC(host, AZ::ConsoleFunctorFlags::DontReplicate, "Opens a multiplayer connection as a host for other clients to connect to"); - void connect([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::Client); From fa55b495c4401b3ac626ec352e59e8a7107871c1 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 8 Jun 2021 13:36:27 -0700 Subject: [PATCH 07/11] Add handling for session provider ticket --- .../Session/ISessionHandlingRequests.h | 6 +- .../AutoGen/Multiplayer.AutoPackets.xml | 1 + .../ClientToServerConnectionData.cpp | 4 +- .../ClientToServerConnectionData.h | 6 +- .../ClientToServerConnectionData.inl | 5 ++ .../ServerToClientConnectionData.h | 3 + .../ServerToClientConnectionData.inl | 10 +++ .../Source/MultiplayerSystemComponent.cpp | 65 +++++++++++++------ 8 files changed, 75 insertions(+), 25 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h b/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h index 10c1d1cdd5..d55f38f65c 100644 --- a/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h +++ b/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h @@ -46,7 +46,7 @@ namespace AzFramework }; //! ISessionHandlingClientRequests - //! Requests made to the client to manage their connection to a session + //! Requests made to the client to manage their membership in a session class ISessionHandlingClientRequests { public: @@ -87,11 +87,11 @@ namespace AzFramework // Retrieves the file location of a pem-encoded TLS certificate for Client to Server communication // @return If successful, returns the file location of TLS certificate file; if not successful, returns // empty string. - virtual AZStd::string GetExternalSessionCertificate() = 0; + virtual AZ::IO::Path GetExternalSessionCertificate() = 0; // Retrieves the file location of a pem-encoded TLS certificate for Server to Server communication // @return If successful, returns the file location of TLS certificate file; if not successful, returns // empty string. - virtual AZStd::string GetInternalSessionCertificate() = 0; + virtual AZ::IO::Path GetInternalSessionCertificate() = 0; }; } // namespace AzFramework diff --git a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml index 642832805d..ce8931107f 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml @@ -9,6 +9,7 @@ + diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.cpp b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.cpp index ee308f6ed8..11207df27d 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.cpp +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.cpp @@ -21,10 +21,12 @@ namespace Multiplayer ClientToServerConnectionData::ClientToServerConnectionData ( AzNetworking::IConnection* connection, - AzNetworking::IConnectionListener& connectionListener + AzNetworking::IConnectionListener& connectionListener, + AZStd::string providerTicket ) : m_connection(connection) , m_entityReplicationManager(*connection, connectionListener, EntityReplicationManager::Mode::LocalClientToRemoteServer) + , m_providerTicket(providerTicket) { m_entityReplicationManager.SetMaxRemoteEntitiesPendingCreationCount(cl_ClientMaxRemoteEntitiesPendingCreationCount); m_entityReplicationManager.SetEntityPendingRemovalMs(cl_ClientEntityReplicatorPendingRemovalTimeMs); diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.h b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.h index 2e7be47842..52c5dea00d 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.h +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.h @@ -24,7 +24,8 @@ namespace Multiplayer ClientToServerConnectionData ( AzNetworking::IConnection* connection, - AzNetworking::IConnectionListener& connectionListener + AzNetworking::IConnectionListener& connectionListener, + AZStd::string providerTicket = "" ); ~ClientToServerConnectionData() override; @@ -38,8 +39,11 @@ namespace Multiplayer void SetCanSendUpdates(bool canSendUpdates) override; //! @} + AZStd::string GetProviderTicket() const; + private: EntityReplicationManager m_entityReplicationManager; + AZStd::string m_providerTicket; AzNetworking::IConnection* m_connection = nullptr; bool m_canSendUpdates = true; }; diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.inl b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.inl index 6d4a332b6e..f23c8dd1d9 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.inl +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.inl @@ -21,4 +21,9 @@ namespace Multiplayer { m_canSendUpdates = canSendUpdates; } + + inline 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 faa11bc225..c171cdbe5d 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h @@ -41,6 +41,8 @@ namespace Multiplayer NetworkEntityHandle GetPrimaryPlayerEntity(); const NetworkEntityHandle& GetPrimaryPlayerEntity() const; + AZStd::string GetProviderTicket() const; + void SetProviderTicket(AZStd::string); private: void OnControlledEntityRemove(); @@ -51,6 +53,7 @@ namespace Multiplayer NetworkEntityHandle m_controlledEntity; EntityStopEvent::Handler m_controlledEntityRemovedHandler; EntityServerMigrationEvent::Handler m_controlledEntityMigrationHandler; + AZStd::string m_ticket; AzNetworking::IConnection* m_connection = nullptr; bool m_canSendUpdates = false; }; diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl index 0a4215a363..0427936f00 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl @@ -32,4 +32,14 @@ namespace Multiplayer { return m_controlledEntity; } + + inline AZStd::string ServerToClientConnectionData::GetProviderTicket() const + { + return m_ticket; + } + + inline void ServerToClientConnectionData::SetProviderTicket(AZStd::string ticket) + { + m_ticket = ticket; + } } diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 6f8569251f..d0d2c609b3 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -176,7 +176,13 @@ namespace Multiplayer INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MPNetworkInterfaceName)); const IpAddress ipAddress(config.m_ipAddress.c_str(), config.m_port, networkInterface->GetType()); - networkInterface->Connect(ipAddress); + ConnectionId connectionId = networkInterface->Connect(ipAddress); + + AzNetworking::IConnection* connection = 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)); + } return true; } @@ -196,18 +202,23 @@ namespace Multiplayer bool MultiplayerSystemComponent::OnCreateSessionBegin(const AzFramework::SessionConfig& sessionConfig) { // Check if session manager has a certificate for us and pass it along if so - AZ::CVarFixedString externalCertPath = AZ::CVarFixedString(AZ::Interface::Get()->GetExternalSessionCertificate()); - if (!externalCertPath.empty()) + if (AZ::Interface::Get() != nullptr) { - AZ::CVarFixedString commandString = "net_SslExternalCertificateFile " + externalCertPath; - AZ::Interface::Get()->PerformCommand(commandString.c_str()); - } + AZ::CVarFixedString externalCertPath = AZ::CVarFixedString( + AZ::Interface::Get()->GetExternalSessionCertificate().c_str()); + if (!externalCertPath.empty()) + { + AZ::CVarFixedString commandString = "net_SslExternalCertificateFile " + externalCertPath; + AZ::Interface::Get()->PerformCommand(commandString.c_str()); + } - AZ::CVarFixedString internalCertPath = AZ::CVarFixedString(AZ::Interface::Get()->GetInternalSessionCertificate()); - if (!internalCertPath.empty()) - { - AZ::CVarFixedString commandString = "net_SslInternalCertificateFile " + internalCertPath; - AZ::Interface::Get()->PerformCommand(commandString.c_str()); + AZ::CVarFixedString internalCertPath = AZ::CVarFixedString( + AZ::Interface::Get()->GetInternalSessionCertificate().c_str()); + if (!internalCertPath.empty()) + { + AZ::CVarFixedString commandString = "net_SslInternalCertificateFile " + internalCertPath; + AZ::Interface::Get()->PerformCommand(commandString.c_str()); + } } Multiplayer::MultiplayerAgentType serverType = sv_isDedicated ? MultiplayerAgentType::DedicatedServer : MultiplayerAgentType::ClientServer; @@ -370,6 +381,17 @@ namespace Multiplayer { if (connection->SendReliablePacket(MultiplayerPackets::Accept(InvalidHostId, sv_map))) { + // 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(); + AZ::Interface::Get()->ValidatePlayerJoinSession(config); + + reinterpret_cast(connection->GetUserData())->SetProviderTicket(packet.GetTicket().c_str()); + } + // Sync our console ConsoleReplicator consoleReplicator(connection); AZ::Interface::Get()->VisitRegisteredFunctors([&consoleReplicator](AZ::ConsoleFunctorBase* functor) { consoleReplicator.Visit(functor); }); @@ -525,16 +547,17 @@ namespace Multiplayer if (connection->GetConnectionRole() == ConnectionRole::Connector) { AZLOG_INFO("New outgoing connection to remote address: %s", connection->GetRemoteAddress().GetString().c_str()); - connection->SendReliablePacket(MultiplayerPackets::Connect(0)); + AZ::CVarFixedString providerTicket; + if (connection->GetUserData() != nullptr) + { + providerTicket = reinterpret_cast(connection->GetUserData())->GetProviderTicket(); + } + connection->SendReliablePacket(MultiplayerPackets::Connect(0, providerTicket)); } else { AZLOG_INFO("New incoming connection from remote address: %s", connection->GetRemoteAddress().GetString().c_str()); m_connAcquiredEvent.Signal(datum); - AzFramework::PlayerConnectionConfig config; - config.m_playerConnectionId = aznumeric_cast(connection->GetConnectionId()); - config.m_playerSessionId = AZStd::to_string(config.m_playerConnectionId); - AZ::Interface::Get()->ValidatePlayerJoinSession(config); } // Hosts will spawn a new default player prefab for the user that just connected @@ -600,20 +623,22 @@ namespace Multiplayer // Signal to session management that a user has left the server if (m_agentType == MultiplayerAgentType::DedicatedServer || m_agentType == MultiplayerAgentType::ClientServer) { - if (connection->GetConnectionRole() == ConnectionRole::Connector) + if (AZ::Interface::Get() != nullptr && + connection->GetConnectionRole() == ConnectionRole::Connector) { AzFramework::PlayerConnectionConfig config; config.m_playerConnectionId = aznumeric_cast(connection->GetConnectionId()); - config.m_playerSessionId = AZStd::to_string(config.m_playerConnectionId); + config.m_playerSessionId = reinterpret_cast(connection->GetUserData())->GetProviderTicket(); AZ::Interface::Get()->HandlePlayerLeaveSession(config); } } // Signal to session management when there are no remaining players in a dedicated server for potential cleanup // We avoid this for client server as the host itself is a user - if (m_agentType == MultiplayerAgentType::DedicatedServer) + if (m_agentType == MultiplayerAgentType::DedicatedServer && connection->GetConnectionRole() == ConnectionRole::Connector) { - if (m_networkInterface->GetConnectionSet().GetConnectionCount() == 0) + if (AZ::Interface::Get() != nullptr + && m_networkInterface->GetConnectionSet().GetConnectionCount() == 0) { AZ::Interface::Get()->HandleDestroySession(); } From cd5867d2fa24bdaa5834556b6df30fcc6e0d331f Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 8 Jun 2021 16:43:36 -0700 Subject: [PATCH 08/11] Cleanup session changes and add on connection disconnected event --- .../Code/Include/Multiplayer/IMultiplayer.h | 7 +- .../ClientToServerConnectionData.cpp | 2 +- .../ClientToServerConnectionData.h | 4 +- .../ClientToServerConnectionData.inl | 2 +- .../ServerToClientConnectionData.h | 2 +- .../ServerToClientConnectionData.inl | 2 +- .../Source/MultiplayerSystemComponent.cpp | 80 ++++++++++++++----- .../Code/Source/MultiplayerSystemComponent.h | 2 + 8 files changed, 76 insertions(+), 25 deletions(-) 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; From 056e170eb6eb1aa37b433f1d792c45eff2190e6b Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 8 Jun 2021 16:48:23 -0700 Subject: [PATCH 09/11] Check for dns name on request player join --- Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 5282490ad6..4462502b24 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -199,7 +199,8 @@ namespace Multiplayer { AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::Client); - const IpAddress ipAddress(config.m_ipAddress.c_str(), config.m_port, m_networkInterface->GetType()); + AZStd::string hostname = config.m_dnsName.empty() ? config.m_ipAddress : config.m_dnsName; + const IpAddress ipAddress(hostname.c_str(), config.m_port, m_networkInterface->GetType()); ConnectionId connectionId = m_networkInterface->Connect(ipAddress); AzNetworking::IConnection* connection = m_networkInterface->GetConnectionSet().GetConnection(connectionId); From da7870bb2a06ce9b25488a0522d16f97874d7555 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 8 Jun 2021 22:46:45 -0700 Subject: [PATCH 10/11] Fix some typos and comments --- Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h | 2 +- Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h index c60119cdee..44472422c7 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h @@ -66,7 +66,7 @@ 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 + //! Adds a ClientDisconnectedEvent Handler which is invoked on the client when a disconnection occurs //! @param handler The ClientDisconnectedEvent Handler to add virtual void AddClientDisconnectedHandler(ClientDisconnectedEvent::Handler& handler) = 0; diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 4462502b24..a1e65aa392 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -126,14 +126,14 @@ namespace Multiplayer 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()) + AZ_Warning("Multiplayer Property", false, "MultiplayerSystemComponent GetOnClientDisconnectedEvent 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()) + AZ_Warning("Multiplayer Property", false, "MultiplayerSystemComponent GetOnClientDisconnected failed. Entity '%s' (id: %s) is missing MultiplayerSystemComponent, be sure to add MultiplayerSystemComponent to this entity.", entity->GetName().c_str(), id.ToString().c_str()) return nullptr; } From 59d0a8817e9122c5c8af1afd1cf8476b869d733b Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 8 Jun 2021 22:56:53 -0700 Subject: [PATCH 11/11] Fix inconsistent naming and cover edge case --- .../Source/ConnectionData/ClientToServerConnectionData.h | 1 + .../Source/ConnectionData/ClientToServerConnectionData.inl | 5 +++++ .../Source/ConnectionData/ServerToClientConnectionData.h | 4 ++-- .../Source/ConnectionData/ServerToClientConnectionData.inl | 6 +++--- Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp | 5 +++++ 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.h b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.h index 693e3814c4..e343bc6dfe 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.h +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.h @@ -40,6 +40,7 @@ namespace Multiplayer //! @} const AZStd::string& GetProviderTicket() const; + void SetProviderTicket(const AZStd::string&); private: EntityReplicationManager m_entityReplicationManager; diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.inl b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.inl index 44cbf10350..a5982dcdb8 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.inl +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.inl @@ -26,4 +26,9 @@ namespace Multiplayer { return m_providerTicket; } + + inline void ClientToServerConnectionData::SetProviderTicket(const AZStd::string& ticket) + { + m_providerTicket = ticket; + } } diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h index 0851299498..dda7c15d69 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h @@ -42,7 +42,7 @@ namespace Multiplayer NetworkEntityHandle GetPrimaryPlayerEntity(); const NetworkEntityHandle& GetPrimaryPlayerEntity() const; const AZStd::string& GetProviderTicket() const; - void SetProviderTicket(AZStd::string); + void SetProviderTicket(const AZStd::string&); private: void OnControlledEntityRemove(); @@ -53,7 +53,7 @@ namespace Multiplayer NetworkEntityHandle m_controlledEntity; EntityStopEvent::Handler m_controlledEntityRemovedHandler; EntityServerMigrationEvent::Handler m_controlledEntityMigrationHandler; - AZStd::string m_ticket; + AZStd::string m_providerTicket; AzNetworking::IConnection* m_connection = nullptr; bool m_canSendUpdates = false; }; diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl index b56ed7097e..1fb104b311 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl @@ -35,11 +35,11 @@ namespace Multiplayer inline const AZStd::string& ServerToClientConnectionData::GetProviderTicket() const { - return m_ticket; + return m_providerTicket; } - inline void ServerToClientConnectionData::SetProviderTicket(AZStd::string ticket) + inline void ServerToClientConnectionData::SetProviderTicket(const AZStd::string& ticket) { - m_ticket = ticket; + m_providerTicket = ticket; } } diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index a1e65aa392..e4256a875c 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -208,6 +208,11 @@ namespace Multiplayer { connection->SetUserData(new ClientToServerConnectionData(connection, *this, config.m_playerSessionId)); } + else + { + reinterpret_cast(connection->GetUserData())->SetProviderTicket(config.m_playerSessionId); + } + return true; }