Initial Commit - Lesson 31 (Commit #1)

This commit is contained in:
Norman Lansing
2026-02-24 22:39:26 -05:00
commit 9591e7f503
4631 changed files with 1019212 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
using UnrealBuildTool;
public class GameLiftMetrics : ModuleRules
{
public GameLiftMetrics(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
bEnableExceptions = true;
bUseRTTI = false;
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"GameLiftServerSDK",
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Core",
"Engine",
"Sockets",
"Networking",
}
);
}
}

View File

@@ -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 "GameLiftMetrics.h"
#include "GameLiftMetricsTypes.h"
#include "GameLiftMetricsConfig.h"
#include "UnrealStatCollector.h"
#include "TickableCollector.h"
#include "GameLiftServerSDK.h"
#include "aws/gamelift/server/GameLiftServerAPI.h"
#include "Sockets.h"
#include "SocketSubsystem.h"
#include "IPAddress.h"
#include "Common/UdpSocketBuilder.h"
#include <aws/gamelift/metrics/GlobalMetricsProcessor.h>
#if PLATFORM_LINUX
#include <unistd.h>
#endif
FString FGameLiftMetricsModule::CurrentGameSessionId;
FThreadSafeCounter FGameLiftMetricsModule::CurrentPlayerCount(0);
FGameLiftMetricsModule& FGameLiftMetricsModule::Load()
{
return FModuleManager::LoadModuleChecked<FGameLiftMetricsModule>(FName("GameLiftMetrics"));
}
FGameLiftMetricsModule& FGameLiftMetricsModule::Get()
{
return FModuleManager::GetModuleChecked<FGameLiftMetricsModule>(FName("GameLiftMetrics"));
}
FGameLiftMetricsModule* FGameLiftMetricsModule::GetPtr()
{
return FModuleManager::GetModulePtr<FGameLiftMetricsModule>(FName("GameLiftMetrics"));
}
#if WITH_GAMELIFT_METRICS
namespace
{
const TCHAR* GAMELIFT_METRICS_FLEET_ID = TEXT("GAMELIFT_SDK_FLEET_ID");
const TCHAR* GAMELIFT_METRICS_PROCESS_ID = TEXT("GAMELIFT_SDK_PROCESS_ID");
GAMELIFT_METRICS_DECLARE_GAUGE(MetricMaxPlayers, "server_max_players", Aws::GameLift::Metrics::Server, Aws::GameLift::Metrics::SampleAll());
GAMELIFT_METRICS_DEFINE_GAUGE(MetricMaxPlayers);
GAMELIFT_METRICS_DECLARE_GAUGE(MetricPlayers, "server_players", Aws::GameLift::Metrics::Server, Aws::GameLift::Metrics::SampleAll());
GAMELIFT_METRICS_DEFINE_GAUGE(MetricPlayers);
GAMELIFT_METRICS_DECLARE_COUNTER(MetricServerCrashes, "game_server_crashes", Aws::GameLift::Metrics::Server, Aws::GameLift::Metrics::SampleAll());
GAMELIFT_METRICS_DEFINE_COUNTER(MetricServerCrashes);
}
void FGameLiftMetricsModule::StartupModule() {}
void FGameLiftMetricsModule::ShutdownModule()
{
// Terminate here just in case the user forgot.
Terminate();
}
void FGameLiftMetricsModule::StartMetricsCollector()
{
FPlatformMisc::SetCrashHandler(&FGameLiftMetricsModule::CrashHandler);
bMetricsRunning = true;
auto *Module = FGameLiftMetricsModule::GetPtr();
if (!Module)
{
UE_LOG(LogGameLiftMetrics, Error, TEXT("GameLiftMetrics module no longer loaded."));
return;
}
/*
* Begin ticking the collector.
*/
const bool bComputeFallbackGameThreadTime = !STATS;
Module->Collector.Reset(new FTickableCollector(bComputeFallbackGameThreadTime));
/*
* Create stats collector.
*/
if (STATS)
{
Module->UnrealStatCollector = TSharedPtr<FUnrealStatCollector>(new FUnrealStatCollector());
Module->UnrealStatCollector->Subscribe();
}
UE_LOG(LogGameLiftMetrics, Log, TEXT("GameLift metrics initialized successfully."));
}
void FGameLiftMetricsModule::Initialize()
{
if (GIsEditor) return;
// Initialize GameLift Server SDK metrics
auto& ServerSDKModule = FModuleManager::LoadModuleChecked<FGameLiftServerSDKModule>("GameLiftServerSDK");
auto InitResult = ServerSDKModule.InitMetrics();
if (!InitResult.IsSuccess())
{
UE_LOG(LogGameLiftMetrics, Error, TEXT("Failed to initialize GameLift metrics: %s"), *InitResult.GetError().m_errorMessage);
return;
}
StartMetricsCollector();
}
void FGameLiftMetricsModule::Initialize(const FMetricsParameters& metricsParameters)
{
if (GIsEditor) return;
// Initialize GameLift Server SDK metrics with parameters
auto& ServerSDKModule = FModuleManager::LoadModuleChecked<FGameLiftServerSDKModule>("GameLiftServerSDK");
auto InitResult = ServerSDKModule.InitMetrics(metricsParameters);
if (!InitResult.IsSuccess())
{
UE_LOG(LogGameLiftMetrics, Error, TEXT("Failed to initialize GameLift metrics with parameters: %s"), *InitResult.GetError().m_errorMessage);
return;
}
StartMetricsCollector();
}
void FGameLiftMetricsModule::Terminate()
{
if (GIsEditor) { return; }
if (UnrealStatCollector)
{
UnrealStatCollector->Unsubscribe();
UnrealStatCollector.Reset();
}
Collector.Reset();
UE_LOG(LogGameLiftMetrics, Log, TEXT("GameLift metrics terminated successfully."));
}
void FGameLiftMetricsModule::OnStartGameSession(const Aws::GameLift::Server::Model::GameSession& Session)
{
if (GIsEditor) { return; }
// Store the session ID regardless of metrics being enabled
CurrentGameSessionId = FString(Session.GetGameSessionId());
CurrentPlayerCount.Set(0);
const UGameLiftMetricsConfig& Config = UGameLiftMetricsConfig::Get();
if (!Config.bEnableMetrics)
{
UE_LOG(LogGameLiftMetrics, Verbose, TEXT("Metrics disabled: Skipping OnStartGameSession metrics"));
return;
}
GAMELIFT_METRICS_SET(MetricMaxPlayers, Session.GetMaximumPlayerSessionCount());
GAMELIFT_METRICS_RESET(MetricPlayers);
}
void FGameLiftMetricsModule::OnAcceptPlayerSession()
{
if (GIsEditor) { return; }
// Always track player count even if metrics are disabled
int32 NewPlayerCount = CurrentPlayerCount.Increment();
const UGameLiftMetricsConfig& Config = UGameLiftMetricsConfig::Get();
if (!Config.bEnableMetrics)
{
return;
}
GAMELIFT_METRICS_SET(MetricPlayers, NewPlayerCount);
}
void FGameLiftMetricsModule::OnRemovePlayerSession()
{
if (GIsEditor) { return; }
// Always track player count even if metrics are disabled
int32 NewPlayerCount = CurrentPlayerCount.Decrement();
if (NewPlayerCount < 0)
{
CurrentPlayerCount.Set(0);
NewPlayerCount = 0;
}
const UGameLiftMetricsConfig& Config = UGameLiftMetricsConfig::Get();
if (!Config.bEnableMetrics)
{
return;
}
GAMELIFT_METRICS_SET(MetricPlayers, NewPlayerCount);
}
void FGameLiftMetricsModule::ReEmitMetrics()
{
if (GIsEditor) { return; }
const UGameLiftMetricsConfig& Config = UGameLiftMetricsConfig::Get();
if (!Config.bEnableMetrics)
{
return;
}
GAMELIFT_METRICS_SET(MetricPlayers, CurrentPlayerCount.GetValue());
}
void FGameLiftMetricsModule::CrashHandler(const FGenericCrashContext& GenericContext)
{
UE_LOG(LogGameLiftMetrics, Log, TEXT("Server crashed, CrashHandler being called..."));
const UGameLiftMetricsConfig& Config = UGameLiftMetricsConfig::Get();
if (!Config.bEnableMetrics)
{
UE_LOG(LogGameLiftMetrics, Log, TEXT("Metrics disabled: Skipping crash metrics"));
return;
}
if (!CurrentGameSessionId.IsEmpty())
{
UE_LOG(LogGameLiftMetrics, Log, TEXT("Incrementing crash metrics for session: %s"), *CurrentGameSessionId);
GAMELIFT_METRICS_INCREMENT(MetricServerCrashes);
GAMELIFT_METRICS_TAG_SET(MetricServerCrashes, "game_session_id", TCHAR_TO_UTF8(*CurrentGameSessionId));
UE_LOG(LogGameLiftMetrics, Log, TEXT("Crash metrics incremented"));
// Force process the metrics
UE_LOG(LogGameLiftMetrics, Log, TEXT("Sending crash metrics..."));
Aws::GameLift::Metrics::MetricsProcess();
// Small sleep to allow the metrics to be sent
UE_LOG(LogGameLiftMetrics, Log, TEXT("Waiting for metrics to send..."));
FPlatformProcess::Sleep(0.2f);
UE_LOG(LogGameLiftMetrics, Log, TEXT("Crash metrics incremented and processed"));
}
else
{
UE_LOG(LogGameLiftMetrics, Warning, TEXT("Server crashed with no active game session"));
}
}
bool FGameLiftMetricsModule::SetMetricsEnabled(bool bEnable)
{
if (GIsEditor)
{
UE_LOG(LogGameLiftMetrics, Warning, TEXT("Cannot enable/disable metrics in editor - only applicable in game server"));
return false;
}
// If the state is already what's requested, no change needed
if (bEnable == bMetricsRunning)
{
UE_LOG(LogGameLiftMetrics, Log, TEXT("Metrics already %s, no change needed"),
bEnable ? TEXT("enabled") : TEXT("disabled"));
return true;
}
// Get current configuration and check current state
UGameLiftMetricsConfig* Config = const_cast<UGameLiftMetricsConfig*>(&UGameLiftMetricsConfig::Get());
if (bEnable)
{
// Enable metrics
UE_LOG(LogGameLiftMetrics, Log, TEXT("Enabling GameLift metrics at runtime"));
// Initialize with default parameters
Initialize();
// Flag is set inside Initialize method based on success
return bMetricsRunning;
}
else
{
// Disable metrics
UE_LOG(LogGameLiftMetrics, Log, TEXT("Disabling GameLift metrics at runtime"));
// Terminate metrics system
Terminate();
Config->bEnableMetrics = false;
bMetricsRunning = false;
return true;
}
}
bool FGameLiftMetricsModule::IsMetricsEnabled() const
{
return bMetricsRunning;
}
#else
void FGameLiftMetricsModule::StartupModule() {}
void FGameLiftMetricsModule::ShutdownModule() {}
void FGameLiftMetricsModule::Initialize() {}
void FGameLiftMetricsModule::Initialize(const FMetricsParameters& metricsParameters) {}
void FGameLiftMetricsModule::Terminate() {}
void FGameLiftMetricsModule::OnStartGameSession(const Aws::GameLift::Server::Model::GameSession& Session) {}
void FGameLiftMetricsModule::OnAcceptPlayerSession() {}
void FGameLiftMetricsModule::OnRemovePlayerSession() {}
void FGameLiftMetricsModule::ReEmitMetrics() {}
bool FGameLiftMetricsModule::SetMetricsEnabled(bool bEnable) { return false; }
bool FGameLiftMetricsModule::IsMetricsEnabled() const { return false; }
#endif // WITH_GAMELIFT_METRICS
IMPLEMENT_MODULE(FGameLiftMetricsModule, GameLiftMetrics)

View File

@@ -0,0 +1,21 @@
/*
* 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 "GameLiftMetricsConfig.h"
const UGameLiftMetricsConfig& UGameLiftMetricsConfig::Get()
{
UGameLiftMetricsConfig* CDO = Cast<UGameLiftMetricsConfig>(StaticClass()->GetDefaultObject());
check(IsValid(CDO));
return *CDO;
}

View File

@@ -0,0 +1,15 @@
/*
* 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 "GameLiftMetricsTypes.h"
DEFINE_LOG_CATEGORY(LogGameLiftMetrics);

View File

@@ -0,0 +1,42 @@
/*
* 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 "MemoryStats.h"
#include "Engine.h"
#include "EngineStats.h"
namespace Aws {
namespace GameLift {
namespace Metrics {
GAMELIFT_METRICS_DEFINE_GAUGE(MetricMemoryPhysicalTotal);
GAMELIFT_METRICS_DEFINE_GAUGE(MetricMemoryPhysicalAvailable);
GAMELIFT_METRICS_DEFINE_GAUGE(MetricMemoryPhysicalUsed);
GAMELIFT_METRICS_DEFINE_GAUGE(MetricMemoryVirtualTotal);
GAMELIFT_METRICS_DEFINE_GAUGE(MetricMemoryVirtualAvailable);
GAMELIFT_METRICS_DEFINE_GAUGE(MetricMemoryVirtualUsed);
}
}
}
void FMemoryStats::Collect()
{
auto MemoryStats = FPlatformMemory::GetStats();
GAMELIFT_METRICS_SET(Aws::GameLift::Metrics::MetricMemoryPhysicalTotal, MemoryStats.TotalPhysical);
GAMELIFT_METRICS_SET(Aws::GameLift::Metrics::MetricMemoryPhysicalAvailable, MemoryStats.AvailablePhysical);
GAMELIFT_METRICS_SET(Aws::GameLift::Metrics::MetricMemoryPhysicalUsed, MemoryStats.UsedPhysical);
GAMELIFT_METRICS_SET(Aws::GameLift::Metrics::MetricMemoryVirtualTotal, MemoryStats.TotalVirtual);
GAMELIFT_METRICS_SET(Aws::GameLift::Metrics::MetricMemoryVirtualAvailable, MemoryStats.AvailableVirtual);
GAMELIFT_METRICS_SET(Aws::GameLift::Metrics::MetricMemoryVirtualUsed, MemoryStats.UsedVirtual);
}

View File

@@ -0,0 +1,35 @@
/*
* 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 "GameLiftMetricsTypes.h"
namespace Aws {
namespace GameLift {
namespace Metrics {
GAMELIFT_METRICS_DECLARE_GAUGE(MetricMemoryPhysicalTotal, "server_mem_physical_total", Aws::GameLift::Metrics::Server, Aws::GameLift::Metrics::SampleAll());
GAMELIFT_METRICS_DECLARE_GAUGE(MetricMemoryPhysicalAvailable, "server_mem_physical_available", Aws::GameLift::Metrics::Server, Aws::GameLift::Metrics::SampleAll());
GAMELIFT_METRICS_DECLARE_GAUGE(MetricMemoryPhysicalUsed, "server_mem_physical_used", Aws::GameLift::Metrics::Server, Aws::GameLift::Metrics::SampleAll());
GAMELIFT_METRICS_DECLARE_GAUGE(MetricMemoryVirtualTotal, "server_mem_virtual_total", Aws::GameLift::Metrics::Server, Aws::GameLift::Metrics::SampleAll());
GAMELIFT_METRICS_DECLARE_GAUGE(MetricMemoryVirtualAvailable, "server_mem_virtual_available", Aws::GameLift::Metrics::Server, Aws::GameLift::Metrics::SampleAll());
GAMELIFT_METRICS_DECLARE_GAUGE(MetricMemoryVirtualUsed, "server_mem_virtual_used", Aws::GameLift::Metrics::Server, Aws::GameLift::Metrics::SampleAll());
}
}
}
class FMemoryStats
{
public:
void Collect();
};

View File

@@ -0,0 +1,106 @@
/*
* 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 "NetworkStats.h"
#include "Engine/NetDriver.h"
#include "Engine.h"
namespace Aws {
namespace GameLift {
namespace Metrics {
GAMELIFT_METRICS_DEFINE_GAUGE(MetricConnectionCount);
GAMELIFT_METRICS_DEFINE_COUNTER(MetricBytesIn);
GAMELIFT_METRICS_DEFINE_COUNTER(MetricBytesOut);
GAMELIFT_METRICS_DEFINE_COUNTER(MetricPacketsIn);
GAMELIFT_METRICS_DEFINE_COUNTER(MetricPacketsInLost);
GAMELIFT_METRICS_DEFINE_COUNTER(MetricPacketsOut);
GAMELIFT_METRICS_DEFINE_COUNTER(MetricPacketsOutLost);
}
}
}
FNetworkStats::FNetworkStats()
{
if (auto NetDriver = GetNetDriver())
{
LastInBytes = NetDriver->InTotalBytes;
LastOutBytes = NetDriver->OutTotalBytes;
LastInPackets = NetDriver->InTotalPackets;
LastOutPackets = NetDriver->OutTotalPackets;
LastInPacketsLost = NetDriver->InTotalPacketsLost;
LastOutPacketsLost = NetDriver->OutTotalPacketsLost;
}
}
void FNetworkStats::Collect()
{
if (auto NetDriver = GetNetDriver())
{
GAMELIFT_METRICS_ADD(Aws::GameLift::Metrics::MetricBytesIn, NetDriver->InTotalBytes - LastInBytes);
GAMELIFT_METRICS_ADD(Aws::GameLift::Metrics::MetricBytesOut, NetDriver->OutTotalBytes - LastOutBytes);
GAMELIFT_METRICS_ADD(Aws::GameLift::Metrics::MetricPacketsIn, NetDriver->InTotalPackets - LastInPackets);
GAMELIFT_METRICS_ADD(Aws::GameLift::Metrics::MetricPacketsOut, NetDriver->OutTotalPackets - LastOutPackets);
GAMELIFT_METRICS_ADD(Aws::GameLift::Metrics::MetricPacketsInLost, NetDriver->InTotalPacketsLost - LastInPacketsLost);
GAMELIFT_METRICS_ADD(Aws::GameLift::Metrics::MetricPacketsOutLost, NetDriver->OutTotalPacketsLost - LastOutPacketsLost);
LastInBytes = NetDriver->InTotalBytes;
LastOutBytes = NetDriver->OutTotalBytes;
LastInPackets = NetDriver->InTotalPackets;
LastOutPackets = NetDriver->OutTotalPackets;
LastInPacketsLost = NetDriver->InTotalPacketsLost;
LastOutPacketsLost = NetDriver->OutTotalPacketsLost;
GAMELIFT_METRICS_SET(Aws::GameLift::Metrics::MetricConnectionCount, NetDriver->ClientConnections.Num());
}
else
{
UE_LOG(LogGameLiftMetrics, Error, TEXT("Net driver is null. Cannot log network metrics."));
}
}
const UNetDriver* FNetworkStats::GetNetDriver() const
{
UEngine* Engine = GEngine;
if (!IsValid(Engine))
{
UE_LOG(LogGameLiftMetrics, Error, TEXT("Engine pointer is not valid when retrieving net driver."));
return nullptr;
}
const auto& WorldContexts = Engine->GetWorldContexts();
if (WorldContexts.Num() == 0)
{
// no worlds are loaded
// generally can only be true before everything is fully initialized
UE_LOG(LogGameLiftMetrics, Error, TEXT("No world is loaded when retrieving net driver."));
return nullptr;
}
// Only one world context should be present outside Editor.
// (editor has multiple)
// Refer to FWorldContext comment in Engine.h
const FWorldContext& WorldContext = WorldContexts[0];
check(WorldContext.WorldType == EWorldType::Game);
// In theory multiple net drivers might be present.
// One will be for gameplay but additional ones might be opened for non-gameplay traffic.
// Might be null during initialisation.
if (WorldContext.ActiveNetDrivers.Num() == 0)
{
UE_LOG(LogGameLiftMetrics, Error, TEXT("No active net drivers. Cannot acquire network metrics."));
return nullptr;
}
return WorldContext.ActiveNetDrivers[0].NetDriver;
}

View File

@@ -0,0 +1,53 @@
/*
* 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 "GameLiftMetricsTypes.h"
class UNetDriver;
namespace Aws {
namespace GameLift {
namespace Metrics {
GAMELIFT_METRICS_DECLARE_GAUGE(MetricConnectionCount, "server_connections", Aws::GameLift::Metrics::Server, Aws::GameLift::Metrics::SampleAll());
GAMELIFT_METRICS_DECLARE_COUNTER(MetricBytesIn, "server_bytes_in", Aws::GameLift::Metrics::Server, Aws::GameLift::Metrics::SampleAll());
GAMELIFT_METRICS_DECLARE_COUNTER(MetricBytesOut, "server_bytes_out", Aws::GameLift::Metrics::Server, Aws::GameLift::Metrics::SampleAll());
GAMELIFT_METRICS_DECLARE_COUNTER(MetricPacketsIn, "server_packets_in", Aws::GameLift::Metrics::Server, Aws::GameLift::Metrics::SampleAll());
GAMELIFT_METRICS_DECLARE_COUNTER(MetricPacketsInLost, "server_packets_in_lost", Aws::GameLift::Metrics::Server, Aws::GameLift::Metrics::SampleAll());
GAMELIFT_METRICS_DECLARE_COUNTER(MetricPacketsOut, "server_packets_out", Aws::GameLift::Metrics::Server, Aws::GameLift::Metrics::SampleAll());
GAMELIFT_METRICS_DECLARE_COUNTER(MetricPacketsOutLost, "server_packets_out_lost", Aws::GameLift::Metrics::Server, Aws::GameLift::Metrics::SampleAll());
}
}
}
class FNetworkStats
{
public:
FNetworkStats();
void Collect();
private:
const UNetDriver* GetNetDriver() const;
private:
int32 LastInBytes = 0;
int32 LastOutBytes = 0;
int32 LastInPackets = 0;
int32 LastOutPackets = 0;
int32 LastInPacketsLost = 0;
int32 LastOutPacketsLost = 0;
};

View File

@@ -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.
*
*/
#include "TickableCollector.h"
#include "TimeStats.h"
#include "GameLiftMetrics.h"
#include "Misc/App.h"
namespace Aws {
namespace GameLift {
namespace Metrics {
GAMELIFT_METRICS_DEFINE_GAUGE(MetricServerUp);
}
}
}
#if WITH_GAMELIFT_METRICS
void FTickableCollector::Tick(float)
{
GAMELIFT_METRICS_SET(Aws::GameLift::Metrics::MetricServerUp, 1);
NetworkStats.Collect();
MemoryStats.Collect();
const double DeltaTime = FApp::GetDeltaTime();
GAMELIFT_METRICS_SET_SEC(Aws::GameLift::Metrics::MetricGameDeltaTime, DeltaTime);
if (bComputeFallbackGameThreadTime)
{
const double EstimatedGameThreadTime = DeltaTime - FApp::GetIdleTime();
GAMELIFT_METRICS_SET_SEC(Aws::GameLift::Metrics::MetricGameTickTime, EstimatedGameThreadTime);
}
// Re-emit specific metrics so that they don't go stale.
// Grafana reports no data for the metric after 5 minutes of it being stale.
FGameLiftMetricsModule::Get().ReEmitMetrics();
Aws::GameLift::Metrics::MetricsProcess();
}
#else
void FTickableCollector::Tick(float) {}
#endif // WITH_GAMELIFT_METRICS
TStatId FTickableCollector::GetStatId() const
{
return TStatId{};
}

View File

@@ -0,0 +1,53 @@
/*
* 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 "CoreMinimal.h"
#include "Tickable.h"
#include "GameLiftMetricsTypes.h"
#include "NetworkStats.h"
#include "MemoryStats.h"
namespace Aws {
namespace GameLift {
namespace Metrics {
GAMELIFT_METRICS_DECLARE_GAUGE(MetricServerUp, "server_up", Aws::GameLift::Metrics::Server, Aws::GameLift::Metrics::SampleAll());
}
}
}
class FTickableCollector : public FTickableGameObject
{
public:
FTickableCollector(bool bComputeFallbackGameThreadTime)
: bComputeFallbackGameThreadTime(bComputeFallbackGameThreadTime) {}
// FTickableGameObject
virtual void Tick(float) override;
virtual bool IsTickable() const override { return true; }
virtual bool IsTickableWhenPaused() const override { return true; }
virtual bool IsTickableInEditor() const override { return false; }
virtual ETickableTickType GetTickableTickType() const override { return ETickableTickType::Always; }
virtual TStatId GetStatId() const override;
// ~FTickableGameObject
private:
bool bComputeFallbackGameThreadTime = false;
FNetworkStats NetworkStats;
FMemoryStats MemoryStats;
};

View File

@@ -0,0 +1,24 @@
/*
* 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 "TimeStats.h"
namespace Aws {
namespace GameLift {
namespace Metrics {
GAMELIFT_METRICS_DEFINE_TIMER(MetricGameDeltaTime);
GAMELIFT_METRICS_DEFINE_TIMER(MetricGameTickTime);
GAMELIFT_METRICS_DEFINE_TIMER(MetricWorldTickTime);
}
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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 "GameLiftMetricsTypes.h"
namespace Aws {
namespace GameLift {
namespace Metrics {
/**
* Server time-step in milliseconds.
*
* This is the time between consecutive ticks.
*
* When the server is under performing, this will exceed the target tick rate,
* otherwise it should hover in its vicinity.
*
* To compute the tick rate, compute the reciprocal: (1000 / server_delta_time)
*/
GAMELIFT_METRICS_DECLARE_TIMER(
MetricGameDeltaTime, "server_delta_time",
Aws::GameLift::Metrics::Server,
Aws::GameLift::Metrics::SampleAll(),
Aws::GameLift::Metrics::Percentiles(0.5, 0.9, 0.95)
);
/**
* The time it takes to process a single tick.
*
* A subset of delta time.
* server_tick_time <= server_delta_time at all times. Typically (much) smaller.
*
* This is the time it takes to actually process the tick. The remaining
* time-step duration is spent waiting for the next tick.
*/
GAMELIFT_METRICS_DECLARE_TIMER(
MetricGameTickTime, "server_tick_time",
Aws::GameLift::Metrics::Server,
Aws::GameLift::Metrics::SampleAll(),
Aws::GameLift::Metrics::Percentiles(0.5, 0.9, 0.95)
);
/**
* The time it takes to process the world update.
*
* A subset of tick time.
*
* This is the time it takes to process the Actors and Blueprints in the
* currently loaded Map. The remainder of server_tick_time is spent in
* other engine subsystems like network replication or physics.
*
* Only available in Debug or Development builds, or Shipping builds with
* USE_FORCE_STATS=1.
*/
GAMELIFT_METRICS_DECLARE_TIMER(
MetricWorldTickTime,
"server_world_tick_time",
Aws::GameLift::Metrics::Server,
Aws::GameLift::Metrics::SampleAll(),
Aws::GameLift::Metrics::Percentiles(0.5, 0.9, 0.95)
);
}
}
}

View File

@@ -0,0 +1,136 @@
/*
* 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 "UnrealStatCollector.h"
#include "Runtime/Launch/Resources/Version.h"
#include "TimeStats.h"
#include "Async/TaskGraphInterfaces.h"
#include "Stats/Stats.h"
#include "Stats/StatsData.h"
#include "EngineStats.h"
#if STATS
namespace
{
#if ENGINE_MAJOR_VERSION >= 5
void IncrementStatsRefCount()
{
StatsPrimaryEnableAdd();
}
void DecrementStatsRefCount()
{
StatsPrimaryEnableSubtract();
}
#else
void IncrementStatsRefCount()
{
StatsMasterEnableAdd();
}
void DecrementStatsRefCount()
{
StatsMasterEnableSubtract();
}
#endif
}
void FUnrealStatCollector::Subscribe()
{
IncrementStatsRefCount();
/*
* From 5.2.0 onwards, UE_STATS_THREAD_AS_PIPE is undefined and the only way to
* access the stat system is via the new API.
*
* Between 5.0.0 and 5.2.0, UE_STATS_THREAD_AS_PIPE enables new API. If it is 0,
* the old API can be used.
*
* See UE engine source commits 10b5e05d00df4f23d3b75f6af5ec08f8ae0e8618
* 327a94d1a4e021714f2c3ca820fe9ed5606465bf
* d48f6660b807c7377926cffd12020645f91772f8
*
* All references to UE_STATS_THREAD_AS_PIPE are removed in
* d48f6660b807c7377926cffd12020645f91772f8.
*/
#if ENGINE_MAJOR_VERSION >= 5 && (ENGINE_MINOR_VERSION >= 2 || UE_STATS_THREAD_AS_PIPE)
{
auto& StatsState = FStatsThreadState::GetLocalState();
StatsState.NewFrameDelegate.AddSP(this, &FUnrealStatCollector::OnNewFrame);
}
#else
FSimpleDelegateGraphTask::CreateAndDispatchWhenReady
(
FSimpleDelegateGraphTask::FDelegate::CreateLambda(
[WeakCollector = TWeakPtr<FUnrealStatCollector>(this->AsShared())]() {
if (!WeakCollector.IsValid()) { return; }
auto& StatsState = FStatsThreadState::GetLocalState();
StatsState.NewFrameDelegate.AddSP(WeakCollector.Pin().Get(), &FUnrealStatCollector::OnNewFrame);
}
),
TStatId{},
nullptr,
/*
* StatsThread is deprecated since 5.0
*
* Need to verify post-5.0 behaviour but:
* - 4.27 to 5.0 should use the current method to subscribe to NewFrameDelegate
* - 5.0 on can subscribe from game thread directly. (See FThreadStats::bUseThreadAsPipe)
*/
ENamedThreads::StatsThread
);
#endif // UE_STATS_THREAD_AS_PIPE
}
void FUnrealStatCollector::Unsubscribe()
{
DecrementStatsRefCount();
}
void FUnrealStatCollector::OnNewFrame(int64 Frame)
{
TArray<FStatMessage> OutStats;
auto& Stats = FStatsThreadState::GetLocalState();
Stats.GetInclusiveAggregateStackStats(Frame, OutStats);
for (auto& Message : OutStats)
{
// GetShortName() is expensive and we're calling it for every stat.
// In practice: we'll stick filter by GetRawName first and stick things in a hashmap
// might also be able to compare FName indices
FName ShortName = Message.NameAndInfo.GetShortName();
// Robustness: we can check the value type from message before blindly grabbing duration (or other types)
// Consult EngineStats.h for stat names
// (There's also many other stats in other subsystems.)
if (ShortName == TEXT("STAT_GameEngineTick"))
{
GAMELIFT_METRICS_SET_MS(Aws::GameLift::Metrics::MetricGameTickTime, FPlatformTime::ToMilliseconds(Message.GetValue_Duration()));
}
else if (ShortName == TEXT("STAT_WorldTickTime"))
{
GAMELIFT_METRICS_SET_MS(Aws::GameLift::Metrics::MetricWorldTickTime, FPlatformTime::ToMilliseconds(Message.GetValue_Duration()));
}
}
}
#else
void FUnrealStatCollector::Subscribe() {}
void FUnrealStatCollector::Unsubscribe() {}
void FUnrealStatCollector::OnNewFrame(int64 Frame) {}
#endif // STATS

View File

@@ -0,0 +1,31 @@
/*
* 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 "CoreMinimal.h"
class FUnrealStatCollector : public TSharedFromThis<FUnrealStatCollector>
{
public:
/**
* Subscribes to NewFrameDelegate in Unreal stats system.
*/
void Subscribe();
/**
* Unsubscribes from NewFrameDelegate in Unreal stats system.
*/
void Unsubscribe();
void OnNewFrame(int64 Frame);
};

View File

@@ -0,0 +1,125 @@
/*
* 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 "CoreMinimal.h"
#include "Modules/ModuleManager.h"
#include "GameLiftMetricsTypes.h"
#include <aws/gamelift/metrics/GameLiftMetrics.h>
// Forward declarations
struct FMetricsParameters;
class FSocket;
class FUnrealStatCollector;
class FTickableCollector;
class UGameLiftMetricsConfig;
namespace Aws {
namespace GameLift {
namespace Server {
namespace Model {
class GameSession;
} // Model
} // Server
} // GameLift
} // Aws
class GAMELIFTMETRICS_API FGameLiftMetricsModule : public IModuleInterface
{
public:
// IModuleInterface
virtual void StartupModule() override;
virtual void ShutdownModule() override;
virtual bool SupportsDynamicReloading() override { return false; }
virtual bool SupportsAutomaticShutdown() override { return true; }
// ~IModuleInterface
static FGameLiftMetricsModule& Load();
static FGameLiftMetricsModule& Get();
static FGameLiftMetricsModule* GetPtr();
/**
* Initialize GameLift Metrics using default configuration. These
* may be overriden by environment variables.
*/
void Initialize();
/**
* Initialize GameLift Metrics with metrics parameters.
*
* @param metricsParameters Metrics parameters to use.
*/
void Initialize(const FMetricsParameters& metricsParameters);
/**
* Terminate the GameLift Metrics SDK.
*/
void Terminate();
/**
* Enable or disable metrics at runtime.
* If metrics were previously disabled, this will initialize the metrics system.
* If metrics were previously enabled, this will terminate the metrics system.
*
* @param bEnable Whether to enable or disable metrics
* @return True if the operation was successful
*/
bool SetMetricsEnabled(bool bEnable);
/**
* Check if metrics are currently enabled and running
*
* @return True if metrics are currently enabled and running
*/
bool IsMetricsEnabled() const;
/**
* Records a new game session.
* Call when starting a new session from the FProcessSettings::OnStartGameSession callback.
*/
void OnStartGameSession(const Aws::GameLift::Server::Model::GameSession&);
/**
* Records a new player session.
*
* Call when accepting a player session.
*/
void OnAcceptPlayerSession();
/**
* Records a removed player session.
*
* Call when removing a player session.
*/
void OnRemovePlayerSession();
private:
void StartMetricsCollector();
TSharedPtr<FSocket> Socket;
TUniquePtr<FTickableCollector> Collector;
TSharedPtr<FUnrealStatCollector> UnrealStatCollector;
static FString CurrentGameSessionId;
static FThreadSafeCounter CurrentPlayerCount;
bool bMetricsRunning = false; // Tracks if metrics are currently running
static void CrashHandler(const FGenericCrashContext& Context);
void ReEmitMetrics();
friend class FTickableCollector;
};

View File

