Build real-time player matching for multiplayer games

Match your players in a blink using Redpanda—no matter how big your game gets

By
on
May 24, 2023

Playing with others is engaging—waiting to be matched with a group of other players isn’t. In this final post about real-time data streaming for gaming, we look at how to match players faster in an online multiplayer game.

If you’re new here, this series covers use cases for data streaming in the gaming industry. We started off with a general overview of the most popular use cases for data streaming in gaming, then dug into each use case with a practical example:

This final post is all about player matching, which basically connects players together for online play. It’s often a complex process assisted by AI and machine learning algorithms to secure fair matches between compatible players. Many gaming companies use external match-making services to bypass this complex work. Communication with an external service works fine when your game is small, but when it gains popularity, the increased number of player requests to be matched up can make your game slow and less reliable.

Our solution is to decouple your gaming frontend from match-making APIs. With Redpanda and two microservices, we provide fast, scalable, and reliable real-time communication with the external API. This will let you scale and evolve your game independently from the match-making requests, responses, and storage.

Furthermore, Redpanda's Tiered Storage and Remote Read Replicas let you run data streaming workloads completely independently of each other. This will prove particularly useful if you need to add other services, like an analytics system or personalized monetization engine— or if you’re adding other gaming platforms and crossplay.

Let’s jump into the example so you can see how it would all work.

Build a scalable, real-time player matching architecture

For the real-time player matching use case, we propose an architecture with Redpanda as a scalable and reliable event broker. The solution is available as a Maven multi-module project implemented with the Quarkus Java framework. We’ve put a reference implementation of the solution in the Redpanda GitHub repo.

A scalable and reliable real-time player matching solution with Redpanda as the event broker
A scalable and reliable real-time player matching solution with Redpanda as the event broker

The match-making service is implemented as a RESTful API and returns a JSON array of player usernames as the match-making response. Two topics in Redpanda, match-requests and match-responses, enable asynchronous communication between the gaming frontend and the match-making API.

1. Set up the player matching frontend module

The frontend module mimics the gaming UI where player interactions happen. This module consists of a simple HTML page (UI) and a REST endpoint (MatchMakerResource).

When a player wants to join a multiplayer game, the frontend sends a POST request to the MatchMaker endpoint, along with the player ID and rank. It’s a non-blocking HTTP call, and the MatchMaker endpoint asynchronously responds with a list of matched players. The game’s logic can use this list to continue with the next steps in matching.

We simulate the match scenario in our solution by clicking a button on the game’s UI and asynchronously updating the UI with a list of matched players. You can find the source code for the UI here.

$(“#request-match”).click((event) => {
    	fetch(“/match/request”, {method: “POST”})
    	.then(res => res.text())
    	.then(response => {  
	  	$(“#request-match”).attr(“disabled”,”disabled”);
    	});		 
   });

	var source = new EventSource(“/match”);
source.onmessage = (event) => {
  	var json = JSON.parse(event.data);
  	$.each(json.players, function(key, value) {   					 
	    	var row = $(`<h4 class=’col-md-12’><strong>Name: </strong>
${value}</h4>`);   					 
    	$(“.matches”).prepend(row);
   });
};

2. Accept player matches with the MatchMaker REST endpoint

To accept player match requests from the UI, we use the MatchMakerResource.java. It’s a REST resource set up like this:

package com.redpanda.gaming.matchmaker.frontend;

import com.redpanda.gaming.matchmaker.frontend.model.MatchResponse;
import java.util.UUID;

import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.eclipse.microprofile.reactive.messaging.Channel;
import org.eclipse.microprofile.reactive.messaging.Emitter;

import io.smallrye.mutiny.Multi;

@Path(“/match”)
public class MatchmakerResource {

  @Channel(“match-requests”)
  Emitter<String> matchRequestEmitter;

  @Channel(“match-responses”)
  Multi<MatchResponse> results;

  /**
   * Endpoint to generate a new match request id and send it to
“match-requests” Redpanda topic using the emitter.
   */
  @POST
  @Path(“/request”)
  @Produces(MediaType.TEXT_PLAIN)
public String createRequest() {
	UUID uuid = UUID.randomUUID();
	matchRequestEmitter.send(uuid.toString());
	return uuid.toString();
}

