From 0657f6f7ec0b9f571e4852b6621bd9eff8a163e6 Mon Sep 17 00:00:00 2001 From: Vincent Liu <5900509+onecent1101@users.noreply.github.com> Date: Wed, 6 Oct 2021 14:01:52 -0700 Subject: [PATCH] [LYN-7050] Add matchmaking ticket tracker component (#4485) Signed-off-by: onecent1101 --- .../AWSGameLiftClientLocalTicketTracker.cpp | 169 +++++++++ .../AWSGameLiftClientLocalTicketTracker.h | 62 +++ .../Source/AWSGameLiftClientManager.cpp | 31 +- .../Source/AWSGameLiftClientManager.h | 18 +- .../AWSGameLiftClientSystemComponent.cpp | 38 +- .../Source/AWSGameLiftClientSystemComponent.h | 15 +- .../Request/IAWSGameLiftInternalRequests.h | 43 +++ .../IAWSGameLiftMatchmakingInternalRequests.h | 39 ++ ...WSGameLiftClientLocalTicketTrackerTest.cpp | 353 ++++++++++++++++++ .../Tests/AWSGameLiftClientManagerTest.cpp | 89 +++-- .../Tests/AWSGameLiftClientMocks.h | 3 + .../AWSGameLiftClientSystemComponentTest.cpp | 27 +- .../awsgamelift_client_files.cmake | 5 + .../awsgamelift_client_tests_files.cmake | 1 + .../Source/AWSGameLiftSessionConstants.h | 1 + 15 files changed, 799 insertions(+), 95 deletions(-) create mode 100644 Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp create mode 100644 Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.h create mode 100644 Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftInternalRequests.h create mode 100644 Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftMatchmakingInternalRequests.h create mode 100644 Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp new file mode 100644 index 0000000000..9ae168aea9 --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.cpp @@ -0,0 +1,169 @@ +/* + * 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 + * + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +namespace AWSGameLift +{ + AWSGameLiftClientLocalTicketTracker::AWSGameLiftClientLocalTicketTracker() + : m_status(TicketTrackerStatus::Idle) + , m_pollingPeriodInMS(AWSGameLiftClientDefaultPollingPeriodInMS) + { + } + + void AWSGameLiftClientLocalTicketTracker::ActivateTracker() + { + AZ::Interface::Register(this); + } + + void AWSGameLiftClientLocalTicketTracker::DeactivateTracker() + { + AZ::Interface::Unregister(this); + StopPolling(); + } + + void AWSGameLiftClientLocalTicketTracker::StartPolling( + const AZStd::string& ticketId, const AZStd::string& playerId) + { + AZStd::lock_guard lock(m_trackerMutex); + if (m_status == TicketTrackerStatus::Running) + { + AZ_TracePrintf(AWSGameLiftClientLocalTicketTrackerName, "Matchmaking ticket tracker is running."); + return; + } + m_status = TicketTrackerStatus::Running; + m_trackerThread = AZStd::thread(AZStd::bind( + &AWSGameLiftClientLocalTicketTracker::ProcessPolling, this, ticketId, playerId)); + } + + void AWSGameLiftClientLocalTicketTracker::StopPolling() + { + AZStd::lock_guard lock(m_trackerMutex); + m_status = TicketTrackerStatus::Idle; + if (m_trackerThread.joinable()) + { + m_trackerThread.join(); + } + } + + void AWSGameLiftClientLocalTicketTracker::ProcessPolling( + const AZStd::string& ticketId, const AZStd::string& playerId) + { + while (m_status == TicketTrackerStatus::Running) + { + auto gameliftClient = AZ::Interface::Get()->GetGameLiftClient(); + if (gameliftClient) + { + Aws::GameLift::Model::DescribeMatchmakingRequest request; + request.AddTicketIds(ticketId.c_str()); + + auto describeMatchmakingOutcome = gameliftClient->DescribeMatchmaking(request); + if (describeMatchmakingOutcome.IsSuccess()) + { + if (describeMatchmakingOutcome.GetResult().GetTicketList().size() == 1) + { + auto ticket = describeMatchmakingOutcome.GetResult().GetTicketList().front(); + if (ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::COMPLETED) + { + m_status = TicketTrackerStatus::Idle; + AZ_TracePrintf(AWSGameLiftClientLocalTicketTrackerName, + "Matchmaking ticket %s is complete.", ticket.GetTicketId().c_str()); + RequestPlayerJoinMatch(ticket, playerId); + return; + } + else if (ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::TIMED_OUT || + ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::FAILED || + ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::CANCELLED) + { + m_status = TicketTrackerStatus::Idle; + AZ_Error(AWSGameLiftClientLocalTicketTrackerName, false, "Matchmaking ticket %s is not complete, %s", + ticket.GetTicketId().c_str(), ticket.GetStatusReason().c_str()); + return; + } + else if (ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::REQUIRES_ACCEPTANCE) + { + // broadcast acceptance requires to player + } + else + { + AZ_TracePrintf(AWSGameLiftClientLocalTicketTrackerName, "Matchmaking ticket %s is processing, %s.", + ticket.GetTicketId().c_str(), ticket.GetStatusReason().c_str()); + } + } + else + { + AZ_Error(AWSGameLiftClientLocalTicketTrackerName, false, "Unable to find expected ticket with id %s", ticketId.c_str()); + } + } + else + { + AZ_Error(AWSGameLiftClientLocalTicketTrackerName, false, AWSGameLiftErrorMessageTemplate, + describeMatchmakingOutcome.GetError().GetExceptionName().c_str(), + describeMatchmakingOutcome.GetError().GetMessage().c_str()); + } + } + else + { + AZ_Error(AWSGameLiftClientLocalTicketTrackerName, false, AWSGameLiftClientMissingErrorMessage); + } + AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(m_pollingPeriodInMS)); + } + } + + void AWSGameLiftClientLocalTicketTracker::RequestPlayerJoinMatch( + const Aws::GameLift::Model::MatchmakingTicket& ticket, const AZStd::string& playerId) + { + auto connectionInfo = ticket.GetGameSessionConnectionInfo(); + AzFramework::SessionConnectionConfig sessionConnectionConfig; + sessionConnectionConfig.m_ipAddress = connectionInfo.GetIpAddress().c_str(); + for (auto matchedPlayer : connectionInfo.GetMatchedPlayerSessions()) + { + if (playerId.compare(matchedPlayer.GetPlayerId().c_str()) == 0) + { + sessionConnectionConfig.m_playerSessionId = matchedPlayer.GetPlayerSessionId().c_str(); + break; + } + } + sessionConnectionConfig.m_port = static_cast(connectionInfo.GetPort()); + + if (!sessionConnectionConfig.m_playerSessionId.empty()) + { + AZ_TracePrintf(AWSGameLiftClientLocalTicketTrackerName, + "Requesting and validating player session %s to connect to the match ...", + sessionConnectionConfig.m_playerSessionId.c_str()); + bool result = + AZ::Interface::Get()->RequestPlayerJoinSession(sessionConnectionConfig); + if (result) + { + AZ_TracePrintf(AWSGameLiftClientLocalTicketTrackerName, + "Started connection process, and connection validation is in process."); + } + else + { + AZ_Error(AWSGameLiftClientLocalTicketTrackerName, false, + "Failed to start connection process."); + } + } + else + { + AZ_Error(AWSGameLiftClientLocalTicketTrackerName, false, + "Player session id is missing for player % to join the match.", playerId.c_str()); + } + } +} diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.h new file mode 100644 index 0000000000..7b317a0485 --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientLocalTicketTracker.h @@ -0,0 +1,62 @@ +/* + * 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 + +#include + +namespace AWSGameLift +{ + enum TicketTrackerStatus + { + Idle, + Running + }; + + //! AWSGameLiftClientLocalTicketTracker + //! GameLift client ticket tracker to describe submitted matchmaking ticket periodically, + //! and join player to the match once matchmaking ticket is complete. + //! 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: + static constexpr const char AWSGameLiftClientLocalTicketTrackerName[] = "AWSGameLiftClientLocalTicketTracker"; + // Set ticket polling period to 10 seconds + // https://docs.aws.amazon.com/gamelift/latest/flexmatchguide/match-client.html#match-client-track + static constexpr const uint64_t AWSGameLiftClientDefaultPollingPeriodInMS = 10000; + + AWSGameLiftClientLocalTicketTracker(); + virtual ~AWSGameLiftClientLocalTicketTracker() = default; + + virtual void ActivateTracker(); + virtual void DeactivateTracker(); + + // IAWSGameLiftMatchmakingInternalRequests interface implementation + void StartPolling(const AZStd::string& ticketId, const AZStd::string& playerId) override; + void StopPolling() override; + + protected: + // For testing friendly access + uint64_t m_pollingPeriodInMS; + TicketTrackerStatus m_status; + + private: + void ProcessPolling(const AZStd::string& ticketId, const AZStd::string& playerId); + void RequestPlayerJoinMatch(const Aws::GameLift::Model::MatchmakingTicket& ticket, const AZStd::string& playerId); + + AZStd::mutex m_trackerMutex; + AZStd::thread m_trackerThread; + }; +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.cpp index ae4fb162d6..ee731993aa 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.cpp @@ -17,11 +17,13 @@ #include #include +#include #include #include #include #include #include +#include #include @@ -31,11 +33,6 @@ namespace AWSGameLift AZ_CVAR(AZ::CVarFixedString, cl_gameliftLocalEndpoint, "", nullptr, AZ::ConsoleFunctorFlags::Null, "The local endpoint to test with GameLiftLocal SDK."); #endif - AWSGameLiftClientManager::AWSGameLiftClientManager() - { - m_gameliftClient.reset(); - } - void AWSGameLiftClientManager::ActivateManager() { AZ::Interface::Register(this); @@ -62,8 +59,7 @@ namespace AWSGameLift bool AWSGameLiftClientManager::ConfigureGameLiftClient(const AZStd::string& region) { - m_gameliftClient.reset(); - + AZ::Interface::Get()->SetGameLiftClient(nullptr); Aws::Client::ClientConfiguration clientConfig; // Set up client endpoint or region AZStd::string localEndpoint = ""; @@ -103,7 +99,8 @@ namespace AWSGameLift AZ_Error(AWSGameLiftClientManagerName, false, AWSGameLiftClientCredentialMissingErrorMessage); return false; } - m_gameliftClient = AZStd::make_shared(credentialResult.result, clientConfig); + AZ::Interface::Get()->SetGameLiftClient( + AZStd::make_shared(credentialResult.result, clientConfig)); return true; } @@ -194,15 +191,15 @@ namespace AWSGameLift AZStd::string AWSGameLiftClientManager::CreateSessionHelper( const AWSGameLiftCreateSessionRequest& createSessionRequest) { - AZStd::shared_ptr gameLiftClient = m_gameliftClient; + auto gameliftClient = AZ::Interface::Get()->GetGameLiftClient(); AZStd::string result = ""; - if (!gameLiftClient) + if (!gameliftClient) { AZ_Error(AWSGameLiftClientManagerName, false, AWSGameLiftClientMissingErrorMessage); } else { - result = CreateSessionActivity::CreateSession(*gameLiftClient, createSessionRequest); + result = CreateSessionActivity::CreateSession(*gameliftClient, createSessionRequest); } return result; } @@ -210,7 +207,7 @@ namespace AWSGameLift AZStd::string AWSGameLiftClientManager::CreateSessionOnQueueHelper( const AWSGameLiftCreateSessionOnQueueRequest& createSessionOnQueueRequest) { - AZStd::shared_ptr gameliftClient = m_gameliftClient; + auto gameliftClient = AZ::Interface::Get()->GetGameLiftClient(); AZStd::string result; if (!gameliftClient) { @@ -265,7 +262,7 @@ namespace AWSGameLift bool AWSGameLiftClientManager::JoinSessionHelper(const AWSGameLiftJoinSessionRequest& joinSessionRequest) { - AZStd::shared_ptr gameliftClient = m_gameliftClient; + auto gameliftClient = AZ::Interface::Get()->GetGameLiftClient(); bool result = false; if (!gameliftClient) { @@ -345,8 +342,7 @@ namespace AWSGameLift AzFramework::SearchSessionsResponse AWSGameLiftClientManager::SearchSessionsHelper( const AWSGameLiftSearchSessionsRequest& searchSessionsRequest) const { - AZStd::shared_ptr gameliftClient = m_gameliftClient; - + auto gameliftClient = AZ::Interface::Get()->GetGameLiftClient(); AzFramework::SearchSessionsResponse response; if (!gameliftClient) { @@ -380,9 +376,4 @@ namespace AWSGameLift { AZ_UNUSED(stopMatchmakingRequest); } - - void AWSGameLiftClientManager::SetGameLiftClient(AZStd::shared_ptr gameliftClient) - { - m_gameliftClient.swap(gameliftClient); - } } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.h index d7211d0472..fbdc1d6104 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientManager.h @@ -13,14 +13,6 @@ #include -namespace Aws -{ - namespace GameLift - { - class GameLiftClient; - } -} - namespace AWSGameLift { struct AWSGameLiftCreateSessionRequest; @@ -127,13 +119,11 @@ namespace AWSGameLift "Missing AWS region for GameLift client."; static constexpr const char AWSGameLiftClientCredentialMissingErrorMessage[] = "Missing AWS credential for GameLift client."; - static constexpr const char AWSGameLiftClientMissingErrorMessage[] = - "GameLift client is not configured yet."; static constexpr const char AWSGameLiftCreateSessionRequestInvalidErrorMessage[] = "Invalid GameLift CreateSession or CreateSessionOnQueue request."; - AWSGameLiftClientManager(); + AWSGameLiftClientManager() = default; virtual ~AWSGameLiftClientManager() = default; virtual void ActivateManager(); @@ -165,16 +155,10 @@ namespace AWSGameLift AzFramework::SearchSessionsResponse SearchSessions(const AzFramework::SearchSessionsRequest& searchSessionsRequest) const override; void LeaveSession() override; - protected: - // Use for automation tests only to inject mock objects. - void SetGameLiftClient(AZStd::shared_ptr gameliftClient); - private: AZStd::string CreateSessionHelper(const AWSGameLiftCreateSessionRequest& createSessionRequest); AZStd::string CreateSessionOnQueueHelper(const AWSGameLiftCreateSessionOnQueueRequest& createSessionOnQueueRequest); bool JoinSessionHelper(const AWSGameLiftJoinSessionRequest& joinSessionRequest); AzFramework::SearchSessionsResponse SearchSessionsHelper(const AWSGameLiftSearchSessionsRequest& searchSessionsRequest) const; - - AZStd::shared_ptr m_gameliftClient; }; } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp index db201920f6..ea5fff99a3 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.cpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include @@ -27,7 +28,8 @@ namespace AWSGameLift { AWSGameLiftClientSystemComponent::AWSGameLiftClientSystemComponent() { - m_gameliftClientManager = AZStd::make_unique(); + m_gameliftManager = AZStd::make_unique(); + m_gameliftTicketTracker = AZStd::make_unique(); } void AWSGameLiftClientSystemComponent::Reflect(AZ::ReflectContext* context) @@ -90,12 +92,20 @@ namespace AWSGameLift void AWSGameLiftClientSystemComponent::Activate() { - m_gameliftClientManager->ActivateManager(); + AZ::Interface::Register(this); + + m_gameliftClient.reset(); + m_gameliftManager->ActivateManager(); + m_gameliftTicketTracker->ActivateTracker(); } void AWSGameLiftClientSystemComponent::Deactivate() { - m_gameliftClientManager->DeactivateManager(); + m_gameliftTicketTracker->DeactivateTracker(); + m_gameliftManager->DeactivateManager(); + m_gameliftClient.reset(); + + AZ::Interface::Unregister(this); } void AWSGameLiftClientSystemComponent::ReflectGameLiftMatchmaking(AZ::ReflectContext* context) @@ -213,9 +223,25 @@ namespace AWSGameLift } } - void AWSGameLiftClientSystemComponent::SetGameLiftClientManager(AZStd::unique_ptr gameliftClientManager) + AZStd::shared_ptr AWSGameLiftClientSystemComponent::GetGameLiftClient() const { - m_gameliftClientManager.reset(); - m_gameliftClientManager = AZStd::move(gameliftClientManager); + return m_gameliftClient; + } + + void AWSGameLiftClientSystemComponent::SetGameLiftClient(AZStd::shared_ptr gameliftClient) + { + m_gameliftClient.swap(gameliftClient); + } + + void AWSGameLiftClientSystemComponent::SetGameLiftClientManager(AZStd::unique_ptr gameliftManager) + { + m_gameliftManager.reset(); + m_gameliftManager = AZStd::move(gameliftManager); + } + + void AWSGameLiftClientSystemComponent::SetGameLiftClientTicketTracker(AZStd::unique_ptr gameliftTicketTracker) + { + m_gameliftTicketTracker.reset(); + m_gameliftTicketTracker = AZStd::move(gameliftTicketTracker); } } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.h index f54461b918..30d4ee0106 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/AWSGameLiftClientSystemComponent.h @@ -11,6 +11,9 @@ #include #include +#include +#include + namespace AWSGameLift { class AWSGameLiftClientManager; @@ -18,6 +21,7 @@ namespace AWSGameLift //! Gem client system component. Responsible for creating the gamelift client manager. class AWSGameLiftClientSystemComponent : public AZ::Component + , public IAWSGameLiftInternalRequests { public: AZ_COMPONENT(AWSGameLiftClientSystemComponent, "{d481c15c-732a-4eea-9853-4965ed1bc2be}"); @@ -32,6 +36,10 @@ namespace AWSGameLift static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + // IAWSGameLiftInternalRequests interface implementation + AZStd::shared_ptr GetGameLiftClient() const override; + void SetGameLiftClient(AZStd::shared_ptr gameliftClient) override; + protected: //////////////////////////////////////////////////////////////////////// // AZ::Component interface implementation @@ -40,7 +48,8 @@ namespace AWSGameLift void Deactivate() override; //////////////////////////////////////////////////////////////////////// - void SetGameLiftClientManager(AZStd::unique_ptr gameliftClientManager); + void SetGameLiftClientManager(AZStd::unique_ptr gameliftManager); + void SetGameLiftClientTicketTracker(AZStd::unique_ptr gameliftTicketTracker); private: static void ReflectGameLiftMatchmaking(AZ::ReflectContext* context); @@ -49,7 +58,9 @@ namespace AWSGameLift static void ReflectCreateSessionRequest(AZ::ReflectContext* context); static void ReflectSearchSessionsResponse(AZ::ReflectContext* context); - AZStd::unique_ptr m_gameliftClientManager; + AZStd::shared_ptr m_gameliftClient; + AZStd::unique_ptr m_gameliftManager; + AZStd::unique_ptr m_gameliftTicketTracker; }; } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftInternalRequests.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftInternalRequests.h new file mode 100644 index 0000000000..dbac9a798a --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftInternalRequests.h @@ -0,0 +1,43 @@ +/* + * 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 + +namespace Aws +{ + namespace GameLift + { + class GameLiftClient; + } +} // namespace Aws + +namespace AWSGameLift +{ + //! IAWSGameLiftRequests + //! GameLift Gem internal interface which is used to fetch gem global GameLift client + class IAWSGameLiftInternalRequests + { + public: + AZ_RTTI(IAWSGameLiftInternalRequests, "{DC0CC1C4-21EE-4A41-B428-D12D697F88A2}"); + + IAWSGameLiftInternalRequests() = default; + virtual ~IAWSGameLiftInternalRequests() = default; + + //! GetGameLiftClient + //! Get GameLift client to interact with Amazon GameLift service + //! @return Shared pointer to the GameLift client + virtual AZStd::shared_ptr GetGameLiftClient() const = 0; + + //! SetGameLiftClient + //! Set GameLift client to provided GameLift client + //! @param gameliftClient Input new GameLift client + virtual void SetGameLiftClient(AZStd::shared_ptr gameliftClient) = 0; + }; +} // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftMatchmakingInternalRequests.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftMatchmakingInternalRequests.h new file mode 100644 index 0000000000..81d04314af --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Request/IAWSGameLiftMatchmakingInternalRequests.h @@ -0,0 +1,39 @@ +/* + * 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/AWSGameLiftClientLocalTicketTrackerTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp new file mode 100644 index 0000000000..6e688b28e3 --- /dev/null +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp @@ -0,0 +1,353 @@ +/* + * 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 + * + */ + +#include + +#include +#include +#include + +#include + +using namespace AWSGameLift; + +static constexpr const uint64_t TEST_RACKER_POLLING_PERIOD_MS = 100; +static constexpr const uint64_t TEST_WAIT_BUFFER_TIME_MS = 10; +static constexpr const uint64_t TEST_WAIT_MAXIMUM_TIME_MS = 10000; + +class TestAWSGameLiftClientLocalTicketTracker + : public AWSGameLiftClientLocalTicketTracker +{ +public: + TestAWSGameLiftClientLocalTicketTracker() = default; + virtual ~TestAWSGameLiftClientLocalTicketTracker() = default; + + void SetUp() + { + ActivateTracker(); + m_pollingPeriodInMS = TEST_RACKER_POLLING_PERIOD_MS; + } + + void TearDown() + { + DeactivateTracker(); + } + + bool IsTrackerIdle() + { + return m_status == TicketTrackerStatus::Idle; + } +}; + +class AWSGameLiftClientLocalTicketTrackerTest + : public AWSGameLiftClientFixture + , public IAWSGameLiftInternalRequests +{ +protected: + void SetUp() override + { + AWSGameLiftClientFixture::SetUp(); + + AZ::Interface::Register(this); + + m_gameliftClientMockPtr = AZStd::make_shared(); + m_gameliftClientTicketTracker = AZStd::make_unique(); + m_gameliftClientTicketTracker->SetUp(); + } + + void TearDown() override + { + m_gameliftClientTicketTracker->TearDown(); + m_gameliftClientTicketTracker.reset(); + m_gameliftClientMockPtr.reset(); + + AZ::Interface::Unregister(this); + + AWSGameLiftClientFixture::TearDown(); + } + + AZStd::shared_ptr GetGameLiftClient() const + { + return m_gameliftClientMockPtr; + } + + void SetGameLiftClient(AZStd::shared_ptr gameliftClient) + { + AZ_UNUSED(gameliftClient); + m_gameliftClientMockPtr.reset(); + } + + void WaitForProcessFinish(uint64_t expectedNum) + { + int processingTime = 0; + while (processingTime < TEST_WAIT_MAXIMUM_TIME_MS) + { + if (::UnitTest::TestRunner::Instance().m_numAssertsFailed == expectedNum) + { + AZ_TEST_STOP_TRACE_SUPPRESSION(expectedNum); + return; + } + AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(TEST_WAIT_BUFFER_TIME_MS)); + processingTime += TEST_WAIT_BUFFER_TIME_MS; + } + } + + void WaitForProcessFinish() + { + int processingTime = 0; + while (processingTime < TEST_WAIT_MAXIMUM_TIME_MS) + { + if (m_gameliftClientTicketTracker->IsTrackerIdle()) + { + return; + } + AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(TEST_WAIT_BUFFER_TIME_MS)); + processingTime += TEST_WAIT_BUFFER_TIME_MS; + } + } + +public: + AZStd::unique_ptr m_gameliftClientTicketTracker; + AZStd::shared_ptr m_gameliftClientMockPtr; +}; + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallWithoutClientSetup_GetExpectedErrors) +{ + AZ::Interface::Get()->SetGameLiftClient(nullptr); + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(1); + ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_MultipleCallsWithoutClientSetup_GetExpectedErrors) +{ + AZ::Interface::Get()->SetGameLiftClient(nullptr); + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(1); + ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButWithFailedOutcome_GetExpectedErrors) +{ + Aws::Client::AWSError error; + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome(error); + + EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(1); + ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallWithMoreThanOneTicket_GetExpectedErrors) +{ + Aws::GameLift::Model::DescribeMatchmakingResult result; + result.AddTicketList(Aws::GameLift::Model::MatchmakingTicket()); + result.AddTicketList(Aws::GameLift::Model::MatchmakingTicket()); + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome(result); + + EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(1); + ASSERT_FALSE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallWithCompleteStatus_ProcessStopsAndGetExpectedResult) +{ + Aws::GameLift::Model::GameSessionConnectionInfo connectionInfo; + connectionInfo.SetIpAddress("DummyIpAddress"); + connectionInfo.SetPort(123); + connectionInfo.AddMatchedPlayerSessions( + Aws::GameLift::Model::MatchedPlayerSession() + .WithPlayerId("player1") + .WithPlayerSessionId("playersession1")); + + Aws::GameLift::Model::MatchmakingTicket ticket; + ticket.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::COMPLETED); + ticket.SetGameSessionConnectionInfo(connectionInfo); + + Aws::GameLift::Model::DescribeMatchmakingResult result; + result.AddTicketList(ticket); + + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome(result); + EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + SessionHandlingClientRequestsMock handlerMock; + EXPECT_CALL(handlerMock, RequestPlayerJoinSession(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(true)); + + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(); + ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButNoPlayerSession_ProcessStopsAndGetExpectedError) +{ + Aws::GameLift::Model::GameSessionConnectionInfo connectionInfo; + connectionInfo.SetIpAddress("DummyIpAddress"); + connectionInfo.SetPort(123); + + Aws::GameLift::Model::MatchmakingTicket ticket; + ticket.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::COMPLETED); + ticket.SetGameSessionConnectionInfo(connectionInfo); + + Aws::GameLift::Model::DescribeMatchmakingResult result; + result.AddTicketList(ticket); + + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome(result); + EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(1); + ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButFailedToJoinMatch_ProcessStopsAndGetExpectedError) +{ + Aws::GameLift::Model::GameSessionConnectionInfo connectionInfo; + connectionInfo.SetIpAddress("DummyIpAddress"); + connectionInfo.SetPort(123); + connectionInfo.AddMatchedPlayerSessions( + Aws::GameLift::Model::MatchedPlayerSession() + .WithPlayerId("player1") + .WithPlayerSessionId("playersession1")); + + Aws::GameLift::Model::MatchmakingTicket ticket; + ticket.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::COMPLETED); + ticket.SetGameSessionConnectionInfo(connectionInfo); + + Aws::GameLift::Model::DescribeMatchmakingResult result; + result.AddTicketList(ticket); + + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome(result); + EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + SessionHandlingClientRequestsMock handlerMock; + EXPECT_CALL(handlerMock, RequestPlayerJoinSession(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(false)); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(1); + ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButTicketTimeOut_ProcessStopsAndGetExpectedError) +{ + Aws::GameLift::Model::MatchmakingTicket ticket; + ticket.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::TIMED_OUT); + + Aws::GameLift::Model::DescribeMatchmakingResult result; + result.AddTicketList(ticket); + + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome(result); + EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(1); + ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButTicketFailed_ProcessStopsAndGetExpectedError) +{ + Aws::GameLift::Model::MatchmakingTicket ticket; + ticket.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::FAILED); + + Aws::GameLift::Model::DescribeMatchmakingResult result; + result.AddTicketList(ticket); + + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome(result); + EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(1); + ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallButTicketCancelled_ProcessStopsAndGetExpectedError) +{ + Aws::GameLift::Model::MatchmakingTicket ticket; + ticket.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::CANCELLED); + + Aws::GameLift::Model::DescribeMatchmakingResult result; + result.AddTicketList(ticket); + + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome(result); + EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(outcome)); + + AZ_TEST_START_TRACE_SUPPRESSION; + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(1); + ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} + +TEST_F(AWSGameLiftClientLocalTicketTrackerTest, StartPolling_CallAndTicketCompleteAtLast_ProcessContinuesAndStop) +{ + Aws::GameLift::Model::MatchmakingTicket ticket1; + ticket1.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::QUEUED); + + Aws::GameLift::Model::DescribeMatchmakingResult result1; + result1.AddTicketList(ticket1); + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome1(result1); + + Aws::GameLift::Model::GameSessionConnectionInfo connectionInfo; + connectionInfo.SetIpAddress("DummyIpAddress"); + connectionInfo.SetPort(123); + connectionInfo.AddMatchedPlayerSessions( + Aws::GameLift::Model::MatchedPlayerSession() + .WithPlayerId("player1") + .WithPlayerSessionId("playersession1")); + + Aws::GameLift::Model::MatchmakingTicket ticket2; + ticket2.SetStatus(Aws::GameLift::Model::MatchmakingConfigurationStatus::COMPLETED); + ticket2.SetGameSessionConnectionInfo(connectionInfo); + + Aws::GameLift::Model::DescribeMatchmakingResult result2; + result2.AddTicketList(ticket2); + Aws::GameLift::Model::DescribeMatchmakingOutcome outcome2(result2); + + EXPECT_CALL(*m_gameliftClientMockPtr, DescribeMatchmaking(::testing::_)) + .WillOnce(::testing::Return(outcome1)) + .WillOnce(::testing::Return(outcome2)); + + SessionHandlingClientRequestsMock handlerMock; + EXPECT_CALL(handlerMock, RequestPlayerJoinSession(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(true)); + + m_gameliftClientTicketTracker->StartPolling("ticket1", "player1"); + WaitForProcessFinish(); + ASSERT_TRUE(m_gameliftClientTicketTracker->IsTrackerIdle()); +} diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp index caa5113a25..e7612618ee 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientManagerTest.cpp @@ -23,8 +23,7 @@ #include #include #include - -#include +#include using namespace AWSGameLift; @@ -117,38 +116,19 @@ public: MOCK_METHOD0(GetDefaultConfig, AWSCore::AwsApiJobConfig*()); }; -class TestAWSGameLiftClientManager - : public AWSGameLiftClientManager -{ -public: - TestAWSGameLiftClientManager() - { - m_gameliftClientMockPtr = nullptr; - } - ~TestAWSGameLiftClientManager() - { - m_gameliftClientMockPtr = nullptr; - } - - void SetUpMockClient() - { - m_gameliftClientMockPtr = AZStd::make_shared(); - SetGameLiftClient(m_gameliftClientMockPtr); - } - - AZStd::shared_ptr m_gameliftClientMockPtr; -}; - class AWSGameLiftClientManagerTest : public AWSGameLiftClientFixture + , public IAWSGameLiftInternalRequests { protected: void SetUp() override { AWSGameLiftClientFixture::SetUp(); - m_gameliftClientManager = AZStd::make_unique(); - m_gameliftClientManager->SetUpMockClient(); + AZ::Interface::Register(this); + + m_gameliftClientMockPtr = AZStd::make_shared(); + m_gameliftClientManager = AZStd::make_unique(); m_gameliftClientManager->ActivateManager(); } @@ -156,10 +136,24 @@ protected: { m_gameliftClientManager->DeactivateManager(); m_gameliftClientManager.reset(); + m_gameliftClientMockPtr.reset(); + + AZ::Interface::Unregister(this); AWSGameLiftClientFixture::TearDown(); } + AZStd::shared_ptr GetGameLiftClient() const + { + return m_gameliftClientMockPtr; + } + + void SetGameLiftClient(AZStd::shared_ptr gameliftClient) + { + AZ_UNUSED(gameliftClient); + m_gameliftClientMockPtr.reset(); + } + AWSGameLiftSearchSessionsRequest GetValidSearchSessionsRequest() { AWSGameLiftSearchSessionsRequest request; @@ -230,7 +224,8 @@ protected: } public: - AZStd::unique_ptr m_gameliftClientManager; + AZStd::unique_ptr m_gameliftClientManager; + AZStd::shared_ptr m_gameliftClientMockPtr; }; TEST_F(AWSGameLiftClientManagerTest, ConfigureGameLiftClient_CallWithoutRegion_GetFalseAsResult) @@ -319,7 +314,7 @@ TEST_F(AWSGameLiftClientManagerTest, CreateSession_CallWithValidRequest_GetSucce Aws::GameLift::Model::CreateGameSessionResult result; result.SetGameSession(Aws::GameLift::Model::GameSession()); Aws::GameLift::Model::CreateGameSessionOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreateGameSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreateGameSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); m_gameliftClientManager->CreateSession(request); @@ -331,7 +326,7 @@ TEST_F(AWSGameLiftClientManagerTest, CreateSession_CallWithValidRequest_GetError request.m_aliasId = "dummyAlias"; Aws::Client::AWSError error; Aws::GameLift::Model::CreateGameSessionOutcome outcome(error); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreateGameSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreateGameSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); AZ_TEST_START_TRACE_SUPPRESSION; @@ -357,7 +352,7 @@ TEST_F(AWSGameLiftClientManagerTest, CreateSessionAsync_CallWithValidRequest_Get Aws::GameLift::Model::CreateGameSessionResult result; result.SetGameSession(Aws::GameLift::Model::GameSession()); Aws::GameLift::Model::CreateGameSessionOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreateGameSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreateGameSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); SessionAsyncRequestNotificationsHandlerMock sessionHandlerMock; @@ -373,7 +368,7 @@ TEST_F(AWSGameLiftClientManagerTest, CreateSessionAsync_CallWithValidRequest_Get request.m_aliasId = "dummyAlias"; Aws::Client::AWSError error; Aws::GameLift::Model::CreateGameSessionOutcome outcome(error); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreateGameSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreateGameSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); SessionAsyncRequestNotificationsHandlerMock sessionHandlerMock; @@ -403,7 +398,7 @@ TEST_F(AWSGameLiftClientManagerTest, CreateSessionOnQueue_CallWithValidRequest_G Aws::GameLift::Model::StartGameSessionPlacementResult result; result.SetGameSessionPlacement(Aws::GameLift::Model::GameSessionPlacement()); Aws::GameLift::Model::StartGameSessionPlacementOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), StartGameSessionPlacement(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, StartGameSessionPlacement(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); m_gameliftClientManager->CreateSession(request); @@ -416,7 +411,7 @@ TEST_F(AWSGameLiftClientManagerTest, CreateSessionOnQueue_CallWithValidRequest_G request.m_placementId = "dummyPlacementId"; Aws::Client::AWSError error; Aws::GameLift::Model::StartGameSessionPlacementOutcome outcome(error); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), StartGameSessionPlacement(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, StartGameSessionPlacement(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); AZ_TEST_START_TRACE_SUPPRESSION; @@ -434,7 +429,7 @@ TEST_F(AWSGameLiftClientManagerTest, CreateSessionOnQueueAsync_CallWithValidRequ Aws::GameLift::Model::StartGameSessionPlacementResult result; result.SetGameSessionPlacement(Aws::GameLift::Model::GameSessionPlacement()); Aws::GameLift::Model::StartGameSessionPlacementOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), StartGameSessionPlacement(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, StartGameSessionPlacement(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); SessionAsyncRequestNotificationsHandlerMock sessionHandlerMock; @@ -451,7 +446,7 @@ TEST_F(AWSGameLiftClientManagerTest, CreateSessionOnQueueAsync_CallWithValidRequ request.m_placementId = "dummyPlacementId"; Aws::Client::AWSError error; Aws::GameLift::Model::StartGameSessionPlacementOutcome outcome(error); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), StartGameSessionPlacement(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, StartGameSessionPlacement(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); SessionAsyncRequestNotificationsHandlerMock sessionHandlerMock; @@ -489,7 +484,7 @@ TEST_F(AWSGameLiftClientManagerTest, JoinSession_CallWithValidRequestButNoReques Aws::GameLift::Model::CreatePlayerSessionResult result; result.SetPlayerSession(Aws::GameLift::Model::PlayerSession()); Aws::GameLift::Model::CreatePlayerSessionOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreatePlayerSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreatePlayerSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); AZ_TEST_START_TRACE_SUPPRESSION; @@ -505,7 +500,7 @@ TEST_F(AWSGameLiftClientManagerTest, JoinSession_CallWithValidRequest_GetErrorOu request.m_playerId = "dummyPlayerId"; Aws::Client::AWSError error; Aws::GameLift::Model::CreatePlayerSessionOutcome outcome(error); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreatePlayerSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreatePlayerSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); AZ_TEST_START_TRACE_SUPPRESSION; @@ -524,7 +519,7 @@ TEST_F(AWSGameLiftClientManagerTest, JoinSession_CallWithValidRequestAndRequestH Aws::GameLift::Model::CreatePlayerSessionResult result; result.SetPlayerSession(Aws::GameLift::Model::PlayerSession()); Aws::GameLift::Model::CreatePlayerSessionOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreatePlayerSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreatePlayerSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); auto response = m_gameliftClientManager->JoinSession(request); @@ -541,7 +536,7 @@ TEST_F(AWSGameLiftClientManagerTest, JoinSession_CallWithValidRequestAndRequestH Aws::GameLift::Model::CreatePlayerSessionResult result; result.SetPlayerSession(Aws::GameLift::Model::PlayerSession()); Aws::GameLift::Model::CreatePlayerSessionOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreatePlayerSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreatePlayerSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); auto response = m_gameliftClientManager->JoinSession(request); @@ -567,7 +562,7 @@ TEST_F(AWSGameLiftClientManagerTest, JoinSessionAsync_CallWithValidRequestButNoR Aws::GameLift::Model::CreatePlayerSessionResult result; result.SetPlayerSession(Aws::GameLift::Model::PlayerSession()); Aws::GameLift::Model::CreatePlayerSessionOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreatePlayerSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreatePlayerSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); SessionAsyncRequestNotificationsHandlerMock sessionHandlerMock; @@ -586,7 +581,7 @@ TEST_F(AWSGameLiftClientManagerTest, JoinSessionAsync_CallWithValidRequest_GetEr request.m_playerId = "dummyPlayerId"; Aws::Client::AWSError error; Aws::GameLift::Model::CreatePlayerSessionOutcome outcome(error); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreatePlayerSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreatePlayerSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); SessionAsyncRequestNotificationsHandlerMock sessionHandlerMock; @@ -608,7 +603,7 @@ TEST_F(AWSGameLiftClientManagerTest, JoinSessionAsync_CallWithValidRequestAndReq Aws::GameLift::Model::CreatePlayerSessionResult result; result.SetPlayerSession(Aws::GameLift::Model::PlayerSession()); Aws::GameLift::Model::CreatePlayerSessionOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreatePlayerSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreatePlayerSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); SessionAsyncRequestNotificationsHandlerMock sessionHandlerMock; @@ -628,7 +623,7 @@ TEST_F(AWSGameLiftClientManagerTest, JoinSessionAsync_CallWithValidRequestAndReq Aws::GameLift::Model::CreatePlayerSessionResult result; result.SetPlayerSession(Aws::GameLift::Model::PlayerSession()); Aws::GameLift::Model::CreatePlayerSessionOutcome outcome(result); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), CreatePlayerSession(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, CreatePlayerSession(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); SessionAsyncRequestNotificationsHandlerMock sessionHandlerMock; @@ -642,7 +637,7 @@ TEST_F(AWSGameLiftClientManagerTest, SearchSessions_CallWithValidRequestAndError Aws::Client::AWSError error; Aws::GameLift::Model::SearchGameSessionsOutcome outcome(error); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), SearchGameSessions(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, SearchGameSessions(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); @@ -657,7 +652,7 @@ TEST_F(AWSGameLiftClientManagerTest, SearchSessions_CallWithValidRequestAndSucce AWSGameLiftSearchSessionsRequest request = GetValidSearchSessionsRequest(); Aws::GameLift::Model::SearchGameSessionsOutcome outcome = GetValidSearchGameSessionsOutcome(); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), SearchGameSessions(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, SearchGameSessions(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); @@ -703,7 +698,7 @@ TEST_F(AWSGameLiftClientManagerTest, SearchSessionsAsync_CallWithValidRequestAnd Aws::Client::AWSError error; Aws::GameLift::Model::SearchGameSessionsOutcome outcome(error); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), SearchGameSessions(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, SearchGameSessions(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); @@ -724,7 +719,7 @@ TEST_F(AWSGameLiftClientManagerTest, SearchSessionsAsync_CallWithValidRequestAnd EXPECT_CALL(coreHandlerMock, GetDefaultJobContext()).Times(1).WillOnce(::testing::Return(m_jobContext.get())); Aws::GameLift::Model::SearchGameSessionsOutcome outcome = GetValidSearchGameSessionsOutcome(); - EXPECT_CALL(*(m_gameliftClientManager->m_gameliftClientMockPtr), SearchGameSessions(::testing::_)) + EXPECT_CALL(*m_gameliftClientMockPtr, SearchGameSessions(::testing::_)) .Times(1) .WillOnce(::testing::Return(outcome)); diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h index a161168159..5c09f43e08 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientMocks.h @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -39,6 +41,7 @@ public: MOCK_CONST_METHOD1(CreateGameSession, Model::CreateGameSessionOutcome(const Model::CreateGameSessionRequest&)); MOCK_CONST_METHOD1(CreatePlayerSession, Model::CreatePlayerSessionOutcome(const Model::CreatePlayerSessionRequest&)); + MOCK_CONST_METHOD1(DescribeMatchmaking, Model::DescribeMatchmakingOutcome(const Model::DescribeMatchmakingRequest&)); MOCK_CONST_METHOD1(SearchGameSessions, Model::SearchGameSessionsOutcome(const Model::SearchGameSessionsRequest&)); MOCK_CONST_METHOD1(StartGameSessionPlacement, Model::StartGameSessionPlacementOutcome(const Model::StartGameSessionPlacementRequest&)); }; diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientSystemComponentTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientSystemComponentTest.cpp index df6fb0d666..096a182819 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientSystemComponentTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/AWSGameLiftClientSystemComponentTest.cpp @@ -13,10 +13,9 @@ #include #include +#include #include -#include - using namespace AWSGameLift; class AWSGameLiftClientManagerMock @@ -30,6 +29,17 @@ public: MOCK_METHOD0(DeactivateManager, void()); }; +class AWSGameLiftClientLocalTicketTrackerMock + : public AWSGameLiftClientLocalTicketTracker +{ +public: + AWSGameLiftClientLocalTicketTrackerMock() = default; + ~AWSGameLiftClientLocalTicketTrackerMock() = default; + + MOCK_METHOD0(ActivateTracker, void()); + MOCK_METHOD0(DeactivateTracker, void()); +}; + class TestAWSGameLiftClientSystemComponent : public AWSGameLiftClientSystemComponent { @@ -37,20 +47,29 @@ public: TestAWSGameLiftClientSystemComponent() { m_gameliftClientManagerMockPtr = nullptr; + m_gameliftClientTicketTrackerMockPtr = nullptr; } ~TestAWSGameLiftClientSystemComponent() { m_gameliftClientManagerMockPtr = nullptr; + m_gameliftClientTicketTrackerMockPtr = nullptr; } void SetUpMockManager() { - AZStd::unique_ptr gameliftClientManagerMock = AZStd::make_unique(); + AZStd::unique_ptr gameliftClientManagerMock = + AZStd::make_unique(); m_gameliftClientManagerMockPtr = gameliftClientManagerMock.get(); SetGameLiftClientManager(AZStd::move(gameliftClientManagerMock)); + + AZStd::unique_ptr gameliftClientTicketTrackerMock = + AZStd::make_unique(); + m_gameliftClientTicketTrackerMockPtr = gameliftClientTicketTrackerMock.get(); + SetGameLiftClientTicketTracker(AZStd::move(gameliftClientTicketTrackerMock)); } AWSGameLiftClientManagerMock* m_gameliftClientManagerMockPtr; + AWSGameLiftClientLocalTicketTrackerMock* m_gameliftClientTicketTrackerMockPtr; }; class AWSCoreSystemComponentMock @@ -145,8 +164,10 @@ TEST_F(AWSGameLiftClientSystemComponentTest, ActivateDeactivate_Call_GameLiftCli { m_entity->Init(); EXPECT_CALL(*(m_gameliftClientSystemComponent->m_gameliftClientManagerMockPtr), ActivateManager()).Times(1); + EXPECT_CALL(*(m_gameliftClientSystemComponent->m_gameliftClientTicketTrackerMockPtr), ActivateTracker()).Times(1); m_entity->Activate(); EXPECT_CALL(*(m_gameliftClientSystemComponent->m_gameliftClientManagerMockPtr), DeactivateManager()).Times(1); + EXPECT_CALL(*(m_gameliftClientSystemComponent->m_gameliftClientTicketTrackerMockPtr), DeactivateTracker()).Times(1); m_entity->Deactivate(); } diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake index 0c7ca68eea..d3b9d8d1b9 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_files.cmake @@ -7,6 +7,7 @@ # set(FILES + ../AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h Include/Request/AWSGameLiftAcceptMatchRequest.h Include/Request/AWSGameLiftCreateSessionOnQueueRequest.h Include/Request/AWSGameLiftCreateSessionRequest.h @@ -27,6 +28,8 @@ set(FILES Source/Activity/AWSGameLiftLeaveSessionActivity.h Source/Activity/AWSGameLiftSearchSessionsActivity.cpp Source/Activity/AWSGameLiftSearchSessionsActivity.h + Source/AWSGameLiftClientLocalTicketTracker.cpp + Source/AWSGameLiftClientLocalTicketTracker.h Source/AWSGameLiftClientManager.cpp Source/AWSGameLiftClientManager.h Source/AWSGameLiftClientSystemComponent.cpp @@ -38,4 +41,6 @@ set(FILES Source/Request/AWSGameLiftSearchSessionsRequest.cpp Source/Request/AWSGameLiftStartMatchmakingRequest.cpp Source/Request/AWSGameLiftStopMatchmakingRequest.cpp + Source/Request/IAWSGameLiftInternalRequests.h + Source/Request/IAWSGameLiftMatchmakingInternalRequests.h ) diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_tests_files.cmake b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_tests_files.cmake index 6614eabb46..130ed28e4e 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_tests_files.cmake +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/awsgamelift_client_tests_files.cmake @@ -12,6 +12,7 @@ set(FILES Tests/Activity/AWSGameLiftJoinSessionActivityTest.cpp Tests/Activity/AWSGameLiftSearchSessionsActivityTest.cpp Tests/AWSGameLiftClientFixture.h + Tests/AWSGameLiftClientLocalTicketTrackerTest.cpp Tests/AWSGameLiftClientManagerTest.cpp Tests/AWSGameLiftClientMocks.h Tests/AWSGameLiftClientSystemComponentTest.cpp diff --git a/Gems/AWSGameLift/Code/AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h b/Gems/AWSGameLift/Code/AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h index 588bd51380..e29df324d3 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h @@ -17,4 +17,5 @@ namespace AWSGameLift static const char* AWSGameLiftSessionStatusReasons[2] = { "NotSet", "Interrupted" }; static constexpr const char AWSGameLiftErrorMessageTemplate[] = "Exception: %s, Message: %s"; + static constexpr const char AWSGameLiftClientMissingErrorMessage[] = "GameLift client is not configured yet."; } // namespace AWSGameLift