@@ -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.
*
*/
#pragma once
#include "CoreMinimal.h"
#include "GameLiftMetricsConfig.generated.h"
/**
* GameLift Metrics module configuration.
*
* Can be configured in `DefaultGame.ini`:
* ```
* [/Script/GameLiftMetricsUnreal.GameLiftMetricsConfig]
* CollectorHost = "127.0.0.1"
* CollectorPort = 8125
* MaxPacketSize = 2048
* CaptureIntervalSeconds = 10
* bEnableMetrics = true
* ```
*/
UCLASS(config=Game, defaultconfig)
class GAMELIFTMETRICS_API UGameLiftMetricsConfig : public UObject
{
GENERATED_BODY()
public:
UGameLiftMetricsConfig() = default;
/**
* Returns the current configuration.
*/
static const UGameLiftMetricsConfig& Get();
/**
* Whether metrics collection is enabled.
* When disabled, no metrics will be collected or sent.
*/
UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category="GameLift Metrics")
bool bEnableMetrics = true;
/**
* Host to send metrics to.
*/
UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category="GameLift Metrics")
FString CollectorHost = TEXT("127.0.0.1");
/**
* UDP port number to send metrics to.
*/
UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category="GameLift Metrics")
int32 CollectorPort = 8125;
/**
* Metrics capture interval before sending data to the collector.
*/
UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category="GameLift Metrics")
float CaptureIntervalSeconds = 10.0f;
/**
* Maximum UDP packet size.
*
* Typical MTU for Internet packets is 1500 bytes.
* We set the default to 1472, leaving room for headers.
*
* For use with collector on same machine or with AWS jumbo packets this value can be bumped up past 1500 bytes.
*/
UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category="GameLift Metrics")
int32 MaxPacketSize = 1472;
};

View File

@@ -0,0 +1,42 @@
/*
* 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 "CoreMinimal.h"
#include <aws/gamelift/metrics/GameLiftMetrics.h>
DECLARE_LOG_CATEGORY_EXTERN(LogGameLiftMetrics, Log, All);
#define WITH_GAMELIFT_METRICS (WITH_GAMELIFT && UE_SERVER)
namespace Aws {
namespace GameLift {
namespace Metrics {
/**
* Logs in all server builds.
*/
GAMELIFT_METRICS_DEFINE_PLATFORM_API(GAMELIFTMETRICS_API, Server, WITH_GAMELIFT_METRICS && UE_SERVER);
/**
* Logs in all development or debug server builds.
*/
GAMELIFT_METRICS_DEFINE_PLATFORM_API(GAMELIFTMETRICS_API, ServerDevelopment, WITH_GAMELIFT_METRICS && UE_SERVER && (UE_BUILD_DEVELOPMENT || UE_BUILD_DEBUG));
/**
* Logs only in debug builds.
*/
GAMELIFT_METRICS_DEFINE_PLATFORM_API(GAMELIFTMETRICS_API, ServerDebug, WITH_GAMELIFT_METRICS && UE_SERVER && UE_BUILD_DEBUG);
} // Metrics
} // GameLift
} // Aws

View File

@@ -0,0 +1,86 @@
/*
* 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.
*
*/
using System.IO;
using UnrealBuildTool;
public class GameLiftServerSDK : ModuleRules
{
public GameLiftServerSDK(ReadOnlyTargetRules Target) : base(Target)
{
PrivateDependencyModuleNames.AddRange(new string[] { "Core", "Projects", "OpenSSL" });
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
bEnableExceptions = true;
bUseRTTI = true;
// Disable windows min/max macros
PublicDefinitions.Add("NOMINMAX");
if (Target.Type == TargetRules.TargetType.Server)
{
PublicDefinitions.Add("WITH_GAMELIFT=1");
}
else
{
PublicDefinitions.Add("WITH_GAMELIFT=0");
}
// Isolate asio namespace to improve packaging compatibility with other modules
PrivateDefinitions.Add("asio=gamelift_asio");
PrivateDefinitions.Add("asio_signal_handler=gamelift_asio_signal_handler");
PrivateDefinitions.Add("AWS_GAMELIFT_EXPORTS");
PrivateDefinitions.Add("ASIO_STANDALONE=1");
PublicDefinitions.Add("SPDLOG_NO_EXCEPTIONS");
// std::invoke_result replaces std::result_of for C++17 and later
// Asio only auto-detects this for MSVC, so override for all compilers
if (Target.CppStandard >= CppStandardVersion.Cpp17) {
PrivateDefinitions.Add("ASIO_HAS_STD_INVOKE_RESULT");
}
PrivateDefinitions.Add("USE_IMPORT_EXPORT=1");
if (Target.Platform == UnrealTargetPlatform.Win64)
{
PrivateDefinitions.Add("_WEBSOCKETPP_CPP11_STRICT_=1");
PrivateDefinitions.Add("SPDLOG_WCHAR_TO_UTF8_SUPPORT=1");
PrivateDefinitions.AddRange(new string[] {
"_WIN32_WINNT_WIN10_TH2=0x0A00",
"_WIN32_WINNT_WIN10_RS1=0x0A00",
"_WIN32_WINNT_WIN10_RS2=0x0A00",
"_WIN32_WINNT_WIN10_RS3=0x0A00",
"_WIN32_WINNT_WIN10_RS4=0x0A00",
"_WIN32_WINNT_WIN10_RS5=0x0A00",
});
}
else if (Target.Platform == UnrealTargetPlatform.Linux || Target.Platform == UnrealTargetPlatform.LinuxArm64)
{
PrivateDefinitions.Add("ASIO_DISABLE_CO_AWAIT");
PrivateDefinitions.Add("RAPIDJSON_NOMEMBERITERATORCLASS");
}
string SpdlogPath = Path.Combine(ModuleDirectory, "../../ThirdParty/spdlog/include");
string SpdlogSrcPath = Path.Combine(ModuleDirectory, "../../ThirdParty/spdlog/src");
string RapidJSONPath = Path.Combine(ModuleDirectory, "../../ThirdParty/rapidjson/include");
string AsioPath = Path.Combine(ModuleDirectory, "../../ThirdParty/asio/include");
string WebSocketPPPath = Path.Combine(ModuleDirectory, "../../ThirdParty/websocketpp");
string ConcurrentQueuePath = Path.Combine(ModuleDirectory, "../../ThirdParty/concurrentqueue");
PublicIncludePaths.Add(SpdlogPath);
PublicIncludePaths.Add(SpdlogSrcPath);
PrivateIncludePaths.Add(RapidJSONPath);
PrivateIncludePaths.Add(AsioPath);
PrivateIncludePaths.Add(WebSocketPPPath);
PrivateIncludePaths.Add(ConcurrentQueuePath);
}
}

View File

