Overview
On the client side, Photon supports various platforms. Even though programming in C, C# and Flash are very differnet things, the basic workflow stays similar. In this online documentation, we try to explain the concepts and background, while language specifics are left to the reference documentation per platform.
Each Photon Client SDK comes with a fitting reference documentation for its platform and language.
Project Setup
On the client side, you need to include a single library to communicate with Photon. Per platform, this is a different setup.
DotNetIn your DotNet project, add a reference to the PhotonDotNet.dll to make its classes known. Import the lib's namespace with: using ExitGames.Client.Photon;
Unity3dIn Unity, you need to import the PhotonUnity3D.dll into your project. Simply open a Unity project/scene, open an explorer and drag and drop the dll from <sdk>\libs\debug\ into the editor. Again, import the lib's namespace with: using ExitGames.Client.Photon;
Workflow
The simplified lifecycle for any multiplayer game looks like this:
- Connect to server
- Make RPC calls
- Receive something
- Dicsonnect
Pretty simple. Let's have a look at each step, assuming you use C# and the Lite Application.
Connect to server
In this step, you actually do a little more than just connect. In short:
- Implement the interface IPhotonPeerListener in one of your classes
- Create a LitePeer instance with your server's address
- In your game loop, call Service() about 10 times a second
- Now call Connect() to establish the connection. The Application Name should match one in the PhotonServer.config
- Your implementation if PeerStatusCallback() is called when the connection is ready or failed
- The returned status be StatusCode.Connect
The peer instance provides you with some methods to talk to Photon. It does not have it's own thread. Instead it's updated when you call Service(). This will keep the connection alive
We assume that Photon is running the "Lite" Application, which it does by default.
Make RPC Calls
After connecting the client can call methods in the Lite Application just about anytime it needs to. The methods available as "remote procedure call" are called Operations.
Operation calls are asynchronous and won't block your game. They are queued internally, until you intently send something by calling SendOutgoingCommands. There will be a short delay until the result is received. These results are defined per operation and are sometimes not used at all.
- Call OpJoin with any room name to get into a game
- Wait for the callback: OperationResult with opCode: LiteOpCode.Join
- Send any Hahstable with content by calling OpRaiseEvent. Other players in the room will receive events.
Joining a room and raising events in it is provided (server side) by the Lite Application logic we use in this sample. You can use other applications and implement your own operations.
Receive something
In the previous step, you already received an operation result. We differentiate those from other incoming messages, so called events. Lite will forward the event you raised to others in a room.
- Receiving events will call IPhotonPeerListener.EventAction. The "evCode" will match the one you used in OpRaiseEvent
The client library will internally queue received packages, so your game decides when to use them. EventAction() is called by your calls to PhotonPeer.Service().
Dicsonnect
If a client is closed, it's best to disconnect. That's nice, but not a must-have as a timeout will disconnect clients that don't answer anymore.
- To close the connection: call Disconnect
- Check "disconnect" return in PeerStatusCallback with statusCode: StatusCode.Disconnect
- Now the client can stop calling Service
LitePeer versus PhotonPeer
Aside from the LitePeer, the client libraries also include a PhotonPeer which could be used. The PhotonPeer has the bare minimum of API needed for Photon. It is the base for LitePeer, which adds Operations like Join, Leave and RaiseEvent, which are available with the Lite Application.
Reliable UDP
Under the hood, Photon clients and servers send Operations and Events via UDP. This guarantees low overhead, flexibility and performance but is unreliable by definition.
Within the UDP packages, Photon uses a thin binary protocol to enable aggregation, ordering, detection of packet loss and more. It is based on eNet and used in similar form on client and server.
So called "commands" are used to transfer the payload, such as Operations. On the client side, Photon gives you full control of when commands are put into a UDP package and send.
As a result, the API also includes methods that manage the connection to the server. Basically, there are two "layers": the "command layer" and the "operation layer":
- Command layer: manages the communication protocol to establish and maintain communication.
- Operation layer: gives access to rooms, events and custom Operations. Client side, methods of this layer are prefixed with "Op".
Reliable vs. Unreliable
With Photon's underlying protocol, everything that's sent across can be made flagged as reliable or unreliable. The Operation request is not changed by this, though, only the command that's used.
Photon keeps track of the ordering. Even unreliable commands are synchronized/sequenced within the order of reliable commands. So an unreliable command which was sent after a certain reliable command, will also also be dispatched after the reliable command was dispatched (even if that command was delayed).
Operations and Events that are sent as unreliable might get lost. As example, this is no problem for position updates, which are quickly replaced. On the server side, Operations that are received unreliable, should not create a result: The client can't expect that the Operation even reached the server.
Connection and Timeout
The client has two rules to define a timeout:
- Retries: A reliable command is sent multiple times to be acknowledged, each time the interval between retries gets longer, depending on general rountrip times. If the number of retries reaches a limit (in DotNet: SentCountAllowance), this causes a disconnect.
- DisconnectTimeout: This defines a maximum number of milliseconds, before a reliable command must be answered. This is independent of the retry counting. In DotNet, the setting is: DisconnectTimeout.
The settings are described in more detail in the client API documentation.