#bevy_replicon
1 messages ยท Page 11 of 1
just bools
Maybe grid as components would be more useful? This way you could replicate only actually changed data.
i don't know if that's actually more performant
Only one way to find out ๐
yeahhhh
well currently i just need to update a grid of bools
as opposed to querying the coordinate, updating the entity, sending to the client the entity with the new state
i've been weighing it in my head all day and it just doesn't seem worth
working on the renet build currently
It will require additional lookup for coordinate - entity, but other then that - it should save a lot of traffic. And traffic is the most valuable resource.
grid of bools with mostly repeated 1s and 0s seems very easily compressable
or pretty cheap even without
it'd have to be benchmarked
but it also works well enough
i feel like all of game dev is just "works well enough" lol
Yeah, always focus on the game. You can optimize it later.
The game is the hardest stuff and the requirement of "make it fun" is super vague
So it's better to figure out what is fun and then iterate on performance
For crates it's different. You have the requirements and you need to choose the right architecture.
I moved grids to their own components and had them be handled with events and it worked perfectly, although I'm still getting the "datagram too large" issue when spawning ~20 ants. This is after setting up replicate_with for transforms
Probably the data still exceeds the maximum size somehow.
Try to switch to renet for now.
Alternatively, I can quickly patch replicon by replacing this constant with something that quinnet accepts ๐
https://github.com/projectharmonia/bevy_replicon/blob/927a8bfb3750635da037b0a260166c295ca00738/src/server/replication_messages/mutate_message.rs#L227
what was the size quinnet needs again? I can just use a fork
I tried renet but couldn't get it to work, skill issue I think
renet replicon has a lot of similar types as replicon, renet ClientId vs replicon ClientId, ServerEvent, etc
quinnet felt easier to use
I have a better idea.
Check what value is used for https://docs.rs/quinn-proto/0.11.8/quinn_proto/struct.Datagrams.html#method.max_size and I will provide a patch release of replicon with a more conservative value for now. With the next release I will fix it properly.
You don't have to touch them at all. You only need to initialize RenetServer or RenetClient. And just use everything else from Replicon. Except for disconnection, you need to call a method on RenetServer.
1162
i figured it out 
Could you open a PR to adjust the constant? This way I will be able to publish a patch-release.
It's not a proper fix, but an acceptable workaround. We definitely should allow backends to set this value.
yeah i see the TODO
made pr, although might need to adjust the tests
why is this asserted as false?
Because the message is already full. We can adjust the algorithm to handle adding zeroes, but we never face it in the code, all entities have size > 0
@hexed cove could you double check that the PR solves your issue?
Just use patch because you want quinn to refer to the correct verison: https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html#testing-a-bugfix
testing currently but some changes with serverevents is causing issues
apparently it was changed to triggers or something?
going from 0.29 -> 0.30 for replicon
Ah, yes. It's a recent release. Check the changelog: https://github.com/projectharmonia/bevy_replicon/blob/master/CHANGELOG.md#0300---2025-02-04
Don't forget to update the quinnet as well.
yup
getting this error when setting the visibility with the observable
removing the visibility, it does appear to be fixed
I do get this issue when summoning like 10k ants
so there is a limit lol
it keeps running, just gets laggy
Could you elaborate on this one?
The panic screenshot is too short to reason about.
Ah, I see what is happening here. Basically, when a new client is connected (emitted by backend) I create an entry in connected clients. But looks like your call is calling earlier.
Make sure you setup your plugins after replicon's. I think observers should be triggered in their registration order.
they should be registered after already?
Strange, I would expect this function to run first:
https://github.com/projectharmonia/bevy_replicon/blob/927a8bfb3750635da037b0a260166c295ca00738/src/server.rs#L177
Well, as a temporary workaround, you can create your own event and trigger it from your own observer. This way you will be able to properly set the visibility. But it's junky ๐ค
@hexed cove Better, create OnAdd observer for your teams and setup the visibility there.
But it's strange, I thought that observers fire in their registration order ๐ค
they do
client connected -> setting visibility
well they do in this case, Idk if it's actually documented
On the screenshot I see logs only from Replicon, they are expected.
the bottom log "setting visibility" was placed by me
But judging by this panic, your function is running before.
Got it, my fault actually.
ConnectedClients are updated immediately, right.
But ReplicatedClients updated from this trigger: https://github.com/projectharmonia/bevy_replicon/blob/927a8bfb3750635da037b0a260166c295ca00738/src/server.rs#L187
Which triggers: https://github.com/projectharmonia/bevy_replicon/blob/927a8bfb3750635da037b0a260166c295ca00738/src/server.rs#L212
Let me put a quick fix.
and they're ordered in a way that causes a 1f delay?
They fire in the same frame, but since you observe for ClientConnected, your function was called earlier.
Oh I see
ClientConnected triggers both my observer and the replicated clients observer
that then generates it
Yes, and yours fire earlier due to how commands are flushed
Won't be an issue anymore :)
Once koe review both PRs, I will draft a patch release ASAP.
In the meanwhile just these apply changes locally.
Sounds good
:(
trying to replicate the grid triggers this again, so it still happens with one large entity
Interesting. Could you try to print the used value and the max size?
I would include this information in the error message. So find the string, patch the crate (maybe PR to the author, good to have) and try it.
do you know why these logs might not be showing up?
working on it
Not quite sure ๐ค I would assume that they are not called. Double check if you have only a single registration for the TeamVisibility. If you have a registration with more components and TeamVisibility, it takes priority.
For example, (Transform, TeamVisibility) will be picked over regular TeamVisibility rule.
oh
what if you want a bundle + replicate_with
like i dont want to replicate every transform
but I also want to use replicate_with
like replicate_group_with<(Ant, Transform)>();
also sadly doesn't work
You can do custom groups. What you pass in replicate_group_with is a bundle.
Oh I see
(Component, Component) is a bundle. Just create your own struct instead and implement ReplicationGroup
yup, missed it in the docs
I need more info on this ^
I see it now
serialization fns
Wait, but you said it worked ๐ค
serializing works, this happens when attempting to deserialize
Interesting, are you sure that functions are the same on client and server?
The panic means a problem with registration
what function id?
When you register a replication group or component, you create its function IDs internally.
When transmitted over the network, we include this ID.
Error means that there is no such function registered for such ID.
Let me improve the error message, so we could see what is happening more clearly.
maybe my compression thing is bad
I don't see an issue with the provided code ๐ค
Let's work on one thing at a time.
just gonna double check if transform works properly
Let's take a look at the datagram limit (I suspect that it's dynamic) and I improve the panic.
Opened: https://github.com/projectharmonia/bevy_replicon/pull/409
Should help us a bit.
Datagram Size 500745, Max Size Some(1414)
Interesting. So you have an entity worth of 500745 bytes
yup
I think so
Okay, let's back to compression. I guess if we solve this problem, we won't need to worry about it.
Huge ๐
testing this
Now I wonder if it worth even merging something like https://github.com/projectharmonia/bevy_replicon/pull/407
We don't know minimal possible size.
probably not
I bet author will appretiate if you submit the improved error message.
No in this form I mean, but put this as part of the error!.
I couldn't find a clean way to add it as part of the error
the error prints the error from quinn
Ah, I see :(
maybe a match to send a unique error for specifically TooLarge
and everything else uses the original
Ill submit a pr
Maybe just edit the display for the error ๐ค
I.e. include more info in the TooLarge and adjust Display impl. But it's not as simple change as I thought. Maybe worth keeping as is.
Yep, this is why I suggesting to include this data in the error to display it properly
But the author might dislike the increased error size
181 is a huge number. It means that you have 181 functions registered. Which is unlikely.
This could mean that we are trying to read some garbage.
On deserialization
Let me think
im gonna check if transform works
I think we have tests with custom serialization, but yeah, double check just in case.
Ah, I see the issue
You replacing the message here instead of appending to it
We trying to not allocate for performance reasons, this is why I pass the buffer ๐
Yeah, it's a possible pitfall. But it's very important to keep replication fast.
hmm
well
this works for a single tick
it fails to decode, but i think it successfully decodes a single time
because the correct vision shows up for a single frame
The decode most likely tried to decompress the whole buffer
Serialize the buffer size first
Then on deserialization check the size first and then pass a slice of such size to zstd
that makes sense
it works but it does eventually grow too large
compression isnt enough ๐
Damn. But at least we strees-tested Replicon ๐
You need to make it deterministic
deterministic ants?
although my end goal is multiplayer ant combat so serverside visibility is my current goal
Yeah, just don't replicate all data. Replicate only necessary data to reproduce the results on clients.
Am I understand correctly that max datagram size is per-connection?
Ah, I mean in the provided API. You get the value from the connection, right?
Yup
entities will never be able to be split across packets right?
Yes, it's by design :)
Otherwise the game logic might break based on network conditions
would it be possible to pass larger entities through a different channel type?
Right now there is no such functionality :(
But even if we do, looks like single entity is already bigger then the datagram size.
well
transmitting grids through events does work
so I think there's some way to make it work through ordered or unordered channels
Replication utilizes both channels. Insertions, removals, despawns and spawns go through reliable channel. Mutation go through unreliable channels. It's because there is no sense in resending the old value, if the packet gets dropped, we send the latest value.
If you want to go this route, the events is the right tool for it.
I agree. Didn't expect someone to transfer that much ๐
Switched to postcard. I described the reasons in the PR:
https://github.com/projectharmonia/bevy_replicon/pull/410
@hexed cove the release with triggers fix is on crates.io.
I will provide a fix for backend message size later. But In your case you want to use events anyway.
Highly suggest to somehow minimize the amount of transferred data and more rely on determinism.
it might be worth adding a fn or recipe in the docs for replicating/events with custom serialization + mapping
๐ค could we have bevy_replicon no_std?
I was thinking of writing a UART or ESP32 wifi transport anyway for fun
Agree. We currently have only for just custom serialization without mapping, right?
A PR would be highly appreciated. Or do you need help to figure out how to do so?
I will try tomorrow
bc it's something I'm doing
which option do you think makes more sense here?
I'm curious about this as well ๐ค
But I have small experience with no_std from work, we provide minimalistic hal for our cores.
But this sounds like fun to try.
From what I understand, we need to replace all std with core, switch to bevy_* (or wait for #1303128171352293410 message), add std feature and disable std by default for bytes and postcard. And the figure out what to do with the buffer I allocate to prepare messages (replace with alloc?)
sounds about right to me, but I think dependencies requiring std may also be an issue. I'm not sure what replicon depends on. alloc shouldn't be a big deal, since IIRC bevy_ecs requires alloc anyway.
oh, and Instant will be a problem. this is the main issue I had with making no_std aeronet_io
Here under example I would write "Serialalizing only translation from transform:" and add one more example with something like "Serializing only entity from componentA:" and add another example.
https://github.com/bevyengine/bevy/tree/main/crates/bevy_platform_support
Looks like this handles Instant for you
We are very tiny on deps, they all support no_std, except bincode. But it's fixable with postcard.
Ah, yes. But in Replicon we don't need Instant.
that's great then, I thought you might have used it for something in replication. I use it in the IO layer.
Lackily I use it only in benchmarks ๐
I think main problem will be Bevy. We might need to support master :(
I mean it's fine, but we won't be able to release it to public
yeah, 0.15 isn't no_std right now I don't think
this is more of an experiment than anything, at least until 0.16
Yeah, I probably could create a branch ๐ค
I'm gonna make an aeronet no_std testing branch, and try to port aeronet_io over to no_std + add esp32 wifi support, if that's even possible
and see if I can port the transport over, which might be more challenging
are you pinning to a specific bevy rev?
How practical it is? Totally unpractical.
But how fun is it? Very fun ๐
It wold be awesome! It's cool that you have esp32. I will try to do the same with Replicon.
Will start with the current master. But I need the postcard PR to be merged. And maybe for BashRat to port bevy_internal.
So you can start, I will catch a bit up later.
Replicon should be easier to migrate since we basically just read data from Bevy and feed it back.
are you building a multiplayer project w no_std?
Did you look at how other options, like say bitcode or speedy, compare?
I trying to build a regular game: #1264625779296174110.
But Bevy community recently expanding no_std support. I wanted to join the hype, but I needed a backend that can utilize that.
@raw idol mentioned in #networking that he have esp32 and considering supporting no_std in aeronet, so how can I resist now? ๐
Bevy community
You say like it's not just @twilit islanddoing 90% of the work ... I'm sure they would appreciate seeing a no_std networking crate tho
I evaluated bitcode. It packs very efficiently, but without streaming support, all data will be byte-aligned, which defeats the purpose. But I keeping an eye on it, will be happy to migrate in the future.
I didn't try speedy, I wanted something that can be used with serde since it's what Bevy uses.
I'm gambling that once 0.16 launches and people can just do:
#![no_std]
use bevy::prelude::*;
That the community will take on the challenge and my work will become competitively small
I thought they said they added streaming support in 0.6, tho I never actually checked since I already rebuild bevy_bundlication on top of replicon at that point ... And yea I guess using custom derives like speedy (or the optional, but much faster mode for bitcode) wouldn't work great, maybe one day when we get compile time reflection :')
That would be pretty neat ... I wonder if we can also no_std most of the ecosystem crates ... I feel like I could probably no_std my rollback and input queue stuff, since they largely only use the ECS and some alloc ๐ค
What I like about bitcode is that it supports custom derive that we could use to more efficiently pack for replication + supports serde for components.
That's exactly my thinking. Not every community crate will be simple, but Bevy already has its own ecosystem for allocation, threading, etc., so a lot of what we'd rely on std for we already just let Bevy do for us lol
I think they actually dropped support for those attributes in the custom derive ๐ค
The "rewrite" PR had this comment: "doesn't require any hints (determines them at runtime)"
Yea, we'd really only need to inject a bit of std functionality where necessary, but can otehrwise interact with no_std APIs. In the case of bevy_replicon this would largely mean that you might need std for a transport (which is easy to replace with something custom when lacking no_std), but the rest could be no_std as long as all the bevy features it touches are
Looks like on the serialization benchmark (if we just look at bandwidth) the only ones that really consistently beat bincode are postcard, bitcode and whatever dlhn is ๐ค
I mean they provide more efficient bare-serialization via https://docs.rs/bitcode/latest/bitcode/fn.encode.html / https://docs.rs/bitcode/latest/bitcode/fn.decode.html. Their trait implemented for built-in types and requires Encode/Decode derive for custom types. And with serde feature they provide https://docs.rs/bitcode/latest/bitcode/fn.serialize.html / https://docs.rs/bitcode/latest/bitcode/fn.deserialize.html. With streaming support we could use encode/decode for our replication messages data (ticks, manual arrays) and serde for components. So users won't need to use any custom derives, just regular Serialize/Deserialize.
Yep, bitcode is the best choice if you don't care about streaming.
I also considered dlhn, but it's quite new, I decided to wait a bit to see if it matures.
@raw idol what do you planning to use for transport? Something like https://github.com/embassy-rs/embassy ?
Yeah:
embassy-executorembassy-timeesp-hal-embassyesp-wifismoltcpaeronet_io+ a custom layer on top
I assume
working on the pr now, can't figure out how to map the entity without having access to the component
since we're trying to only serialize/deserialize the entity inside
Great!
You deserialize the component (MappedComponent in your case) directly first, the just call the map_entities.
Just merged migration to postcard ๐
It's the best option for now. Looking forward to streaming support in bitcode.
@hexed cove I recently merged a change to serialization. But don't worry, I will updating the example, the changes are trivial.
Basically, the changes :
yup, just drop in from bitcode
And Cursor now just Bytes
this probably works i think
ill test it
oh wait i see an isseu
Yep, looks good.
But it's actually mirrors default ser/de functions:
https://github.com/projectharmonia/bevy_replicon/blob/postcard/src/core/replication/replication_registry/rule_fns.rs#L212
https://github.com/projectharmonia/bevy_replicon/blob/postcard/src/core/replication/replication_registry/rule_fns.rs#L195
So maybe add an extra field to the component and write a comment that we skipping the filed for replication? I.e. just to showcase a somehwat real use case scenario.
somewhat real use imo would be to just
for compression or something
(my usecase)
if you just needed to exclude fields you could just seperate the component xd
but sure
oh thats why its called serde

I agree ๐ just don't want to pull extra dependency.
because this issue
no component to call map_entities on
unless I create a new one
You can create MappedComponent with the default value.
But not very practical ๐ค
What if we create a function compress inside the example that actually does nothing? ๐ค
yeah real usecases would be like
compression or if you want to manually do bit manip to like
hyper optimize
Yeah, and in the example it's not necessary should be a real function
We can hide it by prepending #
that makes sense to me
So just copy what you have for you game and replace zstd with dummy compress๐
the compression does use cursors, but i think it can use any thing that impls Read
compression also requires you pass the size in as well

ill try writing something up
Bytes implemet Buf which is cursor-like
And you can call https://docs.rs/bytes/latest/bytes/buf/trait.Buf.html#method.reader
Read bytes from a buffer.
Serialization looks correct. Although, I would pass Vec into compress by mutable reference, will look a bit nicer.
But in deserialization you need cursor on the latest release
And Bytes on main
that would be for inplace right?
Yeah
This function doesn't do anything anyway ๐
Thanks to postcard I switched to varint encoding for ticks: https://github.com/projectharmonia/bevy_replicon/pull/411
We use 32-bit ticks.
With bincode, ticks would start to use 5 bytes after 2^16 (which happens after ~18 minutes with 60 ticks/s).
But with postcard it happens only after 2^28 and with 60 ticks/s it starts to take 5 bytes after ~51 day.
So don't run your sessions for 51 days, otherwise messages will start to take 1 more byte ๐
do player/server only observables exist
workaround, use some sort of marker through resources or state and check in the observable
conditional observables should probably be added to bevy
ReplicationGroup doesn't exist :(
oh it's GroupReplication
mb
if an entity has GridCoords, VisionRange, and TeamLabel, they should all be replicated in the same entity right?
for some reason these components go missing on the client though
no error on the server
maybe they aren't added at the same time
on insert doesn't work either, maybe it's added one by one
Triggering it every time any of the 3 get added, then returning if it doesn't have all 3 is a working solution, but a little junky xd
Yeah, I also looking forward to observer conditions!
yesh
currently I can check the resources to see if it's server/client as well
there's workarounds but it's cleaner w observer conditions
Interesting ๐ค
We insert components via commands. And flushing happens when we done processing entity.
So I would expect all insertions to happen first and then your observer should run.
It still create archetype moves, but I just waiting for https://github.com/bevyengine/bevy/issues/10154
Yep, that's what I do as well
but they're registered seperately
you're saying they should all be inserted even with separate registerations?
Yes. So internaly we call commands.entity(entity).insert(component), but flush when all components for entity is inserted, no matter if they grouped or not.
But maybe I misunderstanding how obsevers work ๐ค
I would expect them to be called after the flush
I can double check my code tomorrow, but all three of those components should be replicated
Could you do commands.entity(entity).log_components inside your observers?
it lists the components in the error I think
GridCoords, Replicated, VisionRange, ConfirmHistory
but I can do that tomorrow
Hm... The error says that the components are present
Except TeamLabel
And that's the one you insert later with events, right?
Weird, for some reason you have other components inserted and this one missed ๐ค
Are you sure that you don't have an entity with these components and without TeamLabel?
Good night ๐
Yeah, I also sometimes like this ๐ข
they should always have vision range
i only spawn vision range in 2 places and both have teamlabel
Somehow you don't have TeamLabel in the trigger, not sure why.
all components on an entity need to be sent together right?
We send all components on an entity together, yes
And insert them via commands
And then flush them.
Basically commands.entity(entity).insert(a).insert(b) and then flush.
weird
hello ! just to confirm, its not currently possible to replicate the whole Transform for insertion, and then only the translation ? im looking at .replicate_with(), but it seems to only allow the DEserialization to be different on insert and in place, but the serialization we can't choose ?
if i understood correctly then, the best way would be to replicate with Transform with only the translation, and on replicate a InitialTransform to set it initially, and after only the translation is sent.
this is for minimizing the server egress.
thanks !
I don't think this is currently possible. I'm not entirely sure if it would even be possible to add this without essentially just doing all the work for delta compression. The server can't just send the value once, since it might get lost, but then replicon also has the init messages which I think contain newly spawned components so that might be relevant ๐ค
Yeah, it's not currently possible.
But you can do a workaround - create your own Rotation and Scale components that updated only once on Transform insertion and replicate only translation for Transform.
If you do this, don't forget to override the in_place function.
I recently updated doc examples to showcase it: https://github.com/projectharmonia/bevy_replicon/pull/412/files#diff-ca6867deb8d662237ab6da10d61c239e34dc35c3b2d957938a71f15bc4b6413fR98
We switched to postcard on the main, but the idea is the same.
oh yeah thats good too ill try. honestly im even wondering if i can just not even replicate my troops and projectiles positions, even with this method to only send translation. a 5min game goes from 2mb to 5mb, which mean practically 2x more expensive games to host. this amout seems normal ? here's a peek of my game, do you think its reasonable to not replicate movement ?
thanks for the answers !
The game looks cool!
I think you can ๐ค Just send the actions, but make them do nothing on client and replicate actual game state from the server.
thanks ! ill continue to not replicate transform then. here's some test with spawning multiples buildings with multiples troops etc:
no transform: 2mb
initialtransform: 5mb
transform: 11mb
running a game without doing anything, so nothing is being replicated, no event etc, its still 0.9mb. looking with wireshark there's a lot of packets every seconds of len 68 or 72, i guess keep alive / tick, but it seems a bit excessive for a idle state or its normal ?
if you have some experience with bandwidth usage in games, do you find these numbers normal for a game of my type ?
thanks !
Yep, it's keepalive. It's a know behavior since the original renet: https://github.com/lucaspoffo/renet/pull/156
Replicon sends packets only if data actually changes. If there are no changes, we don't even send ticks.
As for traffic, I can't tell, simply don't have experience to compare. But if you are optimizing, maybe you would like to try the latest master? I did a bunch of optimizations to reduce the packet size recently.
Use patching in Cargo.toml since backends rely on release version.
@thorn forum you can also check what replicon sends by enabling tracing
RUST_LOG=bevy_replicon=trace cargo run ..
thanks for the everything!
I'll try master.
I already tried tracing and there only tick that's changing !
I don't know much about the inner protocol of renet and networking library, but I would have thought a idle game would use less bandwidth.
Then it's good, it prints the informatioon about sending bytes if there is data to send.
I agree. Ask about it in #1302745044393918646.
Let me know if you need any help with the latest master.
Yes the renet ack protocol is a naive design. It really needs a rework (or at least a full analysis with improvements), but I don't have the time to work on it.
thanks, ive learned a lot i understand now how use the Fncs config. but ill just stick to not replicating positions and timer for max efficency.
so its normal, thanks for the answer. i didn't try on web with ws, but afaik renet2 still use renet in the ws connection ? i may try aeronet which use quic on native, and just ws for wasm.
Looks like this was also merged, nice!
Yep, we're just gonna need some better UX on top now, like disabling hierarchies and the like
hi again, what happens on the client when is it disconnected from the server ? like does it modify/remove some components ?
on the end screen im still showing the game in the background but stopped, and when the server quit and so the client are disconnected, multiples things are modified, including non replicated component, but these are maybe modified indirectly through my systems.
example here. the serve quit 2s after the game ended, to demonstrate the change when the client are disconnected. at the end the health bar are blank, and the animation plays again. i struggle to find the cause.
i dont have any .run_if(client_connected) systems too
it seems that some replicated component are re added to some entities ? i have a Added<Troop> for new troops, and it get triggered on disconnect
Yet another reason to do With<Troop>, Without<WhateverIsMissing> (or just using required components or hooks/observers with insert_if_new)
yeah I'm gonna use observer + required component. I was using added to do multiple things at the same time and especially being able to run it after another system that add a component, with observer I can't order them.
I didn't expect Added to be re triggered
Hm... Replicon doesn't do anything on disconnect.
It just stops sending data.
I like the art! ๐
What conditions do you usually use? Maybe there some .if(server_or_singleplayer) that start evaluating to true when you get disconnect? ๐ค
Yes renet2 uses the renet protocol in websockets, but I downgrade all channels to unreliable which avoids extra overhead (beyond the baseline excessive ack packets).
Keep in mind aeronet has a custom auth protocol that is not battle-tested like netcode (I think? or is there no auth protocol at all?).
hm weird ๐ค
thanks ! it's not from me tho ๐ I was wondering which art to use between this one and the last screen record I sent
oh yeah maybe, I didn't though about "server_or_singleplayer" evaluating to true on the client side lol...
hm thanks for the info !
I like the first one a bit more since I'm more into pixel art ๐
That most likely it. If you have any idea how to properly deal with it, let me know
yeah same it has its own style
I'm may be missing a use case, but what about also including the repliconserver in the run condition?
for example, if there only a client but no server resource, it's a client even if it's disconnected. but if it has both a client a server resources, use the same logic as now ? it would be more logical imo, but I'm probably missing other things
In Replicon server and client always exist. But maybe worth reconsidering this pattern? ๐ค
I have checks for existence only because cargo features might disable them.
Yeah, with resources as entities we can just insert server or client component on backend client or server ๐ค
ohh yeah I got confused with the network backend resources which we add ourselves
You will still need to spawn backend entities.It's because the setup is backend-dependent.
But we will be able to attach replicon components to backends automatically which will be way nicer.
to clarify, aeronet provides no auth, and leaves that as the responsibility of the user (but the IO layer is responsible for encryption, which is separate). I should probably document that
@spring raptor I was curious, how would you handle user accounts via replicon?
I'm starting to think that I need another intermediary layer somewhere above IO for Auth, but I can't quite think of a system for this yet
or rather, keep encryption as the responsibility of the IO layer, and have game-specific Auth above
I'm not sure, we just provide replication. I think that authorization and loading proper data from a database or something else is for transport/user implementation.
so that would be below replicon?
well normally replication starts immediately after connection right
perhaps a "connect", "authenticate", "start replicate"
Thinking about your use case. As a temporary workaround you can have something like End state where you show the end screen. And InGame where you run your logic.
Only when the transport says that you are connected. But I think it won't say so until you finish your authorization? ๐ค
true, but all of the current transport implementations immediately send a replicon connect after a client connect
renet is a bit different to what I'm thinking, since a part of the auth process happens before there's even a connection, i.e. talking to an http service to generate and request a token
So the workflow looks like this: connect to authorization server, it generates the token and then you connect to the game server.
yeah
id like to do all the auth in one shot, with one server
anyway I'm gonna go to sleep and think about this a bit more
Then maybe you need something on top of Replicon, not sure
Yep, let me know how you think it could look like, I'm open to provide the necessary API.
Have a good night ๐
I disagree with this, connecting to your backend infra as a 'user' is distinct from authenticating as a 'player' in a specific game instance. Renet's connect token workflow is robust and effective.
I think there are valid use cases for it ๐ค
I remember playing SAMP and you could connect to a server via password.
Or Counter-Strike
Thereโs a good chance these cases are just logging into the backend and then the backend issuing connect tokens to specific servers.
I already have loading, countdown, running, ended, error ๐ I'll do a custom run condition to check if the logic needs to be run
Makes sense!
Yes, I would check if your game logic run in running state additionally to server_or_singleplayer.
But I planning to fix it with the upcoming rework of replicon resources as entities.
nice :)
renet's way is one valid way of doing it, and I do like it. I think it works nice when you have a central authority for accounts, and it should be a supported way of doing auth. however, that's not the architecture that I would like for my game, since I want a "one-shot" auth + connect flow, where each server manages its own accounts and there is no central authority. and this is a perfectly valid way of doing auth as well.
in general, I'd like both ways to be possible, and require switching between the two to require minimal code changes (at the actual "connect" stage anyway - if you need an auth token from some other authority, then there's obviously more work you have to do)
I'm just not sure where this should live in the networking stack. right now, I'm thinking above the transport layer but below replication.
about my Added<Troop> being re triggered on the client on disconnect, i fixed it, but i would like to understand how it happened.
i used bevy track change detection to print the troop's changed_by():
2025-02-10T12:50:15.449560Z INFO bevy_dev_tools::states: app::game::GameState transition: Some(StartCountdown) => Some(Running)
2025-02-10T12:50:41.337506Z DEBUG app::card::systems: handling Added<Troop> entity 877v6. Troop changed_by at Tick { tick: 309739 } by [...]/bevy_replicon-0.30.1/src/core/replication/replication_registry/command_fns.rs:93:42
2025-02-10T12:50:47.547765Z INFO app::game::systems: received EndGameEvent(Some(ClientId(1739191812323)))
2025-02-10T12:50:47.564847Z INFO bevy_dev_tools::states: app::game::GameState transition: Some(Running) => Some(Ended)
2025-02-10T12:50:48.570843Z ERROR renetcode2::client: Failed to update client: disconnected: connection terminated by server
2025-02-10T12:50:48.605955Z DEBUG app::card::systems: handling Added<Troop> entity 877v6. Troop changed_by at Tick { tick: 309739 } [...]/bevy_replicon-0.30.1/src/core/replication/replication_registry/command_fns.rs:93:42
i dont spawn or insert any Troop after the first one. so i dont know why Troop is marked as just added.
bevy's changed_by() should show from where its been added, but its showing the first add from replicon, not the second one which is added from nowhere.
could it be a bug with bevy's change detection ? because the two last_changed() are at the same tick and location, but just changed to added true (and so changed true too)
i dont insert or modify the Troop component or &mut it anywhere, only &Troop. and its still marked as added + changed
Agree, it's better to investigate! Let's take a look together.
So, backend (I assume you use renet2) systems update RepliconClient state in ClientSet::ReceivePackets.
Then we run ClientSet::Receive (the order is configured here).
Which contains the system that applies the replicated data here.
To me it's configured as expected. Client becomes disconnected first, then run receive_replication only if we are conneected ๐ค
But maybe the condition under run_if is evaluated at the beginning of the schedule? That would explain the issue.
yep renet2.
from what i know and searching in the discord, run condition are run right before running the system.
but maybe some systems ordering are not clearly set when multithreading ?
i dont know where and how to debug this ๐ค
my problem is fixed, so if you dont want to spend time on this no problem, ill just go next too.
we agree that its not normal for bevy's detectchanges to register the component was re-added, but it doesn't have the tick or location info ?
detect change impl: https://docs.rs/bevy_ecs/0.15.2/src/bevy_ecs/change_detection.rs.html#273-318
There is a way to check it. You can try to enable logging for replicon and see if a message is processed even after disconnect
If you want to investigate
Run conditions are ran before the first system that needs it in that schedule, then the rest usually reuse that result I think
oh yeah, ill try
interesting !
i found the problem, sorry for wasting all your time...
the system with the Added<Troop> was running on Update on the client and FixedUpdate on the server.
the fixedupdate system ran after disconnection because of the server_or_singleplayer. yesterday i tried removing it to assert it was the client update system that was running, but i must have failed to do it correctly. the second log was from the system in fixedupdate running for the first time, and so the Added was true. i learned that the same system in different schedule have different states
It's okay, I glad that we figure it out :)
I going to fix server_or_singleplayer in the upcoming release with backends integration rework. I think we can do it even without relying on Bevy main ๐ค
is there any example how to use replicon with avian3d? For some reason i'm having a hard time figuring it out ....
In the most basic form you'd only need to mark a couple of avian's components for replication ... Transform (or Position+Rotation), LinearVelocity, AngularVelocity, and possibly some component that lets the client know what collider and rigid body properties to set
ah thanks, no idea why i havent tried that tho.... thank you very much
Why kind of game are you planning to make? Most likely you don't want to use replicon with avian without prediction and rollback ๐ค
just playing around. i did a small game for a gamejame with lightyear and wanted to try replicon now. But my brain seems to be afk today, so i'm struggling with pretty basic stuff
but thank you very much for your help ๐
Ah, got it!
If you want to build a physics-based game you need a special technique called "client-side prediction". The idea is to remove input delay by applying all actions immediately locally and when server confirms the changes, correct the local state.
Under the hood it rollback your game state in time, applies server changes and re-plays local input on top of it.
And there are 2 ways of doing it:
- Rollback pre-entity. It's cheap, perfert fit for shooters, but not so good for physics-based games or for games where you have a lot of stuff to rollback (it's cheaper to rollback the whole world instead of checking each entity individually for rollback). That's what Lightyear provides.
- Rollback the whole world. Usually more performance-heavy, but perfect fit for physics-based games or games with a lot of rollbackable entities.
Replicon is completely abstracted from it and allows to build any of these methods on top. But the problem is that we currently don't have a crate that does it ๐
@dire aurora is working on a crate for 2, but quite busy with other stuff recently.
I planning to give a talk about replication on the next Bevy meetup, I will try to cover these topics ๐ Maybe this will encourage someone to write a crate for 1.
thank you for your explanation. i'm looking forward to your talk ๐
i really love how easy it was to setup replicon and how helpful the docs where. damn good job
Would also be nice if we got something for lockstep ... Lockstep + some input delay is a fairly common approach too for games that can more easily place limits on how much latency you'll have (like games that have lots of regions the way LoL does it, player-hosted games, or projects that are designed to be more local)
@spring raptor How are we actually supposed to use the example backend? It just throws this panic:
bevy_replicon_example_backend::server::set_running could not access system parameter ResMut<RepliconServer>
```Which happens because the example backend uses the the one from the git dependency, while your code uses the versioned replicon ๐ค
Damn, I forgot that third-party crate won't be able to use it this way...
Should I publish on it crates.io then?
I think it's quite explicit that it's just for example ๐ค
@dire aurora will it work if I set the version of replicon in Cargo.toml?
Hello! I'm updating some of my game's server events to use the new server triggers, but when I run it as a listen server only the non-server clients are receiving the triggers, not the one running the listen server. It runs as expected in single player mode, and it worked previously as a server event. The server_trigger documentation mentions triggering locally if ClientId::SERVER is a recipient of the event, but I can't find how to make it a recipient. Using SendMode::Direct(ClientID::SERVER) as the mode in the trigger also didn't appear to work. I'm was hoping to get some help with that. I've got the following code snippets:
#[derive(Event, Serialize, Deserialize)]
pub struct SetTurnBasedState {
pub state: TurnBasedState,
}```
```rust
app.add_server_trigger::<SetTurnBasedState>(ChannelKind::Ordered);
app.add_observer(receive_turn_based_state);```
```rust
commands.server_trigger(ToClients {
mode: SendMode::Broadcast,
event: SetTurnBasedState {
state: TurnBasedState::RoundBeginning,
},
});```
```rust
fn receive_turn_based_state(
trigger: Trigger<SetTurnBasedState>,
) {
warn!("Received new turn based state: {:?}", trigger.state);
}```
Thanks for the help!
Could be a bug... Going to sleep right now, but I'll take a look tomorrow.
Thanks! I managed to build a workaround, but I'd be happy to provide any additional context tomorrow if it would help. ๐
Maybe? That's how you do it for published vs unpublished crates, just put both a version and path, not sure what that does for git version tho
Just checked, it's only for releases on crates.io
I'll just publish it. For convenience the version number will match the replicon version.
@dire aurora but I want to hear your opinion as a user on this ^ first before I publish ๐
i was also wondering how to trigger an event to both the clients and the server at the same :)
It's a bug, I will push a fix after work later :)
oh ok thanks :)
It should work exactly as remote events.
Should be fine, as long as crates.io also has the warning the readme in that subfolder has
It takes some real mental gymnastics to use a crate with such a warning and example in all the names as a real backend ๐
Do you think using the same version is fine? Just thinking, there might be some cases where we want to update the example backend separately ๐ค
Could use same major but different patch releases
I don't think pushing features to just the example backend outside of a replicon update would be particularly worth it at least
@dire aurora Published for 0.30:
https://crates.io/crates/bevy_replicon_example_backend
As discussed, I'll maintain the same minor and major versions, but different patch versions.
I will adjust the readme on crates.io in the next release. It contains the warning, but lacks some context ๐
Quite a surprising error to get ... Did StartReplication become a trigger? ๐ค
Requested resource bevy_ecs::event::collections::Events<bevy_replicon::server::StartReplication> does not exist in the `World`
Yes!
I mentioned it in the changelog
I guess this just shows how painful the Trigger vs EventReader thing is with Event merged as one concept 
Yes, it makes the migration awful :(
And you can't know if the type is a trigger or an event by trait impls, you need to rely on authors to explicitly state it in the docs.
Having to integrate both loading of inputs while resimulating and listen servers really makes me need to rethink some of the design of my input queue and rollback stuff ...
Questions like:
- How do I avoid generating new inputs while resimulating
- How do I make sure the listen server's inputs have been written before sending
- How can I make the input queue load inputs instead of expecting the generation when it doesn't know rollback exists
Could you describe these issues in more details? ๐ค
So the first one is pretty obvious, as a part of FixedMain you need to generate inputs, but only if it's a real frame, and not a resimulation. I could do some run condition or system set dance but that seems very error prone
The second is a bit more specific to how I've set things up. I receive inputs from clients, use them to update the input queues, then send the inputs. I do this in PreUpdate, however if you are a listen server you also generate inputs, but in FixedMain, and thus miss them. If you send inputs after FixedMain (for example in PostUpdate), you can miss updates because they got consumed from the InputQueue while the listen server was running
For the last one I need resimulations to load inputs from the input history for all players, including the one with authority, instead of only trying to load it if there is no authority (they are unlikely to be there, but I check it just in case anyway, since it can avoid some mispredictions)
Ah, interesting!
But isn't server is the source of thruth?
I thought that you don't need to re-simulate inputs when you are a listen server ๐ค
On the lister server we don't re-simulate, I just need the server to send an accurate list of inputs before it starts consuming them
So clients have inputs for resimulating
Ah, got it now!
I have an issue similar to 2 in my input crate. I need to read inputs in PreUpdate but in FixedMain I need to output it at specific point in time.
LWIM does it, but I still didn't get to it.
Not the same as your problem, but related
Yea ... They sort of come from the same source, which is the fact that we can't really determine during PreUpdate where the inputs should go
If we could, I could easily populate my input history in PreUpdate and immediately send the inputs (now including the host's inputs)
I guess I'll just ignore this specific problem for now and send the inputs at the end of the frame instead, it contains 3 of the past inputs too, so the server needs quite a bit of lag for it to matter ๐ค
Still leaves the other two issues ... I guess the simple solution for the first one is to just generate inputs in FixedPreUpdate, I only resimulate FixedUpdate by default, and if you change things you can just use a custom schedule ... The last one ... I guess I should just make it a bit smarter with when it loads, inputs aren't stored until FixedPostUpdate, so if I only load something if there is history I think the problem should be non-existent, but ofc the responsibility to clear the previous frame's input is now in the developer's hand ... Which I guess is fine, it's their type anyway ๐ค
Actually I guess the last issue would also not exist if inputs could be generated ahead of time, then you could just always load from the history
Seems to work, but then I suddenly hit this:
bevy_replicon-0.30.1/src/core/replicon_client.rs:209:32:
client should have a channel with id 98
Encountered a panic in system `bevy_replicon_example_backend::client::receive_packets`!
Also seems to happen the other way around ๐
bevy_replicon-0.30.1/src/core/replicon_server.rs:175:32:
server should have a receive channel with id 10
Encountered a panic in system `bevy_replicon_example_backend::server::receive_packets`!
Looks like a deserialziation issue. Or do you have 98 client events?
Pretty sure I don't ๐
The only events I register:
.add_client_event::<Connect>(RepliconChannel::from(ChannelKind::Unordered))
.add_server_event::<CurrentTick>(RepliconChannel::from(ChannelKind::Unreliable))
.add_client_event::<LocalEntities>(RepliconChannel::from(ChannelKind::Unordered))
Looks like an issue on my side related to the example backend.
I have tests, but maybe it triggers only with multiple channels.
Here is how I read an write messages, quite simple: https://github.com/projectharmonia/bevy_replicon/blob/master/bevy_replicon_example_backend/src/tcp.rs
Server-authoritative networking crate for the Bevy game engine. - projectharmonia/bevy_replicon
Just tried with multiple channels, still can't reproduce
Could you show how the events look like?
Oh right and these too ofc:
app.add_mapped_client_event::<InputHistory<T>>(ChannelKind::Unreliable)
.add_mapped_server_event::<HistoryFor<T>>(ChannelKind::Unreliable);
#[serde(bound(deserialize = "T: for<'de2> serde::Deserialize<'de2>"))]
pub struct InputHistory<T: InputTrait> {
list: VecDeque<T>,
updated_at: RepliconTick,
}
``````rust
#[serde(bound(deserialize = "T: for<'de2> serde::Deserialize<'de2>"))]
struct HistoryFor<T: InputTrait> {
entity: Entity,
tick: RepliconTick,
past: ArrayVec<(u8, T), 3>,
future: ArrayVec<(u8, T), 7>,
}
```The T in question:
```rust
struct GameInput {
direction: Option<Dir2>,
}
struct Connect;
struct CurrentTick(GameTick);
struct LocalEntities {
car: Entity,
ball: Entity,
}
It seems to happen after some period of time, never right away ๐ค
Strange, maybe there is some weird TCP thing I didn't take into account ๐ค I will try to reproduce.
But let's start with a few simple things
Fixed, thanks: https://github.com/projectharmonia/bevy_replicon/pull/414
Thank you! I'll give it a shot a bit later today.
If you want to apply the fix, don't forget to use patch section in Crates.io (to avoid the issue with backends depending on a release version)
I planning to draft a new release soon.
@dire aurora could you try with trace logging enabled? Just to see that the amount of sending bytes matches the amount of received.
I.e. RUST_LOG=bevy_replicon=trace cargo run ..
It doesn't actually show the number of received bytes anywhere ๐ค
Ah, it only shows the number of messages...
Could you try this branch?
https://github.com/projectharmonia/bevy_replicon/pull/416
This will require you to switch to the latest master which contains the postcard migration. We have a changelog, but maybe seeing this PR will also help (just look at the docs changes): https://github.com/projectharmonia/bevy_replicon/pull/410
The world's most painful migration for sure:
- cursor: &mut Cursor<&[u8]>,
-) -> bevy_replicon::bincode::Result<()> {
+ cursor: &mut Bytes,
+) -> bevy_replicon::postcard::Result<()> {
And changing the example backend version to 0.1 for unknown reasons ๐
client:
2025-02-12T17:31:57.629624Z TRACE bevy_replicon::core::replicon_client: sending 13 bytes over channel 2
2025-02-12T17:31:57.645861Z TRACE bevy_replicon::core::replicon_client: sending 2 bytes over channel 0
2025-02-12T17:31:57.646642Z TRACE bevy_replicon::core::replicon_client: sending 13 bytes over channel 2
2025-02-12T17:31:57.662504Z TRACE bevy_replicon::core::replicon_client: sending 2 bytes over channel 0
2025-02-12T17:31:57.663042Z TRACE bevy_replicon::core::replicon_client: sending 13 bytes over channel 2
server:
2025-02-12T17:31:57.646761Z TRACE bevy_replicon::core::replicon_server: received 1 message(s) totaling 2 bytes from channel 0
2025-02-12T17:31:57.646768Z TRACE bevy_replicon::core::replication::replicated_clients: ClientId(56474) acknowledged mutate message with Tick { tick: 441520 }
2025-02-12T17:31:57.646780Z TRACE bevy_replicon::core::replicon_server: received 0 message(s) totaling 0 bytes from channel 2
2025-02-12T17:31:57.646788Z TRACE bevy_replicon::core::replicon_server: received 0 message(s) totaling 0 bytes from channel 3
2025-02-12T17:31:57.646792Z TRACE bevy_replicon::core::replicon_server: received 0 message(s) totaling 0 bytes from channel 4
bevy_replicon-6598e87287870c1c/13d2c93/src/core/replicon_server.rs:179:32:
server should have a receive channel with id 10
Encountered a panic in system `bevy_replicon_example_backend::server::receive_packets`!
2025-02-12T17:31:57.663598Z TRACE bevy_replicon::core::replicon_server: received 0 message(s) totaling 0 bytes from channel 0
2025-02-12T17:31:57.663613Z TRACE bevy_replicon::core::replicon_server: received 0 message(s) totaling 0 bytes from channel 2
2025-02-12T17:31:57.663617Z TRACE bevy_replicon::core::replicon_server: received 0 message(s) totaling 0 bytes from channel 3
2025-02-12T17:31:57.663621Z TRACE bevy_replicon::core::replicon_server: received 0 message(s) totaling 0 bytes from channel 4
Before that the server still seemed to receive the constant 13 bytes over channel 2 and 2 over channel 0 fine ๐ค
Do you have a lot of custom serialization?
Yeah, it's because on the latest master it's still 0.1, but I already have a PR opened for it.
I think I have an idea, let me quickly try one thing
I have none in this example, this one place is just the authoritative write function :')
My theory is that's because I write into the socket in consequential writes. But I need to write it in one go.
Yea that was also my theory, but I don't see why that would be the case ... Maybe if it's non blocking and it picks up a partial message first, then sets it up for failure later? ๐ค
I suspect that it might picks only channel or size, then hits WouldBlock, exits, but the stream have these bytes read now.
I pushed a small change to tests this theory. Could you try it again?
Pushed a less ugly fix to avoid extra allocations.
@dire aurora sorry, pushed to a wrong branch ๐ Now should be available at https://github.com/projectharmonia/bevy_replicon/pull/416
Not sure if it fixes it (since it's a pretty rare bug) but at the very least it's harder to hit now ๐ค
I guess to really make it reliable the reading side should also ensure it either reads everything or nothing ๐ค
Otherwise it could still go wrong on ticks with a lot of data that might cause things to be split up over different packets
Let me know if it triggers again. But I think that the change is good anyway, it's a single syscal now.
I considered this, but I don't know the size ahead of time :(
The common approach would be to peek the first 3 bytes, then if that fails you continue, but if it succeeds you read the whole message
Does require a buffered reader tho I think
Ah, I didn't know that TcpStream provides peek. Let me adjust the receive impl as well
Oh it does have a peek ... Nice so it's basically free to fix
Pushed
I managed to replace &World in the replication sending system with FilteredEntityRef. It allows other systems that write to non-replicated components to run in parallel. But I started all this to to make it work nicely with server as a component ๐
The logic is almost the same, but I access the pointers via FilteredEntityRef.
The downside is that it's 2x slower (!) due to the extra lookups.
However, I think I can create a custom system parameter that provides access to the replicated components, just like before, but with granular access similar to FilteredEntityRef ๐ค
Which lookups exactly are causing the slowdown?
Access check validation and component storage type fetch. I think I can get rid of all of this by writing my own system param.
You mean the switch from the unsafe get_component_unchecked to get_by_id(id).unwrap_unchecked)?
Yep, basically this:
https://docs.rs/bevy_ecs/0.15.1/src/bevy_ecs/world/entity_ref.rs.html#2412-2418
that calls this:
https://docs.rs/bevy_ecs/0.15.1/src/bevy_ecs/world/unsafe_world_cell.rs.html#950-962
Is slower then my old approach where storage type were cached with no access checks.
Interesting ... I'm surprised it makes that much of a difference ... I guess this just shows how efficient the architecture is otherwise ... Iterating over archetypes and using an archetype cache is pretty cheap after all ๐
Yeah, I also didn't expect it ๐
I will try wrapping UnsafeWorldCell into a SystemParam with granular access. Will work just like before, but Bevy should be able to parallelize it nicely. And without that ugly ParamSet.
Ugh, it seems like just resimulating FixedUpdate won't work ... I wonder if I should just recommend the approach I did for my game (a custom PreSimulation, Simulation, and PostSimulation, in a SimulationMain, which is run in FixedUpdate and by rollback)
In case anyone is wondering what the issue is: There are certain things that should always happen before and after the simulation, and you really don't want to schedule every system around them ... Things like loading inputs and storing the new prediction
Makes sense to me. Unless until Bevy provides a nicer solution
Done: https://github.com/projectharmonia/bevy_replicon/pull/418
The performance is the same for single thread and Bevy can parallelize it better.
But my main goal is to make components as entities to work nicely.
Now I need something similar for the receive side. But the problem is that user can write data into non-replicated components ๐ค
For this I'll wait for actual "components as entities". Because unlike with components, you cannot define access as "all resources except...".
So RepliconServer and RepliconClient will be resources for now.
But we can do some other cool API improvements already ๐
I think main might actually have that capability because of a somewhat niche feature rendering people wanted ๐ค
Fixed the UB I caused ๐
https://github.com/projectharmonia/bevy_replicon/pull/419
Now it's implemented properly.
@dire aurora quick question: do you need full world access in write functions or only ComponentId?
I need read access to some specific resoureces too, we could make a way to whitelist those tho ๐ค
Yeah, this could work.
And the same for components.
You mean like non-replicated components you can modify?
That would be necessary too I guess, my write functions are for any component, but they always write to the AuthoritativeHistory component
Yep, additional non-replicated components.
I also have another idea to try: provide a special world that blocks access to resources used by replication receiving system.
So folks, [I'm looking into still setting up a basic synced app. ](#networking message)
And I know @spring raptor has helped me out but I'm still struggling to set up a good way to set up synced camera and a synced up mesh, which I plan to be changing in the future. Basically 2 seperate apps should have the transform components of their cameras synced up as well as the Mesh3d shape and it's texture as well. I haven't even gotten to syncing textures yet and I'm not even sure if swapping the path on the material will even be the way to do it. But for now just having a camera perma synced would be massive help.
I tried doing the replicated components path and a Camera marker component and it replicates, but it's not too fab as you get a No Camera warning a few times until replication starts. So maybe there's a thing there of creating a window on the client only when the replication starts.
And the second thing is that in the required component path I need a nice way to access my global settings resource as the z value of the camera position is set in the settings, which I'm struggling to grok how to do.
Maybe I should start a thread in the help forum if this would be more helpful.
but it's not too fab as you get a No Camera warning a few times until replication starts
That's expected. In actual game you most likely need a camera to draw menu stuff and when you receive the replication, just despawn the original camera via a trigger.
I need a nice way to access my global settings resource
For that you want to mix it with the blueprint pattern: create a trigger that listens for in-game camera insertion and just update the value from settings.
Maybe I should start a thread in the help forum
This thread is a good place to ask questions like this.
on wasm im getting a panic
replication_registry.rs:141:32:
replication `FnsId(63)` should be registered first
im not using any fns or custom impl, only some replicate mapped
thanks :)
Interesting, it means that on deserialization we read number 63 in a place we shouldn't ๐ค
When you do replicate_mapped, it registers a default FnsId. But I don't think you have 63 replicated components.
I remember that you using this branch https://github.com/projectharmonia/bevy_replicon/pull/405 to fix another problem that happens only in wasm. Am I right?
Does it happen constantly?
it happen everytime when connecting, but only on wasm with websocket renet2 (native socket no problems).
im using the branch trigger-listen-server. since i didn't encounter the panic after switching wifi, i stopped using the branch disabling it.
no i have two replicate_mapped component, and none of them are spawned when connecting
i triend branch main and same thing
Interesting, might be a transport issue then.
Ping @echo lion, maybe you have an idea how to debug it?
The transport should not be mangling messages
You better use this branch instead, I recently introduced an UB in the latest master ๐
So I imagine itโs a replicon issue, possibly UB
But it happens only on transport change ๐ค
It happens in WASM, so perhaps the UB has different effects
wasn't hapenning before i was using unsecure connection, and now it happen since using serverconnecttoken, but it can just be a change i made in my code ordering
mirsella can you test webtransport? Should work on chrome
Okay, let's try to debug it. @thorn forum could you enable logging to see how much bytes we receiving and sending first. Just in case.
thanks, ill just use latest main commit then
No, no, not the lastest master, use the mentioned branch that fixes the small problem :)
oh its not yet merged mb
Yep, I pushed it yesterday, but forgot to request a review. @echo lion will merge it once he have time.
It's not related to your problem, just a fix for the previous commit in master.
with bevy_replicon=trace and with the fix-replication-read-ub branch
I see, a lot of bytes ๐ค
Now could you show logging from the server as well?
RepliconTick(839), we definitely receive something weird
hm yeah seems a lot, dunno if thats just my usage, im replicating a few things on startup
Yeah, it's quite suspicious. The idea is to compare how much replicon sends and receives.
These are bytes without any transport data, just pure replication and events.
Does that client id match your expectations? 552481
on client the "applying message for replicontick" is 51 like on the server
2025-02-17T16:51:06.967353Z TRACE bevy_replicon::server: incremented ResMut(ServerTick(RepliconTick(50)))
2025-02-17T16:51:07.016956Z DEBUG bevy_replicon::server: `ClientId(811578)` connected
2025-02-17T16:51:07.016986Z DEBUG bevy_replicon::core::connected_clients: adding connected `ClientId(811578)`
2025-02-17T16:51:07.017001Z DEBUG bevy_replicon::core::replication::replicated_clients: starting replication for `ClientId(811578)`
2025-02-17T16:51:07.017075Z INFO app::game::replicon::systems: client ClientId(811578) connected
2025-02-17T16:51:07.017089Z INFO app::game::replicon::systems: spawning castle for new ClientId(811578) at Tile(3, 4)
2025-02-17T16:51:07.017625Z TRACE bevy_replicon::core::replicon_server: received 0 message(s) totaling 0 bytes from channel 2
2025-02-17T16:51:07.017636Z TRACE bevy_replicon::core::replicon_server: received 0 message(s) totaling 0 bytes from channel 3
2025-02-17T16:51:07.017642Z TRACE bevy_replicon::core::replicon_server: received 0 message(s) totaling 0 bytes from channel 4
2025-02-17T16:51:07.017657Z TRACE bevy_replicon::core::replicon_server: received 0 message(s) totaling 0 bytes from channel 0
2025-02-17T16:51:07.018208Z TRACE bevy_replicon::server: incremented ResMut(ServerTick(RepliconTick(51)))
2025-02-17T16:51:07.018468Z TRACE bevy_replicon::server: sending update message to ClientId(811578)
2025-02-17T16:51:07.018478Z TRACE bevy_replicon::core::replicon_server: sending 8851 bytes over channel 0
had to truncate the logs for discord, but nothing before or after
nothing in the sense no changes or messages are sent
oh comparing how many are sent and recv, ok will do that
yeah im using current time as micros seconds for now since its anonymous
should just use ms since epoch instead
So you send 8851 bytes. That's quite a lot.
But could you run everything together again just to ensure that sizes match?
yep the sent bytes matches, both 8972 this run
That's a lot of data to send. I wonder what takes so much. Let me improve the logging.
i just tested its also this much data when connecting with native. but it doesn't crash on native. could it be something on wasm being buggy and that much bytes corrupt something ?
I suspect it might be caused by packet constant limit ๐ค
Could you quickly try this branch? https://github.com/projectharmonia/bevy_replicon/pull/407
i set
[patch.crates-io]
bevy_replicon = { git = "https://github.com/newclarityex/bevy_replicon" }
but i get
warning: Patch `bevy_replicon v0.30.0 (https://github.com/newclarityex/bevy_replicon#673caad1)` was not used in the crate graph.
Check that the patched package version and available features are compatible
with the dependency requirements. If the patch has a different version from
what is locked in the Cargo.lock file, run `cargo update` to use the new
version. This may also occur with an optional dependency that is not enabled.
i ran cargo update and deleted cargo.lock to try but same thing
Strange. But it does work like this on the latest master?
If yes, clone the repo, and adjust the constant to some conservative value, like 1000 and use path = "<path to the folder>".
It shouldnโt be a packet size limit, because the message arrives with the same size on the client
doesn't work i get serde function IDs should be obtained from the same instance on wasm and on native it works like always. i think its not really using the newclarityex:master branch
do i still try locally changing the limit ?
yes server and client native and client wasm where recompiled
If you get the warning above, then yes
panic
I meant this warning ^
I would try locally, yes
oh yeah
Maybe packets ordering is swapped ๐ค
ill try locally.
btw i think the patched version doesn't work because its version 30.0 while your versin is 30.1, even if its patch version change maybe its incompatible since its a lower patch version
mirsella do you use renet2_setup on both server and client?
Ok. I will try to test extra-big messages in an hour or so.
@spring raptor ive sent max_pack_size to 1000, but same thing. anyway even 1000 is still bigger than what im sending ~8900 bytes so it doesn't matter ?
only on wasm ๐
Right I will test all the transports
do you still want me to try webtransport ?
Sure that would help
Got it. I suggested a smaller value due to possibly incorrect splitting. We can't fit ~8900 in a single packet. So we split data across multiple packets.
Okay, one more thing to try :)
Could you add byte printing to the debug message locally?
oh okay, so no need to put it even lower
No, why?
Single packet size is limited. And replicon tries to split data up to maximum size. But it depends on the transport. It usually bigger then 1000. We currently have a hardcoded constant, but I planning to fix it in the next release :)
If you have the same error with 1000, then it's not what causes the issue.
Let's try this. Just print the whole message before sending and receiving. Just search for "sending {} bytes" and adjust the trace message
The idea is to compare what we send on client and server.
looking at my code, there shouldn't be even close to 8900 bytes sent, in not replicating a lot
ill try updating the trace
i just print the raw bytes u8 ?
Yes
I will add more detailed logging soon
btw i tried not spawning my tiles, and so not even sending start game event, and it didn't crash
Says not available
How many tiles do you replicate? That could add up to a lot.
oh sorry i thought "unlisted" would work instead of public. https://pastebin.com/0ZLwHC1F
Also not available
- whats replicated on a tile, is a transform, a marker component, and a u32
wtf
they're marked as public but doesn't work
Transform is fairly big, looks like each tile is probably 62 bytes.
Although that does seem a little too big, not sure
That's one. But I need both, server and client to compare
oh ok sorry
oh there also Name replicated on debug mode, and when the client connect, a new building is spawned for it, which is max 200 bytes
Hmm well transform is ~40bytes afaict. Name is probably 10bytes or so.
do you want me to just check if the message on the server is the same as on the client ? simpler than sending two pastebin
This also works :)
what the hell i worked
ill re try it
the only two things that changed is the message length is smaller, and especially running the server in release
the web wasm app was always compiled in release, while the server was in debug. the serialization are different i guess
Interesting, strange
its working everytime now
Maybe this is the problem. If Name isnโt registered in release mode, the fnsid wonโt be known by the client. But idk why the fnsid would be 63.
thanks as always for all your helps :)))))
i tested more, and it seems to be this, Name being replicate on server, and not on client
i tried debug server, release client, and it work as long as both have the same thing for replicate Name
but the amount of data sent, and the number of FnsId is still weird ๐ค
Probably just reads some weird value due to how data serialized
Name is quite huge, so I don't see anything suspicious
I didn't see the problem being debug vs release, because usually when I mismatch compilation mode I get "serde function IDs should be obtained from the same instance" but not this time
Not sure if get you. Could you elaborate?
when I run the client in debug and server in release, the app panic with this message "serde function IDs should be obtained from the same instance"
but with release client and debug server it didn't panic, so I didn't see the two had different compilation mode
Ah, I see, interesting ๐ค
Maybe we should send a hash of the defined protocol? Just to avoid footguns like this.
Yeah good idea. Can send it in the first init message to clients. Might need to use one of the message bits for it.
The error message can include a list of possible reasons for mismatch: rule registration order not the same, replicon channels not the same, different features affecting replicon rules enabled on server/client, etc.
We won't be able to detect this from the hash. Do you want to send more detailed information?
No Iโm saying the error message has a list of possible reasons it can not match
When you check the hash and it doesnโt match
Ah, agree!
Opened: https://github.com/projectharmonia/bevy_replicon/issues/420
I will try to take a look into it soon. Want to rework of sending and receiving first.
Working on receiving right now
I had previously finished sending, but today I opened a PR with a small fix.
Won't work.
For receive we expose Commands that can remove resources.
Unless we provide custom commands that refuse to remove some of the resources ๐
And the same problem with whitelist
Our current approach with removing all required resources isn't bad.
I just can't do the same with components.
Ah, we have https://docs.rs/bevy/latest/bevy/ecs/prelude/struct.EntityWorldMut.html#method.take
Never mind ๐
A mutable reference to a particular Entity, and the entire world.
Wouldn't this cause an archetype move? ๐ค
Yes, but it's only for a single RepliconClient component on a single entity.
Oh, that shouldn't be a major issue then ... Not really sure why any other code would even try to touch it tho ๐ค
Might I ask how one implemente i nterest management? With replicon
Depends on what you mean by interest management ... It could be as simple as using per-entity visibility or as complex as needing to add features to bevy_replicon ๐ค
Yeah, I don't think anyone would touch it. But if someone did, it would lead to UB without the removal.
Yeah, it's a bit of a broad term.
We provide a per-entity toggle: https://docs.rs/bevy_replicon/latest/bevy_replicon/core/replication/replicated_clients/client_visibility/struct.ClientVisibility.html
With a policy on the server plugin: https://docs.rs/bevy_replicon/latest/bevy_replicon/core/replication/replicated_clients/enum.VisibilityPolicy.html
It's fast and opt-in.
Third-party plugins can built on top of it things like this: https://github.com/UkoeHB/bevy_replicon_attributes
It would be nice to have a plugin that automatically toggles visibility based on distance or visibility ๐ค Shouldn't be hard to implement.
I mean fwiw it's a broad term because you kind of need all the approaches to end up with usable interest management ... Just per-entity visibility isn't enough, you want component visibility, priority + packet limits, send rates, etc
Yep, component visibility, priority, and other features are planned. I don't think it can be implemented as a third-party crate. I just haven't gotten to it yet ๐
Heya! I'm going through the quickstart guide, everything is working so far except the ClientConnected trigger.
I followed exactly as it appears here https://github.com/projectharmonia/bevy_replicon/blob/master/bevy_replicon_example_backend/examples/simple_box.rs#L40 but the trigger is never called, even though I can see replicated stuff showing up in the client! I'm using the bevy_replicon_renet backend, is there something I need to do with it to get the connected trigger going?
Ahh, I was using MinimalPlugins on the server, switching it to DefaultPlugins made it work. Wonder which bevy plugin was missing
I'm getting these logs even using DefaultPlugins, are there specific bevy features that need to be enabled for replicon to function properly? I have default-features = false in cargo.toml.
2025-02-20T00:37:38.947731Z WARN bevy_ecs::system::system: System 'bevy_replicon::server::reset' has not run for 3258167296 ticks. Changes older than 3258167295 ticks will not be detected.
2025-02-20T00:37:38.947749Z WARN bevy_ecs::system::system: System 'bevy_replicon_renet::server::RepliconRenetServerPlugin::set_stopped' has not run for 3258167296 ticks. Changes older than 3258167295 ticks will not be detected.
The quickstart uses MinimalPlugins which is a bit confusing though, if ClientConnected isn't expected to work with it
It should just work because we rely on the connection event in Replicon itself: https://github.com/projectharmonia/bevy_replicon/blob/af9129368e589d8988dbaaaebb8418c398a00cc8/src/server.rs#L177
If you see replication happening, then the event was triggered ๐ค
Didn't know about this warning!
But it's expected, reset and set_stopped run on server shutdown. This is why it says "has not run for N ticks".
In the next release these systems will be triggers, so you won't see the warning.
strange.. I might try figure it out later but for now happy enough to use default plugins.
I'm curious if there's a way to have a "replicate only once/on insert" kind of behaviour. I have logic components that are replicated that are basically what certain units are doing, like "move to X". With those replicated the same behaviour will play out on each client. The only issue is when a new player connects they don't get the other units current Transform because I don't have it replicating, so they see the other unit walking from Transform::default().
My best idea is to query for Query<&Transform, With<Replicated>> on the server when a player joins and send them all over with an event to patch the initial positions.
It's a planned feature: https://github.com/projectharmonia/bevy_replicon/issues/331
As a temporary workaround you can create a custom InitialTransform component that on insertion updates regular Transform.
Ah yea, makes sense. I initially expected Replicated to be an enum with that kind of choice, for what it's worth ๐
I don't know if that would help, I'm trying to avoid syncing positions constantly, if I replicated InitialTransform wouldn't that leave me in the same boat? Sorry if I didn't make the criteria I was going for earlier more clear :). I'm going with click on ground to move, so replicating a component that says "moving to x,y" is the only thing that needs to be transmitted ideally (assuming the unit starts in roughly the correct place ๐ )
I, right, my suggestion won't work...
I guess yes, you will need to send positions manually for now.
Damn, I need to get to this feature asap ๐ข
no rush ^^
We also have writing function that provides world access.
Marker-based functions for App.
It's executed after receiving on client. This way you can override writing into Transform, for example, into something else.
Serialization happens as usual.
What is your use case?
Have you considered using reflection?
It's a bit more expensive, but if you need to send dynamic data, it might work
That'll work, yeah
But you will have to track changes on your own
Why not use reflection?
@vocal violet this example is for event, but you can do the exact thing for components:
https://docs.rs/bevy_replicon/latest/bevy_replicon/core/event/client_event/trait.ClientEventAppExt.html#tymethod.add_client_event_with
An extension trait for App for creating client events.
We provide access to the type registry
@dire aurora do you need access to client's ticks on server? ๐ค
In what context? ๐ค
I working on a component-based API. The idea is to replace these resources:
https://docs.rs/bevy_replicon/latest/bevy_replicon/core/replication/replicated_clients/struct.ReplicatedClients.html
https://docs.rs/bevy_replicon/latest/bevy_replicon/core/connected_clients/struct.ConnectedClients.html
https://docs.rs/bevy_replicon/latest/bevy_replicon/server/client_entity_map/struct.ClientEntityMap.html
that map ClientId into data with entities with data as components. It makes it way more ergonomic. And I additionally split ReplicatedClient into ClientVisibility and ClientTicks. And I wonder if this ClientTicks even needs to be exposed. It stores methods like mutation_tick, update_tick, etc (like ones on the current ReplicatedClient).
There are context where the acked ticks are useful, but you'd probably want to explicitly receive it then anyway
Like delta compression
Otherwise I don't really use it
It's for listen server and singleplayer case. Useful if your game needs to work in both modes.
No, singleplayer is when your server is not running.
No, listen server is when server is also a client. All events just getting re-emitted locally. And other players can connect. No packets actually sent locally for efficiency, it's just work.
Singleplayer mode is when you don't use server at all.
Of course you can implement singleplayer by running server locally, but with replicon it's not needed.
In the quick start guide I tried to explain the trick we use to support all modes. Just navigate to the events section because for listen server you don't run replication locally.
You don't send packets. You send events. And events are re-triggered locally. See the events section in the quick start guide.
You can also try running any example in singeplayer mode
@vocal violet
To simplify:
- When you emit
ToClients<T>on server and singleplayer, it's also emitted locally asT. - When you emit
Tit's also emited asFromClient<T>on server and in singeplayer mode.
In singleplayer server won't be running, but events will still be re-triggered.
Let me know if I can somehow improve the quick start guide to make it more clear.
Don't afraid to ask more questions if it's still unclear.
Why?
You are a server (or singleplayer) and emit T - you get FromClient<T> locally.
You are a client and emit T - server receives FromClient<T>.
Not at all, I really need to put a table or something like this to make it more clear ๐ค
If you have any ideas how to improve the docs - let me know.
I think it's there, but I'll double check.
Good idea. If you have a vision of it, a PR would be appreciated.
But I planning to take a look into it myself before the next release.
@dire aurora do you still calculate statistic on your own? ๐ค
I.e. stuff like packet loss, rtt
Yes, but only rtt, and only because backends can't report accurate latency ... And there is that feedback loop that adjusts the timing if inputs ever get dropped
Do you think it's useful to provide statistic in backend-independent way?
Would be helpful for anyone that uses statistics from a backend if they ever want to switch ... Could also maybe simplify clock sync in cases where a more accurate out-of-band measurement isn't practical (yet) ๐ค
Why rtt provided ba backends aren't accurate? I.e. why do you calculate it manually?
The backends run as part of the bevy update loop, which adds latency on each side based on how the packets align with the frames/updates
In theory backends could have an async part to them to avoid that ofc, but not many of them do
Maybe we should contact backend developers ๐ค
But why? ๐ค
Yes, I can't provide world access there, unfortunately.
The example runs on CI
Make sure you have all necessary imports
use serde::de::DeserializeSeed;, for example
@spring raptor Mr shatur are u experienced with ai development?
Unfortunately, no
Ah, I think it's only for events ๐ค
Do you have a component with reflection inside?
I can provide it like I did for events. Could you open an issue for it? I'll fix it soon, just need to finish some other work.
I can't provide all resources or world access nicely. But having TypeRegistry is reasonable.
Just opened a big PR with API rework:
https://github.com/projectharmonia/bevy_replicon/pull/423
You don't have to look at the code, I provided a high-level explanation about the changes in the description.
What problem does this solve?
I dislike our current ergonomics.
Data related to connected clients is located in separate resources, and accessing it is inconvenient. For example, to configure visibility, I have to get ReplicatedClient from ReplicatedClients using client ID (O(n)), and then call visibility_mut. Fetching it from a query is more direct and O(1).
Since we now have identifiers, I removed ClientId because conversions between them were quite confusing for users.
This change leads to using OnAdd / OnRemove triggers to listen for connect/disconnect events. As a result, I removed the special triggers along with the disconnect reason (which we also can't represent nicely).
Not entirety sure about the API, but I think that the direction is promising. It feels more Bevy-way. I'm open to suggestions.
I like it but I'm clearly biased because I already use entities for everything
it's the same conclusion I eventually reached, that as much as possible should just be in the ECS since it makes access easier and more ergonomic (unless there's a very good reason to store it in my own data structure in a resource)
Does any of the messaging backends for replicon implement nat traversal?
Not aware of any backend that has this ๐ข
In what context are we doing NAT traversal here?
If you do hosting mode, you can run your game at a much lower cost if you don't need to pay a full relay.
So you want a player host to be reachable by other players while everyone is behind NAT?
So when the connection gets established, there would be a similar implementation of what Matchbox is doing, most clients would be able to direct message each other instead of going through a relay. Which is both lower cost and faster.
correct.
Yea, you'd probably need a custom transport in a backend for that ... I do something similar for my game (except I only have clients behind NAT but the server is a dedicated one without NAT issues), I handle an initial handshake trough a web API and that allows me to immediately connect afterwards
Is there a preferred way with replicon how to despawn entities with an effect so that they are not removed instantly? (e. g. showing different animations if an entity dies, teleports away or going out of range etc.)
just an animation should be fine, can you explain the difference between fully use state and dead state? my first simple search would be something like, fully use state -> client can handle the identification itself, dead state, the server sends every state and client have to follow?
Ah I see, thats somewhat not yet defined since I would adapt it based on libs like replicon which works best
would you create a new entity on its own with the state on the server and sync or send the dead animation via channels
but I think thats actually it with the new entity then there are no conflicts of the order of changes
ok thanks I will give that a try
Despawning + spawning a new entity should work fine if I understand the way replicon handles despawns and spawns correctly
Games usually do like this ^
Replicating death component or some component removal is inefficient.
It's better to just despawn the entity on server, client replicates it and on despawn spawns a special decoy entity.
Yea the exception would be if the thing that died can come back to life, in which case you need to retain everything, and have some way to prevent them from moving
(That's the reason why death is a status effect in my game, tho I currently still just despawn enemies immediately when their HP hits 0)
hello :) on server commands.server_trigger(ToClients { mode: SendMode::Broadcast, event: () }); doesn't trigger the event locally if the feature client is not active, only the clients are triggered
And then the server isnโt needed anymore? (Meaning the server only handles handshake?)
Yea, once the handshake is done that web API I used to do the handshake is no longer relevant
an it wokrs with the exiting transports? which one are you using?
It's a custom transport, it's pretty simple to make one when you just need to establish a connection based on some known details and use encryption with a key that you also shared trough that handshake
@echo lion Quick remainder about the review ๐
After that I'll open a few PRs with fixes and cut a new release.
Ok Iโll try to get to it this weekend
up :) i think its not the expected behavior ? thanks
The good news: The rollback example seems to function decently without nearly as many hacks as my own game had
The bad news: I suck at this example game 
You mean nauseous? Understandable with my poor driving skills + the awful controls + the complete lack of any references to comprehand what is happening ๐
yes nauseous
But it is nice too se a working physical sample in replicon
Congrats on your achievement
Now we can brag about how the worst rocket league clone in the history of rocket league is written with bevy_replicon 
Sorry, I forgot to reply. I think I fixed it in the latest master. Could you try it?
Awesome! Maybe apply a grid texture to the ground? And maybe adjust the camera a little ๐ค Just interpolate it and increase arm distance when the box speedups
Maybe one of these textures: https://www.kenney.nl/assets/prototype-textures
im already using this commit https://github.com/projectharmonia/bevy_replicon/commit/545d0bbe097bcfba26d8af5209cea0b7ee3290b7 if thats the one you're talking about
Ah, I think it's expected. When the server is not a client, it's not a listen server. All local re-triggering happens only if client feature is enabled.
alright then, but maybe we can improve the documentation to say the server_trigger are not received if the client feature is not activated
Will adjust docs soon, need to merge one big PR first to avoid conflicts :)
Is that available somewhere?
Not yet, but it's in a state where I could probably publish it soon, just need to make the two smaller crates compile again
And then solve the biggest problem: I don't have a name, so it's currently called some_networking_thing_idk ๐
The repo has some stuff for entity disabling (might be able to remove that if I account for changes on main), entity management (mostly reusing existing entities if the spawn conditions are identical), input queue stuff, and of course rollback
Mind describing it I like to think I can help
It's basically a set of tools for networking more complex things on top of replicon, so the naming will probably be x_rollback, x_input_queue, etc ... See my previous message for the list of what I currently have in there ... Still need to add some clock sync stuff tho
Naming is so hard for me as well. I still don't have a good name for my life simulation game...
Isn't it called "Project Harmonia" for now? Better than my game which still goes by "Unnamed ORPG" 
I do have a better name for it, I just haven't put in the tiny amount of effort to do the rename ๐
It's a placeholder name; I just didn't want to call it "Unnamed Life Simulator" ๐
How about Mr Shatur, game of life
You mean like Sid Meier's Civilization, but Mr Shatur's game of life? ๐
Game of life is also quite recognizable, it's an old school classic.
And a game named just "Harmonia" also exists ๐ข
exactly
I wonder if I violate EA's IP by calling my game "Shatur's The Sims" ๐ค
Knowing their policies
Probably yes
"Shatur's Harmonia" ๐ง
The sims had other names before (during development):
- Dollhouse
- Home Tactics
- People Simulator
Bur probably none of them have that certain feeling to it.
Interesting, I didn't know that!
Yeah, The Sims is very catchy ๐ค The fact that they call characters "sims" and money "simoleons" adds to its charm.
There's another game in this genre that's currently in development: Paralives. They call their characters "parafolks" ๐
code name ShH ๐คซ
Do you have any up-to-date footage? I thinking about mentioning your project in my upcoming Bevy talk
Haven't touched it since then 
It's okay, I'll do a text mention ๐
The video is alright, just needs grid textures. With solid colors it's hard to see the movement.
I could push the repo if you want to fix it, but I probably won't have much time to fix it for a while cause I've been focusing more on getting the CNC stuff done
The talk will be in 2 hours, I don't think I'll be able to make it in time.
But it's okay even without a video.
Take your time and enjoy your hobby ๐
Ah yea, 2 hours is definitely too short to fix this example ๐
It's in a pretty rough shape
I guess with how long your presentation took it's good we didn't have fancy demos ๐
True ๐
I should have cut half of the slides and include "part 1" in the title.
It also was my first time going public like this. In the beginning of the presentation I was very nervous ๐
I'm an introvert person, but like to try new things.
So it was a stressful but interesting experience for me ๐
I think you did pretty well overall ... And unlike Jondolf you actually had a functioning microphone ๐
It was streamed on youtube so you can just watch it back
I have to say the most disappointing part was when the 0.16 features were brought up and no one mentioned entity disabling 
Damn, I totally forgot about it ๐ข
Yeah, it's huge for rollback!
I definitely needed you there as a 3rd talker, I would be able to cut 4 slides ๐
Here is the link to the recording: https://www.youtube.com/watch?v=fNuolgA_88k
But then you might've had less time to talk and those 4 slides took the least time ๐
True, I really just rushed through them ๐
I do agree that relations are big tho, for pretty much the reason you described ... If we find a way to network these nicely in replicon then I can throw away all the most cursed parts of my game in one go :')
Yes me too, I'll have to watch the full thing later but you're not coming off as nervous just skimming thru the video
great job
And honestly I find networking really hard to vocalize
Thanks!
You probably know all of this stuff, it's more for beginners ๐
Just wait until we get to part 3 and even Joy learns new stuff ๐
Ah, yeah, I think the way we replicate in ECS might be new ๐
I also planned to make a small call for action in the end: encourage people to build their crates. It would be really nice to see a shooter-style rollback crate.
And things like the mentioned asset streameing could also be a neat crate that utilizes replicon.
But I forgot about that ๐
Still hoping @dire aurora will release their crate this week ๐ค. At least thatโs already one ๐
Is there a way in Replicon to add artificial lag, jitter and package loss?
There are tools for that.
On Linux check out Comcast, on Windows - clumsy.
As far as I understood those work on the network interface and wonโt work if you are testing with localhost? Or do they also allow you to test network conditions locally?
Should work on localhost as well. Clumsy even showcases it in their gif: https://jagt.github.io/clumsy/index.html
Yea I usually do this on localhost with some tc commands ... I have observed it to cause some undesirable side effects however, like adding latency to communication to the session API from both the client and the server ... As long as you're aware of what side effects it could cause it shouldn't be a major problem tho
I could try to fix all the naming, push it to github and claim some names on docs.rs at least ... Probably makes more sense to wait for 0.16 before I do a real release tho (cause rollback without entity disabling really sucks)
For a crate release makes a lot of sense.
On GitHub for just testing and trying it out would be really nice before that.
Just curious to see how you implemented it and how it works
Did you come up with a crate name? ๐
Also wish 0.16 included Resources as Entities ... The part where I do rollback on resources is easily the worst
I think I'll steal that "rewind" name, tho I still have to figure out how I'm going to make the subcrates sensibly structured ๐ค
Same, I really hoped for that.
Maybe use singletone entities in the meanwhile? Or do you have some third-party you want to rollback?
The only resource I use rollback on is Collisions from avian
Outside of that resources just aren't part of my game's simulation 
Ah, from Avian, right...
bevy_rewind? Sounds nice.
But if we get a shooter-style rollback, it might be a bit confusing ๐ค
Maybe better go with bevy_replicon_rewind to clarify that it's for networking
Yea I'm planning to do bevy_replicon_rewind, tho I'd probably also claim bevy_rewind in case I ever make it generic across replication layers, or something like replicon gets upstreamed but rollback doesn't
Just gonna need to figure out how to name all the subcrates, there's the rollback, input queue, entity management, and disabling (still have to experiment on bevy main if this is still necessary tho)
Yeah, naming them as bevy_replicon_rewind_something would be weird
I remember you mentioned that they are fairly standalone? Maybe just call them bevy_replicon_something?
The input queue is standalone at least, would have to check for the other two
They aren't entirely separate tho, like you could use the input queue outside of rollback, but making your own input queue that plays well with this rollback would be annoying
Understandable.
I wish we could have scopes in crates.io
I would just put them under bevy_replicon_something even if some of them rely on rewind
Hmmm, entity management is coupled to rollback (which makes sense I guess, otherwise it ends up doing extra work for entities that aren't even predicted)
The other one I'm not super sure I can remove yet ... Upstream entity disabling has no counter in Disabled, nor does it trigger anything when you disable/enable entities ... Those triggers aren't super necessary but OnAdd/OnRemove observers would need filters (or we'd need query observers) to easily get the same behavior ๐ค
Filtered observers would be really nice. I currently just do checks with queries in my game. Or is it expensive for what you are doing?
I think it would be viable in my game, I don't have all that many predicted entities, and the majority of them are the ones I'd need the observers for
I think it mainly depends on the timeline of this feature
Ah, so the upstreamed solution is better because it doesn't trigger anything?
And current solution is a temporary replacement to make it work with 0.15?
Idk if I would necessarily say better, it lacks features because they would've been controversial, but this makes utilizing it much harder
Ah, I see ๐ค
I knew there was something I still had to do for the entity management crate ...
has_authority: (), // TODO
It doesn't support cases where server vs client isn't statically compiled 
Ah yes, of course I can't get this working on 0.15 ... Would be difficult to get a working example on bevy main tho, considering they don't track main ๐ค
Ah, you rely on Disabled, makes sense.
I can port Replicon to main, but we will also need to port a backend and avian...
BTW, wasn't this under a feature?
The backend is the example backend so that shouldn't be too hard
Ah, right!
A separate crate, but it's the last part I need to get right before I can publish something
So do you need main? I can port replicon to main this evening, just currently at work
Would probably be ideal ... Especially if we also want to mess around with the relations networking before the release ... Would be amazing if we the replicon release for 0.16 would include something for that ๐ค
Iirc the state of networking related entities is that the rework to make it possible was done, but the feature isn't there, right?
Okay!
About relations, In 0.16 we'll have one to many. I don't think we'll need anything special ๐ค If I understand it correctly, you can replicate Parent and Children will be created automatically.
Yea the only thing we'd need is the ability to group them into the same packet ... And I guess a way to specify which relations should have that behavior (only ones where desynchronized state can cause problems would want this I guess?)
Ah, yes ๐ค
Right it won't cause any issues because insertions and spawns are a single reliable message and updates, even if split, won't be applied unless the minimum required update is received. So you will never receive a component that points to an invalid entity.
But it's a bit conservative. With relations we could make it much more performant.
On second thought, if there weren't any insertions, spawns, or removals, but some entities changed their parents and the changes get split, it might be weird to see only half of the changes applied.
So you are right, we might better group related entities smartly
Yea, there's quite a few cases where them updating separately results in something that is wrong in some way
Which is why I currently still use that map for status effects (even if it is wildly inefficient because it now sends all of them when the map changes, rather than just the status that changed)
Definitely will try to look into it before the release
But yea it's definitely gonna be important to have some control over this ... Not every relation will need this, and even when we need it, it might not be needed for every instance, though I'm not sure if replicating huge hierarchies of replicated entities is currently a thing people do ๐ค
It's similar to what Lightear call "entity grouping".
But I deliberately waited for relations for it.
Yea without relations these usecases aren't easy anyway
The replication/rollback side of my status effects is a mess, but so is keeping the stupid map up-to-date ๐
I wonder if we could always use unreliable channel and use relations to ensure partial world updates aren't a mess ๐ค
We will of course need 1:1 and many:many
With the last serialization rework it should be doable, yes.
We also now have connected clients as entities as a PR. Might be useful for relations as well.
But koe doesn't seem to like this change ๐ค
koe's main concern seems to be the removal of client ids
Which I guess makes some sense ... With things like he indexing feature on main (at least if that got merged) or a map from client id to entity we could have both pretty easily however ... The ergonomics of working with entities instead of maps with client id -> client data is definitely better tho
Ah, I totally forgot about the indexing feature! Hope it gets merged.
Yes, after some thinking I also more inclined to keep ClientId and just provide a map (until we don't have the indexing feature). The map will be useful for some third-party plugins, while users can continue to use Entity for better ergonomics.
I'm using bevy_replicon (with renet) and replication works fine, but when trying to get events to work, I get a panic with Called 'send_message' with invalid channel 2 (same but with receive on server side). I just use the default channel init, so I'm not sure if I'm missing something obvious here, anyone knows where to look further?
It means that the channels weren't setup properly ๐ค
Double check how you setup channels, you should pass them from Replicon: https://github.com/projectharmonia/bevy_replicon_renet/blob/aa3443181aadacd33b893f554c2e40a1cea0d624/examples/simple_box.rs#L108
Yes that's what I did. Client side setup looks like this: https://gist.github.com/almindor/030386a484e82ca514fcca875d05269c
Looks correct to me. Are you sure that you register events before setting up the client and server?
Try running with RUST_LOG=bevy_replicon=debug cargo run ..., it should show us what you registering
you mean add_client_event should be called before the insert_resource of client/transport?
doesn't seem to change anything if I change that order
```2025-03-07T19:25:42.448582Z DEBUG bevy_replicon_renet: creating channel config ChannelConfig { channel_id: 0, max_memory_usage_bytes: 5242880, send_type: ReliableOrdered { resend_time: 0ns } }
2025-03-07T19:25:42.448615Z DEBUG bevy_replicon_renet: creating channel config ChannelConfig { channel_id: 1, max_memory_usage_bytes: 5242880, send_type: Unreliable }
2025-03-07T19:25:42.448620Z DEBUG bevy_replicon_renet: creating channel config ChannelConfig { channel_id: 0, max_memory_usage_bytes: 5242880, send_type: ReliableOrdered { resend_time: 0ns } }
2025-03-07T19:25:42.448624Z DEBUG bevy_replicon_renet: creating channel config ChannelConfig { channel_id: 1, max_memory_usage_bytes: 5242880, send_type: Unreliable }
2025-03-07T19:25:42.448774Z DEBUG bevy_replicon::core::event::client_event: registering event gizmo::entities::Controller
2025-03-07T19:25:42.448784Z DEBUG bevy_replicon::core::channels: creating a client channel with ID 2
2025-03-07T19:25:42.577590Z DEBUG bevy_replicon::core::replicon_server: changing RepliconServer running status to true
2025-03-07T19:25:43.295525Z DEBUG bevy_replicon_renet::server: forwarding event ClientConnected { client_id: 0 }
2025-03-07T19:25:43.295622Z DEBUG bevy_replicon::server: ClientId(0) connected
2025-03-07T19:25:43.295637Z DEBUG bevy_replicon::core::connected_clients: adding connected ClientId(0)
2025-03-07T19:25:43.295645Z DEBUG bevy_replicon::core::replication::replicated_clients: starting replication for ClientId(0)
thread 'Compute Task Pool (6)' panicked at /home/ales/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/renet-1.0.0/src/remote_connection.rs:349:13:
Called 'receive_message' with invalid channel 2```
that's server
From this log I can see that you register events after you create channels
Before you read channels from RepliconChannels
Yep, I definitely need to rework my quickstart guide. My todo before the next release ๐
Done: https://github.com/projectharmonia/bevy_replicon/pull/428
It's a quick and dirty migration, but it compiles, tests pass and examples run.
I need to update docs and properly implement mappers on contexts, but I'll do it later. Don't want to get into large conflicts with other PRs. But this should unblock you ๐
what's the expectaion of mapping "this entity is what the player on a client controls" ? I have things set up such that server is standalone and creates the whole world using replication, including the "player controlled" entity. How should the flagging be typically handled for the client? e.g. how can I inform a client "this newly replicated entity? that's your thing for controlling"
2 options:
- Spawn entity on client and send an event to server that says "Hey, I control this entity", server inserts the entity into https://docs.rs/bevy_replicon/latest/bevy_replicon/server/client_entity_map/struct.ClientEntityMap.html and it just works
- Server spawns an entity and says "Hey, you control this entity".
A resource that exists on the server for mapping server entities to entities that clients have already spawned. The mappings are sent to clients as part of replication and injected into the clientโs ServerEntityMap.
i mean I guess I could send the entity id over as an event to specific clients?
right ๐
thanks
is it safe to use an Event as both an event and as a Component ?
e.g. I have a component on an entity, that's also an event I actually send over for event handling
Yes
how does ParentSync work exactly? Do I assign it to the children entities, the parents, both? Do I need to register it for replication?
Only to children ๐
See this example: https://docs.rs/bevy_replicon/latest/bevy_replicon/parent_sync/struct.ParentSync.html
Replicates parent-children relations for an entity.
Won't be needed for Bevy 0.16. In 0.16 you will be able to just register Parent for replication.
Because of relations
In 0.15 this helper is still needed.
how would you go about "hybrid" replication for physics simulated entities you don't want to fully replicate movement of (e.g. fast moving simulated projectiles)? I was thinking if I could somehow "init replicate" them with physical properties, but then let them simulate fully on the client that'd be best (of course "resolution" events would be server authoritative but that's out of the scope of this)
With the described approach you would rely on determinism. And it's really hard to guarantee.
I would recommend taking the Rocket League approach. In short: you simulate locally, the server sends you the state, and you correct it by rolling back the entire world to the last confirmed tick and re-simulating until your current timeline.
@dire aurora is working on a crate that implements this on top of Replicon ๐
What I'm thinking is that client side simulation would only be "path simulation" (e.g. gravity effects etc. yes, but no colission detection or impact on objects). This way server would remain fully authoritative on the "fast/many" elements and their lifetime and impact, but their "traversal" would not need to be replicated. If there are discrepancies, that's ok it should be fairly minor
so e.g. a shot on the client will "fly through" and not impact a target unless the server replicates the despawn and impact info
I'd need to implement a sort of "init only" approach to replication on these tho, e.g. "create a shot with initial transform and velocity" through replication but then stop replicating transform
It depends on the game. If weird glitches are acceptable, then it could work.
it's a top down (2d plane) space shooter kind of game, using capital ships with mounted/aiming weapons
I'm hoping to come up with a replication scheme that will make it viable for up to 10 players
there will be hitscan but most things will be projectiles, physics plays a role since everything is essentially simulated and gravity is a gameplay mechanic (e.g. black hole, that sort of thing)
Then I would go with Rocket League approach
Here is a good video: https://www.youtube.com/watch?v=ueEmiDM94IE
In this 2018 GDC talk, Psyonix's Jared Cone takes viewers through an inside look at the specific game design decisions and implementation details that made the networked physics of Rocket League so successful.
Is starts with physics, so skip to 23:30 for the networking part
Definitely sounds like a good usecase for the rocket league approach yes ... Things like projectiles are already kind of a pain with the shooter approach, but it gets so much worse when physics are involved
I'll at least try and see how much discrepancy I get with the "init and forget" approach. My hope is if I rollback and re-apply the shot physics (e.g. move the shot physics simulation forward to "server time" on client side replication moment) it should minimize visible discrepancies. But I need to test this. Right now I'm just happy to have a jerky basic game going. Thanks for the library btw! it's great. I also love how ECS starts to click on all these things so nicely
Am I correct in assuming that replication and events are not ordered in any order? e.g. if I spawn something replicating on the server and then send it's mapped entity over in an event, the client could get that event before the replication spawn on its end?
No, we guarantee that events will never reference an invalid entity
Hmm, I'm seeing a very odd issue, not sure if it's bevy or replicon/bevy level. If I run my server and client (both render, but slightly in different ways) in one workspace (e.g. 2 windows side by side) things seem to work fine. If I put one on another workspace things get extremely choppy to the point of beeing frozen on both sides. And I don't mean just input event level things, even basic simulation on the server stops. Interestingly tho, debug log shows things such as client sending input events on mouse moves normally
even the initial connection sometimes takes like 10s when done like this, they both sit there, server reports connected, client reports connecting, but nothing happens for a while and when it does it's like 10s lag
I'm guessing it's a window focus issue
nah, not focus, just "visible at all" issue. even unfocused, with continuous winit setting it works, but as soon as the server window is obscured completely it stops processing
works if I go headless
Merged connected clients as entities with all concerns addressed ๐
Planning to submit a few more minor fixes and draft a new release.
Fix for max message size is up: https://github.com/projectharmonia/bevy_replicon/pull/429
It's a public field since in quinnet this value is dynamic: https://docs.rs/quinn-proto/0.11.8/quinn_proto/struct.Datagrams.html#method.max_size
Hey Shatur, what are your opinions on https://github.com/bevyengine/bevy/pull/17687?
I like the new ergonomics, but the requirement for EntityMapper::set_mapped is a bit odd...
In the 0.16-dev branch I created, I just stubbed it since I can't implement it.
I like the general approach, but with the avian porting work I'm doing I'm just a bit confused about it
There's 3 traits for this now and they aren't all compatible
If they were compatible this could really cut down on a lot of multiplayer boilerplate tho 
Could even get rid of mapped vs non-mapped if the trait can always provide the mapping and that gets optimized out when unnecessary
VisitEntities, VisitEntitiesMut and Component?
Yes and MapEntities also still exists
I agree, I don't quite understand why VisitEntities, VisitEntitiesMut are still needed, Component provide these methods
You also get some fun issues like how EntityHashMap can't be marked with #[entities]
Because it doesn't have VisitEntitiesMut
Ah, that's why we need separate trait for non-components ๐ค