@@ -0,0 +1,558 @@
/*
* 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 "GameLiftServerSDK.h"
#include "Core.h"
#include "Modules/ModuleManager.h"
#include "Interfaces/IPluginManager.h"
#include <cstdlib>
#define LOCTEXT_NAMESPACE "FGameLiftServerSDKModule"
void* FGameLiftServerSDKModule::GameLiftServerSDKLibraryHandle = nullptr;
static FProcessParameters GameLiftProcessParameters;
void FGameLiftServerSDKModule::StartupModule()
{
}
bool FGameLiftServerSDKModule::LoadDependency(const FString& Dir, const FString& Name, void*& Handle)
{
FString Lib = Name + TEXT(".") + FPlatformProcess::GetModuleExtension();
FString Path = Dir.IsEmpty() ? *Lib : FPaths::Combine(*Dir, *Lib);
Handle = FPlatformProcess::GetDllHandle(*Path);
if (Handle == nullptr)
{
return false;
}
return true;
}
void FGameLiftServerSDKModule::FreeDependency(void*& Handle)
{
#if !PLATFORM_LINUX
if (Handle != nullptr)
{
FPlatformProcess::FreeDllHandle(Handle);
Handle = nullptr;
}
#endif
}
void FGameLiftServerSDKModule::ShutdownModule()
{
FreeDependency(GameLiftServerSDKLibraryHandle);
}
FGameLiftStringOutcome FGameLiftServerSDKModule::GetSdkVersion() {
#if WITH_GAMELIFT
auto outcome = Aws::GameLift::Server::GetSdkVersion();
if (outcome.IsSuccess()){
return FGameLiftStringOutcome(outcome.GetResult());
}
else {
return FGameLiftStringOutcome(FGameLiftError(outcome.GetError()));
}
#else
return FGameLiftStringOutcome("");
#endif
}
FGameLiftGenericOutcome FGameLiftServerSDKModule::InitSDK() {
#if WITH_GAMELIFT
auto initSDKOutcome = Aws::GameLift::Server::InitSDK();
if (initSDKOutcome.IsSuccess()) {
return FGameLiftGenericOutcome(nullptr);
}
else{
return FGameLiftGenericOutcome(FGameLiftError(initSDKOutcome.GetError()));
}
#else
return FGameLiftGenericOutcome(nullptr);
#endif
}
FGameLiftGenericOutcome FGameLiftServerSDKModule::InitSDK(const FServerParameters &serverParameters) {
#if WITH_GAMELIFT
Aws::GameLift::Server::Model::ServerParameters sdkServerParameters;
sdkServerParameters.SetWebSocketUrl(TCHAR_TO_UTF8(*serverParameters.m_webSocketUrl));
sdkServerParameters.SetFleetId(TCHAR_TO_UTF8(*serverParameters.m_fleetId));
sdkServerParameters.SetProcessId(TCHAR_TO_UTF8(*serverParameters.m_processId));
sdkServerParameters.SetHostId(TCHAR_TO_UTF8(*serverParameters.m_hostId));
sdkServerParameters.SetAuthToken(TCHAR_TO_UTF8(*serverParameters.m_authToken));
sdkServerParameters.SetAwsRegion(TCHAR_TO_UTF8(*serverParameters.m_awsRegion));
sdkServerParameters.SetAccessKey(TCHAR_TO_UTF8(*serverParameters.m_accessKey));
sdkServerParameters.SetSecretKey(TCHAR_TO_UTF8(*serverParameters.m_secretKey));
sdkServerParameters.SetSessionToken(TCHAR_TO_UTF8(*serverParameters.m_sessionToken));
auto initSDKOutcome = Aws::GameLift::Server::InitSDK(sdkServerParameters);
if (initSDKOutcome.IsSuccess()) {
return FGameLiftGenericOutcome(nullptr);
}
else{
return FGameLiftGenericOutcome(FGameLiftError(initSDKOutcome.GetError()));
}
#else
return FGameLiftGenericOutcome(nullptr);
#endif
}
FGameLiftGenericOutcome FGameLiftServerSDKModule::InitMetrics() {
#if WITH_GAMELIFT
auto initMetricsOutcome = Aws::GameLift::Server::InitMetrics();
if (initMetricsOutcome.IsSuccess()) {
return FGameLiftGenericOutcome(nullptr);
}
else {
return FGameLiftGenericOutcome(FGameLiftError(initMetricsOutcome.GetError()));
}
#else
return FGameLiftGenericOutcome(nullptr);
#endif
}
FGameLiftGenericOutcome FGameLiftServerSDKModule::InitMetrics(const FMetricsParameters &metricsParameters) {
#if WITH_GAMELIFT
Aws::GameLift::Server::MetricsParameters sdkMetricsParameters(
TCHAR_TO_UTF8(*metricsParameters.m_statsDHost),
metricsParameters.m_statsDPort,
TCHAR_TO_UTF8(*metricsParameters.m_crashReporterHost),
metricsParameters.m_crashReporterPort,
metricsParameters.m_flushIntervalMs,
metricsParameters.m_maxPacketSize
);
auto initMetricsOutcome = Aws::GameLift::Server::InitMetrics(sdkMetricsParameters);
if (initMetricsOutcome.IsSuccess()) {
return FGameLiftGenericOutcome(nullptr);
}
else {
return FGameLiftGenericOutcome(FGameLiftError(initMetricsOutcome.GetError()));
}
#else
return FGameLiftGenericOutcome(nullptr);
#endif
}
FGameLiftGenericOutcome FGameLiftServerSDKModule::ProcessEnding() {
#if WITH_GAMELIFT
auto outcome = Aws::GameLift::Server::ProcessEnding();
if (outcome.IsSuccess()){
return FGameLiftGenericOutcome(nullptr);
}
else {
return FGameLiftGenericOutcome(FGameLiftError(outcome.GetError()));
}
#else
return FGameLiftGenericOutcome(nullptr);
#endif
}
FGameLiftGenericOutcome FGameLiftServerSDKModule::ActivateGameSession() {
#if WITH_GAMELIFT
auto outcome = Aws::GameLift::Server::ActivateGameSession();
if (outcome.IsSuccess()){
return FGameLiftGenericOutcome(nullptr);
}
else {
return FGameLiftGenericOutcome(FGameLiftError(outcome.GetError()));
}
#else
return FGameLiftGenericOutcome(nullptr);
#endif
}
FGameLiftGenericOutcome FGameLiftServerSDKModule::AcceptPlayerSession(const FString& playerSessionId) {
#if WITH_GAMELIFT
auto outcome = Aws::GameLift::Server::AcceptPlayerSession(TCHAR_TO_UTF8(*playerSessionId));
if (outcome.IsSuccess()){
return FGameLiftGenericOutcome(nullptr);
}
else {
return FGameLiftGenericOutcome(FGameLiftError(outcome.GetError()));
}
#else
return FGameLiftGenericOutcome(nullptr);
#endif
}
FGameLiftGenericOutcome FGameLiftServerSDKModule::RemovePlayerSession(const FString& playerSessionId) {
#if WITH_GAMELIFT
auto outcome = Aws::GameLift::Server::RemovePlayerSession(TCHAR_TO_UTF8(*playerSessionId));
if (outcome.IsSuccess()){
return FGameLiftGenericOutcome(nullptr);
}
else {
return FGameLiftGenericOutcome(FGameLiftError(outcome.GetError()));
}
#else
return FGameLiftGenericOutcome(nullptr);
#endif
}
FGameLiftGenericOutcome FGameLiftServerSDKModule::Destroy()
{
#if WITH_GAMELIFT
auto outcome = Aws::GameLift::Server::Destroy();
if (outcome.IsSuccess()) {
return FGameLiftGenericOutcome(nullptr);
}
else {
return FGameLiftGenericOutcome(FGameLiftError(outcome.GetError()));
}
#else
return FGameLiftGenericOutcome(nullptr);
#endif
}
FGameLiftDescribePlayerSessionsOutcome FGameLiftServerSDKModule::DescribePlayerSessions(const FGameLiftDescribePlayerSessionsRequest &describePlayerSessionsRequest)
{
#if WITH_GAMELIFT
Aws::GameLift::Server::Model::DescribePlayerSessionsRequest request;
request.SetGameSessionId(TCHAR_TO_UTF8(*describePlayerSessionsRequest.m_gameSessionId));
request.SetPlayerId(TCHAR_TO_UTF8(*describePlayerSessionsRequest.m_playerId));
request.SetPlayerSessionId(TCHAR_TO_UTF8(*describePlayerSessionsRequest.m_playerSessionId));
request.SetPlayerSessionStatusFilter(TCHAR_TO_UTF8(*describePlayerSessionsRequest.m_playerSessionStatusFilter));
request.SetLimit(describePlayerSessionsRequest.m_limit);
request.SetNextToken(TCHAR_TO_UTF8(*describePlayerSessionsRequest.m_nextToken));
auto outcome = Aws::GameLift::Server::DescribePlayerSessions(request);
if (outcome.IsSuccess()) {
auto& outres = outcome.GetResult();
FGameLiftDescribePlayerSessionsResult result;
int sessionCount = 0;
auto sessions = outres.GetPlayerSessions(sessionCount);
if (sessionCount > 0) {
TArray<FGameLiftPlayerSession> outSessions;
outSessions.Reserve(sessionCount);
for (int i = 0; i < sessionCount; ++i) {
auto session = sessions + i;
FGameLiftPlayerSession& outSession = outSessions.AddDefaulted_GetRef();
outSession.m_playerSessionId = UTF8_TO_TCHAR(session->GetPlayerSessionId());
outSession.m_playerId = UTF8_TO_TCHAR(session->GetPlayerId());
outSession.m_gameSessionId = UTF8_TO_TCHAR(session->GetGameSessionId());
outSession.m_fleetId = UTF8_TO_TCHAR(session->GetFleetId());
outSession.m_creationTime = session->GetCreationTime();
outSession.m_terminationTime = session->GetTerminationTime();
switch (session->GetStatus()) {
case Aws::GameLift::Server::Model::PlayerSessionStatus::NOT_SET: outSession.m_status = EPlayerSessionStatus::NOT_SET; break;
case Aws::GameLift::Server::Model::PlayerSessionStatus::RESERVED: outSession.m_status = EPlayerSessionStatus::RESERVED; break;
case Aws::GameLift::Server::Model::PlayerSessionStatus::ACTIVE: outSession.m_status = EPlayerSessionStatus::ACTIVE; break;
case Aws::GameLift::Server::Model::PlayerSessionStatus::COMPLETED: outSession.m_status = EPlayerSessionStatus::COMPLETED; break;
case Aws::GameLift::Server::Model::PlayerSessionStatus::TIMEDOUT: outSession.m_status = EPlayerSessionStatus::TIMEDOUT; break;
}
outSession.m_ipAddress = UTF8_TO_TCHAR(session->GetIpAddress());
outSession.m_port = session->GetPort();
outSession.m_playerData = UTF8_TO_TCHAR(session->GetPlayerData());
outSession.m_dnsName = UTF8_TO_TCHAR(session->GetDnsName());
}
result.m_playerSessions = outSessions;
}
result.m_nextToken = (UTF8_TO_TCHAR(outres.GetNextToken()));
return FGameLiftDescribePlayerSessionsOutcome(result);
}
else {
return FGameLiftDescribePlayerSessionsOutcome(FGameLiftError(outcome.GetError()));
}
#else
return FGameLiftDescribePlayerSessionsOutcome(FGameLiftDescribePlayerSessionsResult());
#endif
}
static void OnActivateFunctionInternal(Aws::GameLift::Server::Model::GameSession gameSession, void* state) {
GameLiftProcessParameters.OnActivateFunction(gameSession);
}
static void OnUpdateFunctionInternal(Aws::GameLift::Server::Model::UpdateGameSession updateGameSession, void* state) {
GameLiftProcessParameters.OnUpdateFunction(updateGameSession);
}
static void OnTerminateFunctionInternal(void* state) {
GameLiftProcessParameters.OnTerminateFunction();
}
static bool OnHealthCheckInternal(void* state) {
return GameLiftProcessParameters.OnHealthCheckFunction();
}
FGameLiftGenericOutcome FGameLiftServerSDKModule::ProcessReady(FProcessParameters &processParameters) {
#if WITH_GAMELIFT
GameLiftProcessParameters = processParameters;
char logPathsBuffer[MAX_LOG_PATHS][MAX_PATH_LENGTH];
const char* logPaths[MAX_LOG_PATHS];
memset(logPaths, 0, sizeof(logPaths));
memset(logPathsBuffer, 0, sizeof(logPathsBuffer));
//only use the first MAX_LOG_PATHS values (duplicate logic in cpp SDK)
int32 numLogs = FMath::Min(processParameters.logParameters.Num(), MAX_LOG_PATHS);
for (int i = 0; i < numLogs; i++)
{
FTCHARToUTF8 utf8text(*processParameters.logParameters[i]);
if (utf8text.Length() < MAX_PATH_LENGTH)
{
memcpy(logPathsBuffer[i], utf8text.Get(), utf8text.Length());
}
logPaths[i] = logPathsBuffer[i];
}
const TSharedPtr<IPlugin> StandalonePlugin = IPluginManager::Get().FindPlugin(TEXT("GameLiftPlugin"));
const TSharedPtr<IPlugin> LightweightPlugin = IPluginManager::Get().FindPlugin(TEXT("GameLiftServerSDK"));
FString pluginName;
FString pluginVersion;
if (LightweightPlugin.IsValid())
{
pluginName = LightweightPlugin->GetName();
pluginVersion = LightweightPlugin->GetDescriptor().VersionName;
}
else if (StandalonePlugin.IsValid())
{
pluginName = StandalonePlugin->GetName();
pluginVersion = StandalonePlugin->GetDescriptor().VersionName;
}
else
{
return FGameLiftGenericOutcome(FGameLiftError(Aws::GameLift::GAMELIFT_ERROR_TYPE::SDK_VERSION_DETECTION_FAILED, "Unknown SDK Tool Name", "Couldn't find the GameLift plugin name or version. "
"Please update this code to search for a valid name defined inside GameLift's .uplugin file.")
);
}
// Don't use Unreal's FPlatformMisc::SetEnvironmentVar because it uses Windows specific SetEnvironmentVariable API
// which doesn't mix with GameLift SDK's use of C++ std::getenv()
FString pluginNameEnv = "GAMELIFT_SDK_TOOL_NAME=Unreal" + pluginName;
FString pluginVersionEnv = "GAMELIFT_SDK_TOOL_VERSION=" + pluginVersion;
static std::string pluginNameEnvStr = TCHAR_TO_UTF8(*pluginNameEnv);
static std::string pluginVersionEnvStr = TCHAR_TO_UTF8(*pluginVersionEnv);
#if PLATFORM_WINDOWS
_putenv(pluginNameEnvStr.c_str());
_putenv(pluginVersionEnvStr.c_str());
#else
putenv(const_cast<char*>(pluginNameEnvStr.c_str()));
putenv(const_cast<char*>(pluginVersionEnvStr.c_str()));
#endif
Aws::GameLift::Server::ProcessParameters processParams = Aws::GameLift::Server::ProcessParameters(
OnActivateFunctionInternal,
nullptr,
OnUpdateFunctionInternal,
nullptr,
OnTerminateFunctionInternal,
nullptr,
OnHealthCheckInternal,
nullptr,
processParameters.port,
Aws::GameLift::Server::LogParameters(logPaths, numLogs)
);
auto outcome = Aws::GameLift::Server::ProcessReady(processParams);
if (outcome.IsSuccess()){
return FGameLiftGenericOutcome(nullptr);
}
else {
return FGameLiftGenericOutcome(FGameLiftError(outcome.GetError()));
}
#else
return FGameLiftGenericOutcome(nullptr);
#endif
}
FGameLiftGenericOutcome FGameLiftServerSDKModule::UpdatePlayerSessionCreationPolicy(EPlayerSessionCreationPolicy policy)
{
#if WITH_GAMELIFT
Aws::GameLift::Server::Model::PlayerSessionCreationPolicy internalPolicy = Aws::GameLift::Server::Model::PlayerSessionCreationPolicyMapper::GetPlayerSessionCreationPolicyForName(TCHAR_TO_UTF8(*GetNameForPlayerSessionCreationPolicy(policy)));
auto outcome = Aws::GameLift::Server::UpdatePlayerSessionCreationPolicy(internalPolicy);
if (outcome.IsSuccess()){
return FGameLiftGenericOutcome(nullptr);
}
else {
return FGameLiftGenericOutcome(FGameLiftError(outcome.GetError()));
}
#else
return FGameLiftGenericOutcome(nullptr);
#endif
}
FGameLiftStringOutcome FGameLiftServerSDKModule::GetGameSessionId() {
#if WITH_GAMELIFT
auto outcome = Aws::GameLift::Server::GetGameSessionId();
if (outcome.IsSuccess()){
return FGameLiftStringOutcome(outcome.GetResult());
}
else {
return FGameLiftStringOutcome(FGameLiftError(outcome.GetError()));
}
#else
return FGameLiftStringOutcome("");
#endif
}
FGameLiftLongOutcome FGameLiftServerSDKModule::GetTerminationTime() {
#if WITH_GAMELIFT
auto outcome = Aws::GameLift::Server::GetTerminationTime();
if (outcome.IsSuccess()) {
return FGameLiftLongOutcome(outcome.GetResult());
}
else {
return FGameLiftLongOutcome(FGameLiftError(outcome.GetError()));
}
#else
return FGameLiftLongOutcome(-1);
#endif
}
FGameLiftStringOutcome FGameLiftServerSDKModule::StartMatchBackfill(const FStartMatchBackfillRequest& request) {
#if WITH_GAMELIFT
Aws::GameLift::Server::Model::StartMatchBackfillRequest sdkRequest;
sdkRequest.SetTicketId(TCHAR_TO_UTF8(*request.m_ticketId));
sdkRequest.SetGameSessionArn(TCHAR_TO_UTF8(*request.m_gameSessionArn));
sdkRequest.SetMatchmakingConfigurationArn(TCHAR_TO_UTF8(*request.m_matchmakingConfigurationArn));
for (auto player : request.m_players) {
Aws::GameLift::Server::Model::Player sdkPlayer;
sdkPlayer.SetPlayerId(TCHAR_TO_UTF8(*player.m_playerId));
sdkPlayer.SetTeam(TCHAR_TO_UTF8(*player.m_team));
for (auto entry : player.m_latencyInMs) {
sdkPlayer.WithLatencyMs(TCHAR_TO_UTF8(*entry.Key), entry.Value);
}
std::map<std::string, Aws::GameLift::Server::Model::AttributeValue> sdkAttributeMap;
for (auto attributeEntry : player.m_playerAttributes) {
FAttributeValue value = attributeEntry.Value;
Aws::GameLift::Server::Model::AttributeValue attribute;
switch (value.m_type)
{
case FAttributeType::STRING:
attribute = Aws::GameLift::Server::Model::AttributeValue(TCHAR_TO_UTF8(*value.m_S));
break;
case FAttributeType::DOUBLE:
attribute = Aws::GameLift::Server::Model::AttributeValue(value.m_N);
break;
case FAttributeType::STRING_LIST:
attribute = Aws::GameLift::Server::Model::AttributeValue::ConstructStringList();
for (auto sl : value.m_SL) {
attribute.AddString(TCHAR_TO_UTF8(*sl));
};
break;
case FAttributeType::STRING_DOUBLE_MAP:
attribute = Aws::GameLift::Server::Model::AttributeValue::ConstructStringDoubleMap();
for (auto sdm : value.m_SDM) {
attribute.AddStringAndDouble(TCHAR_TO_UTF8(*sdm.Key), sdm.Value);
};
break;
}
sdkPlayer.WithPlayerAttribute((TCHAR_TO_UTF8(*attributeEntry.Key)), attribute);
}
sdkRequest.AddPlayer(sdkPlayer);
}
auto outcome = Aws::GameLift::Server::StartMatchBackfill(sdkRequest);
if (outcome.IsSuccess()) {
return FGameLiftStringOutcome(outcome.GetResult().GetTicketId());
}
else {
return FGameLiftStringOutcome(FGameLiftError(outcome.GetError()));
}
#else
return FGameLiftStringOutcome("");
#endif
}
FGameLiftGenericOutcome FGameLiftServerSDKModule::StopMatchBackfill(const FStopMatchBackfillRequest& request)
{
#if WITH_GAMELIFT
Aws::GameLift::Server::Model::StopMatchBackfillRequest sdkRequest;
sdkRequest.SetTicketId(TCHAR_TO_UTF8(*request.m_ticketId));
sdkRequest.SetGameSessionArn(TCHAR_TO_UTF8(*request.m_gameSessionArn));
sdkRequest.SetMatchmakingConfigurationArn(TCHAR_TO_UTF8(*request.m_matchmakingConfigurationArn));
auto outcome = Aws::GameLift::Server::StopMatchBackfill(sdkRequest);
if (outcome.IsSuccess()) {
return FGameLiftGenericOutcome(nullptr);
}
else {
return FGameLiftGenericOutcome(FGameLiftError(outcome.GetError()));
}
#else
return FGameLiftGenericOutcome(nullptr);
#endif
}
FGameLiftGetComputeCertificateOutcome FGameLiftServerSDKModule::GetComputeCertificate()
{
#if WITH_GAMELIFT
auto outcome = Aws::GameLift::Server::GetComputeCertificate();
if (outcome.IsSuccess()) {
auto& outres = outcome.GetResult();
FGameLiftGetComputeCertificateResult result;
result.m_certificate_path = UTF8_TO_TCHAR(outres.GetCertificatePath());
result.m_computeName = UTF8_TO_TCHAR(outres.GetComputeName());
return FGameLiftGetComputeCertificateOutcome(result);
}
else {
return FGameLiftGetComputeCertificateOutcome(FGameLiftError(outcome.GetError()));
}
#else
return FGameLiftGetComputeCertificateOutcome(FGameLiftGetComputeCertificateResult());
#endif
}
FGameLiftGetFleetRoleCredentialsOutcome FGameLiftServerSDKModule::GetFleetRoleCredentials(const FGameLiftGetFleetRoleCredentialsRequest &request)
{
#if WITH_GAMELIFT
Aws::GameLift::Server::Model::GetFleetRoleCredentialsRequest sdkRequest;
sdkRequest.SetRoleArn(TCHAR_TO_UTF8(*request.m_roleArn));
sdkRequest.SetRoleSessionName(TCHAR_TO_UTF8(*request.m_roleSessionName));
auto outcome = Aws::GameLift::Server::GetFleetRoleCredentials(sdkRequest);
if (outcome.IsSuccess()) {
auto& outres = outcome.GetResult();
FGameLiftGetFleetRoleCredentialsResult result;
result.m_assumedUserRoleArn = UTF8_TO_TCHAR(outres.GetAssumedUserRoleArn());
result.m_assumedRoleId = UTF8_TO_TCHAR(outres.GetAssumedRoleId());
result.m_accessKeyId = UTF8_TO_TCHAR(outres.GetAccessKeyId());
result.m_secretAccessKey = UTF8_TO_TCHAR(outres.GetSecretAccessKey());
result.m_sessionToken = UTF8_TO_TCHAR(outres.GetSessionToken());
result.m_expiration = FDateTime::FromUnixTimestamp(outres.GetExpiration());
return FGameLiftGetFleetRoleCredentialsOutcome(result);
}
else {
return FGameLiftGetFleetRoleCredentialsOutcome(FGameLiftError(outcome.GetError()));
}
#else
return FGameLiftGetFleetRoleCredentialsOutcome(FGameLiftGetFleetRoleCredentialsResult());
#endif
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FGameLiftServerSDKModule, GameLiftServerSDK)

View File

@@ -0,0 +1,61 @@
/*
* 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 <aws/gamelift/internal/GameLiftCommonState.h>
using namespace Aws::GameLift;
// This is a shared pointer because many contexts (DLLs) may point to the same state.
Aws::GameLift::Internal::GameLiftCommonState * Aws::GameLift::Internal::GameLiftCommonState::m_instance;
Aws::GameLift::Internal::GameLiftCommonState::GameLiftCommonState() {}
Aws::GameLift::Internal::GameLiftCommonState::~GameLiftCommonState() {}
GenericOutcome Aws::GameLift::Internal::GameLiftCommonState::SetInstance(Aws::GameLift::Internal::GameLiftCommonState *instance) {
// If there already is an instance, fail.
if (m_instance) {
return GenericOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::ALREADY_INITIALIZED));
}
// take ownership of the new instance
m_instance = instance;
return GenericOutcome(nullptr);
}
Aws::GameLift::Internal::GetInstanceOutcome Aws::GameLift::Internal::GameLiftCommonState::GetInstance() {
if (!m_instance) {
return Internal::GetInstanceOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::NOT_INITIALIZED));
}
return m_instance;
}
Aws::GameLift::Internal::GetInstanceOutcome Aws::GameLift::Internal::GameLiftCommonState::GetInstance(Aws::GameLift::Internal::GAMELIFT_INTERNAL_STATE_TYPE stateType) {
if (!m_instance) {
return Internal::GetInstanceOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::NOT_INITIALIZED));
}
if (m_instance->GetStateType() != stateType) {
return Internal::GetInstanceOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::INITIALIZATION_MISMATCH));
}
return m_instance;
}
GenericOutcome Aws::GameLift::Internal::GameLiftCommonState::DestroyInstance() {
if (m_instance) {
delete m_instance;
m_instance = nullptr;
return GenericOutcome(nullptr);
}
return GenericOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::NOT_INITIALIZED));
}

View File

@@ -0,0 +1,55 @@
/*
* 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 <aws/gamelift/common/GameLiftToolDetector.h>
#include <cstdlib>
#include <cstring>
namespace Aws {
namespace GameLift {
namespace Common {
void GameLiftToolDetector::SetGameLiftTool() {
static constexpr const char* ENV_VAR_SDK_TOOL_NAME = "GAMELIFT_SDK_TOOL_NAME";
static constexpr const char* ENV_VAR_SDK_TOOL_VERSION = "GAMELIFT_SDK_TOOL_VERSION";
if (!IsToolRunning()) {
return;
}
const char* existingToolName = std::getenv(ENV_VAR_SDK_TOOL_NAME);
const char* existingToolVersion = std::getenv(ENV_VAR_SDK_TOOL_VERSION);
std::string toolName = GetToolName();
std::string toolVersion = GetToolVersion();
if (existingToolName != nullptr && strlen(existingToolName) > 0) {
std::string existingToolNameStr(existingToolName);
std::string existingToolVersionStr(existingToolVersion != nullptr ? existingToolVersion : "");
if (existingToolNameStr.find(toolName) == std::string::npos) {
toolName = existingToolNameStr + "," + toolName;
toolVersion = existingToolVersionStr + "," + toolVersion;
} else {
toolName = existingToolNameStr;
toolVersion = existingToolVersionStr;
}
}
#ifdef _WIN32
_putenv_s(ENV_VAR_SDK_TOOL_NAME, toolName.c_str());
_putenv_s(ENV_VAR_SDK_TOOL_VERSION, toolVersion.c_str());
#else
setenv(ENV_VAR_SDK_TOOL_NAME, toolName.c_str(), 1);
setenv(ENV_VAR_SDK_TOOL_VERSION, toolVersion.c_str(), 1);
#endif
}
} // namespace Common
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,84 @@
/*
* 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 <aws/gamelift/common/MetricsDetector.h>
#include <cstdio>
#include <memory>
#include <stdexcept>
#include <string>
#include <algorithm>
namespace Aws {
namespace GameLift {
namespace Common {
bool MetricsDetector::IsToolRunning() {
try {
#ifdef _WIN32
std::string windowsServiceArgs = std::string(WINDOWS_SERVICE_ARGS) + WINDOWS_SERVICE_NAME;
return CheckService(WINDOWS_SERVICE_COMMAND, windowsServiceArgs, [](const std::string& output) {
return output.find(WINDOWS_RUNNING_STATUS) != std::string::npos;
});
#else
std::string linuxServiceArgs = std::string(LINUX_SERVICE_ARGS) + LINUX_SERVICE_NAME;
return CheckService(LINUX_SERVICE_COMMAND, linuxServiceArgs, [](const std::string& output) {
std::string trimmedOutput = output;
// Trim whitespace
trimmedOutput.erase(trimmedOutput.find_last_not_of(" \n\r\t") + 1);
trimmedOutput.erase(0, trimmedOutput.find_first_not_of(" \n\r\t"));
return trimmedOutput == LINUX_ACTIVE_STATUS;
});
#endif
} catch (...) {
return false;
}
}
bool MetricsDetector::CheckService(const std::string& command, const std::string& arguments, std::function<bool(const std::string&)> outputValidator) {
std::string fullCommand = command + " " + arguments;
#ifdef _WIN32
FILE* pipe = _popen(fullCommand.c_str(), "r");
#else
FILE* pipe = popen(fullCommand.c_str(), "r");
#endif
if (!pipe) {
return false;
}
std::string result;
char buffer[128];
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
result.append(buffer);
}
#ifdef _WIN32
int exitCode = _pclose(pipe);
#else
int exitCode = pclose(pipe);
#endif
return exitCode == 0 && outputValidator(result);
}
std::string MetricsDetector::GetToolName() {
return TOOL_NAME;
}
std::string MetricsDetector::GetToolVersion() {
return TOOL_VERSION;
}
} // namespace Common
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,67 @@
/*
* 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 <aws/gamelift/internal/model/Message.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
#include <aws/gamelift/internal/util/RandomStringGenerator.h>
namespace Aws {
namespace GameLift {
namespace Internal {
std::string Message::Serialize() const {
// Create the buffer & Writer for the object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
// Start the object and call Serialize to serialize.
writer.StartObject();
if (Serialize(&writer)) {
writer.EndObject();
return buffer.GetString();
}
return "";
}
bool Message::Deserialize(const std::string &jsonString) {
// Parse the json into a document
rapidjson::Document doc;
if (doc.Parse(jsonString.c_str()).HasParseError()) {
return false;
}
// Call Deserialize to populate the object's member variables
return Deserialize(doc);
}
bool Message::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
JsonHelper::WriteNonEmptyString(writer, ACTION, m_action);
JsonHelper::WriteNonEmptyString(writer, REQUEST_ID, m_requestId);
return true;
}
bool Message::Deserialize(const rapidjson::Value &value) {
m_action = JsonHelper::SafelyDeserializeString(value, ACTION);
m_requestId = JsonHelper::SafelyDeserializeString(value, REQUEST_ID);
return true;
}
std::ostream &operator<<(std::ostream &os, const Message &message) {
os << message.Serialize();
return os;
}
std::string Message::GenerateRandomRequestId() { return RandomStringGenerator::GenerateRandomAlphaNumericString(32); }
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,40 @@
/*
* 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 <aws/gamelift/internal/model/ResponseMessage.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
namespace Aws {
namespace GameLift {
namespace Internal {
bool ResponseMessage::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
JsonHelper::WritePositiveInt(writer, STATUS_CODE, m_statusCode);
JsonHelper::WriteNonEmptyString(writer, ERROR_MESSAGE, m_errorMessage);
return true;
}
bool ResponseMessage::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
m_statusCode = JsonHelper::SafelyDeserializeInt(value, STATUS_CODE);
m_errorMessage = JsonHelper::SafelyDeserializeString(value, ERROR_MESSAGE);
return true;
}
std::ostream &operator<<(std::ostream &os, const ResponseMessage &responseMessage) {
const Message *message = &responseMessage;
os << message->Serialize();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -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.
*
*/
#include <aws/gamelift/internal/model/Uri.h>
#include <sstream>
namespace Aws {
namespace GameLift {
namespace Internal {
Uri::UriBuilder &Uri::UriBuilder::AddQueryParam(const std::string &key, const std::string &value) {
m_queryParams[key] = value;
return *this;
}
Uri::UriBuilder &Uri::UriBuilder::WithBaseUri(const std::string &baseUriString) {
m_baseUriString = baseUriString;
return *this;
}
Uri Uri::UriBuilder::Build() const { return Uri(m_baseUriString, m_queryParams); }
Uri::Uri(const std::string &baseUriString, const std::map<std::string, std::string> &queryMap) : m_baseUriString(baseUriString), m_queryMap(queryMap) {
SetQueryString(queryMap);
m_uriString = m_baseUriString + m_queryString;
}
void Uri::SetQueryString(const std::map<std::string, std::string> &queryMap) {
std::ostringstream queryStringStream;
queryStringStream << "?";
for (auto const &queryPair : queryMap) {
// All but first param requires "&" delimiter
if (queryStringStream.str().size() != 1) {
queryStringStream << "&";
}
queryStringStream << queryPair.first << "=" << queryPair.second;
}
m_queryString = queryStringStream.str();
}
std::ostream &operator<<(std::ostream &os, const Uri &uri) {
os << uri.GetUriString();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,125 @@
/*
* 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 <aws/gamelift/internal/model/WebSocketAttributeValue.h>
#include <spdlog/spdlog.h>
namespace Aws {
namespace GameLift {
namespace Internal {
std::string WebSocketAttributeValue::Serialize() const {
// Create the buffer & Writer for the object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
// Start the object and call Serialize to serialize.
writer.StartObject();
if (Serialize(&writer)) {
writer.EndObject();
return buffer.GetString();
}
spdlog::warn("Could not parse into WebSocketAttributeValue");
return "";
}
bool WebSocketAttributeValue::Deserialize(const std::string &jsonString) {
// Parse the json into a document
rapidjson::Document doc;
if (doc.Parse(jsonString.c_str()).HasParseError()) {
spdlog::error("WebSocketAttributeValue: Parse error found for: {}", jsonString);
return false;
}
// Call Deserialize to populate the object's member variables
return Deserialize(doc);
}
bool WebSocketAttributeValue::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
writer->String(ATTR_TYPE);
writer->String(GetAttributeTypeAsString().c_str());
switch (m_attrType) {
case WebSocketAttrType::STRING:
writer->String(S);
writer->String(m_S.c_str());
break;
case WebSocketAttrType::DOUBLE:
writer->String(N);
writer->Double(m_N);
break;
case WebSocketAttrType::STRING_LIST:
writer->String(SL);
writer->StartArray();
for (auto it = m_SL.begin(); it != m_SL.end(); ++it) {
writer->String(it->c_str());
}
writer->EndArray();
break;
case WebSocketAttrType::STRING_DOUBLE_MAP:
writer->String(SDM);
writer->StartObject();
for (auto const &property : m_SDM) {
writer->String(property.first.c_str());
writer->Double(property.second);
}
writer->EndObject();
break;
default:
// Do Nothing
break;
}
return true;
}
bool WebSocketAttributeValue::Deserialize(const rapidjson::Value &value) {
std::string attrString = value.HasMember(ATTR_TYPE) ? value[ATTR_TYPE].GetString() : "";
SetAttributeType(attrString);
switch (m_attrType) {
case WebSocketAttrType::STRING:
m_S = value.HasMember(S) ? value[S].GetString() : "";
break;
case WebSocketAttrType::DOUBLE:
m_N = value.HasMember(N) ? value[N].GetDouble() : 0;
break;
case WebSocketAttrType::STRING_LIST:
if (value.HasMember(SL)) {
const rapidjson::Value &slValue = value[SL];
std::vector<std::string> sl;
for (rapidjson::SizeType i = 0; i < slValue.GetArray().Size(); i++) {
sl.push_back(slValue.GetArray()[i].GetString());
}
m_SL = sl;
}
break;
case WebSocketAttrType::STRING_DOUBLE_MAP:
if (value.HasMember(SDM)) {
const rapidjson::Value &sdmValue = value[SDM];
std::map<std::string, double> sdm;
for (rapidjson::Value::ConstMemberIterator iter = sdmValue.MemberBegin(); iter != sdmValue.MemberEnd(); ++iter) {
sdm[iter->name.GetString()] = iter->value.GetDouble();
}
m_SDM = sdm;
}
break;
default:
// Do Nothing
break;
}
return true;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,100 @@
/*
* 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 <aws/gamelift/internal/model/WebSocketGameSession.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
#include <spdlog/spdlog.h>
namespace Aws {
namespace GameLift {
namespace Internal {
std::string WebSocketGameSession::Serialize() const {
// Create the buffer & Writer for the object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
// Start the object and call Serialize to serialize.
writer.StartObject();
if (Serialize(&writer)) {
writer.EndObject();
return buffer.GetString();
}
spdlog::warn("Could not parse into WebSocketGameSession");
return "";
}
bool WebSocketGameSession::Deserialize(const std::string &jsonString) {
// Parse the json into a document
rapidjson::Document doc;
if (doc.Parse(jsonString.c_str()).HasParseError()) {
spdlog::error("WebSocketGameSession: Parse error found for: {}", jsonString);
return false;
}
// Call Deserialize to populate the object's member variables
return Deserialize(doc);
}
bool WebSocketGameSession::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
JsonHelper::WriteNonEmptyString(writer, GAME_SESSION_ID, m_gameSessionId);
JsonHelper::WriteNonEmptyString(writer, NAME, m_name);
JsonHelper::WriteNonEmptyString(writer, FLEET_ID, m_fleetId);
JsonHelper::WriteNonEmptyString(writer, GAME_SESSION_DATA, m_gameSessionData);
JsonHelper::WriteNonEmptyString(writer, MATCHMAKER_DATA, m_matchmakerData);
JsonHelper::WriteNonEmptyString(writer, DNS_NAME, m_dnsName);
JsonHelper::WriteNonEmptyString(writer, IP_ADDRESS, m_ipAddress);
JsonHelper::WritePositiveInt(writer, MAXIMUM_PLAYER_SESSION_COUNT, m_maximumPlayerSessionCount);
JsonHelper::WritePositiveInt(writer, PORT, m_port);
writer->String(GAME_PROPERTIES);
writer->StartObject();
for (auto const &property : m_gameProperties) {
writer->String(property.first.c_str());
writer->String(property.second.c_str());
}
writer->EndObject();
return true;
}
bool WebSocketGameSession::Deserialize(const rapidjson::Value &value) {
m_gameSessionId = JsonHelper::SafelyDeserializeString(value, GAME_SESSION_ID);
m_name = JsonHelper::SafelyDeserializeString(value, NAME);
m_fleetId = JsonHelper::SafelyDeserializeString(value, FLEET_ID);
m_gameSessionData = JsonHelper::SafelyDeserializeString(value, GAME_SESSION_DATA);
m_matchmakerData = JsonHelper::SafelyDeserializeString(value, MATCHMAKER_DATA);
m_dnsName = JsonHelper::SafelyDeserializeString(value, DNS_NAME);
m_ipAddress = JsonHelper::SafelyDeserializeString(value, IP_ADDRESS);
m_maximumPlayerSessionCount = JsonHelper::SafelyDeserializeInt(value, MAXIMUM_PLAYER_SESSION_COUNT);
m_port = JsonHelper::SafelyDeserializeInt(value, PORT);
if (value.HasMember(GAME_PROPERTIES) && !value[GAME_PROPERTIES].IsNull()) {
for (auto itr = value[GAME_PROPERTIES].MemberBegin(); itr != value[GAME_PROPERTIES].MemberEnd(); ++itr) {
if (itr->name.IsString() && itr->value.IsString()) {
m_gameProperties[itr->name.GetString()] = itr->value.GetString();
}
}
}
return true;
}
std::ostream &operator<<(std::ostream &os, const WebSocketGameSession &gameSession) {
const WebSocketGameSession *message = &gameSession;
os << message->Serialize();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,107 @@
/*
* 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 <aws/gamelift/internal/model/WebSocketPlayer.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
#include <iostream>
#include <spdlog/spdlog.h>
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow" // Ignore shadow variable warning for Linux/Mac
#endif
namespace Aws {
namespace GameLift {
namespace Internal {
std::string WebSocketPlayer::Serialize() const {
// Create the buffer & Writer for the object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
// Start the object and call Serialize to serialize.
writer.StartObject();
if (Serialize(&writer)) {
writer.EndObject();
return buffer.GetString();
}
spdlog::warn("Could not parse into WebSocketPlayer");
return "";
}
bool WebSocketPlayer::Deserialize(const std::string &jsonString) {
// Parse the json into a document
rapidjson::Document doc;
if (doc.Parse(jsonString.c_str()).HasParseError()) {
spdlog::error("WebSocketPlayer: Parse error found for: {}", jsonString);
return false;
}
// Call Deserialize to populate the object's member variables
return Deserialize(doc);
}
bool WebSocketPlayer::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
JsonHelper::WriteNonEmptyString(writer, PLAYER_ID, m_playerId);
writer->String(PLAYER_ATTRIBUTES);
writer->StartObject();
for (auto const &playerAttributesIter : m_playerAttributes) {
writer->String(playerAttributesIter.first.c_str());
writer->StartObject();
playerAttributesIter.second.Serialize(writer);
writer->EndObject();
}
writer->EndObject();
writer->String(LATENCY_IN_MS);
writer->StartObject();
for (auto const &latencyMsIter : m_latencyInMs) {
writer->String(latencyMsIter.first.c_str());
writer->Int(latencyMsIter.second);
}
writer->EndObject();
JsonHelper::WriteNonEmptyString(writer, TEAM, m_team);
return true;
}
bool WebSocketPlayer::Deserialize(const rapidjson::Value &value) {
m_playerId = JsonHelper::SafelyDeserializeString(value, PLAYER_ID);
m_team = JsonHelper::SafelyDeserializeString(value, TEAM);
if (value.HasMember(PLAYER_ATTRIBUTES) && !value[PLAYER_ATTRIBUTES].IsNull()) {
for (auto itr = value[PLAYER_ATTRIBUTES].MemberBegin(); itr != value[PLAYER_ATTRIBUTES].MemberEnd(); ++itr) {
if (itr->name.IsString() && !itr->value.IsNull()) {
WebSocketAttributeValue value;
value.Deserialize(itr->value);
m_playerAttributes[itr->name.GetString()] = value;
}
}
}
if (value.HasMember(LATENCY_IN_MS) && !value[LATENCY_IN_MS].IsNull()) {
for (auto itr = value[LATENCY_IN_MS].MemberBegin(); itr != value[LATENCY_IN_MS].MemberEnd(); ++itr) {
if (itr->name.IsString() && itr->value.IsInt()) {
m_latencyInMs[itr->name.GetString()] = itr->value.GetInt();
}
}
}
return true;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -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 <aws/gamelift/internal/model/WebSocketPlayerSession.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
namespace Aws {
namespace GameLift {
namespace Internal {
bool WebSocketPlayerSession::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
JsonHelper::WriteNonEmptyString(writer, PLAYER_SESSION_ID, m_playerSessionId);
JsonHelper::WriteNonEmptyString(writer, PLAYER_ID, m_playerId);
JsonHelper::WriteNonEmptyString(writer, GAME_SESSION_ID, m_gameSessionId);
JsonHelper::WriteNonEmptyString(writer, FLEET_ID, m_fleetId);
JsonHelper::WritePositiveInt64(writer, CREATION_TIME, m_creationTime);
JsonHelper::WritePositiveInt64(writer, TERMINATION_TIME, m_terminationTime);
JsonHelper::WriteNonEmptyString(writer, STATUS, WebSocketPlayerSessionStatusMapper::GetNameForStatus(m_status));
JsonHelper::WriteNonEmptyString(writer, IP_ADDRESS, m_ipAddress);
JsonHelper::WritePositiveInt(writer, PORT, m_port);
JsonHelper::WriteNonEmptyString(writer, PLAYER_DATA, m_playerData);
JsonHelper::WriteNonEmptyString(writer, DNS_NAME, m_dnsName);
return true;
}
bool WebSocketPlayerSession::Deserialize(const rapidjson::Value &value) {
m_playerSessionId = JsonHelper::SafelyDeserializeString(value, PLAYER_SESSION_ID);
m_playerId = JsonHelper::SafelyDeserializeString(value, PLAYER_ID);
m_gameSessionId = JsonHelper::SafelyDeserializeString(value, GAME_SESSION_ID);
m_fleetId = JsonHelper::SafelyDeserializeString(value, FLEET_ID);
m_creationTime = JsonHelper::SafelyDeserializeInt64(value, CREATION_TIME);
m_terminationTime = JsonHelper::SafelyDeserializeInt64(value, TERMINATION_TIME);
m_status = WebSocketPlayerSessionStatusMapper::GetStatusForName(JsonHelper::SafelyDeserializeString(value, STATUS));
m_ipAddress = JsonHelper::SafelyDeserializeString(value, IP_ADDRESS);
m_port = JsonHelper::SafelyDeserializeInt(value, PORT);
m_playerData = JsonHelper::SafelyDeserializeString(value, PLAYER_DATA);
m_dnsName = JsonHelper::SafelyDeserializeString(value, DNS_NAME);
return true;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,75 @@
/*
* 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 <aws/gamelift/internal/model/adapter/DescribePlayerSessionsAdapter.h>
#include <aws/gamelift/common/GameLift_EXPORTS.h>
#include <aws/gamelift/server/model/PlayerSession.h>
#include <aws/gamelift/server/model/PlayerSessionStatus.h>
namespace Aws {
namespace GameLift {
namespace Internal {
Server::Model::DescribePlayerSessionsResult DescribePlayerSessionsAdapter::convert(const WebSocketDescribePlayerSessionsResponse *webSocketResponse) {
Server::Model::DescribePlayerSessionsResult result;
#ifdef GAMELIFT_USE_STD
result.SetNextToken(webSocketResponse->GetNextToken());
for (auto &webSocketPlayerSession : webSocketResponse->GetPlayerSessions()) {
Server::Model::PlayerSession playerSession =
Server::Model::PlayerSession()
.WithPlayerSessionId(webSocketPlayerSession.GetPlayerSessionId())
.WithPlayerId(webSocketPlayerSession.GetPlayerId())
.WithGameSessionId(webSocketPlayerSession.GetGameSessionId())
.WithFleetId(webSocketPlayerSession.GetFleetId())
.WithCreationTime(webSocketPlayerSession.GetCreationTime())
.WithTerminationTime(webSocketPlayerSession.GetTerminationTime())
.WithStatus(static_cast<Server::Model::PlayerSessionStatus>(static_cast<int>(webSocketPlayerSession.GetStatus())))
.WithIpAddress(webSocketPlayerSession.GetIpAddress())
.WithPort(webSocketPlayerSession.GetPort())
.WithPlayerData(webSocketPlayerSession.GetPlayerData())
.WithDnsName(webSocketPlayerSession.GetDnsName());
result.AddPlayerSession(playerSession);
}
#else
result.SetNextToken(webSocketResponse->GetNextToken().c_str());
for (auto &webSocketPlayerSession : webSocketResponse->GetPlayerSessions()) {
Server::Model::PlayerSession playerSession =
Server::Model::PlayerSession()
.WithPlayerSessionId(webSocketPlayerSession.GetPlayerSessionId().c_str())
.WithPlayerId(webSocketPlayerSession.GetPlayerId().c_str())
.WithGameSessionId(webSocketPlayerSession.GetGameSessionId().c_str())
.WithFleetId(webSocketPlayerSession.GetFleetId().c_str())
.WithCreationTime(webSocketPlayerSession.GetCreationTime())
.WithTerminationTime(webSocketPlayerSession.GetTerminationTime())
.WithStatus(static_cast<Server::Model::PlayerSessionStatus>(static_cast<int>(webSocketPlayerSession.GetStatus())))
.WithIpAddress(webSocketPlayerSession.GetIpAddress().c_str())
.WithPort(webSocketPlayerSession.GetPort())
.WithPlayerData(webSocketPlayerSession.GetPlayerData().c_str())
.WithDnsName(webSocketPlayerSession.GetDnsName().c_str());
result.AddPlayerSession(playerSession);
}
#endif
return result;
}
WebSocketDescribePlayerSessionsRequest DescribePlayerSessionsAdapter::convert(const Server::Model::DescribePlayerSessionsRequest &request) {
return WebSocketDescribePlayerSessionsRequest()
.WithGameSessionId(request.GetGameSessionId())
.WithPlayerId(request.GetPlayerId())
.WithPlayerSessionId(request.GetPlayerSessionId())
.WithPlayerSessionStatusFilter(request.GetPlayerSessionStatusFilter())
.WithNextToken(request.GetNextToken())
.WithLimit(request.GetLimit());
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,40 @@
/*
* 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 <aws/gamelift/internal/model/adapter/GetFleetRoleCredentialsAdapter.h>
namespace Aws {
namespace GameLift {
namespace Internal {
Server::Model::GetFleetRoleCredentialsResult GetFleetRoleCredentialsAdapter::convert(const WebSocketGetFleetRoleCredentialsResponse *webSocketResponse) {
Server::Model::GetFleetRoleCredentialsResult result;
result.SetAssumedUserRoleArn(webSocketResponse->GetAssumedRoleUserArn().c_str());
result.SetAssumedRoleId(webSocketResponse->GetAssumedRoleId().c_str());
result.SetAccessKeyId(webSocketResponse->GetAccessKeyId().c_str());
result.SetSecretAccessKey(webSocketResponse->GetSecretAccessKey().c_str());
result.SetSessionToken(webSocketResponse->GetSessionToken().c_str());
// time_t is Unix epoch in seconds, and webSocketResponse->GetExpiration() returns time in milliseconds
// This is why we are dividing webSocketResponse->GetExpiration() by 1000
int64_t expirationTimeInMilliSeconds = webSocketResponse->GetExpiration();
int64_t expirationTimeInSeconds = expirationTimeInMilliSeconds / 1000;
result.SetExpiration(static_cast<time_t>(expirationTimeInSeconds));
return result;
}
WebSocketGetFleetRoleCredentialsRequest GetFleetRoleCredentialsAdapter::convert(const Server::Model::GetFleetRoleCredentialsRequest &request) {
return WebSocketGetFleetRoleCredentialsRequest().WithRoleArn(request.GetRoleArn()).WithRoleSessionName(request.GetRoleSessionName());
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,157 @@
/*
* 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 <aws/gamelift/internal/model/adapter/StartMatchBackfillAdapter.h>
namespace Aws {
namespace GameLift {
namespace Internal {
Server::Model::StartMatchBackfillResult StartMatchBackfillAdapter::convert(const WebSocketStartMatchBackfillResponse *webSocketResponse) {
Server::Model::StartMatchBackfillResult result;
if (webSocketResponse) {
result.SetTicketId(webSocketResponse->GetTicketId().c_str());
}
return result;
}
WebSocketStartMatchBackfillRequest StartMatchBackfillAdapter::convert(const Server::Model::StartMatchBackfillRequest &request) {
std::vector<Server::Model::Player> requestPlayersAsVector;
#ifdef GAMELIFT_USE_STD
requestPlayersAsVector = request.GetPlayers();
#else
int countOfPlayers;
const Server::Model::Player *requestPlayers = request.GetPlayers(countOfPlayers);
for (int i = 0; i < countOfPlayers; i++) {
Server::Model::Player playerToStore = *(requestPlayers + i);
requestPlayersAsVector.push_back(playerToStore);
}
#endif
std::vector<WebSocketPlayer> players;
for (auto it = requestPlayersAsVector.begin(); it != requestPlayersAsVector.end(); ++it) {
Server::Model::Player playerToConvert = *it;
WebSocketPlayer player = WebSocketPlayer()
.WithPlayerId(playerToConvert.GetPlayerId())
.WithTeam(playerToConvert.GetTeam())
.WithPlayerAttributes(fetchAndConvertAttributes(playerToConvert))
.WithLatencyInMs(fetchAndConvertLatency(playerToConvert));
players.push_back(player);
}
return WebSocketStartMatchBackfillRequest()
.WithTicketId(request.GetTicketId())
.WithGameSessionArn(request.GetGameSessionArn())
.WithMatchmakingConfigurationArn(request.GetMatchmakingConfigurationArn())
.WithPlayers(players);
}
std::map<std::string, int> StartMatchBackfillAdapter::fetchAndConvertLatency(const Server::Model::Player &player) {
std::map<std::string, int> result;
#ifdef GAMELIFT_USE_STD
result = player.GetLatencyInMs();
#else
int countOfLatencies;
const Server::Model::Player::RegionAndLatency *regionAndLatency = player.GetLatencyMs(countOfLatencies);
for (int i = 0; i < countOfLatencies; i++) {
Server::Model::Player::RegionAndLatency latencyToConvert = *(regionAndLatency + i);
result[latencyToConvert.GetRegion()] = latencyToConvert.GetLatencyMs();
}
#endif
return result;
}
std::map<std::string, WebSocketAttributeValue> StartMatchBackfillAdapter::fetchAndConvertAttributes(const Server::Model::Player &player) {
std::map<std::string, Server::Model::AttributeValue> attrs;
#ifdef GAMELIFT_USE_STD
attrs = player.GetPlayerAttributes();
#else
int countOfAttributes;
const Server::Model::Player::NamedAttribute *attributes = player.GetPlayerAttributes(countOfAttributes);
for (int i = 0; i < countOfAttributes; i++) {
Server::Model::Player::NamedAttribute attributeToConvert = *(attributes + i);
attrs[attributeToConvert.GetName()] = attributeToConvert.GetValue();
}
#endif
std::map<std::string, WebSocketAttributeValue> result;
for (auto const &attr : attrs) {
Server::Model::AttributeValue attributeValue = attr.second;
WebSocketAttributeValue value;
switch (attributeValue.GetType()) {
case Server::Model::AttributeValue::AttrType::NONE:
value.SetAttributeType(WebSocketAttrType::NONE);
break;
case Server::Model::AttributeValue::AttrType::STRING:
value.SetAttributeType(WebSocketAttrType::STRING);
value.SetS(attributeValue.GetS());
break;
case Server::Model::AttributeValue::AttrType::DOUBLE:
value.SetAttributeType(WebSocketAttrType::DOUBLE);
value.SetN(attributeValue.GetN());
break;
case Server::Model::AttributeValue::AttrType::STRING_LIST:
value.SetAttributeType(WebSocketAttrType::STRING_LIST);
value.SetSL(convertStringList(attributeValue));
break;
case Server::Model::AttributeValue::AttrType::STRING_DOUBLE_MAP:
value.SetAttributeType(WebSocketAttrType::STRING_DOUBLE_MAP);
value.SetSDM(convertStringDoubleMap(attributeValue));
break;
default:
value.SetAttributeType(WebSocketAttrType::NONE);
break;
}
result[attr.first] = value;
}
return result;
}
std::vector<std::string> StartMatchBackfillAdapter::convertStringList(const Server::Model::AttributeValue &attributeValue) {
std::vector<std::string> result;
#ifdef GAMELIFT_USE_STD
result = attributeValue.GetSL();
#else
int countOfStringList;
const Server::Model::AttributeValue::AttributeStringType *stringList = attributeValue.GetSL(countOfStringList);
for (int i = 0; i < countOfStringList; i++) {
const Server::Model::AttributeValue::AttributeStringType *stringListItemPointer = stringList + i;
std::string itemAsString = std::string(*stringListItemPointer);
result.push_back(itemAsString);
}
#endif
return result;
}
std::map<std::string, double> StartMatchBackfillAdapter::convertStringDoubleMap(const Server::Model::AttributeValue &attributeValue) {
std::map<std::string, double> result;
#ifdef GAMELIFT_USE_STD
result = attributeValue.GetSDM();
#else
int countOfStringMap;
const Server::Model::AttributeValue::KeyAndValue *stringAndDoubleMap = attributeValue.GetSDM(countOfStringMap);
for (int i = 0; i < countOfStringMap; i++) {
Server::Model::AttributeValue::KeyAndValue keyAndValue = *(stringAndDoubleMap + i);
std::string key = std::string(keyAndValue.GetKey());
result[key] = keyAndValue.GetValue();
}
#endif
return result;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,71 @@
/*
* 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 <aws/gamelift/internal/model/message/CreateGameSessionMessage.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
namespace Aws {
namespace GameLift {
namespace Internal {
bool CreateGameSessionMessage::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
JsonHelper::WriteNonEmptyString(writer, GAME_SESSION_ID, m_gameSessionId);
JsonHelper::WriteNonEmptyString(writer, GAME_SESSION_NAME, m_gameSessionName);
JsonHelper::WriteNonEmptyString(writer, GAME_SESSION_DATA, m_gameSessionData);
JsonHelper::WriteNonEmptyString(writer, MATCHMAKER_DATA, m_matchmakerData);
JsonHelper::WriteNonEmptyString(writer, DNS_NAME, m_dnsName);
JsonHelper::WriteNonEmptyString(writer, IP_ADDRESS, m_ipAddress);
JsonHelper::WritePositiveInt(writer, MAXIMUM_PLAYER_SESSION_COUNT, m_maximumPlayerSessionCount);
JsonHelper::WritePositiveInt(writer, PORT, m_port);
writer->String(GAME_PROPERTIES);
writer->StartObject();
for (auto const &property : m_gameProperties) {
writer->String(property.first.c_str());
writer->String(property.second.c_str());
}
writer->EndObject();
return true;
}
bool CreateGameSessionMessage::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
m_gameSessionId = JsonHelper::SafelyDeserializeString(value, GAME_SESSION_ID);
m_gameSessionName = JsonHelper::SafelyDeserializeString(value, GAME_SESSION_NAME);
m_gameSessionData = JsonHelper::SafelyDeserializeString(value, GAME_SESSION_DATA);
m_matchmakerData = JsonHelper::SafelyDeserializeString(value, MATCHMAKER_DATA);
m_dnsName = JsonHelper::SafelyDeserializeString(value, DNS_NAME);
m_ipAddress = JsonHelper::SafelyDeserializeString(value, IP_ADDRESS);
m_maximumPlayerSessionCount = JsonHelper::SafelyDeserializeInt(value, MAXIMUM_PLAYER_SESSION_COUNT);
m_port = JsonHelper::SafelyDeserializeInt(value, PORT);
if (value.HasMember(GAME_PROPERTIES) && !value[GAME_PROPERTIES].IsNull()) {
for (auto itr = value[GAME_PROPERTIES].MemberBegin(); itr != value[GAME_PROPERTIES].MemberEnd(); ++itr) {
if (itr->name.IsString() && itr->value.IsString()) {
m_gameProperties[itr->name.GetString()] = itr->value.GetString();
}
}
}
return true;
}
std::ostream &operator<<(std::ostream &os, const CreateGameSessionMessage &createGameSessionMessage) {
const Message *message = &createGameSessionMessage;
os << message->Serialize();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,40 @@
/*
* 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 <aws/gamelift/internal/model/message/RefreshConnectionMessage.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
namespace Aws {
namespace GameLift {
namespace Internal {
bool RefreshConnectionMessage::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
JsonHelper::WriteNonEmptyString(writer, REFRESH_CONNECTION_ENDPOINT, m_refreshConnectionEndpoint);
JsonHelper::WriteNonEmptyString(writer, AUTH_TOKEN, m_authToken);
return true;
}
bool RefreshConnectionMessage::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
m_refreshConnectionEndpoint = JsonHelper::SafelyDeserializeString(value, REFRESH_CONNECTION_ENDPOINT);
m_authToken = JsonHelper::SafelyDeserializeString(value, AUTH_TOKEN);
return true;
}
std::ostream &operator<<(std::ostream &os, const RefreshConnectionMessage &refreshConnectionMessage) {
const Message *message = &refreshConnectionMessage;
os << message->Serialize();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,40 @@
/*
* 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 <aws/gamelift/internal/model/message/TerminateProcessMessage.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
namespace Aws {
namespace GameLift {
namespace Internal {
bool TerminateProcessMessage::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
JsonHelper::WritePositiveInt64(writer, TERMINATION_TIME, m_terminationTime);
return true;
}
bool TerminateProcessMessage::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
m_terminationTime = JsonHelper::SafelyDeserializeInt64(value, TERMINATION_TIME);
return true;
}
std::ostream &operator<<(std::ostream &os, const TerminateProcessMessage &terminateProcessMessage) {
const Message *message = &terminateProcessMessage;
os << message->Serialize();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,56 @@
/*
* 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 <aws/gamelift/internal/model/message/UpdateGameSessionMessage.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
namespace Aws {
namespace GameLift {
namespace Internal {
bool UpdateGameSessionMessage::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
// Serialize game session object
writer->String(GAME_SESSION);
writer->StartObject();
m_gameSession.Serialize(writer);
writer->EndObject();
// Serialize update game session fields
JsonHelper::WriteNonEmptyString(writer, UPDATE_REASON, m_updateReason);
JsonHelper::WriteNonEmptyString(writer, BACKFILL_TICKET_ID, m_backfillTicketId);
return true;
}
bool UpdateGameSessionMessage::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
// Deserialize GameSession
WebSocketGameSession gameSession;
if (value.HasMember(GAME_SESSION) && !value[GAME_SESSION].IsNull()) {
gameSession.Deserialize(value[GAME_SESSION]);
}
m_gameSession = gameSession;
// Deserialize rest of fields
m_updateReason = JsonHelper::SafelyDeserializeString(value, UPDATE_REASON);
m_backfillTicketId = JsonHelper::SafelyDeserializeString(value, BACKFILL_TICKET_ID);
return true;
}
std::ostream &operator<<(std::ostream &os, const UpdateGameSessionMessage &updateGameSessionMessage) {
const Message *message = &updateGameSessionMessage;
os << message->Serialize();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,40 @@
/*
* 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 <aws/gamelift/internal/model/request/AcceptPlayerSessionRequest.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
namespace Aws {
namespace GameLift {
namespace Internal {
bool AcceptPlayerSessionRequest::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
JsonHelper::WriteNonEmptyString(writer, GAME_SESSION_ID, m_gameSessionId);
JsonHelper::WriteNonEmptyString(writer, PLAYER_SESSION_ID, m_playerSessionId);
return true;
}
bool AcceptPlayerSessionRequest::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
m_gameSessionId = JsonHelper::SafelyDeserializeString(value, GAME_SESSION_ID);
m_playerSessionId = JsonHelper::SafelyDeserializeString(value, PLAYER_SESSION_ID);
return true;
}
std::ostream &operator<<(std::ostream &os, const AcceptPlayerSessionRequest &acceptPlayerSessionRequest) {
const Message *message = &acceptPlayerSessionRequest;
os << message->Serialize();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,44 @@
/*
* 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 <aws/gamelift/internal/model/request/ActivateGameSessionRequest.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
namespace Aws {
namespace GameLift {
namespace Internal {
ActivateGameSessionRequest::ActivateGameSessionRequest() { SetAction(ACTIVATE_GAME_SESSION); }
ActivateGameSessionRequest::ActivateGameSessionRequest(std::string gameSessionId) : m_gameSessionId(gameSessionId) { SetAction(ACTIVATE_GAME_SESSION); }
bool ActivateGameSessionRequest::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
JsonHelper::WriteNonEmptyString(writer, GAME_SESSION_ID, m_gameSessionId);
return true;
}
bool ActivateGameSessionRequest::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
m_gameSessionId = JsonHelper::SafelyDeserializeString(value, GAME_SESSION_ID);
return true;
}
std::ostream &operator<<(std::ostream &os, const ActivateGameSessionRequest &activateGameSessionRequest) {
const Message *message = &activateGameSessionRequest;
os << message->Serialize();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,59 @@
/*
* 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 <aws/gamelift/internal/model/request/ActivateServerProcessRequest.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
namespace Aws {
namespace GameLift {
namespace Internal {
ActivateServerProcessRequest::ActivateServerProcessRequest() : m_port(-1) { SetAction(ACTIVATE_SERVER_PROCESS); }
ActivateServerProcessRequest::ActivateServerProcessRequest(std::string sdkVersion, std::string sdkLanguage,
std::string sdkToolName, std::string sdkToolVersion, int port,
const Aws::GameLift::Server::LogParameters &logParameters)
: m_sdkVersion(sdkVersion), m_sdkLanguage(sdkLanguage), m_sdkToolName(sdkToolName), m_sdkToolVersion(sdkToolVersion), m_port(port), m_logParameters(logParameters) {
SetAction(ACTIVATE_SERVER_PROCESS);
};
bool ActivateServerProcessRequest::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
JsonHelper::WriteNonEmptyString(writer, SDK_VERSION, m_sdkVersion);
JsonHelper::WriteNonEmptyString(writer, SDK_LANGUAGE, m_sdkLanguage);
JsonHelper::WriteNonEmptyString(writer, SDK_TOOL_NAME, m_sdkToolName);
JsonHelper::WriteNonEmptyString(writer, SDK_TOOL_VERSION, m_sdkToolVersion);
JsonHelper::WritePositiveInt(writer, PORT, m_port);
JsonHelper::WriteLogParameters(writer, LOG_PATHS, m_logParameters);
return true;
}
bool ActivateServerProcessRequest::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
m_sdkVersion = JsonHelper::SafelyDeserializeString(value, SDK_VERSION);
m_sdkLanguage = JsonHelper::SafelyDeserializeString(value, SDK_LANGUAGE);
m_sdkToolName = JsonHelper::SafelyDeserializeString(value, SDK_TOOL_NAME);
m_sdkToolVersion = JsonHelper::SafelyDeserializeString(value, SDK_TOOL_VERSION);
m_port = JsonHelper::SafelyDeserializeInt(value, PORT);
m_logParameters = JsonHelper::SafelyDeserializeLogParameters(value, LOG_PATHS);
return true;
}
std::ostream &operator<<(std::ostream &os, const ActivateServerProcessRequest &activateServerProcessRequest) {
const Message *message = &activateServerProcessRequest;
os << message->Serialize();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,40 @@
/*
* 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 <aws/gamelift/internal/model/request/HeartbeatServerProcessRequest.h>
namespace Aws {
namespace GameLift {
namespace Internal {
bool HeartbeatServerProcessRequest::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
writer->String(HEALTH_STATUS);
writer->Bool(m_healthy);
return true;
}
bool HeartbeatServerProcessRequest::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
m_healthy = value.HasMember(HEALTH_STATUS) && value[HEALTH_STATUS].IsBool() ? value[HEALTH_STATUS].GetBool() : false;
return true;
}
std::ostream &operator<<(std::ostream &os, const HeartbeatServerProcessRequest &heartbeatServerProcessRequest) {
const Message *message = &heartbeatServerProcessRequest;
os << message->Serialize();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,40 @@
/*
* 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 <aws/gamelift/internal/model/request/RemovePlayerSessionRequest.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
namespace Aws {
namespace GameLift {
namespace Internal {
bool RemovePlayerSessionRequest::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
JsonHelper::WriteNonEmptyString(writer, GAME_SESSION_ID, m_gameSessionId);
JsonHelper::WriteNonEmptyString(writer, PLAYER_SESSION_ID, m_playerSessionId);
return true;
}
bool RemovePlayerSessionRequest::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
m_gameSessionId = JsonHelper::SafelyDeserializeString(value, GAME_SESSION_ID);
m_playerSessionId = JsonHelper::SafelyDeserializeString(value, PLAYER_SESSION_ID);
return true;
}
std::ostream &operator<<(std::ostream &os, const RemovePlayerSessionRequest &removePlayerSessionRequest) {
const Message *message = &removePlayerSessionRequest;
os << message->Serialize();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,23 @@
/*
* 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 <aws/gamelift/internal/model/request/TerminateServerProcessRequest.h>
namespace Aws {
namespace GameLift {
namespace Internal {
TerminateServerProcessRequest::TerminateServerProcessRequest() { SetAction(TERMINATE_SERVER_PROCESS); }
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,42 @@
/*
* 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 <aws/gamelift/internal/model/request/UpdatePlayerSessionCreationPolicyRequest.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
namespace Aws {
namespace GameLift {
namespace Internal {
bool UpdatePlayerSessionCreationPolicyRequest::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
JsonHelper::WriteNonEmptyString(writer, GAME_SESSION_ID, m_gameSessionId);
JsonHelper::WriteNonEmptyString(writer, PLAYER_SESSION_POLICY, GetPlayerSessionCreationPolicyAsString());
return true;
}
bool UpdatePlayerSessionCreationPolicyRequest::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
m_gameSessionId = JsonHelper::SafelyDeserializeString(value, GAME_SESSION_ID);
std::string policyAsString = JsonHelper::SafelyDeserializeString(value, PLAYER_SESSION_POLICY);
SetPlayerSessionCreationPolicy(policyAsString);
return true;
}
std::ostream &operator<<(std::ostream &os, const UpdatePlayerSessionCreationPolicyRequest &updatePlayerSessionCreationPolicyRequest) {
const Message *message = &updatePlayerSessionCreationPolicyRequest;
os << message->Serialize();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,52 @@
/*
* 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 <aws/gamelift/internal/model/request/WebSocketDescribePlayerSessionsRequest.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
namespace Aws {
namespace GameLift {
namespace Internal {
bool WebSocketDescribePlayerSessionsRequest::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
JsonHelper::WriteNonEmptyString(writer, GAME_SESSION_ID, m_gameSessionId);
JsonHelper::WriteNonEmptyString(writer, PLAYER_ID, m_playerId);
JsonHelper::WriteNonEmptyString(writer, PLAYER_SESSION_ID, m_playerSessionId);
JsonHelper::WriteNonEmptyString(writer, PLAYER_SESSION_STATUS_FILTER, m_playerSessionStatusFilter);
JsonHelper::WriteNonEmptyString(writer, NEXT_TOKEN, m_nextToken);
JsonHelper::WritePositiveInt(writer, LIMIT, m_limit);
return true;
}
bool WebSocketDescribePlayerSessionsRequest::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
m_gameSessionId = JsonHelper::SafelyDeserializeString(value, GAME_SESSION_ID);
m_playerId = JsonHelper::SafelyDeserializeString(value, PLAYER_ID);
m_playerSessionId = JsonHelper::SafelyDeserializeString(value, PLAYER_SESSION_ID);
m_playerSessionStatusFilter = JsonHelper::SafelyDeserializeString(value, PLAYER_SESSION_STATUS_FILTER);
m_nextToken = JsonHelper::SafelyDeserializeString(value, NEXT_TOKEN);
m_limit = JsonHelper::SafelyDeserializeInt(value, LIMIT);
return true;
}
std::ostream &operator<<(std::ostream &os, const WebSocketDescribePlayerSessionsRequest &describePlayerSessionsRequest) {
const Message *message = &describePlayerSessionsRequest;
os << message->Serialize();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,35 @@
/*
* 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 <aws/gamelift/internal/model/request/WebSocketGetComputeCertificateRequest.h>
namespace Aws {
namespace GameLift {
namespace Internal {
bool WebSocketGetComputeCertificateRequest::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
return true;
}
bool WebSocketGetComputeCertificateRequest::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
return true;
}
std::ostream &operator<<(std::ostream &os, const WebSocketGetComputeCertificateRequest &getComputeCertificateRequest) {
const Message *message = &getComputeCertificateRequest;
os << message->Serialize();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,38 @@
/*
* 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 <aws/gamelift/internal/model/request/WebSocketGetFleetRoleCredentialsRequest.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
namespace Aws {
namespace GameLift {
namespace Internal {
bool WebSocketGetFleetRoleCredentialsRequest::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
JsonHelper::WriteNonEmptyString(writer, ROLE_ARN, m_roleArn);
JsonHelper::WriteNonEmptyString(writer, ROLE_SESSION_NAME, m_roleSessionName);
return true;
}
bool WebSocketGetFleetRoleCredentialsRequest::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
m_roleArn = JsonHelper::SafelyDeserializeString(value, ROLE_ARN);
m_roleSessionName = JsonHelper::SafelyDeserializeString(value, ROLE_SESSION_NAME);
return true;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,71 @@
/*
* 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 <aws/gamelift/internal/model/request/WebSocketStartMatchBackfillRequest.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
namespace Aws {
namespace GameLift {
namespace Internal {
WebSocketStartMatchBackfillRequest::WebSocketStartMatchBackfillRequest() { SetAction(ACTION); };
bool WebSocketStartMatchBackfillRequest::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
JsonHelper::WriteNonEmptyString(writer, TICKET_ID, m_ticketId);
JsonHelper::WriteNonEmptyString(writer, GAME_SESSION_ARN, m_gameSessionArn);
JsonHelper::WriteNonEmptyString(writer, MATCHMAKING_CONFIGURATION_ARN, m_matchmakingConfigurationArn);
writer->String(PLAYERS);
writer->StartArray();
for (const WebSocketPlayer &player : m_players) {
writer->StartObject();
player.Serialize(writer);
writer->EndObject();
}
writer->EndArray();
return true;
}
bool WebSocketStartMatchBackfillRequest::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
m_ticketId = JsonHelper::SafelyDeserializeString(value, TICKET_ID);
m_gameSessionArn = JsonHelper::SafelyDeserializeString(value, GAME_SESSION_ARN);
m_matchmakingConfigurationArn = JsonHelper::SafelyDeserializeString(value, MATCHMAKING_CONFIGURATION_ARN);
m_players.clear();
if (value.HasMember(PLAYERS) && !value[PLAYERS].IsNull()) {
auto playerList = value[PLAYERS].GetArray();
for (rapidjson::SizeType i = 0; i < playerList.Size(); i++) {
auto &player = playerList[i];
if (!player.IsNull()) {
WebSocketPlayer webSocketPlayer;
webSocketPlayer.Deserialize(player);
m_players.push_back(webSocketPlayer);
}
}
}
return true;
}
std::ostream &operator<<(std::ostream &os, const WebSocketStartMatchBackfillRequest &startMatchBackfillRequest) {
const Message *message = &startMatchBackfillRequest;
os << message->Serialize();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,44 @@
/*
* 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 <aws/gamelift/internal/model/request/WebSocketStopMatchBackfillRequest.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
namespace Aws {
namespace GameLift {
namespace Internal {
bool WebSocketStopMatchBackfillRequest::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
JsonHelper::WriteNonEmptyString(writer, GAME_SESSION_ARN, m_gameSessionArn);
JsonHelper::WriteNonEmptyString(writer, MATCHMAKING_CONFIG_ARN, m_matchmakingConfigurationArn);
JsonHelper::WriteNonEmptyString(writer, TICKET_ID, m_ticketId);
return true;
}
bool WebSocketStopMatchBackfillRequest::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
m_gameSessionArn = JsonHelper::SafelyDeserializeString(value, GAME_SESSION_ARN);
m_matchmakingConfigurationArn = JsonHelper::SafelyDeserializeString(value, MATCHMAKING_CONFIG_ARN);
m_ticketId = JsonHelper::SafelyDeserializeString(value, TICKET_ID);
return true;
}
std::ostream &operator<<(std::ostream &os, const WebSocketStopMatchBackfillRequest &stopMatchBackfillMessage) {
const Message *message = &stopMatchBackfillMessage;
os << message->Serialize();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,63 @@
/*
* 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 <aws/gamelift/internal/model/response/WebSocketDescribePlayerSessionsResponse.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
namespace Aws {
namespace GameLift {
namespace Internal {
bool WebSocketDescribePlayerSessionsResponse::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
JsonHelper::WriteNonEmptyString(writer, NEXT_TOKEN, m_nextToken);
writer->String(PLAYER_SESSIONS);
writer->StartArray();
for (const WebSocketPlayerSession &playerSession : m_playerSessions) {
writer->StartObject();
playerSession.Serialize(writer);
writer->EndObject();
}
writer->EndArray();
return true;
}
bool WebSocketDescribePlayerSessionsResponse::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
m_nextToken = JsonHelper::SafelyDeserializeString(value, NEXT_TOKEN);
m_playerSessions.clear();
if (value.HasMember(PLAYER_SESSIONS) && !value[PLAYER_SESSIONS].IsNull()) {
auto playerSessions = value[PLAYER_SESSIONS].GetArray();
for (rapidjson::SizeType i = 0; i < playerSessions.Size(); i++) {
auto &playerSession = playerSessions[i];
if (!playerSession.IsNull()) {
WebSocketPlayerSession webSocketPlayerSession;
webSocketPlayerSession.Deserialize(playerSession);
m_playerSessions.push_back(webSocketPlayerSession);
}
}
}
return true;
}
std::ostream &operator<<(std::ostream &os, const WebSocketDescribePlayerSessionsResponse &describePlayerSessionsResponse) {
const Message *message = &describePlayerSessionsResponse;
os << message->Serialize();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,44 @@
/*
* 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 <aws/gamelift/internal/model/response/WebSocketGetComputeCertificateResponse.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
namespace Aws {
namespace GameLift {
namespace Internal {
bool WebSocketGetComputeCertificateResponse::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
JsonHelper::WriteNonEmptyString(writer, COMPUTE_NAME, m_computeName);
JsonHelper::WriteNonEmptyString(writer, CERTIFICATE_PATH, m_certificatePath);
return true;
}
bool WebSocketGetComputeCertificateResponse::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
m_computeName = JsonHelper::SafelyDeserializeString(value, COMPUTE_NAME);
m_certificatePath = JsonHelper::SafelyDeserializeString(value, CERTIFICATE_PATH);
return true;
}
std::ostream &operator<<(std::ostream &os, const WebSocketGetComputeCertificateResponse &webSocketGetComputeCertificateResponse) {
const Message *message = &webSocketGetComputeCertificateResponse;
os << message->Serialize();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,46 @@
/*
* 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 <aws/gamelift/internal/model/response/WebSocketGetFleetRoleCredentialsResponse.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
namespace Aws {
namespace GameLift {
namespace Internal {
bool WebSocketGetFleetRoleCredentialsResponse::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
JsonHelper::WriteNonEmptyString(writer, ASSUMED_ROLE_USER_ARN, m_assumedRoleUserArn);
JsonHelper::WriteNonEmptyString(writer, ASSUMED_ROLE_ID, m_assumedRoleId);
JsonHelper::WriteNonEmptyString(writer, ACCESS_KEY_ID, m_accessKeyId);
JsonHelper::WriteNonEmptyString(writer, SECRET_ACCESS_KEY, m_secretAccessKey);
JsonHelper::WriteNonEmptyString(writer, SESSION_TOKEN, m_sessionToken);
JsonHelper::WritePositiveInt64(writer, EXPIRATION, m_expiration);
return true;
}
bool WebSocketGetFleetRoleCredentialsResponse::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
m_assumedRoleUserArn = JsonHelper::SafelyDeserializeString(value, ASSUMED_ROLE_USER_ARN);
m_assumedRoleId = JsonHelper::SafelyDeserializeString(value, ASSUMED_ROLE_ID);
m_accessKeyId = JsonHelper::SafelyDeserializeString(value, ACCESS_KEY_ID);
m_secretAccessKey = JsonHelper::SafelyDeserializeString(value, SECRET_ACCESS_KEY);
m_sessionToken = JsonHelper::SafelyDeserializeString(value, SESSION_TOKEN);
m_expiration = JsonHelper::SafelyDeserializeInt64(value, EXPIRATION);
return true;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,38 @@
/*
* 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 <aws/gamelift/internal/model/response/WebSocketStartMatchBackfillResponse.h>
#include <aws/gamelift/internal/util/JsonHelper.h>
namespace Aws {
namespace GameLift {
namespace Internal {
bool WebSocketStartMatchBackfillResponse::Serialize(rapidjson::Writer<rapidjson::StringBuffer> *writer) const {
Message::Serialize(writer);
JsonHelper::WriteNonEmptyString(writer, TICKET_ID, m_ticketId);
return true;
}
bool WebSocketStartMatchBackfillResponse::Deserialize(const rapidjson::Value &value) {
Message::Deserialize(value);
m_ticketId = JsonHelper::SafelyDeserializeString(value, TICKET_ID);
return true;
}
std::ostream &operator<<(std::ostream &os, const WebSocketStartMatchBackfillResponse &startMatchBackfillResponse) {
const Message *message = &startMatchBackfillResponse;
os << message->Serialize();
return os;
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,83 @@
/*
* 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 <aws/gamelift/internal/network/GameLiftWebSocketClientManager.h>
#include <aws/gamelift/internal/GameLiftServerState.h>
#include <aws/gamelift/internal/model/response/WebSocketDescribePlayerSessionsResponse.h>
#include <aws/gamelift/internal/util/RandomStringGenerator.h>
#include <spdlog/spdlog.h>
using namespace Aws::GameLift;
namespace Aws {
namespace GameLift {
namespace Internal {
GenericOutcome GameLiftWebSocketClientManager::Connect(std::string websocketUrl, const std::string &authToken, const std::string &processId,
const std::string &hostId, const std::string &fleetId, const std::map<std::string, std::string> &sigV4QueryParameters) {
spdlog::info("Connecting to GameLift WebSocket server. websocketUrl: {}, processId: {}, hostId: {}, fleetId: {}",
websocketUrl.c_str(), processId.c_str(), hostId.c_str(), fleetId.c_str());
// Due to the websocket library we're using, base URLs must end with a "/". Ensure that it is
// present.
if (!EndsWith(websocketUrl, REQUIRED_URL_ENDING)) {
websocketUrl = websocketUrl + REQUIRED_URL_ENDING;
}
// Build the WebSocket URI
std::string sdkVersion = Server::GetSdkVersion().GetResult();
std::string idempotencyToken = RandomStringGenerator::GenerateRandomAlphaNumericString(32);
Uri::UriBuilder uriBuilder = Uri::UriBuilder()
.WithBaseUri(websocketUrl)
.AddQueryParam(PID_KEY, processId)
.AddQueryParam(SDK_VERSION_KEY, sdkVersion)
.AddQueryParam(FLAVOR_KEY, GameLiftServerState::LANGUAGE)
.AddQueryParam(COMPUTE_ID_KEY, hostId)
.AddQueryParam(FLEET_ID_KEY, fleetId)
.AddQueryParam(IDEMPOTENCY_TOKEN_KEY, idempotencyToken);
if (!authToken.empty()) {
uriBuilder.AddQueryParam(AUTH_TOKEN_KEY, authToken);
} else if (!sigV4QueryParameters.empty()) {
for (auto sigV4QueryParameter: sigV4QueryParameters) {
uriBuilder.AddQueryParam(sigV4QueryParameter.first, sigV4QueryParameter.second);
}
}
Uri uri = uriBuilder.Build();
// delegate to the websocket client wrapper to connect
return m_webSocketClientWrapper->Connect(uri);
}
GenericOutcome GameLiftWebSocketClientManager::SendSocketMessage(Message &message) {
// Serialize the message
std::string jsonMessage = message.Serialize();
GenericOutcome outcome = m_webSocketClientWrapper->SendSocketMessage(message.GetRequestId(), jsonMessage);
return outcome;
}
void GameLiftWebSocketClientManager::Disconnect() { m_webSocketClientWrapper->Disconnect(); }
bool GameLiftWebSocketClientManager::EndsWith(const std::string &actualString, const std::string &ending) {
const int lengthDifference = (int)(actualString.length() - ending.length());
if (lengthDifference >= 0) {
return actualString.compare(lengthDifference, ending.length(), ending) == 0;
} else {
return false;
}
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,430 @@
/*
* 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 <aws/gamelift/internal/network/WebSocketppClientWrapper.h>
#include <aws/gamelift/internal/model/Message.h>
#include <aws/gamelift/internal/model/ResponseMessage.h>
#include <aws/gamelift/internal/retry/GeometricBackoffRetryStrategy.h>
#include <aws/gamelift/internal/retry/RetryingCallable.h>
#include <memory>
#include <websocketpp/error.hpp>
#include <spdlog/spdlog.h>
namespace Aws {
namespace GameLift {
namespace Internal {
WebSocketppClientWrapper::WebSocketppClientWrapper(std::shared_ptr<WebSocketppClientType> webSocketClient)
: m_webSocketClient(webSocketClient), m_connectionStateChanged(false) {
// configure logging. comment these out to get websocket logs on stdout for debugging
m_webSocketClient->clear_access_channels(websocketpp::log::alevel::all);
m_webSocketClient->clear_error_channels(websocketpp::log::elevel::all);
// initialize ASIO
m_webSocketClient->init_asio();
// start in perpetual mode (do not exit processing loop when there are no connections)
m_webSocketClient->start_perpetual();
// Kick off two threads that will own up to two parallel connections. These threads will live
// until the SDK is shutdown and alternate handling connections. If a connection fails to open,
// the thread will not hang. Instead, the thread will simply wait for another connection to
// appear in queue.
//
// Flow of logic in the two threads:
// --- SDK initialized ---
// socket_thread_1: waiting for connection...
// socket_thread_2: waiting for connection...
// --- Initial connection happens ---
// socket_thread_1: handling 1st connection
// socket_thread_2: waiting for connection...
// --- Connection refresh begins ---
// socket_thread_1: finish handling 1st connection messages
// socket_thread_2: handling 2nd connection
// --- Connection 1 closes ---
// socket_thread_1: waiting for connection...
// socket_thread_2: handling 2nd connection
// --- SDK shut down, and WebSocket client "->stop_perpetual()" is invoked ---
// socket_thread_1: No longer waits for a connection, thread ends
// socket_thread_2: Finishes handling 2nd connection, then thread ends
m_socket_thread_1 = std::unique_ptr<std::thread>(new std::thread([this] { m_webSocketClient->run(); }));
m_socket_thread_2 = std::unique_ptr<std::thread>(new std::thread([this] { m_webSocketClient->run(); }));
// Set callbacks
using std::placeholders::_1;
using std::placeholders::_2;
// Set timeout waiting for GameLift websocket server to respond on initial connection.
// See: https://github.com/zaphoyd/websocketpp/blob/master/websocketpp/connection.hpp#L501
m_webSocketClient->set_open_handshake_timeout(WEBSOCKET_OPEN_HANDSHAKE_TIMEOUT_MILLIS);
m_webSocketClient->set_tls_init_handler(std::bind(&WebSocketppClientWrapper::OnTlsInit, this, _1));
m_webSocketClient->set_open_handler(std::bind(&WebSocketppClientWrapper::OnConnected, this, _1));
m_webSocketClient->set_message_handler(std::bind(&WebSocketppClientWrapper::OnMessage, this, _1, _2));
m_webSocketClient->set_fail_handler(std::bind(&WebSocketppClientWrapper::OnError, this, _1));
m_webSocketClient->set_close_handler(std::bind(&WebSocketppClientWrapper::OnClose, this, _1));
m_webSocketClient->set_interrupt_handler(std::bind(&WebSocketppClientWrapper::OnInterrupt, this, _1));
}
WebSocketppClientWrapper::~WebSocketppClientWrapper() {
// stop perpetual mode, allowing the websocketClient to destroy itself
if (m_webSocketClient) {
m_webSocketClient->stop_perpetual();
}
spdlog::info("Destroying WebsocketPPClientWrapper");
// close connections and join the thread
if (m_connection && m_connection->get_state() == websocketpp::session::state::open) {
Disconnect();
}
if (m_socket_thread_1 && m_socket_thread_1->joinable()) {
m_socket_thread_1->join();
}
if (m_socket_thread_2 && m_socket_thread_2->joinable()) {
m_socket_thread_2->join();
}
}
GenericOutcome WebSocketppClientWrapper::Connect(const Uri &uri) {
spdlog::info("Opening Connection");
// Perform connection with retries.
// This attempts to start up a new websocket connection / thread
m_uri = uri;
websocketpp::lib::error_code errorCode;
GeometricBackoffRetryStrategy retryStrategy;
RetryingCallable callable = RetryingCallable::Builder()
.WithRetryStrategy(&retryStrategy)
.WithCallable([this, &uri, &errorCode] {
spdlog::info("Attempting to perform connection");
WebSocketppClientType::connection_ptr newConnection = PerformConnect(uri, errorCode);
if (newConnection && newConnection->get_state() == websocketpp::session::state::open) {
spdlog::info("Connection established, transitioning traffic");
// "Flip" traffic from our old websocket to our new websocket. Close the old one
// if necessary
WebSocketppClientType::connection_ptr oldConnection = m_connection;
m_connection = newConnection;
if (oldConnection && oldConnection->get_state() == websocketpp::session::state::open) {
spdlog::info("Closing previous connection");
websocketpp::lib::error_code closeErrorCode;
m_webSocketClient->close(oldConnection->get_handle(), websocketpp::close::status::going_away,
"Websocket client reconnecting", closeErrorCode);
if (errorCode.value()) {
spdlog::warn("Failed to close old websocket after a connection refresh, ignoring.");
}
}
return true;
} else {
spdlog::warn("Connection to Amazon GameLift Servers websocket server failed. Retrying connection if possible.");
return false;
}
})
.Build();
callable.call();
if (IsConnected()) {
spdlog::info("Connected to endpoint");
return GenericOutcome(nullptr);
} else {
spdlog::error("Connection to Amazon GameLift Servers websocket server failed. See error message in InitSDK() outcome for details.");
m_connection = nullptr;
switch (errorCode.value()) {
case websocketpp::error::server_only:
switch (m_fail_response_code) {
case websocketpp::http::status_code::value::forbidden:
return GenericOutcome(GAMELIFT_ERROR_TYPE::WEBSOCKET_CONNECT_FAILURE_FORBIDDEN);
default:
return GenericOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::WEBSOCKET_CONNECT_FAILURE, errorCode.category().message(errorCode.value()).c_str(),
errorCode.message().c_str()));
}
case 11001: // Host not found
case websocketpp::error::invalid_uri:
return GenericOutcome(GAMELIFT_ERROR_TYPE::WEBSOCKET_CONNECT_FAILURE_INVALID_URL);
// case websocketpp::error::timeout:
case 0: // No Response after multiple retries, i.e. timeout
case websocketpp::error::open_handshake_timeout:
case websocketpp::error::close_handshake_timeout:
return GenericOutcome(GAMELIFT_ERROR_TYPE::WEBSOCKET_CONNECT_FAILURE_TIMEOUT);
case websocketpp::error::endpoint_not_secure:
case websocketpp::error::no_outgoing_buffers:
case websocketpp::error::no_incoming_buffers:
case websocketpp::error::invalid_state:
case websocketpp::error::bad_close_code:
case websocketpp::error::reserved_close_code:
case websocketpp::error::invalid_close_code:
case websocketpp::error::invalid_utf8:
case websocketpp::error::invalid_subprotocol:
case websocketpp::error::bad_connection:
case websocketpp::error::con_creation_failed:
case websocketpp::error::unrequested_subprotocol:
case websocketpp::error::client_only:
case websocketpp::error::http_connection_ended:
case websocketpp::error::invalid_port:
case websocketpp::error::async_accept_not_listening:
case websocketpp::error::upgrade_required:
case websocketpp::error::invalid_version:
case websocketpp::error::unsupported_version:
case websocketpp::error::http_parse_error:
case websocketpp::error::extension_neg_failed:
default:
return GenericOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::WEBSOCKET_CONNECT_FAILURE, errorCode.category().message(errorCode.value()).c_str(),
errorCode.message().c_str()));
}
}
}
WebSocketppClientType::connection_ptr WebSocketppClientWrapper::PerformConnect(const Uri &uri, websocketpp::lib::error_code &errorCode) {
spdlog::info("Performing connection");
errorCode.clear();
// Create connection request
WebSocketppClientType::connection_ptr newConnection = m_webSocketClient->get_connection(uri.GetUriString(), errorCode);
if (errorCode.value()) {
spdlog::error("Failed to GetConnection. ERROR: {}", errorCode.message());
return newConnection;
} else {
spdlog::info("Connection request created successfully. Waiting for connection to establish...");
}
// Queue a new connection request (the socket thread will act on it and attempt to connect)
try {
m_webSocketClient->connect(newConnection);
}
catch (const std::exception& e) {
spdlog::error("Exception while trying to connect with the webSocketClient: {}", e.what());
}
spdlog::info("Connection request queued.");
// Wait for connection to succeed or fail (this makes connection synchronous)
{
std::unique_lock<std::mutex> lk(m_lock);
m_cond.wait(lk, [this] { return m_connectionStateChanged; });
spdlog::info("Connection state changed: {}", m_fail_error_code.message());
errorCode = m_fail_error_code;
// Reset
m_connectionStateChanged = false;
m_fail_error_code.clear();
}
if (errorCode.value()) {
spdlog::error("Connection failed with errorCode: {}", errorCode.message());
}
else {
spdlog::info("Connection established successfully.");
}
return newConnection;
}
GenericOutcome WebSocketppClientWrapper::SendSocketMessage(const std::string &requestId, const std::string &message) {
if (requestId.empty()) {
spdlog::error("Request does not have request ID, cannot process");
return GenericOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::INTERNAL_SERVICE_EXCEPTION));
}
auto waitForReconnectRetryCount = 0;
while(!IsConnected()) {
// m_connection will be null if reconnect failed after max reties
if(m_connection == nullptr || ++waitForReconnectRetryCount >= WAIT_FOR_RECONNECT_MAX_RETRIES) {
spdlog::warn("WebSocket is not connected... WebSocket failed to send message due to an error.");
return GenericOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::WEBSOCKET_SEND_MESSAGE_FAILURE));
}
spdlog::warn("WebSocket is not connected... isConnected: {}", IsConnected());
std::this_thread::sleep_for(std::chrono::seconds(WAIT_FOR_RECONNECT_RETRY_DELAY_SECONDS));
}
std::future<GenericOutcome> responseFuture;
// Lock whenever we make use of 'm_requestIdToPromise' to avoid concurrent writes/reads
{
std::lock_guard<std::mutex> lock(m_requestToPromiseLock);
// This indicates we've already sent this message, and it's still in flight
if (m_requestIdToPromise.count(requestId) > 0) {
spdlog::error("Request {} already exists", requestId);
return GenericOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::BAD_REQUEST_EXCEPTION));
}
std::promise<GenericOutcome> responsePromise;
responseFuture = responsePromise.get_future();
m_requestIdToPromise[requestId] = std::move(responsePromise);
}
GenericOutcome immediateResponse = SendSocketMessageAsync(message);
if (!immediateResponse.IsSuccess()) {
spdlog::error("Send Socket Message immediate response failed with error {}: {}",
immediateResponse.GetError().GetErrorName(), immediateResponse.GetError().GetErrorMessage());
std::lock_guard<std::mutex> lock(m_requestToPromiseLock);
m_requestIdToPromise.erase(requestId);
return immediateResponse;
}
std::future_status promiseStatus = responseFuture.wait_for(std::chrono::milliseconds(SERVICE_CALL_TIMEOUT_MILLIS));
if (promiseStatus == std::future_status::timeout) {
std::lock_guard<std::mutex> lock(m_requestToPromiseLock);
spdlog::error("Response not received within the time limit of {} ms for request {}", SERVICE_CALL_TIMEOUT_MILLIS, requestId);
spdlog::warn("isConnected: {}", IsConnected());
m_requestIdToPromise.erase(requestId);
// If a call times out, retry
return GenericOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::WEBSOCKET_RETRIABLE_SEND_MESSAGE_FAILURE));
}
return responseFuture.get();
}
GenericOutcome WebSocketppClientWrapper::SendSocketMessageAsync(const std::string &message) {
if (!m_connection) {
spdlog::error("Cannot send message: m_connection is null");
return GenericOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::WEBSOCKET_SEND_MESSAGE_FAILURE));
}
spdlog::info("Sending Socket Message, isConnected:{}", IsConnected());
websocketpp::lib::error_code errorCode;
m_webSocketClient->send(m_connection->get_handle(), message.c_str(), websocketpp::frame::opcode::text, errorCode);
if (errorCode.value()) {
spdlog::error("Error Sending Socket Message: {}", errorCode.value());
switch (errorCode.value()) {
case websocketpp::error::no_outgoing_buffers:
// If buffers are full, send will fail. Retryable since buffers can free up as messages
// send.
return GenericOutcome(GAMELIFT_ERROR_TYPE::WEBSOCKET_RETRIABLE_SEND_MESSAGE_FAILURE);
default:
return GenericOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::WEBSOCKET_SEND_MESSAGE_FAILURE, errorCode.category().message(errorCode.value()).c_str(),
errorCode.message().c_str()));
}
}
return GenericOutcome(nullptr);
}
void WebSocketppClientWrapper::Disconnect() {
spdlog::info("Disconnecting WebSocket");
if (m_connection != nullptr) {
websocketpp::lib::error_code ec;
m_webSocketClient->close(m_connection->get_handle(), websocketpp::close::status::going_away, "Websocket client closing", ec);
if (ec) {
spdlog::error("Error initiating close: {}",ec.message());
}
m_connection = nullptr;
}
}
void WebSocketppClientWrapper::RegisterGameLiftCallback(const std::string &gameLiftEvent, const std::function<GenericOutcome(std::string)> &callback) {
spdlog::info("Registering GameLift CallBack for: {}", gameLiftEvent);
m_eventHandlers[gameLiftEvent] = callback;
}
bool WebSocketppClientWrapper::IsConnected() {
// m_connection is nullptr if 'm_webSocketClient->get_connection()' fails
return m_connection != nullptr && m_connection->get_state() == websocketpp::session::state::open;
}
void WebSocketppClientWrapper::OnConnected(websocketpp::connection_hdl connection) {
spdlog::info("Connected to WebSocket");
// aquire lock and set condition variables (let main thread know connection is successful)
{
std::lock_guard<std::mutex> lk(m_lock);
// set the state change variables and notify the thread that is connecting
m_connectionStateChanged = true;
}
m_cond.notify_one();
}
void WebSocketppClientWrapper::OnError(websocketpp::connection_hdl connection) {
auto con = m_webSocketClient->get_con_from_hdl(connection);
spdlog::error("Error Connecting to WebSocket");
// aquire lock and set condition variables (let main thread know an error has occurred)
{
std::lock_guard<std::mutex> lk(m_lock);
// set the state change variables and notify the thread that is connecting
m_connectionStateChanged = true;
m_fail_error_code = con->get_ec();
m_fail_response_code = con->get_response_code();
}
m_cond.notify_one();
}
void WebSocketppClientWrapper::OnMessage(websocketpp::connection_hdl connection, websocketpp::config::asio_client::message_type::ptr msg) {
std::string message = msg->get_payload();
spdlog::info("Received message from websocket endpoint");
ResponseMessage responseMessage;
Message &gameLiftMessage = responseMessage;
if (!gameLiftMessage.Deserialize(message)) {
spdlog::error("Error Deserializing Message");
return;
}
const std::string &action = responseMessage.GetAction();
spdlog::info("Deserialized Message has Action: {}", action);
const std::string &requestId = responseMessage.GetRequestId();
const int statusCode = responseMessage.GetStatusCode();
const std::string &errorMessage = responseMessage.GetErrorMessage();
// Default to a success response with no result pointer
GenericOutcome response(nullptr);
// Check if the response was an error. If so, update the response based on status code.
// RequestId will be empty when we get a message not associated with a request, in which case we
// don't expect a 200 status code either.
if (statusCode != OK_STATUS_CODE && !requestId.empty()) {
response = GenericOutcome(GameLiftError(statusCode, message.c_str()));
} else {
// If we got a success response, and we have a special event handler for this action, invoke
// it to get the real parsed result
if (m_eventHandlers.count(action)) {
spdlog::info("Executing Amazon GameLift Servers Event Handler for {}", action);
response = m_eventHandlers[action](message);
}
}
// Lock whenever we make use of 'm_requestIdToPromise' to avoid concurrent writes/reads
std::lock_guard<std::mutex> lock(m_requestToPromiseLock);
if (m_requestIdToPromise.count(requestId) > 0) {
m_requestIdToPromise[requestId].set_value(response);
m_requestIdToPromise.erase(requestId);
}
}
websocketpp::lib::shared_ptr<asio::ssl::context> WebSocketppClientWrapper::OnTlsInit(websocketpp::connection_hdl hdl) {
websocketpp::lib::shared_ptr<asio::ssl::context> contextPtr(new asio::ssl::context(asio::ssl::context::tlsv12));
return contextPtr;
}
void WebSocketppClientWrapper::OnClose(websocketpp::connection_hdl connection) {
auto connectionPointer = m_webSocketClient->get_con_from_hdl(connection);
auto localCloseCode = connectionPointer->get_local_close_code();
auto remoteCloseCode = connectionPointer->get_remote_close_code();
bool isNormalClosure = localCloseCode == websocketpp::close::status::normal
|| localCloseCode == websocketpp::close::status::going_away
|| remoteCloseCode == websocketpp::close::status::normal
|| remoteCloseCode == websocketpp::close::status::going_away;
spdlog::info("Connection to Amazon GameLift Servers websocket server lost, Local Close Code = {}, Remote Close Code = {}.",
websocketpp::close::status::get_string(localCloseCode).c_str(),
websocketpp::close::status::get_string(remoteCloseCode).c_str());
if(isNormalClosure) {
spdlog::info("Normal Connection Closure, skipping reconnect.");
return;
} else {
spdlog::info("Abnormal Connection Closure, reconnecting.");
WebSocketppClientWrapper::Connect(m_uri);
}
}
void WebSocketppClientWrapper::OnInterrupt(websocketpp::connection_hdl connection) {
auto connectionPointer = m_webSocketClient->get_con_from_hdl(connection);
auto remoteEndpoint = connectionPointer->get_remote_endpoint();
auto host = connectionPointer->get_host();
auto port = connectionPointer->get_port();
spdlog::warn("Interruption Happened");
spdlog::info("In OnInterrupt(), isConnected:{}, endpoint: {}, host: {}, port: {}", IsConnected(), remoteEndpoint, host, port);
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,54 @@
/*
* 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 <aws/gamelift/internal/network/callback/CreateGameSessionCallback.h>
#include <aws/gamelift/internal/model/message/CreateGameSessionMessage.h>
#include <spdlog/spdlog.h>
using namespace Aws::GameLift;
namespace Aws {
namespace GameLift {
namespace Internal {
GenericOutcome CreateGameSessionCallback::OnStartGameSession(const std::string &data) {
spdlog::info("OnStartGameSession Received with raw data: {}", data);
CreateGameSessionMessage createGameSessionMessage;
Message &message = createGameSessionMessage;
message.Deserialize(data);
GameSession gameSession;
gameSession.WithGameSessionId(createGameSessionMessage.GetGameSessionId().c_str())
.WithName(createGameSessionMessage.GetGameSessionName().c_str())
.WithMaximumPlayerSessionCount(createGameSessionMessage.GetMaximumPlayerSessionCount())
.WithIpAddress(createGameSessionMessage.GetIpAddress().c_str())
.WithPort(createGameSessionMessage.GetPort())
.WithGameSessionData(createGameSessionMessage.GetGameSessionData().c_str())
.WithMatchmakerData(createGameSessionMessage.GetMatchmakerData().c_str())
.WithDnsName(createGameSessionMessage.GetDnsName().c_str());
std::map<std::string, std::string>::const_iterator mapIterator;
for (mapIterator = createGameSessionMessage.GetGameProperties().begin(); mapIterator != createGameSessionMessage.GetGameProperties().end(); mapIterator++) {
GameProperty gameProperty;
gameProperty.SetKey(mapIterator->first.c_str());
gameProperty.SetValue(mapIterator->second.c_str());
gameSession.AddGameProperty(gameProperty);
}
m_gameLiftMessageHandler->OnStartGameSession(gameSession);
return GenericOutcome(nullptr);
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,32 @@
/*
* 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 <aws/gamelift/internal/network/callback/DescribePlayerSessionsCallback.h>
#include <aws/gamelift/internal/model/response/WebSocketDescribePlayerSessionsResponse.h>
#include <spdlog/spdlog.h>
using namespace Aws::GameLift;
namespace Aws {
namespace GameLift {
namespace Internal {
GenericOutcome DescribePlayerSessionsCallback::OnDescribePlayerSessions(const std::string &data) {
spdlog::info("OnDescribePlayerSessions Received with raw data: {}", data);
WebSocketDescribePlayerSessionsResponse *describePlayerSessionsResponse = new WebSocketDescribePlayerSessionsResponse();
Message *message = describePlayerSessionsResponse;
message->Deserialize(data);
return GenericOutcome(describePlayerSessionsResponse);
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,32 @@
/*
* 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 <aws/gamelift/internal/network/callback/GetComputeCertificateCallback.h>
#include <aws/gamelift/internal/model/response/WebSocketGetComputeCertificateResponse.h>
#include <spdlog/spdlog.h>
using namespace Aws::GameLift;
namespace Aws {
namespace GameLift {
namespace Internal {
GenericOutcome GetComputeCertificateCallback::OnGetComputeCertificateCallback(const std::string &data) {
spdlog::info("OnGetComputeCertificate Received");
auto *response = new WebSocketGetComputeCertificateResponse();
Message *message = response;
message->Deserialize(data);
return GenericOutcome(response);
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,32 @@
/*
* 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 <aws/gamelift/internal/network/callback/GetFleetRoleCredentialsCallback.h>
#include <aws/gamelift/internal/model/response/WebSocketGetFleetRoleCredentialsResponse.h>
#include <spdlog/spdlog.h>
using namespace Aws::GameLift;
namespace Aws {
namespace GameLift {
namespace Internal {
GenericOutcome GetFleetRoleCredentialsCallback::OnGetFleetRoleCredentials(const std::string &data) {
spdlog::info("OnGetFleetRoleCredentials Received");
auto *getFleetRoleCredentialsResponse = new WebSocketGetFleetRoleCredentialsResponse();
Message *message = getFleetRoleCredentialsResponse;
message->Deserialize(data);
return GenericOutcome(getFleetRoleCredentialsResponse);
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,34 @@
/*
* 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 <aws/gamelift/internal/network/callback/RefreshConnectionCallback.h>
#include <aws/gamelift/internal/model/message/RefreshConnectionMessage.h>
#include <spdlog/spdlog.h>
using namespace Aws::GameLift;
namespace Aws {
namespace GameLift {
namespace Internal {
GenericOutcome RefreshConnectionCallback::OnRefreshConnection(const std::string &data) {
spdlog::info("OnRefreshConnection Received with raw data: {}", data);
RefreshConnectionMessage refreshConnectionMessage;
Message &message = refreshConnectionMessage;
message.Deserialize(data);
m_gameLiftMessageHandler->OnRefreshConnection(refreshConnectionMessage.GetRefreshConnectionEndpoint(), refreshConnectionMessage.GetAuthToken());
return GenericOutcome(nullptr);
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,32 @@
/*
* 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 <aws/gamelift/internal/network/callback/StartMatchBackfillCallback.h>
#include <aws/gamelift/internal/model/response/WebSocketStartMatchBackfillResponse.h>
#include <spdlog/spdlog.h>
using namespace Aws::GameLift;
namespace Aws {
namespace GameLift {
namespace Internal {
GenericOutcome StartMatchBackfillCallback::OnStartMatchBackfill(const std::string &data) {
spdlog::info("OnStartMatchBackfill Received with raw data: {}", data);
WebSocketStartMatchBackfillResponse *startMatchBackfillResponse = new WebSocketStartMatchBackfillResponse();
Message *message = startMatchBackfillResponse;
message->Deserialize(data);
return GenericOutcome(startMatchBackfillResponse);
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,37 @@
/*
* 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 <aws/gamelift/internal/network/callback/TerminateProcessCallback.h>
#include <aws/gamelift/internal/model/message/TerminateProcessMessage.h>
#include <spdlog/spdlog.h>
using namespace Aws::GameLift;
namespace Aws {
namespace GameLift {
namespace Internal {
GenericOutcome TerminateProcessCallback::OnTerminateProcess(const std::string &data) {
spdlog::info("OnTerminateProcess Received with raw data: {}", data);
TerminateProcessMessage terminateProcessMessage;
Message &message = terminateProcessMessage;
message.Deserialize(data);
long terminationTime = terminateProcessMessage.GetTerminationTime();
m_gameLiftMessageHandler->OnTerminateProcess(terminationTime);
return GenericOutcome(nullptr);
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,59 @@
/*
* 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 <aws/gamelift/internal/network/callback/UpdateGameSessionCallback.h>
#include <aws/gamelift/internal/model/message/UpdateGameSessionMessage.h>
#include <spdlog/spdlog.h>
using namespace Aws::GameLift;
namespace Aws {
namespace GameLift {
namespace Internal {
GenericOutcome UpdateGameSessionCallback::OnUpdateGameSession(const std::string &data) {
spdlog::info("OnUpdateGameSession Received with raw data: {}", data);
UpdateGameSessionMessage updateGameSessionMessage;
Message &message = updateGameSessionMessage;
message.Deserialize(data);
GameSession gameSession;
WebSocketGameSession webSocketGameSession = updateGameSessionMessage.GetGameSession();
gameSession.WithGameSessionId(webSocketGameSession.GetGameSessionId().c_str())
.WithName(webSocketGameSession.GetName().c_str())
.WithFleetId(webSocketGameSession.GetFleetId().c_str())
.WithMaximumPlayerSessionCount(webSocketGameSession.GetMaximumPlayerSessionCount())
.WithIpAddress(webSocketGameSession.GetIpAddress().c_str())
.WithPort(webSocketGameSession.GetPort())
.WithGameSessionData(webSocketGameSession.GetGameSessionData().c_str())
.WithMatchmakerData(webSocketGameSession.GetMatchmakerData().c_str())
.WithDnsName(webSocketGameSession.GetDnsName().c_str());
std::map<std::string, std::string>::const_iterator mapIterator;
for (mapIterator = webSocketGameSession.GetGameProperties().begin(); mapIterator != webSocketGameSession.GetGameProperties().end(); mapIterator++) {
GameProperty gameProperty;
gameProperty.SetKey(mapIterator->first.c_str());
gameProperty.SetValue(mapIterator->second.c_str());
gameSession.AddGameProperty(gameProperty);
}
UpdateGameSession updateGameSession(gameSession, UpdateReasonMapper::GetUpdateReasonForName(updateGameSessionMessage.GetUpdateReason().c_str()),
updateGameSessionMessage.GetBackfillTicketId().c_str());
m_gameLiftMessageHandler->OnUpdateGameSession(updateGameSession);
return GenericOutcome(nullptr);
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,38 @@
/*
* 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 <aws/gamelift/internal/retry/GeometricBackoffRetryStrategy.h>
#include <thread>
#include <spdlog/spdlog.h>
namespace Aws {
namespace GameLift {
namespace Internal {
void GeometricBackoffRetryStrategy::apply(const std::function<bool(void)> &callable) {
int retryIntervalSeconds = m_initialRetryIntervalSeconds;
for (int i = 0; i < m_maxRetries; ++i) {
bool success = callable();
if (success) {
break;
} else {
spdlog::warn("Connection Failed. Retrying in {} seconds...", retryIntervalSeconds);
std::this_thread::sleep_for(std::chrono::seconds(retryIntervalSeconds));
retryIntervalSeconds *= m_retryFactor;
retryIntervalSeconds = retryIntervalSeconds > m_maxRetryIntervalSeconds ? m_maxRetryIntervalSeconds : retryIntervalSeconds;
}
}
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,42 @@
/*
* 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 <aws/gamelift/internal/retry/JitteredGeometricBackoffRetryStrategy.h>
#include <random>
#include <thread>
#include <spdlog/spdlog.h>
namespace Aws {
namespace GameLift {
namespace Internal {
void JitteredGeometricBackoffRetryStrategy::apply(const std::function<bool(void)> &callable) {
int retryIntervalMs = m_initialRetryIntervalMs;
std::random_device rd;
std::mt19937 randGenerator(rd());
for (int i = 0; i < m_maxRetries; ++i) {
bool success = callable();
if (success) {
break;
} else {
std::uniform_int_distribution<> intervalRange(m_minRetryDelayMs, retryIntervalMs);
int currentInterval = intervalRange(randGenerator);
spdlog::warn("Sending Message Failed. Retrying in {} milliseconds...", currentInterval);
std::this_thread::sleep_for(std::chrono::milliseconds(currentInterval));
retryIntervalMs *= m_retryFactor;
}
}
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,38 @@
/*
* 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 <aws/gamelift/internal/retry/RetryingCallable.h>
#include <thread>
namespace Aws {
namespace GameLift {
namespace Internal {
RetryingCallable::Builder &RetryingCallable::Builder::WithCallable(const std::function<bool(void)> &callable) {
m_callable = callable;
return *this;
}
RetryingCallable::Builder &RetryingCallable::Builder::WithRetryStrategy(RetryStrategy *retryStrategy) {
m_retryStrategy = retryStrategy;
return *this;
}
RetryingCallable RetryingCallable::Builder::Build() const { return RetryingCallable(*m_retryStrategy, m_callable); }
RetryingCallable::RetryingCallable(RetryStrategy &retryStrategy, std::function<bool(void)> callable) : m_retryStrategy(retryStrategy), m_callable(callable) {}
void RetryingCallable::call() { m_retryStrategy.apply(m_callable); }
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,186 @@
/*
* 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 <aws/gamelift/internal/security/AwsSigV4Utility.h>
#include <aws/gamelift/internal/util/UriEncoder.h>
#include <openssl/opensslv.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <iomanip>
#include <sstream>
#include <stdexcept>
#include <algorithm>
#include <vector>
using namespace Aws::GameLift::Internal;
Aws::GameLift::Outcome<std::map<std::string, std::string>, std::string> AwsSigV4Utility::GenerateSigV4QueryParameters(const SigV4Parameters &parameters) {
try {
ValidateParameters(parameters);
} catch (std::invalid_argument const &ex) {
return std::string(ex.what());
}
char dateBuffer[16];
char dateTimeBuffer[32];
strftime(dateBuffer, sizeof(dateBuffer), DateFormat, &parameters.RequestTime);
strftime(dateTimeBuffer, sizeof(dateTimeBuffer), DateTimeFormat, &parameters.RequestTime);
const std::string formattedRequestDate(dateBuffer);
const std::string formattedRequestDateTime(dateTimeBuffer);
const std::string canonicalRequest = ToSortedEncodedQueryString(parameters.QueryParams);
const std::string hashedCanonicalRequest = ComputeSha256Hash(canonicalRequest);
const std::string scope = formattedRequestDate + "/" + parameters.AwsRegion + "/" + ServiceName + "/" + TerminationString;
const std::string stringToSign = std::string(Algorithm) + "\n" + formattedRequestDateTime + "\n" + scope + "\n" + hashedCanonicalRequest;
const std::string credential = parameters.Credentials.AccessKey + "/" + scope;
const std::string signature = GenerateSignature(
parameters.AwsRegion,
parameters.Credentials.SecretKey,
formattedRequestDate,
ServiceName,
stringToSign);
return GenerateSigV4QueryParameters(
credential,
formattedRequestDateTime,
parameters.Credentials.SessionToken,
signature);
}
void AwsSigV4Utility::ValidateParameters(const SigV4Parameters &parameters) {
if (parameters.AwsRegion.empty()) {
throw std::invalid_argument("AwsRegion is required");
}
if (parameters.Credentials.AccessKey.empty()) {
throw std::invalid_argument("AccessKey is required");
}
if (parameters.Credentials.SecretKey.empty()) {
throw std::invalid_argument("SecretKey is required");
}
if (parameters.QueryParams.empty()) {
throw std::invalid_argument("QueryParams is required");
}
if (parameters.RequestTime.tm_year == 0) {
throw std::invalid_argument("RequestTime is required");
}
}
std::string AwsSigV4Utility::GenerateSignature(
const std::string &region,
const std::string &secretKey,
const std::string &formattedRequestDateTime,
const std::string &serviceName,
const std::string &stringToSign) {
std::vector<uint8_t> encodedKeySecret = std::vector<uint8_t>(SignatureSecretKeyPrefix, SignatureSecretKeyPrefix +
strlen(SignatureSecretKeyPrefix));
encodedKeySecret.insert(encodedKeySecret.end(), secretKey.begin(), secretKey.end());
auto hashDate = ComputeHmacSha256(encodedKeySecret, formattedRequestDateTime);
auto hashRegion = ComputeHmacSha256(hashDate, region);
auto hashService = ComputeHmacSha256(hashRegion, serviceName);
auto signingKey = ComputeHmacSha256(hashService, TerminationString);
return ToHex(ComputeHmacSha256(signingKey, stringToSign));
}
std::map<std::string, std::string> AwsSigV4Utility::GenerateSigV4QueryParameters(
const std::string &credential,
const std::string &formattedRequestDateTime,
const std::string &sessionToken,
const std::string &signature) {
std::map<std::string, std::string> sigV4QueryParameters;
sigV4QueryParameters[AuthorizationKey] = AuthorizationValue;
sigV4QueryParameters[AmzAlgorithmKey] = Algorithm;
sigV4QueryParameters[AmzCredentialKey] = UriEncoder::UriEncode(credential);
sigV4QueryParameters[AmzDateKey] = formattedRequestDateTime;
sigV4QueryParameters[AmzSignatureKey] = UriEncoder::UriEncode(signature);
if (!sessionToken.empty()) {
sigV4QueryParameters[AmzSecurityTokenHeadersKey] = UriEncoder::UriEncode(sessionToken);
}
return sigV4QueryParameters;
}
std::string AwsSigV4Utility::ToSortedEncodedQueryString(const std::map<std::string, std::string> &queryParameters) {
std::vector<std::pair<std::string, std::string> > sortedParams(queryParameters.begin(), queryParameters.end());
std::sort(sortedParams.begin(), sortedParams.end());
std::ostringstream stringBuffer;
for (auto sortedParam = sortedParams.begin(); sortedParam != sortedParams.end(); ++sortedParam) {
if (sortedParam != sortedParams.begin()) {
stringBuffer << "&";
}
stringBuffer << UriEncoder::UriEncode(sortedParam->first) << "=" << UriEncoder::UriEncode(sortedParam->second);
}
return stringBuffer.str();
}
// Refer to documentation in AwsSigV4Utility.h
std::string AwsSigV4Utility::ComputeSha256Hash(const std::string &data) {
// Because the following methods do not throw exceptions, they are not being surrounded by try-catch or use RAII for cleaning memory.
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, data.c_str(), data.size());
SHA256_Final(hash, &sha256);
const std::vector<uint8_t> sha256Hash = std::vector<uint8_t>(hash, hash + SHA256_DIGEST_LENGTH);
return ToHex(sha256Hash);
}
// Refer to documentation in AwsSigV4Utility.h
std::vector<uint8_t> AwsSigV4Utility::ComputeHmacSha256(const std::vector<uint8_t> &key, const std::string &data) {
// Because the following methods do not throw exceptions, they are not being surrounded by try-catch or use RAII for cleaning memory.
unsigned char hash[EVP_MAX_MD_SIZE];
unsigned int len = 0;
#if OPENSSL_VERSION_NUMBER >= 0x10100000L // OpenSSL 1.1.0 or newer
HMAC_CTX *ctx = HMAC_CTX_new();
HMAC_Init_ex(ctx, key.data(), key.size(), EVP_sha256(), nullptr);
HMAC_Update(ctx, reinterpret_cast<const unsigned char *>(data.c_str()), data.size());
HMAC_Final(ctx, hash, &len);
HMAC_CTX_free(ctx);
#else // Older versions of OpenSSL
HMAC_CTX ctx;
HMAC_CTX_init(&ctx);
HMAC_Init_ex(&ctx, key.data(), key.size(), EVP_sha256(), nullptr);
HMAC_Update(&ctx, reinterpret_cast<const unsigned char *>(data.c_str()), data.size());
HMAC_Final(&ctx, hash, &len);
HMAC_CTX_cleanup(&ctx);
#endif
return std::vector<uint8_t>(hash, hash + len);
}
std::string AwsSigV4Utility::ToHex(const std::vector<uint8_t> &hashBytes) {
std::ostringstream stringBuffer;
for (auto b: hashBytes) {
stringBuffer << std::hex << std::setw(2) << std::setfill('0') << (int) b;
}
return stringBuffer.str();
}

View File

@@ -0,0 +1,61 @@
/*
* 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 <aws/gamelift/internal/security/ContainerCredentialsFetcher.h>
#include <stdexcept>
#include <cstdlib>
#include "rapidjson/document.h"
using namespace Aws::GameLift::Internal;
ContainerCredentialsFetcher::ContainerCredentialsFetcher(HttpClient &httpClient)
: httpClient(httpClient) {}
Aws::GameLift::Outcome<AwsCredentials, std::string> ContainerCredentialsFetcher::FetchContainerCredentials() {
const char *credentialsRelativeUri = std::getenv(EnvironmentVariableContainerCredentialsRelativeUri.c_str());
if (!credentialsRelativeUri) {
return "The environment variable " + EnvironmentVariableContainerCredentialsRelativeUri + " is not set.";
}
std::string relativeUri = credentialsRelativeUri;
HttpResponse response = httpClient.SendGetRequest(ContainerCredentialProviderUrl + relativeUri);
if (!response.IsSuccessfulStatusCode()) {
return std::string(
"Failed to get Container Credentials from Container Credential Provider. HTTP Response Status Code is " +
std::to_string(response.statusCode));
}
rapidjson::Document document;
if (document.Parse(response.body.c_str()).HasParseError()) {
return std::string("Error parsing Container Credential Provider JSON response");
}
if (!document.HasMember("AccessKeyId") || !document["AccessKeyId"].IsString()) {
return std::string("AccessKeyId is not found in Container Credential Provider response");
}
if (!document.HasMember("SecretAccessKey") || !document["SecretAccessKey"].IsString()) {
return std::string("SecretAccessKey is not found in Container Credential Provider response");
}
if (!document.HasMember("Token") || !document["Token"].IsString()) {
return std::string("Token is not found in Container Credential Provider response");
}
AwsCredentials awsCredentials(
document["AccessKeyId"].GetString(),
document["SecretAccessKey"].GetString(),
document["Token"].GetString());
return awsCredentials;
}

View File

@@ -0,0 +1,52 @@
/*
* 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 <aws/gamelift/internal/security/ContainerMetadataFetcher.h>
#include <stdexcept>
#include <cstdlib>
#include "rapidjson/document.h"
using namespace Aws::GameLift::Internal;
ContainerMetadataFetcher::ContainerMetadataFetcher(HttpClient &httpClient)
: httpClient(httpClient) {}
Aws::GameLift::Outcome<ContainerTaskMetadata, std::string> ContainerMetadataFetcher::FetchContainerTaskMetadata() {
const char *containerMetadataUri = std::getenv(EnvironmentVariableContainerMetadataUri.c_str());
if (!containerMetadataUri) {
return "The environment variable " + EnvironmentVariableContainerMetadataUri + " is not set.";
}
std::string baseUrl = containerMetadataUri;
HttpResponse response = httpClient.SendGetRequest(baseUrl + "/" + TaskMetadataRelativePath);
if(!response.IsSuccessfulStatusCode()) {
return std::string(
"Failed to get Container Task Metadata from Container Metadata Service. HTTP Response Status Code is " +
std::to_string(response.statusCode));
}
rapidjson::Document document;
if (document.Parse(response.body.c_str()).HasParseError()) {
return std::string("Error parsing Container Metadata Service JSON response");
}
if (!document.HasMember("TaskARN") || !document["TaskARN"].IsString()) {
return std::string("TaskArn is not found in Container Metadata Service response");
}
std::string taskArn = document["TaskARN"].GetString();
if (taskArn.empty()) {
return std::string("Invalid TaskARN, value is empty");
}
if (taskArn.find('/') == std::string::npos) {
return std::string("Failed to extract Task ID from container TaskArn with value " + taskArn);
}
std::string taskId = taskArn.substr(taskArn.find_last_of('/') + 1);
return ContainerTaskMetadata(taskId);
}

View File

@@ -0,0 +1,47 @@
/*
* 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 <aws/gamelift/internal/util/GuidGenerator.h>
#include <iostream>
#include <string>
#include <random>
#include <sstream>
#include <iomanip>
using namespace Aws::GameLift::Internal;
std::string GuidGenerator::GenerateGuid() {
std::random_device rd;
std::uniform_int_distribution<int> dist(0, 15);
std::uniform_int_distribution<int> dist2(8, 11);
std::stringstream ss;
ss << std::hex << std::setfill('0');
for (int i = 0; i < 8; ++i) ss << dist(rd);
ss << "-";
for (int i = 0; i < 4; ++i) ss << dist(rd);
ss << "-";
ss << "4";
for (int i = 0; i < 3; ++i) ss << dist(rd);
ss << "-";
ss << dist2(rd);
for (int i = 0; i < 3; ++i) ss << dist(rd);
ss << "-";
for (int i = 0; i < 12; ++i) ss << dist(rd);
return ss.str();
}

View File

@@ -0,0 +1,195 @@
/*
* 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 <aws/gamelift/internal/util/HttpClient.h>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
using ssize_t = SSIZE_T;
#else
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#endif
#include <stdexcept>
#include <spdlog/spdlog.h>
using namespace Aws::GameLift::Internal;
bool HttpResponse::IsSuccessfulStatusCode() {
return statusCode >= 200 && statusCode <= 299;
}
HttpResponse HttpClient::SendGetRequest(const std::string &url) {
const std::tuple<std::string, int, std::string> hostAndPortAndPath = GetHostAndPortAndPath(url);
const std::string host = std::get<0>(hostAndPortAndPath);
const int port = std::get<1>(hostAndPortAndPath);
const std::string path = std::get<2>(hostAndPortAndPath);
const std::string request = "GET " + path + " HTTP/1.1\r\nHost: " + host + "\r\nConnection: close\r\n\r\n";
int sock = -1;
#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
throw std::runtime_error("WSAStartup failed, error number: " + std::to_string(WSAGetLastError()));
}
#endif
try {
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
#ifdef _WIN32
throw std::runtime_error("Socket creation failed, error number: " + std::to_string(WSAGetLastError()));
#else
throw std::runtime_error("Socket creation failed, error number: " + std::string(strerror(errno)));
#endif
}
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
#ifdef _WIN32
std::wstring wide_host = std::wstring(host.begin(), host.end());
if (InetPtonW(AF_INET, wide_host.c_str(), &server_addr.sin_addr) <= 0) {
throw std::runtime_error("Invalid address or address not supported, error number: " + std::to_string(WSAGetLastError()));
}
#else
if (inet_pton(AF_INET, host.c_str(), &server_addr.sin_addr) <= 0) {
throw std::runtime_error("Invalid address or address not supported, error number: " + std::string(strerror(errno)));
}
#endif
if (connect(sock, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
#ifdef _WIN32
throw std::runtime_error("Connection failed, error number: " + std::to_string(WSAGetLastError()));
#else
throw std::runtime_error("Connection failed, error number: " + std::string(strerror(errno)));
#endif
}
ssize_t sent = send(sock, request.c_str(), request.length(), 0);
if (sent < 0) {
#ifdef _WIN32
throw std::runtime_error("Send failed, error number: " + std::to_string(WSAGetLastError()));
#else
throw std::runtime_error("Send failed, error number: " + std::string(strerror(errno)));
#endif
} else if (sent != (ssize_t) request.length()) {
throw std::runtime_error("Send incomplete, only " + std::to_string(sent) + " bytes sent.");
}
std::string fullResponse;
char buffer[1024] = {0};
ssize_t bytesReceived;
while ((bytesReceived = recv(sock, buffer, sizeof(buffer) - 1, 0)) > 0) {
fullResponse.append(buffer, bytesReceived);
}
if (bytesReceived < 0) {
#ifdef _WIN32
throw std::runtime_error("Receive failed, error number: " + std::to_string(WSAGetLastError()));
#else
throw std::runtime_error("Receive failed, error number: " + std::string(strerror(errno)));
#endif
}
#ifdef _WIN32
if (closesocket(sock) < 0) {
spdlog::warn("Socket close failed, error number: {}", WSAGetLastError());
}
WSACleanup();
#else
if (close(sock) < 0) {
spdlog::warn("Socket close failed, error number: {}", errno);
}
#endif
sock = -1;
HttpResponse httpResponse = ParseHttpResponse(fullResponse);
return httpResponse;
} catch (const std::runtime_error& e) {
if (sock >= 0) {
#ifdef _WIN32
closesocket(sock);
WSACleanup();
#else
close(sock);
#endif
}
throw e;
}
}
std::tuple<std::string, int, std::string> HttpClient::GetHostAndPortAndPath(const std::string &url) {
std::string host;
std::string path;
int port = 80;
std::size_t protocolPos = url.find("://");
if (protocolPos == std::string::npos) {
throw std::runtime_error("Invalid URL: Missing protocol");
}
std::size_t hostStart = protocolPos + 3;
if (hostStart >= url.size()) {
throw std::runtime_error("Invalid URL: Host is missing");
}
std::size_t pathPos = url.find('/', hostStart);
std::size_t portPos = url.find(':', hostStart);
if (portPos != std::string::npos && (pathPos == std::string::npos || portPos < pathPos)) {
host = url.substr(hostStart, portPos - hostStart);
std::size_t portEnd = (pathPos == std::string::npos) ? url.size() : pathPos;
port = std::stoi(url.substr(portPos + 1, portEnd - portPos - 1));
} else {
if (pathPos == std::string::npos) {
host = url.substr(hostStart);
path = "/";
} else {
host = url.substr(hostStart, pathPos - hostStart);
}
}
if (path.empty()) {
if (pathPos != std::string::npos) {
path = url.substr(pathPos);
} else {
path = "/";
}
}
return std::make_tuple(host, port, path);
}
HttpResponse HttpClient::ParseHttpResponse(const std::string &response) {
HttpResponse httpResponse;
std::size_t statusCodeStart = response.find("HTTP/1.1 ") + 9;
std::size_t statusCodeEnd = response.find(" ", statusCodeStart);
if (statusCodeStart != std::string::npos && statusCodeEnd != std::string::npos) {
httpResponse.statusCode = std::stoi(response.substr(statusCodeStart, statusCodeEnd - statusCodeStart));
}
std::size_t bodyStart = response.find("\r\n\r\n");
if (bodyStart != std::string::npos) {
httpResponse.body = response.substr(bodyStart + 4);
}
if (response.find("Transfer-Encoding: chunked") != std::string::npos
&& response.find("Content-Type: application/json") != std::string::npos) {
int jsonStart = httpResponse.body.find("{");
int jsonEnd = httpResponse.body.find_last_of("}");
if (jsonStart != std::string::npos && jsonEnd != std::string::npos) {
httpResponse.body = httpResponse.body.substr(jsonStart, jsonEnd - jsonStart + 2);
}
}
return httpResponse;
}

View File

@@ -0,0 +1,107 @@
/*
* 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 <aws/gamelift/internal/util/JsonHelper.h>
using namespace Aws::GameLift::Internal;
std::string JsonHelper::SafelyDeserializeString(const rapidjson::Value &value, const char *key) {
return value.HasMember(key) && value[key].IsString() ? value[key].GetString() : "";
}
void JsonHelper::WriteNonEmptyString(rapidjson::Writer<rapidjson::StringBuffer> *writer, const char *key, const std::string &value) {
if (!value.empty()) {
writer->String(key);
writer->String(value.c_str());
}
}
int JsonHelper::SafelyDeserializeInt(const rapidjson::Value &value, const char *key) {
return value.HasMember(key) && value[key].IsInt() ? value[key].GetInt() : -1;
}
void JsonHelper::WritePositiveInt(rapidjson::Writer<rapidjson::StringBuffer> *writer, const char *key, int value) {
if (value > 0) {
writer->String(key);
writer->Int(value);
}
}
int64_t JsonHelper::SafelyDeserializeInt64(const rapidjson::Value &value, const char *key) {
return value.HasMember(key) && value[key].IsInt64() ? value[key].GetInt64() : -1;
}
void JsonHelper::WritePositiveInt64(rapidjson::Writer<rapidjson::StringBuffer> *writer, const char *key, int64_t value) {
if (value > 0) {
writer->String(key);
writer->Int64(value);
}
}
Aws::GameLift::Server::LogParameters JsonHelper::SafelyDeserializeLogParameters(const rapidjson::Value &value, const char *key) {
if (!value.HasMember(key) || !value[key].IsArray() || value[key].Size() == 0) {
return Aws::GameLift::Server::LogParameters();
}
const rapidjson::SizeType numLogPaths = value[key].Size();
#ifdef GAMELIFT_USE_STD
std::vector<std::string> logPaths = std::vector<std::string>();
#else
char **logPaths = new char *[numLogPaths];
#endif
for (rapidjson::SizeType i = 0; i < numLogPaths; i++) {
if (value[key][i].IsString()) {
#ifdef GAMELIFT_USE_STD
logPaths.push_back(value[key][i].GetString());
#else
logPaths[i] = new char[MAX_PATH_LENGTH];
#ifdef WIN32
strcpy_s(logPaths[i], MAX_PATH_LENGTH, value[key][i].GetString());
#else
strncpy(logPaths[i], value[key][i].GetString(), MAX_PATH_LENGTH);
#endif
#endif
}
}
#ifdef GAMELIFT_USE_STD
return Aws::GameLift::Server::LogParameters(logPaths);
#else
Aws::GameLift::Server::LogParameters toReturn(logPaths, numLogPaths);
for (rapidjson::SizeType i = 0; i < numLogPaths; i++) {
delete[] logPaths[i];
}
delete[] logPaths;
return toReturn;
#endif
}
void JsonHelper::WriteLogParameters(rapidjson::Writer<rapidjson::StringBuffer> *writer, const char *key, const Aws::GameLift::Server::LogParameters &value) {
if (value.getLogPathCount() == 0) {
return;
}
writer->String(key);
writer->StartArray();
for (size_t i = 0; i < value.getLogPathCount(); i++) {
const char *logPath = value.getLogPath(i);
// If logPath is not empty, write it.
if (logPath && logPath[0]) {
writer->String(value.getLogPath(i));
}
}
writer->EndArray();
}

View File

@@ -0,0 +1,52 @@
/*
* 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 <aws/gamelift/internal/util/LoggerHelper.h>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/rotating_file_sink.h>
using namespace Aws::GameLift::Internal;
#ifdef GAMELIFT_USE_STD
void LoggerHelper::InitializeLogger(const std::string& process_Id) {
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
std::string serverSdkLog = "logs/gamelift-server-sdk-";
serverSdkLog.append(process_Id).append(".log");
auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(serverSdkLog, 10485760, 5);
console_sink->set_pattern("%^[%Y-%m-%d %H:%M:%S] [%l] %v%$");
file_sink->set_pattern("[%Y-%m-%d %H:%M:%S] [%l] %v");
spdlog::logger logger("multi_sink", { console_sink, file_sink });
logger.set_level(spdlog::level::info);
logger.flush_on(spdlog::level::info);
spdlog::set_default_logger(std::make_shared<spdlog::logger>(logger));
}
#else
void LoggerHelper::InitializeLogger(const char* process_Id) {
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
std::string serverSdkLog = "logs/gamelift-server-sdk-";
serverSdkLog.append(process_Id).append(".log");
auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(serverSdkLog, 10485760, 5);
console_sink->set_pattern("%^[%Y-%m-%d %H:%M:%S] [%l] %v%$");
file_sink->set_pattern("[%Y-%m-%d %H:%M:%S] [%l] %v");
spdlog::logger logger("multi_sink", { console_sink, file_sink });
logger.set_level(spdlog::level::info);
logger.flush_on(spdlog::level::info);
spdlog::set_default_logger(std::make_shared<spdlog::logger>(logger));
}
#endif

View File

@@ -0,0 +1,40 @@
/*
* 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 <aws/gamelift/internal/util/RandomStringGenerator.h>
#include <random>
#include <sstream>
namespace Aws {
namespace GameLift {
namespace Internal {
std::string RandomStringGenerator::GenerateRandomAlphaNumericString(const int stringLength) {
std::stringstream ss;
static const char alphaNumChars[] = "0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
static thread_local std::mt19937 randomNumberGenerator(std::random_device{}());
// Distribution has an inclusive upper bound. "alphaNumChars" will end in a null terminator, so
// we use "sizeof() - 2" to avoid out of bounds errors, and also to avoid including the null
// terminator.
std::uniform_int_distribution<int> distribution(0, sizeof(alphaNumChars) - 2);
for (int i = 0; i < stringLength; i++) {
ss << alphaNumChars[distribution(randomNumberGenerator)];
}
return ss.str();
}
} // namespace Internal
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,31 @@
/*
* 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 <aws/gamelift/internal/util/UriEncoder.h>
#include <sstream>
#include <iomanip>
using namespace Aws::GameLift::Internal;
std::string UriEncoder::UriEncode(const std::string &value) {
std::ostringstream escaped;
escaped.fill('0');
escaped << std::hex << std::uppercase;
for (char c: value) {
if (isalnum(static_cast<unsigned char>(c)) || c == '-' || c == '_' || c == '.' || c == '~') {
escaped << c;
} else {
escaped << '%' << std::setw(2) << static_cast<int>(static_cast<unsigned char>(c));
}
}
return escaped.str();
}

View File

@@ -0,0 +1,102 @@
/*
* 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 <aws/gamelift/metrics/Combiner.h>
#include <cassert>
void Combiner::Add(MetricMessage message) {
if (message.IsGauge()) {
// We have to do a bit more work for gauges to convert GaugeAdd to GaugeSet
UpdateGauge(message);
} else {
auto it = m_combinedMessages.find(message.Metric);
if (it == std::end(m_combinedMessages)) {
m_combinedMessages.emplace(message.Metric, message);
return;
}
assert(message.IsCounter() || message.IsTimer());
if (message.IsCounter()) {
UpdateCounter(it->second, message);
} else if (message.IsTimer()) {
UpdateTimer(it->second, message);
}
}
}
void Combiner::UpdateGauge(const MetricMessage &message) {
assert(message.Type == MetricMessageType::GaugeSet ||
message.Type == MetricMessageType::GaugeAdd);
if (message.Type == MetricMessageType::GaugeSet) {
// If setting - we just use the new value
m_combinedMessages[message.Metric] = message;
m_gaugeHistory[message.Metric] = message;
} else if (message.Type == MetricMessageType::GaugeAdd) {
// If adding - we get historic value (if available) and add to it
// if there's no historic value, we just add to 0
double currentValue = 0;
auto it = m_gaugeHistory.find(message.Metric);
if (it != std::end(m_gaugeHistory)) {
currentValue = it->second.SubmitDouble.Value;
}
currentValue += message.SubmitDouble.Value;
auto newMessage = MetricMessage::GaugeSet(*message.Metric, currentValue);
m_combinedMessages[message.Metric] = newMessage;
m_gaugeHistory[message.Metric] = newMessage;
}
}
void Combiner::UpdateCounter(MetricMessage &current,
const MetricMessage &newMessage) {
assert(newMessage.Type == MetricMessageType::CounterAdd);
// Just sum the values.
current.SubmitDouble.Value += newMessage.SubmitDouble.Value;
}
void Combiner::UpdateTimer(MetricMessage &current,
const MetricMessage &newMessage) {
assert(newMessage.Type == MetricMessageType::TimerSet);
// Grab or init sample count
auto sampleCountIt = m_timerSampleCount.find(current.Metric);
if (sampleCountIt != std::end(m_timerSampleCount)) {
++(sampleCountIt->second);
} else {
// This is the second sample being added, hence we initialize it to 2.
auto pair = m_timerSampleCount.emplace(current.Metric, 2);
const bool insertSuccess = pair.second;
assert(insertSuccess);
sampleCountIt = pair.first;
}
// Welford's algorithm
// numerically stable mean
//
// Knuth - TACOP Vol 2 pg. 216
//
// https://nullbuffer.com/articles/welford_algorithm.html
const double update =
(newMessage.SubmitDouble.Value - current.SubmitDouble.Value) /
sampleCountIt->second;
current.SubmitDouble.Value += update;
}
void Combiner::Clear() {
m_combinedMessages.clear();
m_timerSampleCount.clear();
}

View File

@@ -0,0 +1,145 @@
/*
* 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 <aws/gamelift/metrics/CrashReporterClient.h>
#include <aws/gamelift/internal/retry/RetryingCallable.h>
#include <aws/gamelift/internal/retry/JitteredGeometricBackoffRetryStrategy.h>
#include <spdlog/spdlog.h>
#include <stdexcept>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
namespace Aws {
namespace GameLift {
namespace Metrics {
CrashReporterClient::CrashReporterClient(const std::string& host, int port)
: httpClient(std::make_shared<Aws::GameLift::Internal::HttpClient>()) {
baseUrl = "http://" + host + ":" + std::to_string(port) + "/";
}
CrashReporterClient::CrashReporterClient(std::shared_ptr<Aws::GameLift::Internal::HttpClient> httpClient, const std::string &host, int port)
: httpClient(std::move(httpClient)) {
baseUrl = "http://" + host + ":" + std::to_string(port) + "/";
}
bool CrashReporterClient::isRetryableError(const std::string& errorMessage) const {
return errorMessage.find("Connection refused") != std::string::npos ||
errorMessage.find("Connection failed") != std::string::npos;
}
void CrashReporterClient::RegisterProcessWithRetries() {
#ifdef _WIN32
int processPid = static_cast<int>(GetCurrentProcessId());
#else
int processPid = static_cast<int>(getpid());
#endif
std::string requestUri = baseUrl + RegisterProcessUrlPath + "?" +
ProcessPidParameterName + "=" + std::to_string(processPid);
spdlog::info("Registering process with {} {} in OTEL Collector Crash Reporter", ProcessPidParameterName, processPid);
// 5 retries, 1s base delay with jitter (default)
// Total max wait time: ~1s + 2s + 4s + 8s + 16s = ~31s
Aws::GameLift::Internal::JitteredGeometricBackoffRetryStrategy retryStrategy;
auto callable = [this, &requestUri, processPid]() -> bool {
try {
auto response = httpClient->SendGetRequest(requestUri);
if (response.IsSuccessfulStatusCode()) {
spdlog::info("Successfully registered {} {} to OTEL Collector Crash Reporter", ProcessPidParameterName, processPid);
return true;
} else {
spdlog::error("Failed to register {} {} to OTEL Collector Crash Reporter, Http response: {} - {}",
ProcessPidParameterName, processPid, response.statusCode, response.body);
return true; // Don't retry on HTTP errors (4xx, 5xx)
}
} catch (const std::exception& e) {
std::string errorMsg = e.what();
if (isRetryableError(errorMsg)) {
spdlog::warn("Failed to register {} {} to OTEL Collector Crash Reporter due to connection error: {}",
ProcessPidParameterName, processPid, e.what());
return false; // Retry on connection errors
} else {
spdlog::error("Failed to register {} {} to OTEL Collector Crash Reporter due to error: {}",
ProcessPidParameterName, processPid, e.what());
return true; // Don't retry on other errors
}
}
};
Aws::GameLift::Internal::RetryingCallable::Builder()
.WithRetryStrategy(&retryStrategy)
.WithCallable(callable)
.Build()
.call();
}
void CrashReporterClient::RegisterProcess() {
RegisterProcessWithRetries();
}
void CrashReporterClient::TagGameSession(const std::string& sessionId) {
#ifdef _WIN32
int processPid = static_cast<int>(GetCurrentProcessId());
#else
int processPid = static_cast<int>(getpid());
#endif
std::string requestUri = baseUrl + UpdateProcessUrlPath + "?" +
ProcessPidParameterName + "=" + std::to_string(processPid) + "&" +
SessionIdParameterName + "=" + sessionId;
try {
spdlog::info("Adding {} tag {} to process with {} {} to the OTEL Collector Crash Reporter",
SessionIdParameterName, sessionId, ProcessPidParameterName, processPid);
auto response = httpClient->SendGetRequest(requestUri);
if (!response.IsSuccessfulStatusCode()) {
spdlog::error("Failed to add {} tag {} to process with {} {} in the OTEL Collector Crash Reporter, Http response: {} - {}",
SessionIdParameterName, sessionId, ProcessPidParameterName, processPid,
response.statusCode, response.body);
}
} catch (const std::exception& e) {
spdlog::error("Failed to add {} tag {} to process with {} {} in the OTEL Collector Crash Reporter due to error: {}",
SessionIdParameterName, sessionId, ProcessPidParameterName, processPid, e.what());
}
}
void CrashReporterClient::DeregisterProcess() {
#ifdef _WIN32
int processPid = static_cast<int>(GetCurrentProcessId());
#else
int processPid = static_cast<int>(getpid());
#endif
std::string requestUri = baseUrl + DeregisterProcessUrlPath + "?" +
ProcessPidParameterName + "=" + std::to_string(processPid);
try {
spdlog::info("Unregistering process with {} {} in OTEL Collector Crash Reporter",
ProcessPidParameterName, processPid);
auto response = httpClient->SendGetRequest(requestUri);
if (!response.IsSuccessfulStatusCode()) {
spdlog::error("Failed to deregister {} {} in the OTEL Collector Crash Reporter, Http response: {} - {}",
ProcessPidParameterName, processPid, response.statusCode, response.body);
}
} catch (const std::exception& e) {
spdlog::error("Failed to deregister {} {} in the OTEL Collector Crash Reporter due to error: {}",
ProcessPidParameterName, processPid, e.what());
}
}
} // namespace Metrics
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,25 @@
/*
* 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 <aws/gamelift/metrics/DerivedMetric.h>
namespace Aws {
namespace GameLift {
namespace Metrics {
IDerivedMetric::~IDerivedMetric() {}
IDerivedMetricVisitor::~IDerivedMetricVisitor() {}
IDerivedMetricCollection::~IDerivedMetricCollection() {}
} // namespace Metrics
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,33 @@
/*
* 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 <aws/gamelift/metrics/DynamicMetric.h>
#ifndef GAMELIFT_USE_STD
#include <cstring>
namespace Aws {
namespace GameLift {
namespace Metrics {
void DynamicMetric::SetKey(const char *newKey) {
#ifdef _WIN32
strncpy_s(m_key, DynamicMetric::MAXIMUM_KEY_LENGTH, newKey,
DynamicMetric::MAXIMUM_KEY_LENGTH);
#else
strncpy(m_key, newKey, DynamicMetric::MAXIMUM_KEY_LENGTH);
#endif // _WIN32
}
} // namespace Metrics
} // namespace GameLift
} // namespace Aws
#endif // !GAMELIFT_USE_STD

View File

@@ -0,0 +1,177 @@
/*
* 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 <aws/gamelift/metrics/GlobalMetricsProcessor.h>
#include <aws/gamelift/metrics/IMetricsProcessor.h>
#include <aws/gamelift/metrics/MetricsProcessor.h>
#include <aws/gamelift/metrics/LoggerMacros.h>
#include <aws/gamelift/metrics/CrashReporterClient.h>
#include <aws/gamelift/metrics/StatsDClient.h>
#include <aws/gamelift/server/model/GameSession.h>
#include <cassert>
#include <cstdlib>
#include <string>
#ifdef __linux__
#include <unistd.h>
#elif defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#endif
GAMELIFT_METRICS_DEFINE_GAUGE(ServerUpGauge);
namespace {
std::unique_ptr<::Aws::GameLift::Metrics::IMetricsProcessor>
GlobalProcessor(nullptr);
std::shared_ptr<::Aws::GameLift::Metrics::StatsDClient>
GlobalStatsDClient(nullptr);
std::shared_ptr<::Aws::GameLift::Metrics::CrashReporterClient>
GlobalCrashReporter(nullptr);
void InitializeCrashReporter(const MetricsSettings &settings) {
std::string crashReporterHost;
#ifdef GAMELIFT_USE_STD
crashReporterHost = settings.CrashReporterHost;
#else
crashReporterHost = (settings.CrashReporterHost != nullptr) ? std::string(settings.CrashReporterHost) : "";
#endif
// Skip crash reporter initialization if host is empty
if (crashReporterHost.empty()) {
GAMELIFT_METRICS_LOG_INFO("Crash reporter disabled - host not set");
return;
}
int crashReporterPort = settings.CrashReporterPort;
GlobalCrashReporter = std::make_shared<CrashReporterClient>(crashReporterHost, crashReporterPort);
GlobalCrashReporter->RegisterProcess();
}
void InitializeStatsDClient(const MetricsSettings &settings) {
std::string statsdHost;
#ifdef GAMELIFT_USE_STD
statsdHost = settings.StatsDClientHost;
#else
statsdHost = (settings.StatsDClientHost != nullptr) ? std::string(settings.StatsDClientHost) : "";
#endif
// Skip crash reporter initialization if host is empty
if (statsdHost.empty()) {
GAMELIFT_METRICS_LOG_INFO("StatsDClient disabled - host not set");
return;
}
int statsdPort = settings.StatsDClientPort;
GlobalStatsDClient = std::make_shared<StatsDClient>(statsdHost.c_str(), statsdPort);
GAMELIFT_METRICS_LOG_INFO("Created StatsD client for {}:{}", statsdHost, statsdPort);
}
void InitializeDefaultGlobalTags() {
if (GlobalProcessor) {
// Check if GAMELIFT_SDK_PROCESS_ID environment variable is set
const char *processId = std::getenv(ENV_VAR_PROCESS_ID);
if (processId != nullptr && processId[0] != '\0') {
GlobalProcessor->SetGlobalTag("gamelift_process_id", processId);
GAMELIFT_METRICS_LOG_INFO("Set global tag gamelift_process_id: {}",
processId);
}
// Set the OS process ID (Linux and Windows).
#if defined(_WIN32) || defined(_WIN64)
DWORD pid = GetCurrentProcessId();
std::string pidStr = std::to_string(pid);
#else
pid_t pid = getpid();
std::string pidStr = std::to_string(pid);
#endif
GlobalProcessor->SetGlobalTag("process_pid", pidStr.c_str());
GAMELIFT_METRICS_LOG_INFO("Set global tag process_pid: {}", pidStr);
}
}
} // namespace
namespace Aws {
namespace GameLift {
namespace Metrics {
void MetricsInitialize(const MetricsSettings &settings) {
GAMELIFT_METRICS_LOG_INFO("Initializing GameLift Servers Metrics");
assert(!GlobalProcessor);
InitializeCrashReporter(settings);
InitializeStatsDClient(settings);
// Create settings with StatsD callback only if no callback is already set
MetricsSettings settingsWithCallbackOverride = settings;
if (!settings.SendPacketCallback) {
settingsWithCallbackOverride.SendPacketCallback = [](const char* data, int size) {
if (GlobalStatsDClient) {
GlobalStatsDClient->Send(data, size);
} else {
GAMELIFT_METRICS_LOG_ERROR("StatsDClient is not initialized. Cannot send metrics data.");
}
};
}
GlobalProcessor.reset(new MetricsProcessor(settingsWithCallbackOverride));
InitializeDefaultGlobalTags();
GAMELIFT_METRICS_LOG_INFO(
"GameLift Servers Metrics initialized successfully");
}
void MetricsTerminate() {
GAMELIFT_METRICS_SET(ServerUpGauge, 0);
// Process the final metrics before shutting down
if (GlobalProcessor) {
GlobalProcessor->ProcessMetricsNow();
}
if (GlobalCrashReporter) {
GlobalCrashReporter->DeregisterProcess();
GlobalCrashReporter.reset();
}
if (GlobalStatsDClient) {
GlobalStatsDClient.reset();
}
GlobalProcessor.reset();
}
void MetricsProcess() {
assert(GlobalProcessor);
if (GlobalProcessor) {
GlobalProcessor->ProcessMetrics();
}
}
void OnGameSessionStarted(
const Aws::GameLift::Server::Model::GameSession &session) {
assert(GlobalProcessor);
if (GlobalProcessor) {
GlobalProcessor->OnStartGameSession(session);
}
if (GlobalCrashReporter) {
#ifdef GAMELIFT_USE_STD
GlobalCrashReporter->TagGameSession(session.GetGameSessionId());
#else
GlobalCrashReporter->TagGameSession(std::string(session.GetGameSessionId()));
#endif
}
}
} // namespace Metrics
} // namespace GameLift
} // namespace Aws
::Aws::GameLift::Metrics::IMetricsProcessor *GameLiftMetricsGlobalProcessor() {
return GlobalProcessor.get();
}

View File

@@ -0,0 +1,53 @@
/*
* 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 <aws/gamelift/metrics/HighResolutionClock.h>
#ifndef GAMELIFT_USE_STD
#include <aws/gamelift/metrics/InternalTypes.h>
#include <chrono>
/**
* We hide the internal chrono use and report a time as int64 nanoseconds
* publicly.
*
* Then convert back to chrono internally.
*/
namespace {
using Nanoseconds =
std::chrono::duration<Aws::GameLift::Metrics::Int64, std::nano>;
}
namespace Aws {
namespace GameLift {
namespace Metrics {
namespace Internal {
Int64 HighResolutionClock::Now() {
const auto now = std::chrono::high_resolution_clock::now();
return std::chrono::time_point_cast<Nanoseconds>(now)
.time_since_epoch()
.count();
}
double HighResolutionClock::ToMilliseconds(Int64 duration) {
using Milliseconds = std::chrono::duration<double, std::milli>;
return std::chrono::duration_cast<Milliseconds>(Nanoseconds(duration))
.count();
}
} // namespace Internal
} // namespace Metrics
} // namespace GameLift
} // namespace Aws
#endif // !GAMELIFT_USE_STD

View File

@@ -0,0 +1,74 @@
/*
* 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 <aws/gamelift/metrics/IMetricsProcessor.h>
#include <aws/gamelift/metrics/DynamicTag.h>
namespace Aws {
namespace GameLift {
namespace Metrics {
IMetricsEnqueuer::~IMetricsEnqueuer() {}
IMetricsProcessor::~IMetricsProcessor() {}
#ifdef GAMELIFT_USE_STD
MetricMessage MetricMessage::TagSet(IMetric &metric, std::string key,
std::string value) {
DynamicTag *tag = new DynamicTag(std::move(key), std::move(value));
return MetricMessage(MetricMessageType::TagSet, &metric, MetricSetTag(tag));
}
MetricMessage MetricMessage::TagRemove(Aws::GameLift::Metrics::IMetric &metric,
std::string key) {
DynamicTag *tag = new DynamicTag(std::move(key), "");
return MetricMessage(MetricMessageType::TagRemove, &metric,
MetricSetTag(tag));
}
#else
MetricMessage MetricMessage::TagSet(IMetric &metric, const char *key,
const char *value) {
DynamicTag *tag = new DynamicTag(key, value);
return MetricMessage(MetricMessageType::TagSet, &metric, MetricSetTag(tag));
}
MetricMessage MetricMessage::TagRemove(IMetric &metric, const char *key) {
DynamicTag *tag = new DynamicTag(key, "");
return MetricMessage(MetricMessageType::TagRemove, &metric,
MetricSetTag(tag));
}
#endif
bool operator==(const MetricSetTag &a, const MetricSetTag &b) {
return a.Ptr->Key == b.Ptr->Key && a.Ptr->Value == b.Ptr->Value;
}
void CopyTagMessage(const MetricMessage &original, IMetric &destMetric,
IMetricsEnqueuer &enqueuer) {
switch (original.Type) {
case MetricMessageType::TagSet:
enqueuer.Enqueue(MetricMessage::TagSet(destMetric,
original.SetTag.Ptr->Key.c_str(),
original.SetTag.Ptr->Value.c_str()));
break;
case MetricMessageType::TagRemove:
enqueuer.Enqueue(
MetricMessage::TagRemove(destMetric, original.SetTag.Ptr->Key.c_str()));
break;
default:
break;
}
}
} // namespace Metrics
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,21 @@
/*
* 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 <aws/gamelift/metrics/InternalTypes.h>
namespace Aws {
namespace GameLift {
namespace Metrics {
IMetric::~IMetric() {}
} // namespace Metrics
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,65 @@
/*
* 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 <aws/gamelift/metrics/KeySuffix.h>
#ifdef GAMELIFT_USE_STD
namespace Aws {
namespace GameLift {
namespace Metrics {
void KeySuffix::Apply(const IMetric &original, DynamicMetric &target) {
target.SetKey(std::string(original.GetKey()) + m_suffix);
}
} // namespace Metrics
} // namespace GameLift
} // namespace Aws
#else
#include <algorithm>
#include <cstring>
#include <string>
namespace Aws {
namespace GameLift {
namespace Metrics {
KeySuffix::KeySuffix() {
std::fill(std::begin(m_suffix), std::end(m_suffix), '\0');
}
KeySuffix::KeySuffix(const char *newSuffix) {
#ifdef _WIN32
strncpy_s(m_suffix, KeySuffix::MAXIMUM_SUFFIX_LENGTH, newSuffix,
KeySuffix::MAXIMUM_SUFFIX_LENGTH);
#else
strncpy(m_suffix, newSuffix, KeySuffix::MAXIMUM_SUFFIX_LENGTH);
#endif // _WIN32
}
void KeySuffix::SetSuffix(const char *newSuffix) {
#ifdef _WIN32
strncpy_s(m_suffix, KeySuffix::MAXIMUM_SUFFIX_LENGTH, newSuffix,
KeySuffix::MAXIMUM_SUFFIX_LENGTH);
#else
strncpy(m_suffix, newSuffix, KeySuffix::MAXIMUM_SUFFIX_LENGTH);
#endif // _WIN32
}
void KeySuffix::Apply(const IMetric &original, DynamicMetric &target) {
std::string key = original.GetKey();
key += m_suffix;
target.SetKey(key.c_str());
}
} // namespace Metrics
} // namespace GameLift
} // namespace Aws
#endif // GAMELIFT_USE_STD

View File

@@ -0,0 +1,148 @@
/*
* 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 <aws/gamelift/metrics/MetricsProcessor.h>
#include <aws/gamelift/metrics/DerivedMetric.h>
#include <aws/gamelift/metrics/GaugeMacros.h>
#include <iterator>
#include <unordered_set>
void MetricsProcessor::ProcessMetrics() {
const auto now = ClockT::now();
if (now < m_nextCaptureTime) {
return;
}
// The server_up metric needs to be set on each flush period, as an untouched
// metric will automatically expire in many metrics backends (e.g. Prometheus)
GAMELIFT_METRICS_SET(ServerUpGauge, 1);
ProcessMetricsNow();
}
void MetricsProcessor::ProcessMetricsNow() {
if (m_preProcessCallback) {
m_preProcessCallback();
}
const auto messageCount = m_messageQueueMPSC.size_approx();
m_messageQueueMPSC.try_dequeue_bulk(std::back_inserter(m_processQueue),
messageCount);
m_combinedMetrics.Clear();
ProcessMessages(m_processQueue);
m_processQueue.clear();
m_enqueuer.Clear();
m_nextCaptureTime = ClockT::now() + m_captureInterval;
}
namespace {
void UpdateDerivedMetrics(std::vector<MetricMessage> &messages,
IMetricsEnqueuer &enqueuer) {
for (auto &message : messages) {
struct HandleMessageVisitor final
: public Aws::GameLift::Metrics::IDerivedMetricVisitor {
MetricMessage &m_message;
IMetricsEnqueuer &m_enqueuer;
explicit HandleMessageVisitor(MetricMessage &message,
IMetricsEnqueuer &enqueuer) noexcept
: m_message(message), m_enqueuer(enqueuer) {}
virtual void VisitDerivedMetric(
Aws::GameLift::Metrics::IDerivedMetric &metric) override {
metric.HandleMessage(m_message, m_enqueuer);
}
};
HandleMessageVisitor visitor(message, enqueuer);
message.Metric->GetDerivedMetrics().Visit(visitor);
}
}
void SubmitDerivedMetrics(std::vector<MetricMessage> &messages,
IMetricsEnqueuer &enqueuer) {
std::unordered_set<const Aws::GameLift::Metrics::IMetric *> submittedMetrics;
for (auto &message : messages) {
const bool derivativeMetricsSubmitted =
submittedMetrics.find(message.Metric) != std::end(submittedMetrics);
if (derivativeMetricsSubmitted) {
continue;
}
submittedMetrics.emplace(message.Metric);
class EmitMessageVisitor final
: public Aws::GameLift::Metrics::IDerivedMetricVisitor {
public:
EmitMessageVisitor(const Aws::GameLift::Metrics::IMetric *originalMetric,
IMetricsEnqueuer &enqueuer) noexcept
: m_originalMetric(originalMetric), m_enqueuer(enqueuer) {}
virtual void VisitDerivedMetric(
Aws::GameLift::Metrics::IDerivedMetric &metric) override {
metric.EmitMetrics(m_originalMetric, m_enqueuer);
}
private:
const Aws::GameLift::Metrics::IMetric *m_originalMetric;
IMetricsEnqueuer &m_enqueuer;
};
EmitMessageVisitor visitor(message.Metric, enqueuer);
message.Metric->GetDerivedMetrics().Visit(visitor);
}
}
} // namespace
void MetricsProcessor::ProcessMessages(std::vector<MetricMessage> &messages) {
// Compute derived metrics and appends their messages to the end
UpdateDerivedMetrics(messages, m_enqueuer);
SubmitDerivedMetrics(messages, m_enqueuer);
std::copy(std::begin(m_enqueuer.m_messages), std::end(m_enqueuer.m_messages),
std::back_inserter(messages));
// Combine all the metrics
for (auto &message : messages) {
if (message.IsTag()) {
m_metricTags.Handle(message);
} else {
m_combinedMetrics.Add(message);
}
}
if (m_combinedMetrics.IsEmpty()) {
return;
}
// Build & send packets
for (const auto &message : m_combinedMetrics) {
m_packet.Append(message, m_globalTags, m_metricTags.GetTags(message.Metric),
m_sendPacket);
}
m_packet.Flush(m_sendPacket);
}
void MetricsProcessor::OnStartGameSession(
const Aws::GameLift::Server::Model::GameSession &session) {
#ifdef GAMELIFT_USE_STD
const char *sessionId = session.GetGameSessionId().c_str();
#else
const char *sessionId = session.GetGameSessionId();
#endif
if (sessionId != nullptr && sessionId[0] != '\0') {
SetGlobalTag("session_id", sessionId);
}
}

View File

@@ -0,0 +1,145 @@
/*
* 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 <aws/gamelift/metrics/MetricsUtils.h>
#include <aws/gamelift/metrics/GlobalMetricsProcessor.h>
#include <aws/gamelift/metrics/MetricsSettings.h>
#include <aws/gamelift/common/GameLiftErrors.h>
#include <cstdlib>
#include <cstring>
#include <spdlog/spdlog.h>
using namespace Aws::GameLift::Server;
namespace Aws {
namespace GameLift {
namespace Metrics {
MetricsParameters CreateMetricsParametersFromEnvironmentOrDefault() {
// Start with default values
const char* statsdHost = DEFAULT_STATSD_HOST;
int statsdPort = DEFAULT_STATSD_PORT;
const char* crashReporterHost = DEFAULT_CRASH_REPORTER_HOST;
int crashReporterPort = DEFAULT_CRASH_REPORTER_PORT;
int flushIntervalMs = DEFAULT_FLUSH_INTERVAL_MS;
int maxPacketSize = DEFAULT_MAX_PACKET_SIZE;
// Check environment variables and override defaults
const char* envStatsdHost = std::getenv(ENV_VAR_STATSD_HOST);
if (envStatsdHost && envStatsdHost[0] != '\0') {
statsdHost = envStatsdHost;
spdlog::info("Env override for statsdHost: {}", statsdHost);
}
const char* envStatsdPort = std::getenv(ENV_VAR_STATSD_PORT);
if (envStatsdPort && envStatsdPort[0] != '\0') {
statsdPort = std::atoi(envStatsdPort);
spdlog::info("Env override for statsdPort: {}", statsdPort);
}
const char* envCrashReporterHost = std::getenv(ENV_VAR_CRASH_REPORTER_HOST);
if (envCrashReporterHost && envCrashReporterHost[0] != '\0') {
crashReporterHost = envCrashReporterHost;
spdlog::info("Env override for crashReporterHost: {}", crashReporterHost);
}
const char* envCrashReporterPort = std::getenv(ENV_VAR_CRASH_REPORTER_PORT);
if (envCrashReporterPort && envCrashReporterPort[0] != '\0') {
crashReporterPort = std::atoi(envCrashReporterPort);
spdlog::info("Env override for crashReporterPort: {}", crashReporterPort);
}
const char* envFlushInterval = std::getenv(ENV_VAR_FLUSH_INTERVAL_MS);
if (envFlushInterval && envFlushInterval[0] != '\0') {
flushIntervalMs = std::atoi(envFlushInterval);
spdlog::info("Env override for flushIntervalMs: {}", flushIntervalMs);
}
const char* envMaxPacketSize = std::getenv(ENV_VAR_MAX_PACKET_SIZE);
if (envMaxPacketSize && envMaxPacketSize[0] != '\0') {
maxPacketSize = std::atoi(envMaxPacketSize);
spdlog::info("Env override for maxPacketSize: {}", maxPacketSize);
}
#ifdef GAMELIFT_USE_STD
return MetricsParameters(std::string(statsdHost), statsdPort, std::string(crashReporterHost), crashReporterPort, flushIntervalMs, maxPacketSize);
#else
return MetricsParameters(statsdHost, statsdPort, crashReporterHost, crashReporterPort, flushIntervalMs, maxPacketSize);
#endif
}
MetricsSettings FromMetricsParameters(const Aws::GameLift::Server::MetricsParameters &params) {
MetricsSettings settings;
settings.StatsDClientHost = params.GetStatsDHost();
settings.StatsDClientPort = params.GetStatsDPort();
settings.CrashReporterHost = params.GetCrashReporterHost();
settings.CrashReporterPort = params.GetCrashReporterPort();
settings.MaxPacketSizeBytes = params.GetMaxPacketSize();
settings.CaptureIntervalSec = params.GetFlushIntervalMs() / 1000.0f;
return settings;
}
Aws::GameLift::GenericOutcome ValidateMetricsParameters(const Aws::GameLift::Server::MetricsParameters &params) {
// Validate StatsD host
#ifdef GAMELIFT_USE_STD
if (params.GetStatsDHost().empty()) {
return Aws::GameLift::GenericOutcome(Aws::GameLift::GameLiftError(Aws::GameLift::GAMELIFT_ERROR_TYPE::VALIDATION_EXCEPTION, "StatsDHost cannot be empty"));
}
#else
const char* statsdHost = params.GetStatsDHost();
if (statsdHost == nullptr || statsdHost[0] == '\0') {
return Aws::GameLift::GenericOutcome(Aws::GameLift::GameLiftError(Aws::GameLift::GAMELIFT_ERROR_TYPE::VALIDATION_EXCEPTION, "StatsDHost cannot be empty"));
}
#endif
// Validate StatsD port
int statsdPort = params.GetStatsDPort();
if (statsdPort < PORT_MIN || statsdPort > PORT_MAX) {
return Aws::GameLift::GenericOutcome(Aws::GameLift::GameLiftError(Aws::GameLift::GAMELIFT_ERROR_TYPE::VALIDATION_EXCEPTION, "StatsDPort must be between 1 and 65535"));
}
// Validate CrashReporter host
#ifdef GAMELIFT_USE_STD
if (params.GetCrashReporterHost().empty()) {
return Aws::GameLift::GenericOutcome(Aws::GameLift::GameLiftError(Aws::GameLift::GAMELIFT_ERROR_TYPE::VALIDATION_EXCEPTION, "CrashReporterHost cannot be empty"));
}
#else
const char* crashReporterHost = params.GetCrashReporterHost();
if (crashReporterHost == nullptr || crashReporterHost[0] == '\0') {
return Aws::GameLift::GenericOutcome(Aws::GameLift::GameLiftError(Aws::GameLift::GAMELIFT_ERROR_TYPE::VALIDATION_EXCEPTION, "CrashReporterHost cannot be empty"));
}
#endif
// Validate CrashReporter port
int crashReporterPort = params.GetCrashReporterPort();
if (crashReporterPort < PORT_MIN || crashReporterPort > PORT_MAX) {
return Aws::GameLift::GenericOutcome(Aws::GameLift::GameLiftError(Aws::GameLift::GAMELIFT_ERROR_TYPE::VALIDATION_EXCEPTION, "CrashReporterPort must be between 1 and 65535"));
}
// Validate FlushIntervalMs
int flushIntervalMs = params.GetFlushIntervalMs();
if (flushIntervalMs < 0) {
return Aws::GameLift::GenericOutcome(Aws::GameLift::GameLiftError(Aws::GameLift::GAMELIFT_ERROR_TYPE::VALIDATION_EXCEPTION, "FlushIntervalMs must be non-negative"));
}
// Validate MaxPacketSize
int maxPacketSize = params.GetMaxPacketSize();
if (maxPacketSize < 0) {
return Aws::GameLift::GenericOutcome(Aws::GameLift::GameLiftError(Aws::GameLift::GAMELIFT_ERROR_TYPE::VALIDATION_EXCEPTION, "MaxPacketSize must be non-negative"));
}
return Aws::GameLift::GenericOutcome(nullptr);
}
} // namespace Metrics
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,230 @@
/*
* 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 <aws/gamelift/metrics/PacketBuilder.h>
#include <aws/gamelift/metrics/Samplers.h>
#include <aws/gamelift/metrics/LoggerMacros.h>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <spdlog/sinks/stdout_color_sinks.h>
namespace {
static constexpr int NullTerminator = 1;
}
PacketBuilder::PacketBuilder(size_t packetSize, int floatPrecision)
: m_packetSize(packetSize), m_floatPrecision(floatPrecision) {
m_formatBuffer << std::setprecision(floatPrecision)
<< std::setiosflags(std::ios_base::fixed);
}
void PacketBuilder::Append(
const MetricMessage &message, const TagMap &globalTags,
const TagMap &metricTags,
Aws::GameLift::Metrics::MetricsSettings::SendPacketFunc sendPacketFunc) {
const auto startPosition = m_formatBuffer.tellp();
AppendToStream(message, m_floatPrecision, globalTags, metricTags,
m_formatBuffer);
const auto endPosition = m_formatBuffer.tellp();
const size_t messageLength = endPosition - startPosition;
if (messageLength > GetPacketSize() - NullTerminator) {
// If there's no way a message can fit the packet size, we drop it and log
// an error.
try {
// Use a simple message that doesn't reference message.Metric
// which could be null in tests
GAMELIFT_METRICS_LOG_WARN(
"Message length ({}) exceeds packet size ({}), message has "
"been dropped.",
messageLength, GetPacketSize() - NullTerminator);
} catch (...) {
// Silently continue if logging fails - don't break tests
}
// Reset the stream to the start position so we can continue appending
m_formatBuffer.seekp(startPosition);
return;
}
const size_t formatBufferLength = endPosition;
if (formatBufferLength > GetPacketSize() - NullTerminator) {
// Likely case:
// this message caused us to exceed packet size
//
// 1. Seek back to the end of the previous message (cut the new message
// out of the stream).
// 2. Flush (sending packet).
// 3. Re-append the message to the (now) empty stream.
m_formatBuffer.seekp(startPosition);
Flush(sendPacketFunc);
AppendToStream(message, m_floatPrecision, globalTags, metricTags,
m_formatBuffer);
} else if (formatBufferLength == GetPacketSize() - NullTerminator) {
// Unlikely case:
// we hit the packet size exactly
// so we can just flush
Flush(sendPacketFunc);
}
}
void PacketBuilder::Flush(
Aws::GameLift::Metrics::MetricsSettings::SendPacketFunc sendPacketFunc) {
m_sendBuffer = m_formatBuffer.str();
m_sendBuffer.resize(m_formatBuffer.tellp());
sendPacketFunc(m_sendBuffer.c_str(),
static_cast<int>(m_sendBuffer.size() + NullTerminator));
m_formatBuffer.seekp(0);
}
namespace {
void WriteTags(const PacketBuilder::TagMap &tags, std::ostream &stream) {
if (tags.size() == 0) {
return;
}
auto it = std::begin(tags);
stream << it->first << ':' << it->second;
for (++it; it != std::end(tags); ++it) {
stream << ',' << it->first << ':' << it->second;
}
}
void WriteTags(const PacketBuilder::TagMap &globalTags,
const PacketBuilder::TagMap &metricTags, std::ostream &stream) {
if (globalTags.size() > 0 || metricTags.size() > 0) {
stream << "|#";
WriteTags(globalTags, stream);
if (globalTags.size() > 0 && metricTags.size() > 0) {
stream << ',';
}
WriteTags(metricTags, stream);
}
}
void WriteValue(double value, int floatPrecision, std::ostream &stream) {
const auto valueAsInteger = static_cast<int64_t>(value);
const double fractionalPart = value - valueAsInteger;
/*
* We check if the fractional part of our value is 0 when rounded for display.
*
* We configured ostream to round to a specific float precision earlier.
* Ie with a precision = 2, 1.425 becomes 1.43
*
* Due to floating point imprecision, we may, at some point, end up with a
* value like 1.0000002. When rounded this ends up as 1.00 (with precision = 2
* as before) and we'd prefer to print it as an integer.
*
* The check is simple: we multiply the fraction by 10^precision and see if
* when rounded the value equals to 0.
*
* 0.425 * 10^2 = 42.5 ~= 43 => not zero so we print 1.425 as a real
* 0.0000002 * 10^2 = 0.00002 ~= 0 => zero so we can print 1.0000002 as an
* integer
*
* This also takes care of integer valued doubles in general. (Fractional part
* is already zero in that case.)
*/
const double thousandths = std::pow(10, floatPrecision);
const bool hasFractionalPart = std::round(fractionalPart * thousandths) != 0;
if (hasFractionalPart) {
stream << value;
} else {
stream << valueAsInteger;
}
}
void WriteSampleRate(const MetricMessage &message, std::ostream &stream) {
// Get the sample rate from the sampler
if (message.Metric) {
Aws::GameLift::Metrics::ISampler &sampler = message.Metric->GetSampler();
float rate = sampler.GetSampleRate();
// Only add sample rate if it's less than 1.0 (1.0 is implicit/default in
// StatsD). Packets with a 0.0 sample rate are not sent.
if (rate < 1.0f) {
// Format using a separate stringstream with default formatting
std::ostringstream rateStream;
rateStream << rate;
stream << "|@" << rateStream.str();
}
}
}
void WriteMessage(const MetricMessage &message, int floatPrecision,
std::ostream &stream) {
static constexpr auto showPositiveSign = std::ios_base::showpos;
stream << message.Metric->GetKey() << ':';
if (message.Type == MetricMessageType::GaugeSet) {
WriteValue(message.SubmitDouble.Value, floatPrecision, stream);
stream << "|g";
} else if (message.Type == MetricMessageType::GaugeAdd) {
stream << std::setiosflags(showPositiveSign);
WriteValue(message.SubmitDouble.Value, floatPrecision, stream);
stream << "|g" << std::resetiosflags(showPositiveSign);
} else if (message.Type == MetricMessageType::CounterAdd) {
WriteValue(message.SubmitDouble.Value, floatPrecision, stream);
stream << "|c" << std::resetiosflags(showPositiveSign);
} else if (message.Type == MetricMessageType::TimerSet) {
WriteValue(message.SubmitDouble.Value, floatPrecision, stream);
stream << "|ms";
}
// Add sample rate if using SampleFraction
WriteSampleRate(message, stream);
}
} // namespace
void AppendToStream(const MetricMessage &message, int floatPrecision,
const PacketBuilder::TagMap &globalTags,
const PacketBuilder::TagMap &metricTags,
std::ostream &stream) {
if (message.Type == MetricMessageType::GaugeSet &&
message.SubmitDouble.Value < 0) {
/*
* There's an intentional amgibuity in `gaugor:-10|g`
* It means 'subtract 10 from gaugor'. So the only way to set a
* gauge to a negative value is by setting it to zero first and
* subtracting:
* gaugor:0|g
* gaugor:-10|g
*
* See:
https://github.com/statsd/statsd/blob/master/docs/metric_types.md#gauges
*/
WriteMessage(MetricMessage::GaugeSet(*message.Metric, 0), floatPrecision,
stream);
WriteTags(globalTags, metricTags, stream);
stream << '\n';
WriteMessage(
MetricMessage::GaugeAdd(*message.Metric, message.SubmitDouble.Value),
floatPrecision, stream);
WriteTags(globalTags, metricTags, stream);
stream << '\n';
} else if (message.IsCounter() && message.SubmitDouble.Value <= 0) {
// Skip non-positive or negative counters
} else {
WriteMessage(message, floatPrecision, stream);
WriteTags(globalTags, metricTags, stream);
stream << '\n';
}
}

View File

@@ -0,0 +1,207 @@
/*
* 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 <aws/gamelift/metrics/Percentiles.h>
#include <aws/gamelift/metrics/LoggerMacros.h>
#include <algorithm>
#include <array>
#include <iomanip>
#include <set>
#include <sstream>
#include <vector>
namespace Aws {
namespace GameLift {
namespace Metrics {
namespace {
struct PercentileMetric {
double m_percentile;
DynamicMetric m_metric;
bool m_metricInitialized = false;
explicit PercentileMetric(double percentile) : m_percentile(percentile) {}
double GetPercentile() const { return m_percentile; }
void SetPercentile(double percentile) { m_percentile = percentile; }
PercentileMetric &WithPercentile(double percentile) {
SetPercentile(percentile);
return *this;
}
};
/**
* INTERNAL: concrete percentile metric implementation
*/
class PercentilesImpl : public IDerivedMetric {
public:
template <class It> PercentilesImpl(It begin, It end) {
std::transform(begin, end, std::back_inserter(m_percentiles),
[](double value) { return PercentileMetric(value); });
}
virtual void HandleMessage(MetricMessage &message,
IMetricsEnqueuer &submitter) override {
switch (message.Type) {
case MetricMessageType::GaugeAdd:
m_currentValue += message.SubmitDouble.Value;
AppendValue(m_currentValue);
break;
case MetricMessageType::GaugeSet:
case MetricMessageType::TimerSet:
m_currentValue = message.SubmitDouble.Value;
AppendValue(m_currentValue);
break;
case MetricMessageType::TagSet:
case MetricMessageType::TagRemove:
for (auto &percentile : m_percentiles) {
CopyTagMessage(message, percentile.m_metric, submitter);
}
break;
default:
break;
}
}
virtual void EmitMetrics(const IMetric *originalMetric,
IMetricsEnqueuer &submitter) override {
if (m_numSeenSinceLastEmitCall == 0) {
return;
}
m_numSeenSinceLastEmitCall = 0;
std::sort(std::begin(m_values), std::end(m_values));
for (auto &percentile : m_percentiles) {
EmitPercentile(originalMetric, percentile, submitter);
}
m_values.clear();
}
private:
void AppendValue(double newCurrentValue) {
m_values.emplace_back(newCurrentValue);
m_numSeenSinceLastEmitCall++;
}
void EmitPercentile(const IMetric *originalMetric,
PercentileMetric &percentile,
IMetricsEnqueuer &submitter) {
const double value = ComputePercentile(percentile.GetPercentile());
if (!percentile.m_metricInitialized) {
percentile.m_metric.SetMetricType(originalMetric->GetMetricType());
std::stringstream stream;
stream << originalMetric->GetKey() << ".p" << std::setw(2)
<< std::setfill('0')
<< static_cast<int>(percentile.GetPercentile() * 100.0);
#ifdef GAMELIFT_USE_STD
percentile.m_metric.SetKey(stream.str());
#else
percentile.m_metric.SetKey(stream.str().c_str());
#endif
percentile.m_metricInitialized = true;
}
switch (percentile.m_metric.GetMetricType()) {
case MetricType::Gauge:
submitter.Enqueue(MetricMessage::GaugeSet(percentile.m_metric, value));
break;
case MetricType::Timer:
submitter.Enqueue(MetricMessage::TimerSet(percentile.m_metric, value));
break;
default:
break;
}
}
double ComputePercentile(double percentile) const {
const double startIndexFloat = percentile * (m_values.size() - 1);
const size_t startIndex = static_cast<size_t>(startIndexFloat);
const double fractionalPart = startIndexFloat - startIndex;
const bool isBetweenValues = fractionalPart != 0;
if (isBetweenValues) {
const double start = m_values[startIndex];
const double end = m_values[startIndex + 1];
return start + fractionalPart * (end - start);
} else {
return m_values[startIndex];
}
}
private:
std::vector<PercentileMetric> m_percentiles;
size_t m_numSeenSinceLastEmitCall = 0;
double m_currentValue = 0;
std::vector<double> m_values;
};
/**
* INTERNAL: validate percentiles and report errors / warnings for debug builds
*
* @param begin The begin iterator of percentile collection
* @param end The past-the-end iterator of percentile collection
*/
template <class InputIt>
inline void ValidatePercentiles(InputIt begin, InputIt end) {
if (begin == end) {
GAMELIFT_METRICS_LOG_CRITICAL("Percentile list is empty.");
}
for (auto it = begin + 1; it != end; ++it) {
if (*(it - 1) == *it) {
GAMELIFT_METRICS_LOG_CRITICAL("Duplicate percentiles detected.", *it);
}
}
for (auto it = begin; it != end; ++it) {
const auto &value = *it;
if (value < 0 || value > 1) {
GAMELIFT_METRICS_LOG_CRITICAL("Percentiles must be in the [0, 1] range.",
value);
}
const auto percentileFloat = value * 100;
const auto percentileInteger = static_cast<int>(percentileFloat);
const double fractionalPart = percentileFloat - percentileInteger;
const double absDistance = 0.0001;
if (std::abs(fractionalPart) >= absDistance) {
// Warn about the user entering too many digits past decimal point.
// Diff might be a tiny value due to float rounding, so we check against
// distance to ignore it.
GAMELIFT_METRICS_LOG_WARN(
"Percentile {} ({}) has too many digits past the decimal "
"point. It will be truncated to {}",
percentileFloat, value, percentileInteger);
}
}
}
} // anonymous namespace
namespace Internal {
PercentilesWrapper PercentilesWrapper::Create(double *begin, double *end) {
std::sort(begin, end); // sort percentiles (mostly for validation but might
// as well keep them in this order)
#ifndef NDEBUG
ValidatePercentiles(begin, end);
#endif
return PercentilesWrapper(new PercentilesImpl(begin, end));
}
} // namespace Internal
} // namespace Metrics
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,86 @@
/*
* 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 <aws/gamelift/metrics/Samplers.h>
#include <chrono>
#include <mutex>
#include <random>
namespace Aws {
namespace GameLift {
namespace Metrics {
namespace Internal {
/**
* Basic wrapper around seed_seq so we don't have to leak it in the header
*/
struct SeedState {
std::seed_seq m_seed;
// std::seed_seq has internal state that is updated when we first initialize
// the sampler per-thread
std::mutex m_mutex;
explicit SeedState(Int64 seed) : m_seed{seed} {}
};
} // namespace Internal
namespace {
/**
* Per thread sampler with own RNG.
*
* We use this to avoid synchronization in multithreaded scenarios.
* This way we only have to lock when first initializing the thread.
*/
class PerThreadSampler {
public:
explicit PerThreadSampler(Internal::SeedState &state) noexcept
: m_distribution(0.0, 1.0) {
const std::lock_guard<std::mutex> guard(state.m_mutex);
if (state.m_seed.size() > 0) {
m_engine = std::mt19937(state.m_seed);
}
}
bool ShouldTakeSample(float fraction) {
const float randomValue = m_distribution(m_engine);
return randomValue <= fraction;
}
private:
std::uniform_real_distribution<float> m_distribution;
std::mt19937 m_engine;
};
} // namespace
ISampler::~ISampler() {}
SampleFraction::SampleFraction(float fractionToSample, Int64 seed)
: m_fractionToSample(fractionToSample),
m_seed(new Internal::SeedState(seed)) {}
SampleFraction::~SampleFraction() { delete m_seed; }
bool SampleFraction::ShouldTakeSample() {
// We use per-thread RNG to avoid synchronization.
static thread_local PerThreadSampler sampler(*m_seed);
return sampler.ShouldTakeSample(m_fractionToSample);
}
Int64 SampleFraction::DefaultSeed() {
using Nanoseconds = std::chrono::duration<Int64, std::nano>;
return std::chrono::duration_cast<Nanoseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
}
} // namespace Metrics
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,38 @@
/*
* 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 <aws/gamelift/metrics/StatsDClient.h>
#include <aws/gamelift/metrics/LoggerMacros.h>
using namespace Aws::GameLift::Metrics;
#ifdef GAMELIFT_USE_STD
StatsDClient::StatsDClient(const std::string& host, int port)
#else
StatsDClient::StatsDClient(const char* host, int port)
#endif
: m_socket(m_io_service)
, m_endpoint(asio::ip::address::from_string(host), port) {
try {
m_socket.open(asio::ip::udp::v4());
} catch (const std::exception& e) {
GAMELIFT_METRICS_LOG_ERROR("Failed to open StatsD socket: {}", e.what());
}
}
void StatsDClient::Send(const char* data, int size) {
try {
m_socket.send_to(asio::buffer(data, size), m_endpoint);
} catch (const std::exception& e) {
GAMELIFT_METRICS_LOG_ERROR("Failed to send StatsD packet: {}", e.what());
}
}

View File

@@ -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.
*
*/
#include <aws/gamelift/metrics/Tags.h>
#include <aws/gamelift/metrics/DynamicTag.h>
#include <algorithm>
#include <cassert>
void Tags::Handle(MetricMessage &message) {
assert(message.Type == MetricMessageType::TagSet ||
message.Type == MetricMessageType::TagRemove);
if (message.Type == MetricMessageType::TagSet) {
HandleSet(message.Metric, message.SetTag);
} else if (message.Type == MetricMessageType::TagRemove) {
HandleRemove(message.Metric, message.SetTag);
}
}
void Tags::HandleSet(const IMetric *metric, MetricSetTag &message) {
// Create tag map for current metric if not exist
auto it = m_tags.find(metric);
if (it == std::end(m_tags)) {
it = m_tags.emplace(metric, std::unordered_map<std::string, std::string>())
.first;
}
assert(it != std::end(m_tags));
auto &metricTags = it->second;
auto tagIt = metricTags.find(message.Ptr->Key);
if (tagIt != std::end(metricTags)) {
// Swap-in the new value to avoid copy
// Old value gets deleted below.
using std::swap;
swap(tagIt->second, message.Ptr->Value);
} else {
// Move key and value into dictionary to avoid copy. (We delete empty shells
// below.)
metricTags.emplace(std::move(message.Ptr->Key),
std::move(message.Ptr->Value));
}
delete message.Ptr;
message.Ptr = nullptr;
}
void Tags::HandleRemove(const IMetric *metric, MetricSetTag &message) {
auto it = m_tags.find(metric);
if (it != std::end(m_tags)) {
it->second.erase(message.Ptr->Key);
}
delete message.Ptr;
message.Ptr = nullptr;
}

