Skip to content

Structure of the codebase

Jerome Renaux edited this page Jan 21, 2020 · 5 revisions

This figure indicates the most important files and methods to be aware of in order to start navigating the code. More details below.

Client side

index.html is the entry point for the players. This page loads the client code bundled in dist/client.js, and creates the space where the game canvas will be located.

client/main.js is where the Phaser game object is configured and created. As part of that configuration, 3 Phaser scenes are created: Boot, UI and Engine (see below).

client/Boot.js defines the Boot scene. This scene displays the title screen while receiving some boot parameters from the server. When everything is ready, the "play" and "tutorial" buttons are displayed and the UI scene is launched.

client/UI.js defines the UI scene. As the name implies, this scene is in charge of the UI elements and menus (although this responsibility is still a bit shared with the Engine scene, which is not ideal). When a new players has selected a starting region, or when a returning player is recognized, the Engine scene is launched.

client/Engine.js is the main scene of the game, containing a big part of the game logic. The key methods of that scene are the following, in order of execution:

  • Engine.preload() loads all the assets of the game that haven't been preloaded by the UI scene.
  • Engine.create() sets up a lot of necessary data structures, sets up the camera, the animations (by calling Engine.createAnimations() and sets up the input callbacks. In other words, it lays the basic groundwork for the game being able to run. When it is finished, it calls Client.requestData() which fetches from the server all the information about the player and the world around it. When that data is received, Engine.initWorld() is called.
  • Engine.initWorld() sets up the player character (via Engine.addHero()) and the game world. It also creates all the game menus (via Engine.makeUI()), and start the tutorial if the game is in tutorial mode.

At that point, the player can play the game. The communication with the server is handled in client/Client.js. In addition to establishing the socket.io connection with the server, it contains several methods to send the actions of the player to the server. In exchange, the server sends regular updates, which trigger the Client.socket.on('update') callback. This callback, in turn, calls:

  • Engine.updateSelf() to update properties of the player that are relevant and visible to him/her only (e.g. amount of gold, inventory...)
  • Engine.updateWorld() to update the environment around the player (buildings, animals, etc.)

Server side

The entry point for the server side code is server/server.js. This file is mainly responsible for:

  • Establishing the connection to the database
  • Setting up callbacks to handle the messages coming from the client (see the callbacksMap in the io.on('connection') callback)
  • Launching the game server proper by calling GameServer.readMap() (see below). The GameServer module handles most of the server side game logic.

All communication from Client is sent to the GameServer via server.js. Conversely, all data that needs to be sent to the players comes from the GameServer and is sent to Client by helper methods defined in server.js such as server.sendUpdate().

The most important methods in GameServer are:

  • GameServer.readMap() which creates the entire game world and initializes everything. It starts by reading several static files about the game entities (such as lists of items, animals, etc.) and the world itself (to know its size and how many AOIs to create) and then goes through a series of sequential steps called the initialization sequence, defined by GameServer.initializationMethods. At the end of this, the game server is ready and playable.
  • GameServer.updateClients() is called multiple times per second (the rate is set in the config files) to update the players about their state and the state of the world. This is done in a smart way so as to send only new and relevant information to each player. The details are out of scope of this article, but in a nutshell, each player maintains an individual update package about his own state, while the AOIs around the player maintain their own update packages about their own states. For each player, the updateClients() method merges these different update packages in the proper way and then send them to be consumed by the Client.socket.on('update') callback in Client.