Load a Game using DOTS NetCode
Code and workflow to send server game data to client and have the client confirm loading the data
Last updated
Code and workflow to send server game data to client and have the client confirm loading the data
Last updated
We will send server game data to the client; the client will load the data and send back another RPC.
When a socket connection is made in NetCode the server and client both have the ability to send each other RPCs.
To make the system a bit more flexible, you can use the flow of creating an entity that contains specific netcode components such as
SendRpcCommandRequestComponent
andReceiveRpcCommandRequestComponent
, which this page outlines....
NOTE
If
TargetConnection
is set toEntity.Null
you will broadcast the message. On a client you don't have to set this value because you will only send to the server....
The RpcSystem automatically finds all of the requests, sends them, and then deletes the send request. On the remote side they show up as entities with the same
IRpcCommand
and aReceiveRpcCommandRequestComponent
which you can use to identify which connection the request was received from....
The code generation for RPCs is optional, if you do not wish to use it you need to create a component and a serializer. These can be the same struct or two different ones. To create a single struct which is both the component and the serializer you would need to add:
DataStreamReader
reader
Entity
connection
EntityCommandBuffer.Concurrent
commandBuffer
int
jobIndexBecause the function is static, it needs to use
Deserialize
to read the struct data before it executes the RPC. The RPC then either uses the command buffer to modify the connection entity, or uses it to create a new request entity for more complex tasks. It then applies the command in a separate system at a later time. This means that you don’t need to perform any additional operations to receive an RPC; itsExecute
method is called on the receiving end automatically.
Not sure how you feel about it, but that RPCs documentation is TOUGH 🤯
Basically here's the gist: once you have a client/server connection you can send RPCs back and forth.
If you want to send RPCs with "just" data, NetCode has solid code generation that makes it pretty easy to do. We will do that in this project when the server sends the client the game settings.
If you want your RPCs to also execute code when they get to their intended receiver, then boilerplate code from the (confusing) explanation is involved. You'll see how this works at the point in our project when the client sends back "I have loaded the game" confirmation to the server and the client-sent RPC automatically updates the the server's NCE.
There are 2 more data streams that can be sent between NCEs: (1) Snapshots and (2) Commands. Snapshots send state data and Commands send inputs.
Currently (at this point in the project), by creating a NCE, we have "unlocked" RPCs. Once we put a special NetCode component, "NetworkStreamInGame", on the NCE it signals to NetCode to begin sending game data. Then Snapshots and Commands will be unlocked.
Part of the flow of loading the level will be to send the GameSettings data to the client, the second part is to add the NetworkStreamInGame component to the NCE on both the client and server. Because the GameSettings data is set from the fields on the GameSettings GameObject in the Sub Scene, which is accessible to both the client and server, it is actually unnecessary to send GameSettings from the server to the client (the server would be sending the client data that the client already knows).
However, in our project we implement this data transfer even though it is unnecessary (currently) to show an example of how to send data. In the Multiplayer section we will be sending data that the client actually does not know.
We are going to implement a flow where the server sends an RPC to a newly connected client and the client responds by sending back an RPC
Let's start by creating a folder in "Mixed" called "Commands" where we will store our RPCs
The RPCs need to be in the Mixed folder because both the server and client utilize these RPCs
Make another folder in Mixed called "Components"
In this folder we will put the components that both the server and client touch
Put the GameSettingsComponent in Mixed/Components
Both the server and client use the GameSettingsComponent in this flow (which is why it is in Mixed)
Now create PlayerSpawningStateComponent and put it in Server/Components
So make a Components folder in Server folder :)
PlayerSpawningStateComponent will be used by the server to know when a player is spawning
Paste the code snippet below into PlayerSpawningStateComponentData.cs:
Now in Mixed/Commands create two 2 RPCs
First one is SendClientGameRpc
Paste the code snippet below in SendClientGameRpc.cs:
Second one is SendServerGameLoadedRpc
Paste the code snippet below in SendServerGameLoadedRpc.cs:
You'll notice that SendServerGameLoadedRpc is more complicated than SendClientGameRpc
SendClientGameRpc is used to send data from the server to the client. There's no other funny business going on, just an RPC being used as a way to send data
SendServerGameLoadedRpc does not send any data. Instead, when it reaches the server the InvokeExecute method is called, which updates the receiving NCE
The RPC is able to update the NCE itself, without us needing to implement a system to get it done
Create a folder in Server named "Systems" and within that folder create ServerSendGameSystem
Paste this code snippet below into ServerSendGameSystem.cs:
Create a folder in Client named "Systems" and within that folder create ClientLoadGameSystem
Paste the code snippet below into ClientLoadGameSystem:
Let's hit play and then go take a look the NCE in Server World in the DOTS Hierarchy
Checkout the "NetworkStreamInGame" component on the NCE
"Network Stream In Game" listed under tags
We can see the component has been added on the Client as well
Our asteroids now have movement because we are "in game" (which we signaled by adding the "NetworkStreamInGame" component on the NCE)
The server can send Snapshots of the asteroids
We see our NCEs have been updated by our RPCs and systems
We can now load data on the client sent from the server through an RPC
We created a new component
PlayerSpawningStateComponent
We created 2 RPCs
SendClientGameRpc
SendServerGameLoadedRpc
We created 2 systems
ServerSendGameSystem
ClientLoadGameSystem
Github branch link:
git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/
git checkout 'Loading-a-Game'
Github branch link:
NetCode uses a limited form of RPCs to handle events. A job on the sending side can issue RPCs, and they then execute on a job on the receiving side. This limits what you can do in an RPC; such as what data you can read and modify, and what calls you are allowed to make from the engine. For more information on the Job System see the Unity User Manual documentation on the .
To send the command you need to create an entity and add the command and the special component to it. This component has a member called TargetConnection
that refers to the remote connection you want to send this command to.
The interface has three methods: Serialize, Deserialize, and CompileExecute. Serialize and Deserialize store the data in a packet, while CompileExecute uses Burst to create a FunctionPointer
. The function it compiles takes a by ref that contains:
From