Skip to content

Releases: cBournhonesque/lightyear

0.29.0

Choose a tag to compare

@cBournhonesque cBournhonesque released this 10 Aug 23:24
760f2e8

Lightyear 0.29.0

Deterministic peer-to-peer

Lightyear can now run deterministic, input-only P2P games without an authoritative server. For now this only works when the total number of peers is fixed.

A minimal application setup looks like this:

app.add_plugins(ClientPlugins { tick_duration });
app.add_plugins(P2PSessionPlugin);

// This is a capacity including the local peer, not a required player count.
app.insert_resource(P2PSession::new(max_peers));

You need to add the P2P component on each Link that should be part of the P2P session.

commands.spawn((
    P2P,
    Link::default(),
));

Trigger P2PStart once the initial cohort has been created; Lightyear waits until the frozen cohort and the input timeline are ready, negotiates one future start tick, and then emits the P2PStarted event on every peer.

Most examples can now be run in P2P mode.

One timeline and rollback pipeline

LocalTimeline and InterpolationTimeline were previously components, but it doesn't make sense to have more than one per App. (LocalTimeline is used to update the global Time<Virtual> resource, and Interpolated entities cannot be displayed for multiple interpolation timelines at once).

They have both been changed into Resources.

LocalTimeline is used to get the local fixed-update tick, and LocalTimelineSync is the resource used to sync the local Time<Virtual> with the remote peers.
You can use the SystemParams SyncedInterpolationTimeline and SyncedLocalTimeline to access the resources while skipping the system if the timelines are not synced.

Similarly, prediction needs storing a history according to the local LocalTimeline, so Prediction/Rollback state is now global per application. Rollback and PredictionManager are now resources.

This lets timeline syncing and prediction work similarly for a client-server or a P2P app (where there is no Link entity symbolizing the local peer).

Interpolation rules

Interpolation is now rule based, similar to Replicon's replication rules. You can define multiple rules for a given component, and they will be applied in order of priority.
The main benefit is that now you only need to define the interpolation logic once. The selected rule is then reused in all interpolation situations:

  • delayed interpolation of remote Interpolated entities
  • FrameInterpolate between fixed ticks
  • visual correction after a prediction rollback

For a type that implements Bevy's Ease trait, the common setup stays short:

app.component::<Position>()
    .replicate()
    .predict()
    .add_linear_interpolation()
    .add_correction();

For a custom function, use InterpolationFns::interpolate:

fn lerp_position(start: Position, end: Position, t: f32) -> Position {
    Position(start.0 + (end.0 - start.0) * t)
}

app.interpolate_with::<Position>(
    InterpolationFns::interpolate(lerp_position),
);

InterpolationFns also lets you specialize the interpolation:

  • interpolate_with_context(fn) also passes the interpolation duration to the interpolation function.
  • history_only() keeps a history of confirmed values (ConfirmedHistory<C>) but doesn't perform any interpolation for Interpolated entities. Use this if you want to use your own custom logic for interpolating between history values.
  • history_only().interpolate(fn) does the same thing but also registers a function that will be used for correction or frame interpolation.
  • no_history(fn) registers a function only for frame interpolation or correction.
  • disabled() opts matching entities out of that rule kind.

Multiple rules and priority

You can register several rules for the same component. Filters choose which archetypes can use a rule, while priority decides which matching rule wins:

// Broad default rule. A single-component rule has default priority 1.
app.linear_interpolate::<Position>();

// Projectiles use a different curve.
app.interpolate_with_priority_filtered::<Position, With<Projectile>>(
    10,
    InterpolationFns::interpolate(interpolate_projectile),
);

// Teleporting entities should not interpolate at all.
app.interpolate_with_priority_filtered::<Position, With<Teleporting>>(
    100,
    InterpolationFns::disabled(),
);

For each entity archetype, higher priority wins. If priorities are equal, the rule registered first wins. A filter does not receive an automatic priority bonus, so a filtered override should normally use an explicit higher priority.

