Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,105 @@
/*
* 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.
*
*/
#if !defined(BUILD_GAMELIFT_SERVER) && defined(BUILD_GAMELIFT_CLIENT)
#include <GameLift/Session/DescribeGameSessionsQueueRequest.h>
// To avoid the warning below
// Semaphore.h(50): warning C4251: 'Aws::Utils::Threading::Semaphore::m_mutex': class 'std::mutex' needs to have dll-interface to be used by clients of class 'Aws::Utils::Threading::Semaphore'
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
#include <aws/gamelift/model/DescribeGameSessionQueuesRequest.h>
AZ_POP_DISABLE_WARNING
#include <aws/core/utils/Outcome.h>
#include <aws/gamelift/GameLiftErrors.h>
#include <aws/gamelift/model/DescribeGameSessionQueuesResult.h>
namespace GridMate
{
const AZStd::string GameLiftFleetIdPrefix = "fleet/";
const AZStd::string ExtractFleetIdFromFleetArn(AZStd::string fleetArn)
{
return fleetArn.substr(fleetArn.rfind(GameLiftFleetIdPrefix) + GameLiftFleetIdPrefix.size());
}
DescribeGameSessionsQueueRequest::DescribeGameSessionsQueueRequest(GameLiftClientService* clientService
, const std::shared_ptr<GameLiftRequestInterfaceContext>& context)
: GameLiftRequestInterface(context)
{
}
bool DescribeGameSessionsQueueRequest::Initialize()
{
Aws::GameLift::Model::DescribeGameSessionQueuesRequest request;
request.AddNames(m_context->queueName);
m_context->client.lock()->DescribeGameSessionQueuesAsync(request,
[this](const Aws::GameLift::GameLiftClient* client,
const Aws::GameLift::Model::DescribeGameSessionQueuesRequest& request,
const Aws::GameLift::Model::DescribeGameSessionQueuesOutcome& outcome,
const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context)
{
this->DescribeGameSessionQueuesHandler(client, request, outcome, context);
}
);
return true;
}
void DescribeGameSessionsQueueRequest::DescribeGameSessionQueuesHandler(const Aws::GameLift::GameLiftClient* client,
const Aws::GameLift::Model::DescribeGameSessionQueuesRequest& request,
const Aws::GameLift::Model::DescribeGameSessionQueuesOutcome& outcome,
const std::shared_ptr<const Aws::Client::AsyncCallerContext>&)
{
if (!outcome.IsSuccess())
{
m_context->errorHandler(outcome.GetError().GetMessage());
return;
}
Aws::String queueName = m_context->queueName;
if (outcome.GetResult().GetGameSessionQueues().size() == 0)
{
Aws::String errorMessage = "No Queue found for queue name: %s";
Aws::Utils::StringUtils::Replace(errorMessage, "%s", queueName.c_str());
m_context->errorHandler(errorMessage);
return;
}
AZStd::string fleetId;
for (auto const& gameSessionQueue : outcome.GetResult().GetGameSessionQueues())
{
if (gameSessionQueue.GetName() == queueName && gameSessionQueue.GetDestinations().size() > 0)
{
// Default to first fleet in queue. FleetId is used to test connectivity later.
AZStd::string fleetArn = gameSessionQueue.GetDestinations().size() > 0 ? gameSessionQueue.GetDestinations()[0].GetDestinationArn().c_str() : "";
fleetId = ExtractFleetIdFromFleetArn(fleetArn);
break;
}
}
// This case is very unlikely as this means GameLift queue has no destinations.
if (fleetId.empty())
{
Aws::String errorMessage = "No Destination fleet found %s";
Aws::Utils::StringUtils::Replace(errorMessage, "%s", queueName.c_str());
m_context->errorHandler(errorMessage);
return;
}
m_context->successHandler(Aws::String(fleetId.c_str()));
}
} // namespace GridMate
#endif // BUILD_GAMELIFT_CLIENT
@@ -0,0 +1,320 @@
/*
* 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.
*
*/
#if defined(BUILD_GAMELIFT_CLIENT)
#include <AzCore/Component/TickBus.h>
#include <AzFramework/AzFramework_Traits_Platform.h>
#include <GameLift/Session/GameLiftClientService.h>
#include <GameLift/Session/GameLiftClientSession.h>
#include <GameLift/Session/GameLiftSearch.h>
#include <GameLift/Session/GameLiftSessionRequest.h>
#include <GameLift/Session/GameLiftGameSessionPlacementRequest.h>
#include <GameLift/Session/GameLiftMatchmaking.h>
#include <GameLift/Session/GameLiftRequestInterface.h>
#include <AzCore/IO/FileIO.h>
#include <aws/core/auth/AWSCredentialsProvider.h>
#include <aws/core/utils/Outcome.h>
#include <aws/gamelift/GameLiftClient.h>
#include <aws/gamelift/model/ListBuildsRequest.h>
namespace
{
//This is used when a playerId is not specified when initializing GameLiftSDK with developer credentials.
const char* DEFAULT_PLAYER_ID = "AnonymousPlayerId";
}
namespace GridMate
{
namespace Platform
{
void ResolveCaCertFilePath(Aws::String& caFile);
}
GameLiftClientService::GameLiftClientService(const GameLiftClientServiceDesc& desc)
: SessionService(desc)
, m_serviceDesc(desc)
, m_clientStatus(GameLift_NotInited)
{
}
GameLiftClientService::~GameLiftClientService()
{
}
bool GameLiftClientService::IsReady() const
{
return m_clientStatus == GameLift_Ready;
}
AZStd::shared_ptr<Aws::GameLift::GameLiftClient> GameLiftClientService::GetClient() const
{
return m_clientSharedPtr;
}
Aws::String GameLiftClientService::GetPlayerId() const
{
return m_serviceDesc.m_playerId.c_str();
}
void GameLiftClientService::OnServiceRegistered(IGridMate* gridMate)
{
SessionService::OnServiceRegistered(gridMate);
GameLiftClientSession::RegisterReplicaChunks();
if (!StartGameLiftClient())
{
EBUS_EVENT_ID(m_gridMate, GameLiftClientServiceEventsBus, OnGameLiftSessionServiceFailed, this, "GameLift client failed to start");
}
GameLiftClientServiceBus::Handler::BusConnect(gridMate);
}
void GameLiftClientService::OnServiceUnregistered(IGridMate* gridMate)
{
GameLiftClientServiceBus::Handler::BusDisconnect();
if (m_clientStatus == GameLift_Ready)
{
m_clientSharedPtr.reset();
m_clientStatus = GameLift_NotInited;
}
SessionService::OnServiceUnregistered(gridMate);
}
void GameLiftClientService::Update()
{
if (m_listBuildsOutcomeCallable.valid() && m_listBuildsOutcomeCallable.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready)
{
auto outcome = m_listBuildsOutcomeCallable.get();
if (outcome.IsSuccess())
{
AZ_TracePrintf("GameLift", "Initialized GameLift client successfully.\n");
m_clientStatus = GameLift_Ready;
EBUS_EVENT_ID(m_gridMate, GameLiftClientServiceEventsBus, OnGameLiftSessionServiceReady, this);
EBUS_DBG_EVENT(Debug::SessionDrillerBus, OnSessionServiceReady);
EBUS_EVENT_ID(m_gridMate, SessionEventBus, OnSessionServiceReady);
}
else
{
auto errorMessage = outcome.GetError().GetMessage();
AZ_TracePrintf("GameLift", "Failed to initialize GameLift client: %s\n", errorMessage.c_str());
m_clientStatus = GameLift_Failed;
// defer the notification so Gridmate doesn't destroy this service while updating
AZ::TickBus::QueueFunction([this, errorMessage]()
{
GameLiftClientServiceEventsBus::Event(m_gridMate, &GameLiftClientServiceEventsBus::Events::OnGameLiftSessionServiceFailed, this, errorMessage.c_str());
});
}
}
SessionService::Update();
}
GridSession* GameLiftClientService::JoinSessionBySearchInfo(const GameLiftSearchInfo& searchInfo, const CarrierDesc& carrierDesc)
{
if (m_clientStatus != GameLift_Ready)
{
AZ_TracePrintf("GameLift", "Client API is not initialized.\n");
return nullptr;
}
GameLiftClientSession* session = aznew GameLiftClientSession(this);
if (!session->Initialize(searchInfo, JoinParams(), carrierDesc))
{
delete session;
return nullptr;
}
return session;
}
GridSearch* GameLiftClientService::RequestSession(const GameLiftSessionRequestParams& params)
{
if (m_clientStatus != GameLift_Ready)
{
AZ_TracePrintf("GameLift", "Client API is not initialized.\n");
return nullptr;
}
AZStd::shared_ptr<GameLiftRequestInterfaceContext> context = AZStd::make_shared<GameLiftRequestInterfaceContext>();
context->m_gameLiftClient = m_clientSharedPtr;
context->m_playerId = m_serviceDesc.m_playerId.c_str();
// If queue name is set use queues to create a GameLift session. else use fleetId.
if (!params.m_queueName.empty())
{
context->m_requestParams = params;
GameLiftGameSessionPlacementRequest* request = aznew GameLiftGameSessionPlacementRequest(this, context);
if (!request->Initialize())
{
delete request;
return nullptr;
}
return request;
}
else
{
context->m_requestParams = params;
GameLiftSessionRequest* request = aznew GameLiftSessionRequest(this, context);
if (!request->Initialize())
{
delete request;
return nullptr;
}
return request;
}
}
GridSearch* GameLiftClientService::StartMatchmaking(const AZStd::string& matchmakingConfigName)
{
if (m_clientStatus != GameLift_Ready)
{
AZ_TracePrintf("GameLift", "Client API is not initialized.\n");
return nullptr;
}
AZStd::shared_ptr<GameLiftRequestInterfaceContext> context = AZStd::make_shared<GameLiftRequestInterfaceContext>();
context->m_gameLiftClient = m_clientSharedPtr;
context->m_playerId = m_serviceDesc.m_playerId.c_str();
GameLiftMatchmaking* request = aznew GameLiftMatchmaking(this, context, Aws::String(matchmakingConfigName.c_str()));
if (!request->Initialize())
{
delete request;
return nullptr;
}
return request;
}
GameLiftSearch* GameLiftClientService::StartSearch(const GameLiftSearchParams& params)
{
if (m_clientStatus != GameLift_Ready)
{
AZ_TracePrintf("GameLift", "Client API is not initialized.\n");
return nullptr;
}
AZStd::shared_ptr<GameLiftRequestInterfaceContext> context = AZStd::make_shared<GameLiftRequestInterfaceContext>();
context->m_gameLiftClient = m_clientSharedPtr;
context->m_searchParams = params;
GameLiftSearch* search = aznew GameLiftSearch(this, context);
if (!search->Initialize())
{
delete search;
return nullptr;
}
return search;
}
GameLiftClientSession* GameLiftClientService::QueryGameLiftSession(const GridSession* session)
{
for (GridSession* s : m_sessions)
{
if (s == session)
{
return static_cast<GameLiftClientSession*>(s);
}
}
return nullptr;
}
GameLiftSearch* GameLiftClientService::QueryGameLiftSearch(const GridSearch* search)
{
for (GridSearch* s : m_activeSearches)
{
if (s == search)
{
return static_cast<GameLiftSearch*>(s);
}
}
for (GridSearch* s : m_completedSearches)
{
if (s == search)
{
return static_cast<GameLiftSearch*>(s);
}
}
return nullptr;
}
bool GameLiftClientService::StartGameLiftClient()
{
if (m_clientStatus == GameLift_NotInited)
{
if (!ValidateAWSCredentials())
{
m_clientStatus = GameLift_Failed;
}
else
{
m_clientStatus = GameLift_Initing;
CreateSharedAWSGameLiftClient();
Aws::GameLift::Model::ListBuildsRequest request;
m_listBuildsOutcomeCallable = m_clientSharedPtr->ListBuildsCallable(request);
}
}
return m_clientStatus != GameLift_Failed;
}
void GameLiftClientService::CreateSharedAWSGameLiftClient()
{
Aws::Client::ClientConfiguration config;
config.enableTcpKeepAlive = AZ_TRAIT_AZFRAMEWORK_AWS_ENABLE_TCP_KEEP_ALIVE_SUPPORTED;
config.region = m_serviceDesc.m_region.c_str();
config.endpointOverride = m_serviceDesc.m_endpoint.c_str();
if (m_serviceDesc.m_useGameLiftLocalServer)
{
config.verifySSL = false;
config.scheme = Aws::Http::Scheme::HTTP;
}
else
{
config.verifySSL = true;
config.scheme = Aws::Http::Scheme::HTTPS;
Platform::ResolveCaCertFilePath(config.caFile);
}
Aws::String accessKey(m_serviceDesc.m_accessKey.c_str());
Aws::String secretKey(m_serviceDesc.m_secretKey.c_str());
Aws::Auth::AWSCredentials cred = Aws::Auth::AWSCredentials(accessKey, secretKey);
m_clientSharedPtr = AZStd::make_shared<Aws::GameLift::GameLiftClient>(cred, config);
}
bool GameLiftClientService::ValidateAWSCredentials()
{
if (m_serviceDesc.m_accessKey.empty() || m_serviceDesc.m_secretKey.empty())
{
AZ_TracePrintf("GameLift", "Initialize failed. Cannot use GameLift without access and secret key.\n");
return false;
}
return true;
}
} // namespace GridMate
#endif // BUILD_GAMELIFT_CLIENT
@@ -0,0 +1,623 @@
/*
* 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.
*
*/
#if defined(BUILD_GAMELIFT_CLIENT)
#include <AzCore/PlatformIncl.h>
#include <GameLift/Session/GameLiftClientSession.h>
#include <GameLift/Session/GameLiftClientService.h>
#include <GameLift/Session/GameLiftSearch.h>
#include <GridMate/Online/UserServiceTypes.h>
#include <GridMate/Replica/Replica.h>
#include <GridMate/Replica/ReplicaFunctions.h>
#include <GridMate/Carrier/SocketDriver.h>
#include <GridMate/Carrier/Utils.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/Math/Uuid.h>
#include <aws/core/utils/Outcome.h>
// To avoid the warning below
// Semaphore.h(50): warning C4251: 'Aws::Utils::Threading::Semaphore::m_mutex': class 'std::mutex' needs to have dll-interface to be used by clients of class 'Aws::Utils::Threading::Semaphore'
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
#include <aws/gamelift/model/DescribeGameSessionsRequest.h>
#include <aws/gamelift/model/CreatePlayerSessionRequest.h>
#include <aws/gamelift/model/CreatePlayerSessionResult.h>
AZ_POP_DISABLE_WARNING
namespace
{
const int k_minGameSessionRetryInterval = 100; //< 100 msec minimum interval for retrying gamesession status
const int k_maxGameSessionRetries = 8; //< max 8 retries -> ~50 sec
const int k_gameSessionRetryBase = 200; //< base for exponential retries (200, 400, 800, 1600, 3200 msec, etc...)
}
namespace GridMate
{
class GameLiftMember;
//-----------------------------------------------------------------------------
// GameLiftSessionReplica
//-----------------------------------------------------------------------------
class GameLiftSessionReplica
: public Internal::GridSessionReplica
{
public:
class GameLiftSessionReplicaDesc
: public ReplicaChunkDescriptor
{
public:
GameLiftSessionReplicaDesc()
: ReplicaChunkDescriptor(GameLiftSessionReplica::GetChunkName(), sizeof(GameLiftSessionReplica))
{
}
ReplicaChunkBase* CreateFromStream(UnmarshalContext& mc) override
{
GameLiftClientSession* session = static_cast<GameLiftClientSession*>(static_cast<GridSession*>(mc.m_rm->GetUserContext(AZ_CRC("GridSession", 0x099df4e6))));
AZ_Assert(session, "We need to have a valid session!");
return session->OnSessionReplicaArrived();
}
void DiscardCtorStream(UnmarshalContext&) override {}
void DeleteReplicaChunk(ReplicaChunkBase* chunkInstance) override { delete chunkInstance; }
void MarshalCtorData(ReplicaChunkBase*, WriteBuffer&) override {}
};
GM_CLASS_ALLOCATOR(GameLiftSessionReplica);
static const char* GetChunkName()
{
return "GridMate::GameLiftSessionReplica";
}
GameLiftSessionReplica(GameLiftClientSession* session)
: GridSessionReplica(session)
{
}
};
//-----------------------------------------------------------------------------
// Maintain size and alignment same for corresponding classes in GameLiftServerSession
// GameLiftMemberId -> GameLiftServerMemeberId
// GameLiftMemeber -> GameLiftServerMember
//-----------------------------------------------------------------------------
// GameLiftMemberID
//-----------------------------------------------------------------------------
class GameLiftMemberID
: public MemberID
{
public:
//-----------------------------------------------------------------------------
// Marshaler
//-----------------------------------------------------------------------------
class Marshaler
{
public:
void Marshal(WriteBuffer& wb, const GameLiftMemberID& id) const
{
wb.Write(id.m_id);
}
void Unmarshal(GameLiftMemberID& id, ReadBuffer& rb) const
{
rb.Read(id.m_id);
}
};
//-----------------------------------------------------------------------------
GameLiftMemberID()
: m_id(0)
{
}
explicit GameLiftMemberID(AZ::u32 memberId)
: m_id(memberId)
{
AZ_Assert(m_id != 0, "Invalid member id");
}
string ToString() const override { return string::format("%08X", m_id); }
string ToAddress() const override { return ToString(); }
MemberIDCompact Compact() const override { return m_id; }
bool IsValid() const override { return m_id != 0; }
private:
AZ::u32 m_id;
string m_address;
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// GameLiftMemberInfoCtorContext
//-----------------------------------------------------------------------------
struct GameLiftMemberInfoCtorContext
: public CtorContextBase
{
CtorDataSet<GameLiftMemberID, GameLiftMemberID::Marshaler> m_memberId;
CtorDataSet<RemotePeerMode> m_peerMode;
CtorDataSet<bool> m_isHost;
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// GameLiftMemberState
//-----------------------------------------------------------------------------
class GameLiftMemberState
: public Internal::GridMemberStateReplica
{
public:
GM_CLASS_ALLOCATOR(GameLiftMemberState);
static const char* GetChunkName() { return "GameLiftMemberState"; }
explicit GameLiftMemberState(GridMember* member = nullptr)
: GridMemberStateReplica(member)
{
}
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// GameLiftMember
//-----------------------------------------------------------------------------
class GameLiftMember
: public GridMember
{
friend class GameLiftClientSession;
public:
class GameLiftMemberDesc
: public ReplicaChunkDescriptor
{
public:
GameLiftMemberDesc()
: ReplicaChunkDescriptor(GameLiftMember::GetChunkName(), sizeof(GameLiftMember))
{
}
ReplicaChunkBase* CreateFromStream(UnmarshalContext& mc) override
{
GameLiftMemberInfoCtorContext ctorContext;
ctorContext.Unmarshal(*mc.m_iBuf);
GameLiftClientSession* session = static_cast<GameLiftClientSession*>(static_cast<GridSession*>(mc.m_rm->GetUserContext(AZ_CRC("GridSession"))));
AZ_Assert(session, "Invalid session");
GameLiftMemberID memberId = ctorContext.m_memberId.Get();
RemotePeerMode remotePeerMode = ctorContext.m_peerMode.Get();
bool isMemberHost = ctorContext.m_isHost.Get();
GameLiftMember* member = nullptr;
if (memberId != session->GetMyMember()->GetId())
{
// Put the appscale id back into a buffer so it can be passed to CreateRemoteMember
WriteBufferDynamic memberIdBuf(EndianType::IgnoreEndian);
memberIdBuf.Write(memberId.Compact());
ReadBuffer rb(memberIdBuf.GetEndianType(), memberIdBuf.Get(), memberIdBuf.Size());
member = static_cast<GameLiftMember*>(session->CreateRemoteMember(memberId.ToAddress(), rb, remotePeerMode, isMemberHost ? mc.m_peer->GetConnectionId() : InvalidConnectionID));
}
else
{
member = static_cast<GameLiftMember*>(session->GetMyMember());
}
bool isAdded = session->AddMember(member);
AZ_Assert(isAdded, "Failed to add a member, there is something wrong with the member replicas!");
if (!isAdded)
{
member = nullptr;
AZ_TracePrintf("GameLift", "[CLIENT SESSION] Failed to add a member, there is something wrong with the member replicas, peerid %d", mc.m_peer ? mc.m_peer->GetId() : 0);
}
else
{
AZ_TracePrintf("GameLift", "[CLIENT SESSION] Added a member, peerid %d", mc.m_peer ? mc.m_peer->GetId() : 0);
}
return member;
}
void DiscardCtorStream(UnmarshalContext& mc) override
{
GameLiftMemberInfoCtorContext ctorContext;
ctorContext.Unmarshal(*mc.m_iBuf);
}
void DeleteReplicaChunk(ReplicaChunkBase* chunkInstance) override
{
if (!static_cast<GameLiftMember*>(chunkInstance)->IsLocal())
{
delete chunkInstance;
}
}
void MarshalCtorData(ReplicaChunkBase* chunkInstance, WriteBuffer& wb) override
{
GameLiftMember* member = static_cast<GameLiftMember*>(chunkInstance);
GameLiftMemberInfoCtorContext ctorContext;
ctorContext.m_memberId.Set(member->m_memberId);
ctorContext.m_peerMode.Set(member->m_peerMode.Get());
ctorContext.m_isHost.Set(member->IsHost());
ctorContext.Marshal(wb);
}
};
GM_CLASS_ALLOCATOR(GameLiftMember);
static const char* GetChunkName() { return "GridMate::GameLiftMember"; }
const PlayerId* GetPlayerId() const override
{
return nullptr;
}
const MemberID& GetId() const override
{
return m_memberId;
}
/// Remote member ctor.
GameLiftMember(ConnectionID connId, const GameLiftMemberID& memberId, GameLiftClientSession* session)
: GridMember(memberId.Compact())
, m_memberId(memberId)
{
m_session = session;
m_connectionId = connId;
}
/// Local member ctor.
GameLiftMember(const GameLiftMemberID& memberId, GameLiftClientSession* session)
: GridMember(memberId.Compact())
, m_memberId(memberId)
{
m_session = session;
m_clientState = CreateReplicaChunk<GameLiftMemberState>(this);
m_clientState->m_name.Set(memberId.ToString());
m_clientStateReplica = Replica::CreateReplica(memberId.ToString().c_str());
m_clientStateReplica->AttachReplicaChunk(m_clientState);
}
void OnReplicaDeactivate(const ReplicaContext& rc) override
{
GridMember::OnReplicaDeactivate(rc);
AZ_TracePrintf("GameLift", "[CLIENT SESSION] Deactivating a replica, peerid %d", rc.m_peer ? rc.m_peer->GetId() : 0);
}
using GridMember::SetHost;
GameLiftMemberID m_memberId;
string m_playerSessionId;
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// GameLiftClientSession
//-----------------------------------------------------------------------------
GameLiftClientSession::GameLiftClientSession(GameLiftClientService* service)
: GridSession(service)
, m_gameSessionRetryTimeout(0)
, m_numGameSessionRetryAttempts(0)
, m_clientService(service)
{
}
bool GameLiftClientSession::Initialize(const GameLiftSearchInfo& info, const JoinParams& params, const CarrierDesc& carrierDesc)
{
(void)params;
if (!GridSession::Initialize(carrierDesc))
{
return false;
}
m_state = CreateReplicaChunk<GameLiftSessionReplica>(this);
m_searchInfo = info;
m_sm.SetStateHandler(AZ_HSM_STATE_NAME(SS_GAMELIFT_INIT), AZ::HSM::StateHandler(this, &GameLiftClientSession::OnStateGameLiftInit), SS_NO_SESSION);
SetUpStateMachine();
// If player session id is already set then join existing player session.
if (!info.m_playerSessionId.empty())
{
m_playerSession.SetGameSessionId(m_searchInfo.m_sessionId.c_str());
m_playerSession.SetPlayerSessionId(m_searchInfo.m_playerSessionId.c_str());
m_playerSession.SetPort(m_searchInfo.m_port);
m_playerSession.SetIpAddress(m_searchInfo.m_ipAddress.c_str());
SetGameLiftLocalParams();
m_sessionId = m_searchInfo.m_sessionId;
RequestEvent(SE_MATCHMAKING_JOIN);
}
else
{
RequestEvent(SE_JOIN);
}
return true;
}
GridMember* GameLiftClientSession::CreateLocalMember(bool isHost, bool isInvited, RemotePeerMode peerMode)
{
(void)isInvited;
AZ_Assert(!isHost, "GameLiftClientSession can never run as host!");
AZ_Assert(!m_myMember, "We already have added a local member!");
string ip = Utils::GetMachineAddress(m_carrierDesc.m_familyType);
string address = SocketDriverCommon::IPPortToAddressString(ip.c_str(), m_carrier->GetPort());
string playerSessionId = m_playerSession.GetPlayerSessionId().c_str();
AZ_Assert(!playerSessionId.empty(), "GameLift clients must have a valid playerSessionId to connect to the server!");
GameLiftMemberID myId(AZ::Crc32(playerSessionId.c_str()));
GameLiftMember* member = CreateReplicaChunk<GameLiftMember>(myId, this);
member->SetHost(isHost);
member->m_peerMode.Set(peerMode);
return member;
}
GameLiftSessionReplica* GameLiftClientSession::OnSessionReplicaArrived()
{
AZ_TracePrintf("GameLift", "(%s - %s) has joined session: %s\n",
m_myMember->GetId().ToString().c_str(),
m_myMember->GetId().ToAddress().c_str(),
m_sessionId.c_str());
RequestEvent(GridSession::SE_JOINED);
return static_cast<GameLiftSessionReplica*>(m_state.get());
}
bool GameLiftClientSession::OnStateGameLiftInit(AZ::HSM& sm, const AZ::HSM::Event& e)
{
switch (e.id)
{
case SE_RECEIVED_GAMESESSION:
{
const Aws::GameLift::Model::DescribeGameSessionsResult* result = reinterpret_cast<const Aws::GameLift::Model::DescribeGameSessionsResult*>(e.userData);
if (result->GetGameSessions().size() != 1)
{
AZ_TracePrintf("GridMate", "Game session does not exist %s\n", m_searchInfo.m_sessionId.c_str());
RequestEvent(SE_DELETE);
return true;
}
const Aws::GameLift::Model::GameSession& gameSession = result->GetGameSessions().front();
if (gameSession.GetStatus() == Aws::GameLift::Model::GameSessionStatus::ACTIVE)
{
Aws::GameLift::Model::CreatePlayerSessionRequest request;
request.WithGameSessionId(m_searchInfo.m_sessionId.c_str()).WithPlayerId(m_clientService->GetPlayerId());
m_createPlayerSessionOutcomeCallable = m_clientService->GetClient()->CreatePlayerSessionCallable(request);
}
else if (gameSession.GetStatus() == Aws::GameLift::Model::GameSessionStatus::ACTIVATING && m_numGameSessionRetryAttempts < k_maxGameSessionRetries)
{
m_gameSessionRetryTimeout = k_minGameSessionRetryInterval + k_gameSessionRetryBase * (1 << m_numGameSessionRetryAttempts);
m_gameSessionRetryTimestamp = AZStd::chrono::system_clock::now();
++m_numGameSessionRetryAttempts;
}
else
{
AZ_TracePrintf("GridMate", "Failed to activate session %s\n", gameSession.GetGameSessionId().c_str());
sm.Transition(SS_NO_SESSION);
}
return true;
}
case SE_RECEIVED_PLAYERSESSION:
{
m_playerSession = *reinterpret_cast<const Aws::GameLift::Model::PlayerSession*>(e.userData);
SetGameLiftLocalParams();
m_sessionId = m_playerSession.GetGameSessionId().c_str();
sm.Transition(SS_CREATE);
return true;
}
case SE_CLIENT_FAILED:
{
sm.Transition(SS_NO_SESSION);
return true;
}
}
return false;
}
void GameLiftClientSession::SetGameLiftLocalParams()
{
const auto& clientEndpoint = m_clientService->GetEndpoint();
//To support GameLiftLocal on a remote server, convert the reported 127.0.0.1
// address to the configured GameLiftLocal endpoint address
if (m_clientService->UseGameLiftLocal() &&
m_playerSession.GetIpAddress().compare("127.0.0.1") == 0 &&
//Ignore actual loopback connections
clientEndpoint.find("localhost") == -1 &&
clientEndpoint.find("127.") != 0)
{
const auto portLocation = clientEndpoint.find(":");
if (portLocation != -1)
{
//Copy only the host name/address
m_playerSession.SetIpAddress(clientEndpoint.substr(0, portLocation).c_str());
}
else
{
m_playerSession.SetIpAddress(clientEndpoint.c_str());
}
}
}
bool GameLiftClientSession::OnStateStartup(AZ::HSM& sm, const AZ::HSM::Event& e)
{
switch (e.id)
{
case SE_JOIN:
{
sm.Transition(SS_GAMELIFT_INIT);
return true;
}
case SE_MATCHMAKING_JOIN:
{
sm.Transition(SS_CREATE);
return true;
}
}
return false;
}
bool GameLiftClientSession::OnStateCreate(AZ::HSM& sm, const AZ::HSM::Event& e)
{
bool isProcessed = GridSession::OnStateCreate(sm, e);
switch (e.id)
{
case AZ::HSM::EnterEventId:
{
AZ_Assert(m_carrier, "Carrier must be created!");
m_myMember = CreateLocalMember(false, false, Mode_Peer);
// setting player's session id for handshake
WriteBufferStatic<> wb(kSessionEndian);
string playerSessionId = m_playerSession.GetPlayerSessionId().c_str();
wb.Write(playerSessionId);
SetHandshakeUserData(wb.Get(), wb.Size());
Aws::String resolvedIp = m_playerSession.GetIpAddress();
if (!resolvedIp.empty())
{
m_hostAddress = SocketDriverCommon::IPPortToAddressString(resolvedIp.c_str(), m_playerSession.GetPort());
RequestEvent(SE_CREATED);
}
else
{
AZ_TracePrintf("GameLift", "Error retrieving ipAddress for player session.\n");
sm.Transition(SS_DELETE);
return true;
}
return true;
}
}
return isProcessed;
}
bool GameLiftClientSession::OnStateDelete(AZ::HSM& sm, const AZ::HSM::Event& e)
{
bool isProcessed = GridSession::OnStateDelete(sm, e);
switch (e.id)
{
case AZ::HSM::EnterEventId:
{
RequestEvent(SE_DELETED);
}
return true;
}
return isProcessed;
}
bool GameLiftClientSession::OnStateHostMigrateSession(AZ::HSM& sm, const AZ::HSM::Event& e)
{
(void)sm;
(void)e;
AZ_Assert(false, "Host migration is not supported for GameLift sessions.");
return false;
}
GridMember* GameLiftClientSession::CreateRemoteMember(const string& address, ReadBuffer& data, RemotePeerMode peerMode, ConnectionID connId)
{
(void)address;
AZ::u32 remoteId = 0;
if (data.Read(remoteId))
{
GameLiftMemberID memberId(remoteId);
GameLiftMember* member = CreateReplicaChunk<GameLiftMember>(connId, memberId, this);
member->m_peerMode.Set(peerMode);
return member;
}
else
{
return nullptr;
}
}
void GameLiftClientSession::Update()
{
if (m_gameSessionRetryTimeout >= 0)
{
if (AZStd::chrono::milliseconds(AZStd::chrono::system_clock::now() - m_gameSessionRetryTimestamp).count() >= m_gameSessionRetryTimeout)
{
Aws::GameLift::Model::DescribeGameSessionsRequest request;
request.SetGameSessionId(m_searchInfo.m_sessionId.c_str());
m_describeGameSessionsOutcomeCallable = m_clientService->GetClient()->DescribeGameSessionsCallable(request);
m_gameSessionRetryTimeout = -1;
}
}
if (m_describeGameSessionsOutcomeCallable.valid()
&& m_describeGameSessionsOutcomeCallable.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready)
{
auto result = m_describeGameSessionsOutcomeCallable.get();
if (result.IsSuccess())
{
RequestEventData(SE_RECEIVED_GAMESESSION, result.GetResult());
}
else
{
AZ_TracePrintf("GameLift", "Failed to get game session: %s\n", result.GetError().GetMessage().c_str());
RequestEvent(SE_CLIENT_FAILED);
}
}
if (m_createPlayerSessionOutcomeCallable.valid()
&& m_createPlayerSessionOutcomeCallable.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready)
{
auto result = m_createPlayerSessionOutcomeCallable.get();
if (result.IsSuccess())
{
RequestEventData(SE_RECEIVED_PLAYERSESSION, result.GetResult());
}
else
{
AZ_TracePrintf("GameLift", "Failed to entitle session: %s\n", result.GetError().GetMessage().c_str());
RequestEvent(SE_CLIENT_FAILED);
}
}
GridSession::Update();
}
void GameLiftClientSession::RegisterReplicaChunks()
{
ReplicaChunkDescriptorTable::Get().RegisterChunkType<GameLiftSessionReplica, GameLiftSessionReplica::GameLiftSessionReplicaDesc>();
ReplicaChunkDescriptorTable::Get().RegisterChunkType<GameLiftMember, GameLiftMember::GameLiftMemberDesc>();
ReplicaChunkDescriptorTable::Get().RegisterChunkType<GameLiftMemberState>();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
} // namespace GridMate
#endif // BUILD_GAMELIFT_CLIENT
@@ -0,0 +1,220 @@
/*
* 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.
*
*/
#if defined(BUILD_GAMELIFT_CLIENT)
#include <GameLift/Session/GameLiftGameSessionPlacementRequest.h>
#include <GameLift/Session/GameLiftClientService.h>
#include <aws/core/utils/Outcome.h>
// To avoid the warning below
// Semaphore.h(50): warning C4251: 'Aws::Utils::Threading::Semaphore::m_mutex': class 'std::mutex' needs to have dll-interface to be used by clients of class 'Aws::Utils::Threading::Semaphore'
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
#include <aws/gamelift/model/CreateGameSessionRequest.h>
#include <aws/gamelift/model/StartGameSessionPlacementRequest.h>
#include <aws/gamelift/model/StartGameSessionPlacementResult.h>
#include <aws/gamelift/model/GameSessionPlacementState.h>
#include <aws/gamelift/model/DescribeGameSessionDetailsRequest.h>
#include <aws/gamelift/model/DescribeGameSessionPlacementRequest.h>
AZ_POP_DISABLE_WARNING
namespace GridMate
{
GameLiftGameSessionPlacementRequest::GameLiftGameSessionPlacementRequest(GameLiftClientService* service, const AZStd::shared_ptr<GameLiftRequestInterfaceContext> context)
: GameLiftSearch(service, context)
{
m_isDone = true;
m_queueSessionState = GameSessionPlacementState::Unknown;
}
void GameLiftGameSessionPlacementRequest::AbortSearch()
{
SearchDone();
}
bool GameLiftGameSessionPlacementRequest::Initialize()
{
if (m_queueSessionState != GameSessionPlacementState::Unknown)
{
return false;
}
m_queueSessionState = GameSessionPlacementState::StartPlacement;
Aws::Vector<Aws::GameLift::Model::GameProperty> gameProperties;
for (AZStd::size_t paramIndex = 0; paramIndex < m_context->m_requestParams.m_numParams; ++paramIndex)
{
Aws::GameLift::Model::GameProperty prop;
prop.SetKey(m_context->m_requestParams.m_params[paramIndex].m_id.c_str());
prop.SetValue(m_context->m_requestParams.m_params[paramIndex].m_value.c_str());
gameProperties.push_back(prop);
}
Aws::GameLift::Model::StartGameSessionPlacementRequest placementRequest;
placementRequest.SetGameSessionQueueName(m_context->m_requestParams.m_queueName.c_str());
placementRequest.WithMaximumPlayerSessionCount(m_context->m_requestParams.m_numPublicSlots + m_context->m_requestParams.m_numPrivateSlots)
.WithGameSessionName(m_context->m_requestParams.m_instanceName.c_str())
.WithGameProperties(gameProperties)
.WithPlacementId(AZ::Uuid::Create().ToString<AZStd::string>(false, false).c_str());
m_startGameSessionPlacementOutcomeCallable = m_context->m_gameLiftClient.lock()->StartGameSessionPlacementCallable(placementRequest);
m_isDone = false;
return true;
}
void GameLiftGameSessionPlacementRequest::SearchDone()
{
m_queueSessionState = GameSessionPlacementState::Unknown;
GameLiftSearch::SearchDone();
}
void GameLiftGameSessionPlacementRequest::StartGameSessionPlacement()
{
// Poll with 0 delay to see if callable is ready
if (m_startGameSessionPlacementOutcomeCallable.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready)
{
Aws::GameLift::Model::StartGameSessionPlacementOutcome placementResult = m_startGameSessionPlacementOutcomeCallable.get();
if (!placementResult.IsSuccess())
{
AZ_TracePrintf("GameLift", "Session placement failed with error: %s\n", placementResult.GetError().GetMessage().c_str());
SearchDone();
return;
}
m_placementId = placementResult.GetResult().GetGameSessionPlacement().GetPlacementId().c_str();
m_queueSessionState = GameSessionPlacementState::WaitForPlacement;
}
}
void GameLiftGameSessionPlacementRequest::WaitForGameSessionPlacement()
{
if (!m_describeGameSessionPlacementCallable.valid()) {
Aws::GameLift::Model::DescribeGameSessionPlacementRequest describePlacementRequest;
describePlacementRequest.WithPlacementId(m_placementId);
m_describeGameSessionPlacementCallable = m_context->m_gameLiftClient.lock()->DescribeGameSessionPlacementCallable(describePlacementRequest);
}
// Poll with 0 delay to see if callable is ready
if (m_describeGameSessionPlacementCallable.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready)
{
Aws::GameLift::Model::DescribeGameSessionPlacementOutcome describePlacementResult = m_describeGameSessionPlacementCallable.get();
if (!describePlacementResult.IsSuccess())
{
AZ_TracePrintf("GameLift", "Placement not able to describe: %s\n", m_placementId.c_str());
SearchDone();
return;
}
Aws::GameLift::Model::GameSessionPlacement placement = describePlacementResult.GetResult().GetGameSessionPlacement();
if (placement.GetStatus() == Aws::GameLift::Model::GameSessionPlacementState::FULFILLED)
{
m_gameSessionId = placement.GetGameSessionId();
m_queueSessionState = GameSessionPlacementState::AddGameSessionSearchResult;
}
else if (placement.GetStatus() == Aws::GameLift::Model::GameSessionPlacementState::TIMED_OUT ||
placement.GetStatus() == Aws::GameLift::Model::GameSessionPlacementState::CANCELLED)
{
AZ_TracePrintf("GameLift", "Failed to describe placement: %s\n", m_placementId.c_str());
SearchDone();
return;
}
}
}
const Aws::GameLift::Model::GameSession GameLiftGameSessionPlacementRequest::GetPlacedGameSession()
{
Aws::GameLift::Model::DescribeGameSessionDetailsRequest gameSessionRequest;
gameSessionRequest.WithGameSessionId(m_gameSessionId);
Aws::GameLift::Model::DescribeGameSessionDetailsOutcome describeSessionOutcome = m_context->m_gameLiftClient.lock()->DescribeGameSessionDetails(gameSessionRequest);
if (!describeSessionOutcome.IsSuccess())
{
AZ_TracePrintf("GameLift", "Game Session not able to describe: %s\n", m_gameSessionId.c_str());
SearchDone();
return Aws::GameLift::Model::GameSession();
}
auto gameSessionDetails = describeSessionOutcome.GetResult().GetGameSessionDetails();
if (gameSessionDetails.size() <= 0)
{
AZ_TracePrintf("GameLift", "No Session found: %s\n", m_gameSessionId.c_str());
SearchDone();
return Aws::GameLift::Model::GameSession();
}
return gameSessionDetails[0].GetGameSession();
}
void GameLiftGameSessionPlacementRequest::AddGameSessionSearchResult(const Aws::GameLift::Model::GameSession &gameSession)
{
GameLiftSearchInfo info;
info.m_fleetId = gameSession.GetFleetId().c_str();
info.m_sessionId = gameSession.GetGameSessionId().c_str();
info.m_numFreePublicSlots = gameSession.GetMaximumPlayerSessionCount() - gameSession.GetCurrentPlayerSessionCount();
info.m_numUsedPublicSlots = gameSession.GetCurrentPlayerSessionCount();
info.m_numPlayers = gameSession.GetCurrentPlayerSessionCount();
auto const& properties = gameSession.GetGameProperties();
for (auto const& prop : properties)
{
info.m_params[info.m_numParams].m_id = prop.GetKey().c_str();
info.m_params[info.m_numParams].m_value = prop.GetValue().c_str();
++info.m_numParams;
}
m_results.push_back(info);
}
void GameLiftGameSessionPlacementRequest::Update()
{
if (m_isDone)
{
return;
}
switch (m_queueSessionState)
{
case GameSessionPlacementState::StartPlacement:
{
StartGameSessionPlacement();
break;
}
case GameSessionPlacementState::WaitForPlacement:
{
WaitForGameSessionPlacement();
break;
}
case GameSessionPlacementState::AddGameSessionSearchResult:
{
auto gameSession = GetPlacedGameSession();
if (!gameSession.GetGameSessionId().empty())
{
AddGameSessionSearchResult(gameSession);
SearchDone();
}
break;
}
case GameSessionPlacementState::Unknown:
{
AZ_TracePrintf("GameLift", "Unknown state is not expected for queueName: %s\n", m_context->m_requestParams.m_queueName.c_str());
SearchDone();
break;
}
}
}
} // namespace GridMate
#endif // BUILD_GAMELIFT_CLIENT
@@ -0,0 +1,166 @@
/*
* 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.
*
*/
#if defined(BUILD_GAMELIFT_CLIENT)
#include <GameLift/Session/GameLiftMatchmaking.h>
#include <GameLift/Session/GameLiftClientService.h>
#include <aws/core/utils/Outcome.h>
// To avoid the warning below
// Semaphore.h(50): warning C4251: 'Aws::Utils::Threading::Semaphore::m_mutex': class 'std::mutex' needs to have dll-interface to be used by clients of class 'Aws::Utils::Threading::Semaphore'
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
#include <aws/gamelift/model/StartMatchmakingRequest.h>
#include <aws/gamelift/model/DescribeMatchmakingRequest.h>
#include <aws/gamelift/model/DescribeGameSessionDetailsRequest.h>
#include <aws/gamelift/model/MatchmakingTicket.h>
#include <aws/gamelift/model/Player.h>
AZ_POP_DISABLE_WARNING
namespace GridMate
{
GameLiftMatchmaking::GameLiftMatchmaking(GameLiftClientService* service, const AZStd::shared_ptr<GameLiftRequestInterfaceContext> context
, const Aws::String& matchmakingConfigName)
: GameLiftSearch(service, context)
, m_matchmakingConfigName(matchmakingConfigName)
{
}
bool GameLiftMatchmaking::Initialize()
{
Aws::GameLift::Model::StartMatchmakingRequest request;
Aws::GameLift::Model::Player player;
player.SetPlayerId(m_context->m_playerId.c_str());
request.AddPlayers(player);
request.SetConfigurationName(m_matchmakingConfigName);
m_startMatchmakingOutcomeCallable = m_context->m_gameLiftClient.lock()->StartMatchmakingCallable(request);
m_isDone = false;
return true;
}
void GameLiftMatchmaking::SearchDone()
{
m_isDone = true;
}
void GameLiftMatchmaking::WaitForStartMatchmakingResult()
{
// Poll with 0 delay to see if callable is ready
if (m_startMatchmakingOutcomeCallable.valid()
&& m_startMatchmakingOutcomeCallable.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready)
{
auto result = m_startMatchmakingOutcomeCallable.get();
if (result.IsSuccess())
{
m_matchmakingTicket = result.GetResult().GetMatchmakingTicket();
m_startDescribeMatchmakingTime = AZStd::chrono::system_clock::now();
}
else
{
AZ_TracePrintf("GameLift", "Matchmaking request failed with error: %s\n", result.GetError().GetMessage().c_str());
SearchDone();
return;
}
}
}
// GameLift recommends using Cloudwatch and SNS events instead of polling to avoid TPS limits.
// Documentation: https://docs.aws.amazon.com/gamelift/latest/developerguide/match-notification.html
void GameLiftMatchmaking::PollWithDelayDescribeMatchmaking()
{
GridMate::TimeStamp now = AZStd::chrono::system_clock::now();
float timeElapsed = AZStd::chrono::duration<float>(now - m_startDescribeMatchmakingTime).count();
if (timeElapsed > m_pollDescribeMatchmakingDelay && !m_describeMatchmakingOutcomeCallable.valid() && !m_matchmakingTicket.GetTicketId().empty())
{
m_startDescribeMatchmakingTime = AZStd::chrono::system_clock::now();
Aws::GameLift::Model::DescribeMatchmakingRequest request;
request.AddTicketIds(m_matchmakingTicket.GetTicketId());
m_describeMatchmakingOutcomeCallable = m_context->m_gameLiftClient.lock()->DescribeMatchmakingCallable(request);
}
}
void GameLiftMatchmaking::WaitForDescribeMatchmakingResult()
{
if (m_describeMatchmakingOutcomeCallable.valid()
&& m_describeMatchmakingOutcomeCallable.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready)
{
auto result = m_describeMatchmakingOutcomeCallable.get();
if (result.IsSuccess())
{
for (auto ticket : result.GetResult().GetTicketList())
{
if (ticket.GetTicketId() == m_matchmakingTicket.GetTicketId())
{
if (ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::COMPLETED)
{
m_gameSessionConnectionInfo = ticket.GetGameSessionConnectionInfo();
}
else if (ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::TIMED_OUT
|| ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::FAILED
|| ticket.GetStatus() == Aws::GameLift::Model::MatchmakingConfigurationStatus::CANCELLED)
{
AZ_TracePrintf("GameLift", "Matchmaking request did not complete ticketId:%s status:%s message:%s", m_matchmakingTicket.GetTicketId().c_str(),
ticket.GetStatusReason().c_str(), ticket.GetStatusMessage().c_str());
SearchDone();
}
else
{
AZ_TracePrintf("GameLift", "Matchmaking request waiting to complete ticketId:%s status:%s message:%s", m_matchmakingTicket.GetTicketId().c_str(),
ticket.GetStatusReason().c_str(), ticket.GetStatusMessage().c_str());
}
break;
}
}
}
else
{
AZ_TracePrintf("GameLift", "Matchmaking request failed with error: %s\n", result.GetError().GetMessage().c_str());
SearchDone();
return;
}
}
// Game session connection found. End search and add to results.
if (m_gameSessionConnectionInfo.GameSessionArnHasBeenSet())
{
GameLiftSearchInfo info;
info.m_sessionId = m_gameSessionConnectionInfo.GetGameSessionArn().c_str();
info.m_port = m_gameSessionConnectionInfo.GetPort();
info.m_ipAddress = m_gameSessionConnectionInfo.GetIpAddress().c_str();
for (auto playerSession : m_gameSessionConnectionInfo.GetMatchedPlayerSessions())
{
if (m_context->m_playerId.compare(playerSession.GetPlayerId().c_str()) == 0)
{
info.m_playerSessionId = playerSession.GetPlayerSessionId().c_str();
}
}
m_results.push_back(info);
SearchDone();
}
}
void GameLiftMatchmaking::Update()
{
if (m_isDone)
{
return;
}
WaitForStartMatchmakingResult();
PollWithDelayDescribeMatchmaking();
WaitForDescribeMatchmakingResult();
}
} // namespace GridMate
#endif // BUILD_GAMELIFT_CLIENT
@@ -0,0 +1,192 @@
/*
* 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.
*
*/
#if defined(BUILD_GAMELIFT_CLIENT)
#include <GameLift/Session/GameLiftSearch.h>
#include <GameLift/Session/GameLiftClientService.h>
#include <aws/core/utils/Outcome.h>
// To avoid the warning below
// Semaphore.h(50): warning C4251: 'Aws::Utils::Threading::Semaphore::m_mutex': class 'std::mutex' needs to have dll-interface to be used by clients of class 'Aws::Utils::Threading::Semaphore'
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
#include <aws/gamelift/model/SearchGameSessionsRequest.h>
#include <aws/gamelift/model/DescribeGameSessionQueuesRequest.h>
AZ_POP_DISABLE_WARNING
namespace GridMate
{
const AZStd::string GameLiftFleetIdPrefix = "fleet/";
const AZStd::string ExtractFleetIdFromFleetArn(AZStd::string fleetArn)
{
return fleetArn.substr(fleetArn.rfind(GameLiftFleetIdPrefix) + GameLiftFleetIdPrefix.size());
}
GameLiftSearch::GameLiftSearch(GameLiftClientService* service, const AZStd::shared_ptr<GameLiftRequestInterfaceContext> context)
: GridSearch(service)
, GameLiftRequestInterface(context)
{
m_isDone = true;
}
bool GameLiftSearch::Initialize()
{
if (!m_context->m_searchParams.m_queueName.empty())
{
StartDescribeGameSessionQueue();
}
else
{
StartSearchGameSession();
}
m_isDone = false;
return true;
}
void GameLiftSearch::StartSearchGameSession()
{
Aws::GameLift::Model::SearchGameSessionsRequest request;
m_context->m_searchParams.m_useFleetId ? request.SetFleetId(m_context->m_searchParams.m_fleetId.c_str())
: request.SetAliasId(m_context->m_searchParams.m_aliasId.c_str());
if (!m_context->m_searchParams.m_gameInstanceId.empty())
{
Aws::String filter("gameSessionId = ");
filter += m_context->m_searchParams.m_gameInstanceId.c_str();
request.SetFilterExpression(filter);
}
m_searchGameSessionsOutcomeCallable = m_context->m_gameLiftClient.lock()->SearchGameSessionsCallable(request);
}
void GameLiftSearch::WaitForSearchGameSession()
{
if (m_searchGameSessionsOutcomeCallable.valid()
&& m_searchGameSessionsOutcomeCallable.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready)
{
auto result = m_searchGameSessionsOutcomeCallable.get();
if (result.IsSuccess())
{
auto gameSessions = result.GetResult().GetGameSessions();
for (auto& gameSession : gameSessions)
{
ProcessGameSessionResult(gameSession);
}
}
else
{
AZ_TracePrintf("GameLift", "Session search failed with error: %s\n", result.GetError().GetMessage().c_str());
}
SearchDone();
}
}
void GameLiftSearch::StartDescribeGameSessionQueue()
{
Aws::GameLift::Model::DescribeGameSessionQueuesRequest request;
request.AddNames(m_context->m_searchParams.m_queueName.c_str());
m_describeGameSessionQueueOutcomeCallable = m_context->m_gameLiftClient.lock()->DescribeGameSessionQueuesCallable(request);
}
void GameLiftSearch::WaitDescribeGameSessionQueue()
{
if (m_describeGameSessionQueueOutcomeCallable.valid()
&& m_describeGameSessionQueueOutcomeCallable.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready)
{
auto result = m_describeGameSessionQueueOutcomeCallable.get();
if (result.IsSuccess())
{
if (result.GetResult().GetGameSessionQueues().size() == 0)
{
Aws::String errorMessage = "No Queue found for queue name: %s";
Aws::Utils::StringUtils::Replace(errorMessage, "%s", m_context->m_searchParams.m_queueName.c_str());
AZ_TracePrintf("GameLift", errorMessage.c_str());
SearchDone();
return;
}
auto gameSessionQueue = result.GetResult().GetGameSessionQueues().front();
if (m_context->m_searchParams.m_queueName.compare(gameSessionQueue.GetName().c_str()) == 0
&& gameSessionQueue.GetDestinations().size() > 0)
{
// Default to first fleet in queue. FleetId is used to describe game sessions.
AZStd::string fleetArn = gameSessionQueue.GetDestinations().size() > 0 ? gameSessionQueue.GetDestinations()[0].GetDestinationArn().c_str() : "";
m_context->m_searchParams.m_fleetId = ExtractFleetIdFromFleetArn(fleetArn);
m_context->m_searchParams.m_useFleetId = true;
StartSearchGameSession();
}
}
else
{
AZ_TracePrintf("GameLift", "Game session queue search failed with error: %s\n", result.GetError().GetMessage().c_str());
SearchDone();
}
}
}
unsigned int GameLiftSearch::GetNumResults() const
{
return static_cast<unsigned int>(m_results.size());
}
const SearchInfo* GameLiftSearch::GetResult(unsigned int index) const
{
return &m_results[index];
}
void GameLiftSearch::AbortSearch()
{
SearchDone();
}
void GameLiftSearch::SearchDone()
{
m_isDone = true;
}
void GameLiftSearch::Update()
{
if (m_isDone)
{
return;
}
WaitDescribeGameSessionQueue();
WaitForSearchGameSession();
}
void GameLiftSearch::ProcessGameSessionResult(const Aws::GameLift::Model::GameSession& gameSession)
{
GameLiftSearchInfo info;
info.m_fleetId = gameSession.GetFleetId().c_str();
info.m_sessionId = gameSession.GetGameSessionId().c_str();
info.m_numFreePublicSlots = gameSession.GetMaximumPlayerSessionCount() - gameSession.GetCurrentPlayerSessionCount();
info.m_numUsedPublicSlots = gameSession.GetCurrentPlayerSessionCount();
info.m_numPlayers = gameSession.GetCurrentPlayerSessionCount();
info.m_port = gameSession.GetPort();
auto& properties = gameSession.GetGameProperties();
for (auto& prop : properties)
{
info.m_params[info.m_numParams].m_id = prop.GetKey().c_str();
info.m_params[info.m_numParams].m_value = prop.GetValue().c_str();
++info.m_numParams;
}
m_results.push_back(info);
}
} // namespace GridMate
#endif // BUILD_GAMELIFT_CLIENT
@@ -0,0 +1,80 @@
/*
* 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.
*
*/
#if defined(BUILD_GAMELIFT_SERVER)
#include <GameLift/Session/GameLiftServerSDKWrapper.h>
namespace GridMate
{
Aws::GameLift::Server::InitSDKOutcome GameLiftServerSDKWrapper::InitSDK()
{
return Aws::GameLift::Server::InitSDK();
}
Aws::GameLift::GenericOutcomeCallable GameLiftServerSDKWrapper::ProcessReadyAsync(const Aws::GameLift::Server::ProcessParameters& processParameters)
{
return Aws::GameLift::Server::ProcessReadyAsync(processParameters);
}
Aws::GameLift::GenericOutcome GameLiftServerSDKWrapper::ProcessEnding()
{
return Aws::GameLift::Server::ProcessEnding();
}
Aws::GameLift::GenericOutcome GameLiftServerSDKWrapper::Destroy()
{
return Aws::GameLift::Server::Destroy();
}
Aws::GameLift::GenericOutcome GameLiftServerSDKWrapper::ActivateGameSession()
{
return Aws::GameLift::Server::ActivateGameSession();
}
Aws::GameLift::GenericOutcome GameLiftServerSDKWrapper::TerminateGameSession()
{
return Aws::GameLift::Server::TerminateGameSession();
}
Aws::GameLift::GenericOutcome GameLiftServerSDKWrapper::AcceptPlayerSession(const std::string& playerSessionId)
{
return Aws::GameLift::Server::AcceptPlayerSession(playerSessionId);
}
Aws::GameLift::GenericOutcome GameLiftServerSDKWrapper::RemovePlayerSession(const char* playerSessionId)
{
return Aws::GameLift::Server::RemovePlayerSession(playerSessionId);
}
Aws::GameLift::DescribePlayerSessionsOutcome GameLiftServerSDKWrapper::DescribePlayerSessions(const Aws::GameLift::Server::Model::DescribePlayerSessionsRequest& describePlayerSessionsRequest)
{
return Aws::GameLift::Server::DescribePlayerSessions(describePlayerSessionsRequest);
}
Aws::GameLift::StartMatchBackfillOutcome GameLiftServerSDKWrapper::StartMatchBackfill(const Aws::GameLift::Server::Model::StartMatchBackfillRequest& backfillMatchmakingRequest)
{
return Aws::GameLift::Server::StartMatchBackfill(backfillMatchmakingRequest);
}
Aws::GameLift::GenericOutcome GameLiftServerSDKWrapper::StopMatchBackfill(const Aws::GameLift::Server::Model::StopMatchBackfillRequest& request)
{
return Aws::GameLift::Server::StopMatchBackfill(request);
}
Aws::GameLift::AwsStringOutcome GameLiftServerSDKWrapper::GetGameSessionId()
{
return Aws::GameLift::Server::GetGameSessionId();
}
}
#endif // BUILD_GAMELIFT_SERVER
@@ -0,0 +1,325 @@
/*
* 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.
*
*/
#if defined(BUILD_GAMELIFT_SERVER)
#include <AzCore/Component/TickBus.h>
#include <AzCore/Memory/Memory.h>
#include <GameLift/Session/GameLiftServerSDKWrapper.h>
#include <GameLift/Session/GameLiftServerService.h>
#include <GameLift/Session/GameLiftServerSession.h>
namespace GridMate
{
GameLiftServerService::GameLiftServerService(const GameLiftServerServiceDesc& desc)
: SessionService(desc)
, m_serviceDesc(desc)
, m_serverStatus(GameLift_NotInited)
, m_serverInitOutcome(nullptr)
{
m_gameLiftServerSDKWrapper = AZStd::make_shared<GridMate::GameLiftServerSDKWrapper>();
}
GameLiftServerService::~GameLiftServerService()
{
m_gameLiftServerSDKWrapper.reset();
}
void GameLiftServerService::OnServiceRegistered(IGridMate* gridMate)
{
SessionService::OnServiceRegistered(gridMate);
GameLiftServerSession::RegisterReplicaChunks();
Internal::GameLiftServerSystemEventsBus::Handler::BusConnect();
if (!StartGameLiftServer())
{
EBUS_EVENT_ID(m_gridMate, GameLiftServerServiceEventsBus, OnGameLiftSessionServiceFailed, this);
}
GameLiftServerServiceBus::Handler::BusConnect(gridMate);
}
void GameLiftServerService::OnServiceUnregistered(IGridMate* gridMate)
{
GameLiftServerServiceBus::Handler::BusDisconnect();
Internal::GameLiftServerSystemEventsBus::Handler::BusDisconnect();
Internal::GameLiftServerSystemEventsBus::ClearQueuedEvents();
if (m_serverStatus == GameLift_Ready || m_serverStatus == GameLift_Terminated)
{
GetGameLiftServerSDKWrapper().lock()->ProcessEnding();
GetGameLiftServerSDKWrapper().lock()->Destroy();
m_serverStatus = GameLift_NotInited;
}
delete m_serverInitOutcome;
SessionService::OnServiceUnregistered(gridMate);
}
bool GameLiftServerService::StartGameLiftServer()
{
if (m_serverStatus == GameLift_NotInited)
{
Aws::GameLift::Server::InitSDKOutcome initOutcome = GetGameLiftServerSDKWrapper().lock()->InitSDK();
if (initOutcome.IsSuccess())
{
AZ_TracePrintf("GameLift", "InitSDK succeeded.\n");
AZ_Warning("GameLift", m_serviceDesc.m_port != 0, "Server will be listening on ephemeral port");
std::vector<std::string> logPaths;
for (const string& path : m_serviceDesc.m_logPaths)
{
logPaths.push_back(path.c_str());
}
Aws::GameLift::Server::ProcessParameters processParams(
/*
* onStartGameSession
* Invoked when we push a GameSession to the server
*/
[this](const Aws::GameLift::Server::Model::GameSession& gameSession) {
AZ_TracePrintf("GameLift", "On Activate...\n");
// This callback will be called on GameLift thread
EBUS_QUEUE_EVENT(Internal::GameLiftServerSystemEventsBus, OnGameLiftGameSessionStarted, gameSession);
},
/*
* onUpdateGameSession
* Invoked when the game session is updated after backfill
*/
[this](const Aws::GameLift::Server::Model::UpdateGameSession& updateGameSession) {
AZ_TracePrintf("GameLift", "On Update Game Session...\n");
EBUS_QUEUE_EVENT(Internal::GameLiftServerSystemEventsBus, OnGameLiftGameSessionUpdated, updateGameSession);
},
/*
* onProcessTerminate
* Invoked when GameLift wants to force kill the server
*/
[this]() {
AZ_TracePrintf("GameLift", "On Terminate invoked\n");
EBUS_QUEUE_EVENT(Internal::GameLiftServerSystemEventsBus, OnGameLiftServerWillTerminate);
},
/*
* onHealthCheck
* Invoked every minute to check on health. This callback must return a boolean.
*/
[]() {
return true;
},
/*
* port
* The port the server will be listening on
*/
m_serviceDesc.m_port,
/*
* logParameters
* A vector of log paths the servers will write to (and uploaded)
*/
Aws::GameLift::Server::LogParameters(logPaths)
);
m_serverInitOutcome = new Aws::GameLift::GenericOutcomeCallable(
GetGameLiftServerSDKWrapper().lock()->ProcessReadyAsync(processParams));
}
else
{
AZ_TracePrintf("GameLift", "InitSDK failed.\n");
m_serverStatus = GameLift_Failed;
}
}
return m_serverStatus != GameLift_Failed;
}
bool GameLiftServerService::IsReady() const
{
return m_serverStatus == GameLift_Ready;
}
void GameLiftServerService::OnGameLiftGameSessionStarted(const Aws::GameLift::Server::Model::GameSession& gameSession)
{
AZ_TracePrintf("GameLift", "Dispatching OnGameLiftGameSessionStarted...\n");
EBUS_EVENT_ID(m_gridMate, GameLiftServerServiceEventsBus, OnGameLiftGameSessionStarted, this, gameSession);
}
void GameLiftServerService::OnGameLiftGameSessionUpdated(const Aws::GameLift::Server::Model::UpdateGameSession& updateGameSession)
{
AZ_TracePrintf("GameLift", "Dispatching OnGameLiftGameSessionUpdated...\n");
UpdateGameSession(updateGameSession);
EBUS_EVENT_ID(m_gridMate, GameLiftServerServiceEventsBus, OnGameLiftGameSessionUpdated, this, updateGameSession);
}
void GameLiftServerService::OnGameLiftServerWillTerminate()
{
Internal::GameLiftServerSystemEventsBus::Handler::BusDisconnect();
Internal::GameLiftServerSystemEventsBus::ClearQueuedEvents(); // already terminating, don't need any other events
m_serverStatus = GameLift_Terminated;
EBUS_EVENT_ID(m_gridMate, GameLiftServerServiceEventsBus, OnGameLiftServerWillTerminate, this);
}
void GameLiftServerService::Update()
{
Internal::GameLiftServerSystemEventsBus::ExecuteQueuedEvents();
if (m_serverInitOutcome
&& m_serverInitOutcome->valid()
&& m_serverInitOutcome->wait_for(std::chrono::milliseconds(0)) == std::future_status::ready)
{
auto result = m_serverInitOutcome->get();
if (result.IsSuccess())
{
AZ_TracePrintf("GameLift", "Initialized GameLift server successfully.\n");
m_serverStatus = GameLift_Ready;
if (IsReady())
{
EBUS_EVENT_ID(m_gridMate, GameLiftServerServiceEventsBus, OnGameLiftSessionServiceReady, this);
EBUS_DBG_EVENT(Debug::SessionDrillerBus, OnSessionServiceReady);
EBUS_EVENT_ID(m_gridMate, SessionEventBus, OnSessionServiceReady);
}
}
else
{
AZ_TracePrintf("GameLift", "Failed to initialize GameLift server: %s, %s\n", result.GetError().GetErrorName().c_str(), result.GetError().GetErrorMessage().c_str());
m_serverStatus = GameLift_Failed;
// defer the notification so Gridmate doesn't destroy this service while updating
AZ::TickBus::QueueFunction([this]()
{
GameLiftServerServiceEventsBus::Event(m_gridMate, &GameLiftServerServiceEventsBus::Events::OnGameLiftSessionServiceFailed, this);
});
}
}
SessionService::Update();
}
GameLiftServerSession* GameLiftServerService::QueryGameLiftSession(const GridSession* session)
{
for (GridSession* s : m_sessions)
{
if (s == session)
{
return static_cast<GameLiftServerSession*>(s);
}
}
return nullptr;
}
GridSession* GameLiftServerService::HostSession(const GameLiftSessionParams& params, const CarrierDesc& carrierDesc)
{
AZ_TracePrintf("GameLift", "GameLiftSessionService::HostSession.\n");
if (m_serverStatus != GameLift_Ready)
{
AZ_TracePrintf("GameLift", "Server API is not initialized.\n");
return nullptr;
}
GameLiftServerSession* session = aznew GameLiftServerSession(this);
if (!session->Initialize(params, carrierDesc))
{
AZ_TracePrintf("GameLift", "GameLiftSessionService::HostSession. Could not initialize the session.\n");
delete session;
return nullptr;
}
AZ_TracePrintf("GameLift", "GameLiftSessionService::HostSession. Completed.\n");
return session;
}
void GameLiftServerService::ShutdownSession(const GridSession* gridSession)
{
GameLiftServerSession* session = FindGameLiftServerSession(gridSession->GetId());
if (session)
{
// Shutdown call removes the game session from server service.
session->Shutdown();
delete session;
}
else
{
AZ_TracePrintf("GameLift", "GameSession Failed to Shutdown. No GameLiftServerSession found for :%s", gridSession->GetId().c_str());
}
}
bool GameLiftServerService::StartMatchmakingBackfill(const GridSession* gridSession, AZStd::string& matchmakingTicketId, bool checkForAutoBackfill)
{
GameLiftServerSession* session = FindGameLiftServerSession(gridSession->GetId());
if (session)
{
return session->StartMatchmakingBackfill(matchmakingTicketId, checkForAutoBackfill);
}
else
{
AZ_TracePrintf("GameLift", "GameSession Failed to start backfill. No GameLiftServerSession found for :%s", gridSession->GetId().c_str());
return false;
}
}
bool GameLiftServerService::StopMatchmakingBackfill(const GridSession* gridSession, const AZStd::string& matchmakingTicketId)
{
GameLiftServerSession* session = FindGameLiftServerSession(gridSession->GetId());
if (session)
{
return session->StopMatchmakingBackfill(matchmakingTicketId);
}
else
{
AZ_TracePrintf("GameLift", "GameSession Failed to stop backfill. No GameLiftServerSession found for :%s", gridSession->GetId().c_str());
return false;
}
}
bool GameLiftServerService::UpdateGameSession(const Aws::GameLift::Server::Model::UpdateGameSession& updateGameSession)
{
AZStd::string gameSessionId = updateGameSession.GetGameSession().GetGameSessionId().c_str();
GameLiftServerSession* session = FindGameLiftServerSession(gameSessionId);
if (session)
{
return session->GameSessionUpdated(updateGameSession);
}
else
{
AZ_TracePrintf("GameLift", "GameSession Failed to update. No GameLiftServerSession found for :%s", gameSessionId.c_str());
return false;
}
}
AZStd::weak_ptr<GameLiftServerSDKWrapper> GameLiftServerService::GetGameLiftServerSDKWrapper()
{
return m_gameLiftServerSDKWrapper;
}
GameLiftServerSession* GameLiftServerService::FindGameLiftServerSession(const AZStd::string& id)
{
GridSession* gridSession = nullptr;
for (GridSession* session : m_sessions)
{
if (id.compare(session->GetId()) == 0)
{
gridSession = session;
break;
}
}
return static_cast<GameLiftServerSession*>(gridSession);
}
} // namespace GridMate
#endif // BUILD_GAMELIFT_SERVER
@@ -0,0 +1,633 @@
/*
* 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.
*
*/
#if defined(BUILD_GAMELIFT_SERVER)
#include <GameLift/Session/GameLiftServerSDKWrapper.h>
#include <GameLift/Session/GameLiftServerSession.h>
#include <GameLift/Session/GameLiftServerService.h>
#include <GridMate/Online/UserServiceTypes.h>
#include <GridMate/Replica/Replica.h>
#include <GridMate/Replica/ReplicaFunctions.h>
#include <GridMate/Carrier/SocketDriver.h>
#include <GridMate/Carrier/Utils.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/JSON/error/error.h>
#include <AzCore/JSON/error/en.h>
#include <aws/gamelift/server/GameLiftServerAPI.h>
#include <aws/gamelift/server/model/GameSession.h>
#include <GameLift/Session/GameLiftServerServiceEventsBus.h>
namespace GridMate
{
class GameLiftServerMember;
//-----------------------------------------------------------------------------
// GameLiftServerSessionReplica
//-----------------------------------------------------------------------------
class GameLiftServerSessionReplica
: public Internal::GridSessionReplica
{
public:
class GameLiftSessionReplicaDesc
: public ReplicaChunkDescriptor
{
public:
GameLiftSessionReplicaDesc()
: ReplicaChunkDescriptor(GameLiftServerSessionReplica::GetChunkName(), sizeof(GameLiftServerSessionReplica))
{
}
ReplicaChunkBase* CreateFromStream(UnmarshalContext&) override
{
AZ_Assert(false, "GameLiftServerSessionReplica should never be created from stream on the server!");
return nullptr;
}
void DiscardCtorStream(UnmarshalContext&) override {}
void DeleteReplicaChunk(ReplicaChunkBase* chunkInstance) override { delete chunkInstance; }
void MarshalCtorData(ReplicaChunkBase*, WriteBuffer&) override {}
};
GM_CLASS_ALLOCATOR(GameLiftServerSessionReplica);
static const char* GetChunkName() { return "GridMate::GameLiftSessionReplica"; }
GameLiftServerSessionReplica(GameLiftServerSession* session)
: GridSessionReplica(session)
{
}
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// GameLiftServerMemberID
//-----------------------------------------------------------------------------
class GameLiftServerMemberID
: public MemberID
{
public:
//-----------------------------------------------------------------------------
// Marshaler
//-----------------------------------------------------------------------------
class Marshaler
{
public:
void Marshal(WriteBuffer& wb, const GameLiftServerMemberID& id) const
{
wb.Write(id.m_id);
}
void Unmarshal(GameLiftServerMemberID& id, ReadBuffer& rb) const
{
rb.Read(id.m_id);
}
};
//-----------------------------------------------------------------------------
GameLiftServerMemberID()
: m_id(0)
{
}
GameLiftServerMemberID(const string& address, AZ::u32 memberId)
: m_address(address)
, m_id(memberId)
{
AZ_Assert(m_id != 0, "Invalid member id");
}
string ToString() const override { return string::format("%08X", m_id); }
string ToAddress() const override { return m_address; }
MemberIDCompact Compact() const override { return m_id; }
bool IsValid() const override { return !m_address.empty() && m_id != 0; }
private:
AZ::u32 m_id;
string m_address;
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// GameLiftMemberInfoCtorContext
//-----------------------------------------------------------------------------
struct GameLiftMemberInfoCtorContext
: public CtorContextBase
{
CtorDataSet<GameLiftServerMemberID, GameLiftServerMemberID::Marshaler> m_memberId;
CtorDataSet<RemotePeerMode> m_peerMode;
CtorDataSet<bool> m_isHost;
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// GameLiftServerMemberState
//-----------------------------------------------------------------------------
class GameLiftServerMemberState
: public Internal::GridMemberStateReplica
{
public:
GM_CLASS_ALLOCATOR(GameLiftServerMemberState);
static const char* GetChunkName() { return "GameLiftMemberState"; }
explicit GameLiftServerMemberState(GridMember* member = nullptr)
: GridMemberStateReplica(member)
{
}
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// GameLiftServerMember
//-----------------------------------------------------------------------------
class GameLiftServerMember
: public GridMember
{
friend class GameLiftServerSession;
public:
class GameLiftServerMemberDesc
: public ReplicaChunkDescriptor
{
public:
GameLiftServerMemberDesc()
: ReplicaChunkDescriptor(GameLiftServerMember::GetChunkName(), sizeof(GameLiftServerMember))
{
}
ReplicaChunkBase* CreateFromStream(UnmarshalContext&) override
{
AZ_Assert(false, "GameLiftServerMemberDesc should never be created from stream on the server!");
return nullptr;
}
void DiscardCtorStream(UnmarshalContext& mc) override
{
GameLiftMemberInfoCtorContext ctorContext;
ctorContext.Unmarshal(*mc.m_iBuf);
}
void DeleteReplicaChunk(ReplicaChunkBase* chunkInstance) override
{
if (!static_cast<GameLiftServerMember*>(chunkInstance)->IsLocal())
{
delete chunkInstance;
}
}
void MarshalCtorData(ReplicaChunkBase* chunkInstance, WriteBuffer& wb) override
{
GameLiftServerMember* member = static_cast<GameLiftServerMember*>(chunkInstance);
GameLiftMemberInfoCtorContext ctorContext;
ctorContext.m_memberId.Set(member->m_memberId);
ctorContext.m_peerMode.Set(member->m_peerMode.Get());
ctorContext.m_isHost.Set(member->IsHost());
ctorContext.Marshal(wb);
}
};
GM_CLASS_ALLOCATOR(GameLiftServerMember);
static const char* GetChunkName() { return "GridMate::GameLiftMember"; }
const PlayerId* GetPlayerId() const override
{
return nullptr;
}
const MemberID& GetId() const override
{
return m_memberId;
}
void SetPlayerSessionId(const char* playerSessionId) { AZ_Assert(playerSessionId, "Invalid player session id"); m_playerSessionId = playerSessionId; }
const char* GetPlayerSessionId() const { return m_playerSessionId.c_str(); }
/// Remote member ctor.
GameLiftServerMember(ConnectionID connId, const GameLiftServerMemberID& memberId, GameLiftServerSession* session)
: GridMember(memberId.Compact())
, m_memberId(memberId)
{
m_session = session;
m_connectionId = connId;
}
/// Local member ctor.
GameLiftServerMember(const GameLiftServerMemberID& memberId, GameLiftServerSession* session)
: GridMember(memberId.Compact())
, m_memberId(memberId)
{
m_session = session;
m_clientState = CreateReplicaChunk<GameLiftServerMemberState>(this);
m_clientState->m_name.Set(memberId.ToString());
m_clientStateReplica = Replica::CreateReplica(memberId.ToString().c_str());
m_clientStateReplica->AttachReplicaChunk(m_clientState);
}
void OnReplicaDeactivate(const ReplicaContext& rc) override
{
if (IsMaster() && !IsLocal())
{
GameLiftServerSession* serverSession = static_cast<GameLiftServerSession*>(m_session);
Aws::GameLift::GenericOutcome outcome = serverSession->GetGameLiftServerSDKWrapper().lock()->RemovePlayerSession(m_playerSessionId.c_str());
if (!outcome.IsSuccess())
{
AZ_TracePrintf("GameLift", "[SERVER SESSION] Failed to disconnect a master non-local GameLift player:'%s' with id=%s\n", outcome.GetError().GetErrorName().c_str(), GetId().ToString().c_str());
}
else
{
AZ_TracePrintf("GameLift", "Player removed current used public slots:%d and free public slots:%d", m_session->GetNumUsedPublicSlots(), m_session->GetNumFreePublicSlots());
AZ_TracePrintf("GameLift", "[SERVER SESSION] Sucessfully disconnected a master non-local GameLift player with id=%s\n", GetId().ToString().c_str());
}
}
else
{
AZ_TracePrintf("GameLift", "[SERVER SESSION] Deactivating a gridmember, memberid %d", rc.m_peer ? rc.m_peer->GetId() : 0);
}
GridMember::OnReplicaDeactivate(rc);
}
using GridMember::SetHost;
GameLiftServerMemberID m_memberId;
string m_playerSessionId;
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// GameLiftServerSession
//-----------------------------------------------------------------------------
GameLiftServerSession::GameLiftServerSession(GameLiftServerService* service)
: GridSession(service)
{
}
GameLiftServerSession::~GameLiftServerSession()
{
if(m_gameLiftSession)
{
delete m_gameLiftSession;
m_gameLiftSession = nullptr;
}
}
bool GameLiftServerSession::Initialize(const GameLiftSessionParams& params, const CarrierDesc& carrierDesc)
{
const Aws::GameLift::Server::Model::GameSession* gameSession = params.m_gameSession;
AZ_Assert(gameSession, "No game session instance specified.");
m_gameLiftSession = new Aws::GameLift::Server::Model::GameSession(*gameSession);
if (!GridSession::Initialize(carrierDesc))
{
return false;
}
m_myMember = CreateLocalMember(true, true, Mode_Peer);
m_sessionParams = params;
UpdateMatchmakerData();
const auto& properties = gameSession->GetGameProperties();
GameLiftServerSessionReplica::ParamContainer sessionProperties;
for (AZStd::size_t i = 0; i < properties.size(); ++i)
{
GridSessionParam param;
param.m_id = properties[i].GetKey().c_str();
param.m_value = properties[i].GetValue().c_str();
sessionProperties.push_back(param);
}
m_sessionParams.m_numPublicSlots = gameSession->GetMaximumPlayerSessionCount();
//////////////////////////////////////////////////////////////////////////
// start up the session state we will bind it later
AZ_Assert(m_sessionParams.m_numPublicSlots < 0xff && m_sessionParams.m_numPrivateSlots < 0xff, "Can't have more than 255 slots!");
AZ_Assert(m_sessionParams.m_numPublicSlots > 0 || m_sessionParams.m_numPrivateSlots > 0, "You don't have any slots open!");
GameLiftServerSessionReplica* state = CreateReplicaChunk<GameLiftServerSessionReplica>(this);
state->m_numFreePrivateSlots.Set(static_cast<unsigned char>(m_sessionParams.m_numPrivateSlots));
state->m_numFreePublicSlots.Set(static_cast<unsigned char>(m_sessionParams.m_numPublicSlots));
state->m_peerToPeerTimeout.Set(m_sessionParams.m_peerToPeerTimeout);
state->m_flags.Set(m_sessionParams.m_flags);
state->m_topology.Set(m_sessionParams.m_topology);
state->m_params.Set(sessionProperties);
m_state = state;
//////////////////////////////////////////////////////////////////////////
m_sessionId = gameSession->GetGameSessionId().c_str();
SetUpStateMachine();
RequestEvent(SE_HOST);
return true;
}
bool GameLiftServerSession::GameSessionUpdated(const Aws::GameLift::Server::Model::UpdateGameSession& updateGameSession)
{
// Delete previous game session before updating.
if (m_gameLiftSession)
{
delete m_gameLiftSession;
m_gameLiftSession = nullptr;
}
m_gameLiftSession = new Aws::GameLift::Server::Model::GameSession(updateGameSession.GetGameSession());
Aws::GameLift::Server::Model::UpdateReason updateReason = updateGameSession.GetUpdateReason();
switch (updateReason)
{
case Aws::GameLift::Server::Model::UpdateReason::MATCHMAKING_DATA_UPDATED:
return UpdateMatchmakerData();
case Aws::GameLift::Server::Model::UpdateReason::BACKFILL_CANCELLED:
case Aws::GameLift::Server::Model::UpdateReason::BACKFILL_FAILED:
case Aws::GameLift::Server::Model::UpdateReason::BACKFILL_TIMED_OUT:
case Aws::GameLift::Server::Model::UpdateReason::UNKNOWN:
default:
AZ_TracePrintf("GameLift", "GameSessionUpdate matchmaker error reasonname:%s GameSessionData:%s MatchmakerData:%s", Aws::GameLift::Server::Model::UpdateReasonMapper::GetNameForUpdateReason(updateReason).c_str()
, updateGameSession.GetGameSession().GetGameSessionData().c_str(), updateGameSession.GetGameSession().GetMatchmakerData().c_str());
}
return false;
}
bool GameLiftServerSession::UpdateMatchmakerData()
{
const char* matchmakerData = m_gameLiftSession->GetMatchmakerData().c_str();
rapidjson::ParseResult parseResult = m_matchmakerDataDocument.Parse(matchmakerData);
if (!parseResult)
{
AZ_Error("GameLift", parseResult, "Error parsing matchmaker data Error:%s Offset:%u", rapidjson::GetParseError_En(parseResult.Code()), parseResult.Offset());
return false;
}
return true;
}
bool GameLiftServerSession::GetTeamForPlayerFromMatchmakerData(const char* playerId, AZStd::string& teamName)
{
rapidjson::Value& teams = m_matchmakerDataDocument["teams"];
AZ_Assert(teams.IsArray(), "Teams is not array");
for (rapidjson::SizeType teamIndex = 0; teamIndex < teams.Size(); ++teamIndex)
{
rapidjson::Value& players = teams[teamIndex]["players"];
AZ_Assert(players.IsArray(), "Players is not array");
for (rapidjson::SizeType playerIndex = 0; playerIndex < players.Size(); ++playerIndex)
{
if (std::strcmp(players[playerIndex]["playerId"].GetString(), playerId) == 0)
{
teamName = teams[teamIndex]["name"].GetString();
return true;
}
}
}
return false;
}
bool GameLiftServerSession::StartMatchmakingBackfill(AZStd::string& matchmakingTicketId, bool checkForAutoBackfill = true)
{
if (checkForAutoBackfill && m_matchmakerDataDocument.HasMember("autoBackfillTicketId") && m_matchmakerDataDocument["autoBackfillTicketId"].IsString())
{
const char* autoBackfillTicketId = m_matchmakerDataDocument["autoBackfillTicketId"].GetString();
AZ_TracePrintf("GameLift", "Ignoring backfill request when AUTOMATIC backfill is active %s", autoBackfillTicketId);
return false;
}
if (!m_matchmakerDataDocument.HasMember("matchmakingConfigurationArn"))
{
AZ_TracePrintf("GameLift", "Ignoring backfill request when no matchmaking config arn found");
return false;
}
const char* gameSessionId = m_gameLiftSession->GetGameSessionId().c_str();
rapidjson::Value& teams = m_matchmakerDataDocument["teams"];
AZ_Assert(teams.IsArray(), "Teams is not array");
const char* matchmakingConfigurationArn = m_matchmakerDataDocument["matchmakingConfigurationArn"].GetString();
Aws::GameLift::Server::Model::StartMatchBackfillRequest startBackfillRequest;
startBackfillRequest.SetMatchmakingConfigurationArn(matchmakingConfigurationArn);
startBackfillRequest.SetGameSessionArn(gameSessionId);
// Set matchmaking ticket id if provided.
if (!matchmakingTicketId.empty())
{
startBackfillRequest.SetTicketId(matchmakingTicketId.c_str());
}
const std::vector<Aws::GameLift::Server::Model::PlayerSession> &gameLiftPlayerSessions = GetGameLiftPlayerSessions(gameSessionId, Aws::GameLift::Server::Model::PlayerSessionStatus::ACTIVE);
for (const Aws::GameLift::Server::Model::PlayerSession& playerSession : gameLiftPlayerSessions)
{
Aws::GameLift::Server::Model::Player player;
AZ_TracePrintf("GameLift", "Active member found playerId:%s", playerSession.GetPlayerId().c_str());
player.SetPlayerId(playerSession.GetPlayerId());
AZStd::string teamName;
if (GetTeamForPlayerFromMatchmakerData(playerSession.GetPlayerId().c_str(), teamName))
{
player.SetTeam(teamName.c_str());
}
startBackfillRequest.AddPlayer(player);
}
Aws::GameLift::StartMatchBackfillOutcome backfillOutcome = GetGameLiftServerSDKWrapper().lock()->StartMatchBackfill(startBackfillRequest);
if (backfillOutcome.IsSuccess())
{
matchmakingTicketId = backfillOutcome.GetResult().GetTicketId().c_str();
AZ_TracePrintf("GameLift", "Matchmaking Backfill request success ticketId:%s", matchmakingTicketId.c_str());
return true;
}
else
{
AZ_TracePrintf("GameLift", "Matchmaking Backfill request error:%s gamesession:%s config:%s", backfillOutcome.GetError().GetErrorMessage().c_str(), gameSessionId, matchmakingConfigurationArn);
return false;
}
}
bool GameLiftServerSession::StopMatchmakingBackfill(const AZStd::string& matchmakingTicketId)
{
const char* gameSessionId = m_gameLiftSession->GetGameSessionId().c_str();
rapidjson::Value& teams = m_matchmakerDataDocument["teams"];
AZ_Assert(teams.IsArray(), "Teams is not array");
const char* matchmakingConfigurationArn = m_matchmakerDataDocument["matchmakingConfigurationArn"].GetString();
Aws::GameLift::Server::Model::StopMatchBackfillRequest stopBackfillRequest;
stopBackfillRequest.SetTicketId(matchmakingTicketId.c_str());
stopBackfillRequest.SetMatchmakingConfigurationArn(matchmakingConfigurationArn);
stopBackfillRequest.SetGameSessionArn(gameSessionId);
Aws::GameLift::GenericOutcome backfillOutcome = GetGameLiftServerSDKWrapper().lock()->StopMatchBackfill(stopBackfillRequest);
if (backfillOutcome.IsSuccess())
{
AZ_TracePrintf("GameLift", "Matchmaking Backfill stop success matchmakingTicketId:%s", matchmakingTicketId.c_str());
return true;
}
else
{
AZ_TracePrintf("GameLift", "Matchmaking Backfill stop error:%s gamesession:%s config:%s matchmakingTicketId:%s", backfillOutcome.GetError().GetErrorMessage().c_str()
, gameSessionId, matchmakingConfigurationArn, matchmakingTicketId.c_str());
return false;
}
}
GridMember* GameLiftServerSession::CreateLocalMember(bool isHost, bool isInvited, RemotePeerMode peerMode)
{
AZ_Assert(isHost, "GameLiftServerSession can only run as host!");
AZ_Assert(!m_myMember, "We already have added a local member!");
string ip = Utils::GetMachineAddress(m_carrierDesc.m_familyType);
string address = SocketDriverCommon::IPPortToAddressString(ip.c_str(), m_carrierDesc.m_port);
GameLiftServerMemberID myId(address, AZ::Crc32("GameLiftServer"));
GameLiftServerMember* member = CreateReplicaChunk<GameLiftServerMember>(myId, this);
member->SetHost(isHost);
member->SetInvited(isInvited);
member->m_peerMode.Set(peerMode);
return member;
}
void GameLiftServerSession::Shutdown()
{
Aws::GameLift::GenericOutcome outcome = GetGameLiftServerSDKWrapper().lock()->TerminateGameSession();
if (!outcome.IsSuccess())
{
AZ_Warning("GridMate", outcome.IsSuccess(), "GameLift session failed to terminate:%s:%s\n",
outcome.GetError().GetErrorName().c_str(),
outcome.GetError().GetErrorMessage().c_str());
return;
}
if (m_gameLiftSession)
{
delete m_gameLiftSession;
m_gameLiftSession = nullptr;
}
GridSession::Shutdown();
}
GridMember* GameLiftServerSession::CreateRemoteMember(const string& address, ReadBuffer& data, RemotePeerMode peerMode, ConnectionID connId)
{
string playerSessionId;
data.Read(playerSessionId);
Aws::GameLift::GenericOutcome outcome = GetGameLiftServerSDKWrapper().lock()->AcceptPlayerSession(playerSessionId.c_str());
if (!outcome.IsSuccess())
{
AZ_TracePrintf("GameLift", "Failed to connect GameLift player:'%s' with id=%s\n", outcome.GetError().GetErrorName().c_str(), playerSessionId.c_str());
m_carrier->Disconnect(connId);
return nullptr;
}
GameLiftServerMemberID memberId(address, AZ::Crc32(playerSessionId.c_str()));
GameLiftServerMember* member = CreateReplicaChunk<GameLiftServerMember>(connId, memberId, this);
member->m_peerMode.Set(peerMode);
member->SetPlayerSessionId(playerSessionId.c_str());
return member;
}
bool GameLiftServerSession::OnStateCreate(AZ::HSM& sm, const AZ::HSM::Event& e)
{
bool isProcessed = GridSession::OnStateCreate(sm, e);
switch (e.id)
{
case AZ::HSM::EnterEventId:
{
Aws::GameLift::GenericOutcome activationOutcome = GetGameLiftServerSDKWrapper().lock()->ActivateGameSession();
if (!activationOutcome.IsSuccess())
{
AZ_TracePrintf("GridMate", "GameLift session activation failed: %s\n", activationOutcome.GetError().GetErrorMessage().c_str());
RequestEvent(SE_DELETE);
}
else
{
RequestEvent(SE_CREATED);
}
}
return true;
}
return isProcessed;
}
bool GameLiftServerSession::OnStateDelete(AZ::HSM& sm, const AZ::HSM::Event& e)
{
bool isProcessed = GridSession::OnStateDelete(sm, e);
switch (e.id)
{
case AZ::HSM::EnterEventId:
{
RequestEvent(SE_DELETED);
return true;
}
}
return isProcessed;
}
bool GameLiftServerSession::OnStateHostMigrateSession(AZ::HSM& sm, const AZ::HSM::Event& e)
{
(void)sm;
(void)e;
AZ_Assert(false, "Host migration is not supported for GameLift sessions.");
return false;
}
void GameLiftServerSession::RegisterReplicaChunks()
{
ReplicaChunkDescriptorTable::Get().RegisterChunkType<GameLiftServerSessionReplica, GameLiftServerSessionReplica::GameLiftSessionReplicaDesc>();
ReplicaChunkDescriptorTable::Get().RegisterChunkType<GameLiftServerMember, GameLiftServerMember::GameLiftServerMemberDesc>();
ReplicaChunkDescriptorTable::Get().RegisterChunkType<GameLiftServerMemberState>();
}
AZStd::weak_ptr<GameLiftServerSDKWrapper> GameLiftServerSession::GetGameLiftServerSDKWrapper()
{
GameLiftServerService* gmService = static_cast<GameLiftServerService*>(m_service);
return gmService->GetGameLiftServerSDKWrapper();
}
const std::vector<Aws::GameLift::Server::Model::PlayerSession> GameLiftServerSession::GetGameLiftPlayerSessions(const char* gameSessionId, Aws::GameLift::Server::Model::PlayerSessionStatus playerSessionStaus)
{
Aws::GameLift::Server::Model::DescribePlayerSessionsRequest request;
request.SetPlayerSessionStatusFilter(Aws::GameLift::Server::Model::PlayerSessionStatusMapper::GetNameForPlayerSessionStatus(playerSessionStaus));
request.SetLimit(m_gameLiftSession->GetMaximumPlayerSessionCount());
request.SetGameSessionId(gameSessionId);
Aws::GameLift::DescribePlayerSessionsOutcome playerSessionsOutcome = GetGameLiftServerSDKWrapper().lock()->DescribePlayerSessions(request);
if (!playerSessionsOutcome.IsSuccess())
{
AZ_TracePrintf("GameLift", "describe Player Sessions failed error:%s", playerSessionsOutcome.GetError().GetErrorMessage().c_str());
}
return playerSessionsOutcome.GetResult().GetPlayerSessions();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
} // namespace GridMate
#endif // BUILD_GAMELIFT_SERVER
@@ -0,0 +1,111 @@
/*
* 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.
*
*/
#if defined(BUILD_GAMELIFT_CLIENT)
#include <GameLift/Session/GameLiftSessionRequest.h>
#include <GameLift/Session/GameLiftClientService.h>
#include <aws/core/utils/Outcome.h>
// To avoid the warning below
// Semaphore.h(50): warning C4251: 'Aws::Utils::Threading::Semaphore::m_mutex': class 'std::mutex' needs to have dll-interface to be used by clients of class 'Aws::Utils::Threading::Semaphore'
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
#include <aws/gamelift/model/CreateGameSessionRequest.h>
AZ_POP_DISABLE_WARNING
namespace GridMate
{
GameLiftSessionRequest::GameLiftSessionRequest(GameLiftClientService* service, const AZStd::shared_ptr<GameLiftRequestInterfaceContext> context)
: GameLiftSearch(service, context)
{
m_isDone = true;
}
void GameLiftSessionRequest::AbortSearch()
{
SearchDone();
}
bool GameLiftSessionRequest::Initialize()
{
if (!m_isDone)
{
return false;
}
Aws::Vector<Aws::GameLift::Model::GameProperty> gameProperties;
for (AZStd::size_t i = 0; i < m_context->m_requestParams.m_numParams; ++i)
{
Aws::GameLift::Model::GameProperty prop;
prop.SetKey(m_context->m_requestParams.m_params[i].m_id.c_str());
prop.SetValue(m_context->m_requestParams.m_params[i].m_value.c_str());
gameProperties.push_back(prop);
}
Aws::GameLift::Model::CreateGameSessionRequest request;
m_context->m_requestParams.m_useFleetId ? request.SetFleetId(m_context->m_requestParams.m_fleetId.c_str())
: request.SetAliasId(m_context->m_requestParams.m_aliasId.c_str());
request.WithMaximumPlayerSessionCount(m_context->m_requestParams.m_numPublicSlots + m_context->m_requestParams.m_numPrivateSlots)
.WithName(m_context->m_requestParams.m_instanceName.c_str())
.WithGameProperties(gameProperties);
m_createGameSessionOutcomeCallable = m_context->m_gameLiftClient.lock()->CreateGameSessionCallable(request);
m_isDone = false;
return true;
}
void GameLiftSessionRequest::Update()
{
if (m_isDone || !m_createGameSessionOutcomeCallable.valid())
{
return;
}
if (m_createGameSessionOutcomeCallable.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready)
{
Aws::GameLift::Model::CreateGameSessionOutcome result = m_createGameSessionOutcomeCallable.get();
if (!result.IsSuccess())
{
AZ_TracePrintf("GameLift", "Session creation failed with error: %s\n", result.GetError().GetMessage().c_str());
SearchDone();
return;
}
auto gameSession = result.GetResult().GetGameSession();
GameLiftSearchInfo info;
info.m_fleetId = gameSession.GetFleetId().c_str();
info.m_sessionId = gameSession.GetGameSessionId().c_str();
info.m_numFreePublicSlots = gameSession.GetMaximumPlayerSessionCount() - gameSession.GetCurrentPlayerSessionCount();
info.m_numUsedPublicSlots = gameSession.GetCurrentPlayerSessionCount();
info.m_numPlayers = gameSession.GetCurrentPlayerSessionCount();
auto& properties = gameSession.GetGameProperties();
for (auto& prop : properties)
{
info.m_params[info.m_numParams].m_id = prop.GetKey().c_str();
info.m_params[info.m_numParams].m_value = prop.GetValue().c_str();
++info.m_numParams;
}
m_results.push_back(info);
SearchDone();
}
}
} // namespace GridMate
#endif // BUILD_GAMELIFT_CLIENT