[LYN-4065] Expose GameLift server api to notify server process ready (#2409)

This commit is contained in:
Vincent Liu
2021-08-30 13:06:29 -07:00
committed by GitHub
parent 28e6262a8b
commit da6bdd5b43
9 changed files with 142 additions and 87 deletions
@@ -16,6 +16,8 @@ ly_add_target(
FILES_CMAKE
awsgamelift_server_files.cmake
INCLUDE_DIRECTORIES
PUBLIC
Include
PRIVATE
../AWSGameLiftCommon/Source
Source
@@ -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 <AzCore/EBus/EBus.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/std/string/string.h>
#include <AzFramework/Session/ISessionRequests.h>
namespace AWSGameLift
{
//! IAWSGameLiftServerRequests
//! Server interfaces to expose Amazon GameLift Server SDK
class IAWSGameLiftServerRequests
{
public:
AZ_RTTI(IAWSGameLiftServerRequests, "{D76CD98D-4C37-4C25-82C4-4E8772706D70}");
IAWSGameLiftServerRequests() = default;
virtual ~IAWSGameLiftServerRequests() = default;
//! Notify GameLift that the server process is ready to host a game session.
//! @return Whether the ProcessReady notification is sent to GameLift.
virtual bool NotifyGameLiftProcessReady() = 0;
};
// IAWSGameLiftServerRequests EBus wrapper for scripting
class AWSGameLiftServerRequests
: 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 AWSGameLiftServerRequestBus = AZ::EBus<IAWSGameLiftServerRequests, AWSGameLiftServerRequests>;
} // namespace AWSGameLift
@@ -10,8 +10,11 @@
#include <AWSGameLiftSessionConstants.h>
#include <GameLiftServerSDKWrapper.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Debug/Trace.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Jobs/JobFunction.h>
#include <AzCore/Jobs/JobManagerBus.h>
#include <AzCore/std/bind/bind.h>
@@ -32,6 +35,18 @@ namespace AWSGameLift
m_connectedPlayers.clear();
}
void AWSGameLiftServerManager::ActivateManager()
{
AZ::Interface<IAWSGameLiftServerRequests>::Register(this);
AWSGameLiftServerRequestBus::Handler::BusConnect();
}
void AWSGameLiftServerManager::DeactivateManager()
{
AWSGameLiftServerRequestBus::Handler::BusDisconnect();
AZ::Interface<IAWSGameLiftServerRequests>::Unregister(this);
}
bool AWSGameLiftServerManager::AddConnectedPlayer(const AzFramework::PlayerConnectionConfig& playerConnectionConfig)
{
AZStd::lock_guard<AZStd::mutex> lock(m_gameliftMutex);
@@ -51,6 +66,37 @@ namespace AWSGameLift
}
}
GameLiftServerProcessDesc AWSGameLiftServerManager::BuildGameLiftServerProcessDesc()
{
GameLiftServerProcessDesc serverProcessDesc;
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetDirectInstance();
if (fileIO)
{
const char pathToLogFolder[] = "@log@/";
char resolvedPath[AZ_MAX_PATH_LEN];
if (fileIO->ResolvePath(pathToLogFolder, resolvedPath, AZ_ARRAY_SIZE(resolvedPath)))
{
serverProcessDesc.m_logPaths.push_back(resolvedPath);
}
else
{
AZ_Error(AWSGameLiftServerManagerName, false, "Failed to resolve the path to the log folder.");
}
}
else
{
AZ_Error(AWSGameLiftServerManagerName, false, "Failed to get File IO.");
}
if (auto console = AZ::Interface<AZ::IConsole>::Get(); console != nullptr)
{
[[maybe_unused]] AZ::GetValueResult getCvarResult = console->GetCvarValue("sv_port", serverProcessDesc.m_port);
AZ_Error(AWSGameLiftServerManagerName, getCvarResult == AZ::GetValueResult::Success,
"Lookup of 'sv_port' console variable failed with error %s", AZ::GetEnumString(getCvarResult));
}
return serverProcessDesc;
}
AzFramework::SessionConfig AWSGameLiftServerManager::BuildSessionConfig(const Aws::GameLift::Server::Model::GameSession& gameSession)
{
AzFramework::SessionConfig sessionConfig;
@@ -99,12 +145,12 @@ namespace AWSGameLift
return AZ::IO::Path();
}
bool AWSGameLiftServerManager::InitializeGameLiftServerSDK()
void AWSGameLiftServerManager::InitializeGameLiftServerSDK()
{
if (m_serverSDKInitialized)
{
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftServerSDKAlreadyInitErrorMessage);
return false;
return;
}
AZ_TracePrintf(AWSGameLiftServerManagerName, "Initiating Amazon GameLift Server SDK ...");
@@ -115,8 +161,6 @@ namespace AWSGameLift
AZ_Error(AWSGameLiftServerManagerName, m_serverSDKInitialized,
AWSGameLiftServerInitSDKErrorMessage, initOutcome.GetError().GetErrorMessage().c_str());
return m_serverSDKInitialized;
}
void AWSGameLiftServerManager::HandleDestroySession()
@@ -170,7 +214,7 @@ namespace AWSGameLift
playerSessionId.c_str(), disconnectOutcome.GetError().GetErrorMessage().c_str());
}
bool AWSGameLiftServerManager::NotifyGameLiftProcessReady(const GameLiftServerProcessDesc& desc)
bool AWSGameLiftServerManager::NotifyGameLiftProcessReady()
{
if (!m_serverSDKInitialized)
{
@@ -178,6 +222,7 @@ namespace AWSGameLift
return false;
}
GameLiftServerProcessDesc desc = BuildGameLiftServerProcessDesc();
AZ_Warning(AWSGameLiftServerManagerName, desc.m_port != 0, AWSGameLiftServerTempPortErrorMessage);
AZ::JobContext* jobContext = nullptr;
@@ -16,6 +16,7 @@
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzFramework/Session/ISessionHandlingRequests.h>
#include <AzFramework/Session/SessionConfig.h>
#include <Request/IAWSGameLiftServerRequests.h>
namespace AWSGameLift
{
@@ -31,7 +32,8 @@ namespace AWSGameLift
//! Manage the server process for hosting game sessions via GameLiftServerSDK.
class AWSGameLiftServerManager
: public AzFramework::ISessionHandlingProviderRequests
: public AWSGameLiftServerRequestBus::Handler
, public AzFramework::ISessionHandlingProviderRequests
{
public:
static constexpr const char AWSGameLiftServerManagerName[] = "AWSGameLiftServerManager";
@@ -68,14 +70,14 @@ namespace AWSGameLift
AWSGameLiftServerManager();
virtual ~AWSGameLiftServerManager();
//! Initialize GameLift API client by calling InitSDK().
//! @return Whether the initialization is successful.
bool InitializeGameLiftServerSDK();
void ActivateManager();
void DeactivateManager();
//! Notify GameLift that the server process is ready to host a game session.
//! @param desc GameLift server process settings.
//! @return Whether the ProcessReady notification is sent to GameLift.
bool NotifyGameLiftProcessReady(const GameLiftServerProcessDesc& desc);
//! Initialize GameLift API client by calling InitSDK().
void InitializeGameLiftServerSDK();
// AWSGameLiftServerRequestBus interface implementation
bool NotifyGameLiftProcessReady() override;
// ISessionHandlingProviderRequests interface implementation
void HandleDestroySession() override;
@@ -91,6 +93,9 @@ namespace AWSGameLift
bool AddConnectedPlayer(const AzFramework::PlayerConnectionConfig& playerConnectionConfig);
private:
//! Build the serverProcessDesc with appropriate server port number and log paths.
GameLiftServerProcessDesc BuildGameLiftServerProcessDesc();
//! Build session config by using AWS GameLift Server GameSession Model.
AzFramework::SessionConfig BuildSessionConfig(const Aws::GameLift::Server::Model::GameSession& gameSession);
@@ -74,49 +74,16 @@ namespace AWSGameLift
void AWSGameLiftServerSystemComponent::Activate()
{
if (m_gameLiftServerManager->InitializeGameLiftServerSDK())
{
GameLiftServerProcessDesc serverProcessDesc;
UpdateGameLiftServerProcessDesc(serverProcessDesc);
m_gameLiftServerManager->NotifyGameLiftProcessReady(serverProcessDesc);
}
m_gameLiftServerManager->InitializeGameLiftServerSDK();
m_gameLiftServerManager->ActivateManager();
}
void AWSGameLiftServerSystemComponent::Deactivate()
{
m_gameLiftServerManager->DeactivateManager();
m_gameLiftServerManager->HandleDestroySession();
}
void AWSGameLiftServerSystemComponent::UpdateGameLiftServerProcessDesc(GameLiftServerProcessDesc& serverProcessDesc)
{
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetDirectInstance();
if (fileIO)
{
const char pathToLogFolder[] = "@log@/";
char resolvedPath[AZ_MAX_PATH_LEN];
if (fileIO->ResolvePath(pathToLogFolder, resolvedPath, AZ_ARRAY_SIZE(resolvedPath)))
{
serverProcessDesc.m_logPaths.push_back(resolvedPath);
}
else
{
AZ_Error("AWSGameLift", false, "Failed to resolve the path to the log folder.");
}
}
else
{
AZ_Error("AWSGameLift", false, "Failed to get File IO.");
}
if (auto console = AZ::Interface<AZ::IConsole>::Get(); console != nullptr)
{
[[maybe_unused]] AZ::GetValueResult getCvarResult = console->GetCvarValue("sv_port", serverProcessDesc.m_port);
AZ_Error(
"AWSGameLift", getCvarResult == AZ::GetValueResult::Success, "Lookup of 'sv_port' console variable failed with error %s",
AZ::GetEnumString(getCvarResult));
}
}
void AWSGameLiftServerSystemComponent::SetGameLiftServerManager(AZStd::unique_ptr<AWSGameLiftServerManager> gameLiftServerManager)
{
m_gameLiftServerManager.reset();
@@ -43,10 +43,6 @@ namespace AWSGameLift
void SetGameLiftServerManager(AZStd::unique_ptr<AWSGameLiftServerManager> gameLiftServerManager);
private:
//! Update the serverProcessDesc with appropriate server port number and log paths.
//! @param serverProcessDesc Desc object to update.
void UpdateGameLiftServerProcessDesc(GameLiftServerProcessDesc& serverProcessDesc);
AZStd::unique_ptr<AWSGameLiftServerManager> m_gameLiftServerManager;
};
@@ -10,6 +10,7 @@
#include <AWSGameLiftServerMocks.h>
#include <AzCore/Interface/Interface.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzFramework/Session/SessionConfig.h>
#include <AzFramework/Session/SessionNotifications.h>
@@ -44,26 +45,39 @@ namespace UnitTest
GameLiftServerProcessDesc serverDesc;
m_serverManager = AZStd::make_unique<NiceMock<AWSGameLiftServerManagerMock>>();
// Set up the file IO and alias
m_localFileIO = aznew AZ::IO::LocalFileIO();
m_priorFileIO = AZ::IO::FileIOBase::GetInstance();
AZ::IO::FileIOBase::SetInstance(nullptr);
AZ::IO::FileIOBase::SetInstance(m_localFileIO);
m_localFileIO->SetAlias("@log@", AZ_TRAIT_TEST_ROOT_FOLDER);
}
void TearDown() override
{
AZ::IO::FileIOBase::SetInstance(nullptr);
delete m_localFileIO;
AZ::IO::FileIOBase::SetInstance(m_priorFileIO);
m_serverManager.reset();
AWSGameLiftServerFixture::TearDown();
}
AZStd::unique_ptr<NiceMock<AWSGameLiftServerManagerMock>> m_serverManager;
AZ::IO::FileIOBase* m_priorFileIO;
AZ::IO::FileIOBase* m_localFileIO;
};
TEST_F(GameLiftServerManagerTest, InitializeGameLiftServerSDK_InitializeTwice_InitSDKCalledOnce)
{
EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), InitSDK()).Times(1);
EXPECT_TRUE(m_serverManager->InitializeGameLiftServerSDK());
m_serverManager->InitializeGameLiftServerSDK();
AZ_TEST_START_TRACE_SUPPRESSION;
EXPECT_FALSE(m_serverManager->InitializeGameLiftServerSDK());
m_serverManager->InitializeGameLiftServerSDK();
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
@@ -72,36 +86,34 @@ namespace UnitTest
EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), ProcessReady(testing::_)).Times(0);
AZ_TEST_START_TRACE_SUPPRESSION;
EXPECT_FALSE(m_serverManager->NotifyGameLiftProcessReady(GameLiftServerProcessDesc()));
EXPECT_FALSE(m_serverManager->NotifyGameLiftProcessReady());
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
TEST_F(GameLiftServerManagerTest, NotifyGameLiftProcessReady_SDKInitialized_ProcessReadyNotificationSent)
{
EXPECT_TRUE(m_serverManager->InitializeGameLiftServerSDK());
m_serverManager->InitializeGameLiftServerSDK();
EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), ProcessReady(testing::_)).Times(1);
EXPECT_TRUE(m_serverManager->NotifyGameLiftProcessReady(GameLiftServerProcessDesc()));
EXPECT_TRUE(m_serverManager->NotifyGameLiftProcessReady());
}
TEST_F(GameLiftServerManagerTest, NotifyGameLiftProcessReady_ProcessReadyFails_TerminationNotificationSent)
{
EXPECT_TRUE(m_serverManager->InitializeGameLiftServerSDK());
m_serverManager->InitializeGameLiftServerSDK();
EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), ProcessReady(testing::_))
.Times(1)
.WillOnce(testing::Return(Aws::GameLift::GenericOutcome()));
EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), ProcessEnding()).Times(1);
AZ_TEST_START_TRACE_SUPPRESSION;
EXPECT_TRUE(m_serverManager->NotifyGameLiftProcessReady(GameLiftServerProcessDesc()));
EXPECT_TRUE(m_serverManager->NotifyGameLiftProcessReady());
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
TEST_F(GameLiftServerManagerTest, OnProcessTerminate_OnDestroySessionBeginReturnsFalse_FailToNotifyGameLift)
{
m_serverManager->InitializeGameLiftServerSDK();
m_serverManager->NotifyGameLiftProcessReady(GameLiftServerProcessDesc());
m_serverManager->NotifyGameLiftProcessReady();
if (!AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Get())
{
AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Register(m_serverManager.get());
@@ -122,7 +134,7 @@ namespace UnitTest
TEST_F(GameLiftServerManagerTest, OnProcessTerminate_OnDestroySessionBeginReturnsTrue_TerminationNotificationSent)
{
m_serverManager->InitializeGameLiftServerSDK();
m_serverManager->NotifyGameLiftProcessReady(GameLiftServerProcessDesc());
m_serverManager->NotifyGameLiftProcessReady();
if (!AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Get())
{
AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Register(m_serverManager.get());
@@ -141,7 +153,7 @@ namespace UnitTest
TEST_F(GameLiftServerManagerTest, OnHealthCheck_OnSessionHealthCheckReturnsTrue_CallbackFunctionReturnsTrue)
{
m_serverManager->InitializeGameLiftServerSDK();
m_serverManager->NotifyGameLiftProcessReady(GameLiftServerProcessDesc());
m_serverManager->NotifyGameLiftProcessReady();
SessionNotificationsHandlerMock handlerMock;
EXPECT_CALL(handlerMock, OnSessionHealthCheck()).Times(1).WillOnce(testing::Return(true));
EXPECT_TRUE(m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_healthCheckFunc());
@@ -150,7 +162,7 @@ namespace UnitTest
TEST_F(GameLiftServerManagerTest, OnHealthCheck_OnSessionHealthCheckReturnsFalseAndTrue_CallbackFunctionReturnsFalse)
{
m_serverManager->InitializeGameLiftServerSDK();
m_serverManager->NotifyGameLiftProcessReady(GameLiftServerProcessDesc());
m_serverManager->NotifyGameLiftProcessReady();
SessionNotificationsHandlerMock handlerMock1;
EXPECT_CALL(handlerMock1, OnSessionHealthCheck()).Times(1).WillOnce(testing::Return(false));
SessionNotificationsHandlerMock handlerMock2;
@@ -161,7 +173,7 @@ namespace UnitTest
TEST_F(GameLiftServerManagerTest, OnHealthCheck_OnSessionHealthCheckReturnsFalse_CallbackFunctionReturnsFalse)
{
m_serverManager->InitializeGameLiftServerSDK();
m_serverManager->NotifyGameLiftProcessReady(GameLiftServerProcessDesc());
m_serverManager->NotifyGameLiftProcessReady();
SessionNotificationsHandlerMock handlerMock;
EXPECT_CALL(handlerMock, OnSessionHealthCheck()).Times(1).WillOnce(testing::Return(false));
EXPECT_FALSE(m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_healthCheckFunc());
@@ -170,7 +182,7 @@ namespace UnitTest
TEST_F(GameLiftServerManagerTest, OnStartGameSession_OnCreateSessionBeginReturnsFalse_TerminationNotificationSent)
{
m_serverManager->InitializeGameLiftServerSDK();
m_serverManager->NotifyGameLiftProcessReady(GameLiftServerProcessDesc());
m_serverManager->NotifyGameLiftProcessReady();
SessionNotificationsHandlerMock handlerMock;
EXPECT_CALL(handlerMock, OnCreateSessionBegin(testing::_)).Times(1).WillOnce(testing::Return(false));
EXPECT_CALL(handlerMock, OnDestroySessionBegin()).Times(1).WillOnce(testing::Return(true));
@@ -183,7 +195,7 @@ namespace UnitTest
TEST_F(GameLiftServerManagerTest, OnStartGameSession_ActivateGameSessionSucceeds_RegisterAsHandler)
{
m_serverManager->InitializeGameLiftServerSDK();
m_serverManager->NotifyGameLiftProcessReady(GameLiftServerProcessDesc());
m_serverManager->NotifyGameLiftProcessReady();
SessionNotificationsHandlerMock handlerMock;
EXPECT_CALL(handlerMock, OnCreateSessionBegin(testing::_)).Times(1).WillOnce(testing::Return(true));
EXPECT_CALL(handlerMock, OnDestroySessionBegin()).Times(1).WillOnce(testing::Return(true));
@@ -203,7 +215,7 @@ namespace UnitTest
TEST_F(GameLiftServerManagerTest, OnStartGameSession_ActivateGameSessionFails_TerminationNotificationSent)
{
m_serverManager->InitializeGameLiftServerSDK();
m_serverManager->NotifyGameLiftProcessReady(GameLiftServerProcessDesc());
m_serverManager->NotifyGameLiftProcessReady();
SessionNotificationsHandlerMock handlerMock;
EXPECT_CALL(handlerMock, OnCreateSessionBegin(testing::_)).Times(1).WillOnce(testing::Return(true));
EXPECT_CALL(handlerMock, OnDestroySessionBegin()).Times(1).WillOnce(testing::Return(true));
@@ -13,7 +13,6 @@
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <AzTest/AzTest.h>
namespace UnitTest
@@ -37,22 +36,10 @@ namespace UnitTest
m_AWSGameLiftServerSystemsComponent = aznew NiceMock<AWSGameLiftServerSystemComponentMock>();
m_entity->AddComponent(m_AWSGameLiftServerSystemsComponent);
// Set up the file IO and alias
m_localFileIO = aznew AZ::IO::LocalFileIO();
m_priorFileIO = AZ::IO::FileIOBase::GetInstance();
AZ::IO::FileIOBase::SetInstance(nullptr);
AZ::IO::FileIOBase::SetInstance(m_localFileIO);
m_localFileIO->SetAlias("@log@", AZ_TRAIT_TEST_ROOT_FOLDER);
}
void TearDown() override
{
AZ::IO::FileIOBase::SetInstance(nullptr);
delete m_localFileIO;
AZ::IO::FileIOBase::SetInstance(m_priorFileIO);
m_entity->RemoveComponent(m_AWSGameLiftServerSystemsComponent);
delete m_AWSGameLiftServerSystemsComponent;
delete m_entity;
@@ -70,9 +57,6 @@ namespace UnitTest
AZ::Entity* m_entity;
NiceMock<AWSGameLiftServerSystemComponentMock>* m_AWSGameLiftServerSystemsComponent;
AZ::IO::FileIOBase* m_priorFileIO;
AZ::IO::FileIOBase* m_localFileIO;
};
TEST_F(AWSGameLiftServerSystemComponentTest, ActivateDeactivateComponent_ExecuteInOrder_Success)
@@ -8,6 +8,7 @@
set(FILES
../AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h
Include/Request/IAWSGameLiftServerRequests.h
Source/AWSGameLiftServerManager.cpp
Source/AWSGameLiftServerManager.h
Source/AWSGameLiftServerSystemComponent.cpp