#bevy_replicon

1 messages ยท Page 14 of 1

spring raptor
#

Yep, results in the exact same problem ๐Ÿ˜ข

#

I like of_n! Will rename, thanks ๐Ÿ™‚

About of_ids, do you need this for your crate?
Because I expect users to know the types ๐Ÿค” And this probably requires using reflection of the component and its Hash impl since I need to dynamically query for Hash.

dire aurora
#

Ah right .. Hmmm, well it's not uncommon for me to type erase stuff in my game, so I'd imagine I'll run into it sooner or later ... But maybe we can find a different API once I get there. Perhaps it'd be possible to merge one I'd try to insert with one I can require

#

Needing to pass a fn pointer or hash could also work though

#

Since almost every time I do this I need some type register anyway

spring raptor
dire aurora
#

Yea, could work for now

#

And maybe later we can swap it for using Hash as an ECS trait

#

Components as Entities when ferris_sob

spring raptor
spring raptor
# dire aurora Needing to pass a fn pointer or `hash` could also work though

Added Clone and Copy derives.
Since the component itself is not generic, you can store it somewhere. I think this might be more convenient than storing a pointer to hash ๐Ÿค”
Could also be useful if you decide that from is not enough.

Iโ€™m open to expanding this further.
We could add reflection to make it work via ComponentId or accept user-defined pointers.
However, I'd wait for a concrete use case for it before adding complexity. If you find one, feel free to ask - I'll be able to include it in a patch release.

stone horizon
#

I'm trying to figure out how to replicate sprites (or anything with an asset handle). i dont want to include asset handle logic on the server, but i dont want to sync between an asset-less component and the sprite either...

spring raptor
stone horizon
#

that is what i am doing rn. syncing that into sprites is just kinda weird tho

#

i feel like having a server and client representation of the same (logical) component with to/from functions would be much cleaner

spring raptor
stone horizon
#

my program allows users to change size/color and potentially texture of some sprite. so i will have the actual sprite holding the real data, and then a component that is really just "the values of the last update message". and this component has some of its values copied (OnAdd/OnChange) over into the sprite (e.g. color tint). it's just not a good architecture in general, cf single source of truth

spring raptor
stone horizon
#

that doesnt solve the fact that i then have two instances of the same data living on the client

spring raptor
#

Imagine it's a singleplayer, forget about networking for now

stone horizon
#

The sprite has other relevant properties, in my case pub color: Color,

spring raptor
#

Ah, you want to replicate parts of the sprite

#

Now I get it.

stone horizon
#

so my "message component" becomes something like SpriteRef{color: Color, asset_id: MyAssetEnum}

#

in my mind, the ideal solution would be to have some SpriteRef on the server and Spriteon the client

spring raptor
#

Makes sense. You don't have to create a separate component for adjusting the color.
What you need to do is to enable replication for Sprite, but with custom serialization and deserialization. In these functions you define that you serialize only color and possibly other properties and leave everything as is.

spring raptor
#

We provide replicate_as

spring raptor
stone horizon
#

if i replicate Sprite the server needs to instantiate an image handle

spring raptor
stone horizon
#

and i'd prefer the server to not bother with that (stretch goal, but still)

spring raptor
stone horizon
#

yeah that is the current solution

spring raptor
#

So, the ideas is:
app.replicate::<MyAssetEnum>().replicate_as<Sprite, SpriteColor>().

#

And then you impl From<Sprite> and From<SpriteColor>.

#

And defined an observer for MyAssetEnum insertion.

#

I planning to draft a new release soon. You will need it for replicate_as.
On the latest release you can use replicate_with, but it's not very ergonomic... It has it's own use cases, though.

stone horizon
#

well i would do app.replicate::<SpriteRef>().replicate_as<Sprite>()

spring raptor
stone horizon
#

i'll check master

spring raptor
#

It's just how you send it over the network.

stone horizon
#

yeah that makes sense

empty musk
#

Hi there! Using the high level replication is great for when new players join the game and they get the whole game state automatically, but a system I am currently using this for needs to happen ordered. Is my only option to then use server events/triggers and then manually sending the begin state if a new player joins?

Would be nice if I could still have the initial replication, but I understand that this might not be possible.

Maybe for some context, I was now using it to sync the position of enemies and players in the world, but everything is kind of moving on a chess grid so I also have a function that updates these positions as the positions are updated:

fn position_changed(
    mut q: Query<(Entity, &Position, &Transform, &mut ViewDirection, &OnIsland, Option<&Character>), Changed<Position>>,
    mut islands: ResMut<IslandMaps>,
){
    for (entity, position, transform, mut view_direction, island, character) in q.iter_mut() {
        islands.get_map_mut(island.0).map(|map| {
            position.previous.map(|pos| map.remove_entity(pos));
            map.add_entity_ivec3(position.current, Tile::new(tile_type, entity));
        });
    }
}```

But this has issues as it is does not have the same ordering as on the server, also is it correct on the client side that ```trigger: Trigger<OnRemove, Position>,``` Does not trigger?
spring raptor
empty musk
#

Thanks for the response! Probably a mistake on my end then with the OnRemove, will test tomorrow.

The initial replication would be nice as people can join into the game at any time, so I hoped for an easy way to sync all the positions using replicate, but as I need ordering in the movement for keeping the grid consistent I will have to use triggers/events

spring raptor
spring raptor
stone horizon
#

the tl;dr of building your own backend is just sending items from Replicon{Client, Server}::drain_sent() in PostUpdate and putting them into Replicon{Client, Server}::insert_received() in PreUpdate?

That's what i'm getting from the example backend at least...

stone horizon
#

kinda oof. what does " This can be done via an extension trait that provides a conversion which the user needs to call manually to get channels for the backend." even mean? Is there such a trait somewhere? if i call this manually anyway why do need a trait at all?

#

where does the example backend create channels (looking at latest release)

spring raptor
spring raptor
stone horizon
#

i mean yeah but that's an implementation detail unrelated to the requirements for a backend afaict

spring raptor
spring raptor
spring raptor
#

Serialization via refelct serializes the whole type name and on deserialization it matches by it ๐Ÿ™‚
Dynamic serialization/deserialization is not very efficient ๐Ÿ˜…
But sometimes useful, yeah.

spring raptor
spring raptor
#

Received replication messages represented by Bytes. We unpack data from it by streaming from them.
Deserialization functions accept the message and need to advance "where we at"
Instead of wrapping the message into something like cursor that stores the array and current index, we shift the beginning of Bytes.

#

Feel free to ask more questions if something is unclear ๐Ÿ™‚

spring raptor
#

In that case you need to manually implement DeserializeSeed additionally. It's tricky ๐Ÿ™‚
Let show you an example...

#

Foo is the component in your case?

#

This will be terrible to write ๐Ÿ˜…
If it's 3 levels deep, it's 3 levels of manual impl.

#

I working on an example, so you could see the problem.

spring raptor
#

@vocal violet check this abomination:
https://github.com/simgine/bevy_replicon/pull/576
It's for a struct that contains a reflect. If your struct also contains a map, you need to do the exact same thing 2 levels deeper:

  • Custom ser/de for Bar
  • Custom ser/de for FooBar
    It's not specific to Replicon. That's just how serde works with Bevy reflection.
    So I'd suggest to split this multiple components / entities ๐Ÿ˜…
gilded sail
#

@spring raptor Would you recommend to grab latest version from main instead of what's available in crates.io?

spring raptor
gilded sail
#

Yeah, I figured that would be the case.
Works for my expriments of trying to move away from Unity :)

spring raptor
spring raptor
#

Migrated to 0.17.
It works, but *_trigger_targets does nothing since the entities now stored inside events.
I think I'll re-architecture how events work in general. We no longer need to have custom methods for events (former "triggers").

queen pendant
#

Hi, I'm quite new to replicon.

I have an app where I'm going to replicate some image data. I have two scenarios:

  1. I run the server and client on the same machine
  2. Server is somewhere else

For scenario 1, is there a way for me to use a transport layer that skips networking? I took a look at lightyear and saw that has a crossbeam layer, something like that would probably work.

For scenario 2 I will likely downsample the image e.g. 4x or 8x such that the data is much smaller.

spring raptor
# queen pendant Hi, I'm quite new to replicon. I have an app where I'm going to replicate some ...

Hi!
For this case you want "listen server" mode. It's where server is also a client, but without any networking (even no serialization). If you also don't want for anyone to connect - it's also supported. You even even need to run a server, we call it "singleplayer" in our docs.
Our API designed in a special way, so all these modes (including also pure client) works seamlessly. Check this part of the quick start guide:
https://docs.rs/bevy_replicon/latest/bevy_replicon/#abstracting-over-configurations (you need "The recommended way").

queen pendant
#

Great news, thanks a lot for that!

spring raptor
queen pendant
#

Ah sorry for the lack of due diligence- I was just looking at some crate at work which now uses replicon and asked before I looked at official docs/examples (except I saw the messaging backends didn't have e.g. a channel impl so I made assumptions)

spring raptor
#

Migrated to 0.17.
Logically, it was a trivial migration, and the Bevy team did a great job with the migration guide.
But renaming "event" -> "message" and "trigger" -> "event" was so painful.
It blew up the diff to +3,731 โˆ’3,934 ๐Ÿ˜ข
https://github.com/simgine/bevy_replicon/pull/581

GitHub

I put each logical change into a separate commit. I&#39;d recommend to review them separately.
Some of them are just incremental module renames to make it easier to review &quot;event&q...

dire aurora
#

And here I am just barely getting around to updating bevy_bundlication to unblock using the new stuff in my game so I hopefully get my game working again and can release before I update to 0.17 ๐Ÿคฃ

#

Hopefully with some of our new features this is the last time I need to update bevy_bundlication

#

Because iirc we have the features to make it irrelevant now ๐Ÿ‘€

spring raptor
dire aurora
#

It seems my game broke after I migrated to 0.35 ... There is just no connection happening anymore ... Event aren't being sent ... Is there something new I need to do? ๐Ÿค”

spring raptor
dire aurora
#

I use bevy_replicon_renet now since renet2 hasn't been updated yet and I don't use any of the features of it anyway ๐Ÿคฃ

spring raptor
#

The change that might be related is the migration to states.

dire aurora
spring raptor
#

These systems run when you insert/remove renet resources

spring raptor
#

@dire aurora actually, I think we just don't have any state change logging at all.
Previously it was logged on the Replicon side.
I probably should add logging on the backend side.

dire aurora
#

You could possibly log on the replicon side too in the systems in the OnEnters or something too

spring raptor
#

Now we just need to figure out what is the problem ๐Ÿ˜…
Do you see state changes at least?

dire aurora
#

Gets stuck on connecting ๐Ÿค”

spring raptor
dire aurora
#

Yea, my rewind example works too with the example backend

spring raptor
dire aurora
#

What's the condition on connecting -> connected?

spring raptor
#

It's a run condition.

dire aurora
#
pub fn update(
    &mut self,
    duration: Duration,
    client: &mut RenetClient,
) -> Result<(), NetcodeError> {
    if self.is_disconnected() {
        return Err(NetcodeError::ClientNotConnected);
    }

    client.set_connected();
```Kinda hard for it to not be connected I guess ๐Ÿ˜…
#

I wonder if it misses the connect because it's not just_connected because it was already connected the first time ๐Ÿค”

spring raptor
#

Could you manually check the state of RenetClient resource?

