server | v4 switch to v3  

Photon 플러그인 FAQ

Photon 플러그인은 Enterprise 클라우드 또는 자체 Photon Server v4 에서만 사용할 수 있습니다.

Contents

플러그인 설정

Photon 서버의 커스텀 플러그인의 환경설정은 어떻게 하나요?

app.config 내에 플러그인 XML 노드를 추가해야 합니다.

아래는 필요 최소 엘리먼트 입니다:

WebHooks 플러그인 환경설정 예제 입니다:

상세한 정보는 플러그인 메뉴얼의 "환경설정" 섹션을 읽어 보세요.

Back To Top

Photon 은 여러개의 플러그인을 지원 하나요?

어플리케이션 하나당 하나의 플러그인 어셈블리(DLL) 또는 플러그인 팩토리만 설정 될 수 있습니다. 이 DLL 내에서 다수의 플러그인을 원하는 대로 넣을 수 있습니다. 룸 생성시에는 하나의 플러그인만 로드되며 인스턴스 생성이 됩니다. 룸과 플러그인의 인스턴스는 1:1 관계이므로 각각의 룸은 자신의 플러그인 인스턴스 하나를 가지고 있습니다.

The following configuration is not allowed:

Back To Top

룸을 생성할 때 사용할 플러그인은 어떻게 선택하나요?

플러그인 모델은 팩토리 패턴을 사용합니다. 플러그인은 요구시에 이름으로 인스턴스가 생성됩니다. 클라이언트는 roomOptions.Plugins 를 사용하여 플러그인 설정을 요청하여 룸을 생성합니다. roomOptions.Pluginsstring[] 타입이고 첫 번째 문자열(roomOptions.Plugins[0])은 팩토리에게 전달되는 플러그인 이름이 되어야 합니다.

예제:
roomOptions.Plugins = new string[] { "NameOfYourPlugin" };
또는 roomOptions.Plugins = new string[] { "NameOfOtherPlugin" };

만약 클라이언트가 아무것도 전송 하지 않으면 서버는 기본값(아무것도 설정되어 있지 않을 때) 또는 설정 되어있는 경우에는 생성시 리턴하는 플러그인 팩토리 아무것이나 사용하게 됩니다.

팩토리에서는 다음과 같이 이름을 이용하여 상응하는 플러그인을 로드 하게 됩니다:

만약 클라이언트가 요청한 것과 PluginFactory.Create 에서 리턴된 이름이 일치 하지 않는다면 플러그인은 로드되지 않을 것이며 클라이언트의 생성 또는 참여 오퍼레이션은 PluginMismatch (32757) 오류를 내며 실패하게 됩니다.

Back To Top

저는 실행시에 디스크의 파일을 읽고 싶습니다. 플러그인이 파일 시스템에 접근할 수 있나요? 이러한 파일들을 외부 서버로 부터 다운로드 해야 하나요?

플러그인에 필수 추가 파일들을 업로드 할 수 있습니다. 이 파일들의 경로는 typeof(yourplugin).Assembly.Location 를 사용하여 얻을 수 있습니다.

Back To Top

메인 플러그인 DLL이 로드될 때 의존 모듈(DLL)들이 자동으로 로드 되는건가요? 저는 왜 System.TypeLoadException 오류가 나고 있나요?

외부 모듈의 사용은 DLL 참조 또는 플러그인 솔루션 프로젝트에 의해서 동작되어야만 합니다. 모든 참조 의존성 모듈들이 로드 및 링크되기 위하여 플러그인 DLL이 있는 동일 디렉토리에 배포 되었는지 확인해 주세요.

Back To Top

플러그인 콜백

OnCreateGame 에서 어떻게 액터 번호를 얻을 수 있나요?

게임의 첫 번째 액터, 즉 게임을 만든 플레이어는 항상 액터 번호가 1로 설정됩니다. 게임 상태를 로드하여 "재생성" 하는 룸의 경우에는 로드된 룸 상태에서 ActorList 루프를 돌면서 UserId를 비교하여 액터 번호를 찾아야 합니다.

Back To Top

서버에서 룸을 생성하는 것이 가능한가요?

아니오. 서버에서 룸 생성은 불가능 합니다.

Back To Top

서버에서 룸을 "폐쇄"하는 것이 가능 한가요?

아니오. 현재는 불가능 합니다.

Back To Top

플러그인 이벤트에서 사용할 수 있는 데이터를 알 수 있는 가장 좋은 방법은 무엇인가요?

