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.
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…
For example a ‘stats’ component would be a good example, say:
struct Stats : IComponentData {
public Int32 Strenght;
public Int32 Agility;
public Int32 Intelligence;
public Int32 Stamina;
}
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.
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.
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.
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:
Separate “hot” data from the complex component. Split it into granular components.
Delta compress most expensive components individually.
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
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.).
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 in Unity ECS).
@Joachim_Ante_1 , is there way to serialize EntityCommandBuffer (or plans to do it later)?
I find that feature very helpful (please see the top message)