This document is about: PUN 2
SWITCH TO

PUN Classic (v1), PUN 2, Bolt는 휴업 모드입니다. Unity2022에 대해서는 PUN 2에서 서포트하지만, 신기능의 추가는 없습니다. 현재 이용중인 고객님의 PUN 및 Bolt 프로젝트는 중단되지 않고, 퍼포먼스나 성능이 떨어지는 일도 없습니다. 앞으로의 새로운 프로젝트에는 Photon Fusion 또는 Quantum을 사용해 주십시오.

WebRPCs

WebRPC는 외부 서비스를 Photon 클라우드로 통합하는 유연한 방식 입니다.
예를 들면 WebRPC로 Photon 클라이언트들은 외부 웹서비스에서 데이터를 가져오도록 요청 할 수 있습니다.

웹 서비스 기본

WebRPC 아키텍처는 간단 합니다.
Photon 서버는 클라이언트와 웹서버 사이에서 Proxy 또는 Relay 역할을 합니다.
WebRPC webhook의 확장 또는 커스텀 클라이언트-주도 webhook의 형태가 될 수 있습니다.

WebRPC를 사용하기 위해서는 HTTP 기반 서비스를 구현하고 어플리케이션의 Webhooks 섹션의 BaseURL 에 대한 설정을 해야 합니다.
Photon Webhook과 WebRPC는 동일한 BaseURL을 공유 합니다.

다음의 개발 팁을 고려 해 보시기 바랍니다:

  • 언어, 프레임워크, 웹서버 종류에 관계없이 WebRPC에 대한 서비스를 구현 할 수 있습니다.
  • 모든 경우에 있어서 전송하는 데이터를 최소화하여 플레이어가 데이터의 부담을 느끼지 않도록 해야 합니다.

요청(Request)

클라이언트 측에서 Photon WebRPC는 마스터 또는 게임서버에 접속 후 실행 할 수 있는 Photon 오퍼레이션 입니다.
URL 경로 (WebRPC 메소드 명으로 알려짐)가 필요하고 추가적인 두 번째 아규먼트에는 웹 서비스에게 전송되는 데이터가 포함되어 있습니다(WebRPC 파라미터로 알려져 있음).
이 두개의 아규먼트들은 PhotonNetwork.WebRPC메소드에 전달 될 수 있습니다.

UriPath 는 WebRPC의 상대 경로로 원격 프로시져 이름과 일치해야 합니다.
URL 자체에 파라미터를 전달 하는 것도 가능 합니다.
이 경우에 query string 안의 상대 경로에 프로시져 이름이 추가되어 있어야 합니다.

Photon 서버가 요청을 받을 때 프로시져 이름은 동일 URL의 웹 서비스에게 포워딩 되기전에 BaseURL에 합쳐져서 WebRPC URL의 절대 경로로 만들어지게 됩니다.

HTTP 요청 메소드는 POST로 사용되기 때문에 클라이언트가 전송한 파라미터가 있으면 Photon은 4개의 기본 프로퍼티와 같이 JSON POST 데이터로 전달합니다:

  • AppId:
    게임 클라이언트에서 설정된 어플리케이션의 AppID이며 관리화면에서 찾아 볼 수 있습니다.

  • AppVersion:
    게임클라이언트에서 설정된 어플리케이션의 버전.

  • Region:
    게임 클라이언트 접속 지역.

  • UserId:
    WebRPC를 작성한 액터 ID.

클라이언트가 전송한 다른 프로퍼티는 파라미터의 일부에 있습니다.
클라이언트에서 사용된 WebRpcParameters 타입이 Dictionary<string, object> 이면 모든 키/값 쌍은 post 데이터 객체의 루트내에서 전송 됩니다.
이외에 JSON 유효 타입인 경우에 새로운 프로퍼티인 RpcParams 의 값으로 포함되게 될 것 입니다.

예제 1

BaseURL 이 https://my.service.org/ 이고 클라이언트가 WebRPC("method?id=1", parameters)를 호출 합니다.
파라미터들은 Dictionary<string, object> 타입이며 내용은 {"key1": 1, "key2": "yx", "key3": true} 입니다.
이 경우에 Photon은 https://my.service.org/method?id=1 을 호출 하고 추가적인 JSON을 POST 합니다.