일반적으로 이벤트의 ICallInfo 콜백 파라미터로 어떤 것이 필요 한지 알 수 있습니다. 대부분의 경우에 있어어, 실제 오퍼레이션 요청 파라미터들은 {ICallInfo}.OperationRequest (또는 Request) 프로퍼티를 통해서 얻을 수 있습니다.

Back To Top

플레이어가 룸에 입장 시도 할 때 호출되는 메소드는 무엇인가요?

이 질문에 답변하기 위해서 4가지의 다른 시나리오가 있다는 것을 이해 해야 합니다. 이 시나리오는 플레이어가 룸을 생성, 룸에 참여, 룸에 재참가 하는 시도가 포함 됩니다. 일반적으로 JoinRoomCreateRoom 오퍼레이션의 구조들은 매우 유사하며 JoinMode가 다르게 하나의 논리적 Join 오퍼레이션으로 살펴보면 도움이 될 것 입니다.

  1. Create: OpCreateRoom: 룸이 이미 존재하면 오류가 리턴 됩니다.
  2. Join: OpJoinRoom (JoinMode.Default 또는 미설정), OpJoinRandomRoom: 룸이 존재 하지 않으면 오류가 리턴 됩니다.
  3. CreateIfNotExists: OpJoinRoom (JoinMode.CreateIfNotExist): 룸이 존재하지 않으면 룸을 생성 합니다.
  4. RejoinOnly: OpJoinRoom (JoinMode.RejoinOnly): 룸안에 액터가 존재하지 않으면 오류가 발생 됩니다.

만약 룸이 메모리상에 있다면 2)에서 4)까지는 BeforeJoin가 트리거 되며 {ICallInfo}.Continue() 호출 할 것이라고 가정 하여, OnJoin 이 호출 됩니다.

OnCreateGame 은 플러그인이 셋업되고 액터가 룸에 추가되기 바로 직전에 룸 생성 직후 바로 호출 됩니다.

이 메소드는 1), 3) 과 4)에 의해 트리거 됩니다. 4)는 상태의 저장과 로딩이 올바르게 처리되었을 경우에만 발생 할 수 있습니다. 플레이어가 룸에 대해서 재참여를 요구했으나 해당하는 방이 서버 메모리에서 제거 되었을 경우에 발생하게 됩니다. 어쨋든 Photon 은 룸을 생성하고 플러그인을 셋업하게 됩니다. 그이후 플러그인은 데이터베이스 또는 외부 서비스로 부터 직렬화된 상태를 가져오고 SetSerializedGameState 를 호출 합니다. 상태의 모든 액터의 리스트, 비활성 상태에 있는 모든 내용이 있습니다. 재참여는 활성화 시켜야 하는 액터를 재활성화 해 줍니다.

Back To Top

UseStrictMode 는 어떤 기능 인가요?

플러그인의 사상은 수신 요청 전 또는 후에 "일반적인" Photon 흐름 안으로 후크 하는 것 입니다. 처음에 우리는 무엇인가를 호출 해야만 하는 것을 강요하지 않았습니다. 이는 근본적으로 수신된 요청의 기본 처리를 취소하는 것과 동일한 것입니다. 이것은 개발자가 기본 콜백 로직을 사용하지 않고 처음부터 요구된 모든 것을 구현해야 한다는 것 입니다. 이렇게 하면 예상치 못한 이슈가 발생하게 됩니다. 취소는 개발자가 원하는 것을 항상 처리해 주지 않습니다. 따라서 strict 모드를 도입 하였으며 이 모드는 개발자에 의해서 결정할 수 있도록 합니다. 이제 Continue, Fail 또는 Cancel을 (이중에서) 단 한 번만 호출 할 수 가 있습니다. 프로세스 지연을 위해서 사용되는 Defer메소드도 있습니다.

Back To Top

언제 PluginBase의 콜백메소드를 오버라이딩 해야 하나요,base.XXX() 를 호출 해야 하나요?

PluginBase 내부의 모든 콜백 메소드는 끝에 Continue() 호출이 포함되어 있습니다. PluginBase 부터 상속을 받는 사상으로 필요한 메소드만 오버라이드 하면 됩니다. 때로는 base.XXX() 전후에 추가적인 코드가 필요할 수 도 있으며 때로는 완전하게 기본 행위를 변경할 필요가 있습니다. 첫 번째 경우는 ICallInfo 처리 메소드의 어떤 호출도 추가 해서는 안됩니다. 두 번째 경우에서는 base.XXX()을 호출 해서는 안되며 처음부터 자신만의 메소드를 구현하여 ICallInfo 처리 메소드 중의 하나에 호출을 추가 해야 합니다. 하나의 예외가 있는데 PluginBase.OnLeave입니다. 이 메소드는 현재 하나가 떠나는 경우에 MasterClient 변화를 처리할 것 입니다.

