server | v4 switch to v3  

Adding Operations

In many cases, just sending events is not enough for a game. If you want to provide authorization, persistency or game-specific Operations, it's time to extend LoadBalancing.

This page shows two ways to implement new Operations and how to use them. Our sample Operation expects a message from the client, checks it and provides return values.

Server-Side, Simple Variant

Let's start on the server side, in the LoadBalancing Application. This variant is not very elegant but simple.

    
        //Server SDK
        public void OnOperationRequest(OperationRequest request, SendParameters sendParameters)
        {
            // handle operation here (check request.OperationCode)
            switch (request.OperationCode)
            {
                case 1:
                    {
                        var message = (string)request.Parameters[100];
                        if (message == "Hello World")
                        {
                            // received hello world, send an answer!
                            var response = new OperationResponse
                            {
                                OperationCode = request.OperationCode,
                                ReturnCode = 0,
                                DebugMessage = "OK",
                                Parameters = new Dictionary<byte, object> { { 100, "Hello yourself!" } }
                            };

                            this.SendOperationResponse(response, sendParameters);
                        }
                        else
                        {
                            // received something else, send an error
                            var response = new OperationResponse
                                {
                                    OperationCode = request.OperationCode,
                                    ReturnCode = 1,
                                    DebugMessage = "Don't understand, what are you saying?"
                                }; 

                            this.SendOperationResponse(response, sendParameters);
                        }

                        return;
                    }
            }
        }

The above code first checks the called OperationCode. In this case, it's 1. In Photon, the OperationCode is a shortcut for the name/type of an Operation. We'll see how the client call this below.

If Operation 1 is called, we check the request parameters. Here, parameter 100 is expected to be a string. Again, Photon only uses byte-typed parameters, to keep things lean during transfer.

The code then checks the message content and prepares a response. Both responses have a returnCode and a debug message. The returnCode 0 is a positive return, any other number (here: 1) could mean an error and needs to be handled by the client.

Our positive response also includes a return value. You could add more key-value pairs, but here we stick to 100, which is a string.

This already is a complete implementation of an Operation. This is our convention for a new Operation, which must be carried over to the client to be used.

Client Side

Knowing the definition of above's Operation, we can call it from the client side. This client code calls it on connect:

    
        public void OnStatusChanged(StatusCode status) 
        { 
            // handle peer status callback 
            switch (status) 
            { 
            case StatusCode.Connect: 
                // send hello world when connected         
                var parameter = new Dictionary<byte, object>();
                parameter.Add((byte)100, "Hello World");

                peer.OpCustom(1, parameter, true);
                break; 
                //[...]

As defined above, we always expect a message as parameter 100. This is provided as parameter Dictionary. We make sure that the parameter key is not mistaken for anything but a byte, or else it won't send. And we put in: "Hello World".

The PhotonPeer method OpCustom expects the OperationCode as first parameter. This is 1. The parameters follow and we want to make sure the operation arrives (it's essential after all).

Aside from this, the client just has to do the usual work, which means it has to call PhotonPeer.Service in intervals.

Once the result is available, it can be handled like this:

    
        public void OperationResponse(OperationResponse operationResponse) 
        { 
            // handle response by code (action we called) 
            switch (operationResponse.OperationCode) 
            { 
                // out custom "hello world" operation's code is 1
                case 1: 
                    // OK 
                    if (operationResponse.ReturnCode == 0) 
                    { 
                        // show the complete content of the response
                        Console.WriteLine(operationResponse.ToStringFull()); 
                    } 
                    else 
                    { 
                        // show the error message
                        Console.WriteLine(operationResponse.DebugMessage); 
                    } 
                    break; 
            } 
        }

Server Side, Advanced Version

The preferred way to handle Operations would be to create a class for it. This way it becomes strongly typed and issues due to missing parameters are handled by the framework.

This is the definition of the Operation, its parameters, their types and of the return values:

    
//new Operation Class
namespace MyPhotonServer
{
    using Photon.SocketServer;
    using Photon.SocketServer.Rpc;

    public class MyCustomOperation : Operation
    {
        public MyCustomOperation(IRpcProtocol protocol, OperationRequest request)
            : base(protocol, request)
        {
        }

        [DataMember(Code = 100, IsOptional = false)]
        public string Message { get; set; }

        // GetOperationResponse could be implemented by this class, too
    }
}

With the Operation class defined above, we can map the request to it and also provide the response in a strongly typed manner.

    
     public void OnOperationRequest(OperationRequest request, SendParameters sendParameters)
        {
            switch (request.OperationCode)
            {
                case 1:
                    {
                        var operation = new MyCustomOperation(this.Protocol, request);
                        if (operation.IsValid == false)
                        {
                            // received garbage, send an error
                            var response = new OperationResponse
                            {
                                OperationCode = request.OperationCode,
                                ReturnCode = 1, 
                                DebugMessage = "That's garbage!"
                            };

                            this.SendOperationResponse(response, sendParameters);
                            return;
                        }

                        if (operation.Message == "Hello World")
                        {
                            // received hello world, send an answer! 
                            operation.Message = "Hello yourself!";
                            OperationResponse response = new OperationResponse(request.OperationCode, operation); 
                            this.SendOperationResponse(response, sendParameters);
                        }
                        else
                        {
                            // received something else, send an error
                            var response = new OperationResponse
                            {
                                OperationCode = request.OperationCode,
                                ReturnCode = 1,
                                DebugMessage = "Don't understand, what are you saying?"
                            };

                            this.SendOperationResponse(response, sendParameters);
                        }
                        break;
                    }
            }
        }

 To Document Top