#bevy_replicon

1 messages Β· Page 12 of 1

dire aurora
#

Yea, I just feel like the current impl has potential but might not actually be usable, and could instead lead more confusion, like having Components that still need MapEntities

spring raptor
spring raptor
#

@dire aurora I think the proper way would be require both trait for components.
Manual implementators will need to implement 2 more traits instead of 2 methods, but it would improve the ergonomics for most users by a lot. Having replicate and replicate_mapped is super annoying. This also should solve the problem you mentioned.

And I would probably separate the mapper into Mut and regular trait as well. But it's a separate topic.
Do we also need both VisitEntitiesMut and MapEntities?.. Can't we keep only the last once that is compatible with things like HashMap and HashSet?

dire aurora
#

Got it running on 0.16.0-dev πŸ₯³
Just need to expand the example with a usecase for disabling and the entity management ... And probably see if I can get some decent support for immutable components (while migrating I lazily set all bounds to Mutability = Mutable)

#

Also putting the grid texture on the ground made it surprisingly a lot less awful πŸ˜‚

#

Got to try out more 0.16 features because of it too ...

commands.spawn((
    Transform::from_xyz(0., -0.5, 0.),
    RigidBody::Static,
    Collider::from(Cuboid {half_size: Vec3::new(arena_size, 0.5, arena_size)}),
    children![(
        Transform::from_xyz(0., 0.5, 0.),
        Mesh3d(meshes.add(arena_mesh)),
        MeshMaterial3d(mats.add(StandardMaterial {
            base_color_texture: Some(
                loader.load_with_settings("grid.png", |s: &mut ImageLoaderSettings| {
                    s.sampler = ImageSampler::nearest()
                })
            ),
            ..default()
        })),
    )],
));
spring raptor
dire aurora
#

I mean ideally we just wouldn't set nearly as many bounds like that ... Might be a nicer pattern to handle the differences πŸ€”

spring raptor
dire aurora
#

It already has acceleration, but the max speed and acceleration are both not really set to good values

spring raptor
#

Got it! Yep, I would tune parameters a bit.
But looks really great, excited to see Rocket League style rollback in action.

viscid jacinth
dire aurora
viscid jacinth
#

Even if you rollback on collisions, you will still get rollbacks if you don't replicate the Collisions resource I believe. I'm not sure because I have to wait for resource as entities to test it

dire aurora
#

I mean I always have rollbacks so that doesn't particularly matter, the mispredictions from the lack of determinism there have been very minor there, especially compared to sleeping

viscid jacinth
#

I see

sand radish
#

what's the easiest way to do "selective" replication to clients? I seem to have a fair amount of cases where I don't really want to replicate to the "origin" client (since it's a fire and forget kind of thing)

#

I guess AppMarkerExt ?

spring raptor
true phoenix
#

Continuing the discussion from #networking message here as suggested!

First of all thank you for the answer! The other option would be to host the games on one of the clients, but all the transport layers for replicon don’t seem to support p2p like matchbox, or am I wrong?

spring raptor
# true phoenix Continuing the discussion from https://discord.com/channels/691052431525675048/7...

You can let clients host. This is called a listen server configuration.

P2P and client-server are different concepts - they are referred to as architectures. Think of them as mesh and star networks. P2P is when each client connects to every other client. Client-server is when everyone connects to a single server.
Replicon is designed for client-server architecture.
However, chess is a two-player game, so it doesn't really matter which one you use πŸ˜…

true phoenix
#

right yeah, I used p2p improperly, I meant more like handling NAT traversals etc which was what first lead me to decided to have my own hosted server. But it may actually be simpler to just figure out NAT traversal rather than figuring out how to have a single server handle multiple games (also cheaper to run)

#

I could use quinnet with ipv6 and call it a day, but I worry people may not be able to play because of firewalls etc

spring raptor
sand radish
#

what's the best way to know client's lag? (e.g. mapping of frame numbers)

sand radish
#

say I wanted to rollback a client's controlled entity in order to perform something like a shooting action that depends on that entity's position/velocity etc. How can I "sync up" the clocks between server and clients so I can semi reliably know that "this event, it ocurred roughly 6 ticks in the past" ?

spring raptor
#

Better ask @dire aurora about it ^

sand radish
#

is it safe to do things like sending events to client directly on the ServerEvent from renet, or should I handle client connects differently?

dire aurora
# sand radish say I wanted to rollback a client's controlled entity in order to perform someth...

The strategy here would usually be:

  1. The client measures ping and some sort of timestamp from the server and calculates the average ping + stddev, and the average time offset from local time to the server
  2. The server sends out periodic updates about which tick it was at at which timestamp
  3. The client uses the ping measurements and the time offset to calculate at which time the server would currently be at, and how many frames it will take for packets to arrive there
  4. The client runs its simulation at the estimated current server tick + that offset (+ potentially an extra tick or two for packet loss)
  5. The client sends all its input with timestamps for when they are executed
    Then combined with replicon already including ticks for when things happen you should be able to always know where on the timeline anything fits, because there is only a single timeline
#

Notably the server only has one timeline and ignores everything in the past

#

Also if this sounds like a lot of work ... It's cause it is ... I should include the clock sync stuff in my rollback crate too πŸ˜…

#

Luckily if you're developing localhost there is a very easy clock sync algorithm

#

The "algorithm":

let &CurrentTick(mut tick) = events.read().last() else {
    return;
};
*tick += 5; // Add a few ticks so we are ahead of the server
commands.insert_resource(tick);
sand radish
#

Hmm doing

                writer.send(ToClients {
                    mode: SendMode::Direct(ClientId::new(*client_id)),
                    event: NewPlayerShipEvent { player_ship },
                });

inside a ServerEvent::ClientConnected { client_id } does not seem to work, switching to broadcast does tho. Do I need to defer event sends? (using renet)

sand radish
#

ah no, it's any direct sending that seems to be not working. Is there some global config I need to do in order for SendMode::Direct to work? the client_id I use is definitely correct, but I don't get anything unless I switch to Broadcast

#

seems like my client_id (0) is same as servers, even tho the client is actually there and sending client events ok under the same id:

2025-03-11T20:36:01.820770Z DEBUG bevy_replicon::core::event::server_event: sending event `gizmo::entities::BogusEvent` with `Direct(ClientId(0))`
2025-03-11T20:36:01.820832Z DEBUG bevy_replicon::core::event::server_event: resending event `gizmo::entities::BogusEvent` locally
2025-03-11T20:36:03.052265Z DEBUG bevy_replicon::core::event::client_event: applying event `gizmo::entities::Controller` from `ClientId(0)`
#

I'm guessing I need to disable "server is client" in renet somehow?

#

I'm doing

        app.add_plugins(
            RepliconPlugins
                .build()
                .disable::<ClientPlugin>()
                .disable::<ClientEventPlugin>(),
        );
        app.add_plugins(RepliconRenetServerPlugin);
``` for init on the server (and likewise inverted on client)
spring raptor
#

I planning to draft a new release soon, just want to update the quick start guide.

sand radish
#

the client_id is taken from a mut reader: EventReader<ServerEvent> and replicon debug log also reports client_id as being 0 when I connect

#

e.g. 2025-03-11T21:16:01.851323Z DEBUG bevy_replicon::server: `ClientId(0)` connected

spring raptor
#

You can't use 0

sand radish
#

I'm confused, isn't client_id assigned by the server on each connection?

dire aurora
#

Ideally the server assigns it yes, but I think it's the messaging backend that's in charge of that

sand radish
#

well yes, that's why I was handling ServerEvent to get the client_id and assigned them to a "entity to player" map

#

but the id I get from renet is 0

sand radish
#

aaaah damn πŸ˜„

#

but isn't that dangerous, allowing clients to dictate their IDs?

#

or is that ignored by the server?

spring raptor
dire aurora
sand radish
#

but this shouldn't even be part of the auth no? client Id should just be an atomic increasing index on the server assigned by the server to just map to the connections? seems odd to me πŸ˜„

#

ah for reuse/repair

#

still, seems like it should be a separate optional thing

dire aurora
#

In my game the client ID actually just end up being essentially user account IDs

#

Except there is no registration yet so they are just unique for every time the client is opened

sand radish
#

ah ok so it works now, but interestingly enough ONLY if I use ServerEvent and not the Trigger<ClientConnected> (seems like with the trigger the entity client mapping fails for some reason, seems like perhaps ordering issue to me since I send a "new ship for this player" event which is mapped to a just created-there entity from the server)

#

could've been also me, coz I didn't do before(ClientSet::receive) that handler?

#

maybe just a coincidence

spring raptor
spring raptor
spring raptor
spring raptor
#

On the latest master the API is so much better πŸ€”

sand radish
#

yeah I used an observer vs events

sand radish
spring raptor
sand radish
#

happy to guinea pig πŸ˜„ are there many api level changes? I could probably master-check it

spring raptor
sand radish
#

ah cool, I'll try this tomorrow

sand radish
#

is it possible to use ToClients to send from a client as well? (when using broadcast) I have a case where I want to go both ways with same code

spring raptor
spring raptor
#

For the past few days, I've been working on the ultimate quick-start guide.
It now contains much more information, including details on how to write a messaging backend or implement client-side prediction.
My talk at the last Bevy meetup helped me with this.
Any feedback is appreciated: https://github.com/projectharmonia/bevy_replicon/pull/436

vital mist
#

Thanks @spring raptor

#

I was trying to see wich plugin i should use between replicon and lightyear, and replicon seens let me make my custom stuff easier with this example

#

You are a goat

#

Btw wich message plugins you guys recommend?

spring raptor
spring raptor
vital mist
# spring raptor Providing an extensible core is one of our goals πŸ™‚ If you decide to implement ...

This is what iam trying to do

`
Iam making a battlegrounds game, the way i want the connection is like:

-Player press a button like Move (W)
-Player start moving instantly
-Player send a message to server that he pressed W
-Server basead on the delay it took to reach the server will move the player for the position he would be and keep moving the player forward (since its W)
-It also replicated for all clients, sending the direction he is moving, the current position, and a timestamp
-Others clients use this timestamp + the position sent to also move the player for where he should be and keep moving the player on this direction

Having this on mind wich network creates we have rn on bevy and wich one would be better for a replication like this?'

But iam Already facing some issues with this way, i need find something like this to analsy

spring raptor
# vital mist This is what iam trying to do ` Iam making a battlegrounds game, the way i want...

I saw your message in #networking but assumed that you wanted a built-in implementation.
Yes, you could implement it on top of Replicon.

However, I want to note that this isn't how it's usually done. Instead of correcting the client position at the timestamp, the server allows clients to run ahead in time.
When clients receive updates, they apply the state from the server and replay all inputs from that point in time.
Have you watched the Overwatch Netcode GDC talk?

vital mist
#

Wydm ahead of time? @spring raptor

spring raptor
spring raptor
# vital mist Wydm ahead of time? <@243426730851696640>

If you haven't watched, highly recommend: https://www.youtube.com/watch?v=W3aieHjyNvw

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
vital mist
#

@spring raptor

spring raptor
#

And that's what Lightyear also provides

vital mist
#

My english barrier, could you help me a bit?

spring raptor
spring raptor
vital mist
#

until now i was using the server authorative example

sand radish
#

probably more of a renet question but what does 2025-03-13T22:21:42.548331Z ERROR renetcode::server: Failed to process packet: no private key was found for this address mean exactly? I get it sometimes when clients disconnect

spring raptor
#

For some reason I can't link to the original renet πŸ€”
But it's in #1034543904478998539. Link to the first post: #1038137656107864084 message

brittle mulch
#

Mr Shatur I saw your presentation

#

Nice one

spring raptor
#

Thanks :)

sand radish
#

why would --release on a server cause major jitter? debug server works fluid (no matter the client build), but release on is extra choppy

spring raptor
#

We don't have any flags related to mode. But without interpolation you will always have jitter unless you apply interpolation.

sand radish
#

opt-level=0 works, opt-level=1+ broken o.O

#

but it's not one of avian, bevy, bevy_hanabi, bevy_replicon, bevy_replicon_renet I have those specifically on opt-level=3 per package and it still works if using dev/opt-level=0 global

#

this is bizarre

sand radish
#

the only thing I see in logs is this, if I let the release version of the server run a bit: System 'init_ship_physics' has not run for 3258167296 ticks. Changes older than 3258167295 ticks will not be detected.

#

debug does not seem to get this, the system is one of mine

#

is it possible I'm missing some bevy feature and somehow debug build forces it in or something?

#

yeah that's it, removing no-default-features from the server fixes it... (at the cost of it being huge again heh). I wonder what the minimum is ?

#

doh it's multi_threaded feature. Pretty obvious in hindsight

pulsar garnet
#

I'm a bit confused how to set visibility when spawning a replicated entity. Previously I did something like this

let clients_player = c
    .spawn((
        Player {
            id: player_id.clone(),
            name: "Bob".into(),
        },
        PlayerStats {
            move_speed: 2.0,
            mining_level: 1,
            mining_exp: 0,
        },
        PlayerActivity::None,
        PlayerRepliconInfo(trigger.client_id),
        Replicated,
        VisibleToAllPlayers,
        Transform::default(),
    ))
    .id();

// Tell other players about connected player
replicated_clients.iter_mut().for_each(|client| {
    client.visibility_mut().set_visibility(clients_player, true);
});

But now I need to use ClientVisibility to set visibility, which is fine, but I can't work out how to construct it? It looks like there are no publicly available constructors.

It feels like a bit of a UX regression if I now need to add an observer to detect the OnAdd of ClientVisibilty to then set visibility, instead of just setting it when spawning the replicated entity. I'm also curious what happens if the default mode is everything is replicated, but I want to disable visibility, is there a "race condition" where it could start being replicated before my code has the ability to modify ClientVisibility ? It feels extra awkward because I either add an observer to every entity individually to detect OnAdd which is a lot of observer spam, or have a "generic" top level OnAdd observer then I need to figure out what kind of entity I'm dealing with in the observer

Thanks !

#

Btw nice work with the talk on the meetup πŸ‘ super_bevy

#

If it's because you don't want the VisibilityFilter in ClientVisibility to be different from VisibiltyPolicy for whatever reason, perhaps have a public method on some resource containing the VisibilityPolicy that will construct a ClientVisibility with the correct VisibilityFilter for the user. So I can then do let mut vis = Res<VisibilityPolicy>::client_visibility() -> vis.set_visibility(..) -> c.spawn((..., vis));.

#

To give a bit more context, I'm using the whitelist policy, and here's what happens when a player connects currently, this is all done in the Trigger<ClientConnected> observer

  1. Spawns Replicated player entity
  2. Iterates through all in ReplicatedClients and sets their visibility of player entity to true
  3. Query<Entity, With<VisibleToAllPlayers>> and set visibility of all entities to true for the just connected client
  4. Spawn another Replicated entity to store PlayerPrivate component and components that contains data only the player of the client should see, such as inventory, friend list etc
  5. Set visibility of this "private" entity to true for the just connected client