Rules can also target a bundle:

app.interpolate_bundle_with::<(Position, Rotation)>(
    InterpolationFns::interpolate(interpolate_pose),
);

The default priority is the number of components in the target. A default (Position, Rotation) rule therefore has priority 2 and wins over default single-component rules for Position and Rotation on an archetype where all of them match. Bundle rules are useful when several components must be sampled together.

This means an application can define a broad component rule, add specialized filtered rules for particular gameplay archetypes, and add a higher-priority bundle rule where several values must stay coupled. The rule selected for an entity is shared by delayed interpolation, frame interpolation, and correction rather than configuring those three systems separately.

Avian integration

Replication modes

The Avian integration now uses Position mode by default:

app.add_plugins(LightyearAvianPlugin {
    replication_mode: AvianReplicationMode::Position {
        sync_to_transform: false,
    },
    ..default()
});

There is no longer a mode that replicates Position but interpolates Transform. We can always interpolate the Position/Rotation values, even in frame interpolation. Position mode is preferred because it sends the compact physics pose instead of the full Transform. It also keeps simulation state separate from the temporary visual state produced by frame interpolation and correction.

Child colliders

The old integration had a problem with compound rigid bodies; (RigidBodies that have child Collider entities) this is now fixed.

The default Avian protocol now replicates Position only for entities with their own RigidBody:

app.component::<Position>()
    .replicate_filtered::<With<RigidBody>>()
    .predict()
    .add_linear_interpolation()
    .add_correction();

Child colliders keep their local offset (Transform) and follow the RigidBody root through normal transform propagation.

Misc

Position mode automatically registers a Hermite bundle rule for (Position, Rotation, LinearVelocity, AngularVelocity).

The sync_to_transform option is about the gameplay authoring API, not about what gets interpolated:

  • Keep it false when gameplay updates Position, Rotation.
  • Set it to true only when fixed-update gameplay directly reads or writes Transform.
  • Use AvianReplicationMode::Transform only when transform data such as scale or local hierarchy state is itself part of the networked gameplay state.

Replication improvements

You can add the Persistent marker on replicated entities on the client to not despawn them when the client disconnects. You can add the Persistent marker on the ReplicationReceiver entity to extend this behavior to all replicated entities received by this receiver.

This is the flip-side to the ControlledBy::Lifetime value, which specified what the server does when a client that controls an entity disconnects.

Other changes

  • Tick is now an ordinary non-wrapping u32 newtype. Ordering is numeric and arithmetic that returns another Tick saturates instead of wrapping.

  • Messages and events can also be assigned to a remote timeline with ChannelSettings::on_timeline. The receiver holds them until that timeline reaches the sender tick. This is mostly useful for the server to trigger events on a client's interpolation timeline.

  • the serializer changed to postcard since bincode was unmaintained.

Migration guide

Client setup and connection state

Client is now a unit marker. Connection state can be accessed via the custom QueryData ClientState .

A client that receives replication should include ReplicationReceiver. Prediction is now enabled by inserting PredictionManager as a resource:

app.insert_resource(PredictionManager::default());

commands.spawn((
    Client,
    ReplicationReceiver,
    Link::default().with_conditioner(conditioner),
));

Unlink::reason, Unlinked::reason, and Disconnected::reason are no longer strings. Use UnlinkReason and DisconnectedReason, for example UnlinkReason::UserRequested(None) or DisconnectedReason::TransportError(error.to_string()).

Timelines, prediction, and prespawning

InputTimeline has been removed. Read Res<LocalTimeline> for the current simulation tick. Use SyncedLocalTimeline for systems that should only run once the local clock is synchronized; use Option<SyncedLocalTimeline> in systems shared with server or host modes. (or if local_timeline.is_synced())

InterpolationTimeline and InterpolationConfig are resources. Replace client-entity queries with Res<InterpolationTimeline>, or use SyncedInterpolationTimeline when the system should wait for sync.