JavaScript

{
    "AppId": "00000000-0000-0000-0000-000000000000",
    "AppVersion": "client-x.y.z",
    "Region": "EU",
    "UserId": "userXYZ",
    "key1": 1,
    "key2": "yx",
    "key3": true
}

예제 2

BaseURL은 https://my.service.org/ 이고 클라이언트가 WebRPC("method?id=1", parameters)를 호출 합니다.
파라미터들은 객체들의 배열 입니다:object[] { 0, "xy", false }.
이 경우에 Photon은 https://my.service.org/method?id=1 호출 하고 추가적으로 JSON을 POST 합니다:

JavaScript

{
    "AppId": "00000000-0000-0000-0000-000000000000",
    "AppVersion": "client-x.y.z",
    "Region": "EU",
    "UserId": "userXYZ",
    "RpcParams": [0, "xy", false]
}

예제 3

BaseURL은 https://my.service.org/ 이고 클라이언트가 WebRPC("method?id=1", parameters) 를 호출 합니다.
파라미터들은 단순한 원시 타입입니다. 문자열을 "test"라고 가정해 봅시다.
이 경우에 Photon은 https://my.service.org/method?id=1 호출 하고 추가적인 JSON을 POST 합니다:

JavaScript

{
    "AppId": "00000000-0000-0000-0000-000000000000",
    "AppVersion": "client-x.y.z",
    "Region": "EU",
    "UserId": "userXYZ",
    "RpcParams": "test"
}

데이터의 안전한 전송

WebRPC는 암호화된 객체 AuthCookie를 사용 할 수 있는 경우 웹 서비스로 안전하게 전송 할 수 있는 옵션을 제공 합니다.
클라이언트 코드에서 WebRPC 오퍼레이션 메소드를 호출 할 때 웹 플래그 (SendAuthCookie = 0x02)를 설정하여 수행 될 수 있습니다.
커스텀 인증 프로바이더로 부터 인증을 성공 한 후에 AuthCookie 를 얻을 수 있습니다.
상세한 정보는 커스텀 인증 문서 페이지를 참고 하시기 바랍니다.

응답(Response)

WebRPC의 웹서비스는 Photon이 클라이언트에게 결과를 전송 할 수 있도록 JSON 객체에 응답 해야 합니다.
응답에는 ResultCode, Data 와 선택적으로 Message 가 포함되어 있어야 합니다.

ResultCode 는 정상적인 경우 0이 되어야 하며 다른 값은 오류에 대한 코드 입니다.
이 코드를 통해 클라이언트가 오류에 대한 처리를 할 수 있도록 하고 읽을 수 있는 메시지를 추가하는 것이 가장 좋은 방식 입니다.

Data는 비어 있을 수 있으나 아무 것이라도 보낼 수 있으며 반드시 올바른 JSON 형식이어야 합니다.

모든 Photon 오퍼레이션과 같이 WebRPC의 OperationResponse 에는 ReturnCode 가 있습니다.
만약 오퍼레이션이 성공했다면 ErrorCode.Ok (0) 값을 가지고 있을 것 입니다.
오류가 발생했으면 ErrorCode.ExternalHttpCallFailed (32744) 가 표시 될 것 입니다.
에러의 경우 추가적인 정보는 OperationResponse.DebugMessage 를 검토하여 볼 수 있습니다.

대부분의 클라이언트 SDK에는 WebRPC 결과를 해석 해주는 helper 클래스인 WebRpcResponse 가 포함되어 있습니다.
WebRpcResponse 는 오퍼레이션 응답 파라미터에 있는 다음의 키로 부터 값을 가져와 웹 서비스의 리턴되는 결과 코드, 데이터와 추가적인 메시지를 작성 할 것 입니다.

  • ParameterCode.WebRpcReturnCode (207) 웹 서비스가 리턴 해준 ResultCode 값을 가지고 있습니다.
  • ParameterCode.WebRpcReturnMessage (206) 웹 서버가 리턴한 이벤트 메시지를 담고 있습니다.
  • ParameterCode.WebRpcParameters (208) 웹 서버가 리턴한 데이터를 담고 있습니다.
    웹 서버로부터 전송된 데이터의 타입에 따라 예상되는 타입은 다르지만 유효한 JSON 이어야만 합니다.

