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.
C#
//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
- 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:
C#
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:
C#
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:
C#
//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.
C#
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;
}
}
}
Back to top