PredictionManager, LastConfirmedInput, Rollback, InputTimelineConfig, and PreSpawnedReceiver are resources rather than components on the client entity.

SyncEvent<T> has been replaced by the application-wide LocalTimelineShift { delta } event. Code that relied on tick wraparound should use ordinary comparisons and the new saturating Tick arithmetic.

Interpolation, frame interpolation, and c...

Read more

0.28.0

Choose a tag to compare

@cBournhonesque cBournhonesque released this 26 Jun 04:17

Lightyear 0.28.0

This release updates Lightyear to the Bevy 0.19 ecosystem and focuses on input hardening, input authorization hooks, and replication simplification.

Highlights

  • Updated the dependency stack for Bevy 0.19, including Aeronet 0.21, Bevy Replicon 0.41, Avian 0.7, Bevy Enhanced Input 0.26, Leafwing Input Manager 0.21, and Rust 1.95.
  • Simplified bevy enhanced input replication (you just have to spawn the Action entities on the server and replicate them to the client)
  • Hardened input handling by bounding future input tick ranges and adding server-side validation/authorization hooks. Thanks to @mmannerm
  • Simplified replication internals: resources are replicated like any other component, so there is no need for special-case logic for then.
  • Enable handling diff-compressed components. Instead of replicating the full component state, only the delta between two states is replicated. This can be useful for components that store a Vec

What's Changed

Full Changelog: 0.27.0...0.28.0

0.27.0

Choose a tag to compare

@cBournhonesque cBournhonesque released this 22 Jun 00:53

Changelog

This release is still compatible with bevy 0.18. I will submit another release soon for compatibility with bevy 0.19.

Major changes

  • Switched the replication backend to bevy_replicon.

    • The goal is to reuse the wider Bevy networking ecosystem's work, avoid splitting contributor efforts, and benefit from Replicon's well-optimized and documented code.
    • Lightyear still provides its own higher-level replication API for prediction, interpolation, authority metadata, visibility, hierarchy propagation, and pre-spawning.
    • The old lightyear_replication implementation was more tightly integrated with Lightyear and supported having multiple replication senders/receivers per app by adding ReplicationSender and ReplicationReceiver components. Replicon is centered on server-to-client replication, so other replication patterns (client-to-server replication, distributed authority, etc.) are now unsupported.
    • Some old replication-layer features are not yet at parity, including component-level delta compression, authority switching, per-component priority, and some advanced sender/receiver topologies.
    • The move also brings useful Replicon features into Lightyear, including marker-specific replication rules, a flexible visibility-filter system, and mutation/checkpoint information that can be used by prediction and interpolation.
    • Removed the ReplicationGroup API.
  • Added a structured lightyear_debug tracing layer through lightyear_tools.

    • Debug events are emitted as JSONL rows with stable categories such as timeline, prediction, interpolation, input, sync, messages, entities, transport, components, and manual events.
    • Components can be sampled with typed debug or structured JSON formatters by adding LightyearDebug.
    • Structured debug coverage now spans more of the runtime path, including sync/ping, input buffering, prediction rollback, visual correction, frame interpolation, and replication tick advancement. This is intended to make desync investigations easier to automate and easier to inspect with LLM-assisted analysis.
  • Switched Tick from u16 to u32.

    • This avoids practical tick wraparound during normal game sessions and removes a lot of complicated sync/rollback edge cases.
    • Replicon's replication tick is now treated as a transport/checkpoint index and mapped back to Lightyear's authoritative simulation Tick.
  • Improved component registration API.

    • The preferred component registration API is now
app.component::<C>()
   .replicate()
   .predict()
   .with_rollback_condition(f)
   .add_interpolation_with(f);

The main benefits is that you can register a component with lightyear, and then use any replicon function for the replication logic, e.g. app.component::<C>().replicate_once()
Also now you can only call prediction related functions (with_rollback_condition, etc.) after having called predict().
Enabling rollback without replication has now been renamed to local_rollback instead of add_rollback.

