WebRPCs

WebRPC는 외부 서비스를 Photon 클라우드로 통합하는 유연한 방식 입니다. 예를 들면 WebRPC로 Photon 클라이언트들은 외부 웹서비스에서 데이터를 가져오도록 요청 할 수 있습니다. "메모리 데모"에서 GetGameList WebRPC는 플레이어를 위해 저장된 게임들의 목록을 가져오는데 사용 되었습니다. 인벤토리, 프로필 또는 어떤 것이든 쉽게 가져 올 수 있습니다.

웹 서비스 기본

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

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

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

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

요청(Request)

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

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