spring raptor
dire aurora
#

Wait ... bincode 2 finally released? πŸ€”

spring raptor
# pulsar garnet I'm a bit confused how to set visibility when spawning a replicated entity. Prev...

I would expect that the only change you need to replace this part:

// Tell other players about connected player
replicated_clients.iter_mut().for_each(|client| {
    client.visibility_mut().set_visibility(clients_player, true);
});

With something like this:

for visibility in &mut clients {
    visibility.set_visibility(clients_player, true);
}

Where clients is Query<&mut ClientVisibility>. Or am I missing something?

The only thing changed is that instead of having a resource with connected clients, we store clients as entities.

spring raptor
# dire aurora Wait ... bincode 2 finally released? πŸ€”

Ah, it's great!
But I think for our needs postcard is better due to how integers are encoded: https://docs.rs/bincode/latest/bincode/config/struct.Configuration.html#method.with_variable_int_encoding
And their "flavors" feature is quite cool πŸ™‚

pulsar garnet
vital mist
spring raptor
pulsar garnet
#

Ohhhhhhh, I was super confused because I thought ClientVisibility went on a per entity basis, and on it you set which clients it is replicated to 🀦

#

So on Trigger<OnAdd, ConnectedClient I can also just query for the ClientVisibility for that same entity

spring raptor
dire aurora
#

Hmmm, would I be correct in guessing that people won't have a clue how an already complex rollback example works if it's a 636 line main.rs? πŸ€”

spring raptor
dire aurora
#

Yea making it simple isn't an option, but I could at least split up the relevant bits from the example game

#

Like these 80 lines to set up the worst rocket league arena you have ever seen really aren't relevant to the rollback πŸ˜…

spring raptor
dire aurora
#

It's a regular example, but I can just make it a folder (it actually already is, but only has main.rs)

#

Technically it is a sepratate crate tho, the root of my repo has bevy_rewind_examples as the package name, and the real crate lives in crates/bevy_rewind πŸ˜‚

spring raptor
spring raptor
dire aurora
#

It's cause the repo has 3 crates and the example uses all of them

#

Kind of like that situation with bevy

spring raptor
#

Ah, makes total sense!

#

BTW, if you're planning to have multiple examples, I remember crystalorb had a really cool demo with almost no gameplay logic.

#

I'm not suggesting adding fancy visualization! I think it's unnecessary for examples.

But having an additional example with just a 2D physics ball simulation might be nice. It should be simpler than a 3D Rocket League example.

dire aurora
#

Example has been chopped up into small files πŸ˜…

   48 toy_cars/avian.rs
  181 toy_cars/connect.rs
  295 toy_cars/gameplay.rs
   52 toy_cars/input.rs
   52 toy_cars/main.rs
   42 toy_cars/simulation.rs
   35 toy_cars/tick.rs
  705 total
spring raptor
#

@dire aurora do you need a network link conditioner for the example backend?

dire aurora
#

Would be useful, tho I can't properly showcase it right now unless I add good clock sync first πŸ˜…

spring raptor
dire aurora
#

I still need to pull the clock sync out of my game's code

#

I never put it in a crate πŸ₯²

spring raptor
#

Ah, got it πŸ™‚

vital mist
dire aurora
#

See the game? Yes, #1211045598854127636
See the code? No
See the rollback stuff? Hopefully soon 🀞

dire aurora
#

If only I had the permissions, we'd have first party networking now ferris_sob

vital mist
dire aurora
vital mist
#

Ofc i dont want see the code xd

#

I myself trying make a battlegrounds with spells + combat xd @dire aurora

#

The replication part is the most annoying

spring raptor
vital mist
#

Nice job, you cooked @dire aurora

dire aurora
#

Too bad it still needs 0.16.0-dev ... But hopefully it's at least runnable without all my local repos now πŸ˜‚

pulsar garnet
#

Just first impressions from upgrading to 31.0, I already mixed up a couple times entities after migrating off client_id (because Trigger<FromClient<X>> no longer provides them). I made the mistake of using trigger.entity() instead of trigger.event().client_entity and since there's no type assistance to distinguish things anymore it's an easy mistake to make.

I got everything working without too much hassle, but so many things in bevy are now Entity identifiers it's tricky to keep in my head what they are all supposed to be representing. I'm sure I'll get used to it, and make my own way of keeping things in check as I go further.

I've resorted to just sticking this on all the Player entities so I can query and search for things when I get an event from the client. I'll probably move to a hashmap in a resource though so it's only one lookup instead of searching through all the entities

#[derive(Component, Clone)]
struct PlayerRepliconInfo {
    client_id: ClientId,
    connection_entity: Entity,
}

Apart from that it's been pretty painless πŸ™‚ !

#

Bit of an aside, I wish Bevy had a first party way of creating Entity newtypes to help distinguish things. Entity is getting pretty overloaded for me personally. I want more distinct types, not less haha

#

but hopefully my complaints aren't discouraging, I'm really enjoying using replicon, I don't think I'd even attempt this project without it super_bevy

dire aurora
#

Yea, Entity at this point is about as helpful as uint64 is to store primary keys in an average database using app, you'd go insane if you did that πŸ˜‚

pulsar garnet
#

Maybe I can make a custom SystemParam so I can pass in the connection entity and it gives me the player entity, for handling client triggers

#

I think the move to using entities is even a good thing, I guess I'm just wishing better tooling existed in Bevy for working with this stuff, since it's the direction things are headed (everything is an entity xD)

spring raptor
spring raptor
spring raptor
pulsar garnet
spring raptor
#

You can always get ClientId from ConnectedClient component on client entities

#

I hope with relations movement in Bevy we will have a nicer way to distinguish between entities πŸ€”
For example, in my game tasks are entities, actors are entities, families are entities, etc.

pulsar garnet
#

Yea, if relations are low overhead enough it would be fine to just get the "Player" entity via it's relation to the "Connection" entity, instead of maintaning my own hashmap

#

I guess right now my options are 1. a component on the Connection entity that has the Player entity 2. a hash map that stores Connection entity -> Player entity.

#

Since I don't want to re-use the connection entity for the players character

spring raptor
#

I would suggest to go with 1 option because after 0.16 you will be able to utilize automatic cleanup on disconnect.

#

In 0.16 they added 1:many relationships

pulsar garnet
#

insert It's Happening meme

#

Hmm actually automatic deletion of the player entity is the opposite of what I'd like haha, I want them to hang around even if the owning player disconnects. I can maintain the hashmap myself thats fine.

#

Well even still it might be better to store something on the connection entity πŸ€”

spring raptor
pulsar garnet
#

Hmm. I'll rubber duck this somewhere else haha, thanks for your help already ^^

brittle mulch
#

Didnt know bevy intended on supporting networking

dire aurora
#

It's still far off into the future, but the plan is for bevy to eventually have the features you'd expect from more main stream engines like un___ and godot

swift hamlet
dire aurora
#

Hmmm, probably cause I removed the Cargo.lock but didn't test without it πŸ€”

#

Ah right, probably cause I didn't do the 0.31 migration yet, but the branch is 0.31 now

#

Oh, I can improve the input crate with the client entities

#

No more Res<ClientEntities> now we have an InputTarget<T> component πŸ€”

dire aurora
#

Hmmm ... The "insert ReplicatedClient" thing seems to be broken

dire aurora
sand radish
spring raptor
#

I would recommend to set rev = "hash" in the Cargo.toml.

#

Commit in bevy-0.16-dev branch before the 0.31 migration was bc30ac0890dbb493fa797be34b81687ee4a14bb5.

spring raptor
vital mist
#

you guys are goat

dire aurora
spring raptor
#

Will document it better.

spring raptor
#

I opened PR for several documentation fixes and a patch version bump.
There's a strange issue reported on GitHub for version 0.31 that I need to look into before publishing a new release.

spring raptor
#

@raw idol I better ask in here to avoid flooding the release thread πŸ™‚ Have you tried making a no_std backend? Just curious.

spring raptor
#

I also just tried the example, nice!

#

But I would add backward movement and apply x movement only if y is non-zero πŸ€”

raw idol
#

it's totally possible to make a no_std backend, the main issue I ran into was Instant::now but bevy solves that for me in 0.16

#

oh also the std hashmap, but you can replace that with a no_std hashmap, and other than that it totally works

spring raptor
#

Awesome! I planning to port replicon after 0.16 release. We don't use anything std-related from Bevy, but no_std requires a adjusting a few places on our side.

vital mist
twilit island
dire aurora
#

Wait, bevy::platform_support is no_std compatible? πŸ‘€

twilit island
#

Every item in that crate is compatible with basically any platform that supports Rust

dire aurora
#

Damn, and here I interpreted the name as "support for platforms that have more than just no_std" πŸ˜‚

twilit island
#

Since it's basically just a replacement for std in a Bevy context

#

Like, eventually I'll put an alternative to Path in there for bevy_asset

spring raptor
#

Drafted a patch release with the required Bevy patch version bump and some documentation improvements.

spring raptor
sand radish
#

is it possible to have an event sent by a client but also consumed by itself, or do I have to separate and use two events? (e.g. I want to do something on this event on the client as well, and then something else on the server)

spring raptor
neon canopy
#

Whats the best way to sync materials and I guess any other properties of a mesh? I have an entity of a flat 2D plane spawned on 0,0 on both the server and client. I just want to keep them completely in sync. For cameras i resorted having a serverside and clientside cameras, replicating the transform of the server camera onto clients and syncing their positions in a system, but for a mesh youd need to have the same materials in both client and server loaded to hotswap materials or change their properties. Also side note on the cameras, still dont know what is the best way to sync camera projections as it seems the projection component doesnt work for 'replicate_group::<(MainCamera, Transform, Projection)>'

spring raptor
dire aurora
#

Projection isn't serialize/deserialize so it makes sense that it wouldn't work πŸ€”

#

I'd probably just sync a component containing the minimum amount of data to calculate the projection and sync that

neon canopy
# spring raptor Do you have the same material assets on both client and server? As for camera, ...

I haven't gotten that far just yet.

I'm just trying to scope out what the kind of default ways of doing things are with this replication paradigm and just managed to get the camera syncing to work which was a nice win.

I think for the material case I'm imagining passing custom attributes to a shader based on where the 'server-side master' mouse pointer is on the 2d map so that maybe ripples propagate from there etc. And so you'd need to somehow propagate this information to any clients observing the scene kind of thing. But I think I can see how that can be done by replicating a mouse position component and using that component in a system on both server and client to do these updates while having the material in both client and server. It's just if there's an easier way to keep resources like that in sync it might be less hastle.

As far as the replication of the camera adding projection to the replicate_group doesn't work because of missing Serialize for Projection (see image) and without replicate_group if I just use the Replicated marker component on the Camera that's spawned, nothing happens.

Here's a gist of the code without a few extraneous bits.

neon canopy
spring raptor
#

You can do the same for materials. If you know the material on both the client and server and only need to sync attributes, create a separate component with the minimal information required and update other components using hooks or triggers.

neon canopy
# spring raptor You can do the same for materials. If you know the material on both the client a...

And so essentially would you say the easiest way for example if I have a synced 2d plane with representing a 2d Map in the scene on both client and server, is to keep a component with information like this is what the texture that is on the map object right now, this is how large the dimensions of this 2d plane representing the map is, this is the attributes for the material synced across to all clients and the clients run a system that would keep their things in sync, i.e. change textures, resize things etc?

#

Thank you btw! I was gonna hack this up anyway and try different things but you can 'skin a proverbial cat' so many ways in bevy that sometimes it's just useful to ask for sensible patterns upfront ❀️

spring raptor
neon canopy
#

And would normal Changed change detection presumably work fine with replicon ? Just to cut down scheduling these syncing systems.

spring raptor
neon canopy
spring raptor
#

Will be totally fine then. It's a problem only if you iterate over a lot of entities that may or may not changed.

neon canopy
#

Thanks lemme go and get this trialed.

spring raptor
#

@dire aurora need your advice about https://github.com/projectharmonia/bevy_replicon/issues/336
We can add 3 more varints to this enum for events (and probably rename it). Last variant would be ReliableEvents(u8) because we need separate channels for them. Then we export event registry to public API to provide a way for backends to get the number of reliable events. We also probabl need to provide an API to get used channel by the event type to let users configure them before initializing the backend.
What do you think?

dire aurora
#

Hmmm, I think the RepliconChannel data is enough for specifying the channel (except maybe if we want to allow grouping of reliable ordered channels into one) ... But then we'd at some point pass the registered events to the backend and it would need to make some decision about which channels to really use, and in the case of channels with multiple events add some kind of header to every event that gets sent ... But it's also possible the backend can already group different channels and already has a big channel ID (say u16) so it would make more sense to avoid ever having those headers

spring raptor
# dire aurora Hmmm, I think the RepliconChannel data is enough for specifying the channel (exc...

I had a slightly different idea in mind πŸ€”
But I like what you're suggesting as well. Maybe we can combine both ideas?

Right now, we have RepliconChannel, which can be obtained from RepliconChannels. However, not all backends have parameters like "max channel size" and may have their own parameters.
Instead of trying to write an abstraction that fits all backends, I think we can just delegate this functionality to the backends.

So, instead of having a resource with channels, my original suggestion was to have something like this:

enum MessageId {
    Update,
    Mutation,
    UnreliableEvent,
    UnorderedEvent,
    OrderedEvent(u8),
}

This can be converted into an index. Inside Replicon, we create messages according to these indices. In the documentation, we specify the minimum required reliability. For example, Updates need to be reliable and ordered, while Mutations can be unreliable or stronger. This way, our example backend fits this spec πŸ™‚
For ordered events, we want them to be separated from each other, which is why we need an additional index for the event.

You suggested that backends can already group messages across channels. So we would need a switch to enable or disable serializing the message ID on the Replicon side.
Then, the integration crate would create appropriate channels. After their creation, users would be able to adjust things like resend time or max channel size using backend's API and then pass the channels to the client or server.

dire aurora
#

Ah, I forgot to respond, but that approach seems fine, tho it might be missing some details for the implementation

#

@spring raptor did you look at my input stuff in rewind yet BTW? I remember you were interested in that

spring raptor
spring raptor
dire aurora
#

In the example just a direction vector yea, it's not coupled to any input crate, as it just expects some component with input data to network

vital mist
#

who did replicon?

spring raptor
#

I just haven't had much time, but I'm still interested.

Makes sense.
My first thought is to add a special integration crate that trigger events based on special component changes πŸ€”
But I will try to take a look soon and suggest something more concrete.

spring raptor
vital mist
#

damm you guys are goat

spring raptor
#

@dire aurora btw, if you want - you can also co-maintain it with us. Depends on if you want to review PRs πŸ€”