Back To Top

이벤트

HttpForward 프로퍼티와 WebFlags 클래스는 어떤 일을 하나요?

이 두가지는 WebHooks 와 관련된 기능들 입니다. WebFlags 는 WebHooks v1.2 플러그인 부터 도입 되었습니다. 이 페이지 로 이동하여 WebFlags에 대한 정보를 살펴 보세요. HttpForward 는 webflag 에 상응하는 값을 나타냅니다.

비록 초창기에는 WebHooks와 WebRPC 를 위하여 만들어진 것이지만 플러그인에서도 필요시 사용할 수 있습니다.

Back To Top

플러그인으로 부터 어떻게 이벤트를 전송하나요?

PluginHost.BroadcastEvent 는 이 목적으로 사용되어야 합니다. 클라이언트로 부터 이벤트 전송하는 것과 동일한 방식으로 동작하지만 주요 차이점은 서버에게 전송 액터 번호를 0 으로 설정할 수 있다는 것 입니다.

    this.PluginHost.BroadcastEvent(
                recieverActors: new int[] { targetPlayerNr},
                senderActor: 0,
                data: new Dictionary<byte, object>() { { 1, data } },
                evCode: (byte)code,
                cacheOp: 0,
                sendParameters: new SendParameters() { Unreliable = false });

상세 정보는 플러그인 메뉴얼의 "플러그인으로 부터 이벤트 전송" 섹션을 읽어 보시기 바랍니다.

Back To Top

OnRaiseEvent 와는 다르게 OnJoin 콜백 내부에서 PluginHost.BroadcastEvent 를 이용하여 클라이언트로 이벤트 전송이 절대 성공할 수 없는 이유는 무엇인가요?

OnRaiseEvent 내에서 클라이언트는 이미 참여한 상태로 이벤트를 수신할 수 있습니다. OnJoin 에서는 요청이 {ICallInfo}.Continue() 를 사용한 것이 아니면 클라이언트가 완전하게 참여한 것이 아닙니다.

따라서 {ICallInfo}.Continue() 후에 PluginHost.BroadcastEvent 가 호출되면 이벤트는 타겟 클라이언트들에 의해 수신되어야 합니다.

플러그인 후크는 다음과 같은 방식으로 동작합니다: Photon의 일반적인 처리를 {ICallInfo}.Continue()로 호출하여 트리거합니다. 이 경우에는 참여가 완벽하게 이루어진 이후에 이벤트 전송을 하는 것이 더 상식적입니다.

Back To Top

왜 플러그인에서 전송한 이벤트를 클라이언트가 수신하지 못하나요?

이것은 알려진 이슈 입니다. 클라이언트들은 알려진 키 코드(이벤트 데이터의 245와 액터 번호의 254)로 사전 정의된 구조체로 이벤트가 전송되는 것을 기대하고 있습니다. 수정 하려면 플러그인에서 이벤트 데이터 전송방식을 약간 변경하면됩니다:(Dictionary<byte,object>)eventData 대신에 new Dictionary<byte,object>(){{245,eventData},{254,senderActorNr}} 로 전송하세요.

Back To Top

플러그인이 커스텀 오퍼레이션을 지원 하나요?

아니오. 현재 플러그인은 커스텀 오퍼레이션을 지원하지 않습니다. Photon 플러그인은 네이티브 오퍼레이션("Create", "Join", "SetProperties", "RaiseEvent" 과 "Leave")에 대한 콜백만 지원 하고 있습니다. 플러그인 SDK를 사용하여 커스텀 오퍼레이션(자체 호스트하는 Photon 서버에 추가됨)은 가로챌 수 없거나 새로운 것으로 확장될 수 없습니다.

하지만 2 가지 방식의 이벤트를 교환하여 동일한 결과를 얻을 수 있습니다:

  • 클라이언트에서 플러그인으로 LoadBalancinglient.OpRaiseEvent 호출.
  • 플러그인에서 클라이언트로 PluginHost.BroadcastEvent 호출.

Back To Top

게임 상태

무엇으로 "활성" 사용자와 "비활성" 사용자를 구분 할 수 있나요?

사용자가 Photon 에서 접속해제가 되면 일반적으로 모든 것인 없어지며 액터가 제거 됩니다. 하지만 룸을 생성 할 때 CreateOptions 옵션에서 PlayerTTL 를 정의할 수 있습니다.

값이 양수 이면 Photon 은 제거하기 전에 해당 시간(밀리세컨드로 정의됨) 동안은 대기 합니다. 그러는 동안 액터는 비활성으로 간주되며 게임에 다시 참여 할 수 있습니다. 만약 재참여가 성공 했으면 플레이어는 다시 활성 상태로 변경 됩니다.

