Search Unity

Serializable EntityCommandBuffer for Netcode, Replays and Reactive Systems

Discussion in 'NetCode for ECS' started by rigidbuddy, Oct 17, 2018.

  1. rigidbuddy

    rigidbuddy

    Joined:
    Feb 25, 2014
    Posts:
    39
    We're making fast paced top down shooter with authoritative room servers.

    I need some kind of serializable EntityCommandBuffer with minimal overhead in size.

    Use cases:
    1) Send over network to replicate changes to clients' worlds via network;
    2) Replay system as queue of buffers and starting world snapshot (for debug purposes);
    3) Reactive System Manager which parse command buffer array and sends filtered messages to subscribed systems (in separate worlds).

    I've implemented 1,2,3 in a very basic form with proxy entity manager, native collections holding add/remove/change messages.

    But maybe I've missed some simple way to serialize EntityCommandBuffer? (keeping performance and size in mind)
     
    Last edited: Oct 18, 2018
  2. fholm

    fholm

    Joined:
    Aug 20, 2011
    Posts:
    2,052
    When dealing with networked ECS in general (not specifically networked Unitys ECS) I always found sending complete components as add/remove/update has always been to expensive on bandwidth. It works fine for something like a transform component with position and rotation, but once you start having even slightly more complex components that have partial updates to their state, the wasted bandwidth is too much.

    Of course if it actually is too expensive in your case depends on player count, complexity of the game, etc.
     
  3. e199

    e199

    Joined:
    Mar 24, 2015
    Posts:
    101
    You can split big component to multiple small, right? Then there is no wasted bandwidth
     
  4. fholm

    fholm

    Joined:
    Aug 20, 2011
    Posts:
    2,052
    Sure, but then you always keep splitting it further and further down, even when it might not make logical sense to do so. i.e. it's only done because the networking system needs it, not from the games perspective. And for some things like, maybe representing an inventory as a component it makes even less sense to split it, or if you have a DynamicBuffer, etc..
     
  5. fholm

    fholm

    Joined:
    Aug 20, 2011
    Posts:
    2,052
    For example a 'stats' component would be a good example, say:

    Code (csharp):
    1.  
    2. struct Stats : IComponentData {
    3.   public Int32 Strenght;
    4.   public Int32 Agility;
    5.   public Int32 Intelligence;
    6.   public Int32 Stamina;
    7. }
    8.  
    Sure you can split this into StrengthStat, AgilityStat, IntelligenceStat, StaminaStat. But that gets pretty messy imho, and this is just a simple example.

    Edit: Whenever one of the stats change, you have to send 3 other ints, basically four times the required bandwidth to update one stat.
     
  6. e199

    e199

    Joined:
    Mar 24, 2015
    Posts:
    101
    I don't think that's correct

    I do split components because I use reactive systems, which are executed when something changed.
    For example when stamina is increased we can have a system to unlock something. I don't want it to trigger after change to any of the fields.
     
  7. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    You want to keep your messaging separate. For the reasons @fholm stated and more.

    You never need to send the full state over the network. There are always cheaper representations you will be using here and there. Like sending a single float heading vs complete rotation for character controllers, sending ints vs floats. Some of that might be encapsulated in your serialization, much won't be.

    In addition you want to be batching udp messages and keeping them within MTU limits, and that logic is message specific.

    That said you need to reason about what is responsible for the bulk of the data being sent over the wire. Position/movement related stuff is usually over 95% of what you are sending. So a naive unoptimized message that only accounts for 1% anyways, probably fine as is. I would still keep the messaging for it separate.

    That said again, I often have domain models that live all the way from the database to the client. Not the high rate stuff, and always encapsulated, but it is a win when you can do that. Just keep the messaging layer separate though so when that doesn't work, you haven't shot yourself in the foot.
     
  8. rigidbuddy

    rigidbuddy

    Joined:
    Feb 25, 2014
    Posts:
    39
    In case of authoritative server you have to send results of server's processing.
    Interest management system may filter major part. Further, most expensive are Change events. Options are:
    1. Separate "hot" data from the complex component. Split it into granular components.
    2. Delta compress most expensive components individually.
    I prefer 1) but your mileage may vary.
     
  9. e199

    e199

    Joined:
    Mar 24, 2015
    Posts:
    101
    In my case I use code generator to generate de-/compression systems which can do delta-compression, quantization and some other optimizations techniques. I just need to mark component and fields with attributes and press "generate".

    That prepared data then used by sending system to actually send it
    So I don't really care about networking, it just works
     
  10. fholm

    fholm

    Joined:
    Aug 20, 2011
    Posts:
    2,052
    Sure, but this is specific to your game and not generally applicable. Doing hundreds of tiny small components for entities gets pretty messy imho. And it's not always applicable to every case either, because you're going to have cases where you need more than 'one' of something on an entity where you cant use components and need to use a dynamic buffer or some other similar abstraction.

    Only allowing the networking layer to reason in add/remove/update component is going to be extremely expensive bandwidth wise. I understand that this model is easy to implement and is tempting, and it will work for simpler games with not that many players or entities (but if you're in that realm, why are you using unitys ecs? the main selling point is to be able to have massive amounts of entities and still have good performance).

    I tend to implement very fine grained delta updates of individual entities and their data, and this is a pretty well established way of doing it looking to AAA games (Overwatch, CS, etc.).
     
  11. fholm

    fholm

    Joined:
    Aug 20, 2011
    Posts:
    2,052
    I prefer to have a general delta compression that runs on top of everything, and can properly delta compress component data updates no matter if it's a small or large component. CPU is comparatively cheap, Bandwidth isn't. It also helps when you have the need for a much more complex component becaue of game design requirements, again say an Inventory component (which I suppose would a DynamicBuffer<InventorySlot> in Unity ECS).
     
  12. rigidbuddy

    rigidbuddy

    Joined:
    Feb 25, 2014
    Posts:
    39
    @Joachim_Ante, is there way to serialize EntityCommandBuffer (or plans to do it later)?
    I find that feature very helpful (please see the top message)
     
    Last edited: Oct 18, 2018