dire aurora
#

Are there any more replicon releases planned before 0.16 btw? If not I should probably add rewind to the list in the README

spring raptor
#

I do plan to draft one more with small breakages, I have a few opened PRs. And when 0.16 releases I will draft a new one with pure migration (possibly with no_std support).

dire aurora
dire aurora
#

no_std support would be nice tho, I might see if that's viable for rewind after I get rid of the ugly Mutable bounds πŸ€”

spring raptor
spring raptor
#

It's just really annoying to solve breakages not only from Bevy versions, but also from third-party crates πŸ˜…
This is why I usually try keep releases that update Bevy small

dire aurora
#

Yea that's fair, if all the breakages happen at once it gets pretty painful

spring raptor
#

I even asked Bevy to just release often (like every 2 months), but then it'll be harder for the Bevy team 😒

dire aurora
#

Would be nice if we at least don't have releases that drag on forever like 0.15 did πŸ˜…

#

It's gonna be very fun to port my game soon from 0.14 to 0.16, switching from internal crates to opensourced crates in the process :')

spring raptor
dire aurora
#

Yea, 0.15 is essentially unusable for my game cause I'd have no UI, kayak_ui is essentially unfixable now, while bevy_ui wasn't far enough to even try to use it, but bevy main already had the features I needed a week after 0.15 :')

#

At least I already ported some of the code that got pulled out ... Rewind is even basically already on 0.16 πŸ˜‚

#

Not having switched to 0.15 is also why I haven't yet got any ideas on how to fix bundlication ...

spring raptor
dire aurora
#

I feel like a decent pattern would just be "network (A, B, C) as (CustomA, B, CustomC)"

spring raptor
pulsar garnet
#

What is Bundlication? I still use bundles for most things

dire aurora
#

Tho it used to be a seperate replicon competitor

neon canopy
#

What is the right way to set a function to run on a client inside the Update schedule only after the first replication has happened? I could do some Option<> type stuff but I'd imagine there's some documented way to do things only after the sync?

I guess more specifically I have an observer that fires when it detects a Map object being synced over from the server (trigger: Trigger<OnInsert, Map> event) which inserts a MapTextures resource, however some systems seem to trigger before that while accessing Res<MapTextures> which is yet to be inserted. I guess its a bit complex with the observer firing likely after the systems try to access the MapTextures resource?

spring raptor
spring raptor
pulsar garnet
#

Is there an equivalent to fn on_connected(trigger: Trigger<OnAdd, ConnectedClient>) for clients? I've read through the quickstart and am browsing the repo & source but I'm struggling to find an event/trigger I can observe. I've written my own system that polls RepliconClient but an event that the client has successfully/unsuccessfully connected seems like it should come out of the box xD. Or am I just missing something obvious haha. Not a big deal though, polling is fine

pulsar garnet
#

It's quite counter-intuitive that events don't send from the server if the connection entity doesn't have ReplicatedClient on it. Especially since the clients can send events that the server receives. Hit this while trying to do a "login" request and response.
There's no indication that would be the case in the quickstart guide, on ReplicatedClient, or on .server_trigger().
I can get around it by always adding ReplicateClient and just delay adding Replicated to the entities, and I probably should only add it when the login is fully complete, but it's a weird quick. If nothing else server_trigger doc string should at least mention this behaviour super_bevy

#

If the server can't send messages to connections without ReplicatedClient, it makes replicate_after_connect: false feel like a very awkward thing to use tbh

spring raptor
spring raptor
pulsar garnet
spring raptor
pulsar garnet
#

I guess. But starting replication is a one way thing right, the docs mention you can't remove the ReplicateClient component later

#

Imo just don't send the wrong messages at the wrong time haha. The receiver should always check whether it's valid to apply the new event anyway. Preventing users sending events during the short period before they start Replicating doesn't seem that useful. You can still send all kinds of messages while people are in states that aren't expecting them at any time after replication has started. personally the weirdness of having events swallowed with no warnings it was happening is a tradeoff I would prefer not to take, it feels unusually opinionated for what is otherwise quite a low level library.

But maybe It'll grow on me, who knowns. Probably will just skip using replicate_after_connect though and add Replicate component when I'm ready I think πŸ€”

spring raptor
spring raptor
#

But, as I mentioned, you can opt out of it with make_independent.

#

A warning when you sending a non-independent event to a client would be nice, tho πŸ€”

pulsar garnet
#

I'm wondering how you can "force" users to know the distinction between the two so you don't get more silly ducks like me complaining in discord xD. Maybe in addition to passing the RepliconChannel there's a second argument that's something like EventSync

enum EventSync {
    WithReplication,
    Independent
}

app.add_server_event::<LoginEvent>(ChannelKind::Ordered, EventSync::Independent);
spring raptor
pulsar garnet
# spring raptor Not sure, a bit too verbose for my taste... πŸ€” You almost always want your even...

Yea.. even the chat example you gave earlier, it's easy to think of situations where you want to know ahead of that message some information about the sender.

I guess it comes from a difference in how you think of an event. My assumption was it's akin to a ergonomic packet being send to the other party, where replicon treats them as a kind of temporary data that needs to be replicated for 1 frame. Hence my confusion that replication would have any impact on "packet sending".

#

I guess I really don't have any issues with the independent/nonindependent event stuff, just that how replicate_after_connect works does not fit my mental model. It feels a bit redundant, it only really seems useful if you're using VisibilityPolicy::All, if you're using whitelist you can instead either wait to add Replicate or wait to add visibility, depending on the situation.

spring raptor
spring raptor
#

Its purpose is to exchange messages before replication starts.
We even showcase this in our tic-tac-toe example: we send client cells to properly map them on the server.

spring raptor
pulsar garnet
pulsar garnet
#

My rust isn't good enough to abstract over observers to wrap Trigger<FromClients<E>> to avoid entity soup the big brain way so macros it is xD hopefully this helps me avoid more panics #ecs message

spring raptor
pulsar garnet
#

ah thats handy

raw idol
#

@spring raptor FYI, I've got aeronet_io and aeronet_transport working on no_std with bevy 0.16.0-rc.1 now. If you get replicon working on no_std then lmk, I'd like to update aeronet_replicon to no_std as well

spring raptor
#

Awesome, I planning to take a look into RC support with no_std today after work.

twilit island
spring raptor
#

I wonder if we can have a networking crate for GameBoy link cable πŸ€”

twilit island
#

That'd be insane

dire aurora
#

I wonder how replicon's architecture holds up on a gameboy πŸ€”

spring raptor
#

I only needed to remove a single dependency: https://github.com/projectharmonia/bevy_replicon/pull/449

After @echo lion merges this and a bunch of other unrelated PRs, I'll draft the 0.15 release and an RC with no_std support πŸ™‚

brittle mulch
#

@spring raptor Mr Shature might ask what are the tool utilized in your CI, what is codecov and Typos used for. My project has become quite biggie and I would like to have a good checker like yours

dire aurora
#

codecov is for the code coverage of unit tests, and typoes just picks up common spelling mistakes

spring raptor
# raw idol <@243426730851696640> FYI, I've got aeronet_io and aeronet_transport working on ...

Updated to RC and added no_std support: https://github.com/projectharmonia/bevy_replicon/pull/428
We don't even need to feature-gate anything; the crate is no_std by default. Except for the migration to Bevy RC, I only had to adjust imports.

I haven't published it on crates.io yet, as I want to merge https://github.com/projectharmonia/bevy_replicon/pull/447 to master first. Just waiting for a review from @echo lion.
For now, just use the bevy-0.16-dev branch πŸ™‚

#

@dire aurora looking at the dependencies for bevy_rewind, I think it can also be ported to no_std πŸ€”

dire aurora
#

Yea I'm planning to no_std every crate I can, even if I can't understand the reasons they'd be used in a no_std app

#

I'm sure @twilit island needs rollback networked SDFs on their toasters πŸ˜‚

spring raptor
#

It's also useful for porting to modern consoles where std is not available πŸ™‚

brave sonnet
#

Hi, one question from the client how I know which client entity I am, so I can add the camera to the correct one? Before (on ver. 0.30) I was using client: Res<RepliconClient> to get the id but now I dont know how. thanks πŸ™‚

carmine sand
#

I have the same question as enigma ^ along with a few others:

  1. does replicon only send component data across the wire when it detects that the component changed? I would assume so but I couldn't find it explicitly mentioned

  2. is there a way to send an event to a single client?

  3. Is there a way to hook systems into replicon's tickrate? I want my game logic and server logic to run at 64hz but I feel like if just set bevy's fixed time step and replicon to 64 hz, I may get off-by-one errors if they aren't actually in the same schedule

dire aurora
dire aurora
spring raptor
brave sonnet
spring raptor
brave sonnet
#

Ok, thank you very much, I will do that πŸ™‚

carmine sand
#

awesome, thank you! Glad to hear bevy_replica has everything I need to get to work on my game πŸŽ‰

vital mist
#

I wanted to know How exactly replication and prediction Works, so far i Saw i need save the world state in Ticks, every completly run (First - Last) i increase +1 and save the state It was on this tick right ? This is the principle i Guess

dire aurora
#

In practice it's a huge pain to set up and it saves a lot of time to rely on lightyear or bevy_rewind to support this for you

vital mist
#

Iam getting also ignored a bit by him, so i Will end up need make myself

dire aurora
vital mist
#

But not ALL "weapons" have fast projectike

#

Like Fireball is slow and is a ball projectike

#

Ik How lag compesation projectile stuff, did It a Lot of times on others engines before

#

My only problem is Just movement

#

For projectile i Just send the player when the projectile started, teleport It where it should be than keep moving from it xd

#

So yes Lightyear is not working well for me

#

Mainly making the character controller floating , because that lot of roolback happens when the character jump, since it move back down a bit because the floating being done on both sides

#

So yes.... Its a big issue rn

#

Sadly

dire aurora
vital mist
#

It doenst sadly

#

Like the vΓ­deo shows

#

Sometimes player gets pushed down

#

And fr i have 0 ping here still takes almost half of second to server start the jump

#

Look the vΓ­deo xd

#

So I think i Will need make myself

spring raptor
brittle mulch
#

Hmm weird I have exactly the same thing and it works fine, a float character controller that floats

vital mist
#

And How are you making It float?

#

Can i see It?

carmine sand
#

The guide says to keep all registrations in a common area so that they're always called in the same order, but I'm getting an error when add_server_event is called on the client and vice versa. Is there an indication of which things need to be called on both and which things should only be called on one or the other?

dire aurora
carmine sand
#

ah I'm not using feature flags. I had added client and server plugins separately hoping that would be enough

brittle mulch
#

A really stron one

#

In very very valet tutorial they dont show the "constant values", so you probably just have a very weak spring

brittle mulch
dire aurora
#

Now of course that result might not be what you want but that's an entirely separate problem (and usually much easier to fix)

brittle mulch
vital mist
vital mist
brittle mulch
#

A rollback it seens

spring raptor
carmine sand
#

to be clear the error I get when I try to call add_server_event on the client is Called 'receive_message' with invalid channel 2

spring raptor
spring raptor
carmine sand
#
=> RUST_LOG=bevy_replicon=debug cargo run

thread 'Compute Task Pool (10)' panicked at /home/aekobear/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/renet-1.0.0/src/remote_connection.rs:349:13:
Called 'receive_message' with invalid channel 2
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `bevy_replicon_renet::server::RepliconRenetServerPlugin::receive_packets`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
#

running through the sample app to see if I did anything wrong

spring raptor
carmine sand
#

I do have that already

#

working on adding logging plugin now

#

actually if you just mean bevy's default LogPlugin then yes, that's already on

dire aurora
carmine sand
#

I have the event plugins enabled too. When I print the output of get_client_configs:

let channels = app.world().resource::<RepliconChannels>();
println!("GOT CLIENT CONFIGS: {:?}", channels.get_client_configs());

It only gives me two channels:

[
  ChannelConfig { channel_id: 0, max_memory_usage_bytes: 5242880, send_type: ReliableOrdered { resend_time: 0ns } }, 
  ChannelConfig { channel_id: 1, max_memory_usage_bytes: 5242880, send_type: Unreliable }
]

Shouldn't ReliableUnordered be there? Why would that be missing?

dire aurora
#

Does this code run before or after all events have been registered?

#

Cause that's just the two standard replication channels I'm pretty sure πŸ€”

spring raptor
carmine sand
#

This is run before events have been registered. But I see this as a fundamental discrepancy because the channel config only has 2 channels, but the ChannelKind enum has 3

spring raptor
spring raptor
dire aurora
spring raptor
#

Yes, that's possible common mistake

carmine sand
#

was that documented somewhere?

dire aurora
#

Not sure, but we should probably fix this particular footgun ... It doesn't seem super necessary πŸ€”

carmine sand
#

Yeah if possible that would be great. I just followed the guide from top to bottom and the server creation part came before the event registration part, so I could see other beginners running into the same problem

lost thorn
#

is there any sort of interest management in replicon? is there anything exposed to customize control over the replication process? it seems like kind of a black box at the moment with the only control being binary visibility, something is either constantly replicating or not replicating at all.

dire aurora
viscid jacinth
#

i've been working on splitting up lightyear into multiple crates to make things more modular; that means that I might be able to be backend-agnostic, so I could try supporting both aeronet or replicon as additional backend!

spring raptor
spring raptor
raw idol
#

@spring raptor have you checked if bevy_replicon compiles with --target thumbv6m-none-eabi? I'm working on getting aeronet to not only be no_std, but also no-atomic-CAS for targets without AtomicU64 or alloc::sync::Arc

#

this takes quite a lot more work than just no_std, and there are some upstream crates that I'm battling with to get compilation fixed

#

@twilit island on the topic of going beyond no_std: why choose thumbv6m-none-eabi specifically as the "no std/no-atomic-CAS" target? And is it even worth it to try to get things compiling for a target like this?

twilit island
# raw idol <@623813633679556609> on the topic of going beyond no_std: why choose `thumbv6m-...

Purely chosen because it's the CPU in a GameBoy Advance (the smallest 32 Bit platform that's tangentially related to game development I could think of, and which happens to have excellent support in Rust via agb). Since I already put in the work to ensure the core crates rely on portable-atomic (which can polyfill CAS using critical-section), I figured I'd leave that target in CI as a stretch goal and to prove portable-atomic support still works

#

In fact, you can use bevy::platform_support as a stand-in for some of std (Mutex and RwLock via Spin, Arc from portable-atomic-util, HashMap from Hashbrown, etc.) to jump-start no_std/no_atomic efforts

#

Our goal is that crate will be filled out with Bevy's opinionated alternatives to std, primarily for wasm but also for the no_std platforms too

raw idol
# twilit island In fact, you can use `bevy::platform_support` as a stand-in for some of `std` (`...

