Create a Network Connection using DOTS NetCode
Workflows and code to create a server/client socket connection using NetCode

What you'll develop on this page

Make a Client/server socket connection to create a Network Connection Entity configured from launch objects
In our project, we make a configurable client/server socket connection using Unity NetCode.

Creating a socket connection

First, some background:

If you think back to the "Spawning and Moving Player Prefabs" page in the ECS section of this gitbook, our project has been using the default World creation provided by Unity ECS.
A World organizes entities into isolated groups. A world owns both an EntityManager and a set of Systems. Entities created in one world only have meaning in that world, but can be transferred to other worlds (with EntityManager.MoveEntitiesFrom). Systems can only access entities in the same world. You can create as many worlds as you like.
By default Unity creates a default World when your application starts up (or you enter PlayMode). Unity instantiates all systems (classes that extend ComponentSystemBase) and adds them to this default world. Unity also creates specialized worlds in the Editor. For example, it creates an Editor world for entities and systems that run only in the Editor, not in playmode and also creates conversion worlds for managing the conversion of GameObjects to entities. See WorldFlags for examples of different types of worlds that can be created.
If we take a look at our Entity Debugger we see that we are only running a single world called "Default World".
View of the Entity Debugger when our project is running
Now that we are using NetCode in our Project, we will start working in "Server World" and "Client World."
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:
NOTE
There is no server presentation system group.
...
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.
When we add the NetCode package to our project its default is to create a Client and Server world, which we can override by using ClientServerBootstrap.
At the end of this code-along, user inputs will be ingested in "Client World" and stored as "Commands," which are then sent and played back in the "Server World" as well as played back in the "Client World" to do prediction.
The server is the authoritative source of the game. So if the client "predicts" a response to an input and the server disagrees, the server wins.
Client can predict some movements, but the Server has final say
It is important to note that because Client and Server worlds get automatically created with NetCode, our project will no longer function as expected. All our systems and components will "automatically" put our ECS into client and server worlds.
So when our server creates asteroids and sends them to the client, the asteroids will appear "frozen" to the client. This is because although a server-spawned entity will reach the client, we haven't updated our Asteroid prefab to also transmit its updated location, so that's why they'll appear frozen.
From the "Overview" page we know that Unity is running an authoritative server client-predicted network architecture.

Now, let's implement:

    First, In the Scripts and Prefabs folder create three new folders:
      Client (where we will store files only the client needs)
      Mixed (where we will store files both the client and server need)
      Server (where we will store files only the server needs)
      This is just to help with organization. Our Scripts and Prefabs folder is already hard to navigate, and as we add even more it will soon become a mess
        As we continue to update or add more files, let's move our existing files into these folders
        This will also help us understand what components and systems are run by the server versus the client
Hardcore developers use assembly definition files along with Client/Mixed/Server folder separation so that they can deploy Client, Mixed, or Server only builds.
This is also helpful because when the client-only portion of the code base is adjusted the entire project does not need to be rebuilt, only the client-only code, which helps with development time.
If you're interested in being hardcore, check out Unity's Asteroids sample to see how they use assembly definition files to separate out code and logic. Their approach is a bit overkill for this gitbook, so to keep it simple our approach here is just to separate out the files to just give an idea of what files are expected to be run by the client/server.
Adding Client, Mixed and Server folders to Scripts and Prefabs
    Now lets add NetCode to our manifest.json file (found in the Project Folder)
1
"com.unity.netcode": "0.6.0-preview.7",
Copied!
Adding NetCode package to our project through manifest.json
    Let's hit play and take a look at the Entity Debugger
Taking a look at the Entity Debugger after adding NetCode to Project
    We can see that in addition to Default World we now have "ClientWorld0" and "ServerWorld"
      The reason we have "ClientWorld0" (with a number appended at the end) is because the Unity Editor supports "PlayMode Tools" for NetCode and you can "simulate" having multiple clients in playmode
      Unity appends the client # to the end of the world
        So if there were 2 clients set in "PlayMode Tools" we would see "ClientWorld0" and "ClientWorld1"
    Click through Default World, ClientWorld0 and ServerWorld and check out the different systems in each of the worlds