  /**
   * Endpoint retrieving the matched players and sending them to a
server sent event.
   */
@GET
  @Produces(MediaType.SERVER_SENT_EVENTS) // denotes that server-side
events (SSE) will be produced
  public Multi<MatchResponse> stream() {
	return results.log();
      }
}

The Quarkus framework scans the configuration properties and configures connectivity with Redpanda automatically. You can find the properties in this GitHub folder.

The createRequest() method accepts player match requests from the UI and publishes them to the match-requests Redpanda topic with the Emitter instance (matchRequestEmitter). The results channel subscribes to the match-responses topic, receives matched responses, and streams them back to the gaming frontend as SSEs. SSE requests from the UI are served by the stream() method.

3. Consume player match requests with the worker module

Now, we need to consume the match-requests. We use the MatchWorker microservice for that. It invokes the match-making API over HTTP, waits for the response, and finally writes the response to the match-responses topic.

To make things simple, we simulate the API call with an artificial delay and use a mock response. You can find the service implementation in the MatchWorker.java class.

package com.redpanda.gaming.matchmaker.worker;

import com.redpanda.gaming.matchmaker.frontend.model.MatchResponse;
import io.smallrye.reactive.messaging.annotations.Blocking;
import java.util.ArrayList;
import java.util.List;
import javax.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.reactive.messaging.Incoming;
import org.eclipse.microprofile.reactive.messaging.Outgoing;

@ApplicationScoped
public class MatchWorker {

  @Incoming(“match-requests”)
  @Outgoing(“match-responses”)
  @Blocking
  public MatchResponse process(String playerId) throws
InterruptedException {
	// simulate an external API invocation here
	Thread.sleep(800);

	//Formulate a mock response with some gamer tags
	List<String> players = new ArrayList<>();
	players.add(“EatBullets”);
	players.add(“PR0_GGRAM3D”);
	players.add(“MindlessKilling”);
	players.add(“Shoot2Kill”);
	players.add(“LunaStar”);

	MatchResponse response = new MatchResponse();
	response.setPlayers(players);
	return response;
	}
 }

Like the frontend module, the Quarkus framework automatically configures connectivity with Redpanda by scanning the configuration properties provided in application.properties.

4. Run the fast player matching solution

Now, you’re ready to run the solution. You can run the components as regular Java applications. Just make sure you have JDK version 8 or above installed locally. First, start Redpanda with Docker Compose by typing the following from the root level of the 5-matchmaker folder.

docker compose up -d

This starts a single-node Redpanda cluster. Next, start the frontend like so:

cd frontend
mvn quarkus:dev

Finally, start the worker as well.

cd worker
mvn quarkus:dev

You should now see two topics created in Redpanda: match-requests and match- responses. Type http://localhost:8080 in your browser to access the UI. Then click the Match Me! button to see a list of matched players like this:

Example player matching result
Example player matching result

Congratulations! You now know how to implement faster matching among players without causing latency as your game scales.

Build better games with efficient player matching

Seamlessly matching players in multiplayer online games is a common challenge for game developers. The huge number of events needs to be handled as independently as possible, which is why we use Redpanda to decouple the player-matching API from the player-matching service. With the example presented in this post, you saw exactly how it works and how easy it is to implement yourself.

And with that, we conclude our series on real-time streaming for gaming! You can find all the gaming use cases in our free report: Turbocharge your games with Redpanda, or check out the blog series starting with 5 popular real-time use cases for gaming.

To keep exploring Redpanda, check our documentation and browse the Redpanda blog for tutorials. If you have a specific gaming use case you’d like us to consider, tell us in the Redpanda Community on Slack!

Graphic for downloading streaming data report
Save Your Spot

Related articles

VIEW ALL POSTS
Build an inventory monitoring system with Flink and MongoDB
Rexford A. Nyarko
&
&
&
October 29, 2024
Text Link
Building a real-time Customer 360 solution for Telco with Flink
Artem Oppermann
&
&
&
October 1, 2024
Text Link
Build a blazing fast real-time dashboard with serverless technologies
Nico Acosta
&
&
&
August 29, 2024
Text Link