Search Unity

Feedback [Netcode] Make GhostOwnerComponent also a SharedComponent

Discussion in 'NetCode for ECS' started by UsmanMemon, Oct 28, 2020.

  1. UsmanMemon

    UsmanMemon

    Joined:
    Jan 24, 2020
    Posts:
    87
    We need (atleast I need) GhostOwnerComponent as a SharedComponent so we can avoid iterating over other players ghosts like so

    Code (CSharp):
    1. var localPlayerId = GetSingleton<NetworkIdComponent>().Value;
    2.             Entities.WithSharedComponentFilter(new GhostOwnerComponentShared{NetworkId = localPlayerId}).WithAll<MovableCubeComponent>().WithNone<CubeInput>().ForEach((Entity ent) =>
    3.             {
    4.                 enttCmdBuffer.AddBuffer<CubeInput>(ent);
    5.                 enttCmdBuffer.SetComponent(GetSingletonEntity<CommandTargetComponent>(), new CommandTargetComponent {targetEntity = ent});
    6.             }).WithoutBurst().Run();
    instead of this below
    Code (CSharp):
    1. var localPlayerId = GetSingleton<NetworkIdComponent>().Value;
    2.             Entities.WithAll<MovableCubeComponent>().WithNone<CubeInput>().ForEach((Entity ent,  GhostOwnerComponent ghostOwner) =>
    3.             {
    4.                 if (ghostOwner.NetworkId == localPlayerId)
    5.                 {
    6.                     enttCmdBuffer.AddBuffer<CubeInput>(ent);
    7.                     enttCmdBuffer.SetComponent(GetSingletonEntity<CommandTargetComponent>(), new CommandTargetComponent {targetEntity = ent});
    8.                 }
    9.             }).WithoutBurst().Run();
     
  2. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    992
    I did not dive into net code yet but I fell like having the networkid, which I understand identify each connected client individually, as a shared component would bring lots of chunk fragmentation.
    I'm not sure it would not bring more issues than performance gain as my understanding is that burst compiled iteration with proper early out cost near to nothing. (although I see your code is not bursted)

    Did you perform some performance test in a meaningfull scenario to come to the conclusion that sharedcomponent filtering is better than value checking ?
     
  3. UsmanMemon

    UsmanMemon

    Joined:
    Jan 24, 2020
    Posts:
    87
    No, i did not perform any tests but i feel like this separation is must. In RTS games there are thousands of units and value checking units of other clients feels like a huge performance waste and performance issue for server will be alot alot more as it can have alot of connections. So according to this scenario i feel like GhostOwner SharedComponent should exist or to avoid shared component, GhostOwner ChunkComponent should exist(for this Chunks.ForEach will be handy to use).
     
  4. UsmanMemon

    UsmanMemon

    Joined:
    Jan 24, 2020
    Posts:
    87
    And I think that Chunks fragmentation is better than Entities fragmentation.
     
  5. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    992
    I may be miss conceiving things here but don't the
    var localPlayerId = GetSingleton<NetworkIdComponent>().Value;
    means you are on the client side here ? otherwise you would have more than one NetworkIdComponent, no ?

    And should you not send only the input data to the server and move your entities on server side to have an authrotive server ? In the case of an RTS, I would first send the selection box to the server and it would populate a buffer of selected unit on my player entity (doing the check to make sure the entites in hte box belong to my player, that probably be done through collision layers if you don't have to many teams/player per game), synching that buffer to the client allow to highligth the selected units on screen.
    Then send a input 'command' to the server that would propagate it to all selected entities. If it's a move command the system in charge of that would move all unit from the selected entity buffer not caring about whom they belong to because the population of the selecte unit buffer already guaranted that they belong to the client sending the input.

    Selecting unit should not be a extremly frequent action even for a pro RTS gamer with high apm (200/300 apm not all of which are selecting units), so the filtering of selected unit is not so performace critical and then the code does does not care about unit filtering so it seem better not to fragment them by owner.
     
  6. UsmanMemon

    UsmanMemon

    Joined:
    Jan 24, 2020
    Posts:
    87
    About NetworkIdComponent, You are right about that. I just followed the tutorial. I am learning netcode.

    you can send just input data to server and wait for results or you can predict whats the result of data gonna be and apply it immediately since you have data from other clients as well and validate once the result arrives. You can also be less authoritive if you want.

    In normal MB you would normally keep lists of player gameobjects and enemy gameobjects separately and if there are 1000 player units and 9000 enemy units and you want to kill your units you will loop over player units list and you will save 9000 iterations.

    I realized that they shouldn't make ghostOwnerComponent a shared component, Yes there will be useless fragmentation. I should wait for shared components support and manually divide specific entities based on my requirements. You are right but separation might come in handy, It should give performance in some cases and this separation might help in game design.

    Note : I am learning networking
     
  7. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    992
  8. UsmanMemon

    UsmanMemon

    Joined:
    Jan 24, 2020
    Posts:
    87
    what if there are multiple enemy AIs? then I would need to create multiple tag components by hand. Shared-Component is like a normal component but its type changes depending on the value. You can even replace all normal components with shared components(if there aren't any limits by unity) like so

    NomalComponent >>
    Code (CSharp):
    1. public struct ArcherTag : IComponentData
    2. {
    3.  
    4. }
    and
    Code (CSharp):
    1. public struct EnemyTag : IComponentData
    2. {
    3.  
    4. }
    Equivalent SharedComponents >>
    Code (CSharp):
    1. public struct UniversalComponent : ISharedComponentData
    2. {
    3.     public string Name;
    4. }
    5.  
    6. public class MyClass
    7. {
    8.      void CreateComponents()
    9.      {
    10.            var archerTag = new UniversalComponent{Name = "ArcherTag"};
    11.            var enemyTag = new UniversalComponent{Name = "EnemyTag"};
    12.       }
    13. }
     
  9. UsmanMemon

    UsmanMemon

    Joined:
    Jan 24, 2020
    Posts:
    87
    SharedComponents aren't Scary if used correctly.
     
  10. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    992
    I'm not scared of shared component they are probably very usefull is some situation, I just did not find a use for them right now.

    Your exemple is poorly choosen IMO as the Archer and Enemy define 2 diferent peieces of data. One being the 'class' of the unit the other being does is it 'Friendly'. You could have a firendly archer or a frendly something else likewise with the enemy archer and enemy something else.
    Your archer could probably behave diferrently than your mage let's say. They can share the same king of data (movement/attack speed, range, damage,...) but they could behave differently, for instnace archer target the weakest enemy and mage taget the moste tighthly packed enemy (they do AOE ;) ) It makes more sense to me to make a tag component an handle their behavior in 2 separate system.

    A benefit of shared component is that is reduce memory footprint as the shared component data is 'stored' per chunk instead of per entity. So I would keep those kind of component of larger data structure that are common to lots of entities like mesh, material as said in hte shared component documentation( also colider and nav agent maybe ? ) That means probably not something so specific to gameplay.
    Also I think it need to make sense to be able to iterate over this specific set of entity for the particular reason that it has said shared data.
     
  11. UsmanMemon

    UsmanMemon

    Joined:
    Jan 24, 2020
    Posts:
    87
    I don't know what i was talking about here but aren't chunks always fragmented.
    I believe they are.
     
  12. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    992
    Chunks are 'fragmented' by archetypes (set of component type) you may have multiple chunks for the same archetype if the number of entity of that archetype exceed the size of a chunk.
    You'll fit more entity with few component in a chunk than entities with lot of components or 'big' component (the number of component is not the concern here but rather the sum of the size of each type of components).