Unity DOTS NetCode getting started issues

Edit: Found out the reason was old openvr version, if somebody has issue download latest steamvr from their github. Releases · ValveSoftware/steamvr_unity_plugin · GitHub

Hi, I started a NetCode project and run into some issues.

So I did this tutorial and started receiving errors.

Also, if I do a build and try to start it when a client server setup is running in editor, then the just opened build crashes the editor.

How can I get rid of these errors and how can I test that the netcode works by making a build and connecting it to a server?
I’m on NetCode version 0.5 and latest Unity.

Clearly the Getting started has been working at some point as seen in this video.

Would be nice to know, if someone can confirm.

1 Like

@mikaelK the “Getting started” in the documentation has been out of date since 0.3.

I posted the same question to @timjohansson [here]( DOTS Multiplayer discussion page-5#post-6554188)

When did the error start occurring, when you added the cube? Try following the “get started” and hitting “play” after you make the client/server connection and see if the error still appears.

This might have something to do with it: [Netcode] prediction jitter

also reported here: Issues with netcode 0.5

@mikaelK it seems like this might have been the issue: Bug: NetCode 0.5.0-preview.5: Large serverTick prediction error. Server tick rollback to # delta.

@mikaelK

For this part of the tutorial:

The new NetCode 0.5 has this in the change log:

So change [UpdateInGroup(typeof(ClientSimulationSystemGroup))] to [UpdateInGroup(typeof(GhostInputSystemGroup))] in SampleCubeInput and I think it might fix it.

@adammpolak Thanks for the good help.I don’t know why it still doesn’t work. Here is the code, but hard to say what is the issue since I tried removing code from both of the classes and the issues still persists

namespace Game.Netcode
{
    [UpdateInGroup(typeof(GhostInputSystemGroup))]
    public class SamplePlayerInput : ComponentSystem
    {
        protected override void OnCreate()
        {
            RequireSingletonForUpdate<NetworkIdComponent>();
        }

        protected override void OnUpdate()
        {
            var localInput = GetSingleton<CommandTargetComponent>().targetEntity;
            if (localInput == Entity.Null)
            {
                var localPlayerId = GetSingleton<NetworkIdComponent>().Value;
                Entities.WithAll<MovePlayerComponent>().WithNone<PlayerInput>().ForEach((Entity ent, ref GhostOwnerComponent ghostOwner) =>
                {
                    if (ghostOwner.NetworkId == localPlayerId)
                    {
                        PostUpdateCommands.AddBuffer<PlayerInput>(ent);
                        PostUpdateCommands.SetComponent(GetSingletonEntity<CommandTargetComponent>(), new CommandTargetComponent {targetEntity = ent});
                    }
                });
                return;
            }
            var input = default(PlayerInput);
            input.Tick = World.GetExistingSystem<ClientSimulationSystemGroup>().ServerTick;
            if (Input.GetKey("a"))
                input.horizontal -= 1;
            if (Input.GetKey("d"))
                input.horizontal += 1;
            if (Input.GetKey("s"))
                input.vertical -= 1;
            if (Input.GetKey("w"))
                input.vertical += 1;
            var inputBuffer = EntityManager.GetBuffer<PlayerInput>(localInput);
            inputBuffer.AddCommandData(input);
        }
    }
}
namespace Game.NetCode
{
    [UpdateInGroup(typeof(GhostPredictionSystemGroup))]
    public class MoveCubeSystem : ComponentSystem
    {
        protected override void OnCreate()
        {
            RequireSingletonForUpdate<NetworkIdComponent>();
        }

        protected override void OnUpdate()
        {
         
            var group = World.GetExistingSystem<GhostPredictionSystemGroup>();
            var tick = group.PredictingTick;
            var deltaTime = Time.DeltaTime;
            Entities.ForEach((DynamicBuffer<PlayerInput> inputBuffer, ref Translation trans, ref MovePlayerComponent mp, ref PredictedGhostComponent prediction) =>
            {
                if (!GhostPredictionSystemGroup.ShouldPredict(tick, prediction))
                {
                    return;
                }

                PlayerInput input;
                inputBuffer.GetDataAtTick(tick, out input);
                if (input.horizontal > 0)
                {
                    trans.Value.x += deltaTime;
                }
                if (input.horizontal < 0)
                {
                    trans.Value.x -= deltaTime;
                }
                if (input.vertical > 0)
                {
                    trans.Value.z += deltaTime;
                }
                if (input.vertical < 0)
                {
                    trans.Value.z -= deltaTime;
                }
            });
         
        }
    }
}

@adammpolak On the client world the tick and prediction tick gets updated all the time, but not in the server world.


Image of the server world.

Image of clöient world showing that tick values have changed.

ok there is something weird. There is 2 entities with player component. On client side 1 entity is updating and on server side both entities that has that component are not updating. I don’t think this should be happening

@mikaelK

What are your settings on the multiplayer mode tools (just in case)?

It looks like the client and the server and not sending data back and forth.

Can you send screenshots of your entity debugger and what systems are running on the client/server? (include inactive systems)

Both the client and the server need to have “NetworkStreamInGame” on the Network Connection to send snapshot data:

This occurs in this section of the Getting Started Page:

Can you check the Network Connection Entity on the client and server and see if these components are on it?

EDIT: From this post: Bug: NetCode 0.5.0-preview.5: Large serverTick prediction error. Server tick rollback to # delta. it seems like you have not sent back the “go in game” from the client to the server. This means the server isn’t going through the motions it needs to.

@adammpolak Here, it looks like they are present.


Server

mp tools window


@adammpolak I also add the code for adding these comonents.
GoInGameClientSystem

// When client has a connection with network id, go in game and tell server to also go in game

using Unity.Entities;
using Unity.NetCode;
using UnityEngine;

namespace Game.NetCode.Client
{
    [UpdateInGroup(typeof(ClientSimulationSystemGroup))]
    [DisableAutoCreation]
    [AlwaysUpdateSystem]
    public class GoInGameClientSystem : ComponentSystem
    {
        private bool multiPlayerGameStarted = false;

        public bool MultiPlayerGameStarted
        {
            get { return multiPlayerGameStarted; }
            set { multiPlayerGameStarted = value; }
        }

        protected override void OnCreate()
        {
          
        }

        protected override void OnUpdate()
        {
            Entities.WithNone<NetworkStreamInGame>().ForEach((Entity ent, ref NetworkIdComponent id) =>
            {
                PostUpdateCommands.AddComponent<NetworkStreamInGame>(ent);
                var req = PostUpdateCommands.CreateEntity();

                PostUpdateCommands.AddComponent<GoInGameCommandRequest>(req);

                PostUpdateCommands.AddComponent(req, new SendRpcCommandRequestComponent {TargetConnection = ent});
            });
        }
    }
}

Go in game server system.

// When server receives go in game request, go in game and delete request

using System;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine;

namespace Game.NetCode.Server
{
    [UpdateInGroup(typeof(ServerSimulationSystemGroup))]
  
    public class GoInGameServerSystem : ComponentSystem
    {
        private bool multiPlayerGameStarted = false;

        public bool MultiPlayerGameStarted
        {
            get { return multiPlayerGameStarted; }
            set { multiPlayerGameStarted = value; }
        }

        protected override void OnUpdate()
        {
            HandleIncomingGoInGameRequests();
        }

        private void HandleIncomingGoInGameRequests()
        {
            Entities.WithNone<SendRpcCommandRequestComponent>().ForEach(
                (Entity reqEnt, ref GoInGameCommandRequest req, ref ReceiveRpcCommandRequestComponent reqSrc) =>
                {
                    PostUpdateCommands.AddComponent<NetworkStreamInGame>(reqSrc.SourceConnection);
                    UnityEngine.Debug.Log(String.Format("Server setting connection {0} to in game",
                        EntityManager.GetComponentData<NetworkIdComponent>(reqSrc.SourceConnection).Value));
                    var ghostCollection = Entity.Null;
                    ghostCollection = GetSingletonEntity<GhostPrefabCollectionComponent>();
                    var prefab = Entity.Null;
                    var prefabs = EntityManager.GetBuffer<GhostPrefabBuffer>(ghostCollection);
                    for (int ghostId = 0; ghostId < prefabs.Length; ++ghostId)
                    {
                        if (EntityManager.HasComponent<MovePlayerComponent>(prefabs[ghostId].Value))
                        {
                            prefab = prefabs[ghostId].Value;
                        }
                    }

                    var player = EntityManager.Instantiate(prefab);
                    EntityManager.SetComponentData(player,
                        new GhostOwnerComponent
                        {
                            NetworkId = EntityManager.GetComponentData<NetworkIdComponent>(reqSrc.SourceConnection)
                                .Value
                        });
                    PostUpdateCommands.AddBuffer<PlayerInput>(player);

                    PostUpdateCommands.SetComponent(reqSrc.SourceConnection,
                        new CommandTargetComponent {targetEntity = player});

                    PostUpdateCommands.DestroyEntity(reqEnt);
                });
        }
    }
}

I have added this to a button so the mp game can be started on request. Here is code for it from game manager class.

        public void StartMultiPlayer()
        {

            foreach (var world in World.All)
            {
                if (world.Name== "ServerWorld")
                {
                    App.View.ViewElements.StatsWindowText.text += "found server world" + "\n";
                    ServerWorld = world;
                }
                if (world.Name== "ClientWorld0")
                {
                    App.View.ViewElements.StatsWindowText.text += "found client world" + "\n";
                    ClientWorld = world;
                }
            }
            serverStart = World.DefaultGameObjectInjectionWorld.GetExistingSystem<GameInitConnection>();
            serverStart.MultiPlayerGameStarted = true;
            serverStart.Update();
            server = ServerWorld.GetExistingSystem<GoInGameServerSystem>();
            server.MultiPlayerGameStarted = true;
            server.Update();
            client = ClientWorld.GetOrCreateSystem<GoInGameClientSystem>();
            client.MultiPlayerGameStarted = true;
            client.Update();

            GetComponent<LoadNetCodeSystemAndSubScene>().NetWorkScene.gameObject.GetComponent<SubScene>().enabled = true;
            var playerCount = ServerWorld.GetOrCreateSystem<NetworkGameInformationRelay>().GetPlayerCount();
            App.View.ViewElements.StatsWindowText.text += "Player count: " + playerCount + "\n";
        }

Also I tried using it like in the getting started manual. Without button and the same issue still persists.

@adammpolak Maybe this is related to the subscene based loading. That is required. Its not documented, but when I did the getting started I got message to put the ghost collection inside subscene.
edit. I mean maybe separating the ghost collection group in to a subscene somehow breaks the connection between client and server system stream components(send and receive).

Does you game.cs file have a IRpcCommand in bottom?, the [Bust…] is missing on example
5.0 example works for me

[BurstCompiler]
public struct GoInGameRequest : IRpcCommand
{
}

1 Like

Hi and thanks for reply. I do have that on a separate file. Good to know you got it working. On what Unity version are you working on?

I made empty project and redid the getting started and its now working. Thanks for all the help.
I’ll try to move the other project content to the new. Weird there is no real difference other than the other has vr device added to the project.

2 Likes

@mikaelK nice! Let me know if you need any pointers on Subscenes.

It is not much more than create a Subscene in the Scene Hierarchy. Put all the gameobjects in the Subscene.

Anything in the Subscene gets automatically converted to entities.

1 Like

@adammpolak Thanks for the tips!
I actually found out that source for the prediction errors is open-vr. Since the game I’m developing is a virtual reality game, I unfortunately cannot use the netcode. I submitted a bug report about this.
http://fogbugz.unity3d.com/default.asp?1296576_3jh7gb2umqtmcagc

Anyways thanks for all the help for everyone involved!

Here is the link to new thread.

@mikaelK it looks like we have an answer: DOTS Multiplayer discussion page-5#post-6600661

@adammpolak Thanks a lot!
I answered and made a video to prove I’m not making things up.
I’ll post it here too.