dire aurora
#
2025-10-03T21:13:24.268029Z DEBUG bevy_replicon::client: connecting
Requesting time, connected? true
Requesting time, connected? true
Requesting time, connected? true
```The code printing it:

println!("Requesting time, connected? {}", client.is_connected());

#

Those prints are only twice a second too ๐Ÿ˜…

spring raptor
#

Let me create a branch

dire aurora
#

This works ... Though I think this isn't ideal either, since the Connecting state gets skipped entirely now. What we'd really want is to make sure we always go disconnected -> connecting -> connected

spring raptor
#

Strange that I can't reproduce this problem locally.

dire aurora
#

Doesn't seem to work, I think the run conditions run regardless of the one before it failing, thus it still fails that "just" part of the check

spring raptor
#

Weird, it works perfectly locally ๐Ÿ˜ข

dire aurora
#

Also do you have a transport that connects instantly or not?

spring raptor
#

I just use netcode

dire aurora
#

Ah, yea, I have a custom transport that sets connected to true on the first update, that's not normally possible I think

spring raptor
#

Ah, I see

dire aurora
#

(Because there is no connecting state, you get everything you need before you even connect, IP, port, and a pre shared key)

spring raptor
dire aurora
#

Works! Clever solution ๐Ÿคฃ

spring raptor
#

Great, I drafted a patch!

Other than that, how is the migration going?

dire aurora
#

0.34 -> 0.35 was pretty doable, though the ClientId change was a bit of a pain

#

Also the migration from ClientEntityMap to Signature on my rewind example was a bit silly, but only because I was too lazy to find a good solution

#

Still need to start using the new features

#

Also realized I can't actually use the replicate_as for my original example, since I can't implement a From when I need access to the tick to make it relative, but it should still help when I inevitably do some cleanup to my code

#

Well if it does become a problem I guess we could use a different trait for the conversion that allows some limited world access ... There's also the issue that modifying in place doesn't work optimally if you say replicated transform without the scale, but the scale isn't possible to know in the From impls

spring raptor
spring raptor
dire aurora
#

Yea, though I don't unwrap it, I just put anlet Some(client_entity) = client_id.entity else {continue}

#

But there's also the cases where I need to pass ClientId now instead of the entity from before, etc

#

Not too bad, just a lot of places to change

dire aurora
#

Maybe we already can, idk if implementing a custom trait, then blanketing that where there are Froms/Intos is valid ๐Ÿค”

spring raptor
spring raptor
dire aurora
#

Definitely gonna have to make it nicer later, maybe by using the signature directly and removing the Reason stuff, but for now this should work

#

(Obviously this change isn't all I'll need to do, since my game currently uses Entity in some of these reasons, which would not hash correctly)

spring raptor
dire aurora
#

(This is still going be a painful refactor)

#

Really going to need to rewrite my skill system so I don't have to copy paste logic like this everywhere ๐Ÿ˜…

spring raptor
#

Or wrap this into a macro ๐Ÿ˜ˆ

dire aurora
#

I mean that would clean up these few lines, but it doesn't fix the overal problem that I need to do this correctly for every single entity a skill spawns ๐Ÿ˜…

#

Hmmm, ServerEntityMap gets removed in the loop of applying changes so I can't access it in my hook ... Hmmm ... How am I going to work around that one

spring raptor
#

But it was always like this.

dire aurora
#

Hmmm, I can see if I can apply these changes later ๐Ÿค”

#

Since the replicon logic runs in PreUpdate that should be doable in some way or another at least

spring raptor
#

What I dislike about "message" naming is that in cases like this it looks a bit weird:
I can rename this into ServerMessage, but it's quite ambiguous.

#

(migrating renet)

#

It's renet, which is not related to Bevy. ServerMessage would be just a bad name.

dire aurora
#

Yea, it's definitely not a server message, even server event isn't great, since it's more of a connection-specific thing

#

I guess it's like a ConnectionChange ๐Ÿคฃ

dire aurora
#

Hmmm, when switching server instances I seem to be getting stuck in connecting now ... I wonder if it's because of that condition ๐Ÿค”

2025-10-05T16:25:59.228513Z DEBUG bevy_replicon::shared::event::client_event: sending event `net_sync::Disconnect`
2025-10-05T16:25:59.245786Z DEBUG bevy_replicon::shared::replication::signature: removing hash 0xa43c8848fa3e3678 for `193v1`
2025-10-05T16:25:59.261460Z DEBUG bevy_replicon::client: disconnected
2025-10-05T16:25:59.262094Z DEBUG bevy_replicon_renet: creating channel config ..
2025-10-05T16:25:59.277838Z DEBUG bevy_replicon::client: connecting
#

I guess logically speaking if we aren't connecting then we aren't updating the last status, thus from the perspective of that system it goes from connected to connected

spring raptor
dire aurora
#

Hmmm, actually I think there might be a better fix here, if it only runs during Connecting

#

Why check for just_connected

#

If it's connected at all we can go to Connected

#

Then we still hit every cycle and we no longer rely on that fragile state change there

#

I guess we never had the ability to do this before since we had no states, but we do now

dire aurora
#

Yea now the switch works. Alternative solution would be this:

 set_connected.run_if(
-    in_state(ClientState::Connecting).and(bevy_renet::client_just_connected),
+    in_state(ClientState::Connecting).and(bevy_renet::client_connected),
 ),
#

Which I think should always be correct ๐Ÿค”

spring raptor
dire aurora
#

Because users might want to use Connecting for something, and skipping it could cause unexpected failures because of that, though we could ofc document that it can be skipped

spring raptor
dire aurora
#

Hmmm, might be possible with QUIC and an open server too, but otherwise there's probably some sort of handshake happening

#

Maybe if the transport just blocks until it's done though (not ideal but very easy problem to create)

spring raptor
dire aurora
#

Or other backends if they copy bevy_replicon_renet's code

spring raptor
spring raptor
dire aurora
#

Also works

#

Which confirms it was indeed the just_ part that broke it

spring raptor
spring raptor
#

Because renet itself is not updated.

#

But you can use the branch, it's fully functional.

spring raptor
#

Maybe just help the author to update it?
It's trivial, pretty sure the diff will be identical to the linked PR

#

Most of the diff comes from examples, which you copy-paste and undo changes in parse_cli specific to the backend.

lean thunder
#

Sorry for the noob question here, but I couldn't find it in the docs -- is there a convenient way to tell a client that it "owns" a particular entity that was spawned on the server? Is the best way to just replicate a component storing the relevant backend client ID (as opposed to the replicon client ID which I'm guessing would not work as it's world-dependent), and check against our own?

spring raptor
lean thunder
#

Thanks :)

dire aurora
#

Finished my migration to 0.35 + using the Signature feature. As expected it made my status effects work way better. Still need to make a bevy_bundlication release though so I don't reference some random commit ๐Ÿ˜…

36 files changed, 286 insertions(+), 168 deletions(-)
#

(The extra code is mostly to make sure the client/server actually create identical hashes, since I was using Entity and ComponentId in some of what I would've been hashing)

spring raptor
#

I'm toying with GBA for the last few days ๐Ÿ™‚
I might implement a networking backend for it in the future just for fun. We currently lack the necessary abstraction for it on agb side, but someone started working on it recently. Once done, integrating it with Replicon should be trivial. Just curious if it even runs ๐Ÿ˜…

#

While waiting for it, I'm starting to work on actually useful features.

queen pendant
#

Hi, I can't see any obvious mention of whether resource replication is supported?
I searched this chat and I see some talk about resources as entities, is it waiting for that?

spring raptor
queen pendant
#

Fair, will do.

#

Would it be an idea to add an example to bevy replicon which has the structured mentioned in the docs for headless servers?
That's my use case. Specifically I would want to split it server, client, and shared crates, and I'll run it both in "singleplayer mode" natively but also split into server headless and client wasm

#

Would be nice to have an example which shows best practice for setting up this structure

spring raptor
queen pendant
#

I may find time to do that!

lean thunder
#

Hello! Is there an API to artificially introduce some latency in order to test my prediction/correction code?

#

Maybe in renet?

dire aurora
#

Your best choice would be something OS level

spring raptor
#

But that's because we need it for testing.
Actual backends usually don't include stuff like this because there are os-level tools.

lean thunder
#

Thank you

queen pendant
#

What's the correct way to model something like a UI slider using bevy_replicon?

The client owns the UI, but the server should be authoritative on the value.
I imagine if I drag the slider then there are potentially large bursts of small changes.
Should I client_trigger each of these?

Further, if I have a UI state struct on the client for >1 slider, for example:

struct MyUi {
  slider_1: f32,
  slider_2: f32,
  checkbox: bool
}

then the first thing I would try is to:

  • Add serde + replicated to let the server be authoritative
  • Use it directly on the client to render the ui
  • Add an event derive on it such that a client can client_trigger it to request committing new values

I want to know if this is a bad approach.
I'm using egui which would want to use &mut my_ui.slider_1 directly.
I'm thinking perhaps the client experience will be choppy if replication overwrites the actual state the UI is trying to modify.

queen pendant
#

The "problem" is that e.g. bevy_egui wants to mutate some state locally and update the UI immediately.
While the server which holds the real state runs at a relatively low tick rate. How do you reconcile this elegantly

#

Let's say you keep holding the slider and quickly dragging the value back and forth, when the server starts replying with ACKed values these will be a bit delayed, which I think would cause conflicts if I try to apply it to the client state while it's dragging

#

Should I buffer the server replies until the user stops dragging the slider, then apply?

#

Since the server is authoritative I need to apply what the server tells me to at some point, else the client is out of sync

#

The point is that the UI is a control panel that is displayed on the client, but the server is going to own the real state it represents

#

I know, but at some point I'm going to have to write back what the server replicates to the client side UI state, since the client doesn't own it

#

Maybe I'm simply confused ๐Ÿ˜ฌ I'll try to implement what I thought was problematic then see.

spring raptor
queen pendant
#

Yeah!

spring raptor
#

I'd probably send event with the end value when the client stops using the slider ๐Ÿค”

queen pendant
#

Only thing I don't like is if the slider represents something with scrutiny, e.g. some fine-tuning of something you might want to use the egui hotkey to change the slider very slightly without letting go

#

So being forced to let go of the slider is a bit unergonomic then

spring raptor
#

I think the server ultimatively interested only in the end value

#

Another option is to create an "apply" button or something like that.

queen pendant
#

The slider represents inputs that control GPU work on the server, producing an image that is sent back to the client.

So the client wants to be able to use the slider to see changes applied to the image as quickly as possible (replicating the image efficiently is a separate problem from the slider)

spring raptor
queen pendant
#

Right, so make some kind of debounce on the client side

#

or you put a timer on the "x time since the last change"
Yeah I think I'll want some sort of rate limiter or similar.

Maybe something like this could be a good contender for an example in the repo.
The current examples as far as I can tell have discrete infrequent updates, so there isn't anything showing how to properly do continuous fast updates

#

(which need to be rate limited or similar)

#

Right so having general support for it in bevy_replicon is probably hard if use cases are very different, but an example of a specific use case might help similar use cases still?

#

Since I have these questions I'm sure there are others in similar situations out there

spring raptor
spring raptor
dire aurora
spring raptor
# dire aurora Bit confused on where these masks go

They are stored inside VisibilityMasks component on the entities that represent connected clients.

But after writing this, I wonder if we can use components instead of masks ๐Ÿค”
So instead of doing something like this:

const GUILD_MEMBER: u32 = 0b1;
app.set_visibility_mask::<C>(GUILD_MEMBER);

We would do something like this:

app.set_visibility_component::<GuildMember, C>();
spring raptor
#

Drafted a small patch with recent improvements.

spring raptor
dire aurora
spring raptor
# dire aurora I don't see how this could work per-client, since being in one guild doesn't mak...

Yes, I didn't think this through at the time of writing, but I expanded on this:
https://github.com/simgine/bevy_replicon/issues/304#issuecomment-3393406488

GitHub

I'd like the ability to not only be able to define which entities are visible to specific clients (possible with the bevy_replicon::server::VisibilityPolicy) but also be able to define what com...

spring raptor
#

So it's like bitmasks, but dynamic. But not entirely sure if the complication is worth it.

lean thunder
#

After calling TrackAppExt::track_mutate_messages, I'm panicking with the error telling me that ClientMessages resource does not exist... docs and source both say it is inserted by ClientPlugin... not really sure how to troubleshoot this

#

Hold on there's actually an earlier panic in another thread, let me try and resolve that one and see if it fixes the other

#

The panic is caused by the call to Bytes::advance in client::apply_mutations. The assertion that data_size <= message.len() is failing. I will have a deeper look later, idk if this is a bug in replicon or in my code

spring raptor
spring raptor
lean thunder
#
  1. Yes, 2. No
spring raptor
#

We have a ton of tests, but I feel like I might've missed some combination of this and other things.

lean thunder
spring raptor
lean thunder
#

I have a custom WriteFn when associated with a marker component Predicted<C>. We expect that an entity with Predicted<C> also has a History<C>. The history component is updated for all entities that have it at the end of each physics tick. The custom WriteFn checks the deserialized component against the one for the corresponding tick in that entity's history and if there is a mismatch it inserts a flag that indicates we need to initiate a rollback from (at least) that tick. Right now the only component that is registered to be predicted is Bevy's Transform component

#

I can try to create a minimal repo in a bit

spring raptor
spring raptor
lean thunder
# spring raptor Maybe it's somehow cause by your `WriteFn`

I'm not sure where else the problem could be, but I really can't see anything suspicious in it

fn write_actual<C>(
    ctx: &mut WriteCtx,
    rule_fns: &RuleFns<C>,
    entity: &mut DeferredEntity,
    msg: &mut Bytes,
) -> Result
where
    C: Component<Mutability = Mutable> + PartialEq,
{
    let c = rule_fns.deserialize(ctx, msg)?;

    let Some(history) = entity.get::<History<C>>() else {
        error!("no history for predicted component on {}?", entity.id());
        return Ok(());
    };

    let Some(expected) = history.get(&ctx.message_tick) else {
        error!(
            "no history for tick {} for {}",
            ctx.message_tick.get(),
            entity.id()
        );
        return Ok(());
    };

    if *expected != c {
        match entity.get::<DesyncTick>() {
            Some(DesyncTick(t)) if *t < ctx.message_tick => {}
            _ => {
                entity.insert(DesyncTick(ctx.message_tick));
            }
        }
    }

    Ok(())
}
spring raptor
spring raptor
#

@lean thunder are you sure you enabled it on both client and server?

lean thunder
#

Oops, I definitely did not enable it on the server!! ๐Ÿ˜…

#

My bad. Sorry for wasting your time there

spring raptor
spring raptor
# spring raptor Yes, I didn't think this through at the time of writing, but I expanded on this:...

Playing with this. I really like this system.
It basically allows defining visibility seamlessly without maintaining a separate source of truth.
Want your client to see only Health for components of team members? Just call app.add_component_visibility::<Team, Health>(), where Team implements PartialEq.
So if a game entity has Team(1) and the connected player has Team(1), Health will be visible to them.

It is quite expensive to compute though. I need to dynamically fetch components from clients, and FilteredEntityRef provides checked access (which I need to avoid).
I could write my own QueryParam, but maybe there is a different way.
When I collect changes, I basically need to know things like "Is this entity/component visible?". So maybe I can split this into two layers:

  1. A low-level mechanism that directly tells me what is visible when I iterate over entities. It's managed by the higher-level.
  2. A component-based API with observers that cache the data for the low-level mechanism.
    Still exploring which path forward will be the best.
dire aurora
#

Yea this approach seems better. One thing I'm not sure about is if component visibility as a single yes/no is enough. Like for this example, what if we want to still send a full or empty value to people on other teams?

#

In this case we can certian work around it, but I'm wondering if we can always to so easily ๐Ÿค”

#

Also we can potentially optimize things here in cases like: the entity has a Team, but no Life, and no other components that need Team for visibility

#

Also VisibilityComponent needs to match between client entity and game entity? I think that would be limiting for say physical rooms, you can see rooms that are connected to yours, thus need a list on the client, but a single id on the game entities

#

In that case you might want an associated component that the client has, and default it to Self (if that's even allowed)

spring raptor
spring raptor
spring raptor
queen pendant
#

Is it possible to preserve relationships from server to client?

I have a parent entity on the server that is replicated.

Some time later in the program I spawn a collection of children on that parent.
The client receives them "flattened out", not as children but at the top level.

I tried sync_related_entities::<ChildOf> on the server but seems that's more about not splitting up replication I think.

I'm on 0.34.4 if it matters

queen pendant
#

Ah I just needed replicate::<ChildOf> on client and server, saw there was some notes in the guide for that.
It works now

spring raptor
spring raptor
#

It's fully working, with tests, docs, and examples.
It's a draft only because I am considering changing the internal tracking from hidden entities to visible entities. This should avoid awkward checks in the code, since I can't easily distinguish between an
entity despawning and gaining visibility. It will also be more memory-efficient for games with many players, which is where memory usage actually matters.

So you can already check the API and give me feedback if you want.

#

It's only entity-based visibility for now, but I'll implement component-based visibility in a follow-up by extending this API.

spring raptor
#

@echo lion marked as ready for review ๐Ÿ™‚
Most of the code comes from tests, there aren't a lot of code.

#

This will break bevy_replicon_attributes, but you should be able to port it by turning VisibilityCondition into a visibility filter.

lean thunder
#

@spring raptor where can I find the bit of code that handles the client receiving an out of order replication message?

#

for example receiving a mutation for tick 1 after already receiving a mutation for tick 2

lean thunder
#

I feel that I read somewhere some time ago that mutations for replicated components on the same entity are applied on the same tick, but I now can't find evidence of this -- did I dream it?

dire aurora
#

We network things per-entity, but priority and send rates could break that guarantee

spring raptor
lean thunder
#

Yep, that's exactly what I meant. Thanks!

spring raptor
# dire aurora We network things per-entity, but priority and send rates could break that guara...

We no longer have send rates; the new prioritization system replaced them.
And it's per-entity, like in other engines, so nothing breaks this guarantee right now.
So you don't need to handle it in bevy_rewind ๐Ÿ˜…

I can see how it could be useful to have a different send rate per component,
so we might reintroduce it later, but we need more bookkeeping to implement it properly.
This is something that will be possible after per-component visibility.

#

(becase per-component visibility requires more tracking)

dire aurora
spring raptor
queen pendant
#

Hi, I get

23v1 exceeded memory limit, disconnecting log.target = "aeronet_transport";
log.module_path = "aeronet_transport";

even though I have increased the transport mem limit to fit messages of at least the size I'm sending.

My guess is that the websocket transport (or somewhere higher up) queues up more messages than I'm able to send.
Is it likely this is the case? Is there a way for me to control this?

spring raptor
lean thunder
#

Finally got my client-side prediction working ๐Ÿ˜…

#

Only took multiple weeks

dire aurora
#

If the prediction only takes weeks it's pretty fast thonk

lean thunder
#

It's pretty simple prediction and I was working several hours per day

spring raptor
lean thunder
#

I would like to battle test it a bit more first but I'm thinking I'll publish a crate

#

I still need to implement interp and lag compensation so I might see if I can include all of that

spring raptor
#

Looking forward to it.
It's all quite an advanced topic, pretty sure you find edge cases ๐Ÿ˜…
It's also requires a remote input manager to send inputs efficiently. Are you planning to implement it too?

lean thunder
#

I'm not sure what you mean by that, is there some resources you could point me to?

spring raptor
# lean thunder I'm not sure what you mean by that, is there some resources you could point me t...

Here is a really good video that explains the approach:
https://www.youtube.com/watch?v=W3aieHjyNvw&t=1528s

In this 2017 GDC session, Blizzard's Timothy Ford explains how Overwatch uses the Entity Component System (ECS) architecture to create a rich variety of layered gameplay.

Register for GDC: https://ubm.io/2yWXW38

Join the GDC mailing list: http://www.gdconf.com/subscribe

Follow GDC on Twitter: https://twitter.com/Official_GDC

GDC talks cover...

โ–ถ Play video
lean thunder
#

Ohh, I have seen this a few times actually

#

So you are talking about input buffering and timeline dilation or?

spring raptor
#

Yeah, input buffering ๐Ÿ™‚

lean thunder
#

I see

spring raptor
#

It's probably worth unifying the effort.

lean thunder
#

I put off implementing it for the time-being due to the complexity and I wanted to get onto other areas of my game, but I'm definitely interested in it

spring raptor
#

The rollback approach in bevy_rewind is usually more costly than classical rollback, but it works better for physics-based games.
I assume you're working on a classical approach, where rollback happens per-entity, right?

lean thunder
#

Right

spring raptor
#

I was thinking about unification. Mainly, these things are independent of rollback approach:

  • Input buffering
  • Component history buffering
  • Clock sync

In networking, games usually rely on fixed updates. To make things smooth, interpolation is incredibly common.
There is https://github.com/Jondolf/bevy_transform_interpolation, which is planned for upstreaming into Bevy. Currently, it interpolates between fixed ticks. This works for listen servers (where the server is also a client). But for pure clients, we need to interpolate between network snapshots. I inspected the code, and it's a very simple change: just allow control of overstep via component. I talked with the author, and he agreed to extend it. This way, we will get interpolation, Hermite and extrapolation โ€œfor free.โ€

But to control overstep, we need component history buffering and clock sync. So I'd probably "upstream" this into Replicon and provide interpolation out of the box (behind a feature).
This should also reduce the amount of code needed for rollback crates, such as yours and @dire aurora's.

As for input buffering, I'm not sure if we need to move it inside Replicon. Probably fine keeping as a separate crate. However, @dire aurora's implementation needs a bit of work: we need to integrate it with https://github.com/simgine/bevy_enhanced_input (my another crate ๐Ÿ™‚). That's a crate for high-level input which is planned for upstreaming in 0.18.

#

Component history unification could be a good first step.
@dire aurora already have efficient implementation (I used some of its bits to insert components in batches during replication), I just need to extract it and make a proper review ๐Ÿ™‚
@lean thunder you probably also have your own implementation too. Once you publish, I'll be able to check it as well.

So we'll replace track_mutate_messages with something like buffer_component<C> which automatically enables sending all mutate messages and provides a component that holds history.
I'm planning to get to it right after per-component visibility. But if you want to help - PRs will be very welcome!

#

Just sharing my plans. If you want to focus on the crate - it's totally fine.
We can always upstream common parts to Replicon later.

lean thunder
#

All sounds really exciting!! Hopefully I'll be able to help ๐Ÿ™‚

#

What a great time to be working with bevy

empty musk
#

I am currently migrating, everywhere where i used to have client_trigger_targets i will now have to change the trigger to include the target like below right?


#[derive(Debug, Deserialize, Event, Serialize, MapEntities)]
pub struct ClientShipPosition{
    pub position: Vec3,
    #[entities]
    pub entity: Entity
}```
spring raptor
spring raptor
spring raptor
#

