Amazon Web Services 한국 블로그

Amazon GameLift 글로벌 기반 서버 매치메이킹 기능 활용하기

지난 해 발표된 Amazon GameLift는 세션형 멀티플레어어 게임 서비스의 스케일링 문제를 쉽게 해결해주는 관리형 게임 플랫폼 서비스입니다. GameLift의 기본적인 사용법은 지난  블로그 글을 통해서 소개를 드린 바 있습니다.

Amazon GameLift가 발표된 이후, 많은 AWS 고객 분들의  기능 추가 요청 사항이 있었으며,  한국을 포함한 9개 리전에 확대 적용 되었고, 최근에는 Unity3D 및 Unreal Engine용 플러그인도 공식 출시하였습니다.  즉, Amazon Lumberyard 엔진을 포함하여 C++ 및 C# 프로그래밍 언어를 지원하는 어떠한 게임 엔진도 GameLift를 바로 적용하실 수 있습니다. (자세한 내용은 Launch: Amazon GameLift Now Supports All C++ and C# Game Engines 참고)

뿐만 아니라 지난 GDC 2017에서 GameLift의 이정표가 될 만한 기능인 글로벌 매치 메이킹(Global Matchmaking) 기능을 추가하였습니다. 이 기능은 전 세계에 흩어져 있는 각 게임 플레이어의 위치를 기반으로 가장 가까운 게임 서버를 지능적으로 선택하는 데 사용할 수 있으며, AWS 글로벌 인프라을 활용하여 플레이어에게 가능한 최저 지연 시간을 제공합니다.

지연 시간이 짧은 게임 서버를 사용할 수 없는 경우, 플레이어와 최대한 가까운 곳의 서버에 자동으로 게임 세션을 할당할 수 있도록 도와줍니다. 또한, 새로운 게임 세션이 시작되거나 신규 인스턴스가 시작되기 전까지 플레이어들이 대기열을 유지하고, 대기하고 있는 플레이어들을 지연 시간이 가장 짧은 게임 서버에 배치합니다.

AWS의 가장 큰 장점 중 하나인 광범위한 글로벌 인프라를 손쉽게 활용하여, 전 세계를 대상으로 서비스하는 세션형 멀티플레이어 게임 혹은 지역적으로 가까운 플레이어들끼리 팀을 구성하여 지연 시간이 가장 짧은 서버에 게임을 배치할 수 있는 기능을 제공함으로써 게임 플레이어들에게 좋은 게임 경험을 줄 수 있습니다. 이번 글에서는GameLift의 Matchmaking Queue기능에 대해 좀 더 구체적인 내용과 함께 어떻게 여러분의 게임 서비스에 통합 할 수 있는지에 대해 살펴봅니다.

GameLift Matchmaking Queue 생성하기
GameLift의 전역 매치메이킹 기능을 사용하기 위해서는 게임 세션 생성 요청이 대기할 수 있는 대기열(Matchmaking Queue)을 만들어야 합니다.  대기열은 GameLift 대시보드의 Queues 항목에서 “Create queue” 항목을 통해서 생성할 수 있습니다.

대기열 생성 과정은 상당히 간단합니다. Queue Name을 입력하고, (후에 Application에서 이 Queue Name을 통하여 대기열에 접근하게 됩니다.) 대기열 내에서 요청이 최대한 머무를 수 있는 시간(Queue Timeout)을 입력합니다. 그 후에 이 대기열과 연결할 GameLift Fleet(서버 집합)을 설정하면 됩니다. 서버 플릿은 리전에 관계없이 우선순위를 정하여 지정할 수 있습니다.

위의 그림은 GameLiftSeoulQueue라는 대기열로 들어오는 게임 세션 요청에 대해 3개 지역의 플릿을 연결한 예입니다. 이 예시에서는 게임 세션 생성 요청이 처리될 1순위 플릿은 도쿄 리전(ap-northeast-1)의 플릿이고, 2순위는 오레곤 리전(us-west-2), 마지막 순위는 서울리전(ap-northeast-2)의 플릿으로 설정되어 있는 것을 확인할 수 있습니다.

즉, 대기열에 연결된 플릿의 우선순위는 자유롭게 바꿀 수 있습니다. 게임 세션 생성 요청이 대기열에 도착하면, 기본적으로는 이 우선순위에 따라 게임 세션을 배치하게 됩니다. 물론, 사전에 정해진 우선순위가 아닌 플레이어의 게임 클라이언트와 각 리전과의 지연 시간 기반으로 최적의 플릿이 선택되도록 할 수도 있습니다.