URL 태그

대시보드에서 Webhooks 또는 WebRpc 의 기준 URL을 설정 할 때 하나 이상의 "동적 변수"를 설정 할 수 있습니다.
이러한 변수들은 요청을 전송하기 전에 백엔드에서 상응하는 값들로 교체 되게 됩니다.

어플리케이션에서 사용자들을 서로 다르게 분리하여 처리를 원할 때 URL 태그는 매우 간편합니다.
Photon 은 다음의 URL 태그들을 지원합니다:

  • {AppVersion} 은 클라이언트에서 설정된 어플리케이션 버전을 전달 합니다.
  • {AppId} 는 어플리케이션의 ID 를 전달 합니다.
  • {Region} 은 클라이언트가 접속된 클라우드 지역에 대한 토큰을 전달 합니다. 예, "eu".
  • {Cloud} 는 클라이언트가 연결된 클라우드의 이름을 전달 합니다. 예, "public" 또는 "enigmaticenterprise".

URL 태그 유즈케이스의 예제

  1. https://{Region}.mydomain.com/{AppId}?version={AppVersion}&cloud={Cloud}
  2. https://mydomain.com/{Cloud}/{Region}/{AppId}/{AppVersion}

WebRPC를 선택하는 이유

HTTP 요청을 전송하는 것은 Photon 없이 수행 할 수 있습니다.
스스로 HTTP 클라이언트를 작성하거나 SDK 또는 라이브러리에서 사용 할 수 있는 것을 사용하면 됩니다.
하지만 Photon WebRPC는 더 많은 것을 제공하고 있습니다.
몇 가지 잇점을 나열해 보겠습니다:

  • 모든 로직을 하나의 연결된 클라이언트로 보관하고 "라이프사이클"과 "상태 머신"을 처리하는 것이 더 좋습니다. Photon 클라이언트는 이러한 경우 필요한 것 입니다.
  • WebRPC 오퍼레이션은 JSON 양방향 직렬화를 해 줍니다.
  • "BaseURL" 과 메소드 이름 지원 URL 태그 기능.
  • AuthCookie 사용하거나 서버와 서버간에 더 민감한 데이터 교환시에 클라이언트 식별에 대하여 검증 할 수 있습니다. 이 기능에 대한 사용방법은 여기를 읽어 주시기 바랍니다..

GetGameList

여기에서는 Photon 제품의 내장 기능도 아니고 바로 사용할 수 있는 기능이 아닌 GetGameList 에 대한 예제를 설명합니다.
GitHub 페이지에서 제공하고 있는 샘플 중의 하나를 활용 하려면 원격 프로시져 이름의 철자에 주의 해야 합니다. 여기에는 's' 가 포함되어 있지 않습니다 : GetGamesList.
한편 WebRPC 구현에서는 편한대로 이름을 선택 할 수 있습니다.

GetGameList 는 특별한 WebRPC 로서 "메모리 데모"에서 Photon 의 WebRPC 기능을 설명할 때 처음 소개되었습니다.
대부분의 게임에서 이전에 저장되었던 게임들을 가져올 필요가 있기 때문에 더 특별 합니다.

웹 서비스에 게임의 목록을 가져 오는 요청을 보낸 후 게임에 다시 참여하고 플레이를 계속한다는 것은 기본적인 사상 입니다.
이렇게 하기 위해서는 대시보드 에서 어플리케이션이 올바르게 설정 되었다는 것을 가정 하고 있습니다:BaseUrl 은 반드시 웹서비스 위치를 가리켜야 하고 게임 데이터를 보존 하기 위하여 IsPersistenttrue 로 설정 되어 있어야 하며 GameClose webhook 이 활성화 되어 있어야 합니다.
GameClose Webhook 은 게임데이터로써 매우 중요한데 Photon의 직렬화된 룸 State 에 따라 웹 서비스에 의해 GameClose Webhook 이 리턴 되어야 합니다.

웹 서비스는 저장된 게임 데이터에 접근 할 수 있어야 하며 HTTP POST 요청을 수신, 처리 하여 WebRPC 의 패스(여기에서는GetGameList)로 전송 할 수 있도록 준비가 되어야 합니다.

