Search Unity

Feedback Networked Entity Ids

Discussion in 'Entity Component System' started by JesOb, Apr 19, 2019.

  1. JesOb

    JesOb

    Joined:
    Sep 3, 2012
    Posts:
    1,109
    Allow Set Entity manager to allocate entities from certain Id e.g. 1000, reserve and leave 0-999 for manual allocation.

    So we can manually allocate ids for networking and other needs and will have synchronized ids between client and server.
     
  2. Opeth001

    Opeth001

    Joined:
    Jan 28, 2017
    Posts:
    1,117
    I think it's easier to add a uint id to specific ComponentData eg: playerId, destructableId ....
    It can be even more structured and better for sync.
    In my case I'm using bytes as ids, knowing my game can't contain more than 256 players, each region can't contain more than 256 destructable objects , my map can't contain more than 256 regions.
    So sending a list of players to sync or maybe sync a list of destroyed objects after a disconnection or going from a region to another, will save 75% of bandwidth.
     
  3. JesOb

    JesOb

    Joined:
    Sep 3, 2012
    Posts:
    1,109
    I have actually use EntityIds for network, and send exactly 1 byte over network for each entity. Some thing like your suggestion. But I have not additional field for it and I can access it by direct id no indirection through some id map.

    How you actually address you custom id to EntityId map?
    Say you have netmsg with some data for id 8. It is you internal id. how you find exact entity to access?
     
  4. Opeth001

    Opeth001

    Joined:
    Jan 28, 2017
    Posts:
    1,117
    I think ECS Principles are against this Logic of doing things.
    My Systems are not looking for specific entities based on their ids but iterating over concerned chunks and operating on concerned Entities.
    using ECS means systems should think big and always operate on Groups of entities instead of single entity like the old OOP systems.
     
  5. JesOb

    JesOb

    Joined:
    Sep 3, 2012
    Posts:
    1,109
    OK. I agree.
    But I dont understand simple thing about network object syncing.

    Please set me to the right way.

    How you pass data that came from net to target entity?
    How your system know that some entities and chunk is concerned?
     
  6. Opeth001

    Opeth001

    Joined:
    Jan 28, 2017
    Posts:
    1,117
    Systems logic can be totally different depending on your game specifications.
    in my case im always regrouping entities that i need to operate.

    eg of syncing Destructables :

    knowing my Destructables are Static entities, i'm dividing my Environement into multiple Little regions using SCD (SharedComponentData) depending on their positions. in that way my chunks are already packed and can be Fetched & Filtred Quickly.

    let's say the server is sending a list of destroyed objects into specific regions:
    in server side when a user go into a region i can Quickly get the concerned Destructables and send him the needed Informations.
    in client side the User will not look for all the Destructables but Quickly iterate over those specific to the region and then Operates on them using the list of DestructableIds. knowing my Lists of DestructableIds and Entities are Completely linear into the memory so iterating them is a quick operation.
    knowing the list of DestructableIds is always smaller than Entities existing into that region so i prefer saving iterations by iterating over DestructableIds and looking for the entity other than doing it the other way around.

    you can take a look at this website: https://gametorrahod.com
    there is a lot of Tricks and Explanations about what is happening under the hood using ECS.
     
    Last edited: Apr 20, 2019
  7. JesOb

    JesOb

    Joined:
    Sep 3, 2012
    Posts:
    1,109
    So if I understand correctly you have some Map (may be HashMap) DestructableId to EntityId and you iterate over destructable ids received over network retrieve Entity from Map and Set some new data into entity.
    Is it correct?

    Thanks for it :)
     
  8. Opeth001

    Opeth001

    Joined:
    Jan 28, 2017
    Posts:
    1,117

    what im exactly doing is adding a ComponentData "Destructable" which contain an id and a SharedComponentData "Region" which is an id too but identifying a specific region within my map.
    my two components values are unique to a destructable, the region SharedComponentData will help me to iterate quickly over the concerned destructables and the Destructable ComponentData will help to identify the entity.

    Your Welcome!
     
  9. timjohansson

    timjohansson

    Unity Technologies

    Joined:
    Jul 13, 2016
    Posts:
    473
    Using entity ids for replicated entities was the first thing we looked at when we started looking at netcode for ecs. We quickly went away from it because the entities are hard to keep in sync between client and server, and entities are not always identical on client and server since server needs to run more logic which can require additional data. It also encourages bad memory access patterns since it makes it more convenient to access everything by entity.

    What we are currently doing instead if on the server side to go through all data we want to replicate chunk by chunk. We identify entities by network ids stored as component data and allocated from a separate pool. On the client side we lookup entity from the network id and inject snapshot data into it. Another system later applies the raw snapshot data to the entity. We did not yet optimize the client side much yet, but we think it is possible to split the snapshot storage from the entities to get better cache locality. The chunks are almost never the same between client and server so we cannot process per chunk on the client.
     
    June1111, FROS7 and NotaNaN like this.
  10. JesOb

    JesOb

    Joined:
    Sep 3, 2012
    Posts:
    1,109
    Thanks a lot for reply :)

    Can you elaborate more about this?
    For now we have Entity ids easily synced between client and server difference in component data has no matter because we need only id. e.g. We have for now players, mobs and bullets to sync, player always have ids from 0 to 20 mobs from 20 to 100 and bullets is a different story at all :) so even if player on server has more info, I dont care, client just have another archetype for it. It is not a problem.

    There I totally agree but:
    Isnt you do the same thing there?
    Can you please explain difference?

    The only diff I can see is:
    you ignore entityId and create custom id (that you can control) and send id to client. On client side Lookup client entityId from custom Id and inject snapshot data into it.

    we dont ignore entityId (we control it) we just use it and send id to client. On client side just use received EntityId and inject snapshot data into it.

    Another system later applies the raw snapshot data to the entity.

    I think I just dont understand some core thought and can not see advantage of your solution. just more code mess.

    Our currect solution works good but ECS just have very bad support for entity manipulation. Manual Entity id allocation or Method to Apply Exact Archetype to entity will do the job for us.
     
  11. timjohansson

    timjohansson

    Unity Technologies

    Joined:
    Jul 13, 2016
    Posts:
    473
    What we were initially thinking about was having all entity ids match so you could just use them as ids, which requires all code to be 100% deterministic and the set of entities must be the same. We also want to support replicating multiple server side entities to a single client side entity since you often split a thing into multiple entities.

    If you do not require that but instead require entities to be allocated from a different range if they are synchronized - like it sounds like you are doing - it would be more feasible. But doing so moves a lot of responsibility to the gameplay code, it needs to know what is synchronized and how it is synchronized in order to allocate entity ids from the correct range. It also requires you to know up front how many entities you will have in the worst case.

    If you are allocating entity ids from a separate range for networking they are already a special thing which requires care and custom logic, we found it easier to handle them in a more automated way if they are treated as a completely different thing without merging them with entity ids. It also allows us to reduce bandwidth since we can send the id as a single int (entity id is 2 ints) if we ensure we do not reuse a network id until the despawn has been acked.

    Sounds like we are doing the same thing, I did not mean to imply that using custom network ids makes it possible to do something you cannot do with entity ids, just that it makes it harder to do bad things.

    Which part do you think adds more code mess?

    Keeping the entities ids separate from the network ids allows us to move complexity from gameplay code to the network code - leading to less messy gameplay code. If network ids and entity ids are the same thing gameplay code needs to be aware of that and manually allocate entity ids correctly. Most of the complexity in the network id code is dealing with resending despawn events - which has to happen regardless to make sure you do not reuse an id before it has been deallocated on the client (assuming you're using an unreliable connection).

    I am not claiming that custom network ids doesn't add any complexity, but I think using entity ids moves complexity from the network system to gameplay systems and core ecs. I do not see any reason to have a shared id outside the replication send and receive systems, so the only benefit I see is saving a NativeHashMap lookup per replicated entity on the client side, is there some other benefit I'm missing?

    If you are talking about splitting attaching snapshot data to the entity and applying a raw snapshot to the entity (was part of the quote so not 100% sure) they have to be separate since we need to interpolate between snapshots and apply them at the correct time - which is not always immediately.
     
  12. JesOb

    JesOb

    Joined:
    Sep 3, 2012
    Posts:
    1,109
    Sorry for so late answer :(
    It was so many work and I need time to understand your solution :)

    Thank for clarifying everything :)
    Totally agree with you except reduce bandwidth because we send entityIds as 1byte per entity :)

    Code mess for me is bookkeeping of additional dictionary (map) of networkid to entityid :)
    But now I understand you and you are right so it not worth the discussion :)

    The only thing I think of for now is sync of Archetypes that will guarantee order of entities inside some archetype.
    With such system it will be possible to save snapshot data from network packet to entity with liner memory access pattern.

    It will add some restrictions on such archetypes like entity must be created with this archetype and never move to another. Server and client can have slightly different archetypes (server has some additional host components client - some additional view components etc.) but add and remove entities in archetype must be server only and client must only do sync from net messages and sync must guarantee order of entities in archetype may be by using custom component like networkId.

    All this can be inside network layer without touching of gameplay logic :)