Create jobified client and server
In the workflow Creating a minimal client and server, the client should look like this code example.
#
Prerequisites- Before reading and using this workflow, you should understand how the C# Job System works. Review that information, then continue.
- Install the jobs package to your
manifest.json
file, inside the /Packages folder. To do this:- Open your Unity Editor and create a new Project.
- Go to Window > Package Manager from the main menu to open the Unity Package Manager.
- Click on the
(plus symbol) in the status bar and select Add package from git URL... from the dropdown.
- Enter
com.unity.jobs
for the latest version of the jobs package.
#
Create a Jobified ClientCreate a client job to handle your inputs from the network. As you only handle one client at a time, use IJob as your job type. You need to pass the driver and the connection to the job to handle updates within the Execute
method of the job.
note
The data inside the ClientUpdateJob
is copied. If you want to use the data after the job is completed, you need to have your data in a shared container, such as a NativeContainer.
You may want to update the NetworkConnection
and the done
variables inside your job as you may receive a disconnect message. Verify you can share the data between the job and the caller. In this case, use a NativeArray.
note
You can only use blittable types in a NativeContainer
. In this case, instead of a bool
you need to use a byte
, as its a blittable type.
In your Execute
method, move over your code from the Update
method that you have already in place from ClientBehaviour.cs and you are done.
You need to change any call to m_Connection
to connection[0]
to refer to the first element inside your NativeArray
. The same goes for your done
variable, you need to call done[0]
when you refer to the done
variable. See the following:
#
Update the client MonoBehaviourWhen you have a job, you need to verify that you can execute the job.
Complete changes to ClientBehaviour
:
- Change
m_Done
andm_Connection
to typeNativeArray
- Add a JobHandle to track ongoing jobs
#
Start methodThe Start
method looks pretty similar to before, the major update here is to verify you create your NativeArray
.
#
OnDestroy methodFor the OnDestroy
method, dispose all NativeArray
objects. Add a ClientJobHandle.Complete()
call. This ensures your jobs complete before cleaning up and destroying the data they might be using.
#
Client Update loopFinally update your core game loop:
Before you start running your new frame, check that the last frame has completed. Instead of calling m_Driver.ScheduleUpdate().Complete()
, use the JobHandle
and call ClientJobHandle.Complete()
.
To chain your job, start by creating a job struct:
To schedule the job, pass the JobHandle
dependency that was returned from the m_Driver.ScheduleUpdate
call in the Schedule
function of your IJob
. Start by invoking the m_Driver.ScheduleUpdate
without a call to Complete
, and pass the returning JobHandle
to your saved ClientJobHandle
.
Pass the returned ClientJobHandle
to your own job, returning a newly updated ClientJobHandle
.
You now have a JobifiedClientBehaviour that looks like this.
#
Create a Jobified ServerThe server side is pretty similar to start with. You create the jobs you need and then you update the usage code.
Consider this: you know that the NetworkDriver
has a ScheduleUpdate
method that returns a JobHandle
. The job as you saw above populates the internal buffers of the NetworkDriver
and lets us call PopEvent
/PopEventForConnection
method. What if you create a job that will fan out and run the processing code for all connected clients in parallel? If you look at the documentation for the C# Job System, you can see that there is a IJobParallelFor job type that can handle this scenario
note
Because you don't know how many requests you may receive or how many connections you may need to process at any one time, there is another IJobPrarallelFor
job type that you can use namely: IJobParallelForDefer
.
However, you can't run all of your code in parallel.
In the client example above, you begin by cleaning up closed connections and accepting new ones, which can't be done in parallel. You need to create a connection job.
Start by creating a ServerUpdateConnectionJob
job. Pass both the driver
and connections
to the connection job. Then you want your job to "Clean up connections" and "Accept new connections":
The code above should be almost identical to your old non-jobified code.
With the ServerUpdateConnectionsJob
done, implement the ServerUpdateJob
using IJobParallelFor
:
There are two major differences compared with the other job
:
- You are using the
NetworkDriver.Concurrent
type, this allows you to call theNetworkDriver
from multiple threads, precisely what you need for theIParallelForJobDefer
. Secondly, - You are now passing a
NativeArray
of typeNetworkConnection
instead of aNativeList
. TheIParallelForJobDefer
does not accept any otherUnity.Collections
type than aNativeArray
(more on this later).
#
Execute methodThe only difference between the old code and the jobified example is that you remove the top level for
loop that you had in your code: for (int i = 0; i < m_Connections.Length; i++)
. This is removed because the Execute
function on this job will be called for each connection, and the index
to that a available connection will be passed in.
You can see this index
in use in the top level while
loop:
note
You are using the index
that was passed into your Execute
method to iterate over all the connections
.
You now have two jobs:
- The first job is to update your connection status:
- Add new connections.
- Remove old or stale connections.
- The second job is to parse
NetworkEvent
on each connected client.
#
Update the server MonoBehaviourAccess your MonoBehaviour and start updating the server.
The only change made in your variable declaration is adding a JobHandle
to keep track of your ongoing jobs.
#
Start methodYou don't need to change your Start
method as it should look the same:
#
OnDestroy methodYou need to remember to call ServerJobHandle.Complete
in your OnDestroy
method so you can properly clean up code:
#
Server update loopIn your Update
method, call Complete
on the JobHandle
. This forces the jobs to complete before you start a new frame:
To chain the jobs, you want to follow this process:
NetworkDriver.Update
-> ServerUpdateConnectionsJob
-> ServerUpdateJob
.
Start by populating your ServerUpdateConnectionsJob
:
Then create your ServerUpdateJob
. Remember to use the ToConcurrent
call on your driver, to verify you are using a concurrent driver for the IParallelForJobDefer
:
The final step is to verify the NativeArray
is populated to the correct size. This
can be done using a DeferredJobArray
. When executed, it verifies the connections array is populated with the correct number of items that you have in your list. When runnning ServerUpdateConnectionsJob
first, this may change the size of the list.
Create your job chain and call Scheduele
as follows:
In the code above, you have:
- Scheduled the
NetworkDriver
job. JobHandle
returned as a dependency on theServerUpdateConnectionJob
.- The final link in the chain is the
ServerUpdateJob
that needs to run afterServerUpdateConnectionsJob
. In this line of code, there is a trick to invoke theIJobParallelForDeferExtensions
.m_Connections
NativeList
is passed to theSchedule
method, which updates the count of connections before starting the job. It will fan out and run allServerUpdateConnectionJobs
in parallel.
You should now have a fully functional jobified server.