Migration notes

  • Naming between Lightyear and Replicon does not line up one-to-one.

    • Replicon's native send-side marker is Replicated, and received entities use Remote.
    • Lightyear keeps Replicate as the user-facing send-side component. Code that previously queried Lightyear receive-side replication markers may need to move to Replicon's Remote, ConfirmHistory, or the Lightyear compatibility exports depending on intent.
  • The visibility API now uses Replicon's visibility filters under the hood.

    • Room-based visibility changed significantly: use RoomAllocator to allocate global RoomIds and add Rooms to entities/clients.
    • Room ids are no longer ad-hoc local values; they must be allocated globally so Replicon's filter bitsets can reason about them consistently.
  • bevy_enhanced_input integration no longer relies on general client-to-server entity replication for action entities.

    • Action entities are now expected to use the pre-spawning flow and must be spawned on both the client and server.
    • Input action replication now serializes the action context through NetworkActionOf and handles rebroadcasted action entities explicitly.

Added

  • Added deterministic-replication late-join catch-up support.

    • New clients can join a running deterministic game without replaying the full historical input log. The catch-up flow combines deterministic input replication with a one-time state snapshot gated through Replicon visibility filters. After receiveing the state snapshot, the new client can just simulate the rest of the game in a deterministic fashion with just input replication.
  • Added optional LZ4 transport packet compression.

    • Compression keeps packet headers uncompressed, validates decompressed payload limits, respects MTU constraints, and includes benchmark coverage.

Prediction and interpolation

  • Prediction and interpolation now use Replicon's confirmation and mutation-checkpoint data to reason about entities/components that did not change. This avoids false predictions where an entity was assumed to have changed simply because no correction arrived.

  • Interpolation is more robust under packet loss and visibility changes.

    • Interpolated entities now delay despawn until the interpolation timeline reaches the authoritative server despawn tick.
  • Fixed several rollback and confirmed-history edge cases.

    • Prediction can now handle predicted entities whose latest confirmed states are not all at the same tick.

Transport and networking fixes

  • Timeline sync is more stable under very low latency, keeping the client timeline at least one tick ahead of the remote/server timeline so deterministic inputs can arrive before the server simulates the target tick.

Release 0.26.0

Choose a tag to compare

@cBournhonesque cBournhonesque released this 22 Jan 03:26
83e635d

Release 0.26.0

Mainly an update to match bevy's 0.18 release.