Photon 은 모든 WebRPC 요청 데이터에 UserId 를 전송하기 때문에 커스텀 파라미터를 추가할 필요는 없습니다.
Request는 아무런 아규먼트 없이 전송됩니다.

웹 서비스는 JSON 객체로 Data 에 저장된 게임의 목록을 리턴 해야 합니다. 저장된 게임들의 GameId 가 키가 됩니다.
각 값은 JSON 객체로 두개의 프로퍼티로 구성되어 있습니다:

  • ActorNr: WebRPC 를 호출하는 액터의 원시 ActorNr 와 동일 하며 이 ActorNr 는 처음에 해당 룸에 참여 할 때 부여 받은 것 입니다. ActorNr 는 저장할 때 의도적으로 분리하여 저장한 경우에 쉽게 로드될 수 있으며 각 룸의 저장된 State 객체를 ActorList 와 UserId를 비교하는 루프를 돌아 얻을 수 있습니다.

  • Properties: CustomRoomProperties의 키:값 쌍으로 로비에서 볼 수 있고 각각의 룸의 저장된 State 안에 포함되어 있는 CustomProperties 프로퍼티로 부터 얻을 수 있습니다.

JavaScript

{
   "ResultCode":0,
   "Message":"",
   "Data":{
      "RoomA":{
         "ActorNr":3,
         "Properties":{  
            "Map":"USA",
            "Mode":"FFA",
         "Respawn":true
         }
      },
      "RoomB":{
         "ActorNr":5,
         "Properties":{  
            "Map":"Germany",
            "Mode":"TDM",
         "TeamA":1,
            "TeamB":5
         }
      },
      "RoomC":{
         "ActorNr":1,
         "Properties":{  
            "Map":"Russia",
            "Mode":"CTF",
         "TeamA":1,
            "TeamB":5,
         "Flag":20
         }
      }
   }
}

마스터와 게임서버에서 WebRPC 호출이 허용되어 있을 지라도 룸에 참여하기 전에 GetGameList 를 호출 하는 것이 적절합니다. 즉, 마스터 서버에서 호출하는 것입니다.

웹 서비스로 데이터 전달 하기

WebRPC 는 클라이언트에서 웹 서비스로 데이터를 전달하는 3가지 방식을 지원 합니다.
3가지 타입은 독립적으로 사용하거나 조합하여 사용할 수 있습니다.
예를들면 GetGameList를 확장하여 저장된 게임 목록을 특정 상대를 기반으로 필터 한 후 얻고 싶을 때 세가지 다른 방식을 사용할 수 있습니다.

  1. RESTful 방식:

    상대 URI: /GetGameList/{opponentId}

  2. POST data:

    상대 URI: /GetGameList

    Request data:

    Plain Old Text

    {
      "AppId": "00000000-0000-0000-0000-000000000000",
      "AppVersion": "client-x.y.z",
      "Region": "EU",
      "UserId": "userXYZ",
      "OpponentId": "opponentId"
    }
    
  3. Query string (GET 파리미터로 알려져 있음):

    상대 URI: /GetGameList?opponentId={opponentId}

물론 웹 서비스는 선택된 메소드에 따라 업데이트 되어야 합니다.

데이터 타입 변환

이 섹션에서는 Photon 서버와 웹 서비스간의 데이터 교환의 타입에 대해서만 설명합니다.
클라이언트와 Photon 서버간의 데이터 타입에 대해서는 Photon 의 직렬화 페이지를 참고 하시기 바랍니다.

Photon 서버 -> 웹 서비스

C# / .NET (Photon 지원 타입) JavaScript / JSON
byte number
short
int
long
double
bool bool
string string
byte[] (byte 배열 length < short.MaxValue) string (Base64 encoded)
T[] (array of supported type T, length < short.MaxValue) array
Hashtable (of supported types, count < short.MaxValue, preferably Photon implementation) object
Dictionary (keys and values of supported types, count < short.MaxValue) object
null null

샘플 요청 데이터 (타입들이 연결되어 있습니다)

Photon 서버 전송:

JavaScript

