자료실

[Photon엔진강좌] Photon Server SDK (Part 2) — 게임 서버
작성자 | admin 2020-07-20  |    조회수 : 3832  



Photon Server SDK (Part 2) — 게임 서버

대형 서버 라인의 게임 응용프로그램이나 비지니스 로직(서버 쪽 게임 로직)을 만들려면 MMO처럼 대규모 세계의 게임 타입도 Photon Server SDK 내의 MMO 애플리케이션 프레임 만들기를 참고해 개발할 수 있습니다. 해당 프레임을 이해하고 나면 자신이 원하는 새로운 MMO 서버까지도 만들 수 있습니다.

만약 개발하고 싶은 것이 Room-Based이고, 안전성이나 일괄 관리, 또는 기타 이유로든 게임 응용프로그램이나 비지니스 로직을 서버 쪽에서 실행하고 싶다면, 현재 새 버전인 Photon Server 4에서 이하 두 가지 방식으로 실행할 수 있습니다.

  • 룸 내 기능 추가 : Photon의 Plugins을 통한 커스터마이징
  • ong>룸 외/서버 전체 기능 추가: SDK에서 제공하는 애플리케이션을 커스터마이징

이 두 방식 모두 각자 상대적인 장점을 가지고 있습니다. 우선 이 방식과 게임 서버 간에 어떤 관련성이 있는지 이해하고 나면 실제 프로젝트 조건에 따라 적합한 운용 방식을 선택할 수 있습니다.

Lite(Hive) App 및 게임 서버 관련성

Photon SDK에서 Lite(Hive) 애플리케이션 해당 범례의 설계가 수월하고, 주로 Room-Based 유형 게임의 응용프로그램 기본 프레임을 구현해냅니다. LoadBalancingApplication 내부의 게임 서버 또한 Lite(Hive) app에서 변화 확장되어 나온 것으로, 주기능은 다음과 같습니다.

  • 매 룸마다 일부 소규모 사용자는 동일한 공간에서 상호 교류할 수 있고, 다른 룸의 사용자나 일반 이벤트에도 영향을 받지 않습니다.
  • 룸마다 그에 상응하는 게임이 존재하며, 게임명이 바로 룸을 식별하는 라벨입니다.
  • 사용자마다 해당 공간(룸) 내 어떤 룸이든 가입하거나 탈퇴할 수 있습니다.
  • 연결한 클라이언트마다 사용자를 식별할 수 있는 이름 ID가 존재합니다.
  • 매 룸과 사용자를 겨냥해 일부 프로퍼티(Properties)를 설정한 후 새로 가입한 사용자가 해당 설정을 획득하고 나면 룸 진입 시 초기화 설정을 쉽게 진행할 수 있습니다.


위에서 제공하는 기능을 구현하기 위한 LiteApp 개념 몇 개가 필요합니다.

이하 참고 개념:

Peers

Peer는 바로 이미 서버와 연결한 사용자의 인스턴스입니다.

LitePeer Class 내 Peer의 패킷 및 확장 용법

Lite App에 연결한 클라이언트 네트워크가 있다면, LiteApplication.CreatePeer은 그에 상응하는 LitePeer를 만들고, 해당 클라이언트에서 전해오는 조건(operations) 모두 그에 상응하는 LitePeer에서 처리하게 됩니다.

Lite App 내의 매 LitePeer 마다 상태 지시의 RoomReference 가 존재합니다. 사용자들은 동일 시간 내 임의 룸 안에만 존재할 수 있습니다. 따라서 Join Room 할 때, 해당 상태 지시 기능이 생성됩니다. peer 연결이 끊어질 경우, LitePeer 에서 OnDisconnect를 호출하게 됩니다. 만약 peer가 여전히 어느 한 공간(Room)에 있을 경우, 해당 peer를 삭제할 수 있습니다.

Rooms

Lite App내, peer마다 룸으로 입장하고 나면 그 안에서 함께 서로 데이터를 주고받으면서 게임이 시작됩니다. 게임마다 별개의 명칭을 가지며, 다른 게임과 완벽하게 독립됩니다. 사용자가 전송해 온 요청(request)마다 순서대로 처리됩니다. 여러 게임 공간 모두 밸런싱이 존재해 요청마다 거의 실시간(ticks)으로 처리되기 때문에 이후 프레임 확장에 사용할 수 있는 매우 편리한 방식입니다.

게임 공간 룸 마다 해당 공간 내 사용자 목록을 기록하고, Join / Leave / Disconnect 시 데이터가 업데이트됩니다. 또한, 사용자 간에도 이런 Events를 전송합니다. 룸 내, 사용자 모두 Actor로 표시되고, Actor마다 값 ActorNumber를 가지고 있어 이벤트(event)를 전송할 때마다, 해당값으로 발송자를 식별합니다.

Operations & Events