Breaking changes

  • LocalTimeline is now a resource; since there can only be one per App (because it modifies bevy's Time resource)
  • NetworkVisibilityy::gain_visibility and NetworkVisibility::lose_visibility methods have now been added on the ReplicationState and removed from the NetworkVisibility component. NetworkVisibility is now a marker component.

What's Changed

New Contributors

Full Changelog: 0.25.5...0.26.0

Release 0.25.0

Choose a tag to compare

@cBournhonesque cBournhonesque released this 14 Oct 02:55
a4dac9d

This version now targets bevy 0.17!

Merge Predicted, Confirmed and Interpolated

This is a big change. In the past you would have 2 separate entities:

  • a Confirmed entity that contains the remote state received via replication
  • a Predicted entity that lives in the future compared to the remote and contains the predicted state of the entity, or an Interpolated entity that lives in the past and contains the interpolated state (interpolated between 2 consecutive Confirmed states)

This model had a lot of flexibility since the entities were separate, but it also added a lot of extra complexity:

  • non predicted components had to be synced from the Confirmed to the Predicted entity
  • we had to apply entity-mapping from the Confirmed to the Predicted entity
  • sending a message from the client to the server about a Predicted entity was tedious, as you had to map from Predicted entity to Confirmed entity first

In this version I'm opting to greatly simplify the logic by merging all these entities into one.

If an entity is Predicted/Interpolated, then the marker component will be added, no additional entity will be spawned.
For Predicted/Interpolated components, the replication updates are now inserted as Confirmed<C> instead of C. Other components are inserted directly as C.

This also removes the need to specify a PredictionMode or InterpolationMode, which have been removed. InterpolationManager has also been removed.

PreSpawned can now be used outside of Prediction

There are situations where you might want to replicate an entity from the server to client, but instead of spawning a new entity on the client you want to target an existing entity. We already had a solution for this, the PreSpawned component, but it was only compatibly with Predicted entities.

It now works for all replicated entities.
The PrePredicted component is now removed as it was complicated to reason about, and I believe that all use-cases can be filled by the PreSpawned component.

DeltaCompression Changes

  • DeltaCompression must be added AFTER prediction/interpolation in the Protocol for it to work properly. This is probably a temporary restriction that will be lifted in the future.
  • The Diffable trait now has Delta as a generic type instead of an associated trait. This enables implementing Diffable for a type with multiple choices of Delta. This can be useful for example for Transform, which implements both Diffable<Isometry2d> and Diffable<Isometry3d>

Avian-specific Changes

Lightyear now supports 3 modes to work with Avian:

  • Position: Position is replicated and is used for FrameInterpolation and Correction. In your FixedUdpate systems you must update Position and not Transform.

  • PositionButInterpolateTransform: Position is replicated, but Transform is used for FrameInterpolation/Correction (this can be convenient since FrameInterpolation/Correction are mostly visual concerns). In your FixedUdpate systems you must update Position and not Transform. Note that there might still be issues with TransformPropagation to children.

  • Transform: Transform is replicated and is used for FrameInterpolation and Correction. In your FixedUdpate systems you can update either Position or Transform.

The LightyearAvianPlugin must now be added manually to your app, it's not added automatically when the avian feature is enabled.

The IslandsPlugin and IslandSleepingPlugin need to be disabled if using input-based replication as they cause issues with rollback.

BevyEnhancedInput

  • Rebroadcasting BEI inputs now works correctly in HostServer mode
  • The serialization model has been reworked to consume a LOT less bandwidth (up to 6-7X less)

DeterministicPredicted

A new field skip_despawn has been added to DeterministicPredicted.
By default it's false, and any rollback where the entity was spawned after the start of the rollback will cause the entity to be despawned. This is usually what you want if the entity was spawned as part of some input (for example a projectile that is fired when a button is pressed). But in some cases you don't want the entity to be despawned, for example if the entity was replicated by the server as a one-time event. In those cases you can set skip_despawn=True.

Projectiles example

A new example has been added to showcase different ways of handling projectile replication.
It is by far the most complex example to date and shows how to handle concepts like:

  • entity visibility via rooms
  • physics handling with avian
  • multiple replication models (deterministic-only, interpolated-only, all-predicted, etc.)
  • bevy_enhanced_input with input rebroadcasting for predicting other clients
  • lag compensation via the LagCompensationSpatialQuery

What's Changed

New Contributors

Full Changelog: 0.24.0...0.25.0

Release 0.24.0

Choose a tag to compare

@cBournhonesque cBournhonesque released this 16 Sep 03:02
39b23be

0.24.0

Smaller release focused on bug fixes.

  • WebSocket support with lightyear_websocket
  • Enables input rebroadcast for lightyear_inputs_bei
  • The Client and ClientOf entities are now mapped on the client and server
  • Some important bug fixes, especially related to input handling

What's Changed

New Contributors

Full Changelog: 0.23.0...0.24.0

Release 0.23.0

Choose a tag to compare

@cBournhonesque cBournhonesque released this 12 Aug 04:44
69824f2

Release 0.23.0

Support for bevy_enhanced_input 0.16.0

BEI did a full revamp of their API where actions and bindinds are now separate entities, which are tied to Context entities via relationships. This release now supports the new API. I haven't tested this in all scenarios so please let me know if you encounter issues.

Deterministic replication works in host-client mode

DR still had a couple of caveats, noteably that it did not work in host-client mode. This should now be fixed; you can try running the deterministic_replication example in host-client mode!

What's Changed

New Contributors

Full Changelog: 0.22.5...0.23.0

Release 0.22.0

Choose a tag to compare

@cBournhonesque cBournhonesque released this 20 Jul 02:11

Release 0.22.0

Upgraded rollback detection

Previously there was only one way we could rollback: if we received a new state update on the Confirmed entity which did not match the prediction history.
Now we have separate a RollbackMode for state replication and input replication; the possible values are Always (rollback whenever we get a new state or input), Check (rollback whenever we get a new state that doesn't match the predicted history, or a new input that doesn't match the input history), Never.

The Always mode can be useful to force trigger rollbacks and to check if your game can handle a certain amount of rollbacks.

But the main benefit is that this change allows for deterministic replication; a networking model where only inputs are replicated (instead of state).

Deterministic Replication

Lightyear now supports deterministic replication! In this mode you can choose to replicate only inputs; each client should receive the inputs from all other clients and use that to deterministically simulate the game.
The API is extremely similar to state replication: instead of Predicted you need to add the DeterministicPredicted component!

By adjusting the InputDelayConfig you can decide the level of prediction that is applied:

  • InputDelayConfig::no_prediction: there is no prediction so the clients will progress in lockstep. NOTE: there currently is no check that for a given tick we have received all the inputs from all clients!
  • with input delay: the local client will predict all other clients' actions

A new example has been added that illustrates those 2 modes.

Note that you can also do a mix between state replication and input replication!

Rollback overhaul + bug fixes

The rollback logic has been overhauled, in particular the logic that was handling the smooth correction from the previously predicted value to the new corrected value. Thanks to the help of @maniwani for that!

I've also fixed a big number of subtle bugs related to rollback, timeline syncs and input buffers.
The result is that rollbacks are now MUCH smoother, as you can check yourself by running the deterministic_replication, avian_physics or avian_3d_character examples. This is a huge upgrade that I am extremely happy about.

Support for immutable components / deprecation of RelationshipSync

It is now possible to use immutable components in the protocol; you will have to use add_immutable_prediction instead of add_prediction, and they are not compatible with PredictionMode::Full.
With this change, the RelationshipSync plugins are not needed anymore.
To replicate Relationships you can just add them directly to your protocol, like so: app.register_component::<R>()

What's Changed

New Contributors

Full Changelog: 0.21.0...0.22.0

Release 0.21.0

Choose a tag to compare

@cBournhonesque cBournhonesque released this 04 Jul 01:43
86bf851

Lightyear 0.21.0

Multiple crates

This is a massive release that is basically a full rewrite of the crate. In particular, the crate has been split into multiple subcrates, for the following reasons:

  • faster compile times
  • more modularity by splitting up small pieces into different crates
  • encourages a better separation of concerns

This was definitely challenging but I think it's for the better.
Similarly to bevy, you will have lightyear_* subcrates and a main lightyear crate which imports every subcrate.
It is possible to use the subcrates directly if you want, as the main crate just imports the relevant plugins from the subcrates.

From Resources to Entities

Instead of managing the networking behaviour via a set of Resources (ClientConfig, ClientConnectionManager, etc.), we now switch to a model where you have one entity per network connection between the local peer and the remote peer. (this approach was pioneered by
aeronet)

  • at the transport layer (how to send/remove bytes):

    • you can add one of the IO components (UdpIO, WebTransportIO, etc.) to specify how you will send or receive bytes from the remote peer
    • the IO-agnostic Link component is used to abstract away from the actual io
    • you can access metadata about the io link using the LocalAddr and PeerAddr components
    • the state of the link can be accessed via various marker components (Linked, Unlinked)
    • the Transport component can be added to specify various channels that provide different reliability and ordering guarantees
    • the MessageSender<M> and MessageReceiver<M> components can be added to add the ability to send and receive structs instead of raw bytes. The MessageManager is responsible for handling the serialization and the entity mapping
  • at the connection layer (how to establish a persistent connection on top of the raw io):

    • you can add the Client or Server component to specify the role of the entity. In particular, the Server component will listen for incoming connections and spawn new ClientOf entities for each connected client
    • the state of the connection can be accessed via various marker components (Connected, Disconnected, Started, Stopped)
    • the NetcodeClient or NetcodeServer components can be added to use netcode as the layer providing authentication + persistent IDs on top of the raw IO
  • at the syncing layer (how to make sure that the local and remote peers are in sync)

    • there is now a new Timeline component that is used to represent the various networking timelines:
      • the LocalTimeline simply increments the tick whenever the FixedUpdate schedule runs
      • the RemoteTimeline is the local peer's best estimate of the remote peer's timeline; computed from the received packets. This is used to make sure that all the timelines remain in sync with the remote peer
      • the InterpolationTimeline is in the past compared to the Remote, and is used to define how to interpolate between the received remote packets
      • the InputTimeline is in the future to the Remote, and is used to define when ticks should be buffered
  • at the input layer (how do the clients send inputs to the server)

    • the ActionState component defines the current state of the inputs at the current tick. It is guaranteed to be the same between client and server for a given tick
    • the InputBuffer component can be used to access the received inputs that were locally buffered
    • lightyear now has a shared generic inputs plugin that makes sure input-handling is correct with rollback, input_delay, rebroadcasting to other clients, etc. There are 3 implementations: native, leafwing, and bevy-enhanced-input
  • at the replication layer (how to replicate the state of the World to the remote)

    • you can add the ReplicationSender and ReplicationReceiver components to specify if you want the peer to be part of replication
    • the PredictionManager and InterpolationManager mark a link entity as having prediction/interpolation enabled

The bevy ECS provides a lot of flexibility: you can add or remove these components from a subset of the peers to change their networking configuration. Usually if you make changes, they will be applied on the next connection attempt.
This change was mostly made possibly with the recent ECS innovations: observers and relationships.

Moving away from Client-Server

Lightyear was strongly tied to the concepts of client and server. You had to use some specialized client or server resources (ClientManager and ServerManager) and those would have very similar code that varied in subtle ways. For example the replication code was essentially duplicated between them, apart from some server-specific code.

With the new component-based approach, the code has become fairly agnostic to the roles of client and servers.
For example there is now a unique replication system that works on any entity that has a ReplicationSender component, so each local peer can replicate to a remote peer in the exact same way!

I think this is super exciting as this opens the door to handling P2P topology with code that is very similar to client-server topologies!

Total parity?

This refactor of the crate doesn't achieve total parity with the previous version. Some of the functionality that hasn't been ported is:

  • Resource replication: I believe bevy is moving towards resource-as-entities, where resources are simply components stored on an entity. I will just be waiting for this to be merged before re-adding resource replication
  • input broadcasting: we previously had convenience functions so that a client could send a message to another client (the message would be sent to the server, who would broadcast it to the correct client). This hasn't been re-added yet
  • authority: the handling of authority is still somewhat in flux, I have made steps towards it but it hasn't been properly tested, and the distributed_authority example is still outdated.

Extras

  • Instead of maintaining my own steam, webtransport, websocket io layers, I now defer to the excellent aeronet crate!

  • Special thanks to @hukasu for helping me improve the compile time of lightyear!

Next steps

With the increased flexibility and modularity, I would like to tackle:

  • handling networking modes where only inputs are replicated, such as deterministic lockstep! I believe this should now be very much within reach, as it mostly involves adding a LockstepTimeline to define how the local and remote peers stay in sync
  • handling P2P topologies

What's Changed

Read more

0.20.2

Choose a tag to compare

@cBournhonesque cBournhonesque released this 10 May 10:26
743beb3
  • Upgrade avian to 0.3
  • Fix wasm support

What's Changed

Full Changelog: 0.20.1...0.20.2