Photon Server 4에서 제공한 새로운 프로그램 설계 방법: Photon Plugins(플러그인) 설계의 주요 목적은 편리하고 빠른 프로그램 연결 방식으로 서버 쪽 게임 / 룸의 부가 기능 달성에 있습니다.
Photon Plugins은 Enterprise Cloud나 자체 호스팅(Self-Hosted) Photon Server v4에서만 사용할 수 있습니다.
주의: 기존의 Photon 3 버전이라면 서버 기능 응용프로그램을 추가하거나 확장해야 하며 일반적인 승계 방식을 통해서만 실행할 필요가 있습니다.
때로는 간단한 기능을 사용하거나 신속하게 클라이언트와 연결해 여러 방법을 테스트하고 싶다면 이 새로운 플러그인 방식이 가장 적합합니다.
지금부터 우선 플러그인의 기본 개념과 프로세스에 대해 알아보고, 게임에 서버 쪽 카운팅(Counting / Scoring) 같은 미니 기능을 추가해보겠습니다!
개념:
Photon Plugin마다 각각의 식별 이름이 있으며, 플러그인 프로그램 내용은 우리가 처리하고 싶은 이벤트에 대응하는 콜백(예시: OnJoin, OnLeave)을 만들어 냅니다. 우리가 사용하는 플러그인 프로그램은 dll 조합 파일로 편역되며, 그 다음에 dll 조합 파일이 Photon Server에 설치되거나 Enterprise Cloud로 업로드됩니다.
매번 클라이언트에서 새로운 룸 구축을 요구할 때마다, Photon Server에서 관련된 설정 dll 조합 파일을 자동으로 로딩하게 됩니다. 해당 플러그인 촉발로 생성된 룸은 플러그인 프로그램에서 호스트라 하고, 인스턴스를 생성한 후 플러그인 프로그램에서 직접 해당 호스트 내 매개 변수를 액세스하거나 다른 서버와 연산하거나 외부 WebServices와 연결할 수 있습니다.
또한, 호스트와 플러그인 인스턴스 플러그인에 동일한 프로그램 생명주기(lifecycle)가 존재하고 있으며, 이 둘은 호스트(룸)가 종료될 때 관련 플러그인 인스턴스 플러그인도 함께 종료되는 1:1의 관계입니다.
기본 과정:
사용자 정의 서비스 로직을 추가하고 싶다면, 반드시 당사의 프로그램 코드를 Photon Server에 정의해 놓은 인터페이스(Hooks)에 주입해야 합니다(inject). 현재 Photon Server에서는 게임 서버 플러그인 유형에 대해 오직 룸과 관련된 이벤트로 촉발된 인터페이스만 정의 및 지원(예시: OpJoinRoom)하고 있습니다.
사용자 인터페이스를 지원하지는 않으나 RaiseEvent+Broadcast 방식으로 사용자 이벤트 처리 시스템을 만들 수 있습니다. Photon Server 플러그인에 대한 시리즈 체계로, 이하 6단계로 진행합니다.
- Intercept the hook call -- hook 호출 차단
콜백 촉발 시, 호스트에서 통제권을 인스턴스 플러그인으로 보내게 됩니다. - Alter call info -- 호출 정보 수정(옵션으로 선택)
실제로 hook call을 실행하기 전에, 먼저 클라이언트/서버에서 송출하는 요청 자료(request)를 액세스하고 수정합니다. - Inject custom code -- 시리즈 사용자 일부 프로그램 기능 주입(옵션으로 선택)
Hook call을 실행하기 전에, 우선 호스트와 일부 과정을 처리합니다(예, HTTP 요청 발송, room/actor 상황 조회, 카운트 값설정 등등). - Process hook call -- 기존의 hook 호출 처리
실제로 해당 요청 처리 실행 장소를 결정합니다. 자세한 내용은 "ICallInfo Processing Methods" 다시 검토 가능 - Inject custom code -- 시리즈 사용자 일부 프로그램 기능 재주입(옵션으로 선택)
실행 후, 기존 클라이언트/서버에서 송출한 요청이 "읽기 전용"이 됩니다. 단, 처리 실행 후, 플러그인은 여전히 호스트와 상호 작용하며 변경 데이터를 액세스합니다. - Return - 리턴, 종료
플러그인에서 통제권을 호스트에게 되돌려주면서 해당 작업이 종료됩니다.
Photon Server 4 SDK에 Webhooks 1.2의 소스 코드가 존재합니다. 이 자체가 바로 가장 좋은 Photon Plugins 범례입니다. 관심 있다면 우선 연구해 볼 수도 있겠지요, 잠시 후 WebHooks 사용법에 대해 소개해보겠습니다!
최소 플러그인(Minimal Plugin)
플러그인 요소는 플러그인 자체, 플러그인 Factory 및 Photon Server에 설정한 구성 정보의 세 부분으로 나뉩니다. 따라서 우선 가장 작은 플러그인 형태에 대해 알아봐야만 단계적으로 해당 기능을 확장할 수 있습니다.
(1) 플러그인 자체
가장 간단하면서도 권장할 만한 플러그인 완성 방식은 PluginBase Class 확장으로, 이 방식만이 직접 승계나 모든 IGamePlugin 화면에서 실행하는 것보다 좋은 선택입니다. 그다음 우리가 원하는 기능 하나를 덮어쓰기만 하면 됩니다.
가장 작은 플러그인 작성법은, PluginBase.Name값을 덮어쓰기만 하면 됩니다. 해당 플러그인 식별값이기도 합니다.
주석: Default에는 플러그인 이름을 사용할 수 없습니다.
namespace MyCompany.MyProject.HivePlugin
{
public class CustomPlugin : PluginBase
{
public override string Name
{
get
{
return "CustomPlugin"; // anything other than "Default"
}
}
}
}
(2) The Factory 기능
Photon Plugin 생성 시 소프트웨어 설계 모드 내 factory design pattern을 사용해서 플러그인은 클라이언트에 따라 룸 구축 시, 로딩하려는 플러그인 이름을 설정하면 factory에 그에 상응하는 플러그인 인스턴스를 구축하여 생성하게 됩니다.
Factory Class는 플러그인 모듈의 실제 일부에 속하기 때문에 이들의 namespace도 같습니다. 룸마다, factory는 대응되는 플러그인 인스턴스 플러그인을 책임지고 생성하기 위해 존재합니다.
IPluginFactory.Create로 진입 시, 해당 플러그인에서 생성한 체체인 룸 호스트가 촉발되어, IPluginHost의 자료형으로 전송해오면, 룸 데이터 및 기능 호출을 액세스할 수 있습니다.
이어서 우리가 IGamePlugin.SetupInstance를 다시 호출할 때, 해당 호스트 값을 내보냅니다. 이때 전후로 모두 동일한 매개 변수를 사용합니다. 즉, 동일한 룸 겨냥해 서비스한 것입니다.
편의상 아래의 PluginFactory에서 CustomPlugin의 한 인스턴스를 바로 백패스합니다. 우선은 클라이언트에서 요구한 플러그인 이름을 조사하지 않아도 됩니다.
namespace MyCompany.MyProject.HivePlugin
{
public class PluginFactory : IPluginFactory
{
public IGamePlugin Create(
IPluginHost gameHost,
string pluginName, // name of plugin requested by client
Dictionary<string, string> config, // plugin settings
out string errorMsg)
{
var plugin = new CustomPlugin();
if (plugin.SetupInstance(gameHost, config, out errorMsg))
{
return plugin;
}
return null;
}
}
}
(3) Photon Server Configuration 구성 (Photon Server:)
아래 XML 노드를 우리 게임 서버 애플리케이션 내 Photon.LoadBalancing.dll.config 파일 안에 추가합니다. 매우 간단하게 <PluginSettings> 요소의 Enabled 값을 변경해 이 플러그인 설정 사용 여부를 선택할 수 있습니다.
이 중 <Plugin> 내 속성값 AssemblyName, Version, Type은 모두 반드시 채워 넣어야 하며, 다른 속성 매개 변수는 프로그램 조건에 따라 추가할 수 있습니다.
* AssemblyName: 플러그인 dll 구성 파일 명칭
* Version: 해당 플러그인 버전
* Type: 해당 플러그인 PluginFactory 전체 명칭 생성
<PluginSettings Enabled="true">
<Plugins>
<Plugin
AssemblyName="{filename}.dll"
Version="{version}"
Type="{namespace}. {pluginfactory class name}"
/>
</Plugins>
</PluginSettings>
좋습니다, 위 내용들은, 가장 간단한 Photon Plugin 생성 및 설정 방법이었습니다. 지금부터는 Photon Server 플러그인 프로그램을 이용한 기능 확장에 대한 내용입니다. 관련 내용에 대한 기본적 지식이 있으시죠? 아니면 벌써부터 머리가 아파오시는지요?!
자, 그럼 계속해서 작은 기능을 진행해보겠습니다. 서버 쪽 Counting(Scoring)은 서버에서 진행하는 카운트 기능입니다. 그럼 Photon Plugins 기능 추가의 편리성과 실용성에 대해 직접 구현보겠습니다!
서버측 구현
이곳에서 사용하는 것은 Visual Studio 내 카테고리 라이브러리(.dll) 제작 전용 기능으로, 직접 RaiseEventTestPlugin라 하는 것을 만들어보겠습니다~
우리가 사용하는 framework 버전은 .NET Framework 4이며, 전용 파일 위치 경로는 원하는 위치에 설정할 수 있으니, 최종 생성한 .dll의 위치만 파악하고 있으면 됩니다.
using System;
using System.Collections;
using System.Collections.Generic;
using Photon.Hive;
using Photon.Hive.Plugin;
namespace TestPlugins
{
class RaiseEventTestPlugin : PluginBase
{
public string ServerString { get; private set; }
public int CallsCount { get; private set; }
public RaiseEventTestPlugin()
{
this.UseStrictMode = true;
this.ServerString = "ServerMessage";
this.CallsCount = 0;
}
public override string Name
{
get
{
return this.GetType().Name;
}
}
public override void OnRaiseEvent(IRaiseEventCallInfo info)
{
try
{
base.OnRaiseEvent(info);
}
catch (Exception e)
{
this.PluginHost.BroadcastErrorInfoEvent(e.ToString(), info);
return;
}
if (info.Request.EvCode == 1)
{
++this.CallsCount;
this.PluginHost.BroadcastEvent(
target: ReciverGroup.All,
senderActor: 0,
targetGroup: 0,
data: new Dictionary<byte, object>() { { (byte)245, this.CallsCount } },
evCode: info.Request.EvCode,
cacheOp: 0);
}
}
}
}
상단의 프로그램 코드가 바로 우리의 플러그인 프로그램 그 자체입니다. 우선 해당 코드를 RaiseEventTestPlugin이라 칭하면 되고, 이 플러그인에서는 클라이언트에서 발송해온 특정 이벤트 수신에 주목하면 되는데, 해당 플러그인에서 차단하려는 것은 OnRaiseEvent입니다.
이곳 내부에 변수 CallsCount가 존재하고 있어플러그인 프로그램 생성 시 0으로 초기화되고, 매번 이곳 룸 내 클라이언트에서 PhotonNetwork.RaiseEvent를 호출하고, 이벤트 코드 EventCode = 1을 전달할 때, 서버 플러그인 프로그램 OnRaiseEvent가 실행되기 시작합니다. CallsCount는 1회 카운팅 후, 이어서 CallsCount 값을 해당 룸 내 모든 클라이언트로 전달한 후, 후속 처리를 진행합니다. 이러한 방식은, 클라이언트와 서버 의 쌍방향 통신으로, 우리는 이러한 시스템으로 다른 기능을 개발할 수도 있습니다!
using System.Collections.Generic;
using Photon.Hive.Plugin;
namespace TestPlugins
{
public class PluginFactory : IPluginFactory
{
public IGamePlugin Create(IPluginHost gameHost, string pluginName, Dictionary<string, string> config, out string errorMsg)
{
var plugin = new RaiseEventTestPlugin();
if (plugin.SetupInstance(gameHost, config, out errorMsg))
{
return plugin;
}
return null;
}
}
}』
위 프로그램 코드는 우리 Factory로, 프로그램 코드의 간소화를 위해, 우선 pluginName 값을 생략하고, 바로 RaiseEventTestPlugin 인스턴스로 생성했습니다~
위 그림처럼, 우리의 Photon Server Plugin 전용 파일은 매우 심플하게 두 번째 C# 파일이면 됩니다. 생성하고 나면, 조직 방안 기능으로 dll 구성 파일을 생성할 수 있고, 생성한파일 그룹은 전용 파일 bin\release나 bin\debug안에 두고, 모든 파일을 Photon Server 디렉터리 내 deploy\Plugins\MyTestPlugins\bin 안에 복사해 넣습니다.
<Photon Server → deploy → Plugins → MyTestPlugins → bin 내 구성 파일>
Photon Server에서 해당 플러그인 구성 파일을 찾을 수 있도록 우리가 PhotonServer 디렉터리 아래 deploy\Loadbalancing\GameServer\bin에서 Photon.LoadBalancing.dll.config파일을 찾은 후 PluginSettings를 Enabled="true"로 설정하고, 다시 그 안의 값을 우리의 플러그인 구성 파일과 관련된 설정에 기입합니다. 그럼 아래 그림의 노랑색 글자처럼 보여지게 됩니다.
PhotonServer 디렉터리 내 deploy\Loadbalancing\GameServer\bin 설정 파일
Photon Server에 설계된 Photon Server 애플리케이션은 그에 대응되는 플러그인 한 개만 존재하기 때문에 우선 config 설정 파일에서 기존의 WebHooksPlugin 1.2를 삭제하고(위 그림의 회색 글자 부분), 우리가 생성한 dll 구성 파일(위 그림 속 노랑색 글자 부분)로 변경합니다. 반드시 PluginSettings의 Enabled를 true로 설정해야 합니다!
Photon Server 활성화
상단의 세 번째 부분(플러그인 본체, factory 생성, config 설정) 모두 구성하고 나면 우리의 Photon Server를 부팅할 수 있습니다. 아직 기억하시나요? PhotonControl.exe가 실행되기 시작하면 IP를 설정하고 다시 LoadBalancing(MyCloud) → Start as Application을 선택합니다. 이렇게 해야만, 게임 서버의 로그 파일에서 아래 파란색 부분을 볼 수 있고, 우리의 RaiseEventTestPlugin 구성 파일을 Photon Server에서 찾을 수 있으며 안전하게 로딩하게 됩니다.
게임 서버 내 DLL 찾기 및 로딩 완료
현재 플러그인이 로딩되어 우선 플러그인 관련 자료를 메모리에 로딩하고, 매번 새로운 룸이 생성될 때에만 모두 게임 서버 factory 생성 시스템을 통해 서비스 전용 플러그인 인스턴스가 생성됩니다.
더 업그레이드된 방법은 클라이언트에 룸 생성 시 네임 스트링을 옵션으로 해 별개로 Photon Server로 전달하면 생성하고 싶은 서버 성능을 선택해 서비스로 추가할 수 있습니다! 아주 멋진 시스템이죠! 👍😀👏🏻
잠시 휴식시간을 가지면서 본 파트의 흐름과 운영을 정리하고, 이어서 클라이언트 쪽 구현 부분을 보며 서버 쪽과의 연결 후 운영 방법에 대해 알아보겠습니다! 잠시만 기다려주세요~
댓글
댓글 0개
댓글을 남기려면 로그인하세요.