WebRPCs
WebRPCs are a flexible way to integrate external services with Photon Cloud or the Photon Server.
For instance with a WebRPC, Photon clients can ask the server to fetch data from an external web service.
In the "Memory Demo", GetGameList WebRPC is used to fetch a list of saved games for the player.
You can fetch an inventory, profiles or anything else as easily.
We are phasing out "Persistent Games" support. New titles will not be able to rely on the IsPersistent setting, WebFlags and related features. At this point, using WebRPCs is discouraged. Clients could simply call your backend via https.
Web Service Basics
The WebRPC architecture is simple. Photon Servers play a Proxy or Relay role between client and web server. WebRPCs can also be seen as an extension of webhooks or even custom client-driven webhooks.
To use WebRPCs, you need to implement a HTTP based service and configure the BaseURL in the Webhooks section of your application accordingly.
Photon Webhooks and WebRPCs share the same BaseURL.
Please take the following development tips into consideration:
- You can use any language or framework or web server to implement the service for the WebRPCs.
- In all cases, try to minimize the data you send to keep things lean and cheap for players.
Request
From client side, Photon WebRPC is a Photon operation that is allowed when connected to either Master or Game servers.
It requires a URI path string (a.k.a WebRPC method name) and optionally a second argument which contains the data to be sent to the web service (a.k.a WebRPC parameters).
Both arguments can be passed to LoadBalancingClient.OpWebRpc (PhotonNetwork.WebRPC in PUN).
UriPath is the relative path of the WebRPC which should match the remote procedure name.
It is also possible to send parameters in the URL itself.
In this case, the query string should be included in the procedure name, appended to the relative path.
When the request is received by Photon servers, the procedure's name will be concatenated to the BaseURL to form the absolute path of the WebRPC's URL before forwarding it to the web service at that same URL.
Since the HTTP request method used is POST, if there are any parameters sent by client, Photon will pass them over as JSON POST data along with default properties:
AppId: AppId of your application as set from the game client and found in the Dashboard.
AppVersion: version of your application as set from the game client.
Region: the region to which the game client is connected to.
UserId: ID of the actor making the WebRPC.
Any other property found is part of the parameters sent by the client.
If the WebRpcParameters type used in client is Dictionary<string, object> then all the key/value pairs will be sent in the root object of the post data.
Otherwise, if it is any other JSON valid type, it will be included as a value of a new property RpcParams.
Example 1
Your BaseURL is https://my.service.org and the client calls WebRPC("method?id=1", parameters).
The parameters are a Dictionary<string, object> that has a {{"key1": 1}, {"key2": "yx"}, {"key3": true}} as content.
In this case, Photon will call https://my.service.org/method?id=1 and additionally POST the JSON:
JSON
{
"AppId": "00000000-0000-0000-0000-000000000000",
"AppVersion": "client-x.y.z",
"Region": "EU",
"UserId": "userXYZ",
"key1": 1,
"key2": "yx",
"key3": true
}
Example 2
Your BaseURL is https://my.service.org and the client calls WebRPC("method?id=1", parameters).
The parameters are an array of objects: object[] { 0, "xy", false }.
In this case, Photon will call https://my.service.org/method?id=1 and additionally POST the JSON:
JSON
{
"AppId": "00000000-0000-0000-0000-000000000000",
"AppVersion": "client-x.y.z",
"Region": "EU",
"UserId": "userXYZ",
"RpcParams": [0, "xy", false]
}
Example 3
Your BaseURL is https://my.service.org and the client calls WebRPC("method?id=1", parameters).
The parameters are is a primitive simple type. Let's say a string "test".
In this case, Photon will call https://my.service.org/method?id=1 and additionally POST the JSON:
JSON
{
"AppId": "00000000-0000-0000-0000-000000000000",
"AppVersion": "client-x.y.z",
"Region": "EU",
"UserId": "userXYZ",
"RpcParams": "test"
}
Sending Secure Data
WebRPC also offers an option to securely transmit the encrypted object AuthCookie to the web service when available.
This can be done by setting the appropriate webflag (SendAuthCookie = 0x02) when calling the WebRPC operation method from client code.
The AuthCookie could be retrieved after a successful authentication against a custom authentication provider.
For more information please visit the custom authentication documentation page.
Response
A web service for WebRPCs must respond with a JSON object to allow Photon to send the result back to the clients.
The expected response has to include ResultCode, Data and optionally a Message.
The ResultCode should be 0 for OK and any other code for errors.
You can make them up to help the client handle the error and adding a readable string message is always a best practice.
The Data can be empty but if you send anything, it must be a valid JSON object.
Example 1
JSON
{
"ResultCode": "0"
}
Example 2
JSON
{
"ResultCode": "1",
"Message": "Self-explanatory error message"
}
Example 3<!--
JSON
{
"ResultCode": "1",
"Data": {
"Key1": "V",
"Key2": 1,
"Key3": true
}
}
URL 태그
대시보드에서 Webhooks 또는 WebRpc 의 기준 URL을 설정 할 때 하나 이상의 "동적 변수"를 설정 할 수 있습니다. 이러한 변수들은 요청을 전송하기 전에 백엔드에서 상응하는 값들로 교체 되게 됩니다.
어플리케이션에서 사용자들을 서로 다르게 분리하여 처리를 원할 때 URL 태그는 매우 간편합니다. Photon 은 다음의 URL 태그들을 지원합니다:
{AppVersion}은 클라이언트에서 설정된 어플리케이션 버전을 전달 합니다.{AppId}는 어플리케이션의 ID 를 전달 합니다.{Region}은 클라이언트가 접속된 클라우드 지역에 대한 토큰을 전달 합니다. 예, "eu".{Cloud}는 클라이언트가 연결된 클라우드의 이름을 전달 합니다. 예, "public" 또는 "enigmaticenterprise".
URL 태그 유즈케이스의 예제
https://{Region}.mydomain.com/{AppId}?version={AppVersion}&cloud={Cloud}https://mydomain.com/{Cloud}/{Region}/{AppId}/{AppVersion}
Why Choose WebRPC
Sending HTTP requests can be done without Photon. You can write a HTTP client yourself or use one available from an SDK or a library.
However Photon WebRPCs offer some advantages:
- It may be better to keep all logic inside a single connected client and handle a single "lifecycle" and "state machine". Photon client is what you need in this case.
- WebRPC operation takes care of the JSON serialization for you in both ways.
- You can verify the identity of the client sending the WebRPC using
AuthCookieor exchange more sensitive data server-to-server. Read more about how to make use of this feature here. - "BaseURL" and method names support URL Tags feature.
Troubleshooting
Here is the list of the error codes you could get when making WebRPC calls and how to handle each one:
OperationInvalid(-2)
This generally means that you did not configure or enable WebRPCs server side for your application.
HttpLimitReached(32745)
This means that you are making too many WebRPC requests per second. Check the error message to know the limit you have exceeded.
ExternalHttpCallFailed(32744)
This means that something went wrong when communicating with the configured external web service. Find more details in the error message and act accordingly.
GetGameList
This part explains the particular example GetGameList which is not a built-in nor an out-of-box feature in any of the Photon products.
If you still want to use one of the available samples provided in our GitHub page, pay attention to the spelling of the remote procedure name. It does not include an 's': GetGamesList.
Otherwise, you are free to choose any name you want or even any WebRPC implementation that suits you.
GetGameList is a special WebRPC as it was first introduced in the "Memory Demo" to demonstrate Photon's WebRPC feature.
It is also special because almost any game need to fetch a list of previously saved games.
Its basic idea is to send a request to the web service and expect a list of games which could be rejoined by the calling user and continue to play.
This presumes that the application should be properly configured: the BaseUrl should point to your web service and in order to persist games data IsPersistent should be set to true and PathClose webhook should be activated.
The latter is important as the games data that should be returned by the web service rely on the Photon's serialized room State.
On the other hand, the web service should have access to the saved game data and should be ready to receive and process HTTP POST requests sent to the WebRPC's path (here GetGameList).
More about WebHooks on the Realtime docs from the client side. More about WebHooks on the Server docs
Since Photon sends the UserId in all WebRPC requests data, there is no need to add any custom parameter. The request is sent without extra arguments.
The web service should return the list of saved games in Data as a JSON object. Each saved game's GameId will have a key.
The respective value is also a JSON object composed of two properties:
ActorNr: The exact same original ActorNr of the actor calling the WebRPC that was assigned to him/her when he/she joined that room for the first time. This could be loaded easily in case it was intentionally and separately saved earlier or it could be retrieved from the respective room's savedStateobject by looping on the ActorList and comparing UserIds.Properties: The key:value pairs of CustomRoomProperties visible to the lobby that can be retrieved fromCustomPropertiesproperty contained in the respective room's savedState.
JSON
{
"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
}
}
}
}
Although WebRPC calls are permitted to both Master and Game servers, it makes more sense to call GetGameList when not joined to a room, i.e. from Master server.
Passing data to Web Service
WebRPC offers three different ways of passing data from client to web service. The three different types can either be used separately or combined.
For instance if you want to extend GetGameList and retrieve a filtered list of saved games based on a specific opponent, you can do this in three different ways:
RESTful way:
Relative URI:
/GetGameList/{opponentId}POST data:
Relative URI:
/GetGameListJSON
{ "AppId": "00000000-0000-0000-0000-000000000000", "AppVersion": "client-x.y.z", "Region": "EU", "UserId": "userXYZ", "OpponentId": "opponentId" }Query string (a.k.a. GET parameters):
Relative URI:
/GetGameList?opponentId={opponentId}
Of course the web service should be updated also according to the method chosen.
데이터 타입 변환
이 섹션에서는 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<string, object> |
| 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