Yep I've been doing exactly this, and I've managed to get 2 of my main crates compiling now πŸ₯³ but there are some crates I use like bitvec and ringbuf which don't use portable-atomic-util, and rely on alloc::sync::Arc instead, which I had to fix. And since no-atomic is a more niche target than no-std, other crates in the ecosystem will likely also have this same oversight, so I'm not sure how far I can push no-atomic

#

So, my issues mostly stem from other ecosystem crates, not bevy itself

raw idol
twilit island
twilit island
#

For example, imagine getting rust-gpu support so you can use Bevy on the GPU

raw idol
#

yeah, I just wish there was an easier way to polyfill stuff like Instant and Arc, and have it "just work" in crates without having to worry about critical-section or mocking Instant or anything

twilit island
#

Yeah it's not ideal. At least for Bevy people are already used to relying on it as an augmentation of the standard library; a few extra types doesn't appear to be too crazy

spring raptor
spring raptor
#

@twilit island I just tried building for thumbv6m-none-eabi, but discovered that bevy_log requires std.
I included it for re-export of things like debug!, trace!, etc. I can start to depend on tracing directly, but maybe we could shim bevy_log? πŸ€”

raw idol
twilit island
#

The thing you miss is span support

raw idol
#

no span support is :(

#

I had to remove spans from my transport code to make it work

twilit island
#

Yeah I do have a PR to add portable-atomic to tracing, and they said they're interested, but they haven't reviewed yet

#

Hopefully one day

spring raptor
twilit island
spring raptor
#

To use tracing or log depending on std feature

twilit island
#

Since the shim would just be a re-export of log anyway, I think it's "fine" to just suggest crates bring log in for no_std logging

#

Not ideal though, I do want to change that

spring raptor
#

Makes sense. Will switch to log for now πŸ™‚

spring raptor
raw idol
spring raptor
# raw idol for `bytes/extra-platforms`, I export this under a `critical-section` feature. I...

Otherwise it gives me this:

    Checking portable-atomic v1.11.0
error: dependents require atomic CAS but not available on this target by default;
       consider enabling one of the `unsafe-assume-single-core` or `critical-section` Cargo features.
       see <https://docs.rs/portable-atomic/latest/portable_atomic/#optional-features> for more.
   --> /home/gena/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/portable-atomic-1.11.0/src/lib.rs:455:1
    |
455 | / compile_error!(
456 | |     "dependents require atomic CAS but not available on this target by default;\n\
457 | |     consider enabling one of the `unsafe-assume-single-core` or `critical-section` Cargo features.\n\
458 | |     see <https://docs.rs/portable-atomic/latest/portable_atomic/#optional-features> for more."
459 | | );
    | |_^

error: could not compile `portable-atomic` (lib) due to 1 previous error
raw idol
#

What I do is export a critical-section feature in aeronet_io, which enables bevy_platform_support/critical-section

#

that should work for you as well

#

but I don't like how via this approach, this critical-section feature "infects" all other crates

#

there is a better way I think

spring raptor
#

And looks like I have to pull bevy_platform_support directly

#

Ah, no, I can do bevy/critical-section πŸ€”

#

But it results in a bunch of errors like this:

rror[E0599]: no function or associated item named `sin` found for type `f64` in the current scope
   --> /home/gena/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/glam-0.29.2/src/f64/math.rs:127:14
    |
127 |         f64::sin(f)
    |              ^^^ function or associated item not found in `f64`
    |
help: there is a method `min` with a similar name
    |
127 |         f64::min(f)
    |              ~~~

error[E0599]: no function or associated item named `sin_cos` found for type `f64` in the current scope
   --> /home/gena/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/glam-0.29.2/src/f64/math.rs:132:14
    |
132 |         f64::sin_cos(f)
    |              ^^^^^^^ function or associated item not found in `f64`
#

If I just enable critical-section on portable-atomic - it works.

spring raptor
#

Shouldn't we mark them as ? when specifying dependencies?

#

@raw idol in bytes, they re-export only features they actually use. They don't re-export portable-atomic, for example πŸ€”

raw idol
#

they have extra-platforms feature which enables portable-atomic, right

spring raptor
raw idol
#

ah I see

#

so they enable portable-atomic, but they don't enable critical-section

#

and we can do the same?

spring raptor
#

Yep, they basically don't enable anything.

#

But it's not convenient for CI. I can't pass --features portable-atomic/critical-section because I don't depend on portable-atomic directly.

raw idol
#

yeah, that's really annoying. could we have both feature flags, maybe?

#

or alternatively

#
[features]
extra-platforms = ["dep:portable-atomic"]

[dependencies]
portable-atomic = { optional = true }

but we never actually use portable-atomic in the source code, just include it in dependencies

#

then you can do --features portable-atomic/critical-section

spring raptor
#

Yes, that's also an option.

#

But a bit weird - I wonder what @twilit island thinks about it.
Should we re-export critical-section even if our crates don’t care about it? And if we do, what about all the other features of portable-atomic? Or should we re-export bevy_platform_support/critical-section?
And why does enabling critical-section on bevy pull more deps? πŸ€”

twilit island
# spring raptor But a bit weird - I wonder what <@623813633679556609> thinks about it. Should we...

Enabling critical-section on Bevy shouldn't pull in more dependencies, my guess is I forgot a ? in the feature flags! The error you're getting sounds like I wrote bevy_math/critical-section instead of bevy_math?/critical-section.

As for re-exports, in the embedded world it's typical for users making a binary to directly include portable-atomic and critical-section so they can configure those flags like unsafe-assume-single-core. The only time libraries would enable that feature is when you're making a HAL crate for a platform where you know it's single core.

#

But it's also reasonable to bring in those crates just to re-export the features. Since they're in the dependency graph anyway it's no harm

carmine sand
#

my server is not receiving events sent by the client. my setup is:

// called on both client and server in the same order
fn register_auth_netcode(app: &mut App) {
  app.add_server_event::<AuthResponse>(Channel::Ordered);
  app.add_client_event::<AuthRequest>(Channel::Ordered);
}

// triggered on the client (I have confirmed that this code runs)
fn send_request(mut request: EventWriter<AuthRequest>) {
  request.send(AuthRequest::Foo);
}

// running on the server. (I confirmed the system is running but reader is empty)
fn receive_request(mut requests: EventReader<FromClient<AuthRequest>>) {
  for request in &requests {
    println!("received: {:?}", request);
  }
}

Is there something else I should be doing? The client connects and entity replication is working, only events aren't being received

#

(I'm on the very latest bevy_replicon commit)

carmine sand
#

I haven't done so yet, so that's not the problem. Though good to know, because I plan to do so in the future πŸ‘

#

I forked bevy_replicon to add some print statements for debugging, and weirdly what I'm seeing is that in the ClientEventPlugin build() gets called but finish() never does:

#

IE line 24 executes but line 28 does not

spring raptor
carmine sand
#

oh! that's gotta be it! learned something new today haha, thank you!

spring raptor
spring raptor
# twilit island But it's also reasonable to bring in those crates just to re-export the features...

Agree.

I decided on a practical approach.
I re-export only extra-platforms from bytes which users don't include directly, but my library uses.
Bevy re-exports critical-section, but it uses it, and since users already include it in their Cargo.toml, it makes sense not to re-export it from third-party crates (unless they have some code that depends on it). I can always enable it on CI by passing bevy/critical-section.

#

@raw idol we might want to sync our approaches ^ πŸ™‚

raw idol
#

here's what my CI looks like now:

build-minimal:
  runs-on: ubuntu-latest
  strategy:
    matrix:
      package: ["aeronet_io", "aeronet_transport"]
  steps:
    - uses: actions/checkout@v4
    - uses: dtolnay/rust-toolchain@stable
      with:
        targets: thumbv6m-none-eabi
    - uses: ./.github/actions/install-bevy-deps
    - run: cargo build --target thumbv6m-none-eabi --package '${{ matrix.package }}' --no-default-features --features extra-platforms,bevy_ecs/critical-section
#

it's kind of crazy how a solid networking, replication and rollback stack could be possible on embedded hardware soon

twilit island
# spring raptor Agree. I decided on a practical approach. I re-export only `extra-platforms` fr...

Only suggestion I have is for bevy_platform_support I take advantage of the built-in target_has_atomic Cargo flag to automatically enable portable-atomic when required. I think the same would apply for bytes/extra-platforms too, since it's just a feature to enable portable-atomic:

[target.'cfg(not(all(target_has_atomic = "8", target_has_atomic = "16", target_has_atomic = "32", target_has_atomic = "64", target_has_atomic = "ptr")))'.dependencies]
portable-atomic = { version = "1", default-features = false, features = [
  "fallback",
] }

It's a little lengthy, but basically it forces the inclusion of portable-atomic if any atomic features aren't available. Then inside bevy_platform_support I can use the individual flags to incrementally include from either core::sync::atomic or portable-atomic

#

Downside to that approach is it puts portable-atomic in the Cargo.lock always (since it is required, just for a different "target"), so it's not a clear black and white choice

#

For Bevy it's fine, since we already include so many dependencies like that, so I think a community crate can use the same trick too if they'd like. For a more fundamental crate like bytes it makes sense to have an explicit feature

#

Especially because bytes supports an MSRV before Rust even stabilised the dep:foo syntax

spring raptor
# raw idol here's what my CI looks like now: ```yaml build-minimal: runs-on: ubuntu-lates...

Yes, I'm also planning to add thumbv6m-none-eabi to CI, but waiting for the next RC for this fix since I depend on bevy directly πŸ™‚

I would also suggest depending on bevy directly. If you need to patch Bevy for your game and any crate depends on bevy_*, you'll need to list every single bevy_* dependency (even transitive) in the patch section.
Also, I think you could replace build with check to speed up the CI run πŸ€”

spring raptor
twilit island
raw idol
# spring raptor Yes, I'm also planning to add `thumbv6m-none-eabi` to CI, but waiting for the ne...

I don't want to depend on bevy directly since I don't use most of bevy, only some specific subcrates, and I don't want consumers to have to wait until bevy is built to build my crate. It also means I'm more strict with what features I use, and keep the bevy dependencies minimal.

I prefer using build in CI because it's more comprehensive, and it checks not only that the rust is valid, but also that it can compile and link the object successfully. I have had cases before where check passes but I can't actually build due to some kind of error afterwards.

spring raptor
# raw idol I don't want to depend on bevy directly since I don't use most of bevy, only som...

I also use a minimal subset, controlled by enabled features. That's what's advised on the Bevy website.
The only advantage of using bevy_* crates directly is build times. But this only affects fresh builds, and I don't think the impact is that significant... It's up to you - some crates follow this approach, some don't.
I just hate specifying every Bevy crate when I patch my game πŸ˜…

It checks not only that the Rust code is valid, but also that it can compile and link the object successfully.
When compiling a static library, there's no link stage - it's just a collection of objects. So check skips code generation.
Bevy also uses check.

raw idol
spring raptor
vital mist
spring raptor
#

Weird - I depend on rc.1, but it looks like the latest commit tried to use rc.2 that just dropped πŸ€”

#

Probably it assumes semver compatibility across RC

dire aurora
#

Same for rc.1 -> .0

spring raptor
#

Published RC 2.
replicate_mapped now deprecated, regular replicate now just works.
And compilation for GBA now checked on CI.

twilit island
spring raptor
twilit island
spring raptor
twilit island
#

Yeah that's true, mgba does support multiplayer anyway

spring raptor
#

Having an actual retro console is much cooler, of course πŸ˜… There are so many awesome mods out there: https://www.youtube.com/watch?v=uZyLY2Uyi04

Use this link to SAVE $5 on your first order at PCBWay: https://pcbway.com/g/A311e7

FunnyPlaying recently released a brand new replacement motherboard for the Game Boy Advance. It features all new components only requiring you to transfer over the CPU and RAM chip from a donor console. This is a great product for not just enhancing the GBA c...

β–Ά Play video
#

@dire aurora working on bevy_enhanced_input right now. Want to prepare it for networking.
So not sure if I'll be able network relations for the next release. But it will be my top priority feature πŸ™

raw idol
#

Actually bevy_ecs does use atomics in its own codebase, but I don't, so I would have no use for this - is that correct?

lost thorn
#
    rtt: 0.06253721827819352,
    packet_loss: 0.0,
    sent_bps: 239.29824561403507,
    received_bps: 234.03508771929825,
}```

do these stats make sense for a single client connected to a server on localhost?  this is the only data i'm replicating

```impl Plugin for RepliconLockstep {
    fn build(&self, app: &mut App) {
        app.replicate::<NetworkTick>();

        app.add_systems(PreUpdate,
            replicate_tick
                .before(ServerSet::Send)
                .run_if(resource_changed::<ServerTick>.and(server_running))
        );
    }
}

#[derive(Component, Serialize, Deserialize, Deref, Debug)]
#[require(Replicated)]
pub struct NetworkTick(u32);

fn replicate_tick(server_tick: Res<ServerTick>, mut net_tick: Query<&mut NetworkTick>) {
    if let Ok(mut tick) = net_tick.get_single_mut(){ 
        tick.0 = server_tick.get();
    }
}```
I have the server tick rate set at 1 tick/s currently.
63ms rtt to localhost?  Seems a bit high.
transfer rates seem high also for replicating one u32 per second.
spring raptor
spring raptor
raw idol
spring raptor
twilit island
raw idol
carmine sand
#

what's the best way for a server to detect that a client has disconnected? It looks like the client_just_disconnected condition is designed for clients to use

spring raptor
# raw idol Having another look at depending on bevy vs bevy subcrates, the page you linked ...

I'm not a native speaker, but this seems to explicitly suggest that I should use bevy directly:

To avoid long build times in your plugin and in projects using it, you should aim for a small crate size:
...
You should add default-features = false to the Bevy dependency in your Cargo.toml and manually specify the features you need.
πŸ€”
Thanks for linking to the discussion!

spring raptor
raw idol
#

but I can see how it can cause confusion πŸ˜…

spring raptor
#

I hope the community decide on this and state it explicitly πŸ™‚

carmine sand
#

for what it's worth, the docs specifically say that it's user preference:

The bevy crate is just a container crate that makes it easier to consume Bevy subcrates. 
The defaults provide a β€œfull” engine experience, but you can easily enable / disable features in your project’s Cargo.toml to meet your specific needs. See Bevy’s Cargo.toml for a full list of features available.

If you prefer, you can also consume the individual bevy crates directly. Each module in the root of this crate, except for the prelude, can be found on crates.io with bevy_ appended to the front, e.g. app -> bevy_app.
spring raptor
#

Yeah, there are definitely some use cases where users might want to depend on specific crates.

dire aurora
spring raptor
spring raptor
#

I don't think it's Replicon, maybe some other crate?

#

Because it wouldn't compile with 0.15

#

Yeah, it's weird

#

Check with cargo tree maybe?

#

It's not a cure, it shows dependency tree

#

It looks like you have old bevy_replicon somewhere in your dependency tree

dire aurora
#

I think I had similar confusing errors because cargo was going crazy ... I then removed Cargo.lock and it worked πŸ™ƒ

#

How can you not have a Cargo.lock? cargo automatically makes one every time it does anything πŸ€”

spring raptor
#

Ah, I think it should be v0.32.0-rc.2

#

With a dot

dire aurora
#

Or just 0.32.0-rc

#

wait...

#

Why is that 0.32.0-rc when 0.32 exists?

#

Shouldn't it be 0.33.0-rc?

spring raptor
#

Ah, right! My bad

dire aurora
#

The only way to pick it up right now would be version = "=0.32.0-rc.2"

spring raptor
#

@vocal violet published 0.33.0-rc.2.

vocal violet
spring raptor
#

Or use example backend as a stub πŸ˜…

#

We have a special SERVER constant

#

We just need a special constant that can't be spawned.
Maybe when resources become entities, we could use a real entity for it.

spring raptor
torpid adder
untold hull
#

What did you import to get pick_from() in there? That seems super useful

torpid adder
dire aurora
#

Replicon uses postcard instead of bincode now so that shouldn't be a problem

pulsar garnet
#

It appears that component lifecycle events are not fully replicated on client and server for replicated components.

Eg I insert the PlayerActivityMoveTo component to a replicated entity on the server, this will print out "PlayerActivity Inserted" on both server and client. Then in response to player input, I remove it then add a new PlayerActivityMoveTo component on the same entity. Server correctly prints "PlayerActivity Inserted" but client does not, no OnAdd or OnInsert is triggered on the client. It appears to be mutated in-place. Essentially the semantics of the component operations are lost by how replicon is functioning.

// app.add_observer(...) in shared plugin (user by both server & client)
fn on_player_activity_move_to_inserted(trigger: Trigger<OnInsert, PlayerActivityMoveTo>) {
    info!("PlayerActivity Inserted");
}
#

I can see why this might be the implementation strategy, if a component was removed then added within a replicon tick then from a pure data perspective there has only been a mutation. But in practise it means I can't rely on component lifecycle events to function consistently across both server and client. I can no longer use OnAdd or OnInsert observers for replicated components with confidence. It also means I need to make up my own new event to signal to the client that this operation has happened.

#

Personally it feels like more of an opt-in optimisation to avoid syncing component lifecycle stuff if the user decides they don't actually care about it, but from a library usability perspective it loses information and means if I run the client & server in the same bevy app I will get different behaviour to if I ran them in separate apps while using OnAdd/OnInsert triggers.

spring raptor
empty musk
#

Hi there i have a quick questiono, currently im using the following code and replicating the Ship component: if client_id.is_some() { commands.spawn( Ship(client.id().unwrap()) ).set_parent(overworld_root); } else { commands.spawn( Ship(ClientId::SERVER) ).set_parent(overworld_root); }

to replicate a ship to every client so they can spawn while knowing to which player the ship belongs, is this the correct way of doing so? Or is there a better solution, thanks!

dire aurora
#

In my game I use client prespawning and use ClientEntityMap on the server to sync it back to that same entity, but that only lets you know which ship is yours, not which player owns which ship ... If you need the latter, the only way to improve this is to get rid of the duplicationg by doing Ship(client_id.map(|c| c.id().unwrap()).unwrap_or(ClientId::SERVER)) or whatever (I wonder if we have a shorter way to do that)

empty musk
#

Thanks for your response! Yea i was also wondering the same. Mostly because to check if the client id is the local player ends up looking like this:

if client_id.map_or(ship.0 == ClientId::SERVER, |id| id == ship.0) {
which doesnt look great either

spring raptor
empty musk
#

yea that is what i changed it to! thanks for responding!

spring raptor
#

It's also a bit nicer with in the latest version.

#
commands.spawn(Ship(network_id.unwrap_or(SERVER))).set_parent(overworld_root);
thorn forum
#

hey, just upgrading to latest version. is there a better version for this system ? it feels like a downgrade in DX for connection/disconnection. thanks :)

// before
pub fn on_client_disconnected(trigger: Trigger<ClientDisconnected>) {
    let event = trigger.event();
    info!(
        "client {:?} disconnected: {:?}",
        event.client_id, event.reason
    );
}

// now ?
pub fn on_client_disconnected(
    trigger: Trigger<OnRemove, ConnectedClient>,
    q_connected_clients: Query<&NetworkId>,
    renet_server: Res<RenetServer>,
) {
    let Ok(networkid) = q_connected_clients.get(trigger.entity()) else {
        return warn!("couldn't find the connected client");
    };
    let Some(reason) = renet_server.disconnect_reason(networkid.get()) else {
        return warn!("couldn't get the disconnect reason");
    };
    info!("client {:?} disconnected: {:?}", networkid, reason);
}
spring raptor
thorn forum
#

i was using struct Owner(pub ClientId> on every entity that had a owner.
i want to allow reconnection, and so the ClientId need to stay the same. its not possible by using the Entity directly, so NetworkId is fine ?

thorn forum
spring raptor
spring raptor
thorn forum
#

ClientId::SERVER -> NetworkId::new(0) then

#

ofc its better like this then :)

spring raptor
thorn forum
#

hm ill keep using a clientid of 0, since i have a lot of systems using the clientsid component, and i added it as a resource on the client itself for easier comparaison, so see if a entity is himself or not (especially for sprites). having a option is the correct approach but would require more code everywhere to do the same thing (alias unwrap_or_default). thanks !

spring raptor
thorn forum
spring raptor
thorn forum
#

oh yeah i guess 99% of the usage is just Eq, so it doesn't add more boilerplate to the user. perfect then

lost thorn
#

when my server panics and dies, my client still shows as connected. if i ctrl+C my server then it disconnects fine. does this make sense? using renet.

pulsar garnet
lost thorn
#

i waited for a little bit and it didnt' seem to. maybe i need to wait longer

thorn forum
#

hello, i tried updating to uuid 1.16 and got this:

error: failed to select a version for `uuid`.
    ... required by package `bevy_core v0.15.3`
    ... which satisfies dependency `bevy_core = "^0.15.3"` of package `bevy_internal v0.15.3`
    ... which satisfies dependency `bevy_internal = "^0.15.3"` of package `bevy v0.15.3`
    ... which satisfies dependency `bevy = "^0.15.3"` of package `bevy_replicon v0.32.0`
    ... which satisfies dependency `bevy_replicon = "^0.32.0"` of package `game_client v0.1.10-dev (/home/mirsella/dev/waykeepers/game_client)`
versions that meet the requirements `=1.12` are: 1.12.1, 1.12.0

all possible versions conflict with previously selected packages.

  previously selected package `uuid v1.16.0`
    ... which satisfies dependency `uuid = "^1.16.0"` of package `backend v0.1.0 (/home/mirsella/dev/waykeepers/backend)`

failed to select a version for `uuid` which could resolve this conflict

i tried cargo update, and deleting the lockfile.
im obligated to use 1.12 since all the deps use it ?
cargo tree shows everyone using 1.12 or 1.12.1
thanks

spring raptor
thorn forum
#

oh its a renet2 issue then. 0.16.0 was working with renet 0.0.5 and replicon 0.30 but not 0.0.7 for replicon 0.32. thanks

#

nevermind its bevy locking uuid to version =12. but it was working before upgrading to latest bevy_replicon and renet2 so i dont know how

spring raptor
#

Check your dependencies maybe something uses older uuid version

thorn forum
#

ok so a empty project with

[dependencies]
uuid = "1.16.0"
bevy_replicon = "0.31.0"

works, but not with

[dependencies]
uuid = "1.16.0"
bevy_replicon = "0.32.0"
#

both depends on the same version of bevy 🀷

thorn forum
#

uuid is not anywhere in the dep tree of ordered-multimap. but maybe its indirectly changing a shared crate version, which use uuid...

#

adding ordered-multimap to my test package crate didn't change anything

#

its not important anyway, just weird would have liked to know the problem lol

spring raptor
#

@thorn forum I checked locally. Looks like in both cases Bevy requires uuid 1.12.1.
But Replicon 0.32.0 uses Rust 2024. Maybe something changed about it.

thorn forum
#

ohh its probably then edition yes

spring raptor
#

In any case it's better to use the same version as Bevy. To avoid duplicating the deps in tree

spring raptor
#

And it doesn't work with 1.16.0 on 0.32. Probably because of 2024 edition.

thorn forum
#

ohhh bevy 0.15.1 works but 0.15.2

#

so yes its bevy then

#

bevy 0.15.1 allows using uuid 16, while 0.16.2 doesn't

spring raptor
#

Yeah, we very minimal on dependencies, almost everything comes from Bevy :)

thorn forum
#

i guess bevy switched edition too in this version

spring raptor
#

Not sure. I think they might changed uuid, not the edition.

#

Replicon requires 0.15.3 as minimum anyway.

thorn forum
#
❯ c tree -i uuid --depth 1
uuid v1.16.0
β”œβ”€β”€ a v0.1.0 (/home/mirsella/dev/a)
β”œβ”€β”€ bevy_animation v0.15.1
β”œβ”€β”€ bevy_asset v0.15.1
β”œβ”€β”€ bevy_core v0.15.1
β”œβ”€β”€ bevy_picking v0.15.1
β”œβ”€β”€ bevy_reflect v0.15.1
β”œβ”€β”€ bevy_scene v0.15.1
β”œβ”€β”€ gilrs v0.11.0
└── gilrs-core v0.6.3

uuid v1.16.0
└── bevy_reflect_derive v0.15.1 (proc-macro)
spring raptor
#

Due to some public methods that were exposed in 0.15.3.

thorn forum
#

yep bevy 0.15.2 had this change to fix something

#

isn't that considered a breaking change πŸ€”

#

since it breaks compilation on everyone using uuid recently

spring raptor
#

I think it was a breaking change, yeah πŸ€”

ocean locust
#

Probably a naive question given I load the same set of assets on a server and a client (same relative paths ) can I just replicate the sprite component and it would work or will it just plainly replicate the asset handles and they won’t work because they obviously don’t exist on the client since they are handles generated by the server. Alternatively my best bet is to create a separate component that takes an asset handle and converts it to the asset path (or any other id that I can create the mapping from) replicate that and transform it back to the handle on the client?πŸ€”

dire aurora
#

I don't think you can replicate anything with Handle, in it. But you could make a component that stores the asset path and replicate that

#

As a way to reduce bandwidth usage you could also keep a hash of every asset path and network the hash instead, then pick the path from that list

ocean locust
#

Thought so, hashing is a good idea to limit the data transferred I’ll try that

empty musk
#

Hi i just upgraded to a newer version, i used to do the following to get the local client id

) {
    let client_id = client.id();```

what is the equivalent on the newest version? I used it in the context for adding a camera if it was the local entity:

```        if client_id.map_or(player.0 == SERVER, |id| id == player.0) {
            commands.entity(entity).insert(NewCameraTarget);
            commands.entity(entity).insert(LocalPlayer);
        }```

but this is probably not the correct way to do it, in the example i found that the server now sends a trigger to mark something as the localplayer, is that the idiomatic way?
spring raptor
dire aurora
#

Or Assets as Entities + Indices πŸ€”

spring raptor
empty musk
#

Thank you Shatur

#

Would you discourage the usage of networkId?

spring raptor
carmine sand
#

question: if 1) components are synced only when changed and 2) mutations are sent over an unreliable channel, then does that imply that if a component changes infrequently, there's a chance that packet loss will cause the client to have outdated data for quite a while? IE

  1. server sets a component Player { dead: bool } to player.dead = true
  2. value gets dropped because it was sent over unreliable as a component mutation
  3. player thinks they're still alive because they never got the change. Server does not send a follow-up packet because Player component hasn't changed again