View File

@@ -0,0 +1,26 @@
/*
* 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 <aws/gamelift/metrics/TypeTraits.h>
#ifndef GAMELIFT_USE_STD
namespace Aws {
namespace GameLift {
namespace Metrics {
namespace Internal {
const bool TrueType::value;
const bool FalseType::value;
} // namespace Internal
} // namespace Metrics
} // namespace GameLift
} // namespace Aws
#endif // !GAMELIFT_USE_STD

View File

@@ -0,0 +1,443 @@
/*
* 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 <aws/gamelift/server/GameLiftServerAPI.h>
#include <aws/gamelift/internal/GameLiftServerState.h>
#include <aws/gamelift/internal/network/IWebSocketClientWrapper.h>
#include <aws/gamelift/internal/network/WebSocketppClientWrapper.h>
#include <aws/gamelift/server/ProcessParameters.h>
#include <aws/gamelift/server/MetricsParameters.h>
#include <aws/gamelift/metrics/GlobalMetricsProcessor.h>
#include <aws/gamelift/metrics/MetricsSettings.h>
#include <aws/gamelift/metrics/MetricsUtils.h>
#include <aws/gamelift/internal/util/LoggerHelper.h>
#include <spdlog/spdlog.h>
using namespace Aws::GameLift;
static const std::string sdkVersion = "5.4.0";
#ifdef GAMELIFT_USE_STD
Aws::GameLift::AwsStringOutcome Server::GetSdkVersion() { return AwsStringOutcome(sdkVersion); }
Server::InitSDKOutcome Server::InitSDK() { return InitSDK(Aws::GameLift::Server::Model::ServerParameters()); }
Server::InitSDKOutcome Server::InitSDK(const Aws::GameLift::Server::Model::ServerParameters &serverParameters) {
Internal::LoggerHelper::InitializeLogger(serverParameters.GetProcessId());
spdlog::info("Initializing GameLift SDK");
// Initialize the WebSocketWrapper
std::shared_ptr<Internal::IWebSocketClientWrapper> webSocketClientWrapper;
std::shared_ptr<Internal::WebSocketppClientType> wsClientPointer = std::make_shared<Internal::WebSocketppClientType>();
webSocketClientWrapper = std::make_shared<Internal::WebSocketppClientWrapper>(wsClientPointer);
InitSDKOutcome initOutcome = InitSDKOutcome(Internal::GameLiftServerState::CreateInstance(webSocketClientWrapper));
if (initOutcome.IsSuccess()) {
spdlog::info("Created Instance");
GenericOutcome networkingOutcome = initOutcome.GetResult()->InitializeNetworking(serverParameters);
if (!networkingOutcome.IsSuccess()) {
spdlog::error("Networking outcome failure when init SDK");
return InitSDKOutcome(networkingOutcome.GetError());
}
spdlog::info("Networking outcome success. Init SDK success");
// Set global processor if available
Aws::GameLift::Metrics::IMetricsProcessor* globalProcessor = GameLiftMetricsGlobalProcessor();
if (globalProcessor != nullptr) {
initOutcome.GetResult()->SetGlobalProcessor(globalProcessor);
}
}
return initOutcome;
}
GenericOutcome Server::ProcessReady(const Aws::GameLift::Server::ProcessParameters &processParameters) {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return GenericOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
return serverState->ProcessReady(processParameters);
}
GenericOutcome Server::ProcessEnding() {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return GenericOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
return serverState->ProcessEnding();
}
GenericOutcome Server::ActivateGameSession() {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return GenericOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
return serverState->ActivateGameSession();
}
StartMatchBackfillOutcome Server::StartMatchBackfill(const Aws::GameLift::Server::Model::StartMatchBackfillRequest &request) {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return StartMatchBackfillOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
return serverState->StartMatchBackfill(request);
}
GenericOutcome Server::StopMatchBackfill(const Aws::GameLift::Server::Model::StopMatchBackfillRequest &request) {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return GenericOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
return serverState->StopMatchBackfill(request);
}
GenericOutcome Server::UpdatePlayerSessionCreationPolicy(Aws::GameLift::Server::Model::PlayerSessionCreationPolicy newPlayerSessionPolicy) {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return GenericOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
return serverState->UpdatePlayerSessionCreationPolicy(newPlayerSessionPolicy);
}
Aws::GameLift::AwsStringOutcome Server::GetGameSessionId() {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return AwsStringOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
if (!serverState->IsProcessReady()) {
return AwsStringOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::PROCESS_NOT_READY));
}
return AwsStringOutcome(serverState->GetGameSessionId());
}
Aws::GameLift::AwsLongOutcome Server::GetTerminationTime() {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return AwsLongOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
return AwsLongOutcome(serverState->GetTerminationTime());
}
GenericOutcome Server::AcceptPlayerSession(const std::string &playerSessionId) {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return GenericOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
if (!serverState->IsProcessReady()) {
return GenericOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::PROCESS_NOT_READY));
}
return serverState->AcceptPlayerSession(playerSessionId);
}
GenericOutcome Server::RemovePlayerSession(const std::string &playerSessionId) {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return GenericOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
if (!serverState->IsProcessReady()) {
return GenericOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::PROCESS_NOT_READY));
}
return serverState->RemovePlayerSession(playerSessionId);
}
#else
Aws::GameLift::AwsStringOutcome Server::GetSdkVersion() { return AwsStringOutcome(sdkVersion.c_str()); }
GenericOutcome Server::InitSDK() { return InitSDK(Aws::GameLift::Server::Model::ServerParameters()); }
GenericOutcome Server::InitSDK(const Aws::GameLift::Server::Model::ServerParameters &serverParameters) {
Internal::LoggerHelper::InitializeLogger(serverParameters.GetProcessId());
spdlog::info("Initializing server SDK");
// Initialize the WebSocketWrapper
Internal::InitSDKOutcome initOutcome =
Internal::InitSDKOutcome(Internal::GameLiftServerState::CreateInstance<Internal::WebSocketppClientWrapper, Internal::WebSocketppClientType>());
if (initOutcome.IsSuccess()) {
spdlog::info("Created Instance");
GenericOutcome networkingOutcome = initOutcome.GetResult()->InitializeNetworking(serverParameters);
if (!networkingOutcome.IsSuccess()) {
spdlog::error("Networking outcome failure when init SDK");
return GenericOutcome(networkingOutcome.GetError());
}
spdlog::info("Networking outcome success. Init SDK success");
// Set global processor if available
Aws::GameLift::Metrics::IMetricsProcessor* globalProcessor = GameLiftMetricsGlobalProcessor();
if (globalProcessor != nullptr) {
initOutcome.GetResult()->SetGlobalProcessor(globalProcessor);
}
}
return GenericOutcome(nullptr);
}
GenericOutcome Server::ProcessReady(const Aws::GameLift::Server::ProcessParameters &processParameters) {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return GenericOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
return serverState->ProcessReady(processParameters);
}
GenericOutcome Server::ProcessEnding() {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return GenericOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
return serverState->ProcessEnding();
}
GenericOutcome Server::ActivateGameSession() {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return GenericOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
return serverState->ActivateGameSession();
}
StartMatchBackfillOutcome Server::StartMatchBackfill(const Aws::GameLift::Server::Model::StartMatchBackfillRequest &request) {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return StartMatchBackfillOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
return serverState->StartMatchBackfill(request);
}
GenericOutcome Server::StopMatchBackfill(const Aws::GameLift::Server::Model::StopMatchBackfillRequest &request) {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return GenericOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
return serverState->StopMatchBackfill(request);
}
GenericOutcome Server::UpdatePlayerSessionCreationPolicy(Aws::GameLift::Server::Model::PlayerSessionCreationPolicy newPlayerSessionPolicy) {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return GenericOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
return serverState->UpdatePlayerSessionCreationPolicy(newPlayerSessionPolicy);
}
Aws::GameLift::AwsStringOutcome Server::GetGameSessionId() {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return AwsStringOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
if (!serverState->IsProcessReady()) {
return AwsStringOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::PROCESS_NOT_READY));
}
return AwsStringOutcome(serverState->GetGameSessionId());
}
Aws::GameLift::AwsLongOutcome Server::GetTerminationTime() {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return AwsLongOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
return AwsLongOutcome(serverState->GetTerminationTime());
}
GenericOutcome Server::AcceptPlayerSession(const char *playerSessionId) {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return GenericOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
if (!serverState->IsProcessReady()) {
return GenericOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::PROCESS_NOT_READY));
}
return serverState->AcceptPlayerSession(playerSessionId);
}
GenericOutcome Server::RemovePlayerSession(const char *playerSessionId) {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return GenericOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
if (!serverState->IsProcessReady()) {
return GenericOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::PROCESS_NOT_READY));
}
return serverState->RemovePlayerSession(playerSessionId);
}
#endif
DescribePlayerSessionsOutcome Server::DescribePlayerSessions(const Aws::GameLift::Server::Model::DescribePlayerSessionsRequest &describePlayerSessionsRequest) {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return DescribePlayerSessionsOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
if (!serverState->IsProcessReady()) {
return DescribePlayerSessionsOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::PROCESS_NOT_READY));
}
return serverState->DescribePlayerSessions(describePlayerSessionsRequest);
}
GenericOutcome Server::Destroy() {
Aws::GameLift::Metrics::MetricsTerminate();
spdlog::info("Metrics terminated");
return Internal::GameLiftCommonState::DestroyInstance();
}
GetComputeCertificateOutcome Server::GetComputeCertificate() {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return GetComputeCertificateOutcome(giOutcome.GetError());
}
Internal::GameLiftServerState *serverState = static_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
if (serverState != NULL) {
return serverState->GetComputeCertificate();
}
return GetComputeCertificateOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::NOT_INITIALIZED));
}
GetFleetRoleCredentialsOutcome Server::GetFleetRoleCredentials(const Aws::GameLift::Server::Model::GetFleetRoleCredentialsRequest &request) {
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (!giOutcome.IsSuccess()) {
return GetFleetRoleCredentialsOutcome(giOutcome.GetError());
}
auto *serverState = dynamic_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
if (serverState != nullptr) {
return serverState->GetFleetRoleCredentials(request);
}
return GetFleetRoleCredentialsOutcome(GameLiftError(GAMELIFT_ERROR_TYPE::NOT_INITIALIZED));
}
GenericOutcome Server::InitMetrics() {
return InitMetrics(Aws::GameLift::Metrics::CreateMetricsParametersFromEnvironmentOrDefault());
}
GenericOutcome Server::InitMetrics(const Aws::GameLift::Server::MetricsParameters &metricsParameters) {
// Validate parameters
GenericOutcome validationOutcome = Aws::GameLift::Metrics::ValidateMetricsParameters(metricsParameters);
if (!validationOutcome.IsSuccess()) {
return validationOutcome;
}
// Map MetricsParameters to MetricsSettings
Aws::GameLift::Metrics::MetricsSettings settings = Aws::GameLift::Metrics::FromMetricsParameters(metricsParameters);
Aws::GameLift::Metrics::MetricsInitialize(settings);
// Now that metrics are initialized, set the global processor in the server state
Internal::GetInstanceOutcome giOutcome = Internal::GameLiftCommonState::GetInstance(Internal::GAMELIFT_INTERNAL_STATE_TYPE::SERVER);
if (giOutcome.IsSuccess()) {
auto *serverState = dynamic_cast<Internal::GameLiftServerState *>(giOutcome.GetResult());
if (serverState != nullptr) {
Aws::GameLift::Metrics::IMetricsProcessor* globalProcessor = GameLiftMetricsGlobalProcessor();
if (globalProcessor != nullptr) {
serverState->SetGlobalProcessor(globalProcessor);
}
// Check if there's an active game session
#ifdef GAMELIFT_USE_STD
std::string gameSessionId = serverState->GetGameSessionId();
if (!gameSessionId.empty()) {
Aws::GameLift::Server::Model::GameSession gameSession;
gameSession.SetGameSessionId(gameSessionId);
Aws::GameLift::Metrics::OnGameSessionStarted(gameSession);
}
#else
const char* gameSessionId = serverState->GetGameSessionId();
if (gameSessionId != nullptr && gameSessionId[0] != '\0') {
Aws::GameLift::Server::Model::GameSession gameSession;
gameSession.SetGameSessionId(gameSessionId);
Aws::GameLift::Metrics::OnGameSessionStarted(gameSession);
}
#endif
}
}
return GenericOutcome(nullptr);
}

View File

@@ -0,0 +1,42 @@
/*
* 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 <aws/gamelift/server/LogParameters.h>
#ifndef GAMELIFT_USE_STD
#include "string.h"
#endif
#ifndef GAMELIFT_USE_STD
Aws::GameLift::Server::LogParameters::LogParameters(const char *const *logPaths, int count) {
m_count = (count < MAX_LOG_PATHS) ? count : MAX_LOG_PATHS;
for (int i = 0; i < m_count; ++i) {
#ifdef WIN32
strcpy_s(m_logPaths[i], MAX_PATH_LENGTH, logPaths[i]);
#else
strncpy(m_logPaths[i], logPaths[i], MAX_PATH_LENGTH);
#endif
}
}
Aws::GameLift::Server::LogParameters::LogParameters(const LogParameters &o) {
int count = o.getLogPathCount();
m_count = (count < MAX_LOG_PATHS) ? count : MAX_LOG_PATHS;
for (int i = 0; i < m_count; ++i) {
#ifdef WIN32
strcpy_s(m_logPaths[i], MAX_PATH_LENGTH, o.getLogPath(i));
#else
strncpy(m_logPaths[i], o.getLogPath(i), MAX_PATH_LENGTH);
#endif
}
}
#endif

View File

@@ -0,0 +1,158 @@
/*
* 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 "Modules/ModuleManager.h"
#include "Delegates/DelegateCombinations.h"
#if PLATFORM_WINDOWS
#include "Windows/AllowWindowsPlatformTypes.h"
#endif
#include "aws/gamelift/common/Outcome.h"
#include "aws/gamelift/server/GameLiftServerAPI.h"
#include "GameLiftServerSDKModels.h"
#include <map>
#if PLATFORM_WINDOWS
#include "Windows/HideWindowsPlatformTypes.h"
#endif
DECLARE_DELEGATE_OneParam(FOnStartGameSession, Aws::GameLift::Server::Model::GameSession);
DECLARE_DELEGATE_OneParam(FOnUpdateGameSession, Aws::GameLift::Server::Model::UpdateGameSession);
DECLARE_DELEGATE_RetVal(bool, FOnHealthCheck);
struct GAMELIFTSERVERSDK_API FProcessParameters {
FOnStartGameSession OnStartGameSession;
FOnUpdateGameSession OnUpdateGameSession;
FOnHealthCheck OnHealthCheck;
FSimpleDelegate OnTerminate;
int port = -1;
TArray<FString> logParameters;
void OnTerminateFunction() {
this->OnTerminate.ExecuteIfBound();
}
bool OnHealthCheckFunction() {
if (this->OnHealthCheck.IsBound()){
return this->OnHealthCheck.Execute();
}
return false;
}
void OnActivateFunction(Aws::GameLift::Server::Model::GameSession gameSession) {
this->OnStartGameSession.ExecuteIfBound(gameSession);
}
void OnUpdateFunction(Aws::GameLift::Server::Model::UpdateGameSession updateGameSession) {
this->OnUpdateGameSession.ExecuteIfBound(updateGameSession);
}
};
enum class FAttributeType : uint8
{
NONE,
STRING,
DOUBLE,
STRING_LIST,
STRING_DOUBLE_MAP
};
struct GAMELIFTSERVERSDK_API FAttributeValue {
FString m_S;
double m_N;
TArray<FString> m_SL;
TMap<FString, double> m_SDM;
FAttributeType m_type;
};
struct GAMELIFTSERVERSDK_API FPlayer {
FString m_playerId;
FString m_team;
TMap<FString, FAttributeValue> m_playerAttributes;
TMap<FString, int32> m_latencyInMs;
};
struct GAMELIFTSERVERSDK_API FStartMatchBackfillRequest {
FString m_ticketId;
FString m_gameSessionArn;
FString m_matchmakingConfigurationArn;
TArray<FPlayer> m_players;
FStartMatchBackfillRequest(const FString& ticketId, const FString& gameSessionArn, const FString& matchmakingConfigurationArn, const TArray<FPlayer>& players) :
m_ticketId(ticketId),
m_gameSessionArn(gameSessionArn),
m_matchmakingConfigurationArn(matchmakingConfigurationArn),
m_players(players)
{
}
};
struct GAMELIFTSERVERSDK_API FStopMatchBackfillRequest {
FString m_ticketId;
FString m_gameSessionArn;
FString m_matchmakingConfigurationArn;
FStopMatchBackfillRequest(const FString& ticketId, const FString& gameSessionArn, const FString& matchmakingConfigurationArn) :
m_ticketId(ticketId),
m_gameSessionArn(gameSessionArn),
m_matchmakingConfigurationArn(matchmakingConfigurationArn)
{
}
};
class GAMELIFTSERVERSDK_API FGameLiftServerSDKModule : public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
virtual FGameLiftStringOutcome GetSdkVersion();
// Needs the standalone server to be running locally. If not, this will block at load time.
virtual FGameLiftGenericOutcome InitSDK();
virtual FGameLiftGenericOutcome InitSDK(const FServerParameters &serverParameters);
virtual FGameLiftGenericOutcome InitMetrics();
virtual FGameLiftGenericOutcome InitMetrics(const FMetricsParameters &metricsParameters);
//virtual TGameLiftGenericOutcome ProcessReady(Aws::GameLift::Server::ProcessParameters &processParameters);
virtual FGameLiftGenericOutcome ProcessReady(FProcessParameters &processParameters);
virtual FGameLiftGenericOutcome ProcessEnding();
virtual FGameLiftGenericOutcome ActivateGameSession();
virtual FGameLiftGenericOutcome AcceptPlayerSession(const FString& playerSessionId);
virtual FGameLiftGenericOutcome RemovePlayerSession(const FString& playerSessionId);
virtual FGameLiftGenericOutcome Destroy();
virtual FGameLiftDescribePlayerSessionsOutcome DescribePlayerSessions(const FGameLiftDescribePlayerSessionsRequest& describePlayerSessionsRequest);
virtual FGameLiftGenericOutcome UpdatePlayerSessionCreationPolicy(EPlayerSessionCreationPolicy policy);
virtual FGameLiftStringOutcome GetGameSessionId();
virtual FGameLiftLongOutcome GetTerminationTime();
virtual FGameLiftStringOutcome StartMatchBackfill(const FStartMatchBackfillRequest& request);
virtual FGameLiftGenericOutcome StopMatchBackfill(const FStopMatchBackfillRequest& request);
virtual FGameLiftGetComputeCertificateOutcome GetComputeCertificate();
virtual FGameLiftGetFleetRoleCredentialsOutcome GetFleetRoleCredentials(const FGameLiftGetFleetRoleCredentialsRequest& request);
private:
/** Handle to the dll we will load */
static void* GameLiftServerSDKLibraryHandle;
static bool LoadDependency(const FString& Dir, const FString& Name, void*& Handle);
static void FreeDependency(void*& Handle);
};

