As described in the "Basic Concepts", operations are Remote Procedure Calls, defined by some Photon Application.
The client APIs includes a Photon peer class, which provides methods to call operations on the server.
We use the higher level
LoadBalancingClient class most often for demos to make your life easier.
The LoadBalancingPeer covers most LoadBalancing API operations.
LoadBalancingClient.OpJoinRoom wraps up the call of
Operation Results from Photon
Per Operation, the application on the server can send a result.
Imagine a "GetHighscores" operation and the use for a result becomes obvious and would contain a list of scores.
Other RPCs, like
RaiseEvent, can safely omit the result if it's not useful.
Getting the result of an operation takes a moment in general, as there is network lag.
That's why any result is provided asynchronously by a call to
Per platform, the result callback might look different. The following code is from the C# callback interface, which a game must implement:
OnOperationResponse is only called when your game calls
This makes it easier to get the callback in the right thread context.
In most cases it's enough to call
DispatchIncomingCommands every few frames in your game loop.
Custom Operations are just Operations which are not defined by Exit Games in our applications (Lite, LoadBalancing, etc.). Basically, any operation that's not covered by the LoadBalancing API is "custom".
If you look behind the scenes you will see that even non-custom operations, like
JoinGame, use the very same methods.
As example, here is the code from
As you can see it is a wrapper for
As you can see,
OpJoinRoom internally uses
OpCustom to be sent.
The method is just a wrapper to streamline the usage.
This is what you should do as well.
The call to
OpCustom shows the parts needed to send an operation:
OperationCode: A single byte to identify an operation (to minimize overhead). (byte)
OperationCode.JoinGameequals 255, as example.
Operation Parameters: Is the set of parameters expected by the server. Again, bytes are used instead of names, to minimize overhead per parameter.
Reliability: The third OpCustom parameter defines if the operation must arrive at the server or might become lost (unreliable). Data that's replaced quickly can be unreliable.
Authenticateor similar are better sent reliable (that's why there's a "true" in this case).
ChannelId: Is the sequence, this operation is placed into for ordering. Can be 0 in most cases.
Encrypt: Optionally encrypts data between the client and the server. Should be used only where definitely needed, as it takes away some performance and it not always needed. Here: false.
The latter three values are not exactly operation parts, obviously.
Encrypt are communication settings which can be changed on a per-operation basis.
The byte codes for the operation, parameters and result values are defined on the server-side. To call it, you need to use those on the client correspondingly.
Operation-call Return Value
For simplicity, we just ignored that
OpJoin and all other operations) have a return value.
However, it has a practical impact:
Depending on the current client state an operation might be impossible to call. The result of OpCustom tells you if the operation can be send or is ignored right away (client side):
True:The operation is possible and will be sent to the server (by
SendOutgoingCommands()). All values you passed to OpCustom are serialized now and you can modify your data at will.
False:The operation can not be sent (e.g. because the client is not connected). If this happens, you should check the debug hints that the client library provides (see
Defining New Operations
In most cases, the definition of new operations is done server-side, so the process to implement and use those is explained in that context. Read: "Adding Operations".