In this tutorial, we’ll look at how to use Lumberyard’s awesome networking system, GridMate, to create a basic networked entity. GridMate will synchronise the position and orientation of an entity between multiple networked clients.
This is mostly a lesson on concepts, with a small bit of actual work for you to do to try it all out. I’m using Lumberyard 1.9 for this tutorial.
Networking concepts with GridMate
GridMate is Amazon Lumberyard’s core networking module. It is responsible for creating and managing network connections and handling all network communication. There are a few key concepts that GridMate uses, which we will define here:
A network ‘session’ consists of a host plus zero or more clients, exchanging network traffic and running an individual game instance.
This is the traditional ‘machine that is owning the session’. There can be only 1 host in a session. Normally, the ‘host’ will be the machine who created the network session, or in the case of a dedicated server (e.g. when using GameLift), the host would be that. The host can also be a player partaking in gameplay, or in the case of a dedicated server, it is purely responsible for network traffic and does not represent a player in the game.
This is the term given to all non-host machines in a session. In the common use-case, clients cannot change the state of the world directly; instead, they can only request changes from the host and replicate state from the host. When we are generally talking about ‘the group of machines taking part in a session’, the host machine is also sometimes referred to as a client.
All state in GridMate is represented by ‘replica chunks’. A replica chunk is, at its lowest level, a chunk of replicated data backed by network communication. GridMate is responsible for shipping replica chunks around the network to ensure that all clients experience the same game. A replica chunk can either be a Master or a Proxy. Most Lumberyard components that have a ‘Bind to network’ tick box will implement a replica chunk to handle their network binding.
Master Replica Chunk
Master replica chunks are the ‘state owners’. They are authoritative on the network, and their state is the true state. In the general case, the host machine will be the owner of most master replica chunks. They can have their state changed only on the machine that owns them. Other clients can also call functions (called RPCs) on them, which allows them to request state changes, but not make them directly.
Proxy Replica Chunk
When a master replica chunk is created on a machine (usually the host), GridMate will automatically cause ‘proxy replica chunks’ to be created on all the other clients. A proxy replica chunk is a dummy – it mimics the state of the corresponding master replica chunk on the other client, and it can send requests to the master via RPCs.
A replica is a container that holds several replica chunks that relate to a ‘thing’, e.g. A networked entity or system.
When a client joins a session, it will be immediately transported to the currently loaded map on the host. All proxy replica chunks are created and synchronised automatically. Thereafter, whenever the master replica chunks get changed, GridMate will send all the relevant data over to the new client, which will then update its proxy replica chunks. Whenever the host changes its current loaded map, all connected clients will automatically change with it. This allows easy implementation of lobbies, mid-game joining, and skirmish-type missions with a group of players playing across several maps in sequence.
The replica master/proxy system allows basic anti-cheat mechanisms to be implemented by enforcing that state can only be changed on the master. A proxy may call an RPC to request state change, but the master then has the opportunity to sanity check the request and decide what to do with it. If the master decides to apply the change, the state is then trickled back to the proxy by GridMate. An example use of this feature is that the master could check that a weapon has enough ammo before accepting the request to shoot a bullet.
Set up a networked entity
Let’s get started with a basic demo. First, create a new component entity, add a Static Mesh component and set it to engine/objects/default/primitive_cube.cgf:
Add a Network Binding component. The Network Binding component tells Lumberyard to replicate this entity over the network. It causes GridMate to create and synchronise all replica chunks for this networked entity continuously, so anything that happens on the host will also happen on the clients.
Finally (yes, finally!), enable transform replication by ticking the ‘Bind to network’ option on the Transform component:
Tip: it is probably already ticked, but it only has effect when the entity has a Network Binding component.
And you’re done – you have made a networked entity! It’s that simple, and that’s what I like about GridMate – its simple, intuitive presentation to the user. You haven’t had to worry about error handling, state management, packet loss, prioritisation, keep-alive, acknowledgement, and all the other low-level gubbins of networking. Of course, those things are all exposed, and when you get on to writing your own networked components (I’ll do another tutorial on that one day), you’ll see just how easy those things are too.
But wait! All we’ve done is ticked a box! How do we actually test it out?
Make it move
If we want to see the networked entity actually do something, we need to make it move. This isn’t a tutorial on manipulating objects, so we will just make something really simple for our purposes.
Add a flow graph component to the entity, add a flow graph element to the component, and open it in the flow graph editor:
Create the flow graph shown below:
Tip: To assign the ‘graph entity’ to the nodes, right-click them and click ‘Assign graph entity’. This just means that you want the node to act on the entity who the graph belongs too.
Add a camera to the scene by right clicking ‘Perspective’ at the top of the main viewport and selecting ‘Create camera entity from current view’ (position a good view first), and use Ctrl-G to test it.
If it worked, you should be able to move the entity side to side with the left and right arrow keys.
You can’t test out your new networked entity in the Editor because you need at least 2 clients. The Editor prevents networking as it would interfere with Editor functionality. Instead, we’ll export our new level and test it using the Launcher. You don’t need 2 machines for this – as long as you are running in Windowed mode, and your PC is powerful enough, you can happily run 2 clients on a single machine (I’ve successfully had 6 clients running on my i7–6700k).
First, you need to export the level, by loading it into the Editor and pressing Ctrl-E. It will generate a level.pak file in the level folder, which contains a packed version of all your level files.
Now, close the Editor and run 2 copies of Launcher. Unless you have made changes to make them load a specific level, they will by default load to a black screen. Position the 2 Launchers side by side on your monitor so that you can see them both and switch between them easily:
Running the test map
Let’s load our map. Pick one of the instances to be your ‘host’, and enter the following in the console:
Replace ‘mymap’ with the name of the map you made earlier.
When you press <enter>, the host will load the map, and you will be able to move left and right just like you did in the editor.
Create a session
Next, you need to connect the 2 Launcher instances together. On the chosen host, bring up the console and type:
If the session is created successfully, you should receive some output saying something like the following:
Tip: If you get an error about ‘unknown command’, it is likely because your project does not have the Multiplayer Gem enabled. Check Amazon’s tutorial for details on how to enable this gem.
Now, switch to your other Launcher (the ‘client’), and enter the following in the console:
GridMate should kick into action. Because we specified no parameters, mpjoin will search for and connect to a session on the localhost. If successful, you should see something like this in your console:
If it all works, the client will synchronise the loaded level with the host, essentially ‘following it’ into the level, and bind all the net-bound entities.
You should now be able to move the object on the host, and see it move on the client at the same time. Notice how you can’t move the object yourself on the client – the key presses are going through, but the networked entity on the client knows that it is not the master, so it ignores the move requests and waits for the authoritative versions to come through from the master instead.
Note: There appears to be a bug in the Lumberyard 188.8.131.52 which causes the net binding to fail if you create the session before loading the map, resulting in no movement.
That’s it for now, and I hope you like my post. In future, I will build on this tutorial with more networking tips. It would be great to hear what you think, and if you have any specific requests. Feel free to leave a comment below.