추가적으로, Matchmaking Queue 또한 하나가 아니라 리전 별로 따로 만들 수 있으며 주로 어느 지역의 게임 플레이어로부터 요청을 받을지에 따라 Matchmaking Queue를 둘 리전을 자유롭게 선택하시면 됩니다. 예를 들어, 유럽 지역의 플레이어 대상으로 요청을 받을 대기열은 프랑크프루트 리전에, 아시아태평양 지역의 플레이어 대상으로는 도쿄 리전에, 북미 지역 플레이어 대상으로는 버지니아 리전에 각각 두고, 각각의 대기열로 들어온 요청에 대해 GameLift를 지원하는 모든 리전(현재 9개)의 플릿으로 정해놓은 우선순위에 따라 배치 요청을 보낼(dispatching) 수 있습니다.

GameLift Matchmaking Queue를 통한 게임 세션 생성하기
Matchmaking Queue를 통한 게임 세션 요청은 StartGameSessionPlacement API를 통해 할 수 있습니다. 이 API는 기본적으로 비동기로 동작합니다. 대기열에 게임 세션 생성을 요청한 이후 실제 게임 세션이 적절한 리전에 배치되기까지 시간이 걸리기 때문입니다. 그래서 DescribeGameSessionPlacement API를 통해서 주기적으로 게임 세션 생성이 완료되었는지 확인을 해야 합니다.

게임 세션의 배치가 완료되면 DescribeGameSessionPlacement API 응답의 Status 속성이 “FULFILLED”로 바뀝니다. 이 때, 결과의 GameSessionArn 속성에 담겨 오는 값이 바로 생성된 게임 세션의 ID입니다. 이 세션 ID를 통해서 이후 요청(플레이어 세션 생성 등)을 처리하면 됩니다.

위의 과정을 코드로 표현하면 다음과 같습니다. 전체 구현 코드는 Github을 참고하세요.

// StartGameSessionPlacement API를 통한 게임 세션 배치 요청 예
// 가독성을 위해 오류처리 부분은 생략하였습니다.

Aws::GameLift::Model::StartGameSessionPlacementRequest req;
req.SetGameSessionQueueName(“Matchmaking Queue 이름”);
req.SetMaximumPlayerSessionCount(“게임 세션내 최대 플레이어 수”);
req.SetPlacementId(“랜덤으로 생성된 Unique ID”);

auto outcome = GetAwsClient()->StartGameSessionPlacement(req);
if (outcome.IsSuccess())
{
	auto status = outcome.GetResult().GetGameSessionPlacement().GetStatus();

	if (status == Aws::GameLift::Model::GameSessionPlacementState::PENDING)
	{
			return CheckGameSessionPlacement(); // 아래 함수 정의에서 확인
	}
}

// DescribeGameSessionPlacement API를 사용하여 게임 세션 배치가 완료되었는지 확인
bool CheckGameSessionPlacement()
{
    // polling 방식으로 확인하는 예
    while (true)
	{
		Aws::GameLift::Model::DescribeGameSessionPlacementRequest req;
		req.SetPlacementId(“위의 배치 요청에서 사용된Unique ID”);
		auto outcome = GetAwsClient()->DescribeGameSessionPlacement(req);
		if (outcome.IsSuccess())
		{
			auto gs = outcome.GetResult().GetGameSessionPlacement();

                                           // 게임 세션 배치 완료 확인
			if (gs.GetStatus() == Aws::GameLift::Model::GameSessionPlacementState::FULFILLED)
			{
				auto arn = gs.GetGameSessionArn();
				
				// 게임 세션 ID를 통해 해당 게임 세션이 생성된 서버의 IP주소와 Port를 확인
				Aws::GameLift::Model::DescribeGameSessionDetailsRequest request;
				request.SetGameSessionId(arn);
				auto response = GetAwsClient()->DescribeGameSessionDetails(request);
				if (response.IsSuccess())
				{
					auto result = response.GetResult().GetGameSessionDetails();
					// IP주소 = result[0].GetGameSession().GetIpAddress();
					// Port 번호 = result[0].GetGameSession().GetPort();
					// 이후 플레이어 세션 생성을 하면 됨
                                                                        // … …
   				}
			}

		}
		Sleep(…);
	}
        // … 이하 오류처리 부분은 생략 …
}

보시다시피 GameLift Matchmaking Queue를 이용하면 비교적 간단하게 세션형 멀티플레이어 게임을 전세계 지역을 대상으로 서비스할 수 있습니다. 물론, StartGameSessionPlacement API에 몇 가지 정보만 더 주게 되면, 전세계에 흩어져 있는 플레이어 대상으로 지연 시간 기반의 최적화된 게임 세션 배치를 좀 더 효율적으로 할 수 있습니다. StartGameSessionPlacement API의 요청 구조는 다음과 같습니다.

{
   "DesiredPlayerSessions": [ 
      { 
         "PlayerData": "string",
         "PlayerId": "string"
      }
   ],
   "GameProperties": [ 
      { 
         "Key": "string",
         "Value": "string"
      }
   ],
   "GameSessionName": "string",
   "GameSessionQueueName": "string",
   "MaximumPlayerSessionCount": number,
   "PlacementId": "string",
   "PlayerLatencies": [ 
      { 
         "LatencyInMilliseconds": number,
         "PlayerId": "string",
         "RegionIdentifier": "string"
      }
   ]
}