dire aurora
#

It uses unreliable channel to make sure only relevant changes (the latest state) ever gets sent

#

But there is still a mechanism to resend unacked changes

carmine sand
#

ah okay awesome, that works well for me, thanks πŸ‘

empty musk
#
    mut commands: Commands
){
    println!("Sending trigger");
    commands.client_trigger(RequestLocal);
}

fn init_client(
    trigger: Trigger<FromClient<RequestLocal>>,
    mut commands: Commands
){
    //Not receiving client trigger here
    println!("init: {}", trigger.entity());
    commands.server_trigger_targets(
        ToClients {
            mode: SendMode::Direct(trigger.entity()),
            event: MakeLocal,
        },
        trigger.entity(),
    );
}```

i am having an issue where the client_trigger is not received on the server side, I set it up in the same manner as the tic-tac-toe example of bevy-replicon-renet:

```.add_client_trigger::<RequestLocal>(Channel::Ordered)
        .add_server_trigger::<MakeLocal>(Channel::Ordered)
        .add_observer(client_connected)    .add_observer(client_disconnected)        .add_observer(make_local)
.add_observer(init_client)
.add_systems(Update, request_local.run_if(client_just_connected));```

On the client it just prints "sending trigger" but nothing is received or happening after, what am I missing?
spring raptor
#

@empty musk by any chance you forgot to mark message as independent?

empty musk
spring raptor
empty musk
spring raptor
empty musk
#

Maybe i can figure it out with the bug trace, if not I'll be asking you more questions again πŸ™‚

empty musk
#

I found the issue, for some reason when I trigger on the client:

```2025-04-11T13:30:01.438683Z DEBUG bevy_replicon::shared::event::client_event: sending event bevy_replicon::shared::event::trigger::RemoteTrigger<dice_venture::plugins::network::RequestLocal>


On the server it is applied to the PLACEHOLDER entity, instead of the client entity.

2025-04-11T13:30:01.456884Z DEBUG bevy_replicon::shared::event::client_event: applying event bevy_replicon::shared::event::trigger::RemoteTrigger<dice_venture::plugins::network::RequestLocal> from client 34v1
2025-04-11T13:30:01.457094Z DEBUG bevy_replicon::shared::event::client_trigger: triggering bevy_replicon::shared::event::client_event::FromClient<dice_venture::plugins::network::RequestLocal> from 34v1
init: PLACEHOLDER
2025-04-11T13:30:01.457826Z DEBUG bevy_replicon::shared::event::server_event: sending event bevy_replicon::shared::event::trigger::RemoteTrigger<dice_venture::plugins::network::MakeLocal> with Direct(PLACEHOLDER)


the trigger.entity() should be 34v1 here right? instead of PLACEHOLDER.
spring raptor
#

trigger.client_entity will be 34v1.

empty musk
#