Lite Application 내 정의된 Operation:

  • Join: 지정한 명칭으로 임의 룸 입장이 가능, 지정한 명칭이 존재하지 않을 경우 새로운 룸 생성 만약 Peer 전에 다른 룸에 가입한 적이 있다면, 새로 Join 시 자체적으로 기존의 가입한 룸에서 탈퇴하게 됩니다.
  • Leave: 룸 탈퇴. 단, 서버와의 연결은 유지됩니다.
  • RaiseEvent: 룸에서 전송하려는 Event를 다른 사용자에게 알립니다. 이벤트마다 EventCode 번호 및 일부 데이터를 포함하고 있습니다.
  • GetProperties: Lite App에서 룸 / 플레이어가 설정한 일부 매개 변수를 가져올 수 있습니다.
  • SetProperties: Lite App에서 룸 / 플레이어가 설정한 일부 매개 변수를 저장할 수 있습니다. 서버 쪽에서는 룸 / 플레이어 등의 매개 변수 데이터를 사용하지 않습니다.


Lite Application 내 정의된 Event:

  • Join: Join Room 발생 시, 룸 내 사용자 폼이 업데이트되어 사용자 모두 업데이트 내용을 수신하게 됩니다.(가입한 사용자도 수신함)
  • Leave: 한 사용자가 룸에서 탈퇴 시 동일 룸 내 사용자 모두에게 해당 소식을 알려줍니다.
  • Propertiesupdate: 일부 매개 변수(Actor/Room)가 변경되면 모두가 해당 소식을 접하게 되고, 클라이언트마다 이를 근거로 일부 매개 변수 데이터를 업데이트합니다.


RaiseEvent / Custom Events

Lite app 설계 시, 동일 공간 룸 내 임의 사용자가 다른 사용자에게 임의 데이터를 가진 Event를 전송할 수 있습니다. 물론, 구현 시 소량의 변동 데이터만 전송하는 것이 가장 좋습니다.

아래와 같이, 해당 클라이언트 쪽의 프로그램 코드에서 그에 상응하는 플레이어 위치 자료를 전송보내게 됩니다. RaiseEvent를 직접 호출해 해당 이벤트 및 데이터를 처리 송출합니다.

// Raises an event with the position data of this player.

internal void SendEvMove(LitePeer peer)
{
if (peer == null)
{
return;
}

Hashtable evInfo = new Hashtable();
evInfo.Add((byte)STATUS_PLAYER_POS_X, (byte)this.posX);
evInfo.Add((byte)STATUS_PLAYER_POS_Y, (byte)this.posY);


//OpRaiseEvent(byte eventCode, Hashtable evData, bool sendReliable, byte channelId, bool encrypt)

peer.OpRaiseEvent(EV_MOVE, evInfo, isSendReliable, (byte)0, Game.RaiseEncrypted);
}

LiteApp에서 서버 쪽 전송 과정에서는 다른 처리를 실행하지 않기 때문에 우리가 전송하고 싶은 특정 데이터만 전송하면 됩니다. 위 프로그램의 EV_MOVE는 LiteApp에서 그 의미를 알 수 없기 때문에, 클라이언트 쪽끼리만 상호 인지하고 있으면 됩니다.

수신자 측에서는 위 프로그램 코드에서 EventAction를 호출합니다.

이벤트 및 데이터 처리 예시:

//in Game.cs:
public void EventAction(byte eventCode, Hashtable photonEvent)
{
int actorNr = 0;
if (photonEvent.ContainsKey((byte)LiteEventKey.ActorNr))
{
actorNr = (int) photonEvent[(byte) LiteEventKey.ActorNr];
}

// get the player that raised this event
Player p;
this.Players.TryGetValue(actorNr, out p);

switch (eventCode)
{
case Player.EV_MOVE:
p.SetPosition((Hashtable)photonEvent[(byte)LiteEventKey.Data]);
break;
//[...]
}
}

//in Player.cs:
internal void SetPosition(Hashtable evData)
{
this.posX = (byte)evData[(byte)STATUS_PLAYER_POS_X];
this.posY = (byte)evData[(byte)STATUS_PLAYER_POS_Y];
}

클라이언트에서 이벤트 데이터 상호 전송 이외에, 서버에서도 필요에 따라 모든 이벤트를 전송할 수 있습니다. 이에 관심이 있으신 분은 HandleRaiseEvent 방법을 참고해주세요!

Properties

Lite App에서 properties도 HashTable을 대표하는 구조이고, 사용자의 데이터 설정 및 인출 작업이 용이합니다. 보통 Actor나 룸에 추가합니다. 따라서, 룸 내 일부 데이터 및 사용자 이름 설정에 사용할 수 있고, 클라이언트 데이터 획득도 가능합니다.

SetProperties / GetProperties로 관련 데이터를 얻을 수 있습니다. 사용자가 룸 입장 시, Join할 때 일부 properties를 설정해 넣을 수 있습니다. 다른 사용자가 바로 정보를 획득하기도 합니다. 단, Join할 때마다 기존에 설정한 룸 데이터를 재설정(덮어쓰기)하는데, 새로운 룸을 생성해 Join을 진행해야만 데이터를 설정할 수 있고 그 후, 가입한 사람에 관한 설정은 아무 효과가 없습니다.