View File

@@ -0,0 +1,280 @@
/*
* 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
enum class GAMELIFTSERVERSDK_API EPlayerSessionCreationPolicy
{
NOT_SET,
ACCEPT_ALL,
DENY_ALL
};
inline GAMELIFTSERVERSDK_API EPlayerSessionCreationPolicy GetPlayerSessionCreationPolicyForName(FString& name)
{
if (name == "ACCEPT_ALL") {
return EPlayerSessionCreationPolicy::ACCEPT_ALL;
}
if (name == "DENY_ALL") {
return EPlayerSessionCreationPolicy::DENY_ALL;
}
return EPlayerSessionCreationPolicy::NOT_SET;
}
inline GAMELIFTSERVERSDK_API FString GetNameForPlayerSessionCreationPolicy(EPlayerSessionCreationPolicy value)
{
switch (value) {
case EPlayerSessionCreationPolicy::ACCEPT_ALL:
return FString("ACCEPT_ALL");
case EPlayerSessionCreationPolicy::DENY_ALL:
return FString("DENY_ALL");
default:
return FString("NOT_SET");
}
}
enum class GAMELIFTSERVERSDK_API EPlayerSessionStatus
{
NOT_SET,
RESERVED,
ACTIVE,
COMPLETED,
TIMEDOUT
};
inline GAMELIFTSERVERSDK_API EPlayerSessionStatus GetPlayerSessionStatusForName(FString& name)
{
if (name == "RESERVED") {
return EPlayerSessionStatus::RESERVED;
}
if (name == "ACTIVE") {
return EPlayerSessionStatus::ACTIVE;
}
if (name == "COMPLETED") {
return EPlayerSessionStatus::COMPLETED;
}
if (name == "TIMEDOUT") {
return EPlayerSessionStatus::TIMEDOUT;
}
return EPlayerSessionStatus::NOT_SET;
}
inline GAMELIFTSERVERSDK_API FString GetNameForPlayerSessionStatus(EPlayerSessionStatus value)
{
switch (value) {
case EPlayerSessionStatus::RESERVED:
return FString("RESERVED");
case EPlayerSessionStatus::ACTIVE:
return FString("ACTIVE");
case EPlayerSessionStatus::COMPLETED:
return FString("COMPLETED");
case EPlayerSessionStatus::TIMEDOUT:
return FString("TIMEDOUT");
default:
return FString("NOT_SET");
}
}
struct GAMELIFTSERVERSDK_API FServerParameters
{
FString m_webSocketUrl;
FString m_fleetId;
FString m_processId;
FString m_hostId;
FString m_authToken;
FString m_awsRegion;
FString m_accessKey;
FString m_secretKey;
FString m_sessionToken;
};
struct GAMELIFTSERVERSDK_API FMetricsParameters
{
FString m_statsDHost;
int32 m_statsDPort = 0;
FString m_crashReporterHost;
int32 m_crashReporterPort = 0;
int32 m_flushIntervalMs = 0;
int32 m_maxPacketSize = 0;
};
struct GAMELIFTSERVERSDK_API FGameLiftPlayerSession
{
FString m_playerSessionId;
FString m_playerId;
FString m_gameSessionId;
FString m_fleetId;
long m_creationTime = 0;
long m_terminationTime = 0;
EPlayerSessionStatus m_status = EPlayerSessionStatus::NOT_SET;
FString m_ipAddress;
int m_port = 0;
FString m_playerData;
FString m_dnsName;
};
struct GAMELIFTSERVERSDK_API FGameLiftDescribePlayerSessionsResult
{
TArray<FGameLiftPlayerSession> m_playerSessions;
FString m_nextToken;
};
struct GAMELIFTSERVERSDK_API FGameLiftDescribePlayerSessionsRequest
{
FString m_gameSessionId;
FString m_playerId;
FString m_playerSessionId;
FString m_playerSessionStatusFilter;
int m_limit = 0;
FString m_nextToken;
};
struct GAMELIFTSERVERSDK_API FGameLiftGetComputeCertificateResult
{
FString m_certificate_path;
FString m_computeName;
};
struct GAMELIFTSERVERSDK_API FGameLiftGetFleetRoleCredentialsResult
{
FString m_assumedUserRoleArn;
FString m_assumedRoleId;
FString m_accessKeyId;
FString m_secretAccessKey;
FString m_sessionToken;
FDateTime m_expiration;
};
struct GAMELIFTSERVERSDK_API FGameLiftGetFleetRoleCredentialsRequest
{
FString m_roleArn;
FString m_roleSessionName;
};
struct GAMELIFTSERVERSDK_API FGameLiftError {
Aws::GameLift::GAMELIFT_ERROR_TYPE m_errorType;
FString m_errorName;
FString m_errorMessage;
FGameLiftError(){}
FGameLiftError(Aws::GameLift::GAMELIFT_ERROR_TYPE type, FString name, FString message) :
m_errorType(type),
m_errorName(name),
m_errorMessage(message)
{}
FGameLiftError(Aws::GameLift::GameLiftError error)
{
m_errorMessage = FString(error.GetErrorMessage());
m_errorName = FString(error.GetErrorName());
m_errorType = error.GetErrorType();
}
};
template <typename R, typename E>
class GAMELIFTSERVERSDK_API TGameLiftOutcome
{
public:
TGameLiftOutcome() : success(false)
{
} // Default constructor
TGameLiftOutcome(const R& r) : result(r), success(true)
{
} // Result copy constructor
TGameLiftOutcome(const E& e) : error(e), success(false)
{
} // Error copy constructor
TGameLiftOutcome(R&& r) : result(std::forward<R>(r)), success(true)
{
} // Result move constructor
TGameLiftOutcome(E&& e) : error(std::forward<E>(e)), success(false)
{
} // Error move constructor
TGameLiftOutcome(const TGameLiftOutcome& o) :
result(o.result),
error(o.error),
success(o.success)
{
}
TGameLiftOutcome& operator=(const TGameLiftOutcome& o)
{
if (this != &o)
{
result = o.result;
error = o.error;
success = o.success;
}
return *this;
}
TGameLiftOutcome(TGameLiftOutcome&& o) : // Required to force Move Constructor
result(std::move(o.result)),
error(std::move(o.error)),
success(o.success)
{
}
TGameLiftOutcome& operator=(TGameLiftOutcome&& o)
{
if (this != &o)
{
result = std::move(o.result);
error = std::move(o.error);
success = o.success;
}
return *this;
}
inline const R& GetResult() const
{
return result;
}
inline R& GetResult()
{
return result;
}
/**
* casts the underlying result to an r-value so that caller can't take ownership of underlying resources.
* this is necessary when streams are involved.
*/
inline R&& GetResultWithOwnership()
{
return std::move(result);
}
inline const E& GetError() const
{
return error;
}
inline bool IsSuccess() const
{
return this->success;
}
private:
R result;
E error;
bool success;
};
typedef TGameLiftOutcome<void*, FGameLiftError> FGameLiftGenericOutcome;
typedef TGameLiftOutcome<FString, FGameLiftError> FGameLiftStringOutcome;
typedef TGameLiftOutcome<long, FGameLiftError> FGameLiftLongOutcome;
typedef TGameLiftOutcome<FGameLiftDescribePlayerSessionsResult, FGameLiftError> FGameLiftDescribePlayerSessionsOutcome;
typedef TGameLiftOutcome<FGameLiftGetComputeCertificateResult, FGameLiftError> FGameLiftGetComputeCertificateOutcome;
typedef TGameLiftOutcome<FGameLiftGetFleetRoleCredentialsResult, FGameLiftError> FGameLiftGetFleetRoleCredentialsOutcome;