okay thanks, so I updated it to:

        ToClients {
            mode: SendMode::Direct(trigger.client_entity),
            event: MakeLocal,
        },
        trigger.client_entity,
    );```

but now for :

```fn make_local(
    trigger: Trigger<MakeLocal>, 
    mut commands: Commands,
    mut state: ResMut<NextState<GameState>>
) {
    commands.entity(trigger.entity()).insert(LocalPlayer);```

i receive the following:

```2025-04-11T14:15:05.028707Z ERROR bevy_replicon::shared::event::server_event: unable to map entities `[34v1#4294967330]` from the server, make sure that the event references entities visible to the client
2025-04-11T14:15:05.028932Z ERROR bevy_replicon::shared::event::server_event: ignoring event `bevy_replicon::shared::event::trigger::RemoteTrigger<dice_venture::plugins::network::MakeLocal>` that failed to deserialize: Serde Deserialization Error```

but as I am running it if client_just_connected i expected the entity to be available on the client
spring raptor
#

List of clients on server represented by entities with ConnectedClient component.

empty musk
#

When a player joins my game i give them a boat:

    trigger: Trigger<OnAdd, ConnectedClient>, 
    mut commands: Commands
) {
    info!("{:?} connected", trigger.entity());

    commands.spawn((
        Ship,
        OwnedBy(trigger.entity())
    ));
}```

so by the MakeLocal functions i wanted to insert local player so i can query:

```local_player: Query<Entity, With<LocalPlayer>>```

which i can then use to set that the client is the local owner of that ship and the camera targets it:

if local_player.get_single().map_or(owner.0 == SERVER, |id| id == owner.0) {
commands.entity(entity).insert(NewCameraTarget);
commands.entity(entity).insert(LocalOwnership);
}```

before updating i did this all with client_ids inside of the Ship components

#

so in short im trying to let the client know which ship belongs to them

spring raptor
#

Targets are entities client should be able to see.
SendMode::Direct contains ID of the client, which is also an entity.

empty musk
#

Thanks for all the help! I think i was confused with client_entity being something that the client knows about

#

I will try to get this to work

spring raptor
#
fn client_connected(
    trigger: Trigger<OnAdd, ConnectedClient>, 
    mut commands: Commands
) {
    info!("{:?} connected", trigger.entity());

    let boat_id = commands.spawn((
        Ship,
        OwnedBy(trigger.entity())
    )).id();

    commands.server_trigger_targets(
        ToClients {
            mode: SendMode::Direct(trigger.client_entity),
            event: MakeLocal,
        },
        boat_entity,
    );
}
#

Something like this ^

#

Let me know if you have any questions. And if you find the documentation unclear, please consider asking here and submitting a PR to improve unclear parts πŸ˜…

empty musk
#

Yea sorry for taking a ton of your time! when I get a good grasp I will try to write down all the things that confused me and how they were fixed

spring raptor
#

No problem!

carmine sand
#

does VisibilityPolicy::Blacklist have a performance impact even if not used? I'm making a wrapper and trying to decide whether to expose this option for people to configure, but right now it seems like it's easier to set it to Blacklist instead of All, and users who don't care about visibility just don't need to configure the visibility policy

spring raptor
spring raptor
#

Updated the crate to RC 4.

halcyon meteor
#

axum

lost thorn
#

There is also ClientEventAppExt::add_client_event_with to register an event with special serialization and deserialization functions. This could be used for sending events that contain Box<dyn PartialReflect>, which require access to the AppTypeRegistry resource. Don’t forget to validate the contents of every Box<dyn PartialReflect> from a client, it could be anything!

Is there any example code of doing this? (Box<dyn PartialReflect>) none of the big AIs are helping me out here

spring raptor
untold hull
#

Following off a guide for Quinnet + Replicon, but I think I've botched something replicon specific; on one of my function calls I get no response from the server, and a

ERROR bevy_quinnet::client::connection: try_send_payload_on: Channel with id `2` is unknown

On client log, through hella scrolling in here I found someone with a similar issue who resolved it by registering his client_trigger before RepliconPlugins? But in trying to do the same Bevy gives me a lil slap on the wrist;

Requested resource bevy_replicon::shared::backend::replicon_channels::RepliconChannels does not exist in the `World`.

if i am being πŸ€• mbmb

spring raptor
untold hull
#

I figured as much, went to sleep after scratching my head for a couple hours-- it was just incorrectly using channels_config in the clients .open_connection πŸ€•
Ty for the help tho broski

spring raptor
#

Updated the crate to RC 5

spring raptor
#

@echo lion I've marked the 0.16 PR as ready for review. I've explained every change in the description. Most changes are includes and error type updates.
I'll merge this branch once Bevy releases.

For networking relations, I'll work on a separate branch based on 0.16. If Bevy hasn't released by the time I finish, I'll open a PR against that branch.

lost thorn
#

is it just my ignorance or is it impossible to clone a struct with Box<dyn PartialReflect> fields? I can wrap them in my own custom trait that implements PartialReflect + Clone via a custom clone_box method. However if I do this I cannot downcast to this trait during deserialization. I'm stuck with Box<dyn PartialReflect> only, which I cannot clone, and cannot implement Clone for, since it is defined outside my crate. Any way around this?

spring raptor
lost thorn
#

I'm still having trouble getting this to work.
I have this struct

pub struct ClientSendCommands {
    pub issued_tick: u32,
    pub commands: Vec<Box<dyn PartialReflect>>,
}

impl Clone for ClientSendCommands {
    fn clone(&self) -> Self {
        Self {
            issued_tick: self.issued_tick.clone(),
            commands: self.commands.iter().map(|x| x.clone_value()).collect(),
        }
    }
}```
which I'm trying to test out with this concrete type
```#[derive(Reflect, Clone, Debug)]
pub struct MoveCommand(pub Vec3);```

I send commands from a client
```commands.client_trigger(ClientSendCommands {
    commands: vec![
        Box::new(MoveCommand(Vec3::ZERO))
    ],
    issued_tick: **sim_tick,
});``` This gets broadcast to all clients. Then, if I do 
```info!("Command received... {:#?}", cmd.get_represented_type_info());``` I can see the correct type

```2025-04-17T00:06:10.457849Z  INFO example: Command received... Some(
    TupleStruct(
        TupleStructInfo {
            ty: bevy_replicon_lockstep::commands::types::MoveCommand,
            generics: Generics(
                [],
            ),
            fields: [
                UnnamedField {
                    index: 0,
                    type_info: 0x00007ff7e24733f0,
                    ty: glam::Vec3,
                    custom_attributes: {},
                },
            ],
            custom_attributes: {},
        },
    ),
)```

but i'm failing to downcast

```if let Some(move_cmd) = cmd.try_downcast_ref::<MoveCommand>() {
     info!("received move command {}", move_cmd.0);
}```
am i doing this wrong?
spring raptor
#

You need to use FromReflect::from_reflect. Check the reflection example in Bevy repo.

lost thorn
#

got it! thanks

spring raptor
#

@dire aurora I've written a small high-level description for networking relations. Could you check it? If you agree with the API, I'll start working on it πŸ™‚

#

Asking you since it was your request.

dire aurora
#

I think your spawn is missing the actual relationship component

#

Grouping entities into buckets that need to be networked together seems reasonable, especially if this approach allows us to handle nested hierachies unlike the approach where we just iterate over the relationship components and make sure to include those in the same message

spring raptor
#

Okay, I'll start working on it. Need to think how to properly flatten hierarchies πŸ€”

untold hull
#

Was there a component that gets set to only the client on connection that can be used for queries? I thought I'd remembered seeing one, but couldnt find it in the docs

spring raptor
untold hull
#

Ah word, tyvm king#

spring raptor
untold hull
spring raptor
spring raptor
#

I'm ready to drop a new release but want to finish implementing relationship support first β€” it's almost done.
That shouldn't be a problem though, since Rust treats RC versions as semver-compatible, so you can already use Replicon RC with the 0.16 release.

waxen barn
#

How can I get the latest ping time on the client?

waxen barn
#

I see there is RTT in the diagnostics but that's only every second.

#

But I suppose if I need more accurate, easy enough to do

spring raptor
waxen barn
#

yeah, in my implementations, beceause of the way I setup the snapshots and player inputs, I basically got that information for free, so yes manually

#

but not sure if renet has a way to get it

#

I actually kinda like the idea of needing to do this manually, then theres no sneaky rpcs happening im not aware of

waxen barn
#

ohh sick

spring raptor
#

It's directly from messaging backends, I don't calculate anything.

spring raptor
waxen barn
#

This should be good, as long as its the most recent RTT

spring raptor
waxen barn
#

nice

spring raptor
#

But as far as I know @dire aurora calculates it manually for bevy_rewind for better accuracy.

dire aurora
#

I don't actually calculate RTT in bevy_rewind, I calculate it out-of-band on my game's client but in the examples I just take the server's tick and add 5 πŸ₯²

#

But yea calculating it out-of-band can be more accurate when backends are only polled when their systems run

spring raptor
#

I will draft a new release right after merge.

#

Ping@dire aurora since you requested this feature πŸ™‚

dire aurora
#

πŸ‘€

spring raptor
#

Most of the added lines are tests πŸ˜…
You don't have to review, I just want your opinion on the API.

#

But reviews always helpful, of course.

spring raptor
dire aurora
#

If it works as I'd expect then the API looks fine ... I am a bit curious on how the perf impact of this is when there are a ton of related entities though πŸ€”

spring raptor
#

Probably worth writing a benchmark πŸ€”

dire aurora
#

Also how do we handle cases like:
A -> B
C -> B
Does it send A, B, and C together?

spring raptor
#

Overlaps are allowed, even from different relationships types

dire aurora
spring raptor
#

But on the bright sight, it's bulletproof. You can split, reparent, join, etc.

dire aurora
#

Ah, definitely worth looking into that then (and it's one of those cases where you really want benchmarks before you do too)

spring raptor
#

I expect users rarely change hierarchies. And it affects only hierarchies that are marked for replication together.

dire aurora
#

I remember the fun issue in Avian where rebuilding the BVH every tick was faster than updating it because the updating logic is less than ideal πŸ™ƒ

dire aurora
spring raptor
#

Yeah, though I'll measure the actual performance first.
I don't know if it's actually slow - I just expect it to be heavier than the message splitting logic.

spring raptor
#

Renamed replicate_together to sync_related_entities per Koe's suggestion. Feel free to propose alternative names.

#

The branch is merged, but I working on a follow-up with benchmarks

spring raptor
#

@echo lion what about just network_relationship? πŸ€”

echo lion
spring raptor
#

Okay, let's keep the current variant πŸ™‚

#

I made benchmarks. They are surprising.
The rebuild is actually insanely fast. It's ~0.3 Β΅s for my huge hierarchy in benchmark.

#

But spawning replicated related entities are slow, about 2-3 times slower. We run observers to keep our graph in sync and they are slow 😒

#

@dire aurora you were interested ^

#

I remember a PR for observers optimization, but it didn't get into 0.16

dire aurora
#

Spawns are already pretty slow by theirselves after all, with all the archetype moves and whatnot

spring raptor
dire aurora
#

Ah, nice

#

Shouldn't be a big issue unless people keep despawning and spawning massive hierarchies then I guess?

spring raptor
#

Yep!

waxen barn
#

Am I able to modify the channel on which replication is handled? For example, make it transmit reliably. I just want to test something. Running into an issue where some entities (out of 5000) are not being updated all the time and want to rule some things out.

waxen barn
#

ok I believe the issue I ran into was on my end

spring raptor
# waxen barn ok I believe the issue I ran into was on my end

Glad you figured it out!
For future reference though:

You can modify channels during the creation stage. Replicon only provides information about which channels it requires.
Then the integration crate provides an extension trait to create actual channels for the underlying backend. It returns Vecs with channels that you can modify before passing to Renet.
Here are the indices for replication (the enum represents 0 and 1). Set the Mutations channel to reliable.

#

It would be cool to have something like you working on as an stress-test example in the repo πŸ€”
But some stripped-down version, without any third-party crates with minimal rendering to showcase the replication load.

waxen barn
#

so I have 10,000 replicating every 15 frames at 64hz and its working fine :DDDD

#

not sure what was breaking down last time i tested it, must have been on my side

#

300ms ping if you notice a delay

spring raptor
waxen barn
#

I put zero effort into those πŸ˜›

spring raptor
#

What game are you trying to create, or are you just experimenting? πŸ™‚

waxen barn
#

experimenting, but its giving me ideas for a moshpit simulator LOL

#

mostly because I just went to the Metallica concert on the weekend

#

like maybe it requires Spotify or other streaming services

#

and you can just play music and sync with your friends and mess around, as long as they have the same service

#

not sure how else to realistically do it legally

#

Anyway, where I'm at I'm going to try and simplify it for you, its really simple already. I can just get rid of the pathfinding & navmesh part.

#

However it's less of a stress test and more like "one way you can replicate thousands of npcs"

thorn forum
#

hello !
when a Entity is spawned on the server, with Replicated and another replicated MyComponent, on the client a system that run in PreUpdate after ClientSet::Receive should see MyComponent as added ?
i have a weird behavior, on the client looking at the last_changed(), its a lot older than the current tick when the system run, while it should run right after since its in preupdate. and so .is_added() is false.
but with a observer with OnAdd MyComponent and it works. but not the system πŸ€”.
the system run the same frame as the other, and with the right timing: i use Res<FrameCount>, and the println statements are in the right order, the observer run before the system, both in the same frame.
thanks :p

spring raptor
# thorn forum hello ! when a Entity is spawned on the server, with `Replicated` and another re...

Hi!

on the client a system that run in PreUpdate after ClientSet::Receive should see MyComponent as added ?
Yes. However, if it's a mutation (even via insertion), I'd recommend reading the mutation section for details.

on the client looking at the last_changed(), its a lot older than the current tick when the system run
Bevy's Tick increments with each system run, so differences can occur even within the same frame.

.is_added() is false.
This shouldn't happen if you're using PreUpdate after ClientSet::Receive πŸ€” Are you possibly mutating via insertion? If so, please check the linked docs section.

thorn forum
#

thanks ! ill just abandon this weird behavior and do it another way, too complicated for something easily doable with another filter :/

spring raptor
#

I've been looking at the Playdate.
It features Wi-Fi and a C API for game development. Bevy can run on it with no_std support - I even saw someone using bevy_ecs for a game even before 0.16 πŸ€”
I just wish they were less pricey.

waxen barn
#

I per-client control of what gets replicated a thing? I'm looking at doing a custom priority system that would factor in the view frustrum of the client (sent to the server)

dire aurora
#

There is visibility

#

But only per-entity (for now)

waxen barn
#

ok then I think for now I'll just consider every client's camera location to choose which cells to priotize. Problem right now is I'm doing a rolling update across spatial cells and it's basically top to bottom, not ideal

#

well neither situation is ideal I guess

#

ok I know what to do, if there's an impact between units in any spatial cell, replicate enties in that cell

#

since its mostly just a collision problem atm

#

no, it needs to be a hard impact that causes an entire shift in velocity, otherwise it'll be a spiral of death

#

im replicating 10,000 units in a fluid/crowd sim rofl

#

visibility is good though I think, so that's factoring each client's camera? @dire aurora

dire aurora
#

Might want a bit more than just the client's camera, or things will pop into view the moment it is moved (unless it's static I guess?)

waxen barn
#

I know this would kinda defeat the purpose of using replicon but I may just do RPCs to individual clients for updates to units.

The initial setup and other things can use replicon's replication. It's really just position and velocity that need to be sync'd better

dire aurora
#

Ah yea, that makes things even more difficult, now we'd need update rules and some sort of update rule visibility thing

#

Probably still within the realm of features replicon should eventually have, this is a very common pattern for optimizations after all. But I doubt we're getting that in the next few updates

spring raptor
dire aurora
#

I think that's generally the way to go with replicon, don't bother writing the optimizations and just focus on the game. You can always help with the optimizations at a first-party level if they aren't done by the time you need them

#

Hooking some sort of visibility into this might make it a bit more tricky however

#

Worst case if we have this + component visibility we could just make a workaround though πŸ€”

waxen barn
#

So the way I'm doing this is pretty simple for the most part. I have a replicated component that I do my own dirty tracking with, and so I only set it when it needs to be set, and thats how I handle custom priority.

Noted about the Replication Conditions, shall take a look

spring raptor
#

If you want to replicate something only once, set its condition to always return false. This way, only the initial value will be replicated.
If you want something to replicate with an additional delay, they condition will track track time internally and return true on timer.
Something like this.

waxen barn
#

Speaking of manual sending via RPCs/events, does Replicon allow that per-client from the server or does it always broadcast to all clients? Asking because I will have other things like chat and stuff eventually.

spring raptor
waxen barn
#

ah nice

dire aurora
spring raptor
# waxen barn ah nice

For details see this section. Sending from server to client is a bit different exactly because you need to provide this kind of info (broadcast or send to specific client).

waxen barn
#

Thank you, appreciate the links and solid docs man. Just gotta get used to navigating them

spring raptor
spring raptor
#

@waxen barn would appretiate your thoughts as well. You will be able to apply your existing prioritization logic to it. It should be faster then sending data manually through events because Replicon collects and packs changes very efficiently.

untold hull
#

How would you homies go about making basic, client side interpolation for a 2d game? Are there any good practices I oughta know?

I've got something rogue-likey with tiles and directional movement, but isn't turn based; Which makes using a replicated component handle the transforms feel a lil delayed on higher ping. Luckily this might be the only thing I'd need interpolation for, but I haven't found good examples online

thorn forum
#

hey :)
my insertion order on the client is different than on the server (still on replicon 0.32).
i have this observer to only replicate one time some component, while waiting for replication condition :D