이 기능은 게임 상태를 저장하여 플레이어가 계속 게임을 할 수 있게 하거나 연결상태가 좋지 않아 플레이어의 접속이 해제될 수 있는 RTS 게임의 인스턴스에 매우 유용합니다. 하지만 수 분 이내의 짧은 시간에 다시 복귀 할 수 있도록 하는 것이 좋습니다.

PluginHost.GameActorsActive 에는 룸내(참가한)의 모든 액터들의 정보가 있고 PluginHost.GameActorsInActive 에는 룸에서 나간 모든 액터(퇴장 당하지 않은)들의 정보가 있습니다.

Back To Top

플러그인에서 액터를 방에서 나가게 할 수 있나요? 할 수 있다면 어떻게 하나요?

네. 가능합니다. 플러그인 클래스에서 PluginHost.RemoveActor(int actorNr, string reasonDetails) 를 호출 하셔야 합니다. 3개의 파라미터를 가진 오버로드 메소드를 호출할때 룸에서 나간 이유를 설정 할 수 있습니다:PluginHost.RemoveActor(int actorNr, byte reason, string reasonDetails).

Back To Top

어떻게 룸 상태를 유지 하나요?

룸 상태를 저장하기 위해서는:

  1. PluginHost.GetGameState 를 호출 하고 SerializableGamestate 를 얻습니다.
  2. 상태를 직렬화(serilize) 합니다(예를 들어 JSON 형태로).
  3. 데이터 스토어에 상태를 저장 합니다.

룸 상태를 로드 하기 위해서는:

  1. 데이터 스토어에서 상태를 가져 옵니다.
  2. 상태를 비직렬화(desrialize)합니다.
  3. PluginHost.SetGameState 를 호출 합니다.

노트: {ICallInfo}.Continue()를 호출 하기 전에 OnCreateGame내에서 PluginHost.SetGameState 만 호출 할 수 있습니다.

Back To Top

SerializableGameState 에서 일부 커스텀 룸 프로퍼티들을 찾을 수 없네요? 로비 프로퍼티만 보입니다. 왜 이런가요?

직렬화 가능한 게임 상태에서는 설계에 의하여 모든 커스텀 프로퍼티들을 접근 하도록 제공 하지 않습니다. 로비에서 공유되는 것들만 노출되며 "보기위한" 목적일 뿐 입니다. 모든 프로퍼티들은 바이너리 배열에 들어가 있습니다. 이렇게 해야 JSON 이 시리얼라이즈 할 수 있고 비직렬화 할 때 타입 정보를 잃어버리지 않습니다. 이 기능은 저장/로드 시나리오만을 위하여 설계된 것입니다. 나중에 이 행위를 변경할 수 도 있습니다.

Back To Top

플러그인에서 룸 프로퍼티들( MaxPlayers, IsVisible, IsOpen 등)을 어떻게 접근 하나요?

모든 룸 프로퍼티들은 Hashtable 타입인 PluginBase.PluginHost.GameProperties로 접근 할 수 있습니다. 이러한 프로퍼티들은 "네이티브" 또는 "잘 알려진" 것들 입니다. 이 프로퍼티들은 값을 참조하는 byte 키들을 가지고 있으며 LoadBalancing 클라이언트 SDK 의 LoadBalancing.GamePropertyKey 또는 API로 취득 할 수 있습니다.

키를 byte 로부터 int를 형변환 해야 하는 것에 주의 하세요(예,MaxPlayers = (byte)255). 향후에 이러한 값들을 더 편리하게 가져오는 방식을 제공 할 것 입니다.

PluginHost.GameProperties 에도 커스텀 룸 프로퍼티가 있습니다. 개발자는 이러한 키/값을 처리해야 합니다.

다른 한편으로, 로비에서 보여지는 커스텀 프로퍼티들은 Dictionary<string, object> 형태인 PluginBase.PluginHost.CustomGameProperties 로 저장됩니다.

다음 처럼 플러그인에서 룸과 액터 프로퍼티에 접근(읽기와 쓰기)할 수 있습니다:

Reading examples:

Writing examples:

Back To Top

쓰레딩

.Net 플러그인 컴포넌트에 대한 쓰레딩 요구사항

단일 Photon 서버에서 몇개의 쓰레드가 동작할 수 있나요?

아래와 같이 구분 됩니다:

  1. 네이티브 - 9개 쓰레드
  2. 매니지드 - .Net 디폴트 설정을 사용하는 .Net ThreadPool에 기반

