Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Question Help me understand thin client inputs please

Discussion in 'NetCode for ECS' started by Yoraiz0r, Jul 11, 2023.

  1. Yoraiz0r

    Yoraiz0r

    Joined:
    Apr 26, 2015
    Posts:
    91
    So for the past few hours I've been pouring over ECS Netcode 1.0 documentation, and I'm struggling to implement thin client input simulations.

    The Networked Cube sample recommends you use CubeInput : IInputComponentData, but looking at any existing samples - this does not seem to be usable for thin clients.

    My current understanding is that to simulate inputs on a thin client, I must:
    1. Create a dummy input entity
    2. Give it an input buffer of my input typer
    3. give it a GhostOwner component filled with NetworkId's singleton's value
    4. Make the CommandTarget singleton point at it

    Specifically, the examples in Megacity Multiplayer, and Netcode Samples' Asteroids, both hint that I should add a buffer of inputs to do this...
    But IInputComponentData is not a buffer element...

    In the Asteroids sample, ShipCommandData extends ICommandData, which is a buffer element, however to my understanding this is an outdated sample simply patched for 1.0.0 and written originally for 0.50?

    In the MegaCity sample, the PlayerVehicleInput is an IInputCommandData as expected... but then there's this suspiciously commented out line in the thin input system.
    Code (CSharp):
    1.  
    2.                 // NOTE: The buffer type might not be recognized in your IDE but it will be generated and Unity will recognize it.
    3.                 // TODO : verify this buffer
    4.                 //state.EntityManager.AddBuffer<Unity.MegaCity.Gameplay.Generated.PlayerVehicleInputInputBufferData>(inputEntity);
    I've tried to replicate MegaCity's thin client input system 1-to-1 code wise, simply using my own cube input component data instead, without the commented out line... and it simply doesn't work. Trying to point to whatever generated component could exist for my input fails as well though it might be the IDE's fault and my failure at guessing the proper path.

    I have checked the entities hierarchy and I can confirm that the system does run and I do have a thin client input entity. But there is no input buffer, the server doesn't seem to receive any inputs from it, and holds an empty input buffer.


    It is frustrating that the documentation does not cover thin clients to any usable degree, and that the code involved in getting them working has to be found on other samples. I am failing to grasp where is the process going wrong.

    From what I can see in the Entities Hierarchy, the thin client does not spawn any ghosts, so there is no entity which is tracked by default, despite the real client properly witnessing said thin client, indicating the server did make a successful connection with it and gave it a player...

    Does anyone please have any pointers? What would be required in order to get a thin client working on the networked cube tutorial?
     
  2. CMarastoni

    CMarastoni

    Unity Technologies

    Joined:
    Mar 18, 2020
    Posts:
    929
    This is being covered in the forum multiple time, and I apologies for the scarcity of info in the docs.

    First: Thin client does not work out of the box with AutoCommandTarget. This is also mentioned in the manual .That because it requires on both client and server the ghost to exist in order to work. But Thin clients does not create ghost. So you need to setup the CommandTarget component on the connection entity.

    Second: IInputComponentData are special. As you said, when we bake the entity (ghost) we automatically add an underlying ICommand dynamic buffer to the entity.

    So in order to make this work for thin client you need:

    1) Create an entity
    2) Add your IInputCommmand component to it
    3) (Unfortunate we can't do that better right now) Add the auto-generated Namespace.YouCommandNameInputBufferData dynamic buffer to the entity (see for example the HelloNetocode_ThinClient sample)
    4) You need to setup the CommandTarget component on both client and server to point to the right entity. So in your client code you need to

    Code (csharp):
    1.  
    2. var ent = entityManager.CreateEntity();
    3. entityManager.AddComponent<MyNamespace.MyInputComponent>();
    4. entityManager.AddBuffer<MyNamespace.Generated.MyInputComponentInputBufferData>();
    5. var connection = SystemAPI.GetSingletonEntity<NetworkId>();
    6. entityManager.SetComponentData(connection, new CommandTarget { targetEntity = ent });
    7.  
    On the server, where you spawn the entity for the player you just need to setup as well the command target, like

    Code (csharp):
    1.  
    2. entityManager.SetComponentData(playerConnection, new CommandTarget { targetEntity = ent });
    3.  
     
    Yoraiz0r likes this.
  3. NikiWalker

    NikiWalker

    Unity Technologies

    Joined:
    May 18, 2021
    Posts:
    329
    I'll modify the docs to include this info, thanks both!
     
    Yoraiz0r likes this.
  4. Yoraiz0r

    Yoraiz0r

    Joined:
    Apr 26, 2015
    Posts:
    91
    Thanks for explaining the server also has to set the command target, I believe this is the only part I was missing after more experiments last night.

    I managed to get the generated class IInputComponentData found by adding an assembly definition for my own code (with way too many assembly references just to get ECS working with it) and then adding `
    MyCode.Generated.CubeInputInputBufferData` buffer. Though the IDE doesn't recognize it and now permanently tells me I have an error, the code does compile on Unity's side.

    For the server, to get the specific player's connection, checking the MegaCity multiplayer demo, the code is quite significantly different. It appears that MegaCity checks for connections without NetworkStreamInGame and then spawns them in, as opposed to the explicit request from the player in the networked cube sample. The code that I ended up adapting for the networked cube sample is
    Code (CSharp):
    1. commandBuffer.SetComponent(reqSrc.ValueRO.SourceConnection, new CommandTarget { targetEntity = player });
    After including both the buffer and the commandtarget on the server, I can confirm this works.

    That said, I have to ask whether it would be better to just use ICommandData explicitly, instead? I want to verify my understanding... the purpose IInputComponentData is for convenience with prediction features, as the value is always made to fit the generated buffer matching the frame, is that correct? how would code using CommandData work for prediction reading instead?

    Also, if a connection can only have one command target set explicitly, I assume this design is done so that all player input goes through a single entity? so in a case of me wanting to run multiple entities for a single actual player, I would have one input entity with all their inputs and the other entities just point to that input entity to read its components?

    I'm asking this because I intended to have 3 characters controlled by every player and assumed they would have separate components storing their intended movement data, but if I can only set one command target entity I would have to change how the code is constructed, so I want to make sure I properly understand please. Is there any way to achieve multiple input entities per connection?

    Edit: with thin clients working, I added a button to spawn thin clients on the fly by creating thin client worlds, however, every time I do so, I get this warning:
    Code (CSharp):
    1. Ignoring invalid [Unity.Entities.UpdateBeforeAttribute] attribute on Unity.NetCode.GhostSimulationSystemGroup targeting Unity.NetCode.PredictedSimulationSystemGroup.
    2. This attribute can only order systems that are members of the same ComponentSystemGroup instance.
    3. Make sure that both systems are in the same system group with [UpdateInGroup(typeof(Unity.Entities.SimulationSystemGroup))],
    4. or by manually adding both systems to the same group's update list.
    This only occurs when spawning thin client worlds. Spawning more regular client worlds does not cause this warning. The only two systems of mine that have ThinClient world system filters are "GoInGameClientSystem" as per instructed by the networked cube sample, and "ThinClientCubeInputSystem". The latter uses UpdateInGroup(GhostInputSystemGroup)... my point being that I don't have any UpdateBefore attributes in use at all. What is going on here? how do I get rid of this erronoous warning?
     
    Last edited: Jul 11, 2023
  5. CMarastoni

    CMarastoni

    Unity Technologies

    Joined:
    Mar 18, 2020
    Posts:
    929
    The IInputComponentData is a perfect fit when you control one character and yes, it is a convenience to write code more easily, Indeed you can just use the underlying buffer yourself.

    For ICommandData, the documentation in that sense tell you about how to use it here:
    https://docs.unity3d.com/Packages/com.unity.netcode@1.0/manual/command-stream.html

    In particular, sorry for sparseness,
    You just need to get the command buffer and use the extension method (that is what the generated system for IInputComponentData does).

    Yes, that is a valid solution. Just have one entity (potentially the main character) that hold the commands (or can be even the connection itself) and in the prediction loop you can just read the command for the tick you need and set the input on the controlled entities.

    If the commands are all identical for all the target this work. If you need to have slightly different commands for each controller entity, then a solution may be to embed in the same command data the input for each controller entity (and the common just shared) like:

    Code (csharp):
    1.  
    2. struct
    3. {.
    4.    CommonInput input;
    5.    Character1_Input input1;
    6.    Character2_Input input2;
    7.    Character3_Input input3;
    8. }
    9.  
    Or similar patterns. You can also use a FixedList<XX> for that but then you need to write you own custom serialiser for the command.

    A more flexible, and cleaner approach in a way, is to have multiple command buffers on the entity, one for each input type. The are all sent together (they should fit into one MTU, considering also the 3 input redundancy for each input type).

    [/quote]
    I'm asking this because I intended to have 3 characters controlled by every player and assumed they would have separate components storing their intended movement data, but if I can only set one command target entity I would have to change how the code is constructed, so I want to make sure I properly understand please. Is there any way to achieve multiple input entities per connection?
    [/quote]
    If we exclude Thin clients for a second, by using AutoCommandTarget it is possible to use multiple input per connection without problem.
    Without using the AutoCommandTarget feature (so thin client) a way to achieve the same dispatch mechanics is to replicate it.

    You can write a system that run before the PredictedSimulationSystemGroup (i.e inside the GhostSimulationSystemGroup), and after CommandReceiveSystem, and that just copy the buffer content from the main character to the other controlled ones.

    That would result in the exact same result: each character has now their own command in their buffers.
     
    Yoraiz0r likes this.
  6. Deleted User

    Deleted User

    Guest

    Any resolution to this? I'm encountering this exact "bug", but in my case it appears the bug only exists on creation and still functions identically...
     
    Yoraiz0r likes this.
  7. CMarastoni

    CMarastoni

    Unity Technologies

    Joined:
    Mar 18, 2020
    Posts:
    929
    We made some recent changes to fix that but will be available in a new package update. It will n.t cause any issue in general.
    As a temporary workaroud, removing the [UpdateBefore(PredictedSimulationSystemGroup)] on the GhostSimulationSystemGrop and adding instead a [UpdateAfter(GhostSimulationSystemGroup)] on the PredictedSimulationSystemGroup may fix this.
     
    Yoraiz0r and optimise like this.
  8. optimise

    optimise

    Joined:
    Jan 22, 2014
    Posts:
    2,129
    Btw have u also move auto generated xxxCopyImputToCommandBufferSystem from GhostInputSystemGroup to another new system group so it's possible to add systems in between xxx system and xxxCopyImputToCommandBufferSystem to do something? It's the feature I requested long time ago.
     
    Yoraiz0r likes this.
  9. CMarastoni

    CMarastoni

    Unity Technologies

    Joined:
    Mar 18, 2020
    Posts:
    929
    Yes, this has been done. It ill be present in the Entities 1.1. We added a group that contains all the XXXCopyToXXX
     
    optimise and Yoraiz0r like this.
  10. optimise

    optimise

    Joined:
    Jan 22, 2014
    Posts:
    2,129
    Curious is that 1.1 schedule to next release or it's still small 1.0.x patch release?
     
  11. CMarastoni

    CMarastoni

    Unity Technologies

    Joined:
    Mar 18, 2020
    Posts:
    929
    Some details about that will come. Keep an eye on the ECS channel for updates. It may be we can also have this release in a patch release too, but it is unlikely.
     
    optimise likes this.