#bevy_replicon
14621 messages Β· Page 15 of 15 (latest)
We won't be able to reliably obtain information such as packet loss and RTT.
Most backends already calculate it anyway.
yeah, but latency would be an easy one, might be worth integrating no ?
I don't think getting half of the statistic worth it π€
Plus this won't include backend-specific overhead.
Since it would be end to end it would take backend into account no ?
No, backends include additional information when they create packets, such as channel number.
but that wouldn't be overhead no ? overhead means additional latency in that context no ?
Ah, I meant the statistic will be more complete when the backend-related stuff is counted.
Not in a sense that it will reduce performance or something π
I.e. backends add a few more bytes to each message and it's nice to count them, that's why the API is like this.
Only now finally getting around to doing the migration to 0.37 ... Is there anything I need to do to get the same behavior as the old blacklist visibility policy? My player entity never gets marked as Replicated and thus my game gets stuck on connecting π€
Is there anything I need to do to get the same behavior as the old blacklist visibility policy?
All entities are visible by default. If you want to hide something, just create a visibility filter component. Entities/components with this component will be hidden if client entities don't have this component.
My player entity never gets marked as Replicated and thus my game gets stuck on connecting
Could you elaborate on this part?
My game gets trough the usual connecting loop, then once the clocks are synced it spawns a player locally, throws a signature on it, and waits until Replicated is added when the server sends it back
But that last part doesn't seem to happen currently
So it stopped worked for you after the update? π€
What you describes makes sense to me.
Could you enable logging and see what is happening? Maybe a signature missmatch?
2026-01-10T20:20:20.182040Z ERROR bevy_replicon::shared::message::server_message::message_buffer: ignoring direct message for non-authorized client `193v0`, mark it as independent to allow this
```Huh, why does this not tell you the message that was sent? π€
2026-01-10T20:25:40.464945Z DEBUG bevy_replicon::server: disconnecting client `193v0` due to protocol mismatch (client: `ProtocolHash(7823686126691282433)`, server: `ProtocolHash(5274629916445082958)`)
```Ah now this might be related ... But how does the protocol hash mismatch, I didn't change anything 
I mean to begin with all the types are shared between client and server ...
The type is unknown at this point. It's just bytes.
But if we know it's not marked as independent we fetched something from the registry and should have a name, right?
It's easy to debug, just enable logging and filter it by bevy_replicon::shared::protocol=debug. The order and names should match. On both server and client.
Only difference is that the client has this line twice:
enabling mutate message tracking
enabling mutate message tracking
```Does that matter?
Weird, why twice? It affects the hash.
I'm not sure tbh 
Check for track_mutate_messages calls.
Ah, my own code calls it, then collback also calls it, but it's only on the client so it only gets called once
Why does calling it twice affect the hash, was that always the case?
Independent messages aren't buffered, so they sent immediately.
Replication-dependent messages are tied to replication. We serialize them immediately, but keep the space for the update tick which we serialize later.
We didn't count it for the hash at all, but we should've because it affects the protocol.
But I didn't expect it to be called twice π
It's an easy fix, though.
Looks like it works now, so it being called twice is what broke everything π€¦ββοΈ
Yeah, I'll open PR with a fix to ignore the second call.
Should probably fix my game's code so that failing protocolhashes are actually noticable
Your backend should disconnect on missmatch π€
Ah, okay π
@dire aurora maybe we should enable this via feature instead?
Will be easier for third-party crates to enable.
The track mutate thing? Both works and are about as annoying I guess, only worth the feature if there is some meaningful behavior to gate I think
This will allow us to gate a single type and remove a few branches in non-critical places.
But I thought it might be more convenient to enable.
Nah, it wouldn't be much more convenient, enabling the feature from the rollback crate would have the same issue since the server doesn't depend on it
As long as the timing (and number of times) of when track mutate gets called doesn't matter it shouldn't be a problem
Ah, right.
Okay, let me quickly fix the hashing with extra calls.
I know you already fixed it, but I'll draft a patch with the change once merged:
https://github.com/simgine/bevy_replicon/pull/640
Hmmmm, now to implement visibility in the new system
Though I'm not quite sure how do approach this, since there seems to only be components matching against theirselves implemented
But a player can see multiple rooms, while things can only be in one π€
You can attach multiple visibility filter components to a single entity.
You can also have a single filter component with bitmasks
And you can also manually register and toggle the bits.
For example, you can associate the first bit with specific set of components or the whole entity and simply toggle it on the client entity.
But in most cases component-based API is more convenient.
Well, kinda trying to avoid writing my ugly old code 
Hmmm ... I guess I could try to turn rooms into a bitvec thing or smth π€
Yes, if you have multiple rooms, I'd go with a single component with a bitmask inside
Dungeons have many rooms, though atm I might still be able to get away with 1 u64, and later use a bitvec or just multiple u64s
Wait, player can be only in a single room at a time, right?
If so, you can just store the ID of the room. And the visibility check will be !=.
Yes, but they can see the connected rooms
Ah, got it π€
So a player might have say 5 rooms they can see, but each entity is only one ID
Then I'd go with a bitmask, yes.
A u64 or a compile-time sized bitmask if you need more (assuming you have a known number of rooms)
I don't know the number of rooms, but I could have an upper bound in my code
Are they dynamically generated?
Upper bound makes sense. I'd avoid dynamic allocation.
Like currently my dungeon generation works as follows:
- It generates a main path between 8 and 10 rooms long
- It adds a single branching room
- It adds a branch of 1-2 rooms
- It adds two branch of 2-4 rooms
- It adds a branch of 3-6 rooms
Eventually I'll make this scale in some way to offer sizes, different levels of branching/linearity, and add small graph mutations to make them less noticable branching structures
But I could always just generate these numbers based on some known upper bound
So currently there's max 27 rooms
Makes total sense.
It's possible to store a dynamically-sized bitmask, but in your case you can fix the size without compromising much.
Got it to work π₯³
Pretty sure this implementation is way more efficient too
Few minor differences this way that I can't "fix" but they aren't that relevant (The hallways have the mask of both connected rooms, so you see not just the current room, its hallways and the next rooms, but also all their hallways)
The patch is up on crates.io
You could probably use a different component or bits for hallways π€
But it depends on how important they are for the gameplay.
Hey, new here. I'm playing around a bit with bevy_replicon. I feel like I'm missing something. I can't quite figure out how to replicate resources/global state between server and clients. For example if I have a State (playing/paused) on my server, is there no built in way to replicate that to the clients? And if not, is the best practice to then have it as an entity on the server, and sync that to an actual State on the clients? Not sure if my questioning makes sense.. but I tried π
Update: Just saw this one, I guess it kinda ties in with my question: bevy: Store resources as components on singleton entities
Hi!
Yeah, there is currently no built-in way to replicate entities. That's because Bevy plans to turn resources into entities.
For now, you can either manually sync using events or create singleton entities with replicated immutable components and on insertion update corresponding resources.
Ok thanks! Then Iβm at least not doing something horribly wrong
Since migrating my crates and my game aren't possible, guess I'll take the time to see if I can finally remove bevy_bundlication from my game π€
Due to libraries like avain?
Great, let me know how it went.
Yea, avian blocks my crates, other libs blocks would block my game too
Hmmm, running into a bit of a problem with using the replicate_as nicely because a lot of wrapper types has From<T> and Deref<Target = T>, but not Into<T> π€
I could manually impl them on a wrapper but that's rather ugly
Maybe we should have our custom trait for it π€
And my attempt at making one wrapper from From<T> + Deref<Target = T> into From<T> + Into<T> is getting foiled by impls like impl<T: From<Vec3>> From<AsVec3> for T { not being allowed due to orphan rules 
And ofc implementing the reverse Into is blocked because the impls could overlap 
How your wrappers look like? I thought you define them in your crate. Is this some already existing types?
#[derive(Serialize, Deserialize)]
pub struct AsVec3(Vec3);
Hm, I think adding a simple From impl would work, no?
If I manually do them it'll work I guess, kinda ugly to do that for every use of the wrapper though 
I.e.
impl From<Transform> for AsVec3 {
fn from(value: Transform) -> Self {
Self(value.translation}
}
}
But you need to specify how one type translates into another, I don't think it's avoidable.
Or what you had in mind?
Well, in this case, LinearVelocity, Position, etc all already implement From<Vec3>, so it would be nice if that translated to my wrapper
Similarly, for Rotation and other Quat types I already get From<Quat>
This compiles at least:
#[derive(Serialize, Deserialize)]
pub struct AsVec3(Vec3);
impl From<AsVec3> for LinearVelocity {
fn from(value: AsVec3) -> Self {
Self::from(value.0)
}
}
impl From<AsVec3> for Position {
fn from(value: AsVec3) -> Self {
Self::from(value.0)
}
}
impl<T: core::ops::Deref<Target = Vec3>> From<T> for AsVec3 {
fn from(value: T) -> Self {
Self(*value.deref())
}
}
But like, LinearVelocity and Positon both just lean on existing From<Vec3>s now, this seems a bit silly 
Well, arguably still better than making one of those awful replicate_with wrappers again 
I made the future/past tick ones worse btw, they now handle arbitrary integer types instead of only u16
But aren't the mentioned types already hold Vec3? π€
Why replicate_as them?
Hmmm, I could maybe serialize them directly now that you mention it π€
replicate_as is useful when you want to quantize values or replicate specific fields.
And for these cases you can't automate it, you need to write the actual logic.
Replaced one of the most cursed bundles now ...
Before:rust #[derive(NetworkedBundle, Bundle, TypePath, Default)] pub struct MovementDataBundle { /// The movement state pub state: MoveState, /// The tick at which the character was last grounded, automatically updated pub last_ground: LastGround, /// The position of the character #[bundlication(as = Vec3Data)] pub position: Position, /// The rotation of the character #[bundlication(as = QuatXYData)] pub rotation: Rotation, /// The current velocity of the entity #[bundlication(as = Vec3Data)] pub velocity: LinearVelocity, } After:```rust
.replicate_bundle_filtered::<(MoveState, Position, LinearVelocity), With<CharacterController>>()
.replicate_with_filtered::<_, With<CharacterController>>(
AsPastTick::<u8, LastGround>::default(),
)
.replicate_filtered_as::<Rotation, AsQuatXY, With<CharacterController>>()
Great!
AsQuatXY has the same issue as AsVec3, though being able to get rid of any impls of that just because types lack Serialize+Deserialize saves some code at least
I wonder why Rotation doesn't implement serialize
It probably does, but I don't serialize it as a Quat
I only send x/y rotation since the character controller only allows those
Ah, makes sense
In my skill networking code I could clean up a lot of stuff π
Still did some manual mapping for some reason
And I network skills as effectively (u8, u16), but writing the Serialize/Deserialize logic for that had proven to be impossible since it's a pretty weird enum dispatch type
But now I can just impl a From both ways and it works
That did allow one weird edgecase to slip in, but tbh that never got handled better than a panic, so now the server can just send clients skills that it doesn't understand and they become an Invalid skill variant that does nothing 
Why server might send skills that it doesn't understand? π€
In theory the (u8, u16) can be a value that is not a valid skill
This has happened before in some niche cases
Got it. Yes, I think having Invalid is better than panicking.
Hmmm, do we have something like a "send this if this visibility filter fails"? π€
Basically I have the logic that if a skill is from a player, they get all the info on the player, and everyone else just sees the skill exists, and doesn't know how it's configured or whose it is, but they do still get the stats they need to simulate the effects
Unfortunately, no. But you probably can split your skill into multiple components and make some of them affected by the visibility.
Thing is, I can't unless I make a double visibility filter 
The owning client needs the data, the other clients need the pre-calculated stuff
Those are mutually exclusive
Ah, I see. Hm... I need to think even how to express something like this.
If you have ideas - feel free to suggest.
Perhaps an additional scope that specifies what you see when it's invisible, or the ability to make 2 filters using the same actual component
But if it's the same component, you also need to serialize it differently, right?
No no, I mean 1 visibility filter component
By having the ability to specify the type
impl VisibilityFilter for NotOwner {
type Component = Owner
...
}
Would cause you to burn trough those visibility bits faster though π€
But you can do this via ComponentScope:
type Scope = ComponentScope<Health>;
You just can't specify the component directly due to orphan rule π’
That would just make Health the target of visibility, no?
What I'd need is a pair of two visibility filters on the same rule with the opposite logic ... Or just a way to set 2 scopes, one for when it's there, one for when it's not
Or like an inverted scope π€
Like in this case it'd be (SkillModifications, SkillOwner, Hides<CalculatedStats>) or something like that
As you can see the first two if you own the skill, but the latter disappear when you own the skill
Ah, I get it now!
I think right now you can hack it by creating a special wrapper that holds a boolean and automatically updated on your main component insertion. And they represent 2 separate filters.
But it's quite ugly, yeah.
Ah, that's a decent hack at least
But yea, we really need a better solution for this I think π€£
Also, it does feel a bit limiting that our filters require the same component on both entities. In this case I have a SkillOwner component that I could've matched already with the OwnedPlayer component on a client entity, but instead I'll have to make some new hack for that too :')
Definitely, I'll think about it more π€
Yeah, that's a limitation. But this one should be easy, we can allow defining a type.
Another workaround is to manually control visibility.
You have direct access just like before. You associate a bit with components (or entity) and manually toggle the bit visibility via ClientVisibility
The VisibilityFilter is just an abstraction on top of it.
Defining the type for the other end would work pretty well yea (and if Rust allows that, it can still default to Self)
Sadly, default associated types aren't allowed yet.
But what you describes sounds like a common use case, so we probably should reduce the ergonomics to allow for it.
Yea, putting type ClientComponent = Self; (or the other way around, or whatever other name we pick) should be pretty easy anyway
Just to clarify, when the client is an owner, you want to replicate sensitive information and when it's not, you want to replicate CalculatedStats instead? So when you're an owner, you don't replicate CalculatedStats?
Yep
Thanks, opened:
https://github.com/simgine/bevy_replicon/issues/642
https://github.com/simgine/bevy_replicon/issues/643
It's a bit limiting that our filters require the same component on both entities. For example, something like a SkillOwner that holds an entity doesn't work nicely as a visibility filter si...
Allow to replicate one scope if the filter returns true and a different scope if the filter returns false. For example, you might want to replicate skill modificators to an owner, but for everyone ...
We also probably should pass an entity to the filter, right?
Knowing the entities involved could be useful, though I haven't encountered a case where I needed it yet. Might be because a lot of my visibility stuff is fairly primitive though π€
Got a couple of bugs and crashes to fix (likely due to some minor porting mistakes), but my game now compiles and mostly works without bevy_bundlication π₯³
16 files changed, 344 insertions(+), 353 deletions(-)
```Saved a couple lines too, though it would've been more if I didn't need the hacks for my skill visibility stuff
BTW, I just realized something while messing with some of my marker components ... Is there any room for optimizations (not sending the data, not checking changes) for ZST components? π€
i have a habit of putting replicate_once for ZSTs which should be an at least slight optimization
Great!
We didn't have per-component visibility before, so you now handle it properly? I remember you mentioned that you just sent the whole entity until per-component visibility is not there.
Previously I always used the logic for skill owners, which wasn't ideal but who cares about some performance and cheat prevention while testing
ZST don't take any space in the message, only ID of the type (because we need to know what to insert).
But they are checked for changes. replicate_once allows to skip a few checks, yeah.
(but it's a incredibly small gain)
Hello, Is there an abstraction to send a message from one client to all other clients, (example chat messages), or am I forced to build myself a relay where it sends to the server and server sends to all
Also wondering if there are some existing lobby abstractions or if I have to build them myself
to my knowledge you have to relay through server, and I don't know of any "lobby" abstractions; what exactly do you want a lobby to do?
Like storing lobby data, clients reacting to other clients/the host leaving, and other small things like that
Right now i'm wondering whats the best way to get my clients to go back to menu if host disconnects
yeah i don't think replicon comes with all that. Could make a nice third-party crate
I will probably end up doing that, do you know how I could make the clients exit to lobby when the host leaves in the meantime?
when the host leaves your ClientState should reset to ClientState::Disconnected. Then you can just go ahead and leave the lobby when OnEnter(ClientState::Disconnected) run_if(in_state(LobbyState::InLobby)), or something to that effect
@viscid jacinth about sending a despawn without despawning an entity on the server. Do you need this functionality per-client?
hello!
was wondering if bevy enhanced input can be used with bevy_replicon to replicate inputs? or if there is any third party crate, or any other support?
Thanks, and for a client/host to break all connections (aka "leave"), they would just despawn all the connection entities or is there a less barbaric way ?
Hi!
https://github.com/NiseVoid/bevy_rewind has bevy_rewind_input. But it doesn't have BEI integration, it's general-purpose. It allows to send anything as buffered input.
I think the author uses it with BEI. Can you confirm, @dire aurora?
Depends on the backend. You need to use its API for it.
I do use it with BEI yes, I write data to a component from BEI observers, then network that using my crate
But that would mean having different logic if I want to change backends ?
Yes. It's because backends control the network state and spawn/despawn clients. It can't be bidirectional.
But it's fine, you don't switch between multiple backends. You usually pick one and just swap transports inside it if needed.
I have put a deprecation warning on bevy_bundlication, so feel free to either remove it from the list of replicon, or move it to unmaintained
And I also finished up my commit for removing it from my game, after fixing the bugs I created along the way
Okay, thanks for the info! I think I'll move it to unmaintained
And since avian got a release I guess I'll update bevy_rewind now
Great!
I also encountered a new bug (that I for now just worked around in an ugly way) in bevy_rewind_input
Inputs are components, however #[entities] does not get handled π
Ah, it's probably easy to fix, right? You can just map before sending on the client.
Yea, shouldn't be super hard to fix, I just didn't touch it for now because it might open a whole new can of worms and I wanted to wrap the commit up first π
Done, only 4 small errors from bevy changes, and all were in the migration guide despite being extremely obscure (Commands::new_from_entities changed, and bevy::ptr::dangling_with_align() got removed)
I think this release was one of the easiest to migrate.
I only afraid that on the next one we get all the changes that didn't get through due to holidays and sickness π
I mostly need the opposite (that removing Replicated doesn't replicate despawns).
But otherwise yes it would be per client. I can also just send a message
So you want to be able to remove Replicated on the server and make some of the clients keep the entity? π€
Wait, just to clarify, we have a proposal for proper pause here: https://github.com/simgine/bevy_replicon/issues/629
But I remember you were asking about avoid sending a despawn. Is this a separate thing from #629?
Hi, I have an issue when trying to disconnect/reconnect a client when client_diagnostics feature is enabled
The issue comes from this https://github.com/simgine/bevy_replicon/blob/525d83827051c822749ce05d97529d068f7d891c/src/client/diagnostics.rs#L120
(replication_stats.entities_changed - last_replication_stats.entities_changed) as f64
This triggers an overflow sub (because Res<ClientReplicationStats> gets reset on disconnect but Local<ClientReplicationStats> sill holds previous values, I guess)
Is this something I can handle one my side or does it require a update to bevy_replicon, maybe use replication_stats.entities_changed .saturating_sub(last_replication_stats.entities_changed) instead ?
What is weird tho, is that this only happens when I build in debug (+ bevy/dynamic_linking feature), no error when building in release..
This sounds like something we need to handle.
But do you have that many changed entities so you overflow the max integer of f64?
Yeah, Rust checks for overflow only in debug. In release it wraps.
"Yeah, Rust checks for overflow only in debug. In release it wraps." hoo, make sence then
No I clearly don't have that many entities, but from my understanding is not that entities_changed is too big.
But its that replication_stats.entities_changed (from resource) is 0 because it just got reseted by the https://github.com/simgine/bevy_replicon/blob/525d83827051c822749ce05d97529d068f7d891c/src/client.rs#L182 (OnExit(ClientState::Connected)).
But the last_replication_stats.entities_changed (from Local<ClientReplicationStats>) still have values before disconnect eg: 5 which result in 0 - 5 (on a usize)
Ah, makes sense π
Okay, let me fix it.
Fixed, added a test and drafted a patch release. Just do cargo update -p bevy_replicon.
Nice, thanks a lot !
hey ! i think im having a bug with replicated entities and relationship spawned in state transition before startup. what i do:
- in
OnEnter(GameState::Loading)(default state on the server, so runs beforeStartup) spawns lots of(A, Replicated). - still in the
OnEnterschedule another system run after the first system, spawns some(B, MyRelationShipOf(some_A_entity), Replicated))
A, B, MyRelationShipOf are .replicated().
client side, A without any B relationship are fine and Replicated component on them.
but for some weird reasons, A entities that have the relationship DONT have the Replicated components, its never added. i tried On<Add, A> observer, and only the one that dont have any B linked have Replicated components.
Could you create a tiny test that reproduces the behavior?
Take a look at test/spawn.rs. That would really help!
Hi @spring raptor its me again,
Is it possible to export your impl_filter_scope marcro (https://github.com/simgine/bevy_replicon/blob/c6daae80ff723741a59eb42159a764016b981c25/src/server/visibility.rs#L365C14-L365C31)
Or re-export your ComponentMask https://github.com/simgine/bevy_replicon/blob/c6daae80ff723741a59eb42159a764016b981c25/src/shared/replication/registry/component_mask.rs#L14 which is already pub (and make insert pub)
So we can create our custom visibility/filter scope with multiples Components
(I think you would also need to make init_component_fns pub from ReplicationRegistry)
What is your use case for a custom scope?
I would like to control the visibility of multiples components based on one other, example:
I have 2 entities with the following components:
- Player(1), Position(1,2), Controlled, Stats { ... }
- Player(2), Position(3,4), Controlled, Stats { ... }
I want to replicate Player & Position to all clients but I only want to replicate Controlled & Stats to the Client that is the "owner" of this player.
I currently make it works with just Controlled by adding a ControlledBy visibility filter
#[derive(Component, PartialEq, Clone)]
#[component(immutable)]
pub struct ControlledBy(pub ClientId);
impl VisibilityFilter for ControlledBy {
type Scope = ComponentScope<Controlled>;
fn is_visible(&self, reader: &Self) -> bool {
reader.0 = client;
}
}
Then I just need to add the ControlledBy component to each player entity and their respective client entity (if not the same).
Now I was guessing that a custom Scope would allow my to do something like this:
pub struct ComponentsScope<A: Component, B: Component>(PhantomData<(A, B)>);
impl<A: Component<Mutability: MutWrite<A>>, B: Component<Mutability: MutWrite<B>>> FilterScope
for ComponentsScope<A, B>
{
fn visibility_scope(world: &mut World, registry: &mut ReplicationRegistry) -> VisibilityScope {
let mut mask = ComponentMask::default();
let (index_a, _) = registry.init_component_fns::<A>(world);
mask.insert(index_a);
let (index_b, _) = registry.init_component_fns::<B>(world);
mask.insert(index_b);
VisibilityScope::Components(mask)
}
}
#[derive(Component, PartialEq, Clone)]
#[component(immutable)]
pub struct ControlledBy(pub ClientId);
impl VisibilityFilter for ControlledBy {
type Scope = ComponentsScope<Controlled, Stats>;
fn is_visible(&self, reader: &Self) -> bool {
reader.0 = client;
}
}
But you can just do this:
type Scope = (Controlled, Stats);
I just can't do this for a single component because of the orphan rule. This is why for a single component you need to use a wrapper.
Hoo, my bad then, perfect!
I actually the saw comment :
We can't implement FilterScope for both tuples and all types that implement Component. This is why this wrapper is needed to set the scope for only a single component.
But I miss understood it π
Ah, I see how this could be confusing π€
I'm just not a native speaker. How would you write this?
I am not a native English speaker either, that does not help haha ^^
But maybe just adding "If you want a FilterScope for multiples components you can directly use a tuple: (C1, C2)"
Yeah, makes sense.
Could you open a PR for it? π
Yep I can π
hey ! to use our own Hash with Signature component (i need components added later with entitycommands, that are not present when Signature on add hook runs), i need to use DeterministicHasher<Xxh3Default> like Signature does to support wasm ?
The Signature will take care of the hashing, so you can pass any value that is uniquely identifies the entity.
See Signature::from
ohhh ok cool, it take a single Hash type so I need to have a struct wrap my components to still count use multiple of them. thanks
You can just pass a tuple of values
Or call with_salt
oh cool Hash is impl for tuple of Hash cool
I guess it's the case for any tuple actually
Yep:
No I think i want exactly 629! (i.e. removing Replicated just pauses replication instead of despawning)
hey ! i want to get my client to be able to do client - dedicated server and single player. it works currently by just not using replicon client_trigger and directly calling spawn systems, but i would have liked to simplify the code. so i would need i think to be able to at runtime on command disable or enable the bevy replicon server plugin ? client is always enabled, and server is enabled only in singleplayer, so i can use client_trigger / server_trigger in the same client ?
or server plugin needs to be always added, and i should instead early return in my observers if not authoritative ?
Ah, got it! I'll start working on it.
Have you seen this?
https://docs.rs/bevy_replicon/latest/bevy_replicon/#abstracting-over-configurations
To put it short, the events are re-triggered locally. So it should "just work" π
A server-authoritative replication crate for Bevy.
ohh ok cool, so i dont need the replicon server plugin, but i need to use clientid::server. will try that out thanks
@viscid jacinth I need your answer for https://github.com/simgine/bevy_replicon/issues/635 about the first part. What do you think about having a resource with run condition or an message/event?
I just made the changes I need in the lightyear branch, I think i just made received_count pub
Ah, okay! Feel free to PR, I think it's also a good solution.
About the second part, I'd probably go with unsafe world access since it's less involved (commands require careful flushing).
DeferredEntity stores EntityWorldMut, which can provide the access. I can expose an unsafe access to it.
If it's still needed.
Yes i also do that in my branch
Ah, okay!
I'll wait for your PR then π You can do it later if you want, once everything else is figured out.
I'll start working on replication pause.
hey Channel::Unreliable means what the messages might never arrive ? its more lightweight if everything goes well, but otherwise needs re sending if it wasn't received
Correct!
i still need the server feature actually for single player
Ah, yes, I think it's unavoidable. But it's only for Replicon, you don't need the backend itself.
Potentially we can try to move the events resend logic into shared π€
yes, I'm wondering still what the overhead is of having the servers systems etc running and being added to the app ? pretty small ?
They won't run unless the server is running, so the overhead is tiny π
oh nice thanks
@viscid jacinth Realized that replication pause will also require additional handling for prediction libraries. And requires quite a careful handling when visibility system comes into play.
I'm still open implementing it, but could you explain your use case a bit more? Maybe I can come up with something more specific?
How can I stop sending replication updates for an entity without despawning the entity?
So you just want to be able to keep the entity on the client when you stop replication, or do you also need to continue the replication later on?
That's mainly why I'm asking. I'm trying to understand whether you need a fully generic replication pause/stop or just the ability to keep the entity on the client when replication stops.
Because if you don't continue the replication later on; it has consequences on how to do entity mapping cleanup on the receiver side?
I think just keep the entity on the client when replication stops is enough 99% of hte time
Yes, and resuming requires careful tracking of what changed. Missing a component removal when the entity was paused is probably expected (like with despawns), but I'd probably expect insertions to be replicated on unpause π€
And with visibility, when the entity was hidden and then unpaused, the despawn should be replicated. Pausing is definitely doable, but it adds a bunch of corner cases to handle.
And then I remembered that initially you were asking about avoiding replicating despawns when the entity stops being replicated, so I decided to ask.
And just keeping an entity is actually already supported!
You just need to override this function: https://docs.rs/bevy_replicon/latest/bevy_replicon/shared/replication/registry/struct.ReplicationRegistry.html#structfield.despawn
It accepts an entity, so you can check if it contains any user-despawned component and skip the despawn. No changes required.
Stores configurable replication functions.
Does the VisibilityFilter work on the client entities themselves? (e.g. a visibility filter for client entity A that will only show replicated components of client A to ... client A)
I replicate the client entitites and there are some components I would wish to hide from other players.
Not yet. Currently you need to use the lower-level API by manually toggling the bits.
We're working on a better design for visibility filters that should unlock your use case: https://github.com/simgine/bevy_replicon/issues/642
@spring raptor came across your message here. for the noobs (me) - what tools are you aware of on the os-level that allow that kind of latency simulation (windows, macos.. whatever)
This one should be straightforward: https://github.com/tylertreat/comcast
Hi, someone at work is seeing something strange in replicon. It seems as though 1 mutation of replicated type Foo<T> on the server leads to >1 runs of a system with Single<Foo<T>, Changed<Foo<T>>>. We're looking for bugs on our side, but is this a possible scenario in replicon?
We now debugged it to use the bevy feature .changed_by and on the client it shows 3 times changed in bevy_replicon-0.38.2/src/shared/replication/registry/command_fns.rs:106:49
This is not expected. Even under bad network conditions, where mutations might be sent multiple times because the server hasn't received an ack yet - the client should only apply the latest mutation per entity. We have explicit checks for this.
Could you try to provide a minimal repro so I can take a closer look? By any chance, are you accidentally mutating your component more than once?
We think we arenβt accidentally mutating. The server logs on mutate via Changed<..> and it shows once.
More hints is that it is quite big, a JPEG image. Also it doesnβt happen in server-is-also-client mode,
Iβm going travelling a week now but I appreciate tips if this context helped
More hints is that it is quite big, a JPEG image.
I don't think it could be a problem π€
it doesnβt happen in server-is-also-client mode,
You mean it's not happening for the the server itself whoc is also a client or not happening at all (i.e. for all connected clients)?
Ah I think my colleague didnβt try both at the same time. But my understanding is that when the client runs on the same machine in the mode where replication is off it doesnβt happen.
If the server is also a client (it's called listen server mode), no actual replication happens, so that would explain it.
Does it happen on mutation or insertion on the server side?
It was on mutation
I shouldn't happen:
https://github.com/simgine/bevy_replicon/blob/8d510fd4d7d3a11bffa5a47f53a85bfa5b21c875/src/client.rs#L687
Unless you use a third-party crate that requests history. But in this case the crate shouldn't write into your component and instead do the write in history.
Thanks, will try minimal repro if we canβt find. Is there a way to trace replication with any sort of tool?
Yes, you can enable logging. Scroll to the very end of the quick start guide.
hey ! im getting some error log
ignoring message `ServerTriggerEvent<TookDamage>` that failed to deserialize: unable to map entities `[199v0]` from the server, make sure that the message references entities visible to the client
because im sending the event in the same frame as despawning the entity. is there a way to suppress this error for a specific event ?
But why you send an event for an entity and despawn the entity? π€
The message will just be discarded
Ah, you probably don't know that it will be despawned when you dispatch the event π€
I can downgrade this event to a warning, but also consider somehow checking it since you send bytes that will be discarded.
thats what ive done, just checking if health == 0 but i dont like it, if the unit is revived, is healed, or whatever reason the event will not trigger.
thank you for proposing to downgrade the log level, but i dont want any warnings either in my game π§Ή. is there any other solution ? i guess using a Message instead of a entity event, and mapping it manually on the client by querying the entity mapping hashmap, and silently failing if the entity doesn't exist ? but thats cumbersome.
i send the event at the moment the unit took damage in some observer, and its despawned later in a system that check for unit with health 0
Maybe just reorder the system after despawn?
I mean not downgrade it in your game, but on Replicon side (by changing the code). Not a big fan of this solution since most of the time it's better to warn user.
Another option is to add a special method that will mark the event as ignored if entities can't be mapped.
- i dont think i can re order as this event is triggered inside the observer setting the health to 0 (a took damage handler). the system despawning needs to run after to see the updated health after damages applied to despawn entities.
- yeah i understood replicon side, not a fan too since its not really a fix.
- replicon or user side ? this adds overhead but not much i guess.
thanks.
replicon or user side ? this adds overhead but not much i guess.
On replicon side. The idea is to just discard without a warning when user expects that it's possible to happen. But I also don't like it.
Another option is to track which event stores which entities and drop them from the queue when related entities are despawned. But it will add quite some overhead.
I'd probably recommend to rethink the approach on your side.
yeah, but i really dont see how π§ . there's the damages events being send first (to the client too so he can display some hit marker on the target) and after the despawn systems runs. but whatever this half fix of just checking health for works mostly for now.
thanks !
actually ill just use two messages, one local and one for notifying the client
Hi, I'm Torsteinβs colleague. Here is a reproduction. Tried to minimize it further, but did not manage. If you swap default plugins with minimal, the problem seems to go away
If you figure out how we can improve this - feel free to suggest.
I feel like actually using the library helps me to shape the API nicely. I just never encountered a use case like yours π
I think notifying about inability to map entities inside the event is important.
Created this issue https://github.com/simgine/bevy_replicon/issues/652, so I have something to link to in the workaround at work
A server-authoritative replication crate for Bevy. - simgine/bevy_replicon
Thanks, I'll take a look.
Yeah, issues on github much more preferred.
Thanks, posted here first in case you knew some bonehead mistake I could have made that caused this π
I planned to take a look, but got distracted and forgot π
I'll take a look today after work.
hey ! on the server, all messages send with ToClients<M> and SendMode::Broadcast are also send locally by send_locally ? it check for clientstate::disconnected, but on the server it always disconnected π€
That's correct!
It's needed to support listen server mode.
Do you want to disable it?
pub fn plugin(app: &mut App) {
app.add_mapped_server_message::<TookDamage>(Channel::Unordered)
.add_systems(
FixedUpdate,
retransmit_took_damage
.after(despawn_killed_entities)
.run_if(is_authoritative),
);
}
/// System that retransmits TookDamage messages after despawns.
/// Filters out messages for entities that no longer exist.
pub fn retransmit_took_damage(
mut message_reader: MessageReader<TookDamage>,
mut message_writer: MessageWriter<ToClients<TookDamage>>,
mut commands: Commands,
) {
for TookDamage(entity) in message_reader.read() {
if commands.get_entity(*entity).is_ok() {
message_writer.write(ToClients {
mode: SendMode::BroadcastExcept(ClientId::Server),
message: TookDamage(*entity),
});
}
}
}
i just changed to BroadcastExcept(CliendId::Server), thanks :)
Maybe worth creating a constant SendMode::CLIENTS_ONLY π€
@thorn forum opened: https://github.com/simgine/bevy_replicon/pull/653
cool :)
Hi, big newbie here just playing around.
I actually don't actually know how many 'rooms' would exist at one time as players could be anywhere in the world, but one thing is they are always in 9 rooms (chunks)
Player 1
chunk1 (-1, 1)
chunk2 ( 0, 1)
chunk3 ( 1, 1)
chunk4 (-1, 0)
chunk5 ( 0, 0)
chunk6 ( 1, 0)
chunk7 (-1, -1)
chunk8 ( 0, -1)
chunk9 ( 1, -1)
Player2, Player3
chunk1 (44, 16)
chunk2 (45, 16)
chunk3 (46, 16)
chunk4 (44, 15)
chunk5 (45, 15)
chunk6 (46, 15)
chunk7 (44, 14)
chunk8 (45, 14)
chunk9 (46, 14)
Using this random example, If player 2 were to harvest a tree in any of those chunks, only player 3 would receive that update. I assume this is how it would work if I had it set up correctly, and if so is there any examples out there to help me out.
Hi! Take a look at visibility filters. Create a component that holds a static array of 9 chunks and simply implement the trait to check for the intersection.
Speaking of filters π
I opened a new branch with a rework:
https://github.com/simgine/bevy_replicon/pull/659
@dire aurora it was mainly your request.
If you're interested, I'd recommend to just read the docs of the trait, don't bother looking at the diff.
Once merged, I'll draft a new release.
I really need the cart's bsn branch for my game, so I'll open a draft PR targeting it for all my crates.
I just want to draft a new release with all the accumulated changes first.
Created a branch that targets the bsn branch.
All tests pass, and all examples compile.
I haven't used bsn! inside the examples yet because its syntax might still be unstable. I'd like to wait a bit.
The branch also includes long-awaited resource replication support! See the replicate_resource* methods.
https://github.com/simgine/bevy_replicon/pull/660
I ported bevy_replicon_renet as well:
https://github.com/simgine/bevy_replicon_renet/pull/74
Targets latest Cart's branch with BSN. The PR also implements resource replication from the latest main.
Ah the client component targeting. Based on the trait that looks good to me!
Resource replication will be a nice quality of life improvement, I'm looking forward to it! Does this also mean State replication should be possible? I'd love to cut some of the boilerplate needed to keep states in sync.
i'm a bit lost on the matter, but will bevy_replicon suffer significant changes with the bsn?
All i know is bsn brings better semantics to implement entity/scene graphs which I was looking forward for replicated UIs with replicon
thanks
Yep!
No, I don't expect any BSN-related changes in Replicon's API.
I just wanted to try BSN in my game, so I had to patch all my crates.
But I will update Replicon's examples to use bsn! instead of the regular spawn once things stabilize a little.
hey, just wanted to say Signature component is really awesome :) using it to remap to existing entities on re connect
hello ! i think i have a case in my game where i SignatureMap doesn't get updated when a entity is despawned, dont know how/when, and so bevy_replicon panic:
The entity with ID 7v0 does not exist (enable `track_location` feature for more details)
If you were attempting to apply a command to this entity,
and want to handle this error gracefully, consider using `EntityCommands::queue_handled` or `queue_silenced`.
Encountered a panic in system `bevy_replicon::client::receive_replication`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
is it ok to make it non panicking ? i can't find where its coming from.
its for my use case to remap entities on reconnect, and i also insert Signature component client on entities spawned by the server, it that could be that. im still on bevy_replicon 0.37.2 if something changed since.
thanks !
made this reproduction test:
fn signature_despawned_by_parent_cascade() {
let mut server_app = App::new();
let mut client_app = App::new();
for app in [&mut server_app, &mut client_app] {
app.add_plugins((
MinimalPlugins,
StatesPlugin,
RepliconPlugins.set(ServerPlugin::new(PostUpdate)),
))
.sync_related_entities::<ChildOf>()
.finish();
}
server_app.connect_client(&mut client_app);
let client_parent = client_app
.world_mut()
.spawn((Replicated, Signature::from(0u8)))
.id();
let client_child = client_app
.world_mut()
.spawn((Replicated, Signature::from(1u8), ChildOf(client_parent)))
.id();
let server_parent = server_app
.world_mut()
.spawn((Replicated, Signature::from(0u8)))
.id();
server_app
.world_mut()
.spawn((Replicated, Signature::from(1u8), ChildOf(server_parent)));
server_app.update();
server_app.exchange_with_client(&mut client_app);
client_app.update();
server_app.exchange_with_client(&mut client_app);
assert!(client_app.world().get_entity(client_parent).is_ok());
assert!(client_app.world().get_entity(client_child).is_ok());
server_app.world_mut().despawn(server_parent);
server_app.update();
server_app.exchange_with_client(&mut client_app);
client_app.update();
assert!(client_app.world().get_entity(client_parent).is_err());
assert!(client_app.world().get_entity(client_child).is_err());
let server_parent = server_app
.world_mut()
.spawn((Replicated, Signature::from(0u8)))
.id();
server_app
.world_mut()
.spawn((Replicated, Signature::from(1u8), ChildOf(server_parent)));
server_app.update();
server_app.exchange_with_client(&mut client_app);
client_app.update();
}
it was happening with towers and tower troops in my game.
im using stuff like the card/troop type, tile position and team for the signature hashing. there can be only one at a time, but maybe to workaround this problem i should make the hashing different with a counter for example ?
Thanks!
This definitely shouldn't panic. I'll look into it later today.
thanks ! sadly i dont think we can really "solve" this footgun, there can always be case where the user can despawn its own thing related to it, but you know more about the design. just making it not panic would be a good step i think.
Drafted a patch with the fix π
The provided test just works now.
Right now my challenge is networking effects, specifically sound effects
sure I could send a Message from the server to the client of which sound to play, but I feel it is the weakest solution
anybody else had successful approaches?
i've been trying to trigger sound effects from ashared code, so both the server can play it and the client predictively play the sound, but came across desyncs, playing the effect twice and so on.
sound is surprisingly tricky to network. When you involve prediction, you often want to have a lot of control over how sounds play so you can do things like cut them out during a misprediction correction/rollback. I'd say you should make certain already networked events also cause a sound on the client side when possible. Also note that a dedicated server doesn't play sounds (since no one is listening), that's entirely a clientside issue.
Yeah, usually sound done on the client side based on some gameplay events that either predicted or replicated from the server.
Small behavior change:
https://github.com/simgine/bevy_replicon/pull/666
Asked by @viscid jacinth for Lightyear, but I think it's a more expected behavior on its own.
is there a way to force replicon to send a final mutation before despawning an entity? Such that its final component values on client match the server?
Hm... No, I don't think you can do something like this π€
Maybe we can introduce delayed despawns. I.e. a component that does the despawn once all client synced the entity.
Another option is to expose API to check if specific client acked all mutations entity and let users write this logic.
thought so. Almost thought I had something by inserting instead, but that didn't work. Working on projectile replication/prediction and trying to make sure the authoritative projectile's final position got through so that the client can show an accurate resimulated projectile (it gets forwarded on the server based on RTT, so it might hit something in one tick for short range attacks, so I need clients to rewind the projectile visuals to be able to see it at all)
Have you considered making projectiles deterministic? This way you will be able to predict them on the client, but once server sends an authoritative initial position, you calculate the destination and smoothly interpolate the trajectory from your current position.
Replicating all projectile positions is a bit expensive.
determinism seems like a whole can of worms to get into; I've been following this series as I think it has a pretty nice and robust implementation https://sreitich.github.io/projectile-prediction-1/#introduction
I would consider determinism more if there wasn't a physics engine involved but I don't want to have to deal with subtle floating point errors or whatever
or having to sync random seeds, etc.
the game will not have many characters nor many projectiles in play, so the cost of replication shouldn't be prohibitive?
solution ended up being "uninteresting" networkingwise, I just use bevy's new(ish) clone features to duplicate the replicated entity minus the networking parts, it has no visuals anyway, then apply visuals only to the duplicate and some extra logic to not have the duplicate be purely visual and despawn appropriately
Interesting article!
If physics involved, it becomes more complicated, sure.
Why do you need 2 entities? π€
because of two reasons:
- on any client, projectiles from other clients need to be resimulated (pulled back to the start, then let loose again) so that they visually display correctly despite the server fast forwarding through RTT / 2 of the projectile's initial path to line it up with the owning client
- doing this directly to the auth projectile on a listen server (which is technically a client) would defeat the purpose of forwarding in the first place, so there has to be a non-functional visual projectile as well as the functional but hidden projectile
Ah, makes sense.
But have you considered using receive markers instead? They allow you to override how the data is written per-client. So you can store the received data inside something like Authoritative<T> instead of writing into the component directly. You'll be able to have a single entity this way.
that could work π€
Both approaches are functional, but markers could make it more convenient.
Just updated the docs to also mention despawns override in the quick start guide. Useful together with markers.
https://github.com/simgine/bevy_replicon/issues/315
Resources will become entities soon: bevyengine/bevy#17485
Our current implementation will just start working for resources as well. For now, I would suggest using singleton entities as a temporary workaround π
The linked issue is closed, is the plan still long term for resources to be represented as an entity or is this now something that is worth doing?
The linked PR was superseded by another PR that was merged: https://github.com/bevyengine/bevy/pull/20934
And I already implemented the support on the Replicon side that targets the latest main π
https://github.com/simgine/bevy_replicon/pull/660
Bevy 0.19? What
Premoving
It's a draft for the next Bevy release
I wanted to try BSN and check how resource replication would work.
I didn't track all changes, but I'm excited about resources and BSN
What is bsn?
I better just link: https://github.com/bevyengine/bevy/pull/23413
Thanks Shatur-Senpai
I think my biggest gripe with BSN is that components are always 100% required
meanwhile in my implementation different components are added for a networked entity depending if its on the server or the client
so i could not do something like
bsn! {
Tag("Car")
Health # server side component
WheelAnimation # client side component
}
im interest to see how I could get over this problem using replicon
You can react on Replicated or Remote insertion and insert client or server specific component.
Also can you insert components based on condition with templates?
thats what I've been trying to figure out, as I started trying to get acquinted with BSN/Templates today.
regarding Replicated Observers, that is what I do for now, but I do not think I can have singular scene configuration that is then loaded differently for client and server, due to BSN's constraints.
honestly I'm still pretty unsure about it. I am planning to release my source code in a few weeks with possibly a solution for this head scratcher
I agree, it would be nice to have condition-based. I haven't tried BSN that much, I'm waiting for the PR to be at least merged π
My branch also uses an outdated branch. There is a new PR out.
Are there any other examples of quinnet_replicon? I'm having trouble following along the boids / Tic tac toe examples
I think they have examples: https://github.com/Henauxg/bevy_replicon_quinnet/tree/main/examples
Integration with bevy_quinnet for bevy_replicon. Contribute to Henauxg/bevy_replicon_quinnet development by creating an account on GitHub.
I have a message which functions as both a client to server and server to client message. It is registered something like this,
app.add_client_message::<MyMessage>(Channel::Ordered);
app.add_server_message::<MyMessage>(Channel::Ordered);```
When the client writes the message, it is never received on the server. Is it possible to have a message which is both or is the second registration overriding the previous? I try to pick up the message on the server with something like this:
``` mut from_clients: MessageReader<FromClient<M>>,
mut to_clients: MessageWriter<ToClients<M>>,```
Yes, it's possible.
We even have tests for it: https://github.com/simgine/bevy_replicon/blob/master/tests/bidirectional_event.rs
sick, thank you
I'm switching to replicon from lightyear and I was wondering if there's something similar to lightyear's leafwing integration for input handling, or if I have to send and handle my Actionlike's via client to server messages manually? (authoritative headless server setup)
We don't provide a built-in integration for it. But I'd do it like this:
- Spawn contexts on both sides independently and use
Signatureto uniquely identify each context. The user will need to use game-specific components to calculate the hash, such as a player number. - When sending input, send context hash and index of the action in
Actions<C>(since indices are small numbers, they'll take only 1 byte with varint encoding!). - Sending should be done redundantly: you send all inputs since the last tick confirmed by the server.
It would be really nice to have a crate for it π
thanks, another issue I'm having is children entities. I noticed that when the server replicates the parent, it seems client doesn't get the children (I see the entity itself query ok on the client, but the children optionals are empty). I've read
Some components depend on each other. For example, ChildOf and Children. You can enable replication only for ChildOf and Children will be updated automatically on insertion. This will emit a B0004 warning which can be safely ignored. See #19776 for more details.```
But I'm a bit confused by the wording here. Does this imply I need to register `ChildOf` for replication?
Yes, you need to register ChildOf
And just ignore the warning. It will be fixed with 0.19
hmm, thing is tho it doesn't seem to work, the children still come up empty on the client. I'm trying to see if it's something else I'm missing.
Children entities should have Replicate, just like their parents.
you mean components?
Replicate component should be present on parent and children
what I do is server gets client connect (works) -> spawns a ship entity (works) -> adds physical mount points for guns on ship entity as children (works, serverside) -> replicates ship to client (works, but it seems the ship has no children for some reason)
my replication setup on both sides looks like:
// core
app.replicate::<Transform>();
app.replicate::<ChildOf>();
// base game
app.replicate::<Ship>();
app.replicate::<Faction>();
app.replicate::<Weapon>();
app.replicate::<ShipMounts>();
app.replicate::<Shot>();
...
So I'm a bit baffled why I get the Ship but the Option<Children> is empty even tho I see in server logs that server clearly added the weapons and the "shipmounts" flag in (that one also seems to be on the client)
the way my whole system is setup is "visuals are added on" systems (and audio too). so that server can handle physics but has no visual/audio dependencies
How do you look for Children?
If inside observers, you'll likely to get 1 frame delay if I remember correctly.
Maybe not a frame delay, but some delay
pub fn init_ship_visuals(
mut commands: Commands,
preload: Res<VisualPreload>,
ships: Query<
(Entity, &Ship, Option<&Children>, Option<&Player>),
(With<ShipMounts>, Without<ShipVisuals>),
>,
weapons: Query<(Entity, &Weapon)>,
) {
for (e, ship, subsystems, player) in ships.iter() {
log::info!("init ship visuals");
{
// ensure ship has Visibility before we add children
let mut ship_entity = commands.entity(e);
ship_entity.insert(ShipVisuals);
}
// add sprites to weapons in mounts
if let Some(subsystems) = subsystems {
log::info!("init subsystem visuals"); // this never fires, no children
When do you run this system? π€
ShipMounts seems to be in on the client side coz I see init ship visuals logged as well as the ship visual itself
FixedUpdate
coz I don't know when this will get replicated in
This makes sense. And Replicon will send all insertions together.
So I'm not sure what's happening. Try logging the entities that are supposed to be subsystems.
oh hmm
I wonder if it's server doing thigns in 2 frames
oh wait no, coz ShipMounts only gets added once the weapon is in
just to clarify the logic is "spawn" -> Ship gets inserted. then another system (server side here) will pick up Ship without ShipMounts and put in the weapons as children and ShipMounts as "done with that" flag
and then on client it's supposed to pick those ships with ShipMounts and the weapon subsystem Children and spawn the visuals and mark with ShipVisuals
You could see this behavior if Ship gets replicated first.
If you insert your mounts later.
but I check for ShipMounts in that visual system, which gets added this way
pub fn arm_mountpoints(
mut commands: Commands,
ships: Query<(Entity, &Ship, &Faction), Without<ShipMounts>>,
) {
for (e, ship, &faction) in ships.iter() {
commands.entity(e).insert(ShipMounts);
for mp in ship.class.mount_points() {
let mut weapon = commands.spawn((
faction,
Weapon::light_plasma(),
Transform::from_translation(mp.extend(10.0)),
));
weapon.insert(WeaponState::default());
let weapon = weapon.id();
commands.entity(e).add_child(weapon);
}
}
}
If this system runs after replication, client will get the ship without children first.
which is fine tho no?
coz I only query for Ship with ShipMounts and those should only be there if the Weapon children are done
Not sure If I get this π€
Yourr mounts are children, right? And your issue is that you don't have them on the client.
ok so ShipMounts is not a child, it's a component that gets added to the Ship's entity, once the Weapon children are added (see above)
and then I use that component as a flag to query for ships that need visuals added
(Entity, &Ship, Option<&Children>, Option<&Player>),
(With<ShipMounts>, Without<ShipVisuals>),
>,```
so this ^ query shouldn't pick any "ships that don't have weapons added yet"
so what I'm seeing is that somehow client gets the ShipMounts replicated, but has no children
First thing I'd try - log entities that are supposed to be children.
is app.replicate::<ChildOf>(); all that's required for children to work?
No, you also need to add Replicated.
I mean apart from the obvious Weapon being set too
To all children
If entity has Replicated and ChildOf, it will be replicated as a child.
ah so all component spawns that are children need Replicated when spawning on server?
Yep
aaah ok that explains it then
Just like any other replicated entity
Relations aren't special, they work just like any other components.
yeah that was it, sorry for the long winded explanation π
I assumed Replicated parents would get all registered children replicated without explicit flagging
Maybe worth mentioning in the docs π€
I'm not a native speaker, but I tried my best to explain. If you want to help to improve the wording - a PR would be welcome.
I'll give it a stab
https://github.com/simgine/bevy_replicon/pull/671 π thanks for the lib
hmm actually let me bump it with clearer list including the ChildOf thing
Maybe just mention that children should also have Replicated component? π€
well that's 3. Add Replicated component to both child and parent entities. but I can make that into 2 steps I guess
done
Left a suggestion with a shorter version. Let me know if this is still clear π
sg, applied π
I got my game working fully with bevy_replicon (YAY!) and it works smoothly so that's great! I have a few warnings to figure out though, one of them is:
2026-03-31T00:51:02.144441Z WARN bevy_ecs::hierarchy: warning[B0004]: Entity 81v0 with the GlobalTransform component has a parent (82v0 entity) without GlobalTransform.
This will cause inconsistent behaviors! See: https://bevy.org/learn/errors/b0004
This seems to come from the weapons that we discussed that are added to the ship "mounts" as children. Transform is a required component for them, and I thought GlobalTransform would just be added client side on its own? Is there something I need to do here?
did you happen to add the following line?
app.sync_related_entities::<ChildOf>()
I think this happens because child of is updated first without receiving the transform.
I'm getting ERROR renetcode::client: Failed to update client: disconnected: server denied connection on trying to connect a second client, my client_id is just set to current timestamp u64 (millis) in this case and is not launched immediately so it shouldn't conflict. Is there something else that'd cause this?
the 1st one works fine
reconnects also work as long as it's 1 at a time
oh doh nvm. I forgot i just copy pastad the max_clients: 1 from example π
how can I access the client's user_data or client_id (the one set on the client, not the entity on the server) when a client connects to the server?
I think you have to manually save them.
This is the warning I mentioned in the documentation. It's a Bevy "bug". This will be fixed in 0.19.
I think it's unrelated. It affects only mutations and needed if you want to replicate all mutations of specific component together.
But the mentioned warning triggered for insertion. Insertions already replicated together.
Clients are entities, you can get their components.
If you need an entity by the backend ID, you can use NetworkIdMap.
thanks for correcting
I have some funky replication behaviour going on aswell as replicated relationship doesnt apply correctly some times, but I havent been able to find a root cause or minimal code example to reproduce it
Weird, I wrote a lot of different tests for them.
Let me know if you reproduce.
I think he meant from the client's executable?
Ah, this could be obtained from the backend (renet, aeronet or whatever).
I will try to reproduce it now, but I've not approached it yet, assuming it could be a possible desync from component lifetime observers and the fact it happens for other clients.
but a few details:
- given human & item replicated entities
item is inserted with Contained entity, which is a relationship with human as target
contained also automatically inserts ChildOf to human (maintains) - other client joins in
human has no replicated component, but the contained item does. (but this is just a symptom not a cause)
Relations are only related to mutations.
If you have a desync with this, you most likely have a logical error somewhere.
did a test snippet on bevy_replicon's main, where would you like me to post it?
You can share it here or open a PR, whatever is more convenient for you
What I was aiming for is a sort of "client client_id"
so that I can on the client know that this is meant for me π
what I was expecting was that I can get something from the master server and put it in the authentication as user_data and then use that as the "this is for client x" flag
e.g. in my case I use it to assign controls to the right entity when it gets replicated on each individual client
I just did it via messaging unsecure for now
On the client or on the server?
So the intent is to have an id that identifies the client on the server such that if I send a msg or replicate an entity to the client i can flag things with that id and the client knows "this is for me specifically"
e.g. I replicate a Ship with ControlledBy(client_id) and then the client that has a matching client_id of some sort on its end will know to assign controls to that ship
https://github.com/simgine/bevy_replicon/issues/672
I have opened an issue
so not "exclusive" replication or sending only to that client, just assigning ownership in a way
On the server you can either use entity as ID or NetworkId.
i might be using the test code wrong tho.
yes but the client doesn't know it belongs to them until you tell them no?
No it does not, the server state will have to replicate this ownership to the client
I'm not sure what you mean. When client connects, you receive an event with Id.
my plan is to have a master server that will generate the Auth token and give it to the client. I was hoping I can put the "client id" in the auth token, and then the client would know it right off, and the server would see it as clients connect too so no need to ping-pong a unified client_id
isnt this more regarding the backend rather than replicon itself?
yes, hence my question of, can I get the user_data? π
there's a user_data field inside the auth
if I can access that on the server side, it solves this nicely
e.g.
let authentication = ClientAuthentication::Unsecure {
client_id,
protocol_id: net::SHARED_SETTINGS.protocol_id,
server_addr: addr.0,
user_data: None, // access this on server when client connection is added
};
oh you mean the user data?
this is more related to https://discord.com/channels/691052431525675048/1038137656107864084 rather than replicon itself
https://docs.rs/bevy_renet/latest/bevy_renet/netcode/struct.NetcodeServerTransport.html
has user_data per client
API documentation for the Rust NetcodeServerTransport struct in crate bevy_renet.
Fixed and drafted a new patch release.
Thanks!
just saw it, out of curiosity, what was happening underneath?
When a component with an entity inside is deserialized, I spawn it and create a mapping from server to client.
But it was missing Remote.
I see now.
(curiously my game is still on replicon 0.38, so this ocurred with Replicated instead)
Yep, the same kind of problem π
also, since we are at it...
could I propose two API changes?
Sure. But maybe in an issue?
sure, I can write more developed issue tomorrow, I just wanted to lay out a sort of draft here
it's specifically regarding these two:
- Server Player Entity (I am sure you already tried this before, I think I've seen it, but you must've opted for the current method, but I would like to suggest it again)
- Reforming
FromClient
https://github.com/simgine/bevy_replicon/issues/673
I had an issue for FromClient already written, here it is,
curious to know what you think (perhaps I have missed some logic that can already do this)
Is there a way to decide between replicate_once and replicate per entity with a filter component? Or any other way? For things like when every entity needs a component initialized on them but only a few actually need it updated.
I think there's a workaround with a component that gets replicate_once'd and replaces itself or something but its seems a little hacky and complicated.
ah wait I think I figured it out.
If an entity matches multiple rules, the functions from the rule with higher priority will take precedence for overlapping components. For example, a rule for
HealthandPlayermarker will take precedence over a rule forHealthalone.
Think this will work, just make one rule for replicate_once and one for on_change
example for elaboration/anyone searching for this later
.replicate_once::<Transform>()
.replicate_with::<_>((
RuleFns::<ReplicateTransform>::default(),
(RuleFns::<Transform>::default(), ReplicationMode::OnChange),
))
Yep!
You can also do
app.replicate_filtered::<Transform, With<ReplicateTransform>>()
Assuming your ReplicateTransform is just a marker on the server that you don't need to replicate.
right, but if the tag isn't there then I want it to be replicate_once
You need both:
app.replicate_once::<Transform>()
.replicate_filtered::<Transform, With<ReplicateTransform>>()
I meant replicate_with can be replaced with replicate_filtered.
Filters counts as components. So Transform and your marker rule has priority equal to 2.
oh that actually works... neat. I couldn't really tell from trying to look at the code because there's so much calling into another function with slightly tweaked parameters lol
Yeah, it's all just sugar for replicate_with π
replicate_with supports filters as well.
ah i see
*sugar for replite_with_filtered.
replicate_with is replite_with_filtered with no filters π
few questions
- is there an order of which networked messages are applied? e.g. update components -> events -> messages?
- is there any difference between placing
sync_related_entities()in shared or server code? - do visibility filters update when ClientComponent is added/removed? I wrote a test for it and it seems to be failing
thank you
- Yes, that's the exact order.
- Doesn't matter, it affects only server.
- Actually, they're not updated π€ We probably should do this.
https://github.com/simgine/bevy_replicon/issues/677
I have posted an issue about it with a test.
Perhaps in the add_visibility_filter() we add a new observer for changing ClientComponent values?
Yep!
I commented on GitHub. If you're interested, I'd be happy to receive a PR.
Otherwise I'll take a look mysefl a bit later.
I think I can draft a PR for this soon as it's more urgent for me
im glad to help
I feel like I'm prodding you too much with small changes ahaha
It would be awesome π
It's fine! On contrary, bug reports are always helpful π
I hate bugs, so I usually prioritize fixing them.
At least someone is asking for small changes for once, instead of me and my major feature requests π€
I have posted a draft PR.
saw it merged, cool
I am close to releasing the source code of my game, hopefully it'll bring more insights on how replicon is used aswell.
Already on crates.io, enjoy π
It's great!
@dire aurora remember I borrowed your implementation for batched insertions from rewind? Turns out it's UB π
You're trying to align pointers relative to data in your Vec here:
https://github.com/NiseVoid/bevy_rewind/blob/17a47b618e063862a6235c7a0ac84d8f9e6b42e5/crates/bevy_rewind/src/history/batch.rs#L43-L48
But the actual pointer address to the Vec's data starts at the pointer to its allocation. And even if you try to align relative to it, any Vec reallocation can break the alignment again.
Later you pass pointers to Bevy as aligned (the API requires this, Aligned is the default param), while they most likely aren't:
https://github.com/NiseVoid/bevy_rewind/blob/main/crates/bevy_rewind/src/history/batch.rs#L72-L79
Which triggers UB, Miri catched it.
All modern CPUs support unaligned access, but compiler optimizations could break it.
Here is how I fixed it:
https://github.com/simgine/bevy_replicon/commit/ef10ad7ad505e61197c568fb6dada81b3319e2b4
It's not only simpler, but also 3β8% faster since the data is now actually aligned. Crazy stuff!
@viscid jacinth I also checked how it's done in Lightyear, and you have the same problem. You're writing data into a Vec in this function:
https://github.com/cBournhonesque/lightyear/blob/957b302d03ae7566b18e481feb901923980bcb59/lightyear_replication/src/registry/buffered.rs#L131
And later treat all pointers to it as aligned here:
https://github.com/cBournhonesque/lightyear/blob/957b302d03ae7566b18e481feb901923980bcb59/lightyear_replication/src/registry/buffered.rs#L112
But it probably doesn't matter since you're planning to migrate to Replicon.
You guys gonna make lightyear and replicon together? @spring raptor
lightyear is gonna use replicon iirc
Yes, that's the plan. We'll provide replication and Lightyear higher level abstractions.
Interesting. This definitely used to pass, which means either bevy changed what they do with those pointers, or miri is better yet again
I'll see if I can apply your fix to rewind too
It also could've passed if you were lucky with alignment.
Or if the component is 1-byte aligned, like Component(u8), this always passes.
I had it fail before adding the alignment logic at least
And iirc niri always injects randomness into such things to make sure it fails in a couple of tries
Hey gaymers,
I've just started with Bevy, and I'm not super familiar with Rust, so hopefully I don't look like too much of an idiot.
I'm using bevy_replicon with the bevy_replicon_renet messaging backend.
I've been experimenting with the examples in the bevy_replicon_renet repository for a few days now, and I've got something to work, but I'm not sure if it's implemented very well.
My code is currently based on the authoritative_rts.rs example, and it works fine, but I would like some tips on how I should be structuring my project.
I need a dedicated server binary for my game, so I've created & separated ClientPlugin, ServerPlugin and SharedPlugin into their own crates.
I would prefer that the game specific logic is not inside each of these plugins, but rather in feature plugins.
For example for chunk management I'd have a ChunkManagementPlugin, but that would have implementation details for Client, Server and Shared.
- Would I need to separate it into server/client/shared ChunkManagementPlugins, or should I be using ClientState and the like to check which observers, systems and resources I should be loading?
- Does this bloat the binary?
- Would using feature flags to build a server binary be a better approach than separate crates for my use case?
- Should I use separate client/server crates, plugins, and feature flags all at the same time?
Also, I'm really not sure if this is the right way to set up the client and server;
At the moment this is what I'm doing:
| Game mode | ClientPlugin setup | ServerPlugin setup |
|---------------|----------------------|----------------------|
| client | networking, entities | panic! |
| server | panic! | networking, entities |
| singleplayer | entities | entities |
| listen server | entities | networking, entities |
If anyone could tell me how much I suck and everything that I'm doing wrong, that would be greatly appreciated.
For an idea of what I'm doing, here is my ClientPlugin:
From the table, looks like you actually need 2 compile-time configurations: full game and dedicated server.
In full game you can everything: singleplayer, connect to a server or host (I don't think it makes sense to have a separate "only client app" or "only singleplayer app").
In dedicated server you only run server logic.
So I'd probably won't separate anything plugin-wise. It's just easier to read the code when everything is in one place. And for dedicated server I'd go with cfg for server-specific things.
I don't think it makes sense to have a separate "only client app" or "only singleplayer app"
huh yeah that makes sense actually
i think i was originally thinking that i would have a separate launcher to run the correct binary based on what the user wanted to launch, but that wouldn't make much sense since you would have 3 separate binaries doing effectively the same thing
what do you mean by going with cfg?
for the compile time configurations, i obviously want to minimize the dependencies of the dedicated server. should i use feature flags to avoid compiling client logic conditionally?
like a features = ["client", "server"] builds the full game, but features = ["server"] builds the dedicated server? (maybe this is excessive feature flags since i always want to build server?)
ah you mean #[cfg()] my bad
yeah i think i'll give that a go
time for another refactor :D
i think i'll get rid of the client and server crates and have it all in client and server module directories instead, which will hopefully clean up the public api a bit heh
rust lang best lang
I'd go features = ["full"]. If not enabled, it's a dedicated server. To avoid cfg hell.
yeah, i was thinking features = [ "client" ] and just always compile server features
i'm pretty sure that i can get this in a workable state, thanks so much
Dumb question homies--
When I last worked on my project, I had a function that spawned clients after a connection event, on the clients application.
fn spawnclients(
trigger: On<Add, ConnectedClient>,
...
But haven't managed to get this working on newer versions of bevy or replicon. Is this due to changes with triggers / observers, or a change in replicon?
You probably want AuthorizedClient instead. After the connection, client and server exchange protocol hashes before proceeding (can be disabled, but it's by default).
If you don't get an AuthorizedClient insertion, this means the client fails to authorize. You should be able to see an error message.
wanted to see poeple's opinion on this:
VisibilityFilter should not be feature gated by the server feature.
given shared and server code are in seperate crates, with server enabling replicon's server feature, components in shared cannot impl VisibilityFilter due to rust's orphan rule.
But this would be a bit contradictory due to visibility being overall a server only concern
Ah, I agree. Let's move the trait to shared and keep the server logic in server. What do you think?
exactly what I thought
mind if I PR it real quick?
It would be great!
posted a draft PR.
Going to sleep rn, will merge it tomorrow (I requested one last change) π
Merged. I'm planning to draft a new release with accumulated breaking changes once Bevy RC is out.
I'll do 2 releases:
- Targets 0.18 with all the breakages.
- Pure 0.19 RC update.
I really appreciate a new version purely just for the bevy version upgrade
Bevy added BundleWriter which can be used for this.
It's almost identical to my fix, but with a bit different API and the ability to manually call Drop which is great to recover from errors!
Migrated replicon to it: https://github.com/simgine/bevy_replicon/pull/660/changes/9b914d4b21443fbad0b02bb8cbe79b3ae3043056
In lightyear I was adding some special components first (Predicted, Interpolated) which would affect how components get replicated.
In replicon I set Predicted as a marker component but my custom marker write functions don't trigger on the initial replication since Predicted and Position are both part of the message
Any idea on how to solve this?
You might say to make Predicted purely a client component that is not replicated, but then I would still have the issue of: how can I add the marker on a replicated entity before the replicated components are added?
Maybe I could use set_receive_fns and check if the marker component is present in the components to be inserted in the DeferredEntity? (I would need a public method to access that)
Don't you need to write the first values as is, without any history management? π€
And once Predicted/Interpolated are inserted, you treat the current value as the first one in the history.
It's also possible to spawn an entity on the client with a matching Signature and Predicted/Interpolated.
I don't think itβs good to require client spawning for this, but I think the first suggestion above should work. Please, let me know what you think.
few questions before I try visibility rules further:
- is an entity visible when any or all
FilterBits are true? - (i havent tested this) when an entity becomes non visible in the client, does the entity despawn or does it act like a normal client disconnect (nothing happens)?
Well the first value I receive is a Confirmed value from a past remote server tick, ideally I would store it in history and immediately trigger a rollback. When the Predicted marker is present I automatically write these values in history, but here I'm missing the first one.
I do think the first value should also be added in the history
- All bits should be true
- The entity despawns.
Ah, so the client already predicted an entity and on receive you don't want to just replace the value, you want write into history and trigger rollback?
But since client already spawned an entity (before the server), you can just insert the Predicted marker before receive? Or am I missing something?
No i am receiving this entity for the first time, and I want to immediately add the received components to the history since this entity will be Predicted.
What you're saying is that after the entity is replicated, I could add Predicted on the client and use ConfirmTicks to find the remote tick for these components and put them in the history? Yes that could work too
Yep!
Or if Predicted is replicated from the server - the same logic applies. Inside On<Add<Predicted>> observer you can add components into the history.
If you have any ideas how to simplify this - I'm open to it π
Yea I added the OnAdd observer today after noticing that the marker was not triggering on the initial replication. That works for me; the marker thing is already very convenient!
Been trying to port over some older code, and I find no matter what I do, I can't get the protocol hash to match.
Any replication / message at all is giving two different hash values on the client / server, any idea what might cause this?
Is this the full log?
Including the protocol mismatch itself?
Seems like the problem I was having with Quinnet, just now on renet. I know the replication rules & order can affect the hash, but is there anything else I could be messing up to cause a mismatch
You need to log only shared::protocol. Filter the messages by this and compare them.
```2026-04-25T19:22:15.123835Z DEBUG bevy_replicon::shared::protocol: adding client event ProtocolHash
2026-04-25T19:22:15.123926Z DEBUG bevy_replicon::shared::protocol: adding server event ProtocolMismatch
2026-04-25T19:22:15.123968Z DEBUG bevy_replicon::shared::protocol: making event ProtocolMismatch independent
2026-04-25T19:22:15.124514Z DEBUG bevy_replicon::shared::protocol: adding server message MakeLocal
2026-04-25T19:22:15.138272Z DEBUG bevy_replicon::shared::protocol: calculated hash: 14397123909873343244
2026-04-25T19:22:21.092999Z DEBUG bevy_replicon::shared::protocol: adding client event ProtocolHash
2026-04-25T19:22:21.093105Z DEBUG bevy_replicon::shared::protocol: adding server event ProtocolMismatch
2026-04-25T19:22:21.093149Z DEBUG bevy_replicon::shared::protocol: making event ProtocolMismatch independent
2026-04-25T19:22:21.093815Z DEBUG bevy_replicon::shared::protocol: adding server message MakeLocal
2026-04-25T19:22:21.108571Z DEBUG bevy_replicon::shared::protocol: calculated hash: 899627540550343768
Strange. And it happens on the same machine?
Yeah, same VScode too.
It's quite unusual, with no replicon messages / replication events the hashes match. I assumed I set the server / client up wrong, but if that were the case the hashes shouldn't match in the first place.
Are you sure replicon versions are also the same?
Also the type definition should also be the same. I.e. your MakeLocal should be defined in a shared crate.
@spring raptor i am still on 0.39.2 is this following panic already fixed in newer releases?
thread 'Compute Task Pool (0)' (910073) panicked at /Users/stephan/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bevy_replicon-0.39.2/src/server.rs:358:25:
messages from client `1426v1` should have been removed on disconnect
stack backtrace:
0: __rustc::rust_begin_unwind
at /rustc/fcd630976c460c819c4bbcaf107d0c94501205d8/library/std/src/panicking.rs:689:5
1: core::panicking::panic_fmt
at /rustc/fcd630976c460c819c4bbcaf107d0c94501205d8/library/core/src/panicking.rs:80:14
2: bevy_replicon::server::receive_acks::{closure#0}
at /Users/stephan/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bevy_replicon-0.39.2/src/server.rs:358:25
3: <core::result::Result<bevy_ecs::change_detection::params::Mut<bevy_replicon::shared::replication::client_ticks::ClientTicks>, bevy_ecs::query::error::QueryEntityError>>::unwrap_or_else::<bevy_replicon::server::receive_acks::{closure#0}>
at /Users/stephan/.rustup/toolchains/nightly-2026-01-02-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/result.rs:1622:23
4: bevy_replicon::server::receive_acks
at /Users/stephan/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bevy_replicon-0.39.2/src/server.rs:357:61
5: <bevy_replicon::server::receive_acks as core::ops::function::FnMut<(bevy_ecs::change_detection::params::ResMut<bevy_replicon::shared::backend::server_messages::ServerMessages>, bevy_ecs::change_detection::params::ResMut<bevy_replicon::server::client_pools::ClientPools>, bevy_ecs::system::query::Query<&mut bevy_replicon::shared::replication::client_ticks::ClientTicks>)>>::call_mut
at /Users/stephan/.rustup/toolchains/nightly-2026-01-02-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:166:5
6: <&mut bevy_replicon::server::receive_acks as core::ops::function::FnMut<(bevy_ecs::change_detection::params::ResMut<bevy_replicon::shared::backend::server_messages::ServerMessages>, bevy_ecs::change_detection::params::ResMut<bevy_replicon::server::client_pools::ClientPools>, bevy_ecs::system::query::Query<&mut bevy_replicon::shared::replication::client_ticks::ClientTicks>)>>::call_mut
at /Users/stephan/.rustup/toolchains/nightly-2026-01-02-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:298:21
7: <_ as bevy_ecs::system::function_system::SystemParamFunction<fn(_, _, _) -> _>>::run::call_inner::<(), bevy_ecs::change_detection::params::ResMut<bevy_replicon::shared::backend::server_messages::ServerMessages>, bevy_ecs::change_detection::params::ResMut<bevy_replicon::server::client_pools::ClientPools>, bevy_ecs::system::query::Query<&mut bevy_replicon::shared::replication::client_ticks::ClientTicks>, &mut bevy_replicon::server::receive_acks>
at /Users/stephan/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bevy_ecs-0.18.1/src/system/function_system.rs:908:21
8: <bevy_replicon::server::receive_acks as bevy_ecs::system::function_system::SystemParamFunction<fn(bevy_ecs::change_detection::params::ResMut<bevy_replicon::shared::backend::server_messages::ServerMessages>, bevy_ecs::change_detection::params::ResMut<bevy_replicon::server::client_pools::ClientPools>, bevy_ecs::system::query::Query<&mut bevy_replicon::shared::replication::client_ticks::ClientTicks>)>>::run
...
First time I see it π€
How you reproduced it?
0.39.4, both, one as a dependency for my main crate and one that comes with renet_replicon I believe.
Annnd I have a file called 'protocol.rs' with a ReplicatedPlugin for replication and client/server triggers.
a remote client rage quitting their game is enough
Could you share a minimal example?
lol my game is far from minimal
sorry
I know, the message wasn't addresed to you, don't worry π
sry
Let me take a look. Looks like the backend didn't clean messages. It's bevy_replicon_renet?
I use panics only for things that should never happen and impossible to recover. While this shouldn't happen, it's definitely a recoverable thing, so I'll switch to logging and try to fix the bug in the backend π€
bevy_replicon_renet
yes exactly
Working on it
@wraith copper drafted a patch release
Whole thing is quite minimal. Sorry if I've done anything terribly wrong π
seems to work fine, thanks for this! you rock π
I mean the code I c an compile and run on my machine π
From screenshots everything looks fine, though.
Do examples from the bevy_replicon_renet repo work for you?
Only through using Cli, either I've done something terribly wrong; or whenever I break them off into new files (Client, Server, Protocol etc) do the hashes start to get weird.
Can send you what I've got if you'd like, but I'll probably just ditch protocol matching if I can still get functionality without
This will trigger the problem because ReplicatedBool is not shared across 2 app.
Despite they look identical, for rust it's 2 different types, which will cause the hash to differ.
Ah, I still had the error when I had Replicatedbool in Protocol.rs, I just didn't wanna send three files.
pub struct ReplicatedBool(bool);
in newfile.rs
use protocol::ReplicatedBool;```
in both of the files, would this still cause the mismatch?
It should be in a crate that is shared for both binaries
Hello! is there ability to make visibility filter like blacklist using bevy_replicon?
Yes, just set your filter to enable visibility only if specific component is present.
You can also manually control what is visible.
What about per client visibility?
For a scenario of only replicating entities that are within some distance of each client
Filters are per-client. You insert a component on the client and on the entity.
Then, there's no way to make or filters?
I had a brilliant opportunity to separate my filter in this way
Not with the current API. I'm open extending it, but you can manually control what is visible without using visibility filters.
Filters built on top of this "manual" API.
Visibility as masks for a client.
what's filter bit?
It's a bit associated with a data. Component or entity. You register it like this: https://docs.rs/bevy_replicon/latest/bevy_replicon/server/visibility/registry/struct.FilterRegistry.html#method.register_scope
But in the method I linked previosly you can see a full example.
Maps the VisibilityScope of each filter to a FilterBit.
Hm okay, thank you
oh, i kept reading AppVisibilityExt as AppRuleExt lol, docs blindness
I think it would have helped me if there was a section in the root crate doc thing that was about replication visibility and included all of the factors. like
For component Foo to be replicated:
- Client is connected
- Client entity on server has
AuthorizedClient(added by default) - Game entity has
Replicatedcomponent - Foo is registered for replication via
app.replicate::<Foo>(); - If
Foois registered withreplicate_filteredthe rules must pass - Any remote visibility filters registered via
add_visibility_filtermust be on the Client entity and must pass - Any manual bits set on
ClientVisibilityon the Client entity must be enabled - Allowed by
Prioritization
Or that was use-case based. Like
- Do you want to enable replication for a server entity? Add
Replicatedcomponent - Do you want to replicate entities to only some clients? use
app.add_visibility_filterwithScope=Entity - Do you want to replicate a component only if the entity also has another component? use
app.replicate_filtered
There is a lot of moving parts of replicon that is hard to grasp even re-reading the docs multiple times. Maybe I'll try make a diagram or something to help me understand better
Aside, but did I miss any other ways of doing filtering in my list above?
Yeah, your understanding is correct. But replication rules are a bit more complex. For example, replicate_bundle::<(A, Foo)> will require A also to be present.
I'm open extending the docs. A TL;DR like this would be nice.
On second thought, this will require some careful wording. For example, Foo should match a replication rule. Not sure if makes sense to duplicate information about each replicate_*. After 0.19 we'll get variants for resources for these methods.
Prioritization actually affects only mutations. So I don't think it makes a lot of sense to include it.
Filtering is something that is needed only if you use it. If no filters are present - everything is replicated.
And in the docs I explicitly mentioned that filters work as logical and
So I'm open to restructuring, but this requires careful adjustments. Maybe not in the form of a list π€
I'll draft a new release for 0.18 and the next RC after I merge https://github.com/simgine/bevy_replicon/pull/684
@astral cipher You were interested in this ^
oh cool didnt know this popped up, ill take a look
I like it, specially it hitting before 0.19, i thank you two.
when I have time to get back to my project I will try it out
Great, I'll draft a new release tomorrow (rn it's late night for me)
I'm a bit confused, so client or server can send a broadcast message, and all clients and server receive it?
not exactly. this brings up the point that arguably, broadcast is a bad name for it (what you are talking about is SendMode::Broadcast, which is different)
the idea behind it is to permit shared logic chains allowing for better prediction, as before a client could not emit FromClient<>
Oh, so a client can "react" to it's own sent messages
Yeah, this is particularly useful for the client to predict based on their inputs, by using shared code instead of having to duplicate an authoritative system
Makes sense!
I was experimenting with adding a generic bool to some systems like IS_SERVER so I could do specific operations only when it's running in the server, but it wasn't that great tbh.
Do you have other name suggestions? Because I agree that it's not obvious from the name and kinda collides with SendMode...
I don't have any better ideas :(
@astral cipher I think it will be less confusing if we rename SendMode::Broadcast into SendMode::All π€
Alternatively you can call Broadcast either Raised Relay or Signal or Undirected
few names that came to my mind
Naming is the hardest part in programming π
Opened https://github.com/simgine/bevy_replicon/issues/686
We recently merged #684 and while I like the functionality, the naming is a bit confusing. It collides with SendMode::Broadcast and sounds like the message/event will be send to all clients. What i...
Let's hold the release until we figure out the naming. It would be annoying to introduce a feature and rename everything related to it in the next release.
And here a real use of it for lights, that not only correctly allow to render lights, but also prevent extra bandwidth and hacking that will be allowed if therre was just bigger range of PVS entities replication
pretty cool -- bit of a tangent tho, do you also replicate the opaqueness of walls not in view?
also, after 0.19 I think there are some things I would like try to improve:
- visibility rules not working on replicated client entities (correct me if im wrong)
- allow adding replication rules + events/messages other than with
App. e.g. wanting to replicate components declared at runtime
About the first one - yep, but I'm not sure how we can allow this. Filters check component on client entity and player entities. But any ideas will be welcome. Preferably as an issue first, so we can discuss.
You can also toggle visibility manually for specific entities, which could be done for client entities (if you mark them for replication).
About the second one - also not sure how, protocols should match on both client and server.
But sure, I welcome any improvements.
Filters check component on client entity and player entities
just to be clear, what is the difference between a client entity and player entity here?
If you mean when checking if it is visible viaVisibilityFilter, both are passed as reference, so it should be fine, right?
About the second one - also not sure how, protocols should match on both client and server.
Is imposing a pinky promise that the user will not alter the protocol when RepliconChannels are being used too much?
just to be clear, what is the difference between a client entity and player entity here?
Client entities has client components that is used to filter and decide what to compare with.
The whole idea of visibility filters is to insert a component on player entity and client entity which will evaluate wheter the player entity is visible to a client entity.
Not sure how player and cliententity could be the same thing.
Is imposing a pinky promise that the user will not alter the protocol when RepliconChannels are being used too much?
I'm not sure what you mean. Protocol should match exactly, otherwise replication won't work. You can't add new events on the server, because the same thing should be done on the client and in the exact same order.
Not sure how player and cliententity could be the same thing.
I see what you mean, I thought you were calling a client entity also a player entity interchangeably. I will try to examine it further, but I think it is an inconsistency how client entities cannot have visibility rules applied to them as well. I think visibility filters might have value for replicated client entities here, e.g. secret components (like IP) for a given client should be exposed for admins only
Hopefully this does not bring a change that requires another big change to the visibility system.
Protocol should match exactly, otherwise replication won't work.
I agree with you 100%. What I imagine is both the server and client loading mods that register components at runtime duringapp.run(), and being able to add the exact same messages on the exact same order. This would occur before any connection -- and never during one
In case there is a protocol mismatch, the connection should fail, right?
Yes, you can replicate client entities to clients, it's just visibility filters expect comparison against client entities which determined by components on them.
I'm open to experiments with the API, so feel free to suggest alternatives. You can even write your custom visibility system inside your crate since these scoped filters built on top of the public low-level API. This way you can iterate quicker.
Yes, if you do this before connection in deterministic order - it won't break.
But I was imagining that mods just access App during plugin building and register the necessary things.
For example, you have a bunch of scripts and you expect them to have something like setup(app: &mut App) - during the app initialization you load the scripts and simply call this function. The scripting solution I'm currently working on will support this.
But I see what you mean, sometimes it might be convenient to do this at runtime π€ Like you go into some mods menu and toggle a bunch of scripts... We should support this, I agree with you.
But we need to come up with some clever ID generation to avoid relying on the registration order.
I will surely take a look into the visibility system
and, just to add to this, if we are adding to the protocol at runtime, care should be taken to reset or subtract from the protocol. I also like the ID generation, it could be a first step when approaching this.
I will open issues with what we talked here after 0.19 and I will look into it.
Thanks
Is it possible to replicate some components less frequently? I have some components 'Score' that can be replicated less frequently. They are on an entity that is otherwise replicated frequently
We had it, but at some point I realized that it wasn't implemented properly π’ I want all my features to be 100% working without bugs.
Right now you can do this only per-entity. But I want to bring the per-component tick rate back: https://github.com/simgine/bevy_replicon/issues/638
Sounds good, thanks π