View File

@@ -0,0 +1,363 @@
/*
* 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
#if defined(_MSC_VER) && !defined(GAMELIFT_USE_STD)
#pragma warning(push) // Save warning settings.
#pragma warning(disable : 4996) // Disable deprecated warning for strncpy
#endif
#include <aws/gamelift/common/GameLift_EXPORTS.h>
#ifndef GAMELIFT_USE_STD
#include "string.h"
#endif
namespace Aws {
namespace GameLift {
enum class AWS_GAMELIFT_API GAMELIFT_ERROR_TYPE {
ALREADY_INITIALIZED, // The server or client has already been initialized with
// Initialize().
FLEET_MISMATCH, // The target fleet does not match the fleet of a gameSession or playerSession.
GAMELIFT_CLIENT_NOT_INITIALIZED, // The client has not been initialized.
GAMELIFT_SERVER_NOT_INITIALIZED, // The server SDK has not been initialized.
GAME_SESSION_ENDED_FAILED, // The server SDK could not contact the service to report
// the game session ended.
GAME_SESSION_NOT_READY, // The game session was not activated.
GAME_SESSION_READY_FAILED, // The server SDK could not contact the service to report
// the game session is ready.
GAME_SESSION_ID_NOT_SET, // No game sessions are bound to this process.
INITIALIZATION_MISMATCH, // A client method was called after Server::Initialize(), or vice
// versa.
NOT_INITIALIZED, // The server or client has not been initialized with Initialize().
NO_TARGET_ALIASID_SET, // A target aliasId has not been set.
NO_TARGET_FLEET_SET, // A target fleet has not been set.
PROCESS_ENDING_FAILED, // The server SDK could not contact the service to report the
// process is ending.
PROCESS_NOT_ACTIVE, // The server process is not yet active, not bound to a GameSession, and
// cannot accept or process PlayerSessions.
PROCESS_NOT_READY, // The server process is not yet ready to be activated.
PROCESS_READY_FAILED, // The server SDK could not contact the service to report the
// process is ready.
SDK_VERSION_DETECTION_FAILED, // SDK version detection failed.
SERVICE_CALL_FAILED, // A call to an AWS service has failed.
STX_CALL_FAILED, // A call to the XStx server backend component has failed.
STX_INITIALIZATION_FAILED, // The XStx server backend component has failed to initialize.
UNEXPECTED_PLAYER_SESSION, // An unregistered player session was encountered by the server.
BAD_REQUEST_EXCEPTION, // Generic error when client creates a bad request.
UNAUTHORIZED_EXCEPTION, // Client not authorized to access a required resource due to missing or invalid auth.
FORBIDDEN_EXCEPTION, // Client requesting to use resources/operations they aren't allow to access.
NOT_FOUND_EXCEPTION, // Request needs a backend resource that the client failed to install.
CONFLICT_EXCEPTION, // Client request error caused by stale data.
TOO_MANY_REQUESTS_EXCEPTION, // Client called request too often and is being throttled.
INTERNAL_SERVICE_EXCEPTION, // Generic error when Amazon GameLift Servers WebSocket fails to process a request.
WEBSOCKET_CONNECT_FAILURE, // Failure to connect to the Amazon GameLift Servers WebSocket
WEBSOCKET_CONNECT_FAILURE_FORBIDDEN, // Access denied, e.g. auth token has expired
WEBSOCKET_CONNECT_FAILURE_INVALID_URL, // End point URL is invalid
WEBSOCKET_CONNECT_FAILURE_TIMEOUT, // Timeout
WEBSOCKET_RETRIABLE_SEND_MESSAGE_FAILURE, // Retriable failure to send message to the Amazon GameLift Servers
// Service WebSocket
WEBSOCKET_SEND_MESSAGE_FAILURE, // Failure to send message to the Amazon GameLift Servers WebSocket
VALIDATION_EXCEPTION // Client-side error when invalid parameters are passed.
};
class AWS_GAMELIFT_API GameLiftError {
#ifdef GAMELIFT_USE_STD
public:
GAMELIFT_ERROR_TYPE GetErrorType() const { return m_errorType; }
const std::string &GetErrorName() const { return m_errorName; }
void SetErrorName(const std::string &errorName) { m_errorName = errorName; }
const std::string &GetErrorMessage() const { return m_errorMessage; }
void SetErrorMessage(const std::string &errorMessage) { m_errorMessage = errorMessage; }
GameLiftError() : m_errorType(){};
~GameLiftError(){};
GameLiftError(const int statusCode, const std::string &message) : GameLiftError(GetErrorTypeForStatusCode(statusCode), message){};
GameLiftError(GAMELIFT_ERROR_TYPE errorType)
: m_errorType(errorType), m_errorName(Aws::GameLift::GameLiftError::GetDefaultNameForErrorType(errorType)),
m_errorMessage(Aws::GameLift::GameLiftError::GetDefaultMessageForErrorType(errorType)){};
GameLiftError(GAMELIFT_ERROR_TYPE errorType, const std::string &errorName, const std::string &message)
: m_errorType(errorType), m_errorName(errorName), m_errorMessage(message){};
GameLiftError(GAMELIFT_ERROR_TYPE errorType, const std::string &message)
: m_errorType(errorType), m_errorName(Aws::GameLift::GameLiftError::GetDefaultNameForErrorType(errorType)), m_errorMessage(message){};
GameLiftError(const GameLiftError &rhs) : m_errorType(rhs.GetErrorType()), m_errorName(rhs.GetErrorName()), m_errorMessage(rhs.GetErrorMessage()){};
bool operator==(const GameLiftError other) const {
return other.GetErrorType() == GetErrorType() && other.GetErrorName() == GetErrorName() && other.GetErrorMessage() == GetErrorMessage();
}
private:
GAMELIFT_ERROR_TYPE m_errorType;
std::string m_errorName;
std::string m_errorMessage;
#else
public:
const GAMELIFT_ERROR_TYPE GetErrorType() const { return m_errorType; }
const char *GetErrorName() const { return m_errorName; }
void SetErrorName(const char *errorName) {
strncpy(m_errorName, errorName, sizeof(m_errorName));
m_errorName[sizeof(m_errorName) - 1] = 0;
}
const char *GetErrorMessage() const { return m_errorMessage; }
void SetErrorMessage(const char *errorMessage) {
strncpy(m_errorMessage, errorMessage, sizeof(m_errorMessage));
m_errorMessage[sizeof(m_errorMessage) - 1] = 0;
}
GameLiftError() {
memset(m_errorName, 0, sizeof(m_errorName));
memset(m_errorMessage, 0, sizeof(m_errorMessage));
};
~GameLiftError(){};
GameLiftError(const int statusCode, const char *message) : GameLiftError(GetErrorTypeForStatusCode(statusCode), message){};
void Init(GAMELIFT_ERROR_TYPE errorType, const char *errorName, const char *message) {
m_errorType = errorType;
SetErrorName(errorName);
SetErrorMessage(message);
};
GameLiftError(GAMELIFT_ERROR_TYPE errorType) {
Init(errorType, Aws::GameLift::GameLiftError::GetDefaultNameForErrorType(errorType).c_str(),
Aws::GameLift::GameLiftError::GetDefaultMessageForErrorType(errorType).c_str());
};
GameLiftError(GAMELIFT_ERROR_TYPE errorType, const char *errorName, const char *message) : m_errorType(errorType) {
this->SetErrorName(errorName);
this->SetErrorMessage(message);
};
GameLiftError(GAMELIFT_ERROR_TYPE errorType, const char *message) {
Init(errorType, Aws::GameLift::GameLiftError::GetDefaultNameForErrorType(errorType).c_str(), message);
};
GameLiftError(const GameLiftError &rhs) { Init(rhs.GetErrorType(), rhs.GetErrorName(), rhs.GetErrorMessage()); };
bool operator==(const GameLiftError other) const {
return other.GetErrorType() == GetErrorType() && strcmp(other.GetErrorName(), GetErrorName()) == 0 &&
strcmp(other.GetErrorMessage(), GetErrorMessage()) == 0;
}
private:
GAMELIFT_ERROR_TYPE m_errorType;
char m_errorName[256];
char m_errorMessage[1024];
#endif
static GAMELIFT_ERROR_TYPE GetErrorTypeForStatusCode(const int statusCode) {
if (statusCode >= 400 && statusCode < 500) {
// Catch valid 4xx (client errors) status codes returned by websocket lambda
// All other 4xx codes fallback to "bad request exception"
switch(statusCode) {
case 400:
return GAMELIFT_ERROR_TYPE::BAD_REQUEST_EXCEPTION;
case 401:
return GAMELIFT_ERROR_TYPE::UNAUTHORIZED_EXCEPTION;
case 403:
return GAMELIFT_ERROR_TYPE::FORBIDDEN_EXCEPTION;
case 404:
return GAMELIFT_ERROR_TYPE::NOT_FOUND_EXCEPTION;
case 409:
return GAMELIFT_ERROR_TYPE::CONFLICT_EXCEPTION;
case 429:
return GAMELIFT_ERROR_TYPE::TOO_MANY_REQUESTS_EXCEPTION;
}
return GAMELIFT_ERROR_TYPE::BAD_REQUEST_EXCEPTION;
} else {
// The websocket can return other error types, in this case classify it as an internal
// service exception
return GAMELIFT_ERROR_TYPE::INTERNAL_SERVICE_EXCEPTION;
}
}
static std::string GetDefaultNameForErrorType(GAMELIFT_ERROR_TYPE errorType) {
switch (errorType) {
case GAMELIFT_ERROR_TYPE::ALREADY_INITIALIZED:
return "Already Initialized";
case GAMELIFT_ERROR_TYPE::FLEET_MISMATCH:
return "Fleet mismatch.";
case GAMELIFT_ERROR_TYPE::GAMELIFT_CLIENT_NOT_INITIALIZED:
return "Amazon GameLift Servers client not initialized.";
case GAMELIFT_ERROR_TYPE::GAMELIFT_SERVER_NOT_INITIALIZED:
return "Server SDK not initialized.";
case GAMELIFT_ERROR_TYPE::GAME_SESSION_ENDED_FAILED:
return "Game session failed.";
case GAMELIFT_ERROR_TYPE::GAME_SESSION_NOT_READY:
return "Game session not activated.";
case GAMELIFT_ERROR_TYPE::GAME_SESSION_READY_FAILED:
return "Game session failed.";
case GAMELIFT_ERROR_TYPE::GAME_SESSION_ID_NOT_SET:
return "GameSession id is not set.";
case GAMELIFT_ERROR_TYPE::INITIALIZATION_MISMATCH:
return "Initialization mismatch.";
case GAMELIFT_ERROR_TYPE::NOT_INITIALIZED:
return "Not Initialized";
case GAMELIFT_ERROR_TYPE::NO_TARGET_ALIASID_SET:
return "No target aliasId set.";
case GAMELIFT_ERROR_TYPE::NO_TARGET_FLEET_SET:
return "No target fleet set.";
case GAMELIFT_ERROR_TYPE::PROCESS_ENDING_FAILED:
return "Process ending failed.";
case GAMELIFT_ERROR_TYPE::PROCESS_NOT_ACTIVE:
return "Process not activated.";
case GAMELIFT_ERROR_TYPE::PROCESS_NOT_READY:
return "Process not ready.";
case GAMELIFT_ERROR_TYPE::PROCESS_READY_FAILED:
return "Process ready failed.";
case GAMELIFT_ERROR_TYPE::SDK_VERSION_DETECTION_FAILED:
return "Could not detect SDK version.";
case GAMELIFT_ERROR_TYPE::SERVICE_CALL_FAILED:
return "Service call failed.";
case GAMELIFT_ERROR_TYPE::STX_CALL_FAILED:
return "STX call failed.";
case GAMELIFT_ERROR_TYPE::STX_INITIALIZATION_FAILED:
return "STX Initialization Failed.";
case GAMELIFT_ERROR_TYPE::UNEXPECTED_PLAYER_SESSION:
return "Unexpected player session.";
case GAMELIFT_ERROR_TYPE::BAD_REQUEST_EXCEPTION:
return "Bad request exception.";
case GAMELIFT_ERROR_TYPE::UNAUTHORIZED_EXCEPTION:
return "Unauthorized exception.";
case GAMELIFT_ERROR_TYPE::FORBIDDEN_EXCEPTION:
return "Forbidden exception.";
case GAMELIFT_ERROR_TYPE::NOT_FOUND_EXCEPTION:
return "Not found exception.";
case GAMELIFT_ERROR_TYPE::CONFLICT_EXCEPTION:
return "Conflict exception.";
case GAMELIFT_ERROR_TYPE::TOO_MANY_REQUESTS_EXCEPTION:
return "Throttling exception.";
case GAMELIFT_ERROR_TYPE::INTERNAL_SERVICE_EXCEPTION:
return "Internal service exception.";
case GAMELIFT_ERROR_TYPE::WEBSOCKET_CONNECT_FAILURE:
return "WebSocket Connection Failed.";
case GAMELIFT_ERROR_TYPE::WEBSOCKET_CONNECT_FAILURE_FORBIDDEN:
return "WebSocket Connection Denied.";
case GAMELIFT_ERROR_TYPE::WEBSOCKET_CONNECT_FAILURE_INVALID_URL:
return "WebSocket Connection has invalid URL.";
case GAMELIFT_ERROR_TYPE::WEBSOCKET_CONNECT_FAILURE_TIMEOUT:
return "WebSocket Connection has timed out.";
case GAMELIFT_ERROR_TYPE::WEBSOCKET_SEND_MESSAGE_FAILURE:
return "WebSocket Send Message Failed.";
case GAMELIFT_ERROR_TYPE::VALIDATION_EXCEPTION:
return "Validation exception.";
default:
return "Unknown Error";
}
}
static std::string GetDefaultMessageForErrorType(GAMELIFT_ERROR_TYPE errorType) {
switch (errorType) {
case GAMELIFT_ERROR_TYPE::ALREADY_INITIALIZED:
return "Server SDK has already been initialized. You must call Destroy() before "
"reinitializing the client or server.";
case GAMELIFT_ERROR_TYPE::FLEET_MISMATCH:
return "The Target fleet does not match the request fleet. Make sure GameSessions and "
"PlayerSessions belong to your target fleet.";
case GAMELIFT_ERROR_TYPE::GAMELIFT_CLIENT_NOT_INITIALIZED:
return "The Amazon GameLift Servers client has not been initialized. Please call SetTargetFleet or "
"SetTArgetAliasId.";
case GAMELIFT_ERROR_TYPE::GAMELIFT_SERVER_NOT_INITIALIZED:
return "The server SDK has not been initialized. Please call InitSDK.";
case GAMELIFT_ERROR_TYPE::GAME_SESSION_ENDED_FAILED:
return "The GameSessionEnded invocation failed.";
case GAMELIFT_ERROR_TYPE::GAME_SESSION_NOT_READY:
return "The Game session associated with this server was not activated.";
case GAMELIFT_ERROR_TYPE::GAME_SESSION_READY_FAILED:
return "The GameSessionReady invocation failed.";
case GAMELIFT_ERROR_TYPE::GAME_SESSION_ID_NOT_SET:
return "No game sessions are bound to this process.";
case GAMELIFT_ERROR_TYPE::INITIALIZATION_MISMATCH:
return "The current call does not match the initialization state. Client calls require "
"a call to Client::Initialize(), and Server calls require Server::Initialize(). "
"Only one may be active at a time.";
case GAMELIFT_ERROR_TYPE::NOT_INITIALIZED:
return "Server SDK has not been initialized! You must call Client::Initialize() or "
"Server::InitSDK() before making Amazon GameLift Servers calls.";
case GAMELIFT_ERROR_TYPE::NO_TARGET_ALIASID_SET:
return "The aliasId has not been set. Clients should call SetTargetAliasId() before "
"making calls that require an alias.";
case GAMELIFT_ERROR_TYPE::NO_TARGET_FLEET_SET:
return "The target fleet has not been set. Clients should call SetTargetFleet() before "
"making calls that require a fleet.";
case GAMELIFT_ERROR_TYPE::PROCESS_ENDING_FAILED:
return "ProcessEnding call to Amazon GameLift Servers failed.";
case GAMELIFT_ERROR_TYPE::PROCESS_NOT_ACTIVE:
return "The process has not yet been activated.";
case GAMELIFT_ERROR_TYPE::PROCESS_NOT_READY:
return "The process has not yet been activated by calling ProcessReady(). Processes in "
"standby cannot receive StartGameSession callbacks.";
case GAMELIFT_ERROR_TYPE::PROCESS_READY_FAILED:
return "ProcessReady call to Amazon GameLift Servers failed.";
case GAMELIFT_ERROR_TYPE::SDK_VERSION_DETECTION_FAILED:
return "Could not detect SDK version.";
case GAMELIFT_ERROR_TYPE::SERVICE_CALL_FAILED:
return "An AWS service call has failed. See the root cause error for more information.";
case GAMELIFT_ERROR_TYPE::STX_CALL_FAILED:
return "An internal call to the STX server backend component has failed.";
case GAMELIFT_ERROR_TYPE::STX_INITIALIZATION_FAILED:
return "The STX server backend component has failed to initialize.";
case GAMELIFT_ERROR_TYPE::UNEXPECTED_PLAYER_SESSION:
return "The player session was not expected by the server. Clients wishing to connect "
"to a server must obtain a PlayerSessionID from Amazon GameLift Servers by creating a player "
"session on the desired server's game instance.";
case GAMELIFT_ERROR_TYPE::BAD_REQUEST_EXCEPTION:
return "Bad request exception.";
case GAMELIFT_ERROR_TYPE::UNAUTHORIZED_EXCEPTION:
return "User provided invalid or missing authorization to access a resource/operation.";
case GAMELIFT_ERROR_TYPE::FORBIDDEN_EXCEPTION:
return "User is attempting to access resources/operations that they are not allowed to access.";
case GAMELIFT_ERROR_TYPE::NOT_FOUND_EXCEPTION:
return "A necessary resource was missing when attempting to process the request.";
case GAMELIFT_ERROR_TYPE::CONFLICT_EXCEPTION:
return "Request conflicts with the current state of the target resource.";
case GAMELIFT_ERROR_TYPE::TOO_MANY_REQUESTS_EXCEPTION:
return "Too many requests; please increase throttle limit if needed.";
case GAMELIFT_ERROR_TYPE::INTERNAL_SERVICE_EXCEPTION:
return "Internal service exception.";
case GAMELIFT_ERROR_TYPE::WEBSOCKET_CONNECT_FAILURE:
return "Connection to the Amazon GameLift Servers WebSocket has failed";
case GAMELIFT_ERROR_TYPE::WEBSOCKET_CONNECT_FAILURE_FORBIDDEN:
return "Handshake with Amazon GameLift Servers WebSocket failed. Please verify that values of ServerParameters "
"in InitSDK() are correct. For example, process ID needs to be unique between executions, and "
"the authentication token needs to be correct and unexpired.";
case GAMELIFT_ERROR_TYPE::WEBSOCKET_CONNECT_FAILURE_INVALID_URL:
return "Connection to Amazon GameLift Servers WebSocket failed due to invalid websocket URL. Please verify that "
"websocketUrl in InitSDK() is correct and matches the GameLiftServiceSdkEndpoint output from "
"AWS::GameLift::RegisterCompute().";
case GAMELIFT_ERROR_TYPE::WEBSOCKET_CONNECT_FAILURE_TIMEOUT:
return "Connection to the Amazon GameLift Servers WebSocket Connection has timed out.";
case GAMELIFT_ERROR_TYPE::WEBSOCKET_SEND_MESSAGE_FAILURE:
return "Sending Message to the Amazon GameLift Servers WebSocket has failed.";
case GAMELIFT_ERROR_TYPE::VALIDATION_EXCEPTION:
return "The input is invalid.";
default:
return "An unexpected error has occurred.";
}
}
};
} // namespace GameLift
} // namespace Aws
#if defined(_MSC_VER) && !defined(GAMELIFT_USE_STD)
#pragma warning(pop) // Restore warnings to previous state.
#endif

View File

@@ -0,0 +1,31 @@
/*
* 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 <string>
namespace Aws {
namespace GameLift {
namespace Common {
class GameLiftToolDetector {
public:
virtual ~GameLiftToolDetector() = default;
virtual bool IsToolRunning() = 0;
virtual std::string GetToolName() = 0;
virtual std::string GetToolVersion() = 0;
void SetGameLiftTool();
};
} // namespace Common
} // namespace GameLift
} // namespace Aws

View File

@@ -0,0 +1,45 @@
/*
* 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
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4101)
#elif defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdelete-non-abstract-non-virtual-dtor"
#endif
#include <string>
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
#include <vector>
#ifdef _WIN32
// disable windows complaining about max template size.
#pragma warning(disable : 4503)
#endif
#if defined(_MSC_VER)
#pragma warning(disable : 4251)
#ifdef USE_IMPORT_EXPORT
#ifdef AWS_GAMELIFT_EXPORTS
#define AWS_GAMELIFT_API __declspec(dllexport)
#else
#define AWS_GAMELIFT_API __declspec(dllimport)
#endif /* AWS_GAMELIFT_EXPORTS */
#else
#define AWS_GAMELIFT_API
#endif //
#else /* defined (_WIN32) */
#define AWS_GAMELIFT_API
#endif

