Turn-based & Async. Games

Asynchronous games can be built just like any other game with Photon Realtime. The main difference is that the server-side setup is changed to store rooms. They can be loaded and continued later-on.

Photon has a special set of features for games that are played for days and weeks rather than minutes. An external web service is required to persist the state of rooms. A game can be continued by players at any time and even joined asynchronously.

System Overview

In Photon Realtime, an application can be linked with an external web service.
This service is then wrapped and used by the Photon Servers for special tasks.
Clients never have to communicate with the service directly.

The Web Service

Saving, loading and listing data is a common task for web services, so it makes sense to let Photon use one to persist data.

Once a web service is configured in the "Webhooks" tab of the Dashboard, it will be used in two ways:
Event-driven as "Webhooks" and client-action-driven with "WebRPC" and "Event Forwarding".

Webhooks

Photon Webhooks establish the interface of the web service to save and load games.
The individual tasks are called automatically by Photon where needed, so clients don't have to take care of them.

Example:
Saving a room state is done when all players of the room disconnected.

Photon will call: "<BaseUrl>/GameClose" and pass the state as POST data.

To receive events on your host configure your hooks' paths as your needs be.
From the application list on your dashboard follow the details link, then select Webhooks to set up your configuration.

Read more about "Photon Webhooks".

WebRPC

WebRPCs take the idea of webhooks and open it up for general use.
You can make up your own operations (basically RPCs) and call them on the web service.
Passing parameters back and forth and implementing the web service script is up to you.

Example:
In the MemoryDemo, we use a WebRPC to get a list of rooms that a user played (and saved) earlier.
A fitting script takes the user's ID and fetches the list from a database.

To build your own WebRPC, you need to implement a script on the web service.
The path of the script (minus the base-URL configured in the Dashboard) defines the name of the WebRPC to be used in the client.
Parameters in both directions must be passed in a JSON-friendly format.

Read more about "Photon WebRPCs".

Creating "Asynchronous" Rooms

Once an application is setup in the Dashboard as "IsPersistent", the actual rooms are created in the same way as any other room: with the operation "create room".
They just have a few more options to set.

"Asynchronous" rooms are automatically persisted after a timeout when all players disconnected.
This timeout is called the "room time to live" (room TTL).
It can be set by a client when it creates a room (see OpCreateRoom).
A best-practice value is 12 seconds.

To know more about how to save and load rooms, refer to the "Room Persistence Guide"

Leaving and Abandoning Rooms

In "asynchronous" games, disconnecting the client or leaving a game flags a player as inactive.
Players marked inactive can return later on.
If needs be, players can "abandon" a game explicitly.
To do so, use the leave operation with the fitting parameter value.

Photon will list all active and inactive players in a room but once a player abandons the game, this player is no longer known in the game.
Also, by default, all events and properties of that player will be cleaned up.

Loading a Room

To continue playing in any room, you have to know its name.
On the client side, use OpReJoinRoom(name).

From the client's perspective, it doesn't make a difference if the room is still in Photon's memory or loaded.
The operation fails if the room can't be found (anymore) or the actor in it expired.

To know more about how to save and load rooms, refer to the "Room Persistence Guide"

Getting a List of Saved Rooms

The demo and server scripts samples maintain an online list of rooms per user ID.

The idea is to use "Custom Authentication" to log a user in, then fetch that user's list of rooms.

Read more about "GetGameList WebRPC".

Keeping the Game State

When a player re-joins a room, it will get the properties of the players (active and inactive) and all buffered events.
With this data you should be able to reproduce the state and continue playing.

Properties

You can set room and player properties.
Properties are best used when you need to keep a state but not the history of changes.
You can easily save the state of a game board and the turn number as properties.
Any player can set room properties, so it's not a problem to take turns asynchronously.

Player properties will be cleaned up when a player abandons the game (or times out).

Event Cache

Each room has a cache that stores events on demand.
Caching events keeps the order in which they happened, which is required when you store something that depends on previous actions or data.

By default, events are not cached but OpRaiseEvent has a parameter to control the caching of events.
Send the event as usual but use EventCaching.AddToRoomCache to store it.

You can also remove events from the cache with option EventCaching.RemoveFromRoomCache.
In that case, the eventCode, the "sender actorNumber" and the "content" Hashtable are used as a filter.
You can remove the events of a specific actor, all events with the same eventCode or with a specific content.

Deleting events by content is especially powerful if you send some kind of "tag" along with your events, deliberately.
The "MemoryDemo" sends a "turn" in each event and that's used to delete events of older turns, which keep the history of events short (which is in turn good for loading a game that longs for a long time).

Read more about "Cached Events".

Event Forwarding

Aside from caching an event, you can also forward it to the web service.

Again, OpRaiseEvent on the client defines, using WebFlags, if an event should be forwarded as GameEvent webhook to web server.

Forwarded events have no channel back into the game but can be useful to track some in-game actions and results.
This can be used to calculate the result of a match and to adjust the users' ELO.

The "MemoryDemo" does not make use of this.

Back to top