지연 시간 기반의 배치 요청을 위해서는 크게 두 부분의 속성 값을 제공해야 합니다. 우선 “DesiredPlayerSessions” 속성값을 통하여 생성하고자 하는 플레이어 세션 정보를 입력하여야 합니다. 즉, 게임 세션 생성 요청과 동시에 플레이어 세션 생성도 한꺼번에 할 수 있다는 뜻입니다. (이 경우는 이후에 따로 CreatePlayerSessions API를 호출할 필요 없이 바로 DescribePlayerSessions API를 통해 생성된 플레이어 세션에 대한 정보를 바로 확인 할 수 있습니다.)

플레이어 세션 생성을 위한 정보 이외에 “PlayerLatencies” 속성에 해당하는 지연 시간 정보를 입력해주어야 합니다. 해당 플레이어의 ID정보(“PlayerId”)와 각 리전으로부터 지연 시간 정보(“RegionIdentifier”로부터 “LatencyInMilliseconds”만큼의 지연이 발생하는지)를 제공하면 사전에 설정된 플릿간의 우선순위를 무시하고 게임 세션에 참여하는 플레이어들의 지연 시간에 최적화된 리전 내 플릿을 선택합니다.

즉, 서비스 하고자 하는 게임의 특성에 따라 우선순위 기반 게임 세션 배치를 사용할 것인지, 지연 시간 최적화를 이용한 게임 세션 배치를 사용할 것인지 선택할 수 있습니다.

GameLift Best Practice 아키텍처
게임 세션 생성 또는 플레이어 세션 생성 요청 등에 사용되는 GameLift Client API는 AWS SDK를 통해서 사용할 수 있습니다. 게임 서비스에 있어서 이런 종류의 GameLift Client API를 게임 클라이언트에서 직접 사용하도록 하는 경우 게임 클라이언트가 GameLift API를 호출할 수 있는 권한을 갖고 있어야 합니다.

이 권한은 AWS IAM의 credentials 정보를 직접 클라이언트에 제공하여 권한을 부여할 수도 있고, Amazon Cognito와 같은 인증 서비스를 통해 제공할 수도 있습니다.  그렇지만, 일반적으로 세션형 멀티플레이어 게임을 서비스 할 때, 플레이어들이 게임에 참여하기 전에 대기할 로비(Lobby) 서버나 같이 게임 할 팀원 또는 상대를 찾도록 도와주는 매체메이킹서버를 따로 운영하게 됩니다.

이러한 경우, GameLift Client API사용을 게임 클라이언트에서 직접 수행하기 보다는 로비 서버나 매치메이킹 서버가 이 역할을 수행하게 됩니다. 즉, 게임 클라이언트는 GameLift Client API 호출을 위한 권한이나 인증 정보에 대해 전혀 몰라도 됩니다. 대신 EC2 인스턴스 위에서 운영되는 로비 서버나 매치메이킹 서버에서 직접 GameLift에 API요청을 하면 됩니다. 이때, API 요청 권한을 위한 Credentials 제공을 따로 할 필요 없이 IAM EC2 역할(Role)을 통하여 GameLift API 요청 권한을 부여하면 됩니다. GameLift를 통하여 세션형 멀티플레이어 게임을 서비스함에 있어서 Lobby/Matchmaking서버를 사용하는 일반적인 아키텍처는 다음 그림과 같습니다.

Amazon GameLift Fleet에 배포될 게임 서버에서는 GameLift Server SDK를 사용하여 GameLift 서비스와 통합할 수 있고, EC2 인스턴스 위에서 운영되는 로비나 매치메이킹 서버에서는 AWS SDK의 GameLift Client API를 통하여 GameLift 서비스에 요청하는 구조입니다.

이제 Amazon GameLift의 새로운 기능인 Matchmaking Queue를 활용함으로써, 전세계 AWS 리전에 있는 다수의 게임 서버들을 탄력적으로 활용 할 수 있습니다. 이는 지연 시간에 민감한 세션형 멀티플레이어 게임을 전세계의 플레이어 대상으로 낮은 지연 속도(low-latency) 경험을 제공할 수 있음은 물론, 게임 서비스를 제공하는 회사 입장에서도 글로벌 단위의 GameLift 스케일링 기능을 통하여 비용 효율적으로 게임 서비스를 운영할 수 있음을 의미합니다.

참고하실 글

본 글은 아마존웹서비스 코리아의 솔루션즈 아키텍트가 국내 고객을 위해 전해 드리는 AWS 활용 기술 팁을 보내드리는 코너로서, 이번 글은 구승모 솔루션즈 아키텍트께서 작성해주셨습니다.