Expand this slide for an overview of the difference between Worlds
    Take time and review the image above; understand which systems are run in ClientWorld0 and which are run in ServerWorld
      Seriously, it will save you a lot of heartache if you get comfortable with the difference SystemGroup setups between Server and Client worlds
    We found it best to always imagine that ServerWorld and ClientWorld are run on entirely different machines (which happens in server-only builds where clients connect to a dedicated server)
      When initially starting out with NetCode it is easy to sometimes forget this and think that a component created in ClientWorld should be available to be acted on in ServerWorld
      It gets especially tricky during development because both the client and server are on the same machine (in the editor) so it "feels" like the data should be available
    Notice that there are some systems that are only available on the client like "GhostInputSystem"
      This system is where we will place our updated "InputMovementSystem"
      Only the client needs to ingest inputs, which is why the ServerWorld does not have this system
    Another system is the PresentationSystemGroup which does not exist on the server
      It doesn't exist on the server because the server doesn't need to render data in a presentation layer' the server is just the authoritative store of "state"
    Also, take note of a new folder that has appeared in Assets named "NetCodeGenerated"
NetCodeGenerated folder
    This folder has a lot of code for conventional workflows in multiplayer games that NetCode automatically generates
      Rather than hide all the "under the hood" aspects of NetCode, Unity exposes the generated files so users can customize or extend any of the default workflows