View File

@@ -0,0 +1,44 @@
/*
* 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/common/GameLiftToolDetector.h>
#include <functional>
namespace Aws {
namespace GameLift {
namespace Common {
class MetricsDetector : public GameLiftToolDetector {
public:
bool IsToolRunning() override;
std::string GetToolName() override;
std::string GetToolVersion() override;
private:
static constexpr const char* TOOL_NAME = "Metrics";
static constexpr const char* TOOL_VERSION = "1.0.0";
static constexpr const char* WINDOWS_SERVICE_COMMAND = "sc";
static constexpr const char* WINDOWS_SERVICE_NAME = "GLOTelCollector";
static constexpr const char* WINDOWS_SERVICE_ARGS = "query ";
static constexpr const char* WINDOWS_RUNNING_STATUS = "RUNNING";
static constexpr const char* LINUX_SERVICE_COMMAND = "systemctl";
static constexpr const char* LINUX_SERVICE_NAME = "gl-otel-collector.service";
static constexpr const char* LINUX_SERVICE_ARGS = "is-active ";
static constexpr const char* LINUX_ACTIVE_STATUS = "active";
bool CheckService(const std::string& command, const std::string& arguments, std::function<bool(const std::string&)> outputValidator);
};
} // namespace Common
} // namespace GameLift
} // namespace Aws

Some files were not shown because too many files have changed in this diff Show More