이 셋업은 매우 엄격히 테스트 되었고 다양한 로드 프로필에서 매우 잘 동작하고 있습니다.

매니지드 쓰레드의 사용(.Net Win counters 에 의해 리포트된 것에 따르면)은 다양합니다.

a) 전형적인 Photon 클라우드 RealTime 로드에서 12개 까지 b) 내부 플러그인 커뮤니케이션(locking)을 가진 플러그인이 동작하는 고객 클라우드의 예에서 보면 35 (이상)의 쓰레드는 높은 밀집도 원인 (우리 코드와는 반대)입니다

노트: 필요시에 .Net ThreadPool 설정을 조정할 수 있습니다. 지금까지 기본값으로도 훌륭한 결과를 얻고 있지만 매 버전마다 다를 수 도 있습니다.

Back To Top

플러그인 코드를 작성할 때 쓰레드 안정성에 대해서 주의를 기울여야 하는 것이 있나요?

일반적으로 플러그인으로의 모든 호출은 시리얼라이즈되는 것으로 가정하는 것이 안전합니다(가상 쓰레드이며 반드시 동일한 물리 쓰레드가 아님).

Back To Top

Photon 호스트는 쓰레드에서 안전한가요? 특정 쓰레드가 모든 룸에 언제든지 접근 할 수 있나요?

메시지 전달 아키텍처가 있습니다: 한번에 하나의 쓰레드에서 플러그인이 호출 됩니다. 하지만 쓰레드 풀을 사용하기 때문에 각각의 호출에서는 동일한 쓰레드가 아닙니다.

Back To Top

Enterprise 클라우드

Enterprise 클라우드 실행 환경과 관련된 질문들.

어떻게 플러그인 환경 설정을 하나요?

Photon Enterprise 클라우드 에서는:
Photon 관리화면의 어플리케이션 관리 페이지에서 새로운 플러그인을 추가해야 합니다. 관리페이지에서 페이지 아래의 "새로운 플러그인 생성(Create a new Plugin)" 버튼을 클릭 합니다. 이제 키/값 엔트리를 추가하여 플러그인 환경 설정을 할 수 있습니다. AssemblyName, Version, PathType 은 필수 사항입니다.

Back To Top

Photon 플러그인 제작에서 파이프라인 프로세스는 무엇인가요?

파이프라인 프로세스는 Photon 플러그인을 작성하는 과정으로 간단 합니다:

  1. 필요한 SDK와 서버 바이너리를 다운로드 합니다.
  2. 코드하고 플러그인 어셈블리를 빌드 합니다.
  3. 디플로이 하고 테스트 합니다.
  4. 업로드 합니다.
  5. 환경 설정을 합니다.

Photon 플러그인의 환경 설정은 다음 처럼 수행됩니다:

  • 개발: 로컬 머신
  • 테스트: 로컬 네트워크 내
  • 스테이징: 클라우드로 앱의 스테이징(별도 AppId)
  • 프로덕션: 라이브 AppId

Back To Top

플러그인 업로드는 자동 업로드 되나요??

네. SDK 루트 경로 아래의 "UploadToPhotonCloud" 폴더안에 PowerShell 스크립트를 제공하고 있습니다. 상세한 내용에 대해서는 플러그인 업로드 온라인 지침서를 보시기 바랍니다.

Back To Top

플러그인 성능은 어떻게 모니터링 하나요?

관리화면에서 사용할 수 있는 수많은 카운터를 추적 합니다. 추가적으로 커스텀 카운터를 추가하거나 카운터 외부 툴을 통합할 수도 있습니다.(예, New Relic) 이러한 서비스가 필요하면 컨설팅 계약서 준비가 필요합니다.

Back To Top

플러그인의 로그를 보는 방법이 있나요?

당사 서버의 로그에 접근할 수 있는 권한은 없습니다. 따라서 로그 또는 경우를 위해서 외부 서비스가 사용 되어야 합니다. logentries를 추천해 드립니다.

Back To Top

기타

클라이언트 SDK 에서는 커스텀 타입을 등록하여 직렬화 확장을 지원 합니다. 서버에서 어떻게 이러한 타입을 비직렬화 하나요?

커스텀 타입은 클라이언트 SDK와 동일한 방식으로 다음과 같이 등록할 수 있습니다.

PluginHost.TryRegisterType(type: typeof (CustomPluginType), typeCode: 1, serializeFunction: SerializeFunction, deserializeFunction: DeserializeFunction);

상세한 정보는 플러그인 메뉴얼의 "커스텀 타입" 섹션을 참고하시기 바랍니다.

기술문서 TOP으로 돌아가기