Do you think it's reasonable to have > 32 replicated components on a single entity? ๐Ÿค”

lean thunder
#

Definitely not an issue in my project for the time being, but admittedly it doesn't seem entirely unreasonable to me

#

Or at least reasonably out of the realm of possibility

dire aurora
#

I can imagine scenarios where that would happen

spring raptor
#

That's what I thought, thanks!
Asking because I'm planning to use bitmasks to track which components client received (needed for per-component visibility).
I'll go with https://docs.rs/smallbitvec/latest/smallbitvec/index.html then. For 62 components it's pointer-sized and on 63+ components it will dynamically-allocate (on 64-bit systems).

#

@dire aurora @lean thunder But do you think it's reasonable to have up to 32 components that affect visibility or it's a bit too limiting?

#

Not on a single entity, global.

dire aurora
#

Hmmmm, layering 32 visibility things would be hard ... Maybe in a complex game if they are required to be compile-time known?

#

In my game I could configure them per server instance type if that's supported so I could work with that even if I end up with many such systems

#

Might cause bitmask mismatches if that's networked though ๐Ÿค”

spring raptor
#

Since I'm planning to use it for per-component tracking, I'm just wondering if it's worth using it for visibility tracking as well.

#

I.e., since the dependency will already be in the tree, the only downside of using SmallBitVec is the increase in RAM usage.

lean thunder
#

Memory is pretty cheap. Always the possibility of feature-gating it but I'm guessing that will add a lot of maintenance complexity

dire aurora
#

If this exists purely in the server I think 32 or 64 would be fine, the overhead of having a smallbitvec is probably more noticable

spring raptor
#

Ah, yes, there is definitely a tiny overhead in checking the upper bits.

dire aurora
#

Also I'd worry about all the overhead on checking if there is crazy number of visibility systems, so combining those that you can would be the way to go

#

Like you can't combine party status + distance + room, but do you really need 7 different room systems? Doubt it

spring raptor
dire aurora
#

Yea, the dynamic case seems concerning since MMOs are also the ones most likely to have enough features for this

#

And they shouldn't accidentally create massive overhead like that

spring raptor
#

Thanks for the feedback!
So I'll keep u32 for visibility bitmasks and use SmallBitVec for tracking sent components (since 32 or 64 components on an entity might be limiting).

lean thunder
#

Sounds very reasonable :)

spring raptor
# spring raptor Merged. Will start working on extending it to per-component visibility.

Spent last week making optimizations and refactoring needed for this. The component tracking should be the last piece. It's useful on its own because it adds support for entities that starts to match rules incrementally. It's a niche use case, so I had explicitly avoided supporting it earlier due to the extra bookkeeping (no one complained ๐Ÿ˜… ). But since we now need this tracking for component visibility, this case will start working automatically.

dire aurora
#

"support for entities that starts to match rules incrementally" ... Wait, what?

spring raptor
#

So if you have a replication rule, the insertion can't be split across ticks.

dire aurora
#

Oh

#

Ooooh

#

This has caused me a few rare crashes I think ๐Ÿคฃ

#

Though I'e never had cases where that was supposed to happen, it just shouldn't have caused a crash, just an undesirable 1 frame delay

spring raptor
#

Yeah, it's weird behavior... Even though it's rarely needed, handling it like this isn't great.
But it should be fixed when we start tracking which components were sent.

dire aurora
#

I guess it could cause even sillier behavior with filters?

#

Like you have A, With<B>, and add A, then B, and send nothing

spring raptor
spring raptor
brittle mulch
sinful reef
#

Does anyone have a simple example of using this crate with client auth movement I'm stuck with wondering how to handle networked players and the local player simultaneously

spring raptor
sinful reef
spring raptor
lunar bone
#

hey! was wondering if anyone had an examples of using avian-based physics on the server for checking for collisions? i'm used to just having the physics in non-networked games, so curious how this would work with a server authority and replication ๐Ÿ™‚

spring raptor
dire aurora
#

I mean I am ofc planning to finish it as it is important for my game, but there's a couple of design requirements that are still a bit up in the air right now so I'm trying not to mess with it too much atm

#

Besides some wrong handling of edgecases and missing data it all works fine atm. Main is on 0.17 too now

spring raptor
#

And if you plan to change the API, are you currently focused on the game or on your crates?
Just curious ๐Ÿ™‚

dire aurora
#

Ah, the input stuff is actually very easy atm. You just add both plugins and set up the schedules the right way, can't improve that much until we get better FixedUpdate input stuff

#

As for avian, I might make a crate just for avian integration at some point, though tbh my current integration is still a bit of a hack so I have no clue if it works for every game

dire aurora
spring raptor
dire aurora
#

Yea, no special integration for LWIM or BEI currently exists, I'll have to see how viable the latter is once I switch my game over

spring raptor
#

But no pushing, If you're busy with other stuff that's okay. We can make breaking changes after upstreaming. It's just not as fast.

dire aurora
#

Ah, I should probably try that soon then ... My whole input handling has some technical debt I need to tackle anyway

#

Worst case ofc, the same strategy I do now is probably still possible, but I do hope I can make something prettier, because I have some really ugly input systems

#

This is the code for generating the inputs for combat ... This luckily only runs on the client, and everything in my simulation deals with a simpel component with 3 fields having the exact relevant info ... But damn is it annoying when there's bugs here ๐Ÿคฃ

spring raptor
#

The BEI approach differs significantly from how input management is usually done, that's why I had to create a separate crate instead of just contribute to LWIM.

#

But I suspect that the migration will be a pain ๐Ÿ˜ข

spring raptor
dire aurora
#

Luckily I can start by just generating the correct data into my input struct, which doesn't require migrating as I can just delete this function and write something new ๐Ÿคฃ

#

Then see from there if I can make something without room for worsening of desync (some examples can be seen if you look at the 2 todos and generally the number of ifs before an input is set) or cheating if I network at the BEI level

spring raptor
#

Great!
I'm open to make changes to the crate if necessary.
We now have a better quick start guide, I'd highly suggest to give it a read first ๐Ÿ™‚

dire aurora
#

I have also seen claims of it benefiting from BSN, is it worth grabbing cart's branch first?

spring raptor
spring raptor
#

I noticed that in docs we reference deprecated event names.
Since they are hidden in the docs, it results in incorrect links. But this doesn't trigger a warning, we check docs on CI ๐Ÿค”

sinful reef
#

How would I handle players with signatures and prevent hash collisions my player component is just a marker struct does replicon have builtin client IDs that are synced between the client and the server? or does that depend on my backend

dire aurora
#

Depending on the backend you can access a NetworkId iirc

dire aurora
#

Hmmm, actually getting a funny class of inputs that aren't coming from user inputs, this might make something like LWIM or BEI integration impractical ๐Ÿค”

#

Basically an input might merely put the client in a state where they start sending input based on something that happens (eg where the camera is aiming)

spring raptor
dire aurora
#

With how my game handles it right now this specific example can be one of 3 things:

  • A direction (this is just the direction from the player to where the camera hits a collider)
  • A position on the ground, this might just be where you aim, or the point on the ground from where it hits or where the range limit of a skill applies
  • An entity that you targeted (this one is especially nasty since you could target an entity between ticks)
#

-# I mean this specific input can also be in the states "clear" and "unchanged", but those aren't that special

spring raptor
dire aurora
#

Well yea, the direction is essentially what I'm sending (with some minor details that are left up to the client cause it could be a setting, eg that "the point on the ground at the range limit" could be a client setting)

dire aurora
#

But the direction of the camera isn't really an input, is it? ๐Ÿค”

#

-# I mean it is for the simulation in this case ofc

spring raptor
dire aurora
#

I do always buffer my inputs with how I process them yea, the code just writes to components and accumulates data there until the simulation clears it after it's done

spring raptor
dire aurora
#

Well, I do still need to send the direction as a valid part of an input for a simulation tick

#

The logic in my game is that once you release the skill button the client can generate aim inputs, and they get accepted until the skill has locked its aim

#

They need to be identical between client and server for that entire span of ticks

#

For some skills the input for accepting aim changes iss the entire skill long, until the effect ends and the recovery animation plays (I mean I have no animations but the code has the concept of them)

spring raptor
#

Got it! Maybe it makes sense to avoid coupling BEI actions with what you send over the network.
And I think it might be even more efficient.

dire aurora
#

Yea, the efficiency is definitely highest if you manually pick what you network

#

Some fighting games fit their entire inputs in a single u16 after all

#

Sending the past few frames of input is very cheap at that point!

spring raptor
#

Yeah, it's much more efficient than using syncing actions. You'd need to send the entity, state, and value.

#

But the downside is that on the client you need to react on the logic from BEI, but on the server you react on client data ๐Ÿค”

#

Or do you handle it somehow?

dire aurora
#

Currently I just collect inputs into this struct (and another one for movement):

pub struct ActionInput {
    /// When `true` cancel the current action, if possible
    pub cancel_action: bool,
    /// Activate an action, if possible
    pub activate_action: PlayerAction,
    /// The aim for the current action
    pub aim: SetAim,
    // action_event: ActionEvent,
}
#

Then the simulation always reads this component and uses that to run the simulation

spring raptor
#

Ah, makes total sense.

dire aurora
#

Though my current logic for creating these inputs is all very brittle, the aim thing is clearly a seperately calculated input, gated on an input-based condition, but right now it's implemented more as a sort of state machine ๐Ÿ˜…

#

But the input itself seems fine for the most part

spring raptor
spring raptor
dire aurora
#

Using observers can probably simplify things yea

#

LWIM's design kinda makes it easier to write highly coupled systems

spring raptor
dire aurora
#

While BEI encourages you to consider just what needs to happen for an action, not how that ties into everything

#

Yea, but you can't use LWIM like BEI ๐Ÿ˜…

spring raptor
#

Yeah, sometimes observers are more convenient, but sometimes coupled systems fit better ๐Ÿ™‚

#

I'd say observers usually more convenient ๐Ÿค”

#

But there are cases where I'd use systems

sinful reef
#
2025-11-04T20:34:00.798071Z ERROR bevy_panic_handler: Fatal Error
Unhandled panic! at C:\Users\~\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\bevy_ecs-0.17.2\src\error\handler.rs:125:1:
Encountered an error in system `(bevy_ecs::world::filtered_resource::FilteredResourcesMut, bevy_ecs::world::filtered_resource::FilteredResourcesMut, bevy_ecs::change_detection::ResMut<bevy_replicon::shared::backend::client_messages::ClientMessages>, bevy_ecs::change_detection::Res<bevy_ecs::reflect::AppTypeRegistry>, bevy_ecs::change_detection::Res<bevy_replicon::shared::server_entity_map::ServerEntityMap>, bevy_ecs::change_detection::Res<bevy_replicon::shared::message::registry::RemoteMessageRegistry>, bevy_ecs::change_detection::Res<bevy_replicon::client::ServerUpdateTick>)`: Parameter `ResMut<ClientMessages>` failed validation: Resource does not exist
If this is an expected state, wrap the parameter in `Option<T>` and handle `None` when it happens, or wrap the parameter in `If<T>` to skip the system when it happens.

Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
error: process didn't exit successfully: `target\debug\quinnet-testing.exe client` (exit code: 101)