pub fn on_add_initial_components(
    trigger: Trigger<OnAdd, InitialComponents>,
    mut commands: Commands,
    q_cards: Query<(&InnerCard, &InitialComponents)>,
) {
    let entity = trigger.entity();
    let Ok((card, initial_components)) = q_cards.get(entity) else {
        return warn!("couldn't find a card for new InitialComponents");
    };
    trace!("added Card {} {}, inserting self", card.as_ref(), entity);
    commands.entity(entity).queue(initial_components.0.clone());
}

on the server InnerCard and InitialComponent are both inserted in the same system same frame, just with two entity_commands.insert() different, so two different command in the queue. but the InnerCard insert before the InitialComponent, always.
on the server where its spawned its normal:

226v1 InnerCard added
226v1 InitialComponents added
<the observer above runs fine>

but on the client:

1024v1 InitialComponents added
2025-05-01T08:00:18.426667Z  WARN game_shared::card::components::systems: couldn't find a card for new InitialComponents
1024v1 InnerCard added

πŸ€”

dire aurora
#

It almost sounds more like a Static component that would be useful for rendering or physics rather than a way to do replication conditions

waxen barn
#

Is there a mechanism already for replicating a component once per client?

So for example on the server I can change the component as much as I want and it wont send to existing clients after the first time, then another client joins, and they receive the latest version of that component.

spring raptor
thorn forum
spring raptor
spring raptor
thorn forum
#

is it possible to insert all of these component in the same batch?

#[derive(Clone, Serialize, Deserialize, Reflect, Debug, derive_more::Deref, PartialEq)]
pub struct Card {
    #[deref]
    pub inner: InnerCard,
    pub components: CardComponents,
    pub troop_components: Option<TroopComponents>,
    pub level: Level,
}
impl Card {
    pub fn insert_self(&self, entity_commands: &mut EntityCommands) {
        self.inner.insert_self(entity_commands, &self.components);
        entity_commands.insert_if_new(self.level);
        entity_commands.queue(self.components.clone());
    }
}

using EntityCommand ?

spring raptor
dire aurora
#

But realistically you'd want those to be per-component (or both) to behave as you'd expect πŸ€”

#

And it creates all sorts of ack problems ofc

spring raptor
thorn forum
dire aurora
spring raptor
dire aurora
#

And there's also that issue of knowing on the client side that something got omitted rather than being unchanged

spring raptor
spring raptor
spring raptor
# thorn forum if i understand correctly, the problem is that here im adding my components usin...

No, no, what you are doing is not the issue.
When we collect components for replication, all components are already inserted. We simply iterate over them in archetype order, and clients insert them in the same order we collected.
The actual problem is that none of these insertions are batched. We'll need either command batching implemented in Bevy itself or modifications to Replicon to handle batched component insertion. I currently think that we need to fix Replicon and don't wait for the upstream changes. But I haven't had time to look into it yet.
As a workaround. If inside your insertion trigger you do another trigger, it should be called after all component insertions. Hacky, but it should work.

thorn forum
#

oh ok didn't understand enough to see the problem with no command batching, thanks a lot :)
I'll try to find a "cleaner" way without having two observer a another trigger

spring raptor
spring raptor
thorn forum
#

hey ! for your 3 variant enum idea, what about adding a fourth variant just for creating ? would fill the purpose of replicate once

spring raptor
spring raptor
dire aurora
#

Having something specific to replicate once could definitely work. Definitely the most frequently requested feature ... We'd probably also want something for occasional updates (rather than always or only once) though ... But maybe we could keep those fairly simple to limit how complex the ack-related behavior is

#

If we only have the rules "always", "once" and "every X ticks" we might be able to handle the acks. But arbitrary functions makes it very hard if not impossible to still have eventual consistency πŸ€”

spring raptor
# dire aurora Having something specific to replicate once could definitely work. Definitely th...

Yes, we definitely need something for occasional updates. The main reason I want to separate "replicate once" is because it's not per-client, non-toggleable, and component-related. I can optimize it better this way.

As for "every X ticks" - I was thinking about merging it with visibility settings. Right now visibility is true or false, but it will become something like "always", "only insertions/removals", and "hidden". It's very powerful. You could build "every X ticks" or time-based prioritization like in Unreal Engine. Do you think acks will be hard to handle this way? πŸ€”

dire aurora
spring raptor
# dire aurora If we just toggle things, then we might know the last tick was acked, but not if...

Makes sense.
Currently, I track the last acknowledged tick per-entity. With the current per-entity visibility system, extending this to mutations is straightforward - no additional tracking needed.

However, per-component visibility will definitely complicate things.
But you previously suggested a brilliant idea - make it per-mask rather than per-component. So it won't be that heavy. And since mutations and updates visibility will be unified, mask could work for both. What do you think about this?

dire aurora
# spring raptor Makes sense. Currently, I track the last acknowledged tick per-entity. With th...

"per-mask" as in "per unique combination of 'component layers'" as we discussed before?
I think that could work for just the always vs once case, but toggling it like visibility sounds relatively niche and very impractical. You'd really want the once vs "every x ticks" vs always behavior to be per-component, with some exceptions for things like static entities or moving platforms (though those exceptions could be modeled as higher priority replication groups I think?)

spring raptor
#

Yes!

When you say "once vs every x ticks vs always" do you mean that they should be handled in a unified way?
It would be great, just not entirely sure about the API. Here is my chain of thoughts. I split it into sections to refer to them.

Replicate once

I would expect an API like this:

app.replicate_group_once::<(MovingPlatform, Transform)>();

app.replicate_once::<AnotherDeterministicComponent>();

And then you just spawn your entities as usual. Once replicated, client deterministically simulate them.
Zero to minimal overhead. The parameter will be placed on the rule, so you can also configure it using *_with functions.

Static every X ticks

We could make it like this:

app.replicate_with_delay::<SomeComponent>(Duration::from_secs(1));

Where should I store the timer? Fastest approach would be on the archetype. Won't be accurate for newly joined clients,
but it's the fastest way. Another option is to store it on the client in a component to make it entity per-entity and per-client,
but it's more expensive. Not sure if it's justified.

Dynamic every X ticks

However, this will work for cases where you want deterministic replication with occasional sync-up.
You won't be able to reduce traffic for non-relevant entities for clients. Properties on rules are static and global for all clients.

If we want to support this, we could make it work similar to visibility: each client could have a special component. You could query
it and configure the delay for specific entity and component. But, when I collect replication data, I don't care about the timer. I
only need to know whether I should send mutation for specific component or not. This is why I proposed to provide a simple toggle
and rely on users to toggle this on timer or some other properties. Just to make it flexible. People could build complex abstractions on top of it
like with our current visibility system.

#

Unreal engine

It's always good to look at prior art. I usually look at Unreal Engine since they have built-in networking and I'm familiar with it.
Here is a short article that sums it up: https://www.mattgibson.dev/blog/unreal-replication-settings
They have NetUpdateFrequency which is a global property per-actor. In ECS doing per-entity would be quite limiting.
So I would go with per replication rule (i.e. "static every x ticks").

On top of it there is also relevancy which is basically a visibility system, but a bit more high level.
And there is also prioritization. I think Lightyear supports it too. It allows to include most relevant mutations first.
Then when the connection limit is reached, drop less relevant data. This is what could be used instead of manually
slowing the update for distant entities.

Conclusion

I currently more inclined to "replicate once" with "static replicate every X ticks" and do Unreal-style prioritization as a follow-up. But I would like to hear other opinions!

dire aurora
#

Static every X ticks doesn't need "a timer", just make it tied directly to ticks, so if it's once every 20 ticks you can just do RepliconTick % 20 == 0, etc. This way rollback/interpolation/etc can also figure out the difference between "no change" and "didn't get sent" easily

#

If we have once, static every X ticks, and always. Then priority should be able to take care of the rest

dire aurora
spring raptor
dire aurora
#

Hmmm ... Probably just create some offset by throwing the server entity into a prng algo? πŸ€”

spring raptor
dire aurora
#

We could even just do (RepliconTick + entity.index()) % interval

#

Gotta cast each to a u64 first though

#

But there are still some issues with that approach

#

We'd ideally send it per subgraph of related entities πŸ€”

#

We could probably start by just implementing this without the staggering though, the updates bunching up isn't an issue until we add priority

spring raptor
#

Agreed!
Thanks for all the suggestions!

torpid adder
#

Hi there! I'm upgrading my project to use 0.33, but running into the following:

I have an Inventory component like this:

#[derive(Component, Serialize, Deserialize)]
pub struct Inventory {
  slots: Vec<Option<ItemAmount>>,
}

// entity points to an entity that defines the game object metadata
#[derive(Serialize, Deserialize)]
pub struct ItemAmount(pub Entity, pub usize);

To replicate it, I used to use replicate_mapped and had an implementation of MapEntities for Inventory.

