server | v3 switch to v4  

Calling Operations

As described in the "Basic Concepts", operations are Remote Procedure Calls, defined by some Photon Application.

The client APIs includes a LitePeer class (or similar construct), which provides methods to call operations on the server, because we use the Lite Application most often for demos and to make your life easier

For example, LitePeer.OpJoin wraps up the call of operation Join. The code below will call OpJoin when the connection is established:

    
        public void PeerStatusCallback(StatusCode returnCode)
        {
            // handle returnCodes for connect, disconnect and errors (non-operations)
            switch (returnCode)
            {
                case StatusCode.Connect:
                    this.DebugReturn("Connect(ed)");
                    this.peer.OpJoin(this.gameID);
                    break;
                //[...]

The LitePeer covers all Lite Application operations this way.

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:

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

The callback OperationResult is only called when your game calls peer.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.

Back To Top

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 LitePeer is "custom".

If you look behind the scenes you will see that even non-custom operations, like Join, use the very same methods! As example, here is the code from LitePeer Join:

    
    public virtual bool OpJoin(string gameName, Hashtable gameProperties, Hashtable actorProperties, bool broadcastActorProperties)
    {
        if (this.DebugOut >= DebugLevel.ALL)
        {
            this.Listener.DebugReturn(DebugLevel.ALL, "OpJoin(" + gameName + ")");
        }

        Dictionary<byte, object> opParameters = new Dictionary<byte, object>();
        opParameters[(byte)LiteOpKey.GameId] = gameName;
        if (actorProperties != null)
        {
            opParameters[(byte)LiteOpKey.ActorProperties] = actorProperties;
        }

        if (gameProperties != null)
        {
            opParameters[(byte)LiteOpKey.GameProperties] = gameProperties;
        }

        if (broadcastActorProperties)
        {
            opParameters[(byte)LiteOpKey.Broadcast] = broadcastActorProperties;
        }

        return this.OpCustom((byte)LiteOpCode.Join, opParameters, true, 0, false);
    }

As you can see, OpJoin 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)LiteOpCode.Join 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. Join, Authentication 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.

Back To Top

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

Back To Top

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

기술문서 TOP으로 돌아가기