I'm getting this panic but it seems to be a replicon issue any way to fix this?

spring raptor
sinful reef
#

these are my plugins

spring raptor
sinful reef
# spring raptor This looks correct. How do you handle features?

wdym?

this is my cargo.toml if thats what your asking:

bevy = { version = "0.17", features = ["serialize"] }
bevy-panic-handler = "6.0.0"
bevy_asset_loader = "0.23.0"
bevy_enhanced_input = "0.19.2"
bevy_quinnet = { version = "0.19.0", default-features = false }
bevy_replicon = "0.36"
bevy_replicon_quinnet = "0.15.0"
bevy_rand = { version = "0.12.1", features = ["wyrand"] }
rand = "0.9.2"
clap = { version = "4.5.51", features = ["derive"] }
serde = { version = "1.0.228", features = ["derive"] }
spring raptor
spring raptor
spring raptor
# sinful reef ill check

You can try to call init_resource::<ClientMessages> manually, but it's sus... Ensure that ClientPlugin is actually initialized.

sinful reef
# spring raptor You can try to call `init_resource::<ClientMessages>` manually, but it's sus... ...

I manually added Client plugin and now this error happens instead:

thread 'main' panicked at C:\Users\~\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\bevy_replicon-0.36.1\src\client.rs:86:40:
Requested resource bevy_replicon::shared::AuthMethod does not exist in the `World`.
                Did you forget to add it using `app.insert_resource` / `app.init_resource`?
                Resources are also implicitly added via `app.add_message`,
                and can be added by plugins.
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\quinnet-testing.exe client` (exit code: 101)
spring raptor
spring raptor
sinful reef
#

btw the server works fine its only the client getting these panics

#

heres my code btw

#

ik its really messy but I've just been experimenting with bevy replicon and quinnet

spring raptor
spring raptor
sinful reef
#

oh yeah forgot to use the rc

spring raptor
#

When you pull 2 different Bevy versions, plugins register resources for 0.17, but your main might use 0.16 and the resources simply won't be available.

sinful reef
#

kk ill try running it now

#
Unhandled panic! at C:\Users\~\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\bevy_replicon-0.36.1\src\shared\replication\signature.rs:311:10:
Requested resource bevy_replicon::shared::replication::signature::SignatureMap does not exist in the `World`.
                Did you forget to add it using `app.insert_resource` / `app.init_resource`?
                Resources are also implicitly added via `app.add_message`,
                and can be added by plugins.
Encountered a panic in system `bevy_replicon::client::receive_replication`!
2025-11-04T22:21:50.330998Z ERROR bevy_panic_handler: Fatal Error
Unhandled panic! at C:\Users\~\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\bevy_ecs-0.17.2\src\error\handler.rs:125:1:
Encountered an error in system `(bevy_ecs::world::filtered_resource::FilteredResourcesMut, bevy_ecs::world::filtered_resource::FilteredResourcesMut, bevy_ecs::change_detection::ResMut<bevy_replicon::shared::backend::client_messages::ClientMessages>, bevy_ecs::change_detection::Res<bevy_ecs::reflect::AppTypeRegistry>, bevy_ecs::change_detection::Res<bevy_replicon::shared::server_entity_map::ServerEntityMap>, bevy_ecs::change_detection::Res<bevy_replicon::shared::message::registry::RemoteMessageRegistry>, bevy_ecs::change_detection::Res<bevy_replicon::client::ServerUpdateTick>)`: Parameter `ResMut<ClientMessages>` failed validation: Resource does not exist
If this is an expected state, wrap the parameter in `Option<T>` and handle `None` when it happens, or wrap the parameter in `If<T>` to skip the system when it happens.

Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
error: process didn't exit successfully: `target\debug\quinnet-testing.exe client` (exit code: 101)
#

nope still panics

#

it appears to be something with my code because reverting my main.rs file to an earlier commit fixed the panic

spring raptor
#

If you see "bevy <version number>" in dependencies, it's the problem.

sinful reef
spring raptor
sinful reef
#

I cant find anything that would change how the plugins or resources would get initialized

spring raptor
spring raptor
# sinful reef last working commit was https://github.com/PizzaLvr49/quinnet-testing/commit/4f6...

Ah, I see what's happening.
You have a Player entity that requires Signature. But you spawn it on the server and replicate it to the client. When replication is applied, the SignatureMap and a bunch of other resources are temporarely removed from the world (via resource_scope).
Since the insertion of Player inserts Signature, it tries to access SignatureMap and panics because it doesn't exist.
But since you use a panic handler, the application loop continue and you encounter a panic in other system because now other resources are missing.
I definitely should handle this nicer, will turn into an error logging message tomorrow.

TL;DR; Remove Signature from required component on Player. You don't want to automatically insert it and replicate the player.
You need to either spawn them separately on client and server or receive replication from the server.

queen pendant
#

Hey just to confirm, there is no support for replicating Bevy states right?
And the reason is probably because states are backed by resources (which are not yet entities in Bevy)?

spring raptor
sinful reef
dire aurora
#

I think on the client you retrieve it directly from your backend

sinful reef
dire aurora
#

I mean that works too, client entities are one of the things you need to prespawn the least since you could just have the server tell you which is yours

#

Alternatively the client can tell the server what identifier to use (like how in the past the client sent their local entity and the server then mapped that)

#

The latter is how I do it in my bevy_rewind example, the client just sends a timestamp ... Not ideal for production games but good enough for an example

sinful reef
#

having the server tell the client their Id isn't much of a hassle just would've been nice if my backend exposed it since it does log it when the client connects

#

2025-11-05T21:09:34.812132Z INFO bevy_quinnet::client::connection: Connection 0 connected to [::1]:5000 with client_id Some(1)

dire aurora
#

bevy_quinnet itself does allow you to get the client and from that the client id

dire aurora
#

Afaict you get the QuinnetClient, then call .connection() then .client_id()

sinful reef
dire aurora
#

Hmmm, the ConnectionEvent I guess?

sinful reef
#

ill try

sinful reef
# dire aurora Hmmm, the `ConnectionEvent` I guess?

bevy_quinnet::client::connection::ConnectionEvent is not an Event
the trait bevy::prelude::Event is not implemented for bevy_quinnet::client::connection::ConnectionEvent
consider annotating bevy_quinnet::client::connection::ConnectionEvent with #[derive(Event)]
the following other types implement trait bevy::prelude::Event:
AcquireFocus
CheckChangeTicks
Fire<A>
FocusedInput<M>
FromClient<T>
ProtocolHash
ProtocolMismatch
ReadbackComplete
and 14 others

#

:/

dire aurora
#

It's actually a Message ๐Ÿคฃ

sinful reef
tacit osprey
#

Uh, yes, I might rename those or make them derive Message and Event. When updating to bevy 0.17 I just thought that ConnectionMessage would be a confusing name for a network crate ๐Ÿ˜ซ

tacit osprey
spring raptor
spring raptor
# spring raptor Fixed: https://github.com/simgine/bevy_replicon/pull/604

Originally, I used hash sets for simplicity, but I pushed a commit that switches to SmallBitVec as I originally planned. This significantly improved performance.
It's still not as fast as tracking changes per entity, but it's not that much slower. I think it's a worth price to pay ๐Ÿ™‚

sinful reef
#

how would I send a response to the client like this:

fn on_message(message: On<TestMessage>, mut commands: Commands) {
    commands.server_trigger(ToClients {
        mode: SendMode::Direct(???),
        message: TestMessage(format!("Recieved: {}", message.0)),
    });
}
#

Idk where to get the client ID that sent or if I should include it in the message

#

nvm

#

forgot to read docs

sinful reef
#

How would I split players into 'rooms' like for matchmaking

#

does replicon have anything like that or should I just make it myself

#

ig this is more of a question for #networking

spring raptor
#

And the server is where replicon runs

clear forge
#

Im getting this error with Replicon 0.34.4 and aeronet_replicon 0.16.1

ignoring event `bevy_replicon::shared::event::server_trigger::ServerTriggerEvent<shared::SetLocalPlayer>` that failed to deserialize: unable to map entities `[270v1#4294967566]` from the server, make sure that the event references entities visible to the client

The server triggers an event on ConnectedClient:

fn spawn_clients(
    trigger: Trigger<OnAdd, ConnectedClient>,
    mut commands: Commands,
    mut visibility: Query<&mut ClientVisibility>,
    mut client_player_map: ResMut<ClientPlayerMap>,
) {
    let player = commands
        .entity(trigger.target())
        .insert((
            Player {
                color: *fastrand::choice(PlayerColor::all_variants()).unwrap(),
            },
            Transform::from_xyz(250.0, 0.0, Layers::Player.as_f32()),
            GameSceneId::lobby(),
            Owner::Player(trigger.target()),
            Health { hitpoints: 200. },
        ))
        .id();

    client_player_map.insert(player, player);

    for mut client_visibility in visibility.iter_mut() {
        client_visibility.set_visibility(player, true);
    }

    commands.server_trigger(ToClients {
        mode: SendMode::Direct(player),
        event: SetLocalPlayer(player),
    });
}

and on the client:

fn init_local_player(
    trigger: Trigger<SetLocalPlayer>,
    mut commands: Commands,
    camera: Query<Entity, With<Camera>>,
) {
    let player = trigger.entity();
    let mut player_commands = commands.entity(player);
    player_commands.insert((ControlledPlayer, SpatialListener::new(50.0)));
    commands
        .entity(camera.single().unwrap())
        .insert(CameraFollow::fixed(player).with_offset(Vec2 { x: 0., y: 50. }));
}

How can i fix this error message ?

spring raptor
#

It's also quite an old version ๐Ÿค”

#

Ah, you probably not migrated to 0.17 yet. But this shouldn't be a problem.

clear forge
#

I didnt had this error with replicon 0.33

spring raptor
#

The error means the client knows nothing about this entity.

#

And MapEntities is implemented for the event properly?

clear forge
#
#[derive(Event, Clone, Copy, Debug, Deserialize, Serialize, Deref, DerefMut)]
pub struct SetLocalPlayer(Entity);

impl MapEntities for SetLocalPlayer {
    fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
        self.0 = entity_mapper.get_mapped(self.0);
    }
}
spring raptor
#

Looks correct to me ๐Ÿค”

clear forge
#

I can also try version 0.35

spring raptor
spring raptor
clear forge
#

But VisibilityPolicy::Whitelist worked in version 0.33.

spring raptor
#

I just completely reworked the visibility system recently ๐Ÿ˜…

clear forge
#

I'll need some coffee for that.
If everything works, you wont hear from me :).

Thanks again and also for this amazing crate !

spring raptor
clear forge
#

Sure, that would be nice!. We really rely heavily on the visibility system to reduce bandwidth.

Do you have a https://buymeacoffee.com/ link or something familiar ?
We want to give something back for your lifesaving work.

spring raptor
dire aurora
#

Are these supposed to ever happen?

2025-11-08T13:36:02.317989Z  WARN bevy_replicon::shared::server_entity_map: ignoring duplicate mapping from 198v0 to 298v1
spring raptor
#

I'd assume you have different signatures have the same hash, but you're mapping the same entity.

dire aurora
#

Ah, so it's because I somehow spawn duplicate entities with the same hash ... Yea, that's possible, I seem to get desync now that I'm porting to bevy_enhanced_input, so it's very possible that causes extra spawns

spring raptor
#

Would you prefer to define filters via trait or via functions?

Via trait:

impl VisibilityFilter for TestVisibility {
    type Kind = Entity;

    fn is_visible(&self, entity_filter: &Self) -> bool {
        // ..
    }
}

For components:

impl VisibilityFilter for TestVisibility {
    type Kind = (A, B);
// ..
}

But for a single component I need something like this:

impl VisibilityFilter for TestVisibility {
    type Kind = Single<A>;
// ..
}

Via functions for entities:

app.add_visibility_filter::<F>();

For components

app.add_components_visibility_filter::<F, (A, B)>();

Single component:

app.add_component_visibility_filter::<F, A>();

I think via functions it's a bit too verbose and difference between pluaral ans single too subtle ๐Ÿค”
But Single for trait approach is a bit ugly... But maybe it's a lesser evil?

dire aurora
#

Is this for registering components to a visibility filter?

spring raptor
#

You always need to call add_component_visibility_filter for both versions.
The difference is wheter to add Kind to the trait or additional registrational helpers.

dire aurora
#

Ah, I think I'd need to see a bit more of a complete example to make a propper judgement then

sinful reef
sinful reef
#

Why would you need to do Single<A> and not just A

spring raptor
#

It's possible to do this:

type Kind = (A,);

But it's very ugly ๐Ÿ˜…

spring raptor
#

Another option is to get rid of Kind and have different regitration functions for entity, single component and multiple components. But it's quite wordy...

lean thunder
# spring raptor

I'm pretty confused and would love an example that actually shows an is_visible implementation...

For example, how would I express that I only want the client that "owns" entity x to be able to see component A on x?

spring raptor
# lean thunder I'm pretty confused and would love an example that actually shows an `is_visible...

It's already in the latest master:
https://github.com/simgine/bevy_replicon/blob/a1fa75c4e3dd648261be83012b91a010a98a189b/src/server/visibility.rs#L16

I was asking about configuring which filter gives visibility to entities and which one to components.

GitHub

A server-authoritative replication crate for Bevy. - simgine/bevy_replicon

lean thunder
#

My bad.

spring raptor
#

It's not released yet ๐Ÿ™‚
I just reworked the visibility system first (with a bunch of other related internal changes) and now I adding per component visibility to this system.
Nothing is final, and the documentation will also be improved.

lean thunder
#

The trait seems like a good way to do it

#

Since it's merged into master I'll give it a go in my project and let you know if I have any API feedback

spring raptor
lean thunder
#

Oh, I just saw this is entity visibility that's merged (not per-component). I was wondering how I had missed it. I have to admit I'm extremely tired this evening

It's less immediately useful for my game but if I think of a use-case I'll for sure try it out

spring raptor
#

It's a follow-up to the new visibility system.

dire aurora
#

Hmmm, looking at the example, the only thing I'm wondering is if it's preferable to define the filters in one place, then define which filter components use, or the other way around

#

The latter would look nicer with the trait I think, the former wouldn't work like that at all I think? ๐Ÿค”

spring raptor
dire aurora
#

So lets say I define my filter in one module, and my components that get the visibility applied to them in another module

#

Or even multiple crates

#

What would the dependencies look like between the filtering logic and the replicated components

spring raptor
sinful reef
#

Im trying to get my networking to work but im confused with how im supposed to use like signatures for having consistency on the client and server the repo with my code is linked here #1437634489441456341

#

Im kinda confused with trying to find whats wrong but I think someone else taking a fresh look at my code might help

spring raptor
brittle mulch
#