Alright, let's get into it!
    Create ClientServerConnectionControl in Scripts and Prefabs
      Keep this file in Scripts and Prefabs because it contains systems for both client and server (we'll move it into a better folder later)
        We could split this file into two separate files (because there is a server-specific system and a client-specific system in the file) but for the sake of starting out easy in this gitbook, we're keeping it in one file
    Paste the code snippet below into ClientServerConnectionControl.cs:
1
using Unity.Burst;
2
using Unity.Entities;
3
using Unity.Mathematics;
4
using Unity.Networking.Transport;
5
using Unity.NetCode;
6
using UnityEngine;
7
using Unity;
8
using System;
9
10
#if UNITY_EDITOR
11
using Unity.NetCode.Editor;
12
#endif
13
14
15
//ServerConnectionControl is run in ServerWorld and starts listening on a port
16
//The port is provided by the ServerDataComponent
17
[UpdateInWorld(UpdateInWorld.TargetWorld.Server)]
18
public class ServerConnectionControl : SystemBase
19
{
20
private ushort m_GamePort = 5001;
21
22
private struct InitializeServerComponent : IComponentData
23
{
24
}
25
26
protected override void OnCreate()
27
{
28
// We require the InitializeServerComponent to be created before OnUpdate runs
29
RequireSingletonForUpdate<InitializeServerComponent>();
30
31
//We create a component which will get immediatly destroyed so this system runs once
32
EntityManager.CreateEntity(typeof(InitializeServerComponent));
33
34
}
35
36
protected override void OnUpdate()
37
{
38
//We destroy the InitializeServerComponent so this system only runs once
39
EntityManager.DestroyEntity(GetSingletonEntity<InitializeServerComponent>());
40
41
// This is used to split up the game's "world" into sections ("tiles")
42
// The client is in a "tile" and networked objects are in "tiles"
43
// the client is streamed data based on tiles that are near them
44
//https://docs.unity3d.com/Packages/[email protected]/manual/ghost-snapshots.html
45
//check out "Distance based importance" in the link above
46
var grid = EntityManager.CreateEntity();
47
EntityManager.AddComponentData(grid, new GhostDistanceImportance
48
{
49
ScaleImportanceByDistance = GhostDistanceImportance.DefaultScaleFunctionPointer,
50
TileSize = new int3(80, 80, 80),
51
TileCenter = new int3(0, 0, 0),
52
TileBorderWidth = new float3(1f, 1f, 1f)
53
});
54
55
//Here is where the server creates a port and listens
56
NetworkEndPoint ep = NetworkEndPoint.AnyIpv4;
57
ep.Port = m_GamePort;
58
World.GetExistingSystem<NetworkStreamReceiveSystem>().Listen(ep);
59
Debug.Log("Server is listening on port: " + m_GamePort.ToString());
60
}
61
}
62
63
//ServerConnectionControl is run in ServerWorld and starts listening on a port
64
//The port is provided by the ServerDataComponent
65
[UpdateInWorld(UpdateInWorld.TargetWorld.Client)]
66
public class ClientConnectionControl : SystemBase
67
{
68
public string m_ConnectToServerIp = "127.0.0.1";
69
public ushort m_GamePort = 5001;
70
71
private struct InitializeClientComponent : IComponentData
72
{
73
}
74
75
protected override void OnCreate()
76
{
77
// We require the component to be created before OnUpdate runs
78
RequireSingletonForUpdate<InitializeClientComponent>();
79
80
//We create a component which will get immediatly destroyed so this system runs once
81
EntityManager.CreateEntity(typeof(InitializeClientComponent));
82
}
83
84
protected override void OnUpdate()
85
{
86
// As soon as this runs, the component is destroyed so it doesn't happen twice
87
EntityManager.DestroyEntity(GetSingletonEntity<InitializeClientComponent>());
88
89
NetworkEndPoint ep = NetworkEndPoint.Parse(m_ConnectToServerIp, m_GamePort);
90
World.GetExistingSystem<NetworkStreamReceiveSystem>().Connect(ep);
91
Debug.Log("Client connecting to ip: " + m_ConnectToServerIp + " and port: " + m_GamePort.ToString());
92
}
93
}
Copied!
    We can see within the file there is ServerConnectionControl and ClientConnectionControl
      ServerConnectionControl only runs in the Server world (check it out in the Entity Debugger) because of the decoration at the top of the system
      ClientConnectionControl only runs in the Client world (check it out in the Entity Debugger) because of the decoration at the top of the system
    We can see that in ServerConnectionControl we create an Entity and add a "GhostImportanceDistance" component

Distance based importance

You can use a custom function to scale the importance per chunk. For example, if a singleton entity with the GhostDistanceImportance component on it exists on the server, the netcode makes sure that all the ghosts in the World are split into groups based on the tile size in that singleton.
You must add a GhostConnectionPosition component to each connection to determine which tile the connection should prioritize. This GhostSendSystem passes this information to the ScaleImportanceByDistance in GhostDistanceImportance which then uses it to scale the importance of a chunk based on its distance in tiles or any other metric you define in your code.
    GhostImportanceDistance is a powerful functionality available in NetCode
      This allows clients to selectively receive Snapshot data based on their proximity to different ghosts
      What does this mean in context of a game? In a large-scale map, does a player really need to get the Snapshot data of a grenade thrown on the other side of the map just as fast as the Snapshot data of a grenade thrown right in front of them?
        Probably not.
      In Moetsi's case, we build city-scale live Reality Models
        So sending each client all the data of all network objects in the model is not feasible and also unnecessary
        GhostDistanceImportance allows for the most relevant Snapshot data be sent automatically, which is great stuff!
    Both in ServerConnectionControl and ClientConnectionControl we use NetworkStreamReceiveSystem to Listen/Connect
      The server operating in ServerWorld "listens" on the defined port
      The client operating in ClientWorld "connects" to the defined IP address and port
Creating ClientServerConnectionControl
    Let's hit play and see what happens
Hitting play after creating ClientServerConnectionControl
    We can see that our systems ran and logged their output
    But, what is up with the frozen asteroids?!
      So when we create these asteroids, both the server and client instantiate
      The server is "authoritative," so it decides where the asteroids go, but currently in our Project, we are not yet sending game Snapshot data to the client
      In order to send Snapshot data, the client must go "in game" by adding a special NetCode component, "NetworkStreamInGame"
      Then the client will receive updates
      We will do this in the next section "Loading a Game"
    Hit play then navigate to the Entity Debugger, select SeverWorld, and filter by NetworkIdComponent, and click on the Entity to see it in the Inspector
      The Entity Debugger "filter" is buggy, so if it does not work you can try and find the entity by the list of "chunks" on the right side
      Look for a a chunk with a NetworkIdComponent
Finding the NetworkConnectionEntity (NCE) in ServerWorld
    This is the "NetworkConnectionEntity" (NCE)
      this is not a Unity term, but it is the term we will be using in this gitbook to describe the entity created after making a client/server connection
      When the server makes a connection with a client it creates a NetworkConnectionEntity for each client it connects with
        So a server will have as many NCEs as it has connected clients
    Let's now navigate to "ClientWorld0" and find the NCE
Finding the NetworkConnectionEntity (NCE) in ClientWorld0
    We can see the client also has a NCE
    Navigate to the "Multiplayer" menu at the top and select "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.
    We are going to change the "Num Thin Clients" to 3
    Hit play and navigate to the Entity Debugger
    Notice that there are now 4 client worlds (one for each client)
      ClientWorld0
      ClientWorld1
      ClientWorld2
      ClientWorld3
      It is important to note that in an actual deployed project, the server would not have a client world for every client connected
        This behavior is just in the Editor when using PlayMode Tools
    Navigate to ServerWorld and filter for NetworkIdComponent
      Or find the relevant chunk on the right to filter
Adding 3 thin clients and seeing the 4 NCEs in ServerWorld
    We now have 4 NCEs in ServerWorld
      A NCE for every connection with a client
    Click through and see how the NetworkIdComponent starts at 1 and increases by 1 for every client connection
    Now navigate to ClientWorld0 and filter for the NCE
      (You will actually need to type "NetworkIdComponent" to filter for this) or, alternatively, just click on the chunk with the NetworkIdComponent on the right
Checking the NCE in ClientWorld0
    Notice there is only 1 NCE in ClientWorld0 because there is only 1 NCE on the client
      Clients only have a connection to the server; they do not have connections to other clients
      Servers have connections to each and every client
    Because we will be working with NCEs very heavily it is worth taking a harder look at what components and values the inspector shows for an NCE immediately upon connection
Overview of the server NCE
    Change the "Num Thin Clients" back to 0
      No gif here, we believe in you 💪
We now have a client/server socket connection which creates an NCE on both the client and server
    We added the NetCode package to manifest.json
    We created ClientServerConnectionControl

Creating a configurable socket connection

First, some background:

    Now we are going to make things more complicated...
      For good reason!
        We need to prepare for the upcoming "Multiplayer section" where we will have the ability to select whether we join a game as client-only or we host a game and are a client-server
    Right now the server IP address and port are hardwired into our systems
      They are defined in ServerConnectionControl and ClientConnectionControl
    We will now update this so that the IP address and port are provided by GameObjects
    We will be adding four GameObjects to SampleScene
      ClientServerInfo (with ClientServerInfo script)
      ClientServerConnectionHandler (with ClientServerConnectionHandler script)
      ClientLaunchObject (with ClientLaunchObjectData script)
      ServerLaunchObject (with ServerLaunchObjectData script)
    ClientServerInfo
      This is where we will set what port our game should run on
      We will update it with the IP address provided by the ClientLaunchObject
      Think of it as a store of client and server info
    ClientServerConnectionHandler
      Its script will look for GameObjects with "LaunchObject" tags and take data from those GameObjects to create entities, which will trigger our ClientServerConnectionControl
    ClientLaunchObjectData
      This will have a "LaunchObject" tag and store the IP address the client is connecting to
    ServerGameObject
      This will have a "LaunchObject" tag
    We will update our ClientServerConnectionControl to ingest the component data created by ClientServerConnectionHandler
    We will also create these 4 new components:
      ClientDataComponent (will provide the IP address and port to ClientConnectionControl)
      InitializeClientComponent (will trigger ClientConnectionControl to run)
      ServerDataComponent (will provide the port to ServerConnectionControl)
      InitializeServerComponent (will trigger ServerConnectionControl to run)
Overview of our updated socket connection flow
    Note that the "source of truth" of what IP address our client will connect to is on the "ClientLaunchObject"
      This is how we be able to configure what server to connect to from our "Navigation" scene, later on in the Multiplayer section of this gitbook
    Note that the "source of truth" of what port our server listens on and client connects to is in "ClientServerInfo"
      This was an opinionated choice by Moetsi specifically for this gitbook
      We could provide the port in the ServerLaunchObject and ClientLaunchObject as well
      But we decided that the port number will be set "before" runtime (aka baked into your build)

Now, let's implement:

    Right-click on the Hierarchy in SampleScene and create an empty GameObject named ClientServerInfo
    Create a new script named ClientServerInfo
    Paste the code snippet below into ClientServerInfo.cs:
1
using System.Collections;
2
using System.Collections.Generic;
3
using UnityEngine;
4
using Unity.Collections;
5
using System;
6
7
public class ClientServerInfo : MonoBehaviour
8
{
9
public bool IsServer = false;
10
public bool IsClient = false;
11
public string ConnectToServerIp;
12
public ushort GamePort = 5001;
13
14
// Start is called before the first frame update
15
void Start()
16
{
17
18
}
19
20
// Update is called once per frame
21
void Update()
22
{
23
24
}
25
}
Copied!
Creating the ClientServerInfo GameObject and script
    Click "Add Component" in Inspector on ClientServerInfo and add the ClientServerInfo script
    Navigate to SampleScene, right click in the Hierarchy and create an empty GameObject and name it ClientLaunchObject
      Add a tag called "LaunchObject"
        You do this by selecting the drop down menu next to "Tag" in the Inspector when ClientLaunchObject is highlighted in Hierarchy and choosing the last option "Add Tag...". Hit the + button and type in the name of the tag ("LaunchObject" for this one)
    Create a new script called ClientLaunchObjectData
    Paste the code snippet below into ClientLaunchObjectData.cs:
1
using System.Collections;
2
using System.Collections.Generic;
3
using UnityEngine;
4
using System.Net;
5
6
public class ClientLaunchObjectData : MonoBehaviour
7
{
8
public string IPAddress = "127.0.0.1";
9
// Start is called before the first frame update
10
void Start()
11
{
12
13
}
14
15
// Update is called once per frame
16
void Update()
17
{
18
19
}
20
}
21
Copied!
Create ClientLaunchObject GameObject and ClientLaunchObjectData script
    Click "Add Component" in Inspector on ClientLaunchObject and add ClientLaunchObjectData script
    Navigate to SampleScene, right click in the Hierarchy and create an empty GameObject and name it ServerLaunchObject
      add the "LaunchObject" tag (the tag you just made)
    Create a new script called ServerLaunchObjectData
    Paste the code snippet below into ServerLaunchObjectData.cs:
1
using System.Collections;
2
using System.Collections.Generic;
3
using UnityEngine;
4
using System.Net;
5
6
public class ServerLaunchObjectData : MonoBehaviour
7
{
8
// Start is called before the first frame update
9
void Start()
10
{
11
12
}
13
14
// Update is called once per frame
15
void Update()
16
{
17
18
}
19
}
Copied!
    Click "Add Component" in Inspector on ServerLaunchObject and add the ServerLaunchObjectData script
    Right click in the Hierarchy in SampleScene and create an empty GameObject called ClientServerConnectionHandler
    Create a new script called ClientServerConnectionHandler
    Paste the code snippet below into ClientServerConnectionHandler.cs:
1
using System;
2
using System.Collections;
3
using System.Collections.Generic;
4
using UnityEngine;
5
using Unity.Entities;
6
using Unity.NetCode;
7
using UnityEngine.UIElements;
8
using UnityEngine.SceneManagement;
9
10
public class ClientServerConnectionHandler : MonoBehaviour
11
{
12
//this is the store of server/client info
13
public ClientServerInfo ClientServerInfo;
14
15
// these are the launch objects from Navigation scene that tells what to set up
16
private GameObject[] launchObjects;
17
18
void Awake()
19
{
20
launchObjects = GameObject.FindGameObjectsWithTag("LaunchObject");
21
foreach(GameObject launchObject in launchObjects)
22
{
23
//
24
// checks for server launch object
25
// does set up for the server for listening to connections and player scores
26
//
27
if(launchObject.GetComponent<ServerLaunchObjectData>() != null)
28
{
29
//sets the gameobject server data (mono)
30
ClientServerInfo.IsServer = true;
31
32
//sets the component server data in server world(dots)
33
//ClientServerConnectionControl (server) will run in server world
34
//it will pick up this component and use it to listen on the port
35
foreach (var world in World.All)
36
{
37
//we cycle through all the worlds, and if the world has ServerSimulationSystemGroup
38
//we move forward (because that is the server world)
39
if (world.GetExistingSystem<ServerSimulationSystemGroup>() != null)
40
{
41
var ServerDataEntity = world.EntityManager.CreateEntity();
42
world.EntityManager.AddComponentData(ServerDataEntity, new ServerDataComponent
43
{
44
GamePort = ClientServerInfo.GamePort
45
});
46
//create component that allows server initialization to run
47
world.EntityManager.CreateEntity(typeof(InitializeServerComponent));
48
}
49
}
50
}
51
52
//
53
// checks for client launch object
54
// does set up for client for dots and mono
55
//
56
if(launchObject.GetComponent<ClientLaunchObjectData>() != null)
57
{
58
//sets the gameobject data in ClientServerInfo (mono)
59
//sets the gameobject data in ClientServerInfo (mono)
60
ClientServerInfo.IsClient = true;
61
ClientServerInfo.ConnectToServerIp = launchObject.GetComponent<ClientLaunchObjectData>().IPAddress;
62
63
//sets the component client data in server world(dots)
64
//ClientServerConnectionControl (client) will run in client world
65
//it will pick up this component and use it connect to IP and port
66
foreach (var world in World.All)
67
{
68
//we cycle through all the worlds, and if the world has ClientSimulationSystemGroup
69
//we move forward (because that is the client world)
70
if (world.GetExistingSystem<ClientSimulationSystemGroup>() != null)
71
{
72
var ClientDataEntity = world.EntityManager.CreateEntity();
73
world.EntityManager.AddComponentData(ClientDataEntity, new ClientDataComponent
74
{
75
ConnectToServerIp = ClientServerInfo.ConnectToServerIp,
76
GamePort = ClientServerInfo.GamePort
77
});
78
//create component that allows client initialization to run
79
world.EntityManager.CreateEntity(typeof(InitializeClientComponent));
80
}
81
}
82
}
83
}
84
}
85
86
// Start is called before the first frame update
87
void Start()
88
{
89
90
}
91
92
// Update is called once per frame
93
void Update()
94
{
95
96
}
97
}
Copied!
    You will get a 4 errors because ClientServerConnectionHandler is referencing components we haven't created yet, so let's make our 4 additional components
    Create ClientDataComponent and paste in this code snippet:
1
using System;
2
using Unity.Entities;
3
using Unity.Collections;
4
5
public struct ClientDataComponent : IComponentData
6
{
7
//Must used "FixedStringN" instead of string in IComponentData
8
//This is a DOTS requirement because IComponentData must be a struct
9
public FixedString64 ConnectToServerIp;
10
public ushort GamePort;
11
}
Copied!
    InitializeClientComponent
1
using Unity.Entities;
2
3
public struct InitializeClientComponent : IComponentData
4
{
5
}
Copied!
    ServerDataComponent
1
using Unity.Entities;
2
using Unity.Collections;
3
4
5
public struct ServerDataComponent : IComponentData
6
{
7
public ushort GamePort;
8
}
Copied!
    InitializeServerComponent
1
using Unity.Entities;
2
3
public struct InitializeServerComponent : IComponentData
4
{
5
6
}
Copied!
Creating ClientDataComponent, InitializeClientComponent, ServerDataComponent, InitializeServerComponent
    Now we must update ClientServerConnectionControl to use these new components
    This file already exists so just paste over current code with the code snippet below into ClientServerConnectionControl:
1
using Unity.Burst;
2
using Unity.Entities;
3
using Unity.Mathematics;
4
using Unity.Networking.Transport;
5
using Unity.NetCode;
6
using UnityEngine;
7
using Unity;
8
using System;
9
10
#if UNITY_EDITOR
11
using Unity.NetCode.Editor;
12
#endif
13
14
15
//ServerConnectionControl is run in ServerWorld and starts listening on a port
16
//The port is provided by the ServerDataComponent
17
[UpdateInWorld(UpdateInWorld.TargetWorld.Server)]
18
public class ServerConnectionControl : SystemBase
19
{
20
private ushort m_GamePort;
21
22
protected override void OnCreate()
23
{
24
// We require the InitializeServerComponent to be created before OnUpdate runs
25
RequireSingletonForUpdate<InitializeServerComponent>();
26
27
}
28
29
protected override void OnUpdate()
30
{
31
//load up data to be used OnUpdate
32
var serverDataEntity = GetSingletonEntity<ServerDataComponent>();
33
var serverData = EntityManager.GetComponentData<ServerDataComponent>(serverDataEntity);
34
m_GamePort = serverData.GamePort;
35
36
//We destroy the InitializeServerComponent so this system only runs once
37
EntityManager.DestroyEntity(GetSingletonEntity<InitializeServerComponent>());
38
39
// This is used to split up the game's "world" into sections ("tiles")
40
// The client is in a "tile" and networked objects are in "tiles"
41
// the client is streamed data based on tiles that are near them
42
//https://docs.unity3d.com/Packages/[email protected]/manual/ghost-snapshots.html
43
//check out "Distance based importance" in the link above
44
var grid = EntityManager.CreateEntity();
45
EntityManager.AddComponentData(grid, new GhostDistanceImportance
46
{
47
ScaleImportanceByDistance = GhostDistanceImportance.DefaultScaleFunctionPointer,
48
TileSize = new int3(80, 80, 80),
49
TileCenter = new int3(0, 0, 0),
50
TileBorderWidth = new float3(1f, 1f, 1f)
51
});
52
53
//Here is where the server creates a port and listens
54
NetworkEndPoint ep = NetworkEndPoint.AnyIpv4;
55
ep.Port = m_GamePort;
56
World.GetExistingSystem<NetworkStreamReceiveSystem>().Listen(ep);
57
Debug.Log("Server is listening on port: " + m_GamePort.ToString());
58
}
59
}
60
61
//ClientConnectionControl is run in ClientWorld and connects to an IP address and port
62
//The IP address and port is provided by the ClientDataComponent
63
[UpdateInWorld(UpdateInWorld.TargetWorld.Client)]
64
public class ClientConnectionControl : SystemBase
65
{
66
private string m_ConnectToServerIp;
67
private ushort m_GamePort;
68
69
protected override void OnCreate()
70
{
71
// We require the component to be created before OnUpdate runs
72
RequireSingletonForUpdate<InitializeClientComponent>();
73
74
}
75
76
protected override void OnUpdate()
77
{
78
//load up data to be used OnUpdate
79
var clientDataEntity = GetSingletonEntity<ClientDataComponent>();
80
var clientData = EntityManager.GetComponentData<ClientDataComponent>(clientDataEntity);
81
82
m_ConnectToServerIp = clientData.ConnectToServerIp.ToString();
83
m_GamePort = clientData.GamePort;
84
85
// As soon as this runs, the component is destroyed so it doesn't happen twice
86
EntityManager.DestroyEntity(GetSingletonEntity<InitializeClientComponent>());
87
88
NetworkEndPoint ep = NetworkEndPoint.Parse(m_ConnectToServerIp, m_GamePort);
89
World.GetExistingSystem<NetworkStreamReceiveSystem>().Connect(ep);
90
Debug.Log("Client connecting to ip: " + m_ConnectToServerIp + " and port: " + m_GamePort.ToString());
91
}
92
}
Copied!
Updating ClientServerConnectionControl
    Finally let's add ClientServerInfo as the reference in ClientServerConnectionHandler (drag the ClientServerInfo GameObject into the Client Server Info field in the Client Server Connection Handler in Inspector)
    Hit play
    Our client and server make a connection and update ClientServerInfo
    Finally, let's do some housekeeping and create a new folder called "Multiplayer Setup" in Scripts and Prefabs
    Drag the following files into "Multiplayer Setup" folder:
      ClientServerInfo
      ClientLaunchObjectData
      ServerLaunchObjectData
      ClientServerConnectionHandler
      ClientDataComponent
      InitializeClientComponent
      ServerDataComponent
      InitializeServerComponent
      ClientServerConnectionControl
    No gif here, we believe in you 💪
We can now trigger a client server connection through GameObjects with LaunchObject tags
    We created 4 new GameObjects in the scene
      ClientServerInfo
      ClientLaunchObject
      ServerLaunchObject
      ClientServerConnectionHandler
    We created 4 new scripts
      ClientServerInfo
      ClientLaunchObjectData
      ServerLaunchObjectData
      ClientServerConnectionHandler
    We created 4 new components
      ClientDataComponent
      InitializeClientComponent
      ServerDataComponent
      InitializeServerComponent
Github branch link:
git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/ git checkout 'Creating-a-Socket-Connection'
Last modified 7mo ago