Photon Plugins 및 게임 서버 관련성

Photon 4에서 제공한 새로운 프로그램 설계 방법: Photon Plugins, "플러그인"이라고도 합니다. 이 설계로 우리는 아주 편리한 방식으로 서버 쪽 게임 / 룸 부가 기능--스코어(Scoring), 스코어 랭킹(LeadersBoard) 등의 세세한 기능을 사용할 수 있습니다.

단, 만약 사용한 것이 기존의 Photon 3 버전이라면 서버 기능 응용프로그램을 추가하거나 확장해야 하며 일반적인 승계 방식을 통해서만 실행해야 합니다.

Photon PluginsEnterprise Cloud자체 호스팅(Self-Hosted) Photon Server v4에서만 사용할 수 있습니다.

개념:

사용자 정의 서비스 로직을 추가하고 싶다면, 반드시 우리의 프로그램 코드를 Photon Server에 정의해 놓은 인터페이스(Hooks)에 주입해야 합니다. 현재 Photon Server에서 지원하는 게임 서버 플러그인 유형은 오직 룸과 관련된 이벤트로 촉발된 인터페이스만(예시: OpJoinRoom) 존재합니다.

정의대로 말하면, Photon Plugin 마다 각각의 식별 이름이 있으며, 이벤트에 상응하는 콜백(예시: OnJoin)을 구현해야 합니다. 우리가 입력한 플러그인이 dll 파일로 편역되면, 이것을 우리의 Photon Server에 설치하거나 Enterprise Cloud로 업로드합니다.

새로운 룸이 생성될 때마다 Photon Server에서 이미 설정한 dll을 자동으로 로딩하게 됩니다. 해당 플러그인 촉발로 생성된 룸을 호스트라고 합니다. 로딩된 플러그인에서 해당 호스트를 직접 액세스할 수 있고, 이들은 동일 생명주기(lifecycle)를 가집니다.

기본 과정

Photon과 플러그인하려는 체계로, 이하 6단계로 진행:

1.Intercept the hook call
콜백 촉발 시, 호스트에서 통제권을 플러그인으로 보내게 됩니다.
2.[옵션] Alter call info
실제로 hook call을 실행하기 전에, 클라이언트/서버에서 송출하는 요청 자료(request)를 액세스하고 수정합니다.
3.[옵션] Inject custom code
Hook call을 실행하기 전에 우선 호스트와 일부 과정을 처리합니다(예, HTTP 요청 발송, room/actor 상황 조회, 카운트 값 설정 등등).
4.Process hook call
실제로 해당 요청 처리 방법을 결정합니다. "ICallInfo 프로세싱 메소드" 참고 가능
5.[옵션] Inject custom code
실행 후, 클라이언트/서버에서 송출한 요청이 "읽기 전용"이 됩니다. 단, 처리 실행 후, 플러그인은 여전히 호스트와 상호 작용합니다.
6.Return
플러그인 통제권을 호스트에 반환합니다.

Webhooks은 좋은 Photon Plugins 예시로, Webhooks 1.2의 기존 코드를 플러그인 SDK에 포함합니다. 관심있으신 분은 한 번 연구해보세요~

Webhooks은 좋은 Photon Plugins 예시로, Webhooks 1.2의 기존 코드를 플러그인 SDK에 포함합니다. 관심있으신 분은 한 번 연구해보세요~

혹시 뭔가 이상한 점 못 느끼셨나요? 왜 멀티플레이어 온라인 프로그램을 개발하려면 네트워크 기술 파일과 고유명사는 제쳐두고 거의 모두가 게임 내 필요한 구조 및 일부 호출할 Operation / Events 자체 정의만 있으면 되는 걸까요? 이 또한 Photon Engine 강력한 장점 중 하나입니다. 하하~

그래서, Room-Based의 Hive App 구조에 대해 알아보았고, LoadBalancing 확장을 시작으로, 서버 쪽의 애플리케이션을 작성할 수 있고, 나아가 Photo Plugins 방식을 사용할 수 있어, 바로 Photon Server 4에서 제공하는 LoadBalancing App에서, 빠르게 우리가 생성한 게임 / 룸 특수 기능과 연결할 수 있습니다. 이것이 바로 Photon Engine의 강점입니다~

이어지는 파트에서는 우선 Photon Plugins 방식으로 신속하게 서버 쪽 게임 서버 기능을 추가하는 방법에 대해 알아보겠습니다~ Stay Tuned!


★더 많은 글은Photon HelpCenter
https://support.photonengine.jp/hc/ko/categories/204651467 에서 확인하세요!
★Photon 공식 홈페이지
https://www.photonengine.com/ko-kr/Photon