Client server Worlds
NetCode has a separation of client and server logic, and both the client and server logic are in separate Worlds (the client World, and the server World), based on the hierarchical update system of Unity’s Entity Component System (ECS).
By default, NetCode places systems in both client and server Worlds, but not in the default World.
Note
Systems that update in the PresentationSystemGroup
are only added to the client World.
To override this default behavior, use the UpdateInWorld attribute, or the UpdateInGroup
attribute with an explicit client or server system group. The available explicit client server groups are as follows:
- ClientInitializationSystemGroup
- ServerInitializationSystemGroup
- ClientAndServerInitializationSystemGroup
- ClientSimulationSystemGroup
- ServerSimulationSystemGroup
- ClientAndServerSimulationSystemGroup
- ClientPresentationSystemGroup
Note
There is no server presentation system group.
As well as the attributes listed above, you can use the PlayMode Tools window in the Unity Editor to select what happens when you enter Play Mode. To access PlayMode Tools, go to menu: Multiplayer > PlayMode Tools.
PlayMode Tools
Property | Description |
---|---|
PlayMode Type | Choose to make Play Mode either Client only, Server only, or Client & Server. |
Num Thin Clients | Set the number of thin clients. Thin clients cannot be presented, and never spawn any entities it receives from the server. However, they can generate fake input to send to the server to simulate a realistic load. |
Client send/recv delay | Use this property to emulate high ping. Specify a time (in ms) to delay each outgoing and incoming network packet by. |
Client send/recv jitter | Use this property to add a random value to the delay, which makes the delay a value between the delay you have set plus or minus the jitter value. For example, if you set Client send/recv delay to 45 and Client send/recv jitter to 5, you will get a random value between 40 and 50. |
Client package drop | Use this property to simulate bad connections where not all packets arrive. Specify a value (as a percentage) and NetCode discards that percentage of packets from the total it receives. For example, set the value to 5 and NetCode discards 5% of all incoming and outgoing packets. |
Client auto connect address (Client only) | Specify which server a client should connect to. This field only appears if you set PlayMode Type to Client. The user code needs to read this value and connect because the connection flows are in user code. |
When you enter Play Mode, from this window you can also disconnect clients and choose which client Unity should present if there are multiple. When you change a client that Unity is presenting, it stops calling the update on the ClientPresentationSystemGroup
for the Worlds which it should no longer present. As such, your code needs to be able to handle this situation, or your presentation code won’t run and all rendering objects you’ve created still exist.
Bootstrap
The default bootstrap creates client server Worlds automatically at startup. It populates them with the systems defined in the attributes you have set. This is useful when you are working in the Editor, but in a standalone game, you might want to delay the World creation so you can use the same executable as both a client and server.
To do this, you can create a class that extends ClientServerBootstrap
to override the default bootstrap. Implement Initialize
and create the default World. To create the client and server worlds manually, call ClientServerBootstrap.CreateClientWorld(defaultWorld, "WorldName");
or ClientServerBootstrap.CreateServerWorld(defaultWorld, "WorldName");
.
The following code example shows how to override the default bootstrap to prevent automatic creation of the client server worlds:
public class ExampleBootstrap : ClientServerBootstrap
{
public override bool Initialize(string defaultWorldName)
{
var systems = DefaultWorldInitialization.GetAllSystems(WorldSystemFilterFlags.Default);
GenerateSystemLists(systems);
var world = new World(defaultWorldName);
World.DefaultGameObjectInjectionWorld = world;
DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(world, ExplicitDefaultWorldSystems);
ScriptBehaviourUpdateOrder.AppendWorldToCurrentPlayerLoop(world);
return true;
}
}
Fixed and dynamic timestep
When you use NetCode, the server always updates at a fixed timestep. NetCode limits the maximum number of iterations to make sure that the server does not end up in a state where it takes several seconds to simulate a single frame.
The fixed update does not use the standard Unity update frequency. A singleton entity in the server World controls the update with a ClientServerTickRate component. The ClientServerTickRate
controls SimulationTickRate
which sets the number of simulation ticks per second.
Note
SimulationTickRate
must be divisible by NetworkTickRate
.
The default number of simulation ticks is 60. The component also has values for MaxSimulationStepsPerFrame which controls how many simulations the server can run in a single frame, and TargetFrameRateMode which controls how the server should keep the tick rate. Available values are:
BusyWait
to run at maximum speedSleep
forApplication.TargetFrameRate
to reduce CPU loadAuto
to useSleep
on headless servers andBusyWait
otherwise
The client updates at a dynamic time step, with the exception of prediction code which always runs at a fixed time step to match the server. The prediction runs in the GhostPredictionSystemGroup and applies its own fixed time step for prediction.
Standalone builds
When you build a standalone game, NetCode uses the Server Build property in the Build Settings window to decide what to build. If the property is enabled, NetCode sets the UNITY_SERVER
define and you get a server-only build. If the property is disabled you get a combined client and server build. You can use a combined client and server build to decide if a game should be client, server or both at runtime.
To build a client-only game, add the UNITY_CLIENT
define to the Scripting Define Symbols in the Player Settings (menu: Edit > Project Settings > Player > Configuration). You can have the UNITY_CLIENT
define set when you build a server, but the UNITY_SERVER
define takes precedence and you get a server-only build.
World migration
Sometimes you want to be able to destroy the world you are in and spin up another world without loosing your connection state. In order to do this we supply a DriverMigrationSystem, that allows a user to Store and Load Transport related information so a smooth world transition can be made.
public World MigrateWorld(World sourceWorld)
{
DriverMigrationSystem migrationSystem = default;
foreach (var world in World.All)
{
if ((migrationSystem = world.GetExistingSystem<DriverMigrationSystem>()) != null)
break;
}
var ticket = migrationSystem.StoreWorld(sourceWorld);
sourceWorld.Dispose();
var newWorld = migrationSystem.LoadWorld(ticket);
// NOTE: LoadWorld must be executed before you populate your world with the systems it needs!
// This is because LoadWorld creates a `MigrationTicket` Component that the NetworkStreamReceiveSystem needs in order to be able to Load
// the correct Driver.
return ClientServerBootstrap.CreateServerWorld(DefaultWorld, newWorld.Name, newWorld);
}