From a534fccc9b1b9ba1c928cccb2d65027ece496b59 Mon Sep 17 00:00:00 2001 From: Vincent Liu <5900509+onecent1101@users.noreply.github.com> Date: Mon, 18 Oct 2021 12:47:55 -0700 Subject: [PATCH] Expose matchmaking event polling APIs and add required session notifications (#4636) * Expose matchmaking event polling APIs and add required session notifications Signed-off-by: onecent1101 --- .../Session/SessionNotifications.h | 32 +++++++++-- .../Include/Request/IAWSGameLiftRequests.h | 37 ++++++++++++ .../AWSGameLiftClientLocalTicketTracker.cpp | 6 +- .../AWSGameLiftClientLocalTicketTracker.h | 6 +- .../AWSGameLiftClientSystemComponent.cpp | 14 ++++- .../Source/AWSGameLiftClientSystemComponent.h | 2 +- .../IAWSGameLiftMatchmakingInternalRequests.h | 39 ------------- .../Tests/AWSGameLiftClientMocks.h | 2 - .../awsgamelift_client_files.cmake | 5 +- .../Source/AWSGameLiftServerManager.cpp | 41 ++++++------- .../Source/AWSGameLiftServerManager.h | 2 +- .../Tests/AWSGameLiftServerManagerTest.cpp | 57 +++++++++++++++++-- .../Source/MultiplayerSystemComponent.cpp | 12 ++++ .../Code/Source/MultiplayerSystemComponent.h | 3 + 14 files changed, 173 insertions(+), 85 deletions(-) delete mode 100644 Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftMatchmakingInternalRequests.h diff --git a/Code/Framework/AzFramework/AzFramework/Session/SessionNotifications.h b/Code/Framework/AzFramework/AzFramework/Session/SessionNotifications.h index 902500fe9a..2213343aa2 100644 --- a/Code/Framework/AzFramework/AzFramework/Session/SessionNotifications.h +++ b/Code/Framework/AzFramework/AzFramework/Session/SessionNotifications.h @@ -30,22 +30,42 @@ namespace AzFramework ////////////////////////////////////////////////////////////////////////// // OnSessionHealthCheck is fired in health check process - // @return The result of all OnSessionHealthCheck + // Use this notification to perform any custom health check + // @return True if OnSessionHealthCheck succeeds, false otherwise virtual bool OnSessionHealthCheck() = 0; - // OnCreateSessionBegin is fired at the beginning of session creation + // OnCreateSessionBegin is fired at the beginning of session creation process + // Use this notification to perform any necessary configuration or initialization before + // creating session // @param sessionConfig The properties to describe a session - // @return The result of all OnCreateSessionBegin notifications + // @return True if OnCreateSessionBegin succeeds, false otherwise virtual bool OnCreateSessionBegin(const SessionConfig& sessionConfig) = 0; - // OnDestroySessionBegin is fired at the beginning of session termination - // @return The result of all OnDestroySessionBegin notifications + // OnCreateSessionEnd is fired at the end of session creation process + // Use this notification to perform any follow-up operation after session is created and active + virtual void OnCreateSessionEnd() = 0; + + // OnDestroySessionBegin is fired at the beginning of session termination process + // Use this notification to perform any cleanup operation before destroying session, + // like gracefully disconnect players, cleanup data, etc. + // @return True if OnDestroySessionBegin succeeds, false otherwise virtual bool OnDestroySessionBegin() = 0; - // OnUpdateSessionBegin is fired at the beginning of session update + // OnDestroySessionEnd is fired at the end of session termination process + // Use this notification to perform any follow-up operation after session is destroyed, + // like shutdown application process, etc. + virtual void OnDestroySessionEnd() = 0; + + // OnUpdateSessionBegin is fired at the beginning of session update process + // Use this notification to perform any configuration or initialization to handle + // the session settings changing // @param sessionConfig The properties to describe a session // @param updateReason The reason for session update virtual void OnUpdateSessionBegin(const SessionConfig& sessionConfig, const AZStd::string& updateReason) = 0; + + // OnUpdateSessionBegin is fired at the end of session update process + // Use this notification to perform any follow-up operations after session is updated + virtual void OnUpdateSessionEnd() = 0; }; using SessionNotificationBus = AZ::EBus; } // namespace AzFramework diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/IAWSGameLiftRequests.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/IAWSGameLiftRequests.h index 10269dc8c3..c14ef559b2 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/IAWSGameLiftRequests.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Include/Request/IAWSGameLiftRequests.h @@ -94,4 +94,41 @@ namespace AWSGameLift static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; }; using AWSGameLiftMatchmakingRequestBus = AZ::EBus; + + //! IAWSGameLiftMatchmakingEventRequests + //! GameLift Gem matchmaking event interfaces which is used to track matchmaking ticket event + //! Developer should define the way to poll matchmaking ticket event and behavior based on the ticket status + //! Use AWSGameLiftClientLocalTicketTracker as an example, it uses continuous polling to query matchmaking ticket: + //! StartPolling - local ticket tracker starts monitor process for matchmaking ticket, and joins player + //! to the match once ticket is complete + //! StopPolling - local ticket tracker cancels ongoing matchmaking ticket and stops monitoring process + class IAWSGameLiftMatchmakingEventRequests + { + public: + AZ_RTTI(IAWSGameLiftMatchmakingEventRequests, "{C2DA440E-74E0-411E-813D-5880B50B0C9E}"); + + IAWSGameLiftMatchmakingEventRequests() = default; + virtual ~IAWSGameLiftMatchmakingEventRequests() = default; + + //! StartPolling + //! Request to start process for polling matchmaking ticket based on given ticket id and player Id + //! @param ticketId The requested matchmaking ticket id + //! @param playerId The requested matchmaking player id + virtual void StartPolling(const AZStd::string& ticketId, const AZStd::string& playerId) = 0; + + //! StopPolling + //! Request to stop process for polling matchmaking ticket + virtual void StopPolling() = 0; + }; + + // IAWSGameLiftMatchmakingEventRequests EBus wrapper for scripting + class AWSGameLiftMatchmakingEventRequests + : public AZ::EBusTraits + { + public: + using MutexType = AZStd::recursive_mutex; + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + }; + using AWSGameLiftMatchmakingEventRequestBus = AZ::EBus; } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp index af0cf1c35c..cc2f84cb35 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp @@ -30,12 +30,14 @@ namespace AWSGameLift void AWSGameLiftClientLocalTicketTracker::ActivateTracker() { - AZ::Interface::Register(this); + AZ::Interface::Register(this); + AWSGameLiftMatchmakingEventRequestBus::Handler::BusConnect(); } void AWSGameLiftClientLocalTicketTracker::DeactivateTracker() { - AZ::Interface::Unregister(this); + AWSGameLiftMatchmakingEventRequestBus::Handler::BusDisconnect(); + AZ::Interface::Unregister(this); StopPolling(); } diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.h index 23083e9fd9..04bdd71c85 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.h @@ -12,7 +12,7 @@ #include #include -#include +#include #include @@ -30,7 +30,7 @@ namespace AWSGameLift //! For use in production, please see GameLifts guidance about matchmaking at volume. //! The continuous polling approach here is only suitable for low volume matchmaking and is meant to aid with development only class AWSGameLiftClientLocalTicketTracker - : public IAWSGameLiftMatchmakingInternalRequests + : public AWSGameLiftMatchmakingEventRequestBus::Handler { public: static constexpr const char AWSGameLiftClientLocalTicketTrackerName[] = "AWSGameLiftClientLocalTicketTracker"; @@ -44,7 +44,7 @@ namespace AWSGameLift virtual void ActivateTracker(); virtual void DeactivateTracker(); - // IAWSGameLiftMatchmakingInternalRequests interface implementation + // AWSGameLiftMatchmakingEventRequestBus interface implementation void StartPolling(const AZStd::string& ticketId, const AZStd::string& playerId) override; void StopPolling() override; diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp index ed7830ce57..6ab998b696 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -59,10 +60,17 @@ namespace AWSGameLift behaviorContext->EBus("AWSGameLiftRequestBus") ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") ->Event("ConfigureGameLiftClient", &AWSGameLiftRequestBus::Events::ConfigureGameLiftClient, - {{{"Region", ""}}}) + { { { "Region", "" } } }) ->Event("CreatePlayerId", &AWSGameLiftRequestBus::Events::CreatePlayerId, - {{{"IncludeBrackets", ""}, - {"IncludeDashes", ""}}}); + { { { "IncludeBrackets", "" }, + { "IncludeDashes", "" } } }); + + behaviorContext->EBus("AWSGameLiftMatchmakingEventRequestBus") + ->Attribute(AZ::Script::Attributes::Category, "AWSGameLift") + ->Event("StartPolling", &AWSGameLiftMatchmakingEventRequestBus::Events::StartPolling, + { { { "TicketId", "" }, + { "PlayerId", "" } } }) + ->Event("StopPolling", &AWSGameLiftMatchmakingEventRequestBus::Events::StopPolling); } } diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.h index 30d4ee0106..0560906c35 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.h @@ -11,12 +11,12 @@ #include #include -#include #include namespace AWSGameLift { class AWSGameLiftClientManager; + class AWSGameLiftClientLocalTicketTracker; //! Gem client system component. Responsible for creating the gamelift client manager. class AWSGameLiftClientSystemComponent diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftMatchmakingInternalRequests.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftMatchmakingInternalRequests.h deleted file mode 100644 index 81d04314af..0000000000 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftMatchmakingInternalRequests.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include -#include -#include - -namespace AWSGameLift -{ - //! IAWSGameLiftMatchmakingInternalRequests - //! GameLift Gem matchmaking internal interfaces which is used to communicate - //! with client side ticket tracker to sync matchmaking ticket data and join - //! player to the match - class IAWSGameLiftMatchmakingInternalRequests - { - public: - AZ_RTTI(IAWSGameLiftMatchmakingInternalRequests, "{C2DA440E-74E0-411E-813D-5880B50B0C9E}"); - - IAWSGameLiftMatchmakingInternalRequests() = default; - virtual ~IAWSGameLiftMatchmakingInternalRequests() = default; - - //! StartPolling - //! Request to start process for polling matchmaking ticket based on given ticket id and player id - //! @param ticketId The requested matchmaking ticket id - //! @param playerId The requested matchmaking player id - virtual void StartPolling(const AZStd::string& ticketId, const AZStd::string& playerId) = 0; - - //! StopPolling - //! Request to stop process for polling matchmaking ticket - virtual void StopPolling() = 0; - }; -} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h index 6ba05747aa..01afec5c3f 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h @@ -36,8 +36,6 @@ #include #include -#include - using namespace Aws::GameLift; class GameLiftClientMock diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake index cd54414cc1..629d1596cf 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake @@ -32,12 +32,12 @@ set(FILES Source/Activity/AWSGameLiftLeaveSessionActivity.h Source/Activity/AWSGameLiftSearchSessionsActivity.cpp Source/Activity/AWSGameLiftSearchSessionsActivity.h - Source/AWSGameLiftClientLocalTicketTracker.cpp - Source/AWSGameLiftClientLocalTicketTracker.h Source/Activity/AWSGameLiftStartMatchmakingActivity.cpp Source/Activity/AWSGameLiftStartMatchmakingActivity.h Source/Activity/AWSGameLiftStopMatchmakingActivity.cpp Source/Activity/AWSGameLiftStopMatchmakingActivity.h + Source/AWSGameLiftClientLocalTicketTracker.cpp + Source/AWSGameLiftClientLocalTicketTracker.h Source/AWSGameLiftClientManager.cpp Source/AWSGameLiftClientManager.h Source/AWSGameLiftClientSystemComponent.cpp @@ -50,5 +50,4 @@ set(FILES Source/Request/AWSGameLiftStartMatchmakingRequest.cpp Source/Request/AWSGameLiftStopMatchmakingRequest.cpp Source/Request/IAWSGameLiftInternalRequests.h - Source/Request/IAWSGameLiftMatchmakingInternalRequests.h ) diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp index 70d0063b61..0ef028219c 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp @@ -336,14 +336,11 @@ namespace AWSGameLift BuildServerMatchBackfillPlayerAttributes( players[playerIndex][AWSGameLiftMatchmakingPlayerAttributesKeyName], outPlayer); } - } - else - { - return false; + return true; } } } - return true; + return false; } void AWSGameLiftServerManager::BuildServerMatchBackfillPlayerAttributes( @@ -461,12 +458,16 @@ namespace AWSGameLift AZ_TracePrintf(AWSGameLiftServerManagerName, "Notifying GameLift server process is ending ..."); Aws::GameLift::GenericOutcome processEndingOutcome = m_gameLiftServerSDKWrapper->ProcessEnding(); - AZ_TracePrintf(AWSGameLiftServerManagerName, "ProcessEnding request against Amazon GameLift service is complete."); - - [[maybe_unused]] bool processEndingIsSuccess = processEndingOutcome.IsSuccess(); - - AZ_Error(AWSGameLiftServerManagerName, processEndingIsSuccess, AWSGameLiftServerProcessEndingErrorMessage, - processEndingOutcome.GetError().GetErrorMessage().c_str()); + if (processEndingOutcome.IsSuccess()) + { + AZ_TracePrintf(AWSGameLiftServerManagerName, "ProcessEnding request against Amazon GameLift service succeeded."); + AzFramework::SessionNotificationBus::Broadcast(&AzFramework::SessionNotifications::OnDestroySessionEnd); + } + else + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftServerProcessEndingErrorMessage, + processEndingOutcome.GetError().GetErrorMessage().c_str()); + } } void AWSGameLiftServerManager::HandlePlayerLeaveSession(const AzFramework::PlayerConnectionConfig& playerConnectionConfig) @@ -546,15 +547,16 @@ namespace AWSGameLift { AZ_TracePrintf(AWSGameLiftServerManagerName, "Activating GameLift game session ..."); Aws::GameLift::GenericOutcome activationOutcome = m_gameLiftServerSDKWrapper->ActivateGameSession(); - AZ_TracePrintf(AWSGameLiftServerManagerName, "ActivateGameSession request against Amazon GameLift service is complete."); if (activationOutcome.IsSuccess()) { + AZ_TracePrintf(AWSGameLiftServerManagerName, "ActivateGameSession request against Amazon GameLift service succeeded."); // Register server manager as handler once game session has been activated if (!AZ::Interface::Get()) { AZ::Interface::Register(this); } + AzFramework::SessionNotificationBus::Broadcast(&AzFramework::SessionNotifications::OnCreateSessionEnd); } else { @@ -588,17 +590,18 @@ namespace AWSGameLift void AWSGameLiftServerManager::OnUpdateGameSession(const Aws::GameLift::Server::Model::UpdateGameSession& updateGameSession) { + AzFramework::SessionConfig sessionConfig = BuildSessionConfig(updateGameSession.GetGameSession()); Aws::GameLift::Server::Model::UpdateReason updateReason = updateGameSession.GetUpdateReason(); + AzFramework::SessionNotificationBus::Broadcast(&AzFramework::SessionNotifications::OnUpdateSessionBegin, + sessionConfig, Aws::GameLift::Server::Model::UpdateReasonMapper::GetNameForUpdateReason(updateReason).c_str()); + + // Update game session data locally if (updateReason == Aws::GameLift::Server::Model::UpdateReason::MATCHMAKING_DATA_UPDATED) { UpdateGameSessionData(updateGameSession.GetGameSession()); } - AzFramework::SessionConfig sessionConfig = BuildSessionConfig(updateGameSession.GetGameSession()); - AzFramework::SessionNotificationBus::Broadcast( - &AzFramework::SessionNotifications::OnUpdateSessionBegin, - sessionConfig, - Aws::GameLift::Server::Model::UpdateReasonMapper::GetNameForUpdateReason(updateReason).c_str()); + AzFramework::SessionNotificationBus::Broadcast(&AzFramework::SessionNotifications::OnUpdateSessionEnd); } bool AWSGameLiftServerManager::RemoveConnectedPlayer(uint32_t playerConnectionId, AZStd::string& outPlayerSessionId) @@ -654,7 +657,7 @@ namespace AWSGameLift } else { - AZ_TracePrintf(AWSGameLiftServerManagerName, "StartMatchBackfill request against Amazon GameLift service is complete."); + AZ_TracePrintf(AWSGameLiftServerManagerName, "StartMatchBackfill request against Amazon GameLift service succeeded."); return true; } } @@ -686,7 +689,7 @@ namespace AWSGameLift } else { - AZ_TracePrintf(AWSGameLiftServerManagerName, "StopMatchBackfill request against Amazon GameLift service is complete."); + AZ_TracePrintf(AWSGameLiftServerManagerName, "StopMatchBackfill request against Amazon GameLift service succeeded."); return true; } } diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h index fa2f783eca..ee21751fe9 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h @@ -94,7 +94,7 @@ namespace AWSGameLift static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSTypeName[] = "S"; static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSServerTypeName[] = "STRING"; static constexpr const char AWSGameLiftMatchmakingPlayerAttributeNTypeName[] = "N"; - static constexpr const char AWSGameLiftMatchmakingPlayerAttributeNServerTypeName[] = "NUMBER"; + static constexpr const char AWSGameLiftMatchmakingPlayerAttributeNServerTypeName[] = "DOUBLE"; static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSLTypeName[] = "SL"; static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSLServerTypeName[] = "STRING_LIST"; static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSDMTypeName[] = "SDM"; diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerManagerTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerManagerTest.cpp index d172734ec0..96d6a69d96 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerManagerTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerManagerTest.cpp @@ -34,13 +34,20 @@ R"({ "valueAttribute":"testmode" }, "level":{ - "attributeType":"NUMBER", + "attributeType":"DOUBLE", "valueAttribute":10.0 }, "items":{ "attributeType":"STRING_LIST", "valueAttribute":["test1","test2","test3"] } + }}, + {"playerId":"secondplayer", + "attributes":{ + "mode":{ + "attributeType":"STRING", + "valueAttribute":"testmode" + } }} ]} ] @@ -162,8 +169,11 @@ R"({ MOCK_METHOD0(OnSessionHealthCheck, bool()); MOCK_METHOD1(OnCreateSessionBegin, bool(const AzFramework::SessionConfig&)); + MOCK_METHOD0(OnCreateSessionEnd, void()); MOCK_METHOD0(OnDestroySessionBegin, bool()); + MOCK_METHOD0(OnDestroySessionEnd, void()); MOCK_METHOD2(OnUpdateSessionBegin, void(const AzFramework::SessionConfig&, const AZStd::string&)); + MOCK_METHOD0(OnUpdateSessionEnd, void()); }; class GameLiftServerManagerTest @@ -254,6 +264,7 @@ R"({ EXPECT_CALL(handlerMock, OnDestroySessionBegin()).Times(1).WillOnce(testing::Return(false)); EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), GetTerminationTime()).Times(1); EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), ProcessEnding()).Times(0); + EXPECT_CALL(handlerMock, OnDestroySessionEnd()).Times(0); AZ_TEST_START_TRACE_SUPPRESSION; m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onProcessTerminateFunc(); @@ -274,13 +285,40 @@ R"({ SessionNotificationsHandlerMock handlerMock; EXPECT_CALL(handlerMock, OnDestroySessionBegin()).Times(1).WillOnce(testing::Return(true)); EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), GetTerminationTime()).Times(1); - EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), ProcessEnding()).Times(1); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), ProcessEnding()) + .Times(1) + .WillOnce(testing::Return(Aws::GameLift::GenericOutcome(nullptr))); + EXPECT_CALL(handlerMock, OnDestroySessionEnd()).Times(1); m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onProcessTerminateFunc(); EXPECT_FALSE(AZ::Interface::Get()); } + TEST_F(GameLiftServerManagerTest, OnProcessTerminate_OnDestroySessionBeginReturnsTrue_TerminationNotificationSentButFail) + { + m_serverManager->InitializeGameLiftServerSDK(); + m_serverManager->NotifyGameLiftProcessReady(); + if (!AZ::Interface::Get()) + { + AZ::Interface::Register(m_serverManager.get()); + } + + SessionNotificationsHandlerMock handlerMock; + EXPECT_CALL(handlerMock, OnDestroySessionBegin()).Times(1).WillOnce(testing::Return(true)); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), GetTerminationTime()).Times(1); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), ProcessEnding()) + .Times(1) + .WillOnce(testing::Return(Aws::GameLift::GenericOutcome())); + EXPECT_CALL(handlerMock, OnDestroySessionEnd()).Times(0); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onProcessTerminateFunc(); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + + EXPECT_FALSE(AZ::Interface::Get()); + } + TEST_F(GameLiftServerManagerTest, OnHealthCheck_OnSessionHealthCheckReturnsTrue_CallbackFunctionReturnsTrue) { m_serverManager->InitializeGameLiftServerSDK(); @@ -316,6 +354,7 @@ R"({ m_serverManager->NotifyGameLiftProcessReady(); SessionNotificationsHandlerMock handlerMock; EXPECT_CALL(handlerMock, OnCreateSessionBegin(testing::_)).Times(1).WillOnce(testing::Return(false)); + EXPECT_CALL(handlerMock, OnCreateSessionEnd()).Times(0); EXPECT_CALL(handlerMock, OnDestroySessionBegin()).Times(1).WillOnce(testing::Return(true)); EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), ProcessEnding()).Times(1); AZ_TEST_START_TRACE_SUPPRESSION; @@ -329,6 +368,7 @@ R"({ m_serverManager->NotifyGameLiftProcessReady(); SessionNotificationsHandlerMock handlerMock; EXPECT_CALL(handlerMock, OnCreateSessionBegin(testing::_)).Times(1).WillOnce(testing::Return(true)); + EXPECT_CALL(handlerMock, OnCreateSessionEnd()).Times(1); EXPECT_CALL(handlerMock, OnDestroySessionBegin()).Times(1).WillOnce(testing::Return(true)); EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), ActivateGameSession()) .Times(1) @@ -349,6 +389,7 @@ R"({ m_serverManager->NotifyGameLiftProcessReady(); SessionNotificationsHandlerMock handlerMock; EXPECT_CALL(handlerMock, OnCreateSessionBegin(testing::_)).Times(1).WillOnce(testing::Return(true)); + EXPECT_CALL(handlerMock, OnCreateSessionEnd()).Times(0); EXPECT_CALL(handlerMock, OnDestroySessionBegin()).Times(1).WillOnce(testing::Return(true)); EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), ActivateGameSession()) .Times(1) @@ -359,12 +400,13 @@ R"({ AZ_TEST_STOP_TRACE_SUPPRESSION(1); } - TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithUnknownReason_OnUpdateSessionBeginGetCalledOnce) + TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithUnknownReason_OnUpdateSessionGetCalledOnce) { m_serverManager->InitializeGameLiftServerSDK(); m_serverManager->NotifyGameLiftProcessReady(); SessionNotificationsHandlerMock handlerMock; EXPECT_CALL(handlerMock, OnUpdateSessionBegin(testing::_, testing::_)).Times(1); + EXPECT_CALL(handlerMock, OnUpdateSessionEnd()).Times(1); m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onUpdateGameSessionFunc( Aws::GameLift::Server::Model::UpdateGameSession( @@ -373,12 +415,13 @@ R"({ "testticket")); } - TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithEmptyMatchmakingData_OnUpdateSessionBeginGetCalledOnce) + TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithEmptyMatchmakingData_OnUpdateSessionGetCalledOnce) { m_serverManager->InitializeGameLiftServerSDK(); m_serverManager->NotifyGameLiftProcessReady(); SessionNotificationsHandlerMock handlerMock; EXPECT_CALL(handlerMock, OnUpdateSessionBegin(testing::_, testing::_)).Times(1); + EXPECT_CALL(handlerMock, OnUpdateSessionEnd()).Times(1); m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onUpdateGameSessionFunc( Aws::GameLift::Server::Model::UpdateGameSession( @@ -387,12 +430,13 @@ R"({ "testticket")); } - TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithValidMatchmakingData_OnUpdateSessionBeginGetCalledOnce) + TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithValidMatchmakingData_OnUpdateSessionGetCalledOnce) { m_serverManager->InitializeGameLiftServerSDK(); m_serverManager->NotifyGameLiftProcessReady(); SessionNotificationsHandlerMock handlerMock; EXPECT_CALL(handlerMock, OnUpdateSessionBegin(testing::_, testing::_)).Times(1); + EXPECT_CALL(handlerMock, OnUpdateSessionEnd()).Times(1); Aws::GameLift::Server::Model::GameSession gameSession; gameSession.SetMatchmakerData(TEST_SERVER_MATCHMAKING_DATA); @@ -401,12 +445,13 @@ R"({ gameSession, Aws::GameLift::Server::Model::UpdateReason::MATCHMAKING_DATA_UPDATED, "testticket")); } - TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithInvalidMatchmakingData_OnUpdateSessionBeginGetCalledOnce) + TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithInvalidMatchmakingData_OnUpdateSessionGetCalledOnce) { m_serverManager->InitializeGameLiftServerSDK(); m_serverManager->NotifyGameLiftProcessReady(); SessionNotificationsHandlerMock handlerMock; EXPECT_CALL(handlerMock, OnUpdateSessionBegin(testing::_, testing::_)).Times(1); + EXPECT_CALL(handlerMock, OnUpdateSessionEnd()).Times(1); Aws::GameLift::Server::Model::GameSession gameSession; gameSession.SetMatchmakerData("{invalid}"); diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 4fad5697c7..e8710cdd6c 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -288,6 +288,10 @@ namespace Multiplayer return m_networkInterface->Listen(sessionConfig.m_port); } + void MultiplayerSystemComponent::OnCreateSessionEnd() + { + } + bool MultiplayerSystemComponent::OnDestroySessionBegin() { // This can be triggered external from Multiplayer so only run if we are in an Initialized state @@ -308,12 +312,20 @@ namespace Multiplayer return true; } + void MultiplayerSystemComponent::OnDestroySessionEnd() + { + } + void MultiplayerSystemComponent::OnUpdateSessionBegin(const AzFramework::SessionConfig& sessionConfig, const AZStd::string& updateReason) { AZ_UNUSED(sessionConfig); AZ_UNUSED(updateReason); } + void MultiplayerSystemComponent::OnUpdateSessionEnd() + { + } + void MultiplayerSystemComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) { const 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 ef1fb0da5b..568d5f7801 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -69,8 +69,11 @@ namespace Multiplayer //! @{ bool OnSessionHealthCheck() override; bool OnCreateSessionBegin(const AzFramework::SessionConfig& sessionConfig) override; + void OnCreateSessionEnd() override; bool OnDestroySessionBegin() override; + void OnDestroySessionEnd() override; void OnUpdateSessionBegin(const AzFramework::SessionConfig& sessionConfig, const AZStd::string& updateReason) override; + void OnUpdateSessionEnd() override; //! @} //! AZ::TickBus::Handler overrides.