Merge 'gameliftfeature' branch into 'development' branch (#1534)
This commit is contained in:
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AWSGameLiftServerManager.h>
|
||||
#include <AWSGameLiftSessionConstants.h>
|
||||
#include <GameLiftServerSDKWrapper.h>
|
||||
|
||||
#include <AzCore/Debug/Trace.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Jobs/JobFunction.h>
|
||||
#include <AzCore/Jobs/JobManagerBus.h>
|
||||
#include <AzCore/std/bind/bind.h>
|
||||
#include <AzFramework/Session/SessionNotifications.h>
|
||||
|
||||
namespace AWSGameLift
|
||||
{
|
||||
AWSGameLiftServerManager::AWSGameLiftServerManager()
|
||||
: m_serverSDKInitialized(false)
|
||||
, m_gameLiftServerSDKWrapper(AZStd::make_unique<GameLiftServerSDKWrapper>())
|
||||
, m_connectedPlayers()
|
||||
{
|
||||
}
|
||||
|
||||
AWSGameLiftServerManager::~AWSGameLiftServerManager()
|
||||
{
|
||||
m_gameLiftServerSDKWrapper.reset();
|
||||
m_connectedPlayers.clear();
|
||||
}
|
||||
|
||||
bool AWSGameLiftServerManager::AddConnectedPlayer(const AzFramework::PlayerConnectionConfig& playerConnectionConfig)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_gameliftMutex);
|
||||
if (m_connectedPlayers.contains(playerConnectionConfig.m_playerConnectionId))
|
||||
{
|
||||
if (m_connectedPlayers[playerConnectionConfig.m_playerConnectionId] != playerConnectionConfig.m_playerSessionId)
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftServerPlayerConnectionRegisteredErrorMessage,
|
||||
playerConnectionConfig.m_playerConnectionId, playerConnectionConfig.m_playerSessionId.c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_connectedPlayers.emplace(playerConnectionConfig.m_playerConnectionId, playerConnectionConfig.m_playerSessionId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
AzFramework::SessionConfig AWSGameLiftServerManager::BuildSessionConfig(const Aws::GameLift::Server::Model::GameSession& gameSession)
|
||||
{
|
||||
AzFramework::SessionConfig sessionConfig;
|
||||
|
||||
sessionConfig.m_dnsName = gameSession.GetDnsName().c_str();
|
||||
AZStd::string propertiesOutput = "";
|
||||
for (const auto& gameProperty : gameSession.GetGameProperties())
|
||||
{
|
||||
sessionConfig.m_sessionProperties.emplace(gameProperty.GetKey().c_str(), gameProperty.GetValue().c_str());
|
||||
propertiesOutput += AZStd::string::format("{Key=%s,Value=%s},", gameProperty.GetKey().c_str(), gameProperty.GetValue().c_str());
|
||||
}
|
||||
if (!propertiesOutput.empty())
|
||||
{
|
||||
propertiesOutput = propertiesOutput.substr(0, propertiesOutput.size() - 1); // Trim last comma to fit array format
|
||||
}
|
||||
sessionConfig.m_sessionId = gameSession.GetGameSessionId().c_str();
|
||||
sessionConfig.m_ipAddress = gameSession.GetIpAddress().c_str();
|
||||
sessionConfig.m_maxPlayer = gameSession.GetMaximumPlayerSessionCount();
|
||||
sessionConfig.m_sessionName = gameSession.GetName().c_str();
|
||||
sessionConfig.m_port = gameSession.GetPort();
|
||||
sessionConfig.m_status = AWSGameLiftSessionStatusNames[(int)gameSession.GetStatus()];
|
||||
|
||||
AZ_TracePrintf(AWSGameLiftServerManagerName,
|
||||
"Built SessionConfig with Name=%s, Id=%s, Status=%s, DnsName=%s, IpAddress=%s, Port=%d, MaxPlayer=%d and Properties=%s",
|
||||
sessionConfig.m_sessionName.c_str(),
|
||||
sessionConfig.m_sessionId.c_str(),
|
||||
sessionConfig.m_status.c_str(),
|
||||
sessionConfig.m_dnsName.c_str(),
|
||||
sessionConfig.m_ipAddress.c_str(),
|
||||
sessionConfig.m_port,
|
||||
sessionConfig.m_maxPlayer,
|
||||
AZStd::string::format("[%s]", propertiesOutput.c_str()).c_str());
|
||||
|
||||
return sessionConfig;
|
||||
}
|
||||
|
||||
AZ::IO::Path AWSGameLiftServerManager::GetExternalSessionCertificate()
|
||||
{
|
||||
// TODO: Add support to get TLS cert file path
|
||||
return AZ::IO::Path();
|
||||
}
|
||||
|
||||
AZ::IO::Path AWSGameLiftServerManager::GetInternalSessionCertificate()
|
||||
{
|
||||
// GameLift doesn't support it, return empty path
|
||||
return AZ::IO::Path();
|
||||
}
|
||||
|
||||
bool AWSGameLiftServerManager::InitializeGameLiftServerSDK()
|
||||
{
|
||||
if (m_serverSDKInitialized)
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftServerSDKAlreadyInitErrorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ_TracePrintf(AWSGameLiftServerManagerName, "Initiating Amazon GameLift Server SDK...");
|
||||
Aws::GameLift::Server::InitSDKOutcome initOutcome = m_gameLiftServerSDKWrapper->InitSDK();
|
||||
m_serverSDKInitialized = initOutcome.IsSuccess();
|
||||
|
||||
AZ_Error(AWSGameLiftServerManagerName, m_serverSDKInitialized,
|
||||
AWSGameLiftServerInitSDKErrorMessage, initOutcome.GetError().GetErrorMessage().c_str());
|
||||
|
||||
return m_serverSDKInitialized;
|
||||
}
|
||||
|
||||
void AWSGameLiftServerManager::HandleDestroySession()
|
||||
{
|
||||
// No further request should be handled by GameLift server manager at this point
|
||||
if (AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Get())
|
||||
{
|
||||
AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Unregister(this);
|
||||
}
|
||||
|
||||
AZ_TracePrintf(AWSGameLiftServerManagerName, "Server process is scheduled to be shut down at %s",
|
||||
m_gameLiftServerSDKWrapper->GetTerminationTime().c_str());
|
||||
|
||||
// Send notifications to handler(s) to gracefully shut down the server process.
|
||||
bool destroySessionResult = true;
|
||||
AZ::EBusReduceResult<bool&, AZStd::logical_and<bool>> result(destroySessionResult);
|
||||
AzFramework::SessionNotificationBus::BroadcastResult(result, &AzFramework::SessionNotifications::OnDestroySessionBegin);
|
||||
|
||||
if (!destroySessionResult)
|
||||
{
|
||||
AZ_Error("AWSGameLift", destroySessionResult, AWSGameLiftServerGameSessionDestroyErrorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
AZ_TracePrintf(AWSGameLiftServerManagerName, "Notifying GameLift server process is ending...");
|
||||
Aws::GameLift::GenericOutcome processEndingOutcome = m_gameLiftServerSDKWrapper->ProcessEnding();
|
||||
bool processEndingIsSuccess = processEndingOutcome.IsSuccess();
|
||||
|
||||
AZ_Error(AWSGameLiftServerManagerName, processEndingIsSuccess, AWSGameLiftServerProcessEndingErrorMessage,
|
||||
processEndingOutcome.GetError().GetErrorMessage().c_str());
|
||||
}
|
||||
|
||||
void AWSGameLiftServerManager::HandlePlayerLeaveSession(const AzFramework::PlayerConnectionConfig& playerConnectionConfig)
|
||||
{
|
||||
AZStd::string playerSessionId = "";
|
||||
RemoveConnectedPlayer(playerConnectionConfig.m_playerConnectionId, playerSessionId);
|
||||
if (playerSessionId.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Aws::GameLift::GenericOutcome disconnectOutcome = m_gameLiftServerSDKWrapper->RemovePlayerSession(playerSessionId);
|
||||
AZ_Error(AWSGameLiftServerManagerName, disconnectOutcome.IsSuccess(), AWSGameLiftServerRemovePlayerSessionErrorMessage,
|
||||
playerSessionId.c_str(), disconnectOutcome.GetError().GetErrorMessage().c_str());
|
||||
}
|
||||
|
||||
bool AWSGameLiftServerManager::NotifyGameLiftProcessReady(const GameLiftServerProcessDesc& desc)
|
||||
{
|
||||
if (!m_serverSDKInitialized)
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftServerSDKNotInitErrorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ_Warning(AWSGameLiftServerManagerName, desc.m_port != 0, AWSGameLiftServerTempPortErrorMessage);
|
||||
|
||||
AZ::JobContext* jobContext = nullptr;
|
||||
AZ::JobManagerBus::BroadcastResult(jobContext, &AZ::JobManagerEvents::GetGlobalContext);
|
||||
AZ::Job* processReadyJob = AZ::CreateJobFunction(
|
||||
[this, desc]() {
|
||||
// The GameLift ProcessParameters object expects an vector (std::vector) of standard strings (std::string) as the log paths.
|
||||
std::vector<std::string> logPaths;
|
||||
for (const AZStd::string& path : desc.m_logPaths)
|
||||
{
|
||||
logPaths.push_back(path.c_str());
|
||||
}
|
||||
|
||||
Aws::GameLift::Server::ProcessParameters processReadyParameter = Aws::GameLift::Server::ProcessParameters(
|
||||
AZStd::bind(&AWSGameLiftServerManager::OnStartGameSession, this, AZStd::placeholders::_1),
|
||||
AZStd::bind(&AWSGameLiftServerManager::OnUpdateGameSession, this),
|
||||
AZStd::bind(&AWSGameLiftServerManager::OnProcessTerminate, this),
|
||||
AZStd::bind(&AWSGameLiftServerManager::OnHealthCheck, this), desc.m_port,
|
||||
Aws::GameLift::Server::LogParameters(logPaths));
|
||||
|
||||
AZ_TracePrintf(AWSGameLiftServerManagerName, "Notifying GameLift server process is ready...");
|
||||
auto processReadyOutcome = m_gameLiftServerSDKWrapper->ProcessReady(processReadyParameter);
|
||||
|
||||
if (!processReadyOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false,
|
||||
AWSGameLiftServerProcessReadyErrorMessage, processReadyOutcome.GetError().GetErrorMessage().c_str());
|
||||
this->HandleDestroySession();
|
||||
}
|
||||
}, true, jobContext);
|
||||
processReadyJob->Start();
|
||||
return true;
|
||||
}
|
||||
|
||||
void AWSGameLiftServerManager::OnStartGameSession(const Aws::GameLift::Server::Model::GameSession& gameSession)
|
||||
{
|
||||
AzFramework::SessionConfig sessionConfig = BuildSessionConfig(gameSession);
|
||||
|
||||
bool createSessionResult = true;
|
||||
AZ::EBusReduceResult<bool&, AZStd::logical_and<bool>> result(createSessionResult);
|
||||
AzFramework::SessionNotificationBus::BroadcastResult(
|
||||
result, &AzFramework::SessionNotifications::OnCreateSessionBegin, sessionConfig);
|
||||
|
||||
if (createSessionResult)
|
||||
{
|
||||
AZ_TracePrintf(AWSGameLiftServerManagerName, "Activating GameLift game session...");
|
||||
Aws::GameLift::GenericOutcome activationOutcome = m_gameLiftServerSDKWrapper->ActivateGameSession();
|
||||
|
||||
if (activationOutcome.IsSuccess())
|
||||
{
|
||||
// Register server manager as handler once game session has been activated
|
||||
if (!AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Get())
|
||||
{
|
||||
AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Register(this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftServerActivateGameSessionErrorMessage,
|
||||
activationOutcome.GetError().GetErrorMessage().c_str());
|
||||
HandleDestroySession();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftServerGameInitErrorMessage);
|
||||
HandleDestroySession();
|
||||
}
|
||||
}
|
||||
|
||||
void AWSGameLiftServerManager::OnProcessTerminate()
|
||||
{
|
||||
AZ_TracePrintf(AWSGameLiftServerManagerName, "GameLift is shutting down server process...");
|
||||
|
||||
HandleDestroySession();
|
||||
}
|
||||
|
||||
bool AWSGameLiftServerManager::OnHealthCheck()
|
||||
{
|
||||
bool healthCheckResult = true;
|
||||
AZ::EBusReduceResult<bool&, AZStd::logical_and<bool>> result(healthCheckResult);
|
||||
AzFramework::SessionNotificationBus::BroadcastResult(result, &AzFramework::SessionNotifications::OnSessionHealthCheck);
|
||||
|
||||
return m_serverSDKInitialized && healthCheckResult;
|
||||
}
|
||||
|
||||
void AWSGameLiftServerManager::OnUpdateGameSession()
|
||||
{
|
||||
// TODO: Perform game-specific tasks to prep for newly matched players
|
||||
return;
|
||||
}
|
||||
|
||||
bool AWSGameLiftServerManager::RemoveConnectedPlayer(uint32_t playerConnectionId, AZStd::string& outPlayerSessionId)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_gameliftMutex);
|
||||
if (m_connectedPlayers.contains(playerConnectionId))
|
||||
{
|
||||
outPlayerSessionId = m_connectedPlayers[playerConnectionId];
|
||||
m_connectedPlayers.erase(playerConnectionId);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
outPlayerSessionId = "";
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftServerPlayerConnectionMissingErrorMessage, playerConnectionId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void AWSGameLiftServerManager::SetGameLiftServerSDKWrapper(AZStd::unique_ptr<GameLiftServerSDKWrapper> gameLiftServerSDKWrapper)
|
||||
{
|
||||
m_gameLiftServerSDKWrapper.reset();
|
||||
m_gameLiftServerSDKWrapper = AZStd::move(gameLiftServerSDKWrapper);
|
||||
}
|
||||
|
||||
bool AWSGameLiftServerManager::ValidatePlayerJoinSession(const AzFramework::PlayerConnectionConfig& playerConnectionConfig)
|
||||
{
|
||||
uint32_t playerConnectionId = playerConnectionConfig.m_playerConnectionId;
|
||||
AZStd::string playerSessionId = playerConnectionConfig.m_playerSessionId;
|
||||
if (playerSessionId.empty())
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftServerInvalidConnectionConfigErrorMessage,
|
||||
playerConnectionId, playerSessionId.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!AddConnectedPlayer(playerConnectionConfig))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ_TracePrintf(AWSGameLiftServerManagerName, "Attempting to accept player session connection with Amazon GameLift service...");
|
||||
auto acceptPlayerSessionOutcome = m_gameLiftServerSDKWrapper->AcceptPlayerSession(playerSessionId.c_str());
|
||||
|
||||
if (!acceptPlayerSessionOutcome.IsSuccess())
|
||||
{
|
||||
AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftServerAcceptPlayerSessionErrorMessage,
|
||||
playerSessionId.c_str(), acceptPlayerSessionOutcome.GetError().GetErrorMessage().c_str());
|
||||
RemoveConnectedPlayer(playerConnectionId, playerSessionId);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace AWSGameLift
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <aws/gamelift/server/GameLiftServerAPI.h>
|
||||
#include <aws/gamelift/server/model/GameSession.h>
|
||||
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzFramework/Session/ISessionHandlingRequests.h>
|
||||
#include <AzFramework/Session/SessionConfig.h>
|
||||
|
||||
namespace AWSGameLift
|
||||
{
|
||||
class GameLiftServerSDKWrapper;
|
||||
|
||||
//! GameLift server process settings.
|
||||
struct GameLiftServerProcessDesc
|
||||
{
|
||||
AZStd::vector<AZStd::string> m_logPaths; //!< Log paths the servers will write to. Both relative to the game root folder and absolute paths supported.
|
||||
|
||||
uint16_t m_port = 0; //!< The port the server will be listening on.
|
||||
};
|
||||
|
||||
//! Manage the server process for hosting game sessions via GameLiftServerSDK.
|
||||
class AWSGameLiftServerManager
|
||||
: public AzFramework::ISessionHandlingProviderRequests
|
||||
{
|
||||
public:
|
||||
static constexpr const char AWSGameLiftServerManagerName[] = "AWSGameLiftServerManager";
|
||||
static constexpr const char AWSGameLiftServerSDKNotInitErrorMessage[] =
|
||||
"Amazon GameLift Server SDK is not initialized yet.";
|
||||
static constexpr const char AWSGameLiftServerSDKAlreadyInitErrorMessage[] =
|
||||
"Amazon GameLift Server SDK has already been initialized.";
|
||||
static constexpr const char AWSGameLiftServerTempPortErrorMessage[] =
|
||||
"No server port specified, server will be listening on ephemeral port.";
|
||||
static constexpr const char AWSGameLiftServerGameInitErrorMessage[] =
|
||||
"Failed to process game dependent initialization during OnStartGameSession.";
|
||||
static constexpr const char AWSGameLiftServerGameSessionDestroyErrorMessage[] =
|
||||
"Failed to destroy game session during OnProcessTerminate.";
|
||||
static constexpr const char AWSGameLiftServerPlayerConnectionRegisteredErrorMessage[] =
|
||||
"Player connection id %d is already registered to player session id %s. Remove connected player first.";
|
||||
static constexpr const char AWSGameLiftServerPlayerConnectionMissingErrorMessage[] =
|
||||
"Player connection id %d does not exist.";
|
||||
|
||||
static constexpr const char AWSGameLiftServerInitSDKErrorMessage[] =
|
||||
"Failed to initialize Amazon GameLift Server SDK. ErrorMessage: %s";
|
||||
static constexpr const char AWSGameLiftServerProcessReadyErrorMessage[] =
|
||||
"Failed to notify GameLift server process ready. ErrorMessage: %s";
|
||||
static constexpr const char AWSGameLiftServerActivateGameSessionErrorMessage[] =
|
||||
"Failed to activate GameLift game session. ErrorMessage: %s";
|
||||
static constexpr const char AWSGameLiftServerProcessEndingErrorMessage[] =
|
||||
"Failed to notify GameLift server process ending. ErrorMessage: %s";
|
||||
static constexpr const char AWSGameLiftServerAcceptPlayerSessionErrorMessage[] =
|
||||
"Failed to validate player session connection with id %s. ErrorMessage: %s";
|
||||
static constexpr const char AWSGameLiftServerInvalidConnectionConfigErrorMessage[] =
|
||||
"Invalid player connection config, player connection id: %d, player session id: %s";
|
||||
static constexpr const char AWSGameLiftServerRemovePlayerSessionErrorMessage[] =
|
||||
"Failed to notify GameLift that the player with the player session id %s has disconnected from the server process. ErrorMessage: %s";
|
||||
|
||||
AWSGameLiftServerManager();
|
||||
virtual ~AWSGameLiftServerManager();
|
||||
|
||||
//! Initialize GameLift API client by calling InitSDK().
|
||||
//! @return Whether the initialization is successful.
|
||||
bool InitializeGameLiftServerSDK();
|
||||
|
||||
//! 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);
|
||||
|
||||
// ISessionHandlingProviderRequests interface implementation
|
||||
void HandleDestroySession() override;
|
||||
bool ValidatePlayerJoinSession(const AzFramework::PlayerConnectionConfig& playerConnectionConfig) override;
|
||||
void HandlePlayerLeaveSession(const AzFramework::PlayerConnectionConfig& playerConnectionConfig) override;
|
||||
AZ::IO::Path GetExternalSessionCertificate() override;
|
||||
AZ::IO::Path GetInternalSessionCertificate() override;
|
||||
|
||||
protected:
|
||||
void SetGameLiftServerSDKWrapper(AZStd::unique_ptr<GameLiftServerSDKWrapper> gameLiftServerSDKWrapper);
|
||||
|
||||
//! Add connected player session id.
|
||||
bool AddConnectedPlayer(const AzFramework::PlayerConnectionConfig& playerConnectionConfig);
|
||||
|
||||
private:
|
||||
//! Build session config by using AWS GameLift Server GameSession Model.
|
||||
AzFramework::SessionConfig BuildSessionConfig(const Aws::GameLift::Server::Model::GameSession& gameSession);
|
||||
|
||||
//! Callback function that the GameLift service invokes to activate a new game session.
|
||||
void OnStartGameSession(const Aws::GameLift::Server::Model::GameSession& gameSession);
|
||||
|
||||
//! Callback function that the GameLift service invokes to pass an updated game session object to the server process.
|
||||
void OnUpdateGameSession();
|
||||
|
||||
//! Callback function that the server process or GameLift service invokes to force the server process to shut down.
|
||||
void OnProcessTerminate();
|
||||
|
||||
//! Callback function that the GameLift service invokes to request a health status report from the server process.
|
||||
//! @return Whether the server process is healthy.
|
||||
bool OnHealthCheck();
|
||||
|
||||
//! Remove connected player session id.
|
||||
//! @param playerConnectionId Connection id of the player to remove.
|
||||
//! @param outPlayerSessionId Session id of the removed player. Empty if the player cannot be removed.
|
||||
//! @return Whether the player is removed successfully.
|
||||
bool RemoveConnectedPlayer(uint32_t playerConnectionId, AZStd::string& outPlayerSessionId);
|
||||
|
||||
AZStd::unique_ptr<GameLiftServerSDKWrapper> m_gameLiftServerSDKWrapper;
|
||||
bool m_serverSDKInitialized;
|
||||
|
||||
AZStd::mutex m_gameliftMutex;
|
||||
using PlayerConnectionId = uint32_t;
|
||||
using PlayerSessionId = AZStd::string;
|
||||
AZStd::unordered_map<PlayerConnectionId, PlayerSessionId> m_connectedPlayers;
|
||||
};
|
||||
} // namespace AWSGameLift
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Module/Module.h>
|
||||
|
||||
#include <AWSGameLiftServerSystemComponent.h>
|
||||
|
||||
namespace AWSGameLift
|
||||
{
|
||||
//! Provide the entry point for the gem and register the system component.
|
||||
class AWSGameLiftServerModule
|
||||
: public AZ::Module
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(AWSGameLiftServerModule, "{898416ca-dc11-4731-87de-afe285aedb04}", AZ::Module);
|
||||
AZ_CLASS_ALLOCATOR(AWSGameLiftServerModule, AZ::SystemAllocator, 0);
|
||||
|
||||
AWSGameLiftServerModule()
|
||||
: AZ::Module()
|
||||
{
|
||||
// Push results of [MyComponent]::CreateDescriptor() into m_descriptors here.
|
||||
m_descriptors.insert(m_descriptors.end(), {
|
||||
AWSGameLiftServerSystemComponent::CreateDescriptor(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add required SystemComponents to the SystemEntity.
|
||||
*/
|
||||
AZ::ComponentTypeList GetRequiredSystemComponents() const override
|
||||
{
|
||||
return AZ::ComponentTypeList {
|
||||
azrtti_typeid<AWSGameLiftServerSystemComponent>(),
|
||||
};
|
||||
}
|
||||
};
|
||||
}// namespace AWSGameLift
|
||||
|
||||
AZ_DECLARE_MODULE_CLASS(Gem_AWSGameLift_Server, AWSGameLift::AWSGameLiftServerModule)
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AWSGameLiftServerSystemComponent.h>
|
||||
#include <AWSGameLiftServerManager.h>
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/EditContextConstants.inl>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
|
||||
namespace AWSGameLift
|
||||
{
|
||||
AWSGameLiftServerSystemComponent::AWSGameLiftServerSystemComponent()
|
||||
: m_gameLiftServerManager(AZStd::make_unique<AWSGameLiftServerManager>())
|
||||
{
|
||||
}
|
||||
|
||||
AWSGameLiftServerSystemComponent::~AWSGameLiftServerSystemComponent()
|
||||
{
|
||||
m_gameLiftServerManager.reset();
|
||||
}
|
||||
|
||||
void AWSGameLiftServerSystemComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serialize->Class<AWSGameLiftServerSystemComponent, AZ::Component>()
|
||||
->Version(0)
|
||||
;
|
||||
|
||||
if (AZ::EditContext* ec = serialize->GetEditContext())
|
||||
{
|
||||
ec->Class<AWSGameLiftServerSystemComponent>("AWSGameLiftServer", "Create the GameLift server manager which manages the server process for hosting a game session via GameLiftServerSDK.")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System"))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AWSGameLiftServerSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("AWSGameLiftServerService"));
|
||||
}
|
||||
|
||||
void AWSGameLiftServerSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
incompatible.push_back(AZ_CRC_CE("AWSGameLiftServerService"));
|
||||
}
|
||||
|
||||
void AWSGameLiftServerSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
AZ_UNUSED(required);
|
||||
}
|
||||
|
||||
void AWSGameLiftServerSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
|
||||
{
|
||||
AZ_UNUSED(dependent);
|
||||
}
|
||||
|
||||
void AWSGameLiftServerSystemComponent::Init()
|
||||
{
|
||||
}
|
||||
|
||||
void AWSGameLiftServerSystemComponent::Activate()
|
||||
{
|
||||
if (m_gameLiftServerManager->InitializeGameLiftServerSDK())
|
||||
{
|
||||
GameLiftServerProcessDesc serverProcessDesc;
|
||||
UpdateGameLiftServerProcessDesc(serverProcessDesc);
|
||||
m_gameLiftServerManager->NotifyGameLiftProcessReady(serverProcessDesc);
|
||||
}
|
||||
}
|
||||
|
||||
void AWSGameLiftServerSystemComponent::Deactivate()
|
||||
{
|
||||
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)
|
||||
{
|
||||
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();
|
||||
m_gameLiftServerManager = AZStd::move(gameLiftServerManager);
|
||||
}
|
||||
} // namespace AWSGameLift
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
|
||||
namespace AWSGameLift
|
||||
{
|
||||
struct GameLiftServerProcessDesc;
|
||||
class AWSGameLiftServerManager;
|
||||
|
||||
//! Gem server system component. Responsible for managing the server process for hosting game sessions via the GameLift server manager.
|
||||
class AWSGameLiftServerSystemComponent
|
||||
: public AZ::Component
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(AWSGameLiftServerSystemComponent, "{fa2b46d6-82a9-408d-abab-62bae5ab38c9}");
|
||||
|
||||
AWSGameLiftServerSystemComponent();
|
||||
virtual ~AWSGameLiftServerSystemComponent();
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
|
||||
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
|
||||
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
|
||||
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
|
||||
|
||||
protected:
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// AZ::Component interface implementation
|
||||
void Init() override;
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace AWSGameLift
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <GameLiftServerSDKWrapper.h>
|
||||
|
||||
#include <ctime>
|
||||
|
||||
#pragma warning(disable : 4996)
|
||||
|
||||
namespace AWSGameLift
|
||||
{
|
||||
Aws::GameLift::GenericOutcome GameLiftServerSDKWrapper::AcceptPlayerSession(const std::string& playerSessionId)
|
||||
{
|
||||
return Aws::GameLift::Server::AcceptPlayerSession(playerSessionId);
|
||||
}
|
||||
|
||||
Aws::GameLift::GenericOutcome GameLiftServerSDKWrapper::ActivateGameSession()
|
||||
{
|
||||
return Aws::GameLift::Server::ActivateGameSession();
|
||||
}
|
||||
|
||||
Aws::GameLift::Server::InitSDKOutcome GameLiftServerSDKWrapper::InitSDK()
|
||||
{
|
||||
return Aws::GameLift::Server::InitSDK();
|
||||
}
|
||||
|
||||
Aws::GameLift::GenericOutcome GameLiftServerSDKWrapper::ProcessReady(
|
||||
const Aws::GameLift::Server::ProcessParameters& processParameters)
|
||||
{
|
||||
return Aws::GameLift::Server::ProcessReady(processParameters);
|
||||
}
|
||||
|
||||
Aws::GameLift::GenericOutcome GameLiftServerSDKWrapper::ProcessEnding()
|
||||
{
|
||||
return Aws::GameLift::Server::ProcessEnding();
|
||||
}
|
||||
|
||||
AZStd::string GameLiftServerSDKWrapper::GetTerminationTime()
|
||||
{
|
||||
// Timestamp format is using the UTC ISO8601 format
|
||||
std::time_t terminationTime;
|
||||
Aws::GameLift::AwsLongOutcome GetTerminationTimeOutcome = Aws::GameLift::Server::GetTerminationTime();
|
||||
if (GetTerminationTimeOutcome.IsSuccess())
|
||||
{
|
||||
terminationTime = GetTerminationTimeOutcome.GetResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the current system time if the termination time is not available from GameLift.
|
||||
time(&terminationTime);
|
||||
}
|
||||
|
||||
char buffer[50];
|
||||
strftime(buffer, sizeof(buffer), "%FT%TZ", gmtime(&terminationTime));
|
||||
|
||||
return AZStd::string(buffer);
|
||||
}
|
||||
|
||||
Aws::GameLift::GenericOutcome GameLiftServerSDKWrapper::RemovePlayerSession(const AZStd::string& playerSessionId)
|
||||
{
|
||||
return Aws::GameLift::Server::RemovePlayerSession(playerSessionId.c_str());
|
||||
}
|
||||
} // namespace AWSGameLift
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <aws/gamelift/server/GameLiftServerAPI.h>
|
||||
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
namespace AWSGameLift
|
||||
{
|
||||
/* Wrapper to use to GameLift Server SDK.
|
||||
*/
|
||||
class GameLiftServerSDKWrapper
|
||||
{
|
||||
public:
|
||||
GameLiftServerSDKWrapper() = default;
|
||||
virtual ~GameLiftServerSDKWrapper() = default;
|
||||
|
||||
//! Processes and validates a player session connection.
|
||||
//! This method should be called when a client requests a connection to the server.
|
||||
//! @param playerSessionId the ID of the joining player's session.
|
||||
//! @return Returns a generic outcome consisting of success or failure with an error message.
|
||||
virtual Aws::GameLift::GenericOutcome AcceptPlayerSession(const std::string& playerSessionId);
|
||||
|
||||
//! Reports to GameLift that the server process is now ready to receive player sessions.
|
||||
//! Should be called once all GameSession initialization has finished.
|
||||
//! @return Returns a generic outcome consisting of success or failure with an error message.
|
||||
virtual Aws::GameLift::GenericOutcome ActivateGameSession();
|
||||
|
||||
//! Initializes the GameLift SDK.
|
||||
//! Should be called when the server starts, before any GameLift-dependent initialization happens.
|
||||
//! @return If successful, returns an InitSdkOutcome object indicating that the server process is ready to call ProcessReady().
|
||||
virtual Aws::GameLift::Server::InitSDKOutcome InitSDK();
|
||||
|
||||
//! Notifies the GameLift service that the server process is ready to host game sessions.
|
||||
//! @param processParameters A ProcessParameters object communicating the names of callback methods, port number and game
|
||||
//! session-specific log files about the server process.
|
||||
//! @return Returns a generic outcome consisting of success or failure with an error message.
|
||||
virtual Aws::GameLift::GenericOutcome ProcessReady(const Aws::GameLift::Server::ProcessParameters& processParameters);
|
||||
|
||||
//! Notifies the GameLift service that the server process is shutting down.
|
||||
//! @return Returns a generic outcome consisting of success or failure with an error message.
|
||||
virtual Aws::GameLift::GenericOutcome ProcessEnding();
|
||||
|
||||
//! Returns the time that a server process is scheduled to be shut down.
|
||||
//! @return Timestamp using the UTC ISO8601 format.
|
||||
virtual AZStd::string GetTerminationTime();
|
||||
|
||||
//! Notifies the GameLift service that a player with the specified player session ID has disconnected from the server process.
|
||||
//! @param playerSessionId Unique ID issued by the Amazon GameLift service in response to a call to the AWS SDK Amazon GameLift API action CreatePlayerSession.
|
||||
//! @return Returns a generic outcome consisting of success or failure with an error message.
|
||||
virtual Aws::GameLift::GenericOutcome RemovePlayerSession(const AZStd::string& playerSessionId);
|
||||
};
|
||||
} // namespace AWSGameLift
|
||||
Reference in New Issue
Block a user