{
    "(Dictionary<String,Object>)Dictionary":{
        "(Int32)dk_int":"1",
        "(String)dk_str":"dv2",
        "(Boolean)dk_bool":"True"
    },
    "(Hashtable)Hashtable":{
        "(Byte)hk_byte":"255",
        "(Object[])hk_array":[
            "(Object)0",
            "(Object)xy",
            "(Object)False"
        ],
        "hk_null":"null"
    },
    "null":"null",
    "(String[])string[]":[
        "(String)PUN",
        "(String)TB",
        "(String)RT",
        "(String)Bolt",
        "(String)Chat"
    ],
    "(Byte[])byte[]":[
        "(Byte)255",
        "(Byte)0"
    ],
    "(Int16[])short[]":[
        "(Int16)-32768",
        "(Int16)32767"
    ],
    "(Int32[])int[]":[
        "(Int32)-2147483648",
        "(Int32)2147483647"
    ],
    "(Int64[])long[]":[
        "(Int64)-9223372036854775808",
        "(Int64)9223372036854775807"
    ],
    "(Single[])float[]":[
        "(Single)-3.402823E+38",
        "(Single)3.402823E+38"
    ],
    "(Double[])double[]":[
        "(Double)-1.79769313486232E+308",
        "(Double)1.79769313486232E+308"
    ],
    "(Boolean[])bool[]":[
        "(Boolean)True",
        "(Boolean)False"
    ]
}

Web 서비스 수신:

JavaScript

{
    "(object)Dictionary":{
        "dk_int":"(number)1",
        "dk_str":"(string)dv2",
        "dk_bool":"(boolean)true"
    },
    "(object)Hashtable":{
        "hk_byte":"(number)255",
        "hk_null":null,
        "hk_array":[
            "(number)0",
            "(string)xy",
            "(boolean)false"
        ]
    },
    "null":null,
    "(array)string[]":[
        "(string)PUN",
        "(string)TB",
        "(string)RT",
        "(string)Bolt",
        "(string)Chat"
    ],
    "byte[]":"(string)/wA=",
    "(array)short[]":[
        "(number)-32768",
        "(number)32767"
    ],
    "(array)int[]":[
        "(number)-2147483648",
        "(number)2147483647"
    ],
    "(array)long[]":[
        "(number)-9223372036854776000",
        "(number)9223372036854776000"
    ],
    "(array)float[]":[
        "(number)-3.40282347e+38",
        "(number)3.40282347e+38"
    ],
    "(array)double[]":[
        "(number)-1.7976931348623157e+308",
        "(number)1.7976931348623157e+308"
    ],
    "(array)bool[]":[
        "(boolean)true",
        "(boolean)false"
    ]
}

웹 서비스 -> Photon 서버

C#/.Net 에서 JavaScript/JSON 타입과 각각 대응하는 테이블입니다.
Here is a table that matches each JavaScript/JSON type to its equivalent one in C#/.Net :

JavaScript / JSON C# / .Net
object Dictionary
array object[] (array of objects)
number (integral) long
number (floating) double
string string
boolean bool
null (not a type) null
undefined (when sent) null

응답 데이터 샘플 (타입들이 연결되어 있습니다)

Web Service 전송:

JavaScript

{
    "(object)number": {
        "MAX_VALUE": "(number)1.7976931348623157e+308",
        "MIN_VALUE": "(number)5e-324"
    },
    "(object)object": {
        "string": "(string)xyz",
        "null": null,
        "bool": "(boolean)false",
        "undefined": "(undefined)undefined",
        "number": "(number)-3.14"
    },
    "(array)array": [
        "(string)xyz",
        "(number)0",
        "(boolean)true",
        null,
        "(undefined)undefined"
    ]
}

Photon Server 수신:

JavaScript

{
    "(Dictionary<String,Object>)number":{
        "(Double)MAX_VALUE":"1.79769313486232E+308",
        "(Double)MIN_VALUE":"4.94065645841247E-324"
    },
    "(Dictionary<String,Object>)object":{
        "(String)string":"xyz",
        "null":"null",
        "(Boolean)bool":"False",
        "(Double)number":"-3.14"
    },
    "(Object[])array":[ 
        "(Object)xyz",
        "(Object)0",
        "(Object)True",
        "null",
        "null"
    ]
}
Back to top