For the MapEntities implementation in Bevy 0.16, I had to replace map_entity with get_mapped. (Is that right? Didn't see this in the Bevy migration guide.)

To restore the previous behaviour, I now used replicate_with with these RuleFns (grabbed/migrated the old RuleFns used in replicate_mapped from 0.32):

pub fn rule_fns_default_mapped<C: Component + Serialize + MapEntities + DeserializeOwned>() -> RuleFns<C> {
  return RuleFns::new(default_serialize, default_deserialize_mapped);
}

/// Default component serialization function.
pub fn default_serialize<C: Component + Serialize>(
  _ctx: &SerializeCtx,
  component: &C,
  message: &mut Vec<u8>,
) -> Result<()> {
  postcard_utils::to_extend_mut(component, message)?;
  Ok(())
}

/// Like [`default_deserialize`], but also maps entities before insertion.
pub fn default_deserialize_mapped<C: Component + DeserializeOwned + MapEntities>(
  ctx: &mut WriteCtx,
  message: &mut Bytes,
) -> Result<C> {
  let mut component: C = postcard_utils::from_buf(message)?;
  component.map_entities(ctx);
  Ok(component)
}

I now replicate Inventory with:

app.replicate_with::<Inventory>(rule_fns_default_mapped());

This works! But this feels overly cumbersome. Am I missing a more ergonomic way to do this?

dire aurora
#

The more ergonomic way would involve using the mapping capabilities of Component

#

Iirc you should be able to just tag fields with a #[entities] when using the Component derive

torpid adder
dire aurora
#

The implementation for MapEntities and Component aren't the same thing. Component's map_entities can be called regardless of a Component requiring mapping, which means mapping can be applied to every component. I think this is what replicon does now

torpid adder
dire aurora
#

Shouldn't Vec<T> implement MapEntities if T implements MapEntities? I think the same goes for Option, thus ItemAmount would just need to impl MapEntities, right? πŸ€”

#

There might be some missing impls in bevy though, this stuff changed very late into the RC πŸ˜…

torpid adder
spring raptor
#

I'm working on an API for registering replication conditions.
I thinking about something like this:

    // Single component.
    fn replicate<C>();
    fn replicate_once<C>();
    fn replicate_periodic<C>(); 
    fn replicate_with<C>(rule_fns: RuleFns<C>, condition: ReplicationCondition);

    // Same thing for groups.
    // But unlike before, the trait will returns only `Vec<(ComponentId, FnsId)>`.
    // Otherwise overriding a condition defined in the trait by `_once` or `_periodic` would be 
    fn replicate_group<C>();
    fn replicate_group_once<C>();
    fn replicate_group_periodic<C>();
    fn replicate_group_with<C>(condition: ReplicationCondition, priority: usize);

But this would break bevy_bundlication. @dire aurora you will have 2 options:

  • Remove priority from bundlication definitions.
  • Create an extension trait that calls replicate_group_with for your trait. Something like replicate_bundle.
    What do you think?

I also attempted unifying replicate* and replicate_group* into a single call, but Rust's coherence rules prevent this 😒

Maybe you have some other ideas?

dire aurora
#

The conditions on groups feel odd

#

I'd expect the ability to set conditions per field, rather than per group

#

Or alternatively we'd need filters ASAP

#

Being able to specify say .replicate_group::<(Transform, Rotation), With<Static>>(ReplicationCondition::Once, 100) could potentially work at least

#

-# It does look very ugly though

spring raptor
spring raptor
#

What if instead of replicate_group_with we do something like replicate_custom that would work like before? And all replicate_group_ would assign the same conditions to all members. This should keep the ergonomics, while still allowing full customization.

spring raptor
dire aurora
torpid adder
# dire aurora There might be some missing impls in bevy though, this stuff changed very late i...

I made a PR that adds some missing impls to allow for these arbitrary combinations. Are you interested in providing feedback? https://github.com/bevyengine/bevy/pull/19071

GitHub

Objective
With the current MapEntities impls, it is not possible to derive things like this:
#[derive(Component)]
pub struct Inventory {
#[entities]
slots: Vec<Option<Entity&g...

spring raptor
dire aurora
#

Worth asking, though I'm not sure if this is considered breaking ... Can you impl ExternalTrait on Vec<MyType> at all? πŸ€”

spring raptor
dire aurora
#

Do you have the rights to add it to the milestone or should I do it?

spring raptor
#

I would add, but we don't have a milestone for 0.16.1 πŸ˜…

dire aurora
#

We do though πŸ€”

spring raptor
#

Ah, sorry - I overlooked that! Just added the PR to 0.16.1.

spring raptor
#

@dire aurora what do you think about syntax like this?

app.replicate_group_with((
  RuleFns::<ComponentA>::default(), // Pass functions directly
  RuleFns::::new(custom_serialize, custom_deserialize), // Custom functions
  (RuleFns::<ComponentB>::default(), ReplicationCondition::Once), // Optionally specify condition
  // .. Up to 15 elements
));

I only not sure how to specify priority πŸ€”

dire aurora
#

Priority is still per group right? So it could just be a second argument on another method, but by default use the tuple length

spring raptor
#

Thinking about separate method, otherwise won't format nicely

dire aurora
#

Yea, you also don't want to always specify priority, tuple length is a good default

#

Maybe once we get filters tuple + filter length, or maybe weigh filters more strongly

spring raptor
#

You wanted to replace bundles with some other pattern, maybe this will work?
With this approach I have 2 traits:

  1. The just mentioned tuples; replicate_group_with accepts them by value.
  2. For tuple of components. It's just like before, but accepts a condition that will be applied to each component. Used by replicate_group, replicate_group_once and replicate_group_periodic. These function called without arguments like before replicate::<(A, B)>().
    For bundlication I would need to provide a 3rd trait without global condition like in 2 and without passing by value in 1.
#

Another option is to drop the trait 2.

#

Or keep the only option 1 πŸ€” It's the most flexible and not very boilerplaity.

#

I currently more inclined to keep only the option 1. But I will keep the old trait-based approach if you want.

#

I mean the trait 1. It will be 2 methods, with and without priority.

And 3 method for the old trait.

#

Also ReplicationCondition -> just Condition.

spring raptor
#

Damn, Condition is in Bevy's prelude.

spring raptor
dire aurora
#

In many ways it's not even really a condition and more a frequency

#

You control "How often do we send this", rather than actually specifying a condition under which to replicate it

dire aurora
#

Might even be a SendRate or something like that πŸ€”

spring raptor
#

How about Freq?

dire aurora
#

Could work too yea

spring raptor
#

I like yours more πŸ™‚

The implementation and tests are ready, just need to finish docs.

dire aurora
#

Including the painful cases like "We only send every 5th tick, and tick 1-4 got acked but never 0"?

spring raptor
dire aurora
#

Yea not gonna have an answer for a while yet. Getting SDF collisions rebuilt is taking quite a while πŸ₯²

spring raptor
dire aurora
#

The crate, need to rework it to function nicely with all the changes I made to the crate it's built on, as well as avian

#

Old crate was for avian 0.1 iirc

spring raptor
#

@dire aurora The only breaking change for you is the trait and method rename.
I also realized this trait is needed to conveniently define rules from tuples. So it's here to stay anyway - just like in Bevy.

#

@thorn forum you were interested in this feature ^
You can try it already via [patch.crates-io] (to make backends also depend on this version)

#

@waxen barn I remember you asked about it too. But you will also need the mentioned prioritization for your game.
Planning to implement it soon.

thorn forum
#

thanks for the ping thats thoughtful. for my case the sendrate once and periodic is all i need ! so perfect

spring raptor
#

I'm working on batch insertions/removals.
@dire aurora, I have a question about your code.

In this section, you insert the component ID and skip processing if the size is zero. However, when applying, you iterate over offsets that doesn't include zero-sized components. This creates a mismatch between the ids vector size and the pointers iterator length.

I don't think you need to roll back zero-sized components, but why check the size then? πŸ€”

GitHub

Server-authoritative rollback networking for bevy. Contribute to NiseVoid/bevy_rewind development by creating an account on GitHub.

dire aurora
#

Uh ... That could be a bug that's just never hit. Iirc the early out was originally just to avoid hitting some math that otherwise could've gone wrong

spring raptor
#

Got it! Maybe worth replacing the early-out with an assert.

#

*debug assert

#

I would check the size with regular assert on the registration time

dire aurora
#

Yea, or adding a push to offsets in the if. It definitely shouldn't be generating things that are broken

spring raptor
spring raptor
thorn forum
#

wow cool :D thanks. personally, this and the replicate condition will simplify a lot my code, and make using replicon really straightforward :)

spring raptor
#

Migrated client and server statuses to Bevy's state system.
This allows users to utilize things like StateScoped and run systems more efficiently using OnEnter or OnExit without evaluating conditions every frame.
Moreover, we no longer need custom conditions.
Feedback is highly appreciated.
https://github.com/projectharmonia/bevy_replicon/pull/487

GitHub

This allows users to utilize things like StateScoped and run systems more efficiently using OnEnter or OnExit without evaluating conditions every frame.
As a result, we now require StatesPlugin to ...

spring raptor
#

I'm working on an authorization system.
For this, I need a way to request disconnection from Replicon's side, so I added a special event that requests disconnection from the backend: https://github.com/projectharmonia/bevy_replicon/pull/491

I briefly looked at all current backends, and it looks very easy to handle. However, opinions from backend developers would be appreciated, so pinging @raw idol @echo lion @tacit osprey πŸ™‚

GitHub

Allows sending a message to a client and disconnecting afterward, ensuring the client receives the message before the connection closes.
This can be used to send a proper disconnect reason, for exa...

raw idol
spring raptor
tacit osprey
#

(apart from the delivery guarantee mentioned by Boris here and Koe on GitHub, it seems fine to me πŸ™‚ )

spring raptor
#

Thank you all for the feedback!

@echo lion I've closed the states PR and rebased all other PRs - they're now ready for review.

spring raptor
thorn forum
#

hey ! migrating to bevy 0.16, i tried to replicate ChildOf but it doesn't implement DeserializeOwned noir Serialize. ive enabled the bevy feature serialize but it seems ChildOfjust doesn't have any serialize derive in bevy source code.
its to replace the ParentSync plugin, like said in the changelog
thanks !

#

couldn't find any info about that here in discord weird πŸ€”

#

im probably missing something

spring raptor
thorn forum
thorn forum
#

MappedEntity is not implemented for ChildOf, it should be too on replicon side ? ill do it directly on Entity for now

dire aurora
#

Doesn't the Component impl of map_entities just work?

spring raptor
spring raptor
thorn forum
#

oh cool there's a builtin MapEntities

#

used Component impl then, thanks !
~~```rust
fn deserialize_childof(ctx: &mut WriteCtx, message: &mut Bytes) -> Result<ChildOf> {
let mut child_of = ChildOf(postcard_utils::from_buf(message)?);
Component::map_entities(&mut child_of, ctx);
Ok(child_of)
}

oh no nevermind since the entity inside of ChildOf is not yet annotated with #[entities]
spring raptor
thorn forum
thorn forum
#

hey, does entity mapping work when the entity is not directly on the component ?
i have this component:

// derive Component, MapEntities
pub enum TroopState {
    #[default]
    Idle,
    #[entities]
    Run(Option<Target>),
    #[entities]
    Attack(Target),
    #[entities]
    AttackCooldown(Target),
    Death,
}
// derive MapEntities
pub struct Target {
    #[entities]
    pub entity: Entity,
    pub translation: Vec2,
}

but the target.entity doesn't get mapped :(
i tried #[reflect(Component, MapEntities)]
using a custom deserialization to be sure:

fn serialize_troopstate(
    _ctx: &SerializeCtx,
    troopstate: &TroopState,
    message: &mut Vec<u8>,
) -> Result<()> {
    postcard_utils::to_extend_mut(troopstate, message)?;
    Ok(())
}

fn deserialize_troopstate(ctx: &mut WriteCtx, message: &mut Bytes) -> Result<TroopState> {
    let mut troopstate: TroopState = postcard_utils::from_buf(message)?;
    troopstate.map_entities(ctx);
    Ok(troopstate)
}

thanks !

thorn forum
#

ohh ok troopstate.map_entities(ctx); doesn't work, but calling map_entities on the underlying target if there's one fix it

#

i can open a bevy issue i guess ?

dire aurora
#

Shouldn't need the MapEntities at all, Components getting sent over the network should automatically get mapped by replicon too I think πŸ€”

thorn forum
#

ill try again
edit: it worked !

#

the problem is still there tho, this is just to not have to derive MapEntities too

#

i dont know if its because its a enum, or just because its a sub struct. i guess the second

dire aurora
#

Does it not compile or does it break at runtime? πŸ€”

thorn forum
#

it compile, just doesn't map it at runtime

#

its a bug i think ?

dire aurora
#

Hmmm, interesting ... I think the #[entities] on the variant with the Option shouldn't even compile on 0.16.0 πŸ€”

thorn forum
dire aurora
#

Yea, but then it shouldn't compile

thorn forum
#

maybe because of the enum variant

#

ill try on a struct

dire aurora
#

Maybe the macro just isn't picking up the fields correctly, maybe the annotation needs to be inside the variant or whatever

#

So it would look like Attack(#[entities] Target), if that's what the macro wants

dire aurora
thorn forum
#

so the issue is that it doesn't error on a enum variant

spring raptor
#

You can test it without replicon. Just create a component and try to map entity a to entity b.

#

It will require creating a mapper, but you can just create a newtype over EntityHashMap.

#

Just to isolate the bug. I suspect that it's on the Bevy side.

thorn forum
#

thanks ! i think we isolated the bug ?
its that #[entities] doesn't work when put on the enum variant, as it should be on the associated type directly:
Attack(#[entities] Target),

#

and so they werent mapped as they werent annotated by #[entities]

spring raptor
#

Ah, got it!
And it doesn't compile with Option because it was fixed only recently.

As a temporary workaround I can suggest to manually map entities inside using custom ser/de functions.

thorn forum
#

yep thats what i did thanks !!

fn serialize_troopstate(
    _ctx: &SerializeCtx,
    troopstate: &TroopState,
    message: &mut Vec<u8>,
) -> Result<()> {
    postcard_utils::to_extend_mut(troopstate, message)?;
    Ok(())
}

fn deserialize_troopstate(ctx: &mut WriteCtx, message: &mut Bytes) -> Result<TroopState> {
    let mut troopstate: TroopState = postcard_utils::from_buf(message)?;
    Component::map_entities(&mut troopstate, ctx);
    if let Some(target) = troopstate.target_mut() {
        MapEntities::map_entities(target, ctx);
    }
    Ok(troopstate)
}
#

should i open a issue for the macro to error when put on a enum variant instead of associated type ?

spring raptor
pure arch
#

i've never used bevy_replicon. messaging backends are separate crates you don't control?

dire aurora
#

Yep

pure arch
#

the solution i'd want for this would be to run StateTransition before PreUpdate and have messaging backends put their logic to determine the next state as "compute" systems in StateTransition (not in the current bevy_state but a hypothetical future bevy_state)

#

basically replace the "computed states" concept with providing a place for users to add their own systems in the StateTransition schedule to determine what the next state should be

#

unless this doesn't make sense semantically because i'm missing something about why messaging backends are changing states (i have no context here)

spring raptor
thorn forum
#

hey ! i think having the same remote trigger registered for both side doesn't work:

.add_client_trigger::<DevUpdateCard>(Channel::Ordered)
.add_server_trigger::<DevUpdateCard>(Channel::Ordered)

im triggering it from the client to server, but the observer on the server is never triggered, while with only the add_client_trigger it works

spring raptor
thorn forum
spring raptor
spring raptor
#

@thorn forum fixed: https://github.com/projectharmonia/bevy_replicon/pull/496

I planning to draft a new release soon, just want to add the authorization feature I almost finished.
In the meanwhile, you can patch with this locally. The only breaking change is the module rename which people unlikely import πŸ™‚

GitHub

We need to use distinct event types internally; otherwise, client triggers will be consumed by this system (which is supposed to trigger events from the server):

  bevy_replicon/src/cl...
thorn forum
#

waw thanks that was fast ! for now im just using two struct instead of one

spring raptor
#

I will draft a new release after it

spring raptor
#

I think this time I will make an RC πŸ€”
Just want to hear feedback from messaging backends.

dire aurora
#

Any ideas for a nice workaround for networking this as part of a component?

#[derive(Component, Debug, Reflect)]
pub enum SdfColliderKind {
    Sphere(Sphere),
    Capsule(Capsule3d),
    Arbitrary(Handle<Sdf3d>),
}
```Long term I'm planning to just change how I network rooms and terrain so I no longer need to send components with handles, but that doesn't really make sense to do in an already huge 0.14 -> 0.16 migration πŸ€”
spring raptor
dire aurora
#

Ah, in this case the handle doesn't point to an asset on disk

spring raptor
#

Maybe store Sdf3d itself? I assume it won't be efficient, but maybe fine as a temporary fix?

dire aurora
#

Hmmmm, yea I guess that's gonna be the best option ... Just take the data out of the handle, put it on another component and network that

spring raptor
#

Let us know how big the diff from 0.14 -> 0.16 looks like πŸ™‚

dire aurora
#

Diff was already sitting at:
91 files changed, 3295 insertions(+), 14361 deletions(-)

dire aurora
#

Yea, cause I moved all those crates out of my repo

spring raptor
#

Just published a release candidate!

We usually don't do RCs, but this time I wanted to make sure disconnects can be properly handled by backends - and maybe get some feedback πŸ™‚

As always, @dire aurora @echo lion @granite hill @tacit osprey @raw idol - pinging plugin authors. I'd appreciate it if you could update your crates :)

For backend authors, here is a branch for bevy_replicon_renet that might be helpful
First commit fixes despawns that weren’t properly handled by the backend. Second commit does the actual upgrade. Most changes are in examples and tests.
You can also take a look at the changes in the example backend - they are similar. All tags in the changelog are clickable, so for example, to see all changes since the last release, just click on the release and then navigate to the "Files changed" tab. Here is the direct link.

Looks like RenetClient has a 1-frame delay for some reason. Might be a bug in renet - doesn't happen with the example backend πŸ€”

For bevy_bundlication, there should just be a trait and method rename.