RTS devlog #3: Minimal multiplayer milestone

6
Official Construct Team Post
Ashley's avatar
Ashley
  • 10 Oct, 2022
  • 1,372 words
  • ~5-9 mins
  • 1,857 visits
  • 4 favourites

You can now play a minimally multiplayer version of the RTS live online at CommandAndConstruct.com! It's an unoptimized export running on a cheap web host so it might take a moment to load. (I can improve that later - I'm just doing whatever is quickest to set up at this point.) However the multiplayer part works! One player can choose to host, and a second player can join by entering the host's code. Then each player is assigned three units they can move, and each player sees the other player's movements. The video below shows it in action.

Obviously this is not exactly the greatest multiplayer anyone's ever seen yet. However the reason this is exciting is this demonstrates the foundation on which the rest of the game will be built. Thanks to the architecture, in theory I can just get things working in the single-player mode, and then it should "just work" in multiplayer mode too! We'll see how that pans out in practice though.

Players

The first step to get to this point was giving every unit a #player property, which is the player number that unit belongs to. Each player's units are just tinted a different color to distinguish them - blue for player 0 and red for player 1.

Three units for each player.Three units for each player.

The game client only lets players select and move their own units (SelectionManager now only checks if you clicked one of allLocalPlayerUnits(), i.e. one of the player's own units (incidentally a nice example of a generator function).

However clients can lie, and so this must be enforced on the server-side too. So when GameServer receives a "move-units" instruction, it filters out units that don't belong to the player issuing the command. So even if someone hacks their client to let them select any units, the server will ignore their attempts to move other player's units.

Multiplayer

With the architecture set up to allow multiple clients of the one GameServer, setting up a multiplayer game is then a matter of establishing a network connection and getting the remote player to control the other player. This is something that sounds simple but can be a little bit tricky to get right.

First of all you need some menu screens to allow the player to choose whether they want to host or join a game, and provide the necessary user interface. Even as an experienced programmer, writing code for menu screens can be pretty tedious. Construct's event sheets can get much quicker results, and they're the right tool for the job in this case. So I just threw together some quick layouts and event sheets to handle hosting and joining (which you can see in the clip above). I used buttons and text fields - which are HTML elements - and tweaked them a bit with a stylesheets, since HTML and CSS are great for user interfaces.

This game re-uses Construct's Multiplayer infrastructure, using the public Scirra signalling server to set up peer-to-peer connections, and then handing off the game to the scripting-controlled game layout. The process works something like this.

Step 1: first player hosts

  1. The first player clicks "Host multiplayer"
  2. The game connects to the signalling server, generates a random room name, and joins it
  3. Now they are host of a room, the game displays the room name as the join code, and tells the player to share it with someone.
  4. The game waits until someone connects.

Step 2: second player joins

  1. The second player clicks "Join multiplayer" and enters the host's code
  2. The game connects to the signalling server and joins the given room name, expecting there to already be a host. (If there is no host, the code is wrong.)
  3. Once a connection is made between the host and the joining player, both proceed to the game layout.

Then the JavaScript code for the game layout takes over, knowing that a connection has already been established. This also demonstrates how event sheets and scripting integrate - different layouts can use different development styles to suit the task at hand. The event sheets also use a small piece of embedded JavaScript to tell the code what game mode it's using, showing how code can easily integrate with event sheets too.

Using a snippet of JavaScript code in an event sheet to set the game mode.Using a snippet of JavaScript code in an event sheet to set the game mode.

The game modes

The devlog on architecture included a diagram of the classes involved in a multiplayer game. The event sheets for the menu system set Globals.gameMode to a string indicating which game mode to use - "single-player", "multiplayer-host" or "multiplayer-peer".

Each mode has its own "game mode" class, responsible for managing how that game mode works. In other words the class creates the necessary classes and communication links as drawn in the diagram from the architecture post. These classes are all in a gameModes folder. This means:

  • GameModeSinglePlayer creates a GameServer, a GameClient, and does local communication only (no networking).
  • GameModeMultiplayerHost creates a GameServer, a GameClient, and communicates with another player over the network (sending game state updates and receiving their move commands).
  • GameModeMultiplayerPeer creates only a GameClient. It communicates with the host's GameServer over the network, receiving game state updates and sending their move commands.

The code for handling GameClient messages ended up duplicated between these game mode classes, so that was moved to its own GameClientMessageHandler class.

There could be more game modes: more than 2 players; co-op mode; alliances of players; spectators; and possibly other modes. I'd like to do at least some of those, but I'll stick to these three modes for now to keep things simple. I also suspect they'll be relatively straightforward with this architecture: the tricky bit is syncing things across a network, which 2-player mode does. But we'll see!

Starting the game

One surprisingly tricky aspect of starting a multiplayer game is: when does it start?

Suppose that, for some reason, the host loads instantly but the peer takes 5 seconds to load. They might miss some of the crucial first messages of the game and so fail to initialise correctly.

The peer could message the host to tell it that it's ready, but what if the opposite happens? If the peer loads instantly but the host takes 5 seconds to load, the host might miss the peer's ready message and never start the game.

There are various ways to solve this problem, but the approach I went with is:

  • The peer starts up and then starts sending a "ready" message every 250ms. (This is done by the GameModeMultiplayerPeer class.)
  • When the host receives a "ready" message, it sends back a "start" message and begins the game. (This is done by the GameModeMultiplayerHost class.)
  • When the peer receives the "start" message it stops sending "ready" messages and begins the game.

This way, no matter how long either party takes to load, the game will start when both are ready and able to receive messages from the other side.

A solid foundation

I'm pretty pleased I got to something "playable" this quickly! It's all now hosted at CommandAndConstruct.com so you can try it out very easily. While it is an absolute bare minimum demonstration, it does show the architecture and networking in action and proves they work. I think this also shows how Construct can help you get quick results even when you're writing code.

I'll periodically update the web hosted version when there's a worthwhile update (keep an eye on the version on the title screen). And don't forget the whole project is on GitHub where you can browse the code, see a log of all the changes, and download the entire Construct project.

From here I'm hoping that I can incrementally update single player mode, and it will more or less just work in multiplayer mode too. Stay tuned and we'll find out if it works like that for real! Next up: let's add some turrets and get units to shoot at each other.

Past blog posts

In case you missed them here are the past blogs about this projects:

Subscribe

Get emailed when there are new posts!

  • 6 Comments

  • Order by
Want to leave a comment? Login or Register an account!