hmm curious does rewind impl interpolation?

dire aurora
#

Nope, though the design of rewind and bevy_replicon should allow another crate to do interpolation alongside it ... Iirc Shatur was considering making interpolation either a first party thing or as an official add on crate

#

In theory it shouldn't be too hard if there is a good extensible interpolation crate available after all

spring raptor
#

Yes, my plan is to integrate https://github.com/Jondolf/bevy_transform_interpolation
It can already be used as is in listen server, but for clients we need to interpolate between the received snapshots. Jondolf agreed on extending the crate to support this, but I'm busy with per-component visibility right now.

GitHub

Transfom interpolation for fixed timesteps for the Bevy game engine. - Jondolf/bevy_transform_interpolation

sinful reef
spring raptor
# sinful reef I dont really know whats wrong with my code but its not working I dont see any r...

Looking into someone else's project is quite time-consuming, and "not working" is quite vague ๐Ÿ™‚
I'd recommend asking specific questions. We also have extensive logging - you can try enabling it to see what's happening under the hood: https://docs.rs/bevy_replicon/latest/bevy_replicon/#troubleshooting

I'm having electricity outages, and when I have both free time and electricity, I want to focus on implementing features ๐Ÿ˜…

spring raptor
# spring raptor You interested in per-component visibility specifically? No problem; I think I'l...

It will take more time. I have a somewhat working implementation, but it's very inefficient because of how removals are currently handled (I need to calculate function IDs for all components to properly handle rule layering).
While it was okay-ish for removals, I don't like using it for visibility lost.
But I know how to optimize it using archetype information. It will improve both removals and handling of component visibility lost.

sinful reef
#

turns out I was spawning a new entity with the player data component instead of inserting onto the actual networked entity

#

I love bevy inspector egui now โค๏ธ

#

now i just have a problem where when a player joins it doesnt show the players that joined before it

brittle mulch
lean thunder
spring raptor
#

Honestly, I can't even imagine a use case for interpolating something else ๐Ÿค”
So I'm considering using bevy_transform_interpolation instead of rolling my own. It includes extrapolation and Hermite which is neat.

#
  • it's planned for upstreaming.
brittle mulch
#

Perhaps as I get more familiarized with it I can make the interpolation logic.

spring raptor
spring raptor
#

In both bevy_replicon and bevy_transform_interpolation

#

Or just temporarely set the tickrate to 60 and wait until I get to this ๐Ÿ™‚
But help with this is definitely welcome and definitely accelerate the process. I'm currently busy with the new visibility system.

brittle mulch
#

Hmm interesting, rewind example feels a little brokeish. When two clients are connected it just doesnt seem to match the client states and colliding with the wall is very weird, just wondering if this was mapped

spring raptor
#

Ask @dire aurora about this ^

dire aurora
#

Okay yea there's definitely something wrong there ๐Ÿ˜…

dire aurora
sinful reef
lean thunder
dire aurora
#

Just a double check because I'm getting some cursed issues with the client receiving old data ... Regardless of the tick schedule, we only gather changes in PostUpdate, right?

brittle mulch
#

@spring raptor When it comes to message sending, do you recommend to utilize the same channel for esporadic events/messages such as user click on and so on, or should i make use of more than one

spring raptor
#

Something weird happens on CI recently ๐Ÿค”

error: linking with `cc` failed: exit status: 1
...
  = note: PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
          collect2: fatal error: ld terminated with signal 7 [Bus error], core dumped
          compilation terminated.

And it happens sometimes.

dire aurora
#

Because oddly enough the client gets a state for a tick that doesn't match the server's state on that tick

spring raptor
dire aurora
#

That only works for insertions and not updates, right?

spring raptor
lean thunder
dire aurora
spring raptor
dire aurora
#

I found the source of the bug

#

I apparantly had an issue for quite a while where I double incremented the tick on the server ๐Ÿ˜…

#

But only the RepliconTick

dire aurora
#

Weirdly enough still getting these:

2025-11-14T20:44:39.739579Z  WARN bevy_replicon::shared::server_entity_map: ignoring duplicate mapping from 198v1 to 323v4
```Just a lot less, and now it always happens

Oddly enough the warnings happen *after* the spawns though ๐Ÿค”
#

Would disabling/reenabling entities trigger that message?

spring raptor
dire aurora
#

No, this is on the client

spring raptor
# dire aurora No, this is on the client

I don't think it the source of this warning.
It happens when the server sends a mapping and client already received it. If the client know about it, it shouldn't send it at all.

dire aurora
#

Huh, so something on the server causes the signatures to get resent? ๐Ÿค”

spring raptor
#

The server sends a mappings if the signature is inserted or the entity is considered new for a client (gained visibility or just spawned).

dire aurora
#

Hmmm, neither of those should be happening, so that's strange ๐Ÿค”

spring raptor
#

Set the log to debug and look for inserting hash

dire aurora
#

Player spawn, player casts a skill, skill spawns a projectile:

2025-11-14T20:59:45.908653Z DEBUG bevy_replicon::shared::replication::signature: inserting hash 0x76038a003006384f for `194v0`
2025-11-14T20:59:51.908868Z DEBUG bevy_replicon::shared::replication::signature: inserting hash 0x0e397180d8a93194 for `195v1`
2025-11-14T20:59:52.108900Z DEBUG bevy_replicon::shared::replication::signature: inserting hash 0x381dbf3cd2b90a9c for `199v0`
2025-11-14T20:59:52.308919Z DEBUG bevy_replicon::shared::replication::signature: removing hash 0x0e397180d8a93194 for `195v1`
2025-11-14T20:59:52.459119Z DEBUG bevy_replicon::shared::replication::signature: removing hash 0x381dbf3cd2b90a9c for `199v0`
spring raptor
#

You can filter by bevy_replicon::server

dire aurora
#

Not seeing anything ๐Ÿค”

2025-11-14T21:04:31.677188Z DEBUG bevy_replicon::server: client `192v0` connected
2025-11-14T21:04:31.693814Z DEBUG bevy_replicon::server: marking client `192v0` as authorized
2025-11-14T21:04:37.594286Z DEBUG bevy_replicon::server::related_entities: connecting `194v1` with `193v0` via `Affects`
2025-11-14T21:04:37.594299Z DEBUG bevy_replicon::server::related_entities: connecting `194v1` with `193v0` via `Affects`
2025-11-14T21:04:37.595581Z DEBUG bevy_replicon::server::related_entities: rebuilding graphs
2025-11-14T21:04:37.994442Z DEBUG bevy_replicon::server::related_entities: disconnecting `194v1` from `193v0` via `Affects`
2025-11-14T21:04:37.994452Z DEBUG bevy_replicon::server::related_entities: removing orphan `193v0`
2025-11-14T21:04:37.994456Z DEBUG bevy_replicon::server::related_entities: removing orphan `194v1`
2025-11-14T21:04:37.995228Z DEBUG bevy_replicon::server::related_entities: rebuilding graphs
#

Also odd how the related_entities connecting thing gets duplicated

dire aurora
#
RUST_LOG=bevy_replicon=trace cargo run -p session 2>&1 | grep bevy_replicon::server
#

Pretty sure it's enabled

spring raptor
#

Try spawning a projectile

dire aurora
#

Hold on let me double check that the log flag to limit it to debug doesn't affect things

#

I do see some trace but I know that flag is very inconsistent ๐Ÿ˜…

#

Still nothing

dire aurora
#

Technically that projectile doesn't actually get replicated right now, so it doesn't really need the hash, but the status effect definitely gets sent since I got desynchronized spawn messages before I fixed my input scheduling for the newly changed tick logic that is no longer always wrong

dire aurora
#

The player, the status effect, and the projectile

spring raptor
dire aurora
#

Yea, I'll probably fix that at some point but not now

spring raptor
#

Maybe it's considered new for a client every time somehow?

dire aurora
#

The warning is for the status effect

spring raptor
#

Does it spam with this warning?

dire aurora
#
2025-11-14T21:19:22.495034Z  WARN bevy_replicon::shared::server_entity_map: ignoring duplicate mapping from 198v0 to 323v1
2025-11-14T21:19:22.511634Z  WARN bevy_replicon::shared::server_entity_map: ignoring duplicate mapping from 198v0 to 323v1
2025-11-14T21:19:22.528374Z  WARN bevy_replicon::shared::server_entity_map: ignoring duplicate mapping from 198v0 to 323v1
2025-11-14T21:19:22.544989Z  WARN bevy_replicon::shared::server_entity_map: ignoring duplicate mapping from 198v0 to 323v1
2025-11-14T21:19:22.561657Z  WARN bevy_replicon::shared::server_entity_map: ignoring duplicate mapping from 198v0 to 323v1
2025-11-14T21:19:22.578308Z  WARN bevy_replicon::shared::server_entity_map: ignoring duplicate mapping from 198v0 to 323v1
...
```Many of them, roughly until the status despawns
spring raptor
spring raptor
#

With trace

dire aurora
#

Nope, no messages with mapping in them at all

#

I just get this spam on the client, and a single message about inserting/removing each hash on the server

#

Could it be that it spams because it doesn't register it as sent? ๐Ÿค”

spring raptor
dire aurora
#

Yea, idk why there is no such message

#

Also this is in visibility policy blacklist in case that matters at all

spring raptor
#

And above, for client-specific singatures.

dire aurora
#

I wonder why some trace logging still got trough

#

Oh

#

Ooooh ...

#

It is the projectile that fails

#

That's ... interesting ๐Ÿค”

#

It's not replicated, why does the server send it? ferris_sob

spring raptor
#

I didn't expect Signature to be used for non-replicated entities ๐Ÿ˜…

dire aurora
#

I insert it in my spawn reusing logic, since I use the hashes from that API for signatures

spring raptor
#

I'll fix it. I should just skip sending it.

But you are planning to replicate projectiles?

dire aurora
#

Yea, if someone joins the server after the skill got started they'd miss projectiles otherwise afterall

#

Need to set up some better logic for that though

spring raptor
dire aurora
#

Okay ๐Ÿ‘

#

Time to continue my input rework now that I fixed the random issues that got unearthed by this super_bavy

spring raptor
#

I'd fix now and draft a patch, but since it's also a temporary solution for you, I'll focus on the current release

spring raptor
spring raptor
# spring raptor Something weird happens on CI recently ๐Ÿค” ``` error: linking with `cc` failed: ...

Looks like other people also facing it:
https://github.com/tensorzero/tensorzero/actions/runs/19314725039/job/55243387915

So it's not my cursed code.

GitHub

TensorZero is an open-source stack for industrial-grade LLM applications. It unifies an LLM gateway, observability, optimization, evaluation, and experimentation. - Add check-python-schema job (#45...

spring raptor
#

Looks like Bus error basically means no disk space left. I wonder why the error is so cryptic. On my machine, I see a proper error when there's no space left. Maybe it's because on CI it's in a VM?
Anyway, removing some of the preinstalled junk fixed the problem: https://github.com/simgine/bevy_replicon/pull/611
Left my comment on corresponding LLVM issue: https://github.com/llvm/llvm-project/issues/167785

GitHub

We're running into a space limit, which causes ld to terminate with a bus error.

#

Now I wonder if I couldn't collect test coverage from my example backend because of the same issue.

spring raptor
halcyon prawn
#

Why I'm getting?
Trait EntityEvent is not implemented for `FromClient<LootCommand>
on
commands.spawn(....observe(on_loot_command);

This worked before now it seems it wants me to not put FromClient on entity observers.

halcyon prawn
#

I guess it will be fine moving it to .add_observer(on_loot_command); and everything else stays the same.

spring raptor
#

I think I can easily fix it, hold on

halcyon prawn
#

Also, this part worries me a bit. The changelog says โ€œRename FromClient::client_entity into FromClient::client_id.โ€, but in reality client_id is now a ClientId, not an Entity like before. I donโ€™t mind the change, Iโ€™d just like to understand the idea behind it, and whether thereโ€™s a shorter or simpler way to get the client player entity for basic use cases.

fn on_kill_me_command(
    kill_me_command: On<FromClient<KillMeCommand>>,
    mut player_health: Query<&mut Health, With<Player>>,
) -> Result {
    if let ClientId::Client(player_e) = kill_me_command.client_id {
        let mut health = player_health.get_mut(player_e)?;
        health.kill_mut();
    } else {
        warn!("Received KillMeCommand from server, but it's not from a client");
    }

    Ok(())
}

I also tried this in another place:

let player_e = loot_command
        .client_id
        .entity()
        .expect("LootCommand player entity not found");

Any advice is welcome!

spring raptor
spring raptor
# spring raptor It will take more time. I have a somewhat working implementation, but it's very ...

Archetype-cached removals using a dynamic observer done:
https://github.com/simgine/bevy_replicon/pull/613
It's both simpler and much faster. We just didn't have observers back then.

GitHub

Requires #610.
Use archetypes to get IDs and listen for removals via a dynamic observer. This is not only faster but also much simpler.

#

Needed for component visibility. But a cool change on its own.

brittle mulch
dire aurora
#

Still broken in the same weird way

#

Might be system ordering

vital mist
dire aurora
#

So my game had a weird bug, that I recently discovered. I fixed it, but the same case isn't in my bevy_rewind example, so that's still broken in a different way bavy

livid thunder
#

does replicon support wasm?

spring raptor
vocal violet
#

@spring raptor i was thinking about the Reflection usage for serialization.

Doesn't it only work with the same architecture since the AppTypeRegistry is not stable between architecture/machines ? it uses type_id and typename right ?

spring raptor
#

The reason we don't use reflection under the hood is because it's incredibly inefficient. For both bandwidth and performance.

spring raptor
#

We provide you correct ComponentId in the serialization context.

#

I.e. it's handled for you

vocal violet
#

i speak about PartialReflect serialization/deserialization like.

fn serialize_dynamic(
    ctx: &mut ClientSendCtx,
    dynamic: &InputMsg,
    message: &mut Vec<u8>,
) -> Result<()> {
    serialize_dynamic_with_registry(ctx.type_registry, dynamic, message)
}

fn serialize_dynamic_with_registry(
    registry: &AppTypeRegistry,
    dynamic: &InputMsg,
    message: &mut Vec<u8>,
) -> Result<()> {
    let mut serializer = Serializer {
        output: ExtendMutFlavor::new(message),
    };
    let registry = registry.read();
    ReflectSerializer::new(&*dynamic.0, &registry).serialize(&mut serializer)?;
    Ok(())
}

fn deserialize_dynamic(ctx: &mut ServerReceiveCtx, message: &mut Bytes) -> Result<InputMsg> {
    deserialize_dynamic_with_registry(ctx.type_registry, message)
}

fn deserialize_dynamic_with_registry(
    registry: &AppTypeRegistry,
    message: &mut Bytes,
) -> Result<InputMsg> {
    let mut deserializer = Deserializer::from_flavor(BufFlavor::new(message));
    let registry = registry.read();
    let reflect = ReflectDeserializer::new(&registry).deserialize(&mut deserializer)?;
    Ok(InputMsg(reflect))
}

#[derive(Message)]
pub struct InputMsg(pub Box<dyn PartialReflect>);```
spring raptor
#

Ah, you probably asking whether reflection will work across multiple machines?

vocal violet
#

this code work on my test, but it's on the same machine

spring raptor
#

The answer is yes because the reflection is text-based.

vocal violet
#

#[cfg(test)]
mod tests {
    use super::*;
    use bevy::reflect::Reflect;

    #[derive(Reflect, Clone, Debug, PartialEq)]
    struct DummyInput {
        value: f32,
    }

    #[test]
    fn serialize_dynamic_roundtrip_preserves_payload() {
        let mut registry = AppTypeRegistry::default();
        registry.write().register::<DummyInput>();
        let payload = DummyInput { value: 42.0 };
        let msg = InputMsg(Box::new(payload.clone()));
        let mut bytes = Vec::new();
        serialize_dynamic_with_registry(&registry, &msg, &mut bytes).expect("serialize");

        let mut buf = Bytes::from(bytes);
        let result = deserialize_dynamic_with_registry(&registry, &mut buf).expect("deserialize");
        let reflected = result.0.try_as_reflect().expect("reflect access");
        let recovered = reflected
            .downcast_ref::<DummyInput>()
            .expect("downcast concrete value");
        assert_eq!(recovered, &payload);
    }
}

For ex

spring raptor
vocal violet
#

which is not stable across machine

spring raptor
spring raptor
vocal violet
#

really, any source for this, just to be sure my architecutre is not false

spring raptor
vocal violet
#

Ok i will trust that then.

For my usecase it's doesn't matter the serialization is text based.
But i wonder if you have thought about the case of dynamic serialization alternative to reflection for the future, the main reason we have to use text reflection is that we can't access the world for alternative as serialization/desrialization for reason idk since i haven't yet looked at how replicon work under the hood.

I will not need it but i'm just wondering what was your thought on that.

#

we can probably already bypass this issue for message by serializing the message content before sending the message itself, but it's not possible for component

spring raptor
#

It's already quite complicated with all specializations and such.

vocal violet
#

specializations ?

spring raptor
#

Yeah, you can have different serialization for (Transform, StaticBox) and just Transform.

#

It's a very useful feature.

vocal violet
#

usecase exemples ?

#

i k it exists but can't think on the fly rn

spring raptor
#

Like you don't need to send rotation for boxes

#

Or you can reduce the precision for non-player characters

vocal violet
#

i see

#

well, my dynamic data repliction is not a hot path for now so as long it's stable accross compilations/machines it's fine if it's text based

spring raptor
#

Exactly!

vocal violet
#

but there's usecases where it can be an issue i think

spring raptor
#

There is nothing wrong with using reflection if you need it.
We just don't use it by default because it's a library and we can't assume users won't build an MMO or something like that.

vocal violet
#

nah it's just a graph with nodes which has inputs of a dynamic type i have to replicate

#

that why i need dynamic data replication

#

btw, reflection its only needed for component with dynamic data, for message you can litteraly just replicate it yourself before sending the message

vocal violet
#

oh
The returned string must not be considered to be a unique identifier of a type as multiple types may map to the same type name

#

yet bevy remote uses it ? feels like i'm missing something

spring raptor
vocal violet
#

so the classic typename is not stable but AppTypeRegistry is stable ?

#

i doubt they haven't fixed this issue yet but they don't use type_name then

vocal violet
#

ok thanks you for your help, that all i needed to know

#

i think the only issue this has outside the hotpath is that version has to be the excat same.

just food for thoughts

spring raptor
spring raptor
#

@dire aurora you were interested ^

#

See the docs in visibility module for API details and examples.
Any feedback is very welcome!

#

Once it's merged, I'll draft a new release ๐Ÿ™‚

livid thunder
#

Can anyone point me in the direction of a client server setup with both udp and webtransport for the backend? I seem to be getting issues with self signed certs for webtransport

spring raptor
lunar bone
livid thunder
spring raptor
#

No problem!

woven forge
#

Out of curiosity, what is the reason that everything must be registered in the same order on both client and server as opposed to just the same types?

(reasoning: currently going insane trying to prevent what I can only guess is llvm involuntarily reordering my function for me cuz it's the exact same file)

spring raptor
#

Most likely you have something different between client and server, I don't think the compiler reorders anything.

woven forge
#

Ended up nuking my whole target directory and that seemed to resolve things. Bizarre

#

My client and server run the exact same codebase; it would connect, but authentication (also based on the protocol hash by default iirc) was failing; client didnโ€™t get disconnected though, just didnโ€™t get any replication or events

spring raptor
spring raptor
clear forge
#

@spring raptor It's me again.

I updated to bevy 0.17, Replicon 0.36 and Aeronet (including all packages) 0.17
My messages are working as before, but the events triggered from the client to the server are no longer being received by the server.

Do you have an idea, what the problem could be

spring raptor
clear forge
#

ill try thanks

#

Okay i got the problem @spring raptor . Im sending an empty struct.

mpl Plugin for InteractPlugin {
    fn build(&self, app: &mut App) {
        app.add_client_event::<Interact>(Channel::Ordered)
            .add_observer(interact)
            .add_message::<InteractionTriggeredEvent>()
            .add_systems(
                PostUpdate,
                send_interact
                    .before(ClientSystems::Send)
                    .run_if(in_state(PlayerState::World)),
            );
    }
}

#[derive(Event, Serialize, Deserialize, Debug)]
struct Interact;

fn send_interact(mut commands: Commands, keyboard_input: Res<ButtonInput<KeyCode>>) {
    if keyboard_input.just_pressed(KeyCode::KeyF) {
        commands.client_trigger(Interact);
    }
}

fn interact(
    trigger: On<FromClient<Interact>>,
    mut triggered_events: MessageWriter<InteractionTriggeredEvent>,
    players: Query<(&Transform, &BoxCollider)>,
    interactables: Query<(Entity, &Transform, &BoxCollider, &Interactable)>,
    client_player_map: Res<ClientPlayerMap>,
) -> Result {
    let player = *client_player_map.get_player(&trigger.client_id)?;
    let (player_transform, player_collider) = players.get(player)?;

    let player_bounds = player_collider.at(player_transform);

But when i changed to

#[derive(Event, Serialize, Deserialize, Debug)]
struct Interact(usize);

Then it works

spring raptor
distant hazel
#

Hi! I'm not sure whether this is more of a replicon or a renet problem but I'm leaning towards replicon; I set up bevy_replicon_renet as a backend as in the examples, and swapped out my old backend (aeronet) with it. Aeronet mostly worked with some bugs I want to avoid, so I know I can get things working. But for some reason the renet backend manages to trigger an On<Add, ConnectedClient> event and then the client never gets authorized and receives no warning about e.g. failed protocol matching. I also disabled protocol matching in case that was the issue; it was not. I'm really not sure what else to check, or could be a problem?

distant hazel
#

Found the issue: Was a mix of some logic that no longer worked quite right without aeronet, and leaving some actual aeronet plugins in that seemed to interfere with renet. Works great now!

spring raptor
distant hazel
#

it mostly worked, so I doubt it was a version mismatch; mainly:

  • if I alt-tab, it would instantly run out of some memory buffer and disconnect
  • it doesn't handle zero sized messages right now, which is easily worked around but just one more thing to forget and have go wrong
    I was also leaning towards switching to renet since it's what you use, and you maintain bevy_replicon_renet, so I figured it would probably be overall more likely to be smooth development with renet over aeronet. Also happened to be about 100 less transitive dependencies and simpler setup, which was nice
spring raptor
karmic onyx
#

Hey @spring raptor, thanks for your networking crate. Its really a pleasure working with ๐Ÿ˜Š Thanks also for the thorough documentation. I often find myself going back to it when I'm stuck.
I'm trying to get bevy_transform_interpolation working. Just adding bevy_transform_interpolation::prelude::TransformInterpolation to the entity does not seem to work as described in the crate ๐Ÿค” do you know if it is related to the update schedule required by the transform interpolation?

spring raptor
raw idol
#

Also I really should update aeronet_replicon

distant hazel
#

Was using webtransport. I tried both webtransport and websockets for the alt-tab bug, both had it so it seems to be more of an aeronet_io issue. Not sure what the zero sized messages one applies to, but definitely webtransport

raw idol
#

On WASM or native?

#

the transitive deps is interesting. should have a look at pruning the tree sometime

spring raptor
distant hazel
# raw idol On WASM or native?

native. I'm not 100% certain that aeronet actually has that many transitive dependencies, this has been a long project with a lot of dependency movement so maybe something just got unlodged in the lockfile by chance when I switched to renet and dropped a lot of dependencies, but I did go from 700 to 600 for the client executable.

raw idol
#

Can't seem to reproduce the alt tab thing, I suspect that it's Bevy pausing systems when alt-tabbed, but I can reproduce the empty event thing. trying to diagnose now

spring raptor
thorn forum
distant hazel
#

No browser, native

thorn forum
#

using renet2 and its stays connected during alt tab (ws or wt). using the default WinitSettings. so its probably aeronet yeah

raw idol
raw idol
thorn forum
clear forge
livid thunder
#

Does Replicon have transform interpolation and extrapolation?

spring raptor
livid thunder
#

Thanks for the answer ๐Ÿ™‚ I ended up writing a simple lerp one for now

sinful reef
#

are you confused with #[require(Replicated)] maybe?

spring raptor
#

I also think it's actually great that we don't require a trait because this allows us to replicate components from third-party crates ๐Ÿ™‚

viscid jacinth
#

How is the replication interval controlled in replicon? By specifying a tick_schedule that specifies how often the RepliconTick is incremented?

spring raptor
viscid jacinth
spring raptor
spring raptor
# viscid jacinth What do you mean?

Sorry, I wanted to write why FixedUpdate is separate from replication sending?
I expected it it be tied to the update. But I guess it make sense if you want to send traffic?
I think we can make it work similar to Lightyear, so you can convert ticks simpler without any mappings.

viscid jacinth
#

I'm not sure I understand, sorry!
Tick is incremented every FixedUpdate; and is just used as convenient way to measure time, even between replication intervals. For example i need it for input-handling, prediction or interpolation.
Replication sending is done in PostUpdate, and I just include in the message the current fixed-update Tick. The user can specify a replication frequency (for example 100ms)

#

I saw that in Replicon you include the RepliconTick in the message; but that is only useful for replication, I cannot compare that tick with other fixed-update related timestamps (inputs, prediction, etc.)

spring raptor
viscid jacinth
# spring raptor It's okay, I'm not a native speaker, so maybe it's also a language barrier ๐Ÿ™‚ Q...

Ah right, i have my own! https://github.com/cBournhonesque/lightyear/blob/main/lightyear_core/src/tick.rs#L10
bevy's Tick is only used for replication/change-detection purposes.
My Tick is just incremented by 1 every time FixedUpdate runs, and is what I use to do time-synchronization between multiple peers + as a way to track time in general

GitHub

A networking library to make multiplayer games for the Bevy game engine - cBournhonesque/lightyear

spring raptor
viscid jacinth
#

Yes; in my example RepliconTick would get incremented once every 100ms in PostUpdate, but lightyear's Tick gets replicated every FixedUpdate

spring raptor
#

Sorry for long responses, a bit busy rn ๐Ÿ™‚

#

We also have a size difference: u32 vs u16.
I used u16 originally, but @dire aurora suggested a valid use case: in games that track time in ticks, u16 might be too short. With 30Hz it's only ~36 mins. In some MMOs buffs could be active for 1h+.
Would you consider switching to u32?

viscid jacinth
# spring raptor Got it! But even though you send replication every X increments, the tick itself...

No, the tick gets incremented every FixedUpdate; and whenever a replication message gets sent it include the current value of a tick.
For example the tick could be 45, and then on the next message it is 50.
For u32 vs u16; yes i might switch to u32.
I originally used u16 because i include a tick in a lot of packets (for example Ping/Pong packets) for time sync so reducing the size is nice.

I have a bunch of extra complexity where I currently have to clean up stale sticks every ~10 minutes to avoid wraparound issues, it might be easier to just switch to u32

spring raptor
# viscid jacinth No, the tick gets incremented every FixedUpdate; and whenever a replication mess...

Ah, I think I understand it properly now.
We can add the same configuration setting to Replicon and your Tick will match RepliconTick.
But there is a small problem.

First, let me describe how replication is sent by default (it's relevant!).
We have 2 kinds of replication messages: mutations and updates. Mutation messages only include entities and mutated components. Update messages include everything else. And both messages include the tick.

I mentioned that we send messages every tick, but if there is no data to send, we simply don't send anything.
This works well for games that don't need tick synchronization.
But we have a special switch: https://docs.rs/bevy_replicon/latest/bevy_replicon/shared/replication/track_mutate_messages/trait.TrackAppExt.html#tymethod.track_mutate_messages
This function needs to be called by rollback crates that use Replicon. This way, mutation messages will be sent every tick. If there is nothing to send, the message will only contain the tick.
This allows not only synchronizing ticks, but also distinguishing between predicted and received data. I documented the API here: https://docs.rs/bevy_replicon/latest/bevy_replicon/#ticks-information. It's very low-level, but we went with it for maximum storage efficiency.
But if we don't send mutation messages every tick, how do we distinguish between what was predicted and what was interpolated? How is this done in Lightyear?

viscid jacinth
#

We can add the same configuration setting to Replicon and your Tick will match RepliconTick.
you mean that RepliconTick would be updated every FixedUpdate, but we could still send replication updates every 'send_interval'?

I'm not sure I understand why track_mutate_messages is necessary for prediction/rollback. Could you elaborate? Why do we need to send mutation messages every tick?

But if we don't send mutation messages every tick, how do we distinguish between what was predicted and what was interpolated? How is this done in Lightyear?
Component values received from replication are inserted as Confirmed<C>, and the predicted/interpolated value is the component C.
Each entity has a ConfirmedTick which tracks the latest tick where a replication message was received. I don't use anything like ServerMutateTicks , I only need to know the latest confirmed tick for the entity.

Whenever a mutation is received on a predicted entity, I would:

  • rollback to the earliest confirmed tick of any predicted entity (currently all prediction entities are in the same ReplicationGroup, so there is a guarantee that there are all at the same tick, but I think I want to relax this constraint)
  • re-apply history from there.
spring raptor
#

you mean that RepliconTick would be updated every FixedUpdate, but we could still send replication updates every 'send_interval'?
Correct! That's how your Tick works, right?

For the clarification it's better to ask @dire aurora directly. Could you explain? Because I don't remember the details, only the fact that we need to distinguish for which tick the value was confirmed and for which predicted ๐Ÿค”
Sending every tick allows to check was the mutation missed or the component just didn't change.

dire aurora
#

The case we wanted to solve was not receiving data for an Entity, because nothing changed, but the client not having a clue if it changed or not. The client would assume it's missing and just load a prediction and never the old value (which might've still been the correct authoritative value)

spring raptor
#

Makes sense, I think that's why @viscid jacinth have to use the same replication group.
But I think it's worth using the bevy_rewind approach. But this will require dropping the ability to set the mentioned delay.

viscid jacinth
#

Yea that's what I thought. It's independent from replication groups.

The client might mispredict something, and if the server doesn't send any updates then you never know what the correct value is.

In which case do you need the full history of ConfirmedTick instead of just the latest value?

spring raptor
# viscid jacinth Yea that's what I thought. It's independent from replication groups. The clien...

Yes, but you mentioned that you're using the same replication group because without it looks weird? Maybe it's the missing piece?

Yep, you need the full history. In Replicon we have ConfirmHistory component.
But we also have MutateTicks resource that track for which ticks mutations were received. It needs to be checked additionally if ConfirmHistory missing a tick.
It's for performance reasons. Would be slow to update ConfirmHistory on all entities every time we receive an empty message.

viscid jacinth
#

you mean that RepliconTick would be updated every FixedUpdate, but we could still send replication updates every 'send_interval'?
Correct! That's how your Tick works, right?
Yes, but i guess if you store a ConfirmedTick(RepliconTick) I would still have to map to my own lighyear tick since they might not be the same; I update the lightyear tick to do some tick syncing. Or I guess I could just update the RepliconTick to match my lightyear Tick? (and I update it only when I want to run the replication systems). That should work, right?
So plan would be:

  • maybe add a set_to method in ServerTick
  • switch my lightyear tick to u32
  • everytime I want to run replication, I manually update the ServerTick to be equal to my lightyear tick
#

Yes, but you mentioned that you're using the same replication group because without it looks weird? Maybe it's the missing piece?
I haven't tested it; without it I don't think it would necessarily look weird, but there's more chances of mispredictions. I will change lightyear to be compatible with all predicted entities not being on the same tick.
Yep, you need the full history.
Why do you need the full history? I don't think I ever use the full history.
It needs to be checked additionally if ConfirmHistory missing a tick.
How come? ConfirmHistory can be lossy?

spring raptor
viscid jacinth
#

But i thought that modifying ServerTick is the way to tell replicon to trigger replication.
Also i looked at the code, I don't think it requires consecutive replication system runs to have consecutive ServerTick. It just needs to be monotonically increasing, like in lightyear. i.e. we run replicate once with ServerTick = 100, and then later when ServerTick = 120.
I think it would work. I can actually test it now since increment_by is basically the same as set_to

spring raptor
# viscid jacinth > Yes, but you mentioned that you're using the same replication group because wi...

Why do you need the full history?
If you received a mutation for tick X, it doesn't mean that you received the value for X - 10 to which you might want to rollback.
But just to clarify - when I say full history, I mean only the last 64 ticks.
How come? ConfirmHistory can be lossy?
Imagine you received update to tick X. Now on the next tick there are no changes. We send an empty message. But instead of iterating over all entities with ConfirmHistory and set this tick, we just update a ServerMutatTicks resource.
It's simply for optimization. So the logic is this:
If ConfirmHistory has tick X - it was received.
If not, you need to check whether the message for tick X using ServerMutatTicks. Mutations could be split in multiple parts and this resource allows you to check wheter all parts where received.

spring raptor
spring raptor
viscid jacinth
viscid jacinth
#

So would it be possible to not have any tick_schedule so that I can increment the SreverTick manually?

viscid jacinth
#

The visibility system is interesting but also somewhat unintuitive?
For example if I want to replicate an entity to all clients except one, if I understand correctly I would have to add a
AllExceptOneMarker(Entity) on both the entity itself + all clients except the one I don't want?
That seems a bit tedious

#

Ah i guess another approach would be that each client entity has a

Marker {
   behaviour: Only/Except,  // not used on the client entity, but only used on the entity to replicate
   entity: Entity, // the client entity itself
}

then on the entity to replicate you can add the Marker.
It just seems strange that on the client entities I have to add a Marker component where all the information is either unused or redundant.
Maybe the API should allow settings a VisibilityFilter with 2 distinct components: one for the replicated entity and one for the client entity. And maybe there could be a default case where no component is required and the check is done on the Entity itself? (but that might be expensive)

#

Ideally I would:

  • add ExceptFilter(Entity) on the replicated entity
  • this triggers a visibility filter check against all entities with the <ExceptFilter as VisibilityFilter>::ClientMarker component; in our case the marker is (), so we check against all entities; and we use the Entity itself
  • I can compare the Entity stored in ExceptFilter against the client Entity to decide if I want to send or not.

Or if you don't like the Entity part, I could have a ExceptFilter(network_id) on the replicated entity, which uses the NetworkId component on the client entity.

#

Ah it looks like I can have more fine-grained control with ClientVisibility; I think I can just make things work with that!

spring raptor
# viscid jacinth Ok i see how `ServerMutateTicks` can be used to avoid iterating over all `Confir...

You can just ignore the tick history API then. ๐Ÿ™‚

But let me clarify why bevy_rewind uses it, just in case. Entities can have different confirmed ticks. When a rollback happens, it rolls back to the last confirmed tick among all entities.

This API allows us to distinguish between a missed update and a value that simply did not change.
For example, you have an entity for which you received an authoritative update on tick X. On ticks X+1..=X+5, nothing happened. So on X+5, you still have a valid authoritative value.
So the last confirmed tick for this entity is X+5 and not X, which is important because the crate rollbacks to the last received tick among all entities.
Without this API, we would not be able to tell whether the value simply did not change or whether the update was not received.

It is low-level because it was designed for rollback crates and prioritizes efficiency.

spring raptor
#

(I answer slowly because I'm still at work)

spring raptor
dire aurora
dire aurora
spring raptor
spring raptor
spring raptor
spring raptor
#

Updated to the latest RC.

I also updated bevy_replicon_renet, but it's not on crates.io yet because it uses my renet fork.

viscid jacinth
#

How can

  • stop replication without despawning the entity? simply remove Replicated?
  • despawn an entity without replicating the despawn? just remove Replicated and then despawn?
viscid jacinth
#

Also is there a way to have a component A on the sender be received as component B on the receiver?

#

I'm close to having a testable version of lightyear using replicon for replication, btw

spring raptor
# viscid jacinth How can - stop replication without despawning the entity? simply remove `Replic...

When you remove Replicated, it will despawn the entity on clients ๐Ÿค”
It's because I watch for Replicated removal. We didn't have a Despawn lifecycle event back then.
I think it should be an easy fix: just replace On<Remove<Replicated>> with On<Despawn>.
The only thing I'm not sure how to handle is entity mappings. Right now, I remove the mapping from the list on despawn. But what if you stop replication and then despawn the entity? In that case, the mapping will remain in the client map forever.

viscid jacinth
# spring raptor When you remove `Replicated`, it will despawn the entity on clients ๐Ÿค” It's beca...

So to be clear we are suggesting that

  • removing Replicated just pauses the replication (instead of sending a despawn)
  • a despawn while the entity has Replicated will be replicated
  • a despawn while the entity doesn't have Replicated won't be replicated

I guess we could have an infrequent message sent that lets the client know about entities that were despawned but not replicated, so that the mapping on the receiver side gets cleaned out?

spring raptor
viscid jacinth
spring raptor
viscid jacinth
#

To track if the client received ANY replication messages I need to:

  • enable app.track_mutate_messages on both sender and receiver
  • check both ServerUpdateTick and ServerMutateTicks on the receiver?
#

It looks like i can't access the last tick in ServerUpdateTick
EDIT: nvm i can deref

spring raptor
viscid jacinth
#

I'm still not sure how to get the last confirm tick for an entity. Let's say ConfirmHistory tells us 'not-confirmed' for tick 17.
How can I use ServerMutateTicks to know that we might have received an 'empty' mutate messaage for tick 17, and not a non-empty message?

viscid jacinth
spring raptor
spring raptor
#

All mutation messages include the minimal required tick from update messages to be applied. Until the update messages for the minimal required tick is received, the mutation message will be buffered.

#

So if you received a mutate message for tick X, everything before and including tick X is confirmed.

viscid jacinth
#

ServerMutateTicks::contains(tick) is global. I want to know for a specific Entity the latest confirmed tick

spring raptor
viscid jacinth
#

I see; ServerMutateTicks is true only if the tick was confirmed for ALL entities.
So it's possible that ConfirmHistory's last_tick is higher than the tick in ServerMutateTicks?

spring raptor
#

That's because of how replication works.
We scan for changes and include everything that wasn't acked by the client for tick X. The message split into parts (if necessary) and parts can be applied serparately.
If all parts for tick X was applied - the tick is fully confirmed.

spring raptor
#

So the strategy is to check ServerMutateTicks first. It's a very cheap check. If it wasn't confirmed, check ConfirmTicks.

viscid jacinth
spring raptor
viscid jacinth
#

I still don't get why we wouldn't check ServerUpdateTick. Let's say C is insert at tick 17, then the confirmed_tick for the entity is 17, even if it didn't have any mutations

spring raptor
#

But the entity might not have any mutations on this tick. In this case ConfirmHistory will return false for this tick.
This is why we need ServerUpdateTick. If for this tick we received all mutations, the entity can be considered confirmed because there was no changes.

#

I probably should clarify this in the docs ๐Ÿค”

viscid jacinth
#

Ok here's my understanding, is it correct?

- ServerMutateTicks contains the information of 'were all mutation messages received for a given tick'
  - if a tick is confirmed there, then ALL entities are confirmed for AT LEAST this tick
  - some entities might have a more recent confirmed tick, even though all entities for that tick were not confirmed yet, in which case
    ConfirmHistory.last_tick() > ServerMutateTicks.last_tick()
- ConfirmHistory contains any tick where we get an Mutation or Update. (but only if it was 'explicit')
- with MutationTracking, we also send empty packets if there were no mutations for that tick.
  - these can only be seen in ServerMutateTicks; in that case we would have `ServerMutateTicks.last_tick() > ConfirmHistory.last_tick()`
- ServerUpdateTick is useful to globally check if any Update was received, and also because you could have:
  - entity 1 has an insertion but not entity 2. In which case `ConfirmHistory` will be false for entity 2 for that tick,
    but we know that entity 2 is confirmed for the tick thanks to ServerUpdateTick

And for prediction:

- we need the oldest tick that was confirmed among all predicted entities
  - the most recent of `ServerUpdateTick` and `ServerMutateTick.last_tick()` is the OLDEST possible confirmed tick across all entities 
  - it is possible that the Predicted entities are a subset of all replicated entities, in which case we might want to check 
    each Predicted entity's `ConfirmedHistory` to see if the oldest confirmed tick among them is more recent (but in most cases `ServerUpdateTick` + `ServerMutateTick.last_tick()` is enough
#

Your PR mentions that ServerUpdateTicks is not needed but your previous message just said "This is why we need ServerUpdateTick" haha
I don't know which one it is

spring raptor
viscid jacinth
#

But why is ServerUpdateTicks not needed?
Let's say we receive an Update message which spawns entity 2 at tick T, but doens't include any mutations. (and we have an existing entity 1)
That means that all entities are confirmed for tick T.
I would need to check ServerUpdateTicks to know that since:

  • the information is not present in ServerMutateTicks since there are no mutations
  • the ConfirmHistory of entity 1 was not updated.
spring raptor
viscid jacinth
#

Yes I understand that, that's why I included

- with MutationTracking, we also send empty packets if there were no mutations for that tick.
  - these can only be seen in ServerMutateTicks; in that case we would have `ServerMutateTicks.last_tick() > ConfirmHistory.last_tick()`

But for the case I described above, I think we would need ServerUpdateTicks, no?

spring raptor
viscid jacinth
#

But it will be present. ServerMutateTicks updated every time we receive a mutation message. Even if it's an empty message.
But that was not an empty mutation message, that was an Update message (since another entity was spawned).
Or are you saying that in this case we would send 2 messages:

  • an Update message
  • an empty Mutation message?
    I was under the impression that we never sent mutations at the same time as Updates since they were merged together
spring raptor
viscid jacinth
#

Another thing I'm curious about; the example you showed earlier suggested that you want history enabled for prediction.
@dire aurora may I ask how you're using that?
Don't you want the most recent tick for any predicted entity?
Or is the thought process that it would be useful in the following case:

  • predicted entity 1 has latest tick 7
  • predicted entity 1 has history-ticks 3, 7, 11. The message with tick 7 was received after the message with 11, so if history was enabled that update would have been lost. However with history enabled we get a more correct rollback because we can rollback to the exact value on tick 7?
viscid jacinth
# spring raptor Yes, you'll get 2 messages ๐Ÿ™‚ On tick N it's possible to receive an update mes...

https://github.com/simgine/bevy_replicon/blob/e7fa5364d8a5ee3bd2e6aa68b612b96ed82cd617/src/server.rs#L758
Doesn't this mean that whenever there is ANY update, then all mutations for all entities are included in the update message? Or is that only for that one entity that has updates?
So the rule is:

  • all updates are sent together as a single message. Any mutations for those entities are included in that update message
  • you can still have mutations for other entities. If track_mutation is enabled, then we send an extra mutation message even if there were no mutations
GitHub

A server-authoritative replication crate for Bevy. - simgine/bevy_replicon

#

Finally I have last question ๐Ÿ˜„
Are the confirmed ticks (ConfirmHistory and ServerMutateTicks) updated for past/historical messages?
For example I receive at the same tick a Mutation message for tick 7 and a Mutation message for tick 11 for the same entity.
Will ConfirmHistory also say that tick 7 is confirmed? same for ServerMutateTicks ?

spring raptor
spring raptor
# viscid jacinth Finally I have last question ๐Ÿ˜„ Are the confirmed ticks (`ConfirmHistory` and `S...

Yes if you enable history for the entity via marker config: https://docs.rs/bevy_replicon/latest/bevy_replicon/shared/replication/command_markers/struct.MarkerConfig.html
You set it when you register your markers on the entity. For example, your Predicted marker component will need to enable history to bump ticks and receive all mutations, including past mutations.

#

Feel free to ask as many questions as needed ๐Ÿ™‚

#

It's not the most intuitive API. But we tried to design it in a way that allows you to avoid paying for what you don't need.

#

This markers API basically tell "how to write this component on this entity". By default all components have default writer that just inserts the component.
But sometimes you want to apply it as a different component. Or write into a history buffer that might need previous values.

dire aurora
#

(Unless they never interact ofc)

viscid jacinth
viscid jacinth
dire aurora
#

But again, only relevant if entities interact with eachother but don't get networked together

spring raptor
viscid jacinth
spring raptor
spring raptor
#

Summarized my initial thoughts on client -> server replication based on https://discord.com/channels/691052431525675048/1449832649240612946 discussion in this issue:
https://github.com/simgine/bevy_replicon/issues/634

If you're interested in this feature, I'd really welcome your opinions!

GitHub

Use cases Originally, I wasn't sure about this feature. I thought it's a very niche feature for VR games to avoid rollbacks (which might cause player nausea). But I was wrong! For example, ...

waxen barn
#

But its also not hard to set up a message like that

#

Also I don't think you should ever allow more than one client be the authority of something. Not sure if you have that in there or not. So maybe you just need the client Id on a component which says who gets to control that.

spring raptor
# waxen barn But its also not hard to set up a message like that

That's one the reasons I didn't suggest adding client entity spawning with validation.
Clients can send game-specific messages that can be validated on the server in a system and spawn entities.

And if a client wants to spawn before waiting for confirmation (similar to how client-spawned entities would work on the server), we have the Signature component, which allows entities to be matched via hashes. So when the server confirms the spawn, the client will match the local entity with the server entity using the hash. And If on the server you immediately give the client the authority, it will work exactly like client spawning, but with validation.

spring raptor
# waxen barn Also I don't think you should ever allow more than one client be the authority o...

Ah, I actually forgot to mention it while writing the proposal! I'll update it.
I described ClientAuthority with a single client entity, so this way only a single client can have authority. I think in Lightyear you can also have only a single authority over an entity (which is reasonable).

However, I can imagine a use case where an entity owned by client A can be moved around by anyone, but only client A can change non-transform components (was suggested by NiseVoid) ๐Ÿค”
I'm not sure about this, though... Maybe, to keep things simple, we can go with a single client authority over an entity - mainly because I'm not sure how to implement this efficiently.

#

Thanks for the feedback!

viscid jacinth
#

Is there a way to check if any replication message was received?

spring raptor
# viscid jacinth Is there a way to check if any replication message was received?

You can check messages inside ClientMessages for replication channels. I didn't expose received_count though. I can make it pub if needed.
Or you can check ServerMutateTicks. But the tick is confirmed only if all messages received. If you're interested in "any message received", we'll need to expose the API. In addition, you will need to check ServerUpdateTicks.

viscid jacinth
#

I wanted to have this so that I could do something like: only check for possible rollbacks if any replication message was received.

But now I'm thinking I could do this other approach: every time I receive an mutation/insertion for a Predicted entity, I will:

  • compare the component with the previous value in the prediction history
  • insert the new component in the prediction history
  • trigger a rollback if there is any mismatch

For this I would need to have World access in WriteCtx and RemoveCtx; what do you think of adding a UnsafeWorldCell there (with safety comments) so that I can directly update the world when receiving an update for a predicted entity)?

(right now i insert the replication value in a Confirmed<C> component, and I compare Confirmed<C> with PredictionHistory<C> to see if there is a mismatch. But it would be better if I could skip that step and directly do the mismatch check in WriteFn<C>

#

I guess one issue is that this only checks for rollbacks when an entity receives an update. But when ServerMutateTicks's last_tick is updated, it's also equivalent to 'we received an update for all entities that did not send muations' that their component value did not change, and I should also check for rollbacks for these

spring raptor
spring raptor
viscid jacinth
viscid jacinth
spring raptor
spring raptor
spring raptor
#

Wait, the access is read-only. Yes, I can expose Commands too.

viscid jacinth
viscid jacinth
#

What is the easiest to check if a component is registered for replication ? Is there any public method in the Registry?

spring raptor
#

But I think you don't need all of them for rollback, right?
Because In rewind you register what to rollback (and I think it's the same Lightyear), so only these components are affected?

viscid jacinth
#

Yes but i want to know which of the rolled back components are replicated, as there might be different behaviour for components that are rolled back but not networked. Thanks

spring raptor
#

If you'll have suggestions how we can make the API nicer for you - let us know.

For bevy_rewind it's simpler because they rollback the whole world without checks. If you have many entities or they interact with each other, there is a big chance that rollback is needed.
So they optimize by removing the checks. At least that's my understanding ๐Ÿ™‚

lilac mantle
#

Hello, is there an example and instructions on how to run it for a client host (aka listen server) setup locally ?

I looked at the getting started guide but it seems more about adding it from scratch, but maybe I just missed it

spring raptor
lilac mantle
spring raptor
lilac mantle
# spring raptor Replicon just doesn't have any I/O, that's why I had to put them into the backen...

I don't understand why that would prevent having an examples folder, examples can pull special dependencies if needed, but I don't know much about crate structure and such.

Btw what would you recommend for adding my firestore signalling based webrtc transport for replicon since making a connection is not quite as straightforward as just opening a tcp one.
Would it make sense for it to be it's own backend or use another (like aeronet) for proxy

spring raptor
# lilac mantle I don't understand why that would prevent having an examples folder, examples ca...

Backends depend on Replicon, so this would create a circular dependency.
But I mentioned where to look for examples in the "Getting started" section.

Can't say for sure without looking at your transport ๐Ÿค”
Maybe check other backends to see whether their API fits yours. If it does and you like the backend itself, creating a transport plugin should be simpler. Plus you will get other transports from the backend "for free".
But creating a backend for replicon is also quite simple.

lilac mantle
# spring raptor Backends depend on Replicon, so this would create a circular dependency. But I m...

Thanks, Ideally I would build my transport over matchbox since it is built for webrtc so it would save me some trouble. Right now I managed to make a working matchbox example that uses firestore for signalling, but in truth all of these networking layers (replicon, replicon_matchbox, matchbox, bevy_matchbox) are making it very confusing for me to know where my implementation would exist and how I would wire it all together to use in my actual game.

If I were to go the "replicon backend from scratch" route, how would I know what needs to be implemented for replicon to work correctly ?
Do I just have to just go off the existing implementation or is there some more comprehensive list or way to thing about it ?

spring raptor
# lilac mantle Thanks, Ideally I would build my transport over matchbox since it is built for w...

Nice to see!
About crates, it's actually quite simple:

  1. backend is pure networking crate that doesn't depend on Bevy. Some backends, such as aeronet skips this layer for better ECS integration.
  2. bevy_backend crate integrates backend with bevy. Usually it just allows you to send bytes via resources.
  3. bevy_replicon_backend crate integrates bevy_backend as an I/O layer into bevy_replicon.

To put it in more familiar terms: bevy_replicon is like serde. It's just pure replication (serialization) and you can plug any I/O (formats) that can sometimes function on its own (usable without serde).

spring raptor
viscid jacinth
#

Do you have an idea of how I could keep a mapping from RepliconTick to Tick?
On the transport side, I know the Tick of the remote when they sent a packet. But i'm not sure how to use that to keep a mapping from RepliconTick to Tick, since the messages are just inserted on ClientMessage so I lose the Tick information before the message gets parsed inside Replicon.

This might be needed in client->server replication, since the client's Tick might get modified (even go backwards) to keep the timeline in sync with the server timeline.

echo lion
#

@spring raptor PR that changed the world flush requirement?

viscid jacinth
#

The Replicated component is the same on the send and receiver side?
Would it be possible to split it in to a send-side component and a receive-side component? (for example Replicate and Replicated)

spring raptor
spring raptor
spring raptor
# viscid jacinth about mine

Aren't you incrementing RepliconTick manually? If so, you can create a mapping there. Another option is to create a mapping in a system after sending replication.

viscid jacinth
spring raptor
# viscid jacinth Or is there some kind of way to react to a newly replicated entity so that I can...

I was just about to answer ๐Ÿ™‚
Yes you can create an observer for Replicated and check the state. If it's ClientState::Connected, you're on the client. There are plans for filtered observers, but right now you'll need to do a manual check.
I'm also fine with splitting. But maybe instead of having different components on client and server, we can just add special component for entities received on the client?
For example, Remote. So Replicated will be on both and Remote will be only the client. This simplifies logic, such as state serialization. I.e. I can have the same code that serialized game state for both client and server due to Replicated component, but can have special behavior for the client if needed (like the one you suggested).

viscid jacinth
spring raptor
viscid jacinth
spring raptor
# viscid jacinth I guess i'll wait until things are refactored from Client/Server to Send/Receive...

Of course!
I'm currently just waiting for 0.18 to release and right after that I'll start implementing all your suggestions.
But the client->server replication may take some time... It's quite a huge change even with the proposed simplifications (like avoid client spawning for now to avoid touching entity mappings). And I'm also working on my game.
I remember you mentioned that you want to try to tackle client->server? Or is it fine to wait?

lilac mantle
#

Hello, I finally got replicon to work with my firestore + matchbox backend on native, but I can't test it on wasm since the bevy_cli doesn't support this mix of args bevy run --example tic_tac_toe web -- server (basically the web and extra server arg don't mix), did anyone encounter something similar and have a solution ?

spring raptor
lilac mantle
#

They are working out a patch in the bevy_cli thread I think, thought that someone might've encountered in similar replicon usage

spring raptor
#

I think @thorn forum uses replicon in browser ๐Ÿค”

#

But the question is not replicon-specific

thorn forum
#

hey sorry im not using bevy cli to run my game in browser, only use it to build then import the package in my web app @lilac mantle

lilac mantle
#

thanks still ๐Ÿ‘

thorn forum
#

hello ! im migrating from bevy 0.16 and replicon 0.34 to 0.17 and replicon 0.37.1.
im using renet2, and to re connect player to a running game i was re using NetworktId, it was working great.

now, the client reconnects correctly with renet2 like before, but it's "invisible" to replicon:
on reconnection, a entity is spawned with NetworkId and ConnectedClient but AuthorizedClient is never added back, and manually inserting it doesn't work either, so no events or replication are sent to the client.

thanks :)

spring raptor
thorn forum
spring raptor
thorn forum
#

there's 4 paragraph.

  1. server startup, two client connect, game start normally.
  2. one client manually disconnect (using renetclient.disconnect()), tries automatically reconnecting, its connecting, its NetworkId entity exists, but replicon doesn't acknowledge it from what i understand.
  3. i entirely quit the client that isn't reconnecting, and launching it again, it works.
  4. tried again 2., manually disconnecting and auto reconnecting, same as 2.

by "auto reconnecting" its just a system on client disconnect that get a new renet2 ServerConnectToken and re connect the client.
tested with a 1s delay on auto reconnecting, doesn't work. i dont understand why connecting from a fresh client works but not re connection.

looking at the log it seems the client is not sending the ProtocolHash event when connecting ?

#

thanks for any tips, or things to test :)

thorn forum
#

tried with AuthMethod::None, Authorized gets added alongside other components, but it ends up at the same place when i added authorized manually on reconnection, events are not send.
the main event send on reconnection is ManualSync, as we can see in the 3. paragraph in the logs.

spring raptor
#

It would be interesting to see the client log.
I suspect that renet2 don't handle reconnects properly ๐Ÿค”

thorn forum
#

omg its reconnecting after waiting 1min !
sorry for the logs level i thought adding more context from my app would help.
ive tried removing RenetClient resource on disconnect, and im inserting a new one anyway on each creation, same problem.

it seems renet2 doesn't disconnect the client when they disconnect, but with a 1 min delay (timeout?) ! and then it handle the new connection. so its a renet2 problem ?

edit: closing my bevy client window instantly disconnect the client server side, but using renetclient.disconnect() doesn't, even when removing renetclient and netcodeclienttransport resources.

lost thorn
#

when i connect a client i send a payload, user_data for unsecure, token for secure auth. how do i access this payload from the server if i'm using replicon? i can't figure it out. none of the examples use it. they just use unsecure and None.

lost thorn
#

I see. I thought there would be a way to hook into the handshake process, possibly to validate and/or reject a connection attempt before it actually connects. Seems here we are already connected because we are sending events?

thorn forum
# lost thorn I see. I thought there would be a way to hook into the handshake process, possi...

yes, bevy replicon build upon a transport layer like renet/aeronet. they handle the socket connection, underlying communication etc. bevy replicon use a already created channel/connection between the client and server.
i think disconnecting client in bevy replicon is ok for most use case, but if you really need to reject connection in a earlier stage of connection, you need to do it with your transport layer.
i know renet2 has some auth with tokens that i am using, other transport layer probably also have some way of doing that.

lost thorn
#

yeah im just confused how i would do that since replicon is already consuming all socket input data on the server

spring raptor
spring raptor
thorn forum
#

the server wasn't removing the disconnected client because i removing the RenetClient/NetcodeClientTransport resources in the same frame, and so the disconnect wasn't sent.

i tried waiting 500ms before removing then, 500ms before trying to connect again, and it doesn't work again, ill try to do a reproduction

spring raptor
#

Let me know if you need any help

viscid jacinth
#

I think we had already talked about this but I forgot; removing Replicated currently sends a 'despawn', right?
Would it be possible to change this to "removing Replicated pauses any replication messages"?
That way:

  • if you want to despawn and send a message: just despawn the entity
  • if you want to despawn without sending a message: remove Replicated, then despawn the entity
  • if you want to not despawn locally, but despawn on the remote: this is what removing Replicated currently does, I guess we would have to find an equivalent?
    Or what was the solution for despawning without sending a message
spring raptor
# viscid jacinth I think we had already talked about this but I forgot; removing `Replicated` cur...

Yes and yes ๐Ÿ™‚
I opened https://github.com/simgine/bevy_replicon/issues/629 for it.
I'll implement it after 0.18 release.

GitHub

Right now, removing Replicated on the server removes the entity on the client. It's not the most convenient behavior. I suggest the following: Removing Replicated should just pause replication ...

thorn forum
# spring raptor Let me know if you need any help

thanks :)
update, im still working on a reproduction with an llm, its struggling lol.
on another branch, another llm is migrating from renet2 to aeronet. just got it working now, and instant reconnect is working with aeronet ! so its renet2, or renet2 integration with replicon.
will update again when i get a reproduction.

lilac mantle
#

For once I don't need help, just wanted to say thanks since I got my "serverless" firestore webrtc lobby backend to work as a replicon backend. I'll do a few tests (mostly latency, stability and edge cases) and probably make a crate out of it if it turns out okay.

spring raptor
thorn forum
#

hey, im switching to aeronet anyway i think, but i fixed my problem with renet2.
just needed to remove the resources before adding them back. when i tried it previously, it was done at the wrong moments.

commands.remove_resource::<RenetClient>();
commands.remove_resource::<NetcodeClientTransport>();
commands.insert_resource(renet_client);
commands.insert_resource(transport_client);

in bevy_replicon_renet2/src/client/plugin.rs, the system responsible for starting the connection transition is gated by the resource_added filter:
set_connecting.run_if(resource_added::<RenetClient>), which doesn't run if the resource is overwritten.
maybe just better documentation on the bevy_replicon_renet2 examples can be added.

thanks for the help debugging

spring raptor
thorn forum
#

hey i was using SendRate::Periodic(TICKRATE * 2), to replicate update for a specific component only every 2 seconds, how to do that now ? PriorityMap is per entity, not per component from what i understand ?

spring raptor
#

(We now track changes per-component as we should've)

thorn forum
#

that would be useful thanks :) my use case is for the position component of troops, which is updated on both server and client so i dont have to repliate to save bandwidth, but just updating every few seconds to re sync the position if any desync happened.
i guess in the mean time i can manually sends the troops position via an event every few seconds

spring raptor
lilac mantle
#

Hello, is there a built-in way to get metrics (latency, etc...) or do I have to make my own implementation ?

lean thunder
#

There's a bunch in a resource

#

RepliconStats or something I don't actually remember

#

But you don't have to make your own you can find it

spring raptor
lilac mantle
spring raptor
lilac mantle
spring raptor
#

You can take a look at bevy_replicon_renet

lilac mantle
#

Ah I see, populated by the backend