This document is about: SERVER 4
SWITCH TO

Calling Operations

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.

For example, LoadBalancingClient.OpJoinRoom wraps up the call of JoinGame operation.

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 IPhotonPeerListener.OperationResult.

Per platform, the result callback might look different.
The following code is from the C# callback interface, which a game must implement:

C#

    public interface IPhotonPeerListener
    {
        //[...]
        void OnOperationResponse(OperationResponse operationResponse);
    

The callback OnOperationResponse is only called when your game calls loadBalancingPeer.DispatchIncomingCommands.
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

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 LoadBalancingClient.OpJoinRoom:

C#

    public bool OpJoinRoom(string roomName, string[] expectedUsers = null)
    {
        // [...]
        EnterRoomParams opParams = new EnterRoomParams();
        // [...]
        return this.loadBalancingPeer.OpJoinRoom(opParams);
    }

As you can see it is a wrapper for LoadBalancingPeer.OpJoinRoom:

C#

    public virtual bool OpJoinRoom(EnterRoomParams opParams)
    {
        // [...]
        Dictionary<byte, object> op = new Dictionary<byte, object>();
        // [...]
        return this.OpCustom(OperationCode.JoinGame, op, true);
    }

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.JoinGame equals 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. JoinGame, Authenticate or 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.
SendReliable, ChannelId and 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 OpCustom (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 DebugReturn).

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".

Back to top