🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Should an

Started by
3 comments, last by Karatakos 3 weeks, 2 days ago

Title since the forum doesn’t allow change or deletion: Should snapshot delta packet always contain all Entities in view (and Components) or only those that have changed.

Understand the delta should only contain changes — makes sense.

However, if a snapshot delta no longer contains some component x, how does the client know if it’s because there is no state change or because the server removed the component? Same for entities, an entity may just not have any state change over the last few ticks, or, it may have been destroyed on the server, perhaps it was killed but we missed that state update.

Now, of course I could use a separate command system to relay key entity or component events e.g. entity x destroyed/spawned, etc., but that seems like overkill if it could be handled implicitly via state updates. I could also send all (in view) entity ID’s and component types in any given snapshot delta even if the corresponding components are zero'd out (no change).

Examples:

P2 -- standing in view of P1 -- goes AFK for a few seconds and so the last x snapshots for P1 didn't contain the P2 entity. P1 doesn't know if P2 is dead and so it’s entity needs destroying, or it's actually just doing nothing. The reverse is also true, I.e. P2 could have been killed and the server destroyed that entity, but P1 missed the state update that indicated the death.

Advertisement

No experience, but I think there is no “should”. There are no hard rules how a game must exchange updates. Instead you as designer pick a solution that works best.

No doubt there are common patterns here, because every game has mostly the same problem. Borrowing those is generally a good idea, as people have been tuning those solutions. That is, they are common solutions for good reasons. On the other hand, if they don't work because your game is slightly different (apparently), stepping away from them or making some change is fine.

My first thought after reading your description is to see deletion as a change. However, I don't know if that is a feasible solution for you.

Karatakos said:
Should snapshot delta packet always contain all Entities in view (and Components) or only those that have changed.

A “delta” by definition is the stuff that has changed.

Karatakos said:
However, if a snapshot delta no longer contains some component x, how does the client know if it’s because there is no state change or because the server removed the component?

There was a change.

Specifically the change: “component x was removed”.

If you're serializing the entire world, then the fact that the object ID no longer appears would imply it is no longer in the world. A newly-appearing object ID would imply the creation of a new object.

Karatakos said:
I could also send all (in view) entity ID’s and component types in any given snapshot delta even if the corresponding components are zero'd out (no change).

In game engines like Unreal, replication has two factors in addition to difference: Relevancy and Priority. An update must be relevant for the target player, and it must have a high enough priority to be considered, and only then is the difference taken to see if it's significant enough to transmit. All three must be high enough before the difference is communicated to a client.

They also send object creation and object destruction messages, as both creation and destruction are changes in the world. They may not send them right away, they must be both relevant to a given player and have sufficient priority, but creation and destruction make their way through.

Since you've said in the past that you're looking to only send part of the world in your updates, you're going to need to find a balance of how to limit to part of the world and what to include or exclude in the snapshots.

Karatakos said:
Examples:

Are these real examples that you have seen because you've implemented it in your code and you've found actual issues, or are these theoretical examples that you're trying to solve without having written anything yet?

You are using a library that implements a reliable protocol over the top of UDP. You merely need to flag them as DeliveryMethod.ReliableOrdered and the messages will arrive in order, sequentially, and reliably.

Many people often discover to their surprise that simple solutions work quite well most of the time. Players can transmit megabytes every second, not double-digit or triple-digit bytes. Be careful that you're not wasting time over-engineering your solution, usually there isn't much required these days.

There was a change.

Specifically the change: “component x was removed”.

Yes this was also alluded to by @alberth. I haven’t considered this approach since I was focused predominantly on generating a binary diff by XORing two snapshots for simplicity to auto generate and apply a binary delta.

They also send object creation and object destruction messages, as both creation and destruction are changes in the world. They may not send them right away, they must be both relevant to a given player and have sufficient priority, but creation and destruction make their way through.

Interesting, this Is something I have considered, e.g. distinct from snapshot delta messages, send spawn/destroy commands via a reliable channel. A client would keep the entity alive even if no state updates unless it receives such a command. This would need to extend to components.

In game engines like Unreal, replication has two factors in addition to difference: Relevancy and Priority. An update must be relevant for the target player, and it must have a high enough priority to be considered, and only then is the difference taken

This is fantastic insight thank you. I had considered only ’field of view’. The difference significance is also super interesting I hadn't thought about this and that could be a factor in moving away from an automated binary XOR to something where I get more control over what goes into the delta.

Are these real examples that you have seen because you've implemented it in your code and you've found actual issues

The one example was simply an illustration of why the client needs to know why an entity or component is no longer in a snapshot. There is a fine — sometimes blurry — line between over optimizing and building on sand. I’m asking questions up front to avoid the latter, no matter how stupid or pedantic they may come across.

Advertisement