Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Priority Accumulator for partial and selective replication of entities #57

Closed
RJ opened this issue Sep 29, 2023 · 1 comment
Closed

Comments

@RJ
Copy link
Contributor

RJ commented Sep 29, 2023

Background

This is to summarise a design discussion from a discord chat today, relating to migrating from my custom renet netcode to replicon.

In my game I send spawn messages from server to client on the frame they happen, but only send regular state updates (position, velocity, etc) at a slower rate (10hz) My clients predict everything and reconcile accordingly when updates arrive.

Currently this isn't possible with replicon. If I set the server tickrate to 10hz, my spawns will also be broadcast at 10hz. No good for bullets, which need to appear on clients asap.

This proposal also moves towards replicon deciding how many packets to send per tick, by deciding what goes into each individual packet (~1200bytes or whatever). Sending one large buffer and letting renet fragment it is fine for some games, but loss of one fragment renders the entire update useless. Problem is worse for games with a lot of state data requiring multiple packets. They might be better served by replicon crafting multiple single packets each with replication data for a subset of entities, that can be applied individually.

Priority Accumulator

Described in this gaffer article under the "Priority Accumulator" heading, it could work like this at a high level:

  • Every replicated entity gets an PriorityAccumulator(f32) component.
  • Higher values mean that entity is more deserving of being sent to the clients in an update packet.
  • As time passes, the accumulator can increase gradually.
  • Game logic can increase the accumulator of an entity, for example if a player collides or interacts with it.
  • Newly spawned entities can start with a Very Large accumulator

How the server builds a packet to send

  • When creating a packet of replication data to send to clients, sort all entities by largest priority accumulator first.
  • Work down the sorted list, serializing and writing to the packet buffer until the packet buffer is full (ie, within the MTU).
  • Send packet
  • Set priority accumulator to 0.0 once an entity's data has been included in a packet.
  • No reason to stop at just 1 packet - keep going down the sorted list and write a second packet. You can dynamically adjust the bandwidth by deciding how many packets to send each tick.

Each packet contains all required component data for a subset of entities, so would need per-entity acks
See also: #16

On top of this, it should be possible to have a higher level API like "always send spawns every tick, but otherwise i'm happy with 10hz state updates". But with a reasonable bandwidth budget, and allowances for bursting higher due to lots of spawns, there probably wouldn't be any need to fix the rate of updates, as long as freshly spawned entities started with a very high accumulator.

Small worlds, or when partial state updates are unwanted

If the number of entities is small enough that it fits in a packet, or the game isn't compatible with partial state updates, you could skip sorting by accumulator and just include everything in one large buffer and let renet split it (if needed) like it does at the moment.

@Shatur
Copy link
Contributor

Shatur commented Oct 1, 2023

We decided to implement it as part of #15.

@Shatur Shatur closed this as completed Oct 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants