From deebc8c531b6052e11b5438a98268a611d6cff42 Mon Sep 17 00:00:00 2001 From: Valaphee The Meerkat <32491319+valaphee@users.noreply.github.com> Date: Mon, 11 Mar 2024 00:13:10 +0100 Subject: [PATCH 1/6] Create 76-replication.md --- rfcs/76-replication.md | 81 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 rfcs/76-replication.md diff --git a/rfcs/76-replication.md b/rfcs/76-replication.md new file mode 100644 index 00000000..6030d968 --- /dev/null +++ b/rfcs/76-replication.md @@ -0,0 +1,81 @@ +# Feature Name: Replication + +## Summary + +A simple way to replicate states between Bevy instances. + +## Motivation + +At the moment, Bevy doesn't have its own networking. There are external crates that solve this problem to some extent, but having a solution in Bevy would ensure that it will be maintained in lockstep with Bevy. As networking is a core feature of every newer game engine, having a direct solution in Bevy would allow plugin authors to also consider a replication-friendly design. + +The goal is to be able to replicate resources, events, and components peer-to-peer, with options to configure how and when replication happens, as generally as possible. + +## User-facing explanation + +Replication is split up into replication and transport. + +### Replication + +For replication to work, every resource, event or component has to be registered for sending or receiving. +``` +app.add_plugins(ReplicationPlugin); +app.recv_resource::(); +app.send_resource::(); +app.recv_event::(); +app.send_event::(); +app.recv_component::(); +app.send_component::(); +``` +recv requires the DeserializeOwned trait, and send the Serialize trait. + +### Transport + +The transport part is responsible for the underlying protocol and how the data is transmitted and received. +There is a server and client plugin; they don't have any impact on replication itself. +``` +app.add_plugins(ServerPlugin { + address: todo!(), +}); +app.add_plugins(ClientPlugin { + address: todo!(), +}) +``` + +A connection is an entity with a connection component, so that the lifetime of the connection component is coupled with the lifetime of the actual connection. The connection also has ownership of the entity, including their children. + +## Implementation strategy + +A new crate gets added called bevy_net, or bevy_replication, which contains a replication plugin and could contain multiple transport plugins for every type of transport, and server and client are also two distinguished plugins. + +The replication plugin contains the methods and interfaces for transport to implement and the systems to spawn new connections, send/queue, and handle updates. + +The transport plugin interfaces with the replication plugin to handle the queues and send new packets into the queue. + +## Drawbacks + +- Additional burden of maintaining. +- "No one solution fits all" + +## Rationale and alternatives + +Keeping things as-is and recommending external crates. + +## Prior art + +[bevy_net_valaphee](https://github.com/valaphee/bevy_net) +PoC, which adheres to this RFC and is sufficiently covered with tests, could also be upstreamed. + +[bevy_replicon](https://github.com/projectharmonia/bevy_replicon) +Similar to my implementation also supporting different transports. But no resource replication and separate client and server logic. + +[lightyear](https://github.com/cBournhonesque/lightyear) +Also supports different transports. + +## Unresolved questions + +- How to implement scoping and prioritization. + Unreal Engine, for example, has a way to specify if a property should be replicated (Initial, Owner, Owner+Initial) and what priority an entity has (distance-based). + +## Future possibilities + +- Builtin rollback and/or lock-step. From 5ae3e82190dc78562433d0795f294a66957dd282 Mon Sep 17 00:00:00 2001 From: Valaphee The Meerkat <32491319+valaphee@users.noreply.github.com> Date: Mon, 11 Mar 2024 00:23:26 +0100 Subject: [PATCH 2/6] Update 76-replication.md --- rfcs/76-replication.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/rfcs/76-replication.md b/rfcs/76-replication.md index 6030d968..dda08928 100644 --- a/rfcs/76-replication.md +++ b/rfcs/76-replication.md @@ -17,7 +17,7 @@ Replication is split up into replication and transport. ### Replication For replication to work, every resource, event or component has to be registered for sending or receiving. -``` +```rs app.add_plugins(ReplicationPlugin); app.recv_resource::(); app.send_resource::(); @@ -32,7 +32,7 @@ recv requires the DeserializeOwned trait, and send the Serialize trait. The transport part is responsible for the underlying protocol and how the data is transmitted and received. There is a server and client plugin; they don't have any impact on replication itself. -``` +```rs app.add_plugins(ServerPlugin { address: todo!(), }); @@ -74,7 +74,11 @@ Also supports different transports. ## Unresolved questions - How to implement scoping and prioritization. - Unreal Engine, for example, has a way to specify if a property should be replicated (Initial, Owner, Owner+Initial) and what priority an entity has (distance-based). + - Unreal Engine, for example, has a way to specify if a property should be replicated (Initial, Owner, Owner+Initial) and what priority an entity has (distance-based). +- How to handle reliability and ordering. + - For out-of-order packets, theoretically only the newest state for a resource or component+entity pair has to be applied, ignoring all datagrams that are older.
+ This also means that the state might not be the same as on the server, for example. (Maybe configurable?) + - Events should never be accepted out-of-order, but they can be unreliable for playing animations. ## Future possibilities From b3c09e1c7a761e200d03184306451028025d99cb Mon Sep 17 00:00:00 2001 From: Valaphee The Meerkat <32491319+valaphee@users.noreply.github.com> Date: Mon, 11 Mar 2024 00:27:40 +0100 Subject: [PATCH 3/6] Update 76-replication.md --- rfcs/76-replication.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rfcs/76-replication.md b/rfcs/76-replication.md index dda08928..86ee92e6 100644 --- a/rfcs/76-replication.md +++ b/rfcs/76-replication.md @@ -16,9 +16,10 @@ Replication is split up into replication and transport. ### Replication -For replication to work, every resource, event or component has to be registered for sending or receiving. +For replication to work, every resource, event or component has to be registered for sending or receiving. Which would also allow for some configuration, like relevancy or which serializer to use, etc. ```rs app.add_plugins(ReplicationPlugin); + app.recv_resource::(); app.send_resource::(); app.recv_event::(); From 13b40286cc95fe92496f77cecef0a0c1f5bd4db8 Mon Sep 17 00:00:00 2001 From: Valaphee The Meerkat <32491319+valaphee@users.noreply.github.com> Date: Mon, 11 Mar 2024 00:43:20 +0100 Subject: [PATCH 4/6] Update 76-replication.md --- rfcs/76-replication.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/rfcs/76-replication.md b/rfcs/76-replication.md index 86ee92e6..79e38ac4 100644 --- a/rfcs/76-replication.md +++ b/rfcs/76-replication.md @@ -12,7 +12,7 @@ The goal is to be able to replicate resources, events, and components peer-to-pe ## User-facing explanation -Replication is split up into replication and transport. +Replication allows for transferring state between multiple instances, either Server-Client, or P2P. And is split up into the actual replication part and transport. ### Replication @@ -50,6 +50,10 @@ A new crate gets added called bevy_net, or bevy_replication, which contains a re The replication plugin contains the methods and interfaces for transport to implement and the systems to spawn new connections, send/queue, and handle updates. +To register resources, events, and components, a resource map is created containing hashes of the corresponding types and their serializers and deserializers. Where the deserializers also have the job of handling the state, like inserting resources, sending events, or inserting components,. + +It's theoretically possible to also use Reflect, but it would be substantially slower. + The transport plugin interfaces with the replication plugin to handle the queues and send new packets into the queue. ## Drawbacks @@ -61,6 +65,10 @@ The transport plugin interfaces with the replication plugin to handle the queues Keeping things as-is and recommending external crates. +As stated above, one major benefit of having an in-house solution is that crate authors writing plugins for Bevy may also consider how replication would interact with their plugin. There is less incentive to add support for external plugins. + +No replication at all and just adding a way of sending and receiving packets would also be a solution, but then a user could also just write the boilerplate for creating a server and client, which are like 200 LoC, and it also might fit their needs better. + ## Prior art [bevy_net_valaphee](https://github.com/valaphee/bevy_net) From c3107c57c690538a1344578c0b956e0e770aa312 Mon Sep 17 00:00:00 2001 From: Valaphee The Meerkat <32491319+valaphee@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:11:26 +0100 Subject: [PATCH 5/6] Update 76-replication.md --- rfcs/76-replication.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/rfcs/76-replication.md b/rfcs/76-replication.md index 79e38ac4..faab3c02 100644 --- a/rfcs/76-replication.md +++ b/rfcs/76-replication.md @@ -16,7 +16,7 @@ Replication allows for transferring state between multiple instances, either Ser ### Replication -For replication to work, every resource, event or component has to be registered for sending or receiving. Which would also allow for some configuration, like relevancy or which serializer to use, etc. +For replication to work, every resource, event or component has to be registered for sending or receiving. Which would also allow for some configuration, like relevancy or which serializer to use. ```rs app.add_plugins(ReplicationPlugin); @@ -42,7 +42,14 @@ app.add_plugins(ClientPlugin { }) ``` -A connection is an entity with a connection component, so that the lifetime of the connection component is coupled with the lifetime of the actual connection. The connection also has ownership of the entity, including their children. +Except for the part that a server listens and a client connects, there are no other differences between client and server, which also allows for all sorts of topologies. + +A connection is an entity with a connection component, so that the lifetime of the connection component is coupled with the lifetime of the actual connection. This has the additional effect of ownership, so that an entity is owned by the connection, including their children. + +### Events + +As there are no RPCs, events are the closest thing to achive similar results. Events can either be send normally, or using the `Directed` wrapper, which allows for event routing. +When events are received the event is wrapped inside an `Received` wrapper. ## Implementation strategy From 5c310bbb2f6c37d62e04066713951ff055cee55c Mon Sep 17 00:00:00 2001 From: Valaphee The Meerkat <32491319+valaphee@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:13:16 +0100 Subject: [PATCH 6/6] Update 76-replication.md --- rfcs/76-replication.md | 1 - 1 file changed, 1 deletion(-) diff --git a/rfcs/76-replication.md b/rfcs/76-replication.md index faab3c02..c45c23dc 100644 --- a/rfcs/76-replication.md +++ b/rfcs/76-replication.md @@ -38,7 +38,6 @@ app.add_plugins(ServerPlugin { address: todo!(), }); app.add_plugins(ClientPlugin { - address: todo!(), }) ```