The importance of ping times

In this brief article we will take a look at the simple concept of “ping times” and how we can use it in our multiplayer games to improve the user’s experience.

The connection speed is the single most important key element in an online game as it affects the whole experience. Being able to detect slow downs and “hiccups” could help removing some of the frustration when the client’s network is not behaving as expected. Also, being able to signal critical network issues could help the final user improve their game performance.

» The basics of network lag

When transmitting a packet of data over the network we’re literally sending electrical impulses through miles of electrical wires. These impulses must be encoded/decoded into meaningful data and be checked for validity across a number of devices (routers, gateways, firewalls…) along the signal path.

Without going into too many technical details, the term network lag represents the time for the packet to travel from its source to the target. When we refer to the ping time or roundtrip time we usually mean a complete send/response cycle, in other words: the time from player to server + server to player.

ping-time

 

In example above we have a total of 50 milliseconds of roundtrip time, from client to server and back. Ping times can vary greatly due to the type of connection in use (mobile, DSL, optical fiber etc…), the physical distance between client and server, and the number of nodes (“hops”) that are encountered in the path.

» Monitoring ping times

The SmartFoxServer client API allow to easily monitor the client’s ping time by sampling it every few seconds and calculating an average based on a number of previous measurements.

To activate this function we can invoke the SmartFox.enableLagMonitor(…) method after having logged in. There are several ways to call this method (examples are in Java):

Example 1:

sfs.enableLagMonitor(true);

This call simply turns on/off the lag monitor.

Example 2:

sfs.enableLagMonitor(true, 4);

Here we specify a second parameter indicating the interval (in seconds) with which the lag sampling should run.

Example 3:

sfs.enableLagMonitor(true, 4, 30);

Here we also pass a third argument indicating the number of samples that should be used to calculate the average ping time.

Finally we can listen to the SFSEvent.PING_PONG to handle the ping time updates:

sfs.addEventListener(SFSEvent.PING_PONG, this);

...
...

@Override
public void dispatch(BaseEvent evt) throws SFSException
{
    if (evt.getType().equals(SFSEvent.PING_PONG))
    {
    	Integer lagValue = (Integer) evt.getArguments().get("lagValue");

    	System.out.println("Ping time: " + lagValue + "ms.");
    }
}

» What do I use the ping time for?

Being able to monitor the network conditions will help you provide an overall better experience to your players. For example:

  • Being able to show an on-screen graph of the network performance can help users understand how their connection is working. Further assistance can be provided to players with slow connections such as advising them to stop other downloads while playing  etc…
  • It allows to gather statistics about the average network capabilities of players and provide hints on possible further game optimizations for slower connections.
  • It can also be used as a prerequisite test to check if a player matches the minimum connection speed/lag to play the game smoothly, especially for fast action games.
  • It can be very useful in real-time games to compensate for the lag between client and server when sending certain events. For example we can detect a collision or similar event, slightly in advance (e.g. 50ms before it occurs) to make up for the current network delay.
  • It can be useful as a client side log information to troubleshoot specific player’s complaints and for debugging.

» Full example (Java client API)

The code below shows a basic client performing a connection + login to SmartFoxServer followed by the activation of the LagMonitor tool.

If you’re using a platform other than Java on the client side, you will find the same call and related event in your favorite language. You can check the relative documentation to see an example of usage as well.

package sfs2x.client.example;

import sfs2x.client.SmartFox;
import sfs2x.client.core.BaseEvent;
import sfs2x.client.core.IEventListener;
import sfs2x.client.core.SFSEvent;
import sfs2x.client.requests.LoginRequest;
import sfs2x.client.util.ConfigData;

import com.smartfoxserver.v2.exceptions.SFSException;

public class LagMonitorExample implements IEventListener
{
	private SmartFox sfs;

	public LagMonitorExample()
    {
		ConfigData cfg = new ConfigData();
		cfg.setHost("localhost");
		cfg.setZone("BasicExamples");

		initSmartFox();

		sfs.connect(cfg);
    }

	private void initSmartFox()
	{
		sfs = new SmartFox();
		System.out.println("VERSION: " + sfs.getVersion());

		sfs.addEventListener(SFSEvent.CONNECTION, this);
		sfs.addEventListener(SFSEvent.CONNECTION_LOST, this);
		sfs.addEventListener(SFSEvent.LOGIN, this);
		sfs.addEventListener(SFSEvent.PING_PONG, this);
	}

	@Override
	public void dispatch(BaseEvent evt) throws SFSException
	{
		if (evt.getType().equals(SFSEvent.CONNECTION))
	    {
	    	boolean success = (Boolean) evt.getArguments().get("success");

	    	if (!success)
	    		throw new RuntimeException("Connection attempt failed!");

	    	System.out.println("Connected: " + sfs.getConnectionMode());

	    	// Login as guest in current zone
	    	sfs.send(new LoginRequest("", "", sfs.getCurrentZone()));
	    }

	    else if (evt.getType().equals(SFSEvent.CONNECTION_LOST))
	    {
	    	System.out.println("Disconnected: " + evt.getArguments().get("reason"));
	    }

	    else if (evt.getType().equals(SFSEvent.LOGIN))
	    {
	    	System.out.println("Logged in as: " + sfs.getMySelf().getName());

	    	// Start measuing ping times
	    	sfs.enableLagMonitor(true);
	    }

	    else if (evt.getType().equals(SFSEvent.PING_PONG))
	    {
	    	Integer lagValue = (Integer) evt.getArguments().get("lagValue");

	    	System.out.println("Ping time: " + lagValue + "ms.");
	    }
	}
}