raga.io

đźź  Agar.io Clone with Akka.

View the Project on GitHub elvisperlika/raga.io

Raga.io

AI Disclaimer

During the preparation of this work, the authors used Chat-GPT and Gemini to refine the report by improving sentence structure and correcting grammatical errors. After using these tools, the authors reviewed and edited the content as needed and takes full responsibility for the content of the final report.

Abstract

Agar.io is an online, massively multiplayer action game. Players take on the role of a small, circular cell inside a map that resembles a Petri dish. The primary goal is to grow as large as possible by consuming smaller cells, both those controlled by other players and those that are scattered randomly throughout the game world as food. This simple premise leads to a dynamic and competitive environment where a player’s size directly dictates their power and vulnerability. Players can join a randomly assigned game session, create a new session, or join an existing one using a unique session ID. The session ends only when the player is eaten or decides to quit.

The goal of this project is to design and develop a clone of Agar.io with a robust client–server architecture, ensuring scalability, performance, and smooth multiplayer interaction.

Achievements:

Concept

This project is a desktop application with a graphical user interface (GUI) packaged as a Java Archive (JAR) file. It can be executed on any system with a Java Runtime Environment (JRE) installed.

Requirements

Functional Requirements

  1. Ability to join a random game session.
  2. Ability to create a new game session.
  3. Ability to join an existing session using a unique session ID.
  4. Support for multiple concurrent players in a session.
    • Acceptance criteria: at least 5 concurrent players per session.
  5. Real-time visibility and interaction between players.
    • Acceptance criteria: latency < 200ms.
  6. Low-latency handling of movements and actions.
    • Acceptance criteria: maintain a frame rate of at least 30 FPS during typical gameplay.
  7. Mechanism for consuming smaller cells (players or food) to increase size.
    • Acceptance criteria: players can consume smaller cells and visibly grow in size.
  8. Random spawning of food items within the map.
  9. Collision detection and outcome resolution based on cell size.
  10. Sessions conclude when a player is eliminated or chooses to exit.
  11. Intuitive GUI for session management.
    • Button to join a random session.
    • Nickname input field.
    • Button to create a new session and join it.
    • Input field for entering a session ID to join an existing session.
    • Button to join the specified session.
  12. Visual representation of player cells, food items, and other players.
    • Display player names.
    • Display the session ID so players can share it with friends.
  13. Scalable server architecture to handle increasing player numbers.
  14. A master server coordinating multiple game servers that manage individual game sessions.
  15. Dynamic allocation of players to game servers based on load and availability.
  16. The server architecture should support scaling to accommodate increasing numbers of concurrent sessions and players.
  17. Backup and failover mechanisms to ensure session continuity in case of game server failure.

Design

Architecture

This project employs a client-server architecture with a distributed system design. The architecture consists of three main components: the Client, the Mother Server, and multiple Child Servers.

Infrastructure

Deployment

The system architecture consists of three main components:

drawing

Components

drawing

Client service:

Mother Server service:

Child Server service:

This architecture allows for scalability, as multiple Child Servers can be added to handle more game sessions as needed. The Mother Server is the seed node of the system and must always be started first. Child Servers can be started and stopped dynamically, allowing for flexible resource management. Child Servers can be hosted on different machines or cloud instances to distribute the load effectively, it’s not mandatory to have them on the same network or datacenter as the Mother Server.

Modelling

Client

The client, which has a graphical user interface (GUI), is structured according to the Model-View-Controller (MVC) architectural pattern, which separates the application into three interconnected components: the Model, the View, and the Controller.

drawing

Client Model

The Model encapsulates the game logic and state management:

Client Controller

The Controller handles interactions and updates between the model and the view:

Client View

The View presents the game to the user:

Mother Server

drawing

The Mother Server coordinates the main management logic:

Child Server

drawing

Child Servers manage individual game sessions and receive players from the Mother Server.

Protocol

As seen in the class diagrams above, there are a set of classes shared between the Mother Server and Child Server components. These classes are used to represent the world state of a game session and they are the body of the messages exchanged to synchronize the game state between the servers and the clients and also between the Mother Server and Child Servers. They include:

Full Diagram

drawing

Interaction

Client - Mother Server - Child Server connection and gameplay flow

drawing

Mother Server - Child Server flow

drawing

Backup flow

Behaviour

Client Actor

ClientActor orchestrates the entire lifecycle of the client-side logic: it initializes the UI, discovers the cluster, requests a game session, and runs a synchronized game loop with the server. It transitions between two main modes:

It also processes user actions (e.g., JoinRandomRoom) and handles end-of-game transitions.

Initialization

When apply() is invoked:

At this stage, the actor becomes discoverable and begins reacting to cluster state changes.

Homepage Mode

In this mode manages all events prior to entering a game session. It may hold an optional reference to the Child Game Manager (manager: ActorRef[ChildEvent]).

Handled messages

In-Game Mode

This mode handles the main game loop and implements a deterministic, synchronized update pattern. A timer triggers Tick every 50 ms. The isSynced flag ensures only one update is in-flight between client and server:

Handled Messages

Mother Actor

MotherActor functions as the central coordinator and matchmaker in the Raga.io distributed architecture. It does not manage gameplay logic directly but is responsible for:

The actor operates as a stateful event handler that reacts to cluster events and client lifecycle messages, maintaining a dynamic view of the system through its internal MotherState.

Initialization

When apply() is invoked:

At this stage, the MotherActor is ready to accept and respond to system-level events.

Main Behaviour

The actor maintains two key data structures inside MotherState:

The actor transitions through different states purely based on incoming messages:

The MotherActor maintains consistent global state by:

The actor’s state is updated immutably: every behaviour invocation constructs a new MotherState copy with the updated lists.

The actor uses a simple least-loaded server selection algorithm to distribute clients:

This ensures even load distribution and minimizes performance bottlenecks across child servers.

Child Actor

ChildActor is the authoritative game-session host.
It owns the world state for a single room, handles player joins/leaves, merges client-submitted updates, and broadcasts the authoritative world to all connected clients.
It never renders; it only validates/merges state and notifies clients.

Initialization

When apply() is invoked:

At this point, the child server is ready to accept clients and process gameplay messages.

Main Behaviour

This behaviour maintains two authoritative structures:

Handled Messages

On merge, the logic is:

Broadcasts of ReceivedRemoteWorld are fanned out to all managedPlayers using a short-lived anonymous actor.
This avoids blocking the main child behaviour on per-client messaging and confines the broadcast to a small, stoppable context.

Data and Consistency Issues

The game is designed for temporary sessions, and no persistent data storage is required. All game state data, including player positions, sizes, and food items, are maintained in memory during the session. Once a session ends (either by player elimination or voluntary exit), all associated data is discarded.

Fault-Tolerance

Failure detection via Akka Cluster / Receptionist

Graceful degradation when capacity is unavailable

Cleanup on disconnections

Back-pressure at the client

Data Replication and State Sharing

Gaps and Risks

Availability

The software is designed to be highly available, with a distributed architecture that allows multiple game servers to handle sessions concurrently.

The Mother Server acts as a central coordinator, managing the distribution of players to various Child Servers to avoid overloading any single server. This load balancing enhances availability by ensuring that if one server becomes overwhelmed, new players can be directed to less busy servers.

If one game server fails, players can be redirected to another server without losing their session (it’s mandatory to have a Child Server that is managing a game session).

Security


There are not any kind of security mechanisms implemented, as the game is designed for casual play without sensitive data involved. Users are not required to create accounts or provide personal information, they can simply enter a nickname to join a game session. The system does not store any personal data, and all session data is temporary and discarded once the session ends.

Implementation

We choose to use TCP (Transmission Control Protocol) for several key reasons, with reliability and order being the most critical. Unlike UDP, TCP is a “connection-oriented” protocol that ensures data packets are delivered, and if a packet is lost, it’s automatically resent. This is crucial for maintaining a consistent game state, especially for core mechanics like player movements, collisions, and cell consumption, where a lost packet could lead to players appearing to be in different locations or a desynchronized game world. Furthermore, TCP guarantees that packets arrive in the correct order, which is essential for deterministic game logic. UDP is favored for fast-paced shooters where low latency is paramount, the nature of an Agar.io clone tolerates slightly higher latency in exchange for the absolute reliability and synchronization that TCP provides.

Below is a snippet from the application.conf file showing the configuration for using TCP as the transport protocol with Akka’s remote artery:

  remote {
    artery {
      transport = tcp
    }
  }

To represent in-transit data, we opted for JSON (JavaScript Object Notation) produced by Akka’s Jackson serializer. The converse from entity to JSON and vice versa is handled automatically by Akka, which simplifies the serialization process.

  serializers {
    jackson-json = "akka.serialization.jackson.JacksonJsonSerializer"
  }
  serialization-bindings {
    "akka.actor.typed.ActorRef" = jackson-json
    "akka.actor.typed.internal.adapter.ActorRefAdapter" = jackson-json
    "it.unibo.protocol.Message" = jackson-json
  }

All data is not stored persistently, as the game is designed for temporary sessions.

Technological details

Validation

Acceptance test

Automatic tests were not implemented due to time constraints and the complexity of simulating real-time multiplayer interactions. However, manual testing was conducted extensively to ensure the system met the functional requirements.

Manual testing focused on the following areas:

  1. Session Management: Verified the ability to create, join, and manage game sessions.
  2. Real-time Interaction: Ensured that player movements and size increases, food consumption, and collisions were accurately reflected across all clients.
  3. GUI Functionality: Tested all GUI elements for usability and responsiveness.
  4. Scalability: Simulated multiple players joining a session to observe performance and responsiveness.
  5. Error Handling: Tested the system’s response to network interruptions and server failures.

Release

Each release is packaged into three separate JAR files: one for the Mother Server, one for the Child Server, and one for the Client. This modular structure enables independent deployment and scaling of each component according to demand.

All JAR files follow semantic versioning (e.g., 1.0.0, 1.1.0, 2.0.0), making it easier to track changes and maintain compatibility across components.

The JARs are distributed through a public GitHub repository, where users can download the latest versions. Each release is also tagged in the repository for convenient access.

Since the executables run on any system with a Java Runtime Environment (JRE), they are fully platform-independent. Currently, they are executed locally, but with proper configuration they can also be deployed on remote servers.

The installation process is straightforward:

  1. Ensure that a compatible JRE is installed on the system.
  2. Download the appropriate JAR files from the GitHub repository.
  3. Run the JAR files using the command line with java -jar <filename>.jar. To launch the entire system, follow this sequence (different order is also possible, but the Mother Server must always be started first):
    1. Start the Mother Server first.
    2. Start one or more Child Servers.
    3. Finally, launch the Client application.

Deployment

The following instructions guide you through deploying the Raga.io system from scratch on your local machine - for remote deployment, additional configuration may be required.

To deploy, follow these steps:

User Guide

drawing drawing drawing

In the menu page, the user can:

Game view

drawing drawing drawing